ios/SpectrumKitSample/SpectrumKitSample-iOS/Alerts.swift (129 lines of code) (raw):

// Copyright (c) Facebook, Inc. and its affiliates. // // This source code is licensed under the MIT license found in the // LICENSE file in the root directory of this source tree. import UIKit /// Protocol representing all cases an alert can have. protocol AlertOptions { var title: String { get } var isAvailable: Bool { get } static var allValues: [Self] { get } } extension AlertOptions { var isAvailable: Bool { return true } } /// Representation of an alert content. struct AlertContent<Options: AlertOptions> { let title: String let message: String? let optionsType: Options.Type } /// Binding of an alert content and its action. struct Alert<Options: AlertOptions> { let content: AlertContent<Options> let actionHandler: (Options) -> Void } extension UIViewController { func presentAlert<Options>(_ alertContent: AlertContent<Options>, from sourceView: UIView, using handler: @escaping (Options) -> Void) { let alertController = UIAlertController(alert: Alert(content: alertContent, actionHandler: handler)) alertController.popoverPresentationController?.sourceView = sourceView alertController.popoverPresentationController?.sourceRect = CGRect(x: sourceView.bounds.width - 50, y: sourceView.bounds.origin.x, width: 50, height: sourceView.bounds.height) self.present(alertController, animated: true, completion: nil) } func presentError(_ error: Swift.Error) { let alertController = UIAlertController(title: NSLocalizedString("Error", comment: "Error alert title"), message: extractErrorMessage(from: error), preferredStyle: .alert) alertController.addAction(UIAlertAction(title: NSLocalizedString("Ok", comment: "Ok button"), style: .default, handler: nil)) self.present(alertController, animated: true, completion: nil) } } extension UIAlertController { convenience init<Options>(alert: Alert<Options>) { self.init(title: alert.content.title, message: alert.content.message, preferredStyle: .actionSheet) alert.content.optionsType.allValues .filter { $0.isAvailable } .map { option in UIAlertAction(title: option.title, style: .default, handler: { _ in alert.actionHandler(option) }) } .forEach(self.addAction) self.addAction(UIAlertAction(title: NSLocalizedString("Cancel", comment: "Cancel alert button"), style: .cancel, handler: nil)) } } private func extractErrorMessage(from error: Swift.Error) -> String { let nsError = error as NSError let name: String = { if let name = nsError.userInfo[FSPErrorNameKey] as? String { return name } else { return NSLocalizedString("Unknown", comment: "Unknown error alert message") } }() let messageFormat = NSLocalizedString("Operation failed: %@", comment: "Error alert message format") return String.localizedStringWithFormat(messageFormat, name) } /// Type-erased coordinator to present an AlertContent and assign the picked value to a property struct AnyAlertOptionController { // MARK: - Private Properties private let presentClosure: (UIViewController, UIView) -> Void // MARK: - Public Methods init<Options, Destination>(alertContent: AlertContent<Options>, destination: Destination, keyPath: ReferenceWritableKeyPath<Destination, Options>) { self.presentClosure = { viewController, sourceView in viewController.presentAlert(alertContent, from: sourceView, using: { option in destination[keyPath: keyPath] = option }) } } func presentAlert(from viewController: UIViewController, sourceView: UIView) { self.presentClosure(viewController, sourceView) } } struct ValueHandler<Value> { // MARK: - Private Properties private let updateClosure: (Value) -> Void // MARK: - Public Methods init<Destination>(destination: Destination, keyPath: ReferenceWritableKeyPath<Destination, Value>) { self.updateClosure = { value in destination[keyPath: keyPath] = value } } func update(_ value: Value) { self.updateClosure(value) } } struct Alerts { static let imageSource = AlertContent(title: NSLocalizedString("Select source image source", comment: "Source image alert title"), message: nil, optionsType: ImagePickerCoordinator.Source.self) static let inputType = AlertContent(title: NSLocalizedString("How would should the source be loaded?", comment: "Source load method alert title"), message: nil, optionsType: IOType.self) static let outputType = AlertContent(title: NSLocalizedString("How would should the target be saved?", comment: "Target save method alert title"), message: nil, optionsType: IOType.self) static let inputFormat = AlertContent(title: NSLocalizedString("Select an image input format", comment: "Image input format alert title"), message: nil, optionsType: OutputImageFormat.self) static let outputFormat = AlertContent(title: NSLocalizedString("Select an image output format", comment: "Image output format alert title"), message: nil, optionsType: OutputImageFormat.self) static let encodingMode = AlertContent(title: NSLocalizedString("Select encoding mode", comment: "Image encoding mode title"), message: nil, optionsType: SpectrumViewModel.CompressionMode.self) static let rotation = AlertContent(title: NSLocalizedString("Select a rotation option", comment: "Image rotation format alert title"), message: nil, optionsType: SpectrumViewModel.ImageRotationOptions.self) static let scaling = AlertContent(title: NSLocalizedString("Select a scaling option", comment: "Image scaling format alert title"), message: nil, optionsType: SpectrumViewModel.ImageScalingOptions.self) static let cropping = AlertContent(title: NSLocalizedString("Select a cropping option", comment: "Image cropping format alert title"), message: nil, optionsType: SpectrumViewModel.ImageCroppingOptions.self) static let configurationGeneralDefaultBackgroundColor = AlertContent(title: NSLocalizedString("Select default background color", comment: "Configuration general background color title"), message: nil, optionsType: ConfigurationViewModel.DefaultBackgroundColor.self) static let configurationPngCompressionLevel = AlertContent(title: NSLocalizedString("Select PNG compression level", comment: "Configuration PNG compression level"), message: nil, optionsType: ConfigurationViewModel.CompressionLevel.self) static let configurationWebpMethod = AlertContent(title: NSLocalizedString("Select WebP method", comment: "Configuration webp method title"), message: nil, optionsType: ConfigurationViewModel.WebpMethod.self) static let configurationWebpImageHint = AlertContent(title: NSLocalizedString("Select WebP image hint", comment: "Configuration webp image hint title"), message: nil, optionsType: ConfigurationWebpImageHint.self) }