What are the ways to store data on iOS, and when do you use each?
TL;DR
| Data | Store it in |
|---|---|
| Small settings (theme, flags) | UserDefaults / @AppStorage |
| Secrets (tokens, passwords) | Keychain |
| Files, images, downloads | File system (FileManager) |
| Lots of structured data you search and sort | SwiftData, Core Data, or SQLite (for example GRDB) |
| Sync across a user's devices | CloudKit, or SwiftData / Core Data with CloudKit |
The wrong choice is a common interview red flag. For example, a token in UserDefaults.
UserDefaults
UserDefaults.standard.set(true, forKey: "hasSeenOnboarding")
let seen = UserDefaults.standard.bool(forKey: "hasSeenOnboarding")
// SwiftUI
@AppStorage("theme") private var theme = "system"- Small key-value data only. It is stored in a plist and loaded into memory.
- Not encrypted. Never put secrets here.
- Share between app and extensions with an App Group:
UserDefaults(suiteName: "group.com.example").
Keychain
let query: [String: Any] = [
kSecClass as String: kSecClassGenericPassword,
kSecAttrAccount as String: "authToken",
kSecValueData as String: Data(token.utf8),
kSecAttrAccessible as String: kSecAttrAccessibleAfterFirstUnlock
]
SecItemAdd(query as CFDictionary, nil)- Encrypted by the system. Use it for tokens, passwords and keys.
- Items usually survive app deletion. Clear them on first launch if needed.
- The C API is clunky. Most teams wrap it in a small type.
The file system
Each app has a sandbox with several folders:
| Folder | Use | Backed up? | Can the system delete it? |
|---|---|---|---|
Documents | User-created files | Yes | No |
Library/Application Support | App data the user does not see (databases) | Yes | No |
Library/Caches | Data you can download again | No | Yes, when storage is low |
tmp | Short-lived files | No | Yes |
let url = URL.documentsDirectory.appending(path: "notes.json")
try JSONEncoder().encode(notes).write(to: url, options: [.atomic, .completeFileProtection])
let loaded = try JSONDecoder().decode([Note].self, from: Data(contentsOf: url)).atomic avoids half-written files. .completeFileProtection encrypts the file while the device is locked.
Codable JSON files are great for small data sets, like a few hundred items.
SwiftData (iOS 17+)
@Model
final class Note {
var title: String
var createdAt: Date
init(title: String, createdAt: Date = .now) {
self.title = title
self.createdAt = createdAt
}
}
// In a view
@Query(sort: \Note.createdAt, order: .reverse) private var notes: [Note]Swift-first, works well with SwiftUI, and can sync with iCloud. It is built on top of Core Data's storage.
Core Data
Mature and powerful object graph framework. Still common in older and large apps. Supports migrations, batch operations and iCloud sync with NSPersistentCloudKitContainer. More boilerplate than SwiftData.
SQLite directly
Libraries like GRDB give full SQL control, good performance and clear concurrency rules. A good choice when you need complex queries or know SQL well.
How to choose
- Ask: How much data? Do I need to query it? Is it secret? Must it sync? Can it be re-downloaded?
- Start simple. A Codable file is often enough. Move to a database when you need queries or large data.
Common follow-up questions
- Is
UserDefaultsthread-safe? Yes, reading and writing from many threads is safe. - Why not save images in Core Data? Large blobs slow down the database. Save files on disk and store the path. Core Data's "external storage" option can do this for you.
- How do you share data with a widget? Use an App Group container for files,
UserDefaults, or the database. - What is Data Protection? File encryption tied to the device passcode. Choose a protection class per file.
- What does iCloud backup include?
DocumentsandApplication Support, notCachesortmp. Mark large re-downloadable files withisExcludedFromBackup.