Back to blog
    September 1, 2026Tutorial

    Automating React Native on iOS When the Page Source Is Unreadable

    Short answer: the nested wrappers you see in Appium Inspector are a tooling problem, not a React Native problem. The testID you set in JSX does reach the native layer on both platforms — it just gets buried in a hierarchy most inspectors render unusably deep. Fix what you inspect with, add testID consistently, and one locator will drive both iOS and Android.

    Why the iOS tree looks like that

    React Native composes its UI from host views. A single <Pressable> with a <Text> inside can land as several nested XCUIElementTypeOther nodes, none of which is obviously "the button". Dump that tree and you get forty levels of scaffolding, so people fall back to XPath expressions that describe position rather than identity:

    java
    // Brittle: describes where the element sits today
    driver.findElement(AppiumBy.xpath(
        "//XCUIElementTypeOther[3]/XCUIElementTypeOther[2]/XCUIElementTypeButton[1]"));

    That locator survives exactly until someone wraps the view in one more <View> for layout.

    Android usually hurts less, because the view hierarchy is flatter and resource-id gives you something stable to grab. The asymmetry is why teams end up with two locator strategies for one codebase — and twice the maintenance.

    The fix starts in the app, not the test

    Set testID on every element a test needs to touch. React Native maps it to the accessibility identifier on iOS and the content description / resource-id path on Android, so the same string works on both:

    jsx
    <Pressable testID="checkout-button" onPress={checkout}>
      <Text>Check out</Text>
    </Pressable>
    java
    // One locator, both platforms
    driver.findElement(AppiumBy.accessibilityId("checkout-button")).click();

    Two rules make this hold up over time:

    • Name the role, not the design. checkout-button, never green-cta-v2. A design-derived name is guaranteed to be wrong after the next redesign.
    • Put it on the pressable, not the label. testID on a <Text> inside a <Pressable> finds the text node, and taps on it may not register as taps on the button.

    If you inherited an app with no testIDs, add them to the twenty elements your smoke suite touches before you do anything else. That is usually a single afternoon and it removes most of the XPath.

    But the ids still have to be visible

    Here is where teams get stuck. You add testID correctly, and the iOS inspector still shows a wall of XCUIElementTypeOther. The identifier is there; the tool is not surfacing it in a form you can act on. So people conclude React Native on iOS is simply hard to automate, and go back to XPath.

    It is worth checking that assumption against a different inspector before rewriting your test strategy around it. In the RobotActions inspector a React Native app resolves the way any native app does — named, addressable elements rather than wrapper soup — which is the difference between accessibilityId("checkout-button") being available to you and not.

    That is the whole claim, and it takes about ten minutes on the free tier to confirm or dismiss on your own build.

    Verify on real hardware

    Simulators hide the two things most likely to break a React Native screen:

    • Gesture timing. Synthesised swipes send two coordinates and skip everything in between. Drag handles, carousels, sliders and pull-to-refresh only behave correctly under a continuous stream of pointer events.
    • Permission and system dialogs. OEM Android skins and iOS permission prompts appear at different moments on real devices than in a simulator, and a test that never sees them passes for the wrong reason.

    Both are cheap to check once the app is installed on a real phone you can drive from a browser.

    A reasonable order of work

    1. Add testID to the elements your smoke suite touches. Role-based names.
    2. Inspect on a real iOS device and confirm the ids are actually addressable.
    3. Replace XPath with accessibilityId wherever step 2 made it possible.
    4. Run the same suite on Android — most of those locators should now work unchanged.
    5. Keep XPath only for the handful of cases nothing else reaches, and comment why.

    The goal is not zero XPath. It is that a redesign changes the layout without changing which elements your tests can find.

    Related

    Ready to test on real devices?

    Sign in with Google or GitHub and get real iOS and Android devices in your browser — free to try.

    👋 Hi! Need help? Chat with us!

    Chat with us

    Online

    Before we start

    Share your details so we can follow up with you.