[英]Merging UIDocument changes for iCloud conflicts
我花了一些日子,現在試圖找到或弄清楚自己,如何以編程方式合並UIDocument變化的時候,通知UIDocumentStateChangedNotification火災和文檔的狀態有UIDocumentStateInConflict設置。
我可以找到的所有示例(Apple,Ray Wenderlich等)都詳細說明了提示用戶選擇版本方法。 我找不到能說明以編程方式合並的正確方法的任何方法。 這讓我感到擔心,因為它使我認為信任太不穩定了,通常可以避免使用它作為解決方案? 到目前為止,我的經驗鞏固了這一地位。
讓我詳細說明我嘗試中的每個問題區域。
1)出於合並目的,讀取當前文檔內容和NSFileVersion沖突版本的正確方法是什么? 在完成時使用任何帶有完成塊的東西真的很亂。 UIDocument的openWithCompletionHandler:不打算使用。 實際上,通常,推薦的只讀UIDocument方法是什么? 為什么打開僅用於閱讀的文檔? 我已經嘗試過使用UIDocument的readFromURL:,這對於當前文檔來說很好,但是如果我嘗試在任何NSFileVersion的沖突版本上使用它,它將讀取當前版本,而不是URL上的版本(我已經使用過MacOS終端深入到../ data / .DocumentRevisions-V100 / PerUID / ...文件中以確認這一點。) 對於沖突版本,對我而言唯一有效的方法是直接讀取訪問這些文件。 (例如NSData initWithContentsOfFile:)
2)一旦讀完文件的變體並設法合並,如何正確保存合並? 我找不到任何地方確實沒有記錄此文件。 我成功的唯一方法是重新使用NSFileVersion的沖突文件之一,將其覆蓋,然后使用UIDocument的replaceItemAtURL:使其成為當前文件。 在使用replaceItemAtURL :之后,我還嘗試使用UIDocument的revertToContentsOfURL :,但是它崩潰了,沒有給出任何原因。 由於沒有它,合並似乎可以很好地工作,所以我不擔心,但我想將其作為詳細信息包括在內。
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合並代碼。 我做出的錯誤假設之一是,在解析過程中需要包含UIDocument的revertToContentsOfURL:。 這是一個非常不穩定的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.