🐞 Insidious Bugs #1: Today Extensions
This app could not be installed at this time.
Today Lickability is relaunching Insidious Bugs on our blog. Insidious Bugs is an occasional series in which we share stories of overcoming obscure issues in iOS development with the goal of saving you the time we lost, should you encounter the issues yourself.
In this first entry, we conquer an instance of Xcode’s friendly “This app could not be installed at this time” message. We encountered this when attempting to run our project that includes a Today Extension, or widget, in the iOS Simulator.
Would you believe that all it took to solve this issue was this seemingly useless file? Well, that and many, many hours of failed attempts.
How We Got Here
“This app could not be installed at this time” doesn’t give us a lot to work with, but let’s explore the somewhat unique setup that got us here.
We recently added Today Extensions to multiple projects, which all share the same layout. We tend to keep things DRY (don’t repeat yourself), so we made our approach generic and reusable across multiple projects via a shared framework. In our framework, the Today Extension displays an arbitrary number of cells that have a thumbnail image and two labels. The framework doesn’t care what type of content is to be displayed, whether it’s social media posts, news, reminders, etc.—it simply formats and displays the data written to the shared container by the host app in the exact same way. Here’s an example:
Hooray! This will save us time and tedious boilerplate as we add our Today Extensions to our projects. We simply need to add the framework and associate the view controller in
MainInterface.storyboard (part of the Today Extension’s target) with the one found in the framework. At least that’s what we thought. Everything compiled just fine, but we couldn’t debug our app. “This app could not be installed at this time.” 🤔 Since Xcode’s message didn’t point us in any particular direction on how to resolve the bug, we searched log files for anything that was amiss. Every time we’d attempt to build and run, a message similar to the following was printed to
mobile\_installation.log.0 within the simulator’s logs folder:
<err> (0x700008983000) -[MIExecutableBundle makeExecutableWithError:]: 1162: Failed to chmod /Users/Michael/Library/Developer/CoreSimulator/Devices/51417E58-232D-45D5-9DAB-EB4C6F0AA301/data/Library/Caches/com.apple.containermanagerd/Bundle/Application/0D7DF887-D2D6-45F7-86DB-AACE8F2AE875/LickabilityTweets.app/PlugIns/TodayExtension.appex/TodayExtension : No such file or directory (NSPOSIXErrorDomain:2 (null))
This error was more descriptive, but still didn’t lead us to a solution. “Why wouldn’t the app extension exist on disk after a build succeeded?” we asked, before making dozens of trial and error attempts across several hours to no avail.
Eventually, we found that moving the view controller back to the Today Extension target would resolve the issue, but we weren’t satisfied with duplicating that file across multiple projects. In a stroke of genius (read: luck) we asked each other, “Wait… is it the fact that the view controller needs to be a part of the extension target, or is it just that we linked something to the target that made the difference?” After all, our “Compile Sources” build phase required nothing, since by design, any executable code used by the widget would live in our shared framework.
So we just added something to our target to see if it made any difference. A single Swift file without any code.
We thought we had it. But at least we could build and run now. On launch of the Today Extension target, Xcode’s console would display the following:
Unknown class \_TtC19SharedExtensionCode19TodayViewController in Interface Builder file.
It seemed as though despite the fact that we were selecting the appropriate view controller class from the appropriate framework within
MainInterface.storyboard, our class was still nowhere to be found at run time. After much more trial and error, we added a bit more code to
FixEmptyTarget.swift. We wanted to make sure everything was properly linked. We imported our shared framework into the file to see if it built (it did) and wrote some test code to see if we could actually make reference to the view controller class (we could). 🧐 “How come we can reference this view controller from a different framework in code, but not in a storyboard?” we pondered. We tried running this, just to see if anything more helpful would print to the console with our reference to the disappearing view controller in code, and…
Michael: That… did it?
Andrew: It’s time to relaunch Insidious Bugs.
Simply referencing our view controller in code and adding an otherwise useless file to our target solved this mysterious series of problems, and now we finally have our Today Extension framework. The full, documented version of
FixEmptyTarget.swift looks like the following:
Bananas, right? We sincerely hope you don’t encounter an issue like this in your travels. And if you do, we hope this post helped you get back up and running quickly!
Here are the bugs we’ve filed for Apple about these issues:
- Today Extension with no linked files fails to install in the simulator (39237943, closed as intended behavior)
- Follow-up: Difficult to understand error message with today extension that has no linked files (39974757)
- View controller referenced only from a storyboard not found at runtime (39238390, Closed as duplicate of 4691676)