SendableValue
is a powerful tool provided by Later that allows you to manage thread-safe, mutable values across concurrent tasks in a Swift project. It wraps a value that can be safely shared between different contexts, ensuring that the value is handled properly even when accessed by multiple tasks simultaneously.
The SendableValue
type ensures that the value it holds is safely accessible in a concurrent environment. It uses OSAllocatedUnfairLock
to manage locking, making it efficient and secure for multi-threaded operations.
- Thread-Safe Mutability: Ensures values can be safely mutated across concurrent tasks.
- Concurrency Control: Built-in locking mechanisms manage access in a multi-threaded environment.
- Compatibility with
Sendable
: Allows classes or structs usingSendableValue
to be marked asSendable
even when they contain mutable state.
One of the most common use cases for SendableValue
is in a Sendable
class, where you need to safely share mutable state across multiple tasks.
import Later
final class Counter: Sendable {
private let value: SendableValue<Int>
init(initialValue: Int) {
self.value = SendableValue(initialValue)
}
func increment() {
value.update { $0 += 1 }
}
func getValue() async -> Int {
await value.value
}
}
let counter = Counter(initialValue: 0)
// Increment concurrently
let tasks = (1 ... 10).map { _ in
Task {
counter.increment()
}
}
for task in tasks {
await task.value
}
let finalValue = await counter.getValue()
print(finalValue) // Prints "10"
In this example, Counter
is a Sendable
class that safely manages a mutable integer value using SendableValue
. The value is incremented concurrently across multiple tasks, demonstrating thread-safe access and mutation.
Another key use of SendableValue
is to allow safe sharing of mutable state in Sendable
contexts, where direct use of var
would otherwise make the class or struct non-conformant.
final class SharedData: Sendable {
private let data: SendableValue<String>
init(initialData: String) {
self.data = SendableValue(initialData)
}
func updateData(newData: String) {
data.set(value: newData)
}
func getData() async -> String {
await data.value
}
}
let sharedData = SharedData(initialData: "Initial Value")
// Update data safely and ensure the update is completed before accessing the value
Task {
sharedData.updateData(newData: "Updated Value")
let currentValue = await sharedData.getData()
print(currentValue) // Prints "Updated Value"
}
This updated example ensures that the value is only accessed after the task responsible for updating the data has completed, preventing any indeterminate results.
SendableValue
can also be used in actors to manage mutable state safely and efficiently.
actor SafeCounter {
private let value: SendableValue<Int>
init(initialValue: Int) {
self.value = SendableValue(initialValue)
}
func increment() {
value.update { $0 += 1 }
}
func getValue() async -> Int {
await value.value
}
}
let safeCounter = SafeCounter(initialValue: 0)
// Increment the counter safely within the actor
await safeCounter.increment()
let safeValue = await safeCounter.getValue()
print(safeValue) // Prints "1"
In this actor example, SendableValue
provides additional safety and efficiency, ensuring that the state is not only protected by the actor’s isolation but also optimized for concurrent access.
- Use in Concurrent Environments: Utilize
SendableValue
whenever you need to share and mutate values across multiple concurrent tasks. - Incorporate in
Sendable
Classes: UseSendableValue
inSendable
classes to safely manage mutable state without violatingSendable
conformance. - Combine with Actors: Consider using
SendableValue
within actors to enhance safety and efficiency in managing mutable state.
SendableValue
is an essential component of the Later library that provides a simple, safe way to manage mutable state in asynchronous Swift programming. By using SendableValue
, you can ensure that your values are handled correctly across concurrent tasks, making your code more robust and maintainable.
For more advanced usage, explore other components of the Later library like Future, Deferred, and Stream.