How to determine if a file exists in the iCloud folder?

I have an iOS application that stores files in iCloud. When I launch the application, I want to determine if the previous device downloaded any files. I launch the first device and add files to iCloud (I see them in the Mobile Documents folder on my Mac). Then I run the application on the second device and try to use the following NSMetadataQuery to find out if any files are uploaded, but it returns 0 results. If I continue to execute this query, after about 8-10 seconds it will return the results.

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 a notification resultCount is 0 for the request

 - (void)iCloudQueryDidFinishGathering:(NSNotification *)notification { NSMetadataQuery *query = [notification object]; [query disableUpdates]; [query stopQuery]; NSLog(@"Found %d results from metadata query", query.resultCount); } 

Should NSMetadataQuery return resultCount if the file exists in iCloud, even if it has not been uploaded? Is there a way to check if a file exists other than trying to exceed the time and timeout after 15-30 seconds?

+4
source share
1 answer

It may take some time for the request to retrieve metadata from iCloud. The didFinishGathering command may initially contain only those results that the device already knows about, and not changes that it was not possible to learn about iCloud about.

Instead of stopping and starting NSMetadataQuery, it would be preferable to install it and continue listening, also registering for:

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

... and retrieve updates when they come in. Therefore, you also need to change the finishGathering method, rather than stopping the request and enable Updates at the end.

You will have to reconsider your approach a bit so that the fact that the first set of results will not necessarily still be known. More normally, NSMetadataQuery is used to monitor iCloud, expecting that changes generated by other devices can appear at any time - not only when the application starts.

If you need to make sure that you have the most up-to-date metadata for iCloud, the only approach I have found reliable (both on iOS 5 and iOS 6) is to enter a small file in iCloud (usually using an excellent name form and named with a UUID, so it is guaranteed to be unique), and then in the iCloudQueryDidUpdate method: not counting the results of the request, which will be completed until this file is returned by the request, and the metadata that it is also loaded into iCloud. Once you return, you can be sure that you have received the latest metadata from iCloud.

Check download in iCloudQueryDidUpdate: using:

 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 do not forget to delete the injected file when you are done with it, otherwise they will be mounted every time your application starts.

Edit:

The way I implemented these checks had a built-in delay that, as soon as I took it out, I found a case where the above was not completely reliable.

I deleted the metadata elements (deleted using "Settings / iCloud / Storage" and "Backup / Storage management until the current launch") that were sent, downloaded and uploaded and saved to disk before the full metadata returned for my file attachment . However, after the injected file was registered as downloaded, downloaded and existing locally on the disk, one of these deleted files was still listed in the metadata, downloaded and downloaded - BUT NOT existing on the disk.

Be that as it may, the iCloud daemon hears about a pending deletion from the old data and actually performs the deletion before the metadata that your application sees has been updated to reflect this. Crazy, huh? Thus, I have to update my recommendation above to only consider the results of the query if the item is reported as loaded, loaded AND it exists in the local folder using the [NSFileManager fileExistsAtPath:] method. Code edited above to reflect this.

After that, all you can do is stick to something like a delay of 1 second before acting on the results of the query, to be absolutely sure that all the metadata has been received - although this is something that I DO NOT NEED to do, Holding false time delays in the code to make it work, it feels too close to black magic for me. And it’s significant that you really don’t understand what is happening - although without extra hooks to processing for iCloud, what else do we do?

+5
source

Source: https://habr.com/ru/post/1490006/


All Articles