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
2 changes: 2 additions & 0 deletions Sources/Atoms/Modifier/AnimationModifier.swift
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,8 @@ public extension Atom {
///
/// Note that this modifier does nothing when being watched by other atoms.
///
/// ## Example
///
/// ```swift
/// struct TextAtom: ValueAtom, Hashable {
/// func value(context: Context) -> String {
Expand Down
2 changes: 2 additions & 0 deletions Sources/Atoms/Modifier/ChangesModifier.swift
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
public extension Atom where Produced: Equatable {
/// Prevents the atom from updating its downstream when its new value is equivalent to old value.
///
/// ## Example
///
/// ```swift
/// struct FlagAtom: StateAtom, Hashable {
/// func defaultValue(context: Context) -> Bool {
Expand Down
73 changes: 50 additions & 23 deletions Sources/Atoms/Modifier/ChangesOfModifier.swift
Original file line number Diff line number Diff line change
@@ -1,34 +1,61 @@
public extension Atom {
/// Derives a partial property with the specified key path from the original atom and prevent it
/// from updating its downstream when its new value is equivalent to old value.
///
/// ```swift
/// struct IntAtom: ValueAtom, Hashable {
/// func value(context: Context) -> Int {
/// 12345
/// }
/// }
///
/// struct ExampleView: View {
/// @Watch(IntAtom().changes(of: \.description))
/// var description
///
/// var body: some View {
/// Text(description)
/// }
/// }
/// ```
///
/// - Parameter keyPath: A key path for the property of the original atom value.
///
/// - Returns: An atom that provides the partial property of the original atom value.
#if hasFeature(InferSendableFromCaptures)
/// Derives a partial property with the specified key path from the original atom and prevent it
/// from updating its downstream when its new value is equivalent to old value.
///
/// ## Example
///
/// ```swift
/// struct IntAtom: ValueAtom, Hashable {
/// func value(context: Context) -> Int {
/// 12345
/// }
/// }
///
/// struct ExampleView: View {
/// @Watch(IntAtom().changes(of: \.description))
/// var description
///
/// var body: some View {
/// Text(description)
/// }
/// }
/// ```
///
/// - Parameter keyPath: A key path for the property of the original atom value.
///
/// - Returns: An atom that provides the partial property of the original atom value.
func changes<T: Equatable>(
of keyPath: any KeyPath<Produced, T> & Sendable
) -> ModifiedAtom<Self, ChangesOfModifier<Produced, T>> {
modifier(ChangesOfModifier(keyPath: keyPath))
}
#else
/// Derives a partial property with the specified key path from the original atom and prevent it
/// from updating its downstream when its new value is equivalent to old value.
///
/// ## Example
///
/// ```swift
/// struct IntAtom: ValueAtom, Hashable {
/// func value(context: Context) -> Int {
/// 12345
/// }
/// }
///
/// struct ExampleView: View {
/// @Watch(IntAtom().changes(of: \.description))
/// var description
///
/// var body: some View {
/// Text(description)
/// }
/// }
/// ```
///
/// - Parameter keyPath: A key path for the property of the original atom value.
///
/// - Returns: An atom that provides the partial property of the original atom value.
func changes<T: Equatable>(
of keyPath: KeyPath<Produced, T>
) -> ModifiedAtom<Self, ChangesOfModifier<Produced, T>> {
Expand Down
100 changes: 70 additions & 30 deletions Sources/Atoms/Modifier/LatestModifier.swift
Original file line number Diff line number Diff line change
@@ -1,39 +1,79 @@
public extension Atom {
/// Provides the latest value that matches the specified condition instead of the current value.
///
/// ```swift
/// struct Item {
/// let id: Int
/// let isValid: Bool
/// }
///
/// struct ItemAtom: StateAtom, Hashable {
/// func defaultValue(context: Context) -> Item {
/// Item(id: 0, isValid: false)
/// }
/// }
///
/// struct ExampleView: View {
/// @Watch(ItemAtom())
/// var currentItem
///
/// @Watch(ItemAtom().latest(\.isValid))
/// var latestValidItem
///
/// var body: some View {
/// VStack {
/// Text("Current ID: \(currentItem.id)")
/// Text("Latest Valid ID: \(latestValidItem?.id ?? 0)")
/// }
/// }
/// }
/// ```
///
#if hasFeature(InferSendableFromCaptures)
/// Provides the latest value that matches the specified condition instead of the current value.
///
/// ## Example
///
/// ```swift
/// struct Item {
/// let id: Int
/// let isValid: Bool
/// }
///
/// struct ItemAtom: StateAtom, Hashable {
/// func defaultValue(context: Context) -> Item {
/// Item(id: 0, isValid: false)
/// }
/// }
///
/// struct ExampleView: View {
/// @Watch(ItemAtom())
/// var currentItem
///
/// @Watch(ItemAtom().latest(\.isValid))
/// var latestValidItem
///
/// var body: some View {
/// VStack {
/// Text("Current ID: \(currentItem.id)")
/// Text("Latest Valid ID: \(latestValidItem?.id ?? 0)")
/// }
/// }
/// }
/// ```
///
/// - Parameter keyPath: A key path to a `Bool` property of the atom value that determines whether the value should be retained as the latest.
///
/// - Returns: An atom that provides the latest value that matches the specified condition, or `nil` if no value has matched yet.
func latest(_ keyPath: any KeyPath<Produced, Bool> & Sendable) -> ModifiedAtom<Self, LatestModifier<Produced>> {
modifier(LatestModifier(keyPath: keyPath))
}
#else
/// Provides the latest value that matches the specified condition instead of the current value.
///
/// ## Example
///
/// ```swift
/// struct Item {
/// let id: Int
/// let isValid: Bool
/// }
///
/// struct ItemAtom: StateAtom, Hashable {
/// func defaultValue(context: Context) -> Item {
/// Item(id: 0, isValid: false)
/// }
/// }
///
/// struct ExampleView: View {
/// @Watch(ItemAtom())
/// var currentItem
///
/// @Watch(ItemAtom().latest(\.isValid))
/// var latestValidItem
///
/// var body: some View {
/// VStack {
/// Text("Current ID: \(currentItem.id)")
/// Text("Latest Valid ID: \(latestValidItem?.id ?? 0)")
/// }
/// }
/// }
/// ```
///
/// - Parameter keyPath: A key path to a `Bool` property of the atom value that determines whether the value should be retained as the latest.
///
/// - Returns: An atom that provides the latest value that matches the specified condition, or `nil` if no value has matched yet.
func latest(_ keyPath: KeyPath<Produced, Bool>) -> ModifiedAtom<Self, LatestModifier<Produced>> {
modifier(LatestModifier(keyPath: keyPath))
}
Expand Down
2 changes: 2 additions & 0 deletions Sources/Atoms/Modifier/PreviousModifier.swift
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
public extension Atom {
/// Provides the previous value of the atom instead of the current value.
///
/// ## Example
///
/// ```swift
/// struct CounterAtom: StateAtom, Hashable {
/// func defaultValue(context: Context) -> Int {
Expand Down
4 changes: 4 additions & 0 deletions Sources/Atoms/Modifier/TaskPhaseModifier.swift
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@ public extension TaskAtom {
/// Converts the `Task` that the original atom provides into ``AsyncPhase`` that
/// changes overtime.
///
/// ## Example
///
/// ```swift
/// struct AsyncIntAtom: TaskAtom, Hashable {
/// func value(context: Context) async -> Int {
Expand Down Expand Up @@ -35,6 +37,8 @@ public extension ThrowingTaskAtom {
/// Converts the `Task` that the original atom provides into ``AsyncPhase`` that
/// changes overtime.
///
/// ## Example
///
/// ```swift
/// struct AsyncIntAtom: ThrowingTaskAtom, Hashable {
/// func value(context: Context) async throws -> Int {
Expand Down