锁匠 4.0.0

锁匠 4.0.0

测试已测试
Lang语言 SwiftSwift
许可证 MIT
发布最新发布2017年10月
SwiftSwift 版本3.0
SPM支持 SPM

Matthew Palmer 维护。



锁匠 4.0.0

锁匠

一个强大的、基于协议的库,可帮助您在 Swift 中使用钥匙串。

  • [x]📱iOS 8.0+
  • [x]💻Mac OS X 10.10+
  • [x]⌚️watchOS 2
  • [x]📺tvOS

 

🚀我制作了一个应用 Rocket,您可以在 Mac 上的任何地方使用 Slack 风格的表情符号。

 

详细信息

锁匠与其他钥匙串包装有何不同?

  • 锁匠的 API 既简单又强大
  • 提供对所有钥匙串元数据的访问,具有强类型结果
  • 免费向现有类型添加功能
  • 有用的枚举和 Swift 原生类型

想要了解更多关于锁匠设计的细节吗?我在 Swift 中的面向协议编程 上写了一篇博文。

安装

  • Locksmith 3.0 及更高版本与 Swift 3 兼容。查看 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()

创建、读取和删除各自都有自己的协议:CreateableSecureStorableReadableSecureStorable、和 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 }

对Cocoa Keychain的强大支持

许多对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文件。