I am getting some data from a network response and it comes in as a string but can really be multiple types of data such as an Int, Bool, Date. Is there a nicer way to do this than multiple if statements?
Currently I have (in an enum init)
let value: String = someFieldFromNetworkResponse
if let value = Int(value) {
self = .int(value)
} else if let value = parseDate(value) {
self = .data(value)
} else if let value = parseBool(value) {
self = .bool(value)
} else {
self = .unknown
}
With the actual use case being to parse an associated enum:
public enum IdentificationChallengeNotice: JSONObjectConvertible, Equatable {
case remainingAttempts(_ attempts: Int)
case validUntil(_ date: Date)
case passcodeMatches(_ matches: Bool)
case other(type: String, value: String)
public init(jsonDictionary: JSONDictionary) throws {
let type: String = jsonDictionary.json(atKeyPath: "type") ?? .unknown
let value: String = jsonDictionary.json(atKeyPath: "value") ?? ""
if type == "remainingAttempts", let value = Int(value) {
self = .remainingAttempts(value)
} else if type == "validUntil", let value = parseDate(value) {
self = .validUntil(value)
} else if ... {
...
} else {
self = .other(type: type, value: value)
}
}
I'm trying to do something like this but it doesn't seem possible to have a let in a where clause:
let value: String = someFieldFromNetworkResponse
switch value {
case let v where let value = Int(v):
self = .int(value)
// etc
}
or even something like this would be even more ideal:
switch value {
case let value = Int($0):
self = .int(value)
// etc
}
but it makes me think that switch is probably not the right tool for this kind of thing.
I don't personally think there's anything wrong with just using if statements and you can use return to break up your if statements to make it look a little cleaner:
if type == "remainingAttempts", let intValue = Int(value) {
self = .remainingAttempts(intValue)
return
}
if type == "validUntil", let dateValue = parseDate(value) {
self = .validUntil(dateValue)
return
}
...
self = .other(type: type, value: value)
Yep, I'm find with the if else's it just seems like it's so close to being possible in a switch statement that I thought I was missing something.
public enum IdentificationChallengeNotice: JSONObjectConvertible, Equatable {
case remainingAttempts(_ attempts: Int)
case validUntil(_ date: Date)
case passcodeMatches(_ matches: Bool)
case other(type: String, value: String)
public init(jsonDictionary: JSONDictionary) throws {
let type: String = jsonDictionary.json(atKeyPath: "type") ?? .unknown
let value: String = jsonDictionary.json(atKeyPath: "value") ?? ""
switch type {
case "remainingAttempts", where let value = Int(value):
self = .remainingAttempts(value)
case "validUntil", where let value = parseDate(value):
self = .validUntil(value)
// etc
default:
self = .other(type: type, value: value)
}
}
Seems like valid swift, except you can't have a let value = ... in a where clause. I'm not really sure why not either.
I believe it’s because the case evaluation has to return a Bool i.e case foo and case foo where foo.bar evaluates to a true/false value which is used to decide whether the match is successful or not. I suppose an optional binding (where let a = b) could be supported by treating it as a Bool, however if you really want to do optional binding, the only way to do it now would be inside the case statement:
// For convenience.
func other() -> Self {
return .other(type: type, value: value)
}
switch type {
case “validUntil”:
if let value = parseDate(value) {
self = .validUntil(value)
} else {
self = other()
}
...
}