[{"data":1,"prerenderedAt":-1},["ShallowReactive",2],{"project-7052":3},{"id":4,"name":5,"fullName":6,"owner":7,"repo":5,"description":8,"homepage":9,"htmlUrl":10,"language":11,"languages":10,"totalLinesOfCode":10,"stars":12,"forks":13,"watchers":14,"openIssues":15,"contributorsCount":16,"subscribersCount":16,"size":16,"stars1d":16,"stars7d":16,"stars30d":16,"stars90d":16,"forks30d":16,"starsTrendScore":16,"compositeScore":17,"rankGlobal":10,"rankLanguage":10,"license":18,"archived":19,"fork":19,"defaultBranch":20,"hasWiki":21,"hasPages":19,"topics":22,"createdAt":10,"pushedAt":10,"updatedAt":23,"readmeContent":24,"aiSummary":25,"trendingCount":16,"starSnapshotCount":16,"syncStatus":26,"lastSyncTime":27,"discoverSource":28},7052,"Locksmith","matthewpalmer\u002FLocksmith","matthewpalmer","A powerful, protocol-oriented library for working with the keychain in Swift.","",null,"Swift",2915,269,56,47,0,59.29,"MIT License",false,"master",true,[],"2026-06-12 04:00:32","# Locksmith\n\nA powerful, protocol-oriented library for working with the keychain in Swift.\n\n- [x] 📱 iOS 8.0+\n- [x] 💻 Mac OS X 10.10+\n- [x] ⌚️ watchOS 2\n- [x] 📺 tvOS\n\n> &nbsp;\n>\n> 🚀 I make [Rocket](http:\u002F\u002Fmatthewpalmer.net\u002Frocket?utm_source=locksmith&utm_medium=readme&utm_campaign=open_source), an app that gives you Slack-style emoji everywhere on your Mac.\n>\n> &nbsp;\n\n## Details\n\nHow is Locksmith different to other keychain wrappers?\n\n* Locksmith’s API is both super-simple and deeply powerful\n* Provides access to all of the keychain’s metadata with strongly typed results\n* Add functionality to your existing types for free\n* Useful enums and Swift-native types\n\n> Want to read more about Locksmith’s design? I wrote a blog post on [protocol oriented programming in Swift](http:\u002F\u002Fmatthewpalmer.net\u002Fblog\u002F2015\u002F08\u002F30\u002Fprotocol-oriented-programming-in-the-real-world\u002F).\n\n## Installation\n\n[![Version](https:\u002F\u002Fimg.shields.io\u002Fcocoapods\u002Fv\u002FLocksmith.svg?style=flat)](http:\u002F\u002Fcocoadocs.org\u002Fdocsets\u002FLocksmith)\n[![Carthage compatible](https:\u002F\u002Fimg.shields.io\u002Fbadge\u002FCarthage-compatible-4BC51D.svg?style=flat)](https:\u002F\u002Fgithub.com\u002FCarthage\u002FCarthage)\n[![Build Status](https:\u002F\u002Ftravis-ci.org\u002Fmatthewpalmer\u002FLocksmith.svg?branch=master)](https:\u002F\u002Ftravis-ci.org\u002Fmatthewpalmer\u002FLocksmith)\n\n* Locksmith 4.0 and greater is Swift 4 compatible. See the `swift-3.1` branch for compatibility with older versions of Swift.\n\n### CocoaPods\n\nLocksmith is available through [CocoaPods](http:\u002F\u002Fcocoapods.org).\n\n    pod 'Locksmith'\n\n### Carthage\n\nLocksmith is available through [Carthage](https:\u002F\u002Fgithub.com\u002FCarthage\u002FCarthage).\n\n    github \"matthewpalmer\u002FLocksmith\"\n\n## Quick start\n\n### **Setup**\n1. Choose your target project\n2. Select Capabilties\n3. Enable Keychain Sharing\n\nXcode then automatically creates a .entitlements file and you are ready to use Locksmith!\n\n**Save data**\n\n```swift\ntry Locksmith.saveData([\"some key\": \"some value\"], forUserAccount: \"myUserAccount\")\n```\n\n**Load data**\n\n```swift\nlet dictionary = Locksmith.loadDataForUserAccount(\"myUserAccount\")\n```\n\n**Update data**\n\n- as well as replacing existing data, this writes data to the keychain if it does not exist already\n\n```swift\ntry Locksmith.updateData([\"some key\": \"another value\"], forUserAccount: \"myUserAccount\")\n```\n\n**Delete data**\n\n```swift\ntry Locksmith.deleteDataForUserAccount(\"myUserAccount\")\n```\n\n## Power to the protocols\n\nLocksmith has been designed with Swift 2, protocols, and protocol extensions in mind.\n\nWhy do this? Because you can add existing functionality to your types with only the slightest changes!\n\nSay we have a Twitter account\n\n```swift\nstruct TwitterAccount {\n  let username: String\n  let password: String\n}\n```\n\nand we want to save it to the keychain as a generic password. All we need to do is conform to the right protocols in Locksmith and we get that functionality for free.\n\n```swift\nstruct TwitterAccount: CreateableSecureStorable, GenericPasswordSecureStorable {\n  let username: String\n  let password: String\n\n  \u002F\u002F Required by GenericPasswordSecureStorable\n  let service = \"Twitter\"\n  var account: String { return username }\n\n  \u002F\u002F Required by CreateableSecureStorable\n  var data: [String: AnyObject] {\n    return [\"password\": password]\n  }\n}\n```\n\nNow we get the ability to save our account in the keychain.\n\n```swift\nlet account = TwitterAccount(username: \"_matthewpalmer\", password: \"my_password\")\ntry account.createInSecureStore()\n```\n\nCreating, reading, and deleting each have their own protocols: `CreateableSecureStorable`, `ReadableSecureStorable`, and `DeleteableSecureStorable`. And the best part?\n\n**You can conform to all three protocols on the same type!**\n\n```swift\nstruct TwitterAccount: ReadableSecureStorable,\n                       CreateableSecureStorable,\n                       DeleteableSecureStorable,\n                       GenericPasswordSecureStorable {\n  let username: String\n  let password: String\n\n  let service = \"Twitter\"\n  var account: String { return username }\n  var data: [String: AnyObject] {\n    return [\"password\": password]\n  }\n}\n\nlet account = TwitterAccount(username: \"_matthewpalmer\", password: \"my_password\")\n\n\u002F\u002F CreateableSecureStorable lets us create the account in the keychain\ntry account.createInSecureStore()\n\n\u002F\u002F ReadableSecureStorable lets us read the account from the keychain\nlet result = account.readFromSecureStore()\n\n\u002F\u002F DeleteableSecureStorable lets us delete the account from the keychain\ntry account.deleteFromSecureStore()\n```\n\nSo. cool.\n\n### The details\n\nBy declaring that your type adopts these protocols—which is what we did above with `struct TwitterAccount: CreateableSecureStorable, ...`—you get a bunch of functionality for free.\n\nI like to think about protocols with extensions in terms of “what you get,” “what you’ve gotta do,” and “what’s optional.” Most of the stuff under ‘optional’ should only be implemented if you want to change existing functionality.\n\n#### `CreateableSecureStorable`\n\n**What you get**\n\n```swift\n\u002F\u002F Saves a type to the keychain\nfunc createInSecureStore() throws\n```\n\n**Required**\n\n```swift\n\u002F\u002F The data to save to the keychain\nvar data: [String: AnyObject] { get }\n```\n\n**Optional**\n\n```swift\n\u002F\u002F Perform the request in this closure\nvar performCreateRequestClosure: PerformRequestClosureType { get }\n```\n\n#### `ReadableSecureStorable`\n\n**What you get**\n\n```swift\n\u002F\u002F Read from the keychain\nfunc readFromSecureStore() -> SecureStorableResultType?\n```\n\n**Required**\n\n> Nothing!\n\n**Optional**\n\n```swift\n\u002F\u002F Perform the request in this closure\nvar performReadRequestClosure: PerformRequestClosureType { get }\n```\n\n#### `DeleteableSecureStorable`\n\n**What you get**\n\n```swift\n\u002F\u002F Read from the keychain\nfunc deleteFromSecureStore() throws\n```\n\n**Required**\n\n> Nothing!\n\n**Optional**\n\n```swift\n\u002F\u002F Perform the request in this closure\nvar performDeleteRequestClosure: PerformRequestClosureType { get }\n```\n\n## Powerful support for the Cocoa Keychain\n\nMany wrappers around the keychain have only support certain parts of the API.  This is because there are so many options and variations on the way you can query the keychain that it’s almost impossible to abstract effectively.\n\nLocksmith tries to include as much of the keychain as possible, using protocols and protocol extensions to minimize the complexity. You can mix-and-match your generic passwords with your read requests while staying completely type-safe.\n\nPlease refer to the [Keychain Services Reference](https:\u002F\u002Fdeveloper.apple.com\u002Flibrary\u002Fios\u002Fdocumentation\u002FSecurity\u002FReference\u002Fkeychainservices\u002F) for full information on what each of the attributes mean and what they can do.\n\n> Certificates, keys, and identities are possible—it’s just a matter of translating the `kSec...` constants!\n\n#### `GenericPasswordSecureStorable`\n\nGeneric passwords are probably the most common use-case of the keychain, and are great for storing usernames and passwords.\n\nProperties listed under ‘Required’ have to be implemented by any types that conform; those listed under ‘Optional’ can be implemented to add additional information to what is saved or read if desired.\n\nOne thing to note: if you implement an optional property, its type annotation must match the type specified in the protocol *exactly*. If you implement `description: String?` it can’t be declared as `var description: String`.\n\n**Required**\n\n```swift\nvar account: String { get }\nvar service: String { get }\n```\n\n**Optional**\n\n```swift\nvar comment: String? { get }\nvar creator: UInt? { get }\nvar description: String? { get }\nvar generic: NSData? { get }\nvar isInvisible: Bool? { get }\nvar isNegative: Bool? { get }\nvar label: String? { get }\nvar type: UInt? { get }\n```\n\n#### `InternetPasswordSecureStorable`\n\nTypes that conform to `InternetPasswordSecureStorable` typically come from web services and have certain associated metadata.\n\n**Required**\n\n```swift\nvar account: String { get }\nvar authenticationType: LocksmithInternetAuthenticationType { get }\nvar internetProtocol: LocksmithInternetProtocol { get }\nvar port: String { get }\nvar server: String { get }\n```\n\n**Optional**\n\n```swift\nvar comment: String? { get }\nvar creator: UInt? { get }\nvar description: String? { get }\nvar isInvisible: Bool? { get }\nvar isNegative: Bool? { get }\nvar path: String? { get }\nvar securityDomain: String? { get }\nvar type: UInt? { get }\n```\n\n## Result types\n\nBy adopting a protocol-oriented design from the ground up, Locksmith can provide access to the result of your keychain queries *with type annotations included*—store an `NSDate`, get an `NSDate` back with no type-casting!\n\nLet’s start with an example: the Twitter account from before, except it’s now an `InternetPasswordSecureStorable`, which lets us store a bit more metadata.\n\n```swift\nstruct TwitterAccount: InternetPasswordSecureStorable,\n                       ReadableSecureStorable,\n                       CreateableSecureStorable {\n  let username: String\n  let password: String\n\n  var account: String { return username }\n  var data: [String: AnyObject] {\n    return [\"password\": password]\n  }\n\n  let server = \"com.twitter\"\n  let port = 80\n  let internetProtocol = .HTTPS\n  let authenticationType = .HTTPBasic\n  let path: String? = \"\u002Fapi\u002F2.0\u002F\"\n}\n\nlet account = TwitterAccount(username: \"_matthewpalmer\", password: \"my_password\")\n\n\u002F\u002F Save all this to the keychain\naccount.createInSecureStore()\n\n\u002F\u002F Now let’s get it back\nlet result: InternetPasswordSecureStorableResultType = account.readFromSecureStore()\n\nresult?.port \u002F\u002F Gives us an Int directly!\nresult?.internetProtocol \u002F\u002F Gives us a LocksmithInternetProtocol enum case directly!\nresult?.data \u002F\u002F Gives us a [String: AnyObject] of what was saved\n\u002F\u002F and so on...\n```\n\nThis is *awesome*. No more typecasting.\n\n#### `GenericPasswordSecureStorableResultType`\n\nEverything listed here can be set on a type conforming to `GenericPasswordSecureStorable`, and gotten back from the result returned from `readFromSecureStore()` on that type.\n\n```swift\nvar account: String { get }\nvar service: String { get }\nvar comment: String? { get }\nvar creator: UInt? { get }\nvar description: String? { get }\nvar data: [String: AnyObject]? { get }\nvar generic: NSData? { get }\nvar isInvisible: Bool? { get }\nvar isNegative: Bool? { get }\nvar label: String? { get }\nvar type: UInt? { get }\n```\n\n#### `InternetPasswordSecureStorableResultType`\n\nEverything listed here can be set on a type conforming to `InternetPasswordSecureStorable`, and gotten back from the result returned from `readFromSecureStore()` on that type.\n\n```swift\nvar account: String { get }\nvar authenticationType: LocksmithInternetAuthenticationType { get }\nvar internetProtocol: LocksmithInternetProtocol { get }\nvar port: Int { get }\nvar server: String { get }\nvar comment: String? { get }\nvar creator: UInt? { get }\nvar data: [String: AnyObject]? { get }\nvar description: String? { get }\nvar isInvisible: Bool? { get }\nvar isNegative: Bool? { get }\nvar path: String? { get }\nvar securityDomain: String? { get }\nvar type: UInt? { get }\n```\n\n## Enumerations\n\nLocksmith provides a bunch of handy enums for configuring your requests, so you can say `kSecGoodByeStringConstants`.\n\n#### `LocksmithAccessibleOption`\n\n`LocksmithAccessibleOption` configures when an item can be accessed—you might require that stuff is available when the device is unlocked, after a passcode has been entered, etc.\n\n```swift\npublic enum LocksmithAccessibleOption {\n  case AfterFirstUnlock\n  case AfterFirstUnlockThisDeviceOnly\n  case Always\n  case AlwaysThisDeviceOnly\n  case WhenPasscodeSetThisDeviceOnly\n  case WhenUnlocked\n  case WhenUnlockedThisDeviceOnly\n}\n```\n\n#### `LocksmithError`\n\n`LocksmithError` provides Swift-friendly translations of common keychain error codes. These are thrown from methods throughout the library. [Apple’s documentation](https:\u002F\u002Fdeveloper.apple.com\u002Flibrary\u002Fios\u002Fdocumentation\u002FSecurity\u002FReference\u002Fkeychainservices\u002F#\u002F\u002Fapple_ref\u002Fc\u002Feconst\u002FerrSecSuccess) provides more information on these errors.\n\n```swift\npublic enum LocksmithError: ErrorType {\n  case Allocate\n  case AuthFailed\n  case Decode\n  case Duplicate\n  case InteractionNotAllowed\n  case NoError\n  case NotAvailable\n  case NotFound\n  case Param\n  case RequestNotSet\n  case TypeNotFound\n  case UnableToClear\n  case Undefined\n  case Unimplemented\n}\n```\n\n#### `LocksmithInternetAuthenticationType`\n\n`LocksmithInternetAuthenticationType` lets you pick out the type of authentication you want to store alongside your `.InternetPassword`s—anything from `.MSN` to `.HTTPDigest`. [Apple’s documentation](https:\u002F\u002Fdeveloper.apple.com\u002Flibrary\u002Fios\u002Fdocumentation\u002FSecurity\u002FReference\u002Fkeychainservices\u002F#\u002F\u002Fapple_ref\u002Fdoc\u002Fconstant_group\u002FAuthentication_Type_Values) provides more information on these values.\n\n```swift\npublic enum LocksmithInternetAuthenticationType {\n  case Default\n  case DPA\n  case HTMLForm\n  case HTTPBasic\n  case HTTPDigest\n  case MSN\n  case NTLM\n  case RPA\n}\n```\n\n#### `LocksmithInternetProtocol`\n\n`LocksmithInternetProtocol` is used with `.InternetPassword` to choose which protocol was used for the interaction with the web service, including `.HTTP`, `.SMB`, and a whole bunch more. [Apple’s documentation](https:\u002F\u002Fdeveloper.apple.com\u002Flibrary\u002Fios\u002Fdocumentation\u002FSecurity\u002FReference\u002Fkeychainservices\u002F#\u002F\u002Fapple_ref\u002Fdoc\u002Fconstant_group\u002FProtocol_Values) provides more information on these values.\n\n```swift\npublic enum {\n  case AFP\n  case AppleTalk\n  case DAAP\n  case EPPC\n  case FTP\n  case FTPAccount\n  case FTPProxy\n  case FTPS\n  case HTTP\n  case HTTPProxy\n  case HTTPS\n  case HTTPSProxy\n  case IMAP\n  case IMAPS\n  case IPP\n  case IRC\n  case IRCS\n  case LDAP\n  case NNTP\n  case NNTPS, LDAPS\n  case POP3\n  case POP3S\n  case RTSP\n  case RTSPProxy\n  case SMB\n  case SMTP\n  case SOCKS\n  case SSH\n  case Telnet\n  case TelnetS\n}\n```\n\n## Author\n\n[Matthew Palmer](http:\u002F\u002Fmatthewpalmer.net), matt@matthewpalmer.net\n\n## License\n\nLocksmith is available under the MIT license. See the LICENSE file for more info.\n","Locksmith 是一个强大的、基于协议的Swift库，用于处理iOS、macOS、watchOS和tvOS中的钥匙串。它提供了一个既简单又功能丰富的API来访问所有钥匙串元数据，并且支持强类型结果，允许开发者为现有类型免费添加功能。此外，通过使用Swift原生类型和有用的枚举，Locksmith简化了与钥匙串相关的操作流程。此库非常适合需要安全存储敏感信息（如密码或令牌）的应用程序开发场景中使用，确保这些信息只能被授权的应用程序部分访问。",2,"2026-06-11 03:10:18","top_language"]