Skip to content

Commit

Permalink
Merge pull request #207 from PierreBrisorgueil/refactorErrorsMngt
Browse files Browse the repository at this point in the history
fix(global): error handling 🐛
  • Loading branch information
PierreBrisorgueil authored May 15, 2020
2 parents ef8f1f3 + 5e91e36 commit 5a99476
Show file tree
Hide file tree
Showing 22 changed files with 136 additions and 158 deletions.
10 changes: 9 additions & 1 deletion waosSwift/config/default/development.json
Original file line number Diff line number Diff line change
Expand Up @@ -67,13 +67,21 @@
}
}
],
"img" : {
"img": {
"compresion": 0.25,
"styles": {
"blured": 10,
"overlayFraction": 0.9
}
},
"times": {
"buttons": {
"throttle": 3000
},
"errors": {
"debounce": 2000
},
},
"theme": {
"global": {
"radius": 5,
Expand Down
14 changes: 8 additions & 6 deletions waosSwift/lib/helpers/Errors.swift
Original file line number Diff line number Diff line change
Expand Up @@ -48,14 +48,16 @@ func getError(_ error: Error, file: StaticString = #file, function: StaticString
* @param {Error} error
* @return {CustomError}
*/
func purgeErrors(errors: [DisplayError], titles: [String]) -> [DisplayError] {
var _error: [DisplayError] = errors
for title in titles {
if let index = _error.firstIndex(where: { $0.title == title }) {
_error.remove(at: index)
func purgeErrors(errors: [DisplayError], specificTitles: [String]? = nil) -> [DisplayError] {
var _errors: [DisplayError] = errors
if let _titles = specificTitles {
for title in _titles {
if let index = _errors.firstIndex(where: { $0.title == title }) {
_errors.remove(at: index)
}
}
}
return _error
return _errors
}

/**
Expand Down
1 change: 1 addition & 0 deletions waosSwift/lib/services/Networking.swift
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,7 @@ final class Networking<Target: TargetType>: MoyaProvider<Target> {
let message = "🌎 failure -> \(requestString) (\(response.statusCode)) (\(target))"
log.warning(message, file: file, function: function, line: line)
}
print("totot", response.statusCode)
} else {
let message = "🌎 failure -> \(requestString)\n\(error)"
log.warning(message, file: file, function: function, line: line)
Expand Down
2 changes: 1 addition & 1 deletion waosSwift/modules/app/AppDelegate.swift
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@ class AppDelegate: UIResponder, UIApplicationDelegate {
ToastView.appearance().bottomOffsetPortrait = 100
ToastView.appearance().bottomOffsetLandscape = 100
} else {
let marginTop: CGFloat = 165
let marginTop: CGFloat = 170
ToastView.appearance().bottomOffsetPortrait = max(UIScreen.main.bounds.width, UIScreen.main.bounds.height) - marginTop
ToastView.appearance().bottomOffsetLandscape = min(UIScreen.main.bounds.width, UIScreen.main.bounds.height) - marginTop
}
Expand Down
28 changes: 9 additions & 19 deletions waosSwift/modules/auth/controllers/AuthSigninController.swift
Original file line number Diff line number Diff line change
Expand Up @@ -156,7 +156,7 @@ private extension AuthSignInController {
func bindAction(_ reactor: AuthSigninReactor) {
// button signin
buttonSignin.rx.tap
.throttle(.seconds(3), latest: false, scheduler: MainScheduler.instance)
.throttle(.milliseconds(Metric.timesButtonsThrottle), latest: false, scheduler: MainScheduler.instance)
.map { _ in Reactor.Action.signIn }
.bind(to: reactor.action)
.disposed(by: self.disposeBag)
Expand All @@ -173,7 +173,7 @@ private extension AuthSignInController {
.bind(to: reactor.action)
.disposed(by: self.disposeBag)
self.inputEmail.rx.controlEvent(.editingChanged).asObservable()
.debounce(.seconds(2), scheduler: MainScheduler.instance)
.debounce(.milliseconds(Metric.timesErrorsDebounce), scheduler: MainScheduler.instance)
.map {Reactor.Action.validateEmail}
.bind(to: reactor.action)
.disposed(by: self.disposeBag)
Expand All @@ -183,11 +183,6 @@ private extension AuthSignInController {
.map {Reactor.Action.updatePassword($0!)}
.bind(to: reactor.action)
.disposed(by: self.disposeBag)
self.inputPassword.rx.controlEvent(.editingChanged).asObservable()
.debounce(.seconds(2), scheduler: MainScheduler.instance)
.map {Reactor.Action.validatePassword}
.bind(to: reactor.action)
.disposed(by: self.disposeBag)
}

// MARK: states (Reactor -> View)
Expand All @@ -199,19 +194,14 @@ private extension AuthSignInController {
.distinctUntilChanged()
.bind(to: self.rx.isAnimating)
.disposed(by: disposeBag)
// error
// validation errors
reactor.state
.map { $0.errors.count }
.distinctUntilChanged()
.subscribe(onNext: { count in
if(count > 0) {
let message: [String] = reactor.currentState.errors.map { "\($0.description)." }
ToastCenter.default.cancelAll()
Toast(text: message.joined(separator: "\n"), delay: 0, duration: Delay.long).show()
} else {
ToastCenter.default.cancelAll()
}

.map { $0.errors }
.filter { $0.count > 0 }
.distinctUntilChanged { $0.count == $1.count }
.subscribe(onNext: { errors in
ToastCenter.default.cancelAll()
Toast(text: errors.map { "\($0.description)." }.joined(separator: "\n"), delay: 0, duration: Delay.long).show()
})
.disposed(by: self.disposeBag)
reactor.state
Expand Down
31 changes: 13 additions & 18 deletions waosSwift/modules/auth/controllers/AuthSignupController.swift
Original file line number Diff line number Diff line change
Expand Up @@ -177,7 +177,7 @@ private extension AuthSignUpController {
.disposed(by: self.disposeBag)
// button signup
buttonSignup.rx.tap
.throttle(.seconds(3), latest: false, scheduler: MainScheduler.instance)
.throttle(.milliseconds(Metric.timesButtonsThrottle), latest: false, scheduler: MainScheduler.instance)
.map { _ in Reactor.Action.signUp }
.bind(to: reactor.action)
.disposed(by: self.disposeBag)
Expand All @@ -194,7 +194,7 @@ private extension AuthSignUpController {
.bind(to: reactor.action)
.disposed(by: self.disposeBag)
self.inputFirstName.rx.controlEvent(.editingChanged).asObservable()
.debounce(.seconds(2), scheduler: MainScheduler.instance)
.debounce(.milliseconds(Metric.timesErrorsDebounce), scheduler: MainScheduler.instance)
.map {Reactor.Action.validateFirstName}
.bind(to: reactor.action)
.disposed(by: self.disposeBag)
Expand All @@ -205,7 +205,7 @@ private extension AuthSignUpController {
.bind(to: reactor.action)
.disposed(by: self.disposeBag)
self.inputLastName.rx.controlEvent(.editingChanged).asObservable()
.debounce(.seconds(2), scheduler: MainScheduler.instance)
.debounce(.milliseconds(Metric.timesErrorsDebounce), scheduler: MainScheduler.instance)
.map {Reactor.Action.validateLastName}
.bind(to: reactor.action)
.disposed(by: self.disposeBag)
Expand All @@ -216,7 +216,7 @@ private extension AuthSignUpController {
.bind(to: reactor.action)
.disposed(by: self.disposeBag)
self.inputEmail.rx.controlEvent(.editingChanged).asObservable()
.debounce(.seconds(2), scheduler: MainScheduler.instance)
.debounce(.milliseconds(Metric.timesErrorsDebounce), scheduler: MainScheduler.instance)
.map {Reactor.Action.validateEmail}
.bind(to: reactor.action)
.disposed(by: self.disposeBag)
Expand All @@ -227,7 +227,7 @@ private extension AuthSignUpController {
.bind(to: reactor.action)
.disposed(by: self.disposeBag)
self.inputPassword.rx.controlEvent(.editingChanged).asObservable()
.debounce(.seconds(2), scheduler: MainScheduler.instance)
.debounce(.milliseconds(Metric.timesErrorsDebounce), scheduler: MainScheduler.instance)
.map {Reactor.Action.validatePassword}
.bind(to: reactor.action)
.disposed(by: self.disposeBag)
Expand All @@ -251,19 +251,14 @@ private extension AuthSignUpController {
self?.dismiss(animated: true, completion: nil)
})
.disposed(by: self.disposeBag)
// errors
// validation errors
reactor.state
.map { $0.errors.count }
.distinctUntilChanged()
.subscribe(onNext: { count in
if(count > 0) {
let message: [String] = reactor.currentState.errors.map { "\($0.description)." }
ToastCenter.default.cancelAll()
Toast(text: message.joined(separator: "\n"), delay: 0, duration: Delay.long).show()
} else {
ToastCenter.default.cancelAll()
}

.map { $0.errors }
.filter { $0.count > 0 }
.distinctUntilChanged { $0.count == $1.count }
.subscribe(onNext: { errors in
ToastCenter.default.cancelAll()
Toast(text: errors.map { "\($0.description)." }.joined(separator: "\n"), delay: 0, duration: Delay.long).show()
})
.disposed(by: self.disposeBag)
reactor.state
Expand Down Expand Up @@ -302,7 +297,7 @@ private extension AuthSignUpController {
)
.map { [$0.0, $0.1] }
.map { !$0.contains(true) }
.bind(to: self.buttonSignup.rx.isEnabled)
.bind(to: self.buttonSignin.rx.isEnabled)
.disposed(by: disposeBag)
}
}
22 changes: 13 additions & 9 deletions waosSwift/modules/auth/reactors/AuthSigninReactor.swift
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,6 @@ final class AuthSigninReactor: Reactor {
case updateEmail(String)
case validateEmail
case updatePassword(String)
case validatePassword
case updateIsFilled(Bool)
// others
case signIn
Expand All @@ -35,6 +34,7 @@ final class AuthSigninReactor: Reactor {
case goSignUp
// default
case setRefreshing(Bool)
case validationError(CustomError)
case success(String)
case error(CustomError)
}
Expand Down Expand Up @@ -76,13 +76,11 @@ final class AuthSigninReactor: Reactor {
case .validateEmail:
switch currentState.user.validate(.email) {
case .valid: return .just(.success("\(User.Validators.email)"))
case let .invalid(err): return .just(.error(err[0] as! CustomError))
case let .invalid(err): return .just(.validationError(err[0] as! CustomError))
}
// password
case let .updatePassword(password):
return .just(.updatePassword(password))
case .validatePassword:
return .just(.success("password"))
// form
case let .updateIsFilled(isFilled):
return .just(.updateIsFilled(isFilled))
Expand Down Expand Up @@ -135,18 +133,24 @@ final class AuthSigninReactor: Reactor {
// success
case let .success(success):
log.verbose("♻️ Mutation -> State : succes \(success)")
state.errors = purgeErrors(errors: state.errors, titles: [success, "Schema validation error", "jwt", "unknow"])
state.errors = purgeErrors(errors: state.errors, specificTitles: [success])
// error
case let .validationError(error):
log.verbose("♻️ Mutation -> State : validation error \(error)")
if state.errors.firstIndex(where: { $0.title == error.message }) == nil {
state.errors.insert(DisplayError(title: error.message, description: (error.description ?? "Unknown error")), at: 0)
}
case let .error(error):
log.verbose("♻️ Mutation -> State : error \(error)")
let _error: DisplayError
if error.code == 401 {
self.provider.preferencesService.isLogged = false
state.errors.insert(DisplayError(title: "jwt", description: "Wrong Password or Email."), at: 0)
_error = DisplayError(title: "jwt", description: "Wrong Password or Email.", type: error.type)
} else {
if state.errors.firstIndex(where: { $0.title == error.message }) == nil {
state.errors.insert(DisplayError(title: error.message, description: (error.description ?? "Unknown error")), at: 0)
}
_error = DisplayError(title: error.message, description: (error.description ?? "Unknown error"), type: error.type)
}
ToastCenter.default.cancelAll()
Toast(text: _error.description, delay: 0, duration: Delay.long).show()
}
return state
}
Expand Down
24 changes: 16 additions & 8 deletions waosSwift/modules/auth/reactors/AuthSignupReactor.swift
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,7 @@ final class AuthSignUpReactor: Reactor {
// default
case dismiss
case setRefreshing(Bool)
case validationError(CustomError)
case success(String)
case error(CustomError)
}
Expand Down Expand Up @@ -85,7 +86,7 @@ final class AuthSignUpReactor: Reactor {
case .validateFirstName:
switch currentState.user.validate(.firstname) {
case .valid: return .just(.success("\(User.Validators.firstname)"))
case let .invalid(err): return .just(.error(err[0] as! CustomError))
case let .invalid(err): return .just(.validationError(err[0] as! CustomError))
}
// update password
// lastname
Expand All @@ -94,23 +95,23 @@ final class AuthSignUpReactor: Reactor {
case .validateLastName:
switch currentState.user.validate(.lastname) {
case .valid: return .just(.success("\(User.Validators.lastname)"))
case let .invalid(err): return .just(.error(err[0] as! CustomError))
case let .invalid(err): return .just(.validationError(err[0] as! CustomError))
}
// email
case let .updateEmail(email):
return .just(.updateEmail(email))
case .validateEmail:
switch currentState.user.validate(.email) {
case .valid: return .just(.success("\(User.Validators.email)"))
case let .invalid(err): return .just(.error(err[0] as! CustomError))
case let .invalid(err): return .just(.validationError(err[0] as! CustomError))
}
// password
case let .updatePassword(password):
return .just(.updatePassword(password))
case .validatePassword:
switch currentState.user.validate(.password) {
case .valid: return .just(.success("\(User.Validators.password)"))
case let .invalid(err): return .just(.error(err[0] as! CustomError))
case let .invalid(err): return .just(.validationError(err[0] as! CustomError))
}
// form
case let .updateIsFilled(isFilled):
Expand Down Expand Up @@ -173,17 +174,24 @@ final class AuthSignUpReactor: Reactor {
state.isRefreshing = isRefreshing
case let .success(success):
log.verbose("♻️ Mutation -> State : succes \(success)")
state.errors = purgeErrors(errors: state.errors, titles: [success, "Schema validation error", "jwt", "unknow"])
state.errors = purgeErrors(errors: state.errors, specificTitles: [success])
// error
case let .validationError(error):
log.verbose("♻️ Mutation -> State : validation error \(error)")
if state.errors.firstIndex(where: { $0.title == error.message }) == nil {
state.errors.insert(DisplayError(title: error.message, description: (error.description ?? "Unknown error")), at: 0)
}
case let .error(error):
log.verbose("♻️ Mutation -> State : error \(error)")
let _error: DisplayError
if error.code == 401 {
self.provider.preferencesService.isLogged = false
_error = DisplayError(title: "jwt", description: "Wrong Password or Email.", type: error.type)
} else {
if state.errors.firstIndex(where: { $0.title == error.message }) == nil {
state.errors.insert(DisplayError(title: error.message, description: (error.description ?? "Unknown error")), at: 0)
}
_error = DisplayError(title: error.message, description: (error.description ?? "Unknown error"), type: error.type)
}
ToastCenter.default.cancelAll()
Toast(text: _error.description, delay: 0, duration: Delay.long).show()
}
return state
}
Expand Down
2 changes: 2 additions & 0 deletions waosSwift/modules/core/controllers/CoreController.swift
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,8 @@ class CoreController: UIViewController {
static let tabBarColor = NSString(string: config["theme"]["tabBar"]["color"].string ?? "").boolValue
static let tabBarTintColor = NSString(string: config["theme"]["tabBar"]["tintColor"].string ?? "").boolValue
static let tabBarTitle = NSString(string: config["theme"]["tabBar"]["title"].string ?? "").boolValue
static let timesButtonsThrottle = Int(config["times"]["buttons"]["throttle"].int ?? 2000)
static let timesErrorsDebounce = Int(config["times"]["errors"]["debounce"].int ?? 2000)
}

lazy private(set) var className: String = {
Expand Down
4 changes: 4 additions & 0 deletions waosSwift/modules/core/controllers/CoreFormController.swift
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,10 @@ class CoreFormController: FormViewController {
static let tabBarTitle = NSString(string: config["theme"]["tabBar"]["title"].string ?? "").boolValue
static let imgCompression = CGFloat(config["img"]["compresion"].float ?? 1.0)
static let margin = CGFloat(config["theme"]["global"]["margin"].int ?? 0)
static let radius = CGFloat(config["theme"]["global"]["radius"].int ?? 0)
static let timesButtonsThrottle = Int(config["times"]["buttons"]["throttle"].int ?? 2000)
static let timesErrorsDebounce = Int(config["times"]["errors"]["debounce"].int ?? 2000)
static let avatar = CGFloat(100)
}

lazy private(set) var className: String = {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -132,7 +132,7 @@ private extension OnboardingController {

func bindAction(_ reactor: OnboardingReactor) {
completeButton.rx.tap
.throttle(.seconds(3), latest: false, scheduler: MainScheduler.instance)
.throttle(.milliseconds(Metric.timesButtonsThrottle), latest: false, scheduler: MainScheduler.instance)
.map { _ in Reactor.Action.complete }
.bind(to: reactor.action)
.disposed(by: self.disposeBag)
Expand Down
9 changes: 0 additions & 9 deletions waosSwift/modules/tasks/controllers/TasksListController.swift
Original file line number Diff line number Diff line change
Expand Up @@ -139,15 +139,6 @@ private extension TasksListController {
.distinctUntilChanged()
.bind(to: refreshControl.rx.isRefreshing)
.disposed(by: disposeBag)
// error
reactor.state
.map { $0.error?.description }
.throttle(.seconds(5), scheduler: MainScheduler.instance)
.filterNil()
.subscribe(onNext: { result in
Toast(text: result, delay: 0, duration: Delay.long).show()
})
.disposed(by: self.disposeBag)
}
}

Expand Down
Loading

0 comments on commit 5a99476

Please sign in to comment.