# Circle Story (iOS)

### Use CircleStoryView

```swift
import UIKit
import FireworkVideo

class ViewController: UIViewController {
    override func viewDidLoad() {
        super.viewDidLoad()
        self.addVideoFeedView()
    }

    func addVideoFeedView() {
        let channelID = "<Encoded Channel ID>"
        let playlistID = "<Encoded Playlist ID>"
        let source = VideoFeedContentSource.channelPlaylist(
            channelID: channelID,
            playlistID: playlistID
        )
        let circleStoryView = CircleStoryView(source: source)
        circleStoryView.isPictureInPictureEnabled = true

        circleStoryView.translatesAutoresizingMaskIntoConstraints = false
        self.view.addSubview(circleStoryView)

        NSLayoutConstraint.activate([
            circleStoryView.leadingAnchor.constraint(equalTo: self.view.leadingAnchor),
            circleStoryView.trailingAnchor.constraint(equalTo: self.view.trailingAnchor),
            circleStoryView.heightAnchor.constraint(equalToConstant: 240),
            circleStoryView.centerYAnchor.constraint(equalTo: self.view.centerYAnchor)
        ])
    }
}

```

### Use CircleStorySwiftUIView

```swift
import SwiftUI
import FireworkVideo

let channelID = "<Encoded Channel ID>"
let playlistID = "<Encoded Playlist ID>"

struct ContentView: View {
    var body: some View {
        List {
            Spacer()
            CircleStorySwiftUIView(
                source: .channelPlaylist(channelID: channelID, playlistID: playlistID),
                isPictureInPictureEnabled: true,
                onCircleStoryLoaded: {
                    debugPrint("Circle story loaded successfully.")
                },
                onCircleStoryFailedToLoad: { error in
                    debugPrint("Circle story did fail loading.")
                }
            ).frame(height: 240)
            Button("Refresh") {
                videoFeedContainer.handler?.refresh()
            }
            Spacer()
        }
    }
}

```

### **Content Source**

Please refer to [Video Feed Content Source (iOS)](/firework-for-developers/ios-sdk/integration-guide-for-ios-sdk/video-feed-content-source-ios.md).

### **Viewport-based autoplay support**

By default, autoplay is enabled, and the video begins playing as soon as the component is instantiated. However, when the component is embedded within a `ScrollView`, `TableView`, or `CollectionView`, a more seamless user experience is achieved by initiating autoplay only when the component enters the viewport, and pausing playback when it leaves. This behavior can be implemented setting `useSafeAreaViewport` as `true`. We recommend leveraging `CircleStoryView` and `CircleStorySwiftUIView` for this purpose.

#### Code snippets for CircleStoryView

```swift
import UIKit
import FireworkVideo

class ViewController: UIViewController {
    override func viewDidLoad() {
        super.viewDidLoad()
        self.addVideoFeedView()
    }

    func addVideoFeedView() {
        let channelID = "<Encoded Channel ID>"
        let playlistID = "<Encoded Playlist ID>"
        let videoFeedView = CircleStoryView(source: .channelPlaylist(channelID: channelID, playlistID: playlistID))
        videoFeedView.viewConfiguration = getCircleStoryContentConfiguration()

        videoFeedView.translatesAutoresizingMaskIntoConstraints = false
        self.view.addSubview(videoFeedView)

        NSLayoutConstraint.activate([
            videoFeedView.leadingAnchor.constraint(equalTo: self.view.leadingAnchor),
            videoFeedView.trailingAnchor.constraint(equalTo: self.view.trailingAnchor),
            videoFeedView.heightAnchor.constraint(equalToConstant: 240),
            videoFeedView.centerYAnchor.constraint(equalTo: self.view.centerYAnchor)
        ])
    }

    func getCircleStoryContentConfiguration() -> CircleStoryContentConfiguration {
        var viewConfiguration = CircleStoryContentConfiguration()
        // Enable viewport-based autoplay
        viewConfiguration.useSafeAreaInset = true
        viewConfiguration.itemView.autoplay.isEnabled = true
        viewConfiguration.playerView.playbackButton.isHidden = false
        return viewConfiguration
    }
}
```

#### Code snippets for CircleStorySwiftUIView

```swift
import SwiftUI
import FireworkVideo

let channelID = "<Encoded Channel ID>"
let playlistID = "<Encoded Playlist ID>"

struct ContentView: View {
    let videoFeedContainer = FWSVideoFeedSwiftUIContainer()
    var body: some View {
        List {
            Spacer()
            FWSVideoFeedSwiftUIView(
                source: .channelPlaylist(channelID: channelID, playlistID: playlistID),
                viewConfiguration: getCircleStoryContentConfiguration(),
                isPictureInPictureEnabled: true,
                onVideoFeedLoaded: {
                    debugPrint("Video feed loaded successfully.")
                },
                onVideoFeedFailedToLoad: { error in
                    debugPrint("Video feed did fail loading.")
                }
            ).frame(height: 240)
            Button("Refresh") {
                videoFeedContainer.handler?.refresh()
            }
            Spacer()
        }
    }

    func getCircleStoryContentConfiguration() -> CircleStoryContentConfiguration {
        var viewConfiguration = CircleStoryContentConfiguration()
        // Enable viewport-based autoplay
        viewConfiguration.useSafeAreaInset = true
        viewConfiguration.itemView.autoplay.isEnabled = true
        viewConfiguration.playerView.playbackButton.isHidden = false
        return viewConfiguration
    }
}
```

#### Customize viewport

The default viewport is defined as the screen bounds minus the safe area insets—such as the status bar, top navigation bar, bottom tab bar, and bottom home indicator.

```swift
/// Default Viewport = Screen - Top Safe Area - Bottom Safe Area
/// 
/// Screen (Full Device Screen)
/// ┌─────────────────────────┐ ← Screen Top
/// │   Status Bar            │ ← Safe Area (excluded)
/// ├─────────────────────────┤
/// │   Navigation Bar        │ ← Safe Area (excluded, if present)
/// ├─────────────────────────┤
/// │                         │
/// │                         │
/// │   Default Viewport      │ ← Visible content area
/// │   (Visible Content)     │    (Screen - Safe Area)
/// │                         │
/// │                         │
/// ├─────────────────────────┤
/// │   Tab Bar               │ ← Safe Area (excluded, if present)
/// ├─────────────────────────┤
/// │   Home Indicator        │ ← Safe Area (excluded)
/// └─────────────────────────┘ ← Screen Bottom
```

However, you can further refine this viewport by specifying `safeAreaEdges` and `additionalViewportExcludedInset`. Please refer to the following code snippets for more details.

```swift
viewConfiguration.useSafeAreaViewport = true
/// Viewport Configuration:
/// 
/// With the configuration below, the viewport is calculated as:
/// Viewport = Screen - Top Safe Area - 50pt Bottom Padding
/// 
/// Visual Layout:
/// ┌─────────────────────────┐
/// │ Status Bar              │ \
/// ├─────────────────────────┤  > Excluded (safeAreaEdges = .top)
/// │ Nav Bar (if present)    │ /
/// ╞═════════════════════════╡ ← Viewport Top
/// │                         │
/// │   Viewport Area         │ ← Content visible here
/// │                         │
/// ╞═════════════════════════╡ ← Viewport Bottom
/// │ 50pt bottom padding     │ ← Excluded (additionalViewportExcludedInset.bottom)
/// └─────────────────────────┘
/// 
viewConfig.safeAreaEdges = .top
viewConfig.additionalViewportExcludedInset = UIEdgeInsets(
    top: 0,
    left: 0,
    bottom: 50,
    right: 0
)
```


---

# Agent Instructions: Querying This Documentation

If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter:

```
GET https://docs.firework.com/firework-for-developers/ios-sdk/integration-guide-for-ios-sdk/circle-story-ios.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
