Different values for the Build Location for different XCode projects - ios

The two projects I'm working on happen to both make use of XCode at some point during building.
One project is based on Reactive Native, in which case I'd need to set the Build Location to Custom with
Products: build/Build/Products
Intermediates: build/Build/Intermediates
in order for the command react-native run-ios to run properly (or else I'd run into CFBundleIdentifier", Does Not Exist error).
Another project is based on Unity and make use of a custom framework injection, in which case I'd need to set the Build Location to Legacy as there is a custom script that uses the value in the target settings.
To my astonishment I discovered that every time I work on the other project I'd need to reset the Build Location value to the right one for the project or else I'll run into errors/problems.
Is there a way to define a project-specific configuration so that I can have different values for the Build Location for different XCode projects?
One solution I can think of is to stick to Legacy: for the project based on react-native simply define the build location as build/Build/Products in the target settings. But the drawback is that react native may just override the value to the default one at each build and rendering this approach not practical (I have yet tested it out though).
So I'm wondering if there is a "standard" way of resolving this annoying problem.

Related

Integrating XCFrameworks into the development and release workflows

I have an Xcode workspace with an iOS framework target and an iOS SampleApp target that depends on and embeds the framework. The framework is closed-source and thus gets delivered in binary form to customers, in a release package alongside a copy of the sources for the entire SampleApp project, so that they can test it out with our app and see how the integration is done.
With this setup, during the development phase, I can open the workspace in Xcode and build the SampleApp target, and because it has an explicit dependency on the framework, Xcode immediately triggers a build of the framework first and then the app is built and linked correctly.
Now I want to leverage the new XCFramework format to distribute this framework to customers instead of using lipo'ed fat frameworks. I update my shell script that produces the release package to create an xcframework bundle at the end with all the various platforms supported, and also update the SampleApp project to depend on this xcframework instead of on the individual frameworks as it did before.
But here comes the problem with the updated SampleApp: After removing the dependency on the frameworks, the workspace setup described above doesn't work anymore, i.e. Xcode will no longer automatically build the framework target when the SampleApp target is built, because the explicit dependency link between the two is now broken.
My first attempt at solving this involved making the xcframework part of my regular development workflow as well. For this I added a run-script phase to the framework build target that generates a current-platform-only xcframework bundle and copies it to the place where the SampleApp expects to find it, and then added the framework target to the targets of the SampleApp build scheme, in order for the framework to be built when the SampleApp is built. Unfortunately this approach does not work because Xcode 12 always seems to build both targets in parallel, no matter what the 'Parallelize Build' setting is set to, and because of that the app will fail to build simply because the xcframework is not yet ready when it's processed by the build system, which is very early in the build phases of the SampleApp. So unless there's a way to force Xcode to honor the order in which the build targets are defined in the scheme, this approach is a no-go.
If this approach cannot work then I'd have to ship the xcframework with the SampleApp unmodified, and instruct customers wanting to run the app to manually remove the references to the universal frameworks and then add/drag the xcframework to it. This would work but it's ugly and does not seem user friendly enough to me.
I'm also considering a different approach whereby the release script programmatically modifies the SampleApp.xcodeproj to depend on the xcframework instead of on the universal frameworks, but that seems like it could be a very fragile thing to do, so would rather avoid it if there's a better way.
This scenario does not seem unique to me so I'd guess someone else must have faced this problem too. If so, how did you solve it?
So I ended up solving this by programmatically modifying the SampleApp's project file at release time to link and embed the xcframework bundle instead of the universal frameworks.
Initially tried to use the tool pbxproj but it didn't quite work, the initial fat framework removal phase was not succeeding.
Then I figured I could try and diff the project.pbxproj file after having manually replaced the frameworks with an xcframework, and turns out it takes only a few small edits to get there, so they can be done programmatically with a simple shell script like the following:
#!/bin/bash
PROJECT_FILE=SampleApp/SampleApp.xcodeproj/project.pbxproj
FRAMEWORK_NAME=MyFramework.framework
XCFRAMEWORK_NAME=MyFramework.xcframework
sed -i '' "s|FileType = wrapper.framework; path = ${FRAMEWORK_NAME}; sourceTree = BUILT_PRODUCTS_DIR;|FileType = wrapper.xcframework; path = ${XCFRAMEWORK_NAME}; sourceTree = \"<group>\";|" ${PROJECT_FILE}
sed -i '' "s|${FRAMEWORK_NAME}|${XCFRAMEWORK_NAME}|g" ${PROJECT_FILE}
There might be small differences in other project setups but mostly it boils down to that. Just beware that if the xcframework is meant to be placed somewhere other than in the project root then you need both a name and a path properties in the first sed command, e.g.:
name = "MyFramework.xcframework"; path = "relative/path/to/MyFramework.xcframework"

Xcode 10 build 10A255 requires clean build folder for all changes

After updating Xcode 10 from AppStore, when i add something even one single line, it doesn't apply change to build while i clean project. Is there any trick to prevent this or is it bug.
edit: i'm developing cocoapod, changes in Example app is applied to compile perfect but changes in development pod is require to clean to apply changes.
This is a known issue with the new build system of Xcode 10.
There are radars about this and cocoapods developers are aware of the issue.
In the meantime (because it is not gonna be fixed anytime soon), you can indeed switch to the legacy build system (or clean your build folder each time you want to recompile your example app).
Another option might be to disable cocoapods input/output paths, but it is not recommended.
Please note that switching the Compilation Mode build setting from Incremental to Whole Module will in fact not work as intended.
For more information about this you can check the following issues :
https://github.com/CocoaPods/CocoaPods/issues/7966
https://github.com/CocoaPods/CocoaPods/issues/8073

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.

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.

Make Target's Build Products Path in Xcode dependent on Current Scheme

I have an Xcode Project with three targets:
A Mac app to be distributed on the Mac App Store
The same Mac app, but to be distributed as a demo version on my
website
A login helper app that is a target dependency for the first two
targets
The login helper app is copied on build to the target of the current scheme (let's say the first target), which has a build path of
$(BUILD_DIR)/$(CONFIGURATION)$(EFFECTIVE_PLATFORM_NAME)
When I change the scheme to the second target (the demo), I'd like for the build products path of the third target to be the same, except with _Demo attached to it. Basically, the third target's build path should always be the same as the current scheme's target (either target one or two).
Currently, I do it manually before building, but that's tedious.
Is there no way to make the Per-configuration Build Products Path (and the Per-configuration Intermediate Build Files Path, etc) dependent on the current target?
So when I select the first target, no _Demo gets attached to the path, when I select the second target, _Demo does get attached to the path.
Any pointers would be highly appreciated.
Thank you very much,
Matt
As I understand it, you want the login helper’s building to be aware of which “parent target” it’s being built in. Not sure if that’s possible.
What I’d do in this case, is add a separate “Copy” build step into targets 1 and 2. It sounds like copy is currently a part of building target 3, but it works better if it’s part of building targets 1 and 2.
I have a very similar situation with a command-line helper in one of my tools. Here’s the relevant part of my build settings.

Resources