586 lines
23 KiB
Swift
586 lines
23 KiB
Swift
//
|
||
// MelonDSCoreSettingsViewController.swift
|
||
// Delta
|
||
//
|
||
// Created by Riley Testut on 4/13/20.
|
||
// Copyright © 2020 Riley Testut. All rights reserved.
|
||
//
|
||
|
||
import UIKit
|
||
import SafariServices
|
||
import MobileCoreServices
|
||
import CryptoKit
|
||
|
||
import DeltaCore
|
||
//import MelonDSDeltaCore
|
||
|
||
//import struct DSDeltaCore.DS
|
||
|
||
import Roxas
|
||
//
|
||
//private extension MelonDSCoreSettingsViewController
|
||
//{
|
||
// enum Section: Int
|
||
// {
|
||
// case general
|
||
// case airPlay
|
||
// case performance
|
||
// case dsBIOS
|
||
// case dsiBIOS
|
||
// case changeCore
|
||
// }
|
||
//
|
||
// enum AirPlayRow: Int, CaseIterable
|
||
// {
|
||
// case topScreenOnly
|
||
// case layoutHorizontally
|
||
// }
|
||
//
|
||
// @available(iOS 13, *)
|
||
// enum BIOSError: LocalizedError
|
||
// {
|
||
// case unknownSize(URL)
|
||
// case incorrectHash(URL, hash: String, expectedHash: String)
|
||
// case unsupportedHash(URL, hash: String)
|
||
// case incorrectSize(URL, size: Int, validSizes: Set<ClosedRange<Measurement<UnitInformationStorage>>>)
|
||
//
|
||
// private static let byteFormatter: ByteCountFormatter = {
|
||
// let formatter = ByteCountFormatter()
|
||
// formatter.includesActualByteCount = true
|
||
// formatter.countStyle = .binary
|
||
// return formatter
|
||
// }()
|
||
//
|
||
// var errorDescription: String? {
|
||
// switch self
|
||
// {
|
||
// case .unknownSize(let fileURL):
|
||
// return String(format: NSLocalizedString("%@’s size could not be determined.", comment: ""), fileURL.lastPathComponent)
|
||
//
|
||
// case .incorrectHash(let fileURL, let md5Hash, let expectedHash):
|
||
// return String(format: NSLocalizedString("%@‘s checksum does not match the expected checksum.\n\nChecksum:\n%@\n\nExpected:\n%@", comment: ""), fileURL.lastPathComponent, md5Hash, expectedHash)
|
||
//
|
||
// case .unsupportedHash(let fileURL, let md5Hash):
|
||
// return String(format: NSLocalizedString("%@ is not compatible with this version of Delta.\n\nChecksum:\n%@", comment: ""), fileURL.lastPathComponent, md5Hash)
|
||
//
|
||
// case .incorrectSize(let fileURL, let size, let validSizes):
|
||
// let actualSize = BIOSError.byteFormatter.string(fromByteCount: Int64(size))
|
||
//
|
||
// if let range = validSizes.first, validSizes.count == 1
|
||
// {
|
||
// if range.lowerBound == range.upperBound
|
||
// {
|
||
// // Single value
|
||
// let expectedSize = BIOSError.byteFormatter.string(fromByteCount: Int64(range.lowerBound.converted(to: .bytes).value))
|
||
// return String(format: NSLocalizedString("%@ is %@, but expected size is %@.", comment: ""), fileURL.lastPathComponent, actualSize, expectedSize)
|
||
// }
|
||
// else
|
||
// {
|
||
// // Range
|
||
// BIOSError.byteFormatter.includesActualByteCount = false
|
||
// defer { BIOSError.byteFormatter.includesActualByteCount = true }
|
||
//
|
||
// let lowerBound = BIOSError.byteFormatter.string(fromByteCount: Int64(range.lowerBound.converted(to: .bytes).value))
|
||
// let upperBound = BIOSError.byteFormatter.string(fromByteCount: Int64(range.upperBound.converted(to: .bytes).value))
|
||
// return String(format: NSLocalizedString("%@ is %@, but expected size is between %@ and %@.", comment: ""), fileURL.lastPathComponent, actualSize, lowerBound, upperBound)
|
||
// }
|
||
// }
|
||
// else
|
||
// {
|
||
// var description = String(format: NSLocalizedString("%@ is %@, but expected sizes are:", comment: ""), fileURL.lastPathComponent, actualSize) + "\n"
|
||
//
|
||
// let sortedRanges = validSizes.sorted(by: { $0.lowerBound < $1.lowerBound })
|
||
// for range in sortedRanges
|
||
// {
|
||
// // Assume BIOS with multiple valid file sizes don't use (>1 count) ranges.
|
||
// description += "\n" + BIOSError.byteFormatter.string(fromByteCount: Int64(range.lowerBound.converted(to: .bytes).value))
|
||
// }
|
||
//
|
||
// return description
|
||
// }
|
||
// }
|
||
// }
|
||
//
|
||
// var recoverySuggestion: String? {
|
||
// return NSLocalizedString("Please choose a different BIOS file.", comment: "")
|
||
// }
|
||
// }
|
||
//}
|
||
//
|
||
//class MelonDSCoreSettingsViewController: UITableViewController
|
||
//{
|
||
// private var importingBIOS: SystemBIOS?
|
||
//
|
||
// override func viewDidLoad()
|
||
// {
|
||
// super.viewDidLoad()
|
||
//
|
||
// if let navigationController = self.navigationController, navigationController.viewControllers.first != self
|
||
// {
|
||
// self.navigationItem.rightBarButtonItem = nil
|
||
// }
|
||
//
|
||
// NotificationCenter.default.addObserver(self, selector: #selector(MelonDSCoreSettingsViewController.willEnterForeground(_:)), name: UIApplication.willEnterForegroundNotification, object: nil)
|
||
// }
|
||
//
|
||
// override func viewWillAppear(_ animated: Bool)
|
||
// {
|
||
// super.viewWillAppear(animated)
|
||
//
|
||
// self.tableView.reloadData()
|
||
// }
|
||
//
|
||
// override func viewDidDisappear(_ animated: Bool)
|
||
// {
|
||
// super.viewDidDisappear(animated)
|
||
//
|
||
// if let core = Delta.registeredCores[.ds]
|
||
// {
|
||
// DatabaseManager.shared.performBackgroundTask { (context) in
|
||
// // Prepare database in case we changed/updated cores.
|
||
// DatabaseManager.shared.prepare(core, in: context)
|
||
// context.saveWithErrorLogging()
|
||
// }
|
||
// }
|
||
// }
|
||
//}
|
||
//
|
||
//private extension MelonDSCoreSettingsViewController
|
||
//{
|
||
// func isSectionHidden(_ section: Section) -> Bool
|
||
// {
|
||
// #if BETA
|
||
// let isBeta = true
|
||
// #else
|
||
// let isBeta = false
|
||
// #endif
|
||
//
|
||
// switch section
|
||
// {
|
||
// case .performance:
|
||
// // Hide AltJIT section for public builds.
|
||
// guard isBeta else { return true }
|
||
//
|
||
// guard Settings.preferredCore(for: .ds) == MelonDS.core else { return true }
|
||
// return !UIDevice.current.supportsJIT
|
||
//
|
||
// case .dsBIOS where Settings.preferredCore(for: .ds) == DS.core:
|
||
// // Using DeSmuME core, which doesn't require BIOS.
|
||
// return true
|
||
//
|
||
// case .dsiBIOS where Settings.preferredCore(for: .ds) == DS.core || !isBeta:
|
||
// // Using DeSmuME core, which doesn't require BIOS,
|
||
// // or using public Delta version, which doesn't support DSi (yet).
|
||
// return true
|
||
//
|
||
// case .changeCore where !isBeta:
|
||
// // Using public Delta version, which only supports melonDS core.
|
||
// return true
|
||
//
|
||
// default: return false
|
||
// }
|
||
// }
|
||
//}
|
||
//
|
||
//private extension MelonDSCoreSettingsViewController
|
||
//{
|
||
// func openMetadataURL(for key: DeltaCoreMetadata.Key)
|
||
// {
|
||
// guard let metadata = Settings.preferredCore(for: .ds)?.metadata else { return }
|
||
//
|
||
// let item = metadata[key]
|
||
// guard let url = item?.url else {
|
||
// if let indexPath = self.tableView.indexPathForSelectedRow
|
||
// {
|
||
// self.tableView.deselectRow(at: indexPath, animated: true)
|
||
// }
|
||
//
|
||
// return
|
||
// }
|
||
//
|
||
// let safariViewController = SFSafariViewController(url: url)
|
||
// safariViewController.preferredControlTintColor = .deltaPurple
|
||
// self.present(safariViewController, animated: true, completion: nil)
|
||
// }
|
||
//
|
||
// func locate<BIOS: SystemBIOS>(_ bios: BIOS)
|
||
// {
|
||
// self.importingBIOS = bios
|
||
//
|
||
// var supportedTypes = [kUTTypeItem as String, kUTTypeContent as String, "com.apple.macbinary-archive" /* System UTI for .bin */]
|
||
//
|
||
// // Explicitly support files with .bin and .rom extensions.
|
||
// if let binTypes = UTTypeCreateAllIdentifiersForTag(kUTTagClassFilenameExtension, "bin" as CFString, nil)?.takeRetainedValue()
|
||
// {
|
||
// let types = (binTypes as NSArray).map { $0 as! String }
|
||
// supportedTypes.append(contentsOf: types)
|
||
// }
|
||
//
|
||
// if let romTypes = UTTypeCreateAllIdentifiersForTag(kUTTagClassFilenameExtension, "rom" as CFString, nil)?.takeRetainedValue()
|
||
// {
|
||
// let types = (romTypes as NSArray).map { $0 as! String }
|
||
// supportedTypes.append(contentsOf: types)
|
||
// }
|
||
//
|
||
// let documentPicker = UIDocumentPickerViewController(documentTypes: supportedTypes, in: .import)
|
||
// documentPicker.delegate = self
|
||
//
|
||
// if #available(iOS 13.0, *)
|
||
// {
|
||
// documentPicker.overrideUserInterfaceStyle = .dark
|
||
// }
|
||
//
|
||
// self.present(documentPicker, animated: true, completion: nil)
|
||
// }
|
||
//
|
||
// func changeCore()
|
||
// {
|
||
// let alertController = UIAlertController(title: NSLocalizedString("Change Emulator Core", comment: ""), message: NSLocalizedString("Save states are not compatible between different emulator cores. Make sure to use in-game saves in order to keep using your save data.\n\nYour existing save states will not be deleted and will be available whenever you switch cores again.", comment: ""), preferredStyle: .actionSheet)
|
||
//
|
||
// var desmumeActionTitle = DS.core.metadata?.name.value ?? DS.core.name
|
||
// var melonDSActionTitle = MelonDS.core.metadata?.name.value ?? MelonDS.core.name
|
||
//
|
||
// if Settings.preferredCore(for: .ds) == DS.core
|
||
// {
|
||
// desmumeActionTitle += " ✓"
|
||
// }
|
||
// else
|
||
// {
|
||
// melonDSActionTitle += " ✓"
|
||
// }
|
||
//
|
||
// alertController.addAction(UIAlertAction(title: desmumeActionTitle, style: .default, handler: { (action) in
|
||
// Settings.setPreferredCore(DS.core, for: .ds)
|
||
// self.tableView.reloadData()
|
||
// }))
|
||
//
|
||
// alertController.addAction(UIAlertAction(title: melonDSActionTitle, style: .default, handler: { (action) in
|
||
// Settings.setPreferredCore(MelonDS.core, for: .ds)
|
||
// self.tableView.reloadData()
|
||
// }))
|
||
// alertController.addAction(.cancel)
|
||
// self.present(alertController, animated: true, completion: nil)
|
||
//
|
||
// if let indexPath = self.tableView.indexPathForSelectedRow
|
||
// {
|
||
// self.tableView.deselectRow(at: indexPath, animated: true)
|
||
// }
|
||
// }
|
||
//
|
||
// @IBAction func toggleAltJITEnabled(_ sender: UISwitch)
|
||
// {
|
||
// Settings.isAltJITEnabled = sender.isOn
|
||
// }
|
||
//
|
||
// @IBAction func toggleTopScreenOnly(_ sender: UISwitch)
|
||
// {
|
||
// Settings.features.dsAirPlay.topScreenOnly = sender.isOn
|
||
//
|
||
// self.tableView.performBatchUpdates({
|
||
// let layoutHorizontallyIndexPath = IndexPath(row: AirPlayRow.layoutHorizontally.rawValue, section: Section.airPlay.rawValue)
|
||
// if sender.isOn
|
||
// {
|
||
// self.tableView.deleteRows(at: [layoutHorizontallyIndexPath], with: .automatic)
|
||
// }
|
||
// else
|
||
// {
|
||
// self.tableView.insertRows(at: [layoutHorizontallyIndexPath], with: .automatic)
|
||
// }
|
||
// }) { _ in
|
||
// self.tableView.reloadSections([Section.airPlay.rawValue], with: .none)
|
||
// }
|
||
// }
|
||
//
|
||
// @IBAction func toggleLayoutHorizontally(_ sender: UISwitch)
|
||
// {
|
||
// Settings.features.dsAirPlay.layoutAxis = sender.isOn ? .horizontal : .vertical
|
||
//
|
||
// self.tableView.reloadSections([Section.airPlay.rawValue], with: .none)
|
||
// }
|
||
//
|
||
// @objc func willEnterForeground(_ notification: Notification)
|
||
// {
|
||
// self.tableView.reloadData()
|
||
// }
|
||
//}
|
||
//
|
||
//extension MelonDSCoreSettingsViewController
|
||
//{
|
||
// override func tableView(_ tableView: UITableView, numberOfRowsInSection sectionIndex: Int) -> Int
|
||
// {
|
||
// let section = Section(rawValue: sectionIndex)!
|
||
//
|
||
// switch section
|
||
// {
|
||
// case _ where isSectionHidden(section): return 0
|
||
// case .airPlay where Settings.features.dsAirPlay.topScreenOnly: return 1 // Layout axis is irrelevant if only AirPlaying top screen.
|
||
// default: return super.tableView(tableView, numberOfRowsInSection: sectionIndex)
|
||
// }
|
||
// }
|
||
//
|
||
// override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell
|
||
// {
|
||
// let cell = super.tableView(tableView, cellForRowAt: indexPath)
|
||
//
|
||
// switch Section(rawValue: indexPath.section)!
|
||
// {
|
||
// case .general:
|
||
// let key = DeltaCoreMetadata.Key.allCases[indexPath.row]
|
||
// let item = Settings.preferredCore(for: .ds)?.metadata?[key]
|
||
//
|
||
// cell.detailTextLabel?.text = item?.value ?? NSLocalizedString("-", comment: "")
|
||
// cell.detailTextLabel?.textColor = .gray
|
||
//
|
||
// if item?.url != nil
|
||
// {
|
||
// cell.accessoryType = .disclosureIndicator
|
||
// cell.selectionStyle = .default
|
||
// }
|
||
// else
|
||
// {
|
||
// cell.accessoryType = .none
|
||
// cell.selectionStyle = .none
|
||
// }
|
||
//
|
||
// cell.contentView.isHidden = (item == nil)
|
||
//
|
||
// case .airPlay:
|
||
// let cell = cell as! SwitchTableViewCell
|
||
//
|
||
// let row = AirPlayRow.allCases[indexPath.row]
|
||
// switch row
|
||
// {
|
||
// case .topScreenOnly: cell.switchView.isOn = Settings.features.dsAirPlay.topScreenOnly
|
||
// case .layoutHorizontally: cell.switchView.isOn = (Settings.features.dsAirPlay.layoutAxis == .horizontal)
|
||
// }
|
||
//
|
||
// case .performance:
|
||
// let cell = cell as! SwitchTableViewCell
|
||
// cell.switchView.isOn = Settings.isAltJITEnabled
|
||
//
|
||
// case .dsBIOS:
|
||
// let bios = DSBIOS.allCases[indexPath.row]
|
||
//
|
||
// if FileManager.default.fileExists(atPath: bios.fileURL.path)
|
||
// {
|
||
// cell.accessoryType = .checkmark
|
||
// cell.detailTextLabel?.text = nil
|
||
// cell.detailTextLabel?.textColor = .gray
|
||
// }
|
||
// else
|
||
// {
|
||
// cell.accessoryType = .disclosureIndicator
|
||
// cell.detailTextLabel?.text = NSLocalizedString("Required", comment: "")
|
||
// cell.detailTextLabel?.textColor = .red
|
||
// }
|
||
//
|
||
// cell.selectionStyle = .default
|
||
//
|
||
// case .dsiBIOS:
|
||
// let bios = DSiBIOS.allCases[indexPath.row]
|
||
//
|
||
// if FileManager.default.fileExists(atPath: bios.fileURL.path)
|
||
// {
|
||
// cell.accessoryType = .checkmark
|
||
// cell.detailTextLabel?.text = nil
|
||
// cell.detailTextLabel?.textColor = .gray
|
||
// }
|
||
// else
|
||
// {
|
||
// cell.accessoryType = .disclosureIndicator
|
||
// cell.detailTextLabel?.text = NSLocalizedString("Required", comment: "")
|
||
// cell.detailTextLabel?.textColor = .red
|
||
// }
|
||
//
|
||
// cell.selectionStyle = .default
|
||
//
|
||
// case .changeCore: break
|
||
// }
|
||
//
|
||
// return cell
|
||
// }
|
||
//
|
||
// override func tableView(_ tableView: UITableView, willDisplay cell: UITableViewCell, forRowAt indexPath: IndexPath)
|
||
// {
|
||
// guard let core = Settings.preferredCore(for: .ds) else { return }
|
||
//
|
||
// let key = DeltaCoreMetadata.Key.allCases[indexPath.row]
|
||
// let lastKey = DeltaCoreMetadata.Key.allCases.reversed().first { core.metadata?[$0] != nil }
|
||
//
|
||
// if key == lastKey
|
||
// {
|
||
// // Hide separator for last visible row in case we've hidden additional rows.
|
||
// cell.separatorInset.left = 0
|
||
// }
|
||
// else
|
||
// {
|
||
// cell.separatorInset.left = self.view.layoutMargins.left
|
||
// }
|
||
// }
|
||
//
|
||
// override func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath)
|
||
// {
|
||
// switch Section(rawValue: indexPath.section)!
|
||
// {
|
||
// case .general:
|
||
// let key = DeltaCoreMetadata.Key.allCases[indexPath.row]
|
||
// self.openMetadataURL(for: key)
|
||
//
|
||
// case .dsBIOS:
|
||
// let bios = DSBIOS.allCases[indexPath.row]
|
||
// self.locate(bios)
|
||
//
|
||
// case .dsiBIOS:
|
||
// let bios = DSiBIOS.allCases[indexPath.row]
|
||
// self.locate(bios)
|
||
//
|
||
// case .changeCore:
|
||
// self.changeCore()
|
||
//
|
||
// case .airPlay, .performance: break
|
||
// }
|
||
// }
|
||
//
|
||
// override func tableView(_ tableView: UITableView, titleForHeaderInSection section: Int) -> String?
|
||
// {
|
||
// let section = Section(rawValue: section)!
|
||
//
|
||
// if isSectionHidden(section)
|
||
// {
|
||
// return nil
|
||
// }
|
||
// else
|
||
// {
|
||
// return super.tableView(tableView, titleForHeaderInSection: section.rawValue)
|
||
// }
|
||
// }
|
||
//
|
||
// override func tableView(_ tableView: UITableView, titleForFooterInSection section: Int) -> String?
|
||
// {
|
||
// let section = Section(rawValue: section)!
|
||
// switch section
|
||
// {
|
||
// case _ where isSectionHidden(section): return nil
|
||
// case .airPlay:
|
||
// switch (Settings.features.dsAirPlay.topScreenOnly, Settings.features.dsAirPlay.layoutAxis)
|
||
// {
|
||
// case (true, _): return NSLocalizedString("When AirPlaying DS games, only the top screen will appear on the external display.", comment: "")
|
||
// case (false, .vertical): return NSLocalizedString("When AirPlaying DS games, both screens will be stacked vertically on the external display.", comment: "")
|
||
// case (false, .horizontal): return NSLocalizedString("When AirPlaying DS games, both screens will be placed side-by-side on the external display.", comment: "")
|
||
// }
|
||
//
|
||
// default: return super.tableView(tableView, titleForFooterInSection: section.rawValue)
|
||
// }
|
||
// }
|
||
//
|
||
// override func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat
|
||
// {
|
||
// switch Section(rawValue: indexPath.section)!
|
||
// {
|
||
// case .general:
|
||
// let key = DeltaCoreMetadata.Key.allCases[indexPath.row]
|
||
// guard Settings.preferredCore(for: .ds)?.metadata?[key] != nil else { return 0 }
|
||
//
|
||
// default: break
|
||
// }
|
||
//
|
||
// return super.tableView(tableView, heightForRowAt: indexPath)
|
||
// }
|
||
//
|
||
// override func tableView(_ tableView: UITableView, heightForHeaderInSection section: Int) -> CGFloat
|
||
// {
|
||
// let section = Section(rawValue: section)!
|
||
//
|
||
// if isSectionHidden(section)
|
||
// {
|
||
// return 1
|
||
// }
|
||
// else
|
||
// {
|
||
// return super.tableView(tableView, heightForHeaderInSection: section.rawValue)
|
||
// }
|
||
// }
|
||
//
|
||
// override func tableView(_ tableView: UITableView, heightForFooterInSection section: Int) -> CGFloat
|
||
// {
|
||
// let section = Section(rawValue: section)!
|
||
//
|
||
// if isSectionHidden(section)
|
||
// {
|
||
// return 1
|
||
// }
|
||
// else
|
||
// {
|
||
// return super.tableView(tableView, heightForFooterInSection: section.rawValue)
|
||
// }
|
||
// }
|
||
//}
|
||
//
|
||
//extension MelonDSCoreSettingsViewController: UIDocumentPickerDelegate
|
||
//{
|
||
// func documentPickerWasCancelled(_ controller: UIDocumentPickerViewController)
|
||
// {
|
||
// self.importingBIOS = nil
|
||
// self.tableView.reloadData() // Reloading index path causes cell to disappear...
|
||
// }
|
||
//
|
||
// func documentPicker(_ controller: UIDocumentPickerViewController, didPickDocumentsAt urls: [URL])
|
||
// {
|
||
// defer {
|
||
// self.importingBIOS = nil
|
||
// self.tableView.reloadData() // Reloading index path causes cell to disappear...
|
||
// }
|
||
//
|
||
// guard let fileURL = urls.first, let bios = self.importingBIOS else { return }
|
||
//
|
||
// defer { try? FileManager.default.removeItem(at: fileURL) }
|
||
//
|
||
// do
|
||
// {
|
||
// if #available(iOS 13.0, *)
|
||
// {
|
||
// // Validate file size first (since that's easiest for users to understand).
|
||
//
|
||
// let attributes = try FileManager.default.attributesOfItem(atPath: fileURL.path)
|
||
// guard let fileSize = attributes[.size] as? Int else { throw BIOSError.unknownSize(fileURL) }
|
||
//
|
||
// let measurement = Measurement<UnitInformationStorage>(value: Double(fileSize), unit: .bytes)
|
||
// guard bios.validFileSizes.contains(where: { $0.contains(measurement) }) else { throw BIOSError.incorrectSize(fileURL, size: fileSize, validSizes: bios.validFileSizes) }
|
||
//
|
||
// if bios.expectedMD5Hash != nil || !bios.unsupportedMD5Hashes.isEmpty
|
||
// {
|
||
// // Only calculate hash if we need to.
|
||
//
|
||
// let data = try Data(contentsOf: fileURL)
|
||
//
|
||
// let md5Hash = Insecure.MD5.hash(data: data)
|
||
// let hashString = md5Hash.compactMap { String(format: "%02x", $0) }.joined()
|
||
//
|
||
// if let expectedMD5Hash = bios.expectedMD5Hash
|
||
// {
|
||
// guard hashString == expectedMD5Hash else { throw BIOSError.incorrectHash(fileURL, hash: hashString, expectedHash: expectedMD5Hash) }
|
||
// }
|
||
//
|
||
// guard !bios.unsupportedMD5Hashes.contains(hashString) else { throw BIOSError.unsupportedHash(fileURL, hash: hashString) }
|
||
// }
|
||
// }
|
||
//
|
||
// try FileManager.default.copyItem(at: fileURL, to: bios.fileURL, shouldReplace: true)
|
||
// }
|
||
// catch let error as NSError
|
||
// {
|
||
// let title = String(format: NSLocalizedString("Could not import %@.", comment: ""), bios.filename)
|
||
//
|
||
// var message = error.localizedDescription
|
||
// if let recoverySuggestion = error.localizedRecoverySuggestion
|
||
// {
|
||
// message += "\n\n" + recoverySuggestion
|
||
// }
|
||
//
|
||
// let alertController = UIAlertController(title: title, message: message, preferredStyle: .alert)
|
||
// alertController.addAction(.ok)
|
||
// self.present(alertController, animated: true, completion: nil)
|
||
// }
|
||
// }
|
||
//}
|