#core-data #swiftui
#основные данные #свифтуи
Вопрос:
У меня есть записи, сохраненные в моей модели данных, но мне было интересно, как я мог бы динамически фильтровать и извлекать данные в соответствии с параметрами, которые я передаю, например, моя модель данных относится к типу люди, который включает их имя, возраст и род занятий.
Допустим, я хочу вернуть 20 записей, но каждая запись может немного отличаться. Это мои параметры, они статичны:
let ageParamters = [60, 24, 35, 64, 29, 39, 90, 92, 38, 48, 23, 58, 62, 78, 19, 18, 29, 48, 40]
Я хочу получить единственную запись для человека, возраст которого соответствует текущему индексу массива, поэтому первому человеку будет 60 лет, следующим 24, 35 после этого и так далее.
Но, расширяя это, как я мог бы отфильтровать свои данные, наблюдая за двумя полями, такими как возраст и профессия, например:
let ageOccupationParamters = [[20: "Doctor"], [78: "Retired"], [32: "Police"]]
Я был бы признателен за любую помощь, которую я могу получить. Заранее спасибо.
Комментарии:
1. Существует множество руководств по основным данным, которые показывают, чего вы хотите. Это не то место, где можно получить учебник.
Ответ №1:
Вот пример, он сводится к 2 основным способам.
Способ iOS 15 и способ до iOS 15.
Они оба используют NSPredicate
(один критерий) или NSCompoundPredicate
(несколько критериев).
import SwiftUI import CoreData @available(iOS 15.0, *) struct FilteredListView: View { @StateObject var vm: FilteredListViewModel = .init() @Environment(.managedObjectContext) private var viewContext @FetchRequest( sortDescriptors: [NSSortDescriptor(keyPath: Person.age, ascending: true)], animation: .default) private var people: FetchedResultslt;Persongt; var body: some View { NavigationView{ VStack{ FilterOptionsView(fromAge: $vm.fromAge, toAge: $vm.toAge, selectedOccupation: $vm.occupation, occupations: vm.uniqueOccupations) HStack{ //This is the iOS 15 way SimpleListView(label: "@FetchRequest", people: Array(people)) .onChange(of: vm.toAge, perform: { val in ///This can only be used on iOS15 setThePredicate() }) .onChange(of: vm.fromAge, perform: { val in ///This can only be used on iOS15 setThePredicate() }) .onChange(of: vm.occupation, perform: { val in ///This can only be used on iOS15 setThePredicate() }) //This is the other os compatible version but it does not observe the store SimpleListView(label: "NSFetchRequest", people: Array(vm.people)) } }.toolbar(content: { ToolbarItem(placement: .navigationBarTrailing, content: { Button("add samples", action: { vm.addSamples() }) }) }) } } ///This can only be used on iOS15 func setThePredicate(){ //You can use any of the predicates I created in the extension here people.nsPredicate = Person.ageAndOccupationPredicate(ageFrom: Int16(vm.fromAge), ageTo: Int16(vm.toAge), occupation: vm.occupation) } } class FilteredListViewModel: ObservableObject{ @Published var fromAge: Int = 14{ didSet{ //Update the list when the variable changes getNewList() } } @Published var toAge: Int = 100{ didSet{ //Update the list when the variable changes getNewList() } } @Published var occupation: String = ""{ didSet{ //Update the list when the variable changes getNewList() } } @Published var people: [Person] = [] var uniqueOccupations: [String]{ ///Get all the items, get the unique values of the occupations with the set, and then get the array for the View Array(Set(getObjects(nsPredicate: nil).map{$0.occupation ?? "unknown"})) } func addSamples(){ for _ in 0...20{ let context = PersistenceController.previewAware.container.viewContext _ = Person(context: context) } } //Other os version way of using the predicate func getNewList(){ //This is retrieved using NSFetchRequest it does not observe the store //Any tutorial will show you how to make this request people = getObjects(nsPredicate: Person.ageAndOccupationPredicate(ageFrom: Int16(fromAge), ageTo: Int16(toAge), occupation: occupation)) } func getObjects(nsPredicate: NSPredicate? = nil) -gt; [Person]{ let request = NSFetchRequestlt;Persongt;(entityName: "Person") request.predicate = nsPredicate request.sortDescriptors = [NSSortDescriptor(keyPath: Person.age, ascending: true)] do{ let context = PersistenceController.previewAware.container.viewContext return try context.fetch(request) }catch{ print(error) return [] } } } struct SimpleListView: View{ let label: String var people: [Person] var body: some View{ VStack{ Text(label) List(people, id: .objectID) { person in HStack{ Text(person.age.description) Text(person.occupation ?? "no occupation") } } } } } struct FilterOptionsView: View{ @Binding var fromAge:Int @Binding var toAge:Int @Binding var selectedOccupation: String var occupations: [String] var body: some View{ Slider(value: Binding(get: { //Return the fromAge Double(fromAge) }, set: { val in //If the ages will overlap don't update the variables if val lt; Double(toAge){ fromAge = Int(val) } }), in: 14...100, label: { Text("from") }) Slider(value: Binding(get: { //Return the toAge Double(toAge) }, set: {val in //If the ages will overlap don't update the variables if val gt; Double(fromAge){ toAge = Int(val) } }), in: 14...100, label: { Text("to") }) Picker("occupations", selection: $selectedOccupation, content: { ForEach(occupations, id: .self){ occ in Text(occ).tag(occ) } Text("all occupations").tag("") }) } } @available(iOS 15.0, *) struct FilteredListView_Previews: PreviewProvider { static var previews: some View { FilteredListView().environment(.managedObjectContext, PersistenceController.previewAware.container.viewContext) } } extension Person{ public override func awakeFromInsert() { self.age = Int16.random(in: 16...100) self.occupation = ["Doctor", "Retired", "Police"].randomElement()! _ = PersistenceManager.init(entityType: Person.self).updateObject(object: self) } ///Predicate to filter out by age range static func ageRangePredicate(from: Int16, to: Int16) -gt; NSPredicate{ NSPredicate(format: "age gt;= %d AND age lt;= %d", from, to) } ///Predicate to filter out by age static func agePredicate(age: Int16) -gt; NSPredicate{ NSPredicate(format: "age == %d", age) } ///Predicate to filter out by occupation static func occupationPredicate(occupation: String, ascending: Bool = false) -gt; NSPredicate?{ if occupation.isEmpty{ return nil }else{ return NSPredicate(format: "occupation CONTAINS %@", occupation) } } ///Compound predicate to filter by age range and occupation static func ageAndOccupationPredicate (ageFrom: Int16, ageTo: Int16, occupation: String) -gt; NSCompoundPredicate{ let occPred = occupationPredicate(occupation: occupation) if occPred != nil{ let pred = NSCompoundPredicate(andPredicateWithSubpredicates: [occPred!, ageRangePredicate(from: ageFrom, to: ageTo)]) return pred }else{ let pred = NSCompoundPredicate(andPredicateWithSubpredicates: [ageRangePredicate(from: ageFrom, to: ageTo)]) return pred } } }
Вот как Person
это выглядит в CoreData
Когда вы запустите код, он будет выглядеть примерно так. Ползунки управляют возрастом, а он выбирает профессию