ProgressAlertHandler.swift 3.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687
  1. import UIKit
  2. import DcCore
  3. protocol ProgressAlertHandler: UIViewController {
  4. var progressAlert: UIAlertController? { get set } // needs to be implemented as weak
  5. var progressObserver: NSObjectProtocol? { get set } // set to nil in viewDidDisappear
  6. func showProgressAlert(title: String, dcContext: DcContext)
  7. func updateProgressAlertValue(value: Int?)
  8. func updateProgressAlert(error: String?)
  9. func updateProgressAlertSuccess(completion: VoidFunction?)
  10. func addProgressAlertListener(progressName: Notification.Name, onSuccess: @escaping VoidFunction)
  11. }
  12. extension ProgressAlertHandler {
  13. func showProgressAlert(title: String, dcContext: DcContext) {
  14. let progressAlert = makeProgressAlert(dcContext: dcContext)
  15. progressAlert.actions[0].isEnabled = true
  16. progressAlert.title = title
  17. progressAlert.message = String.localized("one_moment")
  18. self.present(progressAlert, animated: true, completion: nil)
  19. self.progressAlert = progressAlert
  20. }
  21. private func makeProgressAlert(dcContext: DcContext) -> UIAlertController {
  22. let alert = UIAlertController(title: "", message: "", preferredStyle: .alert)
  23. alert.addAction(UIAlertAction(
  24. title: String.localized("cancel"),
  25. style: .cancel,
  26. handler: { _ in
  27. dcContext.stopOngoingProcess()
  28. }))
  29. return alert
  30. }
  31. func updateProgressAlertValue(value: Int?) {
  32. if let value = value {
  33. progressAlert?.message = String.localized("one_moment") + " " + String(value/10) + "%"
  34. }
  35. }
  36. func updateProgressAlert(error message: String?) {
  37. DispatchQueue.main.async(execute: {
  38. // CAVE: show the new alert in the dismiss-done-handler of the previous one -
  39. // otherwise we get the error "Attempt to present <UIAlertController: ...> while a presentation is in progress."
  40. // and the error won't be shown.
  41. // (when animated is true, that works also sequentially, however better not rely on that, also we do not want an animation here)
  42. self.progressAlert?.dismiss(animated: false) {
  43. let errorAlert = UIAlertController(title: String.localized("error"), message: message, preferredStyle: .alert)
  44. errorAlert.addAction(UIAlertAction(title: String.localized("ok"), style: .default, handler: nil))
  45. self.present(errorAlert, animated: true, completion: nil)
  46. }
  47. })
  48. }
  49. func updateProgressAlertSuccess(completion onComplete: VoidFunction?) {
  50. updateProgressAlertValue(value: 1000)
  51. // delay so the user has time to read the success message
  52. DispatchQueue.main.asyncAfter(deadline: .now() + 1, execute: {
  53. self.progressAlert?.dismiss(animated: true) {
  54. onComplete?()
  55. }
  56. })
  57. }
  58. func addProgressAlertListener(progressName: Notification.Name, onSuccess: @escaping VoidFunction) {
  59. let nc = NotificationCenter.default
  60. progressObserver = nc.addObserver(
  61. forName: progressName,
  62. object: nil,
  63. queue: nil
  64. ) { [weak self] notification in
  65. guard let self = self else { return }
  66. if let ui = notification.userInfo {
  67. if ui["error"] as? Bool ?? false {
  68. DcContext.shared.maybeStartIo()
  69. self.updateProgressAlert(error: ui["errorMessage"] as? String)
  70. } else if ui["done"] as? Bool ?? false {
  71. DcContext.shared.maybeStartIo()
  72. self.updateProgressAlertSuccess(completion: onSuccess)
  73. } else {
  74. self.updateProgressAlertValue(value: ui["progress"] as? Int)
  75. }
  76. }
  77. }
  78. }
  79. }