iOS學習筆記32-iCloud入門

一浇垦、iCloud云服務

iCloud是蘋果提供的云端服務,用戶可以將通訊錄斩披、備忘錄溜族、郵件、照片垦沉、音樂煌抒、視頻等備份到云服務器并在各個蘋果設備間直接進行共享而無需關心數據同步問題,甚至即使你的設備丟失后在一臺新的設備上也可以通過Apple ID登錄同步厕倍。

蘋果已經將云端存儲功能開放給開發(fā)者寡壮,可以存儲兩類數據:
  1. key-value data
    分享小量的非關鍵配置數據到應用的多個實例,使用類似于NSUserDefault
  1. document
    存儲用戶文檔和應用數據到用戶的iCloud賬戶
進行iCloud開發(fā)的準備工作:
  1. 在開發(fā)者中心上創(chuàng)建AppleID讹弯,啟用iCloud服務
  1. 生成對應的配置文件(Provisioning Profile)况既,這里可以使用通配Bundle ID
  2. 以上2步是針對真機的,調試模擬器可以忽略
  3. 打開項目的Capabilities组民,找到iCloud服務并開啟它
  4. 在iCloud服務的Service中勾選Key-value storaeiCloud Documents
    以上是使用模擬器的棒仍,使用真機的話,下面2個紅色感嘆就會消失
  5. 你的項目中就會多出一個entitlements文件
  6. 里面的內容是自動生成的臭胜,就像這樣的


  7. 無論真機還是模擬器莫其,都需要進入手機的設置中登陸iCloud賬號


二、Key-Value的iCloud存儲

使用和NSUserDefault差不多耸三,都是以鍵值對的形式存儲乱陡。

使用實例:
#import "iCloudKeysViewController.h"

@interface iCloudKeysViewController()
@property (strong, nonatomic) NSUbiquitousKeyValueStore *keyStore;
@property (strong, nonatomic) IBOutlet UILabel *textLabel;
@end

@implementation iCloudKeysViewController
- (void)viewDidLoad
{
    [super viewDidLoad];
    self.textLabel.text = @"Ready Go";
    //獲取iCloud配置首選項
    self.keyStore = [NSUbiquitousKeyValueStore defaultStore];
    //注冊通知中心,當配置發(fā)生改變的時候仪壮,發(fā)生通知
    NSNotificationCenter *center = [NSNotificationCenter defaultCenter];
    [center addObserver:self
               selector:@selector(ubiquitousKeyValueStoreDidChange:) 
                   name:NSUbiquitousKeyValueStoreDidChangeExternallyNotification
                 object:keyStore];
}
/* UI點擊憨颠,點擊改變按鈕 */
- (IBAction)changeKey
{
    [self.keyStore setString:@"Hello World" forKey:@"MyString"];
    [self.keyStore synchronize];
    NSLog(@"Save key");
}
/* 監(jiān)聽通知,當配置發(fā)生改變的時候會調用 */
- (void)ubiquitousKeyValueStoreDidChange:(NSNotification *)notification
{
    NSLog(@"External Change detected");
    UIAlertView *alert = [[UIAlertView alloc] initWithTitle:@"Change detected"
                                                    message:@"Change detected"
                                                   delegate:nil 
                                          cancelButtonTitle:@"Ok"
                                          otherButtonTitles:nil, nil];
    [alert show];
    //顯示改變的信息
    textLabel.text = [keyStore stringForKey:@"MyString"];
}
@end

三积锅、Document的iCloud存儲

核心類UIDocument
  • 文檔存儲主要是使用UIDocument類來完成爽彤,這個類提供了新建养盗、修改、查詢文檔淫茵、打開文檔爪瓜、刪除文檔的功能。
  • UIDocument對文檔的新增匙瘪、修改铆铆、刪除、讀取全部基于一個云端URL來完成丹喻,對于開發(fā)者而言沒有本地和云端之分薄货,這樣大大簡化了開發(fā)過程。
云端URL的獲取方式:
  • 調用NSFileManager的對象方法
-(NSURL *)URLForUbiquityContainerIdentifier:(NSString *)identifier;
  • 上面需要傳遞一個云端存儲容器的唯一標示碍论,這個可以去自動生成的entitlements文件查看Ubiquity Container Identifiers字段獲得谅猾,如果傳nil,代表第一個容器
補充知識 :

默認的第一個容器標識是iCloud.$(CFBundleIdentifier)鳍悠,
其中$(CFBundleIdentifier)代表Bundle ID税娜,那么根據應用的Bundle ID就可以得知我的第一個容器的標識是iCloud.com.liuting.icloud.iCloudTest

UIDocument的對象方法:
/* 將指定URL的文檔保存到iCloud(可以是新增或者覆蓋,通過saveOperation參數設定)*/
- (void)saveToURL:(NSURL *)url 
 forSaveOperation:(UIDocumentSaveOperation)saveOperation 
completionHandler:(void (^)(BOOL success))completionHandler;
/* 保存操作option */
typedef NS_ENUM(NSInteger, UIDocumentSaveOperation) {
    UIDocumentSaveForCreating,/* 創(chuàng)建 */
    UIDocumentSaveForOverwriting/* 覆蓋寫入 */
};
/* 打開文檔藏研,參數是打開文檔成功回調 */
- (void)openWithCompletionHandler:(void (^)(BOOL success))completionHandler;
刪除文檔使用的是NSFileManager的對象方法:
/* 刪除指定URL下的文件 */
- (BOOL)removeItemAtURL:(NSURL *)URL 
                  error:(NSError **)error;
注意事項:
  • UIDocument在設計的時候敬矩,沒有提供統一的存儲方式來存儲數據,需要我們去繼承它蠢挡,重寫2個對象方法自己操作數據
/**
 *  保存文檔時調用
 *  @param typeName 文檔文件類型
 *  @param outError 錯誤信息輸出
 *  @return 文檔數據
 */
-(id)contentsForType:(NSString *)typeName
                 error:(NSError **)outError;
/**
 *  讀取數據時調用
 *  @param contents 文檔數據
 *  @param typeName 文檔文件類型
 *  @param outError 錯誤信息輸出
 *  @return 讀取是否成功
 */
-(BOOL)loadFromContents:(id)contents
                   ofType:(NSString *)typeName
                    error:(NSError **)outError;
  • UIDocument保存數據的本質:
    將A對應類型的數據轉化為云端存儲的NSData或者NSFileWrapper數據
  • UIDocument讀取數據的本質:
    將云端下載的NSData或者NSFileWrapper數據轉化為A對應類型的數據
下面是我自定義的Document類弧岳,繼承于UIDocument:
LTDocument.h文件
#import <UIKit/UIKit.h>
@interface LTDocument : UIDocument
@property (strong, nonatomic) NSData *data;/*< 文檔數據 */
@end
LTDocument.m文件
#import "LTDocument.h"
@implementation LTDocument
#pragma mark - 重寫父類方法
/**
 *  保存時調用
 *  @param typeName 文檔文件類型后綴
 *  @param outError 錯誤信息輸出
 *  @return 文檔數據
 */
- (id)contentsForType:(NSString *)typeName
                error:(NSError *__autoreleasing *)outError
{
    if (!self.data) {
        self.data = [NSData data];
    } 
    return self.data;
}
/**
 *  讀取數據時調用
 *  @param contents 文檔數據
 *  @param typeName 文檔文件類型后綴
 *  @param outError 錯誤信息輸出
 *  @return 讀取是否成功
 */
- (BOOL)loadFromContents:(id)contents
                  ofType:(NSString *)typeName
                   error:(NSError *__autoreleasing *)outError
{
    self.data = [contents copy];
    return true;
}
@end
  • 如果要加載iCloud中的文檔列表,就需要使用另一個類NSMetadataQuery
  • 通骋堤ぃ考慮到網絡的原因并不會一次性加載所有數據禽炬,而利用NSMetadataQuery并指定searchScopesNSMetadataQueryUbiquitousDocumentScope來限制查找iCloud文檔數據。
  • 使用NSMetadataQuery還可以通過謂詞限制搜索關鍵字等信息勤家,并在搜索完成之后通過通知的形式通知客戶端搜索的情況腹尖。
下面是使用示例:
1. 屬性定義和宏定義:
#import "ViewController.h"
#import "LTDocument.h"

#define kContainerIdentifier @"iCloud.com.liuting.icloud.iCloudTest"

@interface ViewController () <UITableViewDataSource,UITableViewDelegate>
@property (weak, nonatomic) IBOutlet UITextField *documentField;/*< 輸入框 */
@property (weak, nonatomic) IBOutlet UILabel *documentShowLable;/*< 顯示欄 */
@property (weak, nonatomic) IBOutlet UITableView *documentTableView;/* 文檔列表 */
/* 文檔文件信息,鍵為文件名伐脖,值為創(chuàng)建日期 */
@property (strong, nonatomic) NSMutableDictionary *files;
@property (strong, nonatomic) NSMetadataQuery *query;/*< 查詢文檔對象 */
@property (strong, nonatomic) LTDocument *document;/*< 當前選中文檔 */

@end
2. 獲取云端URL方法:
/**
 *  取得云端存儲文件的地址
 *  @param fileName 文件名热幔,如果文件名為nil,則重新創(chuàng)建一個URL
 *  @return 文件地址
 */
- (NSURL *)getUbiquityFileURL:(NSString *)fileName{
    //取得云端URL基地址(參數中傳入nil則會默認獲取第一個容器)晓殊,需要一個容器標示
    NSFileManager *manager = [NSFileManager defaultManager];
    NSURL *url = [manager URLForUbiquityContainerIdentifier:kContainerIdentifier];
    //取得Documents目錄
    url = [url URLByAppendingPathComponent:@"Documents"];
    //取得最終地址
    url = [url URLByAppendingPathComponent:fileName];
    return url;
}
3. 查詢文檔列表方法
/* 從iCloud上加載所有文檔信息 */
- (void)loadDocuments
{
    if (!self.query) {
        self.query = [[NSMetadataQuery alloc] init];
        self.query.searchScopes = @[NSMetadataQueryUbiquitousDocumentsScope];
        //注意查詢狀態(tài)是通過通知的形式告訴監(jiān)聽對象的
        NSNotificationCenter *center = [NSNotificationCenter defaultCenter];
        [center addObserver:self
                   selector:@selector(metadataQueryFinish:)
                       name:NSMetadataQueryDidFinishGatheringNotification
                     object:self.query];//數據獲取完成通知
        [center addObserver:self
                   selector:@selector(metadataQueryFinish:)
                       name:NSMetadataQueryDidUpdateNotification
                     object:self.query];//查詢更新通知
    }
    //開始查詢
    [self.query startQuery];
}
/* 查詢更新或者數據獲取完成的通知調用 */
- (void)metadataQueryFinish:(NSNotification *)notification
{
    NSLog(@"數據獲取成功!");
    NSArray *items = self.query.results;//查詢結果集
    self.files = [NSMutableDictionary dictionary];
    //變量結果集伤提,存儲文件名稱巫俺、創(chuàng)建日期
    [items enumerateObjectsUsingBlock:^(id obj, NSUInteger idx, BOOL *stop) {
        NSMetadataItem *item = obj;
        //獲取文件名
        NSString *fileName = [item valueForAttribute:NSMetadataItemFSNameKey];
        //獲取文件創(chuàng)建日期
        NSDate *date = [item valueForAttribute:NSMetadataItemFSContentChangeDateKey];
        NSDateFormatter *dateformate = [[NSDateFormatter alloc]init];
        dateformate.dateFormat = @"YY-MM-dd HH:mm";
        NSString *dateString = [dateformate stringFromDate:date];
        //保存文件名和文件創(chuàng)建日期
        [self.files setObject:dateString forKey:fileName];
    }];
    //表格刷新
    self.documentShowLable.text = @"";
    [self.documentTableView reloadData];
}

4. UI點擊事件
#pragma mark - UI點擊事件
/* 點擊添加文檔 */
- (IBAction)addDocument:(id)sender {
    //提示信息
    if (self.documentField.text.length <= 0) {
        NSLog(@"請輸入要創(chuàng)建的文檔名");
        self.documentField.placeholder = @"請輸入要創(chuàng)建的文檔名";
        return;
    }
    //創(chuàng)建文檔URL
    NSString *text = self.documentField.text;
    NSString *fileName = [NSString stringWithFormat:@"%@.txt",text];
    NSURL *url = [self getUbiquityFileURL:fileName];
    
    //創(chuàng)建云端文檔對象
    LTDocument *document = [[LTDocument alloc] initWithFileURL:url];
    //設置文檔內容
    NSString *dataString = @"hallo World";
    document.data = [dataString dataUsingEncoding:NSUTF8StringEncoding];
    //保存或創(chuàng)建文檔,UIDocumentSaveForCreating是創(chuàng)建文檔
    [document saveToURL:url
       forSaveOperation:UIDocumentSaveForCreating
      completionHandler:^(BOOL success)
    {
        if (success) {
            NSLog(@"創(chuàng)建文檔成功.");
            self.documentField.text = @"";
            //從iCloud上加載所有文檔信息
            [self loadDocuments];
        }else{
            NSLog(@"創(chuàng)建文檔失敗.");
        }
        
    }];
}
/* 點擊修改文檔 */
- (IBAction)saveDocument:(UIButton *)sender {
    if ([sender.titleLabel.text isEqualToString:@"修改文檔"]) {
        self.documentField.text = self.documentShowLable.text;
        [sender setTitle:@"保存文檔" forState:UIControlStateNormal];
    } else if([sender.titleLabel.text isEqualToString:@"保存文檔"]) {
        [sender setTitle:@"修改文檔" forState:UIControlStateNormal];
        self.documentField.placeholder = @"請輸入修改的文檔內容";
        //要保存的文檔內容
        NSString *dataText = self.documentField.text;
        NSData *data = [dataText dataUsingEncoding:NSUTF8StringEncoding];
        self.document.data = data;
        //保存或創(chuàng)建文檔肿男,UIDocumentSaveForOverwriting是覆蓋保存文檔
        [self.document saveToURL:self.document.fileURL
                forSaveOperation:UIDocumentSaveForOverwriting
               completionHandler:^(BOOL success)
        {
            NSLog(@"保存成功介汹!");
            self.documentShowLable.text = self.documentField.text;
            self.documentField.text = @"";
        }];
    }
}
/* 點擊刪除文檔 */
- (IBAction)removeDocument:(id)sender {
    //提示信息
    if (self.documentField.text.length <= 0) {
        self.documentField.placeholder = @"請輸入要刪除的文檔名";
        return;
    }
    //判斷要刪除的文檔是否存在
    NSString *text = self.documentField.text;
    NSString *fileName = [NSString stringWithFormat:@"%@.txt",text];
    NSArray *fileNames = [self.files allKeys];
    if (![fileNames containsObject:fileName]) {
        NSLog(@"沒有要刪除的文檔");
        return;
    }
    //創(chuàng)建要刪除的文檔URL
    NSURL *url = [self getUbiquityFileURL:fileName];
    NSError *error = nil;
    //刪除文檔文件
    [[NSFileManager defaultManager] removeItemAtURL:url error:&error];
    if (error) {
        NSLog(@"刪除文檔過程中發(fā)生錯誤却嗡,錯誤信息:%@",error.localizedDescription);
        return;
    }
    //從集合中刪除
    [self.files removeObjectForKey:fileName];
    self.documentField.text = @"";
}
5. 視圖控制器初始化和列表顯示
@implementation ViewController
- (void)viewDidLoad {
    [super viewDidLoad];
    self.documentTableView.delegate = self;
    self.documentTableView.dataSource = self;
    /* 從iCloud上加載所有文檔信息 */
    [self loadDocuments];
}
#pragma mark - UITableView數據源
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView {
    return 1;
}
- (NSInteger)tableView:(UITableView *)tableView 
 numberOfRowsInSection:(NSInteger)section 
{
    return self.files.count;
}
- (UITableViewCell *)tableView:(UITableView *)tableView
         cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
    static NSString *identtityKey = @"myTableViewCellIdentityKey1";
    UITableViewCell *cell = 
        [self.documentTableView dequeueReusableCellWithIdentifier:identtityKey];
    if(cell == nil){
        cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleValue1
                                      reuseIdentifier:identtityKey];
    }
    //顯示文檔名和文檔創(chuàng)建日期
    NSArray *fileNames = self.files.allKeys;
    NSString *fileName = fileNames[indexPath.row];
    cell.textLabel.text = fileName;
    cell.detailTextLabel.text = [self.files valueForKey:fileName];
    return cell;
}
#pragma mark - UITableView代理方法
/* 點擊文檔列表的其中一個文檔調用 */
- (void)tableView:(UITableView *)tableView
        didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
    UITableViewCell *cell = [self.documentTableView cellForRowAtIndexPath:indexPath];
    //獲取文檔URL
    NSURL *url = [self getUbiquityFileURL:cell.textLabel.text];
    //創(chuàng)建文檔操作對象
    LTDocument *document = [[LTDocument alloc] initWithFileURL:url];
    self.document = document;
    //打開文檔并讀取文檔內容
    [document openWithCompletionHandler:^(BOOL success) {
        if(success){
            NSLog(@"讀取數據成功.");
            NSString *dataText = [[NSString alloc] initWithData:document.data
                                                       encoding:NSUTF8StringEncoding];
            self.documentShowLable.text = dataText;
        }else{
            NSLog(@"讀取數據失敗.");
        }
    }];
}
@end

上面的代碼Demo點這里:LearnDemo里面的iCloudDemo
這個代碼Demo只有Document的實例代碼

如果有什么問題請在下方評論區(qū)中提出!O(∩_∩)O哈嘹承!
最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
  • 序言:七十年代末窗价,一起剝皮案震驚了整個濱河市,隨后出現的幾起案子叹卷,更是在濱河造成了極大的恐慌撼港,老刑警劉巖,帶你破解...
    沈念sama閱讀 206,482評論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件骤竹,死亡現場離奇詭異帝牡,居然都是意外死亡,警方通過查閱死者的電腦和手機蒙揣,發(fā)現死者居然都...
    沈念sama閱讀 88,377評論 2 382
  • 文/潘曉璐 我一進店門靶溜,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人懒震,你說我怎么就攤上這事罩息。” “怎么了个扰?”我有些...
    開封第一講書人閱讀 152,762評論 0 342
  • 文/不壞的土叔 我叫張陵瓷炮,是天一觀的道長。 經常有香客問我锨匆,道長崭别,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 55,273評論 1 279
  • 正文 為了忘掉前任恐锣,我火速辦了婚禮茅主,結果婚禮上,老公的妹妹穿的比我還像新娘土榴。我一直安慰自己诀姚,他們只是感情好,可當我...
    茶點故事閱讀 64,289評論 5 373
  • 文/花漫 我一把揭開白布玷禽。 她就那樣靜靜地躺著赫段,像睡著了一般。 火紅的嫁衣襯著肌膚如雪矢赁。 梳的紋絲不亂的頭發(fā)上糯笙,一...
    開封第一講書人閱讀 49,046評論 1 285
  • 那天,我揣著相機與錄音撩银,去河邊找鬼给涕。 笑死,一個胖子當著我的面吹牛,可吹牛的內容都是我干的够庙。 我是一名探鬼主播恭应,決...
    沈念sama閱讀 38,351評論 3 400
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼耘眨!你這毒婦竟也來了昼榛?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 36,988評論 0 259
  • 序言:老撾萬榮一對情侶失蹤剔难,失蹤者是張志新(化名)和其女友劉穎胆屿,沒想到半個月后,有當地人在樹林里發(fā)現了一具尸體钥飞,經...
    沈念sama閱讀 43,476評論 1 300
  • 正文 獨居荒郊野嶺守林人離奇死亡莺掠,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 35,948評論 2 324
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現自己被綠了读宙。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片彻秆。...
    茶點故事閱讀 38,064評論 1 333
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖结闸,靈堂內的尸體忽然破棺而出唇兑,到底是詐尸還是另有隱情,我是刑警寧澤桦锄,帶...
    沈念sama閱讀 33,712評論 4 323
  • 正文 年R本政府宣布扎附,位于F島的核電站,受9級特大地震影響结耀,放射性物質發(fā)生泄漏留夜。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 39,261評論 3 307
  • 文/蒙蒙 一图甜、第九天 我趴在偏房一處隱蔽的房頂上張望碍粥。 院中可真熱鬧,春花似錦黑毅、人聲如沸嚼摩。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,264評論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽枕面。三九已至,卻和暖如春缚去,著一層夾襖步出監(jiān)牢的瞬間潮秘,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 31,486評論 1 262
  • 我被黑心中介騙來泰國打工易结, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留枕荞,地道東北人稠通。 一個月前我還...
    沈念sama閱讀 45,511評論 2 354
  • 正文 我出身青樓旁理,卻偏偏與公主長得像奄喂,于是被迫代替她去往敵國和親膨蛮。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 42,802評論 2 345

推薦閱讀更多精彩內容