Convert Figma logo to code with AI

matthewpalmer logoLocksmith

A powerful, protocol-oriented library for working with the keychain in Swift.

2,917
268
2,917
62

Top Related Projects

3,991

Valet lets you securely store data in the iOS, tvOS, or macOS Keychain without knowing a thing about how the Keychain works. It’s easy. We promise.

Simple Swift wrapper for Keychain that works on iOS, watchOS, tvOS and macOS.

Helper functions for saving text in Keychain securely for iOS, OS X, tvOS and watchOS.

Simple Objective-C wrapper for the keychain that works on Mac and iOS

Convenient & secure logging during development & release in Swift 4 & 5

Quick Overview

Locksmith is a powerful and easy-to-use Swift library for working with the iOS Keychain. It provides a simple, protocol-oriented API for storing and retrieving sensitive data securely, making it easier for developers to implement secure data storage in their iOS applications.

Pros

  • Simple and intuitive API for Keychain operations
  • Protocol-oriented design, allowing for easy customization and extension
  • Supports both synchronous and asynchronous operations
  • Compatible with Swift's Codable protocol for easy serialization

Cons

  • Limited to iOS platform (not cross-platform)
  • May require additional setup for advanced Keychain features
  • Documentation could be more comprehensive
  • Not actively maintained (last update was in 2019)

Code Examples

  1. Saving data to Keychain:
let data = "sensitive information".data(using: .utf8)!
try Locksmith.saveData(data: ["secret": data], forUserAccount: "user123")
  1. Retrieving data from Keychain:
if let dictionary = Locksmith.loadDataForUserAccount(userAccount: "user123") {
    let secretData = dictionary["secret"] as! Data
    let secret = String(data: secretData, encoding: .utf8)!
    print(secret) // Output: sensitive information
}
  1. Deleting data from Keychain:
try Locksmith.deleteDataForUserAccount(userAccount: "user123")
  1. Using Codable with Locksmith:
struct User: Codable {
    let username: String
    let password: String
}

let user = User(username: "john", password: "secret123")
try Locksmith.saveData(data: user, forUserAccount: "currentUser")

if let loadedUser: User = Locksmith.loadDataForUserAccount(userAccount: "currentUser") {
    print(loadedUser.username) // Output: john
}

Getting Started

  1. Add Locksmith to your project using Swift Package Manager:

    dependencies: [
        .package(url: "https://github.com/matthewpalmer/Locksmith.git", from: "4.0.0")
    ]
    
  2. Import Locksmith in your Swift file:

    import Locksmith
    
  3. Start using Locksmith to securely store and retrieve data:

    // Save data
    try Locksmith.saveData(data: ["key": "value"], forUserAccount: "myAccount")
    
    // Retrieve data
    if let data = Locksmith.loadDataForUserAccount(userAccount: "myAccount") {
        print(data["key"] as? String ?? "")
    }
    

Competitor Comparisons

3,991

Valet lets you securely store data in the iOS, tvOS, or macOS Keychain without knowing a thing about how the Keychain works. It’s easy. We promise.

Pros of Valet

  • More comprehensive and feature-rich, offering advanced functionality like synchronization and sharing
  • Better maintained with more recent updates and active community support
  • Provides a higher level of abstraction, simplifying complex Keychain operations

Cons of Valet

  • Larger codebase and dependency, which may increase app size
  • Steeper learning curve due to more extensive API and features
  • May be overkill for simple Keychain storage needs

Code Comparison

Valet:

let myValet = Valet.valet(with: Identifier(nonEmpty: "Druidia")!, accessibility: .whenUnlocked)
try myValet.setString("12345", forKey: "combination")
let combination = try myValet.string(forKey: "combination")

Locksmith:

try Locksmith.saveData(["combination": "12345"], forUserAccount: "Druidia")
let dictionary = Locksmith.loadDataForUserAccount("Druidia")
let combination = dictionary?["combination"] as? String

Both libraries provide simple APIs for storing and retrieving data from the Keychain, but Valet offers a more type-safe approach with dedicated methods for different data types. Locksmith uses a dictionary-based approach, which is more flexible but potentially less safe.

Simple Swift wrapper for Keychain that works on iOS, watchOS, tvOS and macOS.

Pros of KeychainAccess

  • More comprehensive documentation and examples
  • Supports a wider range of Keychain operations
  • Actively maintained with frequent updates

Cons of KeychainAccess

  • Slightly more complex API for basic operations
  • Larger codebase, which may increase app size

Code Comparison

KeychainAccess:

let keychain = Keychain(service: "com.example.app")
keychain["username"] = "john_doe"
let password = keychain["password"]

Locksmith:

try Locksmith.saveData(["username": "john_doe"], forUserAccount: "User")
let dictionary = Locksmith.loadDataForUserAccount("User")

Both libraries provide similar functionality for basic Keychain operations. KeychainAccess offers a more object-oriented approach with its Keychain class, while Locksmith uses static methods for a more functional style.

KeychainAccess provides more granular control over Keychain attributes and supports advanced features like synchronization and sharing between apps. Locksmith, on the other hand, offers a simpler API for basic operations, which may be preferable for projects with straightforward Keychain requirements.

Ultimately, the choice between these libraries depends on the specific needs of your project and your preferred coding style.

Helper functions for saving text in Keychain securely for iOS, OS X, tvOS and watchOS.

Pros of keychain-swift

  • Simpler API with fewer methods, making it easier to learn and use
  • Lightweight and focused solely on Keychain operations
  • Supports watchOS in addition to iOS, macOS, and tvOS

Cons of keychain-swift

  • Less feature-rich compared to Locksmith
  • Doesn't provide as much flexibility for advanced Keychain operations
  • Limited options for customizing Keychain query attributes

Code Comparison

keychain-swift:

let keychain = KeychainSwift()
keychain.set("hello world", forKey: "my key")
let value = keychain.get("my key")

Locksmith:

try Locksmith.saveData(["some key": "some value"], forUserAccount: "myUserAccount")
let dictionary = Locksmith.loadDataForUserAccount("myUserAccount")

Both libraries provide simple ways to interact with the Keychain, but Locksmith offers more advanced features and flexibility. keychain-swift focuses on simplicity and ease of use, while Locksmith provides a more comprehensive set of tools for Keychain operations. The choice between the two depends on the specific requirements of your project and the level of control you need over Keychain interactions.

Simple Objective-C wrapper for the keychain that works on Mac and iOS

Pros of SAMKeychain

  • Simpler API with straightforward methods for basic keychain operations
  • Lightweight and focused solely on keychain functionality
  • Longer history and more established in the iOS development community

Cons of SAMKeychain

  • Less feature-rich compared to Locksmith
  • Lacks some advanced functionalities like synchronization options
  • Not actively maintained (last commit was in 2019)

Code Comparison

SAMKeychain:

SAMKeychain.setPassword("secret", forService: "MyApp", account: "user@example.com")
let password = SAMKeychain.password(forService: "MyApp", account: "user@example.com")

Locksmith:

try Locksmith.saveData(["password": "secret"], forUserAccount: "user@example.com")
let dictionary = Locksmith.loadDataForUserAccount("user@example.com")
let password = dictionary?["password"] as? String

SAMKeychain offers a more direct approach to storing and retrieving passwords, while Locksmith provides a more flexible dictionary-based API. Locksmith also includes additional features like error handling and synchronization options, making it more suitable for complex keychain operations. However, SAMKeychain's simplicity may be preferable for basic use cases.

Convenient & secure logging during development & release in Swift 4 & 5

Pros of SwiftyBeaver

  • Comprehensive logging system with multiple destinations (console, file, cloud)
  • Customizable log formats and filtering options
  • Active development and community support

Cons of SwiftyBeaver

  • Primarily focused on logging, not keychain management
  • May require additional setup for advanced features
  • Larger codebase and potential overhead for simple logging needs

Code Comparison

SwiftyBeaver:

import SwiftyBeaver
let log = SwiftyBeaver.self
log.addDestination(ConsoleDestination())
log.info("Hello, SwiftyBeaver!")

Locksmith:

import Locksmith
try Locksmith.saveData(["password": "secret"], forUserAccount: "myAccount")
let dictionary = Locksmith.loadDataForUserAccount("myAccount")

Summary

SwiftyBeaver is a powerful logging framework for Swift applications, offering extensive customization and multiple output destinations. It excels in providing detailed logging capabilities but is not designed for keychain management.

Locksmith, on the other hand, is specifically tailored for keychain access and management in Swift. It provides a simple API for storing and retrieving sensitive data securely.

While both libraries serve different primary purposes, they can be complementary in a Swift project. SwiftyBeaver can handle comprehensive logging needs, while Locksmith can manage secure data storage in the keychain.

Convert Figma logo designs to code with AI

Visual Copilot

Introducing Visual Copilot: A new AI model to turn Figma designs to high quality code using your components.

Try Visual Copilot

README

Locksmith

A powerful, protocol-oriented library for working with the keychain in Swift.

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

 

🚀 I make Rocket, an app that gives you Slack-style emoji everywhere on your Mac.

 

Details

How is Locksmith different to other keychain wrappers?

  • Locksmith’s API is both super-simple and deeply powerful
  • Provides access to all of the keychain’s metadata with strongly typed results
  • Add functionality to your existing types for free
  • Useful enums and Swift-native types

Want to read more about Locksmith’s design? I wrote a blog post on protocol oriented programming in Swift.

Installation

Version Carthage compatible Build Status

  • Locksmith 4.0 and greater is Swift 4 compatible. See the swift-3.1 branch for compatibility with older versions of Swift.

CocoaPods

Locksmith is available through CocoaPods.

pod 'Locksmith'

Carthage

Locksmith is available through Carthage.

github "matthewpalmer/Locksmith"

Quick start

Setup

  1. Choose your target project
  2. Select Capabilties
  3. Enable Keychain Sharing

Xcode then automatically creates a .entitlements file and you are ready to use Locksmith!

Save data

try Locksmith.saveData(["some key": "some value"], forUserAccount: "myUserAccount")

Load data

let dictionary = Locksmith.loadDataForUserAccount("myUserAccount")

Update data

  • as well as replacing existing data, this writes data to the keychain if it does not exist already
try Locksmith.updateData(["some key": "another value"], forUserAccount: "myUserAccount")

Delete data

try Locksmith.deleteDataForUserAccount("myUserAccount")

Power to the protocols

Locksmith has been designed with Swift 2, protocols, and protocol extensions in mind.

Why do this? Because you can add existing functionality to your types with only the slightest changes!

Say we have a Twitter account

struct TwitterAccount {
  let username: String
  let password: String
}

and 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.

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]
  }
}

Now we get the ability to save our account in the keychain.

let account = TwitterAccount(username: "_matthewpalmer", password: "my_password")
try account.createInSecureStore()

Creating, reading, and deleting each have their own protocols: CreateableSecureStorable, ReadableSecureStorable, and DeleteableSecureStorable. And the best part?

You can conform to all three protocols on the same type!

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()

So. cool.

The details

By 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.

I 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.

CreateableSecureStorable

What you get

// Saves a type to the keychain
func createInSecureStore() throws

Required

// The data to save to the keychain
var data: [String: AnyObject] { get }

Optional

// Perform the request in this closure
var performCreateRequestClosure: PerformRequestClosureType { get }

ReadableSecureStorable

What you get

// Read from the keychain
func readFromSecureStore() -> SecureStorableResultType?

Required

Nothing!

Optional

// Perform the request in this closure
var performReadRequestClosure: PerformRequestClosureType { get }

DeleteableSecureStorable

What you get

// Read from the keychain
func deleteFromSecureStore() throws

Required

Nothing!

Optional

// Perform the request in this closure
var performDeleteRequestClosure: PerformRequestClosureType { get }

Powerful support for the Cocoa Keychain

Many 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.

Locksmith 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.

Please refer to the Keychain Services Reference for full information on what each of the attributes mean and what they can do.

Certificates, keys, and identities are possible—it’s just a matter of translating the kSec... constants!

GenericPasswordSecureStorable

Generic passwords are probably the most common use-case of the keychain, and are great for storing usernames and passwords.

Properties 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.

One 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.

Required

var account: String { get }
var service: String { get }

Optional

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

Types that conform to InternetPasswordSecureStorable typically come from web services and have certain associated metadata.

Required

var account: String { get }
var authenticationType: LocksmithInternetAuthenticationType { get }
var internetProtocol: LocksmithInternetProtocol { get }
var port: String { get }
var server: String { get }

Optional

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 }

Result types

By 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!

Let’s start with an example: the Twitter account from before, except it’s now an InternetPasswordSecureStorable, which lets us store a bit more metadata.

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...

This is awesome. No more typecasting.

GenericPasswordSecureStorableResultType

Everything listed here can be set on a type conforming to GenericPasswordSecureStorable, and gotten back from the result returned from readFromSecureStore() on that type.

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

Everything listed here can be set on a type conforming to InternetPasswordSecureStorable, and gotten back from the result returned from readFromSecureStore() on that type.

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 }

Enumerations

Locksmith provides a bunch of handy enums for configuring your requests, so you can say kSecGoodByeStringConstants.

LocksmithAccessibleOption

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.

public enum LocksmithAccessibleOption {
  case AfterFirstUnlock
  case AfterFirstUnlockThisDeviceOnly
  case Always
  case AlwaysThisDeviceOnly
  case WhenPasscodeSetThisDeviceOnly
  case WhenUnlocked
  case WhenUnlockedThisDeviceOnly
}

LocksmithError

LocksmithError provides Swift-friendly translations of common keychain error codes. These are thrown from methods throughout the library. Apple’s documentation provides more information on these errors.

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 lets you pick out the type of authentication you want to store alongside your .InternetPasswords—anything from .MSN to .HTTPDigest. Apple’s documentation provides more information on these values.

public enum LocksmithInternetAuthenticationType {
  case Default
  case DPA
  case HTMLForm
  case HTTPBasic
  case HTTPDigest
  case MSN
  case NTLM
  case RPA
}

LocksmithInternetProtocol

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 provides more information on these values.

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
}

Author

Matthew Palmer, matt@matthewpalmer.net

License

Locksmith is available under the MIT license. See the LICENSE file for more info.