Build Multiple iOS Apps out of a Framework - objective-c

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.
Are there any ways to change that behaviour or better ways to create a Framework?

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.
(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 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.
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?

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.

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];

Related

Build multiple apps with same core

I have few separate apps that have absolute same logic and functions but have different icons and some design elements. The problem is that when some changes to logic and functions are made - I need to manually add this functionality to all apps and after this - I need to resubmit each app.
Maybe there is some way to separate all logic so I need to change it only in one place, and all my apps would get it?
In my opinion the neatest solution is to have one codebase with multiple targets. Yes you still have to resubmit each app when you change some code, but you would have to do that anyway would you not?
You can pick one of your apps to convert to your 'main' codebase.
E.g. Pick app one and duplicate the target multiple times:
You will want to change your scheme names after doing this:
You can set the bundle identifier and deployment info separately for each app just as you did before, and icon sets:
To differentiate between your apps in code you can use compiler flags (Target -> Build settings - Other swift flags) :
You can then do something like this in your code:
#if APP_ONE
...
#else
...
#endif
One solution (though not necessarily the best) is to have a single code base. I.e. you have only one physical copy of each of your classes. All your code files are located in a folder of one of the projects and the other projects use those files as well. It's just a matter of setting paths.
In this structure when you change or add some code in one of the projects (and doesn't really matter which one), all the projects are updated.
The image catalogs are different for each project.
The disadvantages of this approach are that you still need to build and submit each app separately and when adding a new class you need manually to add it to all the projects. Otherwise they won't compile.
The advantages are that when building an app, you build only one app and not all together (less time). It's also easy to manage changes to a specific app - you can just add some extension with additional functionality to only one project - the rest won't need it.

How to manage the code of multiple, very similar Xcode projects

Greetings Stackoverflow Community,
I have taken on the task of 'unifying' 4 mobile iPhone apps that share 95% of the code and differ only in 5% (this is somewhat of an over-simplification, but never mind). Each of the apps has its own hefty set of resources (media files).
After 'unifying' the 4 apps, I will be adding new functionality to the apps, mostly functionality that the apps will share.
I would appreciate hearing your opinions on what's the best way to manage the code of these apps. Here is the approach I'm taking at the moment.
I'm maintaining only one Xcode project which includes the functionality of all 4 apps. The functionality that is not shared among all apps is enclosed in a condition such as: if (appName == 'X')...
Each app has its own info.plist file, so I have 4 of such files: infoX.plist, infoY.plist, ...
Before I build an app, two things are done:
a. in the Build Settings, I specify the name of info.plist to use.
b. I ensure that only the app's resources (media files) are in the project. I delete the other apps' resources.
As the apps are 95% similar in their code, having only "One App to Rule Them All" ensures that when the code gets upgraded, all apps enjoy the upgrade. You can assume that the apps will remain very similar in their code.
As the apps' media files are large and many, I'm keeping them off the Git repository.
How does this all sound?
Many thanks!
There are better ways:
A. Move to framework
It depends on how generic are common parts of the apps. But you should think about putting parts of it in a separate project that is a framework. You can link your 4 apps against that framework. But, of course, not everything will go there.
B. Have different targets
For sure you should have 4 different targets. Xcode let you set build settings for each setting commonly per project or specially per target. Additionally you can customize the build phases (including copy of the media files) on a per target base. So you do not have to rename or delete and insert anything. You simply select a target, you want to build.
C. Project tree
Xcode allows you to have subprojects with common code. Maybe things like common "foundation" classes of your app. You can have different projects for each app in a single workspace, all using the subproject.
Probably the best way is a combination, depending on what is the subject. However I would start with B. and likely add the other techniques, if needed.
I encountered more or less the same case, and we decided to use one Xcode project with multiple targets. That way you can simply change the target before hitting the build button (or configure specific build scripts changing the target). In our project, we had a few files sharing the same names (in different folders), and associated each one with a different target. For example, we had three "Stylesheet.h/.m" with different UIColor & UIFont definitions stored in different folders, and each one of them was linked to a different target. Same thing for the "Localizable.strings".

How do I test the final built product of an iOS static library, as opposed to it's constituent classes?

I have written a static library to re-use some code between iOS projects, let's call it MyLib.
While MyLib has good unit test coverage, it interacts heavily with external resources, and I'd like to make sure that it does the right then when it's hooked up with other pieces of an actual application. In short, I would like to add an application test target to my static library in addition to the unit test target. No drama there.
In thinking about the general setup, I also wondered if it wouldn't be a good idea to ensure that my library's application test target linked against the actual libMyLib.a binary artifact, thereby putting that step of the process under test as well.
So two questions:
Is this even necessary? Is there any significant risk that the final libMyLib.a binary product could behave differently than the series of compiled .m files that my unit tests already exercise? (Possible answer would be that it guards against the accidental exclusion of one of those .m files in the eventual build target).
How would one make certain that the application test target was linking against libMyLib.a? Is this configurable within the build settings? Is any custom build scripting necessary?
I apologize in advance for the basic question if this is common knowledge, I'm slowly coming up to speed on build process, linking, etc. in general, not just how it is implemented using Apple's SDKs.
This is actually accomplished fairly easily. In your static library's project:
(Assumes XCode 3)
Create a new application target.
"Get Info" on the application target to edit its properties.
Click the "General" Tab at the top
Click the "+" button under the "Direct Dependencies" pane, add your static library build target. This will build the static library BEFORE you build the new application target.
Click the "+" button under the "Linked Libraries" pane, add libMyLib.a (or whatever your library is called)
Your project should now link against the actual built artifact.

Merging Xcode project files

There are often conflicts in the Xcode project file (Project.xcodeproj/project.pbxproj) when merging branches (I'm using git). Sometimes it's easy, but at times I end up with a corrupt project file and have to revert. In the worst case I have to fix up the project file manually in a second commit (which can be squashed with the previous) by dragging in files etc.
Does anyone have tips for how to handle merge conflicts in big and complex files like the Xcode project file?
EDIT-- Some related questions:
Git and pbxproj
Should I merge .pbxproj files with git using merge=union?
RESOURCES:
http://www.alphaworks.ibm.com/tech/xmldiffmerge
http://www2.informatik.hu-berlin.de/~obecker/XSLT/#merge
http://tdm.berlios.de/3dm/doc/thesis.pdf
http://www.cs.hut.fi/~ctl/3dm/
http://el4j.svn.sourceforge.net/viewvc/el4j/trunk/el4j/framework/modules/xml_merge/
Break your projects up into smaller, more logical libraries/packages. Massive projects are regularly the sign of a bad design, like the object that does way too much or is way too large.
Design for easy rebuilding -- this also helps if you're writing programs which must be built by multiple tools or IDEs. Many of my 'projects' can be reconstructed by adding one directory.
Remove extraneous build phases. Example: I've removed the "Copy Headers" build phase from all projects. Explicitly include the specific files via the include directive.
Use xcconfig files wherever possible. This also reduces the number of changes you must make when updating your builds. xcconfig files define a collection of build settings, and support #include. Of course, you then delete the (majority of) user defined settings from each project and target when you define the xcconfig to use.
For target dependencies: create targets which perform logical operations, rather than physical operations. This is usually a shell script target or aggregate target. For example: "build dependencies", "run all unit tests", "build all", "clean all". then you do not have to maintain every dependency change every step of a way - it's like using references.
Define a common "Source Tree" for your code, and a second for 3rd party sources.
There are external build tools available. This may be an option for you (at least, for some of your targets).
At this point, a xcodeproj will be much simpler. It will require fewer changes, and be very easy to reconstruct. You can go much further with these concepts to further reduce the complexity of your projects and builds.
You might want to try https://github.com/simonwagner/mergepbx/
It is a script that will help you to merge Xcode project files correctly. Note that it is still alpha.
Disclaimer: I am the author of mergepbx.
The best way I have found is to instruct Git to treat the .pbxproj file as a binary. This prevents messy merges.
Add this to your .gitatributes file:
*.pbxproj -crlf -diff -merge
To compare two Xcode projects open open FileMerge (open xcode and select Xcode (from the manu pane) --> Open developer tools --> FileMerge).
now click "left" button and open xcode project main directory.
click "right" button and open xcode project main directory to compare.
Now click "merge" button!
Thats it!
Another option to consider which may help to reduce the number of times you experience the problem. To explain, I'll call the branch that team members' branches come from the "develop" branch.
Have a convention in your team that when the project file is modified, the changes (along with any other changes required to ensure the build integrity) are committed in a separate commit. That commit is then cherry picked onto the develop branch. Other team members who plan to modify the project file in their branch can then either cherry pick into their branch or rebase their branch on the latest develop. This approach requires communication across the team and some discipline. As I said, it won't always be possible; on some projects it might help a lot and on some projects it might not.

Dividing a project into multiple Xcode project files

An iPad project I have been working on has become bloated with a huge number of files. The application is a prototype and we are considering ways to prevent this when we rewrite it.
One of the members of our team suggests dividing all of the components into separate Xcode projects which will be included in a master Xcode project.
Is this a good idea? What are the reasons, if any, to avoid dividing features/components/controls into separate Xcode projects?
You can add a subsidiary project file to a master project file in Xcode. Just choose "Add File" and add it. When Xcode builds the master it will build the subsidiary as well if needed.
I use a similar system. I often break a project into sub projects just so I can focus on and enforce encapsulation. I write the data model first, then add the app delegate, then specific UI elements. I add each project to the next in turn. This also allows me to go back and change things without as much risk of breaking.
Really, a properly designed objective-c app should be easy to decompose into multiple project. Ideally, all the components are so encapsulate that they don't need any others save the data model.
We have put some of the code in its own project, building a framework which we link against at some of the other projects. It's sometimes annoying that you won't see the implementation files of the framework code right away in another project (by cmd+clicking or cmd+shift+D, or whatever you do normally to navigate). Xcode will only show you the header, you'll have to open the other project and find your file there manually. Not a big deal, but if you look up the code often, it will bother you.
A real problem is that you change the scope of some operations. Stuff like "Find in project" will work on a different file set, which might not be what you want sometimes (trying to find where this method is called / key is used in your whole code, or something); well, there remains Finder / find, so it might be okay. Refactoring is not - all the renaming stuff just breaks, as it will change only the code of the current project, but not of projects referencing this one. If you change interfaces often, better avoid splitting up the project.
A good thing is that you will get less conflicts on your .xcodeproj files (if stored in a shared repository) as someone removing a file from project X won't create a conflict with someone else adding a target on project Y, which where previously the same .xcodeproj (not exactly sure this is a conflict case, but there definitely are some).
Now with Xcode4 you can create a workspace and add all your projects there. Only for documentation purpose :)
To view and modify subproject implementation files, you should add the sub projects directly into the main project.
1 step - Drag and drop the .xcode project files to main project.
2 step - Go to main project TARGETS - > Build Phases. Add subproject target in Target Dependencies. You can also add binary files in Link Binary With Libraries.
3 step - Add subproject source path to main projects header search path.
Go to main project - > Build Settings - > Header Search Paths (e.g $(SRCROOT)/../CoconutKit-master/CoconutKit/Sources )
An Xcode project can have any number of build targets within it, and you can arbitrarily group source files into folders. What makes you think that multiple projects are necessary?