簡體   English   中英

合並針對iCloud沖突的UIDocument更改

[英]Merging UIDocument changes for iCloud conflicts

我花了一些日子,現在試圖找到或弄清楚自己,如何以編程方式合並UIDocument變化的時候,通知UIDocumentStateChangedNotification火災和文檔的狀態有UIDocumentStateInConflict設置。

我可以找到的所有示例(Apple,Ray Wenderlich等)都詳細說明了提示用戶選擇版本方法。 我找不到能說明以編程方式合並的正確方法的任何方法。 這讓我感到擔心,因為它使我認為信任太不穩定了,通常可以避免使用它作為解決方案? 到目前為止,我的經驗鞏固了這一地位。

讓我詳細說明我嘗試中的每個問題區域。

1)出於合並目的,讀取當前文檔內容和NSFileVersion沖突版本的正確方法是什么? 在完成時使用任何帶有完成塊的東西真的很亂。 UIDocumentopenWithCompletionHandler:不打算使用。 實際上,通常,推薦的只讀UIDocument方法是什么? 為什么打開僅用於閱讀的文檔? 我已經嘗試過使用UIDocumentreadFromURL:,這對於當前文檔來說很好,但是如果我嘗試在任何NSFileVersion的沖突版本上使用它,它將讀取當前版本,而不是URL上的版本(我已經使用過MacOS終端深入到../ data / .DocumentRevisions-V100 / PerUID / ...文件中以確認這一點。) 對於沖突版本,對我而言唯一有效的方法是直接讀取訪問這些文件。 (例如NSData initWithContentsOfFile:)

2)一旦讀完文件的變體並設法合並,如何正確保存合並? 我找不到任何地方確實沒有記錄此文件。 我成功的唯一方法是重新使用NSFileVersion的沖突文件之一,將其覆蓋,然后使用UIDocumentreplaceItemAtURL:使其成為當前文件。 在使用replaceItemAtURL 之后,我還嘗試使用UIDocumentrevertToContentsOfURL :,但是它崩潰了,沒有給出任何原因。 由於沒有它,合並似乎可以很好地工作,所以我不擔心,但我想將其作為詳細信息包括在內。

3)在我重新啟動應用程序之前,iPhone / iPad Simulator(V10.0)不會通知沖突。 這是意料之中的還是我做錯了什么? 我問,因為在模擬器的“ 調試”菜單下有“ 觸發器iCloud Sync” ,它可以進行同步,但是直到下一個應用程序重新啟動時才標記沖突。 這僅僅是模擬器的限制嗎?

謝謝,

這是“為什么要打開文檔以供閱讀?”部分的答案。

您只需要確保讀取是“協調的”即可,即與其他進程已經打開並且可能尚未保存的更改的文件沒有沖突。

這是一種遍歷NSDocument URL數組並以同步方式讀取每個URL的方法,即,該例程直到所有文件都被讀取后才返回。 它會強制所有未保存更改的文件在發生任何讀取之前先進行保存。

// NSArray *urls - the urls of UIDocument files you want to read in bulk
NSFileCoordinator *coordinator = [[NSFileCoordinator alloc] init];
NSError *error = nil;
[coordinator prepareForReadingItemsAtURLs:urls options:NSFileCoordinatorReadingWithoutChanges writingItemsAtURLs:@[] options:0 error:&error byAccessor:^(void (^ _Nonnull completionHandler)(void)) {
    for (NSURL *url in self->_urls) {
        NSError *error = nil;
        [coordinator coordinateReadingItemAtURL:url options:0 error:&error byAccessor:^(NSURL * _Nonnull newURL) {
            // Read contents of newURL here and process as required
            // ...

        }];
        if (error) {
            NSLog(@"Error reading: %@ %@", url.path, error.localizedDescription);
        }
    }
    completionHandler();
}];
if (error) {
    NSLog(@"Error preparing for read: %@", error.localizedDescription);
}

經過數周的測試和學習,了解了哪些行得通和哪些行不通,我簡化了UIDocument合並代碼。 我做出的錯誤假設之一是,在解析過程中需要包含UIDocumentrevertToContentsOfURL:。 這是一個非常不穩定的API調用,我發現最好避免,即使在@try()中使用它也無法避免不必要的崩潰。 這使我刪除它只是為了看到會發生什么,如果沒有它,沖突就可以清除。 在developer.apple.com上有用於解決文檔沖突的示例代碼,該代碼暗示應使用此代碼。 它似乎在WWDC2018之后消失了。

剩下的唯一問題是,如果您有兩個設備同時打開,那么您將陷入競爭狀態,因為這兩個設備都會連續合並文檔。

盡管文檔被標記為存在沖突,但我以前有過零沖突版本的經歷,但是最近我還沒有看到這種情況。 一定是我之前做錯了什么。 我將代碼保留在其中,因為它沒有害處。

我想在這里值得一提的另一個陷阱是,如果您不熟悉UIDocument,則應該記住它是UIKit的一部分,您需要確保在主線程上完成更新。 我發現了這個有用的技巧 ,解決了我仍然遇到的一些剩余問題。

- (void) foobar {
    [[NSNotificationCenter defaultCenter] addObserver:self
                                             selector:@selector(handleDocumentStateChange:)
                                                 name:UIDocumentStateChangedNotification
                                               object:_myDocument];
}

- (void) handleDocumentStateChange: (NSNotification *) notification {
    if (_myDocument.documentState & UIDocumentStateInConflict) {
        if (_resolvingConflicts) {
            return;
        }

        NSArray *conflictVersions = [NSFileVersion unresolvedConflictVersionsOfItemAtURL:_myDocument.fileURL];
        if ([conflictVersions count] == 0) {
            return;
        }
        NSMutableArray *docs = [NSMutableArray new];
        [docsData addObject:_myDocument.data]; // Current document data
        _resolvingConflicts = YES;
        for (NSFileVersion *conflictVersion in conflictVersions) {
            MyDocument *myDoc = [[MyDocument alloc] initWithFileURL:conflictVersion.URL];
            NSError *error;
            [myDoc readFromURL:conflictVersion.URL error:&error];
            if ((error == Nil) && (myDoc.data != Nil)) {
                [docs addObject:myDoc.data];
            }
        }

        if ([self mergeDocuments:docs]) {
            [self saveChangesToDocument];
        }

        for (NSFileVersion *fileVersion in conflictVersions) {
            fileVersion.resolved = YES;
        }
        [self deleteiCloudConflictVersionsOfFile:_myDocument.fileURL
                                      completion:^(BOOL success){
                                          self.resolvingConflicts = NO;
                                          dispatch_async(dispatch_get_main_queue(), ^{
                                              // On main thread for UI updates
                                              [[NSNotificationCenter defaultCenter] postNotificationName:kMyDocsUpdateNotification object:nil];
                                          });
                                      }];
    }
}

- (void) deleteiCloudConflictVersionsOfFile : (NSURL *) fileURL {
    dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^(void) {
        NSFileCoordinator* fileCoordinator = [[NSFileCoordinator alloc] initWithFilePresenter:nil];
        [fileCoordinator coordinateWritingItemAtURL:fileURL
                                            options:NSFileCoordinatorWritingForDeleting
                                              error:nil
                                         byAccessor:^(NSURL* writingURL) {
                                             NSError *error;
                                             if ([NSFileVersion removeOtherVersionsOfItemAtURL:writingURL error:&error]) {
                                                 NSLog(@"deleteiCloudConflictVersionsOfFile: success");
                                             } else {
                                                 NSLog(@"deleteiCloudConflictVersionsOfFile: error; %@", [error description]);
                                             }
                                         }];
    });
}

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM