[译] 让我们来简化 UserDefaults 的使用

9,939 阅读2分钟

每个人都用 UserDefaults 来存储一些简单的数据,并且知道使用该存储很容易。但是今天我会改善一点它的交互性!让我们从最明显的解决方案开始,并实现一些新颖且优雅的东西。😌

想象一下我们有一个服务 —— SettingsService。这个服务掌握了应用的设置 —— 正在使用哪个主题(黑暗,明亮),是否启用通知等等。为了实现它,大多数开发人员会首先考虑 UserDefaults。 当然,用哪种方式取决于具体情况,但先让我们来简化 UserDefaults

  1. 我们的第一个最简方案
class SettingsService {

    private enum Keys {
        static let isNotificationsEnabled = "isNotificationsEnabled"
    }

    var isNotificationsEnabled: Bool {
        get {
            let isEnabled = UserDefaults.standard.value(forKey: Keys.isNotificationsEnabled) as? Bool
            return isEnabled ?? true
        }
        set {
            UserDefaults.standard.setValue(newValue, forKey: Keys.isNotificationsEnabled)
        }
    }
}

为了简单化,我直接使用 UserDefaults.standard,但在一个真实项目中,你最好把它存到一个 property 中,并使用依赖注入。

  1. 下一步,我想要摆脱 Keys 枚举——使用 #function 来代替:
class SettingsService {

    var isNotificationsEnabled: Bool {
        get {
            let isEnabled = UserDefaults.standard.value(forKey: #function) as? Bool
            return isEnabled ?? true
        }
        set {
            UserDefaults.standard.setValue(newValue, forKey: #function)
        }
    }
}

看,怎么样!让我们继续:)

  1. 下标时间!我们刚刚把 value(forKey:) 方法封装成支持范型的下标语法形式:
extension UserDefaults {

    subscript<T>(key: String) -> T? {
        get {
            return value(forKey: key) as? T
        }
        set {
            set(newValue, forKey: key)
        }
    }
}

class SettingsService {

    var isNotificationsEnabled: Bool {
        get {
            return UserDefaults.standard[#function] ?? true
        }
        set {
            UserDefaults.standard[#function] = newValue
        }
    }
}

它看起来已经很整洁了!但是 Enums 呢?🤔

enum AppTheme: Int {
    case light
    case dark
}

class SettingsService {

    var appTheme: AppTheme {
        get {
            if let rawValue: AppTheme.RawValue = UserDefaults.standard[#function], let theme = AppTheme(rawValue: rawValue) {
                return theme
            }
            return .light
        }
        set {
            UserDefaults.standard[#function] = newValue.rawValue
        }
    }
}

这里可以重构!

  1. 让我们为 RawRepresentable 值编写一个类似的 subscript
extension UserDefaults {
    
    subscript<T: RawRepresentable>(key: String) -> T? {
        get {
            if let rawValue = value(forKey: key) as? T.RawValue {
                return T(rawValue: rawValue)
            }
            return nil
        }
        set {
            set(newValue?.rawValue, forKey: key)
        }
    }
}

class SettingsService {
    
    var appTheme: AppTheme {
        get {
            return UserDefaults.standard[#function] ?? .light
        }
        set {
            UserDefaults.standard[#function] = newValue
        }
    }
}

马上完成啦!请注意,此扩展仅适用于使用 RawRepresentable 的枚举。


别忘了订阅我的 telegram channel!第一时间了解 iOS 世界的有趣新闻和文章!


希望你能喜欢我写的 extension!如果你有任何改进它的想法请告诉我!查看 UserDefaults最新版本 extension 尽情地体验一下吧:)

这就是在构建 ITC 时候的我

一个在 Rosberry 工作的毛发浓密的 iOS 工程师。热衷响应式编程,开源爱好者,循环引用检测人。:)

感谢 Evgeny MikhaylovRosberry


掘金翻译计划 是一个翻译优质互联网技术文章的社区,文章来源为 掘金 上的英文分享文章。内容覆盖 AndroidiOS前端后端区块链产品设计人工智能等领域,想要查看更多优质译文请持续关注 掘金翻译计划官方微博知乎专栏