Перенос логики синхронизации с 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.


Ответы (0 шт):