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
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
:
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:
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
):
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
What Is the Swift Syntax " .Bar" Called
Swift: How to Read Standard Output in a Child Process Without Waiting for Process to Finish
Add @Published Behaviour for Computed Property
How to Add Material to Modelentity Programatically in Realitykit
Self' Used Before All Stored Properties Are Initialized
Swift Equivalent to _Attribute((Objc_Requires_Super))
Create a Paginated PDF-MAC Os X
Alamofire - Nsurlcache Is Not Working
Uifont' Is Not Convertible to 'Uifont'
Distinction in Swift Between Uppercase "Self" and Lowercase "Self"
Swift: Can Someone Explain This Syntax 'Numbers.Sort { $0 > $1 }' for Me
Where Is Info.Plist in Xcode 13? (Missing, Not Inside Project Navigator)
What Is the Reduce() Function Doing, in Swift
How to Create Custom Notifications in Swift 3
Trying to Know When a Window Closes in a MACos Document Based Application