Skip to content

Instantly share code, notes, and snippets.

@alexdrone
Last active July 23, 2019 09:03
Show Gist options
  • Save alexdrone/3d7e9f873123656ed88bd61db6ac03f9 to your computer and use it in GitHub Desktop.
Save alexdrone/3d7e9f873123656ed88bd61db6ac03f9 to your computer and use it in GitHub Desktop.
Proxy and ProxyBuilder in Swift 5.1 using @dynamicMemberLookup
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
}
}
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