SwiftSecurity
SwiftSecurity is a modern Swift API for Apple Security framework (Keychain API, SharedWebCredentials API, etc).
Why should I use this one?
- Supports every Keychain item class (Generic & Internet Password, Key, Certificate and Identity). The most popular solutions are designed only for
GenericPassword
, and in rare cases for other types. That could be handy for Enterprise use-cases. - Prevents creation of an incorrect set of attributes for items.
- Follows recommendations from Quinn βThe Eskimo!β.
- Compatible with CryptoKit and SwiftUI.
- Free of deprecated calls and legacy implementations (min. target set to iOS 14.0).
- Unified API across Apple systems (iOS, macOS, watchOS, tvOS, visionOS). Uses
useDataProtectionKeychain
key by default (for macOS).
It is built with safety and convenience in mind, yet provides progressive disclosure style for further customizations.
Here's an brief overview of what you can do:
// Keychain (with default Access Group)
let keychain = Keychain.default
// Shared Keychain (through App Group)
let keychain = Keychain(accessGroup: .keychainGroup(teamID: "J42EP42PB2", nameID: "com.example.app"))
// Store secret
try keychain.store("8e9c0a7f", query: .credential(for: "OpenAI"))
// Store secret with biometry protection
try keychain.store("8e9c0a7f", query: .credential(for: "OpenAI"),
accessPolicy: AccessPolicy(.whenUnlocked, options: .userPresence) // Requires biometry/passcode authentication
)
// Store asymmetric NIST keys from CryptoKit
let privateKey = P256.KeyAgreement.PrivateKey()
try keychain.store(privateKey, query: .privateKey(tag: "Alice"))
// Retrieve secret
let token: String? = try keychain.retrieve(.credential(for: "OpenAI"))
// Retrieve secret with pre-evaluated LAContext
let token: String? = try keychain.retrieve(.credential(for: "OpenAI"), authenticationContext: LAContext())
// Retrieve attributes
if let info = try keychain.info(for: .credential(for: "OpenAI")) {
// Creation date
print(info.creationDate)
// Comment
print(info.comment)
}
// Retrieve data and persistent reference for secret
let value = try keychain.retrieve([.data, .persistentReference], query: .privateKey(tag: "Alice"))
if case let .dictionary(info) = value {
// Data
info.data
// Persistent Reference (suitable for `NEVPNProtocol`)
info.persistentReference
}
// Remove secret
try keychain.remove(.credential(for: "OpenAI"))
// Customize queries (with type-checking)
var query = SecItemQuery<GenericPassword>()
query.synchronizable = true // β
Common
query.label = "OpenAI Access Token" // β
Common
query.service = "OpenAI" // β
Only for GenericPassword
query.keySizeInBits = 2048 // β Only for `SecKey`, so not accessible
// Generate data with 20 uniformly distributed random bytes
let randomData = try SecureRandomDataGenerator(count: 20).next()
// Error Handling
do {
let token: String? = try keychain.store("8e9c0a7f", query: .credential(for: "OpenAI"))
} catch {
switch error as? SwiftSecurityError {
case .duplicateItem:
// handle duplicate
default:
// unhandled
}
}
As you can see, access to nearly every low-level parameter is available.