Problem in handling NSMetadataQueryDidUpdate notification for UIDocument on iOS11

Hello,

I have an app which is enabled to synchronize the data with iCloud.


The data inherit from UIDocument class and is overridden the necessary methods.

In my ViewController, I observe the NSMetadataQueryDidUpdate and when I receive the notification, I iterate the results and collect all the changed URLs then reload the data of each URL to update the UI.


In the loading method, I open the document of relevant each URL to get the contents as follows.


for url in urlList {
    autoreleasepool {
        let coordinator = NSFileCoordinator(filePresenter: nil)
        coordinator.coordinate(readingItemAt: url, options: .withoutChanges, error: nil, byAccessor: { (url) in
            let doc = MyDocument(fileURL: url)
            doc.open { (success) in
                :
                let title = doc.title
                let image = doc.image
                :
                doc.close(comeletionHandler: { (success) in
                    :
                }
            }
        }
   }
}


Then I update the UI with the updated contents.


These process has been worked well until I updated to iOS11.

Since I updated to iOS11, NSMetadataQueryDidUpdate notification is sent again after a few seconds of the loading process.

I don't know why and I am not sure but I think the open and close of UIDocument evoke the notification because when I commented out from the open to close part, the notification never came until I actually update some contents.


Anyway, my app is stacked in the repetition of process as follows.


  1. Launch the app and receive the NSMetadataQueryDidUpdate notification first after starting the observation.
  2. Collect the URL list to reload the data from the notification.
  3. Open the documents to get the contents from each URL then close
  4. Update the UI
  5. Again receive the NSMetadataQueryDidUpdate about few seconds after the data loading and repeat the process(back to 2.)


As a result, my app repeats from 2. to 5. infinitely


Am I something wrong? How should I handle this situation?

Replies

Hi magpoc,


I can confirm the same behavior and yes it does seem to be related to opening/closing the UIDocument. Thank you for that insight.


Did you by chance submit a bug report to Apple? I did all the way back on 08/02/2017 and have been going back and forth with log exports and even provided a sample app. They just closed my bug due to my posting additional information that was not related to the original issue and pointed me to Developer Technical Support instead. 😠


Please keep this thread updated with any progress on this issue.


Thanks

Hello swidevices,


Thank you for your suggestion.

I queried the issue to the Apple technical support and they recommended me to post the issue to the Bug Reporter.

So I have posted the bug report to Apple. I hope they will fix it soon.

Hi magpoc,


I too have the same situation. I did not see your question until after I posted my own, See:


Changes To NSMetadataQueryDidUpdate functionality in ios 11.


Post if you get a resolution from Apple.


Note my comments about Ray Wenderlich's tutorial.


I got around it by only using the results of the NSMetadataQueryDidFinishGathering notification and I stopped using the results of NSMetadataQueryDidUpdate.

It seems there is some form of metadata update happening in the [UIDocument openWithCompletionHandler:] method. So I figured why not make my own that hopefully doesn't trigger the update and call that instead. The following solution is working in my testing so far. Create a Category on UIDocument that adds a openReadOnlyWithCompletionHandler: method. Note that you should not call [UIDocument closeWithCompletionHandler:] when calling this new method. If you do the completion handler is never called. Any input regarding this solution is much appreciated.


//  UIDocument+OpenReadOnly.h
#import <Foundation/Foundation.h>
@interface UIDocument (OpenReadOnly)
- (void)openReadOnlyWithCompletionHandler:(void (^ __nullable)(BOOL success))completionHandler;
@end


//  UIDocument+OpenReadOnly.m
#import "UIDocument+OpenReadOnly.h"
@implementation UIDocument (OpenReadOnly)
- (void)openReadOnlyWithCompletionHandler:(void (^ __nullable)(BOOL success))completionHandler
{
    __weak UIDocument* weakSelf = self;
    [self performAsynchronousFileAccessUsingBlock:^{
        UIDocument* strongSelf = weakSelf;
        if (strongSelf)
        {
            NSURL* fileURL = strongSelf.fileURL;
            NSError* err;
          
            NSFileCoordinator* fileCoordinator = [[NSFileCoordinator alloc] initWithFilePresenter:nil];
            [fileCoordinator coordinateReadingItemAtURL:fileURL
                                                options:NSFileCoordinatorReadingWithoutChanges
                                                  error:&err
                                             byAccessor:^(NSURL * _Nonnull newURL) {
                                                 NSError* err;
                                                 BOOL result = [strongSelf readFromURL:newURL error:&err];
                                                 if (err)
                                                 {
                                                     NSLog(@"UIDocument openReadOnlyWithCompletionHandler: failed readFromURL %@", [err localizedDescription]);
                                                 }
                                                 if (completionHandler)
                                                 {
                                                     completionHandler(result);
                                                 }
                                             }];
            if (err)
            {
                NSLog(@"UIDocument openReadOnlyWithCompletionHandler: failed coordinateReadingItemAtURL %@", [err localizedDescription]);
            }
        }
        else
        {
            if (completionHandler)
            {
                completionHandler(NO);
            }
        }
    }];
}
@end

I have received an answer from Apple.

In summary, they said it is an intended behavior though it seems buggy in some cases.


I cannot write the detail here because "UNDER NON-DISCLOSURE" is written in the response. Also it's bit hard to me to understand the detail.


One suggestion form them is to use revertToContentsOfURL method to update the document instead, although I don't know how to implement with it specifically.


Anyway, I have already given up to handle this issue and am modifying my app using CloudKit which is more controllable than the document base implementation I think.