update: 2011/11/03
reference: Beginning iCloud in iOS 5 Tutorial Part 1 | Ray Wenderlich
註: iOS simulator 目前沒有 support iCloud, 因此需要二台安裝 iOS 5
的 iOS devices 來做測試, 並且記得啓用 iCloud 功能.
A. Enabling iCloud in your App
1. Create an iCloud-enabled App ID
a. 到 iOS Developer Center > Member Center (登入) > iOS Provisioning Portal
> App IDs > New App ID
填入:
Description: My iCloud Dox
Bundle Identifier (App ID Suffix): com.blogspot.MyiCloudDox
> submit
b. 手動啟用 iCloud:
(1). 按下: Configure
---------------------------------------------------------------------------------------------
a. iOS Provisioning Portal > Provisioning > New Profile
Profile Name: My iCloud Dox Profile
Certificates: Lanli Chen
App ID: My iCloud Dox
Devices: Lanli's iPad2, Lanli's iPod 3rd
便會安裝到 Xcode 裡.
3. Configure your Xcode project for iCloud
a. 建立新專案:
Xcode > File > New > New Project
> iOS > Application > Single View Application > Next
Product Name: MyiCloudDox
Company Identifier: com.blogspot
Device Family: Universal
只勾選: Use Automatic Reference Counting
> Next > 選擇儲存的位置 > Create
c. 勾選: Enable Entitlements
(1). Entitlements File: 指向一個屬性清單的檔案, 該檔案包含了應用程式
的權利明細.
(2). iCloud Key-Value Store: 代表一個唯一的 ID 指向 iCloud key-value
儲存處.
(3). iCloud Containers: 代表你的應用程式可以在 Cloud 裡讀寫文件的目錄.
說明: 當多個應用程式由同一個 Developer Center 登入帳號所建立
(the same team), 它們可以共同管理 user 的 iCloud Container .
(4). Keychain Access Groups: 應用程式分享 keychain data 時,
所需要的 keys.
B. Checking for iCloud Availability
1. 開啟 AppDelegate.m 檔案, 修改如下: (before the return YES)
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
......
//@add: check if iCloud is available
NSURL *ubiq = [[NSFileManager defaultManager] URLForUbiquityContainerIdentifier:nil];
if (ubiq) {
NSLog(@"iCloud access at %@", ubiq);
// TODO: Load document...
} else {
NSLog(@"No iCloud access");
}
return YES;
}
2. 說明:
a. ubiquity[juˋbɪkwətɪ] : 到處存在, 無所不在, 普遍存在
b. URLForUbiquityContainerIdentifier:
(1). 這個 method 允許傳入一個 container identifier, 並會回傳一個可在
iCloud storage 存取檔案的 URL.
(2). 對於每個要讓應用程式有權限存取其 URL 的 container, 都必需在一開始時
呼叫此 method.
(3). 如果傳入 nil (就像目前的狀況), 會自動回傳專案第一個設置的
iCloud Container
c. 在 iDevice 編譯並執行, 可以在 console 看到類似以下的訊息:
d. 承上, 回傳的 URL 實際上是 iDevice 上的 local URL, 這是因為
iCloud daemon 從 central servers 轉換檔案到 iDevice 的 local 目錄.
因此應用程式就能夠從這個目錄取得檔案, 或發送更新的版本;
iCloud daemon會去同步資料.
C. Subclassing UIDocument
1. Create a new file:
iOS > Cocoa Touch > Objective-C class > Next >
Class: Note
Subclass of: UIDocument
Next > Create
---------------------------------------------------------------------------------------------
2. 修改 Note.h 如下:
#import <UIKit/UIKit.h>
@interface Note : UIDocument
//@add
@property (strong) NSString * noteContent;
@end
---------------------------------------------------------------------------------------------
3. 修改 Note.m 如下: (two override method)
#import "Note.h"
@implementation Note
//@add
@synthesize noteContent;
//@add
// Called whenever the application reads data from the file system
- (BOOL)loadFromContents:(id)contents ofType:(NSString *)typeName
error:(NSError **)outError
{
if ([contents length] > 0) {
self.noteContent = [[NSString alloc]
initWithBytes:[contents bytes]
length:[contents length]
encoding:NSUTF8StringEncoding];
} else {
// When the note is first created, assign some default content
self.noteContent = @"Empty";
}
return YES;
}
//@add
// Called whenever the application (auto)saves the content of a note
- (id)contentsForType:(NSString *)typeName error:(NSError **)outError
{
if ([self.noteContent length] == 0) {
self.noteContent = @"Empty";
}
return [NSData dataWithBytes:[self.noteContent UTF8String]
length:[self.noteContent length]];
}
@end
說明:
a. When we load a file we need a procedure to "transform" the NSData contents
returned by the background queue into a string.
b. When we save we have to encode our string into an NSData object.
1. 修改 AppDelegate.h 如下:
//@add
#import "Note.h"
//@add
@property (strong) Note *doc;
@property (strong) NSMetadataQuery *query;
//@add
- (void)loadDocument;
---------------------------------------------------------------------------------------------
2. 修改 AppDelegate.m 如下:
//@add
#define kFILENAME @"mydocument.dox"
....//@add
@synthesize doc = _doc;
@synthesize query = _query;
....
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
....
//@add: check if iCloud is available
NSURL *ubiq = [[NSFileManager defaultManager] URLForUbiquityContainerIdentifier:nil];
if (ubiq) {
NSLog(@"iCloud access at %@", ubiq);
// TODO: Load document...
[self loadDocument];
} else {
NSLog(@"No iCloud access");
}
....
}
//@add
- (void)loadDocument {
// step01: NSMetadataQuery: represent results of a query related to the properties of an object
NSMetadataQuery *query = [[NSMetadataQuery alloc] init];
_query = query;
// step02: specify parameters and scope
[query setSearchScopes:[NSArray arrayWithObject: NSMetadataQueryUbiquitousDocumentsScope]];
// step03: build a predicate and set it as parameter of a query/search
// p.s.: 1. FS: file system
// 2. %K: for keypaths used, to avoid wrapping it in quotes.
NSPredicate *pred = [NSPredicate predicateWithFormat: @"%K == %@", NSMetadataItemFSNameKey, kFILENAME]; [query setPredicate:pred];
// step04: it is an asynchronous process we need to set up an observer
// to catch a notification when it completes.
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(queryDidFinishGathering:) name:NSMetadataQueryDidFinishGatheringNotification object:query];
[query startQuery];
}
---------------------------------------------------------------------------------------------
//@add
// add this above queryDidFinishGathering
- (void)loadData:(NSMetadataQuery *)query {
// a NSMetadataQuery wraps an array of NSMetadataItems which contain the results.
if ([query resultCount] == 1) {
NSMetadataItem *item = [query resultAtIndex:0];
// NSMetadataItemURLKey, which points to the URL that we need to build our Note instance.
NSURL *url = [item valueForAttribute:NSMetadataItemURLKey];
// When you create a UIDocument (or a subclass of UIDocument like Note),
// you always have to use the initWithFileURL initializer and give it
// the URL of the document to open.
Note *doc = [[Note alloc] initWithFileURL:url];
// store it away in an instance variable.
self.doc = doc;
// open a document
[self.doc openWithCompletionHandler:^(BOOL success) {
if (success) {
NSLog(@"iCloud document opened");
} else {
NSLog(@"failed opening document from iCloud");
}
}];
}
// When the query returns zero results
else {
// Retrieve the local iCloud directory
NSURL *ubiq = [[NSFileManager defaultManager]
URLForUbiquityContainerIdentifier:nil];
// Initialize an instance of document in that directory
NSURL *ubiquitousPackage = [[ubiq URLByAppendingPathComponent:
@"Documents"] URLByAppendingPathComponent:kFILENAME];
Note *doc = [[Note alloc] initWithFileURL:ubiquitousPackage];
self.doc = doc;
// Call the saveToURL method
[doc saveToURL:[doc fileURL]
forSaveOperation:UIDocumentSaveForCreating
completionHandler:^(BOOL success) {
if (success) {
[doc openWithCompletionHandler:^(BOOL success) {
NSLog(@"new document opened from iCloud");
}];
}
}];
} // end of else
}
---------------------------------------------------------------------------------------------
//@add
- (void)queryDidFinishGathering:(NSNotification *)notification {
// once you run a query, if you don’t stop it
// it runs forever or until you quit the application.
NSMetadataQuery *query = [notification object];
// prevents live updates
[query disableUpdates];
// allows you to stop a process without deleting already collected results.
[query stopQuery];
// remove ourselves as an observer to ignore further notifications
[[NSNotificationCenter defaultCenter] removeObserver:self
name:NSMetadataQueryDidFinishGatheringNotification
object:query];
_query = nil;
// finally call a method to load the document, passing the NSMetadataQuery as a parameter.
[self loadData:query];
}
E. 測試
1. 在第一台 iDevice 執行第一次, 於 Debug area 會出現: 2011-11-03 10:11:21.736 MyiCloudDox[335:707] new document opened from iCloud
2. 在第一台 iDevice 執行第二次, 於 Debug area 會出現:
2011-11-03 10:13:41.214 MyiCloudDox[361:707] iCloud document opened
---------------------------------------------------------------------------------------------
3. 測試第二台 iDevice:
a. 先將 AppDelegate.m 中 loadData: method 裡以下部分 mark 起來, 再編譯執行.
- (void)loadData:(NSMetadataQuery *)query {
.... // When the query returns zero results
/*
else {
}
*/
....
}
b. 在第二台 iDevice 編譯執行, 於 Debug area 會出現:
2011-11-03 11:45:41.029 MyiCloudDox[271:707] iCloud document opened
沒有留言:
張貼留言
注意:只有此網誌的成員可以留言。