You've built something great with Rork Max, tested it thoroughly on the simulator, and then — the moment you install it on a real iPhone — it crashes instantly. If this sounds familiar, you're in good company. This is one of the oldest frustrations in iOS development, and AI-generated code doesn't make it go away.
What AI does change is the scale: generated apps can be large and complex, which means more surface area for device-specific issues to hide. The good news is that these crashes tend to fall into a handful of well-known patterns. Once you know what to look for, diagnosing and fixing them is surprisingly straightforward.
Why Simulator and Device Behavior Differ
Before diving into the fixes, it helps to understand why the gap exists at all.
CPU architecture is the most fundamental difference. The iOS Simulator runs on your Mac's architecture (x86_64 on Intel Macs, arm64 on Apple Silicon), while every real iPhone runs arm64. Libraries compiled for the wrong architecture will silently work on the simulator and crash on device.
Memory constraints are dramatically different. The simulator borrows your Mac's abundant RAM, so memory-hungry code runs without issue. A real device has a fraction of that memory, and iOS's memory manager (jetsam) will unceremoniously kill your process if it exceeds its budget.
Permissions and sandboxing are enforced strictly on device. Accessing the camera, microphone, or location without the proper Info.plist entries is often silently ignored in the simulator but causes an immediate crash on a real iPhone.
5 Common Crash Patterns in Rork Max Apps
Pattern 1: Missing Privacy Usage Descriptions in Info.plist
This is the single most common cause. When Rork Max generates code that accesses the camera, microphone, photo library, or location, the corresponding NSXxxUsageDescription keys must be present in Info.plist. Without them, your app crashes instantly on device.
// Rork Max-generated camera access code
import AVFoundation
func requestCameraAccess() {
AVCaptureDevice.requestAccess(for: .video) { granted in
// This will crash on device if Info.plist entry is missing
}
}Add the appropriate entries to your Info.plist (or equivalent configuration in Rork Max):
NSCameraUsageDescription = "Used to take profile photos"
NSPhotoLibraryUsageDescription = "Used to select and save photos"
NSMicrophoneUsageDescription = "Used for audio recording in videos"
NSLocationWhenInUseUsageDescription = "Used to show nearby locations"
A useful prompt tip: tell Rork Max "whenever you add a feature that accesses hardware or user data, include the corresponding Info.plist privacy descriptions."
Pattern 2: Missing Entitlements for EAS Build
Push notifications, App Groups, Sign in with Apple, HealthKit, and similar capabilities require entries in your app's Entitlements file. The simulator often lets these slide; a real device doesn't.
Check your app.json EAS Build configuration:
{
"expo": {
"ios": {
"bundleIdentifier": "com.yourname.yourapp",
"entitlements": {
"aps-environment": "production",
"com.apple.developer.associated-domains": ["applinks:yourapp.com"]
}
}
}
}Pay particular attention to aps-environment: it should be development for TestFlight builds and production for App Store submissions. Getting this wrong is a common source of push notification failures on real devices.
Pattern 3: Third-Party Library Architecture Mismatch
When Rork Max pulls in native modules via npm packages, those modules must be compiled for arm64 to run on a real device. If they're not, you'll see errors like Library missing or image not found in your crash log.
# Check EAS Build logs for architecture errors
eas build --platform ios --profile preview --local
# Inspect the architecture of a built binary
lipo -archs YourApp.app/YourApp
# Should show: arm64The fix is usually straightforward: update the offending library to its latest version (most modern React Native libraries support arm64), or regenerate the feature with a more current library. When prompting Rork Max, specify "use libraries compatible with React Native 0.73 and later" to avoid outdated dependencies.
Pattern 4: Memory Pressure and Jetsam Kills
This one trips up developers who do their testing on a well-specced Mac but ship to users running older iPhones. Code that processes large images, runs ML models, or loads substantial datasets can work perfectly on the simulator while getting killed by iOS's memory manager on device.
If your crash log contains EXC_RESOURCE RESOURCE_TYPE_MEMORY or mentions jetsam, memory is your culprit.
// Problematic: loading all images into memory at once
let images = urls.map { UIImage(contentsOfFile: $0.path) } // Fine on simulator, risky on device
// Better: lazy loading
func loadImage(at url: URL) -> UIImage? {
// Load only when needed, release when done
return UIImage(contentsOfFile: url.path)
}When prompting Rork Max to handle images or data-heavy views, ask it to "use lazy loading and avoid holding large assets in memory." This encourages the use of LazyVStack, AsyncImage, and similar memory-friendly patterns.
Pattern 5: CoreML Model Compatibility Issues
Apps with on-device AI features using CoreML can behave differently on real hardware — sometimes crashing, sometimes just running far slower than expected. This usually comes down to the model not being configured to use the Neural Engine efficiently.
// Specify compute units explicitly when loading a CoreML model
let config = MLModelConfiguration()
config.computeUnits = .all // Automatically chooses between CPU, GPU, and Neural Engine
do {
let model = try MyMLModel(configuration: config)
// Use model
} catch {
// Always handle errors — CoreML can fail on specific hardware
print("Model loading failed: \(error.localizedDescription)")
}When generating CoreML-based features with Rork Max, include this instruction: "Add proper error handling and specify compute units when loading CoreML models."
Reading Crash Logs: Find the Root Cause Fast
The fastest path to diagnosing a device crash is reading the crash log. Don't skip this step — it usually points directly at the problem.
Via TestFlight: Crash reports are automatically collected in App Store Connect → TestFlight → Crashes tab.
Via Xcode Organizer: Connect your device, open Xcode, and go to Window → Organizer → Crashes.
The two fields to focus on:
Exception Type: EXC_BAD_ACCESS (SIGSEGV) ← Memory access violation
Exception Type: EXC_CRASH (SIGABRT) ← Assert or forced termination
Thread 0 Crashed:
0 libswiftCore.dylib 0x... swift_unknownObjectRetain ← Swift memory issue
1 YourApp 0x... ContentView.body.getter ← Your code, crashing here
Read the stack trace from top to bottom. Lines starting with your app's name (not system frameworks) are where your code is involved. That's where to start investigating.
Profiling Memory Usage with Xcode Instruments
When Pattern 4 (memory pressure) is suspected but not confirmed, Xcode Instruments gives you a real-time view of exactly what your app is consuming. Connect your iPhone, open Instruments from Xcode → Open Developer Tool → Instruments, and select the "Leaks" or "Allocations" template.
Run the app through the scenarios where it crashes. Watch for:
- Allocations growing without bound: if memory keeps climbing and never drops, something is holding references it should have released. In Rork Max-generated code, this often happens with closures capturing
selfstrongly.
// Memory leak pattern — closure captures self strongly
class ViewModel: ObservableObject {
var onComplete: (() -> Void)?
func startTask() {
// This captures self strongly, preventing deallocation
onComplete = {
self.doSomething() // ← retain cycle risk
}
}
// Fix: use [weak self]
func startTaskFixed() {
onComplete = { [weak self] in
self?.doSomething()
}
}
func doSomething() { /* ... */ }
}- Resident memory spiking before crash: if you see memory jump sharply and then the app terminates, that is jetsam killing the process. Look at which object type is consuming the most memory in the Allocations instrument.
When you find a leak or excessive allocation in Rork Max-generated code, the most efficient fix is to copy the problematic section back into Rork Max and ask it to "fix the retain cycle and ensure [weak self] is used in all closure captures."
Breaking the Simulator Dependency
The real fix for simulator-only testing is building a habit of putting your app on a real device early and often. Waiting until a feature is "finished" to test on device means you'll discover problems late, when they're expensive to fix.
A practical workflow with Rork Max: every time you generate a significant new feature, trigger an EAS Build and distribute via TestFlight before moving on. The overhead is small, and catching device-specific issues early saves significant debugging time later. See the Rork Max TestFlight Guide for the full distribution workflow.
If you're dealing with build-time errors (rather than runtime crashes), the Rork Max Build Error Complete Guide covers a broader range of compilation and configuration issues.
Quick Checklist When Your App Crashes on Device
Run through these checks first — they cover the majority of device-only crash scenarios:
- Are all required privacy usage descriptions present in Info.plist?
- Are all used capabilities (push notifications, Associated Domains, etc.) in your Entitlements?
- What does the crash log's
Exception Typesay? Use it to narrow down the cause. - Have you tested on an older device with limited memory?
- Are all third-party native libraries up to date and compatible with arm64?
Crashes that only appear on device always have a reason. The simulator is a useful shortcut, but it's not a substitute for real hardware testing. Start with the crash log, match the exception type to one of these patterns, and you'll have a fix in hand faster than you might expect.