Skip to main content

Overview

Retrieving passwords from the keychain is just as simple as storing them. This guide covers the different retrieval methods and how to handle various scenarios like missing data and errors.

Basic Password Retrieval

To retrieve a password, provide the same service and account used when storing it:
NSString *password = [SAMKeychain passwordForService:@"MyApp"
                                             account:@"user@example.com"];

if (password) {
    NSLog(@"Retrieved password: %@", password);
} else {
    NSLog(@"Password not found");
}
The method returns nil if no matching keychain item is found. Always check for nil before using the returned password.

Retrieving String vs. Data

Just like with storage, SAMKeychain provides separate methods for strings and binary data:

String Passwords

// Retrieve as string (automatically converts from UTF-8 NSData)
NSString *password = [SAMKeychain passwordForService:@"MyApp"
                                             account:@"user@example.com"];

Binary Data

// Retrieve as NSData
NSData *encryptionKey = [SAMKeychain passwordDataForService:@"MyApp"
                                                    account:@"encryptionKey"];

if (encryptionKey) {
    // Use the binary data
    NSLog(@"Retrieved %lu bytes", (unsigned long)encryptionKey.length);
}
Use passwordForService:account: for text passwords and passwordDataForService:account: for binary data or serialized objects.

Handling Missing Items

When a keychain item doesn’t exist, the retrieval methods return nil. Here’s how to handle this gracefully:

Basic Nil Check

NSString *password = [SAMKeychain passwordForService:@"MyApp"
                                             account:@"user@example.com"];

if (password) {
    // Password found, proceed with login
    [self loginWithPassword:password];
} else {
    // No saved password, show login screen
    [self showLoginScreen];
}

Checking for Saved Credentials

- (BOOL)hasSavedCredentialsForAccount:(NSString *)account {
    NSString *password = [SAMKeychain passwordForService:@"com.company.MyApp"
                                                 account:account];
    return password != nil;
}

// Usage
if ([self hasSavedCredentialsForAccount:@"user@example.com"]) {
    // Auto-fill login form
    self.passwordField.text = [SAMKeychain passwordForService:@"com.company.MyApp"
                                                      account:@"user@example.com"];
}

Providing Default Values

- (NSString *)passwordForAccount:(NSString *)account {
    NSString *password = [SAMKeychain passwordForService:@"MyApp"
                                                 account:account];
    
    // Return empty string instead of nil for UI binding
    return password ?: @"";
}

Error Handling

For detailed error information, use the error parameter variant:
NSError *error = nil;
NSString *password = [SAMKeychain passwordForService:@"MyApp"
                                             account:@"user@example.com"
                                               error:&error];

if (password) {
    NSLog(@"Password retrieved successfully");
} else if (error) {
    // An error occurred
    NSLog(@"Error retrieving password: %@", error.localizedDescription);
    NSLog(@"Error code: %ld", (long)error.code);
    
    // Handle specific errors
    if (error.code == errSecItemNotFound) {
        NSLog(@"No password saved for this account");
    } else if (error.code == errSecInteractionNotAllowed) {
        NSLog(@"User denied access to keychain");
    }
} else {
    // No error but also no password (shouldn't normally happen)
    NSLog(@"Password not found");
}

Common Error Codes

When retrieving passwords, you may encounter these error codes:
Error CodeConstantMeaning
-25300errSecItemNotFoundNo matching keychain item exists
-25293errSecInteractionNotAllowedUser authentication required or denied
-34018errSecMissingEntitlementApp lacks keychain access entitlement
-25308errSecInteractionRequiredUser interaction required (e.g., Touch ID)
-128errSecAuthFailedAuthentication failed
errSecItemNotFound is not always an error—it simply means no password was previously saved. Handle this as a normal case in your app flow.

Retrieving with SAMKeychainQuery

For more control, use SAMKeychainQuery directly:
SAMKeychainQuery *query = [[SAMKeychainQuery alloc] init];
query.service = @"MyApp";
query.account = @"user@example.com";

NSError *error = nil;
if ([query fetch:&error]) {
    // Access the password
    NSString *password = query.password;
    NSData *passwordData = query.passwordData;
    
    NSLog(@"Retrieved password: %@", password);
} else {
    NSLog(@"Fetch failed: %@", error);
}

Best Practices

Always Check for Nil

// ✅ Good: Check for nil
NSString *password = [SAMKeychain passwordForService:@"MyApp" account:account];
if (password) {
    [self authenticateWithPassword:password];
}

// ❌ Bad: Assuming password exists
NSString *password = [SAMKeychain passwordForService:@"MyApp" account:account];
[self authenticateWithPassword:password]; // Could pass nil!

Use Error Parameter in Production

// For debugging and production, use error parameter
NSError *error = nil;
NSString *password = [SAMKeychain passwordForService:@"MyApp"
                                             account:account
                                               error:&error];

if (!password && error) {
    // Log error details for troubleshooting
    NSLog(@"Keychain error: %@ (code: %ld)", error.localizedDescription, (long)error.code);
    
    // Report to analytics/crash reporting
    [Analytics logError:error];
}

Secure Password Handling

- (void)loginWithSavedCredentials {
    NSString *password = [SAMKeychain passwordForService:@"MyApp"
                                                 account:self.username];
    
    if (password) {
        // Use password immediately
        [self performLoginWithPassword:password];
        
        // Don't store in properties or instance variables
        // Let ARC handle memory cleanup
    }
}

// ❌ Avoid storing passwords in memory longer than needed
@property (nonatomic, strong) NSString *cachedPassword; // Don't do this!

Handle Device Lock State

- (NSString *)retrievePasswordSafely {
    NSError *error = nil;
    NSString *password = [SAMKeychain passwordForService:@"MyApp"
                                                 account:@"user@example.com"
                                                   error:&error];
    
    if (!password && error) {
        if (error.code == errSecInteractionNotAllowed) {
            // Device is locked or requires authentication
            [self showDeviceLockedMessage];
        }
    }
    
    return password;
}

Common Use Cases

Auto-Login Flow

- (void)attemptAutoLogin {
    // Check if we have saved credentials
    NSString *lastUsername = [[NSUserDefaults standardUserDefaults] 
                             stringForKey:@"lastUsername"];
    
    if (!lastUsername) {
        [self showLoginScreen];
        return;
    }
    
    // Try to retrieve password
    NSError *error = nil;
    NSString *password = [SAMKeychain passwordForService:@"com.company.MyApp"
                                                 account:lastUsername
                                                   error:&error];
    
    if (password) {
        // Auto-login
        [self loginWithUsername:lastUsername password:password];
    } else {
        // Password not found or error occurred
        [self showLoginScreenWithUsername:lastUsername];
    }
}

Retrieving Multiple Accounts

- (NSDictionary<NSString *, NSString *> *)allSavedCredentials {
    NSMutableDictionary *credentials = [NSMutableDictionary dictionary];
    
    // Get all accounts for your service
    NSArray *accounts = [SAMKeychain accountsForService:@"com.company.MyApp"];
    
    for (NSDictionary *accountInfo in accounts) {
        NSString *account = accountInfo[kSAMKeychainAccountKey];
        
        // Retrieve password for each account
        NSString *password = [SAMKeychain passwordForService:@"com.company.MyApp"
                                                     account:account];
        
        if (password) {
            credentials[account] = password;
        }
    }
    
    return credentials;
}

Retrieving API Tokens

- (NSString *)apiTokenForService:(NSString *)serviceName {
    NSString *token = [SAMKeychain passwordForService:serviceName
                                              account:@"api_token"];
    
    if (!token) {
        NSLog(@"No API token found for %@", serviceName);
        // Trigger OAuth flow or login
        [self authenticateForService:serviceName];
    }
    
    return token;
}

Decrypting Retrieved Data

- (id)retrieveSecureObject:(NSString *)identifier {
    // Retrieve encrypted data
    NSData *encryptedData = [SAMKeychain passwordDataForService:@"MyApp.SecureStorage"
                                                        account:identifier];
    
    if (!encryptedData) {
        return nil;
    }
    
    // Decrypt and unarchive
    NSData *decryptedData = [self decryptData:encryptedData];
    return [NSKeyedUnarchiver unarchiveObjectWithData:decryptedData];
}

Validating Retrieved Credentials

- (BOOL)validateSavedCredentials:(NSString *)account {
    NSError *error = nil;
    NSString *password = [SAMKeychain passwordForService:@"MyApp"
                                                 account:account
                                                   error:&error];
    
    if (!password) {
        return NO;
    }
    
    // Validate password format/strength
    if (password.length < 8) {
        // Old/invalid password, remove it
        [SAMKeychain deletePasswordForService:@"MyApp" account:account];
        return NO;
    }
    
    return YES;
}

Security Considerations

Don’t Log Passwords

// ❌ NEVER log actual passwords
NSString *password = [SAMKeychain passwordForService:@"MyApp" account:account];
NSLog(@"Password: %@", password); // DON'T DO THIS!

// ✅ Log that password was found without revealing it
NSString *password = [SAMKeychain passwordForService:@"MyApp" account:account];
NSLog(@"Password %@", password ? @"found" : @"not found");

Clear Sensitive Data

- (void)performSecureOperation {
    @autoreleasepool {
        NSString *password = [SAMKeychain passwordForService:@"MyApp"
                                                     account:@"user@example.com"];
        
        if (password) {
            // Use password
            [self authenticateWithPassword:password];
            
            // Password is released when autoreleasepool drains
        }
    }
}

Verify Before Using

- (void)loginWithSavedPassword {
    NSString *password = [SAMKeychain passwordForService:@"MyApp"
                                                 account:self.usernameField.text];
    
    if (password && password.length > 0) {
        // Verify it's a valid string before using
        [self attemptLoginWithUsername:self.usernameField.text
                              password:password];
    } else {
        [self showError:@"Please enter your password"];
    }
}

Next Steps

Storing Passwords

Learn how to securely store passwords

Managing Accounts

List, update, and delete keychain items

Error Handling

Advanced error handling with SAMKeychainQuery

API Reference

Complete API documentation

Build docs developers (and LLMs) love