Skip to content

tvOS modal rich media

Starting from Pushwoosh SDK version 6.10.6, 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.

For more information on Rich Media pages, please refer to our guide.

Installation

Anchor link to

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

Swift Package Manager

Anchor link to

Add the Pushwoosh package to your tvOS project:

  1. In Xcode, select FileAdd 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

Add to your Podfile:

target 'YourTvOSApp' do
platform :tvos, '13.0'
pod 'PushwooshXCFramework'
pod 'PushwooshXCFramework/PushwooshTVOS'
end

Then run:

Terminal window
pod install

Basic integration

Anchor link to

1. Configure Pushwoosh in your AppDelegate

Anchor link to

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

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

Anchor link to

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

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

Anchor link to

Process incoming push notifications with rich media content:

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)
}
}

Rich Media configuration

Anchor link to

Positioning

Anchor link to

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

// 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:

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

Anchor link to

Control how rich media content appears on screen:

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

Anchor link to

Control how rich media content disappears from screen:

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

Anchor link to

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

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

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

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

Configure rich media to appear centered without animations:

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

Close button configuration

Anchor link to

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

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

Focus navigation for Apple TV

Anchor link to

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

Anchor link to
  • 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

Anchor link to

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

Anchor link to

Here’s a complete example showing all features:

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)
}
}
}

Objective-C support

Anchor link to

The tvOS SDK also supports Objective-C. Here’s an example:

#import <UIKit/UIKit.h>
#import <PushwooshTVOS/PushwooshTVOS.h>
#import <PushwooshFramework/PushwooshFramework.h>
@implementation AppDelegate
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
// Configure Pushwoosh
[PWTVoS setAppCode:@"XXXXX-XXXXX"];
// Configure rich media
[PWTVoS configureRichMediaWithPosition:PWTVOSRichMediaPositionCenter
presentAnimation:PWTVOSRichMediaPresentAnimationFromBottom
dismissAnimation:PWTVOSRichMediaDismissAnimationToTop];
[PWTVoS configureCloseButton:YES];
// Register for push notifications
[PWTVoS registerForTvPushNotifications];
return YES;
}
- (void)application:(UIApplication *)application didRegisterForRemoteNotificationsWithDeviceToken:(NSData *)deviceToken {
[PWTVoS registerForTvPushNotificationsWithToken:deviceToken completion:^(NSError * _Nullable error) {
if (error) {
NSLog(@"Failed to register: %@", error);
} else {
NSLog(@"Successfully registered");
}
}];
}
- (void)application:(UIApplication *)application didFailToRegisterForRemoteNotificationsWithError:(NSError *)error {
[PWTVoS handleTvPushRegistrationFailure:error];
}
- (void)application:(UIApplication *)application
didReceiveRemoteNotification:(NSDictionary *)userInfo
fetchCompletionHandler:(void (^)(UIBackgroundFetchResult))completionHandler {
if ([PWTVoS handleTVOSPushWithUserInfo:userInfo]) {
completionHandler(UIBackgroundFetchResultNewData);
} else {
completionHandler(UIBackgroundFetchResultNoData);
}
}
@end

Troubleshooting

Anchor link to

Focus navigation issues

Anchor link to

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

References

Anchor link to