web-dev-qa-db-fra.com

Comment utiliser iCloud pour stocker et synchroniser des fichiers d'application

J'ai déjà une application iPhone qui stocke les données dans un fichier dans le dossier des documents locaux. Maintenant, j'ai appris sur les technologies iCloud et ma première question était: existe-t-il un moyen d'utiliser iCloud comme répertoire lorsque parfois je recherche de nouvelles versions?

Je veux dire: puis-je éviter d'utiliser UIDocument, les coordinateurs de fichiers et les présentateurs de fichiers? Je veux juste savoir si je peux traiter iCloud comme un dossier spécial et utiliser uniquement NSFileManager pour pousser et récupérer des fichiers.

Dernière remarque: je n'utilise pas Core Data ni aucune base de données, je n'ai qu'un fichier de données.

Modifier:

J'ai déjà lu la documentation officielle Apple iCloud donc ne me liez pas à eux. J'ai seulement besoin de quelques exemples de code.

33
edo42

Je sais ce que vous ressentez, iCloud est un peu intimidant. Cependant, je pense qu'il n'y a aucun moyen de contourner UIDocument, les coordinateurs de fichiers, etc. et d'utiliser simplement iCloud comme un simple dossier.

Si vous recherchez un exemple de code facile à comprendre, veuillez consulter cet article:

bases iCloud et exemple de code

J'ai inclus un exemple complet de code qui couvre le strict minimum d'iCloud et l'utilise à peu près comme un répertoire. Peut-être que cela vous rend moins intimidant d'utiliser UIDocument, les coordinateurs de fichiers, etc.

Mais, comme vous, je souhaite qu'il y ait une manière plus simple et plus compatible avec la bonne vieille idée de dossier documentaire. Cependant, comme il s'agit d'iCloud et qu'iCloud fait plusieurs choses de plus (comme garder tout synchronisé sur différents appareils, mettre à jour constamment vers le cloud, etc.), il n'y aura aucun moyen de contourner UIDocument, etc.

16
n.evermind

Ce qui "fonctionne" pour moi est simple:

NSFileManager *fm = [NSFileManager defaultManager];

NSURL *ubiq = [[NSFileManager defaultManager] URLForUbiquityContainerIdentifier:nil];

if (ubiq == nil) {
    return NO;
}

NSError *theError = nil;

[fm setUbiquitous:true itemAtURL:backupUrl destinationURL:[[ubiq URLByAppendingPathComponent:@"Documents" isDirectory:true] URLByAppendingPathComponent:backupName] error:&theError];

Apple dit d'appeler sur le thread non-UI. Faire "déplacer" les fichiers. Vous pouvez les interroger via NSMetaDataQuerycomme ceci:

self.query = [[NSMetadataQuery alloc] init];
[self.query setSearchScopes:[NSArray arrayWithObject:NSMetadataQueryUbiquitousDocumentsScope]];
NSPredicate *pred = [NSPredicate predicateWithFormat: @"%K like '*.db'", NSMetadataItemFSNameKey];
[self.query setPredicate:pred];
[[NSNotificationCenter defaultCenter] addObserver:self 
                                         selector:@selector(queryDidFinishGathering:) 
                                             name:NSMetadataQueryDidFinishGatheringNotification 
                                           object:self.query];

[self.query startQuery];

- (void)queryDidFinishGathering:(NSNotification *)notification {
    NSMetadataQuery *query = [notification object];
    [query disableUpdates];
    [query stopQuery];

    [self loadData:query];

    [[NSNotificationCenter defaultCenter] removeObserver:self name:NSMetadataQueryDidFinishGatheringNotification object:query];

    self.query = nil; 
}

Exemple d'énumération à travers les résultats de la requête:

- (void)loadData:(NSMetadataQuery *)query {
    [self.backups removeAllObjects];

    for (NSMetadataItem *item in [query results]) {
        NSURL *url = [item valueForAttribute:NSMetadataItemURLKey];
        [self.backups addObject:url.lastPathComponent];
    }

    [_table reloadData];

    [self.loadingBackupIndicator stopAnimating];
    self.loadingIndicatorLabel.text = [NSString stringWithFormat: @"%d backups found", [self.backups count]];
}

Et pour lancer le "téléchargement" du fichier concret:

NSFileManager *fm = [NSFileManager defaultManager];

NSURL *ubiq = [[NSFileManager defaultManager] URLForUbiquityContainerIdentifier:nil];

if (ubiq == nil) {
    return NO;
}

NSError *theError = nil;

bool started = [fm startDownloadingUbiquitousItemAtURL:[[ubiq URLByAppendingPathComponent:@"Documents" isDirectory:true] URLByAppendingPathComponent:backupName] error:&theError];

NSLog(@"started download for %@ %d", backupName, started);

if (theError != nil) {
    NSLog(@"iCloud error: %@", [theError localizedDescription]);
}

Avec les vérifications du fichier "en cours de téléchargement":

- (BOOL)downloadFileIfNotAvailable {
    NSNumber *isIniCloud = nil;

    NSURL *ubiq = [[NSFileManager defaultManager] URLForUbiquityContainerIdentifier:nil];

    NSURL *file = [[ubiq URLByAppendingPathComponent:@"Documents" isDirectory:true] URLByAppendingPathComponent:self.backupName];

    if ([file getResourceValue:&isIniCloud forKey:NSURLIsUbiquitousItemKey error:nil]) {
        // If the item is in iCloud, see if it is downloaded.
        if ([isIniCloud boolValue]) {
            NSNumber*  isDownloaded = nil;
            if ([file getResourceValue:&isDownloaded forKey:NSURLUbiquitousItemIsDownloadedKey error:nil]) {
                if ([isDownloaded boolValue]) {
                    [self.loadingBackupIndicator stopAnimating];
                    self.loadingIndicatorLabel.text = @"Downloaded";

                    ....

                    [[NSFileManager defaultManager] copyItemAtPath:[file path] toPath:restorePath error:&theError ];

                    ....

                    return YES;
                }

                self.loadingCheckTimer = [NSTimer timerWithTimeInterval:3.0f target:self selector:@selector(downloadFileIfNotAvailable) userInfo:nil repeats:NO];
                [[NSRunLoop currentRunLoop] addTimer:self.loadingCheckTimer forMode:NSDefaultRunLoopMode];

                return NO;
            }
        }
    }

    return YES;
}

Je ne m'attendais pas à ce que le code soit aussi long et désolé de fournir des extraits très bruts ici. Aucune intention de dire ce qui précède ne peut être une qualité de production de code, juste partager le concept.

Je n'ai pas encore soumis cela dans mon application à Apple, donc je ne peux pas dire que ce serait "approuvé" sur l'App Store (s'ils trouvent ou se soucient ...)

25

Vous pouvez télécharger des fichiers individuels sur iCloud à l'aide de NSFileManager. J'ai publié un procédure pas à pas complète sur la façon de le faire sur mon blog, mais voici le code NSFileManager pertinent:

NSURL *destinationURL = [self.ubiquitousURL URLByAppendingPathComponent:@"Documents/image.jpg"]
[[NSFileManager defaultManager] setUbiquitous:YES 
                                    itemAtURL:sourceURL
                               destinationURL:destinationURL
                                        error:&error]
14
samvermette

Il n'y a pas vraiment de moyen d'utiliser UIDocument. J'ai essayé de le faire dans l'une de mes premières utilisations d'iCloud, mais il s'est avéré être un désastre sans UIDocument. Utiliser UIDocument au premier abord semble être un travail supplémentaire, mais ce n'est pas le cas.

Vous pouvez facilement sous-classer UIDocument en moins d'une heure et le faire fonctionner avec n'importe quel type de fichier (définissez simplement la propriété content comme NSData). Il offre également de nombreux avantages par rapport au système de fichiers standard:

  • Suivi des modifications
  • Résolution des conflits de fichiers
  • Prise en charge de l'état des documents
  • Fonctions de sauvegarde/ouverture/fermeture améliorées

Honnêtement, passer juste une heure ou deux à lire la documentation Apple et ensuite l'utiliser vaut bien le temps et la puissance du cerveau. Un bon article de démarrage sur le stockage de documents iCloud peut être trouvé dans Documentation développeur Apple .


J'ai écrit une sous-classe UIDocument qui fonctionnera avec tout type de fichier (NSData en particulier). Vous pouvez afficher, télécharger et modifier le code de la sous-classe UIDocument sur GitHub .

Créez le document:

// Initialize a document with a valid file path
iCloudDocument *document = [[iCloudDocument alloc] initWithFileURL:fileURL];

// Set the content of the document
document.contents = content;

// Increment the change count
[document updateChangeCount:UIDocumentChangeDone];

Enregistrez un document existant:

// Save and close the document
[document closeWithCompletionHandler:nil];

Enregistrez un nouveau document:

[document saveToURL:document.fileURL forSaveOperation:UIDocumentSaveForCreating completionHandler:nil];

Vous pouvez également synchroniser tous les fichiers stockés dans iCloud à l'aide de NSMetadataQuery. Apple fournit un très bel exemple d'utilisation de la requête NSMetadata pour synchroniser les fichiers d'application. Assurez-vous également de vérifier iCloud avant d'effectuer ces opérations (indice: utilisez la méthode ubiquityIdentityToken sur NSFileManager) .


Vous pouvez également envisager d'utiliser une bibliothèque open source telle que iCloud Document Sync . Le projet iCloud Document Sync facilite le stockage et la synchronisation des fichiers d'application:

Intégrez iCloud dans les projets de document iOS avec des méthodes de code à une ligne. Synchronisez, téléchargez, gérez et supprimez des documents d'iCloud rapidement et facilement. Aide à faire fonctionner iCloud "juste" pour les développeurs aussi.

Dans presque toutes les méthodes de synchronisation de documents iCloud, tout ce que vous avez à faire est de transmettre vos données de fichier en tant que paramètre, puis il gère le reste (enregistrement, synchronisation, etc.).

[~ # ~] Avis de non-responsabilité [~ # ~] : Je suis un développeur contributeur au projet open-source, iCloud Document Sync. Cependant, je crois que ce projet vous sera bénéfique et est pertinent pour cette question. Ce n'est pas une promotion ou une publicité.

6
Samuel Spencer