Skip to content

Instantly share code, notes, and snippets.

@astrokin
Last active July 6, 2017 13:41
Show Gist options
  • Save astrokin/d433f4ac86ab102d0ca0b93b6c8c2e24 to your computer and use it in GitHub Desktop.
Save astrokin/d433f4ac86ab102d0ca0b93b6c8c2e24 to your computer and use it in GitHub Desktop.
DO Alert Service
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