MobiusCore/Source/EffectHandlers/EffectCallback.swift (52 lines of code) (raw):

// Copyright Spotify AB. // SPDX-License-Identifier: Apache-2.0 import Foundation /// An `EffectCallback` can send output and signal completion. /// /// Sending output is done with `.send` and signaling completion is done with `.end`. You can also end in conjunction /// with sending output using `.end(with:)`. /// /// Note: Once either `.end` or `.end(with:)` have been called (from any thread), all operations on this object will be no-ops. /// Note: The closure provided in `onEnd` will only be called once when `.end` is called on this object. public final class EffectCallback<Output> { private let onSend: (Output) -> Void private let onEnd: () -> Void /// Determine if this callback has been ended. /// /// This can be called safely from any thread. /// Note: Once this variable is `true`, it will never be `false` again. public var ended: Bool { return _ended.value } private let _ended = Synchronized<Bool>(value: false) /// Create an `EffectCallback` with some behavior associated with its sending and ending mechanisms. /// /// Note: `onEnd` will only be called once on an instance of this call, regardless of how many times `end` is called. /// Note: `onSend` will not be called if `end` has already been called. /// - Parameter onSend: The closure to called when the underlying `Callback` sends output. /// - Parameter onEnd: The closure to be called when the underlying `Callback` is ended. public init( onSend: @escaping (Output) -> Void, onEnd: @escaping () -> Void ) { self.onSend = onSend self.onEnd = onEnd } /// Invalidate this callback. /// /// Note: any calls to `end`, `end(with:)` or `send(_:)` will be no-ops after this function has been called. public func end() { var runOnEnd = false _ended.mutate { runOnEnd = !$0 $0 = true } if runOnEnd { onEnd() } } /// Send a number of events to the Mobius loop, then `end()` this callback. /// /// Note: After calling this function, all operations on this object will be no-ops. /// - Parameter outputs: The events which should be sent to the loop. public func end(with outputs: Output...) { end(with: outputs) } /// Send a number of events to the Mobius loop and `end()` this callback. /// /// Note: After calling this function, all operations on this object will be no-ops. /// - Parameter outputs: The events which should be sent to the loop. public func end(with outputs: [Output]) { var shouldRun = false _ended.mutate { shouldRun = !$0 $0 = true } if shouldRun { for output in outputs { onSend(output) } onEnd() } } /// Send an event to the Mobius loop. /// /// Note: Calling this function after calling `.end()` or `.end(with:)` is a no-op. /// - Parameter output: the event that should be sent to the loop. public func send(_ output: Output) { _ended.mutate { if !$0 { onSend(output) } } } deinit { end() } }