mono0926
3/17/2017 - 2:04 AM

Protocol OrientedなRxSwift化したUIAlertController

Protocol OrientedなRxSwift化したUIAlertController

import Foundation
import RxSwift

private func getAllEnumValues<T: Hashable>(_: T.Type) -> [T] {
    var i = 0
    let iterator = AnyIterator<T> {
        let next = withUnsafeBytes(of: &i) { $0.load(as: T.self) }
        if next.hashValue != i { return nil }
        i += 1
        return next
    }
    return Array(iterator)
}

public protocol AlertActionType: Hashable {
    var style: UIAlertActionStyle { get }
    static var values: [Self] { get }
}

extension AlertActionType {
    public static var values: [Self] { return getAllEnumValues(Self.self) }
}

public protocol AlertAction {
    associatedtype AType: AlertActionType
    func titie(for type: AType) -> String
}

public struct Alert {
    private init() {}

    public struct OkAction: AlertAction {
        public let okTitle: String

        init(okTitle: String = okString) {
            self.okTitle = okTitle
        }

        public func titie(for type: ActionType) -> String {
            switch type {
            case .ok: return okTitle
            }
        }

        public enum ActionType: String, AlertActionType {
            case ok

            public var style: UIAlertActionStyle {
                switch self {
                case .ok: return .default
                }
            }
        }
    }

    public struct OkCancelAction: AlertAction {
        public let okTitle: String
        public let cancelTitle: String

        init(okTitle: String = okString, cancelTitle: String = cancelString) {
            self.okTitle = okTitle
            self.cancelTitle = cancelTitle
        }

        public func titie(for type: ActionType) -> String {
            switch type {
            case .ok: return okTitle
            case .cancel: return cancelTitle
            }
        }

        public enum ActionType: String, AlertActionType {
            case
            ok,
            cancel

            public var style: UIAlertActionStyle {
                switch self {
                case .ok: return .default
                case .cancel: return .cancel
                }
            }
        }
    }
}

extension UIViewController {
    public func prompt<Action: AlertAction>(title: String?, message: String?, action: Action) -> Observable<Action.AType> {
        return Observable.create { observer in

            let alertView = UIAlertController(title: title, message: message, preferredStyle: .alert)

            for type in Action.AType.values {
                alertView.addAction(UIAlertAction(title: action.titie(for: type), style: type.style) { _ in
                    observer.on(.next(type))
                })
            }

            self.present(alertView, animated: true, completion: nil)

            return Disposables.create {
                alertView.dismiss(animated:false, completion: nil)
            }
        }
    }
    public func prompt(title: String?, message: String?, okTitle: String = okString, didTap: (() -> ())? = {}) {
        prompt(title: title, message: message, action: Alert.OkAction(okTitle: okTitle))
            .subscribeOnMainThread { event in
                switch event {
                case .completed: didTap?()
                case .error: joinusAssert(false)
                case .next:break
                }
            }.disposed(by: rx_disposeBag)
    }
    public func promptWithCancel(title: String?,
                                 message: String?,
                                 okTitle: String = okString,
                                 cancelTitle: String = cancelString,
                                 didTap: (() -> ())? = {}) {
        prompt(title: title,
               message: message,
               action: Alert.OkCancelAction(okTitle: okTitle, cancelTitle: cancelTitle))
            .subscribeOnMainThread { event in
                switch event {
                case .completed: break
                case .error: joinusAssert(false)
                case .next(let action):
                    switch action {
                    case .ok:
                        didTap?()
                    case .cancel:
                        break
                    }
                }
            }.disposed(by: rx_disposeBag)
    }
    public func prompt(error message: String?, didTap: (() -> ())? = {}) {
        prompt(title: errorOccuredString, message: message)
    }
}