Most SwiftUI apps don’t crash from bugs.
They crash because the OS kills them.
Symptoms:
- random terminations
- image-heavy screens freezing
- background tasks killed
- “works on my phone”
- sudden reloads when switching apps
This post shows how to design memory-aware SwiftUI architecture that:
- adapts under pressure
- releases resources safely
- avoids OS termination
- keeps UX stable
This is about survival, not just leaks.
🧠 The Core Principle
Memory is not infinite, and the OS is not forgiving.
Your app must adapt when memory is constrained.
🚨 1. Understand Memory Pressure Signals
iOS sends warnings before killing your app.
Listen:
final class MemoryMonitor {
static let shared = MemoryMonitor()
init() {
NotificationCenter.default.addObserver(
forName: UIApplication.didReceiveMemoryWarningNotification,
object: nil,
queue: .main
) { _ in
self.handlePressure()
}
}
func handlePressure() {
// trigger cleanup
}
}
This should live in AppContainer.
🧱 2. Cache with Eviction
Never cache unbounded.
final class ImageCache {
private let cache = NSCache<NSString, UIImage>()
init() {
cache.countLimit = 100
cache.totalCostLimit = 50 * 1024 * 1024 // 50 MB
}
}
NSCache automatically evicts under pressure.
🧬 3. Release Heavy Resources on Background
func sceneDidEnterBackground() {
imageCache.clear()
videoManager.release()
temporaryStore.clear()
}
Background = prepare for kill.
🧠 4. Lazy Loading Is Mandatory
Bad:
let images = loadAllImages()
Good:
LazyVStack {
ForEach(items) { item in
Row(item)
}
}
Only render what’s visible.
🧭 5. Downsample Images
Never load full-resolution images for UI.
Use downsampling:
CGImageSourceCreateThumbnailAtIndex(...)
Even 12MP images should render as ~1MP for the screen.
🧪 6. Detect Memory Spikes
Use Instruments → Allocations.
Watch for:
- scroll spikes
- repeated image allocations
- task churn
- video buffers
- unbounded arrays
Memory growth should flatten after navigation.
🧠 7. Background Tasks Must Be Cancelable
let task = Task { await loadHeavyData() }
task.cancel()
Never let work continue when UI disappears.
⚠️ 8. Avoid Retaining ViewModels Forever
Bad:
@StateObject var vm = SharedVM.shared
This prevents cleanup.
Good:
- feature-scoped ownership
- release on exit
❌ 9. Common Memory Anti-Patterns
Avoid:
- storing images in AppState
- caching without limits
- holding onto large arrays
- loading all data eagerly
- not responding to memory warnings
- ignoring background transitions
These cause OS kills.
🧠 Mental Model
Think:
Memory rises
→ Pressure warning
→ Cleanup
→ Release
→ Survive
Not:
“It will probably be fine.”
🚀 Final Thoughts
Memory pressure is not an edge case.
It is the default on real devices.
Apps that adapt:
- stay alive
- feel fast
- handle large data
- survive multitasking
Apps that don’t:
- get killed
- lose user trust
- feel unstable
SwiftUI doesn’t manage memory for you.
Architecture does.
Top comments (0)