#if canImport(Combine) import Combine #endif /// A property wrapper type that shares a value with multiple parts of an application. /// /// See the article for more detailed information on how to use this property /// wrapper, in particular . @dynamicMemberLookup @propertyWrapper public struct SharedReader { fileprivate let reference: any Reference init(reference: any Reference) { self.reference = reference } /// Creates a read-only shared reference from another read-only shared reference. /// /// You don't call this initializer directly. Instead, Swift calls it for you when you use a /// property-wrapper attribute on a binding closure parameter. /// /// - Parameter projectedValue: A read-only shared reference. public init(projectedValue: SharedReader) { self = projectedValue } /// Unwraps a read-only shared reference to an optional value. /// /// ```swift /// @SharedReader(.currentUser) var currentUser: User? /// /// if let sharedCurrentUser = SharedReader($currentUser) { /// sharedCurrentUser // SharedReader /// } /// ``` /// /// - Parameter base: A read-only shared reference to an optional value. public init?(_ base: SharedReader) { guard let initialValue = base.wrappedValue else { return nil } func open(_ location: some Reference) -> any Reference { _ReferenceFromOptional(initialValue: initialValue, base: location) } self.init(reference: open(base.reference)) } /// Creates a read-only shared reference from a shared reference. /// /// - Parameter base: A shared reference. public init(_ base: Shared) { self = base.reader } /// Constructs a read-only shared value that remains constant. /// /// This can be useful for providing ``SharedReader`` values to features in previews and tests: /// /// ```swift /// #Preview { /// FeatureView( /// store: Store( /// initialState: Feature.State(count: .constant(42)) /// ) { /// Feature() /// } /// ) /// ) /// ``` public static func constant(_ value: Value) -> Self { Shared(value).reader } /// The underlying value referenced by the shared variable. /// /// This property provides primary access to the value's data. However, you don't access /// `wrappedValue` directly. Instead, you use the property variable created with the /// ``SharedReader`` attribute. In the following example, the shared variable `topics` returns the /// value of `wrappedValue`: /// /// ```swift /// struct State { /// @SharedReader var subscriptions: [Subscription] /// /// var isSubscribed: Bool { /// !subscriptions.isEmpty /// } /// } /// ``` public var wrappedValue: Value { reference.value } /// A projection of the read-only shared value that returns a shared reference. public var projectedValue: Self { get { self } set { reference.touch() self = newValue } } /// Returns a shared reference to the resulting value of a given key path. public subscript( dynamicMember keyPath: KeyPath ) -> SharedReader { func open(_ location: some Reference) -> SharedReader { SharedReader( reference: _ReferenceAppendKeyPath( base: location, keyPath: sendableKeyPath(keyPath) ) ) } return open(reference) } @_disfavoredOverload @available( *, deprecated, message: "Use 'SharedReader($value.optional)' to unwrap optional shared values" ) public subscript( dynamicMember keyPath: KeyPath ) -> SharedReader? { SharedReader(self[dynamicMember: keyPath]) } #if canImport(Combine) /// Returns a publisher that emits events when the underlying value changes. public var publisher: AnyPublisher { func open(_ publisher: some Publisher) -> AnyPublisher { publisher.eraseToAnyPublisher() } return open(self.reference.publisher) } #endif } extension SharedReader: @unchecked Sendable where Value: Sendable {} extension SharedReader: Equatable where Value: Equatable { public static func == (lhs: SharedReader, rhs: SharedReader) -> Bool { lhs.wrappedValue == rhs.wrappedValue } } extension SharedReader: Hashable where Value: Hashable { public func hash(into hasher: inout Hasher) { hasher.combine(self.wrappedValue) } } extension SharedReader: Identifiable where Value: Identifiable { public var id: Value.ID { self.wrappedValue.id } } extension SharedReader: Encodable where Value: Encodable { public func encode(to encoder: any Encoder) throws { do { var container = encoder.singleValueContainer() try container.encode(self.wrappedValue) } catch { try self.wrappedValue.encode(to: encoder) } } } extension SharedReader: CustomDumpRepresentable { public var customDumpValue: Any { self.wrappedValue } } extension SharedReader where Value: RandomAccessCollection & MutableCollection, Value.Index: Hashable & Sendable, Value.Element: Sendable { /// Derives a collection of read-only shared elements from a read-only shared collection of /// elements. /// /// See the documentation for [`@Shared`]()'s ``Shared/elements`` for more /// information. public var elements: some RandomAccessCollection> { zip(self.wrappedValue.indices, self.wrappedValue).lazy.map { index, element in self[index, default: DefaultSubscript(element)] } } }