# Video Feed (iOS)

### **Display Video Feed**

#### Use FWSVideoFeedView

The `FWSVideoFeedView` provides a `UIView` wrapper for the `VideoFeedViewController`. You can customize the `FWSVideoFeedView` just like the `VideoFeedViewController`.

**Integration**

1. Import `FireworkVideo`.
2. Instantiate `FWSVideoFeedView` and embed it.

The following are the sample codes:

```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 = FWSVideoFeedView(source: .channelPlaylist(channelID: channelID, playlistID: playlistID))
        videoFeedView.viewConfiguration = getVideoFeedContentConfiguration()

        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 getVideoFeedContentConfiguration() -> VideoFeedContentConfiguration {
        var viewConfiguration = VideoFeedContentConfiguration()
        viewConfiguration.itemView.autoplay.isEnabled = true
        viewConfiguration.playerView.playbackButton.isHidden = false
        return viewConfiguration
    }
}
```

#### Use FWSVideoFeedSwiftUIView(SwiftUI)

The `FWSVideoFeedSwiftUIView` provides a SwiftUI View wrapper for the `VideoFeedViewController`. You can customize the `FWSVideoFeedSwiftUIView` just like the `VideoFeedViewController`.

**Integration**

1. Import `FireworkVideo`.
2. Instantiate `FWSVideoFeedSwiftUIView` and embed it.

The following are the sample codes:

```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: getVideoFeedContentConfiguration(),
                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 getVideoFeedContentConfiguration() -> VideoFeedContentConfiguration {
        var viewConfiguration = VideoFeedContentConfiguration()
        viewConfiguration.itemView.autoplay.isEnabled = true
        viewConfiguration.playerView.playbackButton.isHidden = false
        return viewConfiguration
    }
}
```

### **Content Source**

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

### **Custom Call-To-Action Button Handling**

Custom Call-To-Action button handling is done via the `FireworkVideoCTADelegate` protocol. This provides control over what occurs when a call-to-action button is tapped.

1. Set the delegate:

```swift
FireworkVideoSDK.ctaDelegate = self
```

2\. Conform to protocol:

```swift
func handleCustomCTAClick(_ viewController: PlayerViewController, url: URL, for video: VideoDetails) -> Bool {
    // Your custom action code here...
    return true
}
```

### Force Refresh

A `VideoFeedViewController` can be forced to refreshed by calling the `refresh()` method on the instance that should be refreshed. This functionality is useful if your feed is embedded along with other components that are also updated and you support features like pull to refresh.

### Receive video feed events

1. Set the delegate

```swift
feedVC.delegate = self
```

2. Conform to `VideoFeedViewControllerDelegate` protocol

```swift
func videoFeedDidLoadFeed(
    _ viewController: VideoFeedViewController
) {
    debugPrint("Video feed loaded successfully.")
}

func videoFeed(
    _ viewController: VideoFeedViewController,
    didFailToLoadFeed error: VideoFeedError
) {
    debugPrint("Video feed did fail loading.")
    if case .contentSourceError(let feedContentSourceError) = error,
       case .emptyFeed = feedContentSourceError {
        // This is a specific error.
        // SDK will trigger this error when the feed is empty.
        // For example, host app can hide video feed for this error.
    } else {
        // Other error
    }
}
```

### **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 `FWSVideoFeedView` and `FWSVideoFeedSwiftUIView` for this purpose.

#### Code snippets for FWSVideoFeedView

```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 = FWSVideoFeedView(source: .channelPlaylist(channelID: channelID, playlistID: playlistID))
        videoFeedView.viewConfiguration = getVideoFeedContentConfiguration()

        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 getVideoFeedContentConfiguration() -> VideoFeedContentConfiguration {
        var viewConfiguration = VideoFeedContentConfiguration()
        // Enable viewport-based autoplay
        viewConfiguration.useSafeAreaInset = true
        viewConfiguration.itemView.autoplay.isEnabled = true
        viewConfiguration.playerView.playbackButton.isHidden = false
        return viewConfiguration
    }
}
```

#### Code snippets for FWSVideoFeedSWiftUIView

```swift
func getVideoFeedContentConfiguration() -> VideoFeedContentConfiguration {
    var viewConfiguration = VideoFeedContentConfiguration()
    // Enable viewport-based autoplay
    viewConfiguration.useSafeAreaViewport = 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
)
```

### Video feed configurations

Please refer to [Video feed configurations (iOS)](https://docs.firework.com/firework-for-developers/ios-sdk/integration-guide-for-ios-sdk/customization-ios/video-feed-configurations-ios).

### Player configurations

Please refer to [Player configurations (iOS)](https://docs.firework.com/firework-for-developers/ios-sdk/integration-guide-for-ios-sdk/customization-ios/player-configurations-ios).
