Background Task — mechanizm iOS, który pozwala aplikacji dokończyć rozpoczętą operację po przejściu w tryb tła. System przydziela ograniczony czas (do 30 sekund) na wykonanie zadania, po czym aplikacja jest przymusowo wstrzymywana. Według Apple, 2026, użycie expiration handler jest obowiązkowym wymogiem poprawnego zakończenia zadania w tle.
Najważniejsze
Background Task — to programowy mechanizm iOS, pozwalający aplikacji wydłużyć czas wykonywania po przejściu w tryb tła. Gdy użytkownik minimalizuje aplikację, system wywołuje UIApplicationDelegate.applicationDidEnterBackground. Jeśli w tym momencie wykonywana jest krytyczna operacja (zapisywanie danych, wysyłanie żądania), aplikacja może poprosić o dodatkowy czas przez beginBackgroundTask.
Bez użycia Background Task aplikacja jest przymusowo wstrzymywana po 3–5 sekundach od przejścia w tło. Wszystkie niezakończone operacje są przerywane: dane nie są zapisywane, żądania sieciowe są urywane, stany są tracone. Background Task daje aplikacji do 30 sekund na poprawne dokończenie tych operacji.
beginBackgroundTask pojawił się w iOS 4.0 — pierwszej wersji obsługującej wielozadaniowość. Przed iOS 4 aplikacja była całkowicie zamykana po naciśnięciu przycisku Home. Od iOS 7 pojawiły się Background Fetch i URLSession background configuration. Począwszy od iOS 13, Apple zaleca BGTaskScheduler dla nowych projektów, ale beginBackgroundTask pozostaje aktualny dla krótkoterminowych operacji.
beginBackgroundTask — metoda UIApplication, która rejestruje zadanie i zwraca unikalny identyfikator UIBackgroundTaskIdentifier. System wydłuża licznik czasu wykonywania w tle. Po upływie czasu wywoływany jest expiration handler, po czym aplikacja musi wywołać endBackgroundTask(identifier:) dla poprawnego zakończenia.
Krok 1 — aplikacja otrzymuje powiadomienie didEnterBackground. Krok 2 — wywoływane jest beginBackgroundTask(expirationHandler:). Krok 3 — wykonywana jest krytyczna operacja (zapisywanie, żądanie sieciowe). Krok 4 — po zakończeniu wywoływane jest endBackgroundTask(identifier:). Jeśli operacja nie zakończy się w ciągu 30 sekund — system wywołuje expiration handler, a aplikacja musi natychmiast zakończyć zadanie.
var backgroundTaskID: UIBackgroundTaskIdentifier = .invalid
func startBackgroundTask() {
backgroundTaskID = UIApplication.shared.beginBackgroundTask {
// Expiration handler
UIApplication.shared.endBackgroundTask(backgroundTaskID)
backgroundTaskID = .invalid
}
}
func finishBackgroundTask() {
UIApplication.shared.endBackgroundTask(backgroundTaskID)
backgroundTaskID = .invalid
}
Właściwość backgroundTimeRemaining zwraca liczbę sekund pozostałych do przymusowego zakończenia zadania w tle. Wartość zmniejsza się w czasie rzeczywistym. Jeśli aplikacja nie jest w tle — zwraca DBL_MAX. Użyj tej właściwości do dostosowania zachowania: gdy pozostało mniej niż 5 sekund, przerywaj długotrwałe operacje i zapisuj postęp.
Aplikacja może zarejestrować kilka Background Task jednocześnie. Każde zadanie otrzymuje własny identyfikator. Łączny czas wykonywania sumuje się — jeśli zarejestrowano 3 zadania, aplikacja może otrzymać do 90 sekund. Jednak system może zakończyć aplikację wcześniej, jeśli zasoby są wyczerpane lub przekroczono limit baterii.
Expiration handler — to blok kodu wywoływany przez system po wyczerpaniu przydzielonego czasu na zadanie w tle. Obecność expiration handler jest obowiązkowym wymogiem Apple. Bez niego aplikacja może zostać przymusowo zakończona przez system, a dane mogą zostać utracone.
Expiration handler powinien wykonać minimalne działania w celu zapisania stanu aplikacji: zapisać bieżące dane w trwałym magazynie, wywołać endBackgroundTask z odpowiednim identyfikatorem i ustawić identyfikator na .invalid. Wewnątrz expiration handler zabronione jest uruchamianie nowych długotrwałych operacji — czas wykonania jest ograniczony do 1–2 sekund.
func handleExpiration() {
// Zapisujemy postęp wykonania
saveProgressToUserDefaults()
// Anulujemy aktywne żądania sieciowe
currentTask?.cancel()
// Kończymy Background Task
UIApplication.shared.endBackgroundTask(backgroundTaskID)
backgroundTaskID = .invalid
}
// Rejestracja z expiration handler
backgroundTaskID = UIApplication.shared.beginBackgroundTask(
withName: "SaveDocument",
expirationHandler: handleExpiration
)
Najczęstszym błędem jest niewywołanie endBackgroundTask wewnątrz expiration handler. W takim przypadku system nadal uważa zadanie za aktywne, aplikacja nie przechodzi w stan Suspended, a bateria się rozładowuje. Drugi błąd to uruchamianie długotrwałych operacji wewnątrz expiration handler. System może zakończyć aplikację przed ich ukończeniem, a dane zostaną utracone.
W expiration handler czas wykonania jest krytycznie krótki — zazwyczaj mniej niż 1 sekunda. Dlatego wewnątrz handler nie można wykonywać: złożonych obliczeń, żądań sieciowych, zapisu dużych ilości danych w CoreData. Tylko atomowe operacje: zapis jednego klucza w UserDefaults, ustawienie flagi stanu, wywołanie endBackgroundTask.
Rozważmy pełną implementację Background Task do zapisu dokumentu podczas przejścia aplikacji w tło. Przykład obejmuje rejestrację zadania, wykonanie operacji z kontrolą pozostałego czasu i poprawne zakończenie przez endBackgroundTask.
Podczas przejścia w tło aplikacja inicjuje zapis dużego dokumentu. Background Task daje do 30 sekund na operację. Jeśli czas wygaśnie — expiration handler zapisuje pośrednie wyniki. Po zakończeniu zapisu wywoływane jest endBackgroundTask w celu zwolnienia zasobów.
class DocumentManager {
private var backgroundTaskID: UIBackgroundTaskIdentifier = .invalid
func saveDocumentInBackground(document: Document) {
backgroundTaskID = UIApplication.shared.beginBackgroundTask {
self.saveProgress(document)
self.endBackgroundTask()
}
DispatchQueue.global().async {
document.save()
self.endBackgroundTask()
}
}
private func endBackgroundTask() {
UIApplication.shared.endBackgroundTask(backgroundTaskID)
backgroundTaskID = .invalid
}
}
Żądania sieciowe w tle wymagają szczególnej uwagi — expiration handler może przerwać żądanie przed otrzymaniem odpowiedzi. Użyj URLSession z dataTask i sprawdzaj backgroundTimeRemaining przed wysłaniem. Jeśli pozostało mniej niż 5 sekund — odłóż wysłanie do następnego przebudzenia aplikacji.
func sendCriticalData(data: Data) {
backgroundTaskID = UIApplication.shared.beginBackgroundTask {
// Zapisujemy dane do ponownego wysłania
saveForRetry(data)
self.endBackgroundTask()
}
let task = URLSession.shared.dataTask(with: request) { _, _, error in
if error != nil {
saveForRetry(data)
}
self.endBackgroundTask()
}
task.resume()
}
W przypadku długotrwałych operacji śledź UIApplication.shared.backgroundTimeRemaining. Jeśli wartość spadnie poniżej progu (10 sekund), przerwij bieżącą operację i uruchom zapisywanie pośrednich wyników. Pozwala to poprawnie zakończyć pracę przed przymusowym wywołaniem expiration handler.
Background Task — potężny mechanizm, ale jego nieprawidłowe użycie prowadzi do problemów z baterią i wydajnością. Przestrzeganie zaleceń Apple i sprawdzonych praktyk społeczności pomoże uniknąć typowych błędów i zapewnić stabilne działanie aplikacji.
Kończ Background Task tak szybko, jak to możliwe. Każda sekunda wykonywania w tle zużywa energię baterii. Optymalny czas — poniżej 5 sekund. Jeśli operacja trwa dłużej — rozważ użycie BGTaskScheduler lub Background Modes. Nie przedłużaj zadania sztucznie przez wywołania beginBackgroundTask bez rzeczywistej potrzeby.
Przechowuj UIBackgroundTaskIdentifier jako właściwość klasy lub struktury. Nigdy nie przekazuj go jako zmiennej globalnej — prowadzi to do konfliktów przy wielu zadaniach. Ustawiaj identyfikator na .invalid po wywołaniu endBackgroundTask, aby zapobiec podwójnemu zakończeniu.
W Xcode Simulator przełącz aplikację w tło przez przycisk Home (Command + Shift + H). Użyj Debug — Simulate Background Fetch do testowania przebudzenia w tle. Aby symulować wygaśnięcie czasu, ustaw zmienną środowiskową BACKGROUND_TASK_SIMULATE_EXPIRATION = YES — expiration handler zostanie wywołany po 5 sekundach.
// Sprawdzenie pozostałego czasu przed rozpoczęciem operacji
let remaining = UIApplication.shared.backgroundTimeRemaining
guard remaining > 10.0 else {
// Niewystarczająco czasu — odłóż zadanie
scheduleForNextLaunch()
return
}
// Wykonanie operacji z kontrolą czasu
backgroundTaskID = UIApplication.shared.beginBackgroundTask {
saveProgress()
self.endBackgroundTask()
}
Expiration handler przechwytuje self — może to utworzyć retain cycle, jeśli backgroundTaskID jest przechowywany jako właściwość tego samego obiektu. Użyj [weak self] w domknięciu lub przechowuj identyfikator osobno. Retain cycle prowadzi do wycieku pamięci — obiekt nie zostanie zwolniony do czasu zakończenia aplikacji.
Wraz z wydaniem iOS 13 Apple przedstawiło BGTaskScheduler — nowoczesny zamiennik starego API Background Task. Oba mechanizmy rozwiązują podobne zadania, ale BGTaskScheduler zapewnia większą kontrolę i elastyczność. Zrozumienie różnic pomoże wybrać odpowiednie narzędzie dla konkretnego scenariusza.
| Cecha | Background Task | BGTaskScheduler |
|---|---|---|
| Wersja iOS | 4.0+ | 13.0+ |
| Maks. czas | 30 sekund | 30 s / 10 min (processing) |
| Inicjacja | didEnterBackground | Planowanie + system |
| Gwarancja wykonania | Tylko przy przejściu w tło | W dowolnym dogodnym dla systemu czasie |
| Expiration handler | Tak, obowiązkowy | Tak, przez task.expirationHandler |
| Internet | Wymagany przy uruchomieniu | Dostępny przez requiresNetworkConnectivity |
| Ładowanie | Niewymagane | Opcjonalne dla processing |
beginBackgroundTask nadaje się do krótkoterminowych operacji, które należy wykonać natychmiast po przejściu w tło: zapis stanu, dokończenie żądania sieciowego, buforowanie danych. API jest proste i nie wymaga konfiguracji Info.plist ani rejestracji identyfikatorów. Idealne dla operacji trwających mniej niż 10 sekund.
BGTaskScheduler nadaje się do zadań, które mogą być wykonane w dowolnym dogodnym czasie: okresowa synchronizacja, czyszczenie pamięci podręcznej, aktualizacja widżetów. System sam wybiera optymalny czas, uwzględniając zachowanie użytkownika i stan baterii. W przypadku długotrwałych operacji (do 10 minut) użyj BGProcessingTask.
Oba API można używać w jednej aplikacji. Background Task — do natychmiastowych operacji przy przejściu w tło (zapis szkicu). BGTaskScheduler — do planowanych aktualizacji (synchronizacja co 6 godzin). Podział odpowiedzialności pozwala zagwarantować poprawne zapisywanie danych i energooszczędną obsługę w tle.
Często zadawane pytania
Tak, beginBackgroundTask można wywołać w dowolnym momencie działania aplikacji. Jednak systemowe przydzielenie czasu rozpocznie się dopiero po przejściu w tło. Jeśli aplikacja jest na pierwszym planie, wywołanie beginBackgroundTask nie ma efektu — backgroundTimeRemaining zwraca DBL_MAX, a zadanie zostanie aktywowane po przejściu w tło.
Jeśli nie wywołasz endBackgroundTask, system nadal będzie uważać aplikację za aktywną w tle. Po 30 sekundach zostanie wywołany expiration handler, ale jeśli w nim również nie ma endBackgroundTask — aplikacja pozostanie w pamięci, zużywając baterię. W iOS 13+ system przymusowo zamyka taką aplikację po 3 minutach.
Standardowy czas to 30 sekund. Wydłużyć go można tylko przez Background Modes: Audio (odtwarzanie), Location (geolokalizacja), Bluetooth (BLE). Lub przez BGProcessingTask (iOS 13+) — do 10 minut przy ładowaniu i Wi-Fi. beginBackgroundTask sam w sobie nie zapewnia sposobu na zwiększenie limitu.
Tak, iPadOS w pełni obsługuje beginBackgroundTask z tymi samymi ograniczeniami co iOS. Na iPadzie z Stage Manager aplikacja może pozostać w pamięci dłużej — system rzadziej wstrzymuje aplikacje ze względu na większą ilość RAM. Jednak limit 30 sekund na zadanie w tle pozostaje.
Podłącz urządzenie do Xcode, uruchom aplikację, zminimalizuj ją — w konsoli pojawią się logi systemowe. Użyj sysdiagnose do zebrania szczegółowych logów: wymuś sysdiagnose z urządzenia (Volume Up + Down + Power). W Xcode Debug Navigator śledź aktywność zadań w tle.
Podsumowanie
Opracujemy aplikację mobilną pod klucz
IT Sectr tworzy aplikacje na iOS i Androida dla startupów i firm od 2017 roku. Doradzimy Ci i zaproponujemy najlepsze rozwiązanie.
Przeczytaj również