SwiftUI에서 Core Data를 사용할때 같이 사용하면 좋은 @FetchRequest에 대해 설명해보겠습니다.
그럼 Core Data가 무엇인가요?
데이터를 디바이스에 저장하는 하는 방법으로는 @AppStorage, UserDefault가 있지만 이러한 것들은 간단한 정보(즉, 원시타입) 를 저장하기에 적합했다면... Core Data는 복잡하고 다른 객체간의 관계를 관리하며 user data를 저장하기에 적합합니다.
@AppStorage에 대해 궁금하다면
어서와(SwiftUi) AppStorage는 처음이지?
@AppStorage 란 무엇인가? - Swift에서는 앱을 빌드하고 실행할때 메모리에는 String과 Int형식으로 저장된다. 상태변화가 될때 사용하는 @State와 @StateObject랑 차이점은 뭔가요? A. 앱을 종료하면 메모리
yeomir.tistory.com
SwiftUI @AppStorage 심화편
@AppStorage 심화편으로 돌아왔습니다. 이번 차트에서는 @AppStorage를 사용하여 프로젝트를 만들어 볼테데요. 기초편에서도 말했지만 간단히 짚고가지면 @AppStorage는 Int, String과 같은 원시타입만 저장
yeomir.tistory.com
Core Data에 대해 궁금하다면
https://zeddios.tistory.com/987
Core Data (1)
안녕하세요 :) Zedd입니다. Core Data를 사용할 일이 생겼는데...제가 옜날에 해봤단 말이죠..!?!?!? 근데 다시 하려니까 생각이 하나도 안나는거에요 그때는 문서 볼 생각도 안했었는데..ㅎㅎㅎㅎ 이
zeddios.tistory.com
https://velog.io/@nala/iOS-SwiftUI%EC%97%90%EC%84%9C-CoreData-%EC%8D%A8%EB%B3%B4%EA%B8%B0
[iOS] SwiftUI에서 CoreData 써보기
이 글을 쓰는 나와 나를 지켜보는 n년차 개발자
velog.io
자 그럼 @FetchRequest에 대해 알아보겠습니다.
일단 @FetchRequest는 SwifUI가 나올때 같이 나왔습니다. iOS13.0부터 Core Data를 사용할때 많이 활용되는 프로퍼티 래퍼입니다.
그럼 @FetchRequest은 언제 사용하나요?
Cora Data에서 데이터를 불러와 List 형식으로 화면에 뿌려줄때 정렬 기준을 설정 할 수 있습니다.
즉, 정렬이 필요할 때 사용 됩니다 (Ex. 시간순, 오름차순, 내림차순 등등...)
Core Data를 만드는 법은 두가지가 있습니다만, 저는 만들어진 프로젝트에 Core Data를 추가하는 방식을 채택해서 프로젝트를 구현해 보겠습니다.
1. 파일을 만들때 com + n을 눌러 파일을 생성해줍니다. 검색창에 data라고 치면 Data Model 파일을 만들 수 있습니다.
2. 모델명을 Entity로 만들고, 하단에 Add Entity를 눌러 Atrributes에 두가지 속성을 추가해줍니다.
3. 파일 추가 ( CoreDataManager) 상단에 import CoreData 까먹지 말기
(엥? 왜 자동완성이 안되지? 하지 ... or 왜 오류가 발생하지.. 하지 않도록! )
import CoreData
class CoreDataManager: ObservableObject {
// 싱글톤 생성
static let shared = CoreDataManager()
//NSPersistentContainer : Core Data Stack을 나타내는 필요한 모든 것
let container = NSPersistentContainer(name: "Entity")
init() {
// loadPersistentStores: 데이터를 저장하고 로드하는 주요 기능
container.loadPersistentStores { description, error in
if let error {
print("코어 데이터에서 에러 발생 \(error.localizedDescription)")
}
}
}
}
SwiftUI는 Swift와 다르게 AppDelegate가 없는데 어디에 적용하면 좋을까?
이런 생각을 하셨다면 당신은 개발자로 성장하기 딱 좋은 인재상...
4. 바로 _APP단에서 설정해주면 됩니다. 참 쉽죠? ( SwiftUI 알럽유...)
import SwiftUI
@main
struct SwifUI_CoreDataFetchRequest_ExamApp: App {
// StateObject를 활용하여 생성
@StateObject private var coreDataManager = CoreDataManager.shared
// 다른 방법 싱글톤 활용
//let coreDataManager2 = CoreDataManager.shared
var body: some Scene {
WindowGroup {
ContentView()
.environment(\.managedObjectContext, coreDataManager.container.viewContext)
//.environment(\.managedObjectContext, coreDataManager2.container.viewContext)
}
}
}
5. ContentView에서 추가되는 것들을 Entity()에 저장하기 위해서 @Environment(~ ) @FetchRequest( ~ ) 를 추가해보겠습니다.
struct ContentView: View {
// viewContext 불러오기 text추가하여 할때 필요
@Environment(\.managedObjectContext) private var viewContext
@FetchRequest(entity: Entity.entity(), sortDescriptors:[] ) var testExample: FetchedResults<Entity>
@State private var text: String = ""
var body: some View {
VStack {
TextField("입력해주세요", text: $text)
Button {
// 데이터 추가하기
} label: {
Text("추가하기")
}
}
}
}
@FetchRequest에 대해서 설명해보자면 공식문서에 보면
코어 데이터 영구 저장소에서 Entity를 검색하는 프로퍼티 래퍼 유형임
fetch request를 하는 여러 방법이 있지만, 이중에서도 세 번째 방법은 init(entity:sortDescriptors:predicate:animation:) 을 사용해보겠습니다.
init(entity:sortDescriptors:predicate:animation:)에 대해 뜯어보자면
@FetchRequest(entity: Entity.entity(), sortDescriptors: [] ) var testExample: FetchedResults<Entity>
이제 이 코드에 적용해서 뜯어보자
entitiy: 내가 만든 코어데이터 모델의 이름을 적고 .entity()로 가져온다.
sortDescriptiors: 아직 사용하지 않는 상태이니 [ ] 빈 배열로 만들어준다.
var 변수명
해치웠나...?
엥?? 뜬금 FetchResults<T> ... 이건또 뭐지...
모를땐 공식문서를 보자!
해석해보자면 Core Data store에서 검색된 결과 모음이다.
쉽게 설명해서 Core Data 모델의 Entities 이름을 적어주면 된다.
자 그럼 이 구문에 대해선 이해가 되었으리라 생각한다.
@FetchRequest(entity: Entity.entity(), sortDescriptors: [] ) var testExample: FetchedResults<Entity>
휴... 해치웠나...?
천만의 말씀 만만의 콩떡... 이거 알면 최소 90년생...
5번의 파트가 꽤 길었지만 이제 마무리만 지으면 완성
6. 함수를 만들어 추가했을때 Entity에 저장되도록 설정해준다.
struct ContentView: View {
@Environment(\.managedObjectContext) private var viewContext
@FetchRequest(entity: Entity.entity(), sortDescriptors: [] ) var testExample: FetchedResults<Entity>
@State private var text: String = ""
var body: some View {
VStack {
TextField("입력해주세요", text: $text)
Button {
} label: {
Text("추가하기")
}
}
}
func addData() {
let data = Entity(context: viewContext)
data.title = text
try? viewContext.save()
}
}
func addData() 함수에 대해 부가적인 설명을 하자면
func addData() {
// Entity 매개 변수로 viewCotext를 설정해준다.
let data = Entity(context: viewContext)
// Entity(Core data 모델에서 만들었던 속성들을 사용할 수 있음
data.title = text
// 작업이 마무리 되었으면 save()
try? viewContext.save()
}
의문이 있는데... 왜 Entity(context: )이 문장이 자동완성되지 않을까요...? ㅠㅠ
추가, 삭제, 업데이트 를 할때 가장 중요한것은 마지막에 꼭 viewContext.save()를 해줘야 된다는 것이다.
7. 만들어진 함수를 button action에 적용해보고, 우리가 만든 @FetchRequest를 적용해서 버튼을 눌렀을 때 하나씩 저장되도록 만들어보자
import SwiftUI
struct ContentView: View {
@Environment(\.managedObjectContext) private var viewContext
@FetchRequest(entity: Entity.entity(), sortDescriptors: [] ) var testExample: FetchedResults<Entity>
@State private var text: String = ""
var body: some View {
VStack {
// CoreData는 optional로 기본값이 저장되기 때문에 바인딩을 통해 나타내줘야한다.
List(testExample) { text in
Text(text.title ?? "")
}
// List 스타일 적용
.listStyle(.plain)
TextField("입력해주세요", text: $text)
// 직관적인 UI를 위해 padding 추가
.padding()
Button {
addData()
// 입력을 받고 다시 TextField 빈값으로 만들어주기
text = ""
} label: {
Text("추가하기")
}
}
}
func addData() {
let data = Entity(context: viewContext)
// TextField에서 작성한것 CoreData.title에 넣기
data.title = text
try? viewContext.save()
}
}
자 이렇게 @FetchRequest에 대해서 알아봤는데요
그렇다면 @FetchRequest 에서 정렬 기능은 어떻게 해나하나요?
sortDescriptors를 보면 정렬의 순서를 정의 할수 있다고 나옵니다.
sortDescriptors를 자세히 뜯어보면 [NSSortDescriptors] 라고 되어있습니다. 또 찾아보면...
공식문서에서 계속 타고 들어가면 이렇게 나옵니다.
아항... 공식문서를 이렇게 쓰는거구나
해석: 지정된 키 경로 및 순서로 정렬 설명자를 만든다고 합니다. 그렇다면 코드로 나타내면 어떻게 표현할까요?
import SwiftUI
struct ContentView: View {
@Environment(\.managedObjectContext) private var viewContext
// [] 빈배열이었던 sortDescriptors안에 NSSortDescriptor 메소드를 사용해서 Core Data에서 만들어준 date를 기준으로 정렬하였습니다.
// 여기서 ascending 이 true면 오름차순 , false 면 내림차순
@FetchRequest(entity: Entity.entity(), sortDescriptors: [NSSortDescriptor(keyPath: \Entity.date, ascending: true)] ) var testExample: FetchedResults<Entity>
@State private var text: String = ""
var body: some View {
VStack {
List(testExample) { text in
Text(text.title ?? "")
}
.listStyle(.plain)
TextField("입력해주세요", text: $text)
.padding()
Button {
addData()
text = ""
} label: {
Text("추가하기")
}
}
}
func addData() {
let data = Entity(context: viewContext)
data.title = text
try? viewContext.save()
}
}
struct ContentView_Previews: PreviewProvider {
static var previews: some View {
ContentView()
}
}
드디어 해치웠습니다!!

'Swift' 카테고리의 다른 글
iOS Custom Calendar 구현하기2 (0) | 2023.04.17 |
---|---|
iOS Custom Calender 구현하기 (2) | 2023.04.17 |
SwiftUI @AppStorage 심화편 (0) | 2023.03.22 |
SwiftUI OpenAPI 적용시키기 (0) | 2023.02.14 |
우당탕탕 앱 출시 리젝... (0) | 2023.02.11 |