iOS 12 present(fromrootviewcontroller) causes app to freeze - swift4.2

Very confused about an issue I've just stumbled across.
I have a swift game, mainly using SpriteKit.
At the end of a game an ad is loaded. Or when the user decides to end the game an ad is also loaded.
If the game is played normally. The flow is as follows...
MenuScene -> GameScene -> [ad loads] -> GameOverScene
If the user quits the game...
MenuScene -> GameScene -> user selects pause -> user clicks quit -> [ad loads] -> Menu Scene
Both scenarios work perfectly for iOS 13
However, in iOS 12, only the user quitting mid game works, in the other scenario the app freezes (no obvious errors to me in the log)
Ending a game, via either method, calls the exact same function. Which uses a notification to load the ad.
The following code is all in the GameViewController. And the notification is posted in the GameScene.
NotificationCenter.default.addObserver(self, selector: #selector(GameViewController.showAd), name: NSNotification.Name(rawValue: "loadAndShow"), object: nil)
An ad is prepared in ViewDidLoad()
myAd = createAd()
And the function is called by the Notification
#objc func showAd() {
if (myAd.isReady) {
myAd.present(fromRootViewController: self)
myAd = createAd()
}
}
func createAd() -> GADInterstitial {
myAd = GADInterstitial(adUnitID: "MY ADMOB ID")
let request = GADRequest()
GADMobileAds.sharedInstance().requestConfiguration.testDeviceIdentifiers = ["MY DEVICE ID"]
myAd.delegate = self
myAd.load(request)
return myAd
}
Any help would be amazing
UPDATE
Have found a potential workaround.
Am now presenting the ad in a different thread and no longer experiencing any freezing.
DispatchQueue.main.async {
self.myAd.present(fromRootViewController: self)
}
Can only assume iOS13 has better handling for this. And the reason the "game quit" scenario worked was because it was transitioning to the menu scene which is also the GameViewController. The app didn't like presenting an ad from the GameViewController while also transitioning to a new scene.

Related

Local notifications not showing in notification center (.provisional) / iOS 15.2.1

Has anyone experienced problems with [.provisional] notifications on iOS 15? Can't get anything to show up in Notification Center in 'Quiet Delivery' mode. I'm clueless..
These are relevant extracts:
UNUserNotificationCenter.current().requestAuthorization([.provisional, .alert])
// -> granted: true
let content = UNMutableNotificationContent()
content.title = "Test"
content.body = "Test"
let trigger = UNTimeIntervalNotificationTrigger(timeInterval: 1, repeats: false)
let request = UNNotificationRequest(identifier: "some_id...", content: content, trigger: trigger)
UNUserNotificationCenter.current().add(request, withCompletionHandler: nil)}
func userNotificationCenter(_ center: UNUserNotificationCenter,
willPresent notification: UNNotification, ...)
// This is called -> returning [.banner] (so it should show in foreground, not that it matters since it's provisional...)
BUT ... notifications center is empty.
The same code works fine using non-provisional mode (user granting explicit consent to notifications).
Is there something obvious I might be missing here?
--
UPDATE: Ok, not much of an answer, but clear observation:
Putting app into background -> above code does deliver notification into notification center.
This means that .provisional mode completely ignores 'willPresent notification:' return value [which allows presentation in foreground] AND does not deliver notification into notification center if app is in foreground.
I guess I can live with that and it kind of makes sense... (?).

How to get state restoration to restore all windows/tabs on macOS?

I have a non-document based macOS AppKit app. It has one window instantiated automatically by the storyboard. I have sub-classed NSWindowController and added a override func newWindowForTab(_ sender: Any?) to enable the + button on the tab-bar. My main view controller lets the user rename the tab title and the window title is set to the same. This is kind of like how Xcode tab renaming works.
Additionally I have sub-classed NSWindow and added a restorableStateKeyPaths to ensure tab and window titles are automatically restored on app restart.
This all works great.
But only for the first tab. The main window is loaded and it has the tab and window titles set automatically.
The other tabs (windows) are not restored.
Any hints on what I miss to make all tabs restored?
My NSWindowController:
class MyWindowController: NSWindowController {
var subview: MyWindowController?
#IBAction override func newWindowForTab(_ sender: Any?) {
let story = self.storyboard
let windowVC = story?.instantiateInitialController() as! Self
window?.addTabbedWindow(windowVC.window!, ordered: .above)
subview = windowVC
windowVC.window?.orderFront(self.window)
windowVC.window?.makeKey()
}
}
My NSWindow:
class MyWindow: NSWindow {
override class var restorableStateKeyPaths: [String] {
return [ "self.tab.title", "self.title" ]
}
}
First you need to make sure that state restoration is enabled for your user, you can do this by going to Preferences->General and unchecking "Close windows when quitting an app".
Then you should use a restoration class in order to restore all open windows.
Basically if an NSWindow doesn't have a restoration class it won't be preserved across launches, that includes your storyboard loaded window. In this case what is happening is Cocoa is ignoring all window preservation because you haven't defined a restoration class for any of your windows so it resorts to its default behavior which is loading the initial storyboard controller.
Implementing restoration class is easy, just create a restoration class that inherits from NSObject and conforms to NSWindowRestoration, then implement its only required type method restoreWindow(identifier:state:completionHandler) like so:
class MyAppWindowRestoration: NSObject, NSWindowRestoration {
static func restoreWindow(withIdentifier identifier: NSUserInterfaceItemIdentifier,
state: NSCoder,
completionHandler: #escaping (NSWindow?, Error?) -> Void) {
// 1.- Retrieve and show the window
// Retrieve a new instance of the only window
let window = (NSStoryboard.main?.instantiateInitialController() as? NSWindowController)?.window
// Call the completion handler with the window and no errors
completionHandler(window, nil)
}
}
Then just assign this class as the window restoration class on every window you want restored, you can do this everywhere after the window has loaded:
window.restorationClass = MyAppWindowRestoration.self
Unfortunately Apple's documentation on state restoration completely sucks so if you have any more questions let me know ;)

UIActivityViewController display is scrolled in last iOS11 GM on iPhone7Plus

Since the last release of iOS11 (GM version), the activity view is displayed scrolled to the top, with a spring resistance preventing the user to 'unscroll' it.
Previous version of iOS 11 (beta version) never suffered from this, nor iOS 10.
Also, one can see a blank margin at the bottom plus the fact that the preview image is not fetched/displayed right in the Website preview.
Here is the code. Any idea?
let textToShare = "\(message) (\(share))"
let objectsToShare : [Any] = [textToShare, url]
let activityViewController = UIActivityViewController(activityItems: objectsToShare as [AnyObject], applicationActivities: nil)
// New Excluded Activities Code
activityViewController.excludedActivityTypes = [.airDrop, .addToReadingList]
activityViewController.completionWithItemsHandler = {
(activityType: UIActivityType?, completed: Bool, returnedItems: [Any]?, error: Error? ) -> Void in
_completed(completed)
}
Ok. It’s only Twitter app itself that had to be updated for iOS11 (as in iOS11, there is no longer centralized system, sharing is handled by apps such as Facebook or Twitter themselves).

Opening NSSavePanel as sheet

I am using XCode7 beta2 to play around with Swift 2. Trying to use a File-Selection Dialog (NSSavePanel) brought me into some trouble.
Running the following code by clicking the associated button won't bring up the dialog as a sheet (not at all) but make my window's decoration disappear, leaving it in a broken state where otherwise functional sheets open as dialogs without decoration. Using the call to the deprecated API beginSheetModalForWindow, like in the commented line, works like expected.
#IBAction func openFileClicked(sender: AnyObject) {
let openPanel = NSSavePanel()
openPanel.canCreateDirectories = true
//openPanel.beginSheetModalForWindow(self.view.window!, completionHandler: {
openPanel.beginSheet(self.view.window!, completionHandler: {
(result) -> Void in
print("opening:\(result)" )
})
}
Is my code broken somehow or is there a issue with the API I am calling.

How to get resolution change event in swift?

I try to make an app, and now i shoud make some changes when screen resolution will change, but i coudn't find how to intercept this event.
Do you have any ideea how can i take that event?
The NSApplicationDidChangeScreenParametersNotification is posted when the configuration of the displays attached to the computer is changed, so
you can register for that notification, e.g. with
NSNotificationCenter.defaultCenter().addObserverForName(NSApplicationDidChangeScreenParametersNotification,
object: NSApplication.sharedApplication(),
queue: NSOperationQueue.mainQueue()) {
notification -> Void in
println("screen parameters changed")
}
Note that there can be various reasons why this notification is
fired, e.g. a change in the dock size (as observed in Cocoa Dock fires NSApplicationDidChangeScreenParametersNotification), so you have to
"remember" the old resolution and compare it with the new resolution.
Swift 4:
The didChangeScreenParametersNotification is posted when the configuration of the displays attached to the computer is changed.
Inside the func applicationDidFinishLaunching() in AppDelegate class or func viewDidLoad() in ViewController class, insert the following code:
NotificationCenter.default.addObserver(forName: NSApplication.didChangeScreenParametersNotification,
object: NSApplication.shared,
queue: OperationQueue.main) {
notification -> Void in
print("screen parameters changed")}
I personally, used it to center the position of my application when switching between the Mac and the external screen.
Here is the updated Swift 3 code:
NotificationCenter.default.addObserver(forName: NSNotification.Name.NSApplicationDidChangeScreenParameters,
object: NSApplication.shared(),
queue: OperationQueue.main) {
notification -> Void in
print("screen parameters changed")
}
Code for Swift 5+
NotificationCenter.default.addObserver(
forName: NSNotification.Name(rawValue: "NSApplicationDidChangeScreenParametersNotification"),
object: NSApplication.shared,
queue: .main) { notification in
self.adjustUIIfNeeded()
}

Resources