Подтвердить что ты не робот

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

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

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

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

Edit:

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

4b9b3361

Ответ 1

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

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

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

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

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

Ответ 2

Что "работает" для меня просто:

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, поэтому не могу сказать, что это будет "одобрено" в магазине приложений (если они найдут или позаботятся...)

Ответ 3

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

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

Ответ 4

На самом деле нет способа использовать 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 Document Sync упрощает хранение и синхронизацию файлов приложений:

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

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

ОТКАЗ ОТ ОТВЕТСТВЕННОСТИ. Я являюсь разработчиком проекта open-source iCloud Document Sync. Однако я считаю, что этот проект будет полезен для вас и имеет отношение к этому вопросу. Это не рекламная кампания или реклама.

Ответ 5

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

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