Spécificités de Swift dans le développement mobile : définition, fonctionnalités clés et fonctionnement

Auteur : IT Sectr Publié le : 2026-06-25 Temps de lecture : 17 min

Swift est le langage de programmation d'Apple qui combine la puissance d'Objective-C avec la sécurité des langages modernes. Contrairement à de nombreux autres langages, Swift propose des constructions uniques : les types valeur avec copie automatique, les Actors avec isolation au niveau du compilateur, les Property Wrappers pour réutiliser la logique des propriétés et la Programmation Orientée Protocole comme alternative à l'héritage classique. Selon la Documentation Apple Swift, ces fonctionnalités rendent le code plus sûr et plus performant.

Points Clés

  • Struct — type valeur, copié lors de l'affectation, ne prend pas en charge l'héritage. Class — type référence, passé par référence, prend en charge l'héritage et deinit.
  • Actor — type référence thread-safe avec isolation au niveau du compilateur. Le compilateur interdit l'accès concurrent à l'état mutable depuis différentes tâches.
  • Property Wrappers — @Published, @State, @Binding encapsulent la logique des propriétés dans SwiftUI et Combine, éliminant le code boilerplate.
  • Generics permettent d'écrire des fonctions et structures type-safe sans liaison à un type spécifique. La Programmation Orientée Protocole utilise extension + protocol au lieu d'une hiérarchie de classes.
  • Enum avec valeurs associées — un outil puissant pour modéliser les états. Chaque cas peut stocker un ensemble arbitraire de données de différents types.

Struct vs Class dans Swift : types valeur et types référence

Dans Swift, struct est un type valeur, tandis que class est un type référence. Cette différence fondamentale détermine le comportement lors de l'affectation, du passage aux fonctions et de la gestion de la mémoire. Struct est copié entièrement à chaque affectation, class est passé par référence — seul le pointeur vers l'objet dans le tas est copié.

Sémantique de valeur de Struct

Struct implémente automatiquement la sémantique de valeur — chaque instance est indépendante. La mutation d'une propriété de struct n'est possible que via var, et les méthodes qui modifient les propriétés doivent être marquées mutating. Struct ne prend pas en charge l'héritage mais peut se conformer aux protocoles. La bibliothèque standard de Swift utilise struct pour String, Array, Dictionary, Int, Bool — tous les types fondamentaux sont des types valeur.

swift
struct User {
    let id: Int
    var name: String

    mutating func updateName(_ newName: String) {
        name = newName
    }
}

var user1 = User(id: 1, name: "Alice")
var user2 = user1
user2.updateName("Bob")
// user1.name === "Alice", user2.name === "Bob"
// Copie complète — instances indépendantes

Sémantique de référence de Class

Class est stocké dans le tas et prend en charge l'égalité par référence. Plusieurs variables peuvent référencer le même objet, les modifications via une référence sont visibles via toutes les autres. Class prend en charge l'héritage, permet la redéfinition de méthodes (override), a deinit pour le nettoyage des ressources. Dans Swift, class est moins utilisée que struct — Apple recommande de commencer par struct et de passer à class seulement lorsque l'héritage ou la sémantique de référence sont nécessaires.

swift
class Car {
    let model: String
    var speed: Int

    init(model: String, speed: Int) {
        self.model = model
        self.speed = speed
    }

    func accelerate(_ amount: Int) {
        speed += amount
    }
}

let car1 = Car(model: "Tesla", speed: 0)
let car2 = car1
car2.accelerate(50)
// car1.speed === 50, car2.speed === 50
// Même référence — modifications visibles partout

Le choix entre struct et class dépend de la sémantique des données. Pour les configurations, modèles de données et états, utilisez struct — la copie est plus sûre et prévisible. Pour les objets avec identité (session utilisateur, connexion base de données) et lorsque l'héritage est nécessaire, utilisez class.

Actor — sécurité des threads dans Swift

Actor est un type référence qui protège son état mutable contre l'accès concurrent. Le compilateur Swift garantit que les variables d'actor ne sont accessibles que depuis le même contexte d'actor — tout accès externe provoque une erreur de compilation. Cela prévient les conditions de course au niveau du langage, sans verrous manuels.

Isolation d'Actor

Toutes les propriétés et méthodes d'actor sont isolées par défaut. L'appel d'une méthode d'actor depuis l'extérieur nécessite await — Swift bascule l'exécution vers l'actor et inversement. L'actor synchronise l'accès séquentiellement : les requêtes de différentes tâches sont mises en file d'attente. Cela garantit un état cohérent à l'intérieur de l'actor. Les méthodes non isolées (nonisolated) peuvent être appelées sans await mais ne peuvent pas accéder aux propriétés mutables.

swift
actor BankAccount {
    private var balance: Double

    init(initialBalance: Double) {
        balance = initialBalance
    }

    func deposit(amount: Double) {
        balance += amount
    }

    func withdraw(amount: Double) throws {
        guard balance >= amount else {
            throw BankError.insufficientFunds
        }
        balance -= amount
    }

    nonisolated func accountInfo() -> String {
        return "Compte Bancaire"
    }
}

let account = BankAccount(initialBalance: 1000)
await account.deposit(amount: 500)
// balance = 1500 — atomique, sans condition de course

Actor est utile pour les gestionnaires d'état, caches et services réseau où plusieurs tâches lisent et écrivent des données concurrentiellement. Contrairement à la synchronisation manuelle via NSLock ou DispatchQueue, l'actor est vérifié à la compilation — il est impossible de faire une erreur. Les erreurs d'isolation apparaissent à la compilation, pas à l'exécution.

Property Wrappers : @Published, @State, @Binding

Property Wrapper est une annotation qui ajoute du comportement à une propriété sans modifier sa déclaration. SwiftUI et Combine fournissent des wrappers intégrés pour gérer l'état des vues. Le compilateur génère du code qui encapsule l'accès à la propriété, ajoutant une logique d'observation, de synchronisation ou de transformation.

@Published et @State

@Published est utilisé dans les classes conformes à ObservableObject pour publier automatiquement les changements. Les abonnés Combine reçoivent des notifications à chaque mise à jour. @State est l'état local d'une vue SwiftUI. SwiftUI redessine automatiquement la vue lorsque @State change. @Binding crée une connexion bidirectionnelle entre une vue parent et enfant sans posséder les données.

swift
import SwiftUI
import Combine

@main
struct CounterApp: App {
    var body: some Scene {
        WindowGroup {
            ContentView()
        }
    }
}

class CounterViewModel: ObservableObject {
    @Published var count: Int = 0

    func increment() {
        count += 1
    }
}

struct ContentView: View {
    @State private var greeting: String = "Bonjour"
    @StateObject private var viewModel = CounterViewModel()

    var body: some View {
        VStack {
            CounterDisplay(count: $viewModel.count)
            Text(greeting)
            Button("Incrémenter", action: viewModel.increment)
        }
    }
}

struct CounterDisplay: View {
    @Binding var count: Int

    var body: some View {
        Text("Compteur : \(count)")
    }
}

Les Property Wrappers personnalisés sont créés avec @propertyWrapper et une structure avec un champ wrappedValue. Cela permet de réutiliser la logique : validation, normalisation, stockage UserDefaults. Les Property Wrappers réduisent considérablement le code boilerplate et rendent l'intention du développeur explicite via l'annotation.

swift
@propertyWrapper
struct Clamped<T: Comparable> {
    private var value: T
    let range: ClosedRange<T>

    init(wrappedValue: T, range: ClosedRange<T>) {
        self.range = range
        self.value = min(max(wrappedValue, range.lowerBound), range.upperBound)
    }

    var wrappedValue: T {
        get { value }
        set { value = min(max(newValue, range.lowerBound), range.upperBound) }
    }
}

struct Settings {
    @Clamped(range: 0...100) var volume: Int = 50
}

var s = Settings()
s.volume = 150
// s.volume === 100 — valeur automatiquement limitée

Generics et Programmation Orientée Protocole

Generics est un mécanisme puissant de Swift pour écrire du code flexible sans perdre la sécurité de typage. Les fonctions et types génériques fonctionnent avec tout type satisfaisant les contraintes. La Programmation Orientée Protocole (POP) est un paradigme Swift où protocol + extension remplacent l'héritage classique des classes, offrant une composition de comportement.

Contraintes Génériques et Types Associés

Swift permet de contraindre les paramètres génériques avec des protocoles (clauses where). Protocole avec type associé est l'équivalent générique pour les protocoles : le type concret est déterminé au moment de la conformité. C'est la base pour les collections et algorithmes type-safe. La bibliothèque standard de Swift utilise activement les generics : Array, Dictionary, Optional — tous sont des types génériques.

swift
protocol Cacheable {
    associatedtype Key: Hashable
    associatedtype Value

    func get(_ key: Key) -> Value?
    mutating func set(_ value: Value, for key: Key)
}

struct MemoryCache<K: Hashable, V>: Cacheable {
    private var storage: [K: V] = [:]

    typealias Key = K
    typealias Value = V

    func get(_ key: K) -> V? {
        return storage[key]
    }

    mutating func set(_ value: V, for key: K) {
        storage[key] = value
    }
}

func firstMatch<T: Equatable>(in array: [T], target: T) -> Int? {
    for (index, item) in array.enumerated() {
        if item == target {
            return index
        }
    }
    return nil
}

let numbers = [10, 20, 30, 40]
let index = firstMatch(in: numbers, target: 30)
// index === 2 — une méthode générique pour tous les types Equatable

Extension de Protocole et Implémentations par Défaut

L'extension de protocole est un mécanisme clé de POP. Dans une extension, on peut fournir une implémentation par défaut des méthodes du protocole. Les types conformes au protocole obtiennent cette implémentation gratuitement et peuvent la redéfinir si nécessaire. Cela permet de construire des hiérarchies de comportement sans héritage de classes — une alternative à l'héritage multiple, qui n'existe pas en Swift.

swift
protocol Drivable {
    var speed: Double { get set }
    func drive()
}

extension Drivable {
    func drive() {
        if speed > 0 {
            print("Conduite à \(speed) km/h")
        }
    }
}

struct Bicycle: Drivable {
    var speed: Double = 15
}

struct Plane: Drivable {
    var speed: Double = 900

    func drive() {
        print("Vol à \(speed) km/h")
    }
}

let bike = Bicycle()
bike.drive() // "Conduite à 15.0 km/h" — par défaut

let plane = Plane()
plane.drive() // "Vol à 900.0 km/h" — redéfini

POP avec extension permet la composition via de multiples protocoles — un type peut se conformer à plusieurs protocoles, obtenant leurs implémentations. C'est plus flexible que l'héritage unique de classes où toute la hiérarchie est fixe. La bibliothèque standard de Swift est construite sur POP : CustomStringConvertible, Equatable, Hashable, Codable — tous sont des protocoles avec extensions.

Enum avec valeurs associées et Extension

Enum dans Swift est une construction nettement plus puissante que dans d'autres langages. Chaque cas peut avoir des valeurs associées — un ensemble arbitraire de données de n'importe quel type. Combiné avec extension et pattern matching, enum devient la base pour modéliser les états, les résultats d'opérations et les machines à états finis.

Valeurs Associées et Pattern Matching

Les valeurs associées permettent à chaque cas de stocker des données uniques. Par exemple, le cas loading ne contient pas de données, success contient le résultat, failure contient une erreur. Le pattern matching via switch extrait les valeurs associées et traite chaque cas. Le compilateur vérifie l'exhaustivité (exhaustive switch) — cela élimine les états oubliés.

swift
enum NetworkResult<T> {
    case loading
    case success(T)
    case failure(Error)
}

enum MediaFile {
    case image(url: URL, width: Int, height: Int)
    case video(url: URL, duration: Double)
    case audio(url: URL, bitrate: Int)
}

func handle(_ result: NetworkResult<String>) {
    switch result {
    case .loading:
        print("Chargement...")
    case .success(let data):
        print("Reçu : \(data)")
    case .failure(let error):
        print("Erreur : \(error.localizedDescription)")
    }
}

let file = MediaFile.video(url: URL(string: "https://example.com/video.mp4")!, duration: 120)

if case .video(let url, let duration) = file {
    print("Vidéo \(url) durée \(duration)s")
}

Extension pour Enum

Swift permet d'ajouter des méthodes, des propriétés calculées et la conformité aux protocoles via extension pour enum. Cela ne nécessite pas de modifier la déclaration originale — le code reste propre et modulaire. Les méthodes dans l'extension peuvent travailler avec des valeurs associées, calculer des données dérivées et implémenter la logique métier.

swift
extension MediaFile {
    var fileName: String {
        switch self {
        case .image(let url, _, _),
             .video(let url, _),
             .audio(let url, _):
            return url.lastPathComponent
        }
    }

    var description: String {
        switch self {
        case .image(_, let w, let h):
            return "Image \(w)x\(h)"
        case .video(_, let d):
            return "Vidéo \(d)s"
        case .audio(_, let b):
            return "Audio \(b) kbps"
        }
    }
}

extension MediaFile: Equatable {
    static func == (lhs: MediaFile, rhs: MediaFile) -> Bool {
        return lhs.fileName == rhs.fileName
    }
}

let files: [MediaFile] = [file]
files.forEach { print($0.description) }
// "Vidéo 120.0s"

Enum avec valeurs associées + extension est le remplacement des classes sealed de Kotlin ou des unions discriminées d'autres langages. Swift utilise cette combinaison pour Result (un type standard de la bibliothèque standard), Optional (Optional est un enum avec .none et .some) et la gestion d'état dans SwiftUI. Le pattern matching garantit que tous les états sont traités — les erreurs d'exécution sont impossibles.

Questions Fréquentes

En quoi struct diffère-t-il de class dans Swift ?

Struct — type valeur, copié lors de l'affectation, ne prend pas en charge l'héritage. Class — type référence, passé par référence, prend en charge l'héritage, deinit et la vérification d'identité (===). Apple recommande de commencer par struct et de passer à class uniquement lorsque la sémantique de référence est nécessaire.

Qu'est-ce qu'Actor dans Swift et quand l'utiliser ?

Actor — type référence thread-safe. Le compilateur interdit l'accès concurrent à l'état mutable depuis différents contextes. Utilisez Actor pour les gestionnaires d'état, caches et services accessibles par plusieurs tâches. L'appel de méthodes d'actor nécessite await.

Quels Property Wrappers sont utilisés dans SwiftUI ?

Principaux : @State — état local de la vue ; @Binding — connexion aux données sans possession ; @Published — publication des changements dans ObservableObject ; @StateObject — possession d'ObservableObject ; @EnvironmentObject — dépendance de l'environnement. Les wrappers personnalisés sont créés via @propertyWrapper.

Qu'est-ce que la Programmation Orientée Protocole ?

POP — un paradigme où protocol + extension définissent le comportement au lieu de l'héritage de classe. Un type se conforme à plusieurs protocoles, obtenant leurs implémentations gratuitement. C'est la composition plutôt que l'héritage — plus flexible et plus sûr. La bibliothèque standard de Swift (Equatable, Codable) est construite sur POP.

En quoi enum dans Swift diffère-t-il d'enum dans d'autres langages ?

Enum dans Swift prend en charge les valeurs associées — chaque cas peut stocker des données arbitraires de différents types. Combiné avec le pattern matching (switch), cela fournit un modèle puissant pour les états, les erreurs et le Domain-Driven Design. Optional et Result sont des enums standard de Swift avec valeurs associées.

Résumé

  • Struct vs Class — différence fondamentale : les types valeur sont copiés, les types référence sont passés par référence. Struct ne prend pas en charge l'héritage, class oui. Apple recommande struct par défaut.
  • Actor — sécurité des threads au niveau du compilateur. État mutable isolé, accès via await, protection contre les conditions de course sans verrous manuels.
  • Property Wrappers — @Published, @State, @Binding et personnalisés via @propertyWrapper. Réduisent le code boilerplate et rendent l'intention explicite via l'annotation.
  • Generics — fonctions et types type-safe avec contraintes where. Protocol + type associé pour les abstractions. La bibliothèque standard de Swift est entièrement construite sur les generics.
  • Programmation Orientée Protocole — composition de comportement via l'extension de protocole. Les implémentations par défaut permettent la réutilisation du code sans héritage de classes.
  • Enum avec valeurs associées — un modèle puissant pour les états. Chaque cas stocke des données uniques. Le pattern matching garantit que tous les états sont traités.
  • Extension — ajoute des méthodes, propriétés calculées et conformité aux protocoles pour tout type, y compris enum. Le code reste modulaire et propre sans modifier la déclaration originale.

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.

Discuter du projet