How to Create Both a Paid and Lite Version of an iPhone App

Learn how to create both a paid and lite version of an iPhone app in the same project, using Xcode build settings. By Dani Arnaout.

Leave a rating/review
Save for later
Share

There are a ton of different ways you can monetize your app, and the most straightforward way is to make it paid. Unfortunately, a large number of users are not willing to purchase an app without trying it first.

One way to solve this problem on the App Store is to offer two different versions of your app:

  • A free/lite version. These are usually the noisy kind of apps that remind you to buy the paid version to unlock all of the amazing premium features. They commonly monetized by integrating ad networks such as iAd or AdMob.
  • A paid/full version. These usually cut the ads and enable features to further enhance the usefulness of the app.

In this tutorial, you will learn how to create both a paid and lite version of an iPhone app in a single Xcode project, using Xcode target settings.

Specifically, you will create an app focused on iOS interview questions, where the lite version has ads and the paid version does not.

Note: An alternate way to accomplish this is to have a single app that is free, which includes an in-app purchase to hide ads or unlock extra features or content. This was done in the popular app Letterpress.

Which option is best for you depends on your particular app. If you decide to go down the in-app purchase route, we have a tutorial for that.

The Bad Approach: Multiple Projects

The bad approach to creating both a paid and lite version of an app is to have multiple projects:

  1. Create a free version of the app.
  2. Copy the project folder and rename it to something like “Paid Version”.
  3. Add your extra features to the paid app, and Voila! You have both a free and paid version that you can build and deploy to the App Store.

That’s nice and simple, and it will work, but pretty soon you’re going to run into some big headaches. You now have two completely separate projects to update – so if you find a bug, you need to update it in both places! That’s a hassle, it’s error prone, and it’s not good programming practice.

So how can you change your approach to managing two versions that will avoid this issue?

The Better Approach: Multiple Targets

The better approach is to have just a single project, with multiple targets instead – one for each version of your app. So what are targets, and how do they help us achieve this goal?

Apple describes targets as follows:

“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.”

In plain english, this means that targets define products that each have their own configurations and source files within a single project. This sounds like a perfect solution for creating a free and paid version of an app while avoiding multiple projects and duplicate code!

Each target has its own plist that defines values like the bundle identifier, version number and build number which allow you to build unique products within a project. The target also has settings accessible from the project editor in XCode, including Preprocessor Macros found under Build Settings:

Target Settings

For this tutorial, you’ll use Preprocessor Macros to identify the product version in your code, thus enabling logical decisions based on the version. You’ll learn how to configure the Plist and Build Settings (most importantly the Preprocessor Macros) to enable product version differences under the same codebase. You’ll also learn how to easily swap assets used by different targets – in this case icons – to further customize product versions.

With that knowledge, your new plan for multiple versions will look something like this:

  1. Create the Free version of your app using a single target.
  2. Create and configure a second target for the paid version.
  3. Tie common source and unique assets (such as icons) to the new target.
  4. Add features for the paid version to your code, wrapped in version checks.

Getting Started

To get you started, I’ve created a simple application that will serve as a cree version of an app focused on iOS interview questions. You will enhance it by adding a paid version.

Download the starter project, unzip it and open it in XCode. Run your project and you’ll see a welcome screen that has some static text and a button:

 Welcome Screen

Once the user presses the Test my skills! button, the quiz will start:

3- Quiz Screen

Play a little bit and see how well you do. Once you hit the question limit, you’ll see an alert with your score:

Game Over

I was able to answer all 5 questions easily, how about you?

Code Tour

Now that you’ve tried the app, let’s take a tour of the code.

IQViewController is the quiz view controller and contains all of the custom code in this simple project. Open up IQViewController.m and take a look at setupQuiz:

- (void)setupQuiz {
  self.navigationItem.title = @"Free Version";
  self.maxAllowedQuestions = 5;
  [self setupAds];
  
  NSString* plistPath = [[NSBundle mainBundle] pathForResource:@"Questions" ofType:@"plist"];
  self.questions = [NSArray arrayWithContentsOfFile:plistPath];
  self.currentQuestionIndex = 0;
  [self showNextQuestion];
}

This is called by viewDidLoad, and configures the UI as well as loading the questions from a plist into an array called questions. Note that along with kicking off iAds, it sets things like the controller title and number of allowed questions. These would all be different in the paid version of the app, so you’ll be seeing this again later in the tutorial.

Since it’s driving much of the app, take a moment to look at Questions.plist:
7- Questions Plist

This is the plist that was stored in the questions array. Each question is a dictionary with the following keys:

  • question: Contains a string with the question to be asked.
  • answer1 .. answer4: These contain strings with the possible answers.
  • correctAnswer: Contains an NSNumber that holds the number of the correct answer.

Head back to IQViewController.m and look at showNextQuestion:

- (void)showNextQuestion {
  //1 - handle last question
  if (self.currentQuestionIndex + 1 > self.maxAllowedQuestions) {
    [[[UIAlertView alloc] initWithTitle:@"Game Over"
                                message:[NSString stringWithFormat:@"Your score is %ld",(long)self.score]
                               delegate:self
                      cancelButtonTitle:@"OK"
                      otherButtonTitles:nil] show];
    return;
  }
  
  //2 - update view for next question
  NSDictionary *questionDetail = self.questions[self.currentQuestionIndex];
  self.questionLabel.text = questionDetail[@"question"];
  
  for (int buttonCount = 1; buttonCount <= 4; buttonCount++) {
    UIButton *answerButton = (UIButton *)[self.view viewWithTag:buttonCount];
    [answerButton setTitle:questionDetail[[NSString stringWithFormat:@"answer%d",buttonCount]]
                  forState:UIControlStateNormal];
  }
}

This method is called each time a new question needs to be displayed, and it does a couple of things:

  1. Once you've hit the question limit stored in maxAllowedQuestions, this displays a UIAlertView with your score.
  2. This grabs the current question from the questions array and then uses it to set the label with the question as well as configuring the buttons with the four possible answers. Note that the answer buttons are referenced via their tag (set in Main.storyboard), and are numbered 1 - 4 to match the question numbers.

Finally, take a look at answerButtonPressed:

- (IBAction)answerButtonPressed:(UIButton *)sender {
  int correctAnswer = [(NSNumber *)self.questions[self.currentQuestionIndex][@"correctAnswer"] intValue];
  if (sender.tag == correctAnswer) {
    self.score++;
    self.scoreLabel.text = [NSString stringWithFormat:@"%ld",(long)self.score];
  }
  
  self.currentQuestionIndex++;
  [self showNextQuestion];
}

Once a user presses an answer button, this checks if the answer is correct, adds the score accordingly, and then loads the next question.

Dani Arnaout

Contributors

Dani Arnaout

Author

Over 300 content creators. Join our team.