How to Debug Native Mobile Apps on Real Devices
Debug native iOS and Android apps on physical devices: connect hardware, inspect code, capture logs, and diagnose issues that simulators miss.
To debug a native mobile app on a real device, connect the phone or tablet to your development environment, select it as the run target, reproduce the issue, and inspect execution with a debugger. When a live session cannot catch an intermittent failure, preserve device logs and crash reports alongside the exact device, OS version, app build, and reproduction steps.
Use Xcode to run and debug iOS apps on a physical Apple device. For Android, enable Developer options and device debugging, then connect through USB or wireless debugging and use Android Studio. A simulator or emulator is useful for fast iteration, but hardware-dependent behavior and physical-device performance need verification on actual hardware. Apple recommends running on one or more physical devices to verify intended behavior. Apple: Running your app on simulated or physical devices.
1. Record the reproduction details
Before changing code or reconnecting devices, write down enough detail to make the failure repeatable:
- Device model and hardware variant, if relevant.
- OS version and build.
- App version, build configuration, and source revision.
- Whether the app was installed fresh, upgraded, or restored from prior data.
- Exact steps, inputs, network conditions, and expected versus actual result.
- Whether the behavior occurs every time, only after a delay, or after a specific lifecycle event such as backgrounding.
Keep this record with logs and crash information. “Works on my simulator” does not identify the device state, permissions, OS version, or hardware conditions needed to reproduce the issue.
2. Debug an iOS app on a physical device
Prepare Xcode and the device
- Open the app project or workspace in Xcode.
- Connect the iPhone or iPad to the Mac. Complete any trust or pairing prompts shown by the device or Xcode.
- In the run destination menu, choose the connected physical device. If it does not appear, check the connection, device trust state, and Xcode’s device list.
- Choose the appropriate scheme and build configuration for the behavior being investigated.
- Set a breakpoint near the suspected failure, then choose Run.
- After the app builds and launches, use Xcode’s debug area to step through code and inspect variables.
Xcode can launch an app on a selected physical Apple device and open a debugger session after a successful build. Simulator runs remain useful for quick iterations, but Apple notes that simulators do not reproduce every physical-device feature or its performance characteristics. Apple’s device and simulator guidance.
Make the debugger session useful
- Break close to the operation that produces the wrong state; avoid starting with a breakpoint far upstream unless you need to trace how that state was formed.
- When the breakpoint hits, inspect the values that determine the branch or request, then step over or into the relevant call.
- Check whether the issue depends on app lifecycle, permissions, orientation, camera or sensor access, or network state.
- Reproduce with the same build configuration and app data state as the report. Debug and release configurations can behave differently.
3. Debug an Android app on a physical device
Enable device debugging and connect
- On the Android device, enable Developer options. The way to reveal this menu can vary by Android release and manufacturer.
- Enable the device debugging option in Developer options, then connect the device to the computer using a USB data connection.
- Accept the device’s authorization prompt if one appears.
- Open the project in Android Studio and choose the physical device as the run target.
- Set a breakpoint and run or attach the debugger to the app process.
Android documents both USB and wireless device debugging. A cable is optional if you use wireless debugging; for USB, use a cable and host port that support data transfer, not just charging. Menu names and locations differ across devices. Android’s developer-options guide says Android 16 (API level 36) and later place Wireless debugging under Settings > System > Developer options; check the target device’s actual settings rather than assuming that path applies everywhere. Android: Run apps on a hardware device and Android: Configure on-device developer options.
Use wireless debugging when it fits
Wireless debugging avoids a tether during repeated runs, but both the host and device must be configured for the documented wireless workflow. Follow the Android Studio and device instructions for the Android version in use. If discovery or pairing fails, return to a known-good USB connection where available and confirm the device is authorized before investigating app code.
Inspect Java and native code
Android Studio supports Java debugging and LLDB for native code. Select the debugger appropriate to the code where the failure occurs, then use breakpoints, stepping, and variable inspection to follow the relevant execution path. For mixed Java and native behavior, verify that the debugger is attached to the process and code section you intend to inspect. See Android Studio: Debug your app.
4. Capture evidence when the bug is intermittent
A debugger can alter timing, and some failures happen only after a long session or outside a convenient breakpoint. When a live session does not catch the problem, collect diagnostic evidence after reproducing it.
On Apple devices
Inspect device console logs for non-crash events and retrieve the crash report for a terminated app. Apple documents crash reports as describing how an app terminated and the code running on each thread at the time. Preserve the report with the device model, OS version, app build, and reproduction steps. See Apple: Analyzing a crash report.
On Android devices
Use Android Studio and Android device tools to inspect the running process and its diagnostic output. Save relevant logs and crash details around the failure, and record the same device, OS, build, and reproduction context. A short log window around the event is often easier to interpret than an unfiltered dump; retain enough preceding context to show what led to it.
Separate live debugging from post-incident diagnosis
- Live debugging: use breakpoints and a debugger to inspect execution while the app is running.
- Post-incident diagnosis: use logs and crash reports to investigate a failure that already occurred or cannot reliably be caught under a debugger.
For intermittent issues, note the time of the event and correlate it with the device log. Avoid treating the absence of a debugger breakpoint as proof that the code path did not run.
5. Choose between a simulator and a real device
| Use a simulator or emulator for | Use physical hardware for |
|---|---|
| Fast iteration, checking layouts, and exploring virtual device or OS configurations. | Verifying behavior that depends on actual device features or physical performance. |
| Reproducing issues that are already known to occur in the simulated environment. | Investigating reports that happen only on a particular model, OS build, sensor, or hardware state. |
| Broad virtual coverage when the relevant behavior is represented accurately. | Confirming the final behavior on one or more representative devices before release. |
Neither environment replaces the other. Use the simulator for speed and repeatability, then verify hardware-sensitive behavior on a device. Apple specifically cautions that simulators do not replicate the performance or all features of physical devices.
6. Expand Android device coverage
If a problem is tied to a model you do not own, or appears only across a subset of devices, cloud-hosted physical-device testing can help broaden coverage. Android’s hardware-device guide points to Firebase Test Lab for testing on a wider range of real devices. Check the service’s current availability and terms before relying on it; device coverage alone does not guarantee that a particular user’s conditions have been reproduced. Android hardware-device guide.
When you receive a failure from another device, ask for its model, OS version, app build, steps, and relevant logs or crash report. That context is needed to decide whether the issue is device-specific, OS-specific, or caused by app state.
7. Troubleshooting
| Symptom | Likely cause | What to do |
|---|---|---|
| iPhone or iPad does not appear in Xcode | Connection, trust, pairing, or device availability issue. | Check the physical connection, respond to trust prompts, and confirm the device appears in Xcode’s device list. Retry after reconnecting. |
| Android device is missing from Android Studio | Device debugging is off, authorization is pending, or the USB connection does not carry data. | Enable device debugging, unlock the device and accept its authorization prompt. Try a known data-capable cable or use the documented wireless setup. |
| Wireless Android debugging cannot pair or discover the device | Wireless setup is incomplete, or the device’s settings differ from the assumed menu path. | Follow the workflow for the device’s Android version, confirm Wireless debugging is enabled, and use USB as a fallback when available. |
| App launches, but the breakpoint never hits | The selected build or process differs from the one expected, the code path was not reached, or the debugger is attached to the wrong process or code type. | Confirm the run target and build, verify the app reaches the relevant action, and attach the Java or LLDB debugger appropriate to the Android code involved. |
| Issue disappears while debugging | Timing or lifecycle behavior changed under the debugger. | Reproduce without a live debugger and collect logs or a crash report. Record the event timing and conditions. |
| Issue occurs on one device but not another | Different OS versions, hardware features, permissions, app state, or device-specific behavior. | Compare the recorded device and OS details, permissions, build, and reproduction steps. Test on the affected physical model when possible. |
| Crash report is hard to interpret | Missing symbol information or insufficient context can make a report difficult to map to source and reproduce. | Use the matching app build and available symbols, preserve the report, and correlate its time with device logs and the reproduction record. |
8. Improve debugging speed and reliability
- Start with one repeatable case. Keep the initial reproduction steps short, then vary one condition at a time, such as OS version, network state, or app data.
- Use simulators for quick iteration. They make it easier to repeat virtual configurations; reserve device time for behavior that depends on hardware or needs physical verification.
- Keep build and device context with every report. A log without the app build and device details can be difficult to compare with another run.
- Capture evidence promptly. Preserve logs and crash reports close to the failure, especially when the issue is intermittent.
- Do not infer broad device behavior from one phone. Use representative physical devices and expand coverage when reports indicate model- or OS-specific patterns.
9. Capture a web page alongside a mobile bug report
Some mobile issues depend on a web page shown in a native web view, an embedded checkout, or content that changes between reports. A screenshot can preserve the visible page state for comparison, but it does not replace device logs, a crash report, or a debugger session. ScreenshotNeo is a website screenshot API and MCP server made by Yorker Media; it captures web pages, not native app screens. See the ScreenshotNeo website.
Or skip the browser setup
If the issue involves a web page inside or linked from your app, a screenshot API can capture that page without setting up browser automation. The request below captures a web page; use your app’s native debugging tools for the device and app itself. See the ScreenshotNeo API documentation.
curl -G "https://api.screenshotneo.com/v1/shot" -d access_key=YOUR_API_KEY --data-urlencode url=https://stripe.com -o shot.webp
import requests
r = requests.get(
"https://api.screenshotneo.com/v1/shot",
params={"access_key": "YOUR_API_KEY", "url": "https://stripe.com"},
timeout=90,
)
open("shot.webp", "wb").write(r.content)
const q = new URLSearchParams({ access_key: 'YOUR_API_KEY', url: 'https://stripe.com' });
const res = await fetch(`https://api.screenshotneo.com/v1/shot?${q}`);
if (!res.ok) throw new Error(`Screenshot request failed: ${res.status}`);
await Bun.write('shot.webp', res);
ScreenshotNeo removes cookie banners, newsletter popups, and chat widgets before the shot. Bot checks, blank pages, and failed loads are never billed. Its MCP server lets AI agents take screenshots, and 1,000 screenshots a month are free with no card; paid plans start at $5 for 3,000. Sign up for 1,000 free screenshots a month.
FAQ
Can I debug an app installed from an app store?
The workflow depends on the build and platform setup. For source-level stepping and variable inspection, run or attach a debuggable build using the platform’s development tools. For a failure in a distributed build, collect its logs and crash information and reproduce with a matching development build when possible.
Do I always need a USB cable for Android debugging?
No. Android documents wireless debugging as well as USB. A data-capable cable is one connection option and a useful fallback if wireless setup is unavailable.
Will screenshots help diagnose a native crash?
A screenshot can show visible web content or app state, but it cannot explain the code path or termination reason. Use the platform debugger, device logs, and crash reports for native diagnosis.
Should I test every phone model?
Start with the affected model and representative devices for the app’s supported range. Expand coverage when reports point to a specific hardware or OS variation; cloud-hosted device testing can help broaden Android coverage.


