Skip to content

Instantly share code, notes, and snippets.

@chriseidhof
Created February 15, 2018 10:23
Show Gist options
  • Save chriseidhof/c722698d5aed1acf8f04e954f189e4b3 to your computer and use it in GitHub Desktop.
Save chriseidhof/c722698d5aed1acf8f04e954f189e4b3 to your computer and use it in GitHub Desktop.
//: [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