-
Notifications
You must be signed in to change notification settings - Fork 0
CoreData
Moonkey edited this page Oct 10, 2023
·
1 revision
- 프로젝트 생성 시 Use Core Data를 체크해서 생성하면 자동으로
.xcdatamodeld파일이 생성된다. - 진행중인 프로젝트의 경우 New File - Data Model을 찾아서 추가하면
.xcdatamodeld파일을 생성할 수 있다.
위에서 생성한
.xcdatamodeld파일을 보면 Entity와 Attribute 등을 확인할 수 있다. 해당 데이터 모델은 객체와 객체들 사이의 관계에 대한 정보를 담고 있다.
CoreData에서 Entity는 class나 struct, Attribute는 Entity에 대한 프로퍼티라고 생각할 수 있다.
- Entity를 추가한다. 예시로 People과 Person이라는 두가지 Entity를 추가했다.
- People안에 Person이 종속 될 수 있게 Property를 생성한다. 이때 아차 싶다. 왜냐하면 Attribute에는 Person이라는 타입이 없기 때문이다. 이때 사용하는 것이 Relationships 개념이다.
- Relationships: Entity들 사이에 어떻게 관련이 있는지, 변경사항이 Entity간에 어떻게 전달될지를 정의한다.
- Attributes 하단에 Realtionships에서 프로퍼티 이름과 Destination(상대 Entity)를 설정해 준다.
- Relationships은 관련된 Entity ahen 설정해주어야 하나의 값이 변했을 때 Core Data에서 인지해서 값을 바꿔줄 수 있어서 Inverse에 연결된 값을 명시해주어야 한다.
- People members의 Relationships Type은 연결 관계가 1:n이기 때문에 Too Many로 변경해주어야 한다.
- 추가로 우측에 Attributes에 대해 optional로 기본으로 되어있어서 해제해주어야 한다.
Model 파일의 우측에 보면 Class에 관한 정보를 설정할 수 있다. 그 중 CodeGen을 바꿔서 class를 생성하는 방법을 설정할 수 있다.
-
Manul/None: 하나의 Entity에 대해 CoreDataClass파일과 CoreDataProperties파일을 만든다.
- CoreDataClass: Entity를 표현하는 클래스. 생성된 클래스는 자동으로 NSManagedObject를 상속한다.
- CoreDataProperties: CoreDataClass의 익스텐션. Attribute들을 프로퍼티로 갖고 있다.
- Class Definition: 기본 설정인 Class Definition은 위의 두 파일을 자동으로 만들어 알아서 관리하기 때문에 개발자가 해당 파일을 볼 수 없어 커스텀 코드를 추가할 수 없다.
- Category/Extension: CoreDataClass 파일만 만들어주고 CoreDataProperties 파일은 Xcode가 자동으로 관리해준다.
이 부분은 어려워서 이후 정리를 한번 더 해야할 것 같습니다. 당장 몰라도 구현은 가능하지만 이후에는 필요해서 추가로 정리하겠습니다.
-
NSManagedObjectModel
- Entities라고 불리는 모델 객체와 다른 Entity들과의 관계를 정의한다.
- 데이터 모델을 로드하고 Core Data Stack에 노출한다.
-
NSManagedObjectContext
- 데이터베이스에 있는 객체를 보고 접근하게 해주는 window이다.
- Core Data Stack의 핵심. managed object context로 Core Data Stack과 소통하기 때문에 이 객체를 개발자가 가장 많이 사용한다.
-
NSPersistentStoreCoordinator
- Core Data Stack의 연결을 도와준다. managed obejct model과 managed object context에 대한 참조를 유지한다.
- managed object model을 통해서 데이터 모델을 이해하고, persistent store를 관리한다.
-
NSPersistentContainer
- Core Data Stack을 캡슐화한 컨테이너. 즉,
NSPersistentContainer클래스가NSManagedObjectModel,NSManagedObjectContext,NSPersistentStoreCoordinator를 프로퍼티로 가지고 있다 - 코어 데이터의 데이터베이스라고 볼 수 있다.
- Core Data Stack을 캡슐화한 컨테이너. 즉,
위에 Core Data Stack에 대해 설명했지만 NSPersistentContainer가 캡슐화를 하고 있어서 NSPersistentContainer만 만들어주면 나머지 것들을 관리할 수 있다.
struct PersistenceController {
static let shared = PersistenceController()
let container: NSPersistentContainer
init(inMemory: Bool = false) {
// 여기 name은 .xcdatamodeld의 파일명과 같이야 한다.
container = NSPersistentContainer(name: "ModelName")
container.loadPersistentStores { _, error in
if let error = error as NSError? {
fatalError("Unresolved error \(error), \(error.userInfo)")
}
}
}
// context 변화에 대해 저장하는 method 생성
func saveContext() {
let context = container.viewContext
if context.hasChanges {
do {
try context.save()
} catch {
let nserror = error as NSError
fatalError("Unresolved error \(nserror), \(nserror.userInfo)")
}
}
}
}-
ProjectNameApp.swift 파일에서 싱글톤으로 만든 persistentController를 Environment로 ContentView에 전달한다.
struct testApp: App { let persistenceController = PersistenceController.shared var body: some Scene { WindowGroup { ContentView() .environment(\.managedObjectContext, persistenceController.container.viewContext) } } }
-
CoreData를 사용할 파일에 접근(또는 생성하여) Environment로 제공된 context에 접근한다.
//CoreDataView.swift @Environment(\.managedObjectContext) var managedObjectContext
-
저장된 데이터를 가져오기 위해 @FetchRequest Property Wrapper를 사용해서 데이터를 가져온다.
- @FetchRequest는 데이터의 변경이 생길 때마다 fetch하기 때문에 데이터와 UI를 동기화하는데 좋다.
- 어떤 데이터를, 어떤 순서로, 어떤 조건으로 가져올 것인지 predicate를 통해 정의할 수 있다.
- predicate refercence
- predicate refercence
//CoreDataView.swift @FetchRequest( entity: Person.entity(), sortDescriptors:[ NSSortDescriptor(keyPath: \Person.age, ascending: true) ] ) var people: FetchedResults<Person>
-
CRUD 메소드를 구현한다.
//CoreDataView.swift func saveContext() { do { try managedObjectContext.save() } catch { print("Error saving managed object context: \(error)") } } func addPerson(name: String, age: Int32) { let newPerson = Person(context: managedObjectContext) newPerson.name = name newPerson.age = age saveContext() } func deletePerson(at offsets: IndexSet) { offsets.forEach { index in let person = self.people[index] self.managedObjectContext.delete(person) } saveContext() }
-
추가 제거를 위한 간단한 UI를 구현한다.
//CoreDataView.swift VStack { Spacer() .frame(height: 50) VStack { ForEach(people, id: \.self) { p in HStack { Text("\(p.name ?? "defaultName")") Spacer() Text("\(p.age)") } .font(.title) .padding(.bottom, 20) } } Spacer() Button { addPerson(name: "person\(people.count)", age: Int32(people.count + 20)) } label: { Text("add person") .frame(width: 300, height: 50) .background(.yellow) } Button { deletePerson(at: IndexSet([people.count - 1])) } label: { Text("delete") .frame(width: 300, height: 50) .background(.orange) } Spacer() .frame(height: 30) } .padding()
//
// testApp.swift
// test
//
// Created by Seungui Moon on 2023/09/26.
//
import CoreData
import SwiftUI
struct PersistenceController {
static let shared = PersistenceController()
let container: NSPersistentContainer
init(inMemory: Bool = false) {
container = NSPersistentContainer(name: "TestModel")
container.loadPersistentStores { _, error in
if let error = error as NSError? {
fatalError("Unresolved error \(error), \(error.userInfo)")
}
}
}
func saveContext() {
let context = container.viewContext
if context.hasChanges {
do {
try context.save()
} catch {
let nserror = error as NSError
fatalError("Unresolved error \(nserror), \(nserror.userInfo)")
}
}
}
}
@main
struct testApp: App {
let persistenceController = PersistenceController.shared
var body: some Scene {
WindowGroup {
ContentView()
.environment(\.managedObjectContext, persistenceController.container.viewContext)
}
}
}//
// CoreDataView.swift
// test
//
// Created by Seungui Moon on 10/9/23.
//
import SwiftUI
struct CoreDataTestView: View {
@Environment(\.managedObjectContext) var managedObjectContext
@FetchRequest(
entity: Person.entity(), sortDescriptors:[
NSSortDescriptor(keyPath: \Person.age, ascending: true)
]
) var people: FetchedResults<Person>
var body: some View {
VStack {
Spacer()
.frame(height: 50)
VStack {
ForEach(people, id: \.self) { p in
HStack {
Text("\(p.name ?? "defaultName")")
Spacer()
Text("\(p.age)")
}
.font(.title)
.padding(.bottom, 20)
}
}
Spacer()
Button {
addPerson(name: "person\(people.count)", age: Int32(people.count + 20))
} label: {
Text("add person")
.frame(width: 300, height: 50)
.background(.yellow)
}
Button {
deletePerson(at: IndexSet([people.count - 1]))
} label: {
Text("delete")
.frame(width: 300, height: 50)
.background(.orange)
}
Spacer()
.frame(height: 30)
}
.padding()
}
func saveContext() {
do {
try managedObjectContext.save()
} catch {
print("Error saving managed object context: \(error)")
}
}
func addPerson(name: String, age: Int32) {
let newPerson = Person(context: managedObjectContext)
newPerson.name = name
newPerson.age = age
saveContext()
}
func deletePerson(at offsets: IndexSet) {
offsets.forEach { index in
let person = self.people[index]
self.managedObjectContext.delete(person)
}
saveContext()
}
}
#Preview {
CoreDataTestView()
}- Core Data 시작하기(2) - Data Model 만들기(1) - entity만들기
- core-data-in-swiftui
- [iOS] SwiftUI에서 CoreData 써보기](https://velog.io/@nala/iOS-SwiftUI%EC%97%90%EC%84%9C-CoreData-%EC%8D%A8%EB%B3%B4%EA%B8%B0)
| 기능 | 스크린샷 |
|---|---|
| Create | ![]() |
| Delete | ![]() |

