Как создать задержку в Swift: 5 проверенных способов

Онлайн-тренажер Python для начинающих

Изучайте Python легко и без перегрузки теорией. Решайте практические задачи с автоматической проверкой, получайте подсказки на русском языке и пишите код прямо в браузере — без необходимости что-либо устанавливать.

Начать курс

«Подожди секунду»: почему задержки в Swift — это магия, а не хаки

Представьте: вы пишете приложение для заказа пиццы. Пользователь нажимает «Оплатить», и мгновенно — бам! — списание денег. Без анимации, без паузы, без чувства «что-то происходит». Пользователь пугается, закрывает приложение, а вы теряете клиента. Задержка в Swift — это не костыль, а инструмент UX-дизайна и правильной архитектуры.

Вопрос «How to create a delay in Swift?» на Stack Overflow собрал десятки тысяч просмотров. И неудивительно: новички пытаются использовать sleep() (спойлер: так нельзя), а профи ищут элегантные решения. Мы разберём 5 рабочих способов — от классического GCD до современных async/await. Поехали!

1. Классика: DispatchQueue.main.asyncAfter (iOS 4+)

Самый популярный способ создания задержки в Swift — это DispatchQueue.main.asyncAfter. Он работает на любой версии iOS и не требует дополнительных импортов. Подходит для выполнения кода на главном потоке (например, обновление UI).

import UIKit

class ViewController: UIViewController {
    override func viewDidLoad() {
        super.viewDidLoad()
        
        // Задержка 2 секунды
        DispatchQueue.main.asyncAfter(deadline: .now() + 2.0) {
            print("Прошло 2 секунды!")
            self.showAlert()
        }
    }
    
    func showAlert() {
        let alert = UIAlertController(title: "Привет!", message: "Это задержка", preferredStyle: .alert)
        alert.addAction(UIAlertAction(title: "OK", style: .default))
        present(alert, animated: true)
    }
}

Плюсы и минусы

  • Плюсы: простота, обратная совместимость, работает на главном потоке.
  • Минусы: нельзя отменить задержку (если не сохранить DispatchWorkItem).

2. Отменяемая задержка через DispatchWorkItem

Иногда нужно отменить задержку — например, пользователь ушёл с экрана. DispatchWorkItem решает эту проблему. Сохраняем ссылку на «работу» и вызываем cancel().

class DelayedTask {
    private var workItem: DispatchWorkItem?
    
    func scheduleDelayedAction() {
        // Создаём задачу
        let item = DispatchWorkItem { [weak self] in
            print("Задача выполнена после задержки")
            self?.doSomething()
        }
        
        // Сохраняем и запускаем
        workItem = item
        DispatchQueue.main.asyncAfter(deadline: .now() + 3.0, execute: item)
    }
    
    func cancelDelayedAction() {
        workItem?.cancel()
        workItem = nil
        print("Задача отменена")
    }
    
    func doSomething() {
        // Ваш код
    }
}

// Использование
let task = DelayedTask()
task.scheduleDelayedAction()
// Через 1 секунду отменяем
DispatchQueue.main.asyncAfter(deadline: .now() + 1.0) {
    task.cancelDelayedAction()
}

Обратите внимание на [weak self] — это обязательно, чтобы избежать утечек памяти в Swift. Задержка может пережить контроллер, если не ослабить ссылку.

3. Timer.scheduledTimer: задержка с повторением

Таймеры Swift — это не только про повторяющиеся задачи. Можно создать одноразовый таймер с задержкой. Плюс: таймер легко отменяется и может быть приостановлен.

class TimerExample {
    var timer: Timer?
    
    func startDelayedAction() {
        // Одноразовый таймер через 1.5 секунды
        timer = Timer.scheduledTimer(withTimeInterval: 1.5, repeats: false) { [weak self] _ in
            print("Таймер сработал через 1.5 сек")
            self?.updateUI()
        }
    }
    
    func stopTimer() {
        timer?.invalidate()
        timer = nil
    }
    
    func updateUI() {
        // Обновляем интерфейс
    }
}

Когда использовать Timer

  • Нужно отменить задержку в любой момент.
  • Задержка может превратиться в периодическое действие.
  • Работа с анимациями (например, задержка перед скрытием элемента).

4. Современный Swift: async/await и Task.sleep (iOS 13+)

Начиная с iOS 13, Swift поддерживает асинхронность через async/await. Task.sleep — это самый «свифтовый» способ создать задержку. Код читается как обычный синхронный, но не блокирует поток.

import SwiftUI

struct ContentView: View {
    @State private var message = "Ожидание..."
    
    var body: some View {
        Text(message)
            .font(.largeTitle)
            .task {
                await simulateNetworkRequest()
            }
    }
    
    func simulateNetworkRequest() async {
        print("Начинаем задержку...")
        
        // Задержка 3 секунды (Task.sleep принимает наносекунды!)
        try? await Task.sleep(nanoseconds: 3_000_000_000)
        
        // Обновляем UI (главный поток гарантирован)
        message = "Готово! Прошло 3 секунды"
        print("Задержка завершена")
    }
}

Важно: Task.sleep использует наносекунды. Чтобы не ошибаться, используйте константы:

let seconds = 2.0
try? await Task.sleep(nanoseconds: UInt64(seconds * 1_000_000_000))

Почему это лучше GCD?

  • Чистый, линейный код без вложенных замыканий.
  • Автоматическая обработка отмены (через Task.isCancelled).
  • Безопасная работа с UI (SwiftUI автоматически переключает поток).

5. Combine: задержка в реактивном стиле (iOS 13+)

Если вы используете Combine для обработки событий, delay(for:scheduler:) — ваш выбор. Позволяет отложить поток данных на определённое время.

import Combine

class CombineViewModel: ObservableObject {
    @Published var searchText = ""
    private var cancellables = Set<AnyCancellable>()
    
    init() {
        $searchText
            .debounce(for: .seconds(0.5), scheduler: RunLoop.main) // Задержка 0.5 сек
            .sink { [weak self] text in
                print("Поиск после паузы: \(text)")
                self?.performSearch(text)
            }
            .store(in: &cancellables)
    }
    
    func performSearch(_ query: String) {
        // Реальный поисковый запрос
    }
}

Этот пример — классический «debounce» для поискового поля. Задержка помогает избежать лишних запросов, пока пользователь печатает.

Чего делать НЕ стоит: sleep() и RunLoop

Золотое правило iOS-разработки: никогда не вызывайте Thread.sleep(forTimeInterval:) или sleep() на главном потоке. Это блокирует UI, приложение «зависает», и пользователь думает, что оно с

Блоги

Рекомендаций по книгам