Sanity Check: Using a forever while loop within a Task to create a service

Hi everyone, thanks for reading.

I have a few long-running services within my app. I'm looking for a sanity check that there isn't anything wrong with doing something like this to create these services:

final actor MyService {
  private var serviceTask: Task<(), Error>?

  func start() {
    serviceTask = Task {
      repeat {
          do {
              try await Task.sleep(nano: 1_000_000_000 * 30) // sleep for 30 seconds. This throws if task is cancelled
              await makeANetworkRequest()
          } catch {
      } while !Task.isCancelled

  func stop() {

The goal here is to do some async work with a 30 second gap between firing. This is all good and well in the world of Swift Concurrency, right? The Task will suspend while sleeping, which allows other code to execute on the same thread, allowing the thread to always make forward progress.

I've been going down some ill-informed rabbit holes and started converting these services to use Timers, but I think I'm realizing that the problems were elsewhere.

1 Like

Hi Mark,

I don't see anything wrong with this approach, in fact this is very similar to code I've been writing in my apps. What's fundamental -- and you are correctly doing -- is checking Task.isCancelled or Task.checkCancellation() so that the loop breaks eventually.


Yeah this will be fine in general.

Minor thing is that you may want to use the Task.sleep(for:) overload which takes a nicer argument in the form of Duration: try await Task.sleep(for: .seconds(30)) sleep(for:tolerance:clock:) | Apple Developer Documentation

I guess one thing to be aware of is that you're making the request then sleeping... if you need the requests to be periodic on the dot you might want to account the time the request takes and substract from the sleep time and things like that. But if you don't care much about the skew this'll be good enough.