Search code examples
objective-cnsfilecoordinatornsfilepresenter

When initialising an NSFilePresenter for a fileURL within an NSFileCoordinator accessor block passing newURL, which URL should I use?


Apple's documentation is not as clear as I would like when it comes to understanding subtle details relating to NSFileCoordinator usage. My current interpretation is as follows:

When you access a fileURL using NSFileCoordinator, the coordinator liaises with all registered NSFilePresenters for that fileURL (or hierarchically dependent on that fileURL) and other NSFileCoordinators accessing it as needed to ensure that reads/writes proceed without conflict in an efficient manner (across processes even), taking into account options you specify.

In order to do this, NSFileCoordinator may move the file in order for it to be accessed. Focusing on synchronous coordination of a single fileURL, the accessor block passes a 'newURL' parameter which should be used to access the underlying file or directory.

According to the docs, newURL constitutes:

A URL identifying the file or directory to read/write to. If other objects or processes are acting on the item at the URL, the actual URL passed to reader/writer parameter may be different from the one in this parameter.

I am file coordinating the fileURL of a file package. Within the accessor block I am reading from this file package using the 'newURL' accessor parameter in order to instantiate an object which itself needs to register a file presenter for its parent file package. Consider for example a 'project' object, born from a 'project file package', which needs to present its parent project file package to maintain current knowledge of the project file package location and file coordinate access for future reads/writes.

The question is, should the instantiated object (e.g., project) initialise its file presenter using the original fileURL, or the newURL provided in the accessor block? As far as I can tell there isn't enough information in Apple's docs to really know for sure.

The crux of the confusion:

  • It appears that NSFileCoordinator can make a temporary copy of the original file and pass the location of the temporary copy as newURL. In such a scenario, I would not want the object instantiated from newURL to present newURL, I would want it to present the original fileURL for the purpose of ongoing operations on the file package.
  • It may also be the case that the original fileURL is legitimately moved by other concurrent operations at the time of file coordination, and thus it seems possible that the accessor block's newURL may NOT be a temporary copy, but could actually represent a new permanent location for the file (package). In that case I would want to present the file package using newURL to facilitate future coordination of the file (package).

Example for clarity:

- (instancetype) initProjectWithFilePackageURL:(NSURL *)projectfilePackageURL
{
    NSFileCoordinator *fc = [[NSFileCoordinator alloc] initWithFilePresenter:nil];
    [fc coordinateReadingItemAtURL:projectFilePackageURL
                             options:NSFileCoordinatorReadingWithoutChanges
                                       error:&coordinationError
                                  byAccessor:^(NSURL * _Nonnull newURL) {

         NSURL *dataURL = fileURLForProjectDataInProjectFilePackageAtURL(newURL);
         self = [EGUtility unarchiveProjectFromDataAtURL:dataURL];
         if (self)
         {
             // Which URL should the project present?
             // It needs to monitor its parent file package indefinitely, not just within the accessor block.
             EGFilePresenter *presenter = [EGPresenter newWithDelegate:project fileURL:<projectFilePackageURL OR newURL>?]
             [project setPresenter:presenter];
         
             // Etc…
         }

    return self;
}

It is difficult to know which I should do, or whether I am missing any other subtleties. I would be very grateful for any insight on this. Thank you.


Solution

  • Having now spent a TSI and discussed the matter with an Apple engineer, here is a summation of what I learned for the benefit of others:

    NSFileCoordinator works hard to identify the right URL to pass to the accessor as the newURL. It does the following:

    1. Standardises the URL to convert special characters in the URL, if any.
    2. Resolves symbolic links, if needed.
    3. Handles downloading if the coordinated URL is on a file provider.
    4. Handles access issues if the URL is remote.
    5. If the coordinated file is moved while the access claim was pending, newURL will take that into account and point to the new location of the file.
    6. If the provided option is NSFileCoordinatorReadingForUploading, newURL will point to a temporary snapshot of the file so that access to the original file is not blocked by the uploading process.

    Generally, I was assured that when initialising a file presenter for the coordinated file within the coordinator's accessor block, the URL to use would be the newURL parameter passed to the accessor block unless specifying NSFileCoordinatorReadingForUploading, in which case the coordinated URL should be used instead.