Last active
July 23, 2019 09:03
-
-
Save alexdrone/3d7e9f873123656ed88bd61db6ac03f9 to your computer and use it in GitHub Desktop.
Proxy and ProxyBuilder in Swift 5.1 using @dynamicMemberLookup
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
import Foundation | |
// MARK: - Proxy | |
public protocol ProxyProtocol { | |
associatedtype ProxyType | |
/// The wrapped proxied object. | |
var proxiedObject: ProxyType { get } | |
} | |
public extension ProxyProtocol { | |
/// Use `@dynamicMemberLookup` keypath subscript to forward the value of the proxied object. | |
subscript<T>(dynamicMember keyPath: KeyPath<ProxyType, T>) -> T { | |
return self.proxiedObject[keyPath: keyPath] | |
} | |
} | |
@dynamicMemberLookup | |
public struct Proxy<T>: ProxyProtocol { | |
public let proxiedObject: T | |
/// Constructs a new proxy for the object passed as argument. | |
init(of object: T) { | |
self.proxiedObject = object | |
} | |
} | |
// MARK: - ProxyBuilder | |
public protocol ProxyBuilderProtocol { | |
associatedtype ObjectType | |
/// The initial instance that is going to be used by the builder. | |
var createInstanceClosure: () -> ObjectType { get } | |
/// All of the `set` commands that will performed by this builder. | |
var keypathSetValueDictionary: [AnyKeyPath: (inout ObjectType) -> Void] { get set } | |
/// All of the values currently set. | |
var keypathGetValueDictionary: [AnyKeyPath: Any] { get set } | |
} | |
public extension ProxyBuilderProtocol { | |
/// Use `@dynamicMemberLookup` keypath subscript to store the object configuration and postpone | |
/// the object construction. | |
subscript<T>(dynamicMember keyPath: WritableKeyPath<ObjectType, T>) -> T? { | |
get { | |
return keypathGetValueDictionary[keyPath] as? T | |
} | |
set { | |
guard let newValue = newValue else { | |
keypathGetValueDictionary.removeValue(forKey: keyPath) | |
keypathSetValueDictionary.removeValue(forKey: keyPath) | |
return | |
} | |
keypathSetValueDictionary[keyPath] = { object in | |
object[keyPath: keyPath] = newValue | |
} | |
} | |
} | |
/// Build the target object. | |
func build() -> ObjectType { | |
var obj = createInstanceClosure() | |
for (_, setValueClosure) in keypathSetValueDictionary { | |
setValueClosure(&obj) | |
} | |
return obj | |
} | |
} | |
@dynamicMemberLookup | |
public class ProxyBuilder<T>: ProxyBuilderProtocol { | |
public let createInstanceClosure: () -> T | |
public var keypathSetValueDictionary: [AnyKeyPath: (inout T) -> Void] = [:] | |
public var keypathGetValueDictionary: [AnyKeyPath: Any] = [:] | |
init(createInstanceClosure: @escaping () -> T) { | |
self.createInstanceClosure = createInstanceClosure | |
} | |
} |
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
import Foundation | |
struct Foo { | |
var bar = "bar" | |
var baz = 42 | |
} | |
// Creates an immutable (read-only) proxy of a given object. | |
var proxyFoo = Proxy(of: Foo()) | |
proxyFoo.baz | |
// Create a proxy-based builder that postpone the object creation to | |
// the invocation of `build()`. | |
var proxyBuilderFoo = ProxyBuilder(createInstanceClosure: { Foo() }) | |
proxyBuilderFoo.baz = 1337 | |
proxyBuilderFoo.bar = "New" | |
let newFoo = proxyBuilderFoo.build() |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment