У меня есть следующие функции:
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()
}





С некоторой корректировкой используемых типов и типов ошибок это должно работать. Сначала вы запрашиваете профиль, затем вы принудительно разворачиваете профиль, если он равен нулю, вы выдаете ошибку, которая будет отправлена в приемник как сбой. Если профиль присутствует, вы вызываете соединение.
getUserProfile()
.tryMap { userDTO -> UserProfileDTO in
if let id = userDTO {
return id
}
throw MyError.noProfileDT
}
.flatMap { id in
connect(id)
}
.sink {
//.....
}
Если вы измените подпись подключения, чтобы вернуть дополнительный профиль:
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 () необязательный параметр, потому что соединение не должно вызываться, если оно есть в профиле.
Привет, Андреа, спасибо за ответ. Прочитав его, я понял, что пропустил важную часть вопроса. По сути, есть третий метод, autoLogin (), который вызывается моделью представления. Функции connect () и getUserProfile () находятся на уровне репозитория. Можно ли как-нибудь изменить ваш ответ в соответствии с этим?