如何使用iCloud存储和同步应用程序文件

edo*_*o42 33 cocoa-touch objective-c nsfilemanager icloud

我已经有一个iPhone应用程序将数据存储在本地文档文件夹中的文件中.现在我了解了iCloud技术,我的第一个问题是:当有时我检查新版本时,有没有办法将iCloud用作目录?

我的意思是:我可以避免使用UIDocument,文件协调员和文件演示者吗?我只想知道是否可以将iCloud视为特殊文件夹,只使用NSFileManager来推送和检索文件.

最后注意:我不使用Core Data或任何数据库,我只有一个数据文件.

编辑:

我已经阅读了官方的Apple iCloud文档,所以不要将我链接到他们.我只需要一些代码示例.

Sta*_*nko 25

对我来说"有效"的方法很简单:

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];
Run Code Online (Sandbox Code Playgroud)

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; 
}
Run Code Online (Sandbox Code Playgroud)

通过查询结果枚举的示例:

- (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]];
}
Run Code Online (Sandbox Code Playgroud)

并开始"下载"具体文件:

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]);
}
Run Code Online (Sandbox Code Playgroud)

检查文件"正在下载":

- (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;
}
Run Code Online (Sandbox Code Playgroud)

我没想到代码会那么长,并且抱歉在这里提供非常原始的代码片段.没有意图说上面的代码可以是生产质量,只是分享这个概念.

我还没有将我的应用程序内容提交给Apple,所以不能说它会被"批准"到应用程序商店(如果他们发现或关心......)

  • 只是要注意上面的代码的"生产"版本将其传递到应用程序商店中. (3认同)

n.e*_*ind 16

我知道你的感受,iCloud有点令人生畏.但是,我认为没有办法绕过UIDocument,文件协调员等,只是简单地使用iCloud作为一个简单的文件夹.

如果您正在寻找易于理解的示例代码,请查看此帖子:

iCloud基础知识和代码示例

我提供了一个完整的示例代码,涵盖了iCloud的最低限度,并且几乎像目录一样使用它.也许这使您不必使用UIDocument,文件协调器等等.

但是,和你一样,我希望有一个更好的,更兼容的方式与良好的旧纪录片文件​​夹的想法.然而,由于这是iCloud,并且iCloud做了更多的事情(比如在不同设备上保持所有内容同步,不断更新到云等),因此无法绕过UIDocument等.


sam*_*tte 14

您可以使用NSFileManager将单个文件上载到iCloud.我在博客上发布了关于如何做到这一点的完整演练,但这里是相关的NSFileManager代码:

NSURL *destinationURL = [self.ubiquitousURL URLByAppendingPathComponent:@"Documents/image.jpg"]
[[NSFileManager defaultManager] setUbiquitous:YES 
                                    itemAtURL:sourceURL
                               destinationURL:destinationURL
                                        error:&error]
Run Code Online (Sandbox Code Playgroud)

  • 当我这样做时,我尝试使用residentData = [[NSMutableArray alloc] initWithContentsOfURL:icloudURL]在iCloud中获取文件; 但每次都返回nil .. (2认同)

Sam*_*cer 6

使用UIDocument并没有什么办法.我试图在我第一次使用iCloud时做到这一点,但结果却是没有UIDocument的灾难.最初使用UIDocument似乎需要做很多额外的工作,但事实并非如此.

您可以在一小时内轻松地将UIDocument子类化,并使其与任何类型的文件一起使用(只需将该content属性设置为NSData).它还提供了许多优于标准文件系统的好处:

  • 更改跟踪
  • 文件冲突解决
  • 记录国家支持
  • 增强的保存/打开/关闭功能

老实说,花一两个小时阅读Apple文档,然后使用它是非常值得的时间和脑力.关于iCloud Document存储的好文章可以在Apple的开发者文档中找到.


我编写了一个UIDocument子类,可以使用任何类型的文件(特别是NSData).您可以在GitHub上查看,下载和修改UIDocument子类的代码.

创建文档:

// 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];
Run Code Online (Sandbox Code Playgroud)

保存现有文档:

// Save and close the document
[document closeWithCompletionHandler:nil];
Run Code Online (Sandbox Code Playgroud)

保存新文档:

[document saveToURL:document.fileURL forSaveOperation:UIDocumentSaveForCreating completionHandler:nil];
Run Code Online (Sandbox Code Playgroud)

您还可以使用NSMetadataQuery同步iCloud中存储的所有文件.Apple提供了一个使用NSMetadata查询来同步应用程序文件的非常好的示例.还要确保在执行这些操作之前检查iCloud(提示:使用ubiquityIdentityTokenNSFileManager上的方法).


您可能还需要考虑使用开源库,例如iCloud Document Sync.iCloud Document Sync项目可以非常轻松地存储和同步应用程序文件:

使用单行代码方法将iCloud集成到iOS文档项目中.快速轻松地从iCloud同步,上传,管理和删除文档.有助于使iCloud对开发人员"正常工作".

在几乎每个iCloud Document Sync方法中,您所要做的就是将文件数据作为参数传递,然后处理其余的(保存,同步等).

免责声明:我是开源项目iCloud Document Sync的贡献开发人员.但是,我相信这个项目对你有益,并且与这个问题有关.这不是促销或广告.