AsyncValue

1.0.2

A simple swift package that provides a Swift Concurrency equivalent to `@Published`.
BrentMifsud/AsyncValue

What's New

.onReceive View Modifier

2022-09-03T09:30:44Z

What's Changed

  • Add .onReceive(stream:perform:) view modifier and UI Tests by @BrentMifsud in #4

Full Changelog: 1.0.1...1.0.2

Build

AsyncValue

This is a simple package that provides a convenience property wrapper around AsyncStream that behaves almost identically to @Published.

Installation

Via Xcode

  1. In the project navigator on the left, click on your project
  2. click Package Dependencies
  3. enter: https://github.com/BrentMifsud/AsyncValue.git into the search bar
  4. 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: test

Observing 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

AsyncValue can be adapted to work seamlessly with ObservableObject with a single line of code:

class MyObservableObject: ObservableObject {
    @AsyncValue var myValue: String = "Test" {
        // IMPORTANT: you must use `willSet` as that is what `@Published` uses under the hood
        willSet { objectWillChange.send() }
    }
}

There is also an .onRecieve(stream:perform:) view modifier that allows you to respond to changes from an @AsyncValue

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" {
        willSet { objectWillChange.send() }
    }
}

Description

  • Swift Tools 5.5.0
View More Packages from this Author

Dependencies

  • None
Last updated: Sun Sep 25 2022 07:23:34 GMT-0500 (GMT-05:00)