使用 Swift 属性包装器 帮助你为属性定义安全存储。
所有键都使用 SHA512 进行哈希处理,所有值都使用 AES-GCM 进行加密,以确保用户信息安全,自动魔术般的完成。对称密钥以完全安全的方式存储在 Keychain 中。
此属性包装器将使用 StoreKey
(任何 String
类型,但我建议使用 String 类型的枚举)将你的属性存储在 UserDefaults 中。你可以选择为属性分配一个默认值,该默认值将在初始化时进行安全存储。
@UserDefault(<#StoreKey#>)
var yourProperty: YourType? = yourDefaultValueIfNeeded
UserDefaultsStorage
也可用,它是 UserDefaults
的子类,具有此库提供的所有安全性,你可以在其中自定义套件名称。
此属性包装器将使用 StoreKey
将你的属性存储在 Keychain 中。
@Keychain(<#StoreKey#>)
var yourProperty: YourType? = yourDefaultValueIfNeeded
与 UserDefaultsStorage
一样,KeychainStorage
也可用,你可以在其中自定义访问权限、组并将它与 iCloud 同步。
此属性包装器将你的属性存储在内存 单例中,每个具有相同包装器和键的属性都可以从任何位置访问或修改该值。
@Singleton(<#StoreKey#>)
var yourProperty: YourType? = yourDefaultValueIfNeeded
与 KeychainStorage
一样,SingletonStorage
也可用。
此属性包装器与 @Singleton
类似,但与 @Register
一起,它将注入你的依赖项。更多详细信息请参考 依赖注入用法 指南。
@Inject
var yourDependency: YourProtocol?
与 SingletonStorage
一样,InjectStorage
也可用。
这是一个自定义包装器,你可以定义自己的 Storage
协议实现。
@Store(<#YourStorage#>, <#StoreKey#>)
var yourProperty: YourType? = yourDefaultValueIfNeeded
与 InjectStorage
一样,DelegatedStorage
也可用,它具有此库的所有魔力。
如果你的属性符合 Codable
协议,只需将 Codable
关键字添加为属性包装器的前缀即可。
为了避免不断解包你的属性,只需将 Unwrapped
关键字添加为属性包装器的前缀,分配一个默认值(除了 @UnwrappedInject
之外,这是强制性的),它将返回存储的值或默认值,但你的属性将始终存在为你服务。
如果需要,你也可以组合之前的案例,请先解包。
此属性包装器将注册你的依赖项的实现。在注入依赖项之前,在任何你想要的地方注册它们,但请确保只注册一次(除非你使用限定符),例如,在一个 Injector
类中。你可以通过协议注册,也可以直接使用你的类实现。
@Register
var yourDependency: YourProtocol = YourImplementation()
@Register
var yourDependency = YourImplementation()
你还可以定义一个构建你的依赖项的闭包。请记住,如果你要通过协议注入依赖项,请强制转换你的依赖项。
@Register
var yourDependency = {
YourImplementation() as YourProtocol
}
@Register
var yourDependency = {
YourImplementation()
}
你也可以仅在有人尝试注入你的依赖项但你尚未注册它们之后才注册它们,为此你可以使用错误闭包。
InjectStorage.standard.errorClosure = { error in
if case InjectError.notFound = error {
YourImplementation.register()
}
}
你可以获得这种语法糖,因为你现在可以在函数参数中使用属性包装器。
static func register(@Register yourDependency: YourProtocol = YourImplementation()) {}
这些属性包装器注入你的依赖项 @Register
实现。
@Inject
var yourDependency: YourProtocol?
@Inject
var yourDependency: YourImplementation?
@UnwrappedInject
var yourUnwrappedDependency: YourProtocol
@UnwrappedInject
var yourUnwrappedDependency: YourImplementation
由于这些属性包装器的工作方式与 @Singleton
类似,因此默认作用域是 .singleton
,但如果你在 @Register
上使用构建器闭包,你可以修改它们以注入单个实例。
@Inject(.instance)
var yourDependency: YourProtocol?
@UnwrappedInject(.instance)
var yourUnwrappedDependency: YourProtocol
你的依赖项在注入时可能需要参数,你可以使用这些属性包装器传递它们。只需定义一个包含你的依赖项参数的模型并传递它即可。它将注入一个使用这些参数构建的新实例。
@Register
var yourDependency = { parameters in
YourImplementation(parameters) as YourProtocol
}
@Inject(YourParameters())
var yourDependency: YourProtocol?
@UnwrappedInject(YourParameters())
var yourUnwrappedDependency: YourProtocol
你可以使用 限定符 来提供特定依赖项的各种实现。限定符只是一个应用于 class
的 @objc protocol
。
例如,你可以声明 Dog
和 Cat
限定符协议并将它应用到另一个符合 Animal
协议的类。要声明此限定符,请使用以下代码
protocol Animal {
func sound()
}
@objc protocol Dog {}
@objc protocol Cat {}
然后,你可以定义多个符合 Animal
协议并使用此限定符的类
class DogImplementation: Animal, Dog {
func sound() { print("Woof!") }
}
class CatImplementation: Animal, Cat {
func sound() { print("Meow!") }
}
该类的两个实现现在都可以 @Register
@Register
var registerDog: Animal = DogImplementation()
@Register
var registerCat: Animal = CatImplementation()
要注入其中一个或另一个实现,只需将限定符添加到你的 @Inject
@UnwrappedInject(Dog.self)
var dog: Animal
@UnwrappedInject(Cat.self)
var cat: Animal
dog.sound() // prints Woof!
cat.sound() // prints Meow!
依赖注入的优势之一是可以使用模拟实现轻松测试代码。这就是为什么有一个 Mock
限定符具有高于一切的优先级,因此你可以将你的依赖项定义在应用程序中,并通过简单地添加此限定符在测试目标中创建你的模拟。
// App target
class YourImplementation: YourProtocol {}
@Register
var yourDependency: YourProtocol = YourImplementation()
@Inject
var yourDependency: YourProtocol?
// Test target
class YourMock: YourProtocol, Mock {}
@Register
var yourDependency: YourProtocol = YourMock()
当你的应用程序中有很多依赖项时,你可能想要优化依赖项解析。
你可以使用 @Register(group:)
和 DependencyGroupKey
对它们进行分组
@Register(group: <#DependencyGroupKey#>)
var yourDependency: YourProtocol = YourImplementation()
@Inject(group:)
将仅在该组中查找这些依赖项
@Inject(group: <#DependencyGroupKey#>)
var yourDependency: YourProtocol?
空谈无益,放码过来。
// Securely stored in UserDefaults.
@UserDefault("username")
var username: String?
// Securely stored in Keychain.
@Keychain("password")
var password: String?
// Securely stored in a Singleton storage.
@Singleton("sessionToken")
var sessionToken: String?
// Securely stored in a Singleton storage.
// Always has a value, the stored or the default.
@UnwrappedSingleton("refreshToken")
var refreshToken: String = "B0610306-A33F"
struct User: Codable {
let username: String
let password: String?
let sessionToken: String?
}
// Codable model securely stored in UserDefaults.
@CodableUserDefault("user")
var user: User?
Package.swift
文件中将 SecurePropertyStorage 声明为依赖项.package(url: "https://github.com/alexruperez/SecurePropertyStorage", from: "0.7.1")
默认情况下,所有属性包装器都已安装,你可以 import
它们,但如果你愿意,你可以只安装其中的一些
有关更多信息,请参阅 Swift Package Manager 文档。
github "alexruperez/SecurePropertyStorage"
Alex Rupérez – @alexruperez – me@alexruperez.com
SecurePropertyStorage 在 MIT 许可下可用。有关更多信息,请参见 LICENSE 文件。