この記事では、SwiftUIでのCoreData内のデータ取得メソッドの実装と、NSPredicateによる条件指定方法を解説します。
CoreData内のデータの取得方法で@FetchRequest(entity: Data.entity(), sortDescriptors: [NSSortDescriptor(key: "date", ascending: true)],animation: .spring()) var results : FetchedResults<Data> というように、FetchedResults<> 型の変数を定義して利用される事が多いですが、今回はデータ取得メソッドを作成する方法と、その際にNSPredicate による条件指定方法を紹介します。
ベースのコードとして、CoreData の基本的な実装方法【CRUD(作成、更新、削除)】を紹介している他の記事を参考にしています。
この記事では、SwiftUIでのCoreData の基本的な実装方法【CRUD(作成、更新、削除)】を紹介しています。簡単なタスク管理App(タスク内容とスケジュールの登録)を作成しながら解説します。 ◆動[…]
◆動作検証環境
・XCode:12.1
・SwiftUI:2.0
・iOS:14.0
・Life Cycle:SwiftUI App
CoreDataの基本的な実装方法
まずは、CoreDataでのCRUD(作成、更新、削除)機能が可能なベースとなるファイルを作成し、コードを編集します。
以下の記事の方法を参考に準備を行います。
【SwiftUI】CoreData の基本的な実装方法【CRUD(作成、更新、削除)】
作成したプロジェクトを実行すると、以下のような簡単なToDo機能のアプリの動作を確認できます。
優先度Attributeの追加
現在のTask EntityのAttributeはcontent,dateの2種類です。
Int型を検索条件として指定できるように、Int型priorityも追加します。
関係するファイルを編集します。
ContentView.swift
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 | ... ScrollView(.vertical,showsIndicators: false, content:{ LazyVStack(alignment: .leading, spacing: 20){ ForEach(results){task in VStack(alignment: .leading, spacing: 5, content: { Text("優先度:\(task.priority)") .fontWeight(.bold) Text(task.content ?? "") .font(.title) .fontWeight(.bold) Text(task.date ?? Date(),style: .date) .fontWeight(.bold) Divider() }) ... |
NewDataSheet.swift
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 | ... TextEditor(text: $viewModel.content) .padding() HStack{ Text("優先度") TextField("", value: $viewModel.priority, formatter: NumberFormatter()) .padding() .background(Color.white) } .padding() Divider() .padding(.horizontal) ... |
ViewModel.swift
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 | import SwiftUI import CoreData class ViewModel : ObservableObject{ @Published var content = "" @Published var date = Date() @Published var priority = 0 @Published var isNewData = false @Published var updateItem : Task! func writeData(context : NSManagedObjectContext ){ if updateItem != nil{ updateItem.date = date updateItem.content = content updateItem.priority = Int16(priority) try! context.save() updateItem = nil isNewData.toggle() content = "" date = Date() priority = 0 return } let newTask = Task(context: context) newTask.date = date newTask.content = content newTask.priority = Int16(priority) do{ try context.save() isNewData.toggle() content = "" date = Date() priority = 0 } catch{ print(error.localizedDescription) } } func EditItem(item:Task){ updateItem = item date = item.date! content = item.content! priority = Int(item.priority) isNewData.toggle() } } |
データ取得メソッドの実装方法
全件データ取得メソッド
以下のメソッドを作成します。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 | func getAllData() -> [Task]{ let persistenceController = PersistenceController.shared let context = persistenceController.container.viewContext let request = NSFetchRequest<Task>(entityName: "Task") do { let tasks = try context.fetch(request) return tasks } catch { fatalError() } } |
動作を確かめるために、FetchedResults<> 型の変数を使用していた所で、このメソッドを呼びます。
1 2 3 4 5 6 7 8 | ... ScrollView(.vertical,showsIndicators: false, content:{ LazyVStack(alignment: .leading, spacing: 20){ ForEach(getAllData()){task in VStack(alignment: .leading, spacing: 5, content: { ... |
並び順の指定
これで、同じように全件データが表示されますが、並び順の指定をしていなので、元の表示とは表示の仕方が変わります。
同じようにdateを条件にする場合は、NSSortDescriptor を使用して指定します。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 | func getAllData() -> [Task]{ let persistenceController = PersistenceController.shared let context = persistenceController.container.viewContext let request = NSFetchRequest<Task>(entityName: "Task") request.sortDescriptors = [NSSortDescriptor(key: "date", ascending: true)] do { let tasks = try context.fetch(request) return tasks } catch { fatalError() } } |
これで、タスクの日付順に表示されるようになります。
条件をつけてデータを取得するメソッド
いくつかの条件を指定してデータを取得します。
下記のサンプルデータを利用して動作を確認します。
Int型データを条件として指定
Int型データを条件として指定例として、指定の優先度以上のデータのみ取得するように編集します。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 | func getAllDataByPriority(priority:Int) -> [Task]{ let persistenceController = PersistenceController.shared let context = persistenceController.container.viewContext let request = NSFetchRequest<Task>(entityName: "Task") request.sortDescriptors = [NSSortDescriptor(key: "date", ascending: true)] let predicate = NSPredicate(format: "priority >= %d", priority) request.predicate = predicate do { let tasks = try context.fetch(request) return tasks } catch { fatalError() } } |
getAllDataByPriority(priority: 2)
これで優先度2以上のデータのみ取得できます。
Date型データを条件として指定
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 | func getAllDataByPriority(priority:Int) -> [Task]{ let persistenceController = PersistenceController.shared let context = persistenceController.container.viewContext let request = NSFetchRequest<Task>(entityName: "Task") request.sortDescriptors = [NSSortDescriptor(key: "date", ascending: true)] let predicate = NSPredicate(format: "priority >= %d", priority) request.predicate = predicate do { let tasks = try context.fetch(request) return tasks } catch { fatalError() } } |
これで本日以降のデータのみ取得します。
String型データを条件として指定
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 | func getAllDataByContent(content:String) -> [Task]{ let persistenceController = PersistenceController.shared let context = persistenceController.container.viewContext let request = NSFetchRequest<Task>(entityName: "Task") request.sortDescriptors = [NSSortDescriptor(key: "date", ascending: true)] let predicate = NSPredicate(format: "content == %@", content) request.predicate = predicate do { let tasks = try context.fetch(request) return tasks } catch { fatalError() } } |
これで”買い物”に合致するデータのみ取得できます。
他にも
CONTAINS :指定の文字列が含まれるデータLIKE :指定の文字列パターンが含まれるデータBEGINSWITH 、ENDSWITH :指定の文字列で始まる、終わるデータIN :指定の文字列のリスト({%@,%@,%@}と指定)のいずれかが合致するデータというように指定できます。
複数の条件を指定して取得
例として、日付と優先度を条件として取得するメソッドを作成します。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 | func getAllDataByDayAndPriority(date:Date,priority:Int) -> [Task]{ let persistenceController = PersistenceController.shared let context = persistenceController.container.viewContext let request = NSFetchRequest<Task>(entityName: "Task") request.sortDescriptors = [NSSortDescriptor(key: "date", ascending: true)] let predicate = NSPredicate(format: "date >= %@ AND priority >= %d", date as CVarArg,priority) request.predicate = predicate do { let tasks = try context.fetch(request) return tasks } catch { fatalError() } } |
getAllDataByDayAndPriority(date: Date(), priority: 2)
これで、日付が本日以降、優先度が2以上のデータのみ取得します。
データの計算結果を参考にして取得
例として、登録されている優先度の平均値を超えるデータのみ取得するメソッドを作成します。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 | func getAllDataByAvePriority() -> [Task]{ let persistenceController = PersistenceController.shared let context = persistenceController.container.viewContext let request = NSFetchRequest<Task>(entityName: "Task") request.sortDescriptors = [NSSortDescriptor(key: "date", ascending: true)] let predicate = NSPredicate(format: "priority >= @avg.priority") request.predicate = predicate do { let tasks = try context.fetch(request) return tasks } catch { fatalError() } } |
@avg 平均値の取得の他にも、@max :最高値、@min :最低値、@sum :合計値、@count :データ件数の取得も可能です。
以上、SwiftUIでのCoreData内のデータ取得メソッドの実装と、NSPredicateによる条件指定方法を解説しました。