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

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

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

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

Изменить:

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


person albianto    schedule 21.10.2011    source источник


Ответы (5)


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

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

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

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

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

person n.evermind    schedule 22.10.2011
comment
Я согласен, если вы скажете, что нет, я прочту руководство. Но я подумал, что должен был быть более простой способ сделать это, например, когда вы пишете или читаете файл в песочнице приложения. Спасибо - person albianto; 23.10.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, поэтому не могу сказать, что это будет «одобрено» в магазине приложений (если они найдут или позаботятся ...)

person Stanislav Dvoychenko    schedule 04.12.2011
comment
просто отметим, что производственная версия приведенного выше кода передала его внутри приложения в магазин приложений. - person Stanislav Dvoychenko; 15.12.2011
comment
не могли бы вы предоставить образец проекта, включая приведенный выше код. Пожалуйста, помогите мне, если это возможно. - person Gypsa; 25.04.2012
comment
@StanislavDvoychenko Здравствуйте, сэр, дайте мне некоторое представление о части кодирования icloud. Означает, как настроить и получить и т. Д., Пожалуйста. Заранее спасибо - person Bajaj; 07.02.2013
comment
NSURLUbiquitousItemIsDownloadedKey устарел в iOS7 и заменен на NSURLUbiquitousItemDownloadingStatusKey. Также хочу сказать спасибо Станиславу за то, что разместил такой полный и полезный пример. - person SafeFastExpressive; 06.04.2014
comment
можем ли мы поделиться ссылкой через iCloud? Как в приложении Pages и Key, а не в приложении. Как можно установить права доступа к файлам и все остальное и поделиться ссылкой по электронной почте - person Govind; 02.11.2016

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

NSURL *destinationURL = [self.ubiquitousURL URLByAppendingPathComponent:@"Documents/image.jpg"]
[[NSFileManager defaultManager] setUbiquitous:YES 
                                    itemAtURL:sourceURL
                               destinationURL:destinationURL
                                        error:&error]
person samvermette    schedule 07.02.2012
comment
Есть ли способ копировать вместо перемещения? - person jjxtra; 25.07.2016
comment
Когда я сделал это, я попытался получить файл в iCloud, используя residentData = [[NSMutableArray alloc] initWithContentsOfURL: icloudURL]; но это возвращает ноль каждый раз .. - person coolcool1994; 31.12.2016

На самом деле нет способа обойтись без 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 все, что вам нужно сделать, это передать данные файла в качестве параметра, а затем он обработает все остальное (сохранение, синхронизацию и т. Д.).

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

person Sam Spencer    schedule 16.11.2013
comment
Именно это я и сделал в итоге. - person albianto; 22.11.2013
comment
Если ваш вариант использования - выгрузить данные в iCloud, вы можете сделать это с помощью координатора файлов, и UIDocument не требуется. - person Ben Affleck; 22.12.2015

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

+ (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;
}
person D.M Patel    schedule 22.05.2013