BrentMifsud/AsyncValue
A simple swift package that provides a Swift Concurrency equivalent to `@Published`.
Via Xcode
- In the project navigator on the left, click on your project
- click
Package Dependencies - enter: https://github.com/BrentMifsud/AsyncValue.git into the search bar
- select the desired version and click
Add Package
Via Package.swift
in your Package.swift file, add the following:
let package = Package(
name: "MyPackage",
dependencies: [
.package(url: "https://github.com/BrentMifsud/AsyncValue.git", from: .init(1, 0, 0))
],
products: [
// Products define the executables and libraries a package produces, and make them visible to other packages.
.library(
name: "MyPackage",
targets: ["MyPackage"]
),
],
targets: [
// Targets are the basic building blocks of a package. A target can define a module or a test suite.
// Targets can depend on other targets in this package, and on products in packages this package depends on.
.target(
name: "MyPackage",
dependencies: ["AsyncValue"]
),
.testTarget(
name: "MyPackageTests",
dependencies: ["MyPackage"]
),
]
)Usage
Accessing the current value
@AsyncValue var myValue: String = "Test"
print(myValue) // prints: testObserving changes to the value
Just like the publisher backing @Published, AsyncValue is backed by an AsyncStream. And you can subscribe to updates to the value.
@AsyncValue myValue: String = "Test"
Task {
for await value in myValue {
print("Value: \(value)")
}
}
Task {
await Task.sleep(nanoseconds: 1_000_000_000)
myValue = "New Value"
}
/* Prints:
Value: test
Value: New Value
*/Observing with multiple tasks
One of the major limitations of AsyncStream out of the box is that you can only for-await-in on it with a single task.
AsyncValue does not have this limitation:
@AsyncValue myValue: String = "Test"
Task {
for await value in myValue {
print("Task 1 Value: \(value)")
}
}
Task {
for await value in myValue {
print("Task 2 Value: \(value)")
}
}
Task {
await Task.sleep(nanoseconds: 1_000_000_000)
myValue = "New Value"
}
/* Prints (note that the order of the tasks printing may vary as this is happening asyncronously):
Task 1 Value: test
Task 2 Value: test
Task 2 Value: New Value
Task 1 Value: New Value
*/Using with SwiftUI
ObservableObject support
AsyncValue can be adapted to work seamlessly with ObservableObject with a single line of code:
class MyObservableObject: ObservableObject {
@AsyncValue var myValue: String = "Test"
}Any changes to an AsyncValue will trigger a view update in the same way @Published does.
Thanks to jlsiewert for coming up with the cool trick for making this work automatically.
.onReceive equivalent for AsyncSequence
There is also an .onRecieve(sequence:perform:) view modifier that allows you to respond to changes from any AsyncSequence.
struct MyView: View {
var body: some View {
Text("Hello World!")
.onReceive(myService.$myValue) { value in
print("The value changed to: \(value)")
}
}
}
class MyService: ObservableObject {
@AsyncValue var myValue: String = "Test"
}Package Metadata
Repository: BrentMifsud/AsyncValue
Stars: 38
Forks: 3
Open issues: 0
Default branch: main
Primary language: swift
License: MIT
Topics: concurrency, swift, swift-concurrency
README: README.md