본문 바로가기

카테고리 없음

103 iOS SQLite, CoreData 사용

** SQLite

=> WebBrowser, Android, iOS 기기 등에 내장된 관계형 데이터베이스 (테이블)

=> 외부기기에서는 접근이 불가능한 데이터베이스

=> 응용 프로그램의 부분저긴 모듈로 동작

=> Mac OS X에소 설치되어 있고 Python에도 설치가 되어 있다.

=> SQL은 MySQL과 거의 유사

 

1. Xcode에서 SQLite 사용

=> Xcode에서 SQLite를 사용할려면 libsqlite 라이브러리가 필요

Project에서 Build Phases 탭에서 Link Binary with Libraries영역에서 추가

=> sqlite 는 C++라이브러리로 만들어져 있다.

대부분의 경우 C++로 직접 사용하지 않고 Objective-C로 래핑된 FMDB와 같은 외부 라이브러리를 이용한다.

Swift 프로젝트에 FMDB 라이브러리를 추가하면 Swift 문법으로 사용할 수 있도록 Bridge Header 파일을 생성해 주어야 한다.

 

2. sqlite를 직접 편집

=> 여러가지 편집 프로그램이 있다.

http://sqlitebrowser.org 같은 곳에서 제공

dbeaver는 만들어져 있으면 접속은 가능한데 파일을 생성하지는 못한다.

 

3. FMDB 사용

=> SQLite를 Objective-C로 사용할 수 있도록 래핑한 라이브러리

1) 주요 클래스

FMDatabase : SQL 실행 클래스

FMResultSet : select 구문의 결과를 저장하기 위한 클래스

FMDatabaseQueue : 다중 스레드로 사용하기 위한 클래스

 

2) 데이터베이스 생성 및 열기

=> 생성은 FMDatabase(path: 파일 경로)

=> 열기는 open() 메소드 호출

 

3) SQL 실행

=> FMDatabase 객체를 가지고 executeStatements(sql문장) 또는 executeQuery(sql문장, withArguments: 배열)

 

4) 닫기

=> close()

 

** 연락처 테이블을 생성해서 전체보기, 삽입, 삭제를 구현 SQLite3 이용

1. 프로젝트를 생성 - PhoneBook

 

2. SQLite3 라이브러리를 추가

 

3. FMDB 라이브러리를 다운로드 받아서 프로젝트에 복사

=> 다운로드 받을 주소 : https://github.com/ccgus/fmdb.git - [Source Control] - [Clone]을 선택하고 URL을 입력하고 Clone을 클릭

=> 브릿지 헤더 생성하고 작성 : C++이나 Objective-C로 만들어진 내용을 Swift에서 사용할 수 있도록 해주는 파일

 

 

폴더를 새로 만들고 복사

 

=> 생성된 브릿지 헤더 파일에 작성

#import "FMDB.h"

 

4. 첫번째 화면을 테이블 뷰 컨트롤러를 만들고 네에게이션 바를 추가

1) UITableViewController로 부터 상속받는 클래스를 생성

 

2) 스토리보드에 TableViewController를 추가하고 Class 속성과 StoryboardID 속성을 수정

=> 첫번째 뷰 컨트롤러가 되어야하는 경우에는 is Initial View Controller 속성을 체크

 

3) TableViewController에 NavigationController를 추가  [Editor] - [Embed in] - [NavigationController]

 

5. 데이터를 저자할 파일과 테이블을 생성

=> 한 번만 생성 : AppDelegate에서 하는 것이 효율적

=> 파일의 존재 여부를 확인해서 없으면 파일을 만들고 있으면 새로 생성할 필요 없다.

=> AppDelegate.swift 파일의 앱이 실행될 때 호출되는 메소드

 

import UIKit

@UIApplicationMain
class AppDelegate: UIResponder, UIApplicationDelegate {

    //앱이 실행될 때 호출되는 메소드
    //launchOptions 에 앱이 실행된 방법을 저장
    //앱은 직접 실행할 수도 있지만 URL을 이용해서 실행할 수도 있다.
    //URL을 이용해서 실행하는 방법이 웹에서 페이스북을 보려고 할 때 앱으로 실행하시겠습니까 물어보고 실행하는 것이나 카카오톡에서 다른 게임이나 앱을 실행할 때 사용한다.
    func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
        
        //파일 경로를 생성 - Document 디렉토리의 phonebook.sqlite
        let fileMgr = FileManager.default
        
        //도큐먼트 디렉토리 경로 만들기
        let docPaths = NSSearchPathForDirectoriesInDomains(.documentDirectory, .userDomainMask, true)
        
        let docDir = docPaths[0] as String
        
        //파일경로 생성
        let dbPath = docDir.appending("/phonebook.sqlite")
        
        //파일의 존재 여부 확인
        if fileMgr.fileExists(atPath: dbPath){
            NSLog("파일이 존재함")
        }else{
            //데이터 베이스 생성
            let contactDB = FMDatabase(path: dbPath)
            //데이터베이스 열기
            if contactDB.open(){
                let sql =
                    """
                        create table if not exists phonebook(num INTEGER not null primary key autoincrement,
                            name TEXT, phone TEXT, addr TEXT)
                    """
                if contactDB.executeStatements(sql){
                    NSLog("테이블 생성 성공")
                }else{
                    NSLog("테이블 생성 실패")
                }
            }else{
                NSLog("데이터베이스 열기 실패")
            }
        }
        
        return true
    }

 

6. DTO & DAO 패턴

=> DTO(Data Transfer Object) : 여러 개의 데이터를 다른 곳에서 사용하고자 할 때 하나로 묶어서 전송하기 위해서 만드는 클래스 또는 객체

일반적인 언어는 클래스로 생성

Swift는 클래스(Class), 구조체(Struct - 값들의 집합, 튜플(값의 모임)

튜플로 만들 때는 (자료형 나열)로 자료형을 만들고 실제 객체를 만들 때는 값을 나열

=>튜플로 DTO 만들기

 typealias 자료형이름 = (각 데이터의 자료형을 나열)

=> 튜플 객체 만들기

(자료형에 맞는 데이터 나열)

 

=> DAO(Data Access Object) : 데이터와 관련된 작업을 모아놓은 클래스 또는 객체

=> 작업을 할 때 서비스 단위로 작업을 하기도 하고 클래스 단위로 작업을 하기도 한다.

=> 서버 애플리케이션을 만들 때 클래스 단위로 작업을 한다면 단위 테스트는 필수이다.

 

7. DAO 클래스 생성 - 전체 데이터 가져오기, 기본키를 가지고 하나의 데이터 찾아오기, 삽입, 삭제하기 - PhonebookDAO

 

//
//  PhoneBookDAO.swift
//  PhoneBook
//
//  Created by Lee Seungbum on 2020/09/08.
//  Copyright © 2020 Lee Seungbum. All rights reserved.
//

import Foundation
import UIKit

class PhoneBookDAO{
    //DTO 생성
    typealias PhoneRecord = (Int, String, String, String)
    
    //지연 생성(클라이언트에서 주로 이용)을 이용한 데이터베이스 연결
    //처음에는 존재하지 않다가 처음 사용할 때 구문을 수행해서 생성
    lazy var fmdb : FMDatabase! = {
        //데이터베이스 파일 경로 가져오기
        let fileMgr = FileManager.default
        let docPaths = NSSearchPathForDirectoriesInDomains(.documentDirectory, .userDomainMask, true)
        let docDir = docPaths[0] as String
        let dbPath = docDir.appending("/phonebook.sqlite")
        
        //데이터베이스 연결
        let db = FMDatabase(path: dbPath)
       
        return db
    }()
    
    //서버프로그래밍에서는 하나의 요청이 오면 데이터베이스를 열고 작업을 수행한 후 닫는다.
    //이 작업을 직접하지는 않고 Connection Pool을 이용해서 수행
    
    //클라이언트 프로그래밍에서는 이럴 필요가 없다.
    //시작할 때 연결하고 종료할 때 닫는 방식을 이용한다.
    //초기화 메소드 - 생성자
    init(){
        //데이터베이스 열기
        self.fmdb.open()
    }
    
    //소멸자
    deinit {
        self.fmdb.close()
    }
    
    //전체 데이터 가져오는 메소드
    //매개변수가 없고 데이터의 모임을 리턴
    func find() -> [PhoneRecord]{
        //결과를 저장할 List를 생성
        //출력하는 곳에서는 반복문을 수행하기 때문에 nil이 리턴되면 안된다.
        var phoneList = [PhoneRecord]()
        
        //SQL 생성
        let sql = "select num,name,phone,addr from phonebook order by num asc"
        //2. 실행
        let rs = try! self.fmdb.executeQuery(sql, values: nil)
        //3. 결과 사용
        while rs.next(){
            let num = rs.int(forColumn: "num")
            let name = rs.string(forColumn: "name")
            let phone = rs.string(forColumn: "phone")
            let addr = rs.string(forColumn: "addr")
            
            phoneList.append((Int(num), name!, phone!, addr!))
        }
        
        //결과 리턴
        return phoneList
    }
    
    //num을 받아서 하나의 데이터를 리턴하는 메소드 - 상세보기
    func get(num:Int) -> PhoneRecord?{
        //1.sql 생성
        let sql = "select num, name, phone, addr from phonebook where num = ?"
        //2.SQL 실행
        let rs = self.fmdb.executeQuery(sql, withArgumentsIn:[num])
        //3.결과가 있는지 확인
        if let _rs = rs{
            _rs.next()
            let num = _rs.int(forColumn: "num")
            let name = _rs.string(forColumn: "name")
            let phone = _rs.string(forColumn: "phone")
            let addr = _rs.string(forColumn: "addr")
            return (Int(num), name!, phone!, addr!)
        }else{
            return nil
        }
    }
    
    //데이터를 삽입하는 메소드
    func create(name:String!, phone:String!, addr:String!) -> Bool {
        
        do{
            //sql 생성
            let sql = "insert into phonebook(name, phone, addr) values(?,?,?)"
            //SQL 실행
            try self.fmdb.executeUpdate(sql, values: [name!, phone!, addr!])
            return true
        }catch let error as NSError{
            NSLog(error.localizedDescription)
            return false
        }
    }
    
    //데이터를 삽입하는 메소드
    func delete(num:Int) -> Bool {
        
        do{
            //sql 생성
            let sql = "delete from phonebook where num=?"
            //SQL 실행
            try self.fmdb.executeUpdate(sql, values: [num])
            return true
        }catch let error as NSError{
            NSLog(error.localizedDescription)
            return false
        }
    }
}

 

 

8. PhoneListVC.swift 파일에 전체 데이터 가져오기 구현

1) 2개의 인스턴스를 선언 - 출력할 데이터를 저장하는 배열, DAO

import UIKit

class PhoneListVC: UITableViewController {
    //데이터 목록을 저장할 변수
    var phoneBook : [(num:Int, name:String, phone:String, addr:String)]!
    //DAO 변수
    let dao = PhoneBookDAO()

 

 

2) viewDidLoad 메소드에 전체 데이터를 가져오고 UI를 초기화하는 코드를 추가

 override func viewDidLoad() {
        super.viewDidLoad()

        //전체 데이터 가져오기
        phoneBook = self.dao.find()
        //네비게이션 바에 레이블 출력
        //새로 생성해서 추가
        let navTitle = UILabel(frame: CGRect(x: 0, y: 0, width: 200, height: 60))
        navTitle.numberOfLines = 2
        navTitle.textAlignment = .center
        navTitle.text = "연락처 목록 \n" + "총\(self.phoneBook.count) 개"
        self.navigationItem.titleView = navTitle
    }

 

 

9.PhoneListVC.swift 파일에 테이블 뷰 출력을 위한 메소드 재정의

 

override func numberOfSections(in tableView: UITableView) -> Int {
        return 1
    }

    override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
        return phoneBook.count
    }

    
    override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
        //출력할 데이터를 찾아오기
        let rowData = phoneBook[indexPath.row]
        
        //셀을 생성
        let cellIdentifier = "Cell"
        var cell = tableView.dequeueReusableCell(withIdentifier: cellIdentifier)
        if cell == nil{
            cell = UITableViewCell(style:.subtitle, reuseIdentifier:cellIdentifier)
        }
        
        cell!.textLabel!.text = rowData.name
        cell!.detailTextLabel!.text = "\(rowData.phone) \(rowData.addr)"
        return cell!
    }

    

10.데이터 추가 기능

=>네비게이션 바의 오른쪽에 바 버튼 아이템을 배치해서 아이템을 클릭하면 대화상자를 출력하고 대화상자에 입력하고 확인을 누르면 테이블에 데이터를 추가하도록 생성

 

1)바버튼 아이템이 호출할 메소드 생성 - add

//바버튼 아이템이 호출할 메소드
    @objc func add(_ sender:Any){
        //대화상자 만들기
        let alert = UIAlertController(title:"데이터 삽입", message:"연락처 등록", preferredStyle:.alert)
        //입력 필드 만들기
        alert.addTextField(){(tf) in tf.placeholder="이름"}
        alert.addTextField(){(tf) in tf.placeholder="전화번호"}
        alert.addTextField(){(tf) in tf.placeholder="주소"}
        
        //버튼 만들기
        alert.addAction(UIAlertAction(title:"취소", style:.cancel))
        alert.addAction(UIAlertAction(title:"확인", style:.default){(_) in
            //입력한 내용 가져오기
            let name = alert.textFields?[0].text
            let phone = alert.textFields?[1].text
            let addr = alert.textFields?[2].text
            
            //테이블에 삽입
            self.dao.create(name:name, phone:phone, addr:addr)
            //데이터를 다시 가져와서 재출력
            self.phoneBook = self.dao.find()
            self.tableView.reloadData()
            
            //레이블도 다시 출력
            let naviTitle = self.navigationItem.titleView as! UILabel
            naviTitle.text = "연락처 목록\n" + "총 \(self.phoneBook.count) 개"
        })
        
        //대화상자 출력
        self.present(alert, animated:true)
    }

 

 

 

 

2)viewDidLoad 메소드에 바버튼 아이템을 배치 - add 라는 메소드를 호출하도록 생성

//네비게이션 바의 오른쪽 바 버튼 아이템 추가
self.navigationItem.rightBarButtonItem = 
UIBarButtonItem(barButtonSystemItem: .add, target: self, action: #selector(add(_:)))

 

11.데이터 삭제 구현

=>네비게이션 바의 왼쪽에 편집 버튼을 두고 편집 버튼을 누르면 삭제 버튼이 보이도록 하고 삭제를 누르면 실제 데이터 삭제

1)viewDidLoad 메소드에 네비게이션 바의 왼쪽에 편집 버튼 배치

        //왼쪽에 편집 버튼 배치
        self.navigationItem.leftBarButtonItem = self.editButtonItem
        self.tableView.allowsSelectionDuringEditing = true

 

2) edit 버튼을 눌렀을 때 보여질 버튼의 종류를 설정하는 메소드 재정의

    //edit 버튼을 눌렀을 때 보여질 버튼 설정 메소드
    override func tableView(_ tableView: UITableView, editingStyleForRowAt indexPath: IndexPath) -> UITableViewCell.EditingStyle{
        .delete
    }

 

3) 삭제를 누를 때 호출되는 메소드 재정의

    //실제 삭제를 눌렀을 때 호출되는 메소드
    override func tableView(_ tableView:UITableView, commit editingStyle:UITableViewCell.EditingStyle, forRowAt indexPath:IndexPath){
        
        let num = self.phoneBook[indexPath.row].num
        
        dao.delete(num:num)
        //삭제하는 애니메이션을 적용
        self.phoneBook.remove(at: indexPath.row)
        tableView.deleteRows(at:[indexPath], with:.fade)
    }

 

 

** In Memory DB

=> 데이터베이스의 내용은 보조 기억 장치에 존재

=> 데이터베이스에서 내용을 가져올 때는 보조 기억 장치에서 가져오기 때문에 속도가 느리다.

=> 데이터베이스의 내용을 보조 기억장치에 저장하지 않고 주기억장치에서 저장해서 사용하는 개념이다.

=> 최근에는 거의 모든 데이터베이스에 이 기능을 내장

=> Apple에서 사용하는 In Memory DB 의 개념이 CoreData

 

** Core Data

=> Apple 의 In Memory DB의 개념

=> SQL을 이용하지 않고 데이터베이스를 사용 - ORM(Object Relation Mapping)

ORM의 개념을가진 프레임워크가 Java의 Hibernate, Node의 시퀄라이즈, Python의 Django, MS의 LINQ, Facebook의 GraphQL(Web Front End에서 최근에 많이 사용 등

 

ORM의 개념 대신에 이전의 SQL Mapper를 이용하기도 함 - SQL은 개발자들이 이미 알고 있는데 ORM은 새로 공부를 해야하고 SQL Mapper 방식은 데이터베이스 구조를 몰라도 사용할 수 있지만 ORM은 데이터베이스 구조를 정확하게 알고 있어야 한다.

 

1. Core Data 사용의 기본 개념

=> 프로젝트를 생성할때 Use Core Data를 선택하면 Core Data 사용에 필요한 객체를 미리 생성해 준다.

프로젝트를 생성할 때 설정하지 않은 경우에는 나중에 객체만 추가해주면 된다.

 

1) Persistency(영속) Container

=> Core Data를 사용할 수 있도록 해주는 객체

=> Documents 디렉토리에 프로젝트이름.sqlite 라는 파일로 존재

 

2) Data Model

=> 데이터 모델링을 수행해주는 개념

=> 프로젝트 이름.xcdatamodelID라는 파일로 존재

=> 더블클릭하면 Editor가 실행

=> Entity(개체) - 테이블(릴레이션)에 해당하는 개념

=> Attribute(속성) - 컬럼(필드)에 해당하는 개념

=> Relation(관계) - 2개 Entity 사이의 관계, 외래키나 조인에 해당하는 개념

 

3) Managed Object Context

=> DAO의 개념으로 NSManagedObjectContext 클래스의 인스턴스

=> Core Data를 사용하도록 만들면 AppDelegate의 persistencyContainer.viewContext를 이용해서 사용이 가능

=> save나 delete와 같은 메소드가 구현되어 있다.

=> Managed Object라는 클래스를 사용하는데 이 클래스가 DTO 클래스의 역할

 

** title(문자열), contents(문자열), runtime(날짜)의 컬럼을 갖는 ToDo라는 테이블을 생성하고 CRUD 작업을 수행 : CoreData 이용

=> 실제 포트폴리오나 프로젝트를 진행할 때는 안드로이도도 고려한다면 SQLite를 선택하는게 나을 수 있고 iOS만 하는 경우에는 Core Data를 사용하는 것이 나을 수 있다.

 

1. Core Data를 사용하는 프로젝트를 생성  ToDo

 

2. 필요한 테이블을 생성

1) ToDo.xcdatamodeld를 더블클릭해서 Editor를 실행

2) [Editor] - [Add Entity] 메뉴를 실행해서 Entity(ToDo)를 추가하고 이름을 수정

3) Entity를 선택하고 오른쪽 창에서 Attribute(title - 문자열, contents - 문자열, runtime - 날짜)를 추가하고 이름과 자료형을 설정

 

 

3. 메인 화면에 TableViewController를 배치하고 상단에 네비게이션 바를 출력하도록 설정

1) UITableViewController롤 부터 상속받는 클래스 생성 - ToDoListVC

 

2) Main.storyboard에서 제공하는 ViewContoller를 제거

 

3) Main.storyboard에 TableViewController를 추가하고 Class 속성과 Storyboard ID 속성을 변경하고 is Initial View Controller 체크

 

4) [Editor] - [Embed In] - [Navigation Controller]를 실행해서 네비게이션 바 추가

 

4. 전체 데이터를 가져와서 출력부분 만들기

1) CoreData 임포트

import CoreData

 

2) 전체 데이터를 읽어오는 메소드를 생성

class ToDoListVC: UITableViewController {
    //전체 데이터를 읽어오는 메소드
    func fetch() -> [NSManagedObject]{
        //1.AppDelegate 참조를 생성
        let appDelegate = UIApplication.shared.delegate as! AppDelegate
        //2.관리 객체 참조 만들기
        let context = appDelegate.persistentContainer.viewContext
        //3.ToDo 테이블에 작업을수행하기 위한 객체를 생성
        let fetchRequest = NSFetchRequest<NSManagedObject>(entityName: "ToDo")
        //4.전체 데이터 가져오기
        let result = try! context.fetch(fetchRequest)
        
        return result
    }

 

 

 

 

3) 전체 데이터를 저장하기 위한 변수를 생성

 

class ToDoListVC: UITableViewController {
    //전체 데이터를 저장하기 위한 변수
    //list를 처음 사용할 때 1번 fetch를 호출해서 결과를 저장
    lazy var list : [NSManagedObject] = {
        return self.fetch()
    }()

 

4) 테이블 뷰 출력 관련 메소드 재정의

 override func numberOfSections(in tableView: UITableView) -> Int {
        return 1
    }

    override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
        return list.count
    }

    override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
        
        //출력할 데이터 찾아오기
        let record = list[indexPath.row]
        //셀 만들기
        var cell = tableView.dequeueReusableCell(withIdentifier: "cell")
        if cell == nil{
            cell = UITableViewCell(style: .subtitle, reuseIdentifier: "cell")
        }
        cell!.textLabel!.text = record.value(forKey: "title") as? String
        cell!.detailTextLabel!.text = record.value(forKey: "contents") as? String

        return cell!
    }

 

 

5. 데이터 추가 작업

=> 새로운 뷰 컨트롤러에 데이터를 입력해서 입력한 내용을 Core Data에 저장하고 다시 출력

 

1) ToDoListViewController.swift 파일에 데이터를 저장하는 메소드를 생성

    //데이터를 삽입하는 메소드
    func save(title:String, contents:String, runtime:Date) -> Bool{
        
        //1. 데이터베이스 객체 가져오기
        let appDelegate = UIApplication.shared.delegate as! AppDelegate
        let context = appDelegate.persistentContainer.viewContext
        
        //2. 추가할 데이터 생성
        let object = NSEntityDescription.insertNewObject(forEntityName: "ToDo", into: context)
        object.setValue(title, forKey: "title")
        object.setValue(contents, forKey: "contents")
        object.setValue(runtime, forKey: "runtime")
        
        do{
            //commit
            try context.save()
            //전체 데이터 다시 가져오기
            //여러 유저가 공유하는 데이터라면 다시 가져오는 것이 돟다.
            //self.list = self.fetch()
            
            //데이터를 list에 추가
            //ToDo 나 email처럼 혼자서만 사용하는 데이터라면 list에 바로 추가해도 된다.
            //self.list.append(object)
            
            //맨 앞에 추가
            self.list.insert(object, at: 0)
            
            return true
        }catch{
            //rollback
            context.rollback()
            return false
        }
    }

 

 

2) 입력화면으로 사용할 ViewController의 소스코드를 작성할 파일을 생성

=> UIViewController로부터 상속받는 ToDoInputVC

 

3) Main.storyboard 파일에 ViewController를 추가하고 Class 속성과 StoryboardID 속성을 변경 - ToDoInputVC

 

4) ToDoInputVC에 문자열 2개와 날짜를 입력받는 컨트롤을 배치하고 하단에 버튼을 2개 배치

=> 제목은 TextField, 내용은 TextView, 날짜는 DatePicker

=> 버튼은 취소와 확인

 

5)ToDoInputVC 에 디자인한 내용을 변수와 메소드로 연결

변수: tfTitle, tvContents, dpRuntime 

메소드: cancel, save

 

6)ToDoInputVC.swift 파일에 cancel 메소드 작성

       @IBAction func cancel(_ sender: Any) {
           //현재 화면 제거
           self.presentingViewController?.dismiss(animated: true)
       }

 

7)ToDoInputVC.swift 파일에 add 메소드 작성

    @IBAction func save(_ sender: Any) {
        //자신을 호출한 ToDoViewController에 대한 참조를 생성
        let naviVC = self.presentingViewController as! UINavigationController
        let toDoListVC = naviVC.topViewController as! ToDoListVC
        
        //삽입할 내용 가져오기
        let title = tfTitle.text
        let contents = tvContents.text
        let runtime = dpRuntime.date
        
        //데이터 삽입하고 결과 가져오기
        let result = toDoListVC.save(title: title!, contents: contents!, runtime: runtime)
        toDoListVC.dismiss(animated: true){() -> Void in
            //삽입에 성공했으면 데이터 다시 출력
            if result == true{
                toDoListVC.tableView.reloadData()
            }else{
                let alert = UIAlertController(title: "데이터 삽입", message: "실패", preferredStyle: .alert)
                alert.addAction(UIAlertAction(title: "확인", style: .default))
                self.present(alert, animated: true)
            }
        }
    }

 

8)ToDoListVC.swift 파일에 바 버튼 아이템이 호출할 메소드를 생성

    //오른쪽 바 버튼이 호출할 메소드
    @objc func add(_ sender:Any){
        //ToDoInputVC 를 화면에 출력
        let toDoInputVc = self.storyboard?.instantiateViewController(withIdentifier: "ToDoInputVC") as! ToDoInputVC
        self.present(toDoInputVc, animated: true)
    }

 

9)ToDoListVC.swift 파일의 viewDidLoad 메소드에 바 버튼을 추가하고 위에서 만든 메소드를 연결

 override func viewDidLoad() {
        super.viewDidLoad()
        
        self.title = "To Do"
        
        let addBtn = UIBarButtonItem(barButtonSystemItem: .add, target: self, action: #selector(add(_:)))
        self.navigationItem.rightBarButtonItem = addBtn
        

 

 

 

6. 삭제 구현

1) 데이터 삭제를 위한 메소드 작성

//데이터 삭제를 위한 메소드
    func delete(object:NSManagedObject) -> Bool{
        let appDelegate = UIApplication.shared.delegate as! AppDelegate
        let context = appDelegate.persistentContainer.viewContext
        
        //데이터 삭제
        context.delete(object)
        
        do{
            try context.save()
            return true
        }catch{
            context.rollback()
            return false
        }
    }

 

 

2) ToDoListVC.swift 파일의 viewDidLoad 메소드에 작성

       //네비게이션 바의 왼쪽에 편집 버튼 추가
        self.navigationItem.leftBarButtonItem = self.editButtonItem

 

3) edit 버튼을 눌렀을 때 보여질 버튼 모양을 설정하는 메소드를 작성

   //편집 버튼을 누를 때 보여질 아이콘 모양을 설정하는 메소드
    override func tableView(_ tableView:UITableView, editingStyleForRowAt indexPath:IndexPath) -> UITableViewCell.EditingStyle{
        return .delete
    }

 

4) 삭제 버튼을 눌렀을 때 수행할 작업을 설정하는 메소드를 작성

    //삭제 버튼을 눌렀을 때 호출되는 메소드
    override func tableView(_ tableView: UITableView, commit editingStyle: UITableViewCell.EditingStyle, forRowAt indexPath: IndexPath) {
        //삭제할 데이터 찾기
        let object = self.list[indexPath.row]
        //삭제를 하고 성공하면
        if self.delete(object: object){
            self.list.remove(at: indexPath.row)
            //self.tableView.reloadData()
            self.tableView.deleteRows(at: [indexPath], with: .right)
        }
    }