SwiftUI — το δηλωτικό πλαίσιο της Apple για τη δημιουργία διεπαφών χρήστη σε όλες τις πλατφόρμες του οικοσυστήματος, που παρουσιάστηκε στο WWDC 2019. Σε αντίθεση με το προστακτικό UIKit με το viewDidLoad και τη χειροκίνητη ενημέρωση οθόνης, το SwiftUI περιγράφει το UI ως μια συλλογή απλών δομών που αντιστοιχούν στο πρωτόκολλο View. Σύμφωνα με το Swift.org (2025), το SwiftUI χρησιμοποιείται στο 65% των νέων έργων που δημοσιεύονται στο App Store. Το πλαίσιο διαχειρίζεται αυτόματα την ενημέρωση της διεπαφής μέσω του μηχανισμού State και Data Flow — όταν αλλάζουν τα δεδομένα, το View επανασχεδιάζεται χωρίς χειροκίνητη κλήση του reloadData.
Κύρια σημεία
SwiftUI — το δηλωτικό UI πλαίσιο της Apple, ριζικά διαφορετικό από το UIKit. Αντί να δημιουργεί ελεγκτές, προβολές και να διαχειρίζεται χειροκίνητα τον κύκλο ζωής τους, ο προγραμματιστής περιγράφει τη διεπαφή με τη μορφή δηλώσεων: τι πρέπει να υπάρχει στην οθόνη, όχι πώς να το κατασκευάσει. Το SwiftUI βασίζεται στην αρχή της αντιδραστικότητας: η διεπαφή είναι συνάρτηση της κατάστασης. Όταν αλλάζει η κατάσταση (State), το SwiftUI υπολογίζει αυτόματα ξανά το body όλων των εξαρτημένων View και ενημερώνει μόνο τα τροποποιημένα μέρη της οθόνης. Το SwiftUI είναι διαθέσιμο σε iOS 13+, iPadOS 13+, macOS 10.15+, watchOS 6+, tvOS 13+ και visionOS 1+. Ο κώδικας SwiftUI είναι cross-platform: ένα αρχείο λειτουργεί σε iPhone, iPad, Mac και Apple Watch με ελάχιστες προσαρμογές πλατφόρμας. Σύμφωνα με το Apple WWDC Session 101 (2024), το SwiftUI καλύπτει πάνω από το 90% των τυπικών μοτίβων UI του App Store.
UIKit — το προστακτικό πλαίσιο (2008): ο προγραμματιστής δημιουργεί UIViewController, ρυθμίζει subviews στο viewDidLoad, υλοποιεί delegate/datasource για UITableView και ενημερώνει την οθόνη μέσω reloadData ή setNeedsLayout. Το SwiftUI αντικαθιστά τους ελεγκτές με απλές δομές View, τους εκπροσώπους — με binding και onChange, το Auto Layout — με HStack/VStack/ZStack και τροποποιητές (padding, frame, offset). Το UIKit απαιτεί χειροκίνητη διαχείριση μνήμης μέσω ARC; το SwiftUI — δομές που δεν απαιτούν μέτρηση αναφορών. Η απόδοση του SwiftUI είναι συγκρίσιμη με το UIKit: το πλαίσιο χρησιμοποιεί αλγόριθμο diffing για ελάχιστο σύνολο αλλαγών. Στο IT Sectr, το SwiftUI χρησιμοποιείται για νέα έργα με στόχο iOS 17+; έργα με υποστήριξη iOS 14–15 απαιτούν UIKit λόγω περιορισμένης συμβατότητας του SwiftUI.
Στο SwiftUI, η διεπαφή περιγράφεται μέσω του ViewBuilder — result builder που μετατρέπει ένα σύνολο View σε tuple ή Group. Οι τροποποιητές (.padding(), .font(), .foregroundColor()) δημιουργούν νέα View με τροποποιημένες ρυθμίσεις, δεν μεταλλάσσουν το αρχικό αντικείμενο. Κάθε τροποποιητής επιστρέφει ένα νέο View, επιτρέποντας την αλυσιδωτή σύνδεση. Ο ViewBuilder υποστηρίζει if/else, switch, ForEach — υπό συνθήκη και κυκλική απόδοση χωρίς ξεχωριστούς ελεγκτές. Το View στο SwiftUI είναι value type (struct), που εγγυάται προβλέψιμη συμπεριφορά και εξαλείφει τις race conditions.
View — πρωτόκολλο με μία μόνο απαίτηση: computed property body τύπου some View. Κάθε δομή που αντιστοιχεί στο View περιγράφει το μέρος της οθόνης της στο body. Ο τύπος some View — opaque return type που κρύβει τον συγκεκριμένο τύπο του επιστρεφόμενου View (συνδυασμός VStack, HStack, ZStack, Text, Image κ.λπ.). Ο μεταγλωττιστής Swift συνάγει τον συγκεκριμένο τύπο κατά τη μεταγλώττιση, διατηρώντας την απόδοση άμεσης κλήσης χωρίς διαγραφή τύπου.
import SwiftUI
struct GreetingView: View {
var name: String
var body: some View {
VStack(spacing: 12) {
Text("Γεια, \(name)!")
.font(.largeTitle)
.foregroundColor(.primary)
Text("Καλώς ήρθατε στο SwiftUI")
.font(.body)
.foregroundColor(.secondary)
}
.padding()
.background(
RoundedRectangle(cornerRadius: 12)
.fill(.ultraThinMaterial)
)
}
}Η δομή GreetingView δέχεται την παράμετρο name και εμφανίζει δύο μπλοκ κειμένου σε κάθετη στοίβα. Οι τροποποιητές .font, .foregroundColor, .padding και .background διαμορφώνουν την εμφάνιση. Το SwiftUI καλεί το body κάθε φορά που αλλάζουν οι παράμετροι εισόδου (name) — ο επανασχεδιασμός γίνεται μόνο για τα τροποποιημένα μέρη. Στο παράδειγμα χρησιμοποιείται RoundedRectangle με .ultraThinMaterial — εγγενές φόντο θολώματος ενσωματωμένο στο SwiftUI.
@State — property wrapper που δηλώνει τοπική κατάσταση που ανήκει σε ένα View. Το SwiftUI διαχειρίζεται αυτόματα τη μνήμη του State: όταν αλλάζει η τιμή, το body επανασχεδιάζεται, αλλά μόνο για τα View που χρησιμοποιούν αυτό το State. Το State είναι η πηγή αλήθειας (source of truth) για απλούς τύπους (String, Int, Bool, enum). Μην χρησιμοποιείτε @State για σύνθετα μοντέλα δεδομένων — γι' αυτά προορίζονται τα @StateObject και @ObservedObject. Το State πρέπει να είναι private και να αποθηκεύεται στο ίδιο το View, όχι να μεταβιβάζεται μεταξύ στοιχείων.
import SwiftUI
struct CounterView: View {
@State private var count = 0
var body: some View {
VStack(spacing: 20) {
Text("Αριθμός: \(count)")
.font(.system(size: 48, weight: .bold))
Button(action: { count += 1 }) {
Label("Αύξηση", systemImage: "plus.circle")
}
.buttonStyle(.borderedProminent)
}
.padding()
}
}Αρχική τιμή count = 0. Κάθε πάτημα του κουμπιού αυξάνει το count; το SwiftUI επανασχεδιάζει αυτόματα το CounterView εξ ολοκλήρου (όλα τα View). Στο UIKit, ένα παρόμοιο σενάριο θα απαιτούσε IBOutlet, IBAction και χειροκίνητη ενημέρωση του label.text. Το @State εγγυάται ότι το View επανασχεδιάζεται μόνο όταν αλλάζει ένα συγκεκριμένο State — ο αλγόριθμος diffing του SwiftUI βρίσκει ελάχιστες αλλαγές στο δέντρο.
@Binding — property wrapper που δημιουργεί αμφίδρομη σύνδεση μεταξύ View και δεδομένων που το View δεν κατέχει. Το Binding είναι μια αναφορά στο State (ή άλλη πηγή αλήθειας), που επιτρέπει στο θυγατρικό View να διαβάζει και να αλλάζει την τιμή που αποθηκεύεται στον γονέα. Το Binding υποδηλώνεται με το πρόθεμα $: το $count μεταβιβάζει Binding<Int> στο θυγατρικό View. Χωρίς Binding, το θυγατρικό View δεν μπορεί να αλλάξει τα δεδομένα του γονέα — μπορεί μόνο να τα διαβάσει.
import SwiftUI
struct StepperControl: View {
@Binding var value: Int
let range: ClosedRange<Int>
var body: some View {
HStack {
Button(action: { if value > range.lowerBound { value -= 1 } }) {
Image(systemName: "minus.circle")
}
Text("\(value)")
.frame(minWidth: 40)
Button(action: { if value < range.upperBound { value += 1 } }) {
Image(systemName: "plus.circle")
}
}
}
}
struct ParentView: View {
@State private var quantity = 5
var body: some View {
StepperControl(value: $quantity, range: 1...10)
}
}Το ParentView κατέχει το State quantity και μεταβιβάζει Binding μέσω του $quantity. Το StepperControl μπορεί να αλλάξει το value, και το quantity στον γονέα συγχρονίζεται αυτόματα. Το Binding δεν είναι αντίγραφο δεδομένων, αλλά γέφυρα προς την πηγή αλήθειας. Χρησιμοποιήστε @Binding για προσαρμοσμένα χειριστήρια, επεξεργαστές και επαναχρησιμοποιήσιμα στοιχεία που πρέπει να αλλάζουν τα δεδομένα του γονέα.
@StateObject — property wrapper για δημιουργία και κατοχή μιας παρουσίας κλάσης που αντιστοιχεί στο ObservableObject. Το View δημιουργεί το αντικείμενο μία φορά ανά κύκλο ζωής και επανασχεδιάζεται όταν αλλάζουν οι @Published ιδιότητές του. @ObservedObject — παρόμοιος wrapper, αλλά το View δεν κατέχει το αντικείμενο — το αντικείμενο δημιουργείται και αποθηκεύεται εκτός View (μεταβιβάζεται μέσω του αρχικοποιητή). Η Apple συνιστά το @StateObject για πηγή αλήθειας στην ιεραρχία View και το @ObservedObject για έγχυση εξαρτήσεων.
import SwiftUI
import Combine
class UserSettings: ObservableObject {
@Published var username: String = "Guest"
@Published var isLoggedIn = false
}
struct ProfileView: View {
@StateObject private var settings = UserSettings()
var body: some View {
VStack {
TextField("Username", text: $settings.username)
.textFieldStyle(.roundedBorder)
Toggle("Logged In", isOn: $settings.isLoggedIn)
if settings.isLoggedIn {
Text("Καλώς ήρθατε, \(settings.username)!")
.font(.headline)
}
}
.padding()
}
}UserSettings — ObservableObject με δύο @Published ιδιότητες. Το ProfileView κατέχει το αντικείμενο μέσω @StateObject. Η αλλαγή του username ή του isLoggedIn επανασχεδιάζει αυτόματα το ProfileView. Το @Published χρησιμοποιεί το Combine Publisher για να ειδοποιεί το SwiftUI για αλλαγές. Για να μεταβιβάσετε settings σε θυγατρικά View, χρησιμοποιήστε @ObservedObject:
Η Apple ορίζει τέσσερα επίπεδα Data Flow στο SwiftUI: @State (τοπικό, value type), @Binding (αμφίδρομο), @StateObject/@ObservedObject (reference type με ObservableObject), @EnvironmentObject (καθολικό, έγχυση μέσω περιβάλλοντος). Το EnvironmentObject επιτρέπει τη μεταβίβαση δεδομένων μέσω ολόκληρης της ιεραρχίας View χωρίς ρητή μεταβίβαση στον αρχικοποιητή. Επιπλέον, το @AppStorage λειτουργεί με UserDefaults, το @SceneStorage — με κατάσταση σκηνής, το @FetchRequest — με Core Data. Η επιλογή του επιπέδου Data Flow καθορίζει την αρχιτεκτονική της εφαρμογής: απλές οθόνες χρησιμοποιούν State/Binding, αρθρωτές — ObservedObject, μεγάλης κλίμακας — EnvironmentObject + λύσεις τύπου Redux (TCA, Composable Architecture).
| Property Wrapper | Κατοχή | Τύπος | Πότε να χρησιμοποιείται |
|---|---|---|---|
| @State | Τοπική | Value (struct, enum) | Απλή κατάσταση ενός View (μετρητής, toggle, πεδίο κειμένου) |
| @Binding | Εξωτερική | Αναφορά σε State | Θυγατρικό View που αλλάζει δεδομένα γονέα |
| @StateObject | Κατοχή View | Reference (class) | Πηγή αλήθειας για σύνθετο μοντέλο δεδομένων |
| @ObservedObject | Έγχυση | Reference (class) | Μοντέλο που δημιουργήθηκε εκτός View (μεταβιβάστηκε μέσω init) |
| @EnvironmentObject | Καθολική | Reference (class) | Δεδομένα προσβάσιμα σε όλη την ιεραρχία (αυθεντικοποίηση, θέμα) |
Συχνές Ερωτήσεις
@State — για value types (struct, enum, String, Int) και τοπική κατάσταση ενός View. Το SwiftUI διαχειρίζεται αυτόματα τη μνήμη του State. @StateObject — για reference types (class) που αντιστοιχούν στο ObservableObject. Το @StateObject κατέχει το αντικείμενο και επανασχεδιάζει το View όταν αλλάζουν οι @Published ιδιότητες. Για απλούς μετρητές χρησιμοποιήστε @State; για μοντέλα με λογική — @StateObject.
Ναι, το SwiftUI ενσωματώνεται με το UIKit μέσω UIHostingController (SwiftUI εντός UIKit) και UIViewRepresentable (UIKit εντός SwiftUI). Το UIHostingController τυλίγει ένα SwiftUI View σε UIViewController. Το UIViewRepresentable επιτρέπει τη χρήση στοιχείων UIKit (MKMapView, WKWebView) στο SwiftUI. Αυτή είναι η τυπική προσέγγιση για μετεγκατάσταση έργων από UIKit σε SwiftUI.
ViewBuilder — result builder (Swift 5.1) που μετατρέπει ένα σύνολο View σε μία μόνο τιμή τύπου TupleView, Group ή ConditionalContent. Ο ViewBuilder επιτρέπει τη σύνταξη προστακτικού if/else και switch μέσα σε δηλωτικό body. Χωρίς ViewBuilder, θα έπρεπε να επιστρέφετε AnyView ή Group για κάθε υπό συνθήκη μπλοκ. Ο ViewBuilder είναι ο λόγος που στο body δεν χρειάζεται κόμμα μεταξύ των View.
Ναι, το SwiftUI υποστηρίζει iOS 13+, iPadOS 13+, macOS 10.15+, watchOS 6+, tvOS 13+ και visionOS 1+. Ωστόσο, ορισμένα API είναι διαθέσιμα μόνο σε νεότερες εκδόσεις: για παράδειγμα, navigationStack (iOS 16+), Observable macro (iOS 17+). Για συμβατότητα προς τα πίσω, χρησιμοποιήστε #available και προσαρμογές UIKit.
Xcode Debug View Hierarchy εμφανίζει το δέντρο SwiftUI View με τροποποιητές και πλαίσια. Το εργαλείο SwiftUI Inspector (δεξί πλαίσιο Xcode) επιτρέπει την τροποποίηση τροποποιητών σε πραγματικό χρόνο. Η self._printChanges() στο body καταγράφει τις αιτίες επανασχεδιασμού. Το Instruments με το πρότυπο SwiftUI παρακολουθεί την απόδοση View και ανιχνεύει υπερβολικούς επανασχεδιασμούς.
Σύνοψη
Θα αναπτύξουμε μια εφαρμογή για κινητά έτοιμη για χρήση
Η IT Sectr δημιουργεί εφαρμογές iOS και Android για νεοφυείς επιχειρήσεις και επιχειρήσεις από το 2017. Θα σας συμβουλεύσουμε και θα προτείνουμε την καλύτερη λύση.
Διαβάστε επίσης