0

有人可以为我指出正确的方向,以了解如何正确监控应用程序 iCloud 容器中的文件何时更改?我的代码基于我查看过的 Apple 和其他 iCloud 教程,但它们都没有处理 iCloud 容器的更新,只是使用初始查询。我已经为此工作了三个星期,但没有成功。我在应用程序中使用 UIDocument 保存到应用程序 iCloud 容器。由于 UIDocument 在添加文档时不会发送通知,因此当应用程序在多个设备上运行时,我无法在另一台 iOS 设备上更新该应用程序。通过监视 UIDocument UIDocumentStateChangedNotification,更改和删除文档可以正常工作。

当应用程序启动或从后台恢复时,我使用查询来初步检查 iCloud 容器,这可以很好地获取设备上 iCloud 容器中的所有文件,包括在应用程序未处于活动状态时添加的任何文档。当发布 NSMetadataQueryDidFinishGatheringNotification 时,我禁用更新以处理查询结果,然后启用查询更新。有时我在启用来自 NSMetadataQueryDidUpdateNotification 的更新后不久会收到一两个更新通知,但仅此而已。永远不会有任何进一步的更新通知,也永远不会来自被添加到 iCloud 容器的文档。

我知道 iCloud 使用的代码有些复杂,我不希望任何人检查我的代码(我提供了一个摘录供参考)来纠正它。如果有人可以向我指出有关在应用程序执行期间跟踪 iCloud 容器更改的细节的更多信息,我将不胜感激。

谢谢,

弗雷德

开始查询的代码摘录:

-(void)loadDocument {

// set iCloud URL to nil for local storage to start
NSURL *ubiq = nil;

// if iCloud is selected get the iCloud container URL
if ([_useiCloud isEqualToString:@"YES"]) {

    // get the app iCloud container URL
    ubiq = DefaultMemoDataController.iCloudContainerURL;
}

// if iCloud URL is available and user chooses to use iCloud, set the query for app memo file names
if (ubiq) {

    // adding to see if not creating another query prevents crash resuming from background
    if (!self.query) {
        self.query = [[NSMetadataQuery alloc] init];
    }

    // set the scope of the query to look in iCloud documents
    [self.query setSearchScopes:[NSArray arrayWithObject:NSMetadataQueryUbiquitousDocumentsScope]];

    // set search to look for a group of file names by setting up a predicate
    // use the note file name format for the app
    NSPredicate *pred = [NSPredicate predicateWithFormat:@"%K like 'FOLMemo_*'", NSMetadataItemFSNameKey];

    // set the query to search with the predicate.
    [self.query setPredicate:pred];

    // set up a notification when the query is complete because the query is an asynchronous call (off the main queue)
    [[NSNotificationCenter defaultCenter] addObserver:self
                                             selector:@selector(queryDidFinishGathering:)
                                                 name:NSMetadataQueryDidFinishGatheringNotification
                                               object:self.query];

    // start the query.
    [self.query startQuery];
    // not sure this is needed, but want to make sure same query is started again for updates.
    DefaultMemoDataController.query = self.query;
    }
}

查询完成时的代码

-(void)queryDidFinishGathering:(NSNotification *)notification {

// stop the query while processing the query results to prevent changes while processing
NSMetadataQuery *query = [notification object];
[query disableUpdates];

// not sure is needed but want to make sure resume updates on same query
DefaultMemoDataController.query = query;

// stop looking for query did finish notifications since the query was completed.
[[NSNotificationCenter defaultCenter] removeObserver:self
                                                name:NSMetadataQueryDidFinishGatheringNotification
                                              object:query];

// start looking for query updates
[[NSNotificationCenter defaultCenter] addObserver:self
                                         selector:@selector(processQueryUpdate:)
                                             name:NSMetadataQueryDidUpdateNotification
                                           object:query];

// load the data from the query
[self loadData:query];
}

处理查询的代码:

-(void)loadData:(NSMetadataQuery *)query {

// add all the memos from the query results to the app memos dictionary
for (NSMetadataItem *item in [query results]) {

    // get the URL for the memo
    NSURL *url = [item valueForAttribute:NSMetadataItemURLKey];

    // load the memo text from the url
    FOLMemoDoc *doc = [[FOLMemoDoc alloc] initWithFileURL:url];

    // open the note
    [doc openWithCompletionHandler:^(BOOL success) {
        if (success) {

            // add the memo UIDocument object to the memo dictionary
            // need temp dictionary since can't change a property dictionary for some reason
            NSMutableDictionary * tempDict = [NSMutableDictionary dictionaryWithDictionary:DefaultMemoDataController.masterMemoDictionary];

            [tempDict setObject:doc forKey:doc.memoDictionaryKey];
            DefaultMemoDataController.masterMemoDictionary = [NSMutableDictionary dictionaryWithDictionary:tempDict];

            NSNotification *notice = [NSNotification notificationWithName:kFlashofLightUpdateMemoNotice
                                                                   object:doc];
            [[NSNotificationCenter defaultCenter] postNotification:notice];


        } else {

            // failed to open document, should probably alert the user
                        }
    }];
}


// enable query updates
[query enableUpdates];

}
4

1 回答 1

1

经过一周的实验,我通过向查询对象的持久 dataController 对象添加一个属性来获得 iCloud 容器的查询更新。通过用持久性 dataController 属性替换我之前代码中的每个查询引用,保持完成查询的观察者(NSMetadataQueryDidFinishGatheringNotification)并且永不停止查询,查询更新现在可以工作(NSMetadataQueryDidUpdateNotification)。应用程序 iCloud 容器的每次更改都会收到 NSMetadataQueryDidUpdateNotification 通知。有时会收到多条通知,但我从来没有遇到过没有发布通知的情况,因此我现在可以在运行该应用程序的所有设备上捕获所有实时更新。

以下是上面修改后的代码摘录。此代码需要其他未包含的方法和设置,因此它不会独立运行,但显示了我必须进行的更改才能让 NSMetadataQueryDidUpdateNotification 通知在我的应用程序中工作。

开始查询的代码摘录:

-(void)loadDocument {


// set iCloud URL to nil for local storage to start
NSURL *ubiq = nil;

// if iCloud is selected get the iCloud container URL
if ([_useiCloud isEqualToString:@"YES"]) {        
    // get the app iCloud container URL
    ubiq = DefaultMemoDataController.iCloudContainerURL;
}


// if iCloud URL is available and user chooses to use iCloud, set the query for app memo file names
if (ubiq) {

    // adding to see if not creating another query prevents crash resuming from background
    if (!DefaultMemoDataController.query) {
        DefaultMemoDataController.query = [[NSMetadataQuery alloc] init];
    }

    // set the scope of the query to look in iCloud documents
    [DefaultMemoDataController.query setSearchScopes:[NSArray arrayWithObject:NSMetadataQueryUbiquitousDocumentsScope]];

    // set search to look for a group of file names by setting up a predicate
    // use the note file name format for the app
    NSPredicate *pred = [NSPredicate predicateWithFormat:@"%K like 'FOLMemo_*'", NSMetadataItemFSNameKey];

    // set the query to search with the predicate.
    [DefaultMemoDataController.query setPredicate:pred];

    //remove observer to make sure no duplicate observers
    [[NSNotificationCenter defaultCenter] removeObserver:self
                            name:NSMetadataQueryDidFinishGatheringNotification
                          object:DefaultMemoDataController.query];

    // set up a notification when the query is complete because the query is an asynchronous call (off the main queue)
    [[NSNotificationCenter defaultCenter] addObserver:self
                                             selector:@selector(queryDidFinishGathering:)
                                                 name:NSMetadataQueryDidFinishGatheringNotification
                                               object:DefaultMemoDataController.query];


    // remove observer to make sure no duplicate observers
    [[NSNotificationCenter defaultCenter] removeObserver:self
                                                    name:NSMetadataQueryDidUpdateNotification
                                                  object:DefaultMemoDataController.query];

    // set observer for query update
    [[NSNotificationCenter defaultCenter] addObserver:self
                                             selector:@selector(processQueryUpdate:)
                                                 name:NSMetadataQueryDidUpdateNotification
                                               object:DefaultMemoDataController.query];


    // start the query.
    [DefaultMemoDataController.query startQuery];

}

查询最初完成时的代码:

-(void)queryDidFinishGathering:(NSNotification *)notification {

// disable the query while processing the query results to prevent changes while processing
DefaultMemoDataController.query
NSMetadataQuery *query = [notification object];
[DefaultMemoDataController.query disableUpdates];


// call loadData with the query results
[self loadData:DefaultMemoDataController.query];

}

处理查询的代码

-(void)loadData:(NSMetadataQuery *)query {


// add all the memos from the query results to the app memos dictionary
for (NSMetadataItem *item in [query results]) {


    // get the URL for the memo
    NSURL *url = [item valueForAttribute:NSMetadataItemURLKey];



    // load the memo text from the url
    FOLMemoDoc *doc = [[FOLMemoDoc alloc] initWithFileURL:url];

    // open the memo
    [doc openWithCompletionHandler:^(BOOL success) {
        if (success) {

            // add the memo UIDocument object to the memo dictionary
            // need temp dictionary since can't change a property dictionary for some reason
            NSMutableDictionary * tempDict = [NSMutableDictionary dictionaryWithDictionary:DefaultMemoDataController.masterMemoDictionary];

            [tempDict setObject:doc forKey:doc.memoDictionaryKey];
            DefaultMemoDataController.masterMemoDictionary = [NSMutableDictionary dictionaryWithDictionary:tempDict];


            // save the memo dictionary
            [DefaultMemoDataController saveMemoDictionary];

            NSNotification *notice = [NSNotification notificationWithName:kFlashofLightUpdateMemoNotice
                                                                   object:doc];
            [[NSNotificationCenter defaultCenter] postNotification:notice];


        } else {

            // failed to open document

            // if there is a memo dictionary key available, delete the memo from master memo dictionary
            if (doc.memoDictionaryKey) {
                // delete memo from master memo dictionary
                [DefaultMemoDataController.masterMemoDictionary removeObjectForKey:doc.memoDictionaryKey];
            }
            // get the dictionary key from the file name and try to delete it that way
            else {
                NSString * filename = [doc.fileURL lastPathComponent];
                if (filename) {   
                    [DefaultMemoDataController.masterMemoDictionary removeObjectForKey:filename];
                }

            }

        }
    }];
}

// enable query updates
[DefaultMemoDataController.query enableUpdates];

}

我希望这对其他人有帮助。

弗雷德

于 2013-10-18T14:20:48.210 回答