🖥️
iOS App with Pop
Swift 4
Swift 4
  • iOS App development
  • Create New Project
  • Introduction to Xcode
  • App's life cycle
  • UIViewController
  • Storyboard
  • First Run
  • Display todo list
  • Basic Auto Layout
  • MVC
  • Model
  • Binding TableView
  • Binding TableViewCell
  • TableViewDelegate
  • Add navigationBar with + button
  • Add new item page
  • TextField and Switch
  • Binding action
  • Add mock item to todo list
  • Finish add item
  • Delete todo item
  • Edit todo item
  • Custom new layout
  • Adding new delegate
  • Refactor
  • Save data
  • Pushing edit view
  • Large navigation
  • Drag item
  • Drop item (in app)
  • Where to go from here?
Powered by GitBook
On this page
  • บันทึกให้ด้วยสิ
  • บันทึกลงไฟล์
  • อ่านจากไฟล์
  • Refactor Code หน่อยยย

Save data

บันทึกให้ด้วยสิ

ถ้าเราไม่ทำให้บันทึกค่าเก็บไว้ปกติ ถ้าเรา kill แอพทิ้งแล้วรันใหม่ข้อมูลเราจะหายไป

ขั้นแรกเราต้องทำให้สามารถ encode และ decode คลาส Todo ของเราให้ได้ก่อน เพราะเราจะได้แปลงเป็น data ที่จะบันทึกได้ต่อไป

วิธีการก็แสนจะง่าย ให้เราเพิ่ม conform Codable ที่ Todo และ TodoItem เป็นอันเสร็จ

Todo.swift
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 }
    }
}
TodoItem.swift
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 ข้อมูลของเรา

TodoListViewController
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

โดย default PropertyListEncoder จะ encode เป็น binary แต่เราจะใช้ xml (บรรทัดที่ 4) ก่อนเพื่อที่จะได้อ่านได้

เราสามารถ print(destinationURL.path) ออกมาดูได้ว่าไฟล์อยู่ที่ไหนและลองเปิดดูได้

อ่านจากไฟล์

เหมือนบันทึกลงไฟล์ แต่คราวนี้ใช้ 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
}
PreviousRefactorNextPushing edit view

Last updated 6 years ago

รายละเอียดเพิ่มเติมเกี่ยวกับเรื่องนี้ได้ที่

Archives and Serialization