Copy pods resources executed everytime I build the project - ios

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?

Related

Dynamically updating xcconfig in Build Phase

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.

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

Slow compile times with react-native iOS app

I am currently working on a react-native iOS app. While developing I noticed that the compile times for the app are really slow. I got a CI hooked up which roughly does the following:
clean git checkout of the applications workspace
run unit tests
run UI tests build the app
Since this is a clean checkout the app always recompiles all of the react static libraries which come with the React.xcodeproj like libyoga.a, libReact.a, etc. This means that a clean CI build roughly takes ~8min only to build all of the react dependencies (~4min * 2 because the UI test target rebuilds React again).
I wondered whether it would be possible to speed up the compile times. I came up with the following idea but need your advice to tell me if it would work or if I am on the wrong track.
Compile all static libraries for iphoneos and iphonesimulator
Put them together in a single file via lipo
Move them to a folder
Put that folder into version control
Link the libraries in that folder into the Xcode project
This way it would only be necessary to build a new set of static libraries if I update the react-native version in the package.json, right ?
Another idea which came to my mind is to build a dynamic framework of react-native. The framework could be build only once and then added to the application via carthage or manually. The dynamic framework would link all the react static libraries and add the correct headers to the header search paths.
Does anyone can give me a hint if this could work or has an idea how to improve the build architecture in order to speed up the compile time ?
Check these two checkbox
1 Show environment variables in build log
2 Run script only when installing
but when you run first time then uncheck it because this load script for first time otherwise you can not open get loading script in packager.

Migrating to Cocoa Pods from Submodules (Xcode iOS)

I'm migrating from submodules to Cocoa Pods. After moving over I have a strange error when I build.
error: unable to open
'/Users/myname/Library/Developer/Xcode/DerivedData/myapp-gzbqnssczmguxecctczxyqqjktqs/Build/Products/Release-iphoneos/myapp.app.dSYM':
No such file or directory
I've Product > Build Clean but get it when running. I don't entirely get derived data, but my understanding is that once I've pod install and run the workspace (as opposed to the project) this should run properly and this missing dSYM should be accessible?
I have actually two build failures, the second being around a different pod, but I'll assume an answer on the more basic question might lead me to an answer on the second.
Well, I would suggest you simply clean the complete derived data. You can do this by going to Window-> Projects and then simply pressing the delete button beside of derived data.
Once you delete that the project should build newly, also creating a new .dysm
Now just build and run and you should be good to go.
Hope that helps, Julian.

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