Contents

markbattistella/keychainkit

`KeychainKit` is a Swift package that provides a clean, type-safe, and extensible API for securely storing, retrieving, and managing values in the system Keychain. It supports primitives, collections, `Codable` types, synchronisation options, accessibility controls, and automated

Features

  • Type-safe Keychain Keys: Define strongly typed keys by conforming to KeychainKeyRepresentable, with automatic prefixing.
  • Secure Storage for Multiple Types: Built-in support for:

- Bool, Int, Float, Double - String, Data, Date, URL - Arrays and dictionaries - Any Codable type

  • Flexible Synchronisation: Control whether items sync via iCloud (KeychainSync).
  • Accessibility Options: Adjust Keychain accessibility via KeychainAccessible.
  • Utility Functions: Persistent references, accessibility lookup, existence checks, and full key listing.
  • Migration Support: Built-in method to migrate Keychain contents between service names and access groups.
  • Automatic Prefixing: Derived from your bundle identifier with optional custom prefixes.

Installation

Add KeychainKit to your Swift project using Swift Package Manager.

dependencies: [
  .package(url: "https://github.com/markbattistella/KeychainKit", from: "1.0.0")
]

Usage

Defining Keys

Define your Keychain keys using an enum conforming to KeychainKeyRepresentable:

enum KeychainKey: String, KeychainKeyRepresentable {
    case authToken
    case userProfile
    case lastLogin
}

Optionally override the prefix:

enum SecureKey: String, KeychainKeyRepresentable {
    static let keyPrefix: String? = "com.example.custom"

    case sessionID
}

Storing Values

Store primitive types, strings, data, or any Encodable value:

let keychain = Keychain.standard

keychain.set("abc123", for: KeychainKey.authToken)
keychain.set(Date(), for: KeychainKey.lastLogin)
keychain.set(123, for: KeychainKey.userProfile)

Or using the throwing version:

try keychain.setThrowing(User(id: 9, name: "Jane"), for: KeychainKey.userProfile)

Retrieving Values

Retrieve values using convenient typed accessors:

let token = keychain.string(for: KeychainKey.authToken)
let loginDate = keychain.date(for: KeychainKey.lastLogin)
let user: User? = keychain.value(for: KeychainKey.userProfile)

Working with URLs, Arrays, and Dictionaries

let website = keychain.url(for: KeychainKey.authToken)

let scores: [Int]? = keychain.array(for: KeychainKey.userProfile)
let metadata: [String: String]? = keychain.dictionary(for: KeychainKey.userProfile)

Using iCloud Keychain Sync

keychain.set("abc123", for: KeychainKey.authToken, sync: .iCloud)
let token = keychain.string(for: KeychainKey.authToken, sync: .iCloud)

Controlling Accessibility

try keychain.setThrowing(
    "secret",
    for: KeychainKey.authToken,
    accessible: .whenPasscodeSetThisDeviceOnly
)

let level = keychain.accessibility(for: KeychainKey.authToken)

Listing and Checking Keys

let allKeys = keychain.allKeys()
let exists = keychain.exists(for: KeychainKey.authToken)

Removing Items

keychain.remove(for: KeychainKey.authToken)
keychain.removeLocalAndCloud(for: KeychainKey.authToken) // local + iCloud
keychain.removeAll() // all keys under current serviceName

Migration

KeychainKit supports migrating Keychain contents between service names or access groups:

let result = Keychain.migrate(
    from: "old.group",
    oldServiceName: "old.service",
    to: "new.group",
    newServiceName: "new.service",
    mode: .perform
)

print(result.migrated)
print(result.failed)

Use .dryRun to simulate migration without making changes:

let result = Keychain.migrate(
    from: "old.group",
    oldServiceName: "old.service",
    to: "new.group",
    mode: .dryRun
)

License

KeychainKit is available under the MIT license. See the LICENCE file for more information.

Package Metadata

Repository: markbattistella/keychainkit

Default branch: main

README: README.md