Security is crucial when managing user data in mobile applications. For iOS apps, Keychain offers a secure method for storing sensitive information like user credentials, API keys, and other confidential data. In this blog, we’ll delve into using Keychain in Swift to store, retrieve, update, and delete user details.
In our previous blog, we defined the key and some functions of Keychain Services. In this blog, we’ll review an example of how you can store user-sensitive data securely using Keychain in Swift.
We’ll cover the steps to store, retrieve, update, and delete user details, ensuring your application handles confidential information safely and effectively.
Read our guide on defining quick actions in your iOS app to enhance user experience and streamline
Setting Up Keychain in Your Swift Project
To interact with Keychain, we’ll use Apple’s Security framework. Begin by importing the Security module in your Swift file:
import Security
Keychain Helper Class
To make Keychain interactions easier, we can create a helper class that will encapsulate all the necessary functions for storing, retrieving, updating, and deleting data.
class KeychainHelper {
static let shared = KeychainHelper()
private init() {}
// MARK: - Save Data
func save(_ data: Data, service: String, account: String) {
let query: [String: Any] = [
kSecClass as String: kSecClassGenericPassword,
kSecAttrService as String: service,
kSecAttrAccount as String: account,
kSecValueData as String: data
]
// Delete any existing items
SecItemDelete(query as CFDictionary)
// Add the new item
let status = SecItemAdd(query as CFDictionary, nil)
guard status == errSecSuccess else { print("Error: \(status)"); return }
}
// MARK: - Retrieve Data
func retrieve(service: String, account: String) -> Data? {
let query: [String: Any] = [
kSecClass as String: kSecClassGenericPassword,
kSecAttrService as String: service,
kSecAttrAccount as String: account,
kSecReturnData as String: true,
kSecMatchLimit as String: kSecMatchLimitOne
]
var dataTypeRef: AnyObject? = nil
let status = SecItemCopyMatching(query as CFDictionary, &dataTypeRef)
guard status == errSecSuccess else { print("Error: \(status)"); return nil }
return dataTypeRef as? Data
}
// MARK: - Update Data
func update(_ data: Data, service: String, account: String) {
let query: [String: Any] = [
kSecClass as String: kSecClassGenericPassword,
kSecAttrService as String: service,
kSecAttrAccount as String: account
]
let attributes: [String: Any] = [
kSecValueData as String: data
]
let status = SecItemUpdate(query as CFDictionary, attributes as CFDictionary)
guard status == errSecSuccess else { print("Error: \(status)"); return }
}
// MARK: - Delete Data
func delete(service: String, account: String) {
let query: [String: Any] = [
kSecClass as String: kSecClassGenericPassword,
kSecAttrService as String: service,
kSecAttrAccount as String: account
]
let status = SecItemDelete(query as CFDictionary)
guard status == errSecSuccess else { print("Error: \(status)"); return }
}
}
Storing User Details
To store user details such as username and password, convert them to Data and use the save method:
let username = "user123"
let password = "password123"
if let usernameData = username.data(using: .utf8),
let passwordData = password.data(using: .utf8) {
KeychainHelper.shared.save(usernameData, service: "com.example.myapp", account: "username")
KeychainHelper.shared.save(passwordData, service: "com.example.myapp", account: "password")
}
Retrieving User Details
To retrieve the stored details, use the retrieve method and convert the data back to a String:
if let usernameData = KeychainHelper.shared.retrieve(service: "com.example.myapp", account: "username"),
let passwordData = KeychainHelper.shared.retrieve(service: "com.example.myapp", account: "password"),
let username = String(data: usernameData, encoding: .utf8),
let password = String(data: passwordData, encoding: .utf8) {
print("Username: \(username)")
print("Password: \(password)")
}
Updating User Details
To update existing details, use the update method with the new data:
let newUsername = "newUser123"
if let newUsernameData = newUsername.data(using: .utf8) {
KeychainHelper.shared.update(newUsernameData, service: "com.example.myapp", account: "username")
}
Deleting User Details
To delete stored details, use the delete method:
KeychainHelper.shared.delete(service: "com.example.myapp", account: "username")
KeychainHelper.shared.delete(service: "com.example.myapp", account: "password")
Key Points to Consider During Keychain Implementation
When implementing Keychain for secure storage in iOS applications, focus on the following high-priority points:
Security and Encryption
- Keychain data is encrypted using the device’s secure hardware.
- Choose appropriate security attributes to control access (e.g., kSecAttrAccessibleWhenUnlocked).
Access Control - Use Access Control Lists (ACLs) to specify who or what can access Keychain items.
- Grant minimal necessary permissions to reduce security risks.
Data Persistence - Keychain items persist across app launches, device reboots, and even app reinstallation if backed up.
- iCloud Keychain can synchronize Keychain items across the user’s devices if enabled.
Data Isolation - Keychain items are isolated to the app that created them by default.
- Use App Groups to share Keychain items between multiple apps in the same group.
Accessibility Levels - Set appropriate accessibility levels based on security requirements (e.g., kSecAttrAccessibleWhenUnlocked).
- Understand how accessibility settings impact Keychain item availability.
Error Handling and Testing - Always check status codes returned by Keychain functions and handle errors gracefully.
- Test Keychain functionality across different scenarios (app reboots, device reboots, various accessibility settings).
Secure Storage Practices - Use Keychain for small pieces of sensitive information (passwords, tokens, keys).
- Minimize storage of Personally Identifiable Information (PII) directly in Keychain.
By focusing on these high-priority points, you can implement Keychain effectively and securely in your iOS applications.
You can find similar examples on GitHub that illustrate how to store user details and cryptographic keys in Keychain. These examples provide comprehensive details on securely managing sensitive data within your iOS applications.
Conclusion
Using Keychain in Swift provides a secure and reliable way to store sensitive user information. By encapsulating Keychain operations in a helper class, we can streamline the process and make our code more maintainable. With the steps outlined in this blog, you can easily implement secure data storage in your iOS applications, ensuring your users’ information is protected.
Happy coding!