|
1 | 1 | /*
|
2 |
| - * Copyright (c) 2020 GitLive Ltd. Use of this source code is governed by the Apache 2.0 license. |
| 2 | + * Copyright (c) 2023 GitLive Ltd. Use of this source code is governed by the Apache 2.0 license. |
3 | 3 | */
|
4 | 4 |
|
5 | 5 | package dev.gitlive.firebase.storage
|
6 | 6 |
|
7 | 7 | import cocoapods.FirebaseStorage.FIRStorage
|
8 | 8 | import cocoapods.FirebaseStorage.FIRStorageReference
|
| 9 | +import cocoapods.FirebaseStorage.FIRStorageTaskStatusFailure |
| 10 | +import cocoapods.FirebaseStorage.FIRStorageTaskStatusPause |
| 11 | +import cocoapods.FirebaseStorage.FIRStorageTaskStatusProgress |
| 12 | +import cocoapods.FirebaseStorage.FIRStorageTaskStatusResume |
| 13 | +import cocoapods.FirebaseStorage.FIRStorageTaskStatusSuccess |
9 | 14 | import dev.gitlive.firebase.Firebase
|
10 | 15 | import dev.gitlive.firebase.FirebaseApp
|
11 | 16 | import dev.gitlive.firebase.FirebaseException
|
12 | 17 | import kotlinx.coroutines.CompletableDeferred
|
| 18 | +import kotlinx.coroutines.cancel |
| 19 | +import kotlinx.coroutines.channels.awaitClose |
| 20 | +import kotlinx.coroutines.channels.trySendBlocking |
| 21 | +import kotlinx.coroutines.flow.FlowCollector |
| 22 | +import kotlinx.coroutines.flow.callbackFlow |
| 23 | +import kotlinx.coroutines.flow.emitAll |
13 | 24 | import platform.Foundation.NSError
|
14 | 25 | import platform.Foundation.NSURL
|
15 | 26 |
|
@@ -50,35 +61,73 @@ actual class StorageReference(val ios: FIRStorageReference) {
|
50 | 61 | actual val storage: FirebaseStorage get() = FirebaseStorage(ios.storage())
|
51 | 62 |
|
52 | 63 | actual fun child(path: String): StorageReference = StorageReference(ios.child(path))
|
| 64 | + |
53 | 65 | actual suspend fun delete() = await { ios.deleteWithCompletion(it) }
|
54 |
| - actual suspend fun getDownloadUrl(): String = awaitResult<NSURL?> { ios.downloadURLWithCompletion(it) }?.absoluteString!! |
55 |
| -} |
56 | 66 |
|
57 |
| -actual open class StorageException(message: String) : FirebaseException(message) |
| 67 | + actual suspend fun getDownloadUrl(): String = ios.awaitResult { |
| 68 | + downloadURLWithCompletion(completion = it) |
| 69 | + }.absoluteString()!! |
| 70 | + |
| 71 | + actual fun putFile(file: File): ProgressFlow { |
| 72 | + val ios = ios.putFile(file.url) |
| 73 | + |
| 74 | + val flow = callbackFlow { |
| 75 | + ios.observeStatus(FIRStorageTaskStatusProgress) { |
| 76 | + val progress = it!!.progress()!! |
| 77 | + trySendBlocking(Progress.Running(progress.completedUnitCount, progress.totalUnitCount)) |
| 78 | + } |
| 79 | + ios.observeStatus(FIRStorageTaskStatusPause) { |
| 80 | + val progress = it!!.progress()!! |
| 81 | + trySendBlocking(Progress.Paused(progress.completedUnitCount, progress.totalUnitCount)) |
| 82 | + } |
| 83 | + ios.observeStatus(FIRStorageTaskStatusResume) { |
| 84 | + val progress = it!!.progress()!! |
| 85 | + trySendBlocking(Progress.Running(progress.completedUnitCount, progress.totalUnitCount)) |
| 86 | + } |
| 87 | + ios.observeStatus(FIRStorageTaskStatusSuccess) { close(FirebaseStorageException(it!!.error().toString())) } |
| 88 | + ios.observeStatus(FIRStorageTaskStatusFailure) { |
| 89 | + when(it!!.error()!!.code) { |
| 90 | + /*FIRStorageErrorCodeCancelled = */ -13040L -> cancel(it.error()!!.localizedDescription) |
| 91 | + else -> close(FirebaseStorageException(it.error().toString())) |
| 92 | + } |
| 93 | + } |
| 94 | + awaitClose { ios.removeAllObservers() } |
| 95 | + } |
58 | 96 |
|
59 |
| -suspend inline fun <reified T> awaitResult(function: (callback: (T?, NSError?) -> Unit) -> Unit): T { |
60 |
| - val job = CompletableDeferred<T?>() |
61 |
| - function { result, error -> |
62 |
| - if(error == null) { |
63 |
| - job.complete(result) |
64 |
| - } else { |
65 |
| - job.completeExceptionally(error.toException()) |
| 97 | + return object : ProgressFlow { |
| 98 | + override suspend fun collect(collector: FlowCollector<Progress>) = collector.emitAll(flow) |
| 99 | + override fun pause() = ios.pause() |
| 100 | + override fun resume() = ios.resume() |
| 101 | + override fun cancel() = ios.cancel() |
66 | 102 | }
|
67 | 103 | }
|
68 |
| - return job.await() as T |
| 104 | + |
69 | 105 | }
|
70 | 106 |
|
71 |
| -suspend inline fun <T> await(function: (callback: (NSError?) -> Unit) -> T): T { |
| 107 | +actual class File(val url: NSURL) |
| 108 | + |
| 109 | +actual class FirebaseStorageException(message: String): FirebaseException(message) |
| 110 | + |
| 111 | +suspend inline fun <T> T.await(function: T.(callback: (NSError?) -> Unit) -> Unit) { |
72 | 112 | val job = CompletableDeferred<Unit>()
|
73 |
| - val result = function { error -> |
| 113 | + function { error -> |
74 | 114 | if(error == null) {
|
75 | 115 | job.complete(Unit)
|
76 | 116 | } else {
|
77 |
| - job.completeExceptionally(error.toException()) |
| 117 | + job.completeExceptionally(FirebaseStorageException(error.toString())) |
78 | 118 | }
|
79 | 119 | }
|
80 | 120 | job.await()
|
81 |
| - return result |
82 | 121 | }
|
83 | 122 |
|
84 |
| -fun NSError.toException() = StorageException(description!!) // TODO: Improve error handling |
| 123 | +suspend inline fun <T, reified R> T.awaitResult(function: T.(callback: (R?, NSError?) -> Unit) -> Unit): R { |
| 124 | + val job = CompletableDeferred<R?>() |
| 125 | + function { result, error -> |
| 126 | + if(error == null) { |
| 127 | + job.complete(result) |
| 128 | + } else { |
| 129 | + job.completeExceptionally(FirebaseStorageException(error.toString())) |
| 130 | + } |
| 131 | + } |
| 132 | + return job.await() as R |
| 133 | +} |
0 commit comments