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 toThe PushwooshTVOS module is optional and can be integrated into your tvOS project using Swift Package Manager or CocoaPods.
Swift Package Manager
Anchor link toAdd the Pushwoosh package to your tvOS project:
- In Xcode, select File → Add Package Dependencies…
- Enter the repository URL:
https://github.com/Pushwoosh/Pushwoosh-XCFramework
- Select the latest version
- Add the following frameworks to your tvOS target:
- PushwooshFramework
- PushwooshCore
- PushwooshBridge
- PushwooshLiveActivities
- PushwooshTVOS
CocoaPods
Anchor link toAdd to your Podfile:
target 'YourTvOSApp' do platform :tvos, '13.0'
pod 'PushwooshXCFramework' pod 'PushwooshXCFramework/PushwooshTVOS'end
Then run:
pod install
Basic integration
Anchor link to1. Configure Pushwoosh in your AppDelegate
Anchor link toImport the required modules and configure Pushwoosh with your application code:
import UIKitimport PushwooshTVOSimport PushwooshFramework
@mainclass 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 toImplement 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 toProcess 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 toPositioning
Anchor link toModal 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 screenPushwoosh.TVoS.configureRichMediaWith(position: .left, presentAnimation: .fromLeft, dismissAnimation: .toLeft)
// Right side of the screenPushwoosh.TVoS.configureRichMediaWith(position: .right, presentAnimation: .fromRight, dismissAnimation: .toRight)
// Top of the screenPushwoosh.TVoS.configureRichMediaWith(position: .top, presentAnimation: .fromTop, dismissAnimation: .toTop)
// Bottom of the screenPushwoosh.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 toControl 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 toControl 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 toConfigure 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 toBy default, a Close button is displayed at the bottom of rich media presentations. You can hide this button if needed:
// Hide the system Close buttonPushwoosh.TVoS.configureCloseButton(false)
Focus navigation for Apple TV
Anchor link toThe 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 toWhen creating HTML content for tvOS rich media:
- Use standard interactive HTML elements:
<button>
,<a>
,<input>
- Ensure sufficient spacing between interactive elements for clear focus indication
- Test your content with the Apple TV simulator to verify focus navigation
- Consider using larger touch targets compared to iOS (minimum 250pt width recommended)
Complete integration example
Anchor link toHere’s a complete example showing all features:
import UIKitimport PushwooshTVOSimport PushwooshFramework
@mainclass 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 toThe 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 toFocus navigation issues
Anchor link toIf focus navigation is not working properly:
- Verify that your HTML content uses standard interactive elements (
<button>
,<a>
) - Test in the Apple TV simulator to verify focus behavior
- Ensure interactive elements have sufficient size and spacing
- Check that your HTML/CSS doesn’t interfere with focus states