Skip to content

DataPublisher created from async function should start lazily #693

@alexvasilkov

Description

@alexvasilkov

I need to show a photo grid where each photo has a tiny preview stored in local DB, and I want to display these previews shortly until a bigger thumbnail is loaded from remote URL.

To load local previews I'm creating a custom ImageRequest like this:

    let request = ImageRequest(
      id: "preview_\(photoId)",
      data: { await loadPreviewFromDb(photoId) },
      options: [.disableDiskCache]
    )

Now when the grid is shown only the first 6 previews are loaded and the rest are just ignored, even though I can confirm that the actual data is loaded from the DB correctly. In the logs I see Data loader returned empty data. errors.

Adding .skipDataLoadingQueue option kinda helps but I still see random blank previews from time to time.

It turns out the async data function is called eagerly when it's converted into Publisher (see DataPublisher.swift/publisher(...) code), and PassthroughSubject will ignore data that was sent while it had no subscribers (queued?).

I managed to fix it by using lazy Publisher created like this:

    let request = ImageRequest(
      id: "preview_\(photoId)",
      dataPublisher: Future { await loadPreviewFromDb(photoId) },
      options: [.disableDiskCache]
    )
private extension Future where Failure == Error {
  convenience init(action: @escaping () async throws -> Output) {
    self.init { promise in
      Task {
        do {
          let result = try await action()
          promise(.success(result))
        } catch {
          promise(.failure(error))
        }
      }
    }
  }
}

This seems to work just fine, but I think it worth fixing it in the library as well.

Metadata

Metadata

Assignees

No one assigned

    Labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions