Hundreds of targets, same code base in xcode configuration - ios

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.

Related

Xcode: merge Unit tests into a single target

I'm developing a modular app with >30 modules, where each module has its own Unit test target. I also have a special test scheme which runs all Unit tests from all the targets.
The problem is - it's too slow. While the tests run fast, it takes a lot of time for Xcode to switch from one test target to another. The "all tests" scheme is supposed to be run in CI so I would really like to improve the performance.
I tried to convert all Unit test targets into static frameworks and link them to a new "merged" Unit test target. In this target I then created a single AllTests.swift file importing the frameworks and manually invoked tests on each of them. The performance improvement was ~4x.
However, this will be quite hard to maintain this file. My assumption is, since they are linked statically, there can be a way to make Xcode run imported in such a way tests automatically. If this works I'll be able to fully automate this process, keeping test targets for development and generating the "merged" target in CI.
What else I tried:
Investigated the option of using SourceKitten to parse the project and generate the AllTests.swift file automatically. This would require building the project one more time, eliminating the performance improvement.
Instead of linking frameworks, include references to source files into the merged target. This introduces a bunch of "multiple files with the same name" and "same declaration" errors that cannot be automatically resolved.
Made all declarations in the static frameworks public.
Googled a lot. This SO question is quite close but the solution doesn't work for me.
So the main question is - is there a way to make Xcode automatically include Unit tests that are part of a static framework the current target is linked against? Or any other suggestion on how I can automate this process will be very much appreciated.

Add Files and Select Multiple Targets in Xcode

I have over 100 projects in my XCode and when I add a new file or class I have to select each target one by one to add it to it. Is there a way to select multiple targets instead?
The usual way is to have targets that don't duplicate each others files. E.g. some targets are libraries that are used by some others, and eventually there's an app or a unit test target that uses them. In this case there's no need to have the same file in multiple targets.
So consider refactoring the common files to a common library target that you can reference from the other targets. In this case when you add a file, you can add it to just that library, and it will be automatically resolved (linked) in the other targets.
Note also that in this case the file you add is only compiled once, and the compilation result is reused in various dependant targets, as opposed to compiling a separate version of that file for each target.
If that's not an option for you, an alternative is to generate the Xcode project file using some tool like CMake, GN, or a custom script that can write xcodeproj files (there are libraries to do that in various languages).

How to re-export a framework and use it in another project in iOS? (Should I?)

I have two projects I'm working on in XCode. Project 2 depends on Project 1, and I want it to be able to use the same frameworks I have embedded in Project 1 (they're Carthage dependencies). I discovered the "Re-exported Framework Names" section in Build Settings, but I'm not at all clear on how I might use it.
What should I put in that field? Just the name? (PromiseKit). Or the filename (PromiseKit.framework)? Or the full path?
How do I then reference this framework in Project 2? Right now I'm getting a build error: framework not found PromiseKit for architecture arm64 and I can't see any obvious way to add that framework link.
And a broader question: do I need to do this? I'm a relative newbie and working on the assumption that setting up separate Carthage dependencies for both projects means that there will be two separate compiled frameworks in my final binary, which would be a waste. But is XCode cleverer than I think and taking this into account?
If the two projects are related, put them in the same Xcode workspace and then you can share the frameworks between the two.
If the projects are independent, put the frameworks into a separate folder, add them separately to both projects and when adding them to the projects, don't choose Copy files if needed.

Compile different code for same project - Swift framework

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.

Can I parallelize my build when linking against multiple interdependent static libraries?

I've built my app as a workspace containing one project with an app binary target and many projects containing static library targets. My app target links against all the static libraries. Some of my static libraries depend on each other, and I handle this by ordering the targets built in my scheme so that all dependencies are built before the libraries that depend on them.
What I'd like to accomplish is to use the 'Parallelize Build' feature to speed up building my app scheme. However I need to ensure that my static libraries are built with respect to their dependencies. It doesn't seem like I can set 'Target Dependencies' between these static library targets because they are not all in the same project (Is this the case or am I misunderstanding how this works? Is it possible to have cross project target dependencies)?
Is there any way to accomplish this? Thanks for any information!
After some research it does seem like the only way to get this working is to set up my app using nested subprojects instead of a workspace (so that I can create target dependencies between my dependent libraries). I'd love to be corrected on this point if anyone knows another way!

Resources