Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ import SwiftUI
struct IsPlayingAtom: StateAtom {
let voiceMemo: VoiceMemo

var key: some Hashable {
var key: URL {
voiceMemo.url
}

Expand Down Expand Up @@ -39,7 +39,7 @@ struct IsPlayingAtom: StateAtom {
struct PlayingElapsedTimeAtom: PublisherAtom {
let voiceMemo: VoiceMemo

var key: some Hashable {
var key: URL {
voiceMemo.url
}

Expand All @@ -58,7 +58,7 @@ struct PlayingElapsedTimeAtom: PublisherAtom {
struct AudioPlayerAtom: ValueAtom {
let voiceMemo: VoiceMemo

var key: some Hashable {
var key: URL {
voiceMemo.url
}

Expand Down
2 changes: 1 addition & 1 deletion Sources/Atoms/Core/Atom/Atom.swift
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
/// and is immediately released when no longer watched.
public protocol Atom<Produced>: Sendable {
/// A type representing the stable identity of this atom.
associatedtype Key: Hashable
associatedtype Key: Hashable & Sendable

/// The type of value that this atom produces.
associatedtype Produced
Expand Down
2 changes: 1 addition & 1 deletion Sources/Atoms/Core/Atom/ModifiedAtom.swift
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ public struct ModifiedAtom<Node: Atom, Modifier: AtomModifier>: Atom where Node.
public typealias Produced = Modifier.Produced

/// A type representing the stable identity of this atom.
public struct Key: Hashable {
public struct Key: Hashable, Sendable {
private let atomKey: Node.Key
private let modifierKey: Modifier.Key

Expand Down
6 changes: 3 additions & 3 deletions Sources/Atoms/Core/AtomKey.swift
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
internal struct AtomKey: Hashable, CustomStringConvertible {
private let key: AnyHashable
internal struct AtomKey: Hashable, Sendable, CustomStringConvertible {
private let key: UnsafeUncheckedSendable<AnyHashable>
private let type: ObjectIdentifier
private let scopeKey: ScopeKey?
private let anyAtomType: Any.Type
Expand All @@ -20,7 +20,7 @@ internal struct AtomKey: Hashable, CustomStringConvertible {
}

init<Node: Atom>(_ atom: Node, scopeKey: ScopeKey?) {
self.key = AnyHashable(atom.key)
self.key = UnsafeUncheckedSendable(atom.key)
self.type = ObjectIdentifier(Node.self)
self.scopeKey = scopeKey
self.anyAtomType = Node.self
Expand Down
2 changes: 1 addition & 1 deletion Sources/Atoms/Core/Modifier/AtomModifier.swift
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ public extension Atom {
/// A modifier that you apply to an atom, producing a new value modified from the original value.
public protocol AtomModifier: Sendable {
/// A type representing the stable identity of this modifier.
associatedtype Key: Hashable
associatedtype Key: Hashable & Sendable

/// A type of base value to be modified.
associatedtype Base
Expand Down
6 changes: 3 additions & 3 deletions Sources/Atoms/Core/OverrideKey.swift
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ internal struct OverrideKey: Hashable, Sendable {

@usableFromInline
init<Node: Atom>(_ atom: Node) {
let key = AnyHashable(atom.key)
let key = UnsafeUncheckedSendable<AnyHashable>(atom.key)
let type = ObjectIdentifier(Node.self)
identifier = .node(key: key, type: type)
}
Expand All @@ -17,8 +17,8 @@ internal struct OverrideKey: Hashable, Sendable {
}

private extension OverrideKey {
enum Identifier: Hashable, @unchecked Sendable {
case node(key: AnyHashable, type: ObjectIdentifier)
enum Identifier: Hashable, Sendable {
case node(key: UnsafeUncheckedSendable<AnyHashable>, type: ObjectIdentifier)
case type(ObjectIdentifier)
}
}
2 changes: 1 addition & 1 deletion Sources/Atoms/Core/ScopeKey.swift
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
@usableFromInline
internal struct ScopeKey: Hashable, CustomStringConvertible {
internal struct ScopeKey: Hashable, Sendable, CustomStringConvertible {
final class Token {}

private let identifier: ObjectIdentifier
Expand Down
2 changes: 1 addition & 1 deletion Sources/Atoms/Core/StoreContext.swift
Original file line number Diff line number Diff line change
Expand Up @@ -440,7 +440,7 @@ private extension StoreContext {
lazy var shouldKeepAlive = !key.isScoped && store.state.caches[key].map { $0.atom is any KeepAlive } ?? false
lazy var isChildrenEmpty = store.graph.children[key]?.isEmpty ?? true
lazy var isSubscriptionEmpty = store.state.subscriptions[key]?.isEmpty ?? true
lazy var shouldRelease = !shouldKeepAlive && isChildrenEmpty && isSubscriptionEmpty
let shouldRelease = !shouldKeepAlive && isChildrenEmpty && isSubscriptionEmpty

guard shouldRelease else {
return
Expand Down
3 changes: 3 additions & 0 deletions Sources/Atoms/Core/UnsafeUncheckedSendable.swift
Original file line number Diff line number Diff line change
Expand Up @@ -7,3 +7,6 @@ internal struct UnsafeUncheckedSendable<Value>: @unchecked Sendable {
self.value = value
}
}

extension UnsafeUncheckedSendable: Equatable where Value: Equatable {}
extension UnsafeUncheckedSendable: Hashable where Value: Hashable {}
2 changes: 1 addition & 1 deletion Sources/Atoms/Modifier/AnimationModifier.swift
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@ public struct AnimationModifier<Produced>: AtomModifier {
public typealias Produced = Produced

/// A type representing the stable identity of this atom associated with an instance.
public struct Key: Hashable {
public struct Key: Hashable, Sendable {
private let animation: Animation?

fileprivate init(animation: Animation?) {
Expand Down
2 changes: 1 addition & 1 deletion Sources/Atoms/Modifier/ChangesModifier.swift
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@ public struct ChangesModifier<Produced: Equatable>: AtomModifier {
public typealias Produced = Produced

/// A type representing the stable identity of this atom associated with an instance.
public struct Key: Hashable {}
public struct Key: Hashable, Sendable {}

/// A unique value used to identify the modifier internally.
public var key: Key {
Expand Down
37 changes: 25 additions & 12 deletions Sources/Atoms/Modifier/ChangesOfModifier.swift
Original file line number Diff line number Diff line change
Expand Up @@ -48,22 +48,35 @@ public struct ChangesOfModifier<Base, Produced: Equatable>: AtomModifier {
/// A type of value the modified atom produces.
public typealias Produced = Produced

/// A type representing the stable identity of this modifier.
public struct Key: Hashable {
private let keyPath: KeyPath<Base, Produced>
#if compiler(>=6) || hasFeature(InferSendableFromCaptures)
/// A type representing the stable identity of this modifier.
public struct Key: Hashable, Sendable {
private let keyPath: KeyPath<Base, Produced> & Sendable

fileprivate init(keyPath: KeyPath<Base, Produced>) {
self.keyPath = keyPath
fileprivate init(keyPath: KeyPath<Base, Produced> & Sendable) {
self.keyPath = keyPath
}
}
}

#if compiler(>=6) || hasFeature(InferSendableFromCaptures)
private let keyPath: KeyPath<Base, Produced> & Sendable

internal init(keyPath: KeyPath<Base, Produced> & Sendable) {
self.keyPath = keyPath
}

/// A unique value used to identify the modifier internally.
public var key: Key {
Key(keyPath: keyPath)
}
#else
public struct Key: Hashable, Sendable {
private let keyPath: UnsafeUncheckedSendable<KeyPath<Base, Produced>>

fileprivate init(keyPath: UnsafeUncheckedSendable<KeyPath<Base, Produced>>) {
self.keyPath = keyPath
}
}

private let _keyPath: UnsafeUncheckedSendable<KeyPath<Base, Produced>>
private var keyPath: KeyPath<Base, Produced> {
_keyPath.value
Expand All @@ -72,12 +85,12 @@ public struct ChangesOfModifier<Base, Produced: Equatable>: AtomModifier {
internal init(keyPath: KeyPath<Base, Produced>) {
_keyPath = UnsafeUncheckedSendable(keyPath)
}
#endif

/// A unique value used to identify the modifier internally.
public var key: Key {
Key(keyPath: keyPath)
}
/// A unique value used to identify the modifier internally.
public var key: Key {
Key(keyPath: _keyPath)
}
#endif

/// A producer that produces the value of this atom.
public func producer(atom: some Atom<Base>) -> AtomProducer<Produced> {
Expand Down
2 changes: 1 addition & 1 deletion Sources/Atoms/Modifier/TaskPhaseModifier.swift
Original file line number Diff line number Diff line change
Expand Up @@ -79,7 +79,7 @@ public struct TaskPhaseModifier<Success: Sendable, Failure: Error>: AsyncAtomMod
public typealias Produced = AsyncPhase<Success, Failure>

/// A type representing the stable identity of this atom associated with an instance.
public struct Key: Hashable {}
public struct Key: Hashable, Sendable {}

/// A unique value used to identify the modifier internally.
public var key: Key {
Expand Down