core data

第一次使用Core Data就上手

林哲緯 2021/06/16 17:49:17
815

第一次使用Core Data就上手

 

前言:

在iOS APP有很多資料庫的應用,如:SQLite,FMDB,Realm,Firebase等,其實在iOS的開發工具的Xcode中就有一個簡化了資料庫的處理,讓你不用了解 SQL 指令也可以快速的為應用程式建立並使用資料庫框架:Core Data 。

新增專案:

打開Xcode -> File -> New -> Project ...

(圖1)

 

選擇iOS ->  App -> Next

(圖2)

 

輸入專案名稱:TPICoreData -> 將Use Croe Data 選項打勾 -> Next

(圖3)

 

專案中的檔案列表多了一個檔案:TPICoreData.xcdatamodeld

這個就是我們可以用來設定Entity和Attribute的檔案

(圖4)

 

設定Entity和Attribute:

點選下方Add Entity 並將新增在 Entities 的 Entity 並重新命名為 Member

(圖5)

 

新增完 Entity之後,點選 Attributes  的 “+” 分別新增三個Attribute:department、id、name

Type 為每一個Attribute的類型,分別設定為 String、Integer32、String

(圖6)

 

建立Entity的NSManagedObject類別

為了要讓建立的Entity能夠在code裡使用,我們要建立一個Entity的類別

選擇TPICoreData.xcdatamodeld -> 點選 Editor -> Create NSManagedObject Subclass...

(圖7)

 

打勾之後點選 Next

(圖8)

 

打勾之後點選 Next

(圖9)

 

選擇檔案路徑後點選Create

(圖10)

 

就會得到兩個檔案:Member+CoreDataClass.swift 和 Member+CoreDataProperties.swift

(圖11)

 

(圖12)

 

我們建立的 Entity類別是繼承自NSManagedObject,透過剛剛的操作讓Xcode這邊幫我們建立了property

 

注意:這邊可能會遇到一個問題

透過剛剛的方式生成檔案可能會出現以下錯誤

(圖14)

 

其實Xcode在我們建立Entity的時候,就已經幫我們自動建立好Entity的類別了

如果要解決這個錯誤,就需要調整Entity的class設定,將Codegen設定為Manual/None

這樣就Xcode就不會自動生成Entity的類別了

(圖15)

 

實作Core Data

剛剛新增的Entity和Attribute是以部門人員為範例

department:部門

id:編號

name:姓名

(圖13)

 

在實作Core Data之前,我們先對畫面(ViewController)做一些設定

點選 Main.Storyboard -> ViewCotroller -> 點選+號 -> 選取TableView -> 將TableView拖拉到ViewController中

(圖16)

 

將剛剛加入的TableView配置適當的Constraints,我這裡設定的是:對上距離0、對左距離0、對右距離0、對下距離

(圖17)

 

 

加入一個Bar Button Item

點選 Main.Storyboard -> ViewCotroller -> 點選+號 -> 選取Bar Button Item -> 將Bar Button Item拖拉到ViewController中的Navugation Bar

(圖18)

 

 

並將Bar Button Item中System Item設定為Add

(圖19)

 

 

加入一個TableViewCell

點選 Main.Storyboard -> ViewCotroller -> 點選+號 -> 選取TableViewCell -> 將TableViewCell拖拉到TableView

(圖22)

 

 

並將TableViewCell的Identifier設定為"Cell"

(圖23)

 

 

將需要使用的元件新增且把樣式設定完畢後,再來就是要加入元件和程式的關聯了(IBOutlet & IBAction func)

(圖20)

 

(圖21)

 

 

完成元件與程式的關聯,再來就是程式撰寫

 

分析一下需要實作的功能

 

1.設定TableViewDelegate、TableViewDataSource和相關參數

2.資料的新增,點擊畫面右上角+號,新增一筆資料

3.資料的查詢,新增的資料透過Tableview的特性,以List的方式顯示

4.資料的修改,點擊TablewView的item,修改資料

5.資料的刪除,點擊TablewView的item,刪除資料

 

將上述功能一一實作就能掌握Coer Data的基本功能了

 

1.設定TableViewDelegate、TableViewDataSource和相關參數

 

    //宣告Core Data 常數
    let context = (UIApplication.shared.delegate as! AppDelegate).persistentContainer.viewContext    

    //宣告一個Array,紀錄資料庫查詢出來的結果
    var memberList:[Member] = []

    override func viewDidLoad() {
        super.viewDidLoad()
        //將TablewView的delegate和dataSource指定給Self(ViewCotroller)
        self.memberTableView.delegate = self
        self.memberTableView.dataSource = self
    }

 

 

//實作UITableViewDelegate,UITableViewDataSource
extension ViewController:UITableViewDelegate,UITableViewDataSource {
    
    func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
        // Core Data 資料長度
        return self.memberList.count
    }
    
    func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
        //顯示 Core Data 資料
        let cell = tableView.dequeueReusableCell(withIdentifier: "Cell", for: indexPath) as UITableViewCell
        cell.textLabel?.text = "編號:" + String(self.memberList[indexPath.row].id) + " , 部門:" + String(self.memberList[indexPath.row].department ?? "") + " , 姓名:" + String(self.memberList[indexPath.row].name ?? "")

        return cell
    }
    
    func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
        //點擊cell 讓使用者選擇刪除或編輯資料
        let alert = UIAlertController.init(
            title:          "更新或刪除一筆資料",
            message:        "",
            preferredStyle: .alert
        )
        let okAction = UIAlertAction.init(title: "更新", style: .default) { _ in
            DispatchQueue.main.async {
                self.updateObject(indexPath: indexPath)
            }
        }
        let cancelAction = UIAlertAction.init(title: "刪除", style: .default) { _ in
            DispatchQueue.main.async {
                self.deleteObject(indexPath: indexPath)
            }
        }
        alert.addAction(okAction)
        alert.addAction(cancelAction)
        self.present(alert, animated: true, completion: nil)
    }
    
    
}

 

 

 

2.資料的新增,點擊畫面右上角+號,新增一筆資料

 

    @IBAction func addItemButtonpressed(_ sender: Any) {
        
        self.showAlert(type:"新增", alertTitle: "新增人員資料", actionHandler: { (textFields: [UITextField]?) in
            DispatchQueue.main.async {
                self.insertObject(department: String(textFields?[0].text ?? ""), id: Int(textFields?[1].text ?? "") ?? 0, name: String(textFields?[2].text ?? ""))
            }
        })
    }
    
    //新增資料
    func insertObject(department: String, id: Int, name: String) {
        let member = NSEntityDescription.insertNewObject(forEntityName: "Member", into: self.context)as! Member
        member.id = Int32(id)
        member.name = name
        member.department = department
        do {
            try self.context.save()
        } catch {
            fatalError("\(error)")
        }
        //新增完畢後查詢資料庫資料並將資料庫資料顯示在TableView上
        self.memberList = self.selectObject()
        DispatchQueue.main.async {
            self.memberTableView.reloadData()
        }
    }

 

    //實作一個Alert 讓user輸入新增或編輯資料
    func showAlert(type:String, alertTitle: String, actionHandler: ((_ textFields: [UITextField]?) -> Void)? = nil) {
        let alert = UIAlertController.init(
            title:          alertTitle,
            message:        "",
            preferredStyle: .alert
        )
        //新增三個輸入框分別讓使用者輸入部門、編號、姓名
        for index in 0...2 {
            alert.addTextField { (textField:UITextField) in
                if index == 0 {
                    textField.placeholder = type + "部門"
                }else if index == 1 {
                    textField.placeholder = type + "編號"
                }else if index == 2 {
                    textField.placeholder = type + "姓名"
                }
            }
        }
        alert.addAction (UIAlertAction.init(title: "確定", style: .default, handler: { (action:UIAlertAction) in
            DispatchQueue.main.async {
                actionHandler?(alert.textFields)
            }
        }))
        
        let cancelAction = UIAlertAction.init(title: "取消", style: .cancel) { _ in
            DispatchQueue.main.async {

            }
        }
        alert.addAction(cancelAction)
        
        self.present(alert, animated: true, completion: nil)
    }

 

3.資料的查詢,新增的資料透過Tableview的特性,以List的方式顯示

 

    //查詢資料
    func selectObject() -> Array<Member> {
        var array:[Member] = []
        let request = NSFetchRequest<Member>(entityName: "Member")
        do {
            let results = try self.context.fetch(request)
            for result in results {
                array.append(result)
            }
        }catch{
            fatalError("Failed to fetch data: \(error)")
        }
        return array
        
    }

 

4.資料的修改,點擊TablewView的item,修改資料

 

    //更新資料
    func updateObject(indexPath:IndexPath) {
        self.showAlert(type: "修改", alertTitle: "修改人員資料") { (textFields: [UITextField]?) in
            //更新:將查詢到的結果更新後,再呼叫context.save()儲存
            let request = NSFetchRequest<Member>(entityName: "Member")
            do {
                let results = try self.context.fetch(request)
                for item in results {
                    if item.id == self.memberList[indexPath.row].id &&
                        item.name == self.memberList[indexPath.row].name &&
                        item.department == self.memberList[indexPath.row].department {
                        
                        item.department = textFields?[0].text
                        item.id = Int32(Int(textFields?[1].text ?? "") ?? 0)
                        item.name = textFields?[2].text
                    }
                }
                try self.context.save()
            }catch{
                fatalError("Failed to fetch data: \(error)")
            }
            //新增完畢後查詢資料庫資料並將資料庫資料顯示在TableView上
            self.memberList = self.selectObject()
            DispatchQueue.main.async {
                self.memberTableView.reloadData()
            }
        }
    }

 

5.資料的刪除,點擊TablewView的item,刪除資料

 

    //刪除資料
    func deleteObject(indexPath:IndexPath) {
        //刪除:將查詢到的結果刪除後,再呼叫context.save()儲存
        let request = NSFetchRequest<Member>(entityName: "Member")
        do {
            let results = try self.context.fetch(request)
            for item in results {
                if item.id == self.memberList[indexPath.row].id &&
                    item.name == self.memberList[indexPath.row].name &&
                    item.department == self.memberList[indexPath.row].department {
                    context.delete(item)
                }
            }
            try self.context.save()
        }catch{
            fatalError("Failed to fetch data: \(error)")
        }
        let alert = UIAlertController.init(
            title:          "已刪除",
            message:        "",
            preferredStyle: .alert
        )
        
        let okAction = UIAlertAction.init(title: "OK", style: .default)
        alert.addAction(okAction)
        
        self.present(alert, animated: true, completion: {
            //新增完畢後查詢資料庫資料並將資料庫資料顯示在TableView上
            self.memberList = self.selectObject()
            DispatchQueue.main.async {
                self.memberTableView.reloadData()
            }
        })
    }

 

結果:

 

結論:

在iOS的開發中一定還有很多資料庫的應用,這邊只是針對Core Data實作一些基本的應用

不論是哪種實作方式,只要能夠完成功能,都是好方法

就讓我們繼續寫Code吧!

 

 

參考資料:

https://developer.apple.com/documentation/coredata

https://www.reddit.com/r/swift/comments/9t3vj0/serious_problem_with_coredata_and_nsmanagedobject/

https://www.hackingwithswift.com/read/38/4/creating-an-nsmanagedobject-subclass-with-xcode

林哲緯