SwiftUI — Динамически фильтровать и извлекать записи CoreData с помощью атрибутов

#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введите описание изображения здесь

Когда вы запустите код, он будет выглядеть примерно так. Ползунки управляют возрастом, а он выбирает профессию

введите описание изображения здесь