Enrollment Polling with Timer and @MainActor

by Annett Schwarze | 2026-06-05

For our Music Theory Learning app, we implemented student enrollment and faced an interesting architectural decision. Rather than maintaining a Supabase Realtime WebSocket connection, a lightweight Timer polls every 10 minutes for pending enrollment requests. The badge counter updates automatically, and the approach is easy to reason about and debug—a pragmatic trade-off that works well for low-frequency events. Running the update logic on @MainActor ensures that UI state changes remain thread-safe and predictable. This design also reduces infrastructure complexity while keeping resource usage low.

func startEnrollmentPolling() {
    stopEnrollmentPolling()
    enrollmentTimer = Timer.scheduledTimer(withTimeInterval: 60 * 10, repeats: true) { [weak self] _ in
        Task { @MainActor [weak self] in
            await self?.fetchPendingEnrollments()
        }
    }
}

func fetchPendingEnrollments() async {
    do {
        let requests = try await enrollmentService.fetchPendingEnrollments()
        pendingEnrollments = requests.map { mapToUI($0) }
        enrollmentCount = pendingEnrollments.count
        hasNewEnrollments = enrollmentCount > 0   // drives the badge
    } catch {
        loadPendingEnrollmentsOffline()  // graceful fallback to cache
    }
}
    
Polling Polling

 

www.admadic.de | webmaster@admadic.de | Legal Notice and Trademarks | Privacy
© 2005-2007 - admaDIC | All Rights Reserved
All other trademarks and/or registered trademarks are the property of their respective owners