Simple interface for querying or modifying keychain items. Use this class for more advanced keychain operations with fine-grained control over keychain attributes.
Properties
account
@property (nonatomic, copy, nullable) NSString *account;
The account name for the keychain item. Corresponds to kSecAttrAccount.
Example:
SAMKeychainQuery *query = [[SAMKeychainQuery alloc] init];
query.account = @"user@example.com";
query.service = @"MyService";
service
@property (nonatomic, copy, nullable) NSString *service;
The service name for the keychain item. Corresponds to kSecAttrService.
Example:
SAMKeychainQuery *query = [[SAMKeychainQuery alloc] init];
query.service = @"MyService";
query.account = @"user@example.com";
label
@property (nonatomic, copy, nullable) NSString *label;
The label for the keychain item. Corresponds to kSecAttrLabel.
Example:
SAMKeychainQuery *query = [[SAMKeychainQuery alloc] init];
query.label = @"My Secure Password";
query.service = @"MyService";
query.account = @"user@example.com";
accessGroup (iOS only)
@property (nonatomic, copy, nullable) NSString *accessGroup;
The access group for the keychain item. Corresponds to kSecAttrAccessGroup. Only used on iOS for sharing keychain items between apps.
Example:
SAMKeychainQuery *query = [[SAMKeychainQuery alloc] init];
query.accessGroup = @"group.com.example.myapp";
query.service = @"MyService";
query.account = @"user@example.com";
This property is only available on iOS 3.0+ and macOS 10.9+.
synchronizationMode
@property (nonatomic) SAMKeychainQuerySynchronizationMode synchronizationMode;
The synchronization mode for the keychain item. Corresponds to kSecAttrSynchronizable. Controls whether the keychain item is synchronized via iCloud Keychain.
Example:
SAMKeychainQuery *query = [[SAMKeychainQuery alloc] init];
query.synchronizationMode = SAMKeychainQuerySynchronizationModeYes;
query.service = @"MyService";
query.account = @"user@example.com";
This property is only available on iOS 7.0+ and macOS 10.9+.
passwordData
@property (nonatomic, copy, nullable) NSData *passwordData;
Root storage for password information. This is the underlying data storage for the password.
Example:
SAMKeychainQuery *query = [[SAMKeychainQuery alloc] init];
query.service = @"MyService";
query.account = @"user@example.com";
query.passwordData = [@"mySecurePassword" dataUsingEncoding:NSUTF8StringEncoding];
[query save:nil];
passwordObject
@property (nonatomic, copy, nullable) id<NSCoding> passwordObject;
This property automatically transitions between an object and the value of passwordData using NSKeyedArchiver and NSKeyedUnarchiver. Use this to store any object that conforms to NSCoding.
Example:
NSDictionary *credentials = @{
@"username": @"user@example.com",
@"apiKey": @"abc123"
};
SAMKeychainQuery *query = [[SAMKeychainQuery alloc] init];
query.service = @"MyService";
query.account = @"user@example.com";
query.passwordObject = credentials;
[query save:nil];
password
@property (nonatomic, copy, nullable) NSString *password;
Convenience accessor for setting and getting a password string. Passes through to passwordData using UTF-8 string encoding.
Example:
SAMKeychainQuery *query = [[SAMKeychainQuery alloc] init];
query.service = @"MyService";
query.account = @"user@example.com";
query.password = @"mySecurePassword";
[query save:nil];
Instance Methods
save:
Save the receiver’s attributes as a keychain item. Existing items with the given account, service, and access group will first be deleted.
- (BOOL)save:(NSError **)error;
Populated should an error occur.
Returns YES if saving was successful, NO otherwise.
Example:
SAMKeychainQuery *query = [[SAMKeychainQuery alloc] init];
query.service = @"MyService";
query.account = @"user@example.com";
query.password = @"mySecurePassword";
NSError *error = nil;
BOOL success = [query save:&error];
if (!success) {
NSLog(@"Error saving to keychain: %@", error);
}
deleteItem:
Delete keychain items that match the given account, service, and access group.
- (BOOL)deleteItem:(NSError **)error;
Populated should an error occur.
Returns YES if deletion was successful, NO otherwise.
Example:
SAMKeychainQuery *query = [[SAMKeychainQuery alloc] init];
query.service = @"MyService";
query.account = @"user@example.com";
NSError *error = nil;
BOOL success = [query deleteItem:&error];
if (!success) {
NSLog(@"Error deleting from keychain: %@", error);
}
fetch:
Fetch the keychain item that matches the given account, service, and access group. The password and passwordData properties will be populated unless an error occurs. The values of password and passwordData are ignored when fetching.
- (BOOL)fetch:(NSError **)error;
Populated should an error occur.
Returns YES if fetching was successful, NO otherwise.
Example:
SAMKeychainQuery *query = [[SAMKeychainQuery alloc] init];
query.service = @"MyService";
query.account = @"user@example.com";
NSError *error = nil;
BOOL success = [query fetch:&error];
if (success) {
NSLog(@"Retrieved password: %@", query.password);
} else {
NSLog(@"Error fetching from keychain: %@", error);
}
fetchAll:
Fetch all keychain items that match the given account, service, and access group. The values of password and passwordData are ignored when fetching.
- (nullable NSArray<NSDictionary<NSString *, id> *> *)fetchAll:(NSError **)error;
Populated should an error occur.
return
NSArray<NSDictionary<NSString *, id> *> *
Returns an array of dictionaries that represent all matching keychain items or nil should an error occur. The order of the items is not determined.
Example:
SAMKeychainQuery *query = [[SAMKeychainQuery alloc] init];
query.service = @"MyService";
NSError *error = nil;
NSArray *items = [query fetchAll:&error];
if (items) {
NSLog(@"Found %lu keychain items", (unsigned long)items.count);
for (NSDictionary *item in items) {
NSLog(@"Account: %@", item[kSAMKeychainAccountKey]);
}
} else {
NSLog(@"Error fetching items: %@", error);
}
Class Methods
isSynchronizationAvailable
Returns a boolean indicating if keychain synchronization is available on the device at runtime. The #define SAMKEYCHAIN_SYNCHRONIZATION_AVAILABLE is only for compile time. If you are checking for the presence of synchronization, you should use this method.
+ (BOOL)isSynchronizationAvailable;
A value indicating if keychain synchronization is available.
Example:
if ([SAMKeychainQuery isSynchronizationAvailable]) {
SAMKeychainQuery *query = [[SAMKeychainQuery alloc] init];
query.synchronizationMode = SAMKeychainQuerySynchronizationModeYes;
query.service = @"MyService";
query.account = @"user@example.com";
query.password = @"mySecurePassword";
[query save:nil];
} else {
NSLog(@"iCloud Keychain synchronization is not available");
}
This method is only available on iOS 7.0+ and macOS 10.9+.
Enumerations
SAMKeychainQuerySynchronizationMode
Synchronization mode for keychain items.
typedef NS_ENUM(NSUInteger, SAMKeychainQuerySynchronizationMode) {
SAMKeychainQuerySynchronizationModeAny,
SAMKeychainQuerySynchronizationModeNo,
SAMKeychainQuerySynchronizationModeYes
};
SAMKeychainQuerySynchronizationModeAny
Query both synchronizable and non-synchronizable items.
Example:
SAMKeychainQuery *query = [[SAMKeychainQuery alloc] init];
query.synchronizationMode = SAMKeychainQuerySynchronizationModeAny;
query.service = @"MyService";
NSArray *items = [query fetchAll:nil];
SAMKeychainQuerySynchronizationModeNo
Query only non-synchronizable items (not synchronized via iCloud).
Example:
SAMKeychainQuery *query = [[SAMKeychainQuery alloc] init];
query.synchronizationMode = SAMKeychainQuerySynchronizationModeNo;
query.service = @"MyService";
query.account = @"user@example.com";
query.password = @"mySecurePassword";
[query save:nil];
SAMKeychainQuerySynchronizationModeYes
Query only synchronizable items (synchronized via iCloud).
Example:
SAMKeychainQuery *query = [[SAMKeychainQuery alloc] init];
query.synchronizationMode = SAMKeychainQuerySynchronizationModeYes;
query.service = @"MyService";
query.account = @"user@example.com";
query.password = @"mySecurePassword";
[query save:nil];
This enumeration is only available on iOS 7.0+ and macOS 10.9+.
Usage Examples
Saving a Password
SAMKeychainQuery *query = [[SAMKeychainQuery alloc] init];
query.service = @"MyService";
query.account = @"user@example.com";
query.password = @"mySecurePassword";
NSError *error = nil;
if ([query save:&error]) {
NSLog(@"Password saved successfully");
} else {
NSLog(@"Error saving password: %@", error);
}
Fetching a Password
SAMKeychainQuery *query = [[SAMKeychainQuery alloc] init];
query.service = @"MyService";
query.account = @"user@example.com";
NSError *error = nil;
if ([query fetch:&error]) {
NSLog(@"Password: %@", query.password);
} else {
NSLog(@"Error fetching password: %@", error);
}
Deleting a Password
SAMKeychainQuery *query = [[SAMKeychainQuery alloc] init];
query.service = @"MyService";
query.account = @"user@example.com";
NSError *error = nil;
if ([query deleteItem:&error]) {
NSLog(@"Password deleted successfully");
} else {
NSLog(@"Error deleting password: %@", error);
}
Using iCloud Keychain Synchronization
if ([SAMKeychainQuery isSynchronizationAvailable]) {
SAMKeychainQuery *query = [[SAMKeychainQuery alloc] init];
query.service = @"MyService";
query.account = @"user@example.com";
query.password = @"mySecurePassword";
query.synchronizationMode = SAMKeychainQuerySynchronizationModeYes;
NSError *error = nil;
if ([query save:&error]) {
NSLog(@"Password saved and will sync via iCloud");
} else {
NSLog(@"Error: %@", error);
}
}
Storing Custom Objects
NSDictionary *credentials = @{
@"username": @"user@example.com",
@"apiKey": @"abc123",
@"token": @"xyz789"
};
SAMKeychainQuery *query = [[SAMKeychainQuery alloc] init];
query.service = @"MyService";
query.account = @"user@example.com";
query.passwordObject = credentials;
NSError *error = nil;
if ([query save:&error]) {
NSLog(@"Credentials saved successfully");
}
// Retrieve the object
SAMKeychainQuery *fetchQuery = [[SAMKeychainQuery alloc] init];
fetchQuery.service = @"MyService";
fetchQuery.account = @"user@example.com";
if ([fetchQuery fetch:&error]) {
NSDictionary *retrievedCredentials = (NSDictionary *)fetchQuery.passwordObject;
NSLog(@"API Key: %@", retrievedCredentials[@"apiKey"]);
}
Using Access Groups for App Groups
SAMKeychainQuery *query = [[SAMKeychainQuery alloc] init];
query.service = @"MyService";
query.account = @"user@example.com";
query.password = @"mySecurePassword";
query.accessGroup = @"group.com.example.myapp";
NSError *error = nil;
if ([query save:&error]) {
NSLog(@"Password saved to shared access group");
} else {
NSLog(@"Error: %@", error);
}