# 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](https://www.airship.com/docs/guides/features/messaging/scenes/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**

```swift
// Show any "home_banner" Embedded Content
AirshipEmbeddedView(embeddedID: "home_banner")
```


<!-- TODO: Add image: embedded-view-basic.png - Screenshot showing an embedded banner Scene displayed in an app screen -->

## 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**

```swift
AirshipEmbeddedView(embeddedID: "home_banner") {
    Text("Placeholder!")
}
```


<!-- TODO: Add image: embedded-view-placeholder.png - Screenshot showing placeholder text displayed when no embedded content is available -->

## 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**

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

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

            }
        }
    }
}
```


<!-- TODO: Add image: embedded-view-scrollview.png - Screenshot showing an embedded view placed within a ScrollView, demonstrating proper sizing -->

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**

```swift
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**

```swift
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**

```swift
AirshipEmbeddedView(embeddedID: "home_banner")
    .setAirshipEmbeddedStyle(CustomEmbeddedViewStyle())
```


<!-- TODO: Add image: embedded-view-custom-style.png - Screenshot showing a custom styled embedded view with a dismiss button above the content -->


## 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**

```swift
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.

<!-- TODO: Add image: embedded-observer-example.png - Screenshot showing the app dynamically updating when embedded content becomes available -->

