MobiusCore/Source/Connectable.swift (22 lines of code) (raw):
// Copyright Spotify AB.
// SPDX-License-Identifier: Apache-2.0
import Foundation
/// API for something that can be connected to be part of a MobiusLoop.
///
/// Primarily used in `Mobius.loop(update:effectHandler:)` to define the effect handler of a Mobius loop. In that case,
/// the incoming values will be effects, and the outgoing values will be events that should be sent back to the loop.
///
/// Alternatively used in `MobiusController.connectView(_:)` to connect a view to the controller. In that case, the
/// incoming values will be models, and the outgoing values will be events.
public protocol Connectable {
associatedtype Input
associatedtype Output
/// Create a new connection that accepts input values and sends outgoing values to a supplied consumer.
///
/// Must return a new `Connection` that accepts incoming values. After `dispose()` is called on the returned
/// `Connection`, the connection must be broken, and no more values may be sent to the output consumer.
///
/// Every call to this method should create a new independent connection that can be disposed of individually
/// without affecting the other connections. If your `Connectable` doesn't support this, it should assert if someone
/// tries to connect a second time before disposing of the first connection.
///
/// - Parameter consumer: the consumer that the new connection should use to emit values
/// - Returns: a new connection
func connect(_ consumer: @escaping Consumer<Output>) -> Connection<Input>
}
/// Type-erased wrapper for `Connectable`s
public struct AnyConnectable<Input, Output>: Connectable {
private let connectClosure: (@escaping Consumer<Output>) -> Connection<Input>
/// Creates a type-erased `Connectable` that wraps the given instance.
public init<C: Connectable>(_ connectable: C) where C.Input == Input, C.Output == Output {
if let anyConnectable = connectable as? AnyConnectable {
self = anyConnectable
} else {
self.init(connectable.connect)
}
}
/// Creates an anonymous `Connectable` that implements `connect` with the provided closure.
public init(_ connect: @escaping (@escaping Consumer<Output>) -> Connection<Input>) {
connectClosure = connect
}
public func connect(_ consumer: @escaping Consumer<Output>) -> Connection<Input> {
return connectClosure(consumer)
}
}