iOS15.4/Cloudkit/SwiftUI/database検索サンプル

やっとわかったことがありますのでお伝えいたします。

AppleのCloudKitというものを使用して、databaseを検索する場合、SwiftUI(Swiftでと言っても良いが)で、どのようにコーディングすれば良いか。これでエラーなくコンパイルできて動くというサンプルがわかりましたので、整理しておきます。

解説

① predicateという変数に検索条件を設定しています。IDというのは、私がテスト用に作成したT100_T_SCHOOL というテーブル(Appleでは、テーブルのことをRECORD TYPEと呼ぶ)の中のひとつの項目です(Appleは、項目のことをFieldsと言います。これはOKです)
またソート条件などを設定する箇所も、この部分です。今回の説明上ではあまり重要な部分ではありません。②と③が重要です。

② recordMatchedBlock というメソッドを使用するのがポイントです。
Appleが、このドキュメントサンプルプロジェクトだとしてしているGitHubcloudkit-sample-queriesでは、recordFetchedBlockを使用していますが、それはiOS14.1ではOKでしたが、iOSの15.4で使用しようとすると、

'recordFetchedBlock' was deprecated in iOS 15.0: Use recordMatchedBlock instead, which surfaces per-record errors という警告になってしまいます。

同様に、上記サンプルプロジェクトでは ③の部分は、queryCompletionBlockを使用していますが、上記②同様に、iOS14.1ではOKですが、iOS15.4では以下のdeprecate警告が出ます。

'queryCompletionBlock' was deprecated in iOS 15.0: Use queryResultBlock instead

③なので、queryResultBlock を使用することがポイントです。

ソースコードを全文掲載します。
入力パラメータが3つほどありますが、重要なのは3つめのcompletionHandler だけで、その他の2つは私の独自仕様のために必要なパラメータなので無視してけっこうです。

    func read_T100(pb_area: User_class,Key searchID: String,completionHandler: @escaping (Result<Void, Error>) -> Void) {

        let predicate = NSPredicate(format: "ID == %@", searchID)
        let sort = NSSortDescriptor(key: "NAME_OF_SCHOOL", ascending: true)
        let query = CKQuery(recordType: "T100_T_SCHOOL", predicate: predicate)
        query.sortDescriptors = [sort]
        let queryOperation = CKQueryOperation(query: query)
        
        queryOperation.recordMatchedBlock = { record, result in
            switch result {
                case .success(let record):
                      DispatchQueue.main.async {
                        pb_area.T100_Count = pb_area.T100_Count + 1
                        pb_area.T100_School_Name = record["NAME_OF_SCHOOL"]!
                        pb_area.T100_Mail_Address = record["MAIL_ADDRESS"]!

                        print("NAME_OF_SCHOOL = \(record["NAME_OF_SCHOOL"]!)")
                        print("MAIL_ADDRESS = \(record["MAIL_ADDRESS"]!)")
                        print("件数 = \(pb_area.T100_Count)")
                    }
                    break
                case .failure(let error):
                    print("error内容 = \(error.localizedDescription)")
                    break
            }
        }
        
        queryOperation.queryResultBlock = { result in
            switch result {
            case .failure(let error):
                completionHandler(.failure(error))
                print("read_T100 Error desu....")
                print(error.localizedDescription)
            case .success:
                print("read_T100 Success desu....")
                print("T100件数 = \(pb_area.T100_Count)")
                break
            }
        }
        database.add(queryOperation)
    }
    

【テストしてわかった非同期の動き】

①〜③まで及びその周辺あちこちに、display命令を入れまくりまして(すこし残骸が見えると思います)テストを何度もしまして、わかったことがあります。

処理は、あっという間に流れて、このメソッドの後の処理まで流れている時に、②と③の処理はまだ、全く動きません(というか非同期なのでしょう。)

タイミングが、少し遅れて動き出します。レコードに該当があり、該当レコードが戻される度に、②の処理が動きます。そして最後のレコードの②の処理が走行した後に、③の処理が動きました。

かなり勉強になりました。2022.05.19