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:
// ✅ CORRECT: Maximum security for sensitive data func saveSensitiveData(_ data: Data, to url: URL) throws { try data.write(to: url, options: .completeFileProtection) }
// Or set on existing file 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:
// ✅ CORRECT: Download in background, but encrypted when closed func startBackgroundDownload(url: URL, destination: URL) throws { try Data().write(to: destination, options: .completeFileProtectionUnlessOpen)
// Open file handle for writing
let fileHandle = try FileHandle(forWritingTo: destination)
// Download continues in background
// File remains accessible because it's open
// When closed, file becomes encrypted
// Later, when download complete:
try fileHandle.close() // Now encrypted until next unlock
}
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:
// ✅ CORRECT: Balanced security for most app data func saveAppData(_ data: Data, to url: URL) throws { try data.write( to: url, options: .completeFileProtectionUntilFirstUserAuthentication ) }
// ✅ This file can be accessed in background after first unlock func backgroundTaskCanAccessFile() { // This works even if device is locked (after first unlock) 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:
// ⚠️ USE SPARINGLY: Only for truly non-sensitive data 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
// ✅ RECOMMENDED: Set protection when writing let sensitiveData = userData.jsonData() try sensitiveData.write( to: fileURL, options: .completeFileProtection )
On Existing Files
// ✅ CORRECT: Change protection on existing file try FileManager.default.setAttributes( [.protectionKey: FileProtectionType.complete], ofItemAtPath: fileURL.path )
Default Protection for Directory
// ✅ CORRECT: Set default protection for directory // New files inherit this protection try FileManager.default.setAttributes( [.protectionKey: FileProtectionType.completeUntilFirstUserAuthentication], ofItemAtPath: directoryURL.path )
Checking Current Protection
// ✅ Check file's current protection level func checkFileProtection(at url: URL) throws -> FileProtectionType? { let attributes = try FileManager.default.attributesOfItem(atPath: url.path) return attributes[.protectionKey] as? FileProtectionType }
// Usage 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
// ✅ CORRECT: Small secrets in Keychain let passwordData = password.data(using: .utf8)! let query: [String: Any] = [ kSecClass as String: kSecClassGenericPassword, kSecAttrAccount as String: "userPassword", kSecValueData as String: passwordData, kSecAttrAccessible as String: kSecAttrAccessibleWhenUnlocked ] SecItemAdd(query as CFDictionary, nil)
// ✅ CORRECT: Files with file protection let userData = try JSONEncoder().encode(user) try userData.write(to: fileURL, options: .completeFileProtection)
Keychain advantages:
-
More granular access control (Face ID/Touch ID)
-
Separate encryption (not tied to file system)
-
Survives app deletion (if configured)
File protection advantages:
-
Works with existing file operations
-
Handles large data efficiently
-
Automatic with minimal code
Background Access Considerations
iOS Background Modes and File Protection
// ❌ WRONG: .complete files can't be accessed in background class BackgroundTask { func performBackgroundSync() { // This FAILS if file has .complete protection and device is locked let data = try? Data(contentsOf: sensitiveFileURL) // data will be nil if device locked } }
// ✅ CORRECT: Use .completeUntilFirstUserAuthentication // Files accessible in background after first unlock try data.write( to: fileURL, options: .completeFileProtectionUntilFirstUserAuthentication )
Handling Protection Errors
// ✅ CORRECT: Handle protection errors gracefully func readFile(at url: URL) -> Data? { do { return try Data(contentsOf: url) } catch let error as NSError { if error.domain == NSCocoaErrorDomain && error.code == NSFileReadNoPermissionError { // File is protected and device is locked print("File protected, device locked") return nil } throw error } }
iCloud and File Protection
How Protection Works with iCloud
Local file protection:
-
Applied to local cached copies
-
Does NOT affect iCloud-stored versions
-
iCloud has its own encryption (in transit and at rest)
iCloud encryption:
-
All iCloud data encrypted at rest (Apple-managed keys)
-
End-to-end encryption available for some data types (Advanced Data Protection)
-
File protection only affects local device
// ✅ CORRECT: Protection on iCloud file affects local copy only func saveToICloud(data: Data, filename: String) throws { guard let iCloudURL = FileManager.default.url( forUbiquityContainerIdentifier: nil ) else { return }
let fileURL = iCloudURL.appendingPathComponent(filename)
// This protection applies to local cached copy
try data.write(to: fileURL, options: .completeFileProtection)
// iCloud has separate encryption for cloud storage
}
Common Patterns
Pattern 1: Default Protection for New Apps
// ✅ RECOMMENDED: Set default protection at app launch func configureDefaultFileProtection() { let fileManager = FileManager.default
let directories: [FileManager.SearchPathDirectory] = [
.documentDirectory,
.applicationSupportDirectory
]
for directory in directories {
guard let url = fileManager.urls(
for: directory,
in: .userDomainMask
).first else { continue }
try? fileManager.setAttributes(
[.protectionKey: FileProtectionType.completeUntilFirstUserAuthentication],
ofItemAtPath: url.path
)
}
}
// Call during app initialization func application(_ application: UIApplication, didFinishLaunchingWithOptions...) { configureDefaultFileProtection() return true }
Pattern 2: Encrypting Database Files
// ✅ CORRECT: Protect SwiftData/SQLite database let appSupportURL = FileManager.default.urls( for: .applicationSupportDirectory, in: .userDomainMask )[0]
let databaseURL = appSupportURL.appendingPathComponent("app.sqlite")
// Set protection before creating database try? FileManager.default.setAttributes( [.protectionKey: FileProtectionType.completeUntilFirstUserAuthentication], ofItemAtPath: appSupportURL.path )
// Now create database - it inherits protection let container = try ModelContainer( for: MyModel.self, configurations: ModelConfiguration(url: databaseURL) )
Pattern 3: Downgrading Protection for Background Tasks
// ⚠️ SOMETIMES NECESSARY: Lower protection for background access func enableBackgroundAccess(for url: URL) throws { try FileManager.default.setAttributes( [.protectionKey: FileProtectionType.completeUntilFirstUserAuthentication], ofItemAtPath: url.path ) }
// Only do this if: // 1. Background access is truly required // 2. Data sensitivity allows it // 3. You've considered security tradeoffs
Debugging File Protection Issues
Issue: File Not Accessible in Background
Symptom: Background tasks fail to read files
// Debug: Check current protection if let protection = try? FileManager.default.attributesOfItem( atPath: url.path )[.protectionKey] as? FileProtectionType { print("Protection: (protection)") if protection == .complete { print("❌ Can't access in background when locked") } }
Solution: Use .completeUntilFirstUserAuthentication instead
Issue: Files Inaccessible After Restart
Symptom: App can't access files immediately after device reboot
Cause: Using .complete or .completeUntilFirstUserAuthentication (works as designed)
Solution: This is expected behavior. Either:
-
Wait for user to unlock device
-
Handle gracefully with appropriate UI
-
Use .none for files that must be accessible (security tradeoff)
Entitlements
File protection generally works without special entitlements, but some features require:
Data Protection Entitlement
<!-- Required for: .complete protection level --> <key>com.apple.developer.default-data-protection</key> <string>NSFileProtectionComplete</string>
When needed:
-
Using .complete protection
-
Some iOS versions for any protection (check documentation)
How to add:
-
Xcode → Target → Signing & Capabilities
-
"+ Capability" → Data Protection
-
Select protection level
Quick Reference Table
Scenario Recommended Protection Accessible When Locked? Background Access?
User health data .complete
❌ No ❌ No
Financial records .complete
❌ No ❌ No
Most app data .completeUntilFirstUserAuthentication
✅ Yes (after first unlock) ✅ Yes
Downloads (large files) .completeUnlessOpen
✅ While open ✅ While open
Database files .completeUntilFirstUserAuthentication
✅ Yes ✅ Yes
Downloaded images .completeUntilFirstUserAuthentication
✅ Yes ✅ Yes
Public caches .none
✅ Yes ✅ Yes
Temp files .none
✅ Yes ✅ Yes
Related Skills
-
axiom-storage — Decide when to use file protection vs other security measures
-
axiom-storage-management-ref — File lifecycle, purging, and disk management
-
axiom-storage-diag — Debug file access issues
Last Updated: 2025-12-12 Skill Type: Reference Minimum iOS: 4.0 (all protection levels) Latest Updates: iOS 26