Cours Swift Complet 🍎

Les 12 chapitres essentiels — Optionals, protocols, enums, closures et l'écosystème Apple

12
Chapitres
100+
Exemples de code
Swift 5.10+
Version
A1→C2
Niveaux

CHAPITRE 01

Introduction et premier programme

🍎 Hello World
print(« Bonjour le monde ! »)
print(« Nom: \(nom), Age: \(age) ») // String interpolation

Swift est simple. Pas besoin de main(), pas de ;, pas de header files. Un fichier .swift suffit. L'interpolation de string \(expression) est puissante : elle accepte n'importe quelle expression.

⚙️ Installer et exécuter
# macOS — Xcode inclut Swift
xcode-select –install

# Ou via Swift.org (Linux/Windows aussi)
# https://www.swift.org/install/

# REPL interactif
swift

# Exécuter un fichier
swift hello.swift

# Créer un package Swift
swift package init –type executable
swift run

# Compiler
swiftc hello.swift -o hello

🏗️ Swift en résumé
CaractéristiqueSwift
TypageStatique et fort avec inférence
ParadigmeMulti-paradigme : POO + fonctionnel + protocol-oriented
MémoireARC (Automatic Reference Counting — pas de GC)
SûretéOptionals (pas de null crash), type-safe
PerformanceCompilé (LLVM), performance proche du C
UsageiOS, macOS, watchOS, tvOS, serveur (Vapor)
ConventioncamelCase (fonctions/variables), PascalCase (types)

Swift a remplacé Objective-C comme langage principal pour l'écosystème Apple. Plus sûr (optionals), plus lisible, et avec des features modernes (enums avec données, pattern matching, async/await). Il est aussi utilisé côté serveur avec Vapor.

🧠 Quiz
Qu'est-ce que ARC en Swift ?
Automatic Reference Counting. Swift compte les références vers chaque objet. Quand le compteur tombe à 0, l'objet est libéré. C'est automatique mais déterministe (pas de pause GC), contrairement au garbage collector de Java ou Go.

CHAPITRE 02

Variables et types de données

📦 let vs var
// let = constante (PRÉFÉRÉ — immuable)
let nom = « Alice » // Type inféré : String
let pi = 3.14159 // Double
let age: Int = 25 // Type explicite

// var = variable (mutable)
var score = 0
score += 10 // ✅ OK

// nom = « Bob » ❌ Erreur — let est immuable

Xcode vous avertit si vous utilisez var alors que la variable n'est jamais modifiée. Privilégiez toujours let — c'est le standard Swift pour du code sûr et prévisible.

🔢 Types fondamentaux
TypeDescriptionExemple
IntEntier (64 bits sur les systèmes modernes)42
DoubleFlottant 64 bits (par défaut)3.14
FloatFlottant 32 bitsFloat(3.14)
BoolBooléentrue / false
StringChaîne Unicode« Hello \(name) »
CharacterUn caractère Unicode« A » as Character
📝 Strings
let s = « Hello World »

// Interpolation
let msg = « J'ai \(age) ans et \(age >= 18 ? « majeur » : « mineur ») »

// Multi-ligne
let texte = «  » »
Première ligne
Deuxième ligne
«  » »

// Méthodes
s.count // 11
s.isEmpty // false
s.uppercased() // « HELLO WORLD »
s.lowercased() // « hello world »
s.contains(« World ») // true
s.hasPrefix(« He ») // true
s.hasSuffix(« ld ») // true
s.replacingOccurrences(of: « World », with: « Swift »)
s.split(separator:  » « ) // [« Hello », « World »]
s.trimmingCharacters(in: .whitespaces)

// Conversion
let n = Int(« 42 ») // Optional Int? (peut échouer)
let s = String(42) // « 42 »

CHAPITRE 03

Optionals

❓ Le concept fondamental de Swift
// Optional = une valeur qui peut être absente (nil)
var nom: String? = « Alice » // String? = Optional
var age: Int? = nil // Pas de valeur

// ⚠️ On ne peut PAS utiliser un Optional directement
// print(nom) → Optional(« Alice ») — pas ce qu'on veut

// 1. if let — unwrap conditionnel (LE PLUS COURANT)
if let name = nom else

// 2. if let raccourci (Swift 5.7+) — même nom
if let nom

// 3. guard let — early exit (préféré dans les fonctions)
func greet(_ nom: String?)

// 4. ?? — valeur par défaut (nil coalescing)
let name = nom ?? « Inconnu » // « Alice » ou « Inconnu »

// 5. ?. — optional chaining
let count = nom?.count // Int? (nil si nom est nil)

// 6. ! — force unwrap (DANGEREUX — crash si nil)
let name = nom! // ⚠️ Crash si nil !

N'utilisez JAMAIS ! en production sauf si vous êtes 100% certain que la valeur n'est pas nil. C'est la source n°1 de crashs iOS. Préférez if let, guard let ou ??.

🧠 Quiz
Différence entre if let et guard let ?
if let = la valeur déballée n'existe que dans le bloc if. guard let = la valeur déballée existe pour tout le reste de la fonction, et le else doit quitter la fonction (return/throw). guard let réduit l'indentation.

CHAPITRE 04

Structures de contrôle

🔀 if, switch, for
// if — pas de parenthèses
if age >= 18 else

// if expression (Swift 5.9+)
let label = if age >= 18 else

// switch — EXHAUSTIF, pas de break (implicite), pas de fallthrough
switch grade

// switch avec ranges et where
switch age

// for-in — la seule boucle nécessaire
for i in 0..<5
for i in 05
for item in array
for (i, val) in array.enumerated()
for _ in 0..<3

// while, repeat-while (do-while)
while condition
repeat while condition

CHAPITRE 05

Collections

📋 Array, Set, Dictionary
// Array — ordonné, doublons possibles
var nums: [Int] = [1, 2, 3] var names = [« Alice », « Bob »] // Type inféré
let empty: [String] = [] // Tableau vide

nums.append(4) // [1, 2, 3, 4]
nums.insert(0, at: 0) // [0, 1, 2, 3, 4]
nums.remove(at: 0) // [1, 2, 3, 4]
nums.removeLast() // [1, 2, 3]
nums.count // 3
nums.isEmpty // false
nums.contains(2) // true
nums.first // Optional(1)
nums.last // Optional(3)
nums.sorted() // Retourne un nouveau tableau trié
nums.reversed() // Retourne une vue inversée

// Set — non ordonné, éléments uniques
var tags: Set<String> = [« swift », « ios », « swift »] //
tags.insert(« mobile »)
tags.contains(« swift ») // true
tags.intersection(other) // éléments communs
tags.union(other) // tous les éléments

// Dictionary — clé/valeur
var ages: [String: Int] = [« Alice »: 25, « Bob »: 30] ages[« Charlie »] = 28 // Ajouter/modifier
ages[« Alice »] // Optional(25) — toujours Optional !
ages[« Eve »] ?? 0 // 0 si absent
ages.removeValue(forKey: « Bob »)

for (nom, age) in ages

CHAPITRE 06

Structs et Classes

📦 Struct (VALUE type — le choix par défaut)
struct Person

var alice = Person(name: « Alice », age: 25)
alice.birthday() // 26

// Struct = VALUE TYPE → copie à l'affectation
var bob = alice // bob est une COPIE indépendante
bob.age = 40 // alice.age n'est pas affecté

🏛️ Class (REFERENCE type)
class Vehicle

// Class = REFERENCE TYPE → partage la même instance
let car1 = Vehicle(brand: « Tesla »)
let car2 = car1 // car2 pointe vers la MÊME instance
car2.speed = 100 // car1.speed = 100 aussi !

Règle d'or Swift : préférez les Structs. Utilisez les classes uniquement pour l'héritage ou quand vous avez besoin de sémantique de référence. Apple recommande Struct par défaut — c'est plus sûr et plus performant.

⚖️ Struct vs Class
CritèreStruct (valeur)Class (référence)
CopieCopie indépendantePartage la même instance
Héritage❌ Non✅ Oui
MutabilitéNécessite mutatingPas besoin
InitMemberwise autoManuelle
ARCNon (stack)Oui (heap)
RecommandéPar défautQuand nécessaire
🧠 Quiz
Pourquoi mutating est nécessaire pour les méthodes de struct ?
Parce qu'une struct est un value type. Par défaut, self est immuable dans une méthode. mutating indique que la méthode va modifier les propriétés de l'instance, ce qui nécessite que la variable soit déclarée avec var.

CHAPITRE 07

Enums et Pattern Matching

🎭 Enums Swift — first-class
// Enum simple
enum Direction
let d: Direction = .north

// Enum avec raw values
enum Planet: Int
Planet.earth.rawValue // 3
Planet(rawValue: 1) // Optional(.mercury)

// Enum avec associated values (données par variante)
enum Result

let r: Result = .success(data: « Hello »)

// Pattern matching avec switch
switch r

Les enums Swift sont aussi puissants que ceux de Rust. Chaque case peut contenir des données différentes (associated values). Le switch doit être exhaustif — le compilateur garantit que tous les cas sont traités.

🔧 Enums avec méthodes et CaseIterable
enum Season: String, CaseIterable

// CaseIterable — itérer sur tous les cas
for season in Season.allCases

CHAPITRE 08

Gestion d'erreurs

🛡️ throw / do-catch
// Définir des erreurs (enum conforme à Error)
enum NetworkError: Error

// Fonction qui peut lancer une erreur
func fetchUser(id: Int) throws -> String

// do-catch
do catch NetworkError.notFound catch NetworkError.serverError(let code) catch

// try? — retourne nil si erreur
let user = try? fetchUser(id: 1) // nil

// try! — crash si erreur (comme force unwrap)
let user = try! fetchUser(id: 1) // ⚠️ Crash si erreur

⚡ Result type
// Result — comme en Rust
func divide(_ a: Double, by b: Double) -> Result<Double, Error>

switch divide(10, by: 3)

CHAPITRE 09

Closures

⚡ Closures (fonctions anonymes)
// Syntaxe complète
let add =
add(3, 4) // 7

// Raccourci — type inféré, return implicite
let add: (Int, Int) -> Int =

// Trailing closure syntax
let nums = [3, 1, 4, 1, 5] let sorted = nums.sorted // [1, 1, 3, 4, 5]
let sorted = nums.sorted(by: <) // Encore plus court

🌊 Méthodes fonctionnelles sur les collections
let nums = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]

// map — transformer chaque élément
let doubled = nums.map // [2, 4, 6, 8, …]

// filter — garder les éléments qui satisfont la condition
let evens = nums.filter // [2, 4, 6, 8, 10]

// reduce — réduire à une seule valeur
let sum = nums.reduce(0, +) // 55

// compactMap — map + filtre nil
let strings = [« 1 », « abc », « 3 »] let ints = strings.compactMap // [1, 3]

// flatMap — aplatir les tableaux imbriqués
let nested = [[1,2], [3,4], [5]] let flat = nested.flatMap // [1, 2, 3, 4, 5]

// Chaîner
let result = nums
.filter
.map
.reduce(0, +) // (2+4+6+8+10)*3 = 90

// Autres méthodes
nums.first(where: ) // Optional(6)
nums.contains(where: ) // true
nums.allSatisfy // true
nums.min() // Optional(1)
nums.max() // Optional(10)

CHAPITRE 10

Generics

🔄 Fonctions et types génériques
// Fonction générique
func swapValues<T>(_ a: inout T, _ b: inout T)

// Avec contrainte
func largest<T: Comparable>(_ a: T, _ b: T) -> T

// Struct générique
struct Stack<Element>

var stack = Stack<Int>()
stack.push(1)
stack.push(2)
stack.pop() // Optional(2)

CHAPITRE 11

Concurrence (async/await)

⚡ async/await (Swift 5.5+)
// Fonction asynchrone
func fetchUser(id: Int) async throws -> User

// Appeler une fonction async
Task

// async let — exécution parallèle
async let user1 = fetchUser(id: 1)
async let user2 = fetchUser(id: 2)
let users = try await [user1, user2] // Parallèle !

// TaskGroup — nombre dynamique de tâches
let results = try await withThrowingTaskGroup(of: User.self)

🔒 Actors (Swift 5.5+)
// Actor = classe thread-safe (accès exclusif garanti)
actor Counter

let counter = Counter()
await counter.increment() // await obligatoire (isolation)
let val = await counter.value()

// @MainActor — exécuter sur le thread principal (UI)
@MainActor
func updateUI()

Les Actors éliminent les data races comme Rust, mais au runtime plutôt qu'à la compilation. L'accès aux propriétés d'un actor nécessite await, garantissant l'exclusion mutuelle.

CHAPITRE 12

Protocols et bonnes pratiques

📋 Protocols — le cœur de Swift
// Protocol = contrat (comme interface)
protocol Describable

// Extension de protocol — implémentation par défaut
extension Describable

// Conformer (struct ou class)
struct Book: Describable

// Composition de protocols
protocol Identifiable

func display(_ item: Describable & Identifiable)

// Protocols standard importants
// Equatable → ==
// Comparable → < >
// Hashable → clé de Dictionary/Set
// Codable → JSON encode/decode
// CustomStringConvertible → print()
// Identifiable → SwiftUI lists

Protocol-Oriented Programming (POP) est le paradigme préféré de Swift. Plutôt que l'héritage de classes, on compose des behaviours avec des protocols + extensions. C'est plus flexible, testable et fonctionne avec les structs.

📦 Codable — sérialisation JSON
// Codable = Encodable + Decodable
struct User: Codable

// Encode (Swift → JSON)
let user = User(name: « Alice », email: « alice@mail.com », age: 25)
let json = try JSONEncoder().encode(user)

// Decode (JSON → Swift)
let decoded = try JSONDecoder().decode(User.self, from: json)

✅ Bonnes pratiques

✅ À FAIRE
let par défaut (immutabilité)
struct par défaut (value type)
guard let pour l'early exit
Codable pour le JSON
?? au lieu de force unwrap
• Protocol-oriented (POP)
async/await pour la concurrence
• Extensions pour organiser le code
• Access control (private, fileprivate)

❌ À ÉVITER
! force unwrap en production
var quand let suffit
• Classes quand struct suffit
• Héritage profond (préférer protocols)
• Massive View Controllers (MVVM)
Any / AnyObject abusif
• Retain cycles (weak/unowned)
• Ignorer les warnings Xcode
• Singletons excessifs

🔄 Retain cycles et weak/unowned
// Problème : deux classes se référencent mutuellement → memory leak
class Person
class Pet

// Dans les closures — capture list
class ViewModel

🧠 Quiz
Qu'est-ce que Protocol-Oriented Programming ?
Un paradigme où le comportement est défini par des protocols + extensions plutôt que par l'héritage de classes. On compose des capacités (Equatable, Codable, Hashable) au lieu de créer des hiérarchies. Fonctionne avec les structs (value types).
Pourquoi weak dans les closures ?
Pour éviter les retain cycles. Si une closure capture self fortement et que l'objet possède la closure, ni l'un ni l'autre ne sera libéré. [weak self] rompt le cycle en ne comptant pas dans ARC.

Cours Swift Complet — Optionals, Protocols et l'écosystème Apple

Référence : The Swift Book | Apple Developer