watchOS macOS tvOS ios


Introducing AppManifest, a library for defining your app’s deployed environment configuration.

Inspired by the Swift Package manifest and SwiftUI APIs, the library aims to provide a type-safe manifest of the available environments, the conditions in which they’re available and even custom settings that will be applied when using that environment.


Using a resultBuilder it couldn’t be easier to define your Environment‘s:

let config = AppConfiguration {
    Environment(name: "Dev", configuration: .debug)
    Environment(name: "Test", distribution: .testFlight)
    Environment(name: "Release", distribution: .appStore)

As you can see a unique name must be provided as well as any conditions (distribution or configuration) where the specified Environment is available. This allows you to define all of your Environment‘s up-front and let the configuration decide which is the preferredEnvironment automatically.

You can review the AppConfiguration header-docs for details on how priority is determined, however in most cases the behaviour should be ‘as expected’. You can also review the unit tests to see working implementations.

Its also common to have Environment specific settings like API keys, URLs and more. The library provides a familiar SwiftUI.Environment style API, making it easy to learn and extend for your own needs.

Environment(name: "Release", distribution: .appStore)
    .setting(\.remoteUrl, URL(string: ""))

The library provides a default implementation of remoteUrl for you. See below for more details on how to create your own settings.

Once you have configured your Environment‘s and any associated settings, you can use the configuration to determine the most appropriate Environment in the current context (e.g. AppStore, Debug, etc)

// Dev when in DEBUG
// Test when installed via TestFlight
// Release when installed via the AppStore

Custom Settings

You can create custom setting values by extending SettingValues similarly to how you would extend the SwiftUI.EnvironmentValues.

private struct MySettingKey: SettingKey {
    static let defaultValue: String = "foo"

extension SettingValues {
    var myCustomValue: String {
        get { self[MySettingKey.self] }
        set { self[MySettingKey.self] = newValue }

You can then apply the setting to your Environment:

Environment(name: "Dev") { }
    .setting(\.myCustomValue, "bar")


If you need to access settings from your current Environment the library also provides a convenient dynamic PropertyWrapper that will cause your View to update whenever the associated setting changes:

@EnvironmentSetting(\.myCustomValue) private var value // "bar"

In order for this to behave correctly however you will need to inject the Environment somewhere at the top-level of your View hierarchy. Ommitting this step will throw an assertion.

struct DemoApp: App {
    var body: some Scene {
            .environment(\.deployedEnvironment, config.preferredEnvironment)

Note deployedEnvironment is a pre-defined EnvironmentKey you must use in order for the property wrapper to function.

Custom Distribution and Configuration

The library includes the most common distributions:

  • AppStore
  • TestFlight
  • Debugger (Xcode, lldb, etc.)

and Configurations

  • Debug
  • Release

But you can also extend the library to support your own custom types:

struct Beta: Configuration {
    // Lets make it active whenever `BETA` has been defined
    var isActive: Bool { 
        #if BETA
        return true
        return false

Taking inspiration from SwiftUI style modifiers:

extension Configuration where Self == Beta {
    static var beta: Self { .init() }

We can now use this to define an Environment:

Environment(name: "Beta", configuration: .beta)

The process for creating a custom Distribution is identical.


You can install manually (by copying the files in the Sources directory) or using Swift Package Manager (preferred)

To install using Swift Package Manager, add this to the dependencies section of your Package.swift file:

.package(url: "", .upToNextMinor(from: "1.0.0"))


View Github