I recently got an App Store rejection because the reviewer could reproduce a fatal crash during a camera/media flow.
The confusing part: I could not reproduce it locally in Debug or Release (from Flutter tool and from Xcode).
Then I checked Firebase Crashlytics and saw a crash report with a stack trace that looked unrelated to my app logic (mostly UIAccessibility frames). At first glance, it did not look like a normal Dart exception.
After correlating Crashlytics with Firebase Analytics breadcrumbs, I could reconstruct the exact reviewer flow.
Here is the truncated stacktrace in Firebase Crashlytics:
Crashed: com.apple.main-thread
0 Flutter 0x43a13c _$LT$core..char..ToLowercase$u20$as$u20$core..iter..traits..double_ended..DoubleEndedIterator$GT$::next_back::h8e46a25d72ae0e8d + 354584
1 UIAccessibility 0x7838 -[NSObject(AXPrivCategory) _accessibilityContainingParentForOrdering] + 200
2 UIAccessibility 0x76c4 _scrollParentForComparingGeometryOfView + 148
3 UIAccessibility 0x5888 -[NSObject _accessibilityCompareGeometry:] + 852
4 UIAccessibility 0x5510 -[NSObject(AXPrivCategory) accessibilityCompareGeometry:] + 52
5 CoreFoundation 0x24c8c __CFSimpleMergeSort + 100
6 CoreFoundation 0x24ab4 CFSortIndexes + 260
7 CoreFoundation 0x26900 -[NSMutableArray sortRange:options:usingComparator:] + 448
8 CoreFoundation 0xc70d4 -[NSMutableArray sortUsingSelector:] + 176
9 UIKit 0x16a1e4 -[UIViewAccessibility _accessibilityUserTestingSubviewsSorted:] + 332
10 UIKit 0x16a068 -[UIViewAccessibility automationElements] + 160
11 UIAccessibility 0x31894 -[NSObject(AXPrivCategory) _iosAccessibilityAttributeValue:] + 220
12 UIAccessibility 0x5d59c -[NSObject(UIAccessibilityAutomation) _accessibilityUserTestingSnapshotDescendantsWithAttributes:maxDepth:maxChildren:maxArrayCount:honorsModalViews:] + 924
13 UIAccessibility 0x5ebb4 -[NSObject(UIAccessibilityAutomation) _accessibilityUserTestingSnapshotWithOptions:] + 940
14 UIAccessibility 0x2e6c0 -[NSObject(AXPrivCategory) _iosAccessibilityAttributeValue:forParameter:] + 1420
15 UIKit 0x575e0 -[UIApplicationAccessibility _iosAccessibilityAttributeValue:forParameter:] + 1648
16 UIAccessibility 0xcb9c _copyParameterizedAttributeValueCallback + 364
17 AXRuntime 0xe268 ___AXXMIGCopyParameterizedAttributeValue_block_invoke + 64
18 AXRuntime 0xd9c0 _handleNonMainThreadCallback + 60
19 AXRuntime 0xe09c _AXXMIGCopyParameterizedAttributeValue + 480
20 AXRuntime 0x808c AXUIElementCopyParameterizedAttributeValueRecursive + 640
21 AXRuntime 0x7d74 AXUIElementCopyParameterizedAttributeValue + 96
22 XCTAutomationSupport 0x7f30 (Missing UUID f4602ce4c6873048a49e7e2d5cfde760)
23 XCTAutomationSupport 0x11be4 (Missing UUID f4602ce4c6873048a49e7e2d5cfde760)
24 XCTAutomationSupport 0xf7d4 (Missing UUID f4602ce4c6873048a49e7e2d5cfde760)
25 XCTAutomationSupport 0x2b8d0 (Missing UUID f4602ce4c6873048a49e7e2d5cfde760)
26 CoreFoundation 0x2d750 __CFRUNLOOP_IS_CALLING_OUT_TO_A_BLOCK__ + 28
27 CoreFoundation 0x2d41c __CFRunLoopDoBlocks + 396
28 CoreFoundation 0x2ee40 __CFRunLoopRun + 848
29 CoreFoundation 0x2e1d0 _CFRunLoopRunSpecificWithOptions + 532
30 GraphicsServices 0x1498 GSEventRunModal + 120
31 UIKitCore 0x1212c4 <redacted> + 796
32 UIKitCore 0x8c158 UIApplicationMain + 332
33 UIKitCore 0x298698 <redacted> + 528
34 Runner 0x6d48 main + 4337397064 (AppDelegate.swift:4337397064)
35 ??? 0x181f45c1c (Missing)
Additonal thing is it occured on iPhone 16 Pro / iOS 26.4.1, which is a high-end model device.
The crash flow I found
- Open a screen with image/video picker.
- Tap Camera as input source.
- Record a video and tap Use Video.
- Return to Flutter screen.
- App is terminated due to memory pressure (Jetsam/OOM).
Why this is easy to misread
When iOS kills your process because of memory pressure, the report can show system-level frames, and it may not look like "your code crashed".
In practice, this is still your app's memory profile problem, especially around camera/media handoff and preview decoding.
What fixed it for me
1) Limit capture payload early
For camera images via image_picker, do not request full-size assets unless really necessary.
Before:
final XFile? image = await _picker.pickImage(source: source);
After:
final XFile? image = await _picker.pickImage(
source: ImageSource.camera,
maxWidth: 1920,
maxHeight: 1920,
imageQuality: 85,
requestFullMetadata: false,
);
For videos, also constrain what you can (for example duration), then compress/transcode if needed before heavy processing.
2) Avoid full-resolution image decode in preview widgets
Before:
Image.file(
_selectedImage!,
height: 250,
width: double.infinity,
fit: BoxFit.cover,
)
After:
Image(
image: ResizeImage(FileImage(_selectedImage!), width: 1080),
height: 250,
width: double.infinity,
fit: BoxFit.cover,
filterQuality: FilterQuality.low,
)
This significantly reduces decode memory while still looking good enough for preview.
3) Evict old preview image when replacing media
If users re-pick media multiple times, stale decoded images can keep memory high.
import 'dart:async';
Future<void> _evictImagePreview(File? imageFile) async {
if (imageFile == null) {
return;
}
await FileImage(imageFile).evict();
}
Future<void> _pickImage(ImageSource source) async {
final File? previousImage = _selectedImage;
final XFile? image = await _picker.pickImage(
source: source,
maxWidth: 1920,
maxHeight: 1920,
imageQuality: 85,
requestFullMetadata: false,
);
if (image != null) {
await _evictImagePreview(previousImage);
setState(() {
_selectedImage = File(image.path);
});
}
}
@override
void dispose() {
unawaited(_evictImagePreview(_selectedImage));
super.dispose();
}
4) Handle memory-pressure callback globally
Listen for memory pressure and clear caches proactively.
class AppState extends State<App> with WidgetsBindingObserver {
@override
void initState() {
super.initState();
WidgetsBinding.instance.addObserver(this);
}
@override
void dispose() {
WidgetsBinding.instance.removeObserver(this);
super.dispose();
}
@override
void didHaveMemoryPressure() {
final ImageCache imageCache = PaintingBinding.instance.imageCache;
imageCache.clear();
imageCache.clearLiveImages();
super.didHaveMemoryPressure();
}
}
Practical checklist before submitting to App Store
- Test media capture with large/long assets in Release mode.
- Repeat pick/retake/cancel cycles to detect memory growth.
- Verify image/video preview widgets do not decode full-resolution files.
- Keep Crashlytics + Analytics breadcrumbs enabled for production diagnostics.
- Validate on at least one lower-memory iPhone model, not only high-end devices.
Built from real experience: CompactKit
I wrote this guide from my own release workflow while building CompactKit. If you want to see a real app shipped, check it out here 👉 compactkit.cc
Evaluative Perspective
If a crash only appears in App Store review but not locally, treat it as a real production condition, not a reviewer anomaly.
Telemetry plus memory-safe media handling usually reveals the root cause quickly.
Happy debugging!