测试已测试 | ✓ |
Lang语言 | SwiftSwift |
许可证 | MIT |
发布最新发布 | 2017年10月 |
SwiftSwift 版本 | 3.0 |
SPM支持 SPM | ✗ |
由 Matthew Palmer 维护。
一个强大的、基于协议的库,可帮助您在 Swift 中使用钥匙串。
🚀 我制作了一个应用 Rocket,您可以在 Mac 上的任何地方使用 Slack 风格的表情符号。
锁匠与其他钥匙串包装有何不同?
想要了解更多关于锁匠设计的细节吗?我在 Swift 中的面向协议编程 上写了一篇博文。
swift-2.3
分支以了解与旧版本 Swift 的兼容性。保存数据
try Locksmith.saveData(["some key": "some value"], forUserAccount: "myUserAccount")
加载数据
let dictionary = Locksmith.loadDataForUserAccount("myUserAccount")
更新数据
try Locksmith.updateData(["some key": "another value"], forUserAccount: "myUserAccount")
删除数据
try Locksmith.deleteDataForUserAccount("myUserAccount")
锁匠是为 Swift 2、协议和协议扩展而设计的。
为什么要这样做?因为您只需进行最小的更改即可向现有类型添加现有功能!
假设我们有一个 Twitter 账户
struct TwitterAccount {
let username: String
let password: String
}
我们想要将其保存到钥匙串中作为通用密码。我们只需在锁匠中遵守正确的协议即可,我们就可以免费获得该功能。
struct TwitterAccount: CreateableSecureStorable, GenericPasswordSecureStorable {
let username: String
let password: String
// Required by GenericPasswordSecureStorable
let service = "Twitter"
var account: String { return username }
// Required by CreateableSecureStorable
var data: [String: AnyObject] {
return ["password": password]
}
}
现在我们能够将我们的账户保存在钥匙串中了。
let account = TwitterAccount(username: "_matthewpalmer", password: "my_password")
try account.createInSecureStore()
创建、读取和删除各自都有自己的协议:CreateableSecureStorable
、ReadableSecureStorable
、和 DeleteableSecureStorable
。最好的部分是?
您可以在同一类型上遵守所有三个协议!
struct TwitterAccount: ReadableSecureStorable,
CreateableSecureStorable,
DeleteableSecureStorable,
GenericPasswordSecureStorable {
let username: String
let password: String
let service = "Twitter"
var account: String { return username }
var data: [String: AnyObject] {
return ["password": password]
}
}
let account = TwitterAccount(username: "_matthewpalmer", password: "my_password")
// CreateableSecureStorable lets us create the account in the keychain
try account.createInSecureStore()
// ReadableSecureStorable lets us read the account from the keychain
let result = account.readFromSecureStore()
// DeleteableSecureStorable lets us delete the account from the keychain
try account.deleteFromSecureStore()
真的很酷。
通过声明您的类型采用这些协议——正如我们上面通过 struct TwitterAccount: CreateableSecureStorable, ...
所做的——您将免费获得一系列功能。
我喜欢从“你得到什么”、“你必须要做什么”和“什么可选”的角度来考虑具有扩展的协议。大部分在“可选”类别下的内容,只有在你想改变现有功能时才应该实施。
CreateableSecureStorable
你得到什么
// Saves a type to the keychain
func createInSecureStore() throws
必需的
// The data to save to the keychain
var data: [String: AnyObject] { get }
可选的
// Perform the request in this closure
var performCreateRequestClosure: PerformRequestClosureType { get }
ReadableSecureStorable
你得到什么
// Read from the keychain
func readFromSecureStore() -> SecureStorableResultType?
必需的
没有任何事物!
可选的
// Perform the request in this closure
var performReadRequestClosure: PerformRequestClosureType { get }
DeleteableSecureStorable
你得到什么
// Read from the keychain
func deleteFromSecureStore() throws
必需的
没有任何事物!
可选的
// Perform the request in this closure
var performDeleteRequestClosure: PerformRequestClosureType { get }
许多对keychain进行封装的库只支持API的部分内容。这是因为你可以查询keychain的方式有如此多的选项和变体,很难有效地进行抽象。
Locksmith试图尽可能多地包含keychain的内容,使用协议和协议扩展来最小化复杂性。你可以混合使用通用密码和读取请求,同时保持完全的类型安全。
请参考Keychain Services参考获取关于每个属性含义及其可以进行什么操作的完整信息。
证书、密钥和身份都是可能的,只是将
kSec...
常量进行转换而已!
GenericPasswordSecureStorable
通用密码可能是keychain最常见的使用场景,非常适合存储用户名和密码。
在“必需的”下列出的事项必须由符合的类型实现;在“可选的”下列出的事项如果需要可以实施,以在保存或读取时添加额外信息。
值得注意的是:如果你实现了一个可选属性,其类型注解必须与协议中指定的类型完全一致。如果你实现了description: String?
,就不能将其声明为var description: String
。
必需的
var account: String { get }
var service: String { get }
可选的
var comment: String? { get }
var creator: UInt? { get }
var description: String? { get }
var generic: NSData? { get }
var isInvisible: Bool? { get }
var isNegative: Bool? { get }
var label: String? { get }
var type: UInt? { get }
InternetPasswordSecureStorable
符合InternetPasswordSecureStorable
的类型通常来自网络服务,并具有某些关联的元数据。
必需的
var account: String { get }
var authenticationType: LocksmithInternetAuthenticationType { get }
var internetProtocol: LocksmithInternetProtocol { get }
var port: String { get }
var server: String { get }
可选的
var comment: String? { get }
var creator: UInt? { get }
var description: String? { get }
var isInvisible: Bool? { get }
var isNegative: Bool? { get }
var path: String? { get }
var securityDomain: String? { get }
var type: UInt? { get }
通过从头开始采用面向协议的设计,Locksmith可以提供带有类型注解的结果对keychain查询的访问——存储一个NSDate
,获取一个没有类型转换的NSDate
!
让我们从之前提到的Twitter账号的例子开始,但现在它变成了一个InternetPasswordSecureStorable
,这让我们能够存储更多的元数据。
struct TwitterAccount: InternetPasswordSecureStorable,
ReadableSecureStorable,
CreateableSecureStorable {
let username: String
let password: String
var account: String { return username }
var data: [String: AnyObject] {
return ["password": password]
}
let server = "com.twitter"
let port = 80
let internetProtocol = .HTTPS
let authenticationType = .HTTPBasic
let path: String? = "/api/2.0/"
}
let account = TwitterAccount(username: "_matthewpalmer", password: "my_password")
// Save all this to the keychain
account.createInSecureStore()
// Now let’s get it back
let result: InternetPasswordSecureStorableResultType = account.readFromSecureStore()
result?.port // Gives us an Int directly!
result?.internetProtocol // Gives us a LocksmithInternetProtocol enum case directly!
result?.data // Gives us a [String: AnyObject] of what was saved
// and so on...
这真是太棒了!不再需要类型转换。
GenericPasswordSecureStorableResultType
这里列出的所有内容都可以设置在符合GenericPasswordSecureStorable
的类型上,并且可以从该类型返回的readFromSecureStore()
的结果中获取。
var account: String { get }
var service: String { get }
var comment: String? { get }
var creator: UInt? { get }
var description: String? { get }
var data: [String: AnyObject]? { get }
var generic: NSData? { get }
var isInvisible: Bool? { get }
var isNegative: Bool? { get }
var label: String? { get }
var type: UInt? { get }
InternetPasswordSecureStorableResultType
这里列出的所有内容都可以设置在符合InternetPasswordSecureStorable
的类型上,并且可以从该类型返回的readFromSecureStore()
的结果中获取。
var account: String { get }
var authenticationType: LocksmithInternetAuthenticationType { get }
var internetProtocol: LocksmithInternetProtocol { get }
var port: Int { get }
var server: String { get }
var comment: String? { get }
var creator: UInt? { get }
var data: [String: AnyObject]? { get }
var description: String? { get }
var isInvisible: Bool? { get }
var isNegative: Bool? { get }
var path: String? { get }
var securityDomain: String? { get }
var type: UInt? { get }
锁匠提供了一系列便捷的枚举类型,以便您配置请求,您可以这样表示:kSecGoodByeStringConstants
。
LocksmithAccessibleOption
LocksmithAccessibleOption
用于配置何时可以访问一项内容——您可能需要当设备解锁、输入密码后等情况下项内容可用。
public enum LocksmithAccessibleOption {
case AfterFirstUnlock
case AfterFirstUnlockThisDeviceOnly
case Always
case AlwaysThisDeviceOnly
case WhenPasscodeSetThisDeviceOnly
case WhenUnlocked
case WhenUnlockedThisDeviceOnly
}
LocksmithError
LocksmithError
提供了对常用的钥匙串错误代码的Swift友好翻译。这些错误在库中的方法中抛出。Apple的文档提供了更多关于这些错误的信息。
public enum LocksmithError: ErrorType {
case Allocate
case AuthFailed
case Decode
case Duplicate
case InteractionNotAllowed
case NoError
case NotAvailable
case NotFound
case Param
case RequestNotSet
case TypeNotFound
case UnableToClear
case Undefined
case Unimplemented
}
LocksmithInternetAuthenticationType
LocksmithInternetAuthenticationType
允许您选择要存储在.InternetPassword
旁边进行身份验证的类型——从.MSN
到.HTTPDigest
不等。Apple的文档提供了更多关于这些值的详细信息。
public enum LocksmithInternetAuthenticationType {
case Default
case DPA
case HTMLForm
case HTTPBasic
case HTTPDigest
case MSN
case NTLM
case RPA
}
LocksmithInternetProtocol
LocksmithInternetProtocol
与.InternetPassword
一起使用,以选择与Web服务交互时使用的协议,包括.HTTP
、.SMB
等。Apple的文档提供了更多关于这些值的详细信息。
public enum {
case AFP
case AppleTalk
case DAAP
case EPPC
case FTP
case FTPAccount
case FTPProxy
case FTPS
case HTTP
case HTTPProxy
case HTTPS
case HTTPSProxy
case IMAP
case IMAPS
case IPP
case IRC
case IRCS
case LDAP
case NNTP
case NNTPS, LDAPS
case POP3
case POP3S
case RTSP
case RTSPProxy
case SMB
case SMTP
case SOCKS
case SSH
case Telnet
case TelnetS
}
Matthew Palmer,[email protected]
锁匠可在MIT许可证下使用。有关更多信息,请参阅LICENSE文件。