Data Protection Classes
iOS Data Protection Overview
iOS provides four data protection classes that determine when encrypted data is accessible:
| Protection Class | When Accessible | Use Case |
|---|---|---|
| .complete | Only when device is unlocked | Highly sensitive data |
| .completeUnlessOpen | When unlocked or file is already open | Background downloads |
| .completeAfterFirstUserAuthentication | After first unlock | Data needed at boot |
| .completeUntilFirstUserAuthentication | Until device restarts | Session data |
.complete Protection
Data is encrypted and only accessible when the device is unlocked:
let fileURL = documentsDirectory.appendingPathComponent("sensitive.dat")
let options: [FileAttributeKey: Any] = [
.protectionKey: FileProtectionType.complete
]
FileManager.default.createFile(
atPath: fileURL.path,
contents: sensitiveData,
attributes: options
)
.completeUnlessOpen Protection
Data remains accessible if the file is already open when the device locks. Useful for background downloads that need to continue:
let options: [FileAttributeKey: Any] = [
.protectionKey: FileProtectionType.completeUnlessOpen
]
FileManager.default.createFile(
atPath: fileURL.path,
contents: data,
attributes: options
)
.completeAfterFirstUserAuthentication
Data becomes accessible after the user unlocks the device for the first time after a restart. This is the most common protection level for most apps:
let options: [FileAttributeKey: Any] = [
.protectionKey: FileProtectionType.completeAfterFirstUserAuthentication
]
Choosing the Right Level
- .complete: Banking credentials, health data, payment information
- .completeUnlessOpen: Email attachments being downloaded, media downloads
- .completeAfterFirstUserAuthentication: User preferences, cached data, most app data
- .completeUntilFirstUserAuthentication: Authentication tokens, session data
File Encryption
Applying Protection to Files
When you create or modify a file, specify the protection level in the attributes:
func saveSecureFile(_ data: Data, named filename: String) throws {
let fileURL = documentsDirectory.appendingPathComponent(filename)
let attributes: [FileAttributeKey: Any] = [
.protectionKey: FileProtectionType.completeAfterFirstUserAuthentication
]
try data.write(to: fileURL, options: [.completeFileProtection])
}
Modifying Existing File Protection
Change the protection level of an existing file:
func changeProtectionLevel(for url: URL, to level: FileProtectionType) throws {
try FileManager.default.setAttributes(
[.protectionKey: level],
ofItemAtPath: url.path
)
}
Encryption with CryptoKit
For additional encryption beyond file protection, use CryptoKit:
import CryptoKit
class EncryptionService {
private let key: SymmetricKey
init() {
// In production, store key in Keychain
self.key = SymmetricKey(size: .bits256)
}
func encrypt(_ data: Data) throws -> Data {
let sealed = try AES.GCM.seal(data, using: key)
return sealed.combined!
}
func decrypt(_ data: Data) throws -> Data {
let sealedBox = try AES.GCM.SealedBox(combined: data)
return try AES.GCM.open(sealedBox, using: key)
}
}
Secure Deletion
Overwrite data before deletion to prevent forensic recovery:
func secureDelete(at url: URL) throws {
let attributes = try FileManager.default.attributesOfItem(atPath: url.path)
guard let fileSize = attributes[.size] as? Int else { return }
// Overwrite with random data
let randomData = Data((0..<fileSize).map { _ in UInt8.random(in: 0...255) })
try randomData.write(to: url, options: .atomic)
// Then delete
try FileManager.default.removeItem(at: url)
}
Keychain Protection
Keychain Access Control
The Keychain stores sensitive data with hardware-backed encryption. Set access control to require biometrics or passcode:
import Security
class KeychainService {
func save(_ data: Data, for key: String, requiresBiometric: Bool) throws {
let access: SecAccessControl?
if requiresBiometric {
access = SecAccessControlCreateWithFlags(
kCFAllocatorDefault,
kSecAttrAccessibleWhenUnlockedThisDeviceOnly,
[.biometryCurrentSet, .or, .devicePasscode],
nil
)
} else {
access = nil
}
let query: [String: Any] = [
kSecClass as String: kSecClassGenericPassword,
kSecAttrAccount as String: key,
kSecValueData as String: data,
kSecAttrAccessible as String: kSecAttrAccessibleWhenUnlockedThisDeviceOnly
]
SecItemDelete(query as CFDictionary)
let status = SecItemAdd(query as CFDictionary, nil)
guard status == errSecSuccess else {
throw KeychainError.saveFailed(status)
}
}
}
Keychain Accessibility Levels
| Level | When Accessible |
|---|---|
| WhenUnlocked | Device unlocked |
| AfterFirstUnlock | After first unlock post-restart |
| WhenUnlockedThisDeviceOnly | Unlocked, not transferred to new device |
| AfterFirstUnlockThisDeviceOnly | After first unlock, not transferred |
Best Practices
- Use ThisDeviceOnly variants to prevent keychain data from transferring to new devices via backup
- Store encryption keys, tokens, and credentials in Keychain
- Set appropriate access control flags for biometric protection
- Handle Keychain errors gracefully and provide fallback authentication
- Never store large data in Keychain, use encrypted files instead
Quiz
1. When is data with .complete protection accessible?
2. Which protection class should you use for email attachments being downloaded?
3. Why should you use ThisDeviceOnly Keychain variants?
4. What framework provides symmetric encryption in iOS?
Flashcards
Question
What are the four iOS data protection classes?
Click to reveal answer
Answer
.complete (unlocked only), .completeUnlessOpen (unlocked or file open), .completeAfterFirstUserAuthentication (after first unlock), .completeUntilFirstUserAuthentication (until restart).
Question
What is the difference between Keychain and NSUserDefaults for sensitive data?
Click to reveal answer
Answer
Keychain encrypts data with hardware-backed security and supports access control. NSUserDefaults stores data in plaintext plist files.
Question
When should you use .complete file protection?
Click to reveal answer
Answer
For highly sensitive data like banking credentials, health data, and payment information that should only be accessible when the device is unlocked.
Revision Notes
Key Takeaways
- 1. iOS provides four data protection classes for different security needs
- 2. .completeAfterFirstUserAuthentication is the most common protection level
- 3. Keychain provides hardware-backed encryption for sensitive data
- 4. CryptoKit offers modern symmetric and asymmetric encryption
- 5. Always use ThisDeviceOnly variants to prevent data transfer
Interview Tips
- • Explain the four data protection classes and when to use each
- • Describe how Keychain differs from NSUserDefaults for sensitive data
- • Discuss how to implement file encryption with CryptoKit
- • Walk through securing user authentication tokens
Cheat Sheet
Data Protection Quick Reference
- .complete: unlocked only
- .completeUnlessOpen: unlocked or file already open
- .completeAfterFirstUserAuthentication: after first unlock (most common)
- Keychain for tokens, keys, credentials
- CryptoKit for additional encryption
- Use ThisDeviceOnly Keychain variants