Xcode Env Setup Issues

Order of execution is important

in Xcode Learnings

September 19, 2021

I was encountering weird issues while running the app recently with a Xcode project. I am running Xcode 12.5.1 if that matters.

We have 3 xcconfigs, and 4 plists, 1 target. The 4th plist is just a ‘default/unused’ plist. Based on the flag in xcconfig, upon building, we then have a Run Script that will copy 1 of the 3 appropriate plist file accordingly to the build’s myapp.app folder, replacing the existing Info.plist inside.


Go Xcode -> Product -> Reveal Build Products Folder to show the path to the .app

However, when running the app, I realised that it wasn’t using the correct plist. I found this out when I tried to change the bundle display name and it didn’t reflect in the app. I went to take a closer look at the script, and it was a very straightfoward cp command, and it seems to have the correct source/dest folders upon some debugging. Despite several attempts at debugging all seemed fine. Some said that we need to set input file in the script setting to work, which I think it sort of did as compilation works and the file was replaced, but I get a run error. I don’t know why (until much later when I encounter the same error), so I removed it.

Then eureka came. While looking at the build log, I realised that there is this Process plist script phase being ran. I assumed this was Xcode’s internal build script that it was processing and copying the plist. It came after our Run Script, which I also assumed that it was overwriting our own Run Script. So I tried to moved move our Run Script all the way down (it was before Embed frameworks or something like that) in an attempt to get it to run last.

More Info:

I actually thought it might be because of the Parallelize Build option for the scheme that was messing with the run script ordering. It could may well be, so I turned it off in my testing just in case, and turned it back on again, and it still worked (as we don’t have anything dependent on it). After reading about it here https://pewpewthespells.com/blog/managing_xcode.html, it is not, but it is something to take note of in your debugging.

Lo and behold, it ran after Process plist. After looking inside myapp.app/Info.plist, it was correct! Meaning the script run and the file copied over. **All was good, or so I thought. **

There is 1 issue, the app wouldn’t run on the phone, despite a successful build.

Running it gives a something about a wrong $(EXECUTABLE_NAME) or some other errors about not being valid to run. This was weird, why is it saying something like $(EXECUTABLE_NAME) could not be run instead of the name myapp could not be run? Turns out, the build settings was configured to the default plist. When the Process plist phase was run, it resolved all those $(ENV_VARS) vars in the plist, and output a correct, resolved, final version. So instead of say $(PRODUCT_NAME) in the plist, it was actually myapp in the final plist version.

Now that we roughly know what the heck is happening, this means 1 thing, we need to get OUR version of plist to be used, run and resolved and copied over, not the default plist.

Sidetrack: For learning purpose, what if we have a normal resolved plist and we modify myapp.app/Info.plist? For my testing, I generated the build as per normal, open up myapp.app/Info.plist in Xcode and changed the bundle display name, ensured it’s saved, and run that app into my phone. The result: I cannot run it - gives some error. It seems like we cannot directly modify the final version as they do some kind of checksum check or something. Either that, or because we output the plist as binary, it might not work that straightforward? I have no idea. I might be doing something wrong, perhaps I need to repackage it or what, I don’t know. If anyone knows more indepth knowledge on this, please let me know!

Fun Fact from trying around stuff:

You know the Info tab in your project settings? It actually loads it based the Info.plist File (INFOPLIST_FILE var) you selected, so if you changed it, you need to reload Xcode for it to refresh the values. If you delete the actual Info.plist file or remote it from the project settings, it will show empty screen. This Info tab just makes your life easier by showing the corresponding plist you have selected in INFOPLIST_FILE for your configuration, e.g. DEBUG (PROD) configuration.

To solve the issue at hand, there is a couple ways I could think of:

  1. Create 3 targets, so each target have their own plist. This is really not preferable, since you need to maintain 3 different targets, and it is really hard to maintain as it gets more complex. Imagine have 6 more targets - 3 for tests and uiTests, super messy and ugly.
  2. Get it to process the plist first before calling our script to replace the default myapp.app/Info.plist. Unfortunately I do not have any knowledge to get Xcode to do that. I have not found any online resources that allow you to swap plist at build time. Actually, now thinking about it, we could probably have tried do that in xcconfigs using INFOPLIST_FILE. Damn.
  3. Delete the usused default plist, merged the other 3 plists instead. Use script to change the plist settings instead using PlistBuddy. This is what I tried to go for. 1 plist maintenance is easier than maintaining 3 plists.

I tried all 3:

  1. It worked perfectly, so this is a viable option if things came down to it.
  2. This actually worked great as well, everything was resolved as intended. For the name of the variables for Xcode build settings, search for the same name and take it from here https://help.apple.com/xcode/mac/11.4/index.html?localePath=en.lproj#/itcaec37c2a6.
  3. Works perfectly as well, replaced the plist selection script with another script that modifies the plist instead, something like this:
# Type a script or drag a script file from your workspace to insert its path.
echo "Configuration '${CONFIGURATION}' detected."

if [ "${SOMECONDITION}" == "YourEnvValue" ]; then
  $PLISTBUDDY -c "Set :NSAppTransportSecurity:NSAllowsLocalNetworking false" "${INFO_PLIST}"
  $PLISTBUDDY -c "Set :NSAppTransportSecurity:NSAllowsLocalNetworking true" "${INFO_PLIST}"

Still have not decide if we should stay with option 2 (4 plists) or option 3 (1 plist, 1 run script).

I also noticed that they something cached the build folder so just do a clean. If you are using the old build system, there are 2 clean, one is normal clean, the other is when you press option key, it will show clean derived folder. There is some workarounds like https://stackoverflow.com/a/45861923/321629 but I don’t feel this is an issue esp. if you are using a build system which should start from a clean build anyway. Good to know though.

Useful Tip #1:

One thing you can try to see if your .app is a valid build, run this:

codesign -vv -d /path/to/.app

and you will be able to see if the values inside are correct, e.g. bundle id, plist. Before I found out the ordering of script run was the issue, I ran this and it output invalid plist under the plist field, so this is useful.

You can also try codesign --verify /path/to/.app to see if your app was signed correctly. In my test, modifying the plist manually is still valid even though it can’t be installed to the phone.

Useful Tip #2 (run the command to show resolved values):

If you are using project:

xcodebuild -project myproject.xcodeproj -target "myapp-uat" -showBuildSettings

If you are using workspace:

xcodebuild -workspace myapp.xcworkspace -scheme myapp-release -showBuildSettings

You can do a grep PODS_ROOT to quickly show the variable you are looking for, or just > ~/files.txt to save it.

This is useful when you are unsure of what a variable is actually pointing to. I use this in conjunction with xcconfigs to figure them out.

I am assuming there is something else that I don’t know enough about, so if you know how to resolve it in another way, let me know!

Update 1 Nov 2021: If you are new to xcconfig, I would recommend reading this: https://www.raywenderlich.com/21441177-building-your-app-using-build-configurations-and-xcconfig
This is a very very good place to start! I just stumbled on it recently.. If only it came out earlier!

comments powered by Disqus