diff --git a/.swiftpm/xcode/xcshareddata/xcschemes/AsyncValue.xcscheme b/.swiftpm/xcode/xcshareddata/xcschemes/AsyncValue.xcscheme
new file mode 100644
index 0000000..8fa57d0
--- /dev/null
+++ b/.swiftpm/xcode/xcshareddata/xcschemes/AsyncValue.xcscheme
@@ -0,0 +1,67 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/AsyncValueTestApp/TestPlans/AllTests.xctestplan b/AsyncValueTestApp/TestPlans/AllTests.xctestplan
index 1fa55f8..984556e 100644
--- a/AsyncValueTestApp/TestPlans/AllTests.xctestplan
+++ b/AsyncValueTestApp/TestPlans/AllTests.xctestplan
@@ -16,7 +16,6 @@
},
"testTargets" : [
{
- "parallelizable" : true,
"target" : {
"containerPath" : "container:AsyncValueTestApp.xcodeproj",
"identifier" : "2AA580C628C2FFD7006EE900",
@@ -24,7 +23,6 @@
}
},
{
- "parallelizable" : true,
"target" : {
"containerPath" : "container:..",
"identifier" : "AsyncValueTests",
diff --git a/Package.swift b/Package.swift
index 65d482c..e551d9a 100644
--- a/Package.swift
+++ b/Package.swift
@@ -1,4 +1,4 @@
-// swift-tools-version: 5.5
+// swift-tools-version: 5.7
// The swift-tools-version declares the minimum version of Swift required to build this package.
import PackageDescription
diff --git a/README.md b/README.md
index 02ac0f2..2ac5166 100644
--- a/README.md
+++ b/README.md
@@ -130,7 +130,7 @@ class MyObservableObject: ObservableObject {
}
```
-There is also an `.onRecieve(stream:perform:)` view modifier that allows you to respond to changes from an @AsyncValue
+There is also an `.onRecieve(sequence:perform:)` view modifier that allows you to respond to changes from any AsyncSequence.
```swift
struct MyView: View {
diff --git a/Sources/AsyncValue/View+AsyncValue.swift b/Sources/AsyncValue/View+AsyncValue.swift
index 621f6b9..41ea342 100644
--- a/Sources/AsyncValue/View+AsyncValue.swift
+++ b/Sources/AsyncValue/View+AsyncValue.swift
@@ -10,14 +10,17 @@
import SwiftUI
public extension View {
- /// Adds a modifier for this view that fires an action when a specific `AsyncValue` changes.
+ /// Adds a modifier for this view that fires an action when a specific `AsyncSequence` yields new values.
/// - Parameters:
- /// - stream: the async stream to recieve updates from
+ /// - sequence: the async sequence to recieve updates from
/// - handler: handler for new values
/// - Returns: some View
///
/// This view modifier works very similarly to `.onReceive(publisher:perform:)` and `.onChange(value:perform:)`
///
+ /// A warning will be printed to the console if the provided `AsyncSequence` throws an error. And no new values will be returned.
+ /// As such, it is recommended that `AsyncStream` is used rather than a custom `AsyncSequence` implementation.
+ ///
/// ```swift
/// struct MyView: View {
/// var body: some View {
@@ -25,6 +28,9 @@ public extension View {
/// .onReceive(myService.$myValue) { value in
/// print("The value changed to: \(value)")
/// }
+ /// .onReceive(myService.myStream) { newValue in
+ /// print("My stream value changed to: \(newValue)")
+ /// }
/// }
/// }
///
@@ -32,16 +38,26 @@ public extension View {
/// @AsyncValue var myValue: String = "Test" {
/// willSet { objectWillChange.send() }
/// }
+ ///
+ /// lazy var myStream: AsyncStream = {
+ /// // initialize and return some async stream here
+ /// }()
/// }
/// ```
- @ViewBuilder func onReceive(
- _ stream: AsyncStream,
- perform handler: @escaping (Value) async -> Void
+ @ViewBuilder func onReceive(
+ _ sequence: Sequence,
+ perform handler: @escaping (Sequence.Element) async -> Void
) -> some View {
if #available(iOS 15.0, macOS 12.0, tvOS 15.0, watchOS 8.0, *) {
task {
- for await value in stream {
- await handler(value)
+ do {
+ for try await value in sequence {
+ await handler(value)
+ }
+ } catch is CancellationError {
+ // cancellation errors are valid and we can ignore them.
+ } catch {
+ Self.printErrorWarning(error: error, sequence: sequence, function: #function, line: #line, file: #file)
}
}
} else {
@@ -49,8 +65,14 @@ public extension View {
onAppear {
task = Task {
- for await value in stream {
- await handler(value)
+ do {
+ for try await value in sequence {
+ await handler(value)
+ }
+ } catch is CancellationError {
+ // cancellation errors are valid and we can ignore them.
+ } catch {
+ Self.printErrorWarning(error: error, sequence: sequence, function: #function, line: #line, file: #file)
}
}
}
@@ -59,6 +81,21 @@ public extension View {
}
}
}
+
+ private static func printErrorWarning(
+ error: Error,
+ sequence: any AsyncSequence,
+ function: StaticString = #function,
+ line: Int = #line,
+ file: StaticString = #file
+ ) {
+ print("""
+ [AsyncValue] - warning: usage of .onReceive(sequence:handler:) with unhandled throwing sequence at: \(file) \(function) \(line)
+ The AsyncSequence throwing the error: \(type(of: sequence))
+ The error has been caught here to help with debugging purposes: \(String(describing: error))
+ Please handle any errors thrown in your custom AsyncSequence implementation, or try using AsyncStream instead.
+ """)
+ }
}
#endif