适配iOS14使用@FocusState报存储属性不可用错误的解决方法
问题说明
开发最低支持iOS 14的SwiftUI应用时,若尝试直接对存储属性(包括带属性包装器的存储属性)添加@available(iOS 15, *)标记以使用@FocusState这类仅高版本系统支持的属性包装器,会触发Stored properties cannot be marked potentially unavailable编译错误。
该写法在Xcode 13中可编译通过属于旧版本编译器的检查漏洞,Xcode 14补全了存储属性的可用性校验逻辑后会正常报错。计算属性无法附加属性包装器,整份复制View代码会产生大量冗余。
触发报错的示例代码:
struct MyView: View { enum Field: Hashable { case username } @State private var myText: String = "" @available(iOS 15, *) @FocusState private var focusedField: Field? var body: some View { if #available(iOS 15, *) { TextEditor(text: $myText) .focused($focusedField, equals: .username) } else { TextEditor(text: $myText) } } }
推荐方案
无需复制整份View代码,将高版本独有的逻辑拆分为被@available标记的独立子组件即可,从根源规避存储属性的版本限制问题:
struct MyView: View { enum Field: Hashable { case username } @State private var myText: String = "" var body: some View { if #available(iOS 15, *) { FocusableTextEditor( text: $myText, targetFocusField: .username ) } else { TextEditor(text: $myText) } } } @available(iOS 15, *) private struct FocusableTextEditor: View { @Binding var text: String let targetFocusField: Field @FocusState private var focusedField: Field? var body: some View { TextEditor(text: $text) .focused($focusedField, equals: targetFocusField) } }
该方案的优势:
- 无冗余重复代码,版本差异逻辑完全收敛在轻量子组件中
- 完全符合Swift语法规则,内存布局安全,不存在兼容性崩溃风险
- 后续扩展高版本独有逻辑时,仅需修改对应子组件,不会污染低版本兼容代码
不推荐的方案
- 不要尝试通过runtime强转、指针操作绕过编译器检查,这类写法会破坏结构体内存布局,在不同系统版本上极易出现随机崩溃
- 不建议用ObservableObject包装FocusState实现版本兼容,该方案代码冗余度更高,可读性远差于子组件拆分方案
- 不建议整份复制主View代码做版本分支,后续维护时需要同步修改两份代码,极易出现逻辑不一致问题
内容的提问来源于stack exchange,提问作者Allan Spreys
相关产品推荐
相关产品推荐

