测试已检测 | ✗ |
语言语言 | SwiftSwift |
许可证 | MIT |
发布上次发布 | 2017年10月 |
SwiftSwift 版本 | 4.0 |
SPM支持 SPM | ✗ |
由 Kazunobu Tasaka 和 kitoko552 维护。
TypedDefaults
是一个用于在类型安全的情况下使用 NSUserDefaults 的实用库。
保持冷静并大量使用类型擦除 这篇由 Gwendolyn Weston 在 try! Swift 2016 上发表的演讲非常棒,它启发了我将 类型擦除 技巧应用在应用开发的具体案例中。
使用 CocoaPods 安装
use_frameworks!
platform :ios, '8.0'
pod 'TypedDefaults'
自定义类型可以安全地存储在 NSUserDefaults 中。
自定义类型只需要采用后面描述的 DefaultsConvertible
协议,无需继承 NSObject。
因此,Swift 原生 Class、Struct 和 Enum 都可用于自定义类型。(当然,也可用 NSObject 的子类。)
DefaultsConvertible
协议public protocol DefaultConvertible {
static var key: String { get }
init?(_ object: AnyObject)
func serialize() -> AnyObject
}
自定义类型作为 AnyObject 存储在 NSUserDefaults 中。
serialize()
在保存时被调用,而 init?(_ object:)
在从 NSUserDefaults 中获取时被调用。
假设每个自定义类型和应用程序中使用的每个配置是一对一的关系。
因此,key
作为类型属性准备,以便将 key
分配给一个 custom type
。
这是一个以 保存照片到 CameraRoll 的标志
和 照片大小
作为相机配置的自定义类型的例子。
struct CameraConfig: DefaultConvertible {
enum Size: Int {
case Large, Medium, Small
}
var saveToCameraRoll: Bool
var size: Size
// MARK: DefaultConvertible
static let key = "CameraConfig"
init?(_ object: AnyObject) {
guard let dict = object as? [String: AnyObject] else {
return nil
}
self.saveToCameraRoll = dict["cameraRoll"] as? Bool ?? true
if let rawSize = dict["size"] as? Int,
let size = Size(rawValue: rawSize) {
self.size = size
} else {
self.size = .Medium
}
}
func serialize() -> AnyObject {
return ["cameraRoll": saveToCameraRoll, "size": size.rawValue]
}
}
PersistentStore
是一个类,用于将自定义类型保存到 NSUserDefaults。
以下是使用示例。
/// Specify a custom type when initializing PersistentStore
let userDefaults = PersistentStore<CameraConfig>()
// Make an instance of CameraConfig
var cs = CameraConfig([:])!
// Set
userDefaults.set(cs)
// Get
userDefaults.get()?.size // Medium
/// Change the size
cs.size = .Large
// Set
userDefaults.set(cs)
// Get
userDefaults.get()?.size // Large
NSUserDefaults 不适合单元测试,因为它在文件系统中持续存储数据。
TypedDefaults
提供了 InMemoryStore
和 AnyStore
类型,用于依赖注入,以便测试基于存储在 NSUserDefaults 中的自定义类型的不同行为。
InMemoryStore
除了继承 PersistentStore
,还采用了 DefaultStoreType
协议。
然而,InMemoryStore
仅保留自定义类型在内存中,与 PersistentStore
不同。
对于 AnyStore
,它是对 PersistentStore
和 InMemoryStore
的抽象类型。
以下示例展示了在单元测试中使用 InMemoryStore
和 AnyStore
而不是 PersistentStore
。
有一个名为 CameraViewController
的类,它继承自 UIViewController。
它有一个 config
属性,用于保留保存在 NSUserDefaults 中的自定义类型。为了支持依赖注入,将 config
的类型设置为 AnyStore
。
class CameraViewController: UIViewController {
lazy var config: AnyStore<CameraConfig> = {
let ds = PersistentStore<CameraConfig>()
return AnyStore(ds)
}()
...
}
因为 config
的类型不是 PersistentStore
但 AnyStore
,所以可以在单元测试中将其替换为 InMemoryStore
,如下所示。
class CameraViewControllerTests: XCTestCase {
var viewController: CameraViewController!
override func setUp() {
viewController = CameraViewController()
let defaultConfig = CameraConfig([:])!
let ds = InMemoryStore<CameraConfig>()
ds.set(defaultConfig) //
viewController.config = AnyStore(ds)
}
}
见 https://github.com/tasanobu/TypedDefaults/releases
TypedDefaults
使用 MIT 许可证发布。请参见 LICENSE 以获取详细信息。