Created
February 15, 2018 10:23
-
-
Save chriseidhof/c722698d5aed1acf8f04e954f189e4b3 to your computer and use it in GitHub Desktop.
Revisions
-
chriseidhof created this gist
Feb 15, 2018 .There are no files selected for viewing
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 charactersOriginal file line number Diff line number Diff line change @@ -0,0 +1,104 @@ //: [Previous](@previous) import Foundation struct Counter { var name: String = "" var age: Int = 33 enum Action { case increment case setName(String?) } mutating func send(_ action: Action) { switch action { case .increment: age += 1 case .setName(let n): name = n ?? "" } } } final class Adapter<State, Action> { let mutate: (inout State, Action) -> () private(set) var state: State { didSet { observers.forEach { $0(state) } } } var observers: [(State) -> ()] = [] init(_ initial: State, mutate: @escaping (inout State, Action) -> ()) { self.state = initial self.mutate = mutate } func send(_ action: Action) { mutate(&self.state, action) } func addObserver(_ o: @escaping (State) -> ()) { o(state) observers.append(o) } } enum Bindable<Source,A> { case constant(A) case dynamic(KeyPath<Source,A>) } extension Bindable { func get(_ s: Source) -> A { switch self { case .constant(let value): return value case .dynamic(let kp): return s[keyPath: kp] } } } struct TextFieldBinder<State, Action> { var text: Bindable<State, String>? var onChange: (String?) -> Action } final class Box<A> { let value: A var strongReferences: [Any] = [] init(_ value: A) { self.value = value } } final class TargetAction { let callback: () -> () init(_ callback: @escaping () -> ()) { self.callback = callback } @objc func action(_ sender: Any) { callback() } } import UIKit func run<State, Action>(_ binder: TextFieldBinder<State, Action>, adapter: Adapter<State, Action>) -> Box<UITextField> { let result = UITextField() let box = Box(result) result.text = binder.text?.get(adapter.state) let ta = TargetAction { [unowned result, unowned adapter] in adapter.send(binder.onChange(result.text)) } box.strongReferences.append(ta) adapter.addObserver { [weak result] in result?.text = binder.text?.get($0) } return Box(result) } let sample = TextFieldBinder<Counter, Counter.Action>(text: .constant("hello"), onChange: Counter.Action.setName) let label = run(sample, adapter: Adapter(Counter(), mutate: { $0.send($1) })) label.value.text = "hello"