Swift 3: los tokens de dispositivo ahora se analizan como '32BYTES'

94

Acabo de actualizar de Xcode 7 a 8 GM y, en medio de los problemas de compatibilidad de Swift 3, noté que los tokens de mi dispositivo dejaron de funcionar. Ahora solo leen '32BYTES'.

    func application(_ application: UIApplication, didRegisterForRemoteNotificationsWithDeviceToken deviceToken: Data)
{
    print(deviceToken) // Prints '32BYTES'
    print(String(data: deviceToken , encoding: .utf8)) // Prints nil
}

Antes de la actualización, podía simplemente enviar el NSData a mi servidor, pero ahora estoy teniendo dificultades para analizar el token.

¿Que me estoy perdiendo aqui?

Editar: Solo estoy probando la conversión de nuevo a NSData y veo los resultados esperados. Así que ahora estoy confundido acerca del nuevo tipo de datos.

    func application(_ application: UIApplication, didRegisterForRemoteNotificationsWithDeviceToken deviceToken: Data)
{
    print(deviceToken) // Prints '32BYTES'
    print(String(data: deviceToken , encoding: .utf8)) // Prints nil

    let d = NSData(data: deviceToken)
    print(d) // Prints my device token
}
usuario1537360
fuente
2
Cambiar a NSDatasimplemente imprime el descriptionde NSData. Todavía no obtienes una cadena de eso.
rmaddy

Respuestas:

189
func application(_ application: UIApplication, didRegisterForRemoteNotificationsWithDeviceToken deviceToken: Data) {
    let token = deviceToken.map { String(format: "%02.2hhx", $0) }.joined()
    print(token)
}
Rok Gregorič
fuente
La operación inversa (cadena hexadecimal -> Datos) está disponible aquí: stackoverflow.com/a/46663290/5252428
Rok Gregorič
4
%02xtambién está bien.
jqgsninimo
1
@Rok, ¿puedes explicar tu respuesta? ¿Cuál es el nuevo formato en el que se formateó el token?
simo
@simo es una conversión de datos a una cadena hexadecimal que puede pasar a un servicio web / API para poder enviar notificaciones automáticas.
Rok Gregorič
Una buena publicación con una buena explicación de las diferencias entre %02.2hhxy %02x nshipster.com/apns-device-tokens/#overturned-in-ios-13
Rok Gregorič
35

Yo tuve el mismo problema. Esta es mi solucion:

func application(_ application: UIApplication, didRegisterForRemoteNotificationsWithDeviceToken deviceToken: Data) {
    var token = ""
    for i in 0..<deviceToken.count {
        token = token + String(format: "%02.2hhx", arguments: [deviceToken[i]])
    }
    print(token)
}
Oleg
fuente
1
Este es un enfoque alternativo para codificar el NSDataen una cadena. Sugerí usar la codificación base64 en mi respuesta. Esto usa codificación base16.
rmaddy
@rmaddy your way también es interesante, pero sería más útil si nos dieras alguna guía con el código.
vivek agravat
29

Aquí está mi extensión Swift 3 para obtener una cadena hexadecimal codificada en base 16:

extension Data {
    var hexString: String {
        return map { String(format: "%02.2hhx", arguments: [$0]) }.joined()
    }
}
phatmann
fuente
He notado que el formato "% 02x" también funciona. Tampoco puedo entender lo que hace "hh"
andrei
1
@andrei "hh" le dice al formateador de cadenas que trate la entrada como un carácter. Sin embargo, en Swift, Data es una colección de UInt8, por lo que no lo necesita
wyu
27

El token del dispositivo nunca ha sido una cadena y, desde luego, tampoco una cadena codificada en UTF-8. Son datos. Son 32 bytes de datos opacos.

La única forma válida de convertir los datos opacos en una cadena es codificarlos, comúnmente a través de una codificación base64.

En Swift 3 / iOS 10, simplemente use el Data base64EncodedString(options:)método.

rmaddy
fuente
Bueno, la diferencia aquí es el nuevo tipo de datos. Intenté convertir el deviceToken a NSData y ahora imprime el token de mi dispositivo como antes. ¿Tiene un ejemplo de cómo manejaría esto sin NSData? Porque esto se siente hack, pero también más sencillo de lo que parecen esperar que hagamos.
user1537360
3
Mantengo lo que dije. NSDatao Datano importa. Los bytes de los datos no son y nunca han sido una cadena. La documentación establece claramente que es un conjunto de datos opaco. El hecho de que tu código solía funcionar es suerte. Siempre fue la forma incorrecta de manejarlo. Simplemente convierta los datos en una cadena codificando en base64 los datos. Esa es la solución adecuada ahora y antes.
rmaddy
La decodificación Base64 no funciona si utiliza un servicio como Amazon SNS. Las soluciones que convierten los datos en caracteres hexadecimales, como @satheeshwaran , producen cadenas de tokens de dispositivo que se asemejan a las anteriores a los cambios en el SDK.
Alexander
@Alexander ¿Quién dijo algo sobre la decodificación Base64? El objetivo de la pregunta y las respuestas es codificar los datos en bruto, no decodificar, en una cadena. La única razón para hacer esto es ver los datos sin procesar. El esquema de codificación específico es irrelevante. La otra respuesta es usar codificación en base 16. Mencioné el uso de codificación base 64.
rmaddy
@rmaddy Me refiero a la codificación Base64 en mi comentario, mis disculpas.
Alexander
15

Prueba esto:

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

   let token = String(data: deviceToken.base64EncodedData(), encoding: .utf8)?.trimmingCharacters(in: CharacterSet.whitespaces).trimmingCharacters(in: CharacterSet(charactersIn: "<>")) 
}
usuario
fuente
2
Parece que ahora devuelve un token diferente
ChikabuZ
8

prueba esto

if #available(iOS 10.0, *) {
   let deviceTokenString = deviceToken.reduce("", {$0 + String(format: "%02X", $1)})
}
azul en ninguna parte
fuente
7

Swift 3

La mejor y más sencilla forma.

deviceToken.base64EncodedString()
Maselko
fuente
3
Esto es más fácil, pero tenga cuidado con lo que está usando su servidor si está entregando el token. Muchas API esperan con codificación hexadecimal o Base-16. Por ejemplo, notificaciones push de Django.
sww314
4

Esta no se declaró como una respuesta oficial (la vi en un comentario), pero es lo que finalmente hice para volver a poner mi token en orden.

let tokenData = deviceToken as NSData
let token = tokenData.description

// remove any characters once you have token string if needed
token = token.replacingOccurrences(of: " ", with: "")
token = token.replacingOccurrences(of: "<", with: ""
token = token.replacingOccurrences(of: ">", with: "")
Bill Burgess
fuente
para los propósitos de nuestra API de backend (Python), esta terminó siendo la solución "más limpia" ¯ \ _ (ツ) _ / ¯
race_carr
1
Esto no funciona en iOS 10. La descripción ahora solo devuelve "32bytes".
edopelawi
@edopelawi se te olvidó poner as NSDataallí. cuando pones como NSData, no los datos devolverán el valor correcto incluso en iOS 10
Jakub
4
func application(_ application: UIApplication, didRegisterForRemoteNotificationsWithDeviceToken deviceToken: Data) {

    let token = deviceToken.map({ String(format: "%02.2hhx", $0)}).joined()
     print("TOKEN: " + token)


}
PacoG
fuente
4
Si bien este bloque de código puede responder a la pregunta, sería mejor si pudiera proporcionar una pequeña explicación de por qué lo hace.
Crispin
3

Acabo de hacer esto,

let token = String(format:"%@",deviceToken as CVarArg).components(separatedBy: CharacterSet.alphanumerics.inverted).joined(separator: "")

dio el mismo resultado que,

let token = deviceToken.map { String(format: "%02.2hhx", $0) }.joined()
Satheeshwaran
fuente
0

Obtenga el token del dispositivo con el formato adecuado.

func application(_ application: UIApplication, didRegisterForRemoteNotificationsWithDeviceToken deviceToken: Data) 
{
            var formattedToken = ""
            for i in 0..<deviceToken.count {
                formattedToken = formattedToken + String(format: "%02.2hhx", arguments: [deviceToken[i]])
            }
            print(formattedToken)
}
Basir Alam
fuente