Notice
Recent Posts
Recent Comments
Link
일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
1 | 2 | |||||
3 | 4 | 5 | 6 | 7 | 8 | 9 |
10 | 11 | 12 | 13 | 14 | 15 | 16 |
17 | 18 | 19 | 20 | 21 | 22 | 23 |
24 | 25 | 26 | 27 | 28 | 29 | 30 |
Tags
- UICollectionView
- HIG
- ribs
- uitableview
- tableView
- 리펙터링
- map
- Human interface guide
- 리펙토링
- Xcode
- 애니메이션
- SWIFT
- UITextView
- Protocol
- clean architecture
- Observable
- swiftUI
- Refactoring
- swift documentation
- rxswift
- 리팩토링
- 스위프트
- Clean Code
- RxCocoa
- combine
- MVVM
- collectionview
- uiscrollview
- ios
- 클린 코드
Archives
- Today
- Total
김종권의 iOS 앱 개발 알아가기
[iOS - SwiftUI] FetchRequest, FetchedResults 사용 방법 (Core Data) 본문
iOS 기본 (SwiftUI)
[iOS - SwiftUI] FetchRequest, FetchedResults 사용 방법 (Core Data)
jake-kim 2022. 10. 30. 23:40FetchRequest 란?
- SwiftUI에서는 Core Data 가져오기 요청 작업을 위한 전용 property wrapper를 지원하고 이것이 FetchRequest
- FetchRequest는 SwiftUI 뷰에 별도 로직 없이 직접 추가가 가능
- @FetchRequest 사용 시 데이터 id와 정렬에 사용되는 값을 선언하여 사용
@FetchRequest(
entity: Quake.entity(),
sortDescriptors: [SortDescriptor(\.time, order: .reverse)]
)
private var quakes: FetchedResults<Quake>
- 이 밖에도 predicate에는 NSPredicate(format:_:)를 추가하여 쿼리 조건을 추가 가능
@FetchRequest(
entity: Quake.entity(),
sortDescriptors: [SortDescriptor(\.time, order: .reverse)],
predicate: NSPredicate(format: "name == %jake", ascending: true)
)
private var quakes: FetchedResults<Quake>
Core Data 모델 추가 (FetchRequest 예제를 위해서 데이터 준비)
- Data Model
- Add Entity 클릭 -> Item이라는 Entity 추가
- name attribute도 추가
- 만약 SwiftUI의 @FetchRequest를 사용하지 않으면 서비스 레이어에서 fetchRequest를 정의하여 사용
FetchRequest
ex) SwiftUI 없이 그냥 Swift 사용할땐 아래처럼 fetchAll() 하여 데이터를 불러오는 코드가 존재
* CoreData를 다루는 상세한 개념은 이전 포스팅 글 참고
import CoreData
struct MyItemModel {
var name: String
}
class MyItemRepository {
let coreDataStorage: CoreDataStorage
init(coreDataStorage: CoreDataStorage = CoreDataStorage.shared) {
self.coreDataStorage = coreDataStorage
}
func add(name: String) {
let context = coreDataStorage.taskContext()
if let savedItem = fetch(name, in: context) {
savedItem.updatedDate = Date()
} else {
create(name, in: context)
}
context.performAndWait {
do {
try context.save()
} catch {
print(error)
}
}
}
private func fetch(_ name: String, in context: NSManagedObjectContext) -> MyItem? {
let fetchRequest = MyItem.fetchRequest()
fetchRequest.predicate = NSPredicate(format: "name == %@", argumentArray: [name])
do {
return try context.fetch(fetchRequest).first
} catch {
print("fetch for update Person error: \(error)")
return nil
}
}
fileprivate func create(_ name: String, in context: NSManagedObjectContext) {
let item = MyItem(context: context)
item.name = name
item.updatedDate = Date()
}
func getItems() -> [MyItemModel] {
fetchAll()
.compactMap(\.name)
.map(MyItemModel.init)
}
private func fetchAll() -> [MyItem] {
let request = MyItem.fetchRequest()
request.sortDescriptors = [NSSortDescriptor(keyPath: \MyItem.updatedDate, ascending: false)]
do {
return try coreDataStorage.viewContext.fetch(request)
} catch {
print("fetch Person error: \(error)")
return []
}
}
}
- SwiftUI에서는 위 fetchAll() 없이도, @FetchRequest로 데이터 로드를 간편하게 구현이 가능
- Fetch만 SwiftUI 방법을 쓸 것이고, 저장은 위에서 구현한 Swift방식대로 할것이기 때문에 위에서 정의한 MyItemRepository()를 통해 예제 데이터 추가
import SwiftUI
import CoreData
struct ContentView: View {
let repo = MyItemRepository()
var body: some View {
Text("Hello, world!")
.padding()
.onAppear {
repo.add(name: "jake")
repo.add(name: "kim")
print(repo.getItems())
// [ExCoredata.MyItemModel(name: "kim"), ExCoredata.MyItemModel(name: "jake")]
}
}
}
- NSManagedObjectContext값을 해당 뷰에 주입해야 coreData 스토리지에 접근할 수 있기 때문에 부모로 부터 주입받을 수 있는 @Environment 사용하여 주입
@Environment(\.managedObjectContext) private var viewContext
* NSManagedObjectContext 란? 위 CoreDataStorage를 정의할때도 나왔지만, 이 값은 유효성 검사, redu/undo와 같은 기능 처리를 할 수 있게끔 Core Data 스토리지 라이프 사이클을 담당
context 주입 코드)
- core data 사용하는 뷰
struct ContentView: View {
@Environment(\.managedObjectContext) private var viewContext
...
}
- 부모 뷰에서 .environment를 통해 CoreDataStorage의 context 주입
@main
struct ExCoredataApp: App {
let viewContext = CoreDataStorage.shared.viewContext
var body: some Scene {
WindowGroup {
ContentView()
.environment(\.managedObjectContext, viewContext)
}
}
}
- @FetchRequest를 선언하고, 타입은 FetchResults로 정의하여 CoreData에 접근하는 프로퍼티 items 추가
struct ContentView: View {
@Environment(\.managedObjectContext) private var viewContext
@FetchRequest(sortDescriptors: [NSSortDescriptor(keyPath: \MyItem.updatedDate, ascending: false)])
private var items: FetchedResults<MyItem> // <-
...
}
- items 사용은 ForEach로 접근하여 사용
struct ContentView: View {
@Environment(\.managedObjectContext) private var viewContext
@FetchRequest(sortDescriptors: [NSSortDescriptor(keyPath: \MyItem.updatedDate, ascending: false)])
private var items: FetchedResults<MyItem>
let repo = MyItemRepository()
var body: some View {
VStack {
ForEach(items) { item in
Text(item.name!)
}
Text("Hello, world!")
.padding()
.onAppear {
print(items.count)
print(repo.getItems().count)
// 샘플 데이터 준비
// repo.add(name: "jake")
// repo.add(name: "kim")
}
}
}
}
결과) kim, jake 데이터가 정상 표출되는 것을 확인
- 따로 CoreDataSotorage를 정의하여 사용하지 않아도, SwiftUI 스럽게 해당 뷰에서 addItem 메소드를 추가하여 코어데이터에 접근하는 방식으로 구현도 가능
// ContentView.swift
private func addItem(_ name: String) {
withAnimation {
let newItem = MyItem(context: viewContext)
newItem.updatedDate = Date()
newItem.name = name
do {
try viewContext.save()
} catch {
print(error)
}
}
}
- 버튼을 누를경우 addItem 호출
var body: some View {
VStack {
ForEach(items) { item in
Text(item.name!)
}
Text("Hello, world!")
.padding()
.onAppear {
print(items.count)
print(repo.getItems().count)
// 샘플 데이터 준비
// repo.add(name: "jake")
// repo.add(name: "kim")
}
Button("Add 'jake123'") {
addItem("jake123")
}
}
}
완성) 버튼을 누를때마다 Core Data에 write하면 FetchResult 프로퍼티인 items이 업데이트
* 전체 코드: https://github.com/JK0369/ExCoreData
* 참고
https://developer.apple.com/documentation/coredata/nsmanagedobjectcontext
https://medium.com/@kyang3200/ios-and-swift-how-to-use-core-data-with-fetchrequest-5e7be62a3092
https://developer.apple.com/documentation/swiftui/fetchrequest
'iOS 기본 (SwiftUI)' 카테고리의 다른 글
Comments