Firebase and UI synchronization

What is the recommended pattern to synchronize UI actions with DB retrieval?

  • UI should become responsive to further actions upon DB retrieval completion
  • background readahead data retrieval should be initiated for the next likely view

Both have the same problem to be solved: how in UI to wait for the completion of the background retrieval from Firebase DB or FireStore. Are there best practice examples with explanations? Is there good detailed documentation on this topic?

I am new to Swift and Firebase, so maybe my questions sound naive, I am open to more in-depth reading, specifically related to the Swift threading model, async execution, and inter-thread synchronization. My background is in Java: does Swift have language or library-supported primitives prevalent in Java world
such as mutexes, semaphores, conditions, barriers etc

Thank you

There are several recommended patterns to synchronize UI actions with DB retrieval in Swift and Firebase:

  1. Combine Framework: Combine is a reactive framework built into Swift that uses publishers and subscribers for data flow. You can use Combine to listen for data updates from Firebase and update the UI accordingly. This approach is declarative and promotes cleaner code. Here's some sample code to get you started with Combine and Firebase:

Swift

// Swift code with Combine
func fetchDataFromFirebase() -> AnyPublisher<[Data], Error> {
  // ... Firebase data fetching logic here
}

fetchDataFromFirebase()
  .sink(receiveCompletion: { completion in
    // Handle completion (e.g., errors)
  }, receiveValue: { data in
    // Update UI with the retrieved data
  })
  .store(in: &cancellables)

  1. Grand Central Dispatch (GCD): GCD is Apple's framework for managing threads and queues. You can use GCD to perform background data retrieval on a separate thread and then dispatch the update back to the main thread to update the UI. Here's an example using GCD:

Swift

// Swift code with GCD
func fetchDataFromFirebaseBackground(completion: @escaping ([Data]) -> Void) {
  DispatchQueue.global().async {
    // ... Firebase data fetching logic here
    DispatchQueue.main.async {
      completion(data)  // Update UI on main thread
    }
  }
}

fetchDataFromFirebaseBackground { data in
  // Update UI with the retrieved data
}
  1. Async/Await: Swift 5.5 introduced async/await syntax for asynchronous programming. You can use async/await to perform asynchronous tasks like fetching data from Firebase and then update the UI when the data is retrieved. Here's an example using async/await:

Swift

// Swift code with async/await
func fetchDataFromFirebaseAsync() async throws -> [Data] {
  // ... Firebase data fetching logic here
}

Task {
  do {
    let data = try await fetchDataFromFirebaseAsync()
    // Update UI with the retrieved data
  } catch {
    // Handle errors
  }
}

Thank you, that helps.

Maybe I was not looking hard enough but I could not find this level of clarity.

Presumably, the async syntax is the latest and probably should be preferred as it is also the most concise and straightforward.
As I understand (correct me if I am wrong)

    let data = try await fetchDataFromFirebaseAsync()
    // Update UI with the retrieved data

should be executed on the main thread. what guarantees that it is happening in this snippet? Should it be explicitly queued to the main thread?