# tvOS modal rich media

Starting from Pushwoosh SDK version 6.11.0, you have the ability to send **Modal Rich Media** to tvOS devices (Apple TV).

Modal Rich Media for tvOS provides an interactive HTML-based content display optimized for Apple TV's remote control navigation. The rich media can be customized with different positions, animations, and focus navigation support.

<video src="/tv_os_rich_media.webm" title="tvOS Rich Media example" autoplay loop muted playsinline />

<Aside type="caution">
Standard push notifications are not supported on tvOS. Apple TV does not display traditional push notification banners or alerts. Only Rich Media content delivered through silent push notifications can be displayed using the PushwooshTVOS module.
</Aside>

> For more information on Rich Media pages, please refer to [our guide](/product/content/rich-media).

## Installation

The PushwooshTVOS module is optional and can be integrated into your tvOS project using Swift Package Manager or CocoaPods.

### Swift Package Manager

Add the Pushwoosh package to your tvOS project:

1. In Xcode, select **File** → **Add Package Dependencies...**
2. Enter the repository URL: `https://github.com/Pushwoosh/Pushwoosh-XCFramework`
3. Select the latest version
4. Add the following frameworks to your tvOS target:
   * **PushwooshFramework**
   * **PushwooshCore**
   * **PushwooshBridge**
   * **PushwooshLiveActivities**
   * **PushwooshTVOS**

### CocoaPods

Add to your Podfile:

```ruby
target 'YourTvOSApp' do
  platform :tvos, '13.0'

  pod 'PushwooshXCFramework'
  pod 'PushwooshXCFramework/PushwooshTVOS'
end
```

Then run:

```bash
pod install
```

## Basic integration

### 1. Configure Pushwoosh in your AppDelegate

Import the required modules and configure Pushwoosh with your application code:

```swift
import UIKit
import PushwooshTVOS
import PushwooshFramework

@main
class AppDelegate: UIResponder, UIApplicationDelegate {

    func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {

        // Configure Pushwoosh with your app code
        Pushwoosh.TVoS.setAppCode("XXXXX-XXXXX")

        // Register for push notifications
        Pushwoosh.TVoS.registerForTvPushNotifications()

        return true
    }
}
```

### 2. Handle device token registration

Implement the device token handler to register the device with Pushwoosh:

```swift
func application(_ application: UIApplication, didRegisterForRemoteNotificationsWithDeviceToken deviceToken: Data) {
    Pushwoosh.TVoS.registerForTvPushNotifications(withToken: deviceToken) { error in
        if let error = error {
            print("Failed to register: \(error)")
        } else {
            print("Successfully registered for push notifications")
        }
    }
}

func application(_ application: UIApplication, didFailToRegisterForRemoteNotificationsWithError error: Error) {
    Pushwoosh.TVoS.handleTvPushRegistrationFailure(error)
}
```

### 3. Handle incoming push notifications

Process incoming push notifications with rich media content:

```swift
func application(_ application: UIApplication,
                 didReceiveRemoteNotification userInfo: [AnyHashable: Any],
                 fetchCompletionHandler completionHandler: @escaping (UIBackgroundFetchResult) -> Void) {

    // Handle push notification and display rich media if present
    if Pushwoosh.TVoS.handleTVOSPush(userInfo: userInfo) {
        completionHandler(.newData)
    } else {
        completionHandler(.noData)
    }
}
```

<Aside type="note">
tvOS only supports silent push notifications with `content-available: 1` flag. When sending push notifications to Apple TV devices through Pushwoosh, make sure to:
- Enable "content-available" flag in your push notification payload
- Include Rich Media content in the notification
</Aside>

## Rich Media configuration

### Positioning

Modal Rich Media for tvOS can be positioned in five locations on the screen:

```swift
// Center position (default)
Pushwoosh.TVoS.configureRichMediaWith(position: .center, presentAnimation: .none, dismissAnimation: .none)

// Left side of the screen
Pushwoosh.TVoS.configureRichMediaWith(position: .left, presentAnimation: .fromLeft, dismissAnimation: .toLeft)

// Right side of the screen
Pushwoosh.TVoS.configureRichMediaWith(position: .right, presentAnimation: .fromRight, dismissAnimation: .toRight)

// Top of the screen
Pushwoosh.TVoS.configureRichMediaWith(position: .top, presentAnimation: .fromTop, dismissAnimation: .toTop)

// Bottom of the screen
Pushwoosh.TVoS.configureRichMediaWith(position: .bottom, presentAnimation: .fromBottom, dismissAnimation: .toBottom)
```

Available position options:

```swift
enum PWTVOSRichMediaPosition {
    case center     // Content positioned at the center of the screen (default)
    case left       // Content positioned at the left side of the screen
    case right      // Content positioned at the right side of the screen
    case top        // Content positioned at the top of the screen
    case bottom     // Content positioned at the bottom of the screen
}
```

### Present animations

Control how rich media content appears on screen:

```swift
enum PWTVOSRichMediaPresentAnimation {
    case none        // No animation, content appears immediately (default)
    case fromTop     // Content slides in from the top of the screen
    case fromBottom  // Content slides in from the bottom of the screen
    case fromLeft    // Content slides in from the left side of the screen
    case fromRight   // Content slides in from the right side of the screen
}
```

### Dismiss animations

Control how rich media content disappears from screen:

```swift
enum PWTVOSRichMediaDismissAnimation {
    case none       // No animation, content disappears immediately (default)
    case toTop      // Content slides out to the top of the screen
    case toBottom   // Content slides out to the bottom of the screen
    case toLeft     // Content slides out to the left side of the screen
    case toRight    // Content slides out to the right side of the screen
}
```

### Configuration examples

Configure rich media to appear on the left side with animations:

```swift
Pushwoosh.TVoS.configureRichMediaWith(
    position: .left,
    presentAnimation: .fromBottom,
    dismissAnimation: .toLeft
)
```

Configure rich media to appear at the bottom with slide animations:

```swift
Pushwoosh.TVoS.configureRichMediaWith(
    position: .bottom,
    presentAnimation: .fromBottom,
    dismissAnimation: .toBottom
)
```

Configure rich media to appear centered without animations:

```swift
Pushwoosh.TVoS.configureRichMediaWith(
    position: .center,
    presentAnimation: .none,
    dismissAnimation: .none
)
```

## Close button configuration

By default, a Close button is displayed at the bottom of rich media presentations. You can hide this button if needed:

```swift
// Hide the system Close button
Pushwoosh.TVoS.configureCloseButton(false)
```

<Aside type="caution">
If you hide the Close button, ensure your rich media HTML content includes a button with the `closeInApp` action to allow users to dismiss the content:

```html
<button onclick="closeInApp()">Close</button>
```
</Aside>

## Focus navigation for Apple TV

The tvOS SDK automatically manages focus navigation for Apple TV's remote control. Interactive elements (buttons, links) in your rich media HTML content will be focusable using the Apple TV remote.

### Focus behavior

- Focusable elements are automatically detected in the HTML content
- Users can navigate between elements using the directional pad on the Apple TV remote
- The currently focused element receives a visual highlight
- Users can activate focused elements by pressing the center button on the remote
- The system Close button (when visible) is also part of the focus navigation

### Best practices for HTML content

When creating HTML content for tvOS rich media:

1. Use standard interactive HTML elements: `<button>`, `<a>`, `<input />`
2. Ensure sufficient spacing between interactive elements for clear focus indication
3. Test your content with the Apple TV simulator to verify focus navigation
4. Consider using larger touch targets compared to iOS (minimum 250pt width recommended)

## Complete integration example

Here's a complete example showing all features:

```swift
import UIKit
import PushwooshTVOS
import PushwooshFramework

@main
class AppDelegate: UIResponder, UIApplicationDelegate {

    func application(_ application: UIApplication,
                     didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {

        // Configure Pushwoosh
        Pushwoosh.TVoS.setAppCode("XXXXX-XXXXX")

        // Configure rich media appearance
        Pushwoosh.TVoS.configureRichMediaWith(
            position: .center,
            presentAnimation: .fromBottom,
            dismissAnimation: .toTop
        )

        // Show close button (default)
        Pushwoosh.TVoS.configureCloseButton(true)

        // Register for push notifications
        Pushwoosh.TVoS.registerForTvPushNotifications()

        return true
    }

    func application(_ application: UIApplication,
                     didRegisterForRemoteNotificationsWithDeviceToken deviceToken: Data) {
        Pushwoosh.TVoS.registerForTvPushNotifications(withToken: deviceToken) { error in
            if let error = error {
                print("Failed to register: \(error)")
            } else {
                print("Successfully registered")
            }
        }
    }

    func application(_ application: UIApplication,
                     didFailToRegisterForRemoteNotificationsWithError error: Error) {
        Pushwoosh.TVoS.handleTvPushRegistrationFailure(error)
    }

    func application(_ application: UIApplication,
                     didReceiveRemoteNotification userInfo: [AnyHashable: Any],
                     fetchCompletionHandler completionHandler: @escaping (UIBackgroundFetchResult) -> Void) {

        if Pushwoosh.TVoS.handleTVOSPush(userInfo: userInfo) {
            completionHandler(.newData)
        } else {
            completionHandler(.noData)
        }
    }
}
```

## Troubleshooting

### Focus navigation issues

If focus navigation is not working properly:

1. Verify that your HTML content uses standard interactive elements (`<button>`, `<a>`)
2. Test in the Apple TV simulator to verify focus behavior
3. Ensure interactive elements have sufficient size and spacing
4. Check that your HTML/CSS doesn't interfere with focus states

## Real device example

Here's how the rich media looks on a real Apple TV device:

![tvOS Rich Media on Apple TV](/tv_os_simulator.webp)

The screenshot shows the rich media content displayed on Apple TV with:
- Custom HTML layout with gradient backgrounds
- Interactive buttons optimized for Apple TV remote control
- Focus navigation support for all interactive elements

## References

- [iOS SDK Source Code](https://github.com/Pushwoosh/pushwoosh-ios-sdk)
- [tvOS Human Interface Guidelines](https://developer.apple.com/design/human-interface-guidelines/tvos)
- [Creating Rich Media Content](/product/content/rich-media)