iOS File Protection Reference
Purpose: Comprehensive reference for file encryption and data protection APIs
Availability: iOS 4.0+ (all protection levels), latest enhancements in iOS 26
Context: Built on iOS Data Protection architecture using hardware encryption
When to Use This Skill
Use this skill when you need to:
- Protect sensitive user data at rest
- Choose appropriate FileProtectionType for files
- Understand when files are accessible/encrypted
- Debug "file not accessible" errors after device lock
- Implement secure file storage
- Compare Keychain vs file protection approaches
- Handle background file access requirements
Overview
iOS Data Protection provides hardware-accelerated file encryption tied to the device passcode. When a user sets a passcode, every file can be encrypted with keys protected by that passcode.
Key concepts:
- Files are encrypted automatically when protection is enabled
- Encryption keys are derived from device hardware + user passcode
- Files become inaccessible when device is locked (depending on protection level)
- No performance cost (hardware AES encryption)
Protection Levels Comparison
| Level |
Encrypted Until |
Accessible When |
Use For |
Background Access |
| complete |
Device unlocked |
Only while unlocked |
Sensitive data (health, finances) |
β No |
| completeUnlessOpen |
File closed |
After first unlock, while open |
Large downloads, videos |
β
If already open |
| completeUntilFirstUserAuthentication |
First unlock after boot |
After first unlock |
Most app data |
β
Yes |
| none |
Never |
Always |
Public caches, temp files |
β
Yes |
Detailed Level Descriptions
.complete
Full Description:
"The file is stored in an encrypted format on disk and cannot be read from or written to while the device is locked or booting."
Use For:
- User health data
- Financial information
- Password vaults
- Sensitive documents
- Personal photos (if app requires maximum security)
Behavior:
- Encrypted: β
Always
- Accessible: Only when device unlocked
- Background access: β No (app can't read while locked)
- Available after boot: β No (until user unlocks)
Code Example:
func saveSensitiveData(_ data: Data, to url: URL) throws {
try data.write(to: url, options: .completeFileProtection)
}
try FileManager.default.setAttributes(
[.protectionKey: FileProtectionType.complete],
ofItemAtPath: url.path
)
Tradeoffs:
- β
Maximum security
- β Can't access in background
- β User sees errors if app tries to access while locked
.completeUnlessOpen
Full Description:
"The file is stored in an encrypted format on disk after it is closed."
Use For:
- Large file downloads (continue in background)
- Video files being played
- Documents being edited
- Any file that needs background access while open
Behavior:
- Encrypted: β
When closed
- Accessible: After first unlock, remains accessible while open
- Background access: β
Yes (if file was already open)
- Available after boot: β No (until first unlock)
Code Example:
func startBackgroundDownload(url: URL, destination: URL) throws {
try Data().write(to: destination, options: .completeFileProtectionUnlessOpen)
let fileHandle = try FileHandle(forWritingTo: destination)
try fileHandle.close()
}
Tradeoffs:
- β
Good security (encrypted when not in use)
- β
Background access (if already open)
- β οΈ Vulnerable while open
.completeUntilFirstUserAuthentication
Full Description:
"The file is stored in an encrypted format on disk and cannot be accessed until after the device has booted."
Use For:
- Most application data
- User preferences
- Downloaded content
- Database files
- Anything that needs background access
Behavior:
- Encrypted: β
Always
- Accessible: After first unlock following boot
- Background access: β
Yes (after first unlock)
- Available after boot: β No (until user unlocks once)
This is the recommended default for most files.
Code Example:
func saveAppData(_ data: Data, to url: URL) throws {
try data.write(
to: url,
options: .completeFileProtectionUntilFirstUserAuthentication
)
}
func backgroundTaskCanAccessFile() {
let data = try? Data(contentsOf: url)
}
Tradeoffs:
- β
Protected during boot (device stolen while off)
- β
Background access (normal operation)
- β οΈ Accessible while locked (less protection than .complete)
.none
Full Description:
"The file has no special protections associated with it."
Use For:
- Public cache data
- Temporary files
- Non-sensitive downloads
- Thumbnails
- Only when absolutely necessary
Behavior:
- Encrypted: β Never
- Accessible: β
Always
- Background access: β
Always
- Available after boot: β
Always
Code Example:
func cachePublicThumbnail(_ data: Data, to url: URL) throws {
try data.write(to: url, options: .noFileProtection)
}
Tradeoffs:
- β
Always accessible
- β No encryption
- β Vulnerable if device is stolen
Setting File Protection
At File Creation
let sensitiveData = userData.jsonData()
try sensitiveData.write(
to: fileURL,
options: .completeFileProtection
)
On Existing Files
try FileManager.default.setAttributes(
[.protectionKey: FileProtectionType.complete],
ofItemAtPath: fileURL.path
)
Default Protection for Directory
try FileManager.default.setAttributes(
[.protectionKey: FileProtectionType.completeUntilFirstUserAuthentication],
ofItemAtPath: directoryURL.path
)
Checking Current Protection
func checkFileProtection(at url: URL) throws -> FileProtectionType? {
let attributes = try FileManager.default.attributesOfItem(atPath: url.path)
return attributes[.protectionKey] as? FileProtectionType
}
if let protection = try? checkFileProtection(at: fileURL) {
switch protection {
case .complete:
print("Maximum protection")
case .completeUntilFirstUserAuthentication:
print("Standard protection")
default:
print("Other protection")
}
}
File Protection vs Keychain
Decision Matrix
| Use Case |
Recommended |
Why |
| Passwords, tokens, keys |
Keychain |
Designed for small secrets |
| Small sensitive values (<few KB) |
Keychain |
More secure, encrypted separately |
| Files >1 KB |
File Protection |
Keychain not designed for large data |
| User documents |
File Protection |
Natural file-based storage |
| Structured secrets |
Keychain |
Query by key, access control |
Code Comparison
let passwordData = password.data(using: .utf8)!
let query: [String: Any] = [
kSecClass as String: kSecClassGenericPassword,
kSecAttrAccount as