-
Notifications
You must be signed in to change notification settings - Fork 0
/
State.swift
212 lines (183 loc) · 6.52 KB
/
State.swift
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
//
// LazyFishObserve.swift
// LazyFish
//
// Created by zjj on 2021/10/22.
//
import Foundation
public struct Changed<T> {
internal typealias ValueGetter = () -> T
var old: T {
_oldGetter()
}
var new: T {
_newGetter()
}
let _oldGetter: ValueGetter
let _newGetter: ValueGetter
init(old: @escaping @autoclosure ValueGetter, new: @escaping @autoclosure ValueGetter) {
_oldGetter = old
_newGetter = new
}
public typealias ObserverHandler = (Changed) -> Void
}
@propertyWrapper public class State<T> {
public var wrappedValue: T {
didSet {
sendChanged(oldValue)
}
}
fileprivate func sendChanged(_ old: T?) {
let val = wrappedValue
let oldVal = old ?? val
let changed = Changed(old: oldVal, new: val)
callAllObservers(changed: changed)
}
public init(wrappedValue: T) {
self.wrappedValue = wrappedValue
}
private var observers = [ObserverTargetAction]()
public var projectedValue: Binding<T> {
Binding(wrapper: self, currentValue: self.currentValue())
}
internal func currentValue() -> T {
return wrappedValue
}
}
extension State {
private class ObserverTargetAction {
weak var target: AnyObject?
var action: Changed<T>.ObserverHandler
init(target: AnyObject, action: @escaping Changed<T>.ObserverHandler) {
self.target = target
self.action = action
}
}
fileprivate func addObserver(target: AnyObject?, observer: @escaping Changed<T>.ObserverHandler) {
removeAnyExpiredObservers()
guard let target = target else {
return
}
observers.append(.init(target: target, action: observer))
let val = wrappedValue
let changed = Changed(old: val, new: val)
observer(changed)
}
private func callAllObservers(changed: Changed<T>) {
// filter && call
observers = observers.filter { tarObj in
if tarObj.target == nil {
return false
}
tarObj.action(changed)
return true
}
}
private func removeAnyExpiredObservers() {
// filter
observers = observers.filter { tarObj in
if tarObj.target == nil {
return false
}
return true
}
}
}
@dynamicMemberLookup
public class Binding<Element>: CustomStringConvertible, CustomDebugStringConvertible {
internal var _currentValueGetter: () -> Element
public func currentValue() -> Element {
return _currentValueGetter()
}
public var description: String {
return "\(currentValue())"
}
public var debugDescription: String {
return "\(currentValue())"
}
private weak var wrapper: State<Element>?
init(wrapper: State<Element>?, currentValue: @escaping @autoclosure () -> Element) {
self.wrapper = wrapper
self._currentValueGetter = currentValue
}
// 这个可以直接访问Element的properties!!
public subscript<Value>(dynamicMember keyPath: KeyPath<Element, Value>) -> Binding<Value> {
get {
return self.map { ele in
return ele[keyPath: keyPath]
}
}
}
public func addObserver(target: AnyObject?, observer: @escaping Changed<Element>.ObserverHandler) {
wrapper?.addObserver(target: target) { changed in
observer(changed)
}
}
public func map<ResultElement>(_ transform: @escaping (Element) -> ResultElement) -> Binding<ResultElement> {
let bindMap = MapBinding<Element, ResultElement>(source: self, map: transform)
return bindMap
}
public func join<OtherElement>(_ other: Binding<OtherElement>) -> Binding<(Element, OtherElement)> {
let bindJoin = JoinBinding<Element, OtherElement>(self, with: other)
return bindJoin
}
public func join<OtherElement, ResultElement>(_ other: Binding<OtherElement>, transform: @escaping (Element, OtherElement) -> ResultElement) -> Binding<ResultElement> {
let joinMap = join(other).map(transform)
return joinMap
}
public func zip<OtherElement, ResultElement>(_ other: Binding<OtherElement>, transform: @escaping (Element, OtherElement) -> ResultElement) -> Binding<ResultElement> {
let joinMap = ZipBinding<Element, OtherElement>(self, with: other).map(transform)
return joinMap
}
}
private class MapBinding<SourceElement, ResultElement>: Binding<ResultElement> {
var source: Binding<SourceElement>
var map: (SourceElement) -> ResultElement
init(source: Binding<SourceElement>, map: @escaping (SourceElement) -> ResultElement) {
self.source = source
self.map = map
super.init(wrapper: nil, currentValue: map(source.currentValue()))
}
public override func addObserver(target: AnyObject?, observer: @escaping Changed<ResultElement>.ObserverHandler) {
let map = self.map
source.addObserver(target: target) { changed in
let changedMap = Changed<ResultElement>(old: map(changed.old), new: map(changed.new))
observer(changedMap)
}
}
}
private class JoinBinding<S1, S2>: Binding<(S1, S2)> {
var source1: Binding<S1>
var source2: Binding<S2>
init(_ source1: Binding<S1>, with source2: Binding<S2>) {
self.source1 = source1
self.source2 = source2
super.init(wrapper: nil, currentValue: (source1.currentValue(), source2.currentValue()))
}
public override func addObserver(target: AnyObject?, observer: @escaping Changed<(S1, S2)>.ObserverHandler) {
var c1: Changed<S1>?
var c2: Changed<S2>?
let shouldClean = (self as? ZipBinding<S1, S2>) != nil
let performChanges = {
if let c1obj = c1, let c2obj = c2 {
if shouldClean {
c1 = nil
c2 = nil
}
let changedAll = Changed<(S1, S2)>(old: (c1obj.old, c2obj.old), new: (c1obj.new, c2obj.new))
observer(changedAll)
}
}
source1.addObserver(target: target) { change in
c1 = change
performChanges()
}
source2.addObserver(target: target) { change in
c2 = change
performChanges()
}
}
}
/// The Zip 要求全部元素都变化才打包发送
private class ZipBinding<S1, S2>: JoinBinding<S1, S2> {
}