brokedid
brokedid

Reputation: 899

Build Multiple iOS Apps out of a Framework

I have a project where I need to build some apps out of one Basic App. For "Sub-Apps" it changes the API-Credentials, Fonts, Sizes, Colors and some features of the App.

So I changed my App that I can easily add features, change colors, ... by changing simple String constants to build new Apps faster.


I thought that the best solution would be to add multiple Targets to this XCode Project and set the individual Settings of the App with Conditional Compiler flags

#ifdef AppTarget1
qr_reader = YES;
#endif

But the Problem is, that in future will be available multiple versions of the Framework. So when updating an App based on a old Framework we always had to do the adjustments to work with the new Version of the framework (if any) and we've no control over the Framework Versions, ect.

One goal is also to allow our Developers with a Simple Podfile build new Apps easily.


So the next idea was to create a Framework Project, what then is included with CocoaPods in our Main Apps. I followed this tutorial: http://chariotsolutions.com/blog/post/using-cocoapods-to-manage-private-libraries/

In the MainApp I included the Framework, the MainApp-AppDelegate Class is a Subclass of the Framework-AppDelegate Class.

@interface MainApp_AppDelegate : FrameworkAppDelegate

The Method where I set all the App-Based Settings I overwrite in the MainApp_AppDelegate.

@implementation MainApp_AppDelegate

-(void)initSettings {
    qr_reader = YES;
}

@end

It worked like a charm, but one Problem I've with images. The AppFramework contains Image training.png and image y.png.

In the Main App xyz I decide that the Image training.png looks not good for this App, so I want to change only that (y.png I want the version included in the Framework). I put the x.png into the Main App Ressources Folder, but after compiling and Running there I see the Image included in the AppFramework Project.

training.png what is in the Framework and in the Main App

Are there any ways to change that behaviour or better ways to create a Framework?

Upvotes: 2

Views: 395

Answers (3)

Guillaume Algis
Guillaume Algis

Reputation: 11016

What is happening is Cocoapods copies its resources in the final .app bundle after your app is done copying his. So your training.jpg gets overwritten by your framework's.

Build phases order

As @masam said, you need to reorder the build phases of your project in order to copy your app's resources after Cocoapods' resources.

Reordering your app build phases

(Notice how "Copy Bundle Resources" is now under "Copy Pods Resources" in the list)

Force resources copy

Unfortunately, this will not be sufficient, as Xcode's build system is (tries to be?) smart and will not copy (or recompile in the case of a source file) a target which is "up-to-date". For a static resource like an image, being up-to-date means that the target path (in the final .app bundle) is newer than the source path (in you Xcode project).

So if you try to run your app now, you will notice no change. Xcode won't copy the training.jpg as it is already present and newer in the .app.

You will need to trick Xcode into thinking that the resources in the .app are outdated and needs to be updated. To do this, add a new "Run Script Build Phase" between "Copy Pods Resources" and "Copy Bundle Resources":

the updated build phases with script to outdate resources

The script should set the modification date of the resources to an earlier date, to force Xcode to copy your app's resources. touch does the job:

find ${TARGET_BUILD_DIR} -name 'training.jpg' -exec touch -ct $(date -v-1d "+%Y%m%d%H%M.%S") {} \;

We use date -v-1d "+%Y%m%d%H%M.%S" to set the resource's modification date to yesterday.

Xcode will now overwrite the framework's image with the app's one.

Manage the targets resources

Again, as @masam said, don't forget to add the resource only to correct targets.

eg.

target membership example

Here, the app image will be used in DummyApp, but DummyApp2 will use the framework image.

Conclusion

tl;dr: Avoid this if possible.

  • For a newcomer on your project, this is impossible at first glance to know why some targets build with one file and not the other;
  • Depending on the kind of resources of the framework you want to override, you will probably need to maintain the "Reset Resources Modification Time" script (probably by making find more inclusive, eg. find ${TARGET_BUILD_DIR} -name '*.png');
  • More of a subjective opinion: I'd recommend against hiding resources from the framework with your owns.

On the one-target-per-app-flavor thingy:

Remember that each modification you make on one target's Build Phases will NOT impact other targets Build Phases. You'll need to edit every targets individually. Hence my previous don't do it if you can avoid it.

I perfectly understand the need to have multiple targets to build multiples apps based on the same codebase, but Xcode is horribly bad at managing a large number of targets. Take it from a guy currently working on an fairly large app (1k+ files, and a lot of dependencies) with 65 different targets: this is a nightmare.

The .pbxproj will basically grow in factor of your number of targets (25MB in my case), and you'll get the beach ball each time a project modification in done in Xcode. And as each target is managed independently, each time you need to eg. add/remove a compile flag, you will need to update each target, one. by. one. (or you will edit the .pbxproj "by hand" with sed/awk/whatever, which is faster, but risky... but fun :)).

Oh... and did I mention merge conflicts?

Upvotes: 2

DjimOnDev
DjimOnDev

Reputation: 399

duplicate your targets !?

The last apps I build by this way were using a different bundle identifier for each target. Then just have a test to you bundle ID to make your different settings programmatically.

NSString *bundleIdentifier = [[NSBundle mainBundle] bundleIdentifier];

Upvotes: 0

masam
masam

Reputation: 2278

This might be due to a certain order in your build phases (Project -> Build Phases). Default, it will copy the bundle resources first and then copy Pods resources (overwriting the resources you had in your main application).

You could change the order of "Copy Pods Resources" to an earlier phase by dragging it to above "Copy Bundle Resources".

You also have to verify that the image resource included in your main project, has a correct target membership, by clicking on the image resource -> file inspector -> target membership. It should be checked for the target you are compiling.

Upvotes: 0

Related Questions