Referring to Environment Variables in Swift

How do I reference environment variables in Swift?

@OOper pointed me in the right direciton. All I had to do was:

let apiKey = ProcessInfo.processInfo.environment["GMAPS_API"]

Using Environment Variables for Per Environment Configuration in iOS

You are correct. The Environment Variables are only meaningful when executing the Scheme in Xcode. Their primary use in that context is to activate debugging features that are not on all the time. For example, if you try to create a bitmap Core Graphics context (CGContext) with an invalid set of parameters, the debugger may tell you to set an environment variable to see additional debugging output from Core Graphics. Or you can set environment variables to turn on memory management debugging features.

When you are running an application on a server, the Unix framework in which the application is running is part of the "user experience". In that context it makes sense for the application to use things like environment variables.

As developers we understand that a mobile app is running inside a unix process, but that unix environment is mostly unavailable to us. A similar feature that is common to Unix apps is command line arguments. An iOS application on startup receives command line arguments (argc and argv) but there is no way specify those when the app is launched either.

There are a number of places you could include configuration information like that which you describe in your application. The most common that I can think of is to include the setting in the applications Info.plist. At runtime you could access the contents of the property list by fetching the main bundle and asking for it's infoDictionary:

let infoBundle = Bundle.main.infoDictionary
let mySetting = infoBundle["SomeSetting"]

When the application's info.plist is created, it DOES have access to the environment variables declared in the Scheme so you could put the environment variables in the scheme, reference them in the Info.plist, and retrieve them at runtime from the main bundle.

Xcode: how to compile environment variables and refer to them in Swift?

You can do a pre-action script in Xcode build section
Xcode pre-action build

Which will modify placeholder with the following code:

let apiKey : String = "<# THE_API_KEY #>"

Modify the code directly in the source file

Before each build.

And you can add another one if you have a production key in the Archive pre-action

Exemple

place the apiKey variable in the file you want to access it

In Pre-action do a script to remplace a place holder text like THE_API_KEY

The script will look like this

 cat $PROJECT/$PATH_TO_FILE | sed 's/THE_API_KEY/YOUR_KEY' > $PROJECT/$PATH_TO_FILE

Don't forget to clean the code to avoid put the API key in you commit

With a Post-action

cat $PROJECT/$PATH_TO_FILE | sed 's/YOUR_KEY/THE_API_KEY' > $PROJECT/$PATH_TO_FILE

Storing Global/Environment Variables in iOS Swift Apps

What I always do is probably a more flexible approach than checking constants in your code and littering different values of the same variable per environment everywhere.

It is not necessary to create .xcconfig files either.

Setting up the whole thing is a little more work but you will see that it is very easy to use afterwards.

In my example you see how I set different REST endpoints for production/UITest/development builds in my Toeppersee iOS app.

Changes to your Info.plist

First, just define a new constant and select a name that identifies your content, in my case it is TS_WEBSERVICE_URL:
Info.plist
Make sure to set its value to the name you chose, enclosed in $() (brackets, not braces!), in my case it's $(TS_WEBSERVICE_URL).

Build settings

In your build settings tab, add custom "user defined" build parameters, again with the same name:

build settings

You see that you can define values for Debug, UITests and Release. And that I use this mechanism for a whole lot of things that are different in the environments.

Adding more environments

If you need more environments, add them on your master project node's Info tab (like I probably did here with UITests):
Add environments

Use the values in code

Now for the easy part: use your newly defined values in your code. It all boils down to getting an NSBundle instance and reading the value defined in your Info.plist which has been substituted automatically during build process.

Objective C:

NSString *webserviceURL = [[NSBundle mainBundle] objectForInfoDictionaryKey:@"TS_WEBSERVICE_URL"];

Swift:

let webserviceURL = Bundle.main.object(forInfoDictionaryKey: "TS_WEBSERVICE_URL") as! String

Just one line of code per environment setting.

One thing I have not shown, but which is also fairly easy: just define a category to NSBundle and supply new methods called webserviceUrl and similar methods. They can then be called like this: Bundle.main.webserviceUrl() which even more simplifies the source code.

Edit: write extension to Bundle object (optional)

To achieve the aforementioned simplified access to the webservice URL, a new Swift file is created and called (for example): Bundle+AppAdditions.swift

import Foundation

// An extension (or category in Objective C) extends any types functionality,
// in our case the NSBundle foundation class.
extension Bundle {
// get the webservice URL from an NSBundle instance
public func webserviceUrl() -> String {
// get an object for info dictionary key and cast it as string
return self.object(forInfoDictionaryKey: "TS_WEBSERVICE_URL") as! String
}
}

The extension works directly on any given Bundle instance, not necessarily only the main bundle. Example:

let url = Bundle.main.webserviceUrl()

or any other bundle, e.g. for an App Clip Extension containing an InitialViewController:

let url = Bundle(for: InitialViewController.self).webserviceUrl()

I would actually add another static extension method to get the app clip extension bundle. But that's optional as well ;-)

Access user environment variable in Swift for macos

I've been struggling with a very similar problem recently. While I don't have a perfect solution for you, I do have some suggestions.

First, it really matters where exactly MY_ENV_VAR has been defined. If it is inside of a .bashrc file, you'll need to start up bash in interactive mode with -i so it reads that file. If it is defined in .bash_profile, you'll need use -l to start a so-called login shell.

In my particular case, I have to use both -i and -l to see the env vars I'm after. This all gets even more complex if the user doesn't use bash. I use fish, for example, so my required ENV variables aren't visible to bash at all, regardless of the bash invocation.

So, what I actually do is first determine what shell is being used, and then invoke that shell with -i -l -c. zsh, bash and fish all support these options.

It's a real pain, and I'm not even sure that method is reliable. But, it is working for me across a number of different user environments.

fastlane.swift cannot find 'ENV' in scope

To access to environment variables in swift you should use ProcessInfo. For instance, there is a sample code in Fastfile.swift:

class Fastfile: LaneFile {

func testLane() {
desc("Description of what the lane does")

if let apiToken = ProcessInfo.processInfo.environment["API_TOKEN"] {
NSLog("API TOKEN: \(apiToken)")
}
else {
NSLog("Error: No token.")
}
}
}

Now you can set the environment variable and run your lane:

$ export API_TOKEN='CACD1B9D-56AA-41F0-BDE3-457DDA30A8D4'
$ fastlane testLane

Outputs:

...
[13:56:58]: $ ./FastlaneRunner lane testLane swiftServerPort 2000 > /dev/null
[13:56:59]: ▸ 2022-05-31 13:56:59.494 FastlaneRunner[34213:828339] API TOKEN: CACD1B9D-56AA-41F0-BDE3-457DDA30A8D4
[13:56:59]: fastlane.tools finished successfully br>


Related Topics



Leave a reply



Submit