Last active
July 6, 2017 13:41
-
-
Save astrokin/d433f4ac86ab102d0ca0b93b6c8c2e24 to your computer and use it in GitHub Desktop.
DO Alert Service
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
enum AlertActionStyle: Int { | |
case `default` | |
case cancel | |
case destructive | |
} | |
enum AlertControllerStyle : Int { | |
case actionSheet | |
case alert | |
} | |
typealias AlertAction = (title: String, action: VoidClosure?, style: AlertActionStyle) | |
protocol AlertServiceProtocol { | |
var errorTitle: String { get } | |
var okTitle: String { get } | |
var cancelTitle: String { get } | |
func showMessage(_ message: String, title: String?, onDismiss: (() -> Void)?) | |
func showErrorMessage(_ message: String, onDismiss: (() -> Void)?) | |
func showMessage(_ message: String?, title: String?, actions: [AlertAction]?, preferredStyle: AlertControllerStyle, onDismiss: (() -> Void)?) | |
} | |
import UIKit | |
import DOAlertController | |
import Localize_Swift | |
import Async | |
final class AlertService: AlertServiceProtocol { | |
static let shared = AlertService() | |
fileprivate(set) lazy var alertControllerStyle: UIAlertControllerStyle = .alert | |
fileprivate(set) lazy var alertsQueue: OperationQueue = OperationQueue() | |
let errorTitle: String | |
let okTitle: String | |
let cancelTitle: String | |
init(errorTitle: String = "Error".localizedString, | |
okTitle: String = "OK".localizedString, | |
cancel: String = "Cancel".localizedString) { | |
self.errorTitle = errorTitle | |
self.okTitle = okTitle | |
cancelTitle = cancel | |
alertsQueue.maxConcurrentOperationCount = 1 | |
} | |
func showMessage(_ message: String, title: String? = nil, onDismiss: (() -> Void)? = nil) { | |
showMessage(message, title: title, actions: nil, onDismiss: onDismiss) | |
} | |
func showErrorMessage(_ message: String, onDismiss: (() -> Void)? = nil) { | |
showMessage(message, title: errorTitle, actions: nil, onDismiss: onDismiss) | |
} | |
func showMessage(_ message: String?, | |
title: String?, | |
actions: [AlertAction]?, | |
preferredStyle: AlertControllerStyle = .actionSheet, | |
onDismiss: (() -> Void)? = nil) { | |
let alertController = alertControllerWith(message, title: title, actions: actions, preferredStyle: preferredStyle.toDOAlertControllerStyle, onDismiss: onDismiss) | |
let op = AlertOperation(alert: alertController) | |
alertsQueue.addOperation(op) | |
} | |
} | |
extension AlertControllerStyle { | |
var toDOAlertControllerStyle: DOAlertControllerStyle { | |
switch self { | |
case .actionSheet: return DOAlertControllerStyle.actionSheet | |
case .alert: return DOAlertControllerStyle.alert | |
} | |
} | |
} | |
// MARK: - Private | |
private extension AlertService { | |
func alertControllerWith(_ message: String?, title: String?, actions: [AlertAction]?, | |
preferredStyle: DOAlertControllerStyle = .actionSheet, onDismiss: (() -> Void)? = nil) -> Alert { | |
let alert = AlertController(title: title, message: message, preferredStyle: preferredStyle) | |
if #available(iOS 9.0, *) { | |
alert.loadViewIfNeeded() | |
} else { | |
_ = alert.view | |
} | |
Style.applyDefault(alertController: alert) | |
if let actArray = actions { // map actions | |
actArray.forEach { title, alertAction, style in | |
let action = DOAlertAction(title: title, style: DOAlertActionStyle(rawValue: style.rawValue) ?? .default, handler: { (_) -> Void in | |
alertAction?() | |
alert.dismiss(onDismiss) | |
}) | |
alert.addAction(action) | |
} | |
} else { // add ok button if there is no actions | |
let okAction = DOAlertAction(title: okTitle, style: .default) { (_) -> Void in | |
alert.dismiss(onDismiss) | |
} | |
alert.addAction(okAction) | |
} | |
return alert | |
} | |
} | |
protocol Alert { | |
var completion: VoidClosure? { get set } | |
func present(_ presenter: UIViewController) | |
} | |
extension Alert { | |
func dismiss(_ onDismiss: VoidClosure?) { | |
onDismiss?() | |
completion?() | |
} | |
} | |
// MARK: - AlertController | |
private class AlertController: DOAlertController, Alert { | |
var completion: VoidClosure? | |
func present(_ presenter: UIViewController) { | |
presenter.present(self, animated: true, completion: nil) | |
} | |
func dismiss(_ onDismiss: VoidClosure?) { | |
onDismiss?() | |
// we need this delay because of custom DOAlertController | |
// we do not know exactly moment of dismiss | |
Async.main(after: 1, { [weak self] in | |
guard let `self` = self else { return } | |
self.completion?() | |
}) | |
} | |
} | |
// MARK: - AlertOperation | |
private class AlertOperation: ConcurrentOperation { | |
var alert: Alert | |
fileprivate var presentViewController: UIViewController! | |
init(alert: Alert) { | |
self.alert = alert | |
} | |
override func start() { | |
if isCancelled { | |
state = .Finished | |
} else { | |
guard let top = UIApplication.topViewController else { | |
logError("[Alert] Failed to present NO top view controller") | |
state = .Finished | |
return | |
} | |
guard top.presentedViewController == nil else { | |
logError("[Alert] Failed to present top view controller already present something") | |
state = .Finished | |
return | |
} | |
if UIApplication.shared.applicationState != .active { | |
logError("[Alert] Failed to present application not active") | |
state = .Finished | |
return | |
} | |
presentViewController = top | |
main() | |
state = .Executing | |
} | |
} | |
override func main() { | |
alert.completion = { [weak self] in | |
guard let `self` = self else { return } | |
self.state = .Finished | |
} | |
Async.main({ [weak self] _ in | |
guard let `self` = self else { return } | |
self.alert.present(self.presentViewController) | |
}) | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment