Introduction
Swift est un langage de programmation puissant et intuitif pour l'écosystème Apple. Cependant, sa syntaxe concise et son typage strict déroutent souvent les débutants. Dans cet article, nous allons analyser les erreurs les plus fréquentes commises par les développeurs iOS et macOS débutants. Nous parcourrons le chemin de la lettre A (Automatic Reference Counting) jusqu'au Z (déballage explicite des optionnels). Chaque point est accompagné d'un exemple de code réel et d'une explication pour éviter le problème.
1. Erreurs avec les Optionals (nil et déballage)
L'un des points forts de Swift est la gestion sécurisée de nil. Mais les débutants ignorent souvent les optionnels ou utilisent l'extraction forcée (!) là où ce n'est pas sûr.
1.1. Extraction forcée (Force Unwrapping) sans vérification
L'erreur la plus courante est de mettre un point d'exclamation sans s'assurer que la valeur n'est pas nil.
// ❌ Erreur : l'application plantera si user n'est pas trouvé
let user = users.first!
print(user.name)
// ✅ Correct : extraction sécurisée
if let user = users.first {
print(user.name)
} else {
print("Utilisateur non trouvé")
}
1.2. Ignorer guard let
Beaucoup de débutants utilisent des if let imbriqués, créant une « pyramide de la mort ». Utilisez guard let pour une sortie anticipée de la fonction.
// ❌ Mauvais : if let imbriqués
func process(user: User?) {
if let unwrappedUser = user {
if let email = unwrappedUser.email {
print(email)
}
}
}
// ✅ Bon : guard let
func process(user: User?) {
guard let unwrappedUser = user, let email = unwrappedUser.email else {
return
}
print(email)
}
2. Gestion de la mémoire (ARC et cycles de références fortes)
Swift utilise l'Automatic Reference Counting (ARC), mais ce n'est pas magique. Les débutants créent souvent des fuites mémoire en oubliant la capture dans les closures.
2.1. Références fortes dans les closures
Lorsque vous passez une closure qui capture self, et que self stocke cette closure, un cycle se crée.
class NetworkManager {
var onComplete: (() -> Void)?
func fetchData() {
// ❌ Erreur : référence forte à self
onComplete = {
self.updateUI()
}
// ✅ Correct : référence faible
onComplete = { [weak self] in
self?.updateUI()
}
}
func updateUI() { /* ... */ }
}
2.2. Oublier unowned
Parfois vous êtes sûr que l'objet ne sera pas nil. Utilisez [unowned self], mais seulement si vous garantissez que self ne sera pas libéré avant la closure.
// Dangereux : unowned avec des appels asynchrones
class ViewController {
func loadImage() {
ImageService.shared.fetch { [unowned self] image in
// Si self est libéré, l'application plantera
self.imageView.image = image
}
}
}
// Plus sûr : weak
func loadImage() {
ImageService.shared.fetch { [weak self] image in
self?.imageView.image = image
}
}
3. Pièges syntaxiques typiques
Même une syntaxe simple peut dérouter si l'on ne connaît pas les nuances de Swift.
3.1. Confusion entre classes et structures
Les débutants font souvent tout en classe, oubliant que les structures sont des value types et les classes des reference types.
// ❌ Erreur : modification d'une propriété de structure à l'intérieur d'elle-même
struct User {
var name: String
mutating func changeName(to newName: String) {
name = newName // Sans mutating, ne compilera pas
}
}
// ✅ Pour une classe, mutating n'est pas nécessaire
class UserClass {
var name: String
func changeName(to newName: String) {
name = newName
}
}
3.2. Mauvaise utilisation de defer
defer s'exécute à la sortie de la portée actuelle, mais les débutants s'attendent parfois à ce qu'il s'exécute immédiatement.
func readFile() {
let file = openFile()
defer {
closeFile(file) // S'exécute à la fin de la fonction
}
// Traitement du fichier...
}
4. Problèmes de protocoles et de délégation
Le pattern de délégation est fondamental en iOS, mais son implémentation peut être source d'erreurs.
4.1. Oublier de marquer le protocole comme class
Si un protocole peut être adopté par une classe, marquez-le avec : class pour permettre des références faibles.
// ❌ Erreur : ne peut pas être utilisé avec weak
protocol DataDelegate {
func didUpdateData()
}
// ✅ Correct : héritage de class
protocol DataDelegate: class {
func didUpdateData()
}
class ViewController {
weak var delegate: DataDelegate? // Fonctionne uniquement avec : class
}
5. Gestion des erreurs (try/catch)
Swift offre une gestion robuste des erreurs, mais les débutants l'ignorent ou l'utilisent mal.
5.1. Ignorer les erreurs avec try?
Utiliser try? sans vérifier le résultat peut masquer des problèmes.
// ❌ Erreur : l'erreur est ignorée
let data = try? fetchData()
// ✅ Correct : gestion explicite
do {
let data = try fetchData()
// Utiliser data
} catch {
print("Erreur : \(error)")
}
Conclusion
Swift est un langage élégant mais exigeant. En évitant ces erreurs courantes, vous écrirez un code plus sûr et plus maintenable. N'oubliez pas : utilisez guard let pour les sorties anticipées, [weak self] dans les closures, et préférez les structures aux classes quand c'est possible. Bonne programmation !