I wrote such code for network request token refresh.
enum NetworkingClient {
private static func _makeUnaryCall<Request: SwiftProtobuf.Message, Response: SwiftProtobuf.Message>(
call maker: @escaping () -> UnaryCall<Request, Response>
) -> Single<Response> {
return Single<Response>.create { (handler) -> Disposable in
let call = maker()
call.response
.whenComplete {
do {
handler(.success(try $0.get()))
} catch {
handler(.error(error))
}
}
return Disposables.create()
}
.subscribeOn(requestScheduler)
}
static func makeUnaryCall<Request: SwiftProtobuf.Message, Response: SwiftProtobuf.Message>(
serverInfo: ServerInfo,
call maker: @escaping () -> UnaryCall<Request, Response>
) -> Single<Response> {
let gid = serverInfo.gid
let uid = serverInfo.uid
return self._makeUnaryCall(call: maker)
.retryWhen { errorObservable -> Observable<Void> in
return errorObservable.flatMap { error -> Observable<Void> in
if let errorCode = error as? NetworkingError, case .tokenExpired(let token) = errorCode {
return VoidToken.reLogin(token: token!, serverInfo: serverInfo)
}
throw error
} }
.do(onError: { (err) in
log.error("\(gid)-\(uid)-\(serverInfo.host)-\(serverInfo.port)-\(err)")
})
.observeOn(MainScheduler.instance)
}
}
enum VoidToken {
static var caches: [String: Observable<Void>] = [:]
static let lock = NSLock()
static func reLogin(token: Token, serverInfo: ServerInfo) -> Observable<Void> {
VoidToken.lock.lock()
if let refreshTokenReq = VoidToken.caches[token.accessToken] {
VoidToken.lock.unlock()
return refreshTokenReq
}
var service: LoginService? = LoginService(serverInfo: serverInfo)
let refreshReq = service!
.refreshToken(refreshToken: token.refreshToken)
.retryWhen({ errorObservable in
return errorObservable.flatMap { error -> Observable<Void> in
if let err = error as? GRPCStatus, err.code == .unavailable {
return .just(())
}
throw error
}
})
.debug("refreshToken")
.asObservable()
.share(replay: 1, scope: .forever)
.map { _ in }
.do(afterCompleted: {
service = nil
})
VoidToken.caches[token.accessToken] = refreshReq
VoidToken.lock.unlock()
return refreshReq
}
}
enum NetworkingError: Error {
case tokenExpired(Token?)
}
extension LoginService {
func refreshToken(refreshToken: String, loginChatService: Bool = true) -> Single<Void> {
let gid = self.gid
let uid = self.uid
return defaultImplementation
.reLogin(refreshToken: refreshToken)
.map { (rsp) -> Token in
precondition(rsp.hasSess)
AccountKit.shared.updateToken(pid: gid + uid, session: rsp.sess)
let token = AccountKit.shared.fetchAuth(gid: gid, uid: uid)!
log.warn("token refresh success, newToken: \(token.accessToken) newIMToken\(token.imToken)")
return token
}
.flatMap { token -> Single<Void> in
guard loginChatService else { return Single.just(()) }
guard let chatService = AccountKit.shared.chatInfos.fetchWrapper(pid: gid + uid) else {
throw SourceContextError()
}
return chatService.rx.login(imToken: token.imToken, updateSyncTime: false)
.map { _ in () }
.catchErrorJustReturn(())
}
}
}
But, I get a log that looks like this
2021-07-19 16:24:24.208: refreshToken -> subscribed
2021-07-19 16:24:24.494: refreshToken -> isDisposed
2021-07-19 16:24:25.037: refreshToken -> subscribed
2021-07-19 16:24:25.251: refreshToken -> Event error(notLogin)
2021-07-19 16:24:25.251: refreshToken -> isDisposed
I don’t know why Refresh Token
isDisposed when there is no ON NEXT
This is an occasional problem, but I don’t know what caused it
Its log output should normally look like this:
2021-07-22 10:04:45.794: refreshToken -> subscribed
2021-07-22 10:04:52.254: refreshToken -> Event next(())
2021-07-22 10:04:52.266: refreshToken -> Event completed
2021-07-22 10:04:52.266: refreshToken -> isDisposed
I'd really appreciate it if you could help me.