Logo for tanaschita.com

Working with the Keychain in iOS

Learn how to securely store sensitive data using Apple’s Keychain Services API.

25 May 2026 · 6 min read

When developing iOS apps, we often need to persist sensitive user data such as passwords, authentication tokens, or cryptographic keys.

For this type of data, storing values in plain files or UserDefaults is usually not appropriate. Instead, iOS provides the Keychain Services API, which stores sensitive data in an encrypted and system-managed database.

In this article, we'll look at how the Keychain API works and how to save, retrieve, update, and delete keychain items in Swift.

Let's dive in.

Sponsorship logo
Architecture & Design Patterns for iOS
This book is a practical guide to essential architectural principles and design patterns for iOS development. It covers strategies for building maintainable apps with Swift and SwiftUI, including dependency injection, navigation, common patterns, and modularization.
LEARN MORE

A note on the API design

The Keychain Services API is a C-based API, which makes it feel less modern compared to many Swift APIs.

In practice, many apps wrap the API in a small Swift abstraction or use a third-party library to simplify common operations.

That said, understanding the underlying query structure is still valuable because most abstractions are ultimately built on top of the same API.

Understanding keychain queries

The Keychain Services API is based around queries.

A keychain query is a dictionary of key-value pairs that describes:

  • the type of item we want to work with
  • how the item can be identified
  • additional configuration or access options
  • the data itself

The same query structure is used for saving, reading, updating, and deleting items.

Saving keychain items

For example, to store a password in the keychain, we first create a query describing the item we want to save:

let query: [CFString: Any] = [
kSecClass: kSecClassGenericPassword,
kSecAttrService: "com.app.service",
kSecAttrAccount: username,
kSecValueData: passwordData]

Let's break this down.

1. Defining the item type kSecClass: kSecClassGenericPassword

kSecClass defines the type of keychain item we want to store. In this case, we use kSecClassGenericPassword for storing a password. Other available types include:

The item type determines which attributes are available for the query.

2. Defining a namespace kSecAttrService: "com.app.service"

A string that namespaces your keychain items, typically the app's bundle identifier or a specific service name. This is important for avoiding collisions when multiple items share the same account name.

3. Defining item attributes kSecAttrAccount: username

Attributes are used to identify the item and provide additional metadata.

In this example, kSecAttrAccount stores the username associated with the password.

We can additionally provide extra query arguments to configure how the keychain item behaves. For example kSecAttrAccessible: kSecAttrAccessibleWhenUnlocked, which controls when the item can be accessed. kSecAttrAccessibleWhenUnlocked ensures that the item is only accessible while the device is unlocked.

4. Providing the sensitive data kSecValueData: passwordData

The sensitive value itself is stored using kSecValueData. The value must be provided as Data.

Adding the item to the keychain

Once the query is ready, we can store the item using SecItemAdd:

let status = SecItemAdd(query as CFDictionary, nil)
guard status == errSecSuccess else {
throw KeychainError.errorWithStatus(status)
}

The method returns a status code indicating whether the operation succeeded.

One error to watch our for is the error errSecDuplicateItem, which occurs when an item with the same attributes already exists. We can handle this by checking for that status and calling SecItemUpdate instead (see the update section below), or by deleting the old item before adding the new one.

Retrieving keychain items

To read a password from the keychain, we can create another query:

let query: [CFString: Any] = [
kSecClass: kSecClassGenericPassword,
kSecAttrService: "com.app.service",
kSecAttrAccount: username,
kSecReturnData: true]

The key difference is kSecReturnData: true which tells the Keychain API that we want the stored value returned.

We can then retrieve the item using SecItemCopyMatching:

var result: CFTypeRef?
let status = SecItemCopyMatching(query as CFDictionary, &result)
guard status != errSecItemNotFound else {
throw KeychainError.noPassword
}
guard status == errSecSuccess else {
throw KeychainError.errorWithStatus(status)
}
guard let passwordData = result as? Data else {
throw KeychainError.unexpectedPasswordData
}

Once we have the data, we can convert it back into a String:

let password = String(data: passwordData, encoding: .utf8)

Updating keychain items

To update an existing item, we first define a query that identifies the item and then provide the updated values separately:

let query: [CFString: Any] = [
kSecClass: kSecClassGenericPassword,
kSecAttrService: "com.app.service",
kSecAttrAccount: username
]
let attributes: [CFString: Any] = [
kSecValueData: newPasswordData
]
let status = SecItemUpdate(query as CFDictionary, attributes as CFDictionary)

The query identifies which item should be updated, while the attributes dictionary contains the new values.

Deleting keychain items

Deleting an item uses the same query-based approach:

let query: [CFString: Any] = [
kSecClass: kSecClassGenericPassword,
kSecAttrService: "com.app.service",
kSecAttrAccount: username
]
let status = SecItemDelete(query as CFDictionary)

If the operation succeeds, the item is permanently removed from the keychain.

Sponsorship logo
Preparing for a technical iOS job interview
Preparing for a technical iOS Job Interview with over 300 questions & answers. Covering Swift & Objective-C, SwiftUI & UIKit, Combine, HTTP Networking, iOS File System, Core Data, Concurrency with async/await, Security, Automated Testing, Dependency Management, AI & Machine Learning and more.
LEARN MORE
Sponsorship logo
Architecture & Design Patterns for iOS
This book is a practical guide to essential architectural principles and design patterns for iOS development. It covers strategies for building maintainable apps with Swift and SwiftUI, including dependency injection, navigation, common patterns, and modularization.
LEARN MORE
Sponsorship logo
Become a sponsor of tanaschita.com
By publishing an article on different iOS topics every week, tanaschita.com is constantly growing in the developer community and may provide a great audience for you as a sponsor.
CLICK TO LEARN MORE

Newsletter

Image of a reading marmot
Subscribe

Like to support my work?

Say hi

Related tags

Articles with related topics

authentication

security

swift

ios

Implementing Passkeys in iOS with AuthenticationServices

A practical introduction to passwordless authentication using passkeys on iOS.

02 Mar 2026 · 7 min read

Latest articles and tips

© 2026 tanaschita.com

Privacy policy

Impressum