Claude-skill-registry apple-tv-troubleshooter
Expert troubleshooting for Apple TV (tvOS) React Native development. Use when users have issues with Siri Remote, focus management, TVEventHandler, TVFocusGuideView, ScrollView not scrolling, tvOS-specific problems, parallax animations, or tvOS vs Android TV differences.
git clone https://github.com/majiayu000/claude-skill-registry
T=$(mktemp -d) && git clone --depth=1 https://github.com/majiayu000/claude-skill-registry "$T" && mkdir -p ~/.claude/skills && cp -r "$T/skills/data/apple-tv-troubleshooter" ~/.claude/skills/majiayu000-claude-skill-registry-apple-tv-troubleshooter && rm -rf "$T"
skills/data/apple-tv-troubleshooter/SKILL.mdApple 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 for swipe-based scrolling |
| TVEventHandler doesn't fire | No focusable component on screen | Add 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 enabled |
| Menu button doesn't work | Not enabled by default | Call |
| Pan/swipe not detected | Disabled by default | Call |
| Expo prebuild fails after changing EXPO_TV | Cached native config | Always run |
| 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 to TouchableHighlight |
| removeClippedSubviews breaks focus | Clipped items lose focus | Set |
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 |
|---|---|
| Remembers last focused child, restores on revisit |
| Prevents focus from leaving in that direction |
| Array of refs to guide focus toward |
| 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:
StatusBarSliderSwitch
(limited support)WebView
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 |
| 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
handlers to debug focus flowonBlur
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"