Embedded Content

Integrate Embedded Content into your iOS app to display Scene content directly within your app’s screens.

For information about Embedded Content, including overview, use cases, and how to create Embedded Content view styles and Scenes, see Embedded Content.

Adding an embedded view

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 scroll view

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())

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.