I. Aim
I wanted my app, iQuake to run quietly and efficiently in the background and alert users if there was an earthquake nearby. No ads, no nonsense etc. Just helpful, timely alerts pulled from official feeds. I figured SwiftUI's .backgroundTask()
and BGAppRefreshTaskRequest
would make this clean and elegant.

II. Starting Off
First things first — the fetch and filter logic. Here's what needed to work:
- Two feeds, two formats: USGS gave me clean GeoJSON. TMD? Classic XML. Enter
XMLParserDelegate
. - Run them in parallel: Swift concurrency with
async let
made this snappy. - Filter the noise: Only alert if the quake is nearby, strong enough, and not during user-defined "quiet hours."
- No double alerts: Tracked quake IDs in a
Set<String>
stored inUserDefaults
.
III. Silence
I had everything in place. Background mode set. Plist updated. Scene phase triggers ready. Ran the app… and then, absolutely nothing. No handler. No log messages. Simulator's "Simulate Background Fetch"? Did less than nothing. I checked everything — identifiers, settings, permissions — and still got the digital equivalent of a shrug.
IV. Crashes
Then came the crash. SIGABRT, thanks to an assertion failure buried in BGTaskScheduler
. Stack trace showed it was SwiftUI’s internal registration system acting up. Time to ditch the shiny tools.
- Hooked up
@UIApplicationDelegateAdaptor
. - Registered the task in
AppDelegate
usingBGTaskScheduler.register
. - Moved the logic into AppDelegate with async
Task { ... }
and adefer
block to mark completion.
No more crashes, finally. But still no background fetch. This time, though, I had something new: dasd
logs. They showed the task being submitted… then declined. DecisionToRun: 0. Apparently the system didn't think the app deserved a background wake-up yet. iOS was gatekeeping me.

V. Progress
Using LLDB I forced a launch: e -l objc -- (void)[[BGTaskScheduler sharedScheduler] _simulateLaunchForTaskWithIdentifier:@"..."]
. It ran! The handler executed. The logic worked. That was huge. But oddly, my iPad wasn’t showing notifications.
Turns out I forgot to request permission with: UNUserNotificationCenter.current().requestAuthorization(...)
.
Once that was added, notifications showed up properly across devices. Classic oversight in the middle of a messy bug hunt.
VI. Lessons Learned
- iOS doesn’t promise background execution. It’s a hint, not a guarantee.
- Console logs matter. Especially for
dasd
and system-level scheduling behavior. - Simulators lie. You need real devices, plugged in, idle.
- Get your Info.plist exactly right. Case, spacing, target membership — it all matters.
- Don’t forget permissions. Notifications, location — all explicit.
- Track what you’ve alerted on. Don’t annoy your users.
- AppDelegate still has its place. Sometimes you just need more control.

This one took longer than it should have, but that happens me quite often. And seeing that first real background-triggered notification hit the lock screen? Worth it.