บันทึกให้ด้วยสิ
ถ้าเราไม่ทำให้บันทึกค่าเก็บไว้ปกติ ถ้าเรา kill แอพทิ้งแล้วรันใหม่ข้อมูลเราจะหายไป
ขั้นแรกเราต้องทำให้สามารถ encode และ decode คลาส Todo ของเราให้ได้ก่อน เพราะเราจะได้แปลงเป็น data ที่จะบันทึกได้ต่อไป
วิธีการก็แสนจะง่าย ให้เราเพิ่ม conform Codable ที่ Todo และ TodoItem เป็นอันเสร็จ
import Foundation
class Todo: Codable {
private var items = [TodoItem]()
var totalItems: Int {
return items.count
}
func item(at index: Int) -> TodoItem {
return items[index]
}
func add(item: TodoItem) {
items.insert(item, at: 0)
}
func remove(at index: Int) {
items.remove(at: index)
}
func index(of item: TodoItem) -> Int? {
return items.index { $0 === item }
}
}
import Foundation
class TodoItem: Codable {
var title: String
var isDone: Bool
init(title: String, isDone: Bool = false) {
self.title = title
self.isDone = isDone
}
func toggleIsDone() {
self.isDone = !self.isDone
}
}
บันทึกลงไฟล์
เราใช้ FileManager ในการสร้าง url ไปยังไฟล์ที่เรากำหนด
ใช้ PropertyListEncoder ในการ encode ข้อมูลของเรา
func saveTodo() {
do {
let encoder = PropertyListEncoder()
encoder.outputFormat = .xml
let data = try encoder.encode(todo)
let fileManager = FileManager.default
var destinationURL = try fileManager.url(for: .documentDirectory, in: .userDomainMask, appropriateFor: nil, create: false)
destinationURL.appendPathComponent("todo")
destinationURL.appendPathExtension("plist")
try data.write(to: destinationURL)
} catch {
print("Cannot save todo to file, Error: \(error)")
}
}
เสร็จแล้วให้เพิ่มเรียก saveTodo() ที่ delegate, dataSource method ที่ใช้ในการ Add, Edit และ Delete TodoItem และ todoItemTableViewCellDidTapCheckboxButton
อ่านจากไฟล์
เหมือนบันทึกลงไฟล์ แต่คราวนี้ใช้ PropertyListDecoder ในการ decode และถ้าไม่มีไฟล์ให้อ่านเราจะข้ามไป
TodoListViewController.swift
func loadTodo() {
do {
let fileManager = FileManager.default
var destinationURL = try fileManager.url(for: .documentDirectory, in: .userDomainMask, appropriateFor: nil, create: false)
destinationURL.appendPathComponent("todo")
destinationURL.appendPathExtension("plist")
if fileManager.fileExists(atPath: destinationURL.path) {
let data = try Data(contentsOf: destinationURL)
let decoder = PropertyListDecoder()
todo = try decoder.decode(Todo.self, from: data)
tableView?.reloadData()
}
} catch {
print("Cannot load todo from file, Error: \(error)")
}
}
เพิ่มโค้ดให้อ่านไฟล์ตอนโหลด
TodoListViewController.swift
override func viewDidLoad() {
super.viewDidLoad()
loadTodo()
}
จากนั้นลองรันแอพ เพิ่มรายการ kill แอพ แล้วเปิดดูใหม่
Refactor Code หน่อยยย
จะเห็นว่าโค้ดที่ใช้หา Path ของไฟล์ที่กำหนดมันใช้ซ้ำกัน เราก็สร้างฟังก์ชั่นใหม่มาครอบดีกว่า
TodoListViewController.swift
func loadTodo() {
do {
let fileManager = FileManager.default
let destinationURL = try makeTodoFileURL(fileManager: fileManager)
if fileManager.fileExists(atPath: destinationURL.path) {
let data = try Data(contentsOf: destinationURL)
let decoder = PropertyListDecoder()
todo = try decoder.decode(Todo.self, from: data)
tableView?.reloadData()
}
} catch {
print("Cannot load todo from file, Error: \(error)")
}
}
func saveTodo() {
do {
let destinationURL = try makeTodoFileURL(fileManager: FileManager.default)
let encoder = PropertyListEncoder()
let data = try encoder.encode(todo)
try data.write(to: destinationURL)
} catch {
print("Cannot save todo to file, Error: \(error)")
}
}
func makeTodoFileURL(fileManager: FileManager) throws -> URL {
var destinationURL = try fileManager.url(for: .documentDirectory, in: .userDomainMask, appropriateFor: nil, create: false)
destinationURL.appendPathComponent("todo")
destinationURL.appendPathExtension("plist")
return destinationURL
}