NSPredicateを使用した@FetchRequestのフィルタリング
Filtering @FetchRequest using NSPredicate
NSPredicate
name」と「universe」の2つの文字列属性を持つShipという新しいエンティティを作成します。
import CoreData import SwiftUI struct ContentView: View { @Environment(\.managedObjectContext) var moc //@FetchRequest(entity: Ship.entity(), sortDescriptors: [], predicate: nil) var ships: FetchedResults<Ship> //@FetchRequest(entity: Ship.entity(), sortDescriptors: [], predicate: NSPredicate(format: "universe == 'Star Wars'")) var ships: FetchedResults<Ship> //@FetchRequest(entity: Ship.entity(), sortDescriptors: [], predicate: NSPredicate(format: "universe == %@", "Star Wars")) var ships: FetchedResults<Ship> //@FetchRequest(entity: Ship.entity(), sortDescriptors: [], predicate: NSPredicate(format: "name < %@", "E")) var ships: FetchedResults<Ship> //@FetchRequest(entity: Ship.entity(), sortDescriptors: [], predicate: NSPredicate(format: "universe IN %@",["Aliens", "Firefly", "Star Trek"])) var ships: FetchedResults<Ship> //@FetchRequest(entity: Ship.entity(), sortDescriptors: [], predicate: NSPredicate(format: "name BEGINSWITH %@", "E")) var ships: FetchedResults<Ship> //@FetchRequest(entity: Ship.entity(), sortDescriptors: [], predicate: NSPredicate(format: "name BEGINSWITH[c] %@", "e")) var ships: FetchedResults<Ship> @FetchRequest(entity: Ship.entity(), sortDescriptors: [], predicate: NSPredicate(format: "NOT name BEGINSWITH[c] %@", "e")) var ships: FetchedResults<Ship> var body: some View { VStack { List(ships, id: \.self) { ship in Text(ship.name ?? "Unknown name") } Button("Add Examples") { let ship1 = Ship(context: self.moc) ship1.name = "Enterprise" ship1.universe = "Star Trek" let ship2 = Ship(context: self.moc) ship2.name = "Defiant" ship2.universe = "Star Trek" let ship3 = Ship(context: self.moc) ship3.name = "Millennium Falcon" ship3.universe = "Star Wars" let ship4 = Ship(context: self.moc) ship4.name = "Executor" ship4.universe = "Star Wars" try? self.moc.save() } } } } struct ContentView_Previews: PreviewProvider { static var previews: some View { let context = (UIApplication.shared.delegate as! AppDelegate).persistentContainer.viewContext return ContentView().environment(\.managedObjectContext, context) } }
BEGINSWITHand などの演算子を使用して、述語を使用して文字列の一部を調べることもできCONTAINSます。たとえば、これは大文字のEで始まるすべての船を返します。
CONTAINS[c] サブストリングで始まるのではなく、属性内のどこにでも置くことができることを除いて、同様に機能します。
より複雑な述語が必要な場合は、それらを使用ANDして必要な精度を構築するか、コアデータのインポートを追加して確認しNSCompoundPredicateます。これにより、いくつかの小さい述語から1つの述語を構築できます。
SwiftUIを使用した@FetchRequestの動的フィルタリング
Dynamically filtering @FetchRequest with SwiftUI
Singerという新しいコアデータエンティティを作成し、「firstName」と「lastName」という2つの文字列属性を割り当てます。データモデルインスペクタを使用して、CodegenをManual / Noneに変更し、[Editor]メニューに移動して[Create NSManagedObject Subclass]を選択すると、Singerカスタマイズ可能なクラスを取得できます。
Xcodeがファイルを生成したら、Singer + CoreDataProperties.swiftを開き、SwiftUIでクラスを使いやすくする次の2つのプロパティを追加します。
var wrappedFirstName: String { firstName ?? "Unknown" } var wrappedLastName: String { lastName ?? "Unknown" }
import SwiftUI struct FilteredList: View { var fetchRequest: FetchRequest<Singer> init(filter: String) { fetchRequest = FetchRequest<Singer>(entity: Singer.entity(), sortDescriptors: [], predicate: NSPredicate(format: "lastName BEGINSWITH %@", filter)) } var body: some View { List(fetchRequest.wrappedValue, id: \.self) { singer in Text("\(singer.wrappedFirstName) \(singer.wrappedLastName)") } } }
import CoreData import SwiftUI struct ContentView: View { @Environment(\.managedObjectContext) var moc @State private var lastNameFilter = "A" var body: some View { VStack { // list of matching singers FilteredList(filter: lastNameFilter) Button("Add Examples") { let taylor = Singer(context: self.moc) taylor.firstName = "Taylor" taylor.lastName = "Swift" let ed = Singer(context: self.moc) ed.firstName = "Ed" ed.lastName = "Sheeran" let adele = Singer(context: self.moc) adele.firstName = "Adele" adele.lastName = "Adkins" try? self.moc.save() } Button("Show A") { self.lastNameFilter = "A" } Button("Show S") { self.lastNameFilter = "S" } } } } struct ContentView_Previews: PreviewProvider { static var previews: some View { let context = (UIApplication.shared.delegate as! AppDelegate).persistentContainer.viewContext return ContentView().environment(\.managedObjectContext, context) } }
さらに進みたいですか?
柔軟性を高めるために、FilteredListビューを改善して、あらゆる種類のエンティティで機能し、あらゆるフィールドでフィルタリングできるようにします。これを適切に機能させるには、いくつかの変更を加える必要があります。
Singerクラスを具体的に参照するのではなく、渡されるものはすべてでなければならないという制約のあるジェネリックスを使用しますNSManagedObject。
lastName属性を持たないエンティティを使用している可能性があるため、フィルタリングするキー名を決定するために2番目のパラメーターを受け入れる必要があります。
各エンティティに何が含まれるかは事前にわからないため、包含ビューに決定を任せます。したがって、歌手の名前のテキストビューを単に使用するのではなく、代わりに、ビューを構成するために実行できるクロージャーを要求します。
NSPredicate属性名を置き換えるために使用できる特別な記号があります%K。これにより、提供される値が挿入されますが、引用符は追加されません。正しい述語はこれです。
NSPredicate(format: "%K BEGINSWITH %@", filterKey, filterValue)
難解なので、後日参照用
SwiftUIを使用した@FetchRequestの動的フィルタリング
import SwiftUI import CoreData struct FilteredList<T: NSManagedObject, Content: View>: View { var fetchRequest: FetchRequest<T> var singers: FetchedResults<T> { fetchRequest.wrappedValue } // this is our content closure; we'll call this once for each item in the list let content: (T) -> Content var body: some View { List(fetchRequest.wrappedValue, id: \.self) { singer in self.content(singer) } } init(filterKey: String, filterValue: String, @ViewBuilder content: @escaping (T) -> Content) { fetchRequest = FetchRequest<T>(entity: T.entity(), sortDescriptors: [], predicate: NSPredicate(format: "%K BEGINSWITH %@", filterKey, filterValue)) self.content = content } }
import CoreData import SwiftUI struct ContentView: View { @Environment(\.managedObjectContext) var moc @State private var lastNameFilter = "A" var body: some View { VStack { // list of matching singers //FilteredList(filter: lastNameFilter) // new version!! FilteredList(filterKey: "lastName", filterValue: lastNameFilter) { (singer: Singer) in Text("\(singer.wrappedFirstName) \(singer.wrappedLastName)") } Button("Add Examples") { let taylor = Singer(context: self.moc) taylor.firstName = "Taylor" taylor.lastName = "Swift" let ed = Singer(context: self.moc) ed.firstName = "Ed" ed.lastName = "Sheeran" let adele = Singer(context: self.moc) adele.firstName = "Adele" adele.lastName = "Adkins" try? self.moc.save() } Button("Show A") { self.lastNameFilter = "A" } Button("Show S") { self.lastNameFilter = "S" } } } } struct ContentView_Previews: PreviewProvider { static var previews: some View { let context = (UIApplication.shared.delegate as! AppDelegate).persistentContainer.viewContext return ContentView().environment(\.managedObjectContext, context) } }
Core Data、SwiftUI、@ FetchRequestとの1対多の関係
One-to-many relationships with Core Data, SwiftUI, and @FetchRequest
保存project名—>> CoreDataProject3 countryフェッチ版
保存project名—>> CoreDataProject3改 Candyフェッチ版
データモデルを開いて、「name」という文字列属性を持つCandyと、「fullName」および「shortName」という文字列属性を持つCountryの2つのエンティティを追加します。一部の種類のキャンディーは同じ名前を持っていますが(米国と英国の「Smarties」を参照)、国は完全に一意であるため、「shortName」の制約を追加してください。
CandyとCountryの間に1対多の関係があることをコアデータに通知する必要があります。
Core Data、SwiftUI、@ FetchRequestとの1対多の関係
import SwiftUI struct ContentView: View { @Environment(\.managedObjectContext) var moc @FetchRequest(entity: Country.entity(), sortDescriptors: []) var countries: FetchedResults<Country> var body: some View { VStack { List { ForEach(countries, id: \.self) { country in Section(header: Text(country.wrappedFullName)) { ForEach(country.candyArray, id: \.self) { candy in Text(candy.wrappedName) } } } } Button("Add") { let candy1 = Candy(context: self.moc) candy1.name = "Mars" candy1.origin = Country(context: self.moc) candy1.origin?.shortName = "UK" candy1.origin?.fullName = "United Kingdom" let candy2 = Candy(context: self.moc) candy2.name = "KitKat" candy2.origin = Country(context: self.moc) candy2.origin?.shortName = "UK" candy2.origin?.fullName = "United Kingdom" let candy3 = Candy(context: self.moc) candy3.name = "Twix" candy3.origin = Country(context: self.moc) candy3.origin?.shortName = "UK" candy3.origin?.fullName = "United Kingdom" let candy4 = Candy(context: self.moc) candy4.name = "Toblerone" candy4.origin = Country(context: self.moc) candy4.origin?.shortName = "CH" candy4.origin?.fullName = "Switzerland" try? self.moc.save() } } } } struct ContentView_Previews: PreviewProvider { static var previews: some View { let context = (UIApplication.shared.delegate as! AppDelegate).persistentContainer.viewContext return ContentView().environment(\.managedObjectContext, context) } }
下は、Candyをフェッチした場合の改定版。上は、Countryをフェッチしたのもの。Candy、Countryはentityの名前。下のextensionは、リレーションによりXcodeが自動生成したもの。
Core Data、SwiftUI、@ FetchRequestとの1対多の関係
import Foundation import CoreData extension Candy { @nonobjc public class func fetchRequest() -> NSFetchRequest<Candy> { return NSFetchRequest<Candy>(entityName: "Candy") } @NSManaged public var name: String? @NSManaged public var origin: Country? public var wrappedName: String { name ?? "Unknown Candy" } public var wrappedoriginfullName: String { return String(origin?.fullName ?? "Unknown Candy") } }
import Foundation import CoreData extension Country { @nonobjc public class func fetchRequest() -> NSFetchRequest<Country> { return NSFetchRequest<Country>(entityName: "Country") } @NSManaged public var fullName: String? @NSManaged public var shortName: String? //@NSManaged public var candy: NSSet? @NSManaged public var candy: Set<Candy>? public var wrappedShortName: String { shortName ?? "Unknown Country" } public var wrappedFullName: String { fullName ?? "Unknown Country" } public var candyArray: [Candy] { //let set = candy as? Set<Candy> ?? [] // fix version ↓ let set = candy ?? [] return set.sorted { $0.wrappedName < $1.wrappedName } } } // MARK: Generated accessors for candy extension Country { @objc(addCandyObject:) @NSManaged public func addToCandy(_ value: Candy) @objc(removeCandyObject:) @NSManaged public func removeFromCandy(_ value: Candy) @objc(addCandy:) @NSManaged public func addToCandy(_ values: NSSet) @objc(removeCandy:) @NSManaged public func removeFromCandy(_ values: NSSet) }
import Foundation import CoreData extension Country { @nonobjc public class func fetchRequest() -> NSFetchRequest<Country> { return NSFetchRequest<Country>(entityName: "Country") } @NSManaged public var fullName: String? @NSManaged public var shortName: String? //@NSManaged public var candy: NSSet? @NSManaged public var candy: Set<Candy>? public var wrappedShortName: String { shortName ?? "Unknown Country" } public var wrappedFullName: String { fullName ?? "Unknown Country" } public var candyArray: [Candy] { //let set = candy as? Set<Candy> ?? [] // fix version ↓ let set = candy ?? [] return set.sorted { $0.wrappedName < $1.wrappedName } } } // MARK: Generated accessors for candy extension Country { @objc(addCandyObject:) @NSManaged public func addToCandy(_ value: Candy) @objc(removeCandyObject:) @NSManaged public func removeFromCandy(_ value: Candy) @objc(addCandy:) @NSManaged public func addToCandy(_ values: NSSet) @objc(removeCandy:) @NSManaged public func removeFromCandy(_ values: NSSet) }