En tant que débutant, j'ai du mal avec iCloud. Il existe quelques exemples, mais ils sont généralement assez détaillés (sur le forum des développeurs, il y en a un pour iCloud et CoreData qui est énorme). Les documents Apple sont OK, mais je ne peux toujours pas voir la situation dans son ensemble. Je vous en prie, soyez patient, certaines de ces questions sont assez fondamentales, mais peut-être faciles à répondre.
Contexte: J'ai une application iCloud très simple en cours d'exécution (exemple de code complet ci-dessous). Il n'y a qu'un seul UITextView montré à l'utilisateur et son entrée est enregistrée dans un fichier appelé text.txt.
Le fichier txt est poussé vers le cloud et mis à la disposition de tous les appareils. Fonctionne parfaitement, mais:
Problème principal: qu'en est-il des utilisateurs qui n'utilisent pas iCloud?
Lorsque je lance mon application (voir le code ci-dessous), je vérifie si l'utilisateur a activé iCloud. Si iCloud est activé, tout va bien. L'application continue et recherche text.txt dans le cloud. S'il est trouvé, il le chargera et l'affichera à l'utilisateur. Si text.txt n'est pas trouvé dans le cloud, il créera simplement un nouveau text.txt et l'affichera à l'utilisateur.
Si l'utilisateur n'a pas activé iCloud, rien ne se passera. Comment faire en sorte que les utilisateurs non-iCloud puissent toujours travailler avec mon application de texte? Ou est-ce que je les ignore simplement? Aurais-je besoin d'écrire des fonctions distinctes pour les utilisateurs non-iCloud? Ie fonctions dans lesquelles je charge simplement un text.txt à partir du dossier documents?
Traitez les fichiers dans iCloud de la même manière que vous traitez tous les autres fichiers dans le bac à sable de votre application.
Cependant, dans mon cas, il n'y a plus de sandbox d'application `` normale ''. C'est dans le cloud. Ou dois-je toujours charger d'abord mon text.txt à partir du disque, puis vérifier avec iCloud s'il y a quelque chose de plus à jour?
Problème connexe: structure de fichiers - Sandbox vs Cloud
Peut-être que mon problème principal est un malentendu fondamental sur la façon dont iCloud est censé fonctionner. Lorsque je crée une nouvelle instance d'un UIDocument, je vais devoir remplacer deux méthodes. D'abord - (BOOL)loadFromContents:(id)contents ofType:(NSString *)typeName error:(NSError **)outError
pour obtenir des fichiers du cloud, puis -(id)contentsForType:(NSString *)typeName error:(NSError **)outError
pour les transférer dans le cloud.
Dois-je incorporer des fonctions séparées qui enregistreront également une copie locale de text.txt dans mon bac à sable? Cela fonctionnera-t-il pour les utilisateurs non-iCloud? Si je comprends bien iCloud, il enregistrera automatiquement une copie locale de text.txt. Il ne devrait donc pas être nécessaire que je sauvegarde quoi que ce soit dans le `` vieux '' bac à sable de mon application (c'est-à-dire comme c'était le cas dans l'ancien temps avant iCloud). À l'heure actuelle, mon bac à sable est totalement vide, mais je ne sais pas si c'est correct. Dois-je conserver une autre copie de text.txt là-dedans? Cela me donne l'impression d'encombrer ma structure de données ... car il y a un text.txt dans le cloud, un dans le bac à sable iCloud sur mon appareil (qui fonctionnera même si je suis hors ligne) et un troisième dans le bon vieux bac à sable de mon appli ...
MON CODE: Un exemple de code iCloud simple
Ceci est vaguement basé sur un exemple que j'ai trouvé dans le forum des développeurs et sur la vidéo de la session WWDC. Je l'ai dépouillé au strict minimum. Je ne suis pas sûr que ma structure MVC soit bonne. Le modèle est dans l'AppDelegate, ce qui n'est pas idéal. Toutes les suggestions pour l'améliorer sont les bienvenues.
EDIT: J'ai essayé d'extraire la question principale et l'ai postée [ici]. 4
APERÇU:
Le bit le plus important qui charge le text.txt depuis le cloud:
// 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
Le 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
LE 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];
}