keepin’ secrets on your pillow

The aim of this post is to provide one of the many available methods for keeping secrets, such as passwords or API keys, out of your base code while still ensuring they are accessible during the development phase.

.env file

One common approach is to create a text file, usually named by convention as .env, although you are free to name it as you prefer. This file will contain the secrets of your project.

This is a keep-it-out-of-version-control

This is a medicine; keep out of reach of children version control system. So be sure that it is included in .gitignore.

Commit and push changes asap:

An important consideration is where to place the folder. It must be located in the same root directory where the source code begins.

If you have already committed and pushed the changes, anyone with access to your repository could potentially access that data. You have two alternatives:

  • Use git rebase: With git rebase, you can modify your commit history, allowing you to remove the problematic commit. This is a cleaner approach but requires careful handling to avoid conflicts.
  • Create a new repository: This option will result in the loss of your commit history but can be simpler if preserving history is not essential.

 

Additionally, remember that your .env file is only stored locally on your machine. Ensure you keep it secure to prevent sensitive information from being exposed.

Lets play with XCode

Create a blank app project in XCode and be sure that following settings are set:

Swift Language version to Swift 6 and …

Strict Concurrency Checking is set to Complete, ensuring that our code, in addition to being secure and preventing disclosure of sensitive information, will also be free from data races. Finally, create a new file with the following component.

import Foundation

@globalActor
actor GlobalManager {
    static var shared = GlobalManager()
}

@GlobalManager final class Env {
    static let env: [String: String] = loadEnvVariables()
    static let filename = ".env"
    private init() {
    }

    static func fetch(key: String) -> String? {
        env[key]
    }

    static func loadEnvVariables() -> [String: String] {
        var envVariables: [String: String] = [:]
        guard let path = Bundle.main.path(forResource: filename, ofType: nil) else {
            return [:]
        }
        do {
            let content = try String(contentsOfFile: path, encoding: .utf8)
            let lines = content.components(separatedBy: .newlines)
            for line in lines {
                let components = line.components(separatedBy: "=")
                if components.count == 2 {
                    let key = components[0].trimmingCharacters(in: .whitespaces)
                    let value = components[1].trimmingCharacters(in: .whitespaces)
                    envVariables[key] = value
                }
            }
        } catch {
            print("Error reading .env: \(error)")
        }
        return envVariables
    }
}

Finally, use the Env component to fetch secret data from the .env file.

struct ContentView: View {
    @State private var apiKey: String?
    var body: some View {
        VStack {
            Image(systemName: "globe")
                .imageScale(.large)
                .foregroundStyle(.tint)
            Text("API_KEY:\(apiKey ?? "Not set")")
        }
        .padding()
        .onAppear {
            Task {
                apiKey = await Env.fetch(key: "API_KEY")
            }
        }
    }
}

This is the default ContentView generated in a blank project. We have added content using the .onAppear() modifier to fetch the secret, which is displayed with a Text view. Run the project, and the final result is as follows:

Screenshot

Conclusions

This is a safe way to keep your project secrets away from indiscreet eyes. However, your secret file is only stored locally in your (or the developer team’s) project folder. On this repository is the source code that I have used for writing this post.

Copyright © 2024-2025 JaviOS. All rights reserved