Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to use custom Xcode Cloud environment variables?

We have these environment variables within the Xcode Scheme

enter image description here

Which works well locally with this code

let webHost = ProcessInfo.processInfo.environment["HOST_URL"]!
let apiHost = ProcessInfo.processInfo.environment["API_URL"]!
let beamsKey = ProcessInfo.processInfo.environment["BEAMS_KEY"]!
let mixpanelKey = ProcessInfo.processInfo.environment["MIXPANEL_KEY"]!

However, when deploying using Xcode Cloud with the same environment variables.

enter image description here

It succeeds in building, but the app crashes with this log.

enter image description here

What is the right way to read these environment variables when using Xcode Cloud?

like image 466
grey Avatar asked Jan 25 '26 06:01

grey


2 Answers

So, this was an absolute headache but I finally figured out a satisfactory way to access and use these variables in code.

My solution uses:

  • A (gitignored) JSON file to store the variables locally
  • Xcode Cloud to send the variables in the CI
  • A ci_pre_xcodebuild.sh file to write the environment variables in the JSON
  • A Swift file that allows you to conveniently access the secrets.

Step 1: Basic JSON file

  • In your .gitignore file, add a new entry for the JSON file and its path
  • Create a JSON file through Xcode
  • Add your keys to this JSON file.
Secrets.json

(at: YourProject/SupportingFiles/secrets.json)

{
    "STRIPE_KEY": "",
    "GOOGLE_MAPS_KEY": "",
    "GOOGLE_PLACES_KEY": "",
    "BASE_URL": "https://dev.api.example.fr"
}

Step 2: Write the variables in Xcode Cloud

Environments values set in the Xcode Cloud job

In this screenshot you can see that I've duplicated the keys for different environments. I didn't expand on this for the sake of brevity, but you can definitely have different secrets JSON files for different Xcode Scheme configurations.

Step 3: Add a ci_pre_xcodebuild.sh file

Important: the name of the files and their position matter.

The goal here is to add a script that the CI (Xcode Cloud) will execute each time it builds. In this script, we're going to create and fill our JSON.

  1. Add a new group at the root of your project named "ci_scripts"

Adding a group with the bottom plus button

  1. In this group, add a new file called ci_pre_xcodebuild.sh

  2. Write the following:

#!/bin/sh

echo "Stage: PRE-Xcode Build is activated .... "

# Move to the place where the scripts are located.
# This is important because the position of the subsequently mentioned files depend of this origin.
cd $CI_PRIMARY_REPOSITORY_PATH/ci_scripts || exit 1

# Write a JSON File containing all the environment variables and secrets.
printf "{\"STRIPE_KEY\":\"%s\",\"GOOGLE_MAPS_KEY\":\"%s\",\"GOOGLE_PLACES_KEY\":\"%s\",\"BASE_URL\":\"%s\"}" "$STRIPE_KEY" "$GOOGLE_MAPS_KEY" "$GOOGLE_PLACES_KEY" "$BASE_URL" >> ../Dream\ Energy/SupportingFiles/Secrets.json

echo "Wrote Secrets.json file."

echo "Stage: PRE-Xcode Build is DONE .... "

exit 0

Of course, you need to change this text depending on your keys and the location of the file. I added a few keys as an example.

  1. In your Terminal, navigate to the new file's folder and run this command: chmod +x ci_pre_xcodebuild.sh. This fixes a warning in Xcode Cloud.

Step 4: [BONUS] A simple Swift file to access the environment variables

import Foundation

struct Secrets {
    private static func secrets() -> [String: Any] {
        let fileName = "Secrets"
        let path = Bundle.main.path(forResource: fileName, ofType: "json")!
        let data = try! Data(contentsOf: URL(fileURLWithPath: path), options: .mappedIfSafe)
        return try! JSONSerialization.jsonObject(with: data) as! [String: Any]
    }

    static var baseURL: String {
        return secrets()["BASE_URL"] as! String
    }

    static var stripeKey: String {
        return secrets()["STRIPE_KEY"] as! String
    }
}

like image 138
Pomme2Poule Avatar answered Jan 28 '26 03:01

Pomme2Poule


I had a similar issue, mostly i wanted to add an api-key in the project without this exist in the source code. So I had to create a ci_pre_xcodebuild.sh file

#!/bin/sh

echo "Stage: PRE-Xcode Build is activated .... "

# for future reference
# https://developer.apple.com/documentation/xcode/environment-variable-reference

cd ../ProjectName/

plutil -replace API_KEY_DEBUG -string $API_KEY_DEBUG Info.plist
plutil -replace API_KEY_RELEASE -string $API_KEY_RELEASE Info.plist

plutil -p Info.plist

echo "Stage: PRE-Xcode Build is DONE .... "

exit 0

and in the code we have

let key = config.preferences.debug ? "API_KEY_DEBUG" : "API_KEY_RELEASE"
guard let apiKey = Bundle.main.infoDictionary?[key] as? String
like image 40
gmetax Avatar answered Jan 28 '26 01:01

gmetax



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!