86

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?

Eric Aya
  • 69,473
  • 35
  • 181
  • 253
Krunal
  • 77,632
  • 48
  • 245
  • 261
  • 2
    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 Jun 06 '17 at 13:39
  • 1
    @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? – mfaani Jun 06 '17 at 14:26
  • I also have same confusion, why Swift 4 is showing me this error indicator... – Krunal Jun 06 '17 at 14:30
  • @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 – mfaani Jun 06 '17 at 14:34
  • I had a problem that extends this that can be found [here](https://stackoverflow.com/questions/44747945/ui-api-called-from-background-thread-swift-4). I had the error message addressed in this question as well as others. – joshLor Jun 25 '17 at 15:44

8 Answers8

150

In swift4

You can solve this issue with

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

Hope this will help...

Community
  • 1
  • 1
Wasim K. Memon
  • 5,979
  • 4
  • 40
  • 55
  • Even simpler, there's no need for the closure: `DispatchQueue.main.async(execute: UIApplication.shared.registerForRemoteNotifications())` – nathan Jul 25 '17 at 15:40
  • @nathan I think you need the closure. I got a `Cannot invoke 'async' with an argument list of type '(execute: Void)'` error using your example. – bvpb Sep 06 '17 at 11:50
  • 4
    Sorry, typo: `DispatchQueue.main.async(execute: UIApplication.shared.registerForRemoteNotifications)`. `execute` expects a function/closure of type `() -> Void` so `registerForRemoteNotifications` works – nathan Sep 06 '17 at 15:02
  • 1
    how to write in objective c. – Pooja Srivastava Oct 12 '17 at 07:37
  • 5
    @PoojaSrivastava `dispatch_async(dispatch_get_main_queue(), ^{ //Do stuff });` – badhanganesh Nov 28 '17 at 06:51
51

For Objective C, the below code works

    dispatch_async(dispatch_get_main_queue(), ^{
        [[UIApplication sharedApplication] registerForRemoteNotifications];
    });
R. Mohan
  • 2,182
  • 17
  • 30
38

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.
badhanganesh
  • 3,427
  • 3
  • 18
  • 39
  • 2
    Curious how did you learn this Xcode 9 new settings so quick? There are no WWDC videos out yet! – mfaani Jun 06 '17 at 14:47
  • 5
    @Honey The release notes usually contain all the necessary changes :) – Sulthan Jun 06 '17 at 15:09
  • 4
    @Honey Some people read the release notes and the documentation ;-) – vadian Jun 06 '17 at 15:10
  • @vadian facepalm...I'm still a noob...this is actually the first time I able to catchup and follow up on a WWDC – mfaani Jun 06 '17 at 15:22
  • 1
    @Honey Seriously, it's always worth it to read the (Xcode) release notes. – vadian Jun 06 '17 at 15:24
  • This thing is tripping on UIKit tasks done in a background context, which has nothing to do with the UI. Don't down vote this....it's good info. – cvb Jun 07 '17 at 02:44
  • 2
    @BadhanGanesh (I didn't downvote nor upvoted) if that's not what you intended then rewrite it...because it's like someone saying I have problem X...and then you replying you *can* do Y....well the OP is looking for answers. If it's just an explanation than make it clear that it's not answer – mfaani Jun 07 '17 at 03:25
  • Hmmmm so even traversing through the subviews is giving me this warning. I'm not trying to manipulate anything in the UI, just creating thumbs in the background. Should I really dispatch every line dealing with UIViews to the main thread? – Philip De Vries Aug 21 '17 at 12:20
  • @PhilipDeVries All UIKit method-calls should be in main thread. You are probably calling methods of UIKit in BG. – badhanganesh Aug 21 '17 at 12:26
  • Will this kind of warning/error be subject to AppStore rejection? I asked this because I have a really special usage of WKWebView in a background thread (acting as a Javascript engine, instead of JSCore, since the WebView has better JS performance with JIT compilation and JS execution) – Ming Zhu Nov 22 '17 at 19:15
  • I am pretty confused, even if I am using the DispatchQueue.main.async, the Main Thread Checker is still displaying the warning – cristallo Mar 23 '18 at 16:53
6

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
        }
}
vadian
  • 274,689
  • 30
  • 353
  • 361
6

In swift 4 or 5

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

on Objectiv-C

dispatch_async(dispatch_get_main_queue(), ^{
        [[UIApplication sharedApplication] registerForRemoteNotifications];
});
Bera Bhavin
  • 673
  • 5
  • 10
3

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()
                }
            }
        })
Sunil Targe
  • 7,251
  • 5
  • 49
  • 80
1

Hope this will help

DispatchQueue.main.async(execute: {
  UIApplication.shared.registerForRemoteNotifications()
})
Basir Alam
  • 1,332
  • 20
  • 24
1

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

DispatchQueue.main.async() { code }
Gibraltar
  • 1,106
  • 9
  • 21