iOS’ta app arka plana geçince işlem süresi çok sınırlı. Kullanıcının battery’sini korumak için Apple agresif policy uyguluyor. Ama bazı işlevler için arka plan iş şart: data sync, cache pre-warm, critical notification.
iOS 13 ile gelen BGTaskScheduler önceki BackgroundAppRefresh API’sinden çok daha güçlü. 4 uygulamamda kullandım, pattern’lar oturdu.
BGTaskScheduler nedir
iOS sizin app’inize “uygun gördüğü zaman” arka plan zamanı veriyor. Uygun zaman:
- Kullanıcı uyuyorken (gece)
- Wifi + charging iken
- iPhone kullanılmıyorken
- Sistem priority’si düştüğünde
“5 dakika sonra koş” diyemiyorsunuz. “En erken 30 dakika sonra uygun olduğunda koş” diyorsunuz. Sistem karar veriyor.
2 task type
BGAppRefreshTask: kısa süreli (30 saniye), sık. Feed refresh, notification count update gibi.
BGProcessingTask: uzun süreli (birkaç dakikaya kadar), seyrek. ML model download, database maintenance, büyük data sync.
İkisinin de farklı scheduling constraint’leri var.
Temel setup
1. Info.plist’e permission:
<key>BGTaskSchedulerPermittedIdentifiers</key>
<array>
<string>com.example.app.refresh</string>
<string>com.example.app.cleanup</string>
</array>2. AppDelegate’te register:
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: ...) -> Bool {
BGTaskScheduler.shared.register(
forTaskWithIdentifier: "com.example.app.refresh",
using: nil
) { task in
self.handleAppRefresh(task: task as! BGAppRefreshTask)
}
BGTaskScheduler.shared.register(
forTaskWithIdentifier: "com.example.app.cleanup",
using: nil
) { task in
self.handleCleanup(task: task as! BGProcessingTask)
}
return true
}3. Schedule:
func scheduleAppRefresh() {
let request = BGAppRefreshTaskRequest(identifier: "com.example.app.refresh")
request.earliestBeginDate = Date(timeIntervalSinceNow: 15 * 60) // 15 dakika sonra
do {
try BGTaskScheduler.shared.submit(request)
} catch {
print("Failed to schedule: \(error)")
}
}scheduleAppRefresh() uygun zamanda (genelde app foreground’dan arka plana geçerken) çağrılmalı.
Handle: iş yap + reschedule
func handleAppRefresh(task: BGAppRefreshTask) {
// Sonraki rescheduling'i hemen yap
scheduleAppRefresh()
task.expirationHandler = {
// Task'a zaman dolunca cleanup
self.cancelCurrentOperation()
}
Task {
let success = await refreshData()
task.setTaskCompleted(success: success)
}
}Kritik:
– setTaskCompleted(success:) mutlaka çağrılmalı, yoksa sistem app’inizi throttle eder
– Reschedule hemen yapılmalı, yoksa bir kere çağrı sonrası hiç çağrılmıyor
– expirationHandler timeout durumunda graceful cleanup için
Gerçek kullanım 1: fitness app – data sync
Bir fitness tracking app’inde health data’sı (adım, kalori) HealthKit’ten çekip kendi backend’e sync ediliyor. Kullanıcı app’i açmadan da son 24 saat’in verisi server’da olmalı (haftalık/aylık agregat için).
Pattern:
– BGAppRefreshTask her 4 saatte bir tetikleniyor (sistem ne zaman uygun görürse)
– HealthKit’ten son 4 saat’in veri delta’sı
– Local DB’ye kaydet (offline-first)
– Backend’e push (wifi varsa)
– Task tamamlandı
Task süresi 30 saniye yetiyor çünkü delta sync.
Sonuç: kullanıcılar app’i açmasa da haftalık özet e-postalarında doğru veri var, streak bozulmuyor.
Gerçek kullanım 2: e-commerce – cart pre-load
Bir e-ticaret app’inde kullanıcı tekrar app açtığında hızlı deneyim için:
- BGAppRefreshTask ile latest cart state backend’den fetch
- Featured products’ı prefetch, local cache’e koy
- Product image’ları önceden download
- Kullanıcı app’i açtığında instant load
Bu pattern app’i “her zaman güncel” hissetiyor. Kullanıcı açınca loading spinner görmüyor.
Ancak dikkat: kullanıcının data plan’i kısıtlıysa, WiFi yokken büyük image download yapmak kötü. URLSessionConfiguration.waitsForConnectivity = true veya wifi-only download.
Gerçek kullanım 3: finance app – database vacuum
Bir harcama takip app’inde uzun süre sonra DB file büyüyor, query yavaşlıyor. BGProcessingTask ile:
- Haftada bir DB vacuum
- Old records archive (30 gün öncesi summary-only)
- Index rebuild
- Sadece charging + wifi iken çalışsın
BGProcessingTaskRequest‘in constraint’leri:
let request = BGProcessingTaskRequest(identifier: "com.example.app.db-maintenance")
request.requiresNetworkConnectivity = false
request.requiresExternalPower = true // sadece şarjdayken
request.earliestBeginDate = Date(timeIntervalSinceNow: 7 * 24 * 60 * 60) // haftalıkKullanıcı fark etmeden DB performansı maintain ediliyor.
Gerçek kullanım 4: messaging app – offline message pull
Bir messaging app’inde push notification gelmiş ama app kapalı. Kullanıcı app’i açınca son mesajlar görünsün.
- BGAppRefreshTask ile son 50 mesajı backend’den çek
- Local DB’ye kaydet
- Unread count güncelle
- Badge number güncelle
Bu background refresh push-based notification’a ek güvence katmanı. Push kaybolursa (rare ama olur) background refresh ile recover.
Test etmenin zorluğu
BGTaskScheduler debugging pain noktası. Simulator’da manual trigger için Xcode debug:
(lldb) e -l objc -- (void)[[BGTaskScheduler sharedScheduler] _simulateLaunchForTaskWithIdentifier:@"com.example.app.refresh"]Production’da “task çalışıyor mu?” görmek için logging şart:
- Task register edildi log
- Task schedule edildi log
- Task başladı log (timestamp)
- Task bitti log (süre, success/fail)
Log’ları Firebase Analytics veya kendi backend’inize push edin, dashboard’da “task run frequency per user” görebiliyorsunuz.
Ne zaman tetikleniyor, ne zaman değil
iOS sizin task’ınızı ne zaman çağıracağı belirsiz. Yaygın pattern:
- Kullanıcı aktif değilse (ekran kapalı, telefon masa üstünde)
- Wifi varsa öncelik
- Battery yeterli ise
- Cihaz idle işlem gücüne sahip ise
- Kullanıcı app’i sık açıyorsa (sık açmıyorsa sistem de az tetikliyor)
App’iniz haftada 1 açılan bir utility ise background refresh nadiren tetiklenir. Apple app’i kullanılmıyor görerek az zaman veriyor.
Daily active user’lar için tipik frequency: günde 3-8 kere tetiklenme.
Kullanıcı gizliliği
Arka plan iş enerji harcıyor. Gereksiz iş yapmayın.
- Background sync’te sadece delta’yı al, tüm data’yı değil
- Background’da network request’leri minimize et
- Heavy computation (ML inference) sadece BGProcessingTask’ta ve power constraint’i ile
- Kullanıcı background app refresh’i kapattıysa app sessizce feature’ı degrade etmeli
App Store submission considerations
Apple arka plan işinizi review’da sorguluyor. Background task identifier için tanımlı amacınız olmalı. Sadece tracking veya ad için background usage reddediliyor.
Legitimate amaç:
– Content freshness
– Offline-first data sync
– Maintenance / cleanup
– Proactive UX (instant load)
Son tavsiye
BGTaskScheduler iOS’un cimri arka plan modelinde size verilen lever. Ama akıllı kullanın. Gereksiz background iş sistem tarafından throttle ediliyor, kullanıcı bataryası ölüyor, 1-star review geliyor.
Pattern:
– Küçük delta’lar
– Idempotent operations
– Graceful expiration
– Constraint’leri respect et
– Logging ile ölç
Doğru yapıldığında kullanıcı “bu app her zaman güncel” hissediyor ama battery impact minimal.