A Swift 2.0 compatible version is in the works. Check out the swift-2.0
branch.
A watchOS 2 support is also in the swift-2.0
branch.
KeychainAccess is a simple Swift wrapper for Keychain that works on iOS and OS X. Makes using Keychain APIs exremely easy and much more palatable to use in Swift.
- 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 & OS X
let keychain = Keychain(service: "com.example.github-token")
keychain["kishikawakatsumi"] = "01234567-89ab-cdef-0123-456789abcdef"
let keychain = Keychain(server: "https://github.com", protocolType: .HTTPS)
keychain["kishikawakatsumi"] = "01234567-89ab-cdef-0123-456789abcdef"
let keychain = Keychain(service: "com.example.github-token")
let keychain = Keychain(service: "com.example.github-token", accessGroup: "12ABCD3E4F.shared")
let keychain = Keychain(server: "https://github.com", protocolType: .HTTPS)
let keychain = Keychain(server: "https://github.com", protocolType: .HTTPS, authenticationType: .HTMLForm)
keychain["kishikawakatsumi"] = "01234567-89ab-cdef-0123-456789abcdef"
keychain[string: "kishikawakatsumi"] = "01234567-89ab-cdef-0123-456789abcdef"
keychain[data: "secret"] = NSData(contentsOfFile: "secret.bin")
keychain.set("01234567-89ab-cdef-0123-456789abcdef", key: "kishikawakatsumi")
if let error = keychain.set("01234567-89ab-cdef-0123-456789abcdef", key: "kishikawakatsumi") {
println("error: \(error)")
}
let token = keychain["kishikawakatsumi"]
let token = keychain[string: "kishikawakatsumi"]
let secretData = keychain[data: "secret"]
let token = keychain.get("kishikawakatsumi")
let token = keychain.getString("kishikawakatsumi")
let data = keychain.getData("kishikawakatsumi")
First, get the failable
(value or error) object
let failable = keychain.getStringOrError("kishikawakatsumi")
1. check enum
state
switch failable {
case .Success:
println("token: \(failable.value)")
case .Failure:
println("error: \(failable.error)")
}
2. check error
object
if let error = failable.error {
println("error: \(error)")
} else {
println("token: \(failable.value)")
}
3. check succeeded
property
if failable.succeeded {
println("token: \(failable.value)")
} else {
println("error: \(failable.error)")
}
4. check failed
property
if failable.failed {
println("error: \(failable.error)")
} else {
println("token: \(failable.value)")
}
keychain["kishikawakatsumi"] = nil
keychain.remove("kishikawakatsumi")
if let error = keychain.remove("kishikawakatsumi") {
println("error: \(error)")
}
let keychain = Keychain(server: "https://github.com", protocolType: .HTTPS)
keychain
.label("github.com (kishikawakatsumi)")
.comment("github access token")
.set("01234567-89ab-cdef-0123-456789abcdef", key: "kishikawakatsumi")
Provides fluent interfaces
let keychain = Keychain(service: "com.example.github-token")
.label("github.com (kishikawakatsumi)")
.synchronizable(true)
.accessibility(.AfterFirstUnlock)
let keychain = Keychain(service: "com.example.github-token")
let keychain = Keychain(service: "com.example.github-token")
.accessibility(.AfterFirstUnlock)
keychain["kishikawakatsumi"] = "01234567-89ab-cdef-0123-456789abcdef"
let keychain = Keychain(service: "com.example.github-token")
keychain
.accessibility(.AfterFirstUnlock)
.set("01234567-89ab-cdef-0123-456789abcdef", key: "kishikawakatsumi")
let keychain = Keychain(service: "com.example.github-token")
.accessibility(.WhenUnlocked)
keychain["kishikawakatsumi"] = "01234567-89ab-cdef-0123-456789abcdef"
let keychain = Keychain(service: "com.example.github-token")
keychain
.accessibility(.WhenUnlocked)
.set("01234567-89ab-cdef-0123-456789abcdef", key: "kishikawakatsumi")
let keychain = Keychain(service: "com.example.github-token", accessGroup: "12ABCD3E4F.shared")
let keychain = Keychain(service: "com.example.github-token")
.synchronizable(true)
keychain["kishikawakatsumi"] = "01234567-89ab-cdef-0123-456789abcdef"
let keychain = Keychain(service: "com.example.github-token")
keychain
.synchronizable(true)
.set("01234567-89ab-cdef-0123-456789abcdef", key: "kishikawakatsumi")
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.
If you want to store the Touch ID protected Keychain item, specify accessibility
and authenticationPolicy
attributes.
let keychain = Keychain(service: "com.example.github-token")
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0)) {
let error = keychain
.accessibility(.WhenPasscodeSetThisDeviceOnly, authenticationPolicy: .UserPresence)
.set("01234567-89ab-cdef-0123-456789abcdef", key: "kishikawakatsumi")
if error != nil {
// Error handling if needed...
}
}
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")
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0)) {
let error = keychain
.accessibility(.WhenPasscodeSetThisDeviceOnly, authenticationPolicy: .UserPresence)
.authenticationPrompt("Authenticate to update your access token")
.set("01234567-89ab-cdef-0123-456789abcdef", key: "kishikawakatsumi")
if error != nil {
// Error handling if needed...
}
}
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, specify an authenticationPrompt
attribute.
If the item not protected, the authenticationPrompt
parameter just be ignored.
let keychain = Keychain(service: "com.example.github-token")
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0)) {
let failable = keychain
.authenticationPrompt("Authenticate to login to server")
.getStringOrError("kishikawakatsumi")
if failable.succeeded {
println("value: \(failable.value)")
} else {
println("error: \(failable.error?.localizedDescription)")
// Error handling if needed...
}
}
The same way as when you remove a normal item. There is no way to show Touch ID or passcode authentication when removing Keychain items.
let keychain = Keychain(service: "com.example.github-token")
let error = keychain.remove("kishikawakatsumi")
if error != nil {
println("error: \(error?.localizedDescription)")
// Error handling if needed...
}
Shared web credentials is a programming interface that enables native iOS apps to share credentials with their website counterparts. For example, a user may log in to a website in Safari, entering a user name and password, and save those credentials using the iCloud Keychain. Later, the user may run a native app from the same developer, and instead of the app requiring the user to reenter a user name and password, shared web credentials gives it access to the credentials that were entered earlier in Safari. The user can also create new accounts, update passwords, or delete her account from within the app. These changes are then saved and used by Safari.
https://developer.apple.com/library/ios/documentation/Security/Reference/SharedWebCredentialsRef/
let keychain = Keychain(server: "https://www.kishikawakatsumi.com", protocolType: .HTTPS)
let username = "kishikawakatsumi@mac.com"
// First, check the credential in the app's Keychain
if let password = keychain.get(username) {
// If found password in the Keychain,
// then log into the server
} else {
// If not found password in the Keychain,
// try to read from Shared Web Credentials
keychain.getSharedPassword(username) { (password, error) -> () in
if password != nil {
// If found password in the Shared Web Credentials,
// then log into the server
// and save the password to the Keychain
keychain[username] = password
} else {
// If not found password either in the Keychain also Shared Web Credentials,
// prompt for username and password
// Log into server
// If the login is successful,
// save the credentials to both the Keychain and the Shared Web Credentials.
keychain[username] = inputPassword
keychain.setSharedPassword(inputPassword, account: username)
}
}
}
Keychain.requestSharedWebCredential { (credentials, error) -> () in
}
Generate strong random password that is in the same format used by Safari autofill (xxx-xxx-xxx-xxx).
let password = Keychain.generatePassword() // => Nhu-GKm-s3n-pMx
- Add a com.apple.developer.associated-domains entitlement to your app. This entitlement must include all the domains with which you want to share credentials.
- Add an apple-app-site-association file to your website. This file must include application identifiers for all the apps with which the site wants to share credentials, and it must be properly signed.
- When the app is installed, the system downloads and verifies the site association file for each of its associated domains. If the verification is successful, the app is associated with the domain.
More details:
https://developer.apple.com/library/ios/documentation/Security/Reference/SharedWebCredentialsRef/
let keychain = Keychain(server: "https://github.com", protocolType: .HTTPS)
println("\(keychain)")
=>
[
[authenticationType: Default, key: kishikawakatsumi, server: github.com, class: InternetPassword, protocol: HTTPS]
[authenticationType: Default, key: hirohamada, server: github.com, class: InternetPassword, protocol: HTTPS]
[authenticationType: Default, key: honeylemon, server: github.com, class: InternetPassword, protocol: HTTPS]
]
let keychain = Keychain(server: "https://github.com", protocolType: .HTTPS)
let keys = keychain.allKeys()
for key in keys {
println("key: \(key)")
}
=>
key: kishikawakatsumi
key: hirohamada
key: honeylemon
let keychain = Keychain(server: "https://github.com", protocolType: .HTTPS)
let items = keychain.allItems()
for item in items {
println("item: \(item)")
}
=>
item: [authenticationType: Default, key: kishikawakatsumi, server: github.com, class: InternetPassword, protocol: HTTPS]
item: [authenticationType: Default, key: hirohamada, server: github.com, class: InternetPassword, protocol: HTTPS]
item: [authenticationType: Default, key: honeylemon, server: github.com, class: InternetPassword, protocol: HTTPS]
iOS 7 or later OS X 10.9 or later
KeychainAccess is available through CocoaPods. To install it, simply add the following lines to your Podfile:
use_frameworks!
pod 'KeychainAccess'
You should install CocoaPods 0.38.0.beta2.
[sudo] gem install cocoapods --pre
Then, add the following lines to your Podfile:
use_frameworks!
pod 'KeychainAccess', :git => 'git@github.com:kishikawakatsumi/KeychainAccess.git', :branch => 'swift-2.0'
You should install CocoaPods 0.38.0.beta2.
[sudo] gem install cocoapods --pre
Then, add the following lines to your Podfile:
use_frameworks!
target 'EampleApp' do
pod 'KeychainAccess', :git => 'git@github.com:kishikawakatsumi/KeychainAccess.git', :branch => 'swift-2.0'
end
target 'EampleApp WatchKit Extension' do
platform :watchos, '2.0'
pod 'KeychainAccess', :git => 'git@github.com:kishikawakatsumi/KeychainAccess.git', :branch => 'swift-2.0'
end
KeychainAccess is available through Carthage. To install it, simply add the following line to your Cartfile:
github "kishikawakatsumi/KeychainAccess"
github "kishikawakatsumi/KeychainAccess" "swift-2.0"
- Add
Lib/KeychainAccess.xcodeproj
to your project - Link
KeychainAccess.framework
with your target - Add
Copy Files Build Phase
to include the framework to your application bundle
See iOS Example Project as reference.
kishikawa katsumi, kishikawakatsumi@mac.com
KeychainAccess is available under the MIT license. See the LICENSE file for more info.