如何确定iCloud文件夹中是否存在文件?

时间:2013-07-06 03:14:54

标签: ios objective-c icloud nsmetadataquery

我有一个iOS应用程序,可以在iCloud中存储文件。当我启动应用程序时,我想确定以前的设备是否已经上传了任何文件。我启动了第一台设备并将文件添加到iCloud(我可以在Mac上的Mobile Documents文件夹中看到它们)。然后我在第二个设备上启动应用程序并尝试使用以下NSMetadataQuery来查看是否已上载任何文件,但它返回0结果。如果我继续运行该查询,大约8-10秒后它会返回结果。

iCloudQuery = [[NSMetadataQuery alloc] init];

iCloudQuery.searchScopes = @[NSMetadataQueryUbiquitousDataScope];

NSString *filePattern = [NSString stringWithFormat:@"*.%@", @"txt"];

iCloudQuery.predicate = [NSPredicate predicateWithFormat:@"%K LIKE %@", NSMetadataItemFSNameKey, filePattern];

[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(iCloudQueryDidFinishGathering:) name:NSMetadataQueryDidFinishGatheringNotification object:iCloudQuery];

[iCloudQuery startQuery];

当我收到通知时,resultCount为0为查询

- (void)iCloudQueryDidFinishGathering:(NSNotification *)notification
{    
    NSMetadataQuery *query = [notification object];
    [query disableUpdates];
    [query stopQuery];

    NSLog(@"Found %d results from metadata query", query.resultCount);
}

如果文件存在于iCloud中,NSMetadataQuery是否应该返回resultCount,即使它尚未下载?有没有办法测试文件是否存在而不是在15-30秒后尝试超过和超时?

1 个答案:

答案 0 :(得分:5)

查询可能需要一些时间才能从iCloud中检索元数据。 didFinishGathering最初可能只保留设备已经知道的结果,而不是它没有机会从iCloud听到的更改。

不是停止并启动你的NSMetadataQuery,最好是设置一个并继续收听它,同时注册:

[[NSNotificationCenter defaultCenter] addObserver:self
                                         selector:@selector(iCloudQueryDidUpdate:)
                                             name:NSMetadataQueryDidUpdateNotification
                                           object:iCloudQuery];

...并在它们进入时检索更新。因此,您还需要更改您的finishGathering方法,而不是停止查询,并在最后启用更新。

你必须重新考虑一下你的方法,以便第一组结果不一定知道所有内容。更常见的情况是,NSMetadataQuery用于监视iCloud,期望其他设备生成的更改可以随时出现 - 而不仅仅是在应用程序启动时。

如果您需要确保拥有iCloud的最新元数据,我发现可靠的唯一方法(在iOS 5和iOS 6上)是注入一个小的将文件写入iCloud(通常使用不同的名称形式,并以UUID命名,因此保证是唯一的),然后在iCloudQueryDidUpdate:方法中,不考虑查询结果完成直到该文件都由查询返回,并且它的元数据报告它也被上传到iCloud。一旦你得到这个,你可以相当肯定你已经从iCloud收到了最新的元数据。

检查iCloudQueryDidUpdate中的上传:使用:

int resultCount = [iCloudQuery resultCount];

for (int i = 0; i < resultCount; i++) {
  NSMetadataItem *item = [iCloudQuery resultAtIndex:i];

  BOOL isUploaded = [[item valueForAttribute:NSMetadataUbiquitousItemIsUploadedKey] boolValue];
  BOOL isDownloaded = [[item valueForAttribute:NSMetadataUbiquitousItemIsDownloadedKey] boolValue];
  NSURL *url = [item valueForAttribute:NSMetadataItemURLKey];
  BOOL documentExists = [[NSFileManager defaultManager] fileExistsAtPath:[url path]];

  // You'll need to check isUploaded against the URL of the file you injected, rather than against any other files your query returns
}

不要忘记在完成注册后删除注入的文件 - 否则每次启动应用程序时都会挂起它们。

修改

我实施这些检查的方式有一个内置的延迟,一旦我把它拿出来,我发现上面的情况并不完全可靠。

我删除了元数据项目(在当前运行之前使用“设置/ iCloud /存储和备份/管理存储”删除)报告为已上载和下载,并且在完整元数据返回到我的注入文件之前存在于磁盘上。但是,一旦注入的文件被报告为在磁盘上本地上载,下载和存在,其中一个已删除的文件仍然在上载和下载的元数据中列出 - 但不存在于磁盘上。

所以它看起来一直在发生的是iCloud守护进程听到旧数据的待删除,并且实际上在应用程序看到的元数据已更新之前执行删除以反映这一点。疯了,是吗?因此,我必须更新上面的建议,仅考虑查询结果完成如果项目被报告为已下载,上传并且它使用[NSFileManager fileExistsAtPath:]方法存在于本地文件夹中。上面编写的代码反映了这一点。

在此之后,你所做的就是在对查询结果采取行动之前坚持使用1秒钟的延迟,以确保所有元数据都有时间接收 - 尽管这是我讨厌必须做的事情。在代码中加入虚假的时间延迟使其工作对我来说感觉有点太接近黑魔法了。并且表明您并不真正理解发生了什么 - 虽然没有更多关注iCloud背后的处理,我们还有什么可做的?