apple-tv-troubleshooter
Apple TV Troubleshooter
You are an expert in Apple TV (tvOS) development with React Native. This skill activates when users encounter:
- Focus management issues on Apple TV
- Siri Remote event handling problems
- TVEventHandler not capturing events
- ScrollView/FlatList not scrolling
- TVFocusGuideView configuration
- tvOS vs Android TV differences
- Expo TV build issues
- Navigation and focus traps
tvOS Focus Engine vs Android TV
Critical Difference: tvOS uses a precision-based focus engine while Android TV uses proximity-based.
| Aspect | Apple TV (tvOS) | Android TV |
|---|---|---|
| Focus Engine | Precision-based (strict alignment) | Proximity-based (nearest element) |
| Remote Input | Siri Remote touchpad (swipe + click) | D-pad directional buttons |
| Focus Recovery | Attempts automatic (inconsistent) | Moves to top-left corner |
| Screen Resolution | 1920x1080 (native) | 960x540 (scaled) |
Implication: UI elements must be properly aligned on tvOS or focus won't move between them.
Siri Remote Event Handling
Using useTVEventHandler Hook (Recommended)
import { useTVEventHandler } from 'react-native';
function MyComponent() {
useTVEventHandler((evt) => {
switch (evt.eventType) {
case 'up':
case 'down':
case 'left':
case 'right':
// Handle navigation
break;
case 'select':
// Center button pressed
break;
case 'playPause':
// Play/Pause button
break;
case 'longPlayPause':
// Long press play/pause (tvOS only)
break;
}
});
return <View>{/* content */}</View>;
}
TVEventControl for Menu and Gestures
import { TVEventControl } from 'react-native';
// Enable Menu button handling (for back navigation)
TVEventControl.enableTVMenuKey();
// Enable pan gesture detection on Siri Remote touchpad
TVEventControl.enableTVPanGesture();
// Disable when component unmounts
TVEventControl.disableTVMenuKey();
TVEventControl.disableTVPanGesture();
Common Problems & Solutions
| Problem | Cause | Solution |
|---|---|---|
| ScrollView won't scroll | Regular ScrollView needs focusable items | Use TVTextScrollView for swipe-based scrolling |
| TVEventHandler doesn't fire | No focusable component on screen | Add hasTVPreferredFocus={true} to parent View or ensure a Touchable exists |
| Event fires twice | Press and release both trigger | Known behavior - debounce or track event state |
| InputText can't receive focus | tvOS limitation | Use native input alternatives or custom keyboards |
| Focus leaves FlatList unexpectedly | Virtualization removes focused item | VirtualizedList auto-wraps with TVFocusGuideView - ensure trapFocus enabled |
| Menu button doesn't work | Not enabled by default | Call TVEventControl.enableTVMenuKey() |
| Pan/swipe not detected | Disabled by default | Call TVEventControl.enableTVPanGesture() |
| Expo prebuild fails after changing EXPO_TV | Cached native config | Always run npx expo prebuild --clean |
| Flipper causes build errors | Incompatible with TV | Set Flipper to false in Podfile, run prebuild --clean |
| Wrong screen dimensions | Platform difference | Use platform-specific StyleSheets |
| Focus doesn't move diagonally | Precision engine limitation | Ensure UI elements are aligned vertically/horizontally |
| BackHandler doesn't work | Different API on tvOS | Use TVEventControl.enableTVMenuKey() for menu/back |
| Parallax not working | Missing props | Add tvParallaxProperties to TouchableHighlight |
| removeClippedSubviews breaks focus | Clipped items lose focus | Set removeClippedSubviews={false} |
TVFocusGuideView Configuration
import { TVFocusGuideView } from 'react-native';
// Basic usage with auto-focus memory
<TVFocusGuideView autoFocus>
<TouchableOpacity>Item 1</TouchableOpacity>
<TouchableOpacity>Item 2</TouchableOpacity>
</TVFocusGuideView>
// Trap focus within container
<TVFocusGuideView
trapFocusUp
trapFocusDown
trapFocusLeft
trapFocusRight
>
{/* Focus cannot escape this container */}
</TVFocusGuideView>
// Custom focus destinations
<TVFocusGuideView destinations={[buttonRef.current]}>
{/* Guides focus to specific elements */}
</TVFocusGuideView>
Key Props
| Prop | Description |
|---|---|
autoFocus |
Remembers last focused child, restores on revisit |
trapFocusUp/Down/Left/Right |
Prevents focus from leaving in that direction |
destinations |
Array of refs to guide focus toward |
focusable |
When false, view and children not focusable |
Platform-Specific Components
TVTextScrollView (for scrolling content)
import { TVTextScrollView } from 'react-native';
// Use instead of ScrollView for non-focusable content
<TVTextScrollView>
<Text>Long text content that should scroll with swipe...</Text>
</TVTextScrollView>
Parallax Animations
<TouchableHighlight
tvParallaxProperties={{
enabled: true,
magnification: 1.1,
pressMagnification: 1.0,
shiftDistanceX: 5,
shiftDistanceY: 5,
}}
>
<Image source={poster} />
</TouchableHighlight>
Unsupported Components on tvOS
These components are disabled or suppressed on Apple TV:
StatusBarSliderSwitchWebView(limited support)
Focus Management Best Practices
1. Set Default Focus on Mount
<TouchableOpacity hasTVPreferredFocus={true}>
Default Focused Item
</TouchableOpacity>
2. Use nextFocus Props for Custom Navigation
<TouchableOpacity
ref={button1Ref}
nextFocusRight={button2Ref.current}
nextFocusDown={button3Ref.current}
>
Button 1
</TouchableOpacity>
3. Capture Events at Top Level
// Good: Capture at parent level
function Screen() {
useTVEventHandler((evt) => {
// Handle all events here, delegate to children
});
return <View>{/* children */}</View>;
}
// Bad: Each small component handles its own events
function SmallButton() {
useTVEventHandler((evt) => { /* ... */ }); // Avoid this pattern
}
4. Use React Context for Focus State
const FocusContext = createContext({ focusedId: null, setFocused: () => {} });
function FocusProvider({ children }) {
const [focusedId, setFocused] = useState(null);
return (
<FocusContext.Provider value={{ focusedId, setFocused }}>
{children}
</FocusContext.Provider>
);
}
Expo TV Specific Issues
Environment Variable
# Must be set BEFORE prebuild
export EXPO_TV=1
# Always clean when changing this variable
npx expo prebuild --clean
Common Expo TV Errors
| Error | Solution |
|---|---|
| "EXPO_TV not recognized" | Ensure using Expo SDK 50+ |
| Build fails after toggling EXPO_TV | Run npx expo prebuild --clean |
| Flipper errors | Disable Flipper in ios/Podfile |
| Dev menu not showing | Use SDK 54+ with RNTV 0.81 for TV dev menu support |
Platform Detection
import { Platform } from 'react-native';
// Check if running on any TV
if (Platform.isTV) {
// TV-specific code
}
// Check specifically for Apple TV (not Android TV)
if (Platform.isTVOS) {
// Apple TV only code
}
// Platform-specific styles
const styles = StyleSheet.create({
container: {
padding: Platform.isTVOS ? 48 : 16,
},
});
Debugging Tips
- LogBox works on TV - Error display supported after RN TV 0.76+
- Use console.log liberally - Metro bundler shows logs
- Test on real device - Simulator misses Siri Remote nuances
- Check focus state - Add
onFocus/onBlurhandlers to debug focus flow
Resources
Cross-Platform Troubleshooting (Also Applies to Apple TV)
TypeError: Cannot read property 'displayName' of undefined
This error affects all TV platforms including Apple TV when using Expo.
Symptoms:
- App builds successfully but crashes immediately on launch
- Metro shows:
ERROR TypeError: Cannot read property 'displayName' of undefined
Common Causes & Fixes:
-
Wrong import in index.js (Most Common)
// ❌ WRONG - Named import when App uses default export import { App } from './App'; // ✅ CORRECT - Default import import App from './App'; -
Metro cache corruption
pkill -f "expo" 2>/dev/null || true rm -rf node_modules/.cache /tmp/metro-* /tmp/haste-map-* npx expo start --clear -
react-tv-space-navigation v6 missing configuration
- v6.0.0+ requires explicit remote control configuration
- Create
src/configureRemoteControl.ts:
import { SpatialNavigation } from 'react-tv-space-navigation'; SpatialNavigation.configureRemoteControl({ remoteControlSubscriber: (callback) => () => {}, remoteControlUnsubscriber: () => {}, });- Import at top of App.tsx:
import './src/configureRemoteControl';
Expo TV Build & Run Issues
Always Use Development Builds for TV
# ❌ May cause SDK version issues on TV simulators
npx expo start
# ✅ Correct for TV development
npx expo run:ios # Apple TV
npx expo run:android # Android TV
# or
npx expo start --dev-client
Apple TV Simulator Quick Commands
# List available simulators
xcrun simctl list devices available | grep -i tv
# Boot Apple TV simulator
xcrun simctl boot "Apple TV"
# Run app
npx expo run:ios --device "Apple TV"
More from giolaq/multi-tv-dev-power
rn-to-tv-quickstart
Quick start guide for experienced React Native developers transitioning to TV app development. Covers Fire OS, Android TV, Apple TV (tvOS), Vega OS, and Web TV platforms. Focuses on what's different from mobile: spatial navigation, remote controls, focus management, and 10-foot UI patterns. Skip the basics - get your first TV app running fast.
10tmdb-integration
TMDB (The Movie Database) API integration for React Native TV streaming apps. Use when users need help with movie/TV show data, poster images, search functionality, trending content, video trailers from TMDB, API authentication, rate limiting, or TypeScript types for TMDB responses.
2multi-tv-builder
Complete knowledge base for building production-ready cross-platform TV applications with React Native - supporting Android TV, Apple TV, Fire TV (Fire OS & Vega OS), and Web TV platforms with a single codebase. Use when implementing TV apps, monorepo architecture, spatial navigation, video playback, platform-specific optimizations, or migrating from Vega to multi-platform.
2