3

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?

pronebird
  • 12,068
  • 5
  • 54
  • 82

2 Answers2

2

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
pronebird
  • 12,068
  • 5
  • 54
  • 82
1

Use CKSubscription:

Upon initialization of CKSubscription you can specify the notification options:

  • CKSubscriptionOptionsFiresOnRecordCreation
  • CKSubscriptionOptionsFiresOnRecordDeletion
  • CKSubscriptionOptionsFiresOnRecordUpdate
  • CKSubscriptionOptionsFiresOnce

iCloud Subscriptions

These looked useful for you too:

https://www.bignerdranch.com/blog/cloudkit-the-fastest-route-to-implementing-the-auto-synchronizing-app-youve-been-working-on/

http://www.raywenderlich.com/83116/beginning-cloudkit-tutorial

mattyb
  • 1,011
  • 2
  • 10
  • 26
  • If I got it right, this is for CloudKit and not for iCloud Documents. I believe those are two separate things. CloudKit uses `CKAsset` and `CKRecord` to store files, while I iCloud documents is purely a magic folder on disk. – pronebird Dec 23 '15 at 16:20
  • Ahh my bad im sorry I didn't realize that was the context, i figured CloudKit was for developers to leverage iCloud – mattyb Dec 23 '15 at 16:21
  • I didn't work much with CKAssets so I had no idea they are so similar. I've added icloud-documents tag to my question to avoid the frustration. (this is how it's referenced in Xcode) – pronebird Dec 23 '15 at 16:23