CloudKitデータベースから通知を発行する/受け取る

全体の流れ
1.CloudKitDatabaseから通知がいくようにセットする
2.リモート通知が受け取れるようにセットする

//CloudKitデータベースから通知がいくようにセットする。どこで実行してもOK。
NSPredicate*predicate=[NSPredicate predicateWithFormat:@”TRUEPREDICATE”];
CKSubscription*subscription=[[CKSubscription alloc]initWithRecordType:@”neko” predicate:predicate options:CKSubscriptionOptionsFiresOnRecordCreation|CKSubscriptionOptionsFiresOnRecordDeletion];
subscription.notificationInfo=[[CKNotificationInfo alloc]init];
subscription.notificationInfo.alertBody=@”データベースからの通知だよ”;
subscription.notificationInfo.shouldBadge=YES;
subscription.notificationInfo.shouldSendContentAvailable=YES;
[savedSubscriptions addObject:subscription];

//リモートの通知が受け取れるようにセットする

– (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
    UIUserNotificationSettings*settings=[UIUserNotificationSettings settingsForTypes:UIUserNotificationTypeAlert|UIUserNotificationTypeBadge|UIUserNotificationTypeSound categories:nil];
    [application registerForRemoteNotifications];
    [application registerUserNotificationSettings:settings];
    [application registerForRemoteNotifications];
    …
    return YES;
}

-(void)application:(UIApplication *)application didReceiveRemoteNotification:(nonnull NSDictionary *)userInfo fetchCompletionHandler:(nonnull void (^)(UIBackgroundFetchResult))completionHandler{
    CKNotification*notif=[CKNotification notificationFromRemoteNotificationDictionary:userInfo];
    if(notif.notificationType==CKNotificationTypeQuery){
        //ここでキャッチした時の処理を書く
    }
    completionHandler(UIBackgroundFetchResultNoData);
}

訂正:
notif.notificationType==CKNotificationTypeQueryって書くと、アクティブ・バックグラウンドの時は実行されるが、アプリ未起動時は実行されない。返ってきてる値が違うのかもしれない。要確認。
とりあえずif文を外して書いた。

CKRecordをNSUserDefaultsやファイルでキャッシュする場合

全体の流れ
1.データを格納しておくカスタムクラスを作る(NSObject<NSCoding>)
2.CKAssetはそのまま保存できないのでNSDataで保存できるようにする
2.NSCodingに必要な物をメソッドを実装する
4. 必要なデータを格納しファイルに保存したりする

//データを格納しておくカスタムクラスを作る
【DataClass.h】
#import <Foundation/Foundation.h>
@import CloudKit;
@interface DataClass : NSObject<NSCoding>{
    CKRecord*record;
    NSData*photoData;
}
@property(readwrite)CKRecord*record;
@property(readwrite)NSData*photoData;
@end

【DataClass.m】
#import “DataClass.h”
@implementation DataClass
@synthesize record;
@synthesize photoData;
-(id)initWithCoder:(NSCoder *)aDecoder{
    self.record=[aDecoder decodeObjectForKey:@”record”];
    self.photoData=[aDecoder decodeObjectForKey:@”photoData”];
    return self;
}

-(void)encodeWithCoder:(NSCoder *)aCoder{
    [aCoder encodeObject:self.record forKey:@”record”];
    [aCoder encodeObject:self.photoData forKey:@”photoData”];
}
@end

//データを格納するクラスを準備する
DataClass*dataClass=[[DataClass alloc]init];
NSMutableArray*dataArray=[[NSMutableArray alloc]initWithCapacity:0];

//データを格納する
dataClass.record=record;
CKAsset*asset=[record valueForKey:@”photoAsset”];
dataClass.photoData=[[NSData alloc]  initWithContentsOfFile:asset.fileURL.path];
[dataArray addObject:dataClass];

//今回は日付のファイル名で保存するのでその準備
NSDateFormatter* formatter = [[NSDateFormatter alloc] init];
[formatter setLocale:[NSLocale currentLocale]];
[formatter setDateFormat:@”yyyyMMdd”];

//ファイルの作成準備
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString*fileName=[[paths objectAtIndex:0] stringByAppendingFormat:@”/cacheData/%@.dat”,[formatter stringFromDate:selectedDate]];

//ファイルに保存する
[NSKeyedArchiver archiveRootObject:dataArray toFile:fileName];

//ファイルから復元する場合
[dataArray addObjectsFromArray:[NSKeyedUnarchiver unarchiveObjectWithFile:fileName]];

CloudKitのデータベースで接続中のユーザーアカウントで作ったレコードを取得する

全体の流れ
1.接続中のユーザーのRecordIDを取得する
2.そのRecordIDを使ってクエリーを書く
3.クエリーを実行する

このまま書くとRecordIDが取得できる前に2が実行されてしまう。
1の部分はアプリ起動時等に先にやっておくのが良いと思う。

//接続中のユーザーのRecordIDを取得する
CKContainer*container=[CKContainer defaultContainer];
[container fetchUserRecordIDWithCompletionHandler:^(CKRecordID * recordID, NSError * error) {
    if(error==nil){
    NSLog(@”user fetch done”);
    self.userID=recordID;
}];

//そのRecordIDを使ってクエリーを書く
NSPredicate*predicate=[NSPredicate predicateWithFormat:@”creatorUserRecordID = %@”,[[CKReference alloc] initWithRecordID:recordID action:CKReferenceActionNone]];
CKQuery*query=[[CKQuery alloc]initWithRecordType:@”TargetRecords” predicate:predicate];

//クエリーを実行する
CKDatabase*publicDB=[CKContainer defaultContainer]publicCloudDatabase];
[publicDB performQuery:query inZoneWithID:nil completionHandler:^(NSArray*results,NSError*error){
    if(results.count>0){
        NSLog(@”userRecord fetch %lu”,(unsigned long)results.count);
    };
}];

CloudKitのデータベースで 現在接続中のUsersレコードを取得する

全体の流れ
1.データベースをセット
1.UsersレコードにアクセスできるOperation取得
2.レコード読み込み時のブロックをセット
4.Operation実行

//データベースをセット
CKDatabase*publicDB=[[CKContainer defaultContainer] publicCloudDatabase];

//UsersレコードにアクセスできるOperation取得
CKFetchRecordsOperation*operation=[CKFetchRecordsOperation fetchCurrentUserRecordOperation];

//レコード読み込み時のブロックをセット    operation.perRecordCompletionBlock=^(CKRecord*record,CKRecordID*recordID,NSError*error){
    if(error==nil){
         NSLog(@”user fetch done”);
         self.userID=recordID;
         self.user=record;
    }
};

//Operation実行
[publicDB addOperation:operation];

 

CloudKitで親子のテーブルのひも付け

全体の流れ
1.親のレコードからCKReferenceを得る
2.子レコード作る
3.子レコードのReferenceに入れる
4.子レコードを保存

親レコード:parentRecord
子レコードのタイプ:RecordType
子レコードにはCKReference型の”ref”カラムがあるとする。

//親レコードからCKReferenceを取得
CKReference*parentRef=[[CKReference alloc]initWithRecord:parentRecord action:CKReferenceActionDeleteSelf];

//子レコードを作る
CKRecord*newRecord=[CKRecord alloc]initWithRecordType:@”RecordType”];

//子レコードにCKReferenceをセット
[newRecord setValue:parentRef forKey:@”ref”];
…ここでその他のデータもセット

//保存
[publicDB saveRecord:record completionHandler:^(CKRecord*savedRecord,NSError*error){
}];

CoreDataのクエリーの書き方(NSPredicate,NSSortDescriptor)の書き方

全体の流れ
1.エンティティをセット
2.検索条件をセット(NSPredicate)
3.並び順をセット(NSSortDescriptor)
4.クエリー実行
5.CoreDataのクエリーだけでなく、CloudKitからのクエリーやNSArrayからの検索にも使える
6.NSPredicateは単純な比較もかけるし、predicateWithBlockを使えばかなり複雑なことも書ける

NSEntityDescription*entity = [NSEntityDescription entityForName:@”Item” inManagedObjectContext:self.context];
NSFetchRequest*req = [[NSFetchRequest alloc]init];

//リクエストにエンティティをセット
[req setEntity:entity];

//検索条件をセット
NSPredicate*predicate=[NSPredicate predicateWithFormat:@”registDate = %@”,selectedDate];
[req setPredicate:predicate];

//ソートを書く
NSSortDescriptor*sort1=[[NSSortDescriptor alloc]initWithKey:@”name” ascending:YES];
NSSortDescriptor*sort2=[[NSSortDescriptor alloc]initWithKey:@”registDate” ascending:YES];
NSArray*sortArray=@[sort1,sort2];
[req setSortDescriptors:sortArray];

//実行
NSArray*result=[self.context executeFetchRequest:req error:&error];


 

//中の要素をみながら比較する場合。条件に一致する場合はYESを返す
NSPredicate*predicate=[NSPredicate predicateWithBlock:^BOOL(id  record, NSDictionary * bindings) {
    if([record valueForKey:@”registDate”] == selectedDate){
        return YES;
    }else{
        return NO;
    }
}];

 

CloudKitで複数のアプリからデータベースを共有する

全体の流れ
1.App1とApp2からデータを共有するプラン
2.App1からは普通に接続
3.App2からはidを指定して接続

App1側
CKContainer*container=[CKContainer defaultContainer];

App2側
//App1のiCloudのidを指定する
CKContainer*container=[CKContainer containerWithIdentifier:@”xxx.iCloud.App1″];