TLDR – If your unit tests crash with DocumentManager service tried to send a message to a deallocated host proxy, make sure you’re dismissing any presented instance of UIDocumentPickerViewController.

In our Callisto Xcode project, we’ve got a lot of unit tests. Like over a thousand. We’re at the point where if tests have a 0.1% chance of failing, then it happens every time. Our tests need to be really rock solid, or there’s no way we’ll get a clean run which is the only way CI will let a build through.

Some time ago, we started noticing the occasional test failure with an uncaught exception:

DocumentManager service tried to send a message to a deallocated host proxy

It would crop up when running test both locally and in CI. With so many tests, we get the weird edge case now and then, but they’re not worth the time to track down. We ignored it. With the recent update to Xcode 14.3 and macOS 13.3, the DocManager exception went from occasional annoyance to ‘omg, this happens every time I unit test on iOS’. So now we have to fix it.

But what’s a DocManager? IDK – there’s nothing with that name in our code and nothing in the docs about an Apple framework called DocManager. Looking through the traceback, it’s pretty obvious that it’s some kind of internal Apple thing. Surprisingly, a Google search for this error returns absolutely no results. That’s never a good sign.

But what’s a DocManager do? Callisto is UIDocument based, so maybe DocManager manages documents? A bunch of the unit tests open instances of a UIDocument and aren’t 100% about cleaning those up, so maybe some housekeeping will help. Cue the plumbing montage. We added a some bookkeeping to make sure that any open UIDocuments were closed at the end of each test, so now we’re sure there are no dangling UIDocuments. No impact – still the DocManager throws the exception.

This particular problem is a huge pain to track down. Somewhere in the tests, things get into a bad state. Later, while another test is running, some background thread discovers the bad state and throws an exception. The cause of the crash and the actual crashing are quite loosely coupled, making it hard to pin down the culprit. It also means that the offending tests will run just fine by itself, but only cause a crash when a large number of tests run, giving the background issue time to percolate to the surface.

After a couple hours of testing the tests and narrowing down which ones fail, it started to look like UIDocumentPickerViewController might be involved. We’ve got some ViewControllers that open a docPicker and get feedback via the docPicker’s delegate methods. To test those, we programmatically tap a button to cause the dockPicker to be presented, get a handle to that docPicker and call the delegate with the docPicker and some fake results. This works great for testing! Except for that pesky exception that gets thrown now and then.

If we take a closer look at the exception, we’ll see there’s a little more info attached in the userInfo dict, specifically a file and line number, DOCRemoteViewControlller.m line 42. Not that we have access to the source, but the name of the file, “Remote View Controller”, does offer a hint of its purpose. Those docPicker view controllers offer our app an escape hatch out of the sandbox and into the rest of the file system on the device. In my limited understanding, the docPickers interface with a separate process that manages file system access, so they actually represent some state from another (remote) long lived process. In the end, these dangling, un-dismissed docPickers were the root of the problem. Make sure all your UIDocumentPickerViewControllers are dismissed properly and the problem goes away.

Whew!