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

Swift中如何让DefaultsKey直接支持Getter/Setter以简化UserDefaults访问?

实现直接存取的UserDefaults属性

当然可以实现你想要的简洁API!不用每次都写.value或者下标操作,有几种优雅的方式来达成这个目标,下面是最常用的两种方案:

方案一:使用@propertyWrapper(推荐)

这是Swift官方推荐的方式,语法最清晰,也最符合Swift的设计理念。我们可以把DefaultsKey定义成一个属性包装器,直接在AppUserDefaults里声明类型化的属性,就能直接进行赋值和取值操作。

实现代码

// 定义属性包装器
@propertyWrapper
struct DefaultsKey<Value> {
    private let key: String
    private let defaultValue: Value
    private let userDefaults: UserDefaults
    
    // 初始化方法,支持自定义UserDefaults实例(默认用standard)
    init(key: String, defaultValue: Value, userDefaults: UserDefaults = .standard) {
        self.key = key
        self.defaultValue = defaultValue
        self.userDefaults = userDefaults
    }
    
    // 核心的存取逻辑
    var wrappedValue: Value {
        get {
            userDefaults.object(forKey: key) as? Value ?? defaultValue
        }
        set {
            // 处理Optional类型的特殊情况,避免存储nil导致的问题
            if let optionalValue = newValue as? AnyOptional, optionalValue.isNil {
                userDefaults.removeObject(forKey: key)
            } else {
                userDefaults.set(newValue, forKey: key)
            }
        }
    }
}

// 辅助协议,用来处理Optional类型的存储
private protocol AnyOptional {
    var isNil: Bool { get }
}

extension Optional: AnyOptional {
    var isNil: Bool { self == nil }
}

// 你的AppUserDefaults类
class AppUserDefaults {
    // 直接声明类型化的属性,用@DefaultsKey包装
    @DefaultsKey(key: "username", defaultValue: "")
    var username: String
    
    @DefaultsKey(key: "isDarkMode", defaultValue: false)
    var isDarkMode: Bool
    
    // 支持Optional类型
    @DefaultsKey(key: "lastLoginDate", defaultValue: nil)
    var lastLoginDate: Date?
}

使用方式

let props = AppUserDefaults()

// 直接赋值
props.username = "bla"
props.isDarkMode = true
props.lastLoginDate = Date()

// 直接取值
let currentUsername = props.username
let isDark = props.isDarkMode

这种方式完全满足你的需求,代码简洁易读,还自动处理了Optional类型的存储逻辑,避免把nil写入UserDefaults。

方案二:给DefaultsKey添加动态存取逻辑

如果你坚持要让AppUserDefaults的属性是DefaultsKey类型,我们可以给DefaultsKey结构体添加计算属性和运算符重载,让它可以直接被赋值和取值。

实现代码

struct DefaultsKey<Value> {
    let key: String
    let defaultValue: Value
    private let userDefaults: UserDefaults
    
    init(_ key: String, defaultValue: Value, userDefaults: UserDefaults = .standard) {
        self.key = key
        self.defaultValue = defaultValue
        self.userDefaults = userDefaults
    }
    
    // 直接通过实例本身存取值
    var value: Value {
        get {
            userDefaults.object(forKey: key) as? Value ?? defaultValue
        }
        set {
            if let optionalValue = newValue as? AnyOptional, optionalValue.isNil {
                userDefaults.removeObject(forKey: key)
            } else {
                userDefaults.set(newValue, forKey: key)
            }
        }
    }
    
    // 重载赋值运算符,支持直接给DefaultsKey实例赋值(属性需为var)
    static func = (key: inout DefaultsKey<Value>, newValue: Value) {
        key.value = newValue
    }
    
    // 让DefaultsKey实例可以直接被当作Value类型使用(通过调用语法)
    func callAsFunction() -> Value {
        value
    }
}

// 同样需要Optional辅助协议
private protocol AnyOptional {
    var isNil: Bool { get }
}

extension Optional: AnyOptional {
    var isNil: Bool { self == nil }
}

// 你的AppUserDefaults类
class AppUserDefaults {
    var username = DefaultsKey<String>("username", defaultValue: "")
    let isDarkMode = DefaultsKey<Bool>("isDarkMode", defaultValue: false)
    let lastLoginDate = DefaultsKey<Date?>("lastLoginDate", defaultValue: nil)
}

使用方式

let props = AppUserDefaults()

// 直接赋值(针对var属性)
props.username = "bla"
// 或者用.value属性赋值(支持let属性)
props.isDarkMode.value = true

// 直接取值
let currentUsername = props.username()
// 或者用.value属性取值
let currentDarkMode = props.isDarkMode.value

这种方式虽然能实现类似的效果,但语法上不如属性包装器直观,而且如果用重载赋值运算符的话,AppUserDefaults里的属性需要声明为var而不是let,灵活性稍差。

总结

推荐使用方案一的@propertyWrapper方式,它不仅完全符合你想要的简洁API,还遵循Swift的现代语法规范,维护性和可读性都更强。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.28 10:08:52