iOS CloudKit的使用

主要講解iCloud工程的創(chuàng)建和CloudKit的使用


CloudKit是什么

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

一、開發(fā)者賬號(hào)中啟用iCloud服務(wù)

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

->>

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

二、在Xcode中啟用iCloud

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

三、關(guān)于 CloudKit Dashboard

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

其中主要使用的是Record Types

一個(gè)Record Type用來定義一個(gè)單獨(dú)的記錄(可以理解為一個(gè)數(shù)據(jù)模型),相當(dāng)于存儲(chǔ)數(shù)據(jù)的模板,和數(shù)據(jù)庫的表結(jié)構(gòu)類似

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

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

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

支持的數(shù)據(jù)類型

四、代碼中使用

CloudKit 基礎(chǔ)對(duì)象類型

CloudKit 的基礎(chǔ)對(duì)象類型有 7 種。

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

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

CKDatabase *database;

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

//創(chuàng)建主鍵ID  這個(gè)ID到時(shí)查找有用到
CKRecordID *noteId = [[CKRecordID alloc] initWithRecordName:recordID];

//創(chuàng)建CKRecord 保存數(shù)據(jù)
CKRecord *noteRecord = [[CKRecord alloc] initWithRecordType:@"User" recordID:noteId];

//設(shè)置數(shù)據(jù)
[noteRecord setObject:name forKey:@"name"];
[noteRecord setObject:password forKey:@"password"];

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

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

  • 如果用戶沒有登錄,提醒用戶登錄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;

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

   if(error) 
   {
       //錯(cuò)誤處理
       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)
       {
           //錯(cuò)誤處理
           NSLog(@"查詢失敗%@",error);
       }
       else
       {
           // 查詢成功
           NSLog(@"查詢成功%@",record);
       }
    };

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

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

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

options參數(shù)的可能值是:

CKSubscriptionOptionsFiresOnRecordCreation,
CKSubscriptionOptionsFiresOnRecordDeletion,
CKSubscriptionOptionsFiresOnRecordUpdate,
CKSubscriptionOptionsFiresOnce.

因?yàn)閛ptions參數(shù)是一個(gè)位掩碼,可以訂閱的改變的類型的任何組合。

例如,您可以通過CKSubscriptionOptionsFiresOnRecordCreation| CKSubscriptionOptionsFiresOnRecordUpdate作為選項(xiàng):參數(shù)來接收所有新數(shù)據(jù)的通知。

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

   //創(chuàng)建一個(gè)訂閱
    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);
                      }

               }];
}
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容