Comment puis-je convertir mon jeton d'appareil (NSData) en NSString?

157

J'implémente des notifications push. Je souhaite enregistrer mon jeton APNS en tant que chaîne.

- (void)application:(UIApplication *)application
didRegisterForRemoteNotificationsWithDeviceToken:(NSData *)newDeviceToken
{
    NSString *tokenString = [NSString stringWithUTF8String:[newDeviceToken bytes]]; //[[NSString alloc]initWithData:newDeviceToken encoding:NSUTF8StringEncoding];
    NSLog(@"%@", tokenString);
    NSLog(@"%@", newDeviceToken);
}

La première ligne de code imprime null. le second imprime le jeton. Comment puis-je obtenir mon newDeviceToken en tant que NSString?

Sheehan Alam
la source
Quelle est la sortie du second NSLog, celui qui imprime newDeviceToken?
rob mayoff
ne pas utiliser la description
Fattie

Réponses:

39

utilisez ceci :

NSString * deviceTokenString = [[[[deviceToken description]
                         stringByReplacingOccurrencesOfString: @"<" withString: @""] 
                        stringByReplacingOccurrencesOfString: @">" withString: @""] 
                       stringByReplacingOccurrencesOfString: @" " withString: @""];

NSLog(@"The generated device token string is : %@",deviceTokenString);
kulss
la source
134
Cela semble une mauvaise idée d'utiliser la description: rien ne garantit que la version ultérieure d'iOS ne changera pas l'implémentation et le résultat de cet appel.
madewulf
16
En effet, c'est une très mauvaise idée.
David Snabel-Caunt
21
@madewulf est très gentil à vous de souligner à quel point c'est une idée si terrible d'utiliser la description .. cela aurait été encore plus agréable si vous aviez suggéré une alternative
abbood
6
La solution ci-dessous avec [deviceToken bytes] fait l'affaire.
madewulf
37
Il s'avère qu'à partir de Swift 3 / iOS 10, .description sur un jeton d'appareil renvoie "32 octets". Alors oui, n'utilisez pas ça.
Victor Luft
231

Si quelqu'un cherche un moyen de le faire dans Swift:

func application(application: UIApplication, didRegisterForRemoteNotificationsWithDeviceToken deviceToken: NSData) {
    let tokenChars = UnsafePointer<CChar>(deviceToken.bytes)
    var tokenString = ""

    for i in 0..<deviceToken.length {
        tokenString += String(format: "%02.2hhx", arguments: [tokenChars[i]])
    }

    print("tokenString: \(tokenString)")
}

Edit: pour Swift 3

Swift 3 introduit le Datatype, avec une sémantique de valeur. Pour convertir le deviceTokenen chaîne, vous pouvez procéder comme suit:

func application(_ application: UIApplication, didRegisterForRemoteNotificationsWithDeviceToken deviceToken: Data) {
    let token = deviceToken.map { String(format: "%02.2hhx", $0) }.joined()
    print(token)
}
Sascha
la source
118
Pourquoi cela doit-il être si compliqué, quel est le problème avec le système d'exploitation qui nous donne une chaîne puisque c'est ce dont tout le monde a besoin? Merci pour cette solution.
Piwaf
3
@Sascha J'espère que vous approuvez ma modification à votre réponse très utile :)
jrturton
16
J'ai refactoré: let token = deviceToken.map { String(format: "%02.2hhx", $0) }.joined() qiita.com/mono0926/items/3cf0dca3029f32f54a09
mono
2
Je ne recommande pas d'utiliser .description car cela n'est pas garanti pour être stable. Découvrez ma réponse ici: stackoverflow.com/questions/9372815/…
swift taylor
7
Pouvez-vous expliquer ce que "%02.2hhxfait?
Honey
155

Quelqu'un m'a aidé avec ça, je suis juste en train de passer

- (void)application:(UIApplication *)application didRegisterForRemoteNotificationsWithDeviceToken:(NSData *)devToken {

    const unsigned *tokenBytes = [deviceToken bytes];
    NSString *hexToken = [NSString stringWithFormat:@"%08x%08x%08x%08x%08x%08x%08x%08x",
                         ntohl(tokenBytes[0]), ntohl(tokenBytes[1]), ntohl(tokenBytes[2]),
                         ntohl(tokenBytes[3]), ntohl(tokenBytes[4]), ntohl(tokenBytes[5]),
                         ntohl(tokenBytes[6]), ntohl(tokenBytes[7])];

    [[MyModel sharedModel] setApnsToken:hexToken];
}
Shubhank
la source
5
C'est la meilleure solution, car encondig octets en hexadécimal implique que vous pouvez le compter;)
loretoparisi
4
Sur XCode 5, j'ai dû lancer le deviceToken pour le faire compiler: const unsigned * tokenBytes = (const unsigned *) [deviceToken bytes];
Ponytech
3
Les jetons seront bientôt plus grands que 32 octets, il faudra donc une boucle sur chaque octet, au lieu de huit entiers codés en dur.
Tom Dalling
5
Serait-ce une meilleure solution? const unsigned *tokenBytes = [deviceToken bytes]; NSMutableString *hexToken = [NSMutableString string]; for (NSUInteger byteCount = 0; byteCount * 4 < [deviceToken length]; byteCount++) { [hexToken appendFormat:@"%08x", ntohl(tokenBytes[byteCount])]; }
Harro
9
Important: APNs device tokens are of variable length. Do not hard-code their size.Dit Apple.
erkanyildiz
141

Vous pouvez utiliser ceci

- (NSString *)stringWithDeviceToken:(NSData *)deviceToken {
    const char *data = [deviceToken bytes];
    NSMutableString *token = [NSMutableString string];

    for (NSUInteger i = 0; i < [deviceToken length]; i++) {
        [token appendFormat:@"%02.2hhX", data[i]];
    }

    return [token copy];
}
Vlad Polyanskiy
la source
11
Cela devrait être la réponse acceptée, car elle est beaucoup plus sûre que l'utilisation description.
DrMickeyLauer
8
C'est la seule réponse correcte en Objective-C qui gérera l'augmentation prochaine de la taille du jeton.
Tom Dalling
Il est convenu que c'est probablement le moyen le plus sûr car il ne suppose aucune taille / longueur de jeton particulière.
Ryan H.
Fonctionne sous iOS 10.
Tjalsma
2
J'ai utilisé [token appendFormat:@"%02.2hhx", data[i]];comme Amazon SNS nécessite des minuscules.
Manuel Schmitzberger
43

Pour ceux qui veulent dans Swift 3 et la méthode la plus simple

func extractTokenFromData(deviceToken:Data) -> String {
    let token = deviceToken.reduce("", {$0 + String(format: "%02X", $1)})
    return token.uppercased();
}
Anand
la source
1
J'ai écrit le même code :) C'est la version la plus rapide, et seulement cela fonctionne
Quver
1
@Anand pouvez-vous s'il vous plaît expliquer ce qui se passe dans ce codedeviceToken.reduce("", {$0 + String(format: "%02X", $1)})
Ramakrishna
1
Il utilise la fonction de réduction de swift qui sérialise les données en chaîne hexadécimale, puis en chaîne. Pour en savoir plus sur la fonction de réduction, lisez useyourloaf.com/blog/swift-guide-to-map-filter-reduce
Anand
15

Explication de %02.2hhxla réponse au vote élevé :

  • %: Présente le xspécificateur de conversion.
  • 02: La largeur minimale de la valeur convertie est de 2. Si la valeur convertie comporte moins d'octets que la largeur du champ, elle doit être complétée par 0à gauche.
  • .2: Donne le nombre minimum de chiffres à afficher pour le xspécificateur de conversion.
  • hh: Spécifie que le xspécificateur de conversion s'applique à un argument char signé ou char non signé (l'argument aura été promu en fonction des promotions d'entiers, mais sa valeur doit être convertie en char signé ou en char non signé avant l'impression).
  • x: L'argument non signé doit être converti au format hexadécimal non signé dans le style "dddd"; les lettres "abcdef" sont utilisées. La précision spécifie le nombre minimum de chiffres à afficher; si la valeur convertie peut être représentée en moins de chiffres, elle doit être développée avec des zéros non significatifs. La précision par défaut est 1. Le résultat de la conversion de zéro avec une précision explicite de zéro ne doit être aucun caractère.

Pour plus de détails, consultez la spécification IEEE printf .


Sur la base de l'explication ci-dessus, je pense qu'il est préférable de passer %02.2hhxà %02xou %.2x.

Pour Swift 5, les méthodes suivantes sont toutes réalisables:

deviceToken.map({String(format: "%02x", $0)}).joined()
deviceToken.map({String(format: "%.2x", $0)}).joined()
deviceToken.reduce("", {$0 + String(format: "%02x", $1)})
deviceToken.reduce("", {$0 + String(format: "%.2x", $1)})

Le test est le suivant:

let deviceToken = (0..<32).reduce(Data(), {$0 + [$1]})
print(deviceToken.reduce("", {$0 + String(format: "%.2x", $1)}))
// Print content:
// 000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f
jqgsninimo
la source
Merci pour cette réponse. Cela fonctionne-t-il également avec iOS 12? Ou cela dépend-il uniquement de la version Swift?
Markus le
1
@Markus Cela fonctionne sous iOS 12, ne dépend que de la version Swift.
jqgsninimo
14

C'est ma solution et cela fonctionne bien dans mon application:

    NSString* newToken = [[[NSString stringWithFormat:@"%@",deviceToken] 
stringByTrimmingCharactersInSet:[NSCharacterSet characterSetWithCharactersInString:@"<>"]] stringByReplacingOccurrencesOfString:@" " withString:@""];
  • convertir NSDataen NSStringavecstringWithFormat
  • couper le "<>"
  • supprimer les espaces
Zeb
la source
10
Cela appelle implicitement -description, donc ce n'est pas plus sûr que la réponse acceptée.
jszumski
Pouvez-vous s'il vous plaît lier votre source? Je ne trouve aucune information à ce sujet. THX.
Zeb
Trouvé! Je pense que c'est un peu différent. Utiliser directement l'attribut description n'est pas sûr car il pourrait changer dans les versions futures, mais si vous l'utilisez via une méthode NSString, vous n'aurez guère de problèmes.
Zeb
5
Non, cela appelle vraiment descriptiondeviceToken comme le dit jszumski.
Jonny
1
@Zeb Il n'est pas sûr de descriptionsavoir si vous l'appelez directement ou si vous l'utilisez via une autre méthode, car le format de la chaîne renvoyée peut être modifié à tout moment. La bonne solution est ici: stackoverflow.com/a/16411517/108105
Tom Dalling
10

Je pense que la conversion de deviceToken en chaîne d'octets hexadécimaux n'a aucun sens. Pourquoi? Vous l'enverrez à votre backend, où il sera transformé en octets pour être poussé vers APNS. Alors, utilisez la méthode de NSDatabase64EncodedStringWithOptions , poussez-la sur le serveur, puis utilisez des données inversées décodées en base64 :) C'est tellement plus facile :)

NSString *tokenString = [tokenData base64EncodedStringWithOptions:NSDataBase64EncodingEndLineWithLineFeed];
Oleg Shanyuk
la source
@ jeet.chanchawat veuillez ne pas ajouter de code aux réponses des autres utilisateurs. Nous ne voulons pas mettre de mots dans leur bouche, en particulier lors de l'ajout de Swift à une réponse Objective-C. Ajoutez plutôt votre propre réponse.
JAL
2
Je ne voulais tout simplement pas plagier la réponse de @Oleg Shanyuk. Comme il s'agit simplement de la traduction dans une autre langue basée sur sa réponse, il mérite donc les votes futurs. Si j'ajoute une autre réponse, cela me donnera des votes positifs pour la réponse qui est la recherche de quelqu'un d'autre. J'espère que cela justifie l'EDIT.
jeet.chanchawat
10

Dans iOS 13 descriptionse cassera alors utilisez ceci

let deviceTokenString = deviceToken.map { String(format: "%02x", $0) }.joined()

Pour plus de clarté, décomposons cela et expliquons chaque partie:

La méthode map opère sur chaque élément d'une séquence. Étant donné que Data est une séquence d'octets dans Swift, la fermeture passée est évaluée pour chaque octet dans deviceToken. L'initialiseur String (format :) évalue chaque octet dans les données (représenté par le paramètre anonyme $ 0) à l'aide du spécificateur de format% 02x, pour produire une représentation hexadécimale à 2 chiffres complétée par zéro de l'entier octet / 8 bits. Après avoir collecté chaque représentation d'octet créée par la méthode map, joins () concatène chaque élément en une seule chaîne.

PS n'utilise pas la description donne une chaîne différente dans iOS 12 et iOS 13 et n'est pas sûre selon la portée future. Les développeurs n'auraient pas dû s'appuyer sur un format spécifique pour la description d'un objet.

// iOS 12
(deviceToken as NSData).description // "<965b251c 6cb1926d e3cb366f dfb16ddd e6b9086a 8a3cac9e 5f857679 376eab7C>"

// iOS 13
(deviceToken as NSData).description // "{length = 32, bytes = 0x965b251c 6cb1926d e3cb366f dfb16ddd ... 5f857679 376eab7c }"

Pour plus d'informations, lisez This .

SuryaKantSharma
la source
10

Dans iOS 13, la description sera dans un format différent. Veuillez utiliser le code ci-dessous pour récupérer le jeton de l'appareil.

- (NSString *)fetchDeviceToken:(NSData *)deviceToken {
    NSUInteger len = deviceToken.length;
    if (len == 0) {
        return nil;
    }
    const unsigned char *buffer = deviceToken.bytes;
    NSMutableString *hexString  = [NSMutableString stringWithCapacity:(len * 2)];
    for (int i = 0; i < len; ++i) {
        [hexString appendFormat:@"%02x", buffer[i]];
    }
    return [hexString copy];
}
Vishnu Prakash
la source
Solution parfaite pour ios 13. Merci Vishnu
Manish
1
Il ne compile pas actuellement - lengthdans la boucle for devrait être changé en len. Apparemment, un changement trop petit pour que je puisse faire un montage .. Mais le reste fonctionne parfaitement!
Anders Friis
vous êtes un
sauveur de
3

C'est une solution un peu plus courte:

NSData *token = // ...
const uint64_t *tokenBytes = token.bytes;
NSString *hex = [NSString stringWithFormat:@"%016llx%016llx%016llx%016llx",
                 ntohll(tokenBytes[0]), ntohll(tokenBytes[1]),
                 ntohll(tokenBytes[2]), ntohll(tokenBytes[3])];
k06a
la source
3

Version Swift fonctionnelle

Bon mot:

let hexString = UnsafeBufferPointer<UInt8>(start: UnsafePointer(data.bytes),
count: data.length).map { String(format: "%02x", $0) }.joinWithSeparator("")

Voici dans une forme d'extension réutilisable et auto-documentée:

extension NSData {
    func base16EncodedString(uppercase uppercase: Bool = false) -> String {
        let buffer = UnsafeBufferPointer<UInt8>(start: UnsafePointer(self.bytes),
                                                count: self.length)
        let hexFormat = uppercase ? "X" : "x"
        let formatString = "%02\(hexFormat)"
        let bytesAsHexStrings = buffer.map {
            String(format: formatString, $0)
        }
        return bytesAsHexStrings.joinWithSeparator("")
    }
}

Vous pouvez également utiliser reduce("", combine: +)au lieu de joinWithSeparator("")pour être considéré comme un maître fonctionnel par vos pairs.


Edit: J'ai changé String ($ 0, radix: 16) en String (format: "% 02x", $ 0), car les nombres à un chiffre devaient avoir un zéro de remplissage

(Je ne sais pas encore comment marquer une question comme un double de cette autre , alors je viens de publier à nouveau ma réponse)

NiñoScript
la source
Fonctionne pour moi, merci.
Hasya
3

2020

jeton sous forme de texte ...

let tat = deviceToken.map{ data in String(format: "%02.2hhx", data) }.joined()

ou si vous préférez

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

(le résultat est le même)

Fattie
la source
2

Jeter ma réponse sur la pile. Évitez d'utiliser l'analyse des chaînes; Ce n'est pas garanti par la documentation que NSData.description fonctionnera toujours de cette façon.

Implémentation Swift 3:

extension Data {
    func hexString() -> String {
        var bytesPointer: UnsafeBufferPointer<UInt8> = UnsafeBufferPointer(start: nil, count: 0)
        self.withUnsafeBytes { (bytes) in
            bytesPointer = UnsafeBufferPointer<UInt8>(start: UnsafePointer(bytes), count:self.count)
        }
        let hexBytes = bytesPointer.map { return String(format: "%02hhx", $0) }
        return hexBytes.joined()
    }
}
taylor rapide
la source
1

J'ai essayé de tester deux méthodes différentes avec le format "%02.2hhx"et"%02x"

    var i :Int = 0
    var j: Int = 0
    let e: Int = Int(1e4)
    let time = NSDate.timeIntervalSinceReferenceDate
    while i < e {
        _ =  deviceToken.map { String(format: "%02x", $0) }.joined()
        i += 1
    }
    let time2 = NSDate.timeIntervalSinceReferenceDate
    let delta = time2-time
    print(delta)

    let time3 = NSDate.timeIntervalSinceReferenceDate
    while j < e {
        _ =  deviceToken.reduce("", {$0 + String(format: "%02x", $1)})
        j += 1
    }
    let time4 = NSDate.timeIntervalSinceReferenceDate
    let delta2 = time4-time3
    print(delta2)

et le résultat est que le plus rapide est "%02x"en moyenne 2,0 contre 2,6 pour la version réduite:

deviceToken.reduce("", {$0 + String(format: "%02x", $1)})
Nicolas Manzini
la source
1

Utilisation updateAccumulatingResult est plus efficace que les diverses autres approches trouvés ici, alors voici la Swiftiest façon de stringify vos Dataoctets:

func application(_ application: UIApplication,
                 didRegisterForRemoteNotificationsWithDeviceToken deviceToken: Data) {
    let token = deviceToken.reduce(into: "") { $0 += String(format: "%.2x", $1) }
    print(token)
}
Alex Curylo
la source
Alex, ne serait-ce pas% 02.2hhx
Fattie
0

Pour Swift:

var characterSet: NSCharacterSet = NSCharacterSet( charactersInString: "<>" )
    var deviceTokenString: String = ( deviceToken.description as NSString )
    .stringByTrimmingCharactersInSet( characterSet )
    .stringByReplacingOccurrencesOfString( " ", withString: "" ) as String

println( deviceTokenString )
Adarsh ​​GJ
la source
0

Qu'en est-il de la solution en une seule ligne?

Objectif c

NSString *token = [[data.description componentsSeparatedByCharactersInSet:[[NSCharacterSet alphanumericCharacterSet]invertedSet]]componentsJoinedByString:@""];

Rapide

let token = data.description.componentsSeparatedByCharactersInSet(NSCharacterSet.alphanumericCharacterSet().invertedSet).joinWithSeparator("")
Nikolay Shubenkov
la source
2
C'est la solution la plus simple et la meilleure. Merci
Emmy
0

Voici comment procéder dans Xamarin.iOS

public override void RegisteredForRemoteNotifications(UIApplication application, NSData deviceToken)
{
    var tokenStringBase64 = deviceToken.GetBase64EncodedString(NSDataBase64EncodingOptions.None);
    //now you can store it for later use in local storage
}
Papa ivre
la source
-1
NSString *tokenString = [[newDeviceToken description] stringByReplacingOccurrencesOfString:@"[<> ]" withString:@"" options:NSRegularExpressionSearch range:NSMakeRange(0, [[newDeviceToken description] length])];
Genja Grishin
la source
excellente solution À partir d'aujourd'hui, elle peut être impliquée dans credentials.token.description.replacingOccurrences (of: "[<>]", with: "", options: .regularExpression, range: nil)
Frank
-1

Rapide:

let tokenString = deviceToken.description.stringByReplacingOccurrencesOfString("[ <>]", withString: "", options: .RegularExpressionSearch, range: nil)
Tony
la source
-2
-(NSString *)deviceTokenWithData:(NSData *)data
{
    NSString *deviceToken = [[data description] stringByTrimmingCharactersInSet:[NSCharacterSet characterSetWithCharactersInString:@"<>"]];
    deviceToken = [deviceToken stringByReplacingOccurrencesOfString:@" " withString:@""];
    return deviceToken;
}
Mallikarjuna SB
la source
-2

Rapide

    // make sure that we have token for the devie on the App
    func application(application: UIApplication
        , didRegisterForRemoteNotificationsWithDeviceToken deviceToken: NSData) {

            var tokenStr = deviceToken.description
            tokenStr = tokenStr.stringByReplacingOccurrencesOfString("<", withString: "", options: [], range: nil)
            tokenStr = tokenStr.stringByReplacingOccurrencesOfString(">", withString: "", options: [], range: nil)
            tokenStr = tokenStr.stringByReplacingOccurrencesOfString(" ", withString: "", options: [], range: nil)



            print("my token is: \(tokenStr)")

    }
Vinod Joshi
la source
-2

Utilisez une excellente catégorie!

// fichier .h

@interface NSData (DeviceToken)

- (NSString *)stringDeviceToken;

@end    

// fichier .m

#import "NSData+DeviceToken.h"

@implementation NSData (DeviceToken)

- (NSString *)stringDeviceToken {
    const unsigned *deviceTokenBytes = [deviceToken bytes];
    NSString *deviceToken = [NSString stringWithFormat:@"%08x%08x%08x%08x%08x%08x%08x%08x",
                     ntohl(deviceTokenBytes[0]), ntohl(deviceTokenBytes[1]), ntohl(deviceTokenBytes[2]),
                     ntohl(deviceTokenBytes[3]), ntohl(deviceTokenBytes[4]), ntohl(deviceTokenBytes[5]),
                     ntohl(deviceTokenBytes[6]), ntohl(deviceTokenBytes[7])];
    return deviceToken;
}

@fin

// AppDelegate.m

#import "NSData+DeviceToken.h"

- (void)application:(UIApplication *)application didRegisterForRemoteNotificationsWithDeviceToken:(NSData *)deviceToken
{
    NSString *token = deviceToken.stringDeviceToken;
}

Fonctionne très bien!

LLIAJLbHOu
la source
Ne comptez pas sur l'utilisation de "description", son format pourrait changer à l'avenir. C'est uniquement à des fins d'affichage.
Michael Peterson
-3

Swift 3:

Si quelqu'un cherche un moyen d'obtenir un jeton d'appareil dans Swift 3. Utilisez l'extrait de code modifié ci-dessous.

    let characterSet: CharacterSet = CharacterSet( charactersIn: "<>" )

    let deviceTokenString: String = (deviceToken.description as NSString)
        .trimmingCharacters(in: characterSet as CharacterSet)
        .replacingOccurrences(of: " ", with: "")
        .uppercased()

    print(deviceTokenString)
Laksh Gandikota
la source
2
Je ne recommande pas d'utiliser .description car il n'est pas garanti que cela reste le même. Voir ma réponse ici: stackoverflow.com/questions/9372815/…
swift taylor
-4
var token: String = ""
for i in 0..<deviceToken.count {
    token += String(format: "%02.2hhx", deviceToken[i] as CVarArg)
}

print(token)
Abdul Yasin
la source
1
L'utilisation de la description n'est pas sûre car elle n'est pas garantie de donner les mêmes résultats à l'avenir.
Sahil Kapoor
-4

La solution @kulss postée ici, tout en manquant d'élégance mais ayant le mérite de la simplicité ne fonctionne plus sous iOS 13, puisqu'elle descriptionfonctionnera différemment pour NSData. Vous pouvez toujours l'utiliser debugDescription.

NSString * deviceTokenString = [[[[deviceToken debugDescription]
                     stringByReplacingOccurrencesOfString: @"<" withString: @""] 
                    stringByReplacingOccurrencesOfString: @">" withString: @""] 
                   stringByReplacingOccurrencesOfString: @" " withString: @""];
johnyu
la source
-7

Essayez celui-ci à moins que les données ne soient terminées par null.

NSString* newStr = [[NSString alloc] initWithData:newDeviceToken encoding:NSUTF8StringEncoding];

Naveed Ahmad
la source
J'ai essayé celui-là, ça ne marche pas. Je l'ai commenté dans mon extrait de code.
Sheehan Alam
@SheehanAlam Ce type a réussi. Regardez comment il se convertit en chaîne. stackoverflow.com/questions/4994302/…
Naveed Ahmad
-9
NSString *tokenstring = [[NSString alloc] initWithData:token encoding:NSUTF8StringEncoding];
Ravikant
la source
Cela fonctionne lorsque les données sont une chaîne, cependant, le deviceToken n'est pas une chaîne.
Simon Epskamp