Created
June 19, 2019 19:54
-
-
Save pteasima/0aa83e2c58ec909b4b684681f7c8f0ff to your computer and use it in GitHub Desktop.
Autoupdating Store
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 SwiftUI | |
import Combine | |
final class Store<State>: BindableObject { | |
let didChange = PassthroughSubject<(), Never>() | |
init(state: State) { | |
self.state = state | |
} | |
var state: State { | |
didSet { | |
//this nesting ensures didChange is always triggered. And, with @dynamicMemberLookup we'll get rid of `.state` nesting everywhere | |
didChange.send(()) | |
} | |
} | |
} | |
// this isnt very Elm-y (yet!), its just a View that has a single Store | |
@dynamicMemberLookup protocol ElmView: View { | |
associatedtype State | |
var store: ObjectBinding<Store<State>> { get } | |
subscript<Subject>(dynamicMember keyPath: WritableKeyPath<State, Subject>) -> Binding<Subject> { get } | |
} | |
extension ElmView { | |
subscript<Subject>(dynamicMember keyPath: WritableKeyPath<State, Subject>) -> Binding<Subject> { | |
let storeToState: ReferenceWritableKeyPath<Store<State>, State> = \.state | |
let storeToSubject = storeToState.appending(path: keyPath) | |
return Binding(getValue: { | |
self.store.delegateValue[dynamicMember: storeToSubject].value | |
}, setValue: { newValue, transaction in | |
//without `transaction`, animated Bindings wouldnt animate. Using transaction like this seems to be the right way to "compose Bindings" | |
self.store.delegateValue[dynamicMember: storeToSubject].transaction(transaction).value = newValue | |
}) | |
} | |
} | |
enum ExampleApp { | |
struct State { | |
var isOn: Bool = true | |
} | |
static let theStore = Store(state: State()) | |
} | |
struct ContentView : ElmView { | |
//we have one store per app, but each View needs its own ObjectBinding | |
let store = ObjectBinding(initialValue: ExampleApp.theStore) | |
var body: some View { | |
List(0...100) { _ in | |
//Bindings to all State properties are accessible on `self` | |
Toggle(isOn: self.isOn.animation()) { | |
Text("is it on?") | |
} | |
} | |
} | |
} | |
#if DEBUG | |
struct ContentView_Previews : PreviewProvider { | |
static var previews: some View { | |
ContentView() | |
} | |
} | |
#endif |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment