Magie de DispatchQueue.main.asyncAfter : comment fonctionne le délai en Swift 3

Formateur Python en ligne pour débutants

Apprenez Python facilement sans surcharge théorique. Résolvez des tâches pratiques avec vérification automatique et écrivez du code directement dans le navigateur.

Start Course

Introduction : pourquoi « attendre » n'est pas aussi simple qu'il n'y paraît

\n\n

Imaginez : vous développez une application iOS en Swift 3. Vous devez afficher une animation, puis la masquer après quelques secondes. Ou envoyer une requête à un serveur, mais pas immédiatement, avec un délai. Cela semble simple, non ? Il suffit d'écrire sleep(2) et le tour est joué. Mais si vous faites cela, votre application se transformera en glaçon : l'interface se bloquera, l'utilisateur commencera à taper sur l'écran, et au final, vous obtiendrez un avis négatif et une perte de classement.

\n\n

La question « How to program a delay in Swift 3 » a cumulé des milliers de vues sur Stack Overflow. Et ce n'est pas un hasard : les débutants tombent constamment dans les mêmes pièges. Dans cet article, nous ne nous contenterons pas de donner une recette toute faite. Nous allons sous le capot du mécanisme des délais, comprendre comment fonctionne DispatchQueue.main.asyncAfter, et saisir pourquoi sleep est un mal. Et je vais aussi vous montrer une astuce qui vous fera gagner des heures de débogage.

\n\n

Pourquoi sleep() est un anti-patron en Swift

\n\n

Commençons par dissiper l'idée reçue la plus courante. Beaucoup de ceux qui viennent d'autres langages (par exemple, de Python avec son time.sleep()) essaient de faire ceci :

\n\n
sleep(2) // bloque le thread pendant 2 secondes\nprint("2 secondes se sont écoulées")
\n\n

Ce code bloque le thread actuel. Si vous l'appelez sur le thread principal (main thread) — c'est fini, l'application « s'endort » pendant 2 secondes. L'interface utilisateur ne se met pas à jour, les boutons ne répondent pas, les animations ne fonctionnent pas. iOS tuera votre application si le thread principal est bloqué trop longtemps (généralement plus de 10 secondes).

\n\n

De plus, sleep() ne tient pas compte de l'état du RunLoop. C'est un appel système brutal qui empêche le système de traiter normalement les événements. En Swift 3 (et versions ultérieures), la bonne approche est d'utiliser Grand Central Dispatch (GCD).

\n\n

DispatchQueue.main.asyncAfter : la bonne façon de créer un délai

\n\n

La méthode la plus courante et recommandée pour différer l'exécution de code en Swift 3 est la méthode asyncAfter de GCD. Voici à quoi elle ressemble :

\n\n
DispatchQueue.main.asyncAfter(deadline: .now() + 2.0) {\n    print("2 secondes se sont écoulées (mode non bloquant)")\n}
\n\n

Ici, nous disons au système : « Dans 2 secondes à partir du moment présent, exécute ce bloc de code sur le thread principal ». Le thread principal reste libre — les animations fonctionnent, les boutons répondent, l'utilisateur est content.

\n\n

Mais creusons plus profondément. Qu'est-ce que deadline ? Pourquoi .now() + 2.0 et pas simplement 2.0 ? Et que se passe-t-il « sous le capot » ?

\n\n

Anatomie de asyncAfter : deadline, DispatchTime et DispatchQoS

\n\n

asyncAfter prend un paramètre deadline de type DispatchTime. Ce n'est pas simplement un nombre de millisecondes, mais un point absolu dans le temps, lié à l'horloge système. Lorsque vous écrivez .now() + 2.0, GCD calcule : « heure actuelle + 2 secondes » et place la tâche dans la file d'attente avec cette étiquette.

\n\n

Sous le capot, GCD utilise mach_absolute_time() — un minuteur matériel qui ne dépend pas des changements de l'heure système. Autrement dit, si l'utilisateur recule l'horloge, votre délai ne sera pas perturbé (contrairement à Timer, qui peut se comporter de manière imprévisible).

\n\n

Un détail important : deadline est le temps minimal avant l'exécution. GCD ne garantit pas que le code s'exécutera exactement dans 2 secondes. Si le système est occupé (par exemple, en plein traitement de données complexes), l'exécution peut être retardée. Pour la plupart des tâches d'interface utilisateur, ce n'est pas critique, mais pour les minuteurs de haute précision (par exemple, dans les jeux), il est préférable d'utiliser CADisplayLink.

\n\n

Un autre paramètre est qos (Quality of Service). Vous pouvez spécifier la priorité de la tâche :

\n\n
DispatchQueue.global(qos: .userInitiated).asyncAfter(deadline: .now() + 1.0) {\n    // tâche à haute priorité\n}
\n\n

Blogs

Book Recommendations