Most people use the following snippet to get a value from an object provided by the JSONSerialization:
guard let name = jsonDictionary["name"] as? String else { return }
...
It's not obvious, but this code is unsafe. It can easily fail if the "name" value will be "111". In this case it may be decoded as
NSNumber
and casting to String
will always fail. The same thing with numbers: guard let id = jsonDictionary["id"] as? Int else { return }
// id can be a "3" String!
...
A value like "3" can be decoded both as NSString
and NSNumber
(more often).I didn't find any tutorial that would take this issue into account. All of them recommend to use
as?
to unknown JSON object to basic type. So I post here the correct snippets that will provide stable and predictable results in all possible cases.JSON helpers
extension String {
init?(jsonObject: Any?) {
guard jsonObject is NSNull == false else { return nil }
let aNSObject = jsonObject as? NSObject
if let description = aNSObject?.description {
self = description
} else {
return nil
}
}
}
extension Int {
var boolValue: Bool { return self != 0 }
init?(jsonObject: Any?) {
if let number = jsonObject as? NSNumber {
self = number.intValue
} else if let string = jsonObject as? NSString {
self = string.integerValue
} else {
return nil
}
}
}
extension Bool {
init?(jsonObject: Any?) {
if let integer = Int(jsonObject: jsonObject) {
self = integer.boolValue
} else {
return nil
}
}
}
extension Double {
init?(jsonObject: Any?) {
if let number = jsonObject as? NSNumber {
self = number.doubleValue
} else if let string = jsonObject as? NSString {
self = string.doubleValue
} else {
return nil
}
}
}
All objects produced by
JSONSerialization
are instances of NSString
, NSNumber
, NSArray
, NSDictionary
, or NSNull
.
No comments:
Post a Comment