Ana Sayfa / Blog / Background processing: BGTaskScheduler ile gerçek kullanımlar

Background processing: BGTaskScheduler ile gerçek kullanımlar

iOS'ta app arka plandayken iş yapmak sınırlı ama mümkün. BGTaskScheduler, BGAppRefreshTask, BGProcessingTask ile üretim uygulamalarında yaptıklarım.

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ık

Kullanı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.

Bu konuda bir projeniz mi var?

Kısa bir özet bırakın, 24 saat içinde size dönüş yapayım.

İletişime Geç