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

如何为可选类型@AppStorage属性设置非空初始默认值并正常持久化

问题原因

  1. 原生@AppStorage 仅为非可选类型提供了带默认值的构造器,可选类型的默认构造方法不支持直接传入默认值,因此直接赋值写法无法编译通过。
  2. 在init中直接判断赋值时,@AppStorage 包装的持久化值还未从UserDefaults完成读取,此时访问属性永远返回nil,所以每次启动都会覆盖已有旧值。

解决方案

方案1:扩展AppStorage实现可选类型带默认值的构造器

仅需扩展一次即可全局使用,写法和原生API完全对齐:

import SwiftUI

extension AppStorage where Value == String? {
    init(wrappedValue: Value, _ key: String, store: UserDefaults? = nil) {
        let targetStore = store ?? .standard
        // 仅当持久化中不存在该key时写入默认值
        if targetStore.object(forKey: key) == nil {
            targetStore.set(wrappedValue, forKey: key)
        }
        self.init(key, store: targetStore)
    }
}

使用时直接按你最初的写法即可编译通过,且不会覆盖已有持久化值:

@AppStorage("navItemSelected") var navItemSelected: String? = "default"

如果需要支持更多可选类型(比如Int?、Bool?等),可以把扩展改成泛型版本:

extension AppStorage where Value: ExpressibleByNilLiteral {
    init<T>(wrappedValue: Value, _ key: String, store: UserDefaults? = nil) where Value == T?, T: _DefaultStored {
        let targetStore = store ?? .standard
        if targetStore.object(forKey: key) == nil {
            targetStore.set(wrappedValue, forKey: key)
        }
        self.init(key, store: targetStore)
    }
}

方案2:用非可选存储+可选计算属性中转

不需要自定义扩展,完全使用原生API实现,兼容性更好:

// 底层用非可选类型存储,直接设置默认值
@AppStorage("navItemSelected") private var storedNavItemSelected: String = "default"

// 对外暴露可选类型,适配NavigationLink入参要求
var navItemSelected: String? {
    get { storedNavItemSelected }
    set { storedNavItemSelected = newValue ?? "default" }
}

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.09.28 19:06:03