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

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

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

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

Edit:

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

5 ответов


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

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

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

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

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


что "работает" для меня-это просто:

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 говорит, чтобы вызвать не-UI поток. Наличие файлов "перемещено". Вы можете запросить их через 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. Проект синхронизации документов 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;
}