Background Task — ο μηχανισμός iOS που επιτρέπει στην εφαρμογή να ολοκληρώσει μια λειτουργία που ξεκίνησε μετά τη μετάβαση σε λειτουργία παρασκηνίου. Το σύστημα διαθέτει περιορισμένο χρόνο (έως 30 δευτερόλεπτα) για την εκτέλεση της εργασίας, μετά τον οποίο η εφαρμογή τίθεται αναγκαστικά σε αναστολή. Σύμφωνα με την Apple, 2026, η χρήση του expiration handler είναι υποχρεωτική απαίτηση για τη σωστή ολοκλήρωση μιας εργασίας παρασκηνίου.
Κύρια σημεία
Το Background Task είναι ένας μηχανισμός λογισμικού iOS που επιτρέπει στην εφαρμογή να παρατείνει τον χρόνο εκτέλεσης μετά τη μετάβαση σε λειτουργία παρασκηνίου. Όταν ο χρήστης ελαχιστοποιεί την εφαρμογή, το σύστημα καλεί το UIApplicationDelegate.applicationDidEnterBackground. Εάν εκείνη τη στιγμή εκτελείται μια κρίσιμη λειτουργία (αποθήκευση δεδομένων, αποστολή αιτήματος), η εφαρμογή μπορεί να ζητήσει επιπλέον χρόνο μέσω του beginBackgroundTask.
Χωρίς τη χρήση Background Task, η εφαρμογή τίθεται αναγκαστικά σε αναστολή 3-5 δευτερόλεπτα μετά τη μετάβαση στο παρασκήνιο. Όλες οι ημιτελείς λειτουργίες διακόπτονται: τα δεδομένα δεν αποθηκεύονται, τα αιτήματα δικτύου διακόπτονται, οι καταστάσεις χάνονται. Το Background Task δίνει στην εφαρμογή έως 30 δευτερόλεπτα για τη σωστή ολοκλήρωση αυτών των λειτουργιών.
Το beginBackgroundTask εμφανίστηκε στο iOS 4.0 — την πρώτη έκδοση που υποστήριζε πολυδιεργασία. Πριν από το iOS 4, η εφαρμογή έκλεινε εντελώς όταν πατιόταν το κουμπί Home. Από το iOS 7, εμφανίστηκαν τα Background Fetch και URLSession background configuration. Από το iOS 13, η Apple συνιστά το BGTaskScheduler για νέα έργα, αλλά το beginBackgroundTask παραμένει σχετικό για βραχυπρόθεσμες λειτουργίες.
Το beginBackgroundTask είναι μια μέθοδος του UIApplication που καταχωρίζει μια εργασία και επιστρέφει ένα μοναδικό αναγνωριστικό UIBackgroundTaskIdentifier. Το σύστημα επεκτείνει τον χρονοδιακόπτη εκτέλεσης παρασκηνίου. Όταν λήξει ο χρόνος, καλείται ο expiration handler, μετά τον οποίο η εφαρμογή πρέπει να καλέσει το endBackgroundTask(identifier:) για σωστή ολοκλήρωση.
Βήμα 1 — η εφαρμογή λαμβάνει την ειδοποίηση didEnterBackground. Βήμα 2 — καλείται το beginBackgroundTask(expirationHandler:). Βήμα 3 — εκτελείται η κρίσιμη λειτουργία (αποθήκευση, αίτημα δικτύου). Βήμα 4 — με την ολοκλήρωση, καλείται το endBackgroundTask(identifier:). Εάν η λειτουργία δεν ολοκληρωθεί εντός 30 δευτερολέπτων — το σύστημα καλεί τον expiration handler και η εφαρμογή πρέπει να τερματίσει αμέσως την εργασία.
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
}
Η ιδιότητα backgroundTimeRemaining επιστρέφει τον αριθμό των δευτερολέπτων που απομένουν έως την αναγκαστική λήξη της εργασίας παρασκηνίου. Η τιμή μειώνεται σε πραγματικό χρόνο. Εάν η εφαρμογή δεν βρίσκεται στο παρασκήνιο — επιστρέφει DBL_MAX. Χρησιμοποιήστε αυτήν την ιδιότητα για να προσαρμόσετε τη συμπεριφορά: όταν απομένουν λιγότερα από 5 δευτερόλεπτα, διακόψτε τις μεγάλες λειτουργίες και αποθηκεύστε την πρόοδο.
Η εφαρμογή μπορεί να καταχωρίσει πολλές Background Task ταυτόχρονα. Κάθε εργασία λαμβάνει το δικό της αναγνωριστικό. Ο συνολικός χρόνος εκτέλεσης αθροίζεται — εάν καταχωριστούν 3 εργασίες, η εφαρμογή μπορεί να λάβει έως 90 δευτερόλεπτα. Ωστόσο, το σύστημα μπορεί να τερματίσει την εφαρμογή νωρίτερα εάν οι πόροι εξαντληθούν ή ξεπεραστεί το όριο μπαταρίας.
Ο Expiration handler είναι ένα μπλοκ κώδικα που καλείται από το σύστημα όταν λήξει ο διατεθειμένος χρόνος για την εργασία παρασκηνίου. Η ύπαρξη expiration handler είναι υποχρεωτική απαίτηση της Apple. Χωρίς αυτόν, η εφαρμογή μπορεί να τερματιστεί αναγκαστικά από το σύστημα και τα δεδομένα μπορεί να χαθούν.
Ο expiration handler πρέπει να εκτελέσει ελάχιστες ενέργειες για την αποθήκευση της κατάστασης της εφαρμογής: να αποθηκεύσει τα τρέχοντα δεδομένα σε μόνιμη αποθήκευση, να καλέσει το endBackgroundTask με το αντίστοιχο αναγνωριστικό και να ορίσει το αναγνωριστικό σε .invalid. Μέσα στον expiration handler απαγορεύεται η έναρξη νέων μεγάλων λειτουργιών — ο χρόνος εκτέλεσης περιορίζεται σε 1-2 δευτερόλεπτα.
func handleExpiration() {
// Αποθηκεύουμε την πρόοδο εκτέλεσης
saveProgressToUserDefaults()
// Ακυρώνουμε ενεργά αιτήματα δικτύου
currentTask?.cancel()
// Ολοκληρώνουμε το Background Task
UIApplication.shared.endBackgroundTask(backgroundTaskID)
backgroundTaskID = .invalid
}
// Καταχώριση με expiration handler
backgroundTaskID = UIApplication.shared.beginBackgroundTask(
withName: "SaveDocument",
expirationHandler: handleExpiration
)
Το πιο συνηθισμένο σφάλμα είναι η μη κλήση του endBackgroundTask μέσα στον expiration handler. Σε αυτήν την περίπτωση, το σύστημα συνεχίζει να θεωρεί την εργασία ενεργή, η εφαρμογή δεν μεταβαίνει σε κατάσταση Suspended και η μπαταρία αποφορτίζεται. Το δεύτερο σφάλμα είναι η έναρξη μεγάλων λειτουργιών μέσα στον expiration handler. Το σύστημα μπορεί να τερματίσει την εφαρμογή πριν από την ολοκλήρωσή τους και τα δεδομένα θα χαθούν.
Στον expiration handler, ο χρόνος εκτέλεσης είναι κρίσιμα σύντομος — συνήθως λιγότερο από 1 δευτερόλεπτο. Επομένως, μέσα στον handler δεν μπορούν να εκτελεστούν: πολύπλοκοι υπολογισμοί, αιτήματα δικτύου, εγγραφή μεγάλου όγκου δεδομένων σε CoreData. Μόνο ατομικές λειτουργίες: εγγραφή ενός κλειδιού στο UserDefaults, ορισμός σημαίας κατάστασης, κλήση endBackgroundTask.
Ας εξετάσουμε την πλήρη υλοποίηση του Background Task για την αποθήκευση ενός εγγράφου όταν η εφαρμογή μεταβαίνει στο παρασκήνιο. Το παράδειγμα περιλαμβάνει καταχώριση εργασίας, εκτέλεση λειτουργίας με έλεγχο υπολειπόμενου χρόνου και σωστή ολοκλήρωση μέσω endBackgroundTask.
Κατά τη μετάβαση στο παρασκήνιο, η εφαρμογή ξεκινά την αποθήκευση ενός μεγάλου εγγράφου. Το Background Task δίνει έως 30 δευτερόλεπτα για τη λειτουργία. Εάν λήξει ο χρόνος — ο expiration handler αποθηκεύει τα ενδιάμεσα αποτελέσματα. Μετά την ολοκλήρωση της αποθήκευσης, καλείται το endBackgroundTask για απελευθέρωση πόρων.
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
}
}
Τα αιτήματα δικτύου στο παρασκήνιο απαιτούν ιδιαίτερη προσοχή — ο expiration handler μπορεί να διακόψει το αίτημα πριν από τη λήψη απάντησης. Χρησιμοποιήστε URLSession με dataTask και ελέγξτε το backgroundTimeRemaining πριν από την αποστολή. Εάν απομένουν λιγότερα από 5 δευτερόλεπτα — αναβάλετε την αποστολή μέχρι την επόμενη αφύπνιση της εφαρμογής.
func sendCriticalData(data: Data) {
backgroundTaskID = UIApplication.shared.beginBackgroundTask {
// Αποθηκεύουμε δεδομένα για επαναποστολή
saveForRetry(data)
self.endBackgroundTask()
}
let task = URLSession.shared.dataTask(with: request) { _, _, error in
if error != nil {
saveForRetry(data)
}
self.endBackgroundTask()
}
task.resume()
}
Για μεγάλες λειτουργίες, παρακολουθείτε το UIApplication.shared.backgroundTimeRemaining. Εάν η τιμή πέσει κάτω από το όριο (10 δευτερόλεπτα), διακόψτε την τρέχουσα λειτουργία και ξεκινήστε την αποθήκευση ενδιάμεσων αποτελεσμάτων. Αυτό επιτρέπει τη σωστή ολοκλήρωση της εργασίας πριν από την αναγκαστική κλήση του expiration handler.
Το Background Task είναι ένας ισχυρός μηχανισμός, αλλά η λανθασμένη χρήση του οδηγεί σε προβλήματα με την μπαταρία και την απόδοση. Η τήρηση των συστάσεων της Apple και των δοκιμασμένων πρακτικών της κοινότητας βοηθά στην αποφυγή τυπικών σφαλμάτων και στη διασφάλιση σταθερής λειτουργίας της εφαρμογής.
Ολοκληρώστε το Background Task το συντομότερο δυνατό. Κάθε δευτερόλεπτο εκτέλεσης παρασκηνίου καταναλώνει ενέργεια μπαταρίας. Βέλτιστος χρόνος — λιγότερο από 5 δευτερόλεπτα. Εάν η λειτουργία διαρκεί περισσότερο — εξετάστε τη χρήση BGTaskScheduler ή Background Modes. Μην παρατείνετε τεχνητά την εργασία μέσω κλήσεων beginBackgroundTask χωρίς πραγματική ανάγκη.
Αποθηκεύστε το UIBackgroundTaskIdentifier ως ιδιότητα μιας κλάσης ή δομής. Μην το μεταβιβάζετε ποτέ ως καθολική μεταβλητή — αυτό οδηγεί σε συγκρούσεις σε πολλαπλές εργασίες. Ορίστε το αναγνωριστικό σε .invalid μετά την κλήση του endBackgroundTask για να αποτρέψετε διπλή ολοκλήρωση.
Στον Xcode Simulator, μεταφέρετε την εφαρμογή στο παρασκήνιο μέσω του κουμπιού Home (Command + Shift + H). Χρησιμοποιήστε το Debug — Simulate Background Fetch για δοκιμή αφύπνισης παρασκηνίου. Για προσομοίωση λήξης χρόνου, ορίστε τη μεταβλητή περιβάλλοντος BACKGROUND_TASK_SIMULATE_EXPIRATION = YES — ο expiration handler θα κληθεί μετά από 5 δευτερόλεπτα.
// Έλεγχος υπολειπόμενου χρόνου πριν από την έναρξη λειτουργίας
let remaining = UIApplication.shared.backgroundTimeRemaining
guard remaining > 10.0 else {
// Ανεπαρκής χρόνος — αναβολή εργασίας
scheduleForNextLaunch()
return
}
// Εκτέλεση λειτουργίας με έλεγχο χρόνου
backgroundTaskID = UIApplication.shared.beginBackgroundTask {
saveProgress()
self.endBackgroundTask()
}
Ο expiration handler συλλαμβάνει το self — αυτό μπορεί να δημιουργήσει retain cycle εάν το backgroundTaskID αποθηκεύεται ως ιδιότητα του ίδιου αντικειμένου. Χρησιμοποιήστε [weak self] στο κλείσιμο ή αποθηκεύστε το αναγνωριστικό ξεχωριστά. Ο retain cycle οδηγεί σε διαρροή μνήμης — το αντικείμενο δεν θα απελευθερωθεί μέχρι τον τερματισμό της εφαρμογής.
Με την κυκλοφορία του iOS 13, η Apple παρουσίασε το BGTaskScheduler — μια σύγχρονη αντικατάσταση του παλιού API Background Task. Και οι δύο μηχανισμοί επιλύουν παρόμοιες εργασίες, αλλά το BGTaskScheduler παρέχει περισσότερο έλεγχο και ευελιξία. Η κατανόηση των διαφορών βοηθά στην επιλογή του κατάλληλου εργαλείου για ένα συγκεκριμένο σενάριο.
| Χαρακτηριστικό | Background Task | BGTaskScheduler |
|---|---|---|
| Έκδοση iOS | 4.0+ | 13.0+ |
| Μέγ. χρόνος | 30 δευτερόλεπτα | 30 δ / 10 λεπ (processing) |
| Εκκίνηση | didEnterBackground | Προγραμματισμός + σύστημα |
| Εγγύηση εκτέλεσης | Μόνο κατά τη μετάβαση στο παρασκήνιο | Οποτεδήποτε βολεύει το σύστημα |
| Expiration handler | Ναι, υποχρεωτικός | Ναι, μέσω task.expirationHandler |
| Διαδίκτυο | Απαιτείται κατά την εκκίνηση | Διαθέσιμο μέσω requiresNetworkConnectivity |
| Φόρτιση | Δεν απαιτείται | Προαιρετικό για processing |
Το beginBackgroundTask είναι κατάλληλο για βραχυπρόθεσμες λειτουργίες που πρέπει να εκτελεστούν αμέσως κατά τη μετάβαση στο παρασκήνιο: αποθήκευση κατάστασης, ολοκλήρωση αιτήματος δικτύου, προσωρινή αποθήκευση δεδομένων. Το API είναι απλό και δεν απαιτεί διαμόρφωση Info.plist ή καταχώριση αναγνωριστικών. Ιδανικό για λειτουργίες που διαρκούν λιγότερο από 10 δευτερόλεπτα.
Το BGTaskScheduler είναι κατάλληλο για εργασίες που μπορούν να εκτελεστούν οποτεδήποτε: περιοδικός συγχρονισμός, εκκαθάριση προσωρινής μνήμης, ενημέρωση widget. Το σύστημα επιλέγει μόνο του τον βέλτιστο χρόνο λαμβάνοντας υπόψη τη συμπεριφορά του χρήστη και την κατάσταση της μπαταρίας. Για μεγάλες λειτουργίες (έως 10 λεπτά) χρησιμοποιήστε το BGProcessingTask.
Και τα δύο API μπορούν να χρησιμοποιηθούν σε μία εφαρμογή. Background Task — για άμεσες λειτουργίες κατά τη μετάβαση στο παρασκήνιο (αποθήκευση προσχεδίου). BGTaskScheduler — για προγραμματισμένες ενημερώσεις (συγχρονισμός κάθε 6 ώρες). Ο διαχωρισμός ευθυνών επιτρέπει την εγγύηση σωστής αποθήκευσης δεδομένων και ενεργειακά αποδοτικής υπηρεσίας παρασκηνίου.
Συχνές ερωτήσεις
Ναι, το beginBackgroundTask μπορεί να κληθεί οποιαδήποτε στιγμή κατά τη λειτουργία της εφαρμογής. Ωστόσο, η κατανομή χρόνου από το σύστημα θα ξεκινήσει μόνο μετά τη μετάβαση στο παρασκήνιο. Εάν η εφαρμογή βρίσκεται στο προσκήνιο, η κλήση του beginBackgroundTask δεν έχει αποτέλεσμα — το backgroundTimeRemaining επιστρέφει DBL_MAX και η εργασία θα ενεργοποιηθεί κατά τη μετάβαση στο παρασκήνιο.
Εάν δεν κληθεί το endBackgroundTask, το σύστημα συνεχίζει να θεωρεί την εφαρμογή ενεργή στο παρασκήνιο. Μετά από 30 δευτερόλεπτα, θα κληθεί ο expiration handler, αλλά εάν ούτε σε αυτόν υπάρχει endBackgroundTask — η εφαρμογή παραμένει στη μνήμη, καταναλώνοντας μπαταρία. Στο iOS 13+, το σύστημα τερματίζει αναγκαστικά μια τέτοια εφαρμογή μετά από 3 λεπτά.
Ο τυπικός χρόνος είναι 30 δευτερόλεπτα. Η αύξησή του είναι δυνατή μόνο μέσω Background Modes: Audio (αναπαραγωγή), Location (γεωτοποθεσία), Bluetooth (BLE). Ή μέσω BGProcessingTask (iOS 13+) — έως 10 λεπτά με φόρτιση και Wi-Fi. Το beginBackgroundTask από μόνο του δεν παρέχει τρόπο αύξησης του ορίου.
Ναι, το iPadOS υποστηρίζει πλήρως το beginBackgroundTask με τους ίδιους περιορισμούς όπως το iOS. Σε iPad με Stage Manager, η εφαρμογή μπορεί να παραμείνει στη μνήμη περισσότερο — το σύστημα αναστέλλει λιγότερο συχνά τις εφαρμογές λόγω μεγαλύτερης ποσότητας RAM. Αλλά το όριο των 30 δευτερολέπτων για την εργασία παρασκηνίου παραμένει.
Συνδέστε τη συσκευή στο Xcode, εκτελέστε την εφαρμογή, ελαχιστοποιήστε την — στην κονσόλα θα εμφανιστούν αρχεία καταγραφής συστήματος. Χρησιμοποιήστε το sysdiagnose για συλλογή λεπτομερών αρχείων καταγραφής: αναγκάστε το sysdiagnose από τη συσκευή (Volume Up + Down + Power). Στο Xcode Debug Navigator, παρακολουθείτε τη δραστηριότητα των εργασιών παρασκηνίου.
Σύνοψη
Θα αναπτύξουμε μια εφαρμογή για κινητά έτοιμη για χρήση
Η IT Sectr δημιουργεί εφαρμογές iOS και Android για νεοφυείς επιχειρήσεις και επιχειρήσεις από το 2017. Θα σας συμβουλεύσουμε και θα προτείνουμε την καλύτερη λύση.
Διαβάστε επίσης