Compile different code for same project - Swift framework - ios

I have several apps that using the same core framework, Since each app have some small differences i'm searching for the best way to build the framework with relevant code and parameters.
The best solution (that is not so good) that i found is:
For different code compilation - using targets that include different classes
For different configuration using different Swift flags for each configuration (e.g. debug, release...)
The problems are:
I'm using several targets and each target duplicate the project configuration and i need to maintain all. this approach can lead to some bugs if i change configuration in one target but forget to change it on other target.
For debug/release & stagingA/stagingB/production I'm creating specific configurations instead combining them (This solution is problematic since for each staging i need to duplicate it for release and debug):
release production
debug production
release stagingA
debug stagingA
release stagingB
debug stagingB
Apps struct is:
App A include CoreFramework with code adjustments for A
App B include CoreFramework with code adjustments for B
Each app have debug, release, production... configuration. This configurations affect the framework (these configuration run also on the framework)
==> I'm looking for other/better way to configure my apps project's

Don't make "code adjustments" in the framework based on which client is calling it.
Construct your framework as if it were something provided as a binary release by an external supplier. Any behaviour that may be variable can then only be controlled by run-time configuration through a public API.

Related

Make framework compatible for different Xcode versions

I have a specific scenario I am trying to overcome and have yet to find a solution (if one exists) to my problem.
I am building a framework that can be used inside of other applications. This framework contains static libraries.
Now, inside my framework, if I want to add classes that use APIs from Xcode12, I would also want my framework to be able to compile in Xcode11.
This could obviously lead to compilation issues as I have no control of my framework inside other applications and as my framework will already be turned into a binary file (.a). Adding any build script to the frameworks' build phases won't be relevant.
I have been trying to look for similar scenarios or solutions for this problem, but have yet to find anything substantial.
Is there any way to overcome this or am I trying to do something that is impossible?

Hundreds of targets, same code base in xcode configuration

We are at 400+ targets in xcode. It still works fine but there has to be a better way to set this up by keeping the same code base but not having all those targets which could slow down xcode.
Android Studio lets you update the appname, which loads that folder from disk so only that project is loaded to run and program against. In XCode that is not the case, all targets are available.
It's been years but is there a better way now, with hundreds of targets that doesnt involve Git or Branching? The questions in regards to this are old and only for a few projects, we are talking hundreds here.
Your question lacks enough context to make a specific recommendation but in general...
Use Frameworks
If you can, combine sensible things into a single (or multiple) framework target. Frameworks can be more than fancy wrappers around a dynamic library, they can contain helper tools and such as well.
Use Workspaces
If there is a logical grouping to your existing targets you can separate them out into their own Xcode projects. Once you have them in their own projects you can create a workspace that references those individual projects. Even if the combined workspace loads in everything upfront (I don't think it does tho) you can still open and use the separate projects for a fast and fluid experience when working on the components.
Use static libraries
If you have a ton of targets such that one requires A, B, and C, but another needs B, C, D then you can actually put A, B, C, and D together in a static library and rely on the linker to strip out unused code from each individual target. This obviously does not reduce the number of targets you have, but you can make the static library its own project and include it in a common workspace. This will also speed up compilation as the files only need be compiled once.
Parameterize Targets or Use Schemes
If your targets are simply wrapping some external build tool/script with hardcoded parameters (I've actually seen this) you can actually pass a ton of existing variables from xcode to these external tools and eliminate "duplicate" targets. Similarly you can add new schemes if some of your targets are simply permutations of each other. A good example I've seen of this are people that have a separate target for "sanitized" (address sanitizer, etc) builds you can instead create a sanitization scheme instead of a target.
Use "Script" Build Phases
If some of your targets are doing something such as linting then you can instead employ a script build phase to call the linter instead of having a separate target to do it.
Offload Targets to an External build System
Xcode can have targets that simply call out to an external tool/script using the Script build phase (and using variable parameters as mentioned above). This can make sense to do if you already use another build system (make, cmake, etc) for another platform. Use Xcode only for the Mac/iOS specific targets and offload everything else to a cross platform build system.
If the build system outputs errors in a format Xcode understands it will even show file and line errors the same as native Xcode targets. I've seen people write thin wrappers around external tools to catch parse and reprint errors into such a format.

Xcode: Inherit `configuration`s from parent project - not possible?

TL;DR
Is it possible to inherit custom configurations from parent project? No, not asking about inheriting some target setting.
Configurations and preprocessor macros/flags
Every project in Xcode is initialized with the two standard configurations called Debug and Release. It is a quite common pattern to setup a DEBUG flag using the setting under the display name Preprocessor Macros in Build Settings for a target (in .pbxproj this is called GCC_PREPROCESSOR_DEFINITIONS), which can be read from Objective-C code like this.
#ifdef DEBUG
print("DEBUG flag set")
#else
print("No debug flag!")
#endif
This also works in Swift, but then we have to use OTHER_SWIFT_FLAGS and declare it like this: -D DEBUG
and reading the variable value just like we did in the Objective C code above.
Many projects => xcconfig files
My app consists of an xcworkspace with a main project and several projects as dependencies. Let's call the projects my app is dependent on frameworks. Since I have several frameworks I do not want to have to setup build settings several times on.
Thus I am using xcconfig files. I have a main config, that is the config file for the main project, let's call it Main.xcconfig. I have another config file called Framework.xcconfig which starts with the line #include "Main.xcconfig", thus inheriting settings from Main. And of course I setup each framework to make use of said Framework.xcconfig file.
It is very convenient to declare the DEBUG flag when we have these config file, in Main.xcconfig we add:
OTHER_SWIFT_FLAGS[config=Debug] = -D DEBUG
GCC_PREPROCESSOR_DEFINITIONS[config=Debug] = DEBUG
And thus declaring the DEBUG flag for the configuration Debug for the main project and the framework projects (since Framework.xcconfig inherits from Main.xcconfig...).
Custom configurations
What if we want to be able to profile the app but with the DEBUG flag set? Profiling should be done with the same optimisation flags as Release. But we absolutely do not want to set the DEBUG flag for Release builds.
Why not create a new configuration, let's call it Profiling. Here comes the problem! Of course we should create this new configuration to the main project. Then we edit our scheme and for Profile and under
Build Configuration we chose the new configuration Profiling.
Now we can set the DEBUG flag for Profiling in the Main.xcconfig file.
OTHER_SWIFT_FLAGS[config=Debug] = -D DEBUG // we keep this
GCC_PREPROCESSOR_DEFINITIONS[config=Debug] = DEBUG // we keep this
OTHER_SWIFT_FLAGS[config=Profiling] = -D DEBUG
GCC_PREPROCESSOR_DEFINITIONS[config=Profiling] = DEBUG
We try running the simulator and we see "No debug flag!", which is expected since for running we are using the Debug configuration and thus not declaring the DEBUG flag.
So we try profiling and start some Instruments measurement and open the console. There we see the message "DEBUG flag set"
It works, great!
Configurations are NOT inherited from parent project
We just checked against the DEBUG flag in the Main project. What happens if we in some of our frameworks want to check against our flags. So we try the #ifdef DEBUG in some framework. That works, since all frameworks have the configuration Debug, since it is default for all project (together with Release).
Then we try #ifdef DEBUG in one of our framework projects and start profiling using Instruments again. And now we see the message "No debug flag!"
Oh no!
It is not working! Why not?! Well I do not know but the only reasonable conclusion must be that our projects added as dependencies - our frameworks - do not inherit the Profiling configuration from the Main project.
For me this is unbelievable... It feels like a flaw in Xcode.
Bad solution
I don't know of any other solution than adding the same configuration Profiling to all framework project (at least for frameworks where I know I want to check against that flag). But this feels like such an ugly solution!. I have at least 10 frameworks and it feels really ugly to have to add a certain configuration to each framework.
Alternative (terrible!) solution
Yes of course another solution would be to use the Release configuration for profiling, and declaring the DEBUG flag in Main.xcconfig like this:
OTHER_SWIFT_FLAGS[config=Release] = -D DEBUG
GCC_PREPROCESSOR_DEFINITIONS[config=Release] = DEBUG
But since we want to be able to check against DEBUG flag in framework we need to add the two lines above, declaring the flag, to Frameworks.xcconfig as well.
And of course use the Release configuration for profiling as Build Configuration for the scheme.
Then we can add a new configuration called AppStore to our Main project, and only the Main project and use that for archiving the app. So far so good?
IT'S A TRAP!
No this is not a good idea! Because I just said that the configurations are not inherited between project and parent project. Thus our frameworks will not inherit this new AppStore configuration, so when the frameworks get built/archived I have seen them "fallback" to the Release configuration (not sure if you can chose "default"/"fallback" configuration somewhere? Maybe it will fallback to the one you used as base for the new configuration?).
But since we just added declaration of the DEBUG flag for the configuration Release for the Main project and all our frameworks, and it is the Release configuration that is used for all our frameworks when we archive the app => our production app will include debug code!. This is highly unwanted and potentially dangerous.
Good solution?
I know of none... Do you? Wouldn't it be great if configurations would be inherited from parent project? That would solve everything! Apple... pretty please?
As workaround: Create xconfig files per project and global ones. In the project xconfig files include the global one via
#include "path/to/File.xcconfig"
Hope this help

#define on ios frameworks

I am creating a set of frameworks to be split into different projects, and separate targets.
In these frameworks i have different configurations, for test data, stubbed data, etc.
I used #define to make sure that stubbed classes werent included in binaries, URL's, etc. as these would be done at compile time.
However now that i am splitting all this into frameworks, i realise that a framework will be compiled against a specific configuration.
Is this correct even with dynamic frameworks?
If so what would be the solution to allow these configurations to be used?
Would the creation a script to link to different frameworks to different configurations at what stage should this happen is it possible to link frameworks through scripts inside xcode?
At the moment i solved this issue by having nested projects inside, where it would require configurations to be changed. But I am not happy about this solution.
I wonder if there would be a more universal solution to this problem.
Any help would be really appreciated.

Creating a distributable framework for iOS Applications

I am currently building a library which should be used internally in a few iOS projects but should also be distributed to customers accessing our services with the library. The Library itself consists purely of C++ code and I am basically able to create Apps with it on iOS which work fine. My problem is creating a single, easily distributable file that can be given out to customers which can easily install them, use the provided headers and don't need to have the headaches that I am currently facing when it comes to linking.
Our code depends on two other projects, namely boost and websocketpp. For boost there is the script on github which I took to generate a framework. For websocketpp, I imported it into XCode and used the scripts from this github project to build a framework. I added both frameworks to my (potential) framework as dependencies and used the same script to build one.
I have an app using my library as a sub-project working fine. Even including the framework into the project and running it on a device works fine. So far so good.
However, trying to create an archive of the App project lead to several questions and headaches.
My library did not seem to contain the code for all architectures. So I tried to archive the Framework projects, which after small modifications in the build scripts to use different locations to search for headers worked fine.
It does not seem to contain all binary code or references to local files (i.e. my specific location of boost). I gathered that from Linker errors that I still get that tell me that some boost calls could not be satisfied.
The second issue made me think that I am must be doing something fundamentally wrong and my intuition tells me that it can't be that difficult and "hackish" to create frameworks or libraries for others for iOS development.
As you probably have found out by now, I am not very experienced when it comes to iOS and I am wondering if I am missing something fundamentally. So, I am sure that this question is rather broad, so some more concrete questsions:
Is there a(nother) way to generate some kind of distributable (preferably a framework) which contains: my public headers, my binary code compiled for all platforms supported for iOS development, the binary code of dependencies?
Is the only way to do that by adding some handwritten scripts to the build process?
I have the feeling that the information I found is quite outdated since it's older than a year and mostly refers to Xcode 4.2 or 4.3 -- so has there anything changed in this regard recently?
For example one error I get is:
File is universal (2 slices) but does not contain a(n) armv7s slice: <file>
The <file> slice is the path to the file in the framework in the Products folder of a different XCode workspace (the library was build in a different workspace then the app). I dropped the framework folder into the project for this test from a completely different location.
What is going on here?
Why does it keep referencing to some internal XCode directory?
How do I properly export it?
Since I guess my setup is probably skrewed up and weird from all the different things I tried up to now: How does this setup look like in a ideal situation?
Yes, there are some questions regarding this on SO already, however, either I don't see or don't understand in those replies:
...how to handle depencies of my code to other third-party code properly.
...how to generate a distributable file.
Have you checked your project build phase under Compile Sources and Copy Files to see if you are including your framework source files in your build?
You may also try the C/C++ Library template under OSX -> Framework & Library.
Finally, there's also kstenerud’s iOS Universal Framework, which I found very useful. I wrote a few articles in my blog on using it.

Resources