EnvironmentConfig is a Swift library that allows you to parse environement variables into a struct, converting them if needed and possible. It allows for eager loading of the configuration, and can be made to volountarily crash your program early if a missing variable is needed.
The library uses property wrappers as java-like annotations to achieve this.
This library is distributed using SwiftPM:
.package(url: "https://github.com/abarisain/SwiftEnvironmentConfig.git", from: "1.0.0-beta1")
EnvironmentConfig can be used in two ways:
- Implicitly, where the library will try to guess the name of the environment variable from the field name.
- Explicitly, where you provide the expected name. The library will only try lower/uppercase versions of the name on your behalf.
struct Config {
@EnvField()
var user: String
@EnvField()
var password: String
}
// You can catch EnvironmentConfigError.unparsableOrMissingValue to see
// why the library failed to load your variables.
let config = Config()
// The library will try to read from user, USER, password and PASSWORD
try EnvironmentConfig.load(config)
Prefixes are supported:
struct Config {
@EnvField()
var user: String
@EnvField()
var password: String
}
let config = Config()
// The library will try to read from mysql_user, MYSQL_USER, mysql_password and MYSQL_PASSWORD
try EnvironmentConfig.load(config, prefix: "mysql")
The library will also try to convert camel cased fields to snake case:
struct Config {
@EnvField()
var databaseName: String?
}
// The library will try to read: databaseName, DATABASENAME, databasename, database_name, DATABASE_NAME
let config = Config()
try EnvironmentConfig.load(config)
If you only use explicit keys, you don't have to call .load()
:
struct Config {
@EnvField(name: "mysql_user")
var user: String
@EnvField(name: "mysql_password")
var password: String
}
// All values will be eagerly fetched at instanciation
// Any non optional value will no default value will crash the program.
let config = Config()
Note: if you mix implicit and explicit names,
.load()
will only throw an error for implicit names. Explicit ones will still crash the program as soon as your struct is instanciated.
The library supports setting default values:
struct Config {
@EnvField(defaultValue:"user")
var user: String
@EnvField(name: "mysql_password", defaultValue: "secret")
var password: String
}
// Will not crash, even if the environment variables are missing
let config = Config()
try EnvironmentConfig.load(config, prefix: "mysql")
Optionals are of course supported:
struct Config {
@EnvField(defaultValue:"user")
var user: String?
@EnvField(name: "mysql_password")
var password: String?
}
// Will not crash, even if the environment variables are missing
let config = Config()
try EnvironmentConfig.load(config, prefix: "mysql")
As of writing, the library supports the following types:
- String
- Int/UInt (Int8, Int16, ...)
- Bool ("1", "true" or "TRUE" for true, "0", "false" or "FALSE" for false)
Note that any other type than String can fail to be converted, in which case the library will throw EnvironmentConfigError.unparsableOrMissingValue
.
Any type that conforms to EnvironmentValueInitializable can be used with @EnvField.
Example of an extension that allows a Data to be initialized by its hexadecimal representation if it begins with "0x":
import Foundation
extension Data: EnvironmentValueInitializable {
public static func from(envValue hexString: String) -> Any? {
var startIndex = hexString.startIndex
if !hexString.hasPrefix("0x") {
return nil
}
startIndex = hexString.index(startIndex, offsetBy: 2)
var data = Data()
while startIndex < hexString.endIndex {
let endIndex = hexString.index(startIndex, offsetBy: 2, limitedBy: hexString.endIndex) ?? hexString.endIndex
var substr = hexString[startIndex ..< endIndex] // 1 or 2 bytes
if substr.count == 1 {
// Assume 0 for the 2nd char
substr += "0"
}
data.append(UInt8(substr, radix: 16)!)
startIndex = endIndex
}
return data
}
}
- Array support
- Support nested structs (if possible)
This library has been inspired by Environment It's basically a lighter version of it, that allows the program to crash early if a variable is missing. This lightens the noisiness of optional checking (or empty default values) for configuration that a program cannot run without. It also adds implicit names.
The need for this library came from using envconfig and wanting a Swift version of it.