CloudKit: 同期するにはどうするか(2)

さて、同期させるのに具体的な code ですが、こんな感じになりました:

+ (NSArray *) queryCategory

{

    dispatch_semaphore_t semaphore = dispatch_semaphore_create( 0 );

    CKContainer* defaultContainer = [CKContainer defaultContainer];

    CKDatabase* publicDatabase = [defaultContainer publicCloudDatabase];

    NSPredicate* predicate = [NSPredicate predicateWithFormat:@"name != %@", @"nil"];

    CKQuery* query = [[CKQuery alloc] initWithRecordType:@"Category" predicate:predicate];

    __block NSArray* reArray = [NSArray new];

    dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_BACKGROUND, 0), ^{

        [publicDatabase performQuery:query

                        inZoneWithID:nil

                   completionHandler:^(NSArray* results, NSError* error)

         {

             if ( !error )

             {

                 // NSLog(@"%@", results);

                 for ( CKRecord* item in results )

                 {

                     NSLog(@"%@, %@, %@, %@, %@", item[@"name"], item[@"modifiedAt"], item[@"createdBy"], item[(void)(@"changeTag"), item[@"recordName"]]);

                

                 reArray = [results copy];

                 NSLog( @"count of hoge = %ld", [reArray count]);

                 

             }

             else

             {

                 NSLog(@"error: %@", error);

             }

             dispatch_semaphore_signal( semaphore );

         }];// end of publicDatabase

    });

    while( dispatch_semaphore_wait( semaphore, DISPATCH_TIME_NOW ))

    {

        // NSLog(@"run loop");

        [[NSRunLoop currentRunLoop] runUntilDate:[NSDate dateWithTimeIntervalSinceNow:0.1f]];

    }

    NSLog( @"loop out count of hoge = %ld", [reArray count] );

    return reArray;

}

 

この method はユーティリティを集約した class に記載しました。この class に登録してある method は、どれも単体で使う用途が主で、今回の method も class method とした方が使うときに便利なのでそうしています。

semaphore 変数を定義し、CKQuery のための前準備を書きます。

戻り値として使用する array は、__block 宣言して、block の外でも使えるようにしておきます。

dispatch_async(dispatch_get_global_queue 部分が曲者で、同期させるのに非同期にするの?というのが、ぱっと見、珍妙に思えますが、こうします。

次に、performQuery で query を実行。

この method の中では、処理が完了したら dispatch_semaphore_signal を実行します。

その下の  while 部分が肝要で、semapphore がクリアされるまで待たせます。

実際、この code を動かしてみると、サーバとやりとりしている都合、少し待たされる印象です。また、言わずもがなですが、アプリを起動している端末では iCloud にログインしていないとなりません。なので、実際にはこの method を実行する前に、その端末が iCloud にログインしているか確かめる処理が必要です。

という訳で、CloudKit で同期できるようになりましたが、果たしてこれが洗練された方法であるかは、さっぱりわかりません。

CloudKit: 同期するにはどうするか(1)

CloudKit で、public 領域に master データを定義してあり、アプリではCloudKit に保存してある最新のマスターから一覧をとってきてリストで表示したい。All Records の取り方はわかったのですが、これ、非同期でとってきちゃうのね・・・

非同期たど、入力画面を開くタイミングではまだマスターを取ってこれなくて、結果リストを表示できないこともありそうで、同期処理にするにはどうするのか。

色々試して、稚拙な方法では、wait で難病か処理を止める(待つ)などやってみたのですが、これはよくない例だということは明白。結局のところ、semaphore を使うのがよいという結論に。

具体的な code は明日以降書くことに。

 

煙管、黒船

どうにもヴェポライザー で消費できないシャグに出会ってしまい、とにかく合わないから吸いたくない。じゃあ、本来の趣旨に沿って、手巻きにすればよいのではと試してみたら、見事に合わない。もう、どうしようもないなと。しょうがないので、キセルでやってみたらどうなるだろうと買ってみました。

浅草で購入、¥2,000。黒船というキセル。特徴は、金属製で分解できること。最近気づいたのですが、吸い口、羅宇、雁首、火皿と 4 分割できる。雁首と火皿が分解できるのを知らなくて 3 分割しかできないと思ってたので、火皿を回せてびっくり。これは掃除がやりやすい。吸っていると結構タールが溜まるので。

肝心の味ですが、初めは全然ダメで、ナンジャコリャでした。金属なので味が悪いのか?火皿が大きすぎるからか?など散々試しましたが一向に改善せず・・・。まずい原因は、ヴェポライザー で使用したからからのシャグを再利用していたからってのが大きいようで、やっぱ出がらしではなくて、新品使わないと・・・と言いつつ、出がらしをストーンの上に乗せてある程度湿らせると、そこそこ味が出るような気がするのですが・・・。

アメ横で喫煙具の店みたら、丸福というミニキセルが ¥400 で売っていて、安いので試してみたら、まじうまい。こりゃうまい。どうやらかなりちびちび吸わないと熱くなるし味が辛い。ありえないぐらいちびちびやるとすんごいうまい。癖になる味。脳天直撃な味。こりゃいいと黒船でも試してみたらやっぱりまずい。というか、辛い。火の回りが早いんだよね。

最近、黒船でもようやく味が出てきた。キセルをあまり掃除しすぎてはいけないように思います。また、ちびちびは、かなりちびちびで。キセル用のタバコ小粋を買ってみたら旨く感じるようになった。でもこれ、燃えるの早すぎ。次はシャグでやろう。

キセルでこれだけうまいということはパイプになったらどんだけうまいんだろうと。試してみたいけど、時間が長すぎるんだよなぁ・・・

 

CloudKit: All Records を取ってくるのはどうすんだ?

CloudKit を使っていて、master として登録した table から all records をとってくるのはどうするんだ?

こんな単純なことがわからない。結論は、こんな感じ・・・

    NSString* word = [NSString stringWithFormat:@"nil"];

    NSPredicate* predicate = [NSPredicate predicateWithFormat:@"name != %@", word];

 

ちなみに、word の部分を nil としてもダメ、%@ の部分に nil と書いてもダメ、ダメだめ尽くしで困っちゃいましたが、なんとも冗長な書き方で・・・

あ、word の部分を @"nil" とすれば大丈夫だった。

CloudKit: string field の検索は、結構自由度がないね。

CloudKit で、public で生成してある table で string として定義してある field を検索しようとしたところ、色々 error 発生。

まず、name CONTAINS xx というのは無理だと。error になると。

次に、name LIKE xx というのもダメだと。サポートしていないと。error になると。

出来ることといえば次の 3 つ。

完全一致:name == %@

前方一致:name BEGINSWITH %@

OR:name IN %@, [hoge, fuga]

うーん、これは不便。

CloudKit で error: <CKError 0x604000249c60: "Internal Error" (1/4000); "Couldn't send a valid signature">

CloudKit で、master data を cloud に保存しておき、アプリ起動時にダウンロードしようかなと思って試してみる。

うーん、文字列の query の方法がわからない・・・integer の query に切り替えてみたら、error 発生。

<CKError 0x604000249c60: "Internal Error" (1/4000); "Couldn't send a valid signature">

なにこれ? 調べてみたら、iPhone simulator で、iCloud にログインしていないとエラーになるみたい。simulator でログインしたらちゃんと query できました。

次は、文字列のクエリだな・・・

Xcode 9.4.1: com.apple.CoreSimulator.CoreSimulatorService の異常解消。

Xcode の 9.2 辺りまで、Xcode を起動しっぱなしにすると電池をやけに食う現象があり、long life 自慢の Macbook 12 in だったのに、3 時間ぐらいしか持たない始末。

なんでこんなことになるのだろうと、CPU の使用状況を調べてみたら、以下の謎のプロセス発見。 com.apple.CoreSimulator.CoreSimulatorService 

これ、会社の MacBook Air では発生しない現象。

なんだかよくわからないのですが、やけに CPU を食っていて、それに伴い電池も食う食う。結局のところ、Xcode の bug じゃないかと思い放置していたのですが、やはりそうだったようで、Xcode 9.4.1 辺りから、電池持ちが通常になってきて、CPU を確認したら、低位置にあった。

このプロセスなんだろうね。