How to get enum case name in Swift from within CustomStringConvertible implementation? - swift

I have an enum that looks like this:
enum IntteruptCause: UInt8 {
case userDisplayDisengage = 0
case userSteeringWheelDisengage = 1
case positionDropout = 2
}
Currently the output from print() will give me the case name.
I would like to implment CustomStringConvertible and have it produce both the case name and the rawValue, e.g. the string User_Steering_Wheel_Disengage (1).
When I implement var description: String I have easy access to rawValue but I cannot seem to find out how to get the enum case name. Normally I would do String(describing: value) but that would create an infinite recursion if used in description implementation.
Is it possible to get the enum case name from within the description implementation?

Related

Automatic enum case count and indexes in enum with values

I am trying to accomplish the following:
I have an enum
enum MyEnum {
case Position(x: Int, y: Int)
case Name(String)
}
and I want to be able to do the following:
MyEnum.caseCount // returns 2
let val = MyEnum.Position(x: 0, y: 0)
val.idx // returns 0
MyEnum.Name("My awesome name").idx // returns 1
So I want a variable holding the total amount of cases + on a specific enum case I want to know its index in the enum
Is there a way I can automatically derive this (probably will be using a protocol)?
I have looked at CaseIterable, but that can't automatically be implemented for enums holding values. I also can't do = enum MyEnum: Int because of the value again.
Another SO question had a lot of answers, all implented in Swift 3, and none of those seemed to still work.
If unsafe code would have to be used for this, I would be ok with that.
Something like this would work ofcourse, however it is a lot of boilerplate for the user to write:
extension MyEnum {
func id() -> Int {
switch self {
case .Position: return 0
case .Name: return 1
}
}
}

how to define an array of a particular enum case in swift

I want to define an enum case which is recursively defined by a particular instance of the same enum
enum Menu {
case item(String)
case submenu([.item]) // i want a submenu to be defined by an array of items
}
the submenu case isn't correct:
case submenu([.item])
how can I confine it?
All cases of an enum are of the same enum type, so you cannot declare an Array that can only hold a specific case of an enum.
However, you can achieve your goals by creating 2 enums instead of one and making the Menu.submenu enum case take the other enum as its associated value.
enum Menu {
case item(MenuItem)
case submenu([MenuItem])
}
enum MenuItem {
case item(String)
}
Then you can use it like
let menu = Menu.item(.item("main"))
let submenu = Menu.submenu([.item("a"), .item("b")])

Swift - Nested enums - how to perform same action on all nested enums with associated values?

I want to print the raw value of nested enums. For example, if I have a top level Food enum with multiple cases (for simplicity let's just say two: fruit, veggie), each of which are string enums (Fruit: String, Vegetable: String, etc), is there a way to print the inner associated values without doing a switch statement one the top level enum?
My current code is below. As you can see, the more cases I add to Food, the more redundant code will end up in var description. I'd like to write one action for all cases that prints the rawValue of the inner enums.
Is that possible without the switch?
enum Foods {
case fruit(Fruit)
case veggie(Vegetable)
var description: String { // this is what I'd like to replace
switch self {
case .fruit(let type):
return type.rawValue // since every case will look like this
case .veggie(let type):
return type.rawValue
}
}
}
enum Fruit: String {
case apple = "Apple"
case banana = "Banana"
}
enum Vegetable: String {
case carrot = "Carrot"
case spinach = "Spinach"
}
Is that possible without the switch?
No, this is not currently possible.
Workaround: I've noticed a pattern with your enum case names. The raw values of each are all capitalized versions of the case name i.e. case apple = "Apple". If this pattern always holds, you can use the following code:
var description: String { ("\(self)".split(separator: ".").last?.dropLast().capitalized)! }
Which will produce:
print(Foods.fruit(.apple).description) // Apple

Swift: Can I (run-time-checked) look up a CodingKey's corresponding member value?

With a CaseIterable CodingKeys enum and Mirror I can almost implement a runtime-type-checked version of the compiler-magic Encodable implementation. Almost, but not quite, because from a CodingKey value I can't get back to the enum case name, which I need to look up the matching value via Mirror(reflecting: myEncodable).
enum CodingKeys: String, CodingKey {
case defaultName // default: .stringValue is also the myEncodable member name
case nonDefaultName = "some-other-name" // .stringValue is not the myEncodable name, no access to "nonDefaultName"
}
I would have expected to be able to find the case name somehow via Mirror(reflecting: CodingKeys.nonDefaultName) but that doesn't seem to be the case.
Am I missing something? Is this in fact possible using Mirror? (I'm still on Xcode 9, by the way, faking CaseIterable until I get the real thing.)
Any alternative ways to get to the same goal (guarantee that all members mentioned in CodingKeys get looked up on the object and fed to the Encoder)?

Swift error. Used undeclared type String

if we insert line "case let dictionary as [String : AnyObject]:" inside struct's method everything works fine. But if use inside nested enums we get error "Used undefined type String"
public struct JSON {
public enum Type : Int {
case Number
case String
case Bool
case Array
case Dictionary
case Null
case Unknown
public static func evaluate(object: AnyObject) -> Type {
switch object {
case let dictionary as [String : AnyObject]: // this lines supply error. Use of undefined type String
return .Dictionary
default:
return .Unknown
}
}
} // enum Type
Could some one explain why I have the error with String type?
It seems enum Type contains case String, and it hides the String what you want. I tried the code in Playground and there is no more error after change String to another name.
EDIT
How about reading the project SwiftyJSON (only single file)
https://github.com/SwiftyJSON/SwiftyJSON/blob/master/Source/SwiftyJSON.swift
I does very similar job.(JSON Handling)
It also contains the code looks like this:
public enum Type :Int {
case Number
case String
case Bool
case Array
case Dictionary
case Null
case Unknown
}
I think this project will be very helpful for you.
(and I guess you may end up using this project)
As already said in the other answer, String inside enum Type
refers to the enumeration value. The same problem would occur with
let a : Array<Int> = []
let b : Bool = false
inside methods of enum Type. Renaming the enumeration values is probably the best solution.
But for the sake of completeness: You can solve the problem
by prepending the "Swift" module name explicitly to refer to the
String type:
case let dictionary as [Swift.String : AnyObject]:
If you want to fix this without renaming the enum case, you can change the argument type to Swift.String, i.e.:
case let dictionary as [Swift.String : AnyObject]:
That should work (I had a similar problem and this solved it).