84
votes

Xcode 9 (iOS 11) showing me an error/warning while registering for Push (remote) notification.

Here is error message

enter image description here

And here is code, I've tried:

let center  = UNUserNotificationCenter.current()
center.delegate = self
center.requestAuthorization(options: [.sound, .alert, .badge]) { (granted, error) in
        if error == nil{
              UIApplication.shared.registerForRemoteNotifications()
        }
 }

Error/Warning Line:

UIApplication.shared.registerForRemoteNotifications()

How to resolve this?

8
As said in the error message, you have to wrap the call to UIApplication.shared.registerForRemoteNotifications() in the main thread. :) Let google how to call it in main thread ... - Duyen-Hoa
@Hoa why would you need to do this from mainThread? It's not UI related...or is it because it has the potential to happen a few seconds later and that could cause unexpected behavior? - Honey
I also have same confusion, why Swift 4 is showing me this error indicator... - Krunal
@Sulthan The UIApplication.shared.registerForRemoteNotifications() isn't UI related (you don't prompt users when you get token for silent Notifications). So the line the error is showing is confusing. However registering for the badges, alerts, sounds is UI related and it's much better to do it from main thread...so overall the entire block of center.requestAuthorization(options:... must be done from main thread...it makes sense - Honey
I had a problem that extends this that can be found here. I had the error message addressed in this question as well as others. - joshLor

8 Answers

147
votes

In swift4

You can solve this issue with

DispatchQueue.main.async {
  UIApplication.shared.registerForRemoteNotifications()
}

Hope this will help...

50
votes

For Objective C, the below code works

    dispatch_async(dispatch_get_main_queue(), ^{
        [[UIApplication sharedApplication] registerForRemoteNotifications];
    });
37
votes

TL;DR:
All UI manipulations should be done in the Main Thread to avoid problems. If failed to do so, Main Thread Checker (Newly introduced debugging feature in Xcode 9) will produce issues at Runtime. So wrap your code in Main Thread block like below to avoid glitches and runtime warnings.

DispatchQueue.main.async {
    UIApplication.shared.registerForRemoteNotifications()
}

In Xcode releases before ver. 9, the warnings related to main thread would get printed in the console area textually. Anyway, you can optionally disable (not a recommended approach) the Main Thread Checker in the Diagnostic settings in Edit Scheme.

Explanation:

Apple introduced a new debugging option in Xcode 9 for checking issues at Runtime for UIKit and other API's that manipulate UI elements. If there's any change to the UI elements from UIKit API at Runtime, without a Main thread block, it is highly likely to cause UI glitches and crashes. The Main Thread Checker is enabled by default to catch those issues at runtime. You can disable Main Thread Checker in the Edit Scheme window just like below, although it is not really recommended to do so:

Disable Main Thread Checker

If you have any older SDK's or Frameworks, when updating to Xcode 9, you may face this warning since some of the UIKit method calls wouldn't have been wrapped in Main Thread. Updating them to latest version would fix the issue (if the developer is aware of it and fixed it).

Quote from Xcode 9 beta release notes:

  • New in Xcode 9 – Main Thread Checker.
  • **Enable detection of UI API

misuse from background thread**

  • Detects AppKit, UIKit, and WebKit method calls that are not made on the main thread.
  • Automatically enabled during debugging, and can be disabled in the Diagnostic tab of the scheme editor.
  • Main Thread Checker works with Swift and C languages.
6
votes

The error message is pretty clear: dispatch registerForRemoteNotifications to the main thread.

I would use the granted parameter and handle the error accordingly

center.requestAuthorization(options: [.sound, .alert, .badge]) { (granted, error) in
        if granted {
              DispatchQueue.main.async {
                  UIApplication.shared.registerForRemoteNotifications()
              }
        } else {
           print(error!)
           // handle the error
        }
}
3
votes

This is also correct way to do in Swift 4.0

UNUserNotificationCenter.current().delegate = self
        UNUserNotificationCenter.current().requestAuthorization(options: [.alert,.sound,.badge], completionHandler: {(granted,error) in
            if granted{
                DispatchQueue.main.async {
                    application.registerForRemoteNotifications()
                }
            }
        })
3
votes

In swift 4 or 5

DispatchQueue.main.async {
  UIApplication.shared.registerForRemoteNotifications()
}

on Objectiv-C

dispatch_async(dispatch_get_main_queue(), ^{
        [[UIApplication sharedApplication] registerForRemoteNotifications];
});
1
votes

Hope this will help

DispatchQueue.main.async(execute: {
  UIApplication.shared.registerForRemoteNotifications()
})
1
votes

This is what worked for me. Courtesy of @Mason11987 in the accepted comment above.

DispatchQueue.main.async() { code }