兼容iOS15适配iOS16小组件时WidgetBundle无法使用#available问题
@WidgetBundleBuilder 不支持在构建闭包内直接使用if #available做版本分支判断,这是WidgetKit的固有API限制:只要在闭包内直接写入RememberAccWidget()初始化代码,即使外层包裹版本判断,低版本系统运行时仍会尝试加载这个仅支持iOS16的类,触发编译报错或运行时崩溃。
适配方案
把iOS16专属的组件收拢到单独标记了16+可用性的WidgetBundle中,在主WidgetBundle里通过版本判断返回对应分支即可,调整后的代码如下:
@main struct RememberWidgetBundle: WidgetBundle { @WidgetBundleBuilder var body: some Widget { // 注意使用iOSApplicationExtension的版本判断,匹配Widget扩展的运行环境 if #available(iOSApplicationExtension 16.0, *) { iOS16WidgetCollection() } RememberWidget() } } // iOS16+专属组件集合,仅在16以上系统才会被加载 @available(iOSApplicationExtension 16.0, *) struct iOS16WidgetCollection: WidgetBundle { var body: some Widget { RememberAccWidget() // 后续新增的iOS16专属锁屏组件、实时活动相关组件都可以放在这里 } } // 原有的RememberAccWidget、RememberWidget代码不需要做任何修改 @available(iOS 16, *) struct RememberAccWidget: Widget { let kind: String = "Remember Accessory 1" var body: some WidgetConfiguration { StaticConfiguration(kind: kind, provider: Provider()) { entry in RememberAccWidgetEntryView(entry: entry) } .configurationDisplayName("Accessory Widget") .description(helpText) .supportedFamilies([.accessoryInline, .accessoryRectangular]) } }
实现逻辑说明
- 所有iOS16专属组件的初始化代码,都放在被
@available(iOSApplicationExtension 16.0, *)标记的iOS16WidgetCollection内部,低版本系统完全不会加载这段代码路径,从根本上避免了找不到类的崩溃 - 主WidgetBundle没有加任何版本限制,全系统版本都能正常初始化
- 版本判断使用
iOSApplicationExtension 16.0而非普通的iOS 16,匹配Widget扩展的运行环境,避免版本判断失效
适配后可完全达到预期效果:iOS15系统仅加载原有桌面小组件,功能完全不受影响;iOS16系统同时加载原有桌面小组件和新增的锁屏配件小组件,两类组件都能正常展示使用。
内容的提问来源于stack exchange,提问作者Lim Thye Chean
相关产品推荐
相关产品推荐

