I would like to create a module, that will refresh token if needed and repeat latest request using Combine
from Apple.
For now, every part is works well, but not this one:
public func executeRequest<T: Decodable, E: ServerErrorType>(
_ request: HTTPRequest,
mapper: ObjectMapper<T, E>
) -> AnyPublisher<(T, HTTPResponse), Error> {
return authentificator // <- handle refresh token stuff
.refreshToken(force: false)
.subscribe(on: DispatchQueue.global())
.flatMap { token in // <- on obtain token - transform it into request
---> (here)
session.publisher(for: request, mapper: mapper, token: token) // <- create request
---> (here)
.tryCatch({ error -> AnyPublisher<(T, HTTPResponse), Error> in
if let error = error as? ServerErrorType,
error.isAuthError {
return authentificator
.refreshToken(force: true)
.subscribe(on: DispatchQueue.global())
.flatMap { token -> AnyPublisher<(T, HTTPResponse), Error> in
session.publisher(for: request, mapper: mapper, token: token) //<- repeat if token refreshed
}
.eraseToAnyPublisher()
} else {
throw error
}
})
.print()
}
.receive(on: DispatchQueue.main)
.print()
.eraseToAnyPublisher()
}
In place where I mark (here)
when I have expired token, tryCatch wont works, instead "received canceled" printed in console. I'm not sure what I did wrong. Can any on advice?
I found the reason - my set with AnyCancellable
is become nil because ViewModel
become nil due to root TabBar
reinit because of SwiftUI
update process. (I have a TabBar
as an OptionalView
in another View
that is root)
var body: some View {
VStack {
switch viewModel.currentFlow {
case .onboarding:
WelcomeView()
.transition(.opacity)
case .main:
MainTabBarView() // <- reinit here cause the issue
.transition(.opacity)
}
}
}
The code is completely correct for refresh token, and the possible fix is:
@StateObject
for ViewModel
@ObservedObject private var viewModel: <MyViewModel>
update to:
@StateObject private var viewModel: <MyViewModel>
TabBar
at any reload.possible fix:
private lazy let mainTabBar: MainTabBarView = .init() // <- init only once
private lazy let welcome: WelcomeView = .init()
var body: some View {
VStack {
switch viewModel.currentFlow {
case .onboarding:
welcome
.transition(.opacity)
case .main:
mainTabBar
.transition(.opacity)
}
}
}