Попытка смоделировать издателя DataService в Combine Swift

Когда я использую издателя, который на самом деле делает вызов API, все работает нормально. Однако, если я попытаюсь смоделировать его, я получу сбой с надписью EXC_BAD_ACCESS (код = 2, адрес = 0x16b827ed0).

Вот модель представления:

`

class ViewModel: ObservableObject {
    
    @Published var dataString: String = ""
    
    let dataService: DataServiceProtocol
    
    init(dataService: DataServiceProtocol) {
        
        self.dataService = dataService
        self.nasaPublisher
            .assign(to: &$dataString). // The app crashes here when i use the mock data service
    }
    
    private lazy var nasaPublisher: AnyPublisher<String, Never> = {
        $dataString
            .flatMap { [unowned self] string in
                
                self.dataService.getNasaData()
            }
            .eraseToAnyPublisher()
    }()
}

`

А вот и служба данных:

`

enum Endpoint: String {
    case search = "/search"
}

protocol DataServiceProtocol {
    func getNasaData() -> AnyPublisher<String, Never>
}

class DataService: DataServiceProtocol {
    
    let host = "https://images-api.nasa.gov"
    
    func getNasaData() -> AnyPublisher<String, Never> {

        var components = URLComponents()
        components.scheme = "https"
        components.host = "images-api.nasa.gov"
        components.path = "/search"
        components.queryItems = [
            URLQueryItem(name: "q", value: "apollo")
        ]
        
        guard let url = components.url else {
            return Just("false").eraseToAnyPublisher()
        }
        
        return URLSession.shared.dataTaskPublisher(for: url)
          .map { data, response in
            do {
              let decoder = JSONDecoder()

                let str = String(decoding: data, as: UTF8.self)
                
                print(data.prettyPrintedJSONString)
              return str
            }
//            catch {
//              return "false"
//            }
          }
          .replaceError(with: "false")
          .eraseToAnyPublisher()

    }
}

class MockDataService: DataServiceProtocol {
    
    let mock = Just("Test string")
        .setFailureType(to: Never.self)
        .eraseToAnyPublisher()
    
    func getNasaData() -> AnyPublisher<String, Never> {
        
        return mock
    }
    
}

`

Любые советы о том, что я делаю неправильно, будут высоко оценены. Спасибо!

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

Стоит ли изучать PHP в 2026-2027 годах?
Стоит ли изучать PHP в 2026-2027 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
0
0
93
1
Перейти к ответу Данный вопрос помечен как решенный

Ответы 1

Ответ принят как подходящий

В init у вас есть:

        self.nasaPublisher
            .assign(to: &$dataString)

Итак, это издатель, который присваивает свой вывод опубликованной переменной dataString

Тогда nasaPublisher определяется как:

    private lazy var nasaPublisher: AnyPublisher<String, Never> = {
        $dataString
            .flatMap { [unowned self] string in
                
                self.dataService.getNasaData()
            }
            .eraseToAnyPublisher()
    }()

Это издатель, который реагирует всякий раз, когда значение в dataString изменяется.

Когда dataString изменяется, это заставляет nasaPublisher выдавать значение. Это значение присваивается dataString, что заставляет nasaPublisher выдавать значение. Это значение присваивается dataString, что заставляет nasaPublisher выдавать значение. Это значение присвоено dataString ...

Это бесконечный цикл.

Когда вы используете службу реальных данных, она создает уникального издателя каждый раз, когда цикл вызывает getNasaData.

Однако при использовании вашего фиктивного сервиса он создает одного издателя и getNasaData каждый раз возвращает один и тот же.

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

Другие вопросы по теме