Swift Combine - асинхронные вызовы

У меня есть следующие функции:

    func getUserProfile() -> AnyPublisher<UserProfileDTO?, Error> {
    return Future { [unowned self] promise in
        do {
            if let data = KeychainWrapper.standard.data(forKey: profileKey) {
                let profileDTO = try PropertyListDecoder().decode(UserProfileDTO.self, from: data)
                setCurrentSession(profileDTO)
                promise(.success(profileDTO))
            }
            else {
                promise(.success(nil))
            }
        }
        catch {
            // Delete current UserProfile if cannot decode
            let _ = KeychainWrapper.standard.removeAllKeys()
            promise(.failure(error))
        }
    }.eraseToAnyPublisher()
}

    func connect(userProfile: UserProfileDTO) -> AnyPublisher<UserProfileDTO, Error> {
    return Future { promise in
        SBDMain.connect(withUserId: userProfile.email) { (user, error) in
            if let error = error {
                promise(.failure(error))
            }
            else {
                promise(.success(userProfile))
            }
        }
    }.eraseToAnyPublisher()
}

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

Я пытался использовать flatMaps, но это не сработало так, как я ожидал. Любая помощь очень ценится.

Решение, над которым я сейчас работаю, таково. Но это не совсем так.

    func autoLoginUser2() -> AnyPublisher<UserProfile?,Error> {
    getUserProfile()
        .tryMap { [unowned self] in
            if let currentProfile = $0 {
                return connect(userProfile: currentProfile)
                    .tryMap {
                        //Map from UserProfileDTO --> UserProfile
                        return UserProfileDTOMapper.map($0)
                        
                    }
            }
            return nil
        }.eraseToAnyPublisher()
}
Стоит ли изучать 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 называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
1
0
64
2

Ответы 2

С некоторой корректировкой используемых типов и типов ошибок это должно работать. Сначала вы запрашиваете профиль, затем вы принудительно разворачиваете профиль, если он равен нулю, вы выдаете ошибку, которая будет отправлена ​​в приемник как сбой. Если профиль присутствует, вы вызываете соединение.

getUserProfile()
.tryMap { userDTO -> UserProfileDTO in
    if let id = userDTO {
        return id
    }
    throw MyError.noProfileDT
}
.flatMap { id in
    connect(id)
}
.sink {
    //.....
}

Привет, Андреа, спасибо за ответ. Прочитав его, я понял, что пропустил важную часть вопроса. По сути, есть третий метод, autoLogin (), который вызывается моделью представления. Функции connect () и getUserProfile () находятся на уровне репозитория. Можно ли как-нибудь изменить ваш ответ в соответствии с этим?

Minon Weerasinghe 08.04.2021 00:30

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

func connect(userProfile: UserProfileDTO) -> AnyPublisher<UserProfileDTO?, Error>

Вы можете сделать что-то вроде этого:

getUserProfile()
    .flatMap { userProfile -> AnyPublisher<UserProfileDTO?, Error> in
            
        if let userProfile = userProfile {
            return connect(userProfile: userProfile)
                .eraseToAnyPublisher()
        } else {
            return Just<UserProfileDTO?>(nil)
                .setFailureType(to: Error.self)
                .eraseToAnyPublisher()
        }
    }
    //.sink etc.

Если вам не нужно, чтобы издатель выдавал nil, вы можете оставить подпись подключения как есть и использовать compactMap:

getUserProfile()
    .compactMap { $0 }
    .flatMap {
        connect(userProfile: $0)
            .eraseToAnyPublisher()
    }

Привет, LuLuGaGa, спасибо за ответ. Прочитав его, я понял, что пропустил важную часть вопроса. По сути, есть третий метод, autoLogin (), который вызывается моделью представления. Функции connect () и getUserProfile () находятся на уровне репозитория. Можно ли как-нибудь изменить ваш ответ в соответствии с этим? Также логически не имеет смысла иметь в connect () необязательный параметр, потому что соединение не должно вызываться, если оно есть в профиле.

Minon Weerasinghe 08.04.2021 00:31

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