Reading time: 1 min

This recipe shows how to perform delayed actions in SwiftUI. It also shows examples of how to implement a delayed onAppear for async and non-async blocks. The end result looks like this:

preview

There are two ways to delay actions in Swift in general:

  • Using DispatchQueue with asyncAfter with a non-async block:
DispatchQueue.main.asyncAfter(deadline: .now() + .seconds(5)) {
 // delayed action
}
  • Using an async block with Task.sleep:
func myFunc() async {
  try? await Task.sleep(nanoseconds: UInt64(5 * 1E9))
  // delayed action
}

The most common use-case of delayed actions in SwiftUI is to perform an action when the view appears after a fixed period of time. Here are a few extensions that implement delayed onAppear:

extension View {
  // sync block on a DispatchQueue at specified deadline
  func perform(on queue: DispatchQueue = .main,
               at deadline: DispatchTime,
               action: @escaping () -> Void) -> some View {
    onAppear {
      queue.asyncAfter(deadline: deadline, execute: action)
    }
  }

  // sync block on a DispatchQueue after the specified interval
  func perform(on queue: DispatchQueue = .main,
               after interval: TimeInterval,
               action: @escaping () -> Void) -> some View {
    perform(on: queue, at: .now() + interval, action: action)
  }

  // async block on main thread after the specified interval
  func perform(after interval: TimeInterval,
               action: @escaping @Sendable () async -> Void) -> some View {
    task {
      try? await Task.sleep(nanoseconds: UInt64(interval * 1E9))
      await action()
    }
  }
}

And here are all three in action:

@State private var text = "Wait for it..."

var body: some View {
  Text(text)
    .perform(at: .now() + .seconds(2)) {
      text = "2 seconds passed"
    }
    .perform(after: 4) {
      text = "4 seconds passed"
    }
    .perform(after: 3) {
      try? await Task.sleep(nanoseconds: UInt64(3 * 1E9))
      text = "6 seconds passed in total"
    }
}

Next Post Previous Post