Created
February 15, 2018 10:23
-
-
Save chriseidhof/c722698d5aed1acf8f04e954f189e4b3 to your computer and use it in GitHub Desktop.
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
//: [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" |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment