Yeni başlayan biri olarak iCloud ile mücadele ediyorum. Bazı örnekler var, ancak genellikle oldukça ayrıntılılar (geliştirici forumunda büyük olan iCloud ve CoreData için bir tane var). Elma dokümanlar Tamam, ama hala büyük resmi göremez. Bu yüzden lütfen bana katlanın, bu sorulardan bazıları oldukça temel, ancak muhtemelen cevaplaması kolay.
Bağlam: Çalışan çok basit bir iCloud uygulamam var (tam örnek kod aşağıdadır). Kullanıcıya gösterilen sadece bir UITextView vardır ve onun girdisi text.txt adlı bir dosyaya kaydedilir.
Txt dosyası buluta gönderilir ve tüm cihazların kullanımına sunulur. Mükemmel çalışıyor, ancak:
Ana sorun: iCloud kullanmayan kullanıcılar ne olacak?
Uygulamamı başlattığımda (aşağıdaki koda bakın), kullanıcının iCloud'u etkinleştirip etkinleştirmediğini kontrol ediyorum. İCloud etkinse, her şey yolunda. Uygulama devam eder ve buluttaki text.txt dosyasını arar. Bulunursa, yükleyecek ve kullanıcıya gösterecektir. Metin.txt bulutta bulunamazsa, basitçe yeni bir text.txt oluşturacak ve bunu kullanıcıya gösterecektir.
Kullanıcının iCloud'u etkin değilse hiçbir şey olmayacaktır. İCloud kullanmayan kullanıcıların metin uygulamamla çalışmaya devam etmesini nasıl sağlayacağım? Yoksa onları görmezden mi gelirim? İCloud olmayan kullanıcılar için ayrı işlevler yazmam gerekir mi? Yani, belgeler klasöründen bir text.txt yüklediğim işlevler?
İCloud'daki dosyalara, uygulamanızın sanal alanındaki diğer tüm dosyalara yaptığınız gibi davranın.
Ancak benim durumumda artık 'normal' uygulama sanal alanı yok. Bulutta. Veya her zaman önce text.txt'mi diskten yükleyip ardından daha güncel bir şey olup olmadığını iCloud ile kontrol etmeliyim?
İlgili sorun: Dosya yapısı - Sandbox vs. Cloud
Belki de asıl sorunum, iCloud'un nasıl çalışacağına dair temel bir yanlış anlaşılmadır. Bir UIDocument'in yeni bir örneğini oluşturduğumda, iki yöntemin üzerine yazmam gerekecek. Önce - (BOOL)loadFromContents:(id)contents ofType:(NSString *)typeName error:(NSError **)outError
buluttan dosya almak ve ardından -(id)contentsForType:(NSString *)typeName error:(NSError **)outError
dosyaları buluta almak.
Text.txt dosyasının yerel bir kopyasını korumalı alanıma kaydedecek ayrı işlevler eklemem gerekir mi? Bu, iCloud kullanmayan kullanıcılar için çalışacak mı? İCloud'u anladığım kadarıyla, text.txt'nin yerel bir kopyasını otomatik olarak kaydedecek. Bu nedenle, uygulamamın 'eski' sanal alanına herhangi bir şey kaydetmeme gerek olmamalı (yani, eskiden, iCloud öncesi günlerde olduğu gibi). Şu anda korumalı alanım tamamen boş, ancak bunun doğru olup olmadığını bilmiyorum. Orada başka bir text.txt kopyası saklamalı mıyım? Bu, veri yapımı karmaşık hale getiriyor gibi geliyor ... çünkü bulutta bir text.txt, aygıtımdaki iCloud korumalı alanında (çevrimdışı olsam bile çalışacak) ve üçüncüsü, eski güzel sanal alanda benim uygulamam ...
KODUM: Basit bir iCloud örnek kodu
Bu, genel olarak geliştirici forumunda ve WWDC oturum videosunda bulduğum bir örneğe dayanmaktadır. Asgari düzeye indirdim. MVC yapımın iyi olup olmadığından emin değilim. Model, ideal olmayan AppDelegate içindedir. Daha iyi hale getirmek için herhangi bir öneri bekliyoruz.
DÜZENLEME: Ana soruyu çıkarmaya çalıştım ve [burada] yayınladım. 4
GENEL BAKIŞ:
Text.txt dosyasını buluttan yükleyen en önemli bit:
// 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
GÖRÜNÜM DENETLEYİCİSİ
//
// 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];
}