Reputation: 30458
Consider this enumeration (note its type is Int
)
enum MyTestEnum : Int{
case one = 1
case eight = 8
case unknown = -1
}
You can easily initialize a version of this based on the raw value, like so...
let value = MyTestEnum(rawValue:-1)
I'm trying to find out if it can be initialized with a string representation of the case name itself (again, not the raw value, but the word after 'case') like so...
let value = MyTestEnum(caseName:"eight")
Note: I want this to work with any enum if possible, regardless of its raw value type. For instance, this one...
enum MyOtherEnum{
case xxx
case yyy
case zzz
}
let value = MyOtherEnum(caseName:"xxx")
So can this be done?
Thoughts:
Upvotes: 1
Views: 7408
Reputation: 918
Based on CodeBender's solution here is a nice extension for this case
extension CaseIterable {
///Note: case value and name can be different
init?(caseName: String) {
for v in Self.allCases where "\(v)" == caseName {
self = v
return
}
return nil
}
}
Upvotes: 3
Reputation: 36660
In Swift 4.2, this is quite easy to do now using CaseIterable
.
enum MyOtherEnum: CaseIterable {
case xxx
case yyy
case zzz
init?(caseName: String) {
for value in MyOtherEnum.allCases where "\(value)" == caseName {
self = value
return
}
return nil
}
}
enum MyTestEnum: Int, CaseIterable{
case one = 1
case eight = 8
case unknown = -1
init?(caseName: String) {
for value in MyTestEnum.allCases where "\(value)" == caseName {
self = value
return
}
return nil
}
}
What I am doing here is creating a failable initializer which iterates through all potential cases, testing to see if "\(value)"
(which returns the name for that potential case) matches the caseName
argument passed in to the initializer.
When a match is found, self
is set and the loop ends. Otherwise, nil
is returned for the call.
Below, are two working and two failing examples:
let myOtherEnum = MyOtherEnum(caseName:"xxx")
print(myOtherEnum) // MyOtherEnum.xxx
let myTestEnum = MyTestEnum(caseName:"eight")
print(myTestEnum?.rawValue) // 8
let myOtherEnumFail = MyOtherEnum(caseName:"aaa")
print(myOtherEnumFail) // nil
let myTestEnumFail = MyTestEnum(caseName:"ten")
print(myTestEnumFail) // nil
Upvotes: 12
Reputation: 137
You can go with custom initializer
extension MyTestEnum {
public static func allValues() -> [MyTestEnum] {
let retVal = AnySequence { () -> AnyIterator<MyTestEnum> in
var raw = 0
return AnyIterator {
let current = withUnsafePointer(to: &raw) {
$0.withMemoryRebound(to: MyTestEnum.self, capacity: 1) { $0.pointee }
}
guard current.hashValue == raw else { return nil }
raw += 1
return current
}
}
return [MyTestEnum](retVal)
}
init?(caseName: String){
for v in MyTestEnum.allValues() {
if "\(v)" == caseName {
self = v
return
}
}
self = MyTestEnum.unknown
}
}
let test = MyTestEnum(caseName: "eight")
or simple manually all your case :)
extension MyTestEnum {
init?(caseName: String){
switch caseName {
case "eight": self.init(rawValue: 8)
case "one": self.init(rawValue: 1)
default: self.init(rawValue: -1)
}
}
}
let test1 = MyTestEnum(caseName: "eight")
let test2 = MyTestEnum(rawValue: 1)
Hope this helps !!
Upvotes: 1
Reputation: 14824
It sounds like your want your enum cases to have raw Int
values and raw String
values. It's not strictly possible to do this, but perhaps this comes close:
enum MyTestEnum : String {
case one
case eight
case unknown
var intValue: Int {
switch self {
case one: return 1
case eight: return 8
case unknown: return -1
}
}
}
Now you can initialize from case names, but you can also retrieve an intValue
when needed. (If needed, you could easily add a failable initializer to allow initialization from integers as well.)
Upvotes: -1