Là một người mới bắt đầu, tôi đang gặp khó khăn với iCloud. Có một số mẫu, nhưng chúng thường khá chi tiết (trên diễn đàn nhà phát triển có một mẫu dành cho iCloud và CoreData rất lớn). Các tài liệu của apple thì OK, nhưng mình vẫn không thấy được hình ảnh lớn. Vì vậy, hãy nhớ với tôi, một số câu hỏi trong số này khá cơ bản, nhưng có thể dễ trả lời.
Bối cảnh: Tôi có một ứng dụng iCloud rất đơn giản đang chạy (mã mẫu đầy đủ bên dưới). Chỉ có một UITextView được hiển thị cho người dùng và thông tin đầu vào của họ được lưu trong một tệp có tên là text.txt.
Txt được đẩy lên đám mây và có sẵn cho tất cả các thiết bị. Hoạt động hoàn hảo, nhưng:
Vấn đề chính: Còn những người dùng không sử dụng iCloud thì sao?
Khi khởi chạy ứng dụng của mình (xem mã bên dưới), tôi kiểm tra xem người dùng đã bật iCloud chưa. Nếu iCloud được kích hoạt, mọi thứ đều ổn. Ứng dụng tiếp tục và tìm kiếm text.txt trên đám mây. Nếu được tìm thấy, nó sẽ tải nó và hiển thị cho người dùng. Nếu text.txt không được tìm thấy trong đám mây, nó sẽ chỉ cần tạo một text.txt mới và sẽ hiển thị điều đó cho người dùng.
Nếu người dùng chưa kích hoạt iCloud thì sẽ không có gì xảy ra. Tôi sẽ làm cách nào để người dùng không sử dụng iCloud vẫn có thể làm việc với ứng dụng văn bản của tôi? Hay tôi chỉ đơn giản là phớt lờ chúng? Tôi có cần viết các chức năng riêng biệt cho người dùng không sử dụng iCloud không? Tức là các chức năng trong đó tôi chỉ cần tải một text.txt từ thư mục tài liệu?
Xử lý các tệp trong iCloud giống như cách bạn xử lý tất cả các tệp khác trong hộp cát ứng dụng của mình.
Tuy nhiên, trong trường hợp của tôi, không có hộp cát ứng dụng 'bình thường' nữa. Nó ở trên đám mây. Hay tôi luôn tải text.txt của mình từ đĩa trước và sau đó kiểm tra với iCloud nếu có thứ gì đó cập nhật hơn?
Sự cố liên quan: Cấu trúc tệp - Sandbox vs. Cloud
Có lẽ vấn đề chính của tôi là sự hiểu lầm cơ bản về cách thức hoạt động của iCloud. Khi tôi tạo một phiên bản mới của UIDocument, tôi sẽ phải ghi đè hai phương thức. Đầu tiên - (BOOL)loadFromContents:(id)contents ofType:(NSString *)typeName error:(NSError **)outError
để tải tệp từ đám mây và sau đó -(id)contentsForType:(NSString *)typeName error:(NSError **)outError
là tải tệp vào đám mây.
Tôi có phải kết hợp các chức năng riêng biệt cũng sẽ lưu bản sao cục bộ của text.txt vào hộp cát của tôi không? Điều này sẽ hoạt động đối với người dùng không sử dụng iCloud? Theo tôi hiểu iCloud, nó sẽ tự động lưu một bản sao cục bộ của text.txt. Vì vậy, tôi không cần thiết phải lưu bất kỳ thứ gì vào hộp cát 'cũ' của ứng dụng của mình (tức là nó đã từng ở trong những ngày cũ, trước iCloud). Hiện tại, hộp cát của tôi hoàn toàn trống, nhưng tôi không biết điều này có chính xác không. Tôi có nên giữ một bản sao khác của text.txt trong đó không? Điều này giống như làm lộn xộn cấu trúc dữ liệu của tôi ... vì có một text.txt trên đám mây, một trong hộp cát iCloud trên thiết bị của tôi (sẽ hoạt động ngay cả khi tôi ngoại tuyến) và một tệp thứ ba trong hộp cát cũ tốt của ứng dụng của tôi...
MÃ CỦA TÔI: Một mã mẫu iCloud đơn giản
Điều này dựa trên một ví dụ tôi tìm thấy trong diễn đàn nhà phát triển và trên video phiên WWDC. Tôi tước nó xuống mức tối thiểu trần. Tôi không chắc rằng cấu trúc MVC của tôi là tốt. Mô hình trong AppDelegate không phải là lý tưởng. Mọi đề xuất để làm cho nó tốt hơn đều được hoan nghênh.
CHỈNH SỬA: Tôi đã cố gắng trích xuất câu hỏi chính và đăng nó [tại đây]. 4
TỔNG QUAT:
Bit quan trọng nhất tải text.txt từ đám mây:
// 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
UIDocument
// 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
NGƯỜI ĐĂNG KÝ XEM
//
// 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];
}