Access a parent class from an enum - swift

So I created a class and typecasted one of the variables to be from an enum i created. The enum has a variable named description that I call and put on the page so that as the variable changes status its description on the page will update.
That part is fine and dandy. But I would like to put a variable in the description, but have it reference the variable from its object from that class.
Here is an example:
class room: NSObject{
var roomNumber :Int
var status: requestStatus = .none
var assignedAssociate: String?
init(roomNumber:Int){
self.roomNumber = roomNumber
}
}
enum requestStatus {
case waitingForHelp = "Waiting For Help"
case beingHelped = "Being helped"
case requestClosed
case none
var description: String {
switch self {
case .waitingForHelp: return "Needs Help\n "
case .beingHelped: if return "Being Helped\n by \(super.assignedAssociate)"
case .requestClosed: return "Completed"
case .none: return ""
}
}
}
So as you can see, I would like to reference assignedAssociate from the object that was instantiated from the room class inside the description for the enum beingHelped.
I just wrote in super.assignedAssociate so that you could see kinda what I wanted to do.
Oh and before everyone jumps on me, I have spend a while trying to find the information on this but swift is still so new its hard to find good sources for this stuff.
Thanks!

Things do not automatically know what contains them (this is true in every language I know). You will need to store the information in the enum using associated data. For example, I would probably implement it this way:
struct Room {
let roomNumber: Int
var status: RequestStatus?
init(roomNumber: Int) {
self.roomNumber = roomNumber
}
}
enum RequestStatus {
case WaitingForHelp
case BeingHelped(associate: String)
case Closed
var description: String {
switch self {
case .WaitingForHelp: return "Needs Help"
case .BeingHelped(let associate): return "Being Helped by \(associate)"
case .Closed: return "Completed"
}
}
}
Note how I removed assignedAssociate. This was intentional. Instead, that information is attached to the status when the status is BeingHelped.
Also note that I removed the .none case. If the status may be "nothing" that is generally better indicated with an Optional. If you really do want an "unassigned status", then you should call it Unassigned or something similar. None looks too much like Optional.None and this can lead to surprising compiler behavior. (Also note that all types and enum values should have a leading cap.) Personally, I'd probably just get rid of the Optional, too. Can there really be "no status?"

Related

How can I get similar print result of NSLog for int enum in swift?

enum ImportType: Int {
case First = 1
case None
case Original
}
var type: ImportType = .First
print(type) --------------------> This will output "First"
NSLog("%#", String(type) --------------------> I can't do this.
NSLog("%d", type.rawValue) --------------------> This will output "1"
Hi All,
I want to get similar result of NSLog as the print function, it more readable for people, but I can't found a way to do this, I got some result that need to do additional handling inside the enum, but I am using other body's source code and just want to collect some information directly.
Are there easy transform way to do what I want?
Thanks~~
Eric
print uses String.init(describing:) under the hood to convert whatever you give it to a String, so you can do that too:
NSLog("%#", String(describing: type))
But really though, the enum should conform to CustomStringConvertible:
enum ImportType: Int, CustomStringConvertible {
case First = 1
case None
case Original
var description: String {
switch self {
case .First:
return "First"
case .None:
return "None"
case .Original:
return "Original"
}
}
}
and you should not rely on this default behaviour of String(describing:), because its behaviour is not specified unless the type conforms to TextOutputStreamable, CustomStringConvertible or CustomDebugStringConvertible. See here for more info.

Sub enumeration rawValue

Consider the following code, where I declared an enum with sub enums inside of it.
enum LocalizeKey {
case message(Messages)
case buttons(Buttons)
enum Buttons: String {
case remove = "Remove"
case add = "Add"
}
enum Messages: String {
case success = "Success"
case failure = "Failure"
}
}
In a normal enum with no sub enums we can easily access .rawValue property and get the raw value of whatever case we picked.
For this case, i created a function like this just to check out what am i getting .
func keyString(for type: LocalizeKey) {
print(type)
}
keyString(for: .message(.failure)) // usage
Problem : there are no other properties than .self to access for this LocalizeKey enum .
What I am trying to achieve: perhaps you can relate by the naming, i am trying to wrap my localized keys, so i can access them easily based on the key type etc, and the rawValue that is refering to the actual key will go into the getLocalizedValue function .
Playground Output : using the function above the playground output was
message(__lldb_expr_21.LocalizeKey.Messages.failure)
Edit: without having to create a variable that switches self on every case, imagine if we had +400 key that would be a huge mess probably.
You need to switch on the type parameter and do pattern matching:
switch type {
case .message(let messages): return messages.rawValue
case .buttons(let buttons): return buttons.rawValue
}
You can also make this an extension of LocalizeKey:
extension LocalizeKey {
var keyString: String {
switch self {
case .message(let messages): return messages.rawValue
case .buttons(let buttons): return buttons.rawValue
}
}
}
You are going to have to switch somewhere. If there are only a handful of "sub-enums", it is probably the easiest to just write a switch manually:
func keyString(for type: LocalizeKey) {
switch type {
case .message(let message):
print(message.rawValue)
case .buttons(let button):
print(button.rawValue)
}
}
If you don't want to write this manually, you either have to change your data structure so it is not needed, or use a code generation tool that generates the boilerplate for you.
Although The mentioned answers do provide the solution, I'd mention the issue of the approach itself:
At this point, each new case (key) has to be added in your switch statement with an associated value, which seems to be undesired boilerplate coding; I assume that you could imagine how it will look like when having many cases in the enums.
Therefore, I'd recommend to follow an approach to be more dynamic instead of adding the value of each case manually in a switch statement. Example:
protocol Localizable {
var value: String { get }
}
extension RawRepresentable where Self: Localizable, Self.RawValue == String {
var value: String { return rawValue }
}
extension CustomStringConvertible where Self: RawRepresentable, Self.RawValue == String {
var description: String { return rawValue }
}
struct LocalizeKey {
enum Buttons: String, Localizable, CustomStringConvertible {
case remove = "Remove"
case add = "Add"
}
enum Messages: String, Localizable, CustomStringConvertible {
case success = "Success"
case failure = "Failure"
}
}
We are applying the same logic for your code, with some improvements to make it easier to maintain.
Based on that, you still able to implement your function as:
func keyString(for type: Localizable) {
print(type)
}
Usage:
keyString(for: LocalizeKey.Buttons.add) // Add
keyString(for: LocalizeKey.Messages.success) // Success
IMO, I find calling it this way seems to be more readable, straightforward rather than the proposed approach (keyString(for: .message(.failure))).

How to access associate enum in generic way in swift

I am trying to understand swift enum and for that, I decided to create a data source using an associated enum. Now I am not sure if my question line is correct or not but I'll try to explain what exactly I am trying to do here.
Struct SampleClass {
enum Country: String {
case US(cityList: ChianCityList)
case Chian(cityList: USCityList)
}
enum ChianCityList {
case Bijing
case Shanghai
static var allCases = [.Bijing, .Shanghai]
}
enum USCityList {
case NewYork
case LA
static var allCases = [.NewYork, .LA]
var isCaptial:Bool
}
var country: Country
var allCityList: [?] {
switch self.conuntry {
case Chian
return CityList.allCases
case US
return USCityList.allCases
}
init(country: Country)
{
self.country = Country
}
}
Now I don't know what would be the return type of var 'allCityList'. I want it to be generic. secondly, I don't want to call 'allCases' for each enum. Is there anyway to make it more generic? This is just a simple example there is a lot of scenarios like this. Like 'isCaptial'. How could i make it more generic so that based on the country I can found out?
Currently, USCityList and ChinaCityList are two separate types. If you return one type you can‘t return the other. You have two options.
enum ChianCityList {
case Bijing
case Shanghai
static var allCases = [.Bijing, .Shanghai]
}
enum USCityList {
case NewYork
case LA
static var allCases = [.NewYork, .LA]
var isCaptial:Bool
}
Option 1: Convert the one you currently return to the same type and make said type as the return type
Example:
enum ChianCityList: String, CaseIterable {
case Bijing
case Shanghai
}
enum USCityList: String, CaseIterable {
case NewYork
case LA
var isCaptial:Bool
}
extension CaseIterable {
var allCasesToStrings: [String] {
return allCases.map{ String(describing: $0) }
}
var allCityList: [String] {
switch self.conuntry {
case Chian
return CityList.allCasesToStrings
case US
return USCityList.allCasesToStrings
}
Option 2: Make both types conform to the same protocol, then return an instance of said protocol.
Example:
protocol CityList {
var cityList: [String] { get }
Var capitalCity: String { get }
}
// Have both implement the protocol
var allCityList: CityList {
switch self.conuntry {
case Chian
return CityList.cityList
case US
return USCityList.cityList
}
For your example, I wouldn't use enums. This is because you want to store additional information about the cities (like if it's a capital city). Instead, I would use a struct called City that encapsulates all your information. Then you can have a variable chinaCities and usCities that is an array of all its respective cities.
If you truly wanted to force the use of enums, I would create one giant enum called City and store an array of Citys into two separate variables to differentiate their locations.
enum City: String {
case beijing = "beijing"
case newYork = "new york"
}
Of course, if you do this, you'll also have to do extra work to keep track of whether a city is a variable. One way you can achieve this is by having a function that takes in a City enum and returns true or false depending on whether it is a capital.
As a side note, I just wanted to point out that you spelled China and Beijing wrong. Hope this helps!

Swift - Nested Enums default values

I have an enum as follows
enum AccountForm: String {
case Profile
enum Content: String {
case Feedback
case Likes
}
enum Actions: String {
case Redeem
case Help
}
}
This represents a form, where profile content and actions are sections and the cases are rows.
These resolve to strings and work as expected
AccountForm.Profile.rawValue returns "Profile"
AccountForm.Content.Feedback.rawValue returns "Feedback"
However, I'd like AccountForm.Content.rawValue to return "Content"
Is this possible? Or is there a better way besides enums to achieve this?
I'm guessing you've got an answer to this by now but just in case you didn't try this:
enum AccountForm : String {
case profile
enum Content: String {
static let rawValue = "Content"
case feedback = "Feedback"
case likes = "Likes"
}
enum Actions : String {
static let rawValue = "Actions"
case redeem = "Redeem"
case help = "Help"
}
}
Static properties on both the Content and Actions enumerations should achieve what you want. A word of warning though. By calling the properties rawValue you're obviously implying the returned values are raw values when technically they aren't. If you can I'd think of a better name (maybe sectionTitle?).
Couple of other things to note.
First, you have to define the properties as static as it sounds like you want to call them on the enumeration type (e.g. AccountForm.Content.rawValue) rather than on an individual enumeration case (e.g. AccountForm.Content.feedback.rawValue). I'll leave you to decide whether that makes sense in your context.
Secondly, when Swift 3.0 arrives, the recommendation for enumeration cases is going to be that case labels follow a lowerCamelCase naming convention rather than the UpperCamelCase convention that was recommended in Swift 2.2.
I've followed the Swift 3.0 recommendation here but the result is that explicit raw-value assignments is needed as you won't be able to rely on using the implicit raw-value assignment mechanism assigning a string with an UpperCamelCase representation which is kind of annoying but those are the implications.
Anyway, hope it helps.
enum Typo {
case Bold
case Normal
case Italic
case All
}
enum Format: CustomStringConvertible {
case Header(Typo)
case Text(Typo)
var description:String {
switch self {
case .Header(let value) where value != .All:
return "Header.\(value)"
case .Header(let value) where value == .All:
return "Header"
case .Text(let value) where value == .All:
return "Text"
case .Text(let value) where value != .All:
return "Text.\(value)"
default:
return ""
}
}
}
let a:Format = .Header(.Bold)
let b:Format = .Text(.Italic)
Format.Header(.All) // Header
Format.Text(.Bold) // Text.Bold
Format.Text(.All) // Text

Why class cannot be initialised in Switch statement in Swift

wondering why Swift switch statement does not allow to instantiate classes like in other languages and how to solve this. Would be glad if anyone can help out on this.
In the example i have created a simple Vehicle class and trying to instantiate its sub classes via switch depending on classSetter value. However the final line of print statement cannot print name property of any of the classes if it is instantiated within switch (or seems to any other kind of conditional) statement.
import UIKit
class Vehicle {
var name: String {return ""}
}
class Car: Vehicle {
override var name: String {return "Car"}
}
class Bike: Vehicle {
override var name: String {return "Bike"}
}
var classSetter:Int = 1
switch classSetter {
case 1:
println("Initializing Car")
var vehicle = Car()
case 2:
println("Initialized Bike")
let vehicle = Bike()
default:
println("just defaulted")
}
println("Name property from initialization is \(vehicle.name)")
Your two vehicles are being declared within the switch’s { }, so they only exist in that block (that is their “scope”). They don’t exist outside it, so you can’t refer to them there, hence the error.
The default solution to this (that other answers are giving) is to declare the vehicle as a var outside the switch, but here’s an alternative: wrap the switch in a closure expression and return a value from it. Why do this? Because then you can use let and not var to declare vehicle:
let vehicle: Vehicle = { // start of a closure expression that returns a Vehicle
switch classSetter {
case 1:
println("Initializing Car")
return Car()
case 2:
println("Initialized Bike")
return Bike()
default:
println("just defaulted")
return Vehicle()
}
}() // note the trailing () because you need to _call_ the expression
println("Name property from initialization is \(vehicle.name)")
It would be nice if if and switch were expressions (i.e. evaluated to a result) so you didn’t need this closure, but for now it’s a reasonable workaround.
Note, several of the answers here that use the var approach suggest making vehicle an Optional value (i.e. Vehicle?). This is not necessary – so long as the code is guaranteed to assign vehicle a value before it is used (and the compiler will check this for you), it doesn’t have to be optional. But I still think the closure expression version is a better way.
By the way, you might want to consider using a protocol for Vehicle instead of a base class, since that way you don’t have to give Vehicle a default but invalid implementation for name:
protocol Vehicle {
var name: String { get }
}
// one of the benefits of this is you could
// make Car and Bike structs if desired
struct Car: Vehicle {
var name: String {return "Car"}
}
struct Bike: Vehicle {
var name: String {return "Bike"}
}
Though this would mean you couldn’t have a default return from the switch statement of a Vehicle(). But chances are that would be bad anyway – an optional Vehicle? with nil representing failure might be a better option:
let vehicle: Vehicle? = {
switch classSetter {
case 1:
println("Initializing Car")
return Car()
case 2:
println("Initialized Bike")
return Bike()
default:
println("no valid value")
return nil
}
}()
// btw since vehicle is a Vehicle? you need to unwrap the optional somehow,
// one way is with optional chaining (will print (nil) here)
println("Name property from initialization is \(vehicle?.name)")
If you didn’t want this to be a possibility at all, you could consider making the indicator for different kinds of vehicles an enum so it could only be one of a valid set of vehicles:
enum VehicleKind {
case Bike, Car
}
let classSetter: VehicleKind = .Car
let vehicle: Vehicle = {
switch classSetter {
case .Car:
println("Initializing Car")
return Car()
case .Bike:
println("Initialized Bike")
return Bike()
// no need for a default clause now, since
// those are the only two possibilities
}
}()
Your assumption is incorrect.
The scope of the variable vehicle is inside the switch. You are then trying to access it outside the switch.
You need to create the variable outside the switch. You can still instantiate it inside.
var vehicle: Vehicle?
Switch... {
Case
vehicle = Car()
}
println(vehicle)
Writing on my phone so couldn't give full proper code but this will give you the idea.
What you're doing doesn't work in other languages either. You need to declare the vehicle variable before you enter the switch so that it's in the same scope as your println. After that, you an assign it whatever you need to inside the switch.