iosNudge

What are the ways to store data on iOS, and when do you use each?

EasyAsked oftenPersistenceSecurity 3 min read

TL;DR

DataStore it in
Small settings (theme, flags)UserDefaults / @AppStorage
Secrets (tokens, passwords)Keychain
Files, images, downloadsFile system (FileManager)
Lots of structured data you search and sortSwiftData, Core Data, or SQLite (for example GRDB)
Sync across a user's devicesCloudKit, 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:

FolderUseBacked up?Can the system delete it?
DocumentsUser-created filesYesNo
Library/Application SupportApp data the user does not see (databases)YesNo
Library/CachesData you can download againNoYes, when storage is low
tmpShort-lived filesNoYes
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 UserDefaults thread-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? Documents and Application Support, not Caches or tmp. Mark large re-downloadable files with isExcludedFromBackup.