소피it블로그

[Core Data] 코어 데이터 스택 셋업하기 본문

개발_iOS/데이터 관리

[Core Data] 코어 데이터 스택 셋업하기

sophie_l 2022. 8. 17. 17:37

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

 

Apple Developer Documentation

 

developer.apple.com

앱의 객체들을 관리하고 저장해주는 클래스를 셋업해보자.

 

1. 개요

 

데이터 모델 파일을 생성한 후에 앱의 모델 계층을 통합적으로 지원하는 클래스들을 생성하라. 이 클래스들을 통틀어 Core Data stack 이라고 부른다.

 

  • NSManagedObjectModel의 인스턴스는 앱의 타입, 프라퍼티, 관계를 묘사하는 모델 파일을 나타낸다.
  • NSManagedObjectContext의 인스턴스는 앱의 타입의 인스턴스들의 변화를 감지한다.
  • NSPersistentStoreCoordinator의 인스턴스는 앱의 타입의 인스턴스를 스토어로부터 저장하고 페치한다.
  • NSPersistentContainer는 모델, 컨텍스트, 스토어 코디네이터를 모두 한 번에 셋업한다.

2. Persistent Container 초기화하기

 

코어 데이터는 대개 앱이 시작할 때 초기화한다. persistent container를 lazy 변수로 생성함으로써 앱의 delegate에서 처음 사용될 때로 초기화를 연기할 수 있다.

엑스코드 프로젝트를 새로 생성할 당시에 Core Data에 체크를 했으면, AppDelegate 내부에 이 셋업 코드가 자동적으로 포함된다.

  1. NSPersistentContainer 타입의 lazy 변수를 선언하기
  2. 데이터 모델 파일명을 이니셜라이저에 전달함으로써 persistent container 인스턴스를 생성하기
  3. persistent store가 있다면 로딩하기. 없는 경우 새로 생성함
class AppDelegate: UIResponder, UIApplicationDelegate {

    ...

    lazy var persistentContainer: NSPersistentContainer = {        
        let container = NSPersistentContainer(name: "DataModel")
        container.loadPersistentStores { description, error in
            if let error = error {
                fatalError("Unable to load persistent stores: \(error)")
            }
        }
        return container
    }()

    ...
}

persistent container는 한 번 생성된 후에는 모델, 컨텍스트, 스토어 코디네이터 인스턴스들에 대한 레퍼런스를 각각 managedObjectModel, viewContext, persistentStoreCoordinator 프라퍼티에 각각 가진다.

이제 유저 인터페이스의 컨테이너에 레퍼런스를 전달할 수 있다.

 

3. Persistent Container Reference를 뷰컨트롤러로 전달하기

 

앱의 루트 뷰 컨트롤러에 Core Data를 임포트하고 persistent container의 레퍼런스를 홀드할 변수를 생성하라.

import UIKit
import CoreData

class ViewController: UIViewController {

    var container: NSPersistentContainer!
    
    override func viewDidLoad() {
        super.viewDidLoad()
        guard container != nil else {
            fatalError("This view needs a persistent container.")
        }
        // The persistent container is available.
    }
}

앱의 델리게이트로 돌아가보자. application(_:didFinishLaunchingWithOptions:) 내부에서 앱 윈도우의 rootViewController를 앱의 루트 뷰 컨트롤러 타입으로 다운캐스팅하라. 이 레퍼런스에서 루트 뷰 컨트롤러의 container 프라퍼티를 persistent container로 세팅하라.

class AppDelegate: UIResponder, UIApplicationDelegate {

    ...

    func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {        
        if let rootVC = window?.rootViewController as? ViewController {
            rootVC.container = persistentContainer
        }        
        return true
    }

    ...
}

persistent container를 추가적인 뷰 컨트롤러에 패스하기 위해서는 각 뷰 컨트롤러에서 container 변수를 반복하여 생성하고, 그 값을 이전 뷰 컨트롤러의 prepare(for:sender:)에 세팅하라.

class ViewController: UIViewController {

    ...
    
    override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
        if let nextVC = segue.destination as? NextViewController {
            nextVC.container = container
        }
    }
}

4. Persistent Container 서브클래싱하기

 

NSPersistentContainer는 서브클래스되도록 의도되었다. 서브클래스는 데이터의 subset을 리넡하고 디스크에 데이터를 저장하게 호출하는 함수와 같은, 코어 데이터와 관련된 코드를 저장하기에 편리한 장소이다.

import CoreData

class PersistentContainer: NSPersistentContainer {    

    func saveContext(backgroundContext: NSManagedObjectContext? = nil) {
        let context = backgroundContext ?? viewContext
        guard context.hasChanges else { return }
        do {
            try context.save()
        } catch let error as NSError {
            print("Error: \(error), \(error.userInfo)")
        }
    }    
}

위의 예시에서는 컨테이너에 saveContext 함수를 추가함으로써 변화가 있을 경우에만 컨텍스트를 저장하도록 하여 성능을 향상시킨다.