iOS Embedded Content

Embedded Content is an alternative Scene format that can be displayed on any app or web screen in a view defined by a developer. It can also be presented in Story format. iOS SDK 18.7+

About Embedded Content

Present the content of a SceneA single or multi-screen in-app experience cached on users’ devices and displayed when users meet certain conditions in your app or website, such as viewing a particular screen or when a Custom Event occurs. They can be presented in fullscreen, modal, or embedded format using the default swipe/click mode or as a Story. Scenes can also contain survey questions. on any app screen. There are three primary components:

ComponentDescription
A "view" in your app where the content will displayAn app developer creates an AirshipEmbeddedView that controls the dimensions of the content and its location in your app. They also determine what content can be displayed in the view by setting a value for the view's embeddedId that matches the ID of an Embedded Content view style.
A view style in your project settingsA marketer creates an Embedded Content view style and assigns an ID for reference in the app view's embeddedId.
A Scene using an Embedded Content view styleThis is the source of the content that will be displayed in the view.

Once the Scene is triggered for display and matches the specified audience conditions, its content is available to users when visiting a screen that contains the AirshipEmbeddedView.

The view is populated with the content from all active Scenes with the matching ID, sorted based on priority. When an embedded view loads content, the highest priority Scene that is queued for display will be shown. The same content will be shown across app and web sessions until it is dismissed by the user or is no longer available. The view will then show the next Scene with the highest priority from the display queue.

Airship first prepares the content when the triggering event occurs and then refreshes it upon every app open or web session start. This ensures that users always experience the most up-to-date message. So, after updating active Embedded Content, users will see the latest version the next time they open the app or load the app or web page containing the view.

Embedded Content behavior is the same as In-App AutomationsMessages cached on users’ devices and displayed when users meet certain conditions within your app, such as viewing a particular screen or opening the app a certain number of times. and modal and fullscreen Scenes:

  • The content displays only within the app.
  • When the app is terminated, the content is not automatically dismissed. It continues to display in the next app session.

If you are not already on iOS SDK 18.7+, see the Airship iOS SDK 17.x to 18.0 migration guide.

Adding an AirshipEmbeddedView

The AirshipEmbeddedView is a SwiftUI view that defines a place for an Airship Embedded Content to be displayed. When defining an AirshipEmbeddedView, specify the embeddedId for the content it should display. The value of the embeddedId must be the ID of an Embedded Content view style in your project.

Basic integration
// Show any "home_banner" Embedded Content
AirshipEmbeddedView(embeddedID: "home_banner")

Placeholders

If content is unavailable to display, the default behavior is to show an EmptyView. You can customize this by providing a placeholder.

Basic integration with placeholder
AirshipEmbeddedView(embeddedID: "home_banner") {
    Text("Placeholder!")
}

Placing in a ScrollView

When placed directly in a ScrollView, or a child view within the ScrollView that is allowed to grow unbounded in the scrollable direction, you need to pass the maximum size of the embedded view to make percent-based sizing work correctly. The easiest way is to wrap the ScrollView in a GeometryReader and pass the size info to the embedded view.

GeometryReader example
struct ScrollViewExample: View {
    var body: some View {
        GeometryReader { geometryProxy in
            ScrollView(showsIndicators: false) {

                AirshipEmbeddedView(
                    embeddedID: "home_banner",
                    embeddedSize: AirshipEmbeddedSize(
                        parentBounds: geometryProxy.size
                    )
                )

            }
        }
    }
}

When using a GeometryReader, it takes up as much space as allowed. To avoid this and instead measure the current size of the content, you can use the view extension airshipMeasureView.

airshipMeasureView example
struct ScrollViewExample: View {
    @State var state: CGSize?

    var body: some View {
        ScrollView(showsIndicators: false) {

            AirshipEmbeddedView(
                embeddedID: "home_banner",
                embeddedSize: AirshipEmbeddedSize(
                    maxWidth: state?.width,
                    maxHeight: state?.height
                )
            )

        }
        .airshipMeasureView(self.$state)
    }
}

Styling

You can set a custom style on the embedded view, which allows you to modify how the content is displayed or what pending content is displayed.

In this example, the embedded view has a Dismiss Button above it:

Custom style
public struct CustomEmbeddedViewStyle: AirshipEmbeddedViewStyle {
    @ViewBuilder
    public func makeBody(configuration: AirshipEmbeddedViewStyleConfiguration) -> some View {
        if let view = configuration.views.first {
            VStack {
                Button("Dismiss") {
                    view.dismiss()
                }
                view
            }
        } else {
            configuration.placeHolder
        }
    }
}

Setting the style
AirshipEmbeddedView(embeddedID: "home_banner")
    .setAirshipEmbeddedStyle(CustomEmbeddedViewStyle())

iOS 14 and iOS 15 support

AirshipEmbeddedView takes advantage of custom SwiftUI layouts to properly size itself based on the bounds of the view. The Layout protocol is only available for iOS 16+. AirshipEmbeddedView will still work on older iOS versions, but you must provide the parent’s bounds to enable properly sizing itself:

Setting the max size
AirshipEmbeddedView(
    embeddedID: "home_banner",
    embeddedSize: AirshipEmbeddedSize(
        parentBounds: CGSize(100, 200)
    )
)

Observing available Embedded Content

Embedded Content is not always available, and even after being triggered, it still needs to be prepared before it can be displayed. An AirshipEmbeddedView will automatically update when content is available and transition from the placeholder to the content once content is available. If you need to query the availability of Embedded Content, you can use an AirshipEmbeddedObserver to watch for updates.

An AirshipEmbeddedObserver is an ObservableObject that you can use as a StateObject to automatically refresh the view when new Embedded Content is available. It allows for more dynamic handling of Embedded Content than just content or a placeholder.

Observable example
struct ObservableExample: View {

    @StateObject
    private var embeddedObserver: AirshipEmbeddedObserver = AirshipEmbeddedObserver(embeddedID: "home_banner")

    @State var tabIndex = 0
    var body: some View {
        if (embeddedObserver.embeddedInfos.isEmpty) {
            Text("No banner available")
        } else {
            Text("Banner available")
            AirshipEmbeddedView(embeddedID: "home_banner")
        }
    }
}

The AirshipEmbeddedObserver can be created to watch for one embeddedID, all embedded IDs, or use custom filtering for embedded IDs. The embeddedInfos is the FIFO order of embedded info, including the extras you can set through the Scene composer when creating the content.