Перенос логики синхронизации с Kotlin корутин на iOS Swift в связке RxSwift+RxAlamofire
Есть некий функционал синхронизации, который хорошо рализован на корутинах. Код-пример ниже:
class SyncExample {
companion object {
const val TAG = "SyncExample"
val progress: MutableLiveData<Pair<Int, Int>> = MutableLiveData(Pair(0, 100))
}
data class Doc(val id: String, var uploaded: Boolean = false)
data class Photo(val path: String, var uploaded: Boolean = false)
private val exceptionHandler = CoroutineExceptionHandler() { coroutineContext: CoroutineContext, throwable: Throwable ->
Log.e(TAG, "ExceptionHandler. coroutineContext: $coroutineContext, throwable: $throwable")
}
private val scope = CoroutineScope(IO + exceptionHandler)
private var job: Job? = null
//Стартуем синхронизацию
fun start() {
//Визуализируем показ прогресса
progress.observeForever {
Log.e(TAG, "Progress: $it")
}
job?.cancelChildren()
job?.cancel()
job = scope.launch {
//Будем считать, что прогресс выгрузки документов от 0 до 50, а фоток - от 51 до 100
val docs = arrayOf(Doc("id1"), Doc("id2"), Doc("id3"), Doc("id4"), Doc("id5"), Doc("id6"), Doc("id7"), Doc("id8"), Doc("id9"))
docs.forEachIndexed { index, doc ->
//Приходится вот так экранировать, чтобы сетевой эксепшн не останавливал всю корутину
try {
uploadOneDoc(doc = doc)
} catch (e: Exception) {
e.printStackTrace(System.err)
}
progress.postValue(Pair(((index+1).toDouble() / docs.size.toDouble() * 50.00).toInt(), 100))
}
//Обновляем все доки в БД
//db.updateDocs(docs)
//Получаем фотки из БД у документов(родителей) которых uploaded==true
val photos = arrayListOf(Photo("path1"), Photo("path2"), Photo("path3"), Photo("path4"))
photos.forEachIndexed { index, photo ->
//Приходится вот так экранировать, чтобы сетевой эксепшн не останавливал всю корутину
try {
uploadOnePhoto(photo = photo)
} catch (e: Exception) {
e.printStackTrace(System.err)
}
progress.postValue(Pair(50+((index+1).toDouble() / docs.size.toDouble() * 50.00).toInt(), 100))
}
//Для красоты сделаем задержку в обновлении прогресса
delay(300)
progress.postValue(Pair(100, 100))
//Обновляем все фотки в БД
//db.updatePhotos(photos)
//И потихоньку завершаем работу синхронайзера))
delay(300)
Log.e(TAG, "Success")
}
}
//Эмуляция сетевого запроса по выгрузке документа. Можем получить данные, по которым uploaded делаем == true, а может наоборот
//Так же можем получить Exception, например какой-нибудь TimeoutException
private suspend inline fun uploadOneDoc(doc: Doc) = withContext(IO) {
delay((200..1000).random().toLong())
//Получаем результат и по логике ставим флаг true
val i = (1..15).random()
if (i % 2 == 0) {
doc.uploaded = true
} else if (i % 3 == 0) {
throw Exception("i % 3 == 0")
}
Log.e(TAG, "Doc with id ${doc.id} uploaded = ${doc.uploaded}")
}
//Эмуляция сетевого запроса по выгрузке фотки. Можем получить данные, по которым uploaded делаем == true, а может наоборот
//Так же можем получить Exception, например какой-нибудь TimeoutException
private suspend inline fun uploadOnePhoto(photo: Photo) = withContext(IO) {
delay((200..1000).random().toLong())
//Получаем результат и по логике ставим флаг true
val i = (1..15).random()
if (i % 2 == 0) {
photo.uploaded = true
} else if (i % 5 == 0) {
throw Exception("i % 5 == 0")
}
Log.e(TAG, "Photo with path ${photo.path} uploaded = ${photo.uploaded}")
}
}
Теперь надо перенести его на ios Swift. На свифте есть проблема - Alamofire не умеет делать синхронные запросы (на сколько я знаю), поэтому у меня там ужасный говнокод с кучей колбеков, даже показывать не буду...
С Rx на Android я только начинаю знакомиться и сразу в голове вырисовывается будущая связка на iOS -> RxSwift + RxAlamofire, но вот вообще не могу понять какая последовательность нужна.
Суть в следующем: Надо выгрузить все документы по одному. По результату ответа помечаем док выгруженным. После всего этого надо взять все фотки, у которых док выгружен и уже грузить их по одной, так же помечая успешно выгруженные как uploaded.
Во время синхронизации надо показывать прогресс, а так же, при exception надо, чтобы все продолжалось. Худшее, что должно произойти при exception - документ или фотка не пометится как выгруженная и в следующий раз будет повторная попытка выгрузки.
В принципе мне будет достаточно примера на Kotlin + RxJava, а как пойму как он работает, уже будет не сложно перенести в RxSwift.