You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何让Binding扩展支持非Sendable类型,实现非空时显示详情Sheet?

问题:适配非Sendable类型的Binding.notNilBinding扩展

问题背景

需要在optionalDetailItem非空时显示详情sheet,且不额外引入布尔@State变量,当前调用代码如下:

.sheet(isPresented: $optionalDetailItem.notNilBinding()) {
    Text("Details")
}

现有Binding扩展要求泛型T遵循Sendable协议,移除该约束后出现编译错误:

Capture of 'self' with non-sendable type 'Binding<Optional>' in a '@Sendable' closure

原扩展代码:

@available(iOS 13.0, macOS 10.15, *)
public extension Binding {
    /// Careful, never set this to `true`.
    func notNilBinding<T: Sendable>() -> Binding<Bool> where Value == Optional<T> {
        Binding<Bool>(
            get: { self.wrappedValue != nil },
            set: { newValue in
                if !newValue {
                    wrappedValue = nil
                } else {
                    // In this context we cannot not know what to set `wrappedValue` to.
                    fatalError("This must not be set to `true`.")
                }
            }
        )
    }
}

解决方案

错误根源是SwiftUI的Binding构造器默认要求闭包为@Sendable,当Binding<Optional<T>>的T不遵循Sendable时,捕获self会触发Sendability检查。以下两种方法可解决:

方法一:显式标记闭包为@unchecked Sendable

直接在set闭包上添加@unchecked Sendable,告知编译器我们确认闭包的Sendability安全(仅将wrappedValue设为nil,无跨线程传递非Sendable实例的风险):

@available(iOS 13.0, macOS 10.15, *)
public extension Binding {
    /// 注意:请勿将此Binding的值设为true
    func notNilBinding<T>() -> Binding<Bool> where Value == Optional<T> {
        Binding<Bool>(
            get: { self.wrappedValue != nil },
            set: { @unchecked Sendable [self] newValue in
                guard !newValue else {
                    fatalError("此Binding不允许被设为true")
                }
                wrappedValue = nil
            }
        )
    }
}

方法二:通过辅助协议重构扩展

定义OptionalType协议来约束Optional类型,让扩展逻辑更清晰,同时规避泛型约束的Sendable限制:

@available(iOS 13.0, macOS 10.15, *)
public extension Binding where Value: OptionalType {
    /// 注意:请勿将此Binding的值设为true
    func notNilBinding() -> Binding<Bool> {
        Binding<Bool>(
            get: { self.wrappedValue != nil },
            set: { @unchecked Sendable [self] newValue in
                guard !newValue else {
                    fatalError("此Binding不允许被设为true")
                }
                wrappedValue = nil
            }
        )
    }
}

// 辅助协议,用于匹配所有Optional类型
public protocol OptionalType {
    associatedtype Wrapped
    var wrappedValue: Wrapped? { get set }
}

extension Optional: OptionalType {
    public var wrappedValue: Wrapped? {
        get { self }
        set { self = newValue }
    }
}

注意事项

  • 使用@unchecked Sendable时,需确保闭包内不会将非Sendable的T实例传递到其他线程,避免潜在的数据竞争风险。
  • 两种方法都保留了原逻辑:仅允许将Binding设为false(清空optional值),设为true会触发fatalError。

内容的提问来源于stack exchange,提问作者John

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.17 16:12:43