Правильно использовать переменные RxSwift

Я пытаюсь преобразовать проект в использование RxSwift и MVVM. У меня есть служба, которая синхронизирует список данных из Parse при каждом запуске приложения, и я в основном хочу убедиться, что я использую правильный подход.

То, что я сделал, является объектом Variable, а затем позволяет моим моделям слушать это. ParseService:

let rx_parseMushrooms = Variable<[ParseMushroom]>([])

MushroomLibraryModel:

_ = parseService.rx_parseMushrooms
    .asObservable()
    .map { (parseMushrooms:[ParseMushroom]) -> [Mushroom] in
        let mushrooms = parseMushrooms.map { (parseMushroom:ParseMushroom) -> Mushroom in
            let mushroom = Mapper<Mushroom>().map(parseMushroom.dictionaryWithValuesForKeys(parseMushroom.allKeys()))
            return mushroom!
        }

        return mushrooms
    }
    .subscribeNext({ (mushrooms:[Mushroom]) -> Void in
        self.mushrooms = mushrooms
        print(mushrooms)
    })

Я делаю то же самое для выражения состояния синхронизации.

ParseService:

struct SyncState {
    enum State {
        case Unsynced, ConnectingToServer, SyncingInfo, FetchingImageList, SyncingImages, SyncComplete, SyncCompleteWithError
    }

    var infoToSync = 0
    var imagesToSync = 0
    var imagesSynced = 0

    var state = State.Unsynced
}

let rx_syncState = Variable(SyncState())

Затем я обновляю переменную a la

self.rx_syncState.value = self.syncState

SyncViewModel:

_ = parseService.rx_syncState
     .asObservable()
     .subscribeNext { [weak self] (syncState:ParseService.SyncState) -> Void in
          switch syncState.state {
              //show stuff based on state struct
          }
      }

В любом случае я был бы очень признателен, если кто-нибудь скажет мне, если это хороший способ обойти это, или если я злоупотребляю RxSwift (и расскажу, как я должен это делать...)

Ура!

Ответ 1

Хмм... Вот статья об использовании Variable (обратите внимание, что Variable является оберткой вокруг BehaviorSubject.)

http://davesexton.com/blog/post/To-Use-Subject-Or-Not-To-Use-Subject.aspx

В вашем случае у вас уже есть холодный наблюдаемый (сетевой вызов), поэтому вам не требуется Тема/Переменная. Все, что вам нужно сделать, это опубликовать наблюдаемый, который у вас уже есть, и использовать replay (1) для кэширования значения. Я бы ожидал, что класс назван как-то вроде ParseServer, который содержит вычисленное свойство с именем mushrooms.

Чтобы помочь получить грибы из синтаксического анализа, вы можете использовать это (это создаст необходимый вам холод):

extension PFQuery {

    var rx_findObjects: Observable<[PFObject]> {
        return Observable.create { observer in
            self.findObjectsInBackgroundWithBlock({ results, error in
                if let results = results {
                    observer.on(.Next(results))
                    observer.on(.Completed)
                }
                else {
                    observer.on(.Error(error ?? RxError.Unknown))
                }
            })
            return AnonymousDisposable({ self.cancel() })
        }
    }

}

И тогда у вас будет что-то вроде:

class ParseServer {
    var mushrooms: Observable<[Mushroom]> {
        return PFQuery(className: "Mushroom").rx_findObjects
        .map { $0.map { Mushroom(pfObject: $0) } }
        .publish()
        .replay(1)
    }
}

Я думаю, что это правильно. Я не запускал его через компилятор, но, тем не менее, тестировал его. Это может потребоваться для редактирования.

Идея состоит в том, что при первом вызове myParseServer.mushrooms система будет вызывать Parse, чтобы вытащить грибы и кешировать их. С этого момента он просто вернет предыдущие карточные грибы.