Skip to content

Instantly share code, notes, and snippets.

@martijnwalraven
Created October 13, 2017 05:41
Show Gist options
  • Star 3 You must be signed in to star a gist
  • Fork 1 You must be signed in to fork a gist
  • Save martijnwalraven/7535b673a1cf5208a2ba2116397f2622 to your computer and use it in GitHub Desktop.
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
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