Skip to main content

Overview

SAMKeychain provides comprehensive methods for managing keychain accounts. This guide covers how to list all accounts, filter by service, update passwords, and delete items from the keychain.

Listing All Accounts

Retrieve all keychain items accessible to your app:
NSArray<NSDictionary *> *accounts = [SAMKeychain allAccounts];

for (NSDictionary *accountInfo in accounts) {
    NSString *account = accountInfo[kSAMKeychainAccountKey];
    NSString *service = accountInfo[kSAMKeychainWhereKey];
    
    NSLog(@"Account: %@, Service: %@", account, service);
}
The allAccounts method returns an array of dictionaries containing metadata about each keychain item. Passwords are not included in the results.

Available Metadata Keys

Each account dictionary contains the following keys:
KeyDescriptionExample
kSAMKeychainAccountKeyAccount identifier"user@example.com"
kSAMKeychainWhereKeyService name"com.company.MyApp"
kSAMKeychainLabelKeyItem label"MyApp Account"
kSAMKeychainCreatedAtKeyCreation timestamp"2024-03-08T10:30:00Z"
kSAMKeychainLastModifiedKeyLast modified timestamp"2024-03-08T14:22:00Z"
kSAMKeychainDescriptionKeyItem description"User credentials"
NSArray *accounts = [SAMKeychain allAccounts];

for (NSDictionary *accountInfo in accounts) {
    NSString *account = accountInfo[kSAMKeychainAccountKey];
    NSString *service = accountInfo[kSAMKeychainWhereKey];
    NSString *label = accountInfo[kSAMKeychainLabelKey];
    NSString *created = accountInfo[kSAMKeychainCreatedAtKey];
    
    NSLog(@"Account: %@", account);
    NSLog(@"  Service: %@", service);
    NSLog(@"  Label: %@", label);
    NSLog(@"  Created: %@", created);
}

Filtering Accounts by Service

Retrieve only accounts for a specific service:
NSString *serviceName = @"com.company.MyApp";
NSArray<NSDictionary *> *accounts = [SAMKeychain accountsForService:serviceName];

NSLog(@"Found %lu accounts for %@", (unsigned long)accounts.count, serviceName);

for (NSDictionary *accountInfo in accounts) {
    NSString *account = accountInfo[kSAMKeychainAccountKey];
    NSLog(@"  - %@", account);
}

Listing All Services

To get a list of all services with saved credentials:
- (NSArray<NSString *> *)allServices {
    NSArray *accounts = [SAMKeychain allAccounts];
    NSMutableSet *services = [NSMutableSet set];
    
    for (NSDictionary *accountInfo in accounts) {
        NSString *service = accountInfo[kSAMKeychainWhereKey];
        if (service) {
            [services addObject:service];
        }
    }
    
    return [services allObjects];
}

Updating Passwords

To update a password, simply call setPassword:forService:account: with the same service and account:
// Update existing password
BOOL success = [SAMKeychain setPassword:@"newPassword123"
                             forService:@"com.company.MyApp"
                                account:@"user@example.com"];

if (success) {
    NSLog(@"Password updated successfully");
}
SAMKeychain automatically:
  1. Checks if an item exists with the given service and account
  2. Updates the password if it exists
  3. Creates a new item if it doesn’t exist
You don’t need to delete the old password before saving a new one. SAMKeychain handles the update automatically.

Deleting Accounts

Deleting a Single Account

BOOL success = [SAMKeychain deletePasswordForService:@"com.company.MyApp"
                                            account:@"user@example.com"];

if (success) {
    NSLog(@"Account deleted");
} else {
    NSLog(@"Failed to delete account");
}

Deleting with Error Handling

NSError *error = nil;
BOOL success = [SAMKeychain deletePasswordForService:@"com.company.MyApp"
                                            account:@"user@example.com"
                                              error:&error];

if (success) {
    NSLog(@"Account deleted successfully");
} else if (error) {
    if (error.code == errSecItemNotFound) {
        NSLog(@"Account doesn't exist");
    } else {
        NSLog(@"Error deleting account: %@", error.localizedDescription);
    }
}

Deleting All Accounts for a Service

- (void)deleteAllAccountsForService:(NSString *)serviceName {
    NSError *error = nil;
    NSArray *accounts = [SAMKeychain accountsForService:serviceName error:&error];
    
    if (error) {
        NSLog(@"Error fetching accounts: %@", error);
        return;
    }
    
    for (NSDictionary *accountInfo in accounts) {
        NSString *account = accountInfo[kSAMKeychainAccountKey];
        
        BOOL deleted = [SAMKeychain deletePasswordForService:serviceName
                                                     account:account
                                                       error:&error];
        
        if (deleted) {
            NSLog(@"Deleted account: %@", account);
        } else {
            NSLog(@"Failed to delete %@: %@", account, error);
        }
    }
}

Deleting All Accounts

- (void)deleteAllKeyChainItems {
    NSArray *accounts = [SAMKeychain allAccounts];
    
    for (NSDictionary *accountInfo in accounts) {
        NSString *service = accountInfo[kSAMKeychainWhereKey];
        NSString *account = accountInfo[kSAMKeychainAccountKey];
        
        [SAMKeychain deletePasswordForService:service account:account];
    }
    
    NSLog(@"Deleted %lu keychain items", (unsigned long)accounts.count);
}
Deleting keychain items is permanent and cannot be undone. Always confirm with the user before deleting credentials.

Common Use Cases

Account Switcher

- (void)showAccountSwitcher {
    NSString *serviceName = @"com.company.MyApp";
    NSArray *accounts = [SAMKeychain accountsForService:serviceName];
    
    UIAlertController *alert = [UIAlertController 
        alertControllerWithTitle:@"Switch Account"
                         message:@"Select an account"
                  preferredStyle:UIAlertControllerStyleActionSheet];
    
    // Add option for each saved account
    for (NSDictionary *accountInfo in accounts) {
        NSString *account = accountInfo[kSAMKeychainAccountKey];
        
        UIAlertAction *action = [UIAlertAction 
            actionWithTitle:account
                      style:UIAlertActionStyleDefault
                    handler:^(UIAlertAction *action) {
                        [self switchToAccount:account];
                    }];
        
        [alert addAction:action];
    }
    
    // Add cancel button
    [alert addAction:[UIAlertAction actionWithTitle:@"Cancel"
                                             style:UIAlertActionStyleCancel
                                           handler:nil]];
    
    [self presentViewController:alert animated:YES completion:nil];
}

- (void)switchToAccount:(NSString *)account {
    NSString *password = [SAMKeychain passwordForService:@"com.company.MyApp"
                                                 account:account];
    
    if (password) {
        [self loginWithAccount:account password:password];
    }
}

Checking for Existing Accounts

- (BOOL)hasAnyAccounts {
    NSArray *accounts = [SAMKeychain accountsForService:@"com.company.MyApp"];
    return accounts.count > 0;
}

- (BOOL)hasAccount:(NSString *)account {
    NSArray *accounts = [SAMKeychain accountsForService:@"com.company.MyApp"];
    
    for (NSDictionary *accountInfo in accounts) {
        NSString *savedAccount = accountInfo[kSAMKeychainAccountKey];
        if ([savedAccount isEqualToString:account]) {
            return YES;
        }
    }
    
    return NO;
}

Migrating Service Names

- (void)migrateFromOldServiceToNewService {
    NSString *oldService = @"MyApp";
    NSString *newService = @"com.company.MyApp";
    
    // Get all accounts from old service
    NSArray *accounts = [SAMKeychain accountsForService:oldService];
    
    for (NSDictionary *accountInfo in accounts) {
        NSString *account = accountInfo[kSAMKeychainAccountKey];
        
        // Get password from old service
        NSString *password = [SAMKeychain passwordForService:oldService
                                                     account:account];
        
        if (password) {
            // Save to new service
            [SAMKeychain setPassword:password
                          forService:newService
                             account:account];
            
            // Delete from old service
            [SAMKeychain deletePasswordForService:oldService
                                          account:account];
            
            NSLog(@"Migrated account: %@", account);
        }
    }
}

Password Change Flow

- (void)changePasswordForCurrentUser {
    UIAlertController *alert = [UIAlertController
        alertControllerWithTitle:@"Change Password"
                         message:@"Enter your new password"
                  preferredStyle:UIAlertControllerStyleAlert];
    
    [alert addTextFieldWithConfigurationHandler:^(UITextField *textField) {
        textField.placeholder = @"New Password";
        textField.secureTextEntry = YES;
    }];
    
    [alert addTextFieldWithConfigurationHandler:^(UITextField *textField) {
        textField.placeholder = @"Confirm Password";
        textField.secureTextEntry = YES;
    }];
    
    UIAlertAction *changeAction = [UIAlertAction
        actionWithTitle:@"Change"
                  style:UIAlertActionStyleDefault
                handler:^(UIAlertAction *action) {
                    UITextField *newPassword = alert.textFields[0];
                    UITextField *confirmPassword = alert.textFields[1];
                    
                    if ([newPassword.text isEqualToString:confirmPassword.text]) {
                        [self updatePassword:newPassword.text];
                    } else {
                        [self showError:@"Passwords don't match"];
                    }
                }];
    
    [alert addAction:changeAction];
    [alert addAction:[UIAlertAction actionWithTitle:@"Cancel"
                                             style:UIAlertActionStyleCancel
                                           handler:nil]];
    
    [self presentViewController:alert animated:YES completion:nil];
}

- (void)updatePassword:(NSString *)newPassword {
    NSString *currentUser = self.currentUsername;
    NSError *error = nil;
    
    BOOL success = [SAMKeychain setPassword:newPassword
                                 forService:@"com.company.MyApp"
                                    account:currentUser
                                      error:&error];
    
    if (success) {
        [self showSuccess:@"Password updated successfully"];
    } else {
        [self showError:[NSString stringWithFormat:@"Failed to update: %@",
                        error.localizedDescription]];
    }
}

Account Cleanup on Logout

- (void)logoutWithOptions:(LogoutOptions)options {
    NSString *currentAccount = self.currentUsername;
    
    if (options & LogoutOptionRememberAccount) {
        // Keep password in keychain for next login
        NSLog(@"Password saved for %@", currentAccount);
    } else if (options & LogoutOptionForgetAccount) {
        // Remove password from keychain
        BOOL deleted = [SAMKeychain deletePasswordForService:@"com.company.MyApp"
                                                     account:currentAccount];
        
        if (deleted) {
            NSLog(@"Password removed for %@", currentAccount);
        }
    }
    
    // Clear session
    [self clearSession];
    [self showLoginScreen];
}

Building an Account List UI

- (NSArray<AccountViewModel *> *)savedAccounts {
    NSMutableArray *viewModels = [NSMutableArray array];
    
    NSArray *accounts = [SAMKeychain accountsForService:@"com.company.MyApp"];
    
    for (NSDictionary *accountInfo in accounts) {
        AccountViewModel *vm = [[AccountViewModel alloc] init];
        vm.username = accountInfo[kSAMKeychainAccountKey];
        vm.label = accountInfo[kSAMKeychainLabelKey];
        vm.lastModified = accountInfo[kSAMKeychainLastModifiedKey];
        
        // Check if this is the current account
        vm.isCurrent = [vm.username isEqualToString:self.currentUsername];
        
        [viewModels addObject:vm];
    }
    
    return viewModels;
}

Best Practices

Confirm Before Deleting

- (void)deleteAccount:(NSString *)account {
    UIAlertController *alert = [UIAlertController
        alertControllerWithTitle:@"Delete Account"
                         message:[NSString stringWithFormat:
                                 @"Remove saved password for %@?", account]
                  preferredStyle:UIAlertControllerStyleAlert];
    
    UIAlertAction *deleteAction = [UIAlertAction
        actionWithTitle:@"Delete"
                  style:UIAlertActionStyleDestructive
                handler:^(UIAlertAction *action) {
                    [SAMKeychain deletePasswordForService:@"com.company.MyApp"
                                                  account:account];
                }];
    
    [alert addAction:deleteAction];
    [alert addAction:[UIAlertAction actionWithTitle:@"Cancel"
                                             style:UIAlertActionStyleCancel
                                           handler:nil]];
    
    [self presentViewController:alert animated:YES completion:nil];
}

Handle Empty Results

- (void)displaySavedAccounts {
    NSArray *accounts = [SAMKeychain accountsForService:@"com.company.MyApp"];
    
    if (accounts.count == 0) {
        [self showEmptyState:@"No saved accounts"];
    } else {
        [self showAccountList:accounts];
    }
}

Use Consistent Service Names

// Define service name as a constant
static NSString * const kMyAppKeychainService = @"com.company.MyApp";

- (NSArray *)savedAccounts {
    return [SAMKeychain accountsForService:kMyAppKeychainService];
}

- (void)savePassword:(NSString *)password forAccount:(NSString *)account {
    [SAMKeychain setPassword:password
                  forService:kMyAppKeychainService
                     account:account];
}

Next Steps

Advanced Queries

Use SAMKeychainQuery for complex operations

Synchronization

Sync passwords across devices with iCloud

Storing Passwords

Learn best practices for password storage

API Reference

Complete API documentation