SwiftUI — le framework déclaratif d'Apple pour la création d'interfaces utilisateur sur toutes les plateformes de l'écosystème, présenté à la WWDC 2019. Contrairement à UIKit impératif avec son viewDidLoad et ses mises à jour manuelles de l'écran, SwiftUI décrit l'UI comme une collection de structures simples conformes au protocole View. Selon Swift.org (2025), SwiftUI est utilisé dans 65% des nouveaux projets publiés sur l'App Store. Le framework gère automatiquement les mises à jour de l'interface via le mécanisme State et Data Flow — lorsque les données changent, la View se redessine sans appels manuels à reloadData.
Points clés
SwiftUI — le framework déclaratif d'UI d'Apple, radicalement différent d'UIKit. Au lieu de créer des contrôleurs, des vues et de gérer manuellement leur cycle de vie, le développeur décrit l'interface comme des déclarations : ce qui doit être à l'écran, pas comment le construire. SwiftUI repose sur le principe de réactivité : l'interface est une fonction de l'état. Lorsque l'état change, SwiftUI recalcule automatiquement le body de toutes les Views dépendantes et met à jour uniquement les parties modifiées de l'écran. SwiftUI est disponible sur iOS 13+, iPadOS 13+, macOS 10.15+, watchOS 6+, tvOS 13+ et visionOS 1+. Le code SwiftUI est multiplateforme : un seul fichier fonctionne sur iPhone, iPad, Mac et Apple Watch avec des adaptations minimales. Selon Apple WWDC Session 101 (2024), SwiftUI couvre plus de 90% des motifs d'UI standards de l'App Store.
UIKit — framework impératif (2008) : le développeur crée un UIViewController, configure les subviews dans viewDidLoad, implémente delegate/datasource pour UITableView et met à jour l'écran via reloadData ou setNeedsLayout. SwiftUI remplace les contrôleurs par des structures View simples, les délégués par des bindings et onChange, Auto Layout par HStack/VStack/ZStack avec des modificateurs (padding, frame, offset). UIKit nécessite une gestion manuelle de la mémoire via ARC ; SwiftUI utilise des structures qui ne nécessitent pas de comptage de références. Les performances de SwiftUI sont comparables à celles d'UIKit : le framework utilise un algorithme de diffing pour des ensembles de modifications minimaux. Chez IT Sectr, SwiftUI est utilisé pour les nouveaux projets avec un target iOS 17+ ; les projets prenant en charge iOS 14–15 nécessitent UIKit en raison de la compatibilité limitée de SwiftUI.
Dans SwiftUI, l'interface est décrite via ViewBuilder — un result builder qui transforme un ensemble de Views en un tuple ou Group. Les modificateurs (.padding(), .font(), .foregroundColor()) créent de nouvelles Views avec des paramètres modifiés plutôt que de muter l'objet d'origine. Chaque modificateur retourne une nouvelle View, permettant le chaînage. ViewBuilder prend en charge if/else, switch, ForEach — rendu conditionnel et cyclique sans contrôleurs séparés. View dans SwiftUI est un value type (struct), garantissant un comportement prévisible et éliminant les conditions de course.
View — un protocole avec une seule exigence : la propriété calculée body de type some View. Chaque structure conforme à View décrit sa partie de l'écran dans body. Le type some View est un type de retour opaque qui masque le type concret de la View retournée (empilement VStack, HStack, ZStack, Text, Image, etc.). Le compilateur Swift infère le type concret à la compilation, préservant les performances des appels directs sans effacement de type.
import SwiftUI
struct GreetingView: View {
var name: String
var body: some View {
VStack(spacing: 12) {
Text("Bonjour, \(name) !")
.font(.largeTitle)
.foregroundColor(.primary)
Text("Bienvenue dans SwiftUI")
.font(.body)
.foregroundColor(.secondary)
}
.padding()
.background(
RoundedRectangle(cornerRadius: 12)
.fill(.ultraThinMaterial)
)
}
}La structure GreetingView prend un paramètre name et affiche deux blocs de texte dans une pile verticale. Les modificateurs .font, .foregroundColor, .padding et .background configurent l'apparence. SwiftUI appelle body chaque fois que les paramètres d'entrée (name) changent — le redessin se produit uniquement pour les parties modifiées. L'exemple utilise RoundedRectangle avec .ultraThinMaterial — un fond de flou natif intégré dans SwiftUI.
@State — un property wrapper qui déclare un état local appartenant à une seule View. SwiftUI gère automatiquement la mémoire de State : lorsque la valeur change, le body se redessine, mais uniquement pour les Views qui utilisent ce State. State est la source de vérité pour les types simples (String, Int, Bool, enum). N'utilisez pas @State pour les modèles de données complexes — utilisez plutôt @StateObject et @ObservedObject. State doit être privé et stocké dans la View elle-même, non transféré entre composants.
import SwiftUI
struct CounterView: View {
@State private var count = 0
var body: some View {
VStack(spacing: 20) {
Text("Compteur : \(count)")
.font(.system(size: 48, weight: .bold))
Button(action: { count += 1 }) {
Label("Incrémenter", systemImage: "plus.circle")
}
.buttonStyle(.borderedProminent)
}
.padding()
}
}La valeur initiale de count = 0. Chaque pression de bouton incrémente count ; SwiftUI redessine automatiquement tout CounterView (toutes les Views). Dans UIKit, un scénario similaire nécessiterait IBOutlet, IBAction et des mises à jour manuelles de label.text. @State garantit qu'une View n'est redessinée que lorsqu'un State spécifique change — l'algorithme de diffing de SwiftUI trouve les modifications minimales dans l'arbre.
@Binding — un property wrapper qui crée une liaison bidirectionnelle entre une View et des données que la View ne possède pas. Un Binding est une référence à State (ou une autre source de vérité), permettant à une View enfant de lire et modifier la valeur stockée dans le parent. Binding est noté par le préfixe $ : $count transmet un Binding<Int> à la View enfant. Sans Binding, une View enfant ne peut pas modifier les données du parent — elle peut seulement les lire.
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 possède le State quantity et transmet Binding via $quantity. StepperControl peut modifier la valeur, et quantity dans le parent se synchronise automatiquement. Un Binding n'est pas une copie des données, mais un pont vers la source de vérité. Utilisez @Binding pour les contrôles personnalisés, les éditeurs et les composants réutilisables qui doivent modifier les données du parent.
@StateObject — un property wrapper pour créer et posséder une instance d'une classe conforme à ObservableObject. La View crée l'objet une fois par cycle de vie et se redessine lorsque ses propriétés @Published changent. @ObservedObject — un wrapper similaire, mais la View ne possède pas l'objet — l'objet est créé et stocké en dehors de la View (transmis via un initialisateur). Apple recommande @StateObject pour la source de vérité dans une hiérarchie de Views et @ObservedObject pour l'injection de dépendances.
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("Bienvenue, \(settings.username) !")
.font(.headline)
}
}
.padding()
}
}UserSettings — un ObservableObject avec deux propriétés @Published. ProfileView possède l'objet via @StateObject. Les modifications de username ou isLoggedIn redessinent automatiquement ProfileView. @Published utilise Combine Publisher pour notifier SwiftUI des changements. Pour transmettre settings aux Views enfants, utilisez @ObservedObject :
Apple définit quatre niveaux de Data Flow dans SwiftUI : @State (local, value type), @Binding (bidirectionnel), @StateObject/@ObservedObject (reference type avec ObservableObject), @EnvironmentObject (global, injecté via l'environnement). EnvironmentObject permet de transmettre des données dans toute la hiérarchie de Views sans passage explicite dans l'initialisateur. De plus, @AppStorage fonctionne avec UserDefaults, @SceneStorage avec l'état de la scène, @FetchRequest avec Core Data. Le choix du niveau de Data Flow détermine l'architecture de l'application : les écrans simples utilisent State/Binding, les modulaires utilisent ObservedObject, les grandes échelles utilisent EnvironmentObject + des solutions de type Redux (TCA, Composable Architecture).
| Property Wrapper | Propriété | Type | Quand l'utiliser |
|---|---|---|---|
| @State | Local | Value (struct, enum) | État simple d'une seule View (compteur, bouton, champ texte) |
| @Binding | Externe | Référence à State | View enfant modifiant les données du parent |
| @StateObject | Propriété de la View | Reference (class) | Source de vérité pour modèle de données complexe |
| @ObservedObject | Injection | Reference (class) | Modèle créé en dehors de la View (transmis via init) |
| @EnvironmentObject | Global | Reference (class) | Données disponibles pour toute la hiérarchie (auth, thème) |
Questions fréquentes
@State — pour les value types (struct, enum, String, Int) et l'état local d'une seule View. SwiftUI gère automatiquement la mémoire de State. @StateObject — pour les reference types (class) conformes à ObservableObject. @StateObject possède l'objet et redessine la View lorsque les propriétés @Published changent. Pour les compteurs simples utilisez @State ; pour les modèles avec logique métier utilisez @StateObject.
Oui, SwiftUI s'intègre avec UIKit via UIHostingController (SwiftUI dans UIKit) et UIViewRepresentable (UIKit dans SwiftUI). UIHostingController encapsule une SwiftUI View dans un UIViewController. UIViewRepresentable permet d'utiliser des composants UIKit (MKMapView, WKWebView) dans SwiftUI. C'est l'approche standard pour migrer des projets de UIKit vers SwiftUI.
ViewBuilder — un result builder (Swift 5.1) qui transforme un ensemble de Views en une seule valeur de type TupleView, Group ou ConditionalContent. ViewBuilder permet d'écrire du if/else et switch impératifs à l'intérieur d'un body déclaratif. Sans ViewBuilder, vous devriez retourner AnyView ou Group pour chaque bloc conditionnel. ViewBuilder est la raison pour laquelle body n'a pas besoin de virgules entre les Views.
Oui, SwiftUI prend en charge iOS 13+, iPadOS 13+, macOS 10.15+, watchOS 6+, tvOS 13+ et visionOS 1+. Cependant, certaines API ne sont disponibles que sur les versions récentes : par exemple, navigationStack (iOS 16+), Observable macro (iOS 17+). Pour la rétrocompatibilité, utilisez #available et des adaptations UIKit.
Xcode Debug View Hierarchy montre l'arborescence des Views SwiftUI avec modificateurs et cadres. L'outil SwiftUI Inspector (panneau droit de Xcode) permet de modifier les modificateurs en temps réel. self._printChanges() dans le body enregistre les raisons du redessin. Instruments avec le modèle SwiftUI trace les performances des Views et identifie les redessins excessifs.
Résumé
Nous développerons une application mobile clé en main
IT Sectr crée des applications iOS et Android pour les startups et les entreprises depuis 2017. Nous vous conseillerons et vous proposerons la meilleure solution.
Lisez aussi