diff --git a/Common/Collection View/GameCollectionViewDataSource.swift b/Common/Collection View/GameCollectionViewDataSource.swift index b96ea2c..cbfcd64 100644 --- a/Common/Collection View/GameCollectionViewDataSource.swift +++ b/Common/Collection View/GameCollectionViewDataSource.swift @@ -46,23 +46,23 @@ class GameCollectionViewDataSource: NSObject let fetchRequest = Game.rst_fetchRequest() - var predicates: [Predicate] = [] + var predicates: [NSPredicate] = [] if let identifiers = self.supportedGameCollectionIdentifiers { for identifier in identifiers { - let predicate = Predicate(format: "SUBQUERY(%K, $x, $x.%K == %@).@count > 0", Game.Attributes.gameCollections.rawValue, GameCollection.Attributes.identifier.rawValue, identifier) + let predicate = NSPredicate(format: "SUBQUERY(%K, $x, $x.%K == %@).@count > 0", Game.Attributes.gameCollections.rawValue, GameCollection.Attributes.identifier.rawValue, identifier) predicates.append(predicate) } } if predicates.count > 0 { - fetchRequest.predicate = CompoundPredicate(orPredicateWithSubpredicates: predicates) + fetchRequest.predicate = NSCompoundPredicate(orPredicateWithSubpredicates: predicates) } - fetchRequest.sortDescriptors = [SortDescriptor(key: Game.Attributes.type.rawValue, ascending: true), SortDescriptor(key: Game.Attributes.name.rawValue, ascending: true)] + fetchRequest.sortDescriptors = [NSSortDescriptor(key: Game.Attributes.type.rawValue, ascending: true), NSSortDescriptor(key: Game.Attributes.name.rawValue, ascending: true)] self.fetchedResultsController = NSFetchedResultsController(fetchRequest: fetchRequest, managedObjectContext: DatabaseManager.sharedManager.managedObjectContext, sectionNameKeyPath: Game.Attributes.type.rawValue, cacheName: nil) self.fetchedResultsController.delegate = previousDelegate diff --git a/Common/Components/LoadImageOperation.swift b/Common/Components/LoadImageOperation.swift index b3e2eef..fa4dab4 100644 --- a/Common/Components/LoadImageOperation.swift +++ b/Common/Components/LoadImageOperation.swift @@ -25,7 +25,7 @@ public class LoadImageOperation: RSTOperation } } - public var imageCache: Cache? { + public var imageCache: NSCache? { didSet { // Ensures if an image is cached, it will be returned immediately, to prevent temporary flash of placeholder image self.isImmediate = self.imageCache?.object(forKey: self.URL) != nil diff --git a/Common/Database/DatabaseManager.swift b/Common/Database/DatabaseManager.swift index 96a027f..3593831 100644 --- a/Common/Database/DatabaseManager.swift +++ b/Common/Database/DatabaseManager.swift @@ -29,7 +29,7 @@ class DatabaseManager private init() { - let modelURL = Bundle.main.urlForResource("Model", withExtension: "momd") + let modelURL = Bundle.main.url(forResource: "Model", withExtension: "momd") let managedObjectModel = NSManagedObjectModel(contentsOf: modelURL!) let persistentStoreCoordinator = NSPersistentStoreCoordinator(managedObjectModel: managedObjectModel!) @@ -52,9 +52,9 @@ class DatabaseManager func startWithCompletion(_ completionBlock: ((performingMigration: Bool) -> Void)?) { - DispatchQueue.global(attributes: DispatchQueue.GlobalAttributes.qosUserInitiated).async { + DispatchQueue.global(qos: .userInitiated).async { - let storeURL = try! DatabaseManager.databaseDirectoryURL.appendingPathComponent("Delta.sqlite") + let storeURL = DatabaseManager.databaseDirectoryURL.appendingPathComponent("Delta.sqlite") let options = [NSMigratePersistentStoresAutomaticallyOption: true, NSInferMappingModelAutomaticallyOption: true] @@ -106,42 +106,28 @@ class DatabaseManager { let identifier = FileHash.sha1HashOfFile(atPath: URL.path) as String - var filename = identifier - if let pathExtension = URL.pathExtension - { - filename += "." + pathExtension - } + let filename = identifier + "." + URL.pathExtension let game = Game.insertIntoManagedObjectContext(managedObjectContext) - game.name = try! URL.deletingPathExtension().lastPathComponent ?? NSLocalizedString("Game", comment: "") + game.name = URL.deletingPathExtension().lastPathComponent ?? NSLocalizedString("Game", comment: "") game.identifier = identifier game.filename = filename - if let pathExtension = URL.pathExtension - { - let gameCollection = GameCollection.gameSystemCollectionForPathExtension(pathExtension, inManagedObjectContext: managedObjectContext) - game.type = GameType(rawValue: gameCollection.identifier) - game.gameCollections.insert(gameCollection) - } - else - { - game.type = GameType.delta - } + let gameCollection = GameCollection.gameSystemCollectionForPathExtension(URL.pathExtension, inManagedObjectContext: managedObjectContext) + game.type = GameType(rawValue: gameCollection.identifier) + game.gameCollections.insert(gameCollection) do { - let destinationURL = try! DatabaseManager.gamesDirectoryURL.appendingPathComponent(game.identifier + "." + game.preferredFileExtension) + let destinationURL = DatabaseManager.gamesDirectoryURL.appendingPathComponent(game.identifier + "." + game.preferredFileExtension) - if let path = destinationURL.path + if FileManager.default.fileExists(atPath: destinationURL.path) { - if FileManager.default.fileExists(atPath: path) - { - try FileManager.default.removeItem(at: URL) - } - else - { - try FileManager.default.moveItem(at: URL, to: destinationURL) - } + try FileManager.default.removeItem(at: URL) + } + else + { + try FileManager.default.moveItem(at: URL, to: destinationURL) } identifiers.append(game.identifier) @@ -194,14 +180,14 @@ extension DatabaseManager if UIDevice.current.userInterfaceIdiom == .tv { - documentsDirectoryURL = FileManager.default.urlsForDirectory(FileManager.SearchPathDirectory.cachesDirectory, inDomains: FileManager.SearchPathDomainMask.userDomainMask).first! + documentsDirectoryURL = FileManager.default.urls(for: .cachesDirectory, in: .userDomainMask).first! } else { - documentsDirectoryURL = FileManager.default.urlsForDirectory(FileManager.SearchPathDirectory.documentDirectory, inDomains: FileManager.SearchPathDomainMask.userDomainMask).first! + documentsDirectoryURL = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask).first! } - let databaseDirectoryURL = try! documentsDirectoryURL.appendingPathComponent("Database") + let databaseDirectoryURL = documentsDirectoryURL.appendingPathComponent("Database") self.createDirectoryAtURLIfNeeded(databaseDirectoryURL) return databaseDirectoryURL @@ -209,7 +195,7 @@ extension DatabaseManager class var gamesDirectoryURL: URL { - let gamesDirectoryURL = try! DatabaseManager.databaseDirectoryURL.appendingPathComponent("Games") + let gamesDirectoryURL = DatabaseManager.databaseDirectoryURL.appendingPathComponent("Games") self.createDirectoryAtURLIfNeeded(gamesDirectoryURL) return gamesDirectoryURL @@ -217,7 +203,7 @@ extension DatabaseManager class var saveStatesDirectoryURL: URL { - let saveStatesDirectoryURL = try! DatabaseManager.databaseDirectoryURL.appendingPathComponent("Save States") + let saveStatesDirectoryURL = DatabaseManager.databaseDirectoryURL.appendingPathComponent("Save States") self.createDirectoryAtURLIfNeeded(saveStatesDirectoryURL) return saveStatesDirectoryURL @@ -225,7 +211,7 @@ extension DatabaseManager class func saveStatesDirectoryURLForGame(_ game: Game) -> URL { - let gameDirectoryURL = try! DatabaseManager.saveStatesDirectoryURL.appendingPathComponent(game.identifier) + let gameDirectoryURL = DatabaseManager.saveStatesDirectoryURL.appendingPathComponent(game.identifier) self.createDirectoryAtURLIfNeeded(gameDirectoryURL) return gameDirectoryURL @@ -322,7 +308,7 @@ private extension DatabaseManager } // Remove empty collections - let collections = GameCollection.instancesWithPredicate(Predicate(format: "%K.@count == 0", GameCollection.Attributes.games.rawValue), inManagedObjectContext: self.validationManagedObjectContext, type: GameCollection.self) + let collections = GameCollection.instancesWithPredicate(NSPredicate(format: "%K.@count == 0", GameCollection.Attributes.games.rawValue), inManagedObjectContext: self.validationManagedObjectContext, type: GameCollection.self) for collection in collections { diff --git a/Common/Database/Model/Game.swift b/Common/Database/Model/Game.swift index c052773..1b88e05 100644 --- a/Common/Database/Model/Game.swift +++ b/Common/Database/Model/Game.swift @@ -46,7 +46,7 @@ class Game: NSManagedObject, GameProtocol var fileURL: URL! self.managedObjectContext?.performAndWait { - fileURL = try! DatabaseManager.gamesDirectoryURL.appendingPathComponent(self.filename) + fileURL = DatabaseManager.gamesDirectoryURL.appendingPathComponent(self.filename) } return fileURL diff --git a/Common/Database/Model/GameCollection.swift b/Common/Database/Model/GameCollection.swift index b9b773c..2be6a1b 100644 --- a/Common/Database/Model/GameCollection.swift +++ b/Common/Database/Model/GameCollection.swift @@ -76,7 +76,7 @@ class GameCollection: NSManagedObject index = Int16(INT16_MAX) } - let predicate = Predicate(format: "%K == %@", GameCollection.Attributes.identifier.rawValue, identifier) + let predicate = NSPredicate(format: "%K == %@", GameCollection.Attributes.identifier.rawValue, identifier) var gameCollection = GameCollection.instancesWithPredicate(predicate, inManagedObjectContext: managedObjectContext, type: GameCollection.self).first if gameCollection == nil diff --git a/Common/Database/Model/SaveState.swift b/Common/Database/Model/SaveState.swift index eb56994..965c61f 100644 --- a/Common/Database/Model/SaveState.swift +++ b/Common/Database/Model/SaveState.swift @@ -55,13 +55,13 @@ class SaveState: NSManagedObject, SaveStateProtocol @NSManaged var previewGame: Game? var fileURL: URL { - let fileURL = try! DatabaseManager.saveStatesDirectoryURLForGame(self.game).appendingPathComponent(self.filename) + let fileURL = DatabaseManager.saveStatesDirectoryURLForGame(self.game).appendingPathComponent(self.filename) return fileURL } var imageFileURL: URL { let imageFilename = (self.filename as NSString).deletingPathExtension + ".png" - let imageFileURL = try! DatabaseManager.saveStatesDirectoryURLForGame(self.game).appendingPathComponent(imageFilename) + let imageFileURL = DatabaseManager.saveStatesDirectoryURLForGame(self.game).appendingPathComponent(imageFilename) return imageFileURL } diff --git a/Common/Extensions/NSManagedObject+Conveniences.swift b/Common/Extensions/NSManagedObject+Conveniences.swift index fc203f7..ca6324a 100644 --- a/Common/Extensions/NSManagedObject+Conveniences.swift +++ b/Common/Extensions/NSManagedObject+Conveniences.swift @@ -39,7 +39,7 @@ extension NSManagedObject return self.instancesWithPredicate(nil, inManagedObjectContext: managedObjectContext, type: type) } - class func instancesWithPredicate(_ predicate: Predicate?, inManagedObjectContext managedObjectContext: NSManagedObjectContext, type: T.Type) -> [T] + class func instancesWithPredicate(_ predicate: NSPredicate?, inManagedObjectContext managedObjectContext: NSManagedObjectContext, type: T.Type) -> [T] { let fetchRequest = self.rst_fetchRequest() fetchRequest.predicate = predicate diff --git a/Common/Importing/GamePickerController.swift b/Common/Importing/GamePickerController.swift index 0707cf0..04b8381 100644 --- a/Common/Importing/GamePickerController.swift +++ b/Common/Importing/GamePickerController.swift @@ -53,7 +53,7 @@ class GamePickerController: NSObject let importAction = UIAlertAction(title: NSLocalizedString("Import", comment: ""), style: .default) { action in - let documentsDirectoryURL = try! DatabaseManager.databaseDirectoryURL.deletingLastPathComponent() + let documentsDirectoryURL = DatabaseManager.databaseDirectoryURL.deletingLastPathComponent() do { @@ -91,7 +91,7 @@ class GamePickerController: NSObject DatabaseManager.sharedManager.managedObjectContext.perform() { - let predicate = Predicate(format: "%K IN (%@)", Game.Attributes.identifier.rawValue, identifiers) + let predicate = NSPredicate(format: "%K IN (%@)", Game.Attributes.identifier.rawValue, identifiers) let games = Game.instancesWithPredicate(predicate, inManagedObjectContext: DatabaseManager.sharedManager.managedObjectContext, type: Game.self) self.delegate?.gamePickerController(self, didImportGames: games) diff --git a/Delta/Emulation/GameViewController.swift b/Delta/Emulation/GameViewController.swift index d0160b5..464602c 100644 --- a/Delta/Emulation/GameViewController.swift +++ b/Delta/Emulation/GameViewController.swift @@ -48,6 +48,26 @@ class GameViewController: DeltaCore.GameViewController private var sustainButtonsBlurView: UIVisualEffectView! private var sustainButtonsBackgroundView: RSTBackgroundView! + override var previewActionItems: [UIPreviewActionItem] + { + if let previewActionItems = self.overridePreviewActionItems + { + return previewActionItems + } + + guard let game = self.game as? Game else { return [] } + + let presentingViewController = self.presentingViewController + + let launchGameAction = UIPreviewAction(title: NSLocalizedString("Launch \(game.name)", comment: ""), style: .default) { (action, viewController) in + // Delaying until next run loop prevents self from being dismissed immediately + DispatchQueue.main.async { + presentingViewController?.present(viewController, animated: true, completion: nil) + } + } + return [launchGameAction] + } + required init() { self.reactivateSustainedInputsQueue = OperationQueue() @@ -86,7 +106,7 @@ class GameViewController: DeltaCore.GameViewController { super.gameController(gameController, didActivate: input) - if gameController is ControllerView && UIDevice.current().isVibrationSupported + if gameController is ControllerView && UIDevice.current.isVibrationSupported { UIDevice.current.vibrate() } @@ -97,7 +117,7 @@ class GameViewController: DeltaCore.GameViewController { self.addSustainedInput(input, for: gameController) } - else if let sustainedInputs = self.sustainedInputs[ObjectIdentifier(gameController)], sustainedInputs.contains({ $0.isEqual(input) }) + else if let sustainedInputs = self.sustainedInputs[ObjectIdentifier(gameController)], sustainedInputs.contains(where: { $0.isEqual(input) }) { // Perform on next run loop DispatchQueue.main.async { @@ -157,26 +177,6 @@ extension GameViewController self.controllerView.isHidden = self.isPreviewing } - override func previewActionItems() -> [UIPreviewActionItem] - { - if let previewActionItems = self.overridePreviewActionItems - { - return previewActionItems - } - - guard let game = self.game as? Game else { return [] } - - let presentingViewController = self.presentingViewController - - let launchGameAction = UIPreviewAction(title: NSLocalizedString("Launch \(game.name)", comment: ""), style: .default) { (action, viewController) in - // Delaying until next run loop prevents self from being dismissed immediately - DispatchQueue.main.async { - presentingViewController?.present(viewController, animated: true, completion: nil) - } - } - return [launchGameAction] - } - // MARK: - Segues /// KVO @@ -228,7 +228,8 @@ extension GameViewController self.emulatorCore?.audioManager.enabled = false // Re-enable after delay - DispatchQueue.main.after(when: .now() + 0.1) { + + DispatchQueue.main.asyncAfter(deadline: .now() + 0.1) { self.emulatorCore?.audioManager.enabled = true } } @@ -292,14 +293,12 @@ extension GameViewController: SaveStatesViewControllerDelegate { func saveStatesViewController(_ saveStatesViewController: SaveStatesViewController, updateSaveState saveState: SaveState) { - guard let filepath = saveState.fileURL.path else { return } - var updatingExistingSaveState = true self.emulatorCore?.save { (temporarySaveState) in do { - if FileManager.default.fileExists(atPath: filepath) + if FileManager.default.fileExists(atPath: saveState.fileURL.path) { try FileManager.default.replaceItem(at: saveState.fileURL, withItemAt: temporarySaveState.fileURL, backupItemName: nil, options: [], resultingItemURL: nil) } @@ -407,7 +406,7 @@ extension GameViewController: CheatsViewControllerDelegate let backgroundContext = DatabaseManager.sharedManager.backgroundManagedObjectContext() backgroundContext.performAndWait { - let predicate = Predicate(format: "%K == %@", Cheat.Attributes.game.rawValue, game) + let predicate = NSPredicate(format: "%K == %@", Cheat.Attributes.game.rawValue, game) let cheats = Cheat.instancesWithPredicate(predicate, inManagedObjectContext: backgroundContext, type: Cheat.self) for cheat in cheats @@ -488,7 +487,7 @@ private extension GameViewController { var inputs = self.sustainedInputs[ObjectIdentifier(gameController)] ?? [] - guard !inputs.contains({ $0.isEqual(input) }) else { return } + guard !inputs.contains(where: { $0.isEqual(input) }) else { return } inputs.append(input) self.sustainedInputs[ObjectIdentifier(gameController)] = inputs @@ -514,7 +513,7 @@ private extension GameViewController // Must deactivate first so core recognizes a secondary activation gameController.deactivate(input) - let dispatchQueue = DispatchQueue(label: "com.rileytestut.Delta.sustainButtonsQueue", attributes: DispatchQueueAttributes.serial) + let dispatchQueue = DispatchQueue(label: "com.rileytestut.Delta.sustainButtonsQueue") dispatchQueue.async { let semaphore = DispatchSemaphore(value: 0) diff --git a/Delta/Extensions/UILabel+FontSize.swift b/Delta/Extensions/UILabel+FontSize.swift index c187823..b010932 100644 --- a/Delta/Extensions/UILabel+FontSize.swift +++ b/Delta/Extensions/UILabel+FontSize.swift @@ -18,7 +18,7 @@ internal extension UILabel context.minimumScaleFactor = self.minimumScaleFactor // Using self.attributedString returns incorrect calculations, so we create our own attributed string - let attributedString = AttributedString(string: text, attributes: [NSFontAttributeName: self.font]) + let attributedString = NSAttributedString(string: text, attributes: [NSFontAttributeName: self.font]) attributedString.boundingRect(with: self.bounds.size, options: [.usesLineFragmentOrigin, .usesFontLeading], context: context) let scaleFactor = context.actualScaleFactor diff --git a/Delta/Game Selection/GamesViewController.swift b/Delta/Game Selection/GamesViewController.swift index a600ca3..0ab83be 100644 --- a/Delta/Game Selection/GamesViewController.swift +++ b/Delta/Game Selection/GamesViewController.swift @@ -28,7 +28,7 @@ class GamesViewController: UIViewController required init?(coder aDecoder: NSCoder) { let fetchRequest = GameCollection.rst_fetchRequest() - fetchRequest.sortDescriptors = [SortDescriptor(key: GameCollection.Attributes.index.rawValue, ascending: true)] + fetchRequest.sortDescriptors = [NSSortDescriptor(key: GameCollection.Attributes.index.rawValue, ascending: true)] self.fetchedResultsController = NSFetchedResultsController(fetchRequest: fetchRequest, managedObjectContext: DatabaseManager.sharedManager.managedObjectContext, sectionNameKeyPath: nil, cacheName: nil) diff --git a/Delta/Pause Menu/Cheats/CheatTextView.swift b/Delta/Pause Menu/Cheats/CheatTextView.swift index 1b81624..cb01562 100644 --- a/Delta/Pause Menu/Cheats/CheatTextView.swift +++ b/Delta/Pause Menu/Cheats/CheatTextView.swift @@ -23,7 +23,7 @@ class CheatTextView: UITextView } } - @NSCopying private var attributedFormat: AttributedString? + @NSCopying private var attributedFormat: NSAttributedString? required init?(coder aDecoder: NSCoder) { @@ -117,9 +117,9 @@ extension CheatTextView: NSLayoutManagerDelegate let bufferSize = max(attributedFormat.length + 1, glyphCount * 2) // Allocate our replacement buffers - let glyphBuffer = UnsafeMutablePointer(allocatingCapacity: bufferSize) - let propertyBuffer = UnsafeMutablePointer(allocatingCapacity: bufferSize) - let characterBuffer = UnsafeMutablePointer(allocatingCapacity: bufferSize) + let glyphBuffer = UnsafeMutablePointer.allocate(capacity: bufferSize) + let propertyBuffer = UnsafeMutablePointer.allocate(capacity: bufferSize) + let characterBuffer = UnsafeMutablePointer.allocate(capacity: bufferSize) var offset = 0 @@ -156,9 +156,9 @@ extension CheatTextView: NSLayoutManagerDelegate layoutManager.setGlyphs(glyphBuffer, properties: propertyBuffer, characterIndexes: characterBuffer, font: aFont, forGlyphRange: NSRange(location: glyphRange.location, length: glyphCount + offset)) // Clean up memory - characterBuffer.deallocateCapacity(bufferSize) - propertyBuffer.deallocateCapacity(bufferSize) - glyphBuffer.deallocateCapacity(bufferSize) + characterBuffer.deallocate(capacity: bufferSize) + propertyBuffer.deallocate(capacity: bufferSize) + glyphBuffer.deallocate(capacity: bufferSize) // Return total number of glyphs return glyphCount + offset diff --git a/Delta/Pause Menu/Cheats/CheatValidator.swift b/Delta/Pause Menu/Cheats/CheatValidator.swift index 7ae51c6..649c1db 100644 --- a/Delta/Pause Menu/Cheats/CheatValidator.swift +++ b/Delta/Pause Menu/Cheats/CheatValidator.swift @@ -33,7 +33,7 @@ struct CheatValidator let code = cheat.code // Find all cheats that are for the same game, don't have the same identifier as the current cheat, but have either the same name or code - let predicate = Predicate(format: "%K == %@ AND %K != %@ AND (%K == %@ OR %K == %@)", Cheat.Attributes.game.rawValue, cheat.game, Cheat.Attributes.identifier.rawValue, cheat.identifier, Cheat.Attributes.code.rawValue, code, Cheat.Attributes.name.rawValue, name) + let predicate = NSPredicate(format: "%K == %@ AND %K != %@ AND (%K == %@ OR %K == %@)", Cheat.Attributes.game.rawValue, cheat.game, Cheat.Attributes.identifier.rawValue, cheat.identifier, Cheat.Attributes.code.rawValue, code, Cheat.Attributes.name.rawValue, name) let cheats = Cheat.instancesWithPredicate(predicate, inManagedObjectContext: self.managedObjectContext, type: Cheat.self) for cheat in cheats diff --git a/Delta/Pause Menu/Cheats/CheatsViewController.swift b/Delta/Pause Menu/Cheats/CheatsViewController.swift index 949db04..5172888 100644 --- a/Delta/Pause Menu/Cheats/CheatsViewController.swift +++ b/Delta/Pause Menu/Cheats/CheatsViewController.swift @@ -86,8 +86,8 @@ private extension CheatsViewController { let fetchRequest = Cheat.rst_fetchRequest() fetchRequest.returnsObjectsAsFaults = false - fetchRequest.predicate = Predicate(format: "%K == %@", Cheat.Attributes.game.rawValue, self.game) - fetchRequest.sortDescriptors = [SortDescriptor(key: Cheat.Attributes.name.rawValue, ascending: true)] + fetchRequest.predicate = NSPredicate(format: "%K == %@", Cheat.Attributes.game.rawValue, self.game) + fetchRequest.sortDescriptors = [NSSortDescriptor(key: Cheat.Attributes.name.rawValue, ascending: true)] self.fetchedResultsController = NSFetchedResultsController(fetchRequest: fetchRequest, managedObjectContext: DatabaseManager.sharedManager.managedObjectContext, sectionNameKeyPath: nil, cacheName: nil) self.fetchedResultsController.delegate = self diff --git a/Delta/Pause Menu/Cheats/EditCheatViewController.swift b/Delta/Pause Menu/Cheats/EditCheatViewController.swift index 6374c09..1386537 100644 --- a/Delta/Pause Menu/Cheats/EditCheatViewController.swift +++ b/Delta/Pause Menu/Cheats/EditCheatViewController.swift @@ -54,6 +54,43 @@ class EditCheatViewController: UITableViewController @IBOutlet private var nameTextField: UITextField! @IBOutlet private var typeSegmentedControl: UISegmentedControl! @IBOutlet private var codeTextView: CheatTextView! + + override var previewActionItems: [UIPreviewActionItem] + { + guard let cheat = self.cheat else { return [] } + + let copyCodeAction = UIPreviewAction(title: NSLocalizedString("Copy Code", comment: ""), style: .default) { (action, viewController) in + UIPasteboard.general.string = cheat.code + } + + let presentingViewController = self.presentingViewController! + + let editCheatAction = UIPreviewAction(title: NSLocalizedString("Edit", comment: ""), style: .default) { (action, viewController) in + // Delaying until next run loop prevents self from being dismissed immediately + DispatchQueue.main.async { + let editCheatViewController = viewController as! EditCheatViewController + editCheatViewController.presentWithPresentingViewController(presentingViewController) + } + } + + let deleteAction = UIPreviewAction(title: NSLocalizedString("Delete", comment: ""), style: .destructive) { [unowned self] (action, viewController) in + self.delegate?.editCheatViewController(self, deactivateCheat: cheat) + + let backgroundContext = DatabaseManager.sharedManager.backgroundManagedObjectContext() + backgroundContext.perform { + let temporaryCheat = backgroundContext.object(with: cheat.objectID) + backgroundContext.delete(temporaryCheat) + backgroundContext.saveWithErrorLogging() + } + } + + let cancelDeleteAction = UIPreviewAction(title: NSLocalizedString("Cancel", comment: ""), style: .default) { (action, viewController) in + } + + let deleteActionGroup = UIPreviewActionGroup(title: NSLocalizedString("Delete", comment: ""), style: .destructive, actions: [deleteAction, cancelDeleteAction]) + + return [copyCodeAction, editCheatAction, deleteActionGroup] + } } extension EditCheatViewController @@ -155,43 +192,6 @@ extension EditCheatViewController } } - override func previewActionItems() -> [UIPreviewActionItem] - { - guard let cheat = self.cheat else { return [] } - - let copyCodeAction = UIPreviewAction(title: NSLocalizedString("Copy Code", comment: ""), style: .default) { (action, viewController) in - UIPasteboard.general.string = cheat.code - } - - let presentingViewController = self.presentingViewController! - - let editCheatAction = UIPreviewAction(title: NSLocalizedString("Edit", comment: ""), style: .default) { (action, viewController) in - // Delaying until next run loop prevents self from being dismissed immediately - DispatchQueue.main.async { - let editCheatViewController = viewController as! EditCheatViewController - editCheatViewController.presentWithPresentingViewController(presentingViewController) - } - } - - let deleteAction = UIPreviewAction(title: NSLocalizedString("Delete", comment: ""), style: .destructive) { [unowned self] (action, viewController) in - self.delegate?.editCheatViewController(self, deactivateCheat: cheat) - - let backgroundContext = DatabaseManager.sharedManager.backgroundManagedObjectContext() - backgroundContext.perform { - let temporaryCheat = backgroundContext.object(with: cheat.objectID) - backgroundContext.delete(temporaryCheat) - backgroundContext.saveWithErrorLogging() - } - } - - let cancelDeleteAction = UIPreviewAction(title: NSLocalizedString("Cancel", comment: ""), style: .default) { (action, viewController) in - } - - let deleteActionGroup = UIPreviewActionGroup(title: NSLocalizedString("Delete", comment: ""), style: .destructive, actions: [deleteAction, cancelDeleteAction]) - - return [copyCodeAction, editCheatAction, deleteActionGroup] - } - override func didReceiveMemoryWarning() { super.didReceiveMemoryWarning() @@ -380,7 +380,7 @@ extension EditCheatViewController: UITextViewDelegate // We need to manually add back the attributes when manually modifying the underlying text storage // Otherwise, pasting text into an empty text view will result in the wrong font being used - let attributedString = AttributedString(string: sanitizedText, attributes: textView.typingAttributes) + let attributedString = NSAttributedString(string: sanitizedText, attributes: textView.typingAttributes) textView.textStorage.replaceCharacters(in: range, with: attributedString) // We must add attributedString.length, not range.length, in case the attributed string's length differs diff --git a/Delta/Pause Menu/PauseViewController.swift b/Delta/Pause Menu/PauseViewController.swift index 4f6dc42..4510920 100644 --- a/Delta/Pause Menu/PauseViewController.swift +++ b/Delta/Pause Menu/PauseViewController.swift @@ -60,15 +60,14 @@ class PauseViewController: UIViewController, PauseInfoProviding override var navigationController: UINavigationController? { return self.pauseNavigationController } + + override var preferredStatusBarStyle: UIStatusBarStyle { + return .lightContent + } } extension PauseViewController { - override func preferredStatusBarStyle() -> UIStatusBarStyle - { - return .lightContent - } - override func viewDidLayoutSubviews() { super.viewDidLayoutSubviews() diff --git a/Delta/Pause Menu/Presentation Controller/PausePresentationController.swift b/Delta/Pause Menu/Presentation Controller/PausePresentationController.swift index 52231f1..3d1093b 100644 --- a/Delta/Pause Menu/Presentation Controller/PausePresentationController.swift +++ b/Delta/Pause Menu/Presentation Controller/PausePresentationController.swift @@ -25,17 +25,7 @@ class PausePresentationController: UIPresentationController @IBOutlet private weak var pauseIconImageView: UIImageView! @IBOutlet private weak var stackView: UIStackView! - override init(presentedViewController: UIViewController, presenting presentingViewController: UIViewController?) - { - self.blurringView = UIVisualEffectView(effect: nil) - self.vibrancyView = UIVisualEffectView(effect: nil) - - super.init(presentedViewController: presentedViewController, presenting: presentingViewController) - - self.contentView = Bundle.main.loadNibNamed("PausePresentationControllerContentView", owner: self, options: nil)?.first as! UIView - } - - override func frameOfPresentedViewInContainerView() -> CGRect + override var frameOfPresentedViewInContainerView: CGRect { guard let containerView = self.containerView else { return super.frameOfPresentedViewInContainerView } @@ -55,6 +45,16 @@ class PausePresentationController: UIPresentationController return frame } + override init(presentedViewController: UIViewController, presenting presentingViewController: UIViewController?) + { + self.blurringView = UIVisualEffectView(effect: nil) + self.vibrancyView = UIVisualEffectView(effect: nil) + + super.init(presentedViewController: presentedViewController, presenting: presentingViewController) + + self.contentView = Bundle.main.loadNibNamed("PausePresentationControllerContentView", owner: self, options: nil)?.first as! UIView + } + override func presentationTransitionWillBegin() { if let provider = self.presentedViewController as? PauseInfoProviding @@ -164,13 +164,13 @@ class PausePresentationController: UIPresentationController if self.presentingViewController.transitionCoordinator == nil { - self.presentedView?.frame = self.frameOfPresentedViewInContainerView() + self.presentedView?.frame = self.frameOfPresentedViewInContainerView } // Unhide pauseIconImageView so its height is involved with layout calculations self.pauseIconImageView.isHidden = false - self.contentView.frame = CGRect(x: 0, y: statusBarHeight, width: self.containerView!.bounds.width, height: self.frameOfPresentedViewInContainerView().minY - statusBarHeight) + self.contentView.frame = CGRect(x: 0, y: statusBarHeight, width: self.containerView!.bounds.width, height: self.frameOfPresentedViewInContainerView.minY - statusBarHeight) self.contentView.setNeedsLayout() // Ensures that layout will actually occur (sometimes the system thinks a layout is not needed, which messes up calculations) self.contentView.layoutIfNeeded() diff --git a/Delta/Pause Menu/Save States/SaveStatesViewController.swift b/Delta/Pause Menu/Save States/SaveStatesViewController.swift index 21c3c9a..d8f91e7 100644 --- a/Delta/Pause Menu/Save States/SaveStatesViewController.swift +++ b/Delta/Pause Menu/Save States/SaveStatesViewController.swift @@ -58,7 +58,7 @@ class SaveStatesViewController: UICollectionViewController private var fetchedResultsController: NSFetchedResultsController! private let imageOperationQueue = RSTOperationQueue() - private let imageCache = Cache() + private let imageCache = NSCache() private var emulatorCoreSaveState: SaveStateProtocol? private var selectedSaveState: SaveState? @@ -160,8 +160,8 @@ private extension SaveStatesViewController { let fetchRequest = SaveState.rst_fetchRequest() fetchRequest.returnsObjectsAsFaults = false - fetchRequest.predicate = Predicate(format: "%K == %@", SaveState.Attributes.game.rawValue, self.game) - fetchRequest.sortDescriptors = [SortDescriptor(key: SaveState.Attributes.type.rawValue, ascending: true), SortDescriptor(key: SaveState.Attributes.creationDate.rawValue, ascending: true)] + fetchRequest.predicate = NSPredicate(format: "%K == %@", SaveState.Attributes.game.rawValue, self.game) + fetchRequest.sortDescriptors = [NSSortDescriptor(key: SaveState.Attributes.type.rawValue, ascending: true), NSSortDescriptor(key: SaveState.Attributes.creationDate.rawValue, ascending: true)] self.fetchedResultsController = NSFetchedResultsController(fetchRequest: fetchRequest, managedObjectContext: DatabaseManager.sharedManager.managedObjectContext, sectionNameKeyPath: SaveState.Attributes.type.rawValue, cacheName: nil) self.fetchedResultsController.delegate = self diff --git a/Delta/Pause Menu/Segues/PauseStoryboardSegue.swift b/Delta/Pause Menu/Segues/PauseStoryboardSegue.swift index f154328..d470193 100644 --- a/Delta/Pause Menu/Segues/PauseStoryboardSegue.swift +++ b/Delta/Pause Menu/Segues/PauseStoryboardSegue.swift @@ -73,7 +73,7 @@ extension PauseStoryboardSegue: UIViewControllerAnimatedTransitioning transitionContext.containerView.addSubview(presentedView) self.animator.addAnimations { [unowned self] in - presentedView.frame = self.presentationController.frameOfPresentedViewInContainerView() + presentedView.frame = self.presentationController.frameOfPresentedViewInContainerView } self.animator.addCompletion { position in diff --git a/Delta/Settings/ControllersSettingsViewController.swift b/Delta/Settings/ControllersSettingsViewController.swift index 85ca6e5..9b6b54c 100644 --- a/Delta/Settings/ControllersSettingsViewController.swift +++ b/Delta/Settings/ControllersSettingsViewController.swift @@ -39,7 +39,7 @@ class ControllersSettingsViewController: UITableViewController } } - private var connectedControllers = ExternalControllerManager.shared.connectedControllers.sorted(isOrderedBefore: { $0.playerIndex ?? NSIntegerMax < $1.playerIndex ?? NSIntegerMax }) + private var connectedControllers = ExternalControllerManager.shared.connectedControllers.sorted(by: { $0.playerIndex ?? NSIntegerMax < $1.playerIndex ?? NSIntegerMax }) private lazy var localDeviceController: LocalDeviceController = { let device = LocalDeviceController() @@ -176,7 +176,7 @@ extension ControllersSettingsViewController { cell.textLabel?.text = NSLocalizedString("None", comment: "") - if Settings.localControllerPlayerIndex != self.playerIndex && !self.connectedControllers.contains({ $0.playerIndex == self.playerIndex }) + if Settings.localControllerPlayerIndex != self.playerIndex && !self.connectedControllers.contains(where: { $0.playerIndex == self.playerIndex }) { cell.accessoryType = .checkmark }