func loadItems(with request: NSFetchRequest<Item> = Item.fetchRequest(), predicate: NSPredicate? = nil){
// 이런 식으로 원하는 조건을 적용시켜 값을 가져올 수 있다
// 원하는 값 가져오는 조건문 설정
let categoryPredicate = NSPredicate(format: "parentCategory.name MATCHES %@", selectedCategory!.name!)
if let additionalPredicate = predicate {
request.predicate = NSCompoundPredicate(andPredicateWithSubpredicates: [categoryPredicate, additionalPredicate])
} else {
request.predicate = categoryPredicate
}
do {
itemArray = try context.fetch(request)
} catch {
print("error \(error)")
}
tableView.reloadData()
}
NSPredicate는 메모리 내에서 특정 값을 가져오는데 사용하는 조건을 설정하는 것이다.
여기서 조건을 설정하는 방법은 아래와 같다.
let categoryPredicate = NSPredicate(format: "parentCategory.name MATCHES %@", selectedCategory!.name!)
조건을 설정하는 방법이다.
NSCompoundPredicate는 predicate인데 로직을 이용하여 다른 predicate들과 조합할 수 있다고 한다.
즉 다시말해 하단에 NSCompoundPredicate에서 andPredicateWithSubpredicates는 AND를 사용하여 categoryPredicate와 additionalPredicate를 동시에 만족시켜야 된다는 Predicate를 생성하여 request에 담은 것이다.
request.predicate = NSCompoundPredicate(andPredicateWithSubpredicates: [categoryPredicate, additionalPredicate])
공식 문서에 보면 AND, OR, NOT을 쓸 수 있다는 것을 확인할 수 있다
Core Data
- 데이터베이스가 아니다
- XML, Binary, SQLite 을 데이터를 저장하는데 사용하는데 Core Data는 default로 사용된다.
- 현재 개발자들은 sqlite을 가장 많이 사용하고 있는 중이다.
- 우측 하단에 있는게 저장소인데 sqlite를 주로 사용한다.
- Persistent container는 model, context, storeCoordinator를 관리한다.
- NSManagedObjectModel: 이 객체는 앱의 type, property, relationship들을 model 파일에 저장한다.
> Model에 따른 스키마를 나타내는 곳인 듯 하고, Entities라고 불리는 모델 객체와 다른 Entity들과의 관계를 정의한다.
> CoreData, Model Data 파일을 생성했을때 만들어지는 앱이름.xcdatamodeld 파일 내 객체들을 설명하는 클래스다.
- NSManagedObjectContext: 앱의 type들의 변화를 관찰하고 관리한다.
> Core Data에 있는 객체를 보고 접근하게 해주는 window다.
> Core Data Stack의 핵심으로 Core Data Stack과 소통을 하기 때문에 개발자들이 가장 많이 이 객체를 사용한다고 한다.
> 객체가 생성되고 메모리에서 해제될 때까지 life-cycle을 관리한다.
> 앱에서는 관리 객체의 생성, 삭제, 편집 등을 수행하기 위해서 NSManagedObjectContext와 통신한다고 한다.
> 즉 쉽게 말해서 Core Data에 값을 생성, 수정, 삭제 또는 검색을 할 수 있게 해준다.
- NSPersistentStoreCoordinator: 객체들을 저장하고 가져온다. (중요)
> object model과 store를 연결하는 다리 역할을 한다.
> NSManagedObjectModel 을 이용하여 entity를 만들고, 이미 entity가 존재한다면 NSPersistenStore로부터 데이터를 가져온다.
- Core Data는 내부적으로 SQLite를 사용하는 Object-Oriented DataBase다.
- Object-Oriented DataBase라고 하는 이유는, 데이터베이스에 직접 CRUD를 명령하는 일반적인 DB와는 달리 Core Data는 DB와 직접 소통하지 않는다. (Fluent 프레임워크랑 같은 원리라고 생각하면 될 것 같다.)
- Core Data의 DB는 NSPersistentContainer , 그 위에서 DB에 접근하게 해주는 window가 NSManagedObjectContext다.
- 데이터를 persistence store에 저장하거나 가져오기 위해서는 context를 반드시 거쳐야 한다.
그리고 Entity의 설정을 보면 Codegen에 Class Definition 설정이 기본으로되어 있다.
Class Definition은 그 의미는 Xcode가 entity editor의 변화를 지켜보고 자동으로 업데이트 및 생성을 해준다.
Manual/None
- 이 옵션은 아무런 것도 하지 않겠다는 것이다
- 어떠한 자동적인 연결도 필요없고 모든 것을 개발자가 하겠다라고 설정하는 것이다
Class Definition
- 이 설정은 Data, Entities, Attribute 들을 자동으로 Class화 시켜주고 Property화 시켜주는 역할을 한다.
- 예시: /Users/taewonyoon/Library/Developer/Xcode/DerivedData/Devote-hclyuaezlisssdfbqsfonzrecyht/Build/Intermediates.noindex/Devote.build/Debug-iphonesimulator/Devote.build/DerivedSources/CoreDataGenerated 위치에 들어가면 아래 사진처럼 구성되어 있는 것을 확인할 수 있다.
import Foundation
import CoreData
@objc(Item)
public class Item: NSManagedObject {
}
import Foundation
import CoreData
extension Item {
@nonobjc public class func fetchRequest() -> NSFetchRequest<Item> {
return NSFetchRequest<Item>(entityName: "Item")
}
@NSManaged public var task: String?
@NSManaged public var timestamp: Date?
}
extension Item : Identifiable {
}
그럼 이제 Item이 클래스화되어 관리되고 있다는 것을 확인할 수 있다.
- Class 파일은 생성되면 Xcode에 의해서 수정되지 않는다.
- Properties 파일은 Xcode 필요(Attribute를 수정하는것같이)에 따라서 끊임없이 재생성 된다.
Category/Extension
- 코드를 통해서 Core Data 파일들을 제어하기 원한다면 이 옵션을 선택한다. (하지만 Class Definition과 다르게 class file, properties file 두개로 구성된다)
- 이 옵션을 선택하면 우리는 스스로 Entity들의 이름으로 Class들을 직접 생성해야 한다. 그럼 Xcode가 자동으로 연결해준다.
Core Data의 저장 방법
- core data는 In-merory, XML, Binary, SQLite 가 있다.
- SQLite가 가장 많이 사용되고 있다.
230925 기준
persistent container만 만들어주면 모든 model, context, store coordinator를 관리할 수 있다.
struct PersistenceController {
static let shared = PersistenceController()
let container: NSPersistentContainer
init(inMemory: Bool = false) {
container = NSPersistentContainer(name: "컨테이너 이름")
if inMemory {
container.persistentStoreDescriptions.first!.url = URL(fileURLWithPath: "/dev/null")
}
container.loadPersistentStores(completionHandler: { (storeDescription, error) in
if let error = error as NSError? {
fatalError("Unresolved error \(error), \(error.userInfo)")
}
})
}
}
1. static let shared = PersistenceController( )
모든 앱에서 하나만 존재하는 싱글톤이다.
해당 컨트롤러는 model과 context 그리고 store coordinator를 한번에 setup한다.
컨테이너에 이름은 .xcdatamodelld의 파일 이름과 같아야한다.
2. let container: NSPersistentContainer
- 이 프로퍼티는 core data를 사용하기 위한 storage property다.
- persistent container는 core data stack과 core data model를 초기화 한다.
3. init(inMemory: Bool = false) { ...
- 값이 저장된 persistent store를 불러오는 것이다,
init(inMemory: Bool = false) {
container = NSPersistentContainer(name: "컨테이너 이름")
if inMemory {
container.persistentStoreDescriptions.first!.url = URL(fileURLWithPath: "/dev/null")
}
container.loadPersistentStores(completionHandler: { (storeDescription, error) in
if let error = error as NSError? {
fatalError("Unresolved error \(error), \(error.userInfo)")
}
})
}
- container.loadPersistentStores 를 통해서 SQLite Store 기반인 Core Data에 저장된 값을 가져올 수 있다.
- inMemory가 true면 실제 메모리에 저장하지 않고 테스트 하는 용도로 사용할 수 있다, 테스트가 끝나면 해당 DB는 메모리에서 제거
- inMemory가 false면 실제 메모리에 데이터를 저장하도록 하여 영구적으로 저장할 수 있다.
4. 값을 저장한다.
// PersistenceController
func saveContext() {
let context = persistentContainer.viewContext
if context.hasChanges {
do {
try context.save()
} catch {
let nserror = error as NSError
fatalError("Unresolved error \(nserror), \(nserror.userInfo)")
}
}
}
5. context를 Environment를 통해 모든 뷰에 전달한다.
@main
struct CoreDataPracticeApp: App {
let persistenceController = PersistenceController.shared
var body: some Scene {
WindowGroup {
ContentView()
.environment(\.managedObjectContext, persistenceController.container.viewContext)
}
}
}
- managedObjectContext 는 관리되는 활성화된 core data를 저장하도록 설계되었다.
- 간단히 말해서, 한 줄의 코드로, Core Data 컨테이너의 mangedObjectContext는 모든 뷰에 전달되어 사용할 수 있도록 하였다.
6. 사용할 뷰에서 managedObjectContext를 사용한다.
@Environment(\.managedObjectContext) var managedObjectContext
- 위에 Environment 객체인 managedObjectContext로 값을 가져오고 저장할 수 있다.
7. 다음으로 값을 가져오는 FetchRequest를 작성한다. (조건을 사용하여 원하는 특정 값을 가져올 수 있다)
@FetchRequest(
entity: 값을 가져오고 싶은 엔티티이름.entity(),
sortDescriptors: [
NSSortDescriptor(keyPath: \정렬하고 싶은 기준, ascending: true)
],
predicate: NSPredicate(format: "")
) var friend: FetchedResults<엔티티이름>
- 내가 찾고 싶은 값을 지정하여 값을 가져올 수 있다.
- sortDescriptors 는 최대 4개의 인자값을 가질 수 있다. (1. Entity / 2. Sort descriptor. /. 3. Predicate. / 4. Animation)
- Entity는 우리가 하고 싶은 query 다.
- Sort Descriptor는 우리가 반환받은 값을 정렬하는데 사용된다.
- Predicate는 data를 filter하는데 사용된다. (상단에 사용 방법)
- 네 그리고 마지막으로, animation은 가져온 결과에 대한 변경 사항에 사용됩니다.
> tmp를 통해서 Core Data에 저장되어 있는 모든 엔티티의 값을 가져와 사용할 수 있다.
func addMovie(friend_id: String) {
let newFriend = Friend(context: managedObjectContext)
newFriende.friend_id = friend_id
saveContext()
}
func deleteMovie(at offsets: IndexSet) {
offsets.forEach { index in
let friend = self.friend[index]
self.managedObjectContext.delete(friend)
}
saveContext()
}
추가로 아래에 Core Data를 사용하면 기본적으로 제공하는 DateFormatter인데 자유롭게 사용하자.
let itemFormatter: DateFormatter = {
let formatter = DateFormatter()
formatter.dateStyle = .short
formatter.timeStyle = .medium
return formatter
}()
'swift' 카테고리의 다른 글
swift - Realm 설치 후 No such module "Realm" 오류 해결방법 (0) | 2023.06.25 |
---|---|
swift - NSFetchRequest (0) | 2023.06.25 |
swift - userdefault에 저장된 값 확인하는법 (0) | 2023.06.19 |
Swift DispatchQueue란? (0) | 2023.06.14 |
URLSession, URLSessionConfiguration (0) | 2023.06.11 |