Skip to main content

Overview

SAMKeychain makes it easy to securely store passwords and sensitive data in the system keychain. This guide covers the different storage options and best practices for keeping your users’ data safe.

Basic Password Storage

The simplest way to store a password is using the class method that takes a service name and account identifier:
BOOL success = [SAMKeychain setPassword:@"mySecretPassword"
                             forService:@"MyApp"
                                account:@"user@example.com"];

if (success) {
    NSLog(@"Password saved successfully");
}
The service name typically represents your app or the service the credentials are for, while the account identifies the specific user or account.

Storing vs. Updating

When you call setPassword:forService:account:, SAMKeychain automatically:
  1. Checks if an item already exists for the given service and account
  2. Updates the existing item if found
  3. Creates a new item if not found
You don’t need to check whether the item exists first—SAMKeychain handles this for you.
// First time: creates new keychain item
[SAMKeychain setPassword:@"password123"
              forService:@"MyApp"
                 account:@"user@example.com"];

// Second time: updates existing keychain item
[SAMKeychain setPassword:@"newPassword456"
              forService:@"MyApp"
                 account:@"user@example.com"];

String vs. Data Storage

SAMKeychain provides two storage options depending on your data type:

String Passwords

For text-based passwords and tokens:
NSString *password = @"myPassword123";
[SAMKeychain setPassword:password
              forService:@"MyApp"
                 account:@"user@example.com"];
Internally, strings are converted to UTF-8 encoded NSData before storage.

Binary Data

For binary data, encryption keys, or custom data formats:
// Store binary data (e.g., encryption key)
NSData *encryptionKey = [[NSData alloc] initWithBytes:keyBytes length:32];
[SAMKeychain setPasswordData:encryptionKey
                  forService:@"MyApp"
                     account:@"encryptionKey"];

// Store archived objects
NSData *archivedData = [NSKeyedArchiver archivedDataWithRootObject:myObject];
[SAMKeychain setPasswordData:archivedData
                  forService:@"MyApp"
                     account:@"cachedObject"];
Use setPasswordData: for non-text data like encryption keys, certificates, or serialized objects.

Error Handling

For production code, always check for errors:
NSError *error = nil;
BOOL success = [SAMKeychain setPassword:@"myPassword"
                             forService:@"MyApp"
                                account:@"user@example.com"
                                  error:&error];

if (!success) {
    NSLog(@"Failed to save password: %@", error.localizedDescription);
    
    // Handle specific error codes
    if (error.code == SAMKeychainErrorBadArguments) {
        NSLog(@"Invalid arguments provided");
    }
}

Choosing Accessibility Options

iOS and macOS provide different accessibility levels that control when keychain items can be accessed:
1

Understand the options

  • kSecAttrAccessibleWhenUnlocked - Data accessible only while device is unlocked (most secure)
  • kSecAttrAccessibleAfterFirstUnlock - Data accessible after first unlock since boot (good for background tasks)
  • kSecAttrAccessibleAlways - Data always accessible (least secure, deprecated in iOS 12+)
  • kSecAttrAccessibleWhenPasscodeSetThisDeviceOnly - Requires device passcode, never synced
  • kSecAttrAccessibleWhenUnlockedThisDeviceOnly - Never synced to iCloud or other devices
  • kSecAttrAccessibleAfterFirstUnlockThisDeviceOnly - Never synced, available after first unlock
2

Set the global accessibility type

#import <Security/Security.h>

// Set for all future keychain operations
[SAMKeychain setAccessibilityType:kSecAttrAccessibleWhenUnlocked];

// Now store passwords - they'll use this accessibility level
[SAMKeychain setPassword:@"secret"
              forService:@"MyApp"
                 account:@"user@example.com"];
3

Choose based on your use case

Foreground apps: Use kSecAttrAccessibleWhenUnlocked for maximum security
[SAMKeychain setAccessibilityType:kSecAttrAccessibleWhenUnlocked];
Background apps: Use kSecAttrAccessibleAfterFirstUnlock to access data while app runs in background
[SAMKeychain setAccessibilityType:kSecAttrAccessibleAfterFirstUnlock];
Non-syncing sensitive data: Use the ThisDeviceOnly variants
[SAMKeychain setAccessibilityType:kSecAttrAccessibleWhenUnlockedThisDeviceOnly];
The default accessibility (when not set) is highly insecure. Always explicitly set an accessibility type appropriate for your use case.

Best Practices

Choose Meaningful Service and Account Names

// Good: Clear, consistent naming
[SAMKeychain setPassword:userPassword
              forService:@"com.company.MyApp"
                 account:userEmail];

// Avoid: Generic names that might conflict
[SAMKeychain setPassword:userPassword
              forService:@"app"
                 account:@"user"];

Use Reverse Domain Notation for Services

// Prevents conflicts with other apps
NSString *serviceName = @"com.yourcompany.yourapp";
[SAMKeychain setPassword:password
              forService:serviceName
                 account:account];

Never Store Passwords in UserDefaults

// ❌ NEVER do this
[[NSUserDefaults standardUserDefaults] setObject:password forKey:@"password"];

// ✅ Always use SAMKeychain
[SAMKeychain setPassword:password forService:@"MyApp" account:account];

Set Accessibility Before First Save

- (void)applicationDidFinishLaunching:(NSNotification *)notification {
    // Configure accessibility early
    [SAMKeychain setAccessibilityType:kSecAttrAccessibleAfterFirstUnlock];
    
    // Rest of your app initialization...
}

Validate Input Before Storing

- (BOOL)savePassword:(NSString *)password forAccount:(NSString *)account {
    // Validate inputs
    if (!password || password.length == 0) {
        NSLog(@"Password cannot be empty");
        return NO;
    }
    
    if (!account || account.length == 0) {
        NSLog(@"Account cannot be empty");
        return NO;
    }
    
    // Store in keychain
    NSError *error = nil;
    BOOL success = [SAMKeychain setPassword:password
                                 forService:@"com.company.MyApp"
                                    account:account
                                      error:&error];
    
    if (!success) {
        NSLog(@"Failed to save: %@", error);
    }
    
    return success;
}

Common Use Cases

Storing User Credentials

- (void)saveLoginCredentials:(NSString *)username password:(NSString *)password {
    NSString *serviceName = @"com.company.MyApp.login";
    
    NSError *error = nil;
    BOOL success = [SAMKeychain setPassword:password
                                 forService:serviceName
                                    account:username
                                      error:&error];
    
    if (success) {
        NSLog(@"Credentials saved for %@", username);
    } else {
        NSLog(@"Failed to save credentials: %@", error.localizedDescription);
    }
}

Storing API Tokens

- (void)saveAPIToken:(NSString *)token forService:(NSString *)serviceName {
    // Use a standard account name for API tokens
    NSString *account = @"api_token";
    
    [SAMKeychain setPassword:token
                  forService:serviceName
                     account:account];
}

Storing OAuth Refresh Tokens

- (void)saveOAuthTokens:(NSString *)accessToken 
           refreshToken:(NSString *)refreshToken
             forAccount:(NSString *)account {
    
    NSString *service = @"com.company.MyApp.oauth";
    
    // Store access token
    [SAMKeychain setPassword:accessToken
                  forService:[service stringByAppendingString:@".access"]
                     account:account];
    
    // Store refresh token
    [SAMKeychain setPassword:refreshToken
                  forService:[service stringByAppendingString:@".refresh"]
                     account:account];
}

Next Steps

Retrieving Passwords

Learn how to safely retrieve stored passwords

Managing Accounts

List, update, and delete keychain items

Synchronization

Sync passwords across devices with iCloud Keychain

Advanced Queries

Use SAMKeychainQuery for complex operations