Using Xcode Asset Catalogs with Multiple Targets
A few months ago, I released an App called NYC Bikes that quickly became the most popular non-official Citi Bike app. It resonates with busy New Yorkers because it takes far less time to get the same amount of information than the official app, and it actually works.
Expanding to other cities where Bike Shares exist was a no brainer. Just this last week I released the second of these bike share apps: Chicago Bikes.
The apps share the same UI, 99% of the same code base, with only API endpoints, colors, strings and images swapped out. When building the base framework that would allow me to create new apps for new cities, I found it particularly easy to use Xcode's new image asset catalog feature. This allowed me to place all shared images in one asset catalog, and the local images in another that would only apply to that particular city, separated by targets.
Then building the apps would pull in the right assets for that target from the
file that pertained to it. Unfortunately, Xcode had other plans.
Despite me checking and double checking that the target memberships were accurate, despite
cleaning my build again and again, despite wiping derived data folders and machine restarts,
Xcode would persistently copy the wrong images over from another
I didn't do any deep-dive debugging on the characteristics of the erroneous behavior,
but it appeared that it took images from the first
xcassets it found alphabetically.
In my case, all other targets copied their resources from my
file because it was the first one.
Googling around for solutions, I ended up trying all permutations of separating the bundles in Xcode folders, Finder folders, renaming bundles and other shenanigans, all to no avail. It appeared I would have to abandon the asset catalog all together and manually manage my images... like it was 2012 or something.
Alas, while sipping on a tasty Bulleit Rye Old Fashioned, I came up with a brilliant idea to hack together a fix! Here is how to hack it yourself:
Xcode didn't like it when I had multiple localized
xcassets bundles in the project,
so I ended up making just one. Create a
Local Images.xcassets file and add it to the project,
then ensure it belongs to all your targets:
Then, for each target, add a Run Script Build Phase. It should run near the beginning of your build process, and should definitely be run before any compilation or copy bundle files steps.
The basic premise here is that we are going to swap out the correct
file before the files get copied over with the binary. Enter the following code:
In plain English, this basically checks to see if the location of the target-specific
file (really a directory) exists. If it does, then check to see if it is different than the one already
added to Xcode. If it is different, remove the
xcassets that Xcode knows about and replace
it with the one for this target.
For my setup,
xcassetsFile (the one Xcode knows about) is at the root of the Images directory.
appSpecificXcassetsFile assets catalog is located within a folder outside
the project directory. This is essential otherwise Xcode will find the wrong images.
The App Specific directory it located up a level (notice the
and corresponds to the target's
You will have to edit the locations of the
xcassets files as they pertain to your project,
and perhaps some variables here and there, but this will get you started.
In the end, it should look something like this:
One downside of this method is that if you are making edits to the
xcassets file within Xcode,
you will need to copy those changes back to the app-specific ones to prevent them from being overwritten
on the next build. I am no longer tweaking these files on a regular basis so it works great for my workflow.
Lastly, I run a small software company called Urban Apps. It pays the bills so I can take the time to write helpful posts like this. If you found this page helpful at all, I would really appreciate it if you would check out my Apps on the iTunes App Store.
Was this page helpful for you? Buy me a slice of 🍕 to say thanks!