Last active
May 30, 2020 15:25
-
-
Save maximkrouk/c804a7df4af93d056ba5b9e36565d3f6 to your computer and use it in GitHub Desktop.
This file contains 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
public class CancellablesManager { | |
private let lock = NSLock() | |
public typealias Storage = Set<AnyCancellable> | |
private var cancellables: Storage = [] | |
func store(_ element: AnyCancellable?, _ precondition: Bool = true) { | |
lock.execute { | |
guard precondition, let element = element else { return } | |
cancellables.insert(element) | |
print("i", cancellables.map { $0.hashValue }) | |
} | |
} | |
func release(_ element: AnyCancellable?, completion: () -> Void = {}) { | |
lock.execute { | |
guard let element = element else { return } | |
cancellables.remove(element) | |
completion() | |
} | |
} | |
} | |
extension AnyCancellable { | |
func store(in manager: CancellablesManager) { | |
manager.store(self) | |
} | |
} | |
extension Publisher { | |
@discardableResult | |
public func sink(using manager: CancellablesManager, | |
onCompletion: @escaping (Subscribers.Completion<Self.Failure>) -> Void, | |
onSuccess: @escaping (Self.Output) -> Void) -> AnyCancellable { | |
var cancellable: AnyCancellable? | |
var didComplete = false | |
let release: () -> Void = { [weak manager] in | |
manager?.release(cancellable) { didComplete = true } | |
} | |
cancellable = handleEvents(receiveCancel: release) | |
.sink( | |
receiveCompletion: { completion in | |
release() | |
onCompletion(completion) | |
}, | |
receiveValue: { value in | |
release() | |
onSuccess(value) | |
}) | |
manager.store(cancellable, !didComplete) | |
return cancellable! | |
} | |
} |
This file contains 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
typealias Reducer<State, Action> = (inout State, Action) -> Void | |
final class Store<State, Action>: ObservableObject { | |
typealias Effect = AnyPublisher<Action, Never> | |
@Published private(set) var state: State | |
private let lock: NSLock = .init() | |
private let reducer: Reducer<State, Action> | |
private let cancellablesManager = CancellablesManager() | |
init(state: State, reducer: @escaping Reducer<State, Action>) { | |
self.state = state | |
self.reducer = reducer | |
} | |
func send(_ effect: Effect) { | |
effect | |
.receive(on: RunLoop.main) | |
.sink(using: cancellablesManager, | |
onCompletion: executeNothing(), | |
onSuccess: send) | |
} | |
func send(_ action: Action) { | |
lock.execute { | |
print(log(state, action)) | |
reducer(&state, action) | |
} | |
} | |
func log(_ state: State, _ action: Action) -> String { | |
let logId = UUID() | |
return | |
"#startlog {\(logId.uuidString)}:\n" + | |
"> Date : \(Date())\n" + | |
"> State : \(state)\n" + | |
"> Action : \(action)\n" + | |
"#endlog {\(logId.uuidString)}." | |
} | |
} | |
extension Store { | |
func binding<Value>( | |
for keyPath: KeyPath<State, Value>, | |
_ action: @escaping (Value) -> Action | |
) -> Binding<Value> { | |
.init(get: { self.state[keyPath: keyPath] }, set: { self.send(action($0)) }) | |
} | |
} |
This file contains 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
extension NSLock { | |
func store<T>(_ value: T, in object: inout T) { | |
mutate(&object, with: { $0 = value }) | |
} | |
func mutate<T: AnyObject>(_ object: T, with closure: (T) -> Void) { | |
execute { closure(object) } | |
} | |
func mutate<T>(_ object: inout T, with closure: (inout T) -> Void) { | |
execute { closure(&object) } | |
} | |
@discardableResult | |
func execute<T>(code closure: () -> T) -> T { | |
lock() | |
defer { unlock() } | |
return closure() | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Take a look at
Cancellables
from my library Merge, does exactly what you're trying to achieve withCancellablesManager
.