Configuration

No two apps are the same. That's why Parra supports a wide variety of configuration options both for our own features and different styles of apps. This page details how you can use our primary SDK configuration object, as well as other configurations for different styles of apps, like integrating custom app delegates, scene delegates, or working with multi-window apps.

ParraApp Configuration

Configuration in the Parra SDK is largely centralized to the ParraConfiguration object. An instance of this object can optionally be passed to the configuration parameter when you create your ParraApp instance. The configuration object controls global component attributes, theme, and other options.

struct ParraConfiguration {
    let appInfoOptions: ParraAppInfoOptions
    let theme: ParraTheme
    let globalComponentAttributes: ParraGlobalComponentAttributes
    let launchScreenOptions: ParraLaunchScreen.Options
    let loggerOptions: ParraLoggerOptions
    let pushNotificationOptions: ParraPushNotificationOptions
    let whatsNewOptions: ParraWhatsNewOptions
}

You can find documentation on the values for the theme and globalComponentAttributes parameters in the customization section. The options available for the remaining parameters can be found below.

App Info Options

struct ParraAppInfoOptions {
    /// The bundle ID of the application that should be reported to the Parra API.
    /// This generally only needs to be specified if you're building for an environment where
    /// you don't want this value to match the bundle ID from the Info.plist.
    let bundleId: String
}

Logger Options

struct ParraLoggerOptions {
    /// Controls the behavior of the logger. In debug logs go to the console. In production
    /// logs are uploaded to Parra. Options are debug, production, or automatic. Automatic 
    /// is used by default and uses debug under the #DEBUG compilation condition and
    /// production otherwise.
    let environment: ParraLoggerEnvironment
    
    /// Logs below the minimum log level will be filtered out. By default, this is set 
    /// to "info". The order is defined as:
    /// trace < debug < info < warn < error < fatal
    let minimumLogLevel: ParraLogLevel

    /// A description of which pieces of information should be included in a log written
    /// to the console, in order.
    let consoleFormatOptions: [ParraLoggerConsoleFormatOption]
}

What's New Options

struct ParraWhatsNewOptions {
    /// How the what's new popup should be displayed. Options are "modal" or "toast."
    let presentationStyle: PresentationStyle

    /// Options are "automatic", "delayed" and "manual". Use automatic if you want the
    /// what's new popup to be displayed as soon as it is available (assuming it is), delayed
    /// is the same after a specified timeout. Use "manual" mode if you intend to use your own
    /// trigger to display this later, via the presentParraRelease API.
    let presentationMode: PresentationMode
}

Using a custom App or Scene Delegate

The Parra SDK provides default implementations of the app delegate and scene delegate internally, which it uses for a wide variety reasons, like hooking into events for push notification token creation and observing changes in scenes. The app delegate needs to know about the scene delegate during initialization and the app delegate's type must be passed to the @UIApplicationDelegateAdaptor modifier inside your App struct.

This means, that if you'd like to provide your own implementation of either UIApplicationDelegate or UIWindowSceneDelegate, it must be done by subclassing Parra's implementations and overriding whichever methods you require. The type of the resulting new app delegate class should then be passed to the @UIApplicationDelegateAdaptor.

class SceneDelegate: ParraSceneDelegate {}

class AppDelegate: ParraAppDelegate<SceneDelegate> {

    override func application(
        _ application: UIApplication,
        didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey : Any]? = nil
    ) -> Bool {
        guard super.application(
            application,
            didFinishLaunchingWithOptions: launchOptions
        ) else {
            return false
        }

        // Custom app launch setup

        return true
    }
}

@main
struct ParraDemoApp: App {
    @UIApplicationDelegateAdaptor(AppDelegate.self) var appDelegate

    var body: some Scene {
        ParraApp(tenantId: <#TENANT_ID#>, applicationId: <#APPLICATION_ID#>, appDelegate: appDelegate) {
            WindowGroup {
                ParraOptionalAuthWindow {
                    ContentView()
                }
            }
        }
    }
}

Working with Windows

The ParraApp makeScene closure creates the scene for the app. For most use-cases, your scene will only have a single WindowGroup that contains your app's content. If you have an iPadOS app that supports multiple windows or window groups in your scene, you can create multiple instances of them within this closure. You'll still need to wrap the content views for each window in your choice of required or optional Parra auth window if you need to access to Parra from within that window.

@main
struct ParraDemoApp: App {
    @UIApplicationDelegateAdaptor(AppDelegate.self) var appDelegate

    var body: some Scene {
        ParraApp(tenantId: <#TENANT_ID#>, applicationId: <#APPLICATION_ID#>, appDelegate: appDelegate) {
            WindowGroup {
                ParraOptionalAuthWindow {
                    ContentView()
                }
            }

            WindowGroup(id: "secondary-window-id") {
                ParraOptionalAuthWindow {
                    ContentView()
                }
            }
        }
    }
}

Supporting Multiple Environments

Different apps have different requirements for what environments are used during their development, testing and release. To facilitate this we expose information that you might need to make conditional as inputs to the top level ParraApp View. This means that you can keep the default debug/release behavior, or implement custom conditions for your configuration. Here's an example of how you might use a custom build condition to use different application IDs, bundle IDs, or modify the logger config.

import Parra
import SwiftUI

@main
struct MyApp: App {
    @UIApplicationDelegateAdaptor(
        ParraAppDelegate<ParraSceneDelegate>.self
    ) var appDelegate

    var body: some Scene {
        #if MY_CUSTOM_STAGING_ENVIRONMENT_COMPILATION_CONDITION
        let applicationId = "<#my-staging-application-id#>"
        let bundleId = "<#my-staging-bundle-id#>"
        let loggerEnvironment: ParraLoggerEnvironment = .production
        #else
        let applicationId = "<#my-default-application-id#>"
        let bundleId = "<#my-default-bundle-id#>"
        let loggerEnvironment: ParraLoggerEnvironment = .automatic
        #endif

        ParraApp(
            tenantId: "<#my-tenant-id#>",
            applicationId: applicationId,
            appDelegate: appDelegate,
            configuration: ParraConfiguration(
                appInfoOptions: ParraAppInfoOptions(bundleId: bundleId),
                loggerOptions: ParraLoggerOptions(environment: loggerEnvironment)
            )
        ) {
            WindowGroup {
                ParraOptionalAuthWindow {
                    ContentView()
                }
            }
        }
    }
}

Was this page helpful?