Swift - Nested Enums default values - swift

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

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))).

Create an enum with parameter

I have some Outputs in my code so I regrouped all that with an Enum String.
The problem is that I have some Outputs containing variable.
Is It possible to create an Enum who takes variable ?
Exemple with this string
print("The name of the Team is \(team.name)")
I wanted to do something like that:
enum Exemple: String {
case TEAM_NAME(name: String) = "The name of the Team is \(name)"}
print(Exemple.TEAM.NAME("Team 1").rawvalue)
Thank you
It's possible to have an enum with associated values for cases. But in order to get the output you're looking for you will need a function.
enum Example {
case teamName(name: String)
case teamId(id: Int)
func printName() {
switch self {
case .teamName(name: let name):
print(name)
default:
break
}
}
}
let team = Example.teamName(name: "team1")
team.printName() // prints team1
You can define an instance method or computed property for enum that will return a string value in depend of enumeration case and associated value. See example for playground.
enum Example {
case firstItem
case secondItem(withText: String)
var stringValue: String {
switch self {
case .firstItem: return "Simple string"
case .secondItem(withText: let text): return "String with additional text: \(text)"
}
}
}
let myEnumItem: Example = .secondItem(withText: "Test")
let text = myEnumItem.stringValue
It is not possible for Enum to have both raw value and associated value. I think you can get by with the associated value. Without the raw value the enum still can provide you enough information to compose the message

Swift Switch case on enum with Classes

This is the first time I use this kind of enums, enum with associated value type, I need to make a switch statement depending on the object's type, I cannot managed to do it, this is the enum:
enum TypeEnum {
case foo(FooClass)
case doo(DooClass)
case roo(RooClass)
}
My object has a variable of type TypeEnum, now I need to check which kind of object is in the enum:
if let anObject = object as? TypeEnum {
switch anObject {
case .foo(???):
return true
...
default:
return false
}
}
I have no idea what to put instead of ???. Xcode tells me to put something, but I just want to switch on .foo.
Any ideas?
You can use let to capture the associated values for that:
switch anObject {
case .foo(let fooObj):
...
}
or nothing at all if you just don't care about them:
switch anObject {
case .foo:
...
}
Please be sure to check the Swift Programming Language book for further details.
You could use the underscore to indicate that you aren't interested in the associated type:
case .foo(_):
...

In Swift, is there a way of determining whether an enum is based on a certain type (eg. String)?

In order to write generic code for an NSValueTransformer, I need to be able to check that an enum is of type String for example. Ie.:
enum TestEnum: String {
case Tall
case Short
}
I am expecially interested in a test that can be used with the guard statement. Something allong the line of:
guard let e = myEnum as <string based enum test> else {
// throw an error
}
Please note that not all enums have raw values. For eample:
enum Test2Enum {
case Fat
case Slim
}
Hence a check on the raw value type can not be used alone for this purpose.
EDIT
After some further investigation it has become clear that NSValueTransformer can not be used to transform Swift enums. Please see my second comment from matt's answer.
First, it's your enums, so you can't not know what type they are. Second, you're not going to receive an enum type, but an enum instance. Third, even if you insist on pretending not to know what type this enum is, it's easy to make a function that can be called only with an enum that has a raw value and check what type that raw value is:
enum E1 {
case One
case Two
}
enum E2 : String {
case One
case Two
}
enum E3 : Int {
case One
case Two
}
func f<T:RawRepresentable>(t:T) -> Bool {
return T.RawValue.self == String.self
}
f(E3.One) // false
f(E2.One) // true
f(E1.One) // compile error
Generics to the rescue :
func enumRawType<T>(of v:T)-> Any?
{ return nil }
func enumRawType<T:RawRepresentable>(of v:T)-> Any?
{
return type(of:v.rawValue)
}
enumRawType(of:E1.One) // nil
enumRawType(of:E2.One) // String.Type
enumRawType(of:E3.One) // Int.Type