Swift5 (+ RxSwift) 'self' захватывается замыканием до инициализации всех членов - PullRequest
1 голос
/ 08 мая 2019

Я учу MVVM + RxSwift. Я хотел бы сохранить значение для повторного использования в следующем потоке. Но я столкнулся с ошибкой выше.

Я предполагаю, что я инициализировал значение "translationText", но ошибка все еще появляется.

Я пробовал несколько способов инициализации с помощью объявления и так далее ...


import UIKit
import RxSwift

final class ViewModel {


    private let api: apiProtocol
    let validationText: Observable<String>
    let getObservable: Observable<String>
    var translatedText: String
    // var translatedText: String = "" //this case also dosn't work

    init(inputObservable: Observable<String?>, changeButtonClicked: Observable<Void>, model: ModelProtocol, api: apiProtocol = APICntl()) {

        self.api = api
        self.translatedText = "" //I guess I initialised this here

        let event = inputObservable
            .flatMap { input -> Observable<Event<Void>> in
                if let text = input {
                    self.translatedText = text // error. i want to save "input" to use at "let tapEvent"
                }
                return model
                    .validate(text: input)
                    .materialize()
            }
            .share()

        self.validationText = event
            .flatMap { event -> Observable<String> in
                switch event {
                case .next:
                    return .just("")
                case let .error(error as ModelError):
                    return .just(error.errorLabel)
                case .error, .completed:
                    return .empty()
                }
            }
            .startWith(ModelError.invalidBlank.errorLabel)

        let tapEvent = changeButtonClicked
            .flatMap { (result) -> Observable<Event<String>> in
                return api
                    .fetch(text: self.translatedText) // I want use it here
                    .materialize()
        }
        .share()

        self.getObservable = tapEvent
            .flatMap { event -> Observable<String> in
                switch event {
                case .next:
                    return .just(event.element!)
                case let .error(error as ModelError):
                    return .just(error.errorLabel)
                case .error, .completed:
                    return .empty()
                }
            }
    }

}

У вас, ребята, есть лучшие решения?

----- самостоятельное решение -----

Я просто создал временную переменную перед событием let, и она работает, как я хочу.

final class ViewModel {


    private let api: apiProtocol
    let validationText: Observable<String>
    let getObservable: Observable<String>

    init(inputObservable: Observable<String?>, changeButtonClicked: Observable<Void>, model: ModelProtocol, api: apiProtocol = APICntl()) {

        self.api = api
        var temp = ""

        let event = inputObservable
            .flatMap { input -> Observable<Event<Void>> in
                if let text = input {
                    temp = text
                }
                return model
                    .validate(text: input)
                    .materialize()
            }
            .share()

Ответы [ 3 ]

0 голосов
/ 08 мая 2019

---- самостоятельное решение -----

Я просто создал временную переменную перед событием let, и она работает, как я хочу.

final class ViewModel {


    private let api: apiProtocol
    let validationText: Observable<String>
    let getObservable: Observable<String>

    init(inputObservable: Observable<String?>, changeButtonClicked: Observable<Void>, model: ModelProtocol, api: apiProtocol = APICntl()) {

        self.api = api
        var temp = ""

        let event = inputObservable
            .flatMap { input -> Observable<Event<Void>> in
                if let text = input {
                    temp = text
                }
                return model
                    .validate(text: input)
                    .materialize()
            }
            .share()
0 голосов
/ 08 мая 2019

Вы не должны связывать два независимых наблюдаемых потока друг с другом каким-то случайным блоком «временной» памяти.Два потока зависят от inputObservable, и это должно быть ясно.Кроме того, ваша модель представления может быть значительно упрощена ...

final class ViewModel {
    let validationText: Observable<String>
    let getObservable: Observable<String>

    init(inputObservable: Observable<String?>, changeButtonClicked: Observable<Void>, model: ModelProtocol, api: apiProtocol = APICntl()) {

        let translatedText = inputObservable.share() // this line is unnecessary if `inputObservable` is already hot.

        self.validationText = translatedText
            .flatMap { input in
                return model
                    .validate(text: input)
                    .map { "" }
                    .catchError { error in .just((error as? ModelError)?.errorLabel ?? error.localizedDescription) }
            }
            .startWith(ModelError.invalidBlank.errorLabel)

        self.getObservable = changeButtonClicked
            .withLatestFrom(translatedText)
            .compactMap { $0 }
            .flatMap { translatedText in
                return api
                    .fetch(text: translatedText)
                    .catchError { error in .just((error as? ModelError)?.errorLabel ?? error.localizedDescription) }
            }
    }
}
0 голосов
/ 08 мая 2019

Ваша проблема в том, что self не полностью инициализируется при вызове flatMap.В вашем случае validationText и getObservable находятся в неопределенном состоянии.

Самым быстрым способом было бы сделать их неявно развернутыми опциями, но вы действительно должны гарантировать, что они не доступны до инициализации:

final class ViewModel {


    private let api: apiProtocol
    let validationText: Observable<String>!
    let getObservable: Observable<String>!
    var translatedText: String!

    // ...
}
...