Core Data 사용해 CRUD 구현하기

sanghee·2021년 9월 25일
4

👏iOS 스터디

목록 보기
6/10
post-thumbnail

Core Data란?

코어데이터(Core Data)란 macOS 및 iOS 운영 체제에서 Apple이 제공하는 객체 그래프 및 지속성 프레임워크이다.

Core Data vs UserDefaults

UserDefaults는 앱의 설정과 같은 간단한 데이터를 저장하기에 적합한 반면, 코어데이터는 복잡하고 큰 데이터를 저장하기에 적합하다.

Data Model

새롭게 프로젝트를 생성한다면 Use Core Data를 체크한다. 반대로 기존 프로젝트에 추가한다면 Data Model 파일을 추가해야 한다. 그리고 Bookmark Entity에 관련 정보를 저장한다. 공지에서 북마크 버튼을 클릭하면 여기에 저장한다. 북마크는 Notice 모델이므로 Attribute에 title, time, url을 추가한다.

struct Notice: Codable, Equatable {
    let title: String
    let time: String
    let url: String
}

Core Data Stack

Core Data Stack은 Model, Context, Store coordinator, 그리고 Persistent container로 이루어져 있다.

  • Model: Entity를 설명하는 Database 스키마이다. managed objects의 struct를 정의한다.
  • Store coordinator: persistent storage(영구 저장소)와 managed object model을 연결한다.
  • Context: managed objects를 생성하고, 저장하고, 가져오는 작업을 제공한다.
  • Persistent container: Core Data Stack을 나타내는 모든 객체를 포함한다.


CoreData 사용 과정

  1. Persistent container를 만든다.
  2. Context와 Entity를 가져온다.
  3. object를 만들고 값을 세팅한다.
  4. Context를 저장한다.

CoreDataManager 만들기

코어데이터를 관리하는 CoreDataManager를 만들어보자. 프로젝트에 CoreDataManager 파일을 생성한다.

1. Persistent container 만들기

Persistent container를 생성한다. "Model" 위치에는 Data Model 파일명을 넣는다. 프로젝트에서 CoreDataManager를 한번만 생성하고 싶으므로 shared를 정의한다.

// CoreDataManager.swift

import CoreData
import Foundation

class CoreDataManager {
    static var shared: CoreDataManager = CoreDataManager()
    
    lazy var persistentContainer: NSPersistentContainer = {
        let container = NSPersistentContainer(name: "Model")
        container.loadPersistentStores(completionHandler: { (storeDescription, error) in
            if let error = error as NSError? {
                fatalError("Unresolved error \(error), \(error.userInfo)")
            }
        })
        return container
    }()
}

2. Context와 Entity 가져오기

context와 북마크 Entity를 가져온다.

class CoreDataManager {
    ...
    
    var context: NSManagedObjectContext {
        return persistentContainer.viewContext
    }
    
    var bookmarkEntity: NSEntityDescription? {
        return  NSEntityDescription.entity(forEntityName: "Bookmark", in: context)
    }
}

3. object를 만들고 값 세팅하기

북마크 Entity로부터 managedObject를 생성한다. Bookmark는 title, time, url을 가지고 있으므로 오브젝트에 각각 해당 값들을 세팅한다. insertBookmark라는 함수는 notice를 받아 각각 title, time, url를 세팅한다.

class CoreDataManager {
    ...

    func insertBookmark(_ notice: Notice) {
        if let entity = bookmarkEntity {
            let managedObject = NSManagedObject(entity: entity, insertInto: context)
            managedObject.setValue(notice.title, forKey: "title")
            managedObject.setValue(notice.time, forKey: "time")
            managedObject.setValue(notice.url, forKey: "url")
        }
    }
}

4. Context 저장하기

managedObject를 세팅한 이후 context를 저장하는 saveToContext 함수를 이용해 저장한다.

class CoreDataManager {
    ...
    
    func saveToContext() {
        do {
            try context.save()
        } catch {
            print(error.localizedDescription)
        }
    }

    func insertBookmark(_ notice: Notice) {
        if let entity = bookmarkEntity {
            let managedObject = NSManagedObject(entity: entity, insertInto: context)
            managedObject.setValue(notice.title, forKey: "title")
            managedObject.setValue(notice.time, forKey: "time")
            managedObject.setValue(notice.url, forKey: "url")
            saveToContext()
        }
    }
}

CRUD 구현하기

Create 함수는 위에서 insertBookmark 함수로 구현했으니, 나머지 Read, Update, Delete 기능을 구현해본다.

Read 구현하기

fetchBookmarks 함수는 bookmark에 저장된 데이터를 fetch하는 함수이다. 그리고 getBookmarks 함수는 북마크들을 notice 모델로 변환해 notice 배열을 반환하는 함수이다.

class CoreDataManager {
    ...
    
    func fetchBookmarks() -> [Bookmark] {
        do {
            let request = Bookmark.fetchRequest()
            let results = try context.fetch(request)
            return results
        } catch {
            print(error.localizedDescription)
        }
        return []
    }

    func getBookmarks() -> [Notice] {
        var notices: [Notice] = []
        let fetchResults = fetchBookmarks()
        for result in fetchResults {
            let notice = Notice(title: result.title ?? "", time: result.time ?? "", url: result.url ?? "")
            notices.append(notice)
        }
        return notices
    }
}

Update 구현하기

updateBookmark함수는 notice를 받아 제목을 수정하는 함수이다. 사용하지 않지만 Update함수를 구현하기 위해 만들었다. 이 함수에서 수정할 title, time, url을 옵셔널로 받아서 수정하면 좋겠다.

class CoreDataManager {
    ...

    func updateBookmark(_ notice: Notice) {
        let fetchResults = fetchBookmarks()
        for result in fetchResults {
            if result.url == notice.url {
                result.title = "업데이트한 제목"
            }
        }
        saveToContext()
    }
}

Delete 구현하기

deleteBookmark 함수는 notice를 받아서 코어데이터에 해당 notice를 삭제하는 함수이다. notice의 url은 고유의 값이므로, filter를 사용하여 해당 notice를 찾아 삭제하였다.

deleteAllBookmarks 함수는 코어데이터에 있는 북마크 관련 정보들을 다 삭제하는 함수이다. 두 함수 모두, 값을 변경한 이후에 context를 저장한다.

class CoreDataManager {
    ...
    
    func deleteBookmark(_ notice: Notice) {
        let fetchResults = fetchBookmarks()
        let notice = fetchResults.filter({ $0.url == notice.url })[0]
        context.delete(notice)
        saveToContext()
    }
    
    func deleteAllBookmarks() {
        let fetchResults = fetchBookmarks()
        for result in fetchResults {
            context.delete(result)
        }
        saveToContext()
    }
}

후기

처음에 UserDefaults로 구현했다가(링크는 밑에 첨부), 위의 데이터를 저장하기에 더 적합한 코어데이터로 변경하게 되었다. 처음 써봤는데, 하루 고생해서 구현을 하게 되어 매우 뿌듯하다😆

CoreDataManager 깃허브 링크

https://github.com/della-padula/YappUltraHardPractice/blob/main/Sanghee/Practice1/Practice1/Utilities/CoreDataManager.swift

UserDefaults로 구현한 깃허브 링크

https://github.com/della-padula/YappUltraHardPractice/blob/ba691475925cfefd0c2ca7c796fad627ca8552bd/Sanghee/Practice1/Practice1/Controller/DetailController.swift

참고 사이트

https://zeddios.tistory.com/987

profile
👩‍💻

2개의 댓글

comment-user-thumbnail
2023년 3월 30일

혹시 이 부분에서 만약 찾는 결과가 없을 경우 런타임 에러가 날 가능성이 있을까요?
let notice = fetchResults.filter({ $0.url == notice.url })[0][0] 이 부분이요

1개의 답글