Search code examples
swiftswiftuiviewbuilder

SwiftUI ViewBuilder and non-sendable type 'Binding<Bool>' in a `@Sendable` closure


I have a ViewBuilder that I use to lock/unlock some views. It uses a Binding to pass the locked/unlocked State.

I am now getting this warning on the if line:

Capture of 'isUnlocked' with non-sendable type 'Binding' in a @Sendable closure

extension View {
    @ViewBuilder
    func requestUnlock(isUnlocked: Binding<Bool>) -> some View {
        
        self.task {
            if isUnlocked.wrappedValue == false {
                let unlocked = await Unlock.authenticate()
                isUnlocked.wrappedValue = unlocked
            }
        }
    }
}

As this is an extension of View I cannot add or ignore Sendable compliance and i'm not sure what magic is needed for me to clear this warning.


Solution

  • Create a view modifier instead and let SwiftUI handle the updates and actor isolation for you via the property wrapper:

    struct UnlockRequester: ViewModifier {
        @Binding var isUnlocked: Bool
        
        func body(content: Content) -> some View {
            content.task {
                if isUnlocked == false {
                    isUnlocked = await Unlock.authenticate()
                }
            }
        }
    }
    

    Then:

    extension View {
        func requestUnlock(isUnlocked: Binding<Bool>) -> some View {
            self.modifier(UnlockRequester(isUnlocked: isUnlocked))
        }
    }
    

    This gives no warnings even with strict concurrency checking enabled.