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
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?
Run the app again to reproduce the problem. Once the breakpoint hits, type the following commands to the lldb prompt:
The filename would be shown. Disable the breakpoint. Use the number # which was returned at breakpoint set before. e.g. 3
(2) What object does the app try to decode?
Same as above. Use a set of
po
commands to get some information. It seems an array of something.(3) What class does the app reject to decode?
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.
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()
ofAppDelegate
to get a chance of manually setting breakpoints.Appended:
I had misunderstood. The correction:
The
func configurePersistentStoreCoordinator
seems to be called when a classNSPersistentStore
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.func configurePersistentStoreCoordinator
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 theNSPersistentStore
we provided in that func are already there, I think.Tips for setting a breakpoint
Could you try this nasty workaround?
Good news and bad news
I have succeeded to reproduce the exact same error with Xcode 9.2 running on MacOS 10.13.2.
NSBinaryStoreSecureDecodingClasses
option.NSPersistentStoreCoordinator .migratePersistentStore()
aka-[NSPersistentStoreCoordinator migratePersistentStore:toURL:options:withType:error:]:
migratePersistentStore
,NSBinaryStoreSecureDecodingClasses
option is not set for both writing to and reading from a temporary file.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.Another, simpler workaround:The following code has a bug. Please refer to the bug fixed version cited below.
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 toNSPersistentStoreCoordinator.migratePersistentStore(...)
and then be used to instantiateNSPersistentStore
.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.
References: