I have two iOS projects and I want to make it as a single project and run one project at a time depending on a condition.
For example : I have 2 projects named ProjectA and ProjectB.I want to embed ProjectA and ProjectB into a single project named ProjectC. ProjectC will have a condition depending on the condition I have to run either projectA or ProjectB code.
Note: Condition to run a project will be applied on runtime not on compile time.
I doubt is this really possible ? I need expertise guidance on how to approach this problem.
Okay, so based on reading the question and asking some questions ... it really isn't clear to me why the answer regarding pods has gotten so many votes. It does not solve the problem.
Let's say we have existing ProjectA and ProjectB. ProjectC is not yet in existence, but the desire is to make ProjectC be a "combination" of A and B where A or B will be run based on some condition. Once run, the app will stay running that version until re-launched.
Two basic approaches would be to either combine all the code and assets into ProjectC or trying to make A and B frameworks which you load. However, with either scenario, you are going to have make adjustments to your code base. The amount of work you will need to do is also a byproduct of how complicated the projects are.
I've worked on a project where we successfully did what you wanted for a large app. We essentially made a "universal app" by taking our iPhone and iPad projects, combined the code/assets. We "launched" the appropriate version at runtime.
Before you go down such an effort, you will need to weigh the consequences. I'll list a few.
You would probably get more success combining source/assets of both A and B into C. In other words you will not be adding the project files for A or B into C. Why? Because you need to be able to easily identify all the conflict points and then provide a work around. A simple example of a conflict is AppDelegate
, which is what project creates by default. You'll have 3 of them (A, B, C).
Keep in mind all approaches are fraught with issues. If you go for frameworks (regardless if they are Pods or not) and you decide your assets go into the framework bundle, you have to change your code to access them, as they are no longer in the mainBundle
.
Okay, what are the general guidelines?
AppDelegate
you could always do ProjectAAppDelegate
and ProjectBAppDelegate
.Info.plist
. This is a good source of other conflicts. C's Info.plist
will be a combination of the two. One of your other key things you will need to get control of is your entry point based on the decision. If you can decide before calling UIApplicationMain
in main.m
, you could do something like:
Class appDelegate;
if (runA) {
appDelegate = [ProjectAAppDelegate class];
} else {
appDelegate = [ProjectBAppDelegate class];
}
return UIApplicationMain(argc, argv, nil, NSStringFromClass(appDelegate));
IF this does not work, then you'll have to do this in - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
. You'll probably want to then have Project C's AppDelegate be a proxy for A and B. For example:
- (void)applicationDidBecomeActive:(UIApplication *)application {
// Restart any tasks that were paused (or not yet started) while the application was inactive. If the application was previously in the background, optionally refresh the user interface.
[self.projectAppDelegate applicationDidBecomeActive:application];
}
where projectAppDelegate
was set to the right version you need.
Note you will also need to manually load your storyboard to make sure the right one is launched. And keep in mind that if you spend to much time making your decision during loading (you said it was a network call) your app can be booted.
You may be able to find some nifty dynamic loading you could do if they were frameworks, but the key should be maintainability/ease of debugging.
I'm going to stop here mainly because there are so many different things to do and I really I don't have the time to write them all out.
Additional info based on follow up question
Odds are high that you will have some file duplication. I gave the example of AppDelegate
. enum
will be less frequent, but will occur. Also keep in mind, it is not the file name you care about, it is the class or other defined data type you are concerned about. Those are the conflicts that matter to the compiler/linker.
For example say:
ProjectA:
typedef NS_ENUM(NSInteger, State) {}
Project B:
typedef NS_ENUM(NSInteger, State) {}
This was what I meant about having a strategy ahead of time. Example strategies:
So for 1, the result would be
ProjectA:
typedef NS_ENUM(NSInteger, State) {}
Project B:
typedef NS_ENUM(NSInteger, PBState) {}
Note what I did hear. To make my life easier I used some pre-defined prefix to designate it is ProjectB. In this case PB.
For 2: ProjectA:
typedef NS_ENUM(NSInteger, PAState) {}
Project B:
typedef NS_ENUM(NSInteger, State) {}
For 3: ProjectA:
typedef NS_ENUM(NSInteger, PAState) {}
Project B:
typedef NS_ENUM(NSInteger, PBState) {}
It will be up to you to make the rules, but be consistent.
You will need a strategy for how you will resolve all the data type changes. For example, if you are going from State
-> PBState
, you obviously only want to modify ProjectB. Here is where having it in it's own project would help. However, you can use Xcode's Search Scope
to help control this.
Oh some other things.
Up front, invest on a script to find all the duplicate files in ProjectA and ProjectB. You basically need to do a find
on ProjectA and ProjectB based on the extensions you need (eg. .m, .h, .xib, etc). This will give you a list of potential candidates and from there you can strategize on rules.
As I was the sucker that did this part of the project, I basically kept this list in a text file. When I consolidated the file, I moved it to a different section (separated by several newlines) in the file. There are diff ways of doing the accounting on it, it is simply the method I chose.
I would also make sure you have a good diff tool like Araxis Merge (which is what I used).
Also, take frequent snapshots just in case. You can use git branching. I often just copied the actual directories so I could diff them later if needed.