轻量级、严格的基于协议的 PropertyKit,可以帮助您轻松且安全地在各种情况下处理 iOS、macOS 和 tvOS 上大型 Swift 项目的保证值、键或类型。
安装
CocoaPods
pod 'PropertyKit'
Carthage
github "metasmile/PropertyKit"
Swift 包管理器
.Package(url: "https://github.com/metasmile/PropertyKit.git")
模块
- PropertyWatchable 用于 NSKeyValueObservation
- PropertyDefaults 用于 UserDefaults
- ~
PropertyDefaults
管理 UserDefaults 的最简单且可靠的方式。PropertyDefaults 会自动将 Swift 属性的值和数据类型绑定到 UserDefaults 的键和值。它强制采用仅关注语法驱动的值处理的协议扩展模式,因此有助于避免不安全的使用字符串键。因此,Swift 编译器将自然地保护所有缺失或重复的状态。
PropertyDefaults 是由 nmdias 提出的一个全新的 DefaultsKit 分支,采用完全不同的方法。
特性
- Swift 4 编解码支持
- 编译时对 UserDefaults 的保证
- 键、类型和值的关联安全性,不使用字符串字面量
- 结构扩展、协议驱动,而非意向式
- 权限控制
- 自动私有作用域 - 文件、类、结构体或函数内部
使用方法
用一个示例来定义带有基本 Codable 类型自动 UserDefaults 键
extension Defaults: PropertyDefaults {
public var autoStringProperty: String? {
set{ set(newValue) } get{ return get() }
}
public var autoDateProperty: Date? {
set{ set(newValue) } get{ return get() }
}
}
var sharedDefaults = Defaults()
sharedDefaults.autoStringProperty = "the new value will persist in shared scope"
// sharedDefaults.autoStringProperty == Defaults.shared.autoStringProperty
Defaults.shared.autoStringProperty = "another new value will persist in shared scope"
// Defaults.shared.autoStringProperty == sharedDefaults.autoStringProperty
var localDefaults = Defaults(suiteName:"local")
localDefaults.autoStringProperty = "the new value will persist in local scope"
// localDefaults.autoStringProperty != Defaults.shared.autoStringProperty
直接以 Codable 类型保存/加载
public struct CustomValueType: Codable{
var key:String = "value"
var date:Date?
var data:Data?
}
extension Defaults: PropertyDefaults {
// non-optional - must define the default value with the keyword 'or'
public var autoCustomNonOptionalProperty: CustomValueType {
set{ set(newValue) } get{ return get(or: CustomValueType()) }
}
// optional with/without setter default value
public var autoCustomOptionalProperty: CustomValueType? {
set{ set(newValue) } get{ return get() }
}
public var autoCustomOptionalPropertySetterDefaultValue: CustomValueType? {
set{ set(newValue, or: CustomValueType()) } get{ return get() }
}
}
通过 Swift 编译器强保证唯一密钥。
//CodeFile1_ofLargeProject.swift
protocol MyDefaultsKeysUsingInA : PropertyDefaults{
var noThisIsMyKeyNotYours:Int?{ get }
}
extension Defaults : MyDefaultsKeysUsingInA{
var noThisIsMyKeyNotYours:Int?{ set{ set(newValue) } get{ return get() } }
}
//CodeFile2_ofLargeProject.swift
protocol MyDefaultsKeysUsingInB : PropertyDefaults{
var noThisIsMyKeyNotYours:Int?{ get }
}
extension Defaults : MyDefaultsKeysUsingInB{
var noThisIsMyKeyNotYours:Int?{ set{ set(newValue) } get{ return get() } }
}
❗️Swift Compiler Error
~.swift:30:9: Invalid redeclaration of 'noThisIsMyKeyNotYours'
~.swift:21:9: 'noThisIsMyKeyNotYours' previously declared here
按照这种模式,正如您所知,您还可以通过协议来控制访问权限。这意味着您可以使用 'private' 或 'file-private' 默认访问修饰符。
// MyFile.swift
fileprivate protocol PrivateDefaultKeysInThisSwiftFile: PropertyDefaults{
var filePrivateValue: String? {set get}
}
extension Defaults: PrivateDefaultKeysInThisSwiftFile {
public var filePrivateValue: String? {
set{ set(newValue) } get{ return get() }
}
}
// Can access - 👌
Defaults.shared.filePrivateValue
// MyOtherFile.swift
// Not able to access - ❌
Defaults.shared.filePrivateValue
是的,这是一种打破我们的设计意图的方法。
var p1:Int{
Defaults.shared.set(2)
return Defaults.shared.get(or:0)
}
var p2: Int{
return Defaults.shared.get(or:0)
}
p1 // == 2
p2 // == 0
//It means that are function/property-scoped capsulated defaults values.
PropertyWatchable
基于 NSKeyValueObservation 的协议扩展。它简单地允许一个类对象成为类型安全的 keypath 观察对象。所有观察者都将自动分配一个唯一的观察者标识符,从而防止重复的回调调用,并可以让你原子性地管理连接队列之间的关键值流。
特性
- 仅使用协议即可创建观察对象。
- Swift 属性字面量基于的 keypath 观察方式。
- 严格保证回调参数的类型。
- 支持自动唯一标识符。
- 支持文件范围观察者移除。
- 支持队列私有的原子操作。
使用方法
使用它的最简单的例子。
class WatchableObject:NSObject, PropertyWatchable{
@objc dynamic
var testingProperty:String?
}
let object = WatchableObject()
object.watch(\.testingProperty) {
object.testingProperty == "some value"
//Do Something.
}
object.testingProperty = "some value"
所有选项和强类型参数都与 NSKeyValueObservation 相同。
// Default option is the default of NSKeyValueObservation (.new)
// (WatchableObject, NSKeyValueObservedChange<Value>)
object.watch(\.testingProperty, options: [.initial, .new, .old]) { (target, changes) in
target.testingProperty == "some value"
//Dd Something.
}
let object = WatchableObject()
object.testingProperty = "initial value"
config.watch(\.testingProperty, options: [.initial]) { (o, _) in
o.testingProperty == "initial value"
}
支持自动按行生成标识符。
object.watch(\.testingProperty) {
// Listening as a unique observer 1
}
object.watch(\.testingProperty) {
// Listening as a unique observer 2
}
object.watch(\.testingProperty, id:"myid", options:[.old]) {
// Listening as an observer which has identifier "myid"
}
// total 3 separated each observers are listening each callbacks.
支持使用不同选项来移除观察。
// Remove only an observer which has "myid" only
object.unwatch(\.testingProperty, forIds:["myid"])
// Remove all observers that are watching ".testingProperty"
object.unwatch(\.testingProperty)
//Automatically remove all observers in current file.
object.unwatchAllFilePrivate()
//Automatically remove entire observers in application-wide.
object.unwatchAll()