Customization

One of Parra's primary goals is for our UI elements to blend seamlessly into your app. To this end, we provide a wide variety of ways that you can customize our SDK to suite your needs.

Themes

Themes allow you to define a set of styles that we will use everywhere that we render views in your app. Themes allow you to configure color palettes for light and dark mode, typography, corner radius and element padding. You can tune this to your liking, or omit it to fall back on Parra's default theme, which matches common iOS design language.

ParraTheme(
    lightPalette: .defaultLight,
    darkPalette: .defaultDark,
    typography: ParraTypography(
        textStyles: [
            .body: .init(font: .systemFont(ofSize: 12), color: .black),
            .largeTitle: .init(font: .boldSystemFont(ofSize: 24), color: .black)
        ]
    ),
    cornerRadius: ParraCornerRadiusConfig(
        xs: RectangleCornerRadii(allCorners: 2),
        sm: RectangleCornerRadii(allCorners: 4),
        md: RectangleCornerRadii(allCorners: 6),
        lg: RectangleCornerRadii(allCorners: 8),
        xl: RectangleCornerRadii(allCorners: 12),
        xxl: RectangleCornerRadii(allCorners: 16),
        xxxl: RectangleCornerRadii(allCorners: 24),
        full: RectangleCornerRadii(allCorners: 9_999)
    ),
    padding: ParraPaddingConfig(
        xs: EdgeInsets(2),
        sm: EdgeInsets(4),
        md: EdgeInsets(6),
        lg: EdgeInsets(8),
        xl: EdgeInsets(12),
        xxl: EdgeInsets(16),
        xxxl: EdgeInsets(24)
    )
)

Once you've constructed your ParraTheme object, simply pass it to the theme parameter of your ParraConfiguration instance that is supplied to the ParraApp.

Global Component Attributes

Taking it a step further than themes, the ParraConfiguration object accepts a globalComponentAttributes parameter which can be used to override components more globally. What this means, is that you can provide an instance of a subclass of ParraGlobalComponentAttributes which overrides any of its methods that you'd like for controlling view attributes. Everywhere that we render a View, we'll use your implementation if you provide one, falling back on the default. This method provides you with flexibility to override whichever methods you need and conditionally call into the super implementation for cases where you don't wish to provide customization. For example:

class MyAppComponentAttributes: ParraGlobalComponentAttributes {
    override func textAttributes(
        for textStyle: Font.TextStyle?,
        localAttributes: ParraAttributes.Text? = nil,
        theme: ParraTheme
    ) -> ParraAttributes.Text {
        switch textStyle {
        case .largeTitle:
            return ParraAttributes.Text(
                font: .boldSystemFont(ofSize: 25)
            )
        case .caption:
            return ParraAttributes.Text(
                font: .boldSystemFont(ofSize: 9)
            )
        default:
            return super.textAttributes(
                for: textStyle,
                localAttributes: localAttributes,
                theme: theme
            )
        }
    }
}

Parra View Configs

Each of Parra's widgets (feedback form, login, changelog, etc) provide an optional config object that can be passed along when instantiating or presenting them. On top of the component level configuration that they adapt to via changes to theme and global component attributes, you can provide this additional level of configuration that is specific to the screen. The fields available for config vary by widget, but as an example here this is how you present a roadmap sheet with a custom configuration.

struct RoadmapButton: View {
    @State private var isRoadmapPresented = false

    var body: some View {
        Button("Show roadmap") {
            isRoadmapPresented = true
        }
        .presentParraRoadmap(
            isPresented: $isRoadmapPresented,
            config: ParraRoadmapWidgetConfig(
                addRequestSuccessToastTitle: "Request added!",
                addRequestSuccessToastSubtitle: "We'll get your request added to our backlog ASAP."
            )
        )
    }
}

Launch Screen

The launch screen is what the system shows between the user launching your app from the Home Screen, and the app's launch completing. It is often necessary to come up with a way to extend the time this screen is shown so that you can load state from disk or perform network operations. Parra takes care of this for you. Simply provide information about your launch screen's configuration and we'll create a new view, matching the system launch screen that is shown during this loading period, before transitioning to your app.

There are three possible ways to configure your launch screen. Each option should create a ParraLaunchScreen.Options object and pass it to the launchScreenOptions parameter of your ParraConfiguration object. If you don't provide any configuration, we'll attempt to automatically determine your launch screen configuration by looking at Info.plist keys.

  1. Using a Storyboard. When choosing this method, make sure you select the same storyboard you define here as your launch screen in Xcode's project settings.
    ParraLaunchScreen.Options(
        type: .storyboard(
            ParraStoryboardLaunchScreen.Config(
                name: "LaunchScreen",
                bundle: .main,
                viewControllerIdentifier: "LaunchScreenID"
            )
        ),
        fadeDuration: 0.5
    )
    
  2. Using UILaunchScreen Info.plist keys. The modern way to create a launch screen is to define it by filling out keys in your Info.plist. Apple's documentation cover all the possible configuration of these keys.
    ParraLaunchScreen.Options(
        type: ParraLaunchScreenType.default(
            ParraDefaultLaunchScreen.Config(
                colorName: "MyPrimaryColor",
                imageName: "MyLaunchScreenBackground",
                imageRespectsSafeAreaInsets: false,
                navigationBar: ParraDefaultLaunchScreen.Config.BarConfig(
                    replacementImage: "MyNavigationBarImage"
                ),
                tabBar: nil,
                toolBar: nil,
                bundle: .main
            )
        ),
        fadeDuration: 0.5
    )
    
  3. A custom SwiftUI View. If you choose this method, note that any launch screen you configure in Xcode will appear before the view defined here is loaded.
    ParraLaunchScreen.Options(
        type: .custom(
            VStack {
                Text("App Name")
            }
        ),
        fadeDuration: 0.5
    )
    

Was this page helpful?