초보자로서 저는 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
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
뷰 컨트롤러
//
// 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];
}