Как использовать iCloud для хранения и синхронизации файлов приложений - PullRequest
32 голосов
/ 21 октября 2011

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

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

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

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

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

Ответы [ 5 ]

24 голосов
/ 05 декабря 2011

То, что «работает» для меня, просто:

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

15 голосов
/ 22 октября 2011

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

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

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

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

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

14 голосов
/ 08 февраля 2012

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

NSURL *destinationURL = [self.ubiquitousURL URLByAppendingPathComponent:@"Documents/image.jpg"]
[[NSFileManager defaultManager] setUbiquitous:YES 
                                    itemAtURL:sourceURL
                               destinationURL:destinationURL
                                        error:&error]
6 голосов
/ 16 ноября 2013

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

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

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

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

0 голосов
/ 22 мая 2013

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

+ (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;
}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...