Files
swift-composable-architectu…/Sources/ComposableArchitecture/SharedState/PersistenceKey.swift
Brandon Williams 7c6fb26700 Address @Shared sendability. (#3329)
* Address @Shared sendability.

* Undo UncheckedSendable<UserDefaults>.

* clean up

* wip

* drop AnySendable.

* wip

* Address `Effect.throttle` sendability (#3325)

* Address effect cancellation sendability (#3326)

* Address effect cancellation sendability

* fix

* wip

* wip

* Separate SendableDefaultSubscript from DefaultSubscript.

* fix test

* drop escaping

* switch on swift 6 language mode

* xcode 16

* update test

* wip

---------

Co-authored-by: Stephen Celis <stephen@stephencelis.com>
2024-09-06 16:28:23 -04:00

95 lines
3.5 KiB
Swift

/// A type that can load and subscribe to state in an external system.
///
/// Conform to this protocol to express loading state from an external system, and subscribing to
/// state changes in the external system. It is only necessary to conform to this protocol if the
/// ``AppStorageKey``, ``FileStorageKey``, or ``InMemoryKey`` strategies are not sufficient for your
/// use case.
///
/// See the article <doc:SharingState> for more information, in particular the
/// <doc:SharingState#Custom-persistence> section.
public protocol PersistenceReaderKey<Value>: Sendable {
/// A type that can be loaded or subscribed to in an external system.
associatedtype Value: Sendable
/// A type representing the hashable identity of a persistence key.
associatedtype ID: Hashable = Self
/// The hashable identity of a persistence key.
///
/// Used to look up existing shared references associated with this persistence key.
var id: ID { get }
/// Loads the freshest value from storage. Returns `nil` if there is no value in storage.
///
/// - Parameter initialValue: An initial value assigned to the `@Shared` property.
/// - Returns: An initial value provided by an external system, or `nil`.
func load(initialValue: Value?) -> Value?
/// Subscribes to external updates.
///
/// - Parameters:
/// - initialValue: An initial value assigned to the `@Shared` property.
/// - didSet: A closure that is invoked with new values from an external system, or `nil` if the
/// external system no longer holds a value.
/// - Returns: A subscription to updates from an external system. If it is cancelled or
/// deinitialized, the `didSet` closure will no longer be invoked.
func subscribe(
initialValue: Value?,
didSet: @escaping @Sendable (_ newValue: Value?) -> Void
) -> Shared<Value>.Subscription
}
extension PersistenceReaderKey where ID == Self {
public var id: ID { self }
}
extension PersistenceReaderKey {
public func subscribe(
initialValue: Value?,
didSet: @escaping @Sendable (_ newValue: Value?) -> Void
) -> Shared<Value>.Subscription {
Shared.Subscription {}
}
}
/// A type that can persist shared state to an external storage.
///
/// Conform to this protocol to express persistence to some external storage by describing how to
/// save to and load from the external storage, and providing a stream of values that represents
/// when the external storage is changed from the outside. It is only necessary to conform to this
/// protocol if the ``AppStorageKey``, ``FileStorageKey``, or ``InMemoryKey`` strategies are not
/// sufficient for your use case.
///
/// See the article <doc:SharingState> for more information, in particular the
/// <doc:SharingState#Custom-persistence> section.
public protocol PersistenceKey<Value>: PersistenceReaderKey {
/// Saves a value to storage.
func save(_ value: Value)
}
extension Shared {
/// A subscription to a ``PersistenceReaderKey``'s updates.
///
/// This object is returned from ``PersistenceReaderKey/subscribe(initialValue:didSet:)``, which
/// will feed updates from an external system for its lifetime, or till ``cancel()`` is called.
public class Subscription {
let onCancel: () -> Void
/// Initializes the subscription with the given cancel closure.
///
/// - Parameter cancel: A closure that the `cancel()` method executes.
public init(_ cancel: @escaping () -> Void) {
self.onCancel = cancel
}
deinit {
self.cancel()
}
/// Cancels the subscription.
public func cancel() {
self.onCancel()
}
}
}