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). Le site documents sur la pomme sont OK, mais je n'arrive toujours pas à voir la vue d'ensemble. Je vous prie donc d'être indulgent avec moi, certaines de ces questions sont assez fondamentales, mais peut-être faciles à répondre.
Le 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'une seule UITextView affichée à l'utilisateur et sa saisie est enregistrée dans un fichier appelé text.txt.
Le fichier txt est poussé vers le nuage et mis à la disposition de tous les appareils. Cela fonctionne parfaitement, mais :
Principal problème : 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 va chercher le fichier text.txt dans le nuage. Si elle le trouve, elle le charge et l'affiche à l'utilisateur. Si le fichier text.txt n'est pas trouvé dans le nuage, elle crée simplement un nouveau fichier text.txt et l'affiche à l'utilisateur.
Si l'utilisateur n'a pas activé iCloud, rien ne se passera. Comment faire en sorte que les utilisateurs qui n'ont pas iCloud puissent quand même travailler avec mon application texte ? Ou dois-je simplement les ignorer ? Devrais-je écrire des fonctions distinctes pour les utilisateurs non-iCloud ? Par exemple, des fonctions dans lesquelles je charge simplement un text.txt depuis le dossier des documents ?
Traitez les fichiers dans iCloud de la même manière que tous les autres fichiers de votre bac à sable d'applications.
Cependant, dans mon cas, il n'y a plus de sandbox d'application "normale". Il est dans le nuage. Ou dois-je d'abord charger mon text.txt depuis le disque et ensuite vérifier avec iCloud s'il y a quelque chose de plus à jour ?
Problème connexe : Structure des fichiers - Sandbox vs. Cloud
Peut-être que mon principal problème est une incompréhension fondamentale de la façon dont iCloud est censé fonctionner. Lorsque je crée une nouvelle instance d'un UIDocument, je dois écraser deux méthodes. D'abord - (BOOL)loadFromContents:(id)contents ofType:(NSString *)typeName error:(NSError **)outError
pour récupérer des fichiers dans le nuage et ensuite -(id)contentsForType:(NSString *)typeName error:(NSError **)outError
pour mettre les fichiers dans le nuage.
Dois-je intégrer des fonctions distinctes 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 enregistre automatiquement une copie locale du fichier text.txt. Il ne devrait donc pas être nécessaire que j'enregistre quoi que ce soit dans l'"ancien" bac à sable de mon application (c'est-à-dire tel qu'il était à l'époque, avant iCloud). Actuellement, mon bac à sable est totalement vide, mais je ne sais pas si c'est correct. Devrais-je y conserver une autre copie de text.txt ? J'ai l'impression d'encombrer ma structure de données... car il y a un text.txt dans le nuage, un autre dans le bac à sable iCloud de mon appareil (qui fonctionnera même si je suis hors ligne), et un troisième dans le bon vieux bac à sable de mon application...
MON CODE : Un exemple simple de code iCloud
Ceci est librement 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 réduit 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. Toute suggestion pour l'améliorer est la bienvenue.
EDIT : J'ai essayé d'extraire la question principale et l'ai postée [ici]. 4
VUE D'ENSEMBLE :
La partie la plus importante qui charge le fichier text.txt depuis le nuage :
// 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 CONTRÔLEUR DE VUE
//
// 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];
}