.task { } — τι είναι, τροποποιητής async και φόρτωση δεδομένων σε View

Συγγραφέας: IT Sectr Δημοσιεύτηκε: 2026-06-26 Χρόνος ανάγνωσης: 9 λεπ

.task { } — είναι ένας τροποποιητής στο SwiftUI, που παρουσιάστηκε στο iOS 15, ο οποίος ξεκινά μια ασύγχρονη λειτουργία όταν εμφανίζεται το View και την ακυρώνει αυτόματα όταν εξαφανίζεται. Σε αντίθεση με το .onAppear, το οποίο εκτελεί σύγχρονο κώδικα χωρίς δυνατότητα ακύρωσης, το .task λειτουργεί με περιβάλλον async/await και λαμβάνει υπόψη τον κύκλο ζωής του View: όταν το View εξαφανίζεται, το SwiftUI καλεί cancel() στο δημιουργημένο Task. Αυτό αποτρέπει διαρροές μνήμης και εκτέλεση λειτουργιών αφού το View δεν χρειάζεται πλέον ενημέρωση. Σύμφωνα με το Apple WWDC Session 10132 — Meet async/await in SwiftUI (2024), το .task είναι η προτιμώμενη μέθοδος φόρτωσης δεδομένων στο SwiftUI, καθώς λειτουργεί με ασφάλεια με το Structured Concurrency και διαχειρίζεται αυτόματα τη διάρκεια ζωής της ασύγχρονης λειτουργίας.

Κύρια σημεία

  • .task { } — τροποποιητής SwiftUI για ασύγχρονη φόρτωση δεδομένων κατά την εμφάνιση View, διαθέσιμος από iOS 15.
  • Αυτόματη ακύρωση — όταν το View εξαφανίζεται, το SwiftUI ακυρώνει το Task, αποτρέποντας διαρροές μνήμης.
  • Περιβάλλον async/await — εντός του .task είναι διαθέσιμες async κλήσεις χωρίς ανάγκη για DispatchQueue ή Combine.
  • .task(id:) — παραλλαγή με αναγνωριστικό επανεκκινεί την εργασία όταν αλλάξει η καθορισμένη τιμή.
  • Structured Concurrency — το .task υποστηρίζει Structured Concurrency και TaskGroup για παράλληλες λειτουργίες.

Τι είναι το .task { } στο SwiftUI

.task { } — είναι ένας τροποποιητής View που δημιουργεί ένα Task σε περιβάλλον async όταν το View εμφανίζεται στην οθόνη. Το SwiftUI εκκινεί το παρεχόμενο closure στο παρασκήνιο, ενώ το κύριο νήμα παραμένει ελεύθερο για λειτουργίες UI. Όταν το View εξαφανίζεται, το SwiftUI ακυρώνει αυτόματα το Task μέσω του μηχανισμού Structured Concurrency — αυτό εγγυάται ότι η ασύγχρονη λειτουργία δεν θα συνεχιστεί αφού το αποτέλεσμά της δεν είναι πλέον απαραίτητο.

Σύμφωνα με το Apple — Swift Programming Language (2025), το .task χρησιμοποιεί την έννοια του Structured Concurrency, που εισήχθη στο Swift 5.5. Κάθε .task δημιουργεί μια θυγατρική εργασία στο πλαίσιο της γονικής εργασίας του View. Εάν η γονική εργασία ακυρωθεί (το View εξαφανιστεί), όλες οι θυγατρικές εργασίες ακυρώνονται επίσης αυτόματα. Αυτό απλοποιεί ριζικά τη διαχείριση του κύκλου ζωής των ασύγχρονων λειτουργιών σε σύγκριση με τη μη αυτόματη αποθήκευση αναφορών σε DispatchWorkItem ή AnyCancellable.

Σε αντίθεση με την παραδοσιακή προσέγγιση με @State + μη αυτόματη κλήση στο .onAppear, το .task δεν απαιτεί αποθήκευση αναφοράς στο Task για μεταγενέστερη ακύρωση. Το SwiftUI το κάνει αυτόματα, μειώνοντας την ποσότητα boilerplate κώδικα και εξαλείφοντας τον κίνδυνο να ξεχάσετε να ακυρώσετε την εργασία.

swift
struct ArticlesView: View {
    @State var articles: [Article] = []
    @State var error: Error?
    
    var body: some View {
        List(articles) { article in
            Text(article.title)
        }
        .task {
            do {
                articles = await APIClient().fetchArticles()
            } catch {
                self.error = error
            }
        }
    }
}

.task vs .onAppear: βασικές διαφορές

Πολλοί προγραμματιστές έχουν συνηθίσει να φορτώνουν δεδομένα στο .onAppear, αλλά με την εμφάνιση των async/await και .task, αυτή η προσέγγιση είναι ξεπερασμένη. Το .onAppear εκτελεί κώδικα σύγχρονα — για ασύγχρονες λειτουργίες εντός του .onAppear πρέπει να τυλίξετε την κλήση σε Task { } και να αποθηκεύσετε μη αυτόματα μια αναφορά σε αυτό για πιθανή ακύρωση. Το .task το κάνει αυτόματα.

Χαρακτηριστικό.task { }.onAppear
Περιβάλλον asyncΕνσωματωμένο async/awaitΑπαιτεί περιτύλιγμα Task { }
Αυτόματη ακύρωσηΝαι, όταν το View εξαφανίζεταιΌχι, πρέπει να υλοποιηθεί χειροκίνητα
Structured ConcurrencyΥποστηρίζειΔεν υποστηρίζει
ΕπανεκκίνησηΜόνο όταν αλλάξει το idΚάθε φορά που εμφανίζεται
Σύσταση AppleΠροτιμώμενη μέθοδοςΓια σύγχρονες λειτουργίες

Το .onAppear εξακολουθεί να είναι χρήσιμο για σύγχρονες λειτουργίες — για παράδειγμα, καταγραφή ή αρχική διαμόρφωση UI. Αλλά για ασύγχρονη φόρτωση δεδομένων, αιτήματα δικτύου, εργασία με βάση δεδομένων ή σύστημα αρχείων, χρησιμοποιήστε το .task. Είναι ασφαλέστερο και καθαρότερο από αρχιτεκτονική άποψη.

swift
// ❌ Παλιά προσέγγιση: Task στο .onAppear χωρίς ακύρωση
var loadTask: Task<Void, Never>?
func body() { var body: some View { Text("") }
    .onAppear {
        loadTask = Task { await loadData() }
    }
    .onDisappear { loadTask?.cancel() }

// ✅ Σύγχρονη προσέγγιση: .task διαχειρίζεται την ακύρωση
func body() { var body: some View { Text("") }
    .task { await loadData() }

.task(id:) — επανεκκίνηση όταν αλλάζουν τα δεδομένα

Ο τροποποιητής .task(id:) δέχεται μια πρόσθετη παράμετρο — ένα αναγνωριστικό. Όταν η τιμή του αναγνωριστικού αλλάζει, το SwiftUI ακυρώνει την τρέχουσα εργασία και ξεκινά μια νέα με το νέο αναγνωριστικό. Αυτό είναι ιδανικό για οθόνες όπου τα δεδομένα εξαρτώνται από μια επιλεγμένη παράμετρο — για παράδειγμα, λίστα άρθρων ανά κατηγορία ή λεπτομέρειες προϊόντος ανά ID.

swift
struct CategoryView: View {
    let categoryId: Int
    @State var items: [Item] = []
    
    var body: some View {
        List(items) { item in
            Text(item.name)
        }
        .task(id: categoryId) {
            await loadItems(for: categoryId)
        }
    }
    
    func loadItems(for id: Int) async {
        do {
            items = await APIClient().fetchItems(categoryId: id)
        } catch {
            // διαχείριση σφάλματος
        }
    }
}

Όταν το categoryId αλλάζει, το SwiftUI ακυρώνει το προηγούμενο αίτημα και ξεκινά ένα νέο. Αυτό είναι ιδιαίτερα σημαντικό κατά τη γρήγορη εναλλαγή κατηγοριών — τα παλιά αιτήματα δεν θα ανταγωνίζονται τα νέα για την ενημέρωση της κατάστασης. Χωρίς .task(id:) θα πρέπει να παρακολουθείτε μη αυτόματα τις αλλαγές μέσω .onChange και να διαχειρίζεστε μη αυτόματα το Task.

Ακύρωση εργασιών και έλεγχος isCancelled

Αν και το .task ακυρώνει αυτόματα την εργασία όταν το View εξαφανίζεται, η ίδια η ασύγχρονη λειτουργία πρέπει να ελέγχει συνεργατικά την ακύρωση. Η Swift χρησιμοποιεί συνεργατικό μοντέλο ακύρωσης — το Task.cancel() δεν σταματά βίαια την εκτέλεση, αλλά απλώς ορίζει τη σημαία isCancelled. Ο κώδικας εντός της εργασίας πρέπει να ελέγχει περιοδικά αυτή τη σημαία.

swift
struct LoadingView: View {
    @State var progress: Double = 0
    
    var body: some View {
        ProgressView(value: progress)
            .task {
                for i in 0..<100 {
                    // Έλεγχος ακύρωσης
                    try Task.checkCancellation()
                    
                    await Task.sleep(nanoseconds: 50_000_000)
                    progress = Double(i + 1) / 100.0
                }
            }
    }
}

Task.checkCancellation() πετάει CancellationError εάν η εργασία έχει ακυρωθεί. Αυτός είναι ο απλούστερος τρόπος ελέγχου — λειτουργεί σε οποιοδήποτε περιβάλλον async. Εναλλακτικά — μη αυτόματος έλεγχος του Task.isCancelled πριν από δαπανηρές λειτουργίες. Για το URLSession, τα αιτήματα δικτύου ακυρώνονται αυτόματα κατά την ακύρωση της εργασίας, καθώς το URLSession υποστηρίζει Structured Concurrency εξ αρχής.

Πρακτικά παραδείγματα .task

.task είναι κατάλληλο για πολλά σενάρια: από απλή φόρτωση JSON έως σύνθετες παράλληλες λειτουργίες με TaskGroup. Ας δούμε τρία τυπικά παραδείγματα χρήσης.

Φόρτωση με διαχείριση σφαλμάτων

swift
struct ProfileView: View {
    @State var profile: Profile?
    @State var isLoading = true
    
    var body: some View {
        Group {
            if isLoading {
                ProgressView()
            } else if let profile {
                Text(profile.name)
            } else {
                Text("Αποτυχία φόρτωσης")
            }
        }
        .task {
            defer { isLoading = false }
            do {
                profile = await APIClient().fetchProfile()
            } catch {
                // το προφίλ παραμένει nil
            }
        }
    }
}

Παράλληλη φόρτωση με TaskGroup

swift
struct DashboardView: View {
    @State var stats: DashboardStats?
    
    var body: some View {
        Text("Πίνακας ελέγχου")
            .task {
                stats = await Task {
                    await withThrowingTaskGroup { group in
                        group.addTask { await API().fetchUsers() }
                        group.addTask { await API().fetchOrders() }
                        group.addTask { await API().fetchRevenue() }
                        
                        return DashboardStats(
                            users: try await group.next(),
                            orders: try await group.next(),
                            revenue: try await group.next()
                        )
                    }
                }.value
            }
    }
}

Συνηθισμένα λάθη με το .task

Το πιο συνηθισμένο λάθος — η μεταβολή ιδιοτήτων UI εντός του .task χωρίς εναλλαγή στο κύριο νήμα. Αν και το SwiftUI επιστρέφει αυτόματα τις ενημερώσεις στο κύριο νήμα όταν αλλάζει το @State σε περιβάλλον async, οι άμεσοι χειρισμοί στοιχείων UIKit εντός του .task μπορεί να προκαλέσουν crash.

  • Ξέχασες το try/catch — το .task δεν διαχειρίζεται σφάλματα αυτόματα. Όλες οι συναρτήσεις που πετούν σφάλματα εντός πρέπει να τυλιχτούν σε do/catch, διαφορετικά η εφαρμογή θα καταρρεύσει.
  • Race condition — εάν πολλά .task(id:) εκτελούνται με διαφορετικά id και ενημερώνουν την ίδια κατάσταση, είναι πιθανές συνθήκες ανταγωνισμού. Χρησιμοποιήστε ξεχωριστές ιδιότητες για διαφορετικές πηγές δεδομένων.
  • Μεγάλες σύγχρονες λειτουργίες — το .task δεν κάνει τον σύγχρονο κώδικα ασύγχρονο. Εάν υπάρχει βαριά σύγχρονη εργασία εντός του .task, τυλίξτε την σε Task.detached ή μεταφέρετέ την σε ξεχωριστή async μέθοδο.
  • Αγνόηση του CancellationError — κατά τον έλεγχο του Task.checkCancellation(), το σφάλμα CancellationError πρέπει να διαδίδεται προς τα πάνω, όχι να καταστέλλεται. Η καταστολή της ακύρωσης μπορεί να οδηγήσει σε διαρροές μνήμης.
swift
// ❌ Σφάλμα: χωρίς διαχείριση σφαλμάτων
.task {
    let data = await fetchData() // καταρρέει στο σφάλμα!
    items = data
}

// ✅ Σωστά: do/catch
.task {
    do {
        items = await fetchData()
    } catch {
        errorMessage = error.localizedDescription
    }
}

Συχνές Ερωτήσεις

Ποια είναι η διαφορά μεταξύ .task και χρήσης Task { } στο .onAppear;

Το .task ακυρώνει αυτόματα την εργασία όταν το View εξαφανίζεται και υποστηρίζει Structured Concurrency. Το Task { } στο .onAppear απαιτεί μη αυτόματη αποθήκευση αναφοράς στην εργασία και κλήση cancel() στο .onDisappear. Το .task είναι επίσης πιο ευανάγνωστο — υποδεικνύει ρητά ότι η φόρτωση δεδομένων είναι μέρος του κύκλου ζωής του View.

Μπορεί να χρησιμοποιηθεί το .task για εγγραφή σε publisher;

Ναι, για εγγραφή σε AsyncSequence ή AsyncStream χρησιμοποιήστε for await value in publisher.values εντός του .task. Αυτό λειτουργεί τόσο με async/await όσο και με Combine μέσω της επέκτασης Publisher.values. Όταν το View εξαφανίζεται, η επανάληψη τερματίζεται αυτόματα και η εγγραφή ακυρώνεται.

Πώς λειτουργεί το .task με TabView — επανεκκινείται η εργασία κατά την εναλλαγή καρτελών;

Ναι, εάν το View εντός του TabView αναδημιουργείται σε κάθε εναλλαγή. Από το iOS 18, το TabView μπορεί να αποθηκεύει το View στη μνήμη — σε αυτή την περίπτωση, το .task δεν επανεκκινείται. Χρησιμοποιήστε .task(id:) με το αναγνωριστικό της καρτέλας εάν χρειάζεται να φορτώσετε ξανά δεδομένα σε κάθε εναλλαγή.

Τι συμβαίνει εάν το View με .task εξαφανιστεί πριν ολοκληρωθεί το αίτημα;

Το SwiftUI ακυρώνει την εργασία όταν το View εξαφανίζεται. Εάν το αίτημα URLSession ήταν εντός της εργασίας, θα ακυρωθεί επίσης. Εάν η εργασία δεν υποστηρίζει συνεργατική ακύρωση (π.χ. δεν ελέγχει το isCancelled), θα συνεχίσει να εκτελείται, αλλά το αποτέλεσμά της δεν θα εφαρμοστεί στην κατάσταση, καθώς το View δεν υπάρχει πλέον.

Μπορούν να χρησιμοποιηθούν πολλαπλά .task σε ένα View;

Ναι, μπορείτε να προσθέσετε πολλαπλούς τροποποιητές .task σε ένα View. Κάθε ένας δημιουργεί μια ανεξάρτητη εργασία. Αυτό είναι βολικό για διαχωρισμό διαφορετικών πηγών δεδομένων: ένα .task για φόρτωση προφίλ, δεύτερο για εγγραφή σε WebSocket, τρίτο για παρακολούθηση γεωτοποθεσίας.

Περίληψη

  • .task { } — τροποποιητής SwiftUI για ασύγχρονες λειτουργίες με αυτόματη ακύρωση όταν το View εξαφανίζεται.
  • Υποστήριξη async/await — εντός του .task είναι διαθέσιμο πλήρες περιβάλλον async χωρίς περιτύλιγμα Task.
  • .task(id:) — επανεκκινεί την εργασία όταν αλλάζει το αναγνωριστικό, αντικαθιστώντας το μη αυτόματο .onChange.
  • Συνεργατική ακύρωση — χρησιμοποιήστε το Task.checkCancellation() για έλεγχο ακύρωσης εντός της εργασίας.
  • Structured Concurrency — το .task υποστηρίζει TaskGroup και παράλληλες λειτουργίες με ακύρωση θυγατρικών εργασιών.
  • Αντικατάσταση του .onAppear — για ασύγχρονη φόρτωση δεδομένων χρησιμοποιήστε .task αντί του συνδυασμού .onAppear + Task + .onDisappear.
  • Διαχείριση σφαλμάτων — όλες οι async κλήσεις εντός του .task πρέπει να τυλιχτούν σε do/catch για την αποφυγή crash.

Θα αναπτύξουμε μια εφαρμογή για κινητά έτοιμη για χρήση

Η IT Sectr δημιουργεί εφαρμογές iOS και Android για νεοφυείς επιχειρήσεις και επιχειρήσεις από το 2017. Θα σας συμβουλεύσουμε και θα προτείνουμε την καλύτερη λύση.

Συζήτηση έργου

Διαβάστε επίσης