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.

install
source · Clone the upstream repo
git clone https://github.com/majiayu000/claude-skill-registry
Claude Code · Install into ~/.claude/skills/
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"
manifest: skills/data/apple-tv-troubleshooter/SKILL.md
source content

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.

AspectApple TV (tvOS)Android TV
Focus EnginePrecision-based (strict alignment)Proximity-based (nearest element)
Remote InputSiri Remote touchpad (swipe + click)D-pad directional buttons
Focus RecoveryAttempts automatic (inconsistent)Moves to top-left corner
Screen Resolution1920x1080 (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

ProblemCauseSolution
ScrollView won't scrollRegular ScrollView needs focusable itemsUse
TVTextScrollView
for swipe-based scrolling
TVEventHandler doesn't fireNo focusable component on screenAdd
hasTVPreferredFocus={true}
to parent View or ensure a Touchable exists
Event fires twicePress and release both triggerKnown behavior - debounce or track event state
InputText can't receive focustvOS limitationUse native input alternatives or custom keyboards
Focus leaves FlatList unexpectedlyVirtualization removes focused itemVirtualizedList auto-wraps with TVFocusGuideView - ensure
trapFocus
enabled
Menu button doesn't workNot enabled by defaultCall
TVEventControl.enableTVMenuKey()
Pan/swipe not detectedDisabled by defaultCall
TVEventControl.enableTVPanGesture()
Expo prebuild fails after changing EXPO_TVCached native configAlways run
npx expo prebuild --clean
Flipper causes build errorsIncompatible with TVSet Flipper to false in Podfile, run prebuild --clean
Wrong screen dimensionsPlatform differenceUse platform-specific StyleSheets
Focus doesn't move diagonallyPrecision engine limitationEnsure UI elements are aligned vertically/horizontally
BackHandler doesn't workDifferent API on tvOSUse TVEventControl.enableTVMenuKey() for menu/back
Parallax not workingMissing propsAdd
tvParallaxProperties
to TouchableHighlight
removeClippedSubviews breaks focusClipped items lose focusSet
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

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

  • StatusBar
  • Slider
  • Switch
  • WebView
    (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

ErrorSolution
"EXPO_TV not recognized"Ensure using Expo SDK 50+
Build fails after toggling EXPO_TVRun
npx expo prebuild --clean
Flipper errorsDisable Flipper in ios/Podfile
Dev menu not showingUse 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

  1. LogBox works on TV - Error display supported after RN TV 0.76+
  2. Use console.log liberally - Metro bundler shows logs
  3. Test on real device - Simulator misses Siri Remote nuances
  4. Check focus state - Add
    onFocus
    /
    onBlur
    handlers 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:

  1. 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';
    
  2. Metro cache corruption

    pkill -f "expo" 2>/dev/null || true
    rm -rf node_modules/.cache /tmp/metro-* /tmp/haste-map-*
    npx expo start --clear
    
  3. 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"