I've been developing a small server-side Swift project that requires the use of an API, and so access to an API Token on the server-side. I was wondering if there's a way to retrieve this token as a string from a bash environmental variable, this would mean I can make the source code public and still save the credential in an env variable for CI or deployment.
I'm open to any alternatives. I basically just wanted a semi-automated way that would let me use an API token (String) in my code without having it hardcoded.
I came across this on StackOverflow but it didn’t work. I believe this is for Xcode env variables? I’m trying to do this with a SwiftPM only project. No Xcode in Linux :)
Thanks I've been trying to use it with env variables, although I'm not sure I understand the syntax for env variables. What exactly do they mean by PATH__TO__CONFIGURATION=value? Say, how would I read an env variable called API_KEY?
Configuration uses a double underscore to separate paths. This can be used to group settings in a hierarchy.
If your variable is called API_KEY, it doesn't contain any path separators, so you should be able to read it with:
let settings = ConfigurationManager().load(.environmentVariables)
let apiKey = settings["API_KEY"] as? String
If your variable was named MYAPP__API_KEY, you would load it as:
let apiKey = settings["MYAPP:API_KEY"] as? String
I find hierarchies very useful as I load default settings (excluding secrets) from a JSON file and then overwrite (and add secrets) where needed via environment variables. For example:
Part of settings.json file:
{
"DATABASE": {
"URI": "something"
}
}
I can then load this as follows:
let settings = ConfigurationManager()
.load(file: "Configuration/settings.json", relativeFrom: .project)
.load(.environmentVariables)
let uri = settings["DATABASE:URI"] as? String
And if needed, I can overwrite this by setting a DATABASE__URI environment variable.
I am using this technique in a project I hope to open source soon.
Thanks a lot for everyone's help.
I was able to get everything working with the FoundationProcessInfo.
IBM's Configuration seems amazing, especially for config files, but in my case, I just need to read this one env variable and as my code doesn't run on REPL, Foundation suffices. I might use it on another bigger project though, have been looking for something like this for quite a while.
Any tricks for getting this to work? I have tried starting xcode from my shell, running my build from command line with xcodebuild, and nothing seems to pick up the environment variable.
I found my self here with the same question and I think some of the confusion comes from Xcode. If I run a swift project in Xcode I get the Environment Variables from the schemeplbus some Xcode ones.
If I build and run using swift build followed by run I get the system environment variables.
My next session in life is to work out how to get he system environment variables when I run in Xcode.
adding them to the Xcode schema means the secret will end up in git
IIRC, only shared schemes end up getting committed into Git.
Alternatively, one common practice is to support passing in credentials via a command line argument with a prefix that denotes where to get them. For example:
--password ENV:xxx gets the password from the xxx environment variable
--password FILE:xxx gets it from a file
--password KEYCHAIN:xxx gets it from a keychain item
--password STDIN gets it from stdin
ps I’m not a fan of passing credentials in via an environment variable because, on the Mac at least, you can view (the initial) environment variables of any process using ps.
Environment variables on any platform are not safe in memory. I agree long term not ideal. I will eventually add a platform specific secrets manager. For the time being:
In my macOS command line app, I would like to access some environment vars when running in continuous integration. Can I use processInfo to read vars without defining them in the scheme?
e.g. given a command line app foo.out,
export var=1
./foo.out
In foo.out:
print(ProcessInfo.processInfo[var])
Can I use processInfo to read vars without defining them in the
scheme?
Maybe I’m reading more into this than I should be, but it seems like you’re missing the point of schemes:
ProcessInfo.processInfo.environment lets you read and write the process’s environment.
The scheme lets you add items to the environment when the code is run by Xcode.
If you build a command-line tool and then run it outside of Xcode, the environment settings of the scheme are irrelevant. For example, if you build a command-line tool with this in main.swift:
import Foundation
print(ProcessInfo.processInfo.environment["Hello"] ?? "-")
you can then run it in Terminal as follows:
% ./MyTool
-
% Hello="Cruel World" ./MyTool
Cruel World