Find an iOS Simulator Crash With `simctl` Logs and a Reproduction Window
Simulator console output is noisy. Capture the app process log around a reproducible launch, then distinguish an application crash from SpringBoard or simulator startup noise.
Reproduce with a clear time boundary
Boot the target simulator, launch the app, note the time, and reproduce one action. Then query the simulator's unified log for the application process:
xcrun simctl spawn booted log show --last 5m --style compact \
--predicate 'process == "com.example.app"'Replace the bundle identifier with the actual process name if it differs. booted can be ambiguous when multiple simulators are running; use the device UDID in repeatable scripts.
Separate a crash report from ordinary logs
A fatal exception, watchdog termination, or memory kill may produce a crash report under the simulator's device data. Console messages from SpringBoard or a plugin can be warnings without causing the termination. Match the timestamp and process, then inspect the exception type and crashed thread in the report.
For a symbolicated trace, keep the matching dSYM from the same build. A UUID mismatch means the symbols do not correspond to the binary, even if the app name is identical. Preserve the build number and archive identifier when collecting a report from CI or a tester.
If the failure happens only on a clean simulator, inspect assumptions about permissions, keychain, and persisted state. If it happens only after an upgrade, compare migration and launch paths. Logs narrow the event; they do not replace a deterministic reproduction. Add a test or startup assertion once the cause is known, and avoid pasting user tokens or private record contents into shared logs.
Capture the right crash artifact
Use a single simulator UDID in scripts and create a time boundary around one reproduction. Filter unified logs by process, subsystem, or bundle identifier, then inspect crash reports for exception type, termination reason, crashed thread, and binary image UUIDs. A warning from SpringBoard or a plugin may be adjacent to the crash without causing it. Match timestamps and process identity before treating a line as causal.
Symbolication requires the dSYM from the same build. A similarly named archive with a different UUID will produce misleading or unsymbolicated frames. Store build number, commit, configuration, simulator runtime, and dSYM identity with CI artifacts. Remove tokens, customer data, and full request bodies before sharing logs in a ticket.
Compare cold launch, warm launch, and upgrade launch. A crash after migration points to persisted state; a crash only after a permission denial points to an unhandled platform branch. Once the cause is known, add a regression test or a startup guard that fails with a useful message. Logging should narrow the event, not become the permanent substitute for a reproducible test. Include a test for the first user action after launch, not only process startup. Correlate crash reports with exact build symbols and turn the smallest confirmed path into a regression case.
Preserve the simulator runtime, process name, timestamp, exception type, and matching dSYM UUID with the crash report. Redact tokens, private URLs, and customer data before sharing. If the crash disappears after reset, reproduce the removed migration, permission, or keychain state deliberately. Otherwise the reset has changed the experiment rather than fixed the application.
A warning from SpringBoard or a plugin may be adjacent to a crash without causing it. Correlate process and timestamp, then inspect the exception type, termination reason, and crashed thread. Keep a minimal redacted crash bundle and turn the smallest confirmed path into a regression test.
Include a test for the first user action after launch, not only process startup. Watchdog termination, memory pressure, and an exception in a background callback can look like a launch crash when timestamps are close. Correlate the crash report with app logs and exact build symbols, then turn the smallest confirmed path into a regression case.
Keep a minimal crash bundle containing timestamp, process, build number, simulator runtime, exception type, and matching dSYM identity. Redact user data, tokens, private URLs, and database contents before sharing. If a crash disappears after erasing the simulator, reproduce the removed migration, permission, or keychain state deliberately instead of assuming the erase fixed application code.
A warning from SpringBoard or a plugin may be adjacent to a crash without causing it. Correlate process and timestamp, then inspect exception type, termination reason, and the crashed thread. Compare clean, upgraded, and permission-denied state deliberately, and turn the smallest confirmed path into a regression test.