【Swift/Core Data】NSFetchRequestでデータ取得!ソート/フィルタリングなどの使い方
この記事からわかること
- SwiftでCore Dataを利用する方法
- データを取得する方法
- NSFetchRequestの使い方
- ソートやフィルタリング、NSPredicateの指定方法
index
[open]
\ アプリをリリースしました /
友達や家族の誕生日をメモ!通知も届く-みんなの誕生日-
posted withアプリーチ
環境
- Xcode:15.0.1
- iOS:17.0
- Swift:5.9
- macOS:Sonoma 14.1
Core Dataでデータを取得する方法:NSFetchRequest
Core Dataでデータベースに格納してあるデータを取得するにはNSFetchRequest
クラスを使用してデータを取得するためのクエリを定義します。データの検索だけでなく、ソートやフィルタリングなどを行うことが可能です。
実際にデータを取得する際にはNSFetchRequest
インスタンスを生成し、NSManagedObjectContext
のfetch
メソッドの引数に渡します。インスタンス化する際には引数entityName
に取得したいエンティティクラスの名前を文字列で渡す必要があります。
public func fetch() -> [Person] {
// String(describing: Person.self) → "Person" と同じ
let fetchRequest = NSFetchRequest<Person>(entityName: String(describing: Person.self))
do {
return try context.fetch(fetchRequest)
} catch let error as NSError {
print("Could not fetch. \(error), \(error.userInfo)")
return []
}
}
NSManagedObjectContext.fetchメソッド
公式リファレンス:NSManagedObjectContext.fetchメソッド
NSManagedObjectContext
のfetch
メソッドは引数に受け取ったNSFetchRequestResult
を元にデータベースから保存してある対象のクラスのデータを全て取得し配列形式で返します。
func fetch(_ request: NSFetchRequest<NSFetchRequestResult>) throws -> [Any]
throws
が付与されているので実行する際にはdo-catch
文などを使用してエラーハンドリングします。
fetchRequestは自動生成される
エンティティを定義した際にクラスファイルとプロパティファイルが自動生成される設定(Class Definition)にしている場合はプロパティファイルにfetchRequest
メソッドが定義されます。Class Definition
については以下の記事を参考にしてください。
おすすめ記事:【Swift/Core Data】NSManagedObjectのインスタンス作成方法!違いと使い方
@nonobjc public class func fetchRequest() -> NSFetchRequest<Person> {
return NSFetchRequest<Person>(entityName: "Person")
}
そのためエンティティクラス.fetchRequest
メソッドで対象のNSFetchRequestResult
を取得することが可能です。
/// 取得処理
public func fetch() -> [Person] {
let fetchRequest = Person.fetchRequest()
do {
return try context.fetch(fetchRequest)
} catch let error as NSError {
print("Could not fetch. \(error), \(error.userInfo)")
return []
}
}
ソートして取得する:NSSortDescriptor
データを取得する際にソートされた状態で取得したい場合はNSFetchRequestResult
のsortDescriptors
プロパティにNSSortDescriptor
インスタンスを配列形式で渡します。
let fetchRequest = NSFetchRequest<Person>(entityName: String(describing: Person.self))
// nameプロパティの昇順にソート
fetchRequest.sortDescriptors = [
NSSortDescriptor(keyPath: \Person.name, ascending: true)
]
init(keyPath:ascending:)
ソート条件はイニシャライザinit(keyPath:ascending:)
にソート対象のプロパティ名と昇順(true)/降順(false)のフラグを渡します。
複数の条件でソートを掛けたい場合は配列に格納するNSSortDescriptor
インスタンスを増やしていけばOKです。例えば以下の場合、名前の昇順でソートした後に同名であれば年齢の降順で並び替えています。
fetchRequest.sortDescriptors = [
NSSortDescriptor(keyPath: \Person.name, ascending: true),
NSSortDescriptor(keyPath: \Person.age, ascending: false)
]
init(key:ascending:)
引数違いでイニシャライザinit(key:ascending:)
もありますが、こちらは文字列でプロパティ名を指定する必要があります。
NSSortDescriptor(key: "age", ascending: false)
フィルタリングして取得する:NSPredicate
データを取得する際にフィルタリングされた状態で取得したい場合はNSFetchRequestResult
のpredicate
プロパティにNSPredicate
インスタンスを渡します。
// 年齢が10歳以上のデータのみ取得
fetchRequest.predicate = NSPredicate(format: "age => %d", 10)
NSPredicate
クラスはCore Dataに限ったものではなくRealmなどでも使用できるデータベースからデータを取得する際の条件を含んだクエリを構築できるクラスです。条件は主に文字列と比較演算子を組み合わせて定義できます。
ジェネリクスで汎用的に
1つのプロジェクトの中で複数のエンティティクラスを扱っている場合はジェネリクスを使用して汎用的に実装すると便利です。
public func fetch<T: NSManagedObject>() -> [T] {
let fetchRequest = NSFetchRequest<T>(entityName: String(describing: T.self))
do {
return try viewContext.fetch(fetchRequest)
} catch let error as NSError {
print("Could not fetch. \(error), \(error.userInfo)")
return []
}
}
public func fetchSingle<T: NSManagedObject>(predicate: NSPredicate? = nil, sorts: [NSSortDescriptor]? = nil) -> T {
let fetchRequest = NSFetchRequest<T>(entityName: String(describing: T.self))
// フィルタリング
if let predicate = predicate {
fetchRequest.predicate = predicate
}
var result: T!
// ソート
if let sorts = sorts {
fetchRequest.sortDescriptors = sorts
}
do {
let entitys = try viewContext.fetch(fetchRequest)
if let entity = entitys.first {
result = entity
}
} catch let error as NSError {
print("Could not fetch. \(error), \(error.userInfo)")
}
return result
}
まだまだ勉強中ですので間違っている点や至らぬ点がありましたら教えていただけると助かります。
ご覧いただきありがとうございました。