Swift concurrency: return cache and then load remote

Hi,

Is there any way to return a value instantly and then reload the remote resources in an async/await call?
I want to serve cached response, reload remote data and return on asynchronous call finish.

Is it possible?

Thanks!

I see no blockers to do that. Something like:

func getValue(andUpdateIfNeeded updateCallback: @escaping (Value) -> Void) -> Value {
  let cachedValue = ...
  
  if isExpired(cachedValue) {
    makeRequest(completion: { newValue in
      updateCallback(newValue)
    }
  }

  return cachedValue
}

or

func getValue() -> (cachedValue: Value, updateIfNeeded: () async -> Value?) {
}

let (cachedValue, updateIfNeeded) = getValue()

// use cached value synchronously 

Task {
  let updatedValue = await updateIfNeeded()
  if let updatedValue {
    // ...
  }
}

I'd prefer the callback version with the callback which is called twice:

func getResource(url: URL, execute: @escaping (Data) -> Void) {
    let cachedResponse = ...
    execute(cachedResponse?.data)
    reload { execute($0) }
}

print("before")
getResource { data in
    print("got data")
}
print("after")

Outputs:

The async/await version of that could be done to make the use sites like so:

print("before")
for await data in url.asyncData {
    print("got data")
}
print("after")

with "got data" printed twice, first with cached data then with the new data after reload. Note that in this case the output would be:

Thanks @Dmitriy_Ignatyev and @tera

Looking at your suggestions, I think I've found the solution:

extension URLSession {
    
    func cachedDataAndReloadStream(for request: URLRequest) -> AsyncStream<(Data, URLResponse)> {
        
        AsyncStream<(Data, URLResponse)> { continuation in
            
            // Return cache
            if let cached = URLCache.shared.cachedResponse(for: request) {
                continuation.yield((cached.data, cached.response))
            }
            
            // Reload
            Task {
                
                let response = try await URLSession.shared.data(for: request)
                continuation.yield(response)
                continuation.finish()
                
            }
            
        }
        
    }
    
}
let request = makeRequest(baseURLString: "https://www.google.es")
for await response in URLSession.shared.cachedDataAndReloadStream(for: request) {
    
    // First cache, then reloaded
    
}
3 Likes