本地端資料儲存 Document directory

Patty
6 min readAug 15, 2022

應用於時鐘 App 開啟世界時鐘或鬧鐘分頁都可以儲存每次新增修改的結果,即使關閉 App 重新開啟仍保留上次儲存結果。

  • 透過型別方法寫鬧鐘型別 Alarm 的儲存 saveAlarm 和讀取 loadAlarms
  • 型別須遵從 Codable 才可以編碼、解碼

【檔案位置】

static let documentsDirectory = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask).first!

編碼:自訂型別轉成 Data、解碼:Data 轉成自訂型別

  • 自訂型別需要遵從 Codable 的 protocol:則可編碼、解碼
    其中的 property 也需要遵從 Codable
// 編碼
let encoder = JSONEncoder()
let data = try? encoder.encode(alarms)
// 解碼
let decoder = JSONDecoder()
return try? decoder.decode([Self].self, from: data)

資料儲存:存在 Containers 下的 Data 的 Documents Directory

// 存檔
let url = documentsDirectory.appendingPathComponent("alarm") // 路徑
try? data?.write(to: url) // 寫入
// 讀取
let url = documentsDirectory.appendingPathComponent("alarm")
guard let data = try? Data(contentsOf: url) else { return nil }

【儲存】

// 自訂型別編碼後, 再寫入 documentDirectory
static func saveAlarm(_ alarms: [Alarm]) {
// 編碼
let encoder = JSONEncoder()
let data = try? encoder.encode(alarms)
// 存檔
let url = documentsDirectory.appendingPathComponent("alarm") // 路徑
try? data?.write(to: url) // 寫入
}

【讀取】

// 讀取 documentDirectory 再解碼成自訂型別
static func loadAlarms() -> [Self]? { // [Self]: Self (大寫的 S) 代表型別 Alarm
// 讀取
let url = documentsDirectory.appendingPathComponent("alarm")
guard let data = try? Data(contentsOf: url) else { return nil }
// 解碼
let decoder = JSONDecoder()
return try? decoder.decode([Self].self, from: data)
}

〖應用〗

偵測屬性變更的 property observer:didSet

使用的時機:想要將鬧鐘列表依鬧鐘時間排序

原來不需要特別將鬧鐘的 cell 重新排列,只需要對資料內容做設定。概念是透過偵測屬性變更的 property observer — didSet,只要當資料內容變動時不論新增、修改或刪除時會觸發排序,就可以讓資料內容永遠呈現有序狀態,自然而然鬧鐘列表就會依鬧鐘的時間排序。

var alarmList = [Alarm]() {
didSet {
alarmList = alarmList.sorted { $0.alarmTime < $1.alarmTime }
}
}

綜合自訂型別解碼編碼和資料儲存

Alarm.swift

// MARK: - 本地端資料儲存 Document directory
static let documentsDirectory = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask).first!
// 自訂型別編碼後,再寫入 documentDirectory
static func saveAlarm(_ alarms: [Alarm]) {
// 編碼
let encoder = JSONEncoder()
let data = try? encoder.encode(alarms)
// 存檔
let url = documentsDirectory.appendingPathComponent("alarm") // 路徑
try? data?.write(to: url) // 寫入
}
// 讀取 documentDirectory 再解碼成自訂型別
static func loadAlarms() -> [Self]? { // [Self]: Self (大寫的 S) 代表型別 Alarm
// 讀取
let url = documentsDirectory.appendingPathComponent("alarm")
guard let data = try? Data(contentsOf: url) else { return nil }
// 解碼
let decoder = JSONDecoder()
return try? decoder.decode([Self].self, from: data)
}

AlarmTableViewController.swift

// 資料內容有變動時則儲存
var alarmList = [Alarm]() {
didSet {
alarmList = alarmList.sorted { $0.alarmTimeForDateFormatter < $1.alarmTimeForDateFormatter}
Alarm.saveAlarm(alarmList)
}
}
// 鬧鐘分頁的開始畫面呼叫讀取鬧鐘內容
override func viewDidLoad() {
super.viewDidLoad()
if let alarmList = Alarm.loadAlarms(){
self.alarmList = alarmList
}
}

--

--