I’ve been working on an app that targets both iOS and OS X devices, mainly to teach myself to code in Objective C, but also to scratch an itch in the best open source tradition. In Xcode I’ve had two projects and been syncing code between them using copy and paste. This is not a great experience so I looked around for more elegant solutions. The one I settled on was inspired by Session 233: Sharing code between iOS and OS X from the 2014 WWDC. This involves creating a single project with multiple targets aimed at different platforms and devices.
I was bothered by the fact that despite following what was done in the session I couldn’t get my project to look the same, eventually after much experimenting I came up with the following process. The caveat is that this works for me, there are quite possibly better ways to achieve the same result, which I’d love to hear!
Create the project
The first thing you need to do is create a new project in Xcode, I usually start from the iOS Single View Application template, but it doesn’t actually matter.
On the above screen the key change is in setting the project’s name. If you chose an iOS application name the project MyApp-iOS, substituting MyApp for your own project name. If you chose an OS X application name the project MyApp-OSX. Click next and finish creating the project by selecting a save location and enabling Git for version control if needed.
Rename the project
In the project navigator select the top item, the project itself. Then move across to the file inspector on the righthand side and rename the project, dropping the suffix, to MyApp.
The window shown above pops up offering to rename the targets, however as we will be adding targets for other platforms later we want to prevent Xcode from being over helpful by deselecting the targets:
Clicking on rename might pop up another window where Xcode offers to turn on snapshots, I normally click enable but it doesn’t affect what we are trying to achieve here.
Adding a New Target
The next step is to add a new target to build your application for a different Apple platform. Click the + button at the bottom of the window, below the existing targets. In the window that opens select a different platform – in my case that’s an OS X Cocoa Application. Click next and name the target using the same convention as when you created the project: MyApp-OSX. Once you’ve added the additional target your project should look something like:
Adding Common Code
The whole point of this exercise to create common code that can be used in both targets. To do this create a group in the Project Navigator by right clicking on the project and selecting New Group. I normally name this MyApp-Common. Right clicking on the MyApp-Common group select New File and choose to create a new Objective C class.
The two things you need to take care with are: making sure you don’t create a class that inherits from a platform specific class and that you remember to add the class to both targets, as shown in the screenshot above. You should then end up with a project that looks like this:
The final task is to rename the products of the two targets, if you compile and then install or distribute you will end up with applications named MyApp-iOS and MyApp-OSX which isn’t what users expect to see. Renaming the product is a two step process. First, for each non-test target, you need to select the target, select Build Settings and search for Product Name. Once you’ve got the Product Name setting you can change the value to the name you want for your application:
Renaming the product in this way has an unexpected side effect – tests will no longer build and run. This is due to the linker being unable to link the test classes to the application classes as it’s still looking for the old application name (details here). To fix this you need to change the value for Test Host in the same way as you did for Product Name. Do this for both test targets, replacing all occurrences of MyApp-iOS and MyApp-OSX with MyApp (or whatever name you gave to your product in the previous step).
At this point you should be able to build, install, and run both targets. As I said at the start, I’m sure there’s either things I’m missing or a better way to do this entirely – I will be looking into creating a framework that builds for both iOS and OSX, and then including the framework in a multi-platform application project.