Разбор JSON и обработка ответа об ошибке от API в RxSwift - PullRequest
0 голосов
/ 20 декабря 2018

Я новичок в RxSwift.У меня есть BackendProvider с обработкой связи с моим API.Я хочу синхронизировать файл конфигурации, чтобы можно было динамически получать некоторые параметры.У меня есть запасной вариант с локальным сохраненным файлом JSON, к которому я могу получить доступ в случае, если мой API недоступен или мой анализ JSON не выполняется:

ConfigFileBackendService

open func getLatestConfig() -> Observable<ConfigFile?> {            
        let urlString = IoC.urlProviderService.getConfigFileUrl()?.absoluteString ?? ""
        let configFileJSONData = IoC.backendCommunicationService.getJsonData(url: urlString)

        return configFileJSONData.map { data in
            if let configFile = try? JSONDecoder().decode(ConfigFile.self, from: data) {
                return configFile
            } else {
                return nil
            }
        }
    }

ConfigFileProcessService

Это тот, который обращается к локальному сохраненному файлу:

func getConfigFile() -> Observable<ConfigFile> {
        return IoC.configFileBackendService.getLatestConfig()
            .map { configFile in
                guard let configFile = configFile else { fatalError() }
                return configFile
            }
            .catchError { error in
                // Use default config
                let localURL = IoC.urlProviderService.getLocalConfigFileUrl()
                do {
                    let data = try Data(contentsOf: localURL)
                    let configFile = try JSONDecoder().decode(ConfigFile.self, from: data)
                    return Observable.just(configFile)
                } catch {
                    fatalError("Error loading local config")
                }
        }
    }

Этот подходработает, но у меня есть сомнения с блоками .map / .catchError.Есть ли лучший способ справиться с ошибкой?Может быть, я должен пойти с onNext, а затем onError?Заранее спасибо!

Ответы [ 2 ]

0 голосов
/ 21 декабря 2018

Следуя подходу @Daniel T., я избавился от nil и сделал сбой кода только в случае сбоя процесса загрузки локального файла конфигурации:

open class ConfigFileBackendService: ConfigFileBackendServiceProtocol {
    open func getLatestConfig() -> Observable<ConfigFile> {
        let urlString = IoC.urlProviderService.getConfigFileUrl()
        let configFileJSONData = IoC.backendCommunicationService.getJsonData(url: urlString)

        return configFileJSONData.map { try JSONDecoder().decode(ConfigFile.self, from: $0) }
    }
}

class ConfigFileProcessService: ConfigFileProcessServiceProtocol {
    func getConfigFile() -> Observable<ConfigFile> {
        return IoC.configFileBackendService.getLatestConfig()
            .catchError { [weak self] error in
                // Load local config file
                NSLog(error.localizedDescription)
                guard let strongSelf = self else { fatalError() }
                return Observable.just(strongSelf.getLocalFile())
        }

    }

    private func getLocalFile() -> ConfigFile {
        let localURL = IoC.urlProviderService.getLocalConfigFileUrl()
        guard
            let data = try? Data(contentsOf: localURL),
            let configFile = try? JSONDecoder().decode(ConfigFile.self, from: data)
            else { fatalError("Error loading local config") }
        return configFile
    }
}
0 голосов
/ 21 декабря 2018

То, что у вас хорошо выглядит, за исключением нескольких подходов к обработке ошибок.В одном случае вы используете try?, а в другом - do... catch, и, вероятно, ваш getJsonData(url:) может выдать наблюдаемую ошибку.Вы повсюду.Я предлагаю вам выбрать одну систему обработки ошибок и придерживаться ее.Самый гибкий - Event.error.Так что-то вроде этого:

func getLatestConfig() -> Observable<ConfigFile> {
    let urlString = IoC.urlProviderService.getConfigFileUrl()?.absoluteString ?? ""
    let configFileJSONData = IoC.backendCommunicationService.getJsonData(url: urlString)
    return configFileJSONData.map { try JSONDecoder().decode(ConfigFile.self, from: $0) }
}

Обратите внимание, что я просто позволяю ошибкам декодирования переходить в событие наблюдаемой ошибки.Не нужно иметь дело с nil таким образом.

func getConfigFile() -> Observable<ConfigFile> {
    return IoC.configFileBackendService.getLatestConfig()
        .catchError { _ in
            let localURL = IoC.urlProviderService.getLocalConfigFileUrl()
            let data = try! Data(contentsOf: localURL)
            let configFile = try! JSONDecoder().decode(ConfigFile.self, from: data)
            return Observable.just(configFile)
        }
}

Поскольку вы терпите крах, если любая из попыток все равно не удалась, просто положите на них !.Это имеет тот же эффект.Вам следует рассмотреть возможность помещения блока ошибок в отдельную тестируемую функцию, потому что нет никакой гарантии, что вы попадете на него во время обычного запуска программы, и он может быть сломан, даже если вы этого не заметите.

Наконец,с учетом вышесказанного нет причин предоставлять обработчик onError: в подписке, поскольку наблюдаемая getConfigFile() никогда не выдаст ошибку.Возможно, вы захотите, чтобы функция возвращала драйвер, чтобы сделать этот факт более явным.

...