ในฐานะผู้เริ่มต้นฉันกำลังดิ้นรนกับ iCloud มีตัวอย่างอยู่บ้าง แต่โดยปกติแล้วจะมีรายละเอียดค่อนข้างมาก (ในฟอรัมนักพัฒนาจะมีหนึ่งสำหรับ iCloud และ CoreData ซึ่งมีขนาดใหญ่มาก) เอกสารแอปเปิ้ลจะ OK แต่ฉันยังคงไม่สามารถมองเห็นภาพใหญ่ ดังนั้นโปรดอดทนกับฉันคำถามเหล่านี้บางคำถามค่อนข้างพื้นฐาน แต่อาจตอบได้ง่าย
บริบท:ฉันมีแอพ iCloud ที่ใช้งานง่ายมาก (โค้ดตัวอย่างเต็มด้านล่าง) มี UITextView เพียงรายการเดียวที่แสดงต่อผู้ใช้และข้อมูลของเขา / เธอจะถูกบันทึกไว้ในไฟล์ที่เรียกว่า text.txt
ไฟล์ txt จะถูกพุชไปยังระบบคลาวด์และพร้อมใช้งานสำหรับทุกอุปกรณ์ ทำงานได้อย่างสมบูรณ์ แต่:
ปัญหาหลัก: แล้วผู้ใช้ที่ไม่ใช้ iCloud ล่ะ?
เมื่อฉันเปิดแอพ (ดูรหัสด้านล่าง) ฉันตรวจสอบว่าผู้ใช้เปิดใช้งาน iCloud หรือไม่ หากเปิดใช้งาน iCloud ทุกอย่างก็เรียบร้อยดี แอปไปข้างหน้าและค้นหา text.txt ในระบบคลาวด์ หากพบก็จะโหลดและแสดงให้ผู้ใช้เห็น หากไม่พบ text.txt ในระบบคลาวด์ระบบจะสร้าง text.txt ใหม่และจะแสดงให้ผู้ใช้เห็น
หากผู้ใช้ไม่ได้เปิดใช้งาน iCloud จะไม่มีอะไรเกิดขึ้น ฉันจะทำให้เป็นไปได้อย่างไรที่ผู้ใช้ที่ไม่ใช่ iCloud จะยังสามารถใช้งานแอพข้อความของฉันได้ หรือว่าฉันเฉยๆ? ฉันจะต้องเขียนฟังก์ชันแยกต่างหากสำหรับผู้ใช้ที่ไม่ใช่ iCloud หรือไม่? ได้แก่ ฟังก์ชันที่ฉันเพียงแค่โหลด text.txt จากโฟลเดอร์เอกสาร?
รักษาไฟล์ใน iCloud แบบเดียวกับที่คุณใช้กับไฟล์อื่น ๆ ทั้งหมดในแซนด์บ็อกซ์ของแอพ
อย่างไรก็ตามในกรณีของฉันไม่มีแซนด์บ็อกซ์ของแอป 'ปกติ' อีกต่อไป มันอยู่ในระบบคลาวด์ หรือฉันมักจะโหลด text.txt จากดิสก์ก่อนจากนั้นตรวจสอบกับ iCloud ว่ามีอะไรที่ทันสมัยกว่านี้หรือไม่?
ปัญหาที่เกี่ยวข้อง: โครงสร้างไฟล์ - แซนด์บ็อกซ์กับคลาวด์
บางทีปัญหาหลักของฉันอาจเป็นความเข้าใจผิดพื้นฐานว่า iCloud ควรจะทำงานอย่างไร เมื่อฉันสร้างอินสแตนซ์ใหม่ของ UIDocument ฉันจะต้องเขียนทับสองวิธี ขั้นแรก- (BOOL)loadFromContents:(id)contents ofType:(NSString *)typeName error:(NSError **)outError
ให้รับไฟล์จากระบบคลาวด์จากนั้นจึง-(id)contentsForType:(NSString *)typeName error:(NSError **)outError
จะรับไฟล์เข้าสู่ระบบคลาวด์
ฉันต้องรวมฟังก์ชันแยกต่างหากซึ่งจะบันทึกสำเนา text.txt ในเครื่องลงในแซนด์บ็อกซ์ของฉันด้วยหรือไม่ สิ่งนี้จะใช้ได้กับผู้ใช้ที่ไม่ใช่ iCloud หรือไม่ ตามที่ฉันเข้าใจ iCloud มันจะบันทึกสำเนา text.txt ในเครื่องโดยอัตโนมัติ ดังนั้นฉันจึงไม่จำเป็นต้องบันทึกอะไรลงในแซนด์บ็อกซ์ 'เก่า' ของแอพของฉัน (เช่นที่เคยเป็นในยุคก่อน iCloud) ตอนนี้แซนด์บ็อกซ์ของฉันว่างเปล่าทั้งหมด แต่ฉันไม่รู้ว่าถูกต้องหรือไม่ ฉันควรเก็บ text.txt อีกสำเนาไว้ที่นั่นหรือไม่ สิ่งนี้รู้สึกเหมือนทำให้โครงสร้างข้อมูลของฉันยุ่งเหยิง ... เนื่องจากมี text.txt หนึ่งรายการในคลาวด์หนึ่งรายการในแซนด์บ็อกซ์ iCloud บนอุปกรณ์ของฉัน (ซึ่งจะใช้งานได้แม้ว่าฉันจะออฟไลน์) และอีกอันที่สามในแซนด์บ็อกซ์เก่าที่ดีของ แอพของฉัน ...
รหัสของฉัน: โค้ดตัวอย่างง่ายๆของ iCloud
นี่เป็นไปตามตัวอย่างที่ฉันพบในฟอรัมนักพัฒนาและในวิดีโอเซสชัน WWDC ฉันถอดมันลงจนเหลือน้อยที่สุด ฉันไม่แน่ใจว่าโครงสร้าง MVC ของฉันดีหรือไม่ โมเดลอยู่ใน AppDelegate ซึ่งไม่เหมาะ ข้อเสนอแนะใด ๆ ที่จะทำให้ดีขึ้นยินดีต้อนรับ
แก้ไข: ฉันพยายามแยกคำถามหลักและโพสต์ไว้ [ที่นี่] 4
ภาพรวม:
บิตที่สำคัญที่สุดที่โหลด text.txt จากคลาวด์:
// AppDelegate.h
// iCloudText
#import <UIKit/UIKit.h>
@class ViewController;
@class MyTextDocument;
@interface AppDelegate : UIResponder <UIApplicationDelegate> {
NSMetadataQuery *_query;
}
@property (strong, nonatomic) UIWindow *window;
@property (strong, nonatomic) ViewController *viewController;
@property (strong, nonatomic) MyTextDocument *document;
@end
// AppDelegate.m
// iCloudText
#import "AppDelegate.h"
#import "MyTextDocument.h"
#import "ViewController.h"
@implementation AppDelegate
@synthesize window = _window;
@synthesize viewController = _viewController;
@synthesize document = _document;
- (void)dealloc
{
[_window release];
[_viewController release];
[super dealloc];
}
- (void)loadData:(NSMetadataQuery *)query {
// (4) iCloud: the heart of the load mechanism: if texts was found, open it and put it into _document; if not create it an then put it into _document
if ([query resultCount] == 1) {
// found the file in iCloud
NSMetadataItem *item = [query resultAtIndex:0];
NSURL *url = [item valueForAttribute:NSMetadataItemURLKey];
MyTextDocument *doc = [[MyTextDocument alloc] initWithFileURL:url];
//_document = doc;
doc.delegate = self.viewController;
self.viewController.document = doc;
[doc openWithCompletionHandler:^(BOOL success) {
if (success) {
NSLog(@"AppDelegate: existing document opened from iCloud");
} else {
NSLog(@"AppDelegate: existing document failed to open from iCloud");
}
}];
} else {
// Nothing in iCloud: create a container for file and give it URL
NSLog(@"AppDelegate: ocument not found in iCloud.");
NSURL *ubiq = [[NSFileManager defaultManager] URLForUbiquityContainerIdentifier:nil];
NSURL *ubiquitousPackage = [[ubiq URLByAppendingPathComponent:@"Documents"] URLByAppendingPathComponent:@"text.txt"];
MyTextDocument *doc = [[MyTextDocument alloc] initWithFileURL:ubiquitousPackage];
//_document = doc;
doc.delegate = self.viewController;
self.viewController.document = doc;
[doc saveToURL:[doc fileURL] forSaveOperation:UIDocumentSaveForCreating completionHandler:^(BOOL success) {
NSLog(@"AppDelegate: new document save to iCloud");
[doc openWithCompletionHandler:^(BOOL success) {
NSLog(@"AppDelegate: new document opened from iCloud");
}];
}];
}
}
- (void)queryDidFinishGathering:(NSNotification *)notification {
// (3) if Query is finished, this will send the result (i.e. either it found our text.dat or it didn't) to the next function
NSMetadataQuery *query = [notification object];
[query disableUpdates];
[query stopQuery];
[self loadData:query];
[[NSNotificationCenter defaultCenter] removeObserver:self name:NSMetadataQueryDidFinishGatheringNotification object:query];
_query = nil; // we're done with it
}
-(void)loadDocument {
// (2) iCloud query: Looks if there exists a file called text.txt in the cloud
NSMetadataQuery *query = [[NSMetadataQuery alloc] init];
_query = query;
//SCOPE
[query setSearchScopes:[NSArray arrayWithObject:NSMetadataQueryUbiquitousDocumentsScope]];
//PREDICATE
NSPredicate *pred = [NSPredicate predicateWithFormat: @"%K == %@", NSMetadataItemFSNameKey, @"text.txt"];
[query setPredicate:pred];
//FINISHED?
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(queryDidFinishGathering:) name:NSMetadataQueryDidFinishGatheringNotification object:query];
[query startQuery];
}
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
NSLog(@"AppDelegate: app did finish launching");
self.window = [[[UIWindow alloc] initWithFrame:[[UIScreen mainScreen] bounds]] autorelease];
// Override point for customization after application launch.
if ([[UIDevice currentDevice] userInterfaceIdiom] == UIUserInterfaceIdiomPhone) {
self.viewController = [[[ViewController alloc] initWithNibName:@"ViewController_iPhone" bundle:nil] autorelease];
} else {
self.viewController = [[[ViewController alloc] initWithNibName:@"ViewController_iPad" bundle:nil] autorelease];
}
self.window.rootViewController = self.viewController;
[self.window makeKeyAndVisible];
// (1) iCloud: init
NSURL *ubiq = [[NSFileManager defaultManager] URLForUbiquityContainerIdentifier:nil];
if (ubiq) {
NSLog(@"AppDelegate: iCloud access!");
[self loadDocument];
} else {
NSLog(@"AppDelegate: No iCloud access (either you are using simulator or, if you are on your phone, you should check settings");
}
return YES;
}
@end
เอกสาร UID
// MyTextDocument.h
// iCloudText
#import <Foundation/Foundation.h>
#import "ViewController.h"
@interface MyTextDocument : UIDocument {
NSString *documentText;
id delegate;
}
@property (nonatomic, retain) NSString *documentText;
@property (nonatomic, assign) id delegate;
@end
// MyTextDocument.m
// iCloudText
#import "MyTextDocument.h"
#import "ViewController.h"
@implementation MyTextDocument
@synthesize documentText = _text;
@synthesize delegate = _delegate;
// ** READING **
- (BOOL)loadFromContents:(id)contents ofType:(NSString *)typeName error:(NSError **)outError
{
NSLog(@"UIDocument: loadFromContents: state = %d, typeName=%@", self.documentState, typeName);
if ([contents length] > 0) {
self.documentText = [[NSString alloc] initWithBytes:[contents bytes] length:[contents length] encoding:NSUTF8StringEncoding];
}
else {
self.documentText = @"";
}
NSLog(@"UIDocument: Loaded the following text from the cloud: %@", self.documentText);
// update textView in delegate...
if ([_delegate respondsToSelector:@selector(noteDocumentContentsUpdated:)]) {
[_delegate noteDocumentContentsUpdated:self];
}
return YES;
}
// ** WRITING **
-(id)contentsForType:(NSString *)typeName error:(NSError **)outError
{
if ([self.documentText length] == 0) {
self.documentText = @"New Note";
}
NSLog(@"UIDocument: Will save the following text in the cloud: %@", self.documentText);
return [NSData dataWithBytes:[self.documentText UTF8String] length:[self.documentText length]];
}
@end
VIEWCONTROLLER
//
// ViewController.h
// iCloudText
#import <UIKit/UIKit.h>
@class MyTextDocument;
@interface ViewController : UIViewController <UITextViewDelegate> {
IBOutlet UITextView *textView;
}
@property (nonatomic, retain) UITextView *textView;
@property (strong, nonatomic) MyTextDocument *document;
-(void)noteDocumentContentsUpdated:(MyTextDocument *)noteDocument;
@end
// ViewController.m
// iCloudText
#import "ViewController.h"
#import "MyTextDocument.h"
@implementation ViewController
@synthesize textView = _textView;
@synthesize document = _document;
-(IBAction)dismissKeyboard:(id)sender {
[_textView resignFirstResponder];
}
-(void)noteDocumentContentsUpdated:(MyTextDocument *)noteDocument
{
NSLog(@"VC: noteDocumentsUpdated");
_textView.text = noteDocument.documentText;
}
-(void)textViewDidChange:(UITextView *)theTextView {
NSLog(@"VC: textViewDidChange");
_document.documentText = theTextView.text;
[_document updateChangeCount:UIDocumentChangeDone];
}