Skip to content

Instantly share code, notes, and snippets.

@beyondeye
Created May 27, 2017 20:32
Show Gist options
  • Star 6 You must be signed in to star a gist
  • Fork 1 You must be signed in to fork a gist
  • Save beyondeye/f69ba427938f79801c291d18131ff1d9 to your computer and use it in GitHub Desktop.
Save beyondeye/f69ba427938f79801c291d18131ff1d9 to your computer and use it in GitHub Desktop.
integration of firebase wth kotlin coroutines
/**
* allow to define callback wrappers that are protected from accidental multiple calls to resume/resumeWithException
* Created by daely on 3/30/2017.
*/
class WrappedContinuation<T>(val c: Continuation<T>) : Continuation<T> {
var isResolved = false
override val context: CoroutineContext
get() = c.context
override fun resume(value: T) {
if (!isResolved) {
isResolved = true
c.resume(value)
}
}
override fun resumeWithException(exception: Throwable) {
if (!isResolved) {
isResolved = true
c.resumeWithException(exception)
}
}
}
inline suspend fun <T> suspendCoroutineW(crossinline block: (WrappedContinuation<T>) -> Unit): T =
suspendCoroutine { c ->
val wd = WrappedContinuation(c)
block(wd)
}
suspend fun<T> readFromDatabaseAsync(dbref: DatabaseReference, dataType: Class<T>): T = suspendCoroutineW { d ->
dbref.addListenerForSingleValueEvent(object : ValueEventListener {
override fun onCancelled(e: DatabaseError?) {
d.resumeWithException(e?.toException() ?: Exception("cancelled"))
}
override fun onDataChange(snapshot: DataSnapshot) {
try {
val data: T? = snapshot.getValue(dataType)
if (data != null) {
d.resume(data)
} else {
val errmsg =
if (snapshot.value == null)
"data missing"
else
"invalid read data format"
d.resumeWithException(Exception(errmsg))
}
} catch(e: Exception) {
d.resumeWithException(e)
}
}
})
}
suspend fun writeToDatabaseAsync(dbref: DatabaseReference, data: Any?): String = suspendCoroutineW { d->
dbref.setValue(data, DatabaseReference.CompletionListener { databaseError, databaseReference ->
if (databaseError != null) {
d.resumeWithException(databaseError.toException())
} else {
d.resume(databaseReference.key) //return the key that was written
}
})
}
suspend fun writeMultiToDatabaseAsync(dbref: DatabaseReference, data: Map<String,Any?>):String =suspendCoroutineW { d->
dbref.updateChildren(data, DatabaseReference.CompletionListener { databaseError, databaseReference ->
if (databaseError != null) {
d.resumeWithException(databaseError.toException())
} else {
d.resume(databaseReference.key?:"")
}
})
}
suspend fun getFileDownloadUrlAsync(file_sref: StorageReference): Uri = suspendCoroutineW { d ->
val srcPath = file_sref.toString()
try {
with(file_sref.downloadUrl) { //<---- call to Firebase getDownloadUrl()
addOnCompleteListener { //<--- this callback can trigger more than once!
if (it.isSuccessful) {
urlCache.put(srcPath, it.result)
d.resume(it.result)
} else
d.resumeWithException(Exception("some error"))
}
addOnFailureListener {
d.resumeWithException(it)
}
}
} catch (e: Exception) {
d.resumeWithException(e)
}
}
suspend fun uploadFileToStorageAsync(dest_sref: StorageReference,
srcFileUri: Uri,
metadata: StorageMetadata?,
progressListener: OnProgressListener<in UploadTask.TaskSnapshot>?=null):String = suspendCoroutineW { d->
val uploadTask= dest_sref.putFile(srcFileUri,metadata ?: StorageMetadata())
uploadTask.addOnFailureListener { e->
d.resumeWithException(e)
}
uploadTask.addOnSuccessListener { snapshot ->
d.resume(snapshot.storage.toString()) //return the download path
}
(progressListener)?.let { uploadTask.addOnProgressListener( progressListener) }
}
suspend fun deleteFileFromStorageAsync(dest_sref: StorageReference):String = suspendCoroutineW { d->
//for documentation see https://firebase.google.com/docs/storage/android/delete-files
val deleteTask= dest_sref.delete()
deleteTask.addOnFailureListener { e->
d.resumeWithException(e)
}
deleteTask.addOnSuccessListener { snapshot ->
d.resume(dest_sref.toString())
}
}
@paour
Copy link

paour commented Nov 23, 2017

I'm struggling to find the suspendCoroutine function even on recent versions of the core coroutine library, even though it's documented here: https://kotlinlang.org/api/latest/jvm/stdlib/kotlin.coroutines.experimental/suspend-coroutine.html

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment