SwiftyUserDefaults
NSUserDefaults
提供 Modern Swift API
为 SwiftyUserDefaults 通过结合表达式的 Swifty API 和静态类型的好处,使得使用用户默认项目变得愉快。在一个位置定义您的键,轻松使用值类型,并免费获得额外的安全性和方便的编译时检查。
旧版本文档:[版本 4.0.0](https://github.com/sunshinejr/SwiftyUserDefaults/blob/566ace16ee91242b61e2e9da6cdbe7dfdadd926c/README.md),[版本 3.0.1](https://github.com/sunshinejr/SwiftyUserDefaults/blob/14b629b035bf6355b46ece22c3851068a488a895/README.md)
迁移指南:[从 4.x 迁移到 5.x](MigrationGuides/migration_4_to_5.md),[从 4.0.0-alpha.1 迁移到 4.0.0-alpha.3](MigrationGuides/migration_4_alpha_1_to_4_alpha_2.md),[从 3.x 迁移到 4.x](MigrationGuides/migration_3_to_4.md)
版本 5.0.0
特性 • 用法 • Codable • NSCoding • RawRepresentable • 扩展现有类型 • 自定义类型
属性包装器 • KVO • dynamicMemberLookup • 启动参数 • 实用工具 • 安装
特性
开始使用SwifyUserDefaults只需一步
定义您的关键字!
extension DefaultsKeys {
var username: DefaultsKey<String?> { .init("username") }
var launchCount: DefaultsKey<Int> { .init("launchCount", defaultValue: 0) }
}
然后就可以使用了]
// Get and set user defaults easily
let username = Defaults[\.username]
Defaults[\.hotkeyEnabled] = true
// Modify value types in place
Defaults[\.launchCount] += 1
Defaults[\.volume] -= 0.1
Defaults[\.strings] += "… can easily be extended!"
// Use and modify typed arrays
Defaults[\.libraries].append("SwiftyUserDefaults")
Defaults[\.libraries][0] += " 2.0"
// Easily work with custom serialized types
Defaults[\.color] = NSColor.white
Defaults[\.color]?.whiteComponent // => 1.0
如果您使用Swift 5.1 - 好消息!您还可以使用键路径 dynamicMemberLookup
Defaults.color = NSColor.white
更多内容请参见KeyPath dynamicMemberLookup部分。
用法
定义您的关键字
为了充分发挥SwifyUserDefaults的作用,请提前定义用户默认选项关键字
let colorKey = DefaultsKey<String>("color", defaultValue: "")
只需创建一个DefaultsKey
对象,在尖括号中放入您想要存储的值的类型,在括号中放入关键字名称,即可使用。如果您要有一个非可选值,只需在关键字中提供一个defaultValue
(参看上面示例)。
现在您可以使用Defaults
快捷方式访问这些值
Defaults[key: colorKey] = "red"
Defaults[key: colorKey] // => "red", typed as String
编译器不会允许您设置错误的数据类型,并且获取操作方便地返回String
。
使用快捷键
为了更方便,通过扩展 DefaultsKeys
类并添加静态属性来自定义你的快捷键
extension DefaultsKeys {
var username: DefaultsKey<String?> { .init("username") }
var launchCount: DefaultsKey<Int> { .init("launchCount", defaultValue: 0) }
}
并用快捷键点语法
Defaults[\.username] = "joe"
Defaults[\.launchCount] += 1
支持类型
SwiftyUserDefaults 支持所有标准的 NSUserDefaults
类型,例如字符串、数字、布尔值、数组和字典。
以下是内置单值默认值的完整表格
单值 | 数组 |
---|---|
字符串 |
[String] |
Int |
[Int] |
Double |
[Double] |
Bool |
[Bool] |
Data |
[Data] |
Date |
[Date] |
URL |
[URL] |
[String: Any] |
[[String: Any]] |
但这不仅仅如此!
Codable
从版本 4 开始,SwiftyUserDefaults
支持了 Codable
!只需要让你的类型遵循 DefaultsSerializable
final class FrogCodable: Codable, DefaultsSerializable {
let name: String
}
无需实现!这样做你将得到一个可选的 DefaultsKey
的选项
let frog = DefaultsKey<FrogCodable?>("frog")
此外,你将免费获得数组支持
let froggies = DefaultsKey<[FrogCodable]?>("froggies")
NSCoding
NSCoding
在版本 4 之前得到了支持,但在这个版本中我们将其支持提升到了另一个层次。不再需要自定义下标!类似地,以与 Codable
支持相同的方式支持自定义的 NSCoding
类型
final class FrogSerializable: NSObject, NSCoding, DefaultsSerializable { ... }
同样无需实现!这样做你将得到一个可选的 DefaultsKey
的选项
let frog = DefaultsKey<FrogSerializable?>("frog")
此外,你还将免费获得数组支持
let froggies = DefaultsKey<[FrogSerializable]?>("froggies")
RawRepresentable
最后但同样重要的是,支持 RawRepresentable
!再次,与 NSCoding
和 Codable
一样的情形。
enum BestFroggiesEnum: String, DefaultsSerializable {
case Andy
case Dandy
}
同样无需实现!这样做你将得到一个可选的 DefaultsKey
的选项
let frog = DefaultsKey<BestFroggiesEnum?>("frog")
此外,你还将免费获得数组支持
let froggies = DefaultsKey<[BestFroggiesEnum]?>("froggies")
扩展现有类型
假设您想扩展一个支持 UIColor
或其他是 NSCoding
、Codable
或 RawRepresentable
的类型。要使其能够与 SwiftyUserDefaults
兼容,应该非常简单:
extension UIColor: DefaultsSerializable {}
如果不是这样,我们有两个选择
a) 这是一个我们不知道如何序列化的自定义类型,在这种情况下请在 自定义类型 处添加信息
b) 这是一个bug,它应该得到支持。在这种情况下,请提交issue (+你可以在中间时间使用 自定义类型 方法作为临时解决方案)
自定义类型
如果您想添加我们尚未支持的自己的自定义类型,我们为您提供了解决方案。我们使用许多不同类型的 DefaultsBridge
来指定如何获取/设置值和值的数组。当您查看 DefaultsSerializable
协议时,它期望在每个类型中都有两个属性:`_defaults` 和 `_defaultsArray`,它们都是 DefaultsBridge
类型。
例如,这是一个针对使用 NSKeyedArchiver
/NSKeyedUnarchiver
存储和检索单值数据的桥接器
public struct DefaultsKeyedArchiverBridge<T>: DefaultsBridge {
public func get(key: String, userDefaults: UserDefaults) -> T? {
userDefaults.data(forKey: key).flatMap(NSKeyedUnarchiver.unarchiveObject) as? T
}
public func save(key: String, value: T?, userDefaults: UserDefaults) {
userDefaults.set(NSKeyedArchiver.archivedData(withRootObject: value), forKey: key)
}
public func deserialize(_ object: Any) -> T? {
guard let data = object as? Data else { return nil }
return NSKeyedUnarchiver.unarchiveObject(with: data) as? T
}
}
用于存储和检索数组的默认值的桥接器
public struct DefaultsArrayBridge<T: Collection>: DefaultsBridge {
public func save(key: String, value: T?, userDefaults: UserDefaults) {
userDefaults.set(value, forKey: key)
}
public func get(key: String, userDefaults: UserDefaults) -> T? {
userDefaults.array(forKey: key) as? T
}
public func deserialize(_ object: Any) -> T? {
nil
}
}
现在,为了在我们的类型中使用这些桥接器,我们只需 declare 它如下所示:
struct FrogCustomSerializable: DefaultsSerializable {
static var _defaults: DefaultsKeyedArchiverBridge( { DefaultsKeyedArchiverBridge() }
static var _defaultsArray: DefaultsKeyedArchiverBridge { DefaultsKeyedArchiverBridge() }
let name: String
}
不幸的是,如果您发现自己需要自定义桥接器,您可能需要自己编写。
final class DefaultsFrogBridge: DefaultsBridge {
func get(key: String, userDefaults: UserDefaults) -> FrogCustomSerializable? {
let name = userDefaults.string(forKey: key)
return name.map(FrogCustomSerializable.init)
}
func save(key: String, value: FrogCustomSerializable?, userDefaults: UserDefaults) {
userDefaults.set(value?.name, forKey: key)
}
func deserialize(_ object: Any) -> FrogCustomSerializable? {
guard let name = object as? String else { return nil }
return FrogCustomSerializable(name: name)
}
}
final class DefaultsFrogArrayBridge: DefaultsBridge {
func get(key: String, userDefaults: UserDefaults) -> [FrogCustomSerializable]? {
userDefaults.array(forKey: key)?
.compactMap { $0 as? String }
.map(FrogCustomSerializable.init)
}
func save(key: String, value: [FrogCustomSerializable]?, userDefaults: UserDefaults) {
let values = value?.map { $0.name }
userDefaults.set(values, forKey: key)
}
func deserialize(_ object: Any) -> [FrogCustomSerializable]? {
guard let names = object as? [String] else { return nil }
return names.map(FrogCustomSerializable.init)
}
}
struct FrogCustomSerializable: DefaultsSerializable, Equatable {
static var _defaults: DefaultsFrogBridge { DefaultsFrogBridge() }
static var _defaultsArray: DefaultsFrogArrayBridge { DefaultsFrogArrayBridge() }
let name: String
}
为了支持具有不同桥接器的现有类型,您可以类似地进行扩展。
extension Data: DefaultsSerializable {
public static var _defaultsArray: DefaultsArrayBridge<[T]> { DefaultsArrayBridge() }
public static var _defaults: DefaultsDataBridge { DefaultsDataBridge() }
}
另外,查看我们的源代码(或测试用例)以了解更多桥接器的示例。如果您对所有这些桥接器感到困惑,请 创建一个issue,我们将设法解决这个问题。
属性包装器
SwiftyUserDefaults为Swift 5.1提供了属性封装!属性封装@SwiftyUserDefault
提供了使用键路径和选项(缓存或观察)的能力。
缓存意味着我们将为您存储值,几乎不会去调用UserDefaults
,除非是第一次检索值。
观察意味着我们将通过KVO来观察您的属性,所以您不必担心它是否在其他地方保存过,并且使用缓存。
现在开始使用吧!给定的键
extension DefaultsKeys {
var userColorScheme: DefaultsKey<String> { .init("userColorScheme", defaultValue: "default") }
var userThemeName: DefaultsKey<String?> { .init("userThemeName") }
var userLastLoginDate: DefaultsKey<Date?> { .init("userLastLoginDate") }
}
您可以声明一个Settings
结构体
struct Settings {
@SwiftyUserDefault(keyPath: \.userColorScheme)
var userColorScheme: String
@SwiftyUserDefault(keyPath: \.userThemeName, options: .cached)
var userThemeName: String?
@SwiftyUserDefault(keyPath: \.userLastLoginDate, options: [.cached, .observed])
var userLastLoginDate: Date?
}
KVO
KVO支持所有为DefaultsSerializable
类型。然而,如果您有自定义类型,它需要在其中正确定义桥接和序列化。
要观察本地键的值
let nameKey = DefaultsKey<String>("name", defaultValue: "")
Defaults.observe(key: nameKey) { update in
// here you can access `oldValue`/`newValue` and few other properties
}
要观察在DefaultsKeys扩展中定义的键的值
Defaults.observe(\.nameKey) { update in
// here you can access `oldValue`/`newValue` and few other properties
}
默认情况下,我们使用[.old, .new]
选项来观察,但您可以提供您自己的选项
Defaults.observe(key: nameKey, options: [.initial, .old, .new]) { _ in }
KeyPath动态成员查找
SwiftyUserDefaults使KeyPath动态成员查找在Swift 5.1中变得可行!
extension DefaultsKeys {
var username: DefaultsKey<String?> { .init("username") }
var launchCount: DefaultsKey<Int> { .init("launchCount", defaultValue: 0) }
}
然后就可以使用了]
// Get and set user defaults easily
let username = Defaults.username
Defaults.hotkeyEnabled = true
// Modify value types in place
Defaults.launchCount += 1
Defaults.volume -= 0.1
Defaults.strings += "… can easily be extended!"
// Use and modify typed arrays
Defaults.libraries.append("SwiftyUserDefaults")
Defaults.libraries[0] += " 2.0"
// Easily work with custom serialized types
Defaults.color = NSColor.white
Defaults.color?.whiteComponent // => 1.0
启动参数
您喜欢通过UserDefaults来自定义应用程序/脚本/测试吗?现在我们全面支持这项功能,当然,支持静态类型。
注意:目前我们只支持Bool
、Double
、Int
、String
值,但如果您有其他对该功能的需求,请提交一个问题或PR,我们可以讨论在新的版本中实现它。
您可以在您的模式中传递参数
或者您可以使用XCUIApplication的启动参数
func testExample() {
let app = XCUIApplication()
app.launchArguments = ["-skipLogin", "true", "-loginTries", "3", "-lastGameTime", "61.3", "-nickname", "sunshinejr"]
app.launch()
}
或者将它们作为命令行参数传递!
./script -skipLogin true -loginTries 3 -lastGameTime 61.3 -nickname sunshinejr
工具
移除所有键
要重置用户默认值,请使用removeAll
方法。
Defaults.removeAll()
共享用户默认值
如果您在不同的应用程序或应用程序与其扩展之间共享用户默认值,您可以使用SwiftyUserDefaults通过用自己的重写Defaults
快捷方式。只需在您的应用程序中添加即可
var Defaults = DefaultsAdapter<DefaultsKeys>(defaults: UserDefaults(suiteName: "com.my.app")!, keyStore: .init())
检查键
如果您想检查我们是否为DefaultsKey
获取了值
let hasKey = Defaults.hasKey(\.skipLogin)
安装
需求
Swift 版本 >= 4.1
iOS 版本 >= 9.0
macOS 版本 >= 10.11
tvOS 版本 >= 9.0
watchOS 版本 >= 2.0
CocoaPods
如果您使用CocoaPods,只需在Podfile中添加以下行
pod 'SwiftyUserDefaults', '~> 5.0'
在终端运行此命令进行安装
pod install
然后导入所有使用它的文件中的库
import SwiftyUserDefaults
Carthage
只需将其添加到Cartfile中
github "sunshinejr/SwiftyUserDefaults" ~> 5.0
Swift包管理器
只需将其添加到 dependencies 下的 Package.swift
中
let package = Package(
name: "MyPackage",
products: [...],
dependencies: [
.package(url: "https://github.com/sunshinejr/SwiftyUserDefaults.git", .upToNextMajor(from: "5.0.0"))
],
targets: [...]
)
类似内容
喜欢 SwiftyUserDefaults 的话,可以看看 SwiftyTimer,它可以运用相同的 swifty 方法应用到 NSTimer
上。
您可能也对我的博客文章感兴趣,这些文章解释了那些库的设计过程。
贡献
如果您有评论、投诉或改进的想法,请随时提出问题或拉取请求。
作者和许可
维护者:Łukasz Mróz
创建者:Radek Pietruszewski
SwiftyUserDefaults遵循MIT许可。有关更多信息,请参阅LICENSE文件。