hborla
(Holly Borla)
5
@isolated(any) function types capture the value that represents whatever the isolation of the closure is. @isolated(any) has no effect on the inferred isolation of a given closure argument; the default inference rules still apply. However, I believe this Binding initializer uses @_inheritActorContext, just like Task.init and the SwiftUI task modifier, so it does capture the static isolation of the context the binding is formed in.
I believe the reason why adding the isolated parameter like @vns suggested does not resolve the warning is because the function type is also @Sendable. In Xcode 16 Beta 1, @Sendable is implied by @isolated(any) (which was removed per the acceptance notes of SE-0431), but I believe @Sendable is correct for this API, because Binding itself conforms Sendable. Now, if the dynamic isolation is an actor like @MainActor, this code is safe because the closure cannot be invoked multiple times concurrently. The code is only unsafe if the function is dynamically nonisolated. If you never need this functionality for non-isolated bindings, then you can resolve the issue by marking the function with @MainActor*. SE-0434: Usability of global-actor-isolated types (which is to be accepted as soon as I write up a section in the alternatives considered to address some LSG feedback), allows you to capture non-Sendable / mutable values in global-actor-isolated closures because if the closure is isolated, the actor will always serialize calls to the closure, so there's no access to the mutable capture.
*I don't think the implementation of SE-0434 is in Xcode 16 Beta 1. However, it has landed on GitHub on the release/6.0 branch, and this code compiles with no errors in the latest development 6.0 snapshot when compiled with -swift-version 6 against the Xcode 16 Beta 1 SDK:
import SwiftUI
extension Binding where Value == String {
@MainActor func nonEmpty() -> Self {
var isFirstEdit = true
return Self {
(wrappedValue.isEmpty && isFirstEdit) ? " " : wrappedValue
} set: { newValue, transaction in
var newValue = newValue
if isFirstEdit && newValue.first == " " {
newValue.removeFirst()
isFirstEdit = false
}
self.transaction(transaction).wrappedValue = newValue
}
}
}