¿Te registras para recibir notificaciones push en Xcode 8 / Swift 3.0?

121

Estoy tratando de hacer que mi aplicación funcione en Xcode 8.0 , y me encuentro con un error. Sé que este código funcionó bien en versiones anteriores de swift, pero supongo que el código para esto se cambió en la nueva versión. Aquí está el código que estoy tratando de ejecutar:

let settings = UIUserNotificationSettings(forTypes: [.Sound, .Alert, .Badge], categories: nil)     
UIApplication.sharedApplication().registerUserNotificationSettings(settings)
UIApplication.shared().registerForRemoteNotifications()

El error que obtengo es "Las etiquetas de argumento '(para Tipos :, categorías :)' no coinciden con ninguna sobrecarga disponible"

¿Hay un comando diferente que podría intentar para que esto funcione?

Asher Hawthorne
fuente
2
Escribí una guía sobre cómo hacer eso: eladnava.com/…
Elad Nava

Respuestas:

307

Importe el UserNotificationsmarco y agregue el UNUserNotificationCenterDelegateen AppDelegate.swift

Solicitar permiso de usuario

func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [NSObject: AnyObject]?) -> Bool {


        let center = UNUserNotificationCenter.current()
        center.requestAuthorization(options:[.badge, .alert, .sound]) { (granted, error) in
            // Enable or disable features based on authorization.
        }
        application.registerForRemoteNotifications()
        return true
}

Obtener token de dispositivo

func application(_ application: UIApplication, didRegisterForRemoteNotificationsWithDeviceToken deviceToken: Data) {

    let deviceTokenString = deviceToken.reduce("", {$0 + String(format: "%02X", $1)})
    print(deviceTokenString)
}

En caso de error

func application(_ application: UIApplication, didFailToRegisterForRemoteNotificationsWithError error: Error) {

        print("i am not available in simulator \(error)")
}

En caso de que necesite conocer los permisos otorgados

UNUserNotificationCenter.current().getNotificationSettings(){ (settings) in

            switch settings.soundSetting{
            case .enabled:

                print("enabled sound setting")

            case .disabled:

                print("setting has been disabled")

            case .notSupported:
                print("something vital went wrong here")
            }
        }
Anish Parajuli 웃
fuente
1
Recibo un error en Swift 2.3: UNUserNotificationCenter no tiene miembro actual
Async-
Hay, ¿puedes proporcionar la salvación en el objetivo c?
Ayaz
Solo una nota, ya no devuelve el token del dispositivo. Al menos en mi caso, simplemente devuelve "32 bytes"
Brian F Leighty
1
@ Async: no estás viendo current () porque solo funciona en Swift 3.
Allen
44
@PavlosNicolaou Importar el marco de notificaciones de usuario
Anish Parajuli 웃
48
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [NSObject: AnyObject]?) -> Bool {

    if #available(iOS 10, *) {

        //Notifications get posted to the function (delegate):  func userNotificationCenter(_ center: UNUserNotificationCenter, didReceive response: UNNotificationResponse, withCompletionHandler completionHandler: () -> Void)"


        UNUserNotificationCenter.current().requestAuthorization(options: [.alert, .badge, .sound]) { (granted, error) in

            guard error == nil else {
                //Display Error.. Handle Error.. etc..
                return
            }

            if granted {
                //Do stuff here..

                //Register for RemoteNotifications. Your Remote Notifications can display alerts now :)
                DispatchQueue.main.async {
                    application.registerForRemoteNotifications()
                }
            }
            else {
                //Handle user denying permissions..
            }
        }

        //Register for remote notifications.. If permission above is NOT granted, all notifications are delivered silently to AppDelegate.
        application.registerForRemoteNotifications()
    }
    else {
        let settings = UIUserNotificationSettings(types: [.alert, .badge, .sound], categories: nil)
        application.registerUserNotificationSettings(settings)
        application.registerForRemoteNotifications()
    }

    return true
}
Brandon
fuente
¿Cuál es el beneficio adicional de este nuevo marco? Lo que veo aquí es el uso de un enfoque 'completionHandler sobre delegado y luego se da la toma de decisiones a usted de inmediato: error, sentado, o notGranted .... En 6 <IOS <10 que tenía que hacer application.isRegisteredForRemoteNotifications()para ver si se trata de concedido, y use otro método de delegado en caso de que haya un error. ¿Correcto? ¿Algo más?
Miel
¿Por qué es diferente de la respuesta aceptada? Él tiene un application.registerForRemoteNotifications() después de sucenter.requestAuthorization
Miel
1
@Miel; Eso se agrega si desea habilitar las notificaciones "Remotas". Cuando escribí mi respuesta, no existía otra respuesta y @OP no especificó si querían soporte remoto o local o iOS 10, así que agregué todo lo que pude. Nota: No debe registrarse para RemoteNotifications hasta que el usuario haya otorgado acceso (de lo contrario, todas las notificaciones remotas se entregan en silencio [a menos que eso sea lo que desee], sin ventanas emergentes). Además, la ventaja de la nueva API es que admite archivos adjuntos. En otras palabras, puede agregar GIF y otras imágenes, videos, etc. a sus notificaciones.
Brandon
3
En el cierre, deberá realizar tareas relacionadas con la interfaz de usuario en el subproceso principal ... DispatchQueue.main.async {... hacer cosas aquí ...}
Chris Allinson
1
Beneficio de esta solución cuando no se usa en AppDelegate para hacer lo mismo en el código
Codenator81
27
import UserNotifications  

A continuación, vaya al editor del proyecto para su objetivo y, en la pestaña General, busque la sección Marcos vinculados y bibliotecas.

Haga clic en + y seleccione UserNotifications.framework:

// iOS 12 support
if #available(iOS 12, *) {  
    UNUserNotificationCenter.current().requestAuthorization(options:[.badge, .alert, .sound, .provisional, .providesAppNotificationSettings, .criticalAlert]){ (granted, error) in }
    application.registerForRemoteNotifications()
}

// iOS 10 support
if #available(iOS 10, *) {  
    UNUserNotificationCenter.current().requestAuthorization(options:[.badge, .alert, .sound]){ (granted, error) in }
    application.registerForRemoteNotifications()
}
// iOS 9 support
else if #available(iOS 9, *) {  
    UIApplication.shared.registerUserNotificationSettings(UIUserNotificationSettings(types: [.badge, .sound, .alert], categories: nil))
    UIApplication.shared.registerForRemoteNotifications()
}
// iOS 8 support
else if #available(iOS 8, *) {  
    UIApplication.shared.registerUserNotificationSettings(UIUserNotificationSettings(types: [.badge, .sound, .alert], categories: nil))
    UIApplication.shared.registerForRemoteNotifications()
}
// iOS 7 support
else {  
    application.registerForRemoteNotifications(matching: [.badge, .sound, .alert])
}

Usar métodos de delegado de notificaciones

// Called when APNs has assigned the device a unique token
func application(_ application: UIApplication, didRegisterForRemoteNotificationsWithDeviceToken deviceToken: Data) {  
    // Convert token to string
    let deviceTokenString = deviceToken.reduce("", {$0 + String(format: "%02X", $1)})
    print("APNs device token: \(deviceTokenString)")
}

// Called when APNs failed to register the device for push notifications
func application(_ application: UIApplication, didFailToRegisterForRemoteNotificationsWithError error: Error) {  
    // Print the error to console (you should alert the user that registration failed)
    print("APNs registration failed: \(error)")
}

Para recibir notificaciones push

func application(_ application: UIApplication, didReceiveRemoteNotification userInfo: [AnyHashable : Any], fetchCompletionHandler completionHandler: @escaping (UIBackgroundFetchResult) -> Void) {
    completionHandler(UIBackgroundFetchResult.noData)
}

Configurar notificaciones push es habilitar la función dentro de Xcode 8 para su aplicación. Simplemente vaya al editor del proyecto para su objetivo y luego haga clic en la pestaña Capacidades . Busque notificaciones push y cambie su valor a ON .

Consulte el siguiente enlace para obtener más métodos de delegado de notificaciones

Manejo de notificaciones locales y remotas UIApplicationDelegate - Manejo de notificaciones locales y remotas

https://developer.apple.com/reference/uikit/uiapplicationdelegate

Mohamed Jaleel Nazir
fuente
20

Tuve problemas con las respuestas aquí al convertir el objeto DeviceToken Data en una cadena para enviar a mi servidor con la versión beta actual de Xcode 8. Especialmente el que estaba usando deviceToken.description como en 8.0b6 que devolvería "32 Bytes" que no es muy útil :)

Esto es lo que funcionó para mí ...

Cree una extensión en Datos para implementar un método "hexString":

extension Data {
    func hexString() -> String {
        return self.reduce("") { string, byte in
            string + String(format: "%02X", byte)
        }
    }
}

Y luego utilícelo cuando reciba la devolución de llamada al registrarse para recibir notificaciones remotas:

func application(_ application: UIApplication, didRegisterForRemoteNotificationsWithDeviceToken deviceToken: Data) {
    let deviceTokenString = deviceToken.hexString()
    // Send to your server here...
}
tomwilson
fuente
8
También tuve el problema de "32bytes". Gran solución, puedes hacer la conversión en línea sin crear una extensión. Así: let deviceTokenString = deviceToken.reduce("", {$0 + String(format: "%02X", $1)})
Alain Stulz
1
Absurdo que no haya una solución proveniente de la API en sí misma
Aviel Gross
1
Sí lo ha sido siempre bastante raro que la API .. sorprende que no lo fijan al hacer el nuevo marco de notificaciones en iOS10
tomwilson
17

En iOS10 en lugar de su código, debe solicitar una autorización de notificación con lo siguiente: (No olvide agregar el UserNotificationsMarco)

if #available(iOS 10.0, *) {
        UNUserNotificationCenter.current().requestAuthorization([.alert, .sound, .badge]) { (granted: Bool, error: NSError?) in
            // Do something here
        }
    }

Además, el código correcto para usted es (use en la elsecondición anterior, por ejemplo):

let setting = UIUserNotificationSettings(types: [.alert, .badge, .sound], categories: nil)
UIApplication.shared().registerUserNotificationSettings(setting)
UIApplication.shared().registerForRemoteNotifications()

Finalmente, asegúrese de que Push Notificationesté activado en target-> Capabilities-> Push notification. (configúralo On)

tsnkff
fuente
1
ver: Página 73 del documento de Apple aquí
tsnkff
2
¡Muchas gracias por la respuesta! Sin embargo, usando el código, dice "Uso del identificador no resuelto 'UNUserNotificationCenter'"
Asher Hawthorne
Y muchas gracias por la documentación, blablabla! No vi eso en su sitio, me alegro de que exista. : D
Asher Hawthorne
44
¡Espera, creo que lo tengo! Solo tenía que importar el marco de notificaciones. XD
Asher Hawthorne
1
Sí. Editaré mi respuesta para agregar esto para el futuro lector. Además, lea sobre las nuevas notificaciones, ahora hay formas más potentes e interactivas. :)
tsnkff
8

Bueno, esto funciona para mí. Primero en AppDelegate

import UserNotifications

Luego:

   func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey: Any]?) -> Bool {
        // Override point for customization after application launch.
        registerForRemoteNotification()
        return true
    }

    func registerForRemoteNotification() {
        if #available(iOS 10.0, *) {
            let center  = UNUserNotificationCenter.current()
            center.delegate = self
            center.requestAuthorization(options: [.sound, .alert, .badge]) { (granted, error) in
                if error == nil{
                    UIApplication.shared.registerForRemoteNotifications()
                }
            }
        }
        else {
            UIApplication.shared.registerUserNotificationSettings(UIUserNotificationSettings(types: [.sound, .alert, .badge], categories: nil))
            UIApplication.shared.registerForRemoteNotifications()
        }
    }

Para obtener el dispositivo token:

  func application(_ application: UIApplication, didRegisterForRemoteNotificationsWithDeviceToken deviceToken: Data) {

       let deviceTokenString = deviceToken.reduce("", {$0 + String(format: "%02X", $1)})

}
GoIn Su
fuente
5

Atención, deberías estar usando el hilo principal para esta acción.

let center = UNUserNotificationCenter.current()
center.requestAuthorization(options:[.badge, .alert, .sound]) { (granted, error) in
        if granted {
            DispatchQueue.main.async(execute: {
                UIApplication.shared.registerForRemoteNotifications()
            })
        }
    }
Mavrick Laakso
fuente
2

Primero , escuche el estado de notificación del usuario, es decir, registerForRemoteNotifications()para obtener el token del dispositivo APN;
Segundo , solicitar autorización. Cuando el usuario lo autorice, deviceToken se enviará al oyente, el AppDelegate;
Tercero , informe el token del dispositivo a su servidor.

extension AppDelegate {
    /// 1. 监听 deviceToken
    UIApplication.shared.registerForRemoteNotifications()

    /// 2. 向操作系统索要推送权限(并获取推送 token)
    static func registerRemoteNotifications() {
        if #available(iOS 10, *) {
            let uc = UNUserNotificationCenter.current()
            uc.delegate = UIApplication.shared.delegate as? AppDelegate
            uc.requestAuthorization(options: [.alert, .badge, .sound]) { (granted, error) in
                if let error = error { // 无论是拒绝推送,还是不提供 aps-certificate,此 error 始终为 nil
                    print("UNUserNotificationCenter 注册通知失败, \(error)")
                }
                DispatchQueue.main.async {
                    onAuthorization(granted: granted)
                }
            }
        } else {
            let app = UIApplication.shared
            app.registerUserNotificationSettings(UIUserNotificationSettings(types: [.badge, .sound, .alert], categories: nil)) // 获取用户授权
        }
    }

    // 在 app.registerUserNotificationSettings() 之后收到用户接受或拒绝及默拒后,此委托方法被调用
    func application(_ app: UIApplication, didRegister notificationSettings: UIUserNotificationSettings) {
        // 已申请推送权限,所作的检测才有效
        // a 征询推送许可时,用户把app切到后台,就等价于默拒了推送
        // b 在系统设置里打开推送,但关掉所有形式的提醒,等价于拒绝推送,得不token,也收不推送
        // c 关掉badge, alert和sound 时,notificationSettings.types.rawValue 等于 0 和 app.isRegisteredForRemoteNotifications 成立,但能得到token,也能收到推送(锁屏和通知中心也能看到推送),这说明types涵盖并不全面
        // 对于模拟器来说,由于不能接收推送,所以 isRegisteredForRemoteNotifications 始终为 false
       onAuthorization(granted: app.isRegisteredForRemoteNotifications)
    }

    static func onAuthorization(granted: Bool) {
        guard granted else { return }
        // do something
    }
}

extension AppDelegate {
    func application(_ app: UIApplication, didRegisterForRemoteNotificationsWithDeviceToken deviceToken: Data) {
        //
    }

    // 模拟器得不到 token,没配置 aps-certificate 的项目也得不到 token,网络原因也可能导致得不到 token
    func application(_ app: UIApplication, didFailToRegisterForRemoteNotificationsWithError error: Error) {
        //
    }
}
DawnSong
fuente
¿Cómo agregar notificaciones múltiples?
ArgaPK
@ArgaPK, enviar notificaciones push es lo que hace su plataforma de servidor.
DawnSong
0

La respuesta de ast1 es muy simple y útil. A mí me funciona, muchas gracias. Solo quiero señalarlo aquí, para que las personas que necesitan esta respuesta puedan encontrarla fácilmente. Entonces, aquí está mi código para registrar notificaciones locales y remotas (push).

    //1. In Appdelegate: didFinishLaunchingWithOptions add these line of codes
    let mynotif = UNUserNotificationCenter.current()
    mynotif.requestAuthorization(options: [.alert, .sound, .badge]) {(granted, error) in }//register and ask user's permission for local notification

    //2. Add these functions at the bottom of your AppDelegate before the last "}"
    func application(_ application: UIApplication, didRegister notificationSettings: UNNotificationSettings) {
        application.registerForRemoteNotifications()//register for push notif after users granted their permission for showing notification
}
    func application(_ application: UIApplication, didRegisterForRemoteNotificationsWithDeviceToken deviceToken: Data) {
    let tokenString = deviceToken.reduce("", {$0 + String(format: "%02X", $1)})
    print("Device Token: \(tokenString)")//print device token in debugger console
}
    func application(_ application: UIApplication, didFailToRegisterForRemoteNotificationsWithError error: Error) {
    print("Failed to register: \(error)")//print error in debugger console
}
Luthfi Rahman
fuente
0

Simplemente haga lo siguiente en didFinishWithLaunching::

if #available(iOS 10.0, *) {

    let center = UNUserNotificationCenter.current()

    center.delegate = self
    center.requestAuthorization(options: []) { _, _ in
        application.registerForRemoteNotifications()
    }
}

Recuerde sobre la declaración de importación:

import UserNotifications
Bartłomiej Semańczyk
fuente
Creo que esta debería ser la respuesta aceptada. Parece correcto llamar registerForRemoteNotifications()al controlador de finalización de requestAuthorization(). Es posible que desee rodear registerForRemoteNotifications()con una if granteddeclaración: center.requestAuthorization(options:[.badge, .alert, .sound]) { (granted, error) in if granted { UIApplication.shared.registerForRemoteNotifications() } }
Bocaxica
-1

Echa un vistazo a este código comentado:

import Foundation
import UserNotifications
import ObjectMapper

class AppDelegate{

    let center = UNUserNotificationCenter.current()
}

extension AppDelegate {

    struct Keys {
        static let deviceToken = "deviceToken"
    }

    // MARK: - UIApplicationDelegate Methods
    func application(_: UIApplication, didRegisterForRemoteNotificationsWithDeviceToken deviceToken: Data) {

        if let tokenData: String = String(data: deviceToken, encoding: String.Encoding.utf8) {
            debugPrint("Device Push Token \(tokenData)")
        }

        // Prepare the Device Token for Registration (remove spaces and < >)
        setDeviceToken(deviceToken)
    }

    func application(_: UIApplication, didFailToRegisterForRemoteNotificationsWithError error: Error) {
        debugPrint(error.localizedDescription)
    }

    // MARK: - Private Methods
    /**
     Register remote notification to send notifications
     */
    func registerRemoteNotification() {

        center.requestAuthorization(options: [.alert, .sound, .badge]) { (granted, error) in

            // Enable or disable features based on authorization.
            if granted  == true {

                DispatchQueue.main.async {
                    UIApplication.shared.registerForRemoteNotifications()
                }
            } else {
                debugPrint("User denied the permissions")
            }
        }
    }

    /**
     Deregister remote notification
     */
    func deregisterRemoteNotification() {
        UIApplication.shared.unregisterForRemoteNotifications()
    }

    func setDeviceToken(_ token: Data) {
        let token = token.map { String(format: "%02.2hhx", arguments: [$0]) }.joined()
        UserDefaults.setObject(token as AnyObject?, forKey: “deviceToken”)
    }

    class func deviceToken() -> String {
        let deviceToken: String? = UserDefaults.objectForKey(“deviceToken”) as? String

        if isObjectInitialized(deviceToken as AnyObject?) {
            return deviceToken!
        }

        return "123"
    }

    func isObjectInitialized(_ value: AnyObject?) -> Bool {
        guard let _ = value else {
                return false
         }
            return true
    }
}

extension AppDelegate: UNUserNotificationCenterDelegate {

    func userNotificationCenter(_ center: UNUserNotificationCenter, willPresent notification: UNNotification, withCompletionHandler completionHandler: @escaping(UNNotificationPresentationOptions) -> Swift.Void) {

        ("\(notification.request.content.userInfo) Identifier: \(notification.request.identifier)")

        completionHandler([.alert, .badge, .sound])
    }

    func userNotificationCenter(_ center: UNUserNotificationCenter, didReceive response: UNNotificationResponse, withCompletionHandler completionHandler: @escaping() -> Swift.Void) {

        debugPrint("\(response.notification.request.content.userInfo) Identifier: \(response.notification.request.identifier)")

    }
}

¡Avísame si hay algún problema!

CrazyPro007
fuente