Created
October 13, 2017 05:41
-
-
Save martijnwalraven/7535b673a1cf5208a2ba2116397f2622 to your computer and use it in GitHub Desktop.
Hacky way of getting a custom GraphQL scalar for raw JSON to work in Apollo iOS
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
typealias Raw = [String: JSONDecodable & JSONEncodable] | |
extension Dictionary: JSONDecodable { | |
public init(jsonValue value: JSONValue) throws { | |
guard let dictionary = forceBridgeFromObjectiveC(value) as? Dictionary else { | |
throw JSONDecodingError.couldNotConvert(value: value, to: Dictionary.self) | |
} | |
self = dictionary | |
} | |
} | |
extension Array: JSONDecodable { | |
public init(jsonValue value: JSONValue) throws { | |
guard let array = forceBridgeFromObjectiveC(value) as? Array else { | |
throw JSONDecodingError.couldNotConvert(value: value, to: Array.self) | |
} | |
self = array | |
} | |
} | |
// The types we get from `JSONSerialization` are Objective-C implementation types like | |
// `NSTaggedPointerString`, `__NSCFNumber`, `__NSSingleEntryDictionaryI`, etc. | |
// and although these get implicitly converted to Swift types in most contexts, | |
// they don’t allow us to check for protocol conformance. | |
// That means `NSTaggedPointerString` is not `JSONDecodable` and `JSONEncodable` | |
// for example, even though `String` is. Force casting to Swift types solves this. | |
private func forceBridgeFromObjectiveC(_ value: Any) -> Any { | |
switch value { | |
case is NSString: | |
return value as! String | |
// Can't use NSNumber here because we don't know whether to bridge to Bool, Int or Double. | |
// So instead we have to check possible conversions one by one. | |
// This isn't strictly correct because it converts 0 and 1 to Bool for example. | |
// See https://bugs.swift.org/browse/SR-5863 | |
case is Bool: | |
return value as! Bool | |
case is Int: | |
return value as! Int | |
case is Double: | |
return value as! Double | |
case is NSDictionary: | |
return Dictionary(uniqueKeysWithValues: (value as! NSDictionary).map { ( $0.key as! AnyHashable, forceBridgeFromObjectiveC($0.value)) }) | |
case is NSArray: | |
return (value as! NSArray).map { forceBridgeFromObjectiveC($0) } | |
default: | |
return value | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment