Swift 3 - Les jetons d'appareil sont maintenant analysés comme '32BYTES'

94

Je viens de passer de Xcode 7 au 8 GM et au milieu des problèmes de compatibilité Swift 3, j'ai remarqué que mes jetons d'appareil avaient cessé de fonctionner. Ils ne lisent plus que «32 octets».

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

Avant la mise à jour, je pouvais simplement envoyer les NSData à mon serveur, mais maintenant j'ai du mal à analyser le jeton.

Qu'est-ce que j'oublie ici?

Edit: Je viens de tester la conversion vers NSData et je vois les résultats attendus. Alors maintenant, je suis juste confus au sujet du nouveau type de données.

    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
}
utilisateur1537360
la source
2
La modification de NSDatasimplement imprimer le descriptiondu NSData. Vous n'obtenez toujours pas une chaîne de cela.
rmaddy le

Réponses:

189
func application(_ application: UIApplication, didRegisterForRemoteNotificationsWithDeviceToken deviceToken: Data) {
    let token = deviceToken.map { String(format: "%02.2hhx", $0) }.joined()
    print(token)
}
Rok Gregorič
la source
L'opération inverse (chaîne hexadécimale -> Données) est disponible ici: stackoverflow.com/a/46663290/5252428
Rok Gregorič
4
%02xça va aussi.
jqgsninimo
1
@Rok pouvez-vous expliquer votre réponse? quel est le nouveau format dans lequel le jeton a été formaté?
simo
@simo est une conversion de données en chaîne hexadécimale que vous pouvez transmettre à un service Web / API pour pouvoir envoyer des notifications push.
Rok Gregorič
Un bon article avec une bonne explication des différences entre %02.2hhxet %02x nshipster.com/apns-device-tokens/#overturned-in-ios-13
Rok Gregorič
35

J'ai eu le même problème. Voici ma solution:

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
la source
1
Il s'agit d'une approche alternative pour encoder le NSDatadans une chaîne. J'ai suggéré d'utiliser l'encodage base64 dans ma réponse. Cela utilise l'encodage base16.
rmaddy
@rmaddy votre chemin est également intéressant mais il serait plus utile que vous nous donniez quelques conseils avec du code !!!
vivek agravat
29

Voici mon extension Swift 3 pour obtenir une chaîne hexadécimale encodée en base 16:

extension Data {
    var hexString: String {
        return map { String(format: "%02.2hhx", arguments: [$0]) }.joined()
    }
}
Phatmann
la source
J'ai remarqué que le format «% 02x» fonctionne également. Je ne comprends pas non plus ce que fait "hh"
andrei
1
@andrei "hh" indique au formateur de chaîne de traiter l'entrée comme un caractère. Cependant, dans Swift, Data est une collection de UInt8, vous n'en avez donc pas besoin
wyu
27

Le jeton d'appareil n'a jamais été une chaîne et certainement pas une chaîne encodée en UTF-8. Ce sont des données. C'est 32 octets de données opaques.

Le seul moyen valide de convertir les données opaques en une chaîne est de les encoder - généralement via un encodage base64.

Dans Swift 3 / iOS 10, utilisez simplement la Data base64EncodedString(options:)méthode.

rmaddy
la source
Eh bien, la différence ici est le nouveau type de données. J'ai juste essayé de convertir le deviceToken en NSData et il imprime maintenant mon jeton d'appareil comme auparavant. Avez-vous un exemple de la façon dont vous traiteriez cela sans NSData? Parce que cela semble piraté, mais aussi plus simple que ce qu'ils semblent s'attendre à ce que nous fassions.
user1537360
3
Je maintiens ce que j'ai dit. NSDataou Datapeu importe. Les octets des données ne sont pas et n'ont jamais été une chaîne. La documentation indique clairement qu'il s'agit d'un ensemble opaque de données. Le fait que votre code fonctionnait est la chance. C'était toujours la mauvaise façon de gérer cela. Convertissez simplement les données en chaîne en encodant les données en base64. C'est la bonne solution maintenant et avant.
rmaddy le
Le décodage Base64 ne fonctionne pas si vous utilisez un service tel qu'Amazon SNS. Les solutions qui convertissent les données en caractères hexadécimaux, tels que @satheeshwaran , produisent des chaînes de jetons de périphérique qui ressemblent à celles antérieures aux modifications apportées au SDK.
Alexander
@Alexander Qui a dit quelque chose sur le décodage Base64? Le but de la question et des réponses est d' encoder les données brutes, et non de les décoder, dans une chaîne. La seule raison de faire cela est de visualiser les données brutes. Le schéma de codage spécifique n'est pas pertinent. L'autre réponse utilise le codage en base 16. J'ai mentionné l'utilisation du codage en base 64.
rmaddy
@rmaddy Je voulais dire l'encodage Base64 dans mon commentaire, mes excuses.
Alexander
15

Essaye ça:

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

   let token = String(data: deviceToken.base64EncodedData(), encoding: .utf8)?.trimmingCharacters(in: CharacterSet.whitespaces).trimmingCharacters(in: CharacterSet(charactersIn: "<>")) 
}
utilisateur
la source
2
Il semble que maintenant il renvoie un jeton différent
ChikabuZ
8

essaye ça

if #available(iOS 10.0, *) {
   let deviceTokenString = deviceToken.reduce("", {$0 + String(format: "%02X", $1)})
}
bluenoù
la source
7

Swift 3

Le moyen le meilleur et le plus simple.

deviceToken.base64EncodedString()
Maselko
la source
3
C'est le plus simple, mais faites attention à ce que votre serveur utilise si vous remettez le jeton. Beaucoup d'API attendent avec Hex encoded ou Base-16. Par exemple, Django Push Notifications.
sww314 le
4

Celui-ci n'a pas été déclaré comme une réponse officielle (je l'ai vu dans un commentaire), mais c'est ce que j'ai finalement fait pour remettre mon jeton en ordre.

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
la source
pour les besoins de notre API backend (Python), cela a fini par être la solution la plus "propre" ¯ \ _ (ツ) _ / ¯
race_carr
1
Cela ne fonctionne pas sous iOS 10. La description ne renvoie désormais que "32 octets".
edopelawi
@edopelawi vous avez oublié de mettre as NSDatalà. lorsque vous mettez NSData pas de données, la valeur retournera même sur 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
la source
4
Bien que ce bloc de code puisse répondre à la question, il serait préférable que vous fournissiez une petite explication pour expliquer pourquoi il le fait.
Crispin
3

Je viens de faire ça

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

il a donné le même résultat que,

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

Obtenez un jeton d'appareil au format approprié.

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
la source