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
5 changes: 1 addition & 4 deletions Package.swift
Original file line number Diff line number Diff line change
Expand Up @@ -5,10 +5,7 @@ import PackageDescription
let package = Package(
name: "sqlite-data",
platforms: [
.iOS(.v13),
.macOS(.v10_15),
.tvOS(.v13),
.watchOS(.v7),
.macOS(.v15),
],
products: [
.library(
Expand Down
5 changes: 1 addition & 4 deletions Package@swift-6.0.swift
Original file line number Diff line number Diff line change
Expand Up @@ -5,10 +5,7 @@ import PackageDescription
let package = Package(
name: "sqlite-data",
platforms: [
.iOS(.v13),
.macOS(.v10_15),
.tvOS(.v13),
.watchOS(.v7),
.macOS(.v15),
],
products: [
.library(
Expand Down
46 changes: 46 additions & 0 deletions Sources/SQLiteData/FetchSubscription.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
import ConcurrencyExtras
import Sharing

/// A subscription associated with `@FetchAll`, `@FetchOne`, and `@Fetch` observation.
///
/// This value can be useful in associating the lifetime of observing a query to the lifetime of a
/// SwiftUI view _via_ the `task` view modifier. For example, loading a query in a view's `task`
/// will automatically cancel the observation when drilling down into a child view, and restart
/// observation when popping back to the view:
///
/// ```swift
/// .task {
/// try? await $reminders.load(Reminder.all).task
/// }
/// ```
public struct FetchSubscription: Sendable {
let cancellable = LockIsolated<Task<Void, any Error>?>(nil)
let onCancel: @Sendable () -> Void

init<Value>(sharedReader: SharedReader<Value>) {
onCancel = { sharedReader.projectedValue = SharedReader(value: sharedReader.wrappedValue) }
}

/// An async handle to the given fetch observation.
///
/// This handle will suspend until the current task is cancelled, at which point it will terminate
/// the observation of the associated ``FetchAll``, ``FetchOne``, or ``Fetch``.
public var task: Void {
get async throws {
let task = Task {
try await withTaskCancellationHandler {
try await Task.never()
} onCancel: {
onCancel()
}
}
cancellable.withValue { $0 = task }
try await task.cancellableValue
}
}

/// Cancels the database observation of the associated ``FetchAll``, ``FetchOne``, or ``Fetch``.
public func cancel() {
cancellable.value?.cancel()
}
}
Loading