Swift Combine Conditional FlatMap Results (Условные результаты FlatMap)

Как вы можете иметь разные типы возвращаемых значений для flatMap при использовании Swift Combine? У меня есть первый издатель, который генерирует значение, а затем я картографирую его, чтобы преобразовать его в нового издателя. Однако, исходя из исходного значения, мне может потребоваться другой издатель, который возвращает другой тип.

Ниже я добавил базовый пример.

import Combine


class Testing{
    var subscriptions = Set<AnyCancellable>()
    
    
    func getTestScore()->AnyPublisher<Int, Never>{
        return Just(80).eraseToAnyPublisher()
    }
    
    func congratulate()->AnyPublisher<String, Never>{
        return Just("Good Job!").eraseToAnyPublisher()
    }
    
    func getGPA()->AnyPublisher<Double, Never>{
        return Just(2.2).eraseToAnyPublisher()
    }
    
    init() {
        getTestScore()
            .flatMap{ score in
                if score < 70{
                    return self.getGPA()
                } else{
                    return self.congratulate()
                }
            }
            .sink { _ in } receiveValue: { value in
                print(value)
            }.store(in: &subscriptions)
    }
}

let testing = Testing()


person Richard Witherspoon    schedule 23.11.2020    source источник
comment
getGPA испускает Double значения, а congratulate испускает String значения .. Что .sink должно ожидать? Это похоже на желание вернуть Double или String из функции. Вы, конечно, можете вернуть Any, но это просто плохой выбор дизайна. Возможно, вы можете рассмотреть перечисление со связанными значениями.   -  person New Dev    schedule 24.11.2020
comment
Вы правы, что любое - не лучшее решение. Перечисление со связанными значениями, вероятно, тоже не лучший выбор.   -  person Richard Witherspoon    schedule 24.11.2020
comment
В своем вопросе вы разместили игрушечный пример ... может быть, настоящая проблема не очевидна в этом примере?   -  person New Dev    schedule 24.11.2020
comment
Это почти то же самое. Настоящая проблема заключается в том, что у меня есть представление профиля, в котором я загружаю пользователя. Затем я должен проверить, является ли пользователь текущим авторизованным пользователем. Если это так, то мне нужно сделать два сетевых вызова (a и b). Если это не вошедший в систему пользователь, мне нужно только сделать сетевой вызов a.   -  person Richard Witherspoon    schedule 24.11.2020
comment
Звучит по-другому. Я предлагаю вам задать другой вопрос, более близкий к тому, что вам действительно нужно.   -  person New Dev    schedule 24.11.2020


Ответы (1)


Как сказал New Dev, возврат условных типов данных невозможен - если вы не хотите стирать тип до Any.

Мое предложение состоит в том, чтобы создать одного специализированного издателя, который будет генерировать сообщения, если оценка превышает (или опускается) определенный предел. Теперь используйте своего нового издателя, чтобы создать два отдельных конвейера, безопасных по типу. Я знаю, что вы просили использовать только один конвейер. Однако я думаю, что такой подход принесет вам больше пользы.

Пожалуйста, найдите мой рабочий пример ниже:

import Combine

class Testing{
    var subscriptions = Set<AnyCancellable>()
    
    func getTestScore()->AnyPublisher<Int, Never> {
        return Just(80).eraseToAnyPublisher()
    }
    
    func congratulate()->AnyPublisher<String, Never> {
        return Just("Good Job!").eraseToAnyPublisher()
    }
    
    func getGPA()->AnyPublisher<Double, Never> {
        return Just(2.2).eraseToAnyPublisher()
    }
    
    init() {
        let scoreExceedsLimit: AnyPublisher<Bool, Never> = getTestScore()
            .map { $0 >= 70 }
            .eraseToAnyPublisher()
        
        scoreExceedsLimit
            .filter { $0 == true }
            .flatMap { _ in self.congratulate() }
            .sink(receiveValue: { value in
                print("first pipeline: \(value)")
            })
            .store(in: &subscriptions)
        
        scoreExceedsLimit
            .filter { $0 == false }
            .flatMap { _ in self.getGPA() }
            .sink(receiveValue: { value in
                print("second pipeline: \(value)")
            })
            .store(in: &subscriptions)
    }
}

let testing = Testing()
person Nikolai    schedule 25.11.2020