Dynamically updating xcconfig in Build Phase - ios

I'm currently working with a couple of Xcode workspaces. The goal of the workspaces is to allow one version be built with prebuilt versions of the dependency projects while other one will be built with all the dependencies as projects.
Say we have main project A. And that A has dependencies B and C. We have two workspaces that contain the following projects.
Workspace 1:
A.xcodeproj
B.xcodeproj
Workspace 2:
A.xcodeproj
B.xcodeproj
C.xcodeproj
For Workspace 1, A and B exist as projects but C would be treated as a prebuilt framework. For Workspace 2, A, B, and C exist as projects. In other words, A and B are debuggable in Workspace 1 whereas A, B, and C are debuggable in Workspace 2.
The goal is to be able to have these 2 (or more) workspaces in such a way that building does not result in having to first modify any of the existing project files.
I have this working by way of a Build Phase which copies an xcconfig file which "configures" A's Build Settings. This Build Phase is done with another project file which which I'll call Bootstrap.xcodeproj. This project file contains the Build Phase which copies the xcconfig and also has A as the Build Target defined in it's Scheme. What I notice is that regardless if Parallel Builds is on or off, sometimes Bootstrap is run before or after A. And even if run before A, the Build Settings don't always get updated.
This is noticeable if I build Workspace 1 and then Workspace 2. When building Workspace 2, the first build will fail because it is still using the xcconfig from Workspace 1.
Seeing if anyone knows of a way to get Xcode to re-update Build Settings from xcconfig dynamically or better orchestrate this.

I found a solution for this from here: how to make xcode run a script before dependencies?
I've moved my Build Phase script in Bootstrap to a pre-action script. I've done a few builds and this seems to correctly allow the copied over xcconfig to be used.

Related

Copy pods resources executed everytime I build the project

I have an app (A) and 3 frameworks(F1, F2, CommonFramework)
A depends on all 3 frameworks
F1 and F2 depend internally on CommonFramework
To integrate these frameworks in app and to manage dependencies among frameworks I have used Cocoapod
Problem Statement
Build time is too long
Observations
I analyzed the build time reports and observed that every time it tries to Copy pod resources
It is trying to copy files from derived data
Hacky solution:
I have one hacky solution to check the 'for install builds only' flag under the Build phase
Can you please suggest a proper solution?

Can't assign xcassets to the main target on Xcode 11

On Xcode 11 my .xcassets, that don't cause any issue on Xcode 10, cause the compilation to fail with the following message:
error: Multiple commands produce '/Users/user/Library/Developer/Xcode/DerivedData/project-enjiypsgxtcdbnaripixgtnjlagx/Build/Products/Debug-iphonesimulator/project.app/Assets.car':
1) Target 'project' (project 'project') has compile command with input '/Users/user/sandbox/project/Resources/buttons.xcassets'
2) That command depends on command in Target 'project' (project 'project'): script phase “[CP] Copy Pods Resources”
The only way I'm able to compile the project is by removing the .xcassets from the target with the obvious downside of having them not available on the build.
PS: This is happening for 2 ObjC projects.
Add below line at the top of your pod file, right after platform :ios
install! 'cocoapods', :disable_input_output_paths => true
Reference
The answer above is only partially correct: yes, this happens because the pod phase produces Assets.car as an output file, which coincides with the output name for your xcassets - hence Multiple commands produce... error: one command being the script phase [CP] Copy Pods Resources and another - your own project's compile command.
But removing Assets.car from output files will cause the build system to fail to see that the script processes the file, thus it skips it (read this for more details). So what we can do is add Assets.car to the input files - this will create a dependency for the script, telling the build system to wait for the file before executing the script.
NB: like the solution above, you will have to do this every time after updating your pods, since [CP] Copy Pods Resources is generated every time you run pod install or pod update
To be sure that all build steps are performed in the correct order, yet as many steps in parallel as possible, Xcode needs to know for every build step which input files it depends on and which output files it will generate.
E.g. if step A depends on an input file that is the output file of step B, step B surely has to run before step A. If neither step requires the output file of the other step, they can both run in parallel.
While several steps can surely depend on the same input file, it cannot be the case that multiple steps produce the same output file as in that case, whichever step runs last would overwrite the output file of whichever step runs first and this means the resulting output file content would be unpredictable and that is an error in a build process that shall created predictable results!
In your case the problem is that your Copy Resources build phase contains one (or more) .xcassets bundle(s). Xcode won't just copy these bundles, instead it will combine them, convert and optimize their content, and create a file called Assets.car that contains the content of all the assets catalogs in your project.
Yet if you integrate a Pod and this Pod also has an asset catalog, exactly the same thing will happen and a second Assets.car file is created and now Xcode has a problem: Two build steps both say they create an Assets.car file. Which one shall win? Which of these two files shall end up in the final application? And what about other steps that depend on this file? After which of the build steps do they have to run? This is a serious problem that Xcode cannot resolve.
There are currently two possibly solutions:
Use frameworks. When you use frameworks instead of static libraries, the resources of Pods don't end up in the resources folder of your main application but in the resources folder of the built framework. All you need to do is to add use_frameworks! to your Podfile, either top level or just for a specific target. The downside is that your overall app size will grow a bit and app start time will increase a bit.
Have the Pod-author fix the Pod. Instead of resources s/he shall use resources_bundle as the Podspec documentation also strongly recommends. Using resources actually has other disadvantages (resource files are not optimized, name conflicts can arise between different Pods), whereas using a resource bundle is safe, regardless if Pods are embedded as static libraries or as dynamic frameworks. Note, though, that it is not enough to just alter the Podspec, all code that loads bundle resources must be adopted to load them from the correct bundle (the name will be known as when using resources_bundle in a Podspec file, you also must give the bundle(s) a name).
There is an on-going bug report for this issue, you can find it here:
https://github.com/CocoaPods/CocoaPods/issues/8122
Yet the problem currently is, that every possible solution other than the two mentioned above would basically be an ugly hack and the Cocoapods developers are not happy implementing ugly hacks. The best solution, as hard as it seems, is probably to not fix that problem at all but to stop supporting resources in Podspec files and make resources_bundle mandatory for Pods that contain own resources.
In your library project, such as "LADetailPage", open Targets' "Build Phases", create a predefined phase named "Copy Files", then drag your *.xcassets to the new phase.
Then the library won't make Assets.car from *.xcassets files when building, instead, cocoapods copies the *.xcassets to your main project which imports the lib, and the main project builds all *.xcassets into a single Assets.car file.
Please check your dependencies if there is some pods use 'resources'(not 'resources_bundle') to link its own xcassets in podspec.
This way has beed deprecated, because its output file name is 'Assets.car'. It is same with your project xcassets' compile output name.
Xcode->File->Workspace settings->Building System-> Legacy Build System
example

In Xcode 9's new build system, how do I execute commands before the check dependencies phase?

I have an foo.xcconfig file that will have different values depending where in the world a developer is. Currently, we use a phase to create this file (which is imported by other config files). In the new build system, Xcode is now resolving the dependencies before I have a chance to create a dummy to satisfy the include.
This is easily solved for CI builds by doing the work before calling xcodebuild, but for local builds using the GUI, there does not seem to be a good solution.

Embedded framework build directory not matching scheme

I have several frameworks embedded in my Xcode workspace. In my scheme list, I have four schemes - 2 products: 1 release, 1 beta version of each. Each scheme has its own info.plist, product name, build settings, build path. In my main scheme, my app will build and archive correctly.
When I change to the alternate scheme (release or beta) I get a build error because my embedded frameworks are building in the original build/Release-iphoneos directory and not the build/Release AlternateName-iphoneos. I have tried everything I can think of to get my Framework SearchPaths and Library SearchPaths to point to the embedded framework build directory but I'm having no luck.
Has anyone run into this problem before or have any suggestions? Would the best solution be to have a run script phase that copies the embedded builds into the target directory?

Looking for an Xcode build script to copy files between targets

Does anyone have a script that can copy files from one target to another, during the build process?
I have a project with one target that builds an app and another target that builds a library. The library needs to contain a large subset of the files from the app and I want to avoid manually copying them over.
In the build phase of an XCode 4.x project you can add a build phase to either copy files or just run a script (that does your copying for you)
See this SO question adding-external-scripts-to-xcode4, for additional details

Resources