【Swift/Core Data】NSFetchRequestでデータ取得!ソート/フィルタリングなどの使い方

この記事からわかること

  • SwiftCore Data利用する方法
  • データ取得する方法
  • NSFetchRequest使い方
  • ソートフィルタリングNSPredicateの指定方法

index

[open]

\ アプリをリリースしました /

みんなの誕生日

友達や家族の誕生日をメモ!通知も届く-みんなの誕生日-

posted withアプリーチ

公式リファレンス:Core Data

環境

Core Dataでデータを取得する方法:NSFetchRequest

公式リファレンス:NSFetchRequest

Core Dataでデータベースに格納してあるデータを取得するにはNSFetchRequestクラスを使用してデータを取得するためのクエリを定義します。データの検索だけでなく、ソートやフィルタリングなどを行うことが可能です。

実際にデータを取得する際にはNSFetchRequestインスタンスを生成し、NSManagedObjectContextfetchメソッドの引数に渡します。インスタンス化する際には引数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メソッド

NSManagedObjectContextfetchメソッドは引数に受け取った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

データを取得する際にソートされた状態で取得したい場合NSFetchRequestResultsortDescriptorsプロパティに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

データを取得する際にフィルタリングされた状態で取得したい場合NSFetchRequestResultpredicateプロパティに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
}

まだまだ勉強中ですので間違っている点や至らぬ点がありましたら教えていただけると助かります。

ご覧いただきありがとうございました。

searchbox

スポンサー

ProFile

ame

趣味:読書,プログラミング学習,サイト制作,ブログ

IT嫌いを克服するためにITパスを取得しようと勉強してからサイト制作が趣味に変わりました笑
今はCMSを使わずこのサイトを完全自作でサイト運営中〜

New Article

index