iOS UserDefaults

UserDefaults 是 iOS 開發中最常見的資料持久化方式,適合用來儲存少量的使用者設定(例如:音量大小、是否開啟通知、使用者暱稱、最後一次登入時間等)。

它是一個 Key-Value (鍵值對) 儲存系統,用法類似於字典 (Dictionary)。資料會被儲存在各個 App 的 sandboxed file system 中的 .plist 檔案裡。

什麼不適合存?

  • 大量資料:如大圖片或數千筆記錄,請使用 FileManager, CoreDataSwiftData
  • 敏感資料:如使用者密碼、Token,請務必使用 Keychain,因 UserDefaults 的資料是未加密的純文字。

基本用法

Swift 提供了一個單例 (Singleton) UserDefaults.standard 來讓我們存取預設的儲存區。

儲存資料 (Set)

使用 set(_:forKey:) 方法。

// 儲存整數
UserDefaults.standard.set(25, forKey: "TextSize")

// 儲存布林值
UserDefaults.standard.set(true, forKey: "IsDarkMode")

// 儲存字串
UserDefaults.standard.set("Mike", forKey: "UserName")

// 儲存日期
UserDefaults.standard.set(Date(), forKey: "LastLogin")

讀取資料 (Get)

UserDefaults 針對不同型別提供了對應的讀取方法。注意部份方法有預設行為:

// 讀取整數 (若 key 不存在則回傳 0)
let size = UserDefaults.standard.integer(forKey: "TextSize")

// 讀取布林值 (若 key 不存在則回傳 false)
let isDarkMode = UserDefaults.standard.bool(forKey: "IsDarkMode")

// 讀取字串 (回傳 String?,可能為 nil)
let name = UserDefaults.standard.string(forKey: "UserName")

// 讀取日期 (回傳 Any?,需轉型)
let loginDate = UserDefaults.standard.object(forKey: "LastLogin") as? Date

移除資料 (Remove)

UserDefaults.standard.removeObject(forKey: "UserName")

支援的資料型別

UserDefaults 原生僅支援以下型別 (PropertyList 兼容型別):

  • Data
  • String
  • Number (Int, Float, Double, Bool)
  • Date
  • Array (成員必須也是支援的型別)
  • Dictionary (成員必須也是支援的型別)

儲存自定義物件 (Custom Objects)

如果你想儲存一個自定義的 Struct (例如 User),直接塞給 UserDefaults 會 Crash。

正確做法是:

  1. 讓 Struct 遵循 Codable 協議。
  2. 使用 JSONEncoder 編碼成 Data 存入。
  3. 讀取時使用 JSONDecoder 解碼。
struct UserProfile: Codable {
    var name: String
    var age: Int
}

let user = UserProfile(name: "Alice", age: 30)

// --- 儲存 ---
if let encoded = try? JSONEncoder().encode(user) {
    UserDefaults.standard.set(encoded, forKey: "CurrentUser")
}

// --- 讀取 ---
if let savedData = UserDefaults.standard.data(forKey: "CurrentUser"),
   let loadedUser = try? JSONDecoder().decode(UserProfile.self, from: savedData) {
    print("User: \(loadedUser.name), Age: \(loadedUser.age)")
}

SwiftUI 的 @AppStorage

在 SwiftUI 中,我們很少直接寫 UserDefaults.standard.set。取而代之的是使用 @AppStorage 這個 Property Wrapper。

它會自動將變數與 UserDefaults 綁定,當數值改變時,會自動寫入,並且自動更新 View

import SwiftUI

struct SettingsView: View {
    // 當你在 UI 修改 isDarkMode,UserDefaults 裡的 "isDarkMode" key 也會跟著變
    // 若 UserDefaults 裡沒值,則使用 false 為預設值
    @AppStorage("isDarkMode") private var isDarkMode = false
    @AppStorage("username") private var username = "Guest"
    
    var body: some View {
        Form {
            Toggle("暗黑模式", isOn: $isDarkMode)
            TextField("暱稱", text: $username)
        }
    }
}
@AppStorage 目前僅支援基本的型別 (String, Int, Double, Bool, URL, Data),不支援直接存取 Struct/Class。若要存複雜物件,仍建議封裝一層 ViewModel 使用上述 Codable 的方法。