KeychainAccess
Simple Swift wrapper for Keychain that works on iOS, watchOS, tvOS and macOS.
Install / Use
/learn @kishikawakatsumi/KeychainAccessREADME
KeychainAccess
KeychainAccess is a simple Swift wrapper for Keychain that works on iOS and macOS. Makes using Keychain APIs extremely easy and much more palatable to use in Swift.
<img src="https://github.com/kishikawakatsumi/KeychainAccess/assets/40610/4de4aae1-6fc1-4477-af6d-afbe6d164da0" width="320px" /> <img src="https://github.com/kishikawakatsumi/KeychainAccess/assets/40610/2980ea84-862b-4067-b9b7-90de629171b9" width="320px" /> <img src="https://github.com/kishikawakatsumi/KeychainAccess/assets/40610/3299347d-eb1b-446c-921c-778fa493f818" width="320px" />
:bulb: Features
- Simple interface
- Support access group
- Support accessibility
- Support iCloud sharing
- Support TouchID and Keychain integration (iOS 8+)
- Support Shared Web Credentials (iOS 8+)
- Works on both iOS & macOS
- watchOS and tvOS are supported
- Mac Catalyst is supported
- Swift 3, 4 and 5 compatible
:book: Usage
:eyes: See also:
:key: Basics
Saving Application Password
let keychain = Keychain(service: "com.example.github-token")
keychain["kishikawakatsumi"] = "01234567-89ab-cdef-0123-456789abcdef"
Saving Internet Password
let keychain = Keychain(server: "https://github.com", protocolType: .https)
keychain["kishikawakatsumi"] = "01234567-89ab-cdef-0123-456789abcdef"
:key: Instantiation
Create Keychain for Application Password
let keychain = Keychain(service: "com.example.github-token")
let keychain = Keychain(service: "com.example.github-token", accessGroup: "12ABCD3E4F.shared")
Create Keychain for Internet Password
let keychain = Keychain(server: "https://github.com", protocolType: .https)
let keychain = Keychain(server: "https://github.com", protocolType: .https, authenticationType: .htmlForm)
:key: Adding an item
subscripting
for String
keychain["kishikawakatsumi"] = "01234567-89ab-cdef-0123-456789abcdef"
keychain[string: "kishikawakatsumi"] = "01234567-89ab-cdef-0123-456789abcdef"
for NSData
keychain[data: "secret"] = NSData(contentsOfFile: "secret.bin")
set method
keychain.set("01234567-89ab-cdef-0123-456789abcdef", key: "kishikawakatsumi")
error handling
do {
try keychain.set("01234567-89ab-cdef-0123-456789abcdef", key: "kishikawakatsumi")
}
catch let error {
print(error)
}
:key: Obtaining an item
subscripting
for String (If the value is NSData, attempt to convert to String)
let token = keychain["kishikawakatsumi"]
let token = keychain[string: "kishikawakatsumi"]
for NSData
let secretData = keychain[data: "secret"]
get methods
as String
let token = try? keychain.get("kishikawakatsumi")
let token = try? keychain.getString("kishikawakatsumi")
as NSData
let data = try? keychain.getData("kishikawakatsumi")
:key: Removing an item
subscripting
keychain["kishikawakatsumi"] = nil
remove method
do {
try keychain.remove("kishikawakatsumi")
} catch let error {
print("error: \(error)")
}
:key: Set Label and Comment
let keychain = Keychain(server: "https://github.com", protocolType: .https)
do {
try keychain
.label("github.com (kishikawakatsumi)")
.comment("github access token")
.set("01234567-89ab-cdef-0123-456789abcdef", key: "kishikawakatsumi")
} catch let error {
print("error: \(error)")
}
:key: Obtaining Other Attributes
PersistentRef
let keychain = Keychain()
let persistentRef = keychain[attributes: "kishikawakatsumi"]?.persistentRef
...
Creation Date
let keychain = Keychain()
let creationDate = keychain[attributes: "kishikawakatsumi"]?.creationDate
...
All Attributes
let keychain = Keychain()
do {
let attributes = try keychain.get("kishikawakatsumi") { $0 }
print(attributes?.comment)
print(attributes?.label)
print(attributes?.creator)
...
} catch let error {
print("error: \(error)")
}
subscripting
let keychain = Keychain()
if let attributes = keychain[attributes: "kishikawakatsumi"] {
print(attributes.comment)
print(attributes.label)
print(attributes.creator)
}
:key: Configuration (Accessibility, Sharing, iCloud Sync)
Provides fluent interfaces
let keychain = Keychain(service: "com.example.github-token")
.label("github.com (kishikawakatsumi)")
.synchronizable(true)
.accessibility(.afterFirstUnlock)
<a name="accessibility"> Accessibility
Default accessibility matches background application (=kSecAttrAccessibleAfterFirstUnlock)
let keychain = Keychain(service: "com.example.github-token")
For background application
Creating instance
let keychain = Keychain(service: "com.example.github-token")
.accessibility(.afterFirstUnlock)
keychain["kishikawakatsumi"] = "01234567-89ab-cdef-0123-456789abcdef"
One-shot
let keychain = Keychain(service: "com.example.github-token")
do {
try keychain
.accessibility(.afterFirstUnlock)
.set("01234567-89ab-cdef-0123-456789abcdef", key: "kishikawakatsumi")
} catch let error {
print("error: \(error)")
}
For foreground application
Creating instance
let keychain = Keychain(service: "com.example.github-token")
.accessibility(.whenUnlocked)
keychain["kishikawakatsumi"] = "01234567-89ab-cdef-0123-456789abcdef"
One-shot
let keychain = Keychain(service: "com.example.github-token")
do {
try keychain
.accessibility(.whenUnlocked)
.set("01234567-89ab-cdef-0123-456789abcdef", key: "kishikawakatsumi")
} catch let error {
print("error: \(error)")
}
:couple: Sharing Keychain items
let keychain = Keychain(service: "com.example.github-token", accessGroup: "12ABCD3E4F.shared")
<a name="icloud_sharing"> :arrows_counterclockwise: Synchronizing Keychain items with iCloud
Creating instance
let keychain = Keychain(service: "com.example.github-token")
.synchronizable(true)
keychain["kishikawakatsumi"] = "01234567-89ab-cdef-0123-456789abcdef"
One-shot
let keychain = Keychain(service: "com.example.github-token")
do {
try keychain
.synchronizable(true)
.set("01234567-89ab-cdef-0123-456789abcdef", key: "kishikawakatsumi")
} catch let error {
print("error: \(error)")
}
<a name="touch_id_integration"> :cyclone: Touch ID (Face ID) integration
Any Operation that require authentication must be run in the background thread.
If you run in the main thread, UI thread will lock for the system to try to display the authentication dialog.
To use Face ID, add NSFaceIDUsageDescription key to your Info.plist
:closed_lock_with_key: Adding a Touch ID (Face ID) protected item
If you want to store the Touch ID protected Keychain item, specify accessibility and authenticationPolicy attributes.
let keychain = Keychain(service: "com.example.github-token")
DispatchQueue.global().async {
do {
// Should be the secret invalidated when passcode is removed? If not then use `.WhenUnlocked`
try keychain
.accessibility(.whenPasscodeSetThisDeviceOnly, authenticationPolicy: [.biometryAny])
.set("01234567-89ab-cdef-0123-456789abcdef", key: "kishikawakatsumi")
} catch let error {
// Error handling if needed...
}
}
:closed_lock_with_key: Updating a Touch ID (Face ID) protected item
The same way as when adding.
Do not run in the main thread if there is a possibility that the item you are trying to add already exists, and protected. Because updating protected items requires authentication.
Additionally, you want to show custom authentication prompt message when updating, specify an authenticationPrompt attribute.
If the item not protected, the authenticationPrompt parameter just be ignored.
let keychain = Keychain(service: "com.example.github-token")
DispatchQueue.global().async {
do {
// Should be the secret invalidated when passcode is removed? If not then use `.WhenUnlocked`
try keychain
.accessibility(.whenPasscodeSetThisDeviceOnly, authenticationPolicy: [.biometryAny])
.authenticationPrompt("Authenticate to update your access token")
.set("01234567-89ab-cdef-0123-456789abcdef", key: "kishikawakatsumi")
} catch let error {
// Error handling if needed...
}
}
:closed_lock_with_key: Obtaining a Touch ID (Face ID) protected item
The same way as when you get a normal item. It will be displayed automatically Touch ID or passcode authentication If the item you try to get is protected.
If you want to show custom authentication prompt message
Related Skills
healthcheck
327.7kHost security hardening and risk-tolerance configuration for OpenClaw deployments
node-connect
327.7kDiagnose OpenClaw node connection and pairing failures for Android, iOS, and macOS companion apps
prose
327.7kOpenProse VM skill pack. Activate on any `prose` command, .prose files, or OpenProse mentions; orchestrates multi-agent workflows.
frontend-design
80.7kCreate distinctive, production-grade frontend interfaces with high design quality. Use this skill when the user asks to build web components, pages, or applications. Generates creative, polished code that avoids generic AI aesthetics.
