Pragmatism in the real world

Introducing SwiftDotEnv

Regardless of which language you use, there are always configuration settings that need to be handled.

One of the tenets of twelve factor app is that config should be stored in the environment. This is easy enough on the command line or in the config of our preferred hosting solution. When developing locally without a VM however it gets a little more complicated as we may need the same environment variable set to different values for the apps we’re running.

One solution to this is dotenv. This was originally a Ruby library. It reads a file on disk, .env by default and loads the data within it into the environment.

A typical .env file might look like this:

DB_HOSTNAME="localhost"
DB_PORT=5984
DB_HTTPS=false

When the application runs, the .env file is loaded and then each item becomes a standard environment variable that is used for configuration (of the db in the example).

When developing locally or maybe running a build within a CI environment, the configuration is determined by the .env file and then in production, you use the configured environment variables. Hence you should ensure that .env is listed in the project’s .gitignore file.

In addition to Ruby, there are dotenv libraries for a fair few languages such as for PHP, JS, Haskell, Python, etc, but I couldn’t find one for Swift.

Hence I wrote one.

SwiftDotEnv is a very simple package for Swift 3 that reads .env files and loads them into the environment, so that they are accessible via getenv() and NSProcessInfo.processInfo().environment.

You install it via Swift Package Manager, so add this to the dependencies array in Package.swift:

.Package(url: "https://github.com/SwiftOnTheServer/SwiftDotEnv.git", majorVersion: 0),

To use it, you import the module, instantiate it and then use various get methods:

import DotEnv

let env = DotEnv()

let host = env.get("DB_HOST") ?? "localhost"
let port = env.getInt("DB_PORT") ?? 3306
let isEnabled = env.getBool("DB_HTTPS") ?? true

For getBool(), A variable is determined to be true if it case-insensitively matches "true", "yes" or "1", otherwise it’s false.

By default, DotEnv will look for a file called .env in your root directory, but you can use let env = DotEnv(withFile: "env.txt") to load env.txt should you wish to.

I also implemented subscript access which makes `env` look like an array if that feels cleaner, so you can also do:

let host = env["DB_HOST") ?? "localhost"
let port = Int(env["DB_PORT"] ?? 3306)

This is a nice simple library that will no doubt be improved over time, but it solves a problem I have today!