Hey guys! I’m struggling with `withContext` in pro...
# coroutines
g
Hey guys! I’m struggling with
withContext
in production code (as opposite to hello world examples)… Where do you put
withContext
in your code? Is it inside suspending function body, or at the call site? Also, if you have nested suspending functions, let’s say a typical android app with ViewModel > Repository > DataSource, where DataSource contains suspending functions - where do you put
withContext
? Is there any official guidelines for this?
s
I tend to put it in the body of a
suspend
function, around the block of code that may be blocking (e.g. network access or something).
☝️ 1
o
The general rule is you just put it wherever you need to change into another context. For example, before a blocking file read / network access, I do
withContext(<http://Dispatchers.IO|Dispatchers.IO>) { readFile() }
☝️ 1
Generally you do not use it when starting a new coroutine, as most coroutine builders accept a
context
parameter that is more efficient
s
Copy code
class DataStoreImpl(private val context: CoroutineContext, private val service: NetworkService) : DataStore {
    ...
    suspend override fun getData() : MyResult = withContext(context) {
        val nwData = service.getData() // blocking call
        val data : MyResult = ... ... nwData ...
        data
    }
    ...
}
And you’d provide
<http://Dispatchers.IO|Dispatchers.IO>
for the
context
property.
m
You do it closer to the actual code that needs it. A repository for example may make network requests, file operations and heavy parsing. It would switch to IO for the former two and Default for the latter. Outside of the repository you can't (shouldn't) know what you need because of abstraction.
☝️ 1
s
^^^ Applying SOLID principles 🙂
g
Ok, so if my DataSource has a suspend function that uses Firebase Storage (which is callback based), you think the best way of doing it would be like this?
Copy code
class DataSource {
  suspend fun getFile(...) = withContext(<http://Dispatchers.IO|Dispatchers.IO>) {
      suspendCoroutine {
          // firebase code...
      }
  }
}
o
you don't need
<http://Dispatchers.IO|Dispatchers.IO>
if it's callback based
s
But you want to use coroutines to avoid callbacks 🙂
o
yes, wrapping it with
suspendCoroutine
is the right call
s
Ah yes, of course… 🙂
g
So is my whole snippet correct or not? ’cause I’m confused now ;D
s
Looks good! Does firebase use a callback?
g
…Or shall I skip withContext in this case?
Yep, Firebase has this addOnSuccessListener, addOnFailureListener etc.
o
drop the
withContext
-- it's not necessary in this case, because it's for blocking code. if you suspend, you don't need to be in that context
s
Using withContext, in your example, would be for blockig code. Yours doesn’t block, like Kenzie said.
When your callback is called by firebase, use the
Continuation
provided by
suspendCoroutine
to resume the
suspend fun getFile(...)
.
g
Hmmm, you got me… I thought that this “waiting” for callback to be called also blocks the thread.
s
How does the firebase code look like?
g
@streetsofboston yeah, I’m doing it
I can’t show you the whole code, but I’m calling resume & resumeWithException from firebase callbacks. And it works. I just thought I still need to set the proper context. But maybe you’re right guys 😉
s
Ah… good. No, that is not necessary. Sometimes, e.g. with some Bluetooth stuff, you must even call the callback registration functions on a particular thread (or on the same thread). Then you’d use
withContext
as well.
g
My code looks something like this:
Copy code
class DataSource {
  suspend fun getFile(...) = withContext(<http://Dispatchers.IO|Dispatchers.IO>) {
      suspendCoroutine {
          firebase.getFile(...)
            .addOnSuccessListener { resume(it) }
            .addOnFailureListener { resumeWithException(it) }
      }
  }
}
But thanks to you I know I can skip withContext entirely 🙂
s
Yep;
firebase
doesn’t require its methods to be called on a particular thread, they can be called from any thread. You won’t need
withContext
.
g
Ok, thanks a lot guys! I really appreciate it 👍