I recently decided to monetise the upcoming version of Mignori as a free “Lite” app and a paid “Pro” app. This was a prevalent business model before IAPs were a thing, and it almost died off completely when they were introduced. Despite all that, some apps still monetise this way, and I think it’s worth talking about how to do it properly.
Before we get started, I hate writing iOS SDK tutorials and specifying I’m going to be using an specific language. SDK tutorials should be written to be language-agnostic (while the code snippets can be in an specific language), because anyone who knows Objective-C can pick up Swift and vice versa. Despite all that, I’m explicitly stating that we will be using Swift here because to achieve this, you need to know about macros, and many people think Swift doesn’t support macros the way Objective-C does, so many people think that what I’m going to show you in this tutorial is simply not possible. Let me say beforehand that the techniques here apply in both languages, but the things you have to tinker with will be slightly different depending on what language you are writing your app in.
The techniques described here will be useful for many other things. Some apps release different “versions” of their apps that are not necessarily “Lite” and “Pro” only (MoneyWiz comes to mind). This post will walk you through doing exactly that.
So let’s get started!
What Not To Do
If you have been writing for iOS for a long time, chances are you have attempted to create both versions of your app, and you did it wrong. Let me tell how you shouldn’t approach the idea of writing a “Lite” and “Pro” versions of your app.
So you just finished writing the “Pro” version of your app and you decided to build the “Lite” version of it. No easier way than to Right Clicking your project in Finder and hitting “duplicate”, right?
This will cause an unnecessary mess because you will have two projects to maintain. Even if the projects are exactly the same at first, as you “numb down” the second project to make it the “Lite” version, chances are you are going to break things in the existing codebase. Not only that, but when you find a bug in one of your versions, you will have to fix it in both projects. And not only that, but if your Lite version is heavily modified, what guarantees you the same fix will work in both projects?
Another chaotic way of doing things is to maintain one project, but to branch out to different versions of your app.
Subversion tools like Git do a great job trying to keep your codebase consistent across different branches, so you may be thinking that keeping a common “core” branch and then two different branches for your app versions is a good idea. It isn’t. It will still be chaotic to maintain.
What To Do Instead
Targets are the right way to create “Lite” and “Pro” versions of your app. Targets, amongst other things, allow you to compile different projects with the same sources.
From Apple’s Documentation:
A target specifies a product to build and contains the instructions for building the product from a set of files in a project or workspace. A target defines a single product; it organizes the inputs into the build system—the source files and instructions for processing those source files—required to build that product. Projects can contain one or more targets, each of which produces one product.
So each Target produces exactly one product. In this case, your “Lite” app will be a target, and the “Pro” one will be another. If you have written App Extensions or Apple Watch apps, you have worked with Targets before.
From here on out I will show specific techniques and configurations I did on Mignori, as well as related techniques that, while I personally did not use, they may be useful to you in your projects.
We are going to assume you just finihed your “Pro” app, and are ready to numb it down to “Lite”. Click your Project’s “Blueprint” and then take a look at your targets (if your target’s don’t show up, click the button to show them). Depending on how you configured your project (whether you added unit tests or not, UI tests, etc), you may have more than just your main app target.
In the unlikely case you don’t know what these configurations are, you basically configure your targets, and the Bundle Identifier must be unique for each app target, otherwise your users won’t be able to have both versions installed at the same time. Doing this mistake was and is still pretty common. Please don’t do it. If you plan on having exportable data from the “Lite” version to the “Pro” version, you are gonna have a hard time if you make that mistake.
Viewing your targets, right-click your app target and click “duplicate”. This will do exactly what you expect it to do.
Initial Target Configurations
We have been through this already, so the first thing you should do is to change the bundle identifier of the new app.
I personally follow the standard reverse DNS format, and then the last “period” contains the app name, along with a version identifier.
You can also rename the target itself, by slowly double-clicking it. My two targets are named “Mignori” and “MignoriLite” respectively. If your target name contains a space, it won’t compile – I have searched heavens and oceans to find the solution to this, but couldn’t. By default, apps will have the same name of your Target. I do not want the “Lite” app to be called “MignoriLite” in my users’ screens, but I’d rather call it “Mignori Lite”. To fix this, open the “Info” tab of the target you want to rename and search for the “Bundle name” key. You can type your app name with spaces here to add the “Lite”.
The app is called “NewMignori” because it has been rewritten from scratch. That’s besides the point and don’t worry about giving such words to your own apps.
With that essential configuration out of the way, the easiest thing to change is the app icon. To change the icon of your targets (you may want to do this to add a “Lite” label, for example), open the graphics assets file (Assets.xcassets), click the “+” icon to the bottom, hover over “App Icon & Launch Images”, and select “New iOS App Icon”. You can rename the assets here as you wish. My “Pro” app’s icon is simply called “AppIcon”, and the “Lite” apps’s icon is called “AppIcon-Lite”.
Go back to your Targets and select your new “Lite” target from the previous step. Find the “App Icons and Launch Images” section. When you click the drop down in “App Icons Source”, you will see all your app icons sources (if you named your icons like mine, you will see “AppIcon” and “AppIcon-Lite”. You know which one to select. Easy!
If you select a file – any file (other than the Blueprint) – you can select what target it belongs to. Take a look at the Utilities window and find the “Target Membership” section. This allows you to create target-specific resources and even code files. Creating target-specific code files is probably going to be overkill in most cases. I will show you how to execute code specific to each target in just one file later, but the option to do it is there, if you ever really need to. Some valid uses for this may be, for example, different “License.txt” files, or even different demo data sources.
The Fun Part – Target-Specific Code, In The Same File
Like I said, creating whole new Code files for targets can be overkill and won’t be necessary 99% of the time. If you were to do that for every source file, you would have the same problem as if you created different projects for both versions of your app. Think about it – You would be creating redundant code that you would have to maintain in at least two different places!
Compiler Custom Flags
If you have written lots of Objective-C and even pure C or C++ code before, you know you can create conditionalyl-compilated code. You do this using #define macros:
#define DEBUG 1 #if DEBUG // Conditional code to run when the DEBUG flag is set. #endif
This code would only be run when the macros “DEBUG” exists. As you may know, before the compiler actually compiles anything, it searches for and replaces appearances of #define macros with their current value. Chances are you have seen header guards in good ol’ C or C++ (or even on bridge files from Objetive-C to Swift!).
#ifndef Bridge_h #define Bridge_h #import "SWRevealViewController.h" #endif /* Bridge_h */
This prevents the #import statements from being run more than once, as multiple same includes wouldn’t compile (technically they would, as #import and #include work differently, but when you create a bridging header, the header guard is there). In C and C++, anything that starts with a pound sign (#) is known as a preprocessor directive, and it’s code that gets “replaced” before the compiler tries to do its job.
Why all this discussion on the C and C++ macros? Because as Objective-C is a superset of C, it is expected you can use them with Objective-C too. And you can. What many people don’t know about is that they (sorta) work with Swift, too. At least for our intents and purposes, they work.
I’m telling you all this because there’s a particular limitation on how Swift deals with preprocessor macros vs. Objective-C (or its subset). In Objective-C, you can grab the actual value of the macros to use it later. Consider this example:
#define MY_STRING @"OH EM GEE" NSLog(MY_STRING)
This would work because the preprocessor will actually replace MY_STRING with @”OH EM GEE”, then the code will be handed to the compiler to do its job.
But in Swift, this is not the case. The most useful thing you can do with macros is to check if a definition has taken place. You can do pretty insane stuff with macros, so it makes sense the people behind Swift chose not to fully support them.
Just cut to the chase already…
Basically, you can do this in Swift (and in Objective-C):
#if PRO_VERSION // Code to compile if the app is the pro version // Adapt your UI, do anything that you need for your app's full version. #endif #if LITE_VERSION // Code to compile if the app is the lite version // Limit functionality, add (annoying) reminders to buy the pro version, etc.. #endif
How do we achieve this? For each target, open the “Build Settings” tab and search for “Swift Compiler – Custom Flags”. You set the “flags” (in the example above, LITE_VERSION and PRO_VERSION are flags) you want to use here, and not only can you set them per target, but also per build configuration (debug or release).
For example, this is what my app’s Lite version looks like:
And for my Pro version:
Each flag, like you can see, needs to be preceded by a “-D”. This tells your app that Swift will provide those definitions to it.
Slightly irrelevant to this post, you can also see that I have -DDEBUG and -DRELEASE flags. I tend to do more verbose logging when running in debug mode, and these flags help me do just that.
You need to compile and run each project separately, so keep that in mind if you use autoincrementing build numbers (so you don’t wonder why the build numbers of your main and lite apps are so different). You also need to archive them individually for App Store submission. But even with these extra steps, it’s much easier and faster than keeping track of separate projects or subversion branches.
I hope you also realised there’s a lot you can do with the techniques described here. Each target has their own extensive configuration, so you can play around and see what else you can do. You can even change the deployment target of each app, so if you ever need to support older iOS versions but the chances of breaking new code are high, you can just create a new target and work from there. The options are vast, and I hope you learned something new.
I based the initial draft of this post on http://www.raywenderlich.com/68613/create-paid-lite-version-iphone-app about the same topic. However, not only is that tutorial outdated, but I also personally think it’s overkill to create a whole demo project to talk about this. Tutorials that cover everything, from the “File > New Project” Step to the point of the tutorial, can be helpful for newcomers, but they can be overly verbose for people who are already familiar with the topic in hand (in this case, Xcode and iOS development). That said, I apologise for pointing out the obvious, but a post can sound weird if it doesn’t have certain words or instructions (like “right-click” and “duplicate”).