Monday, September 16, 2013

XCode project with two different targets

Hi dudes, it's been a long time since i wrote a blog post here. This post will be about converting a project in XCode into two target project.

First of all, let me explain what is a "target" in Xcode. According to Apple 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."
https://developer.apple.com/library/ios/featuredarticles/XcodeConcepts/Concept-Targets.html

This means that a target is a single product of your code. Yeah dude, that means you can create more than one product(different builds) from your single code.

I'll continue to do this operation from my previous blog's project: "Capless Paint". You can do it in your any project since I'll just go over the concepts here.

First of all go to your project settings:

Figure 1


In the left bar of the settings screen. You can see two sections: "PROJECT" and "TARGETS". Project depends on your code there. You can imagine it as a "set of codes to navigate between." However, "TARGETS" sections defines your products. In my example, i have just one target which is CaplessPaint. That means i'm having just one build, product, target whatever you say from this piece of code.

Let's have another target there to have a product different than CaplessPaint. Assume we want to have a Paint app that is customized for children. We want it to be less optional and usable for little childs too. Therefore, we'll remove color options for another product named "HighSchooleanPaint".

Let's define our new target: "HighSchooleanPaint". Press "Add Target"(on the bottom) button in the figure 1. Select the options as in the figure 2:

Figure 2

You should see that it's like creating a new project in xcode! see:


Figure 3
Note that, if you would have more than one project on the navigator, you would be able to select on which project you want your target to be built. When you tap finish, you will have two different targets:

Figure 4
Realise that you can now make any settings to your new target different than the old one. Customize it as much as you can. In the code, in order to know which target the is being built for, we'll use macros. Here is how we do it. Go to search bar and type "macro". Then add HIGHSCHOOLEANPAINT key as in the pictures below:

Before:
Figure 5


After Edit:
Figure 6

Do the same thing for "CaplessPaint" target too and name the macro as "CAPLESSPAINT".

You may have realised that we now have a second AppDelegate file in our file navigator!!. Just like having two apps in one code right??

//
//  FCAppDelegate.m
//  HaighSchooleanPaint
//
//  Created by Rıfat Ordulu on 9/16/13.
//
//

#import "FCAppDelegate.h"
#import "TouchDrawView.h"
#import "TouchDrawViewController.h"

@implementation FCAppDelegate

- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
    self.window = [[UIWindow alloc] initWithFrame:[[UIScreen mainScreen] bounds]];
    // Override point for customization after application launch.
    self.window.backgroundColor = [UIColor whiteColor];
    [self.window makeKeyAndVisible];
   
    TouchDrawViewController *ct = [[TouchDrawViewController alloc] init];
   
    // Set tabBarController as rootViewController of window
    [[self window] setRootViewController:ct];
   
    return YES;
}

@end




and your FCAppDelegate.h to:

//
//  FCAppDelegate.h
//  HaighSchooleanPaint
//
//  Created by Rıfat Ordulu on 9/16/13.
//
//

#import <UIKit/UIKit.h>

@interface FCAppDelegate : UIResponder <UIApplicationDelegate>

@property (strong, nonatomic) UIWindow *window;

@end




Now our appdelegate is just like the one in old product (You can customise it anyway you want). One big problem is remaining. When you add .m, .xib and .png etc files to the project, you decide which targets it will be added to. Since we just created our target, we need to add those resource files to our new target too. You can easily do it by selecting the resource and then under the target membership section(on the right navigator of xcode.) select "HaighSchooleanPaint" target too. (Haigh Schoolean :(( i've just realised that i mistyped it but, i can't change it now, we've got a long way here. sorry. ) You should do it to all files that you'll use for this target. If you don't, you will have plenty of build errors, saying like no linker found matching blabla.o blabla.xib etc. See the figure to learn how to add your files to a target:

Figure 7


Note: you may also need to add all the frameworks that are added in the previous target. If you get compile errors like: "Undefined symbols for architecture i386:
  "_OBJC_CLASS_$_CABasicAnimation", referenced from:"  just make sure all the frameworks are added to our new target.



 From now on, we can check for macros to customize the two targets in code. However, using macros everywhere in code is not a good solution. Lets create a class named "TargetIdentifier"  a subclass of "NSObject". Change them as follows:

TargetIdentifier.m

//
//  TargetIdentifier.m
//  CaplessPaint
//
//  Created by Rıfat Ordulu on 9/16/13.
//
//

#import "TargetIdentifier.h"

@implementation TargetIdentifier

+(TargetIdentifierType)currentTarget{
#ifdef HIGHSCHOOLEANPAINT
    return TargetIdentifierTypeHighSchoolean;
#elif CAPLESSPAINT
    return TargetIdentifierTypeCapless;
#else
    return TargetIdentifierTypeCapless;
#endif
}

@end




Change TargetIdentifier.h to:

//
//  TargetIdentifier.h
//  CaplessPaint
//
//  Created by Rıfat Ordulu on 9/16/13.
//
//

#import <Foundation/Foundation.h>

typedef enum{
    TargetIdentifierTypeCapless,
    TargetIdentifierTypeHighSchoolean
}TargetIdentifierType;

@interface TargetIdentifier : NSObject

+(TargetIdentifierType)currentTarget;

@end



Now we can know what target we're building for, in code. What we wanted to achieve is that, to remove the color selection from the screen since High Schooleans are too lazy to use it. Go to viewDidLoad method of "TouchDrawViewController.m" import "TargetIdentifier.h" and add this line of code to the end of viewDidLoad method:


    if([TargetIdentifier currentTarget] == TargetIdentifierTypeHighSchoolean){
        [selector1 removeFromSuperview];
        [selector2 removeFromSuperview];
        [selector3 removeFromSuperview];
        [selector4 removeFromSuperview];
        [selector5 removeFromSuperview];
        [selector6 removeFromSuperview];
        [selector7 removeFromSuperview];
        [selector8 removeFromSuperview];
        [selector9 removeFromSuperview];
        [selector10 removeFromSuperview];
        [selector11 removeFromSuperview];
        [selector12 removeFromSuperview];
        [selector13 removeFromSuperview];
        [selector14 removeFromSuperview];
        [selector15 removeFromSuperview];
        [selector16 removeFromSuperview];

    }

Yeay!! we've done with configurations and coding. Let's build for our new product named: "HighSchooleanPaint" and then see the change (removal of color pickers). You folks should have one remaining question??!?!! How do we build for new product?

Too easy.

In the picture below, you can easily figure out how to do it:

Figure 8


CONGRATS!! You now have two targets working properly in an xcode project.

Keep on coding caplessly.