Планировщик задач Swift для iOS/macOS: EventKit и DispatchSource

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

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

Начать курс

Планировщик задач на Swift для iOS и macOS: полное руководство

В современной разработке под iOS и macOS планировщик задач (task scheduler) — это ключевой компонент для создания эффективных и отзывчивых приложений. Swift предоставляет несколько мощных инструментов для реализации планирования: от простых таймеров до интеграции с системным календарём через EventKit и низкоуровневых механизмов DispatchSource. В этой статье мы подробно разберём, как создать надёжный планировщик задач на Swift для обеих платформ.

Основные компоненты планировщика задач Swift

Прежде чем писать код, важно понять, какие инструменты доступны разработчику Swift:

  • Timer — базовый класс для выполнения кода через заданные интервалы или с задержкой.
  • DispatchSourceTimer — более производительный таймер на основе Grand Central Dispatch (GCD).
  • EventKit — фреймворк для работы с календарём и напоминаниями на iOS/macOS.
  • UNUserNotificationCenter — система локальных уведомлений для отложенного оповещения пользователя.
  • Background Tasks — для выполнения фоновых задач на iOS 13+.

Базовый таймер с использованием Timer

Самый простой способ создать планировщик задач — использовать Timer. Однако помните, что он работает в основном потоке, если не указать обратное.

import Foundation

class SimpleTaskScheduler {
    var timer: Timer?
    
    func scheduleTask(interval: TimeInterval, repeats: Bool, task: @escaping () -> Void) {
        timer = Timer.scheduledTimer(withTimeInterval: interval, repeats: repeats) { _ in
            task()
        }
    }
    
    func cancel() {
        timer?.invalidate()
        timer = nil
    }
}

// Пример использования
let scheduler = SimpleTaskScheduler()
scheduler.scheduleTask(interval: 5.0, repeats: true) {
    print("Задача выполнена через 5 секунд")
}

Продвинутый планировщик с DispatchSourceTimer

Для более точного и производительного планирования используйте DispatchSourceTimer. Он работает в фоновых очередях и не блокирует UI.

import Foundation

class AdvancedTaskScheduler {
    private var timer: DispatchSourceTimer?
    private let queue = DispatchQueue(label: "com.pythonlib.scheduler", qos: .background)
    
    func scheduleTask(deadline: DispatchTime, interval: DispatchTimeInterval? = nil, leeway: DispatchTimeInterval = .seconds(1), task: @escaping () -> Void) {
        timer?.cancel()
        timer = DispatchSource.makeTimerSource(queue: queue)
        
        if let interval = interval {
            timer?.schedule(deadline: deadline, repeating: interval, leeway: leeway)
        } else {
            timer?.schedule(deadline: deadline, leeway: leeway)
        }
        
        timer?.setEventHandler { [weak self] in
            guard self?.timer != nil else { return }
            task()
        }
        
        timer?.activate()
    }
    
    func cancel() {
        timer?.cancel()
        timer = nil
    }
}

// Использование
let advancedScheduler = AdvancedTaskScheduler()
advancedScheduler.scheduleTask(deadline: .now() + 2.0, interval: .seconds(10)) {
    print("Фоновая задача выполняется каждые 10 секунд")
}

Интеграция с EventKit для календарных задач

EventKit позволяет создавать задачи, которые синхронизируются с системным календарём. Это идеально для приложений-планировщиков.

import EventKit

class CalendarTaskScheduler {
    let eventStore = EKEventStore()
    
    func requestAccess(completion: @escaping (Bool) -> Void) {
        eventStore.requestAccess(to: .event) { granted, error in
            completion(granted)
        }
    }
    
    func createScheduledTask(title: String, startDate: Date, endDate: Date, notes: String? = nil) -> EKEvent? {
        let event = EKEvent(eventStore: eventStore)
        event.title = title
        event.startDate = startDate
        event.endDate = endDate
        event.notes = notes
        event.calendar = eventStore.defaultCalendarForNewEvents
        
        do {
            try eventStore.save(event, span: .thisEvent)
            return event
        } catch {
            print("Ошибка сохранения задачи: \(error.localizedDescription)")
            return nil
        }
    }
    
    func fetchTasks(from startDate: Date, to endDate: Date) -> [EKEvent] {
        let predicate = eventStore.predicateForEvents(withStart: startDate, end: endDate, calendars: nil)
        return eventStore.events(matching: predicate)
    }
}

// Пример
let calendarScheduler = CalendarTaskScheduler()
calendarScheduler.requestAccess { granted in
    if granted {
        let task = calendarScheduler.createScheduledTask(
            title: "Встреча с командой",
            startDate: Date().addingTimeInterval(3600),
            endDate: Date().addingTimeInterval(7200),
            notes: "Обсуждение спринта"
        )
        print("Задача создана: \(task?.title ?? "Ошибка                    

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