Have you ever wanted to share code between apps? I have and honestly, code reuse with Xcode (in particular between apps) is not as straightforward as you might imagine. You do have some options which I’ll talk about here and once you get something set up this problem will be solved easily.
The reason I started looking into this was because I wanted to release several variations of my app (or rather my app’s database engine). Previously, I have done this by simply copying the core libraries as needed. Essentially, each app of mine would have it’s own Xcode project and point (or have copies) of the code files I needed.
This solution is unsatisfying as hell and it became enough of a chore in terms of app maintenance that I ended up pulling many of my apps from the store in 2011.
So, last month I started to investigate some options that would help with code reuse among apps. But first, let’s backup and talk about code reuse and why most developers strive to reuse code.
Let’s Talk About Code Reuse
Coders will often be tasked with solving the same or similar problems when making software. Since most people don’t want to code or copy the same thing over and over again we try to come up with ways of organizing code so that we can avoid repetition. Instead of writing a few lines of code we will encapsulate that same code in a function that we can reuse over and over again. Write once and use forever.
Not only does this help us keep our code smaller we can avoid mistakes in the future because we will build on a collection of code we already know works. When bugs are fixed, they are fixed for anything that uses that code. Instead of correcting an algorithm in 100 places, we correct it once.
In an Objective-C program like an iOS app, you may see code reuse implemented with functions, classes that support sub-classing and delegation, categories and blocks. Most developers are comfortable with this practice at the app level.
Tasting Notes and the system behind the app already followed patterns that enabled code reuse since I’m a stickler for object oriented patterns. However, a problem emerged when I wanted to expand the reach of the Tasting Notes database engine.
The Code Reuse Problem Across Apps
Here’s what I wanted to do:
I wanted a entire suite of applications that used the Tasting Notes database engine.
The database engine is about 40% of the Tasting Notes code. Some of the apps in the suite could also reuse the user interface elements on the Storyboards, while others like the Mac version would also use the model classes. Many apps on the app store are a part of a suite like this and I’m sure they reuse the code that they can.
I was very surprised that setting up my app suite didn’t go as smoothly as I thought. What started as a day or two project ended up turning into a multiple weeks project where I tested my options with Xcode. Here are the solutions I tried.
Static Library with Xcode Workspaces
Surely this was the way to go right? My plan was to remove the database engine (and model classes) from Tasting Notes and turned this into a static library. The same would go for the standard user interface components. I would have started out with three separate Xcode projects that I could keep organized with one Xcode workspace.
This works in principle, but the reality of managing the three projects even at this level was a pain. The UI part would need to reference the model part which in turn was referenced by the app. Each one had it’s own frameworks to link. Odd linking errors would show up. This solution was it’s own kind of mess, however with the simplest situation it did work more or less.
Problems really started to happen when I attempted to add a Mac Cocoa app to the workspace. Obviously, I couldn’t reuse the iOS user interface. But it turns out that even the database engine couldn’t be added to my Mac app without a lot of very confusing configuration changes to the static library. This was the point where I gave up to look for better solutions.
BOTTOM LINE While the most obvious solution, the static libraries added too much work to the process to make it worthwhile to use.
Pointing to Code with Workspaces
The next thing I tried still involved using Xcode workspaces (I really thought that this was the way to go). What I did was create a group independent of any Xcode project in the workspace that held the shared code. This was once step up from what I did before.
This didn’t really help much either. First of – the way I set up the group folders meant that there was no Git support which I need. Of course, I could have added this myself but I wasn’t sure if I could actually integrate with Xcode in this way. Things were already starting to get to complicated.
Also, this solution didn’t help me as much as I wanted with or without Git. Each project needed to add each code file and there is almost a hundred of them. I also had to link to the other frameworks needed by my shared model classes. Linking to other frameworks isn’t a big deal when its the built in stuff, but including the Google, Dropbox and other third-party frameworks I use because a chore with this approach.
BOTTOM LINE This approach didn’t help at all and actually made some things a bit more difficult.
This is something I really didn’t know you could do. Or rather, I knew about multiple targets but was so entrenched in how I used to set up projects that I never really considered what the purpose or implications of multiple targets were.
Here’s the deal: when you first create a new Xcode project you get one project and one target. The project contains all the assets like code, graphics and databases while the target defines what goes into the bundle that you use to actually run the application. A project is what you work on as a developer. The target is the app that you test in the simulator and ultimately gets sent to the app store.
It’s tempting to assume that the project and the target are the same thing. They’re not.
You can add as many targets as you like to your Xcode project. So you have use the same project and have targets for a bunch a iPhone apps, an iPad app and a Mac app. Use the Add Target button or select New > Target from the menu bar to add a new target. Each target gets it’s own configuration, it’s own Storyboard or starting Nib and it’s own app delegate.
Any file can be included in any target by setting the file’s target membership (located in the identity inspector). This includes the linked frameworks. If one target needs a custom version of a file you can add a custom file to the new target’s group and only include that file in the new target. This gives you a way to override entire code files.
The only issue here is that each time you add a new target you must make sure that each file is included in the new target. As far as I know the only way to do this is to go and look at each implementation file and add them to the target using the check box.
This was the way to go for me: I was able to have full Git support and this approach gives me code reuse. The solution is not very complicated. The only drawback is that I need to manually go into each file to set it’s target membership.
So, that’s how I did it.
What are you thoughts on this?
Please comment below…