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



我有一个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秒后超时之外,还有什么方法可以测试文件是否存在吗?

查询从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
}

当你完成注入的文件时,不要忘记删除它,否则每次启动应用程序时,这些文件都会增加。

编辑:

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

我已经删除了元数据项目(在当前运行之前使用Settings/iCloud/Storage&Backup/Manage Storage删除),这些项目被报告为已上传和下载,并且在我注入的文件的完整元数据返回之前存在于磁盘上。然而,一旦注入的文件被报告为已上传、已下载并在磁盘上本地存在,其中一个已删除的文件仍在元数据中列出为已上传和已下载,但不存在于磁盘上。

因此,看起来发生的情况是,iCloud守护进程听说了旧数据的挂起删除,并在您的应用程序看到的元数据更新以反映这一点之前进行了删除。疯了吧?因此,我必须更新我上面的建议,只有当项目被报告为下载、上传并且使用[NSFileManager fileExistsAtPath:]方法存在于本地文件夹中时,才考虑查询结果完成。上面的代码经过编辑以反映这一点。

在这之后,你所能做的就是在对查询结果采取行动之前,先延迟1秒,以确保所有元数据都有时间接收-尽管这是我讨厌不得不做的事情。在代码中粘贴虚假的时间延迟以使其工作对我来说有点太接近魔术了。这表明你并不真正了解发生了什么——尽管如果没有更多的挂钩来处理iCloud,我们还能做什么?

最新更新