Created
November 2, 2017 22:18
-
-
Save nubbel/a67713415e96a8fcb43a2082872f5d3a to your computer and use it in GitHub Desktop.
Apollo-Link Context
This file contains 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
//: Playground - noun: a place where people can play | |
import Foundation | |
public struct ContextKey<Value> { | |
public let name: String | |
public let defaultValue: () -> Value | |
public init(_ name: String, default defaultValue: @escaping @autoclosure () -> Value) { | |
self.name = name | |
self.defaultValue = defaultValue | |
} | |
} | |
extension ContextKey: Equatable { | |
public static func ==(lhs: ContextKey<Value>, rhs: ContextKey<Value>) -> Bool { | |
return lhs.name == rhs.name | |
} | |
} | |
extension ContextKey: Hashable { | |
public var hashValue: Int { | |
return name.hashValue | |
} | |
} | |
public final class Context { | |
private var data = [AnyHashable: Any]() | |
public func get<Value>(_ key: ContextKey<Value>) -> Value { | |
return data[key, default: key.defaultValue()] as! Value | |
} | |
public func set<Value>(_ value: Value, for key: ContextKey<Value>) { | |
data[key] = value | |
} | |
public subscript<Value>(_ key: ContextKey<Value>) -> Value { | |
get { | |
return get(key) | |
} | |
set { | |
set(newValue, for: key) | |
} | |
} | |
} | |
public enum ContextKeys { | |
} | |
// in HTTP link | |
extension ContextKeys { | |
public static let HTTPHeaders = ContextKey<[String: String]>("http.headers", default: [:]) | |
public static let HTTPMethod = ContextKey<String>("http.method", default: "GET") | |
} | |
// Usage | |
let context = Context() | |
// in Auth link | |
context[ContextKeys.HTTPHeaders]["Authorization"] = "Bearer my-jwt-token" | |
context[ContextKeys.HTTPHeaders] // => ["Authorization": "Bearer my-jwt-token"] | |
// in another link | |
context[ContextKeys.HTTPMethod] = "POST" | |
var headers = context[ContextKeys.HTTPHeaders] | |
headers["X-Environment"] = "development" | |
headers["Content-Type"] = "application/json" | |
context[ContextKeys.HTTPHeaders] = headers | |
// in HTTP link | |
context[ContextKeys.HTTPMethod] // => POST | |
context[ContextKeys.HTTPHeaders] // => ["Content-Type": "application/json", "X-Environment": "development", "Authorization": "Bearer my-jwt-token"] | |
Nifty little trick, I like it!
I'm not sure if value semantics actually do make sense here. In each link, after making changes to the context, one would need to remember to call the context setter on the operation. Example:
class SomeLink {
func request(op Operation) {
var ctx = op.context
// do something with the context
op.context = ctx // do not forget this!
}
}
Wouldn't it be more convenient to just do the changes to the context via a reference type?
Regarding your simpler solution: I agree, it is indeed much more simpler and also provides the simplest API possible.
However, for third-party links we would need to make data
public in order to allow them to add accessors.
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
I like the experiment :) It's a shame you can't store static properties in generic types, because I'd rather allow people to use
context[.HTTPMethod]
thancontext[ContextKeys.HTTPMethod]
. If found a little trick you can use if you makeKey
a class and let it inherit from a non-generic superclass where you put the static properties on:(I made
Context
a struct because I think value semantics make sense here.)As much as I like experimenting however, it seems adding computed properties directly to
Context
would be a simpler solution:What do you think?