configurePersistentStoreCoordinator not called for

2020-04-18 06:03发布

I experience an odd behaviour regarding saving an NSPersistentDocument. I can create a new document which is autosaved without an issue. But when I save it write(to: ofType: for: absoluteOriginalContentsURL:) is called but it turns to that configurePersistentStoreCoordinator(for: ofType: modelConfiguration: storeOptions:) isn't called. That is unfortunate as I need to configure the store. Reason is that I need to register NSColor as decodable options[NSBinaryStoreSecureDecodingClasses] = NSSet(object: NSColor.self).

First attempt was to call it myself, but that didn't lead me anywhere. Calling it myself didn't register the settings as expected.

Code in my NSPersistentDocument looks like this:

 override func configurePersistentStoreCoordinator(for url: URL, ofType fileType: String, modelConfiguration configuration: String?, storeOptions: [String : Any]? = nil) throws {
        Swift.print("VTDocment.configurePersistentStoreCoordinator for \(url.lastPathComponent)")
        var options = addOptions(to: storeOptions)
        try super.configurePersistentStoreCoordinator(for: url, ofType: fileType, modelConfiguration: configuration, storeOptions: options)
    }      

    func addOptions(to: [String : Any]?) -> [String : Any] {
        var options = to != nil ? to! : [String:Any]()
        if #available(OSX 10.13, *) {
            options[NSBinaryStoreSecureDecodingClasses] = NSSet(object: NSColor.self)
        }
        options[NSMigratePersistentStoresAutomaticallyOption] = true
        options[NSInferMappingModelAutomaticallyOption] = true
        return options
    } 

override func write(to url: URL, ofType typeName: String, for saveOperation: NSDocument.SaveOperationType, originalContentsURL absoluteOriginalContentsURL: URL?) throws {
    try self.configurePersistentStoreCoordinator(for: url, ofType: typeName, modelConfiguration: "Document")
    do {
        Swift.print("Now... VTDocment.write to \(url.lastPathComponent)")
        try super.write(to: url, ofType: typeName, for: saveOperation, originalContentsURL: absoluteOriginalContentsURL)
    } catch {
        Swift.print("VTDocment.write error: \(error)")
    }
}

Result is the following output (second configurePersistentStoreCoordinator output is starting after the save as through menu command):

VTDocment.configurePersistentStoreCoordinator for Unsaved Visual Thinking with IBIS Document 12.ibisVT
VTDocment.configurePersistentStoreCoordinator for test.ibisVT
Now... VTDocment.write to test.ibisVT
value for key 'NS.objects' was of unexpected class 'NSColor'. Allowed classes are '{(
    NSNumber,
    NSString,
    NSCalendarDate,
    NSOrderedSet,
    NSDecimalNumber,
    NSUUID,
    NSDate,
    NSSet,
    NSNull,
    NSURL,
    NSData,
    NSDictionaryMapNode,
    NSDictionary,
    NSArray
)}'.
(null)

105827995370488

2018-02-09 05:53:52.250312+0100 Visual Thinking with IBIS[42589:19295813] -[NSException initialize]: unrecognized selector sent to instance 0x60400025d7c0
value for key 'NS.objects' was of unexpected class 'NSColor'. Allowed classes are '{(
    NSNumber,
    NSString,
    NSCalendarDate,
    NSOrderedSet,
    NSDecimalNumber,
    NSUUID,
    NSDate,
    NSSet,
    NSNull,
    NSURL,
    NSData,
    NSDictionaryMapNode,
    NSDictionary,
    NSArray
)}'.

EDIT Thanks for the questions @Tora! Here is a related stack trace. It's not from the same session but leading to the same result.

    0   CoreFoundation                      0x00007fff40f9ffcb __exceptionPreprocess + 171
1   libobjc.A.dylib                     0x00007fff67c41c76 objc_exception_throw + 48
2   Foundation                          0x00007fff430ef08f -[NSCoder(Exceptions) __failWithExceptionName:errorCode:format:] + 0
3   Foundation                          0x00007fff430ef20c -[NSCoder(Exceptions) __failWithExceptionName:errorCode:format:] + 381
4   Foundation                          0x00007fff43017555 -[NSCoder _validateAllowedClass:forKey:allowingInvocations:] + 239
5   Foundation                          0x00007fff42fd948c _decodeObjectBinary + 1944
6   Foundation                          0x00007fff42fdaa6f -[NSKeyedUnarchiver _decodeArrayOfObjectsForKey:] + 1945
7   Foundation                          0x00007fff42fde8c5 -[NSArray(NSArray) initWithCoder:] + 202
8   Foundation                          0x00007fff42fd9523 _decodeObjectBinary + 2095
9   Foundation                          0x00007fff42fd8bee _decodeObject + 417
10  Foundation                          0x00007fff42ff171b -[NSKeyedUnarchiver decodeObjectOfClasses:forKey:] + 409
11  CoreData                            0x00007fff40b77491 -[NSDictionaryMapNode initWithCoder:] + 97
12  Foundation                          0x00007fff42fd9523 _decodeObjectBinary + 2095
13  Foundation                          0x00007fff42fdaa6f -[NSKeyedUnarchiver _decodeArrayOfObjectsForKey:] + 1945
14  Foundation                          0x00007fff42fd9de8 -[NSDictionary(NSDictionary) initWithCoder:] + 202
15  Foundation                          0x00007fff42fd9523 _decodeObjectBinary + 2095
16  Foundation                          0x00007fff42fd8bee _decodeObject + 417
17  Foundation                          0x00007fff42ff171b -[NSKeyedUnarchiver decodeObjectOfClasses:forKey:] + 409
18  CoreData                            0x00007fff40bcfcc2 -[NSBinaryObjectStoreFile readBinaryStoreFromData:originalPath:error:] + 994
19  CoreData                            0x00007fff40bd019a -[NSBinaryObjectStoreFile readFromFile:error:] + 474
20  CoreData                            0x00007fff40aed247 -[NSDictionaryStoreMap initWithStore:fromPath:] + 215
21  CoreData                            0x00007fff40b75bd3 -[NSBinaryObjectStore initWithPersistentStoreCoordinator:configurationName:URL:options:] + 739
22  CoreData                            0x00007fff40b56e35 __91-[NSPersistentStoreCoordinator addPersistentStoreWithType:configuration:URL:options:error:]_block_invoke + 1845
23  CoreData                            0x00007fff40b69132 gutsOfBlockToNSPersistentStoreCoordinatorPerform + 210
24  CoreData                            0x00007fff40b52f75 _perform + 213
25  CoreData                            0x00007fff40a7d2cf -[NSPersistentStoreCoordinator addPersistentStoreWithType:configuration:URL:options:error:] + 559
26  CoreData                            0x00007fff40b596e4 __84-[NSPersistentStoreCoordinator migratePersistentStore:toURL:options:withType:error:]_block_invoke + 1284
27  CoreData                            0x00007fff40b69132 gutsOfBlockToNSPersistentStoreCoordinatorPerform + 210
28  libdispatch.dylib                   0x00007fff687f7d50 _dispatch_client_callout + 8
29  libdispatch.dylib                   0x00007fff6880b1d6 _dispatch_queue_barrier_sync_invoke_and_complete + 60
30  CoreData                            0x00007fff40b52f62 _perform + 194
31  CoreData                            0x00007fff40b590ef -[NSPersistentStoreCoordinator migratePersistentStore:toURL:options:withType:error:] + 351
32  AppKit                              0x00007fff3ebc8fd6 -[NSPersistentDocument writeToURL:ofType:forSaveOperation:originalContentsURL:error:] + 2367
33  Visual Thinking with IBIS           0x000000010003edb2 _T025Visual_Thinking_with_IBIS10VTDocumentC5writey10Foundation3URLV2to_SS6ofTypeSo10NSDocumentC013SaveOperationK0O3forAGSg016originalContentsH0tKF + 1570
34  Visual Thinking with IBIS           0x000000010003f22c _T025Visual_Thinking_with_IBIS10VTDocumentC5writey10Foundation3URLV2to_SS6ofTypeSo10NSDocumentC013SaveOperationK0O3forAGSg016originalContentsH0tKFTo + 284
35  AppKit                              0x00007fff3f0996aa -[NSDocument(NSDocumentSaving) _writeSafelyToURL:ofType:forSaveOperation:forceTemporaryDirectory:error:] + 870
36  AppKit                              0x00007fff3f09a394 -[NSDocument(NSDocumentSaving) _writeSafelyToURL:ofType:forSaveOperation:error:] + 28
37  AppKit                              0x00007fff3ebc9eb3 -[NSPersistentDocument writeSafelyToURL:ofType:forSaveOperation:error:] + 446
38  AppKit                              0x00007fff3f0a5294 __85-[NSDocument(NSDocumentSaving) _saveToURL:ofType:forSaveOperation:completionHandler:]_block_invoke_2.1146 + 238
39  AppKit                              0x00007fff3f0a5197 __85-[NSDocument(NSDocumentSaving) _saveToURL:ofType:forSaveOperation:completionHandler:]_block_invoke.1143 + 454
40  AppKit                              0x00007fff3f0a33ff __85-[NSDocument(NSDocumentSaving) _saveToURL:ofType:forSaveOperation:completionHandler:]_block_invoke_3.999 + 1962
41  AppKit                              0x00007fff3eae0eb4 -[NSDocument(NSDocumentSerializationAPIs) continueFileAccessUsingBlock:] + 238
42  AppKit                              0x00007fff3f0a04b9 __119-[NSDocument(NSDocumentSaving) _fileCoordinator:asynchronouslyCoordinateReadingContentsAndWritingItemAtURL:byAccessor:]_block_invoke_2 + 94
43  AppKit                              0x00007fff3ea5f8c0 __62-[NSDocumentController(NSInternal) _onMainThreadInvokeWorker:]_block_invoke.2153 + 157
44  CoreFoundation                      0x00007fff40f3858c __CFRUNLOOP_IS_CALLING_OUT_TO_A_BLOCK__ + 12
45  CoreFoundation                      0x00007fff40f1b043 __CFRunLoopDoBlocks + 275
46  CoreFoundation                      0x00007fff40f1ae08 __CFRunLoopRun + 3128
47  CoreFoundation                      0x00007fff40f19f43 CFRunLoopRunSpecific + 483
48  HIToolbox                           0x00007fff40231e26 RunCurrentEventLoopInMode + 286
49  HIToolbox                           0x00007fff40231b96 ReceiveNextEventCommon + 613
50  HIToolbox                           0x00007fff40231914 _BlockUntilNextEventMatchingListInModeWithFilter + 64
51  AppKit                              0x00007fff3e4fcf5f _DPSNextEvent + 2085
52  AppKit                              0x00007fff3ec92b4c -[NSApplication(NSEvent) _nextEventMatchingEventMask:untilDate:inMode:dequeue:] + 3044
53  AppKit                              0x00007fff3e4f1d6d -[NSApplication run] + 764
54  AppKit                              0x00007fff3e4c0f1a NSApplicationMain + 804
55  Visual Thinking with IBIS           0x0000000100019bed main + 13
56  libdyld.dylib                       0x00007fff68831115 start + 1

Many thanks in advance.

Update Solution from Tora (via method swizzling) works fine. However, sometimes (not sure when) the same issue can result. Here is the updated stack trace. This is mysterious. :-)

0   CoreFoundation                      0x00007fff40f9ffcb __exceptionPreprocess + 171
1   libobjc.A.dylib                     0x00007fff67c41c76 objc_exception_throw + 48
2   Foundation                          0x00007fff430ef08f -[NSCoder(Exceptions) __failWithExceptionName:errorCode:format:] + 0
3   Foundation                          0x00007fff430ef20c -[NSCoder(Exceptions) __failWithExceptionName:errorCode:format:] + 381
4   Foundation                          0x00007fff43017555 -[NSCoder _validateAllowedClass:forKey:allowingInvocations:] + 239
5   Foundation                          0x00007fff42fd948c _decodeObjectBinary + 1944
6   Foundation                          0x00007fff42fdaa6f -[NSKeyedUnarchiver _decodeArrayOfObjectsForKey:] + 1945
7   Foundation                          0x00007fff42fde8c5 -[NSArray(NSArray) initWithCoder:] + 202
8   Foundation                          0x00007fff42fd9523 _decodeObjectBinary + 2095
9   Foundation                          0x00007fff42fd8bee _decodeObject + 417
10  Foundation                          0x00007fff42ff171b -[NSKeyedUnarchiver decodeObjectOfClasses:forKey:] + 409
11  CoreData                            0x00007fff40b77491 -[NSDictionaryMapNode initWithCoder:] + 97
12  Foundation                          0x00007fff42fd9523 _decodeObjectBinary + 2095
13  Foundation                          0x00007fff42fdaa6f -[NSKeyedUnarchiver _decodeArrayOfObjectsForKey:] + 1945
14  Foundation                          0x00007fff42fd9de8 -[NSDictionary(NSDictionary) initWithCoder:] + 202
15  Foundation                          0x00007fff42fd9523 _decodeObjectBinary + 2095
16  Foundation                          0x00007fff42fd8bee _decodeObject + 417
17  Foundation                          0x00007fff42ff171b -[NSKeyedUnarchiver decodeObjectOfClasses:forKey:] + 409
18  CoreData                            0x00007fff40bcfcc2 -[NSBinaryObjectStoreFile readBinaryStoreFromData:originalPath:error:] + 994
19  CoreData                            0x00007fff40bd019a -[NSBinaryObjectStoreFile readFromFile:error:] + 474
20  CoreData                            0x00007fff40aed247 -[NSDictionaryStoreMap initWithStore:fromPath:] + 215
21  CoreData                            0x00007fff40b75bd3 -[NSBinaryObjectStore initWithPersistentStoreCoordinator:configurationName:URL:options:] + 739
22  CoreData                            0x00007fff40b56e35 __91-[NSPersistentStoreCoordinator addPersistentStoreWithType:configuration:URL:options:error:]_block_invoke + 1845
23  CoreData                            0x00007fff40b69132 gutsOfBlockToNSPersistentStoreCoordinatorPerform + 210
24  CoreData                            0x00007fff40b52f75 _perform + 213
25  CoreData                            0x00007fff40a7d2cf -[NSPersistentStoreCoordinator addPersistentStoreWithType:configuration:URL:options:error:] + 559
26  CoreData                            0x00007fff40b596e4 __84-[NSPersistentStoreCoordinator migratePersistentStore:toURL:options:withType:error:]_block_invoke + 1284
27  CoreData                            0x00007fff40b69132 gutsOfBlockToNSPersistentStoreCoordinatorPerform + 210
28  libdispatch.dylib                   0x00007fff687f7d50 _dispatch_client_callout + 8
29  libdispatch.dylib                   0x00007fff6880b1d6 _dispatch_queue_barrier_sync_invoke_and_complete + 60
30  CoreData                            0x00007fff40b52f62 _perform + 194
31  CoreData                            0x00007fff40b590ef -[NSPersistentStoreCoordinator migratePersistentStore:toURL:options:withType:error:] + 351
32  AppKit                              0x00007fff3ebc8fd6 -[NSPersistentDocument writeToURL:ofType:forSaveOperation:originalContentsURL:error:] + 2367
33  Visual Thinking with IBIS           0x00000001000404a2 _T025Visual_Thinking_with_IBIS10VTDocumentC5writey10Foundation3URLV2to_SS6ofTypeSo10NSDocumentC013SaveOperationK0O3forAGSg016originalContentsH0tKF + 1570
34  Visual Thinking with IBIS           0x000000010004091c _T025Visual_Thinking_with_IBIS10VTDocumentC5writey10Foundation3URLV2to_SS6ofTypeSo10NSDocumentC013SaveOperationK0O3forAGSg016originalContentsH0tKFTo + 284
35  AppKit                              0x00007fff3f0996aa -[NSDocument(NSDocumentSaving) _writeSafelyToURL:ofType:forSaveOperation:forceTemporaryDirectory:error:] + 870
36  AppKit                              0x00007fff3f09a394 -[NSDocument(NSDocumentSaving) _writeSafelyToURL:ofType:forSaveOperation:error:] + 28
37  AppKit                              0x00007fff3ebc9eb3 -[NSPersistentDocument writeSafelyToURL:ofType:forSaveOperation:error:] + 446
38  AppKit                              0x00007fff3f0a5294 __85-[NSDocument(NSDocumentSaving) _saveToURL:ofType:forSaveOperation:completionHandler:]_block_invoke_2.1146 + 238
39  AppKit                              0x00007fff3f0a5197 __85-[NSDocument(NSDocumentSaving) _saveToURL:ofType:forSaveOperation:completionHandler:]_block_invoke.1143 + 454
40  AppKit                              0x00007fff3f0a33ff __85-[NSDocument(NSDocumentSaving) _saveToURL:ofType:forSaveOperation:completionHandler:]_block_invoke_3.999 + 1962
41  AppKit                              0x00007fff3eae0eb4 -[NSDocument(NSDocumentSerializationAPIs) continueFileAccessUsingBlock:] + 238
42  AppKit                              0x00007fff3f0a04b9 __119-[NSDocument(NSDocumentSaving) _fileCoordinator:asynchronouslyCoordinateReadingContentsAndWritingItemAtURL:byAccessor:]_block_invoke_2 + 94
43  AppKit                              0x00007fff3ea5f8c0 __62-[NSDocumentController(NSInternal) _onMainThreadInvokeWorker:]_block_invoke.2153 + 157
44  CoreFoundation                      0x00007fff40f3858c __CFRUNLOOP_IS_CALLING_OUT_TO_A_BLOCK__ + 12
45  CoreFoundation                      0x00007fff40f1b043 __CFRunLoopDoBlocks + 275
46  CoreFoundation                      0x00007fff40f1ae08 __CFRunLoopRun + 3128
47  CoreFoundation                      0x00007fff40f19f43 CFRunLoopRunSpecific + 483
48  HIToolbox                           0x00007fff40231e26 RunCurrentEventLoopInMode + 286
49  HIToolbox                           0x00007fff40231b96 ReceiveNextEventCommon + 613
50  HIToolbox                           0x00007fff40231914 _BlockUntilNextEventMatchingListInModeWithFilter + 64
51  AppKit                              0x00007fff3e4fcf5f _DPSNextEvent + 2085
52  AppKit                              0x00007fff3ec92b4c -[NSApplication(NSEvent) _nextEventMatchingEventMask:untilDate:inMode:dequeue:] + 3044
53  AppKit                              0x00007fff3e4f1d6d -[NSApplication run] + 764
54  AppKit                              0x00007fff3e4c0f1a NSApplicationMain + 804
55  Visual Thinking with IBIS           0x000000010001a3dd main + 13
56  libdyld.dylib                       0x00007fff68831115 start + 1

4条回答
▲ chillily
2楼-- · 2020-04-18 06:31

Thank you @Wizard of Kneup!

That clearly shows the application certainly read a document file and then encounter the error.

Let's start investigation.

(1) What file does the app attempt to read?

(lldb) breakpoint set -n '-[NSBinaryObjectStoreFile readFromFile:error:]'    
Breakpoint 3: # locations.

Run the app again to reproduce the problem. Once the breakpoint hits, type the following commands to the lldb prompt:

po $rdi
p (SEL)$rsi
po $rdx
po $rcx
po $r8 
po $r9

The filename would be shown. Disable the breakpoint. Use the number # which was returned at breakpoint set before. e.g. 3

(lldb) breakpoint disable 3

(2) What object does the app try to decode?

(lldb) breakpoint set -n '-[NSKeyedUnarchiver _decodeArrayOfObjectsForKey:]'
Breakpoint 4: # locations.

Same as above. Use a set of po commands to get some information. It seems an array of something.

(lldb) breakpoint disable 4

(3) What class does the app reject to decode?

(lldb) breakpoint set -n '-[NSCoder _validateAllowedClass:forKey:allowingInvocations:]'

I wrote the breakpoints one by one and disable each time. But, no need to do that. Alternatively, set all breakpoints at a time and do hit-and-investigate-then-continue.

The questions are why the app reads the file. That might be for migration.

-[NSPersistentStoreCoordinator migratePersistentStore:toURL:options:withType:error:]

But why override func configurePersistentStoreCoordinator() is not called? I have no knowledge on that now.

I hope you would have some hints for getting closer to a solution.

Added:

To set a breakpoint, sometime we need to stop at the very beginning of execution of application.

For instance, set a breakpoint at init() of AppDelegate to get a chance of manually setting breakpoints.

class AppDelegate: NSObject, NSApplicationDelegate {

  override init() {
=>  super.init()
  }

}

Appended:

I had misunderstood. The correction:

The func configurePersistentStoreCoordinator seems to be called when a class NSPersistentStore is instantiated with a desired URL, i.e. filename. So the timing is not related to the action of reading from nor writing to a document file. Instead, it is the first time when the internal document is about to be connected to the URL.

  • Opening an existing file -> the func configurePersistentStoreCoordinator is called.
  • File > New and then Save... -> the func is called.

The fact we had seen that func configurePersistentStoreCoordinator was not called upon SaveAs seems to be normal, if the document has been loaded from an existing file in advance. The options for the NSPersistentStore we provided in that func are already there, I think.

Tips for setting a breakpoint

enter image description here

查看更多
贪生不怕死
3楼-- · 2020-04-18 06:31

Could you try this nasty workaround?

class CustomPersistentStoreCoordinator : NSPersistentStoreCoordinator {

  static var m1 : Method? = nil
  static var m2 : Method? = nil

  override func migratePersistentStore(_ store: NSPersistentStore, to URL: URL, options: [AnyHashable : Any]? = nil, withType storeType: String) throws -> NSPersistentStore {
    var opt: [AnyHashable : Any] = options ?? [:]

    if #available(OSX 10.13, *) {
      opt[NSBinaryStoreSecureDecodingClasses] = NSSet(array: [ NSColor.self ])
    }

    let m1 = CustomPersistentStoreCoordinator.m1!
    let m2 = CustomPersistentStoreCoordinator.m2!
    method_exchangeImplementations(m2, m1)

    let x = try self.migratePersistentStore(store, to: URL, options: opt, withType: storeType)

    method_exchangeImplementations(m1, m2)
    return x
  }
}

class Document: NSPersistentDocument {

  override init() {
    super.init()

    let s1 = #selector(NSPersistentStoreCoordinator.migratePersistentStore(_:to:options:withType:))
    let s2 = #selector(CustomPersistentStoreCoordinator.migratePersistentStore(_:to:options:withType:))
    let m1 = class_getInstanceMethod(NSPersistentStoreCoordinator.self, s1)!
    let m2 = class_getInstanceMethod(CustomPersistentStoreCoordinator.self, s2)!
    CustomPersistentStoreCoordinator.m1 = m1
    CustomPersistentStoreCoordinator.m2 = m2
    method_exchangeImplementations(m1, m2)
  }

  override func configurePersistentStoreCoordinator(for url: URL, ofType fileType: String, modelConfiguration configuration: String?, storeOptions: [String : Any]? = nil) throws {
    // keep yours
  }

}
查看更多
走好不送
4楼-- · 2020-04-18 06:51

Good news and bad news

I have succeeded to reproduce the exact same error with Xcode 9.2 running on MacOS 10.13.2.

  1. The version 1 of document file is still in a Autosave directory.
  2. Document.xcdatamodel is updated to version 2 (Document 2.xcdatamodel)
  3. When the application starts, the file of version 1 in the Autosave directory is read with NSBinaryStoreSecureDecodingClasses option.
  4. The app works well.
  5. File > Save... triggers NSPersistentStoreCoordinator .migratePersistentStore() aka -[NSPersistentStoreCoordinator migratePersistentStore:toURL:options:withType:error:]:
  6. During the course of migratePersistentStore, NSBinaryStoreSecureDecodingClasses option is not set for both writing to and reading from a temporary file.
  7. The error occurs.

Somewhat strange for me. Why the migration is executed upon saving, instead of upon opening a file? The lightweight migration has been already done at the step 3?

EDIT:

It seems that the name of migratePersistentStore does not mean migration from old version to new version. It seems to mean migrate from an old persistent store with an old URL aka filename and/or file format to a new persistent store with a new URL and/or file format. So it is called when Save As... or Saving a new document which is already saved in an Autosave directory.

查看更多
Juvenile、少年°
5楼-- · 2020-04-18 06:58

Another, simpler workaround:

The following code has a bug. Please refer to the bug fixed version cited below.

extension NSPersistentStoreCoordinator {

  @objc func x_migratePersistentStore(_ store: NSPersistentStore, to URL: URL, options: [AnyHashable : Any]? = nil, withType storeType: String) throws -> NSPersistentStore {
    var opt: [AnyHashable : Any] = options ?? [:]

    if #available(OSX 10.13, *) {
      opt[NSBinaryStoreSecureDecodingClasses] = NSSet(array: [ NSColor.self ])
    }

    return try x_migratePersistentStore(store, to: URL, options: opt, withType: storeType)
  }

}

class Document: NSPersistentDocument {

  override init() {
    super.init()

    let s1 = #selector(NSPersistentStoreCoordinator.migratePersistentStore(_:to:options:withType:))
    let s2 = #selector(NSPersistentStoreCoordinator.x_migratePersistentStore(_:to:options:withType:))
    let m1 = class_getInstanceMethod(NSPersistentStoreCoordinator.self, s1)!
    let m2 = class_getInstanceMethod(NSPersistentStoreCoordinator.self, s2)!
    method_exchangeImplementations(m1, m2)
  }

  override func configurePersistentStoreCoordinator(for url: URL, ofType fileType: String, modelConfiguration configuration: String?, storeOptions: [String : Any]? = nil) throws {
    // keep yours
  }

}

Added:

This workaround is going to be a practical solution until they enhance NSPersistentDocument.write(to...) to take into account of options or they implement other means to handle options. Those options will be given to NSPersistentStoreCoordinator.migratePersistentStore(...) and then be used to instantiate NSPersistentStore.

Bug Fixed Version:

There was a bug in the preceding workaround. Opening document files, creating new files, making changes, waiting for thirty second to the document being auto-saved, closing them, and/or save-as-ing them randomly would cause the initial error. Reported by Wizard of Kneup.

Here is a fixed version using singleton to make sure swizzling is applied only once.

extension NSPersistentStoreCoordinator {

  @objc func x_migratePersistentStore(_ store: NSPersistentStore, to URL: URL, options: [AnyHashable : Any]? = nil, withType storeType: String) throws -> NSPersistentStore {
    var opt: [AnyHashable : Any] = options ?? [:]

    if #available(OSX 10.13, *) {
      opt[NSBinaryStoreSecureDecodingClasses] = NSSet(array: [ NSColor.self ])
    }

    return try x_migratePersistentStore(store, to: URL, options: opt, withType: storeType)
  }

  class MigratePersistentStoreInitializer {
    init() {
      let s1 = #selector(NSPersistentStoreCoordinator.migratePersistentStore(_:to:options:withType:))
      let s2 = #selector(NSPersistentStoreCoordinator.x_migratePersistentStore(_:to:options:withType:))
      let m1 = class_getInstanceMethod(NSPersistentStoreCoordinator.self, s1)!
      let m2 = class_getInstanceMethod(NSPersistentStoreCoordinator.self, s2)!
      method_exchangeImplementations(m1, m2)
    }

    static let singlton = MigratePersistentStoreInitializer()  // Lazy Stored Property
  }

}

class Document: NSPersistentDocument {

  override init() {
    super.init()
    let _ = NSPersistentStoreCoordinator.MigratePersistentStoreInitializer.singlton
  }

  override func configurePersistentStoreCoordinator(for url: URL, ofType fileType: String, modelConfiguration configuration: String?, storeOptions: [String : Any]? = nil) throws {
    // keep yours
  }

}

References:

查看更多
登录 后发表回答