Skip to content

Instantly share code, notes, and snippets.

@davidalbers
Created October 14, 2021 21:23
Show Gist options
  • Save davidalbers/6c10ac744b4ef49560e646475ecd7757 to your computer and use it in GitHub Desktop.
Save davidalbers/6c10ac744b4ef49560e646475ecd7757 to your computer and use it in GitHub Desktop.
/**
* Signal the apollo client to fetch the data from both the network and the cache. If cached data is not present, only
* network data will be returned. If cached data is available, but network experiences an error, only cached data is
* returned. If cache data is not available, and network data is not available, the error
* of the network request will be propagated. If both network and cache are available, both will be returned. Cache data
* is guaranteed to be returned first.
*
* Modified from Apollo's CacheAndNetworkFetcher
* https://github.com/apollographql/apollo-android/blob/f72c3afd17655591aca90a6a118dbb7be9c50920/apollo-runtime/src/main/java/com/apollographql/apollo/internal/fetcher/CacheAndNetworkFetcher.java#L1
*/
class CacheWithOptionalNetworkFetcher : ResponseFetcher {
override fun provideInterceptor(apolloLogger: ApolloLogger): ApolloInterceptor {
return CacheAndNetworkInterceptor()
}
private class CacheAndNetworkInterceptor : ApolloInterceptor {
private var cacheResponse = Optional.absent<InterceptorResponse>()
private var networkResponse = Optional.absent<InterceptorResponse>()
private var cacheException = Optional.absent<ApolloException>()
private var networkException = Optional.absent<ApolloException>()
private var dispatchedCacheResult = false
private var originalCallback: ApolloInterceptor.CallBack? = null
@Volatile
private var disposed = false
override fun interceptAsync(
request: InterceptorRequest, chain: ApolloInterceptorChain,
dispatcher: Executor, callBack: ApolloInterceptor.CallBack
) {
if (disposed) return
originalCallback = callBack
val cacheRequest = request.toBuilder().fetchFromCache(true).build()
chain.proceedAsync(cacheRequest, dispatcher, object : ApolloInterceptor.CallBack {
override fun onResponse(response: InterceptorResponse) {
handleCacheResponse(response)
}
override fun onFailure(e: ApolloException) {
handleCacheError(e)
}
override fun onCompleted() {}
override fun onFetch(sourceType: FetchSourceType) {
callBack.onFetch(sourceType)
}
})
val networkRequest = request.toBuilder().fetchFromCache(false).build()
chain.proceedAsync(networkRequest, dispatcher, object : ApolloInterceptor.CallBack {
override fun onResponse(response: InterceptorResponse) {
handleNetworkResponse(response)
}
override fun onFailure(e: ApolloException) {
handleNetworkError(e)
}
override fun onCompleted() {}
override fun onFetch(sourceType: FetchSourceType) {
callBack.onFetch(sourceType)
}
})
}
override fun dispose() {
disposed = true
}
@Synchronized
fun handleNetworkResponse(response: InterceptorResponse) {
networkResponse = Optional.of(response)
dispatch()
}
@Synchronized
fun handleNetworkError(exception: ApolloException) {
networkException = Optional.of(exception)
dispatch()
}
@Synchronized
fun handleCacheResponse(response: InterceptorResponse) {
cacheResponse = Optional.of(response)
dispatch()
}
@Synchronized
fun handleCacheError(exception: ApolloException) {
cacheException = Optional.of(exception)
dispatch()
}
@Synchronized
private fun dispatch() {
if (disposed) {
return
}
if (!dispatchedCacheResult) {
if (cacheResponse.isPresent) {
val cached = cacheResponse.get()
originalCallback!!.onResponse(cached)
dispatchedCacheResult = true
} else if (cacheException.isPresent) {
dispatchedCacheResult = true
}
}
// Only send the network result after the cache result has been dispatched
if (dispatchedCacheResult) {
if (networkResponse.isPresent) {
originalCallback!!.onResponse(networkResponse.get())
originalCallback!!.onCompleted()
}
}
if (cacheException.isPresent && networkException.isPresent) {
// only send network exception if both network and cache have failed
originalCallback!!.onFailure(networkException.get())
}
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment