Search code examples
iosicloudicloud-documents

Get notified on file changes in iCloud


I create a file with name File - 1.jpg on two different devices and put it in iCloud container.

I don't use UIDocument and even though I tried to use it, it does not create a conflict. Instead what I see is that documents are being automatically renamed and moved by iCloud.

So after upload one file or another becomes File - 2.jpg. All of this is fine but now I don't have a reference to file so I have no idea which is which...

Is there any way to get notified on the app side that file was renamed/moved/deleted in iCloud?


Solution

  • Eventually, I had to create a class that implements NSFilePresenter and point it to iCloud container folder.

    Live updates from iCloud may be quite late and happen only when iCloud pulls metadata.

    Also, I had to associate each created file with each device and iCloud account and persist this data, in my case in CoreData. This is where ubiquityIdentityToken becomes useful.

    All file ops in iCloud container should certainly happen using NSFileCoordinator.

    For add/remove events it's better to use NSMetadataQuery, NSFileCoordinator does not report those at all, but is still useful to detect when files were moved, this is what metadata query reports as updates.

    This is a very basic boilerplate that can be used as a starting point:

    @interface iCloudFileCoordinator () <NSFilePresenter>
    
    @property (nonatomic) NSString *containerID;
    @property (nonatomic) NSURL *containerURL;
    
    @property (nonatomic) NSOperationQueue *operationQueue;
    
    @end
    
    @implementation iCloudFileCoordinator
    
    - (instancetype)initWithContainerID:(NSString *)containerID {
        self = [super init];
        if(!self) {
            return nil;
        }
    
        self.containerID = containerID;
        self.operationQueue = [[NSOperationQueue alloc] init];
        self.operationQueue.qualityOfService = NSQualityOfServiceBackground;
    
        [self addFilePresenter];
    
        return self;
    }
    
    - (void)dealloc {
        [self removeFilePresenter];
    }
    
    - (void)addFilePresenter {
        [NSFileCoordinator addFilePresenter:self];
    }
    
    - (void)removeFilePresenter {
        [NSFileCoordinator removeFilePresenter:self];
    }
    
    #pragma mark - NSFilePresenter
    #pragma mark - 
    
    - (NSURL *)presentedItemURL {
        NSURL *containerURL = self.containerURL;
    
        if(containerURL) {
            return containerURL;
        }
    
        NSFileManager *fileManager = [[NSFileManager alloc] init];
    
        containerURL = [fileManager URLForUbiquityContainerIdentifier:self.containerID];
    
        self.containerURL = containerURL;
    
        return containerURL;
    }
    
    - (NSOperationQueue *)presentedItemOperationQueue {
        return self.operationQueue;
    }
    
    - (void)presentedSubitemAtURL:(NSURL *)oldURL didMoveToURL:(NSURL *)newURL {
        NSLog(@"Moved file from %@ to %@", oldURL, newURL);
    }
    
    /*
     ... and other bunch of methods that report on sub item changes ...
     */
    
    @end