Retry after an error
This commit is contained in:
parent
cdbab6ec07
commit
fd8c099e23
|
@ -1,3 +1,5 @@
|
||||||
|
import Foundation
|
||||||
|
|
||||||
protocol ErrorActionHandling {
|
protocol ErrorActionHandling {
|
||||||
func handle(error: RocketChatError)
|
func handle(error: RocketChatError)
|
||||||
}
|
}
|
||||||
|
@ -12,18 +14,26 @@ final class ErrorActionHandler {
|
||||||
init(server: Server) {
|
init(server: Server) {
|
||||||
self.server = server
|
self.server = server
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
extension ErrorActionHandler: ErrorActionHandling {
|
private func handleOnMain(error: RocketChatError) {
|
||||||
func handle(error: RocketChatError) {
|
|
||||||
switch error {
|
switch error {
|
||||||
|
case .server(let response):
|
||||||
|
router.present(error: response)
|
||||||
case .unauthorized:
|
case .unauthorized:
|
||||||
router.route(to: .serverList)
|
router.route(to: .serverList)
|
||||||
|
|
||||||
database.remove()
|
database.remove()
|
||||||
serversDB.remove(server)
|
serversDB.remove(server)
|
||||||
default:
|
case .unknown:
|
||||||
break
|
print("Unexpected error on Client.")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
extension ErrorActionHandler: ErrorActionHandling {
|
||||||
|
func handle(error: RocketChatError) {
|
||||||
|
DispatchQueue.main.async {
|
||||||
|
self.handleOnMain(error: error)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,16 +2,27 @@ import Foundation
|
||||||
|
|
||||||
protocol AppRouting {
|
protocol AppRouting {
|
||||||
func route(to route: Route)
|
func route(to route: Route)
|
||||||
|
func present(error: ErrorResponse)
|
||||||
}
|
}
|
||||||
|
|
||||||
final class AppRouter: ObservableObject {
|
final class AppRouter: ObservableObject {
|
||||||
@Published private(set) var route: Route = .loading
|
@Published private(set) var route: Route = .loading
|
||||||
|
|
||||||
|
@Published var error: ErrorResponse?
|
||||||
}
|
}
|
||||||
|
|
||||||
extension AppRouter: AppRouting {
|
extension AppRouter: AppRouting {
|
||||||
func route(to route: Route) {
|
func route(to route: Route) {
|
||||||
self.route = route
|
self.route = route
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func present(error: ErrorResponse) {
|
||||||
|
guard self.error == nil else {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
self.error = error
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
enum Route: Equatable {
|
enum Route: Equatable {
|
||||||
|
|
|
@ -26,6 +26,11 @@ struct AppView: View {
|
||||||
.onAppear {
|
.onAppear {
|
||||||
loadRoute()
|
loadRoute()
|
||||||
}
|
}
|
||||||
|
.sheet(item: $router.error) { error in
|
||||||
|
Text(error.error)
|
||||||
|
.multilineTextAlignment(.center)
|
||||||
|
.padding()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private func loadRoute() {
|
private func loadRoute() {
|
||||||
|
|
|
@ -12,6 +12,8 @@ protocol RocketChatClientProtocol {
|
||||||
}
|
}
|
||||||
|
|
||||||
final class RocketChatClient: NSObject {
|
final class RocketChatClient: NSObject {
|
||||||
|
@Dependency private var errorActionHandler: ErrorActionHandling
|
||||||
|
|
||||||
private let server: Server
|
private let server: Server
|
||||||
|
|
||||||
init(server: Server) {
|
init(server: Server) {
|
||||||
|
@ -42,22 +44,27 @@ final class RocketChatClient: NSObject {
|
||||||
urlRequest.httpBody = request.body
|
urlRequest.httpBody = request.body
|
||||||
|
|
||||||
return session.dataTaskPublisher(for: urlRequest)
|
return session.dataTaskPublisher(for: urlRequest)
|
||||||
.tryMap { (data, response) in
|
.tryMap { data, response in
|
||||||
guard let httpResponse = response as? HTTPURLResponse, 200...299 ~= httpResponse.statusCode else {
|
if let response = response as? HTTPURLResponse, response.statusCode == 401 {
|
||||||
throw RocketChatError.unauthorized
|
throw RocketChatError.unauthorized
|
||||||
}
|
}
|
||||||
|
|
||||||
return try data.decode(T.Response.self)
|
let decoder = JSONDecoder()
|
||||||
}
|
|
||||||
.mapError { error in
|
if let response = try? decoder.decode(T.Response.self, from: data) {
|
||||||
if let error = error as? DecodingError {
|
return response
|
||||||
return .decoding(error: error)
|
|
||||||
}
|
|
||||||
if let error = error as? RocketChatError {
|
|
||||||
return error
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return .unknown(error: error)
|
let response = try decoder.decode(ErrorResponse.self, from: data)
|
||||||
|
throw RocketChatError.server(response: response)
|
||||||
|
}
|
||||||
|
.mapError { [weak self] error in
|
||||||
|
guard let error = error as? RocketChatError else {
|
||||||
|
return .unknown
|
||||||
|
}
|
||||||
|
|
||||||
|
self?.errorActionHandler.handle(error: error)
|
||||||
|
return error
|
||||||
}
|
}
|
||||||
.eraseToAnyPublisher()
|
.eraseToAnyPublisher()
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,7 +1,15 @@
|
||||||
import Foundation
|
import Foundation
|
||||||
|
|
||||||
enum RocketChatError: Error {
|
struct ErrorResponse: Codable, Identifiable {
|
||||||
case decoding(error: Error)
|
var id: String {
|
||||||
case unknown(error: Error)
|
error
|
||||||
case unauthorized
|
}
|
||||||
|
|
||||||
|
let error: String
|
||||||
|
}
|
||||||
|
|
||||||
|
enum RocketChatError: Error {
|
||||||
|
case server(response: ErrorResponse)
|
||||||
|
case unauthorized
|
||||||
|
case unknown
|
||||||
}
|
}
|
||||||
|
|
|
@ -16,7 +16,6 @@ final class MessagesLoader {
|
||||||
@Dependency private var client: RocketChatClientProtocol
|
@Dependency private var client: RocketChatClientProtocol
|
||||||
@Dependency private var database: Database
|
@Dependency private var database: Database
|
||||||
@Dependency private var serversDB: ServersDatabase
|
@Dependency private var serversDB: ServersDatabase
|
||||||
@Dependency private var errorActionHandler: ErrorActionHandling
|
|
||||||
|
|
||||||
private var roomID: String?
|
private var roomID: String?
|
||||||
|
|
||||||
|
@ -34,8 +33,8 @@ final class MessagesLoader {
|
||||||
client.syncMessages(rid: rid, updatedSince: date)
|
client.syncMessages(rid: rid, updatedSince: date)
|
||||||
.receive(on: DispatchQueue.main)
|
.receive(on: DispatchQueue.main)
|
||||||
.sink { [weak self] completion in
|
.sink { [weak self] completion in
|
||||||
if case .failure(let error) = completion {
|
if case .failure = completion {
|
||||||
self?.errorActionHandler.handle(error: error)
|
self?.scheduledSyncMessages(in: room, from: newUpdatedSince)
|
||||||
}
|
}
|
||||||
} receiveValue: { [weak self] messagesResponse in
|
} receiveValue: { [weak self] messagesResponse in
|
||||||
let messages = messagesResponse.result.updated
|
let messages = messagesResponse.result.updated
|
||||||
|
@ -58,9 +57,9 @@ final class MessagesLoader {
|
||||||
|
|
||||||
client.getHistory(rid: rid, t: room.t ?? "", latest: date)
|
client.getHistory(rid: rid, t: room.t ?? "", latest: date)
|
||||||
.receive(on: DispatchQueue.main)
|
.receive(on: DispatchQueue.main)
|
||||||
.sink { [weak self] completion in
|
.sink { completion in
|
||||||
if case .failure(let error) = completion {
|
if case .failure(let error) = completion {
|
||||||
self?.errorActionHandler.handle(error: error)
|
print(error)
|
||||||
}
|
}
|
||||||
} receiveValue: { [weak self] messagesResponse in
|
} receiveValue: { [weak self] messagesResponse in
|
||||||
let messages = messagesResponse.messages
|
let messages = messagesResponse.messages
|
||||||
|
@ -85,9 +84,9 @@ final class MessagesLoader {
|
||||||
|
|
||||||
client.sendRead(rid: rid)
|
client.sendRead(rid: rid)
|
||||||
.receive(on: DispatchQueue.main)
|
.receive(on: DispatchQueue.main)
|
||||||
.sink { [weak self] completion in
|
.sink { completion in
|
||||||
if case .failure(let error) = completion {
|
if case .failure(let error) = completion {
|
||||||
self?.errorActionHandler.handle(error: error)
|
print(error)
|
||||||
}
|
}
|
||||||
} receiveValue: { _ in
|
} receiveValue: { _ in
|
||||||
|
|
||||||
|
|
|
@ -11,7 +11,6 @@ final class RoomsLoader {
|
||||||
@Dependency private var client: RocketChatClientProtocol
|
@Dependency private var client: RocketChatClientProtocol
|
||||||
@Dependency private var database: Database
|
@Dependency private var database: Database
|
||||||
@Dependency private var serversDB: ServersDatabase
|
@Dependency private var serversDB: ServersDatabase
|
||||||
@Dependency private var errorActionHandler: ErrorActionHandling
|
|
||||||
|
|
||||||
private var timer: Timer?
|
private var timer: Timer?
|
||||||
private var cancellable = CancelBag()
|
private var cancellable = CancelBag()
|
||||||
|
@ -33,8 +32,8 @@ final class RoomsLoader {
|
||||||
)
|
)
|
||||||
.receive(on: DispatchQueue.main)
|
.receive(on: DispatchQueue.main)
|
||||||
.sink { [weak self] completion in
|
.sink { [weak self] completion in
|
||||||
if case .failure(let error) = completion {
|
if case .failure = completion {
|
||||||
self?.errorActionHandler.handle(error: error)
|
self?.scheduledLoadRooms(in: server)
|
||||||
}
|
}
|
||||||
} receiveValue: { (roomsResponse, subscriptionsResponse) in
|
} receiveValue: { (roomsResponse, subscriptionsResponse) in
|
||||||
let rooms = roomsResponse.update
|
let rooms = roomsResponse.update
|
||||||
|
|
Loading…
Reference in New Issue