Introdução: por que "esperar" não é tão simples quanto parece
\n\nImagine: você está escrevendo um aplicativo para iOS em Swift 3. Precisa mostrar uma animação e, depois de alguns segundos, ocultá-la. Ou enviar uma requisição ao servidor, mas não imediatamente, sim com um atraso. Parece simples, não? É só pegar e escrever sleep(2) — e pronto. Mas se você fizer isso, seu aplicativo vai congelar: a interface trava, o usuário começa a tocar na tela e, no final, você recebe uma avaliação negativa e perde reputação.
A pergunta "How to program a delay in Swift 3" teve milhares de visualizações no Stack Overflow. E não é por acaso: iniciantes sempre pisam nos mesmos ancinhos. Neste artigo, não vamos apenas dar uma receita pronta. Vamos mergulhar por baixo dos panos do mecanismo de atrasos, entender como funciona o DispatchQueue.main.asyncAfter e compreender por que o sleep é um problema. E ainda vou mostrar um truque que vai economizar horas de depuração.
Por que sleep() é um antipadrão no Swift
\n\nVamos logo esclarecer o equívoco mais comum. Muitos que vêm de outras linguagens (como Python com seu time.sleep()) tentam fazer assim:
sleep(2) // bloqueia a thread por 2 segundos\nprint("Passaram-se 2 segundos")\n\nEsse código bloqueia a thread atual. Se você o chamar na thread principal (main thread) — pronto, o aplicativo "dorme" por 2 segundos. A interface do usuário não é atualizada, os botões não respondem, as animações não funcionam. O iOS simplesmente mata seu aplicativo se a thread principal ficar bloqueada por muito tempo (geralmente mais de 10 segundos).
\n\nAlém disso, sleep() não considera o estado do RunLoop. É uma chamada de sistema bruta que não permite que o sistema processe eventos normalmente. No Swift 3 (e posteriores), o caminho correto é usar o Grand Central Dispatch (GCD).
DispatchQueue.main.asyncAfter: a maneira correta de criar um atraso
\n\nA forma mais comum e recomendada de adiar a execução de código no Swift 3 é o método asyncAfter do GCD. Veja como ele é:
DispatchQueue.main.asyncAfter(deadline: .now() + 2.0) {\n print("Passaram-se 2 segundos (modo não bloqueante)")\n}\n\nAqui, dizemos ao sistema: "Daqui a 2 segundos a partir do momento atual, execute este bloco de código na thread principal". A thread principal permanece livre — as animações funcionam, os botões respondem, o usuário fica feliz.
\n\nMas vamos mais fundo. O que é deadline? Por que .now() + 2.0 e não apenas 2.0? E o que acontece "por baixo dos panos"?
Anatomia do asyncAfter: deadline, DispatchTime e DispatchQoS
\n\nasyncAfter aceita o parâmetro deadline do tipo DispatchTime. Não é apenas um número de milissegundos, mas um ponto absoluto no tempo, vinculado ao relógio do sistema. Quando você escreve .now() + 2.0, o GCD calcula: "hora atual + 2 segundos" e coloca a tarefa na fila com essa marca.
Por baixo dos panos, o GCD usa mach_absolute_time() — um timer de hardware que não depende de alterações na hora do sistema. Ou seja, se o usuário atrasar o relógio, seu atraso não será afetado (ao contrário do Timer, que pode se comportar de forma imprevisível).
Uma nuance importante: deadline é o tempo mínimo até a execução. O GCD não garante que o código será executado exatamente após 2 segundos. Se o sistema estiver ocupado (por exemplo, processando dados complexos), a execução pode atrasar. Para a maioria das tarefas de UI, isso não é crítico, mas para timers de alta precisão (como em jogos), é melhor usar CADisplayLink.
Outro parâmetro é qos (Quality of Service). Você pode definir a prioridade da tarefa:
DispatchQueue.global(qos: .userInitiated).asyncAfter(deadline: .now() + 1.0) {\n // tarefa de alta prioridade\n}\n\n
Blogs
Book Recommendations
Leia também
Monorepositório com Java: organização de código na prática
Sessão PHP expira em 30 minutos: 4 maneiras funcionais
Depuração de código PHP: técnicas e ferramentas para profissionais
CI/CD para projetos Rust: configuração do GitHub Actions
Depuração de código Go: do iniciante ao profissional em uma noite