0

アプリの iCloud コンテナでファイルが変更されたときに正しく監視する方法を見つけるために、誰かが私を正しい方向に向けることができますか? 私は自分のコードを、私がレビューした Apple および他の iCloud チュートリアルに基づいていますが、最初のクエリを使用するだけで、iCloud コンテナーの更新を処理するものはありません。私はこれに3週間取り組んできましたが、成功していません。アプリで UIDocument を使用して、アプリの iCloud コンテナーに保存します。UIDocument はドキュメントが追加されても通知を送信しないため、アプリが複数のデバイスで実行されている場合、別の iOS デバイスでアプリを更新することはできません。ドキュメントの変更と削除は、UIDocument UIDocumentStateChangedNotification を監視することで正常に機能します。

アプリがバックグラウンドで起動または再開するときに、最初にクエリを使用して iCloud コンテナーをチェックします。これにより、アプリがアクティブでないときに追加されたドキュメントを含め、デバイス上の iCloud コンテナー内のすべてのファイルを取得できます。NSMetadataQueryDidFinishGatheringNotification がポストされたときにクエリ結果を処理するための更新を無効にしてから、クエリの更新を有効にします。NSMetadataQueryDidUpdateNotification からの更新が投稿されていることを有効にした直後に、1 つまたは 2 つの更新通知を受け取ることがありますが、それだけです。それ以上の更新通知や、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

もう 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 に答える