반응형
swiftUI 기반 환경에서 CoreData를 사용시 데이터를 필터하여 보여주어야 할 때가 있다. 차근차근 알아보자
데이터 불러오기
먼저 FoodEntity라는 데이터를 사용한다고 가정해보자. 다음과 같이 CoreData로 부터 데이터를 불러올 것이다.
@FetchRequest(
sortDescriptors: [NSSortDescriptor(keyPath: \FoodEntity.timestamp, ascending: true)],
animation: .default
)
private var foods: FetchedResults<FoodEntity>
필터 적용하기
여기에 predicate인자를 통해 필터를 적용할수 있다
@State var filterKey = "한식"
@FetchRequest(
sortDescriptors: [NSSortDescriptor(keyPath: \FoodEntity.timestamp, ascending: true)],
predicate: NSPredicate(format: "category == %s", filterKey)
animation: .default
)
하지만 이렇게되면 컴파일 에러가 발생한다. 왜냐하면, @FetchRequest는 var filterKey 보다 먼저 실행되기 때문이다. 즉, 변수를 활용해서 dynamic 하게 데이터에 필터를 적용할 수 없다는 뜻이다.
NSPredicate 관련 문법은 다음을 참고하자
Predicate Programming Guide
https://developer.apple.com/library/archive/documentation/Cocoa/Conceptual/Predicates/AdditionalChapters/Introduction.html#//apple_ref/doc/uid/TP40001789
맘에 안드는 해결방법
아래 링크된 대로 FilteredList를 사용하며, View를 새로 init 하는 방법이 있는데 코드량만 늘어나고 가독성만 나빠질뿐, swiftui 스럽지 않다고 생각했다.
https://www.hackingwithswift.com/books/ios-swiftui/dynamically-filtering-fetchrequest-with-swiftui
진짜 해결방법
다음과 같이 필터를 wrapping 하는 struct를 만들어주자
struct FilterScope: Equatable {
var filter: String?
var predicate: NSPredicate? {
guard let filter = filter else { return nil }
return NSPredicate(format: "category == %@", filter)
}
}
인스턴스화 해주고
@State private var filterScope: FilterScope = FilterScope(filter: nil)
아래와 같이 onChange 콜백을 걸어준다. 앞으로 filter가 변경될때 마다 감지되어 자동으로 FetchedResults에 필터를 적용시켜줄 것이다.
NavigationView {
// some view..
}.onChange(of: filterScope) { newValue in
foods.nsPredicate = filterScope.predicate
}
swiftui 스럽죠? 주의할점은 struct에 Equatable을 붙여주는것을 잊지말자
반응형
'iOS' 카테고리의 다른 글
SwiftUI Navigation View To Root - 네비게이션 뷰 관리하기 (0) | 2021.12.04 |
---|