Как использовать iCloud для хранения и синхронизации файлов приложений


У меня уже есть приложение для iPhone, которое хранит данные в файле в папке local documents. Теперь я узнал о технологиях iCloud и мой первый вопрос был: есть ли способ использовать iCloud в качестве каталога, когда иногда я проверяю наличие новых версий?

Я имею в виду: могу ли я избежать использования UIDocument, файловых координаторов и файловых докладчиков? Я просто хочу знать, можно ли рассматривать iCloud как специальную папку и использовать только NSFileManager для перемещения и извлечения файлов.

Последнее замечание: Я не использую основные данные или любая база данных, у меня есть только файл данных.

Редактировать:

Я уже прочитал официальную документацию Apple iCloud, поэтому не связывайте меня с ними. Мне нужны только некоторые примеры кода.

5 32

5 ответов:

Я знаю, как вы себя чувствуете, iCloud немного пугает. Однако я думаю, что нет никакого способа обойти UIDocument, файловые координаторы и т. д. и просто использовать iCloud как простую папку.

Если вы ищете простой для понимания пример кода, пожалуйста, посмотрите на этот пост:

Основы ICloud и пример кода

Я включил полный пример кода, который охватывает голые минимумы iCloud и в значительной степени использует его как каталог. Возможно, это делает его менее пугающим для вас. используйте UIDocument, файловые координаторы и т.д.

Но, как и вы, я хотел бы, чтобы был более простой и совместимый способ с идеей старой доброй папки для документов. Однако, поскольку это iCloud и поскольку iCloud делает еще несколько вещей (например, синхронизирует все на разных устройствах, постоянно обновляет облако и т. д.), не будет никакого способа обойти UIDocument и т. д.

То, что "работает" для меня, Просто:

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 говорит, чтобы вызвать поток, не связанный с пользовательским интерфейсом. Имея файлы "перемещены". Вы можете запросить их через NSMetaDataQueryследующим образом:

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; 
}

Пример перечисления по результатам запроса:

- (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]];
}

И начать "загрузку" конкретного файла:

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]);
}

С проверкой файла "загружается":

- (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;
}

Я не ожидал, что код будет таким длинным, и прошу прощения за предоставление очень сырых фрагментов здесь. Никакое намерение сказать выше не может будьте производственным качеством кода, просто разделяя концепцию.

Я еще не представил это внутри моего приложения в Apple, поэтому не могу сказать, что это будет" одобрено " в app store (если они найдут или заботятся...)

Вы можете загружать отдельные файлы в iCloud с помощью NSFileManager. Я опубликовал полное пошаговое руководство о том, как это сделать в моем блоге, но вот соответствующий код NSFileManager:

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

На самом деле нет способа обойти использование UIDocument. Я попытался сделать это в одном из моих первых применений iCloud, но это оказалось катастрофой без UIDocument. Использование UIDocument на первый взгляд кажется большой дополнительной работой, но это не так.

Вы можете легко подклассировать UIDocument менее чем за час и заставить его работать с любым типом файла (просто установите свойство content как NSData). Он также предоставляет множество преимуществ по сравнению со стандартной файловой системой:
  • отслеживание изменений
  • файл разрешение конфликтов
  • документ государственной поддержки
  • расширенные функции сохранения / открытия / закрытия
Честно говоря, потратить всего час или два на чтение документации Apple, а затем использовать ее, стоит того, чтобы потратить время и силы мозга. Хорошая стартовая статья о хранении документов iCloud может быть найдена в документации разработчика Apple .

Я написал подкласс UIDocument, который будет работать с любым типом файлов (в частности, с NSData). Вы можете посмотреть, загрузите и измените код подкласса UIDocument на GitHub.

Создайте документ:

// 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];

Сохранение существующего документа:

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

Сохранить новый документ:

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

Вы также можете синхронизировать все файлы, хранящиеся в iCloud с помощью NSMetadataQuery. Apple предоставляет очень хороший пример использования запроса NSMetadata для синхронизации файлов приложений. Также обязательно проверьте наличие iCloud перед выполнением этих операций (подсказка: используйте метод ubiquityIdentityToken на NSFileManager).


Вы также можете рассмотреть возможность использования библиотеки с открытым исходным кодом, такой как iCloud Document Sync. Проект документа iCloud синхронизация делает его очень легко хранить и синхронизировать файлы приложение:

Интегрируйте iCloud в проекты документов iOS с помощью методов однострочного кода. Синхронизируйте, загружайте, управляйте и удаляйте документы из iCloud быстро и легко. Помогает сделать iCloud "просто работать" для разработчиков тоже.

Практически в каждом методе синхронизации документов iCloud, все, что вам нужно сделать, это передать в файл данные в качестве параметра, а затем он обрабатывает остальные (сохранение, синхронизация и т.д.).

Отказ от ответственности : я участвую в разработке проекта с открытым исходным кодом, iCloud Document Sync. Однако я считаю, что этот проект будет выгоден вам и имеет отношение к данному вопросу. Это не продвижение или реклама.

Используйте этот код:

+ (NSString *)Pathsave {
    NSString *os5 = @"5.0";

    NSString *currSysVer = [[UIDevice currentDevice] systemVersion];
    NSString *path = [NSHomeDirectory() stringByAppendingPathComponent:@"Documents"];

    if ([currSysVer compare:os5 options:NSNumericSearch] == NSOrderedAscending) {
       // Lower than 4
        return path;
    } else if ([currSysVer compare:os5 options:NSNumericSearch] == NSOrderedDescending) {
        // 5.0.1 and above        
        return path;
    } else {
        // iOS 5
        path = [NSHomeDirectory() stringByAppendingPathComponent:@"Library/Caches"];
        return path;
    }

    return nil;
}