iOS CloudKit的使用

主要講解iCloud工程的創建和CloudKit的使用


CloudKit是什么

  • 云端數據庫,存儲數據,提供簡單的增刪改查功能
  • 特點是方便簡單,適合不懂后臺的個人開發者.
  • 安全,畢竟是蘋果自家的產品.不需要復雜的登錄認證體系.
  • 目前蘋果允許你使用 CloudKit 存儲 10 GB 資源,100 M 數據庫存儲,每天 2 GB 流量;當你的用戶數量增加的時候,這些免費額度也相應地增加到 1 PB 存儲、10 TB 數據庫存儲,以及每天 200 TB 流量,幾乎用不完.你感覺不夠用的時候,你的用戶量已經很龐大了....
  • 由兩部分組成
  1. 一個儀表web頁面,用于管理公開數據的記錄類型.
  2. 一組API接口,用于iCloud和設備之間的數據傳遞.

一、開發者賬號中啟用iCloud服務

1.選擇要添加iCloud功能的appid,勾選iCloud,選擇Include CloudKit support (requires Xcode 6),后點擊Edit,選擇需要額外添加的Container。
42B967C8-332B-4259-A0F5-DEC606A1E3B7.png
2.每個bundleid下本身會有一個Container,如果需要額外的Container,可以通過iCloud Containers創建
E6670FF0-5D68-4E7C-AB3F-EEAA001E977D.png
3.然后可以在Edit里選擇需要的Container后,點擊continue 以及Assign就完成了添加。
18BB7039-E95E-4A07-BFA1-A960BBF20096.png

->>

32CBA831-1CD4-47FE-A542-CD1C546E406A.png

二、在Xcode中啟用iCloud

1.必須在Xcode中添加賬號,選擇對應的Team
7B55F20E-9685-4EA8-AE91-17997179A68C.png
2.在Capabilities里,打開iCloud開關,并勾選CloudKit,如果需要額外的容器,勾選Specify custom containers,選擇額外的容器,這個容器也可以在其他app中添加。
503D2F6C-0972-48A7-A381-7FCA60CC25CB.png

三、關于 CloudKit Dashboard

1.可以點擊上圖中的CloudKit Dashboard按鈕進入,也可以在https://developer.apple.com里進入
572E8EC0-2D44-4B46-BA79-F8DD6ECBC75F.png
2.選擇該Team下其中一個容器
403D8483-B2E3-4188-8E2F-5E6E388BBB38.png
D9CEE71F-8AC5-4781-AF6A-DDFBE137DA6C.png

其中主要使用的是Record Types

一個Record Type用來定義一個單獨的記錄(可以理解為一個數據模型),相當于存儲數據的模板,和數據庫的表結構類似

PUBLIC DATA 和 PRIVATE DATA 就是你保存數據的地方,開發者可以查看所有的共享數據,但是只能看到自己的私密數據,無法看到用戶的私密數據;這里沒有顯示PRIBATE DATA,其結果和PUBLIC DATA是一樣的;

可以在Dashboard中添加記錄、添加記錄模型等

492E3956-9141-4AB3-8F2C-D49C985604C7.png
D6619556-DE0D-470F-A3E0-3F267EB66E8B.png

支持的數據類型

四、代碼中使用

CloudKit 基礎對象類型

CloudKit 的基礎對象類型有 7 種。

  • CKContainer: Containers 就像應用運行的沙盒一樣,一個應用只能訪問自己沙盒中的內容而不能訪問其他應用的。Containers 就是最外層容器,每個應用有且僅有一個屬于自己的 container。(事實上,經過開發者授權配置 CloudKit Dashboard 之后,一個應用也可以訪問其他應用的 container,即共享容器)
  • CKDatabase: Database 即數據庫,私有數據庫用來存儲敏感信息,比如說用戶的性別年齡等,用戶只能訪問自己的私有數據庫。應用也有一個公開的數據庫來存儲公共信息,例如你在構建一個根據地理位置簽到的應用,那么地理位置信息就應該存儲在公共數據庫里以便所有用戶都能訪問到。
  • CKRecord: 即數據庫中的一條數據記錄。CloudKit 使用 record 通過 k/v 結構來存儲結構化數據。關于鍵值存儲,目前值的架構支持 NSString、NSNumber、NSData、NSDate、CLLocation,和 CKReference、CKAsset(這兩個下面我們會說明),以及存儲以上數據類型的數組。
  • CKRecordZone: Record 不是以零散的方式存在于 database 之中的,它們位于 record zones 里。每個應用都有一個 default record zone,你也可以有自定義的 record zone。
  • CKRecordIdentifier: 是一條 record 的唯一標識,用于確定該 record 在數據庫中的唯一位置。
  • CKReference: Reference 很像 RDBMS 中的引用關系。還是以地理位置簽到應用為例,每個地理位置可以包含很多用戶在該位置的簽到,那么位置與簽到之間就形成了這樣一種包含式的從屬關系。
  • CKAsset: 即資源文件,例如二進制文件。還是以簽到應用為例,用戶簽到時可能還包含一張照片,那么這張照片就會以 asset 形式存儲起來。
1.增加一條記錄
//獲取默認的容器
CKContainer *container = [CKContainer defaultContainer];

//    如果是自定義的容器
//    CKContainer *shareContainer = [CKContainer containerWithIdentifier:ContainerID];

CKDatabase *database;

if(isPublic)
{
    database = container.publicCloudDatabase;//公共數據庫
}
else
{
     database = container.privateCloudDatabase;//私有數據庫
}

//創建主鍵ID  這個ID到時查找有用到
CKRecordID *noteId = [[CKRecordID alloc] initWithRecordName:recordID];

//創建CKRecord 保存數據
CKRecord *noteRecord = [[CKRecord alloc] initWithRecordType:@"User" recordID:noteId];

//設置數據
[noteRecord setObject:name forKey:@"name"];
[noteRecord setObject:password forKey:@"password"];

       //保存操作
[database saveRecord:noteRecord completionHandler:^(CKRecord *_Nullablerecord,NSError *_Nullableerror) {
    if(!error)
    {
        NSLog(@"保存成功");
    }
    else
    {
        NSLog(@"保存失敗: %@",error);
    }
 }];
注意 :
  • 默認用戶只能只讀數據庫,要添加修改則需要登錄icloud賬戶

  • 公有數據庫所有的用戶(安裝app的用戶,不是指開發者)都可以訪問,私有的只能當前用戶能訪問.

  • 如果用戶沒有登錄,提醒用戶登錄icloud

    [[CKContainer defaultContainer] accountStatusWithCompletionHandler:^(CKAccountStatusaccountStatus,NSError *_Nullableerror) {
       if(accountStatus ==CKAccountStatusNoAccount)
       {
             handler(NO);
       }
       else
       {
             //登錄過了
             handler(YES);
       }
     }];
    
2.獲取一條記錄
CKRecordID *noteId = [[CKRecordID alloc]initWithRecordName:recordID];
CKDatabase *publicDatabase = [[CKContainer defaultContainer] publicCloudDatabase];

//通過主鍵ID查找記錄
[publicDatabase fetchRecordWithID:noteId completionHandler:^(CKRecord *_Nullablerecord,NSError *_Nullableerror) {
   if(!error)
    {
       NSLog(@"查詢成功: %@",record);
    }
    else
    {
       NSLog(@"查詢失敗: %@",error);
    }
}];
3.查詢多條記錄
CKDatabase *publicDatabase = [[CKContainer defaultContainer] publicCloudDatabase];

NSPredicate *predicate = [NSPredicate predicateWithFormat:@"name != %@",@"xiaowang"];
CKQuery *query = [[CKQuery alloc] initWithRecordType:recordTypeName predicate:predicate];

NSSortDescriptor *firstDescriptor = [[NSSortDescriptor alloc] initWithKey:@"gender" ascending:NO];
NSSortDescriptor *secondDescriptor = [[NSSortDescriptor alloc] initWithKey:@"age" ascending:NO];

query.sortDescriptors = @[firstDescriptor,secondDescriptor];

//通過謂詞查找記錄
[publicDatabase performQuery:query inZoneWithID:nil completionHandler:^(NSArray<CKRecord*>  *_Nullableresults,NSError *_Nullableerror) {
    if(!error)
    {
       NSLog(@"results: %@",results);
    }
    else
    {
       NSLog(@"查詢失敗: %@",error);
    }
}];
4.更新一條記錄
CKRecordID *noteId = [[CKRecordID alloc] initWithRecordName:recordID];
CKDatabase *publicDatabase = [[CKContainer defaultContainer] publicCloudDatabase];

//先找到記錄,再修改記錄
[publicDatabase fetchRecordWithID:noteId completionHandler:^(CKRecord *_Nullablerecord,NSError *_Nullableerror) {
   if(!error) 
   {
       [recordsetObject:@"123456789" forKey:@"password"];
       [recordsetObject:@"m" forKey:@"gender"];
       [recordsetObject:@20 forKey:@"age"];

       //修改后保存記錄
       [database saveRecord:record completionHandler:^(CKRecord *_Nullablerecord,NSError *_Nullableerror) {
           if(!error) 
           {
               NSLog(@"修改成功 %@",record);
           }
           else
           {
               NSLog(@"修改失敗: %@",error);
           }
        }];
    }
    else
    {
       NSLog(@"找不到該記錄,查詢失敗: %@",error);
    }
}];
5.刪除一條記錄
CKRecordID *noteId = [[CKRecordID alloc] initWithRecordName:recordID];
CKDatabase *publicDatabase = [[CKContainer defaultContainer] publicCloudDatabase];

[publicDatabase deleteRecordWithID:noteId completionHandler:^(CKRecordID *_NullablerecordID,NSError *_Nullableerror) {

   if(!error)
   {
       NSLog(@"刪除成功");
   }
   else
   {
       NSLog(@"刪除失敗: %@",error);
   }
}];
6.保存大文件
CKDatabase *publicDatabase = [[CKContainer defaultContainer] publicCloudDatabase];

CKRecordID *noteId = [[CKRecordID alloc] initWithRecordName:recordID];
CKRecord *noteRecord = [[CKRecord alloc]initWithRecordType:@"User" recordID:noteId];

NSString *path = [[NSBundle mainBundle] pathForResource:@"lcz" ofType:@"jpg"];
CKAsset *asset = [[CKAsset alloc] initWithFileURL:[NSURL fileURLWithPath:path]];

[noteRecord setObject:name forKey:@"name"];
[noteRecord setObject:password forKey:@"password"];
[noteRecord setObject:asset forKey:@"userImage"];

[publicDatabase saveRecord:noteRecord completionHandler:^(CKRecord *_Nullablerecord,NSError *_Nullableerror) {

   if(!error) 
   {
       NSLog(@"保存成功");

   }
   else
   {
       NSLog(@"保存失敗: %@",error);
   }
}];
7.添加地理位置
__weak typeof(self) weakSelf = self;

CLGeocoder *geocoder = [CLGeocoder new];
[geocoder geocodeAddressString:@"北京" completionHandler:^(NSArray<CLPlacemark*>  *_Nullableplacemarks,NSError *_Nullableerror) {

   if(!error) 
   {
       if(placemarks.count>0)
       {
           CLPlacemark *placemark = placemarks[0];
           NSLog(@"%@",placemark.location);

           weakSelf.person.location = placemark.location;

          [weakSelf saveRecordWithPublic:YES andKey:@"location" andObject:weakSelf.person.location andRecordType:@"Person" andRecordID:@"sunjie2"];
        }
    }
}];
8.添加引用(外鍵)
- (void)addReferenceWithPublic:(BOOL)isPublic
                        action:(CKReferenceAction)action
               andReferenceKey:(NSString*)key
             andSourceRecordID:(NSString*)sourceRecordID
             andTargetRecordID:(NSString*)targetRecordID
{

    CKRecordID *noteID = [[CKRecordID alloc]initWithRecordName:targetRecordID];
    CKReference *reference =  [[CKReference alloc]initWithRecordID:noteID action:action];
    CKContainer *container = [CKContainer defaultContainer];

    CKDatabase *database;

    if(isPublic)
    {
        database = container.publicCloudDatabase;
    }
    else
    {
        database = container.privateCloudDatabase;
    }

    CKRecordID *sourceRecordId = [[CKRecordID alloc]initWithRecordName:sourceRecordID];

    [database fetchRecordWithID:sourceRecordId completionHandler:^(CKRecord *_Nullablerecord,NSError *_Nullableerror) {

       if(!error)
       {
            [record setObject:reference forKey:key];

            [database saveRecord:record completionHandler:^(CKRecord *_Nullablerecord,NSError *_Nullableerror) {

               if(!error)
               {
                   NSLog(@"保存成功");
                   self.person.workN = reference;
               }
               else
               {
                   NSLog(@"保存失敗: %@",error);
               }
            }];
        }
    }];
}
9.查詢引用的記錄
//拿到引用的id
CKRecordID *recordID = reference.recordID;

//根據id查詢
[database fetchRecordWithID:recordID completionHandler:^(CKRecord *record,NSError *error) {

   if(error) 
   {
       //錯誤處理
       NSLog(@"查詢失敗%@",error);
   }
   else
   {
       // 查詢成功
       NSLog(@"查詢成功%@",record);
   }
}];
10.批操作處理

CKFetchRecordsOperation;
CKModifyRecordsOperation;
CKQueryOperation;
CKDatabaseOperation;
CKModifyBadgeOperation;
CKOperation;
CKSubscriptionOptions;
CKModifySubscriptionsOperation;
CKFetchSubscriptionsOperation;

- (void)fetchOperationWithRecordID:(NSArray<CKRecordID*>*)fetchRecordIDs
{
    CKFetchRecordsOperation *fetchRecordsOperation = [[CKFetchRecordsOperation alloc] initWithRecordIDs:fetchRecordIDs];

    fetchRecordsOperation.perRecordCompletionBlock = ^(CKRecord *record,CKRecordID *recordID,NSError *error) {

       if(error)
       {
           //錯誤處理
           NSLog(@"查詢失敗%@",error);
       }
       else
       {
           // 查詢成功
           NSLog(@"查詢成功%@",record);
       }
    };

    fetchRecordsOperation.fetchRecordsCompletionBlock= ^(NSDictionary*recordsByRecordID,NSError*error) {

       if(error) 
       {
           //錯誤處理
           NSLog(@"查詢失敗%@",error);
       }
       else
       {
           // 查詢成功
           NSLog(@"查詢成功%@",recordsByRecordID);
       }
    };

    fetchRecordsOperation.database = [[CKContainer defaultContainer] publicCloudDatabase];
    [fetchRecordsOperation start];
}
11.添加訂閱和通知

options參數的可能值是:

CKSubscriptionOptionsFiresOnRecordCreation,
CKSubscriptionOptionsFiresOnRecordDeletion,
CKSubscriptionOptionsFiresOnRecordUpdate,
CKSubscriptionOptionsFiresOnce.

因為options參數是一個位掩碼,可以訂閱的改變的類型的任何組合。

例如,您可以通過CKSubscriptionOptionsFiresOnRecordCreation| CKSubscriptionOptionsFiresOnRecordUpdate作為選項:參數來接收所有新數據的通知。

- (void)addSubscriptionAndNotificationsWithRecordID:(NSString*)recordID AndRecordType:(NSString*)recordType
{
   NSPredicate *predicate = [NSPredicate predicateWithFormat:@"name = %@",@"Lu"];

   //創建一個訂閱
    CKSubscription*subscription = [[CKSubscription alloc]
                               initWithRecordType:recordType
                               predicate:predicate
                               options:CKSubscriptionOptionsFiresOnRecordCreation];

    CKNotificationInfo *notificationInfo = [CKNotificationInfo new];
    notificationInfo.alertLocalizationKey = @"訂閱新推送";
    notificationInfo.shouldBadge=YES;
    subscription.notificationInfo= notificationInfo;

    CKDatabase *publicDatabase = [[CKContainer defaultContainer] publicCloudDatabase];

    [publicDatabase saveSubscription:subscription
                   completionHandler:^(CKSubscription *subscription,NSError *error) {
                      if(!error)
                      {
                          NSLog(@"訂閱成功%@",subscription);
                      }
                      else
                      {
                          NSLog(@"訂閱失敗%@",error);
                      }

               }];
}
最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 230,563評論 6 544
  • 序言:濱河連續發生了三起死亡事件,死亡現場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機,發現死者居然都...
    沈念sama閱讀 99,694評論 3 429
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事。” “怎么了?”我有些...
    開封第一講書人閱讀 178,672評論 0 383
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 63,965評論 1 318
  • 正文 為了忘掉前任,我火速辦了婚禮,結果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當我...
    茶點故事閱讀 72,690評論 6 413
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發上,一...
    開封第一講書人閱讀 56,019評論 1 329
  • 那天,我揣著相機與錄音,去河邊找鬼。 笑死,一個胖子當著我的面吹牛,可吹牛的內容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 44,013評論 3 449
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 43,188評論 0 290
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當地人在樹林里發現了一具尸體,經...
    沈念sama閱讀 49,718評論 1 336
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 41,438評論 3 360
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發現自己被綠了。 大學時的朋友給我發了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 43,667評論 1 374
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 39,149評論 5 365
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質發生泄漏。R本人自食惡果不足惜,卻給世界環境...
    茶點故事閱讀 44,845評論 3 351
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 35,252評論 0 28
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 36,590評論 1 295
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 52,384評論 3 400
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 48,635評論 2 380

推薦閱讀更多精彩內容