UIApplication.registerForRemoteNotifications () należy wywołać tylko z głównego wątku


81

Xcode 9 (iOS 11) wyświetla mi błąd / ostrzeżenie podczas rejestracji w celu otrzymania (zdalnego) powiadomienia.

Oto komunikat o błędzie

wprowadź opis obrazu tutaj

A oto kod, próbowałem:

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

Linia błędu / ostrzeżenia:

UIApplication.shared.registerForRemoteNotifications ()

Jak rozwiązać ten problem?


2
Jak powiedziano w komunikacie o błędzie, musisz zawinąć wywołanie UIApplication.shared.registerForRemoteNotifications()w głównym wątku. :) Google jak to nazwać w głównym wątku ...
Duyen-Hoa

@Hoa, dlaczego miałbyś to robić z mainThread? Nie jest to związane z interfejsem użytkownika ... czy może dlatego, że może wystąpić kilka sekund później i może spowodować nieoczekiwane zachowanie?
Honey

Też mam to samo zamieszanie, dlaczego Swift 4 pokazuje mi ten wskaźnik błędu ...
Krunal

@Sulthan To UIApplication.shared.registerForRemoteNotifications()nie jest związane z interfejsem użytkownika (nie monitujesz użytkowników, gdy otrzymasz token do cichych powiadomień). Więc wiersz, który pokazuje błąd, jest mylący. Jednak rejestracja w celu uzyskania odznak, alertów, dźwięków jest związana z interfejsem użytkownika i znacznie lepiej jest zrobić to z głównego wątku ... więc ogólnie cały blok center.requestAuthorization(options:...musi być zrobiony z głównego wątku ... ma to sens
Kochanie

Miałem problem, który to rozszerza, który można znaleźć tutaj . Otrzymałem komunikat o błędzie w tym pytaniu, jak również w innych.
joshLor

Odpowiedzi:


143

W swift4

Możesz rozwiązać ten problem za pomocą

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

Mam nadzieję, że to pomoże ...


Jeszcze prościej, nie ma potrzeby zamykania:DispatchQueue.main.async(execute: UIApplication.shared.registerForRemoteNotifications())
nathan

@nathan Myślę, że potrzebujesz zamknięcia. Wystąpił Cannot invoke 'async' with an argument list of type '(execute: Void)'błąd na Twoim przykładzie.
bvpb

4
Niestety, literówka: DispatchQueue.main.async(execute: UIApplication.shared.registerForRemoteNotifications). executeoczekuje funkcji / zamknięcia typu, () -> Voidwięc registerForRemoteNotificationsdziała
nathan

1
jak pisać w celu c.
Pooja Srivastava

5
@PoojaSrivastavadispatch_async(dispatch_get_main_queue(), ^{ //Do stuff });
badhanganesh

46

Poniższy kod działa w przypadku celu C

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

37

TL; DR:
Wszystkie manipulacje interfejsem użytkownika należy wykonywać w głównym wątku, aby uniknąć problemów. Jeśli tego nie zrobi, Main Thread Checker (nowo wprowadzona funkcja debugowania w XCode 9) spowoduje problemy w czasie wykonywania. Więc zawiń swój kod w bloku głównego wątku, jak poniżej, aby uniknąć usterek i ostrzeżeń w czasie wykonywania.

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

W wydaniach Xcode przed wer. 9, ostrzeżenia związane z głównym wątkiem byłyby drukowane w obszarze konsoli tekstowo. W każdym razie możesz opcjonalnie wyłączyć ( nie jest to zalecane podejście ) Kontroler głównego wątku w ustawieniach diagnostycznych w Edytuj schemat .


Wyjaśnienie:

Firma Apple wprowadziła nową opcję debugowania w XCode 9 do sprawdzania problemów w czasie wykonywania dla UIKit i innych interfejsów API, które manipulują elementami interfejsu użytkownika. Jeśli wystąpią jakiekolwiek zmiany w elementach interfejsu użytkownika z interfejsu API UIKit w czasie wykonywania, bez bloku wątku głównego, jest wysoce prawdopodobne, że spowoduje to usterki interfejsu użytkownika i awarie. Narzędzie do sprawdzania wątków głównych jest domyślnie włączone, aby wychwytywać te problemy w czasie wykonywania. Możesz wyłączyć Kontroler wątków głównych w oknie Edytuj schemat, tak jak poniżej, chociaż nie jest to zalecane:

Wyłącz sprawdzanie głównego wątku

Jeśli masz jakieś starsze SDK lub frameworki, podczas aktualizacji do Xcode 9 możesz napotkać to ostrzeżenie, ponieważ niektóre wywołania metod UIKit nie zostałyby opakowane w główny wątek. Zaktualizowanie ich do najnowszej wersji rozwiązałoby problem (jeśli programista jest tego świadomy i naprawił go).

Cytat z informacji o wersji beta XCode 9:

  • Nowość w Xcode 9 - Main Thread Checker.
    • Włącz wykrywanie niewłaściwego użycia interfejsu API interfejsu użytkownika w wątku w tle
    • Wykrywa wywołania metod AppKit, UIKit i WebKit, które nie są wykonywane w głównym wątku.
    • Automatycznie włączane podczas debugowania i można je wyłączyć na karcie Diagnostyka w edytorze schematów.
    • Main Thread Checker działa z językami Swift i C.

1
Ciekawi Cię, jak szybko nauczyłeś się nowych ustawień Xcode 9? Nie ma jeszcze żadnych filmów WWDC!
Honey

4
@Honey Informacje o wydaniu zwykle zawierają wszystkie niezbędne zmiany :)
Sulthan

4
@ Kochanie Niektórzy czytają informacje o wydaniu i dokumentację ;-)
vadian

1
@ Kochanie Poważnie, zawsze warto przeczytać informacje o wydaniu (Xcode).
vadian

2
@BadhanGanesh (nie zagłosowałem ani nie przegłosowałem), jeśli nie to zamierzałeś, przepisz to ... ponieważ to tak, jakby ktoś powiedział, że mam problem X ... a potem odpowiadasz, że możesz zrobić Y ... cóż, OP szuka odpowiedzi. Jeśli to tylko wyjaśnienie, to daj jasno do zrozumienia, że ​​nie jest odpowiedzią
Kochanie,

6

Komunikat o błędzie jest dość jasny: wyślij registerForRemoteNotificationsdo głównego wątku.

Chciałbym użyć grantedparametru i obsługiwać errorodpowiednio

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

3

Jest to również poprawny sposób postępowania w 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()
                }
            }
        })

1

Mam nadzieję, że to pomoże

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

1

To właśnie zadziałało dla mnie. Dzięki uprzejmości @ Mason11987 w zaakceptowanym komentarzu powyżej.

DispatchQueue.main.async() { code }
Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.