Comment arrêter d'avoir peur des délais et aimer GCD en Swift 3, 4 et 5
\n\nAvez-vous déjà écrit du code qui devait s'exécuter après trois secondes, mais qui se déclenchait instantanément ? Ou, au contraire, avez-vous attendu une éternité alors que vous prévoyiez quelques millisecondes ? Cela vous dit quelque chose ? Alors vous avez certainement rencontré dispatch_after — l'un des outils les plus populaires, mais aussi les plus sournois de Grand Central Dispatch (GCD).
\n\nAvant, à l'époque de Swift 2 et Objective-C, cette méthode ressemblait à une incantation : dispatch_after(dispatch_time(DISPATCH_TIME_NOW, Int64(nanoseconds)), dispatch_get_main_queue()). Mais avec l'arrivée de Swift 3, 4 et 5, l'API GCD a été entièrement réécrite. Ce n'est pas simplement un changement de nom — c'est une nouvelle façon de penser. Dans cet article, nous allons voir comment écrire dispatch_after en Swift sur toutes les versions actuelles, dissiper les mythes et fournir des solutions prêtes à l'emploi. C'est parti !
Qu'est-il arrivé à dispatch_after ? Une brève histoire de douleur
\nAvant Swift 3, les développeurs utilisaient le GCD de style C :
\nlet delay = dispatch_time(DISPATCH_TIME_NOW, Int64(2.0 * Double(NSEC_PER_SEC)))\ndispatch_after(delay, dispatch_get_main_queue()) {\n print("Bonjour du passé !")\n}\nCela fonctionnait, mais c'était lourd. En Swift 3, Apple a décidé de "swiftiser" GCD, le transformant en une API orientée objet. Désormais, dispatch_after est devenu la méthode asyncAfter de la structure DispatchQueue. Et ce n'est pas qu'une simple rénovation cosmétique — c'est une syntaxe entièrement nouvelle qui nécessite une compréhension.
La recette de base : asyncAfter en Swift 3, 4 et 5
\nQuelle que soit la version de Swift (3, 4 ou 5), la syntaxe de base de dispatch_after est désormais la même. Voici l'exemple le plus simple d'exécution différée sur le thread principal :
\n\nlet delayInSeconds: Double = 2.0\nDispatchQueue.main.asyncAfter(deadline: .now() + delayInSeconds) {\n print("2 secondes se sont écoulées !")\n}\nPoints clés :
\n- \n
- DispatchQueue.main — le thread principal (UI). \n
- .now() + delayInSeconds — crée une deadline (échéance) par rapport au moment présent. \n
- asyncAfter — exécution asynchrone après le temps spécifié. \n
Ce code fonctionne en Swift 3.0, 4.0 et 5.0 sans modification. La magie réside dans le fait qu'Apple a laissé cette API stable depuis 2016.
\n\nEt dispatch_time ? Travailler avec les nanosecondes
\nParfois, vous devez spécifier un délai en nanosecondes (par exemple, pour des minuteries précises ou de la logique de jeu). Dans la nouvelle API, on utilise DispatchTime :
\nlet nanoseconds: Int = 1_500_000_000 // 1.5 secondes\nlet deadline = DispatchTime.now() + .nanoseconds(nanoseconds)\nDispatchQueue.global().asyncAfter(deadline: deadline) {\n print("Exécuté après 1.5 seconde sur un thread d'arrière-plan")\n}\nVous pouvez également utiliser des constantes prêtes à l'emploi : .seconds(5), .milliseconds(500) et même .microseconds(1000). C'est plus pratique que de multiplier par NSEC_PER_SEC.
Questions fréquentes (et réponses !) sur dispatch_after en Swift
\nSur Stack Overflow, cette question a recueilli des milliers de vues. Voici les pièges les plus courants dans lesquels tombent les débutants.
\n\n1. Pourquoi mon code s'exécute-t-il immédiatement au lieu d'être différé ?
\nL'erreur la plus courante est la confusion entre asyncAfter et async. Si vous oubliez de spécifier deadline: ou si vous écrivez DispatchQueue.main.async { ... }, le code s'exécutera immédiatement. La deuxième raison : la transmission d'une valeur négative. .now() + (-5.0) est un temps passé, GCD exécutera le bloc immédiatement.
2. Comment annuler dispatch_after ?
\nContrairement à Timer, asyncAfter n'a pas de méthode cancel() intégrée. Mais il existe deux solutions :
- \n
- Drapeau d'annulation : Utilisez un
DispatchWorkItemet appelez sa méthodecancel(). \n - Variable booléenne : Vérifiez une condition avant d'exécuter le bloc. \n
Voici un exemple avec DispatchWorkItem :
let workItem = DispatchWorkItem {\n print("Ceci peut être annulé")\n}\nDispatchQueue.main.asyncAfter(deadline: .now() + 3.0, execute: workItem)\n// Pour annuler :\nworkItem.cancel()