> For the complete documentation index, see [llms.txt](https://docs.firework.com/firework-for-developers/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://docs.firework.com/firework-for-developers/ios-sdk/integration-guide-for-ios-sdk/customization-ios/customize-click-behaviors-ios.md).

# Customize click behaviors (iOS)

The SDK provides behavior delegate methods to customize click behaviors, including those for the video CTA button, product cards, and shopping CTA button.

### Customize video overlay CTA button click behavior

1. Set `FireworkVideoCTADelegate` delegate:

```swift
FireworkVideoSDK.ctaDelegate = <FireworkVideoCTADelegate delegate>
```

2\. Implement `handleCustomCTAClick(_:url:for:)`:

```swift
func handleCustomCTAClick(
    _ viewController: PlayerViewController,
    url: URL,
    for video: VideoDetails
) -> Bool {
    // Here, you could write codes to navigate to the host app page.
    // For best practices on navigating to the host page,
    // please consult "Navigate to the host app page" below.

    // Return true to customize video overlay CTA button click behavior
    return true
}
```

{% hint style="info" %}
You could write codes to navigate to the host app page within the delegate method. For best practices on navigating to the host page, please consult [Navigate to the host app](#navigate-to-the-host-app-page) page below.
{% endhint %}

### Customize click behaviors for shopping

To customize click behaviors for shopping, you need to set `FireworkVideoShoppingDelegate` delegate first:

```swift
FireworkVideoSDK.shopping.delegate = <FireworkVideoShoppingDelegate delegate>
```

#### Customize product card click behavior

Implement `fireworkShopping(_:didTapProductVariant:forVideo:)`:

```swift
func fireworkShopping(
    _ fireworkShopping: FireworkVideoShopping,
    didTapProductVariant item: SelectedProductVariant,
    forVideo video: VideoDetails
) -> Bool {
    // Here, you could write codes to navigate to the host app page.
    // For best practices on navigating to the host page,
    // please consult "Navigate to the host app page" below.

    // Return true to customize product card click behavior
    return true
}
```

{% hint style="info" %}
You could write codes to navigate to the host app page within the delegate method. For best practices on navigating to the host page, please consult [Navigate to the host app](#navigate-to-the-host-app-page) page below.
{% endhint %}

#### Customize shopping CTA click behavior

Implement `fireworkShopping(_:productVariantCTASelected:fromVideo:ctaCompletionHandler:)`:

```swift
func fireworkShopping(
    _ fireworkShopping: FireworkVideoShopping,
    productVariantCTASelected item: SelectedProductVariant,
    fromVideo video: VideoDetails,
    ctaCompletionHandler: @escaping ShoppingCTAHandler
) {
    // Here, you could write codes to navigate to the host app page.
    // For best practices on navigating to the host page,
    // please consult "Navigate to the host app page" below.

    // Calling completion closure with none refer to
    // host app will customize the click behavior
    ctaCompletionHandler(.none)
}
```

{% hint style="info" %}
You could write codes to navigate to the host app page within the delegate method. For best practices on navigating to the host page, please consult [Navigate to the host app](#navigate-to-the-host-app-page) page below.
{% endhint %}

#### Customize product link button click behavior

Implement `fireworkShopping(_:didTapLinkButtonAt:fromVideo:withURL:)`:

```swift
func fireworkShopping(
        _ fireworkShopping: FireworkVideoShopping,
        didTapLinkButtonAt item: SelectedProductVariant,
        fromVideo video: VideoDetails,
        withURL itemURL: String
    ) -> Bool {
        // Here, you could write codes to navigate to the host app page.
        // For best practices on navigating to the host page,
        // please consult "Navigate to the host app page" below.

        // Return true to customize product link button click behavior
        return true
}
```

{% hint style="info" %}
You could write codes to navigate to the host app page within the delegate method. For best practices on navigating to the host page, please consult [Navigate to the host app](#navigate-to-the-host-app-page) page below.
{% endhint %}

#### Customize cart icon click behavior

{% hint style="warning" %}
The SDK does not provide a cart page of its own. The page the user sees after tapping the cart icon is always supplied by the host app, through one of the two options below. The cart icon is also hidden by default — to show it, see [Shopping configurations (iOS)](/firework-for-developers/ios-sdk/integration-guide-for-ios-sdk/customization-ios/shopping-configurations-ios.md).
{% endhint %}

`FireworkVideoSDK.shopping.cartAction` determines what happens when the user taps the cart icon. There are two options.

**Option 1: Show the host app cart view inside the SDK player (`.showEmbeddedCart`)**

This is the default value of `cartAction`. The SDK requests a cart view controller from the host app and shows it inside the player, so the host app must supply one.

1. Implement `CartViewControllerProviding` to return your own cart view controller. `CartViewController` is a typealias for `UIViewController & CartViewRepresentable`:

```swift
class HostAppCartViewProvider: CartViewControllerProviding {
    func cartViewController(for video: VideoDetails) -> CartViewController {
        // Return the host app cart view controller.
        return HostAppCartViewController()
    }
}
```

2. Set `cartAction` to `showEmbeddedCart` and assign the provider:

```swift
// `.showEmbeddedCart` is the default value of `cartAction`
FireworkVideoSDK.shopping.cartAction = .showEmbeddedCart
FireworkVideoSDK.shopping.cartViewControllerProvider = HostAppCartViewProvider()
```

3. The cart view controller can close the drawer that contains it through `drawerController`:

```swift
class HostAppCartViewController: UIViewController, CartViewRepresentable {
    private func handleContinueShopping() {
        drawerController?.closeDrawer(animated: true)
    }
}
```

{% hint style="warning" %}
`cartViewControllerProvider` must be set for this option. If it is `nil`, tapping the cart icon does nothing at all.
{% endhint %}

{% hint style="info" %}
The same provider is used when the host app calls `ctaCompletionHandler(.showEmbeddedCart)` from `fireworkShopping(_:productVariantCTASelected:fromVideo:ctaCompletionHandler:)`, which shows the cart after the user taps the shopping CTA button.
{% endhint %}

**Option 2: Handle the cart icon click in the host app (`.custom`)**

With this option the SDK presents nothing and only notifies the host app, so the host app is free to navigate to its own page.

1. Set `cartAction` to `custom`:

```swift
FireworkVideoSDK.shopping.cartAction = .custom
```

2. Implement `fireworkShopping(_:didTapCartIconForVideo:)`:

```swift

func fireworkShopping(
    _ fireworkShopping: FireworkVideoShopping,
    didTapCartIconForVideo videoDetails: VideoDetails
) {
    // Here, you could write codes to navigate to the host app page.
    // For best practices on navigating to the host page,
    // please consult "Navigate to the host app page" below.
}
```

{% hint style="info" %}
You could write codes to navigate to the host app page within the delegate method. For best practices on navigating to the host page, please consult [Navigate to the host app](#navigate-to-the-host-app-page) page below.
{% endhint %}

{% hint style="warning" %}
If this delegate method is not implemented, tapping the cart icon does nothing at all.
{% endhint %}

### Custom livestream link interaction click behavior

Use `FireworkLiveStreamBehaviorDelegate` to handle livestream link interactions, including links to pages in your app.

**Default behavior**

If `liveStreamBehaviorDelegate` is not set, the delegate method is not implemented, or the method returns `false`, the SDK uses the following default behavior:

| Link                                             | Default behavior                                     |
| ------------------------------------------------ | ---------------------------------------------------- |
| `http://` or `https://`                          | Opens in an in-app `SFSafariViewController`.         |
| Custom URL schemes, such as `myapp://`           | Does not open the link. The host app must handle it. |
| An invalid URL that cannot be converted to `URL` | Does not open the link.                              |

The SDK does not call `UIApplication.shared.open` as a fallback for custom URL schemes. An HTTPS URL uses the in-app browser path; changing a custom scheme to HTTPS alone does not guarantee navigation to a native app page.

**Set up the delegate**

1. Set `FireworkVideoSDK.liveStreamBehaviorDelegate` to an object that conforms to `FireworkLiveStreamBehaviorDelegate`. For example, use `self` if your app delegate conforms to this protocol:

```swift
FireworkVideoSDK.liveStreamBehaviorDelegate = self
```

{% hint style="info" %}
The SDK holds a weak reference to this delegate. Keep a strong reference to the delegate object for as long as it should receive callbacks. Do not assign a temporary delegate instance without retaining it elsewhere.
{% endhint %}

2. Implement `fireworkLiveStream(_:didTapLinkInteraction:)`. The `interaction` parameter exposes:

| Property            | Type     | Description                                                      |
| ------------------- | -------- | ---------------------------------------------------------------- |
| `interaction.title` | `String` | The title of the link interaction.                               |
| `interaction.url`   | `String` | The link URL. Convert it using `URL(string:)` before routing it. |

For links to your own app, route the URL through your app's existing navigation or deep link handler. Return `true` when your app takes responsibility for handling the link, and `false` to use the SDK's default behavior.

Alternatively, the following example asks iOS to open a custom URL scheme. Replace `myapp` with the scheme your app supports, and ensure the destination app is registered to handle it:

```swift
func fireworkLiveStream(
    _ liveStream: LiveStreamEventDetails,
    didTapLinkInteraction interaction: LiveStreamLinkInteraction
) -> Bool {
    guard let url = URL(string: interaction.url),
          url.scheme?.lowercased() == "myapp" else {
        // The SDK opens HTTP/HTTPS links in its in-app browser.
        // Other schemes remain unhandled unless your app handles them.
        return false
    }

    UIApplication.shared.open(url, options: [:]) { opened in
        guard !opened else { return }
        // Replace this diagnostic with your app's error handling if needed.
        debugPrint("The app could not open the livestream link.")
    }

    // The host app has taken responsibility for this open request.
    return true
}
```

{% hint style="warning" %}
Returning `true` stops the SDK's default handling; it does not open the URL by itself. Returning `true` without navigating or initiating an open request results in no navigation. In the asynchronous example above, `true` indicates that the host app has taken responsibility for the request, not that iOS has successfully opened the URL. If opening fails, the host app must handle the failure; the SDK does not retry automatically.
{% endhint %}

{% hint style="info" %}
For guidance on presenting a host app page or managing the fullscreen player before navigation, see [Navigate to the host app page](#navigate-to-the-host-app-page). A page pushed onto the host app's navigation controller may otherwise remain beneath the fullscreen player.
{% endhint %}

#### Customize social attribution click behavior

1. Set `FireworkVideoSocialDelegate` delegate:

```swift
FireworkVideoSDK.socialDelegate = <FireworkVideoSocialDelegate delegate>
```

2. Implement `handleSocialAttributionClick(_:fromVideo:):`

```swift
func handleSocialAttributionClick(
    _ source: VideoSocialAttributionSource,
    fromVideo video: VideoDetails,
) -> Bool {
    // This delegate method allows the host app to customize the behavior when users tap on
    // social media attribution displayed in videos. If the host app returns `false` or if
    // this delegate is not set, the SDK will open the URL using `UIApplication.shared.open`.
    return false
}
```

#### Customize video feed item click behavior

1. Set VideoFeedViewControllerDelegate delegate:

```swift
videoFeedVC.delegate = <VideoFeedViewControllerDelegate delegate>
```

2. Implement videoFeed`(_:didTapFeedItem:):`

<pre class="language-swift"><code class="lang-swift"><strong>func videoFeed(
</strong>    _ viewController: VideoFeedViewController,
    didTapFeedItem feedItemDetails: FeedEventDetails
) -> Bool {
    /// Called when the user taps a feed item.
    ///
    /// Return `true` to indicate that the tap has been handled externally,
    /// preventing the SDK from opening the full-screen player.
    /// Return `false` to let the SDK proceed with the default behavior.
    return false
}
</code></pre>

### Navigate to the Host App Page

We have three ways to navigate to the host app page when customizing click behaviors.

1. Present the host app page over the SDK player.
2. Push the host app page inside the SDK player.
3. Use the host app navigation controller to push the host app page.

#### Present the Host App Page Over the SDK Player

Use `present` to display the host app PDP page over the SDK player:

```swift
func fireworkShopping(
    _ fireworkShopping: FireworkVideoShopping,
    didTapProductVariant item: SelectedProductVariant,
    forVideo video: VideoDetails
) -> Bool {
    // The following PDP view controller is a sample.
    // Please create your own PDP view controller.
    let pdpVC = UIViewController()
    // As we may have other flows in PDP(such as the checkout page),
    // we could embed the PDP controller with navigation controller.
    let nav = UINavigationController(rootViewController: pdpVC)
    nav.modalPresentationStyle = .fullScreen
    UIViewController.topPresentedVC()?.present(nav, animated: true)
    
    // Return true to customize product card click behavior
    return true
}
```

#### Push the Host App Page Inside the SDK Player

`UIViewController.topNavigationController()` resolves to different navigation controllers depending on the current context:

* **Inside the fullscreen player**: returns the player's own navigation controller, so the page is pushed within the player context.
* **Inside a story block**: falls back to the host app's navigation controller.

Because the fullscreen player hides the navigation bar, the pushed view controller must explicitly show and configure it in `viewWillAppear`. Using `viewWillAppear` (rather than `viewDidLoad`) is important — when the user navigates back, UIKit calls `viewWillAppear` on the player, allowing it to hide the bar again and restore its original state.

```swift
func fireworkShopping(
    _ fireworkShopping: FireworkVideoShopping,
    didTapProductVariant item: SelectedProductVariant,
    forVideo video: VideoDetails
) -> Bool {
    // `topNav` resolves to the fullscreen player's navigation controller when inside
    // the fullscreen player, or to the host app's navigation controller inside a story block.
    if let topNav = UIViewController.topNavigationController() {
        let vc = HostAppProductDetailsController()
        vc.hidesBottomBarWhenPushed = true
        topNav.pushViewController(vc, animated: true)
    }
    
    // Return true to customize product card click behavior
    return true
}

class HostAppProductDetailsController: UIViewController {
    override func viewDidLoad() {
        super.viewDidLoad()
        view.backgroundColor = .red
    }

    override func viewWillAppear(_ animated: Bool) {
        super.viewWillAppear(animated)
        // Show and configure the navigation bar here to match your design.
        // Since the navigation bar is shared with the fullscreen player, configuring it in
        // `viewWillAppear` ensures the player's original bar state (hidden, style, etc.)
        // is restored when navigating back.
        navigationController?.setNavigationBarHidden(false, animated: true)
        configNavigationBar()
    }

    private func configNavigationBar() {
        self.title = "PDP"
        guard let navBar = navigationController?.navigationBar else { return }
        let bgColor = UIColor.white
        if #available(iOS 15, *) {
            let navigationBarAppearance = UINavigationBarAppearance()
            navigationBarAppearance.backgroundColor = bgColor

            navBar.scrollEdgeAppearance = navigationBarAppearance
            navBar.standardAppearance = navigationBarAppearance
        } else {
            navBar.barTintColor = bgColor
        }
    }
}
```

#### Use the Host App Navigation Controller to Push the Host App Page

When the fullscreen player is visible, the host app's navigation controller sits beneath it, so any page pushed directly would be obscured by the player.

`FireworkVideoSDK.startPiPOrDismissFullScreenPlayer` handles this by transitioning the player to a floating window (if PiP is enabled) or dismissing it (if PiP is disabled), ensuring the new page is fully visible. To always dismiss the player regardless of PiP, use `FireworkVideoSDK.dismissFullScreenPlayer` instead.

```swift
func fireworkShopping(
    _ fireworkShopping: FireworkVideoShopping,
    didTapProductVariant item: SelectedProductVariant,
    forVideo video: VideoDetails
) -> Bool {
    /*
     When a Firework full-screen player is present, startPiPOrDismissFullScreenPlayer will either
     transition it to a floating player (if PiP is enabled), or dismiss it (if PiP is disabled).
     If you wish to consistently dismiss the full-screen player, regardless of whether PiP is
     enabled or not, you can use the dismissFullScreenPlayer API.
     */
    FireworkVideoSDK.startPiPOrDismissFullScreenPlayer { _ in
        // Use host app navigation controller to push the host app page.
        // But you can also use your own method to push the host app page.
        if let nav = FireworkVideoSDK.getHostAppNavigationController() {
            // The following PDP view controller is a sample.
            // Please create your own PDP view controller.
            let pdpVC = UIViewController()
            nav.pushViewController(pdpVC, animated: true)
        }
    }
    return true
}
```

{% hint style="info" %}
The above code snippets are based on `fireworkShopping(_:didTapProductVariant:forVideo:)`. But they are also applicable to other behavior delegate methods.
{% endhint %}
