简体   繁体   English

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

[英]How do I determine if file exists in iCloud folder?

I have an iOS app that stores files in iCloud. 我有一个iOS应用程序,可将文件存储在iCloud中。 When I start up the app I want to determine if a previous device has already uploaded any files. 启动应用程序时,我想确定以前的设备是否已经上传了任何文件。 I start the first device and it adds the files to iCloud (I can see them in the Mobile Documents folder on my Mac). 我启动第一台设备,它将文件添加到iCloud(我可以在Mac上的“移动文档”文件夹中看到它们)。 I then start the app on a second device and try to use the following NSMetadataQuery to see if any files have been uploaded, but it returns 0 results. 然后,我在第二台设备上启动该应用程序,并尝试使用以下NSMetadataQuery来查看是否已上传任何文件,但它返回0结果。 If I keep running that query, after about 8-10 seconds it does return results. 如果我继续运行该查询,则大约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];

When I get the notification resultCount is 0 for the query 当我收到通知时,查询的resultCount为0

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

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

Shouldn't NSMetadataQuery return a resultCount if the file exists in iCloud, even if it hasn't been downloaded? 即使文件尚未下载,iSM中是否存在该文件,NSMetadataQuery也不应该返回resultCount吗? Is there any way to test if a file exists other than trying over an over and timing out after 15-30 seconds? 除了尝试过度并在15-30秒后超时之外,是否有其他方法可以测试文件是否存在?

It might take some time for the query to retrieve the metadata from iCloud. 查询可能需要一些时间才能从iCloud检索元数据。 The didFinishGathering may initially only hold the results that the device already knows about, not changes it hasn't had a chance to hear about from iCloud. didFinishGathering最初可能只保存设备已经知道的结果,而不保存它没有机会从iCloud得知的更改。

Rather than stopping and starting your NSMetadataQuery, it would be preferable to set one up and keep listening to it by also registering for: 与其停止并启动NSMetadataQuery,还不如通过注册以下内容来设置并继续收听:

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

... and retrieve the updates when they come in. So you'll need to change your finishGathering method as well, not to stop the query, and to enableUpdates at the end. ...并在更新进入时检索更新。因此,您还需要更改finishGathering方法,而不是停止查询并在最后启用Updates。

You'll have to rethink your approach somewhat to allow for the fact that the first set of results won't necessarily know everything yet. 您必须重新考虑您的方法,以考虑到第一组结果并不一定了解所有事实。 More normally, NSMetadataQuery is used to keep watch on iCloud, with the expectation that changes generated by other devices can come along at any time - not just on app launch. 通常,NSMetadataQuery用于监视iCloud,并期望其他设备生成的更改可以随时出现 -而不仅仅是在应用程序启动时。

If you need to be sure you have the most up-to-date metadata for iCloud the only approach I've found reliable (on both iOS 5 and iOS 6) is to inject a small file into iCloud (usually with a distinct form of name, and named with a UUID so it is guaranteed to be unique), and then in the iCloudQueryDidUpdate: method, not considering the query results to be complete until that file is both returned by the query, and it's metadata is reporting that it is also uploaded to iCloud. 如果您需要确保拥有iCloud的最新元数据,我发现可靠的唯一方法(在iOS 5和iOS 6上)是一个小文件注入iCloud(通常使用不同形式的名,并用UUID,因此可保证是唯一的)命名,然后在iCloudQueryDidUpdate:方法,不考虑查询结果是完整的 ,直到该文件被两个查询返回的,而且它的元数据报告,这是还上传到iCloud。 Once you get this back, you can be fairly certain you have received the latest metadata from iCloud. 一旦找到答案,您就可以肯定地从iCloud那里收到了最新的元数据。

Check for upload in iCloudQueryDidUpdate: using: 使用以下命令在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
}

And don't forget to delete the injected file when you are finished with it - otherwise these will mount up each time your app launches. 而且,不要忘记在完成注入后删除注入的文件-否则,每次您的应用启动时,这些文件都会挂载。

Edit: 编辑:

The way I'd implemented these checks had an in-built delay, that once I took it out I found a case where the above wasn't entirely reliable. 我实施这些检查的方式存在固有的延迟,一旦我将其取出,便发现上述情况并不完全可靠。

I had deleted metadata items (deleted using Settings/iCloud/Storage & Backup/Manage Storage before the current run) being reported as uploaded and downloaded and existing on disk before the full metadata came back for my injected file. 我已经删除了元数据项(在当前运行之前使用“设置” / iCloud /存储和“备份/管理存储”删除),报告为已上传并下载并存在于磁盘上,然后再为注入文件返回完整的元数据。 However, once the injected file was reported as uploaded, downloaded, and existing locally on disk, one of these deleted files was still listed in the metadata as uploaded and downloaded - BUT NOT existing on disk. 但是,一旦注入文件被报告为已上传,下载并在磁盘上本地存在,这些已删除文件之一仍会在元数据中列为已上传和下载-但磁盘上不存在。

So what it looks like has been happening is the iCloud daemon hears about the pending deletion on the old data, and actually carries out the deletion BEFORE the metadata your app sees has been updated to reflect this. 因此,看起来好像正在发生的事情是iCloud守护进程听到了对旧数据的待删除操作,并实际上在执行删除操作之前删除了您看到的元数据以反映这一点。 Crazy, huh? 疯了吧? So, I have to update my recommendation above, to only consider query results complete if the item is reported as downloaded, uploaded AND it exists in the local folder by using the [NSFileManager fileExistsAtPath:] method. 因此,我必须更新上面的建议,仅报告该项目为已下载,上载且使用[NSFileManager fileExistsAtPath:]方法存在于本地文件夹中时,才认为查询结果完整 Code above edited to reflect this. 上面的代码经过编辑以反映这一点。

After this, all you can do is stick something like a 1 second delay in before acting on the results of the query, to be absolutely sure all metadata has had time to be received - though this is something I HATE having to do. 此后,您可以做的就是在对查询结果采取行动之前坚持1秒钟左右的延迟,以确保所有元数据都有时间被接收-尽管我讨厌这样做。 Sticking spurious time delays into code to make it work feels a bit too close to black magic to me. 在代码中添加虚假的时间延迟以使其正常工作对我来说有点像黑魔法。 And indicative that you don't really understand what is going on - though without more hooks into the processing behind iCloud, what else are we to do? 并表明您不太了解正在发生的事情-尽管没有更多了解iCloud背后处理的信息,我们还要做什么?

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM