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
相关产品推荐
相关产品推荐

