Come posso convertire il token del mio dispositivo (NSData) in una NSString?


157

Sto implementando le notifiche push. Vorrei salvare il mio token APNS come stringa.

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

La prima riga di codice stampa null. il secondo stampa il token. Come posso ottenere il mio newDeviceToken come NSString?


Qual è l'output del secondo NSLog, quello che stampa newDeviceToken?
Rob Mayoff,


NON usare la descrizione
Fattie

Risposte:


39

Usa questo :

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

NSLog(@"The generated device token string is : %@",deviceTokenString);

134
Sembra una cattiva idea usare la descrizione: nulla garantisce che la versione successiva di iOS non cambierà l'implementazione e il risultato di questa chiamata.
madewulf

16
Anzi, questa è una pessima idea.
David Snabel-Caunt,

21
@madewulf è stato molto gentile da parte tua sottolineare come sia una terribile idea usare la descrizione .. sarebbe stato ancora più bello se tu avessi suggerito un'alternativa
circa il

6
La soluzione qui sotto con [deviceToken byte] si adatta al conto.
madewulf

37
Risulta da Swift 3 / iOS 10, .description su un token dispositivo restituisce "32 byte". Quindi sì, non usarlo.
Victor Luft,

231

Se qualcuno è alla ricerca di un modo per farlo in 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)")
}

Modifica: per Swift 3

Swift 3 introduce il Datatipo, con semantica di valore. Per convertire la deviceTokenstringa in una stringa, puoi fare come segue:

func application(_ application: UIApplication, didRegisterForRemoteNotificationsWithDeviceToken deviceToken: Data) {
    let token = deviceToken.map { String(format: "%02.2hhx", $0) }.joined()
    print(token)
}

118
Perché questo deve essere così complicato, cosa c'è di sbagliato nel fatto che il sistema operativo ci dia una stringa poiché questo è ciò di cui tutti hanno bisogno? Grazie per questa soluzione
Piwaf,

3
@Sascha Spero che approvi la mia modifica alla tua utilissima risposta :)
jrturton,

16
Ho refactored: let token = deviceToken.map { String(format: "%02.2hhx", $0) }.joined() qiita.com/mono0926/items/3cf0dca3029f32f54a09
mono

2
Non consiglio di usare .description in quanto non è garantito che sia stabile. Scopri la mia risposta qui: stackoverflow.com/questions/9372815/…
swift taylor,

7
Puoi spiegare cosa fa "%02.2hhx?
Miele

155

Qualcuno mi ha aiutato in questo. Sto solo passando

- (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];
}

5
Questa è la soluzione migliore, poiché encondig byte come esadecimale, implica che puoi
contarlo

4
Su XCode 5 ho dovuto trasmettere il deviceToken per farlo compilare: const unsigned * tokenBytes = (const unsigned *) [deviceToken bytes];
Ponytech,

3
I token saranno presto più grandi di 32 byte, quindi questo dovrà essere un ciclo su ogni byte, anziché otto numeri interi codificati.
Tom Dalling,

5
Questa sarebbe una soluzione migliore? 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.Dice Apple.
Erkanyildiz,

141

Potresti usare questo

- (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];
}

11
Questa dovrebbe essere la risposta accettata, poiché è molto più sicura dell'uso description.
DrMickeyLauer,

8
Questa è l'unica risposta corretta in Objective-C che gestirà il prossimo aumento delle dimensioni del token.
Tom Dalling,

Convenuto che questo è probabilmente il modo più sicuro in quanto non assume alcuna dimensione / lunghezza del token particolare.
Ryan H.

Funziona su iOS 10.
Tjalsma,

2
Ho usato [token appendFormat:@"%02.2hhx", data[i]];come Amazon SNS richiede lettere minuscole.
Manuel Schmitzberger,

43

Per chi desidera Swift 3 e il metodo più semplice

func extractTokenFromData(deviceToken:Data) -> String {
    let token = deviceToken.reduce("", {$0 + String(format: "%02X", $1)})
    return token.uppercased();
}

1
Ho scritto lo stesso codice :) Questa è la versione più veloce e funziona solo
Quver

1
@Se, per favore, puoi spiegare cosa sta succedendo in questo codicedeviceToken.reduce("", {$0 + String(format: "%02X", $1)})
Ramakrishna,

1
Utilizza la funzione di riduzione di swift che serializza i dati in stringa esadecimale e quindi in stringa. Per saperne di più sulla funzione di riduzione leggi useyourloaf.com/blog/swift-guide-to-map-filter-reduce
Anand

15

Spiegazione della risposta%02.2hhx in alto voto :

  • %: Introduce l' xidentificatore di conversione.
  • 02: La larghezza minima del valore convertito è 2. Se il valore convertito ha meno byte della larghezza del campo, deve essere riempito con 0a sinistra.
  • .2: Fornisce il numero minimo di cifre da visualizzare per l' xidentificatore di conversione.
  • hh: Specifica che l' xidentificatore di conversione si applica a un carattere con segno o un argomento con carattere senza segno (l'argomento sarà stato promosso in base alle promozioni con numeri interi, ma il suo valore deve essere convertito in carattere con segno o carattere senza segno prima della stampa).
  • x: L'argomento unsigned deve essere convertito in formato esadecimale unsigned nello stile "dddd"; vengono utilizzate le lettere "abcdef". La precisione specifica il numero minimo di cifre da visualizzare; se il valore convertito può essere rappresentato in meno cifre, deve essere espanso con zeri iniziali. La precisione predefinita è 1. Il risultato della conversione di zero con una precisione esplicita di zero non deve contenere caratteri.

Per ulteriori dettagli, consultare la specifica IEEE printf .


Sulla base della spiegazione sopra, penso che sia meglio cambiare %02.2hhxin %02xo%.2x .

Per Swift 5, sono possibili tutti i seguenti metodi:

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)})

Il test è il seguente:

let deviceToken = (0..<32).reduce(Data(), {$0 + [$1]})
print(deviceToken.reduce("", {$0 + String(format: "%.2x", $1)}))
// Print content:
// 000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f

Grazie per questa risposta Funziona anche con iOS 12? O dipende solo dalla versione Swift?
Markus

1
@Markus Funziona su iOS 12, dipende solo dalla versione Swift.
jqgsninimo,

14

È la mia soluzione e funziona bene nella mia app:

    NSString* newToken = [[[NSString stringWithFormat:@"%@",deviceToken] 
stringByTrimmingCharactersInSet:[NSCharacterSet characterSetWithCharactersInString:@"<>"]] stringByReplacingOccurrencesOfString:@" " withString:@""];
  • converti NSDatain NSStringconstringWithFormat
  • tagliare "<>"
  • rimuovere gli spazi

10
Questo chiama implicitamente -description, quindi non è più sicuro della risposta accettata.
jszumski,

Puoi per favore collegare la tua fonte? Non riesco a trovare informazioni al riguardo da nessuna parte. Grazie.
Zeb,

Trovato! Penso che sia un po 'diverso. Utilizzare direttamente l'attributo description non è sicuro perché potrebbe cambiare nelle versioni future, ma se lo si utilizza ATTRAVERSO un metodo NSString difficilmente si avranno problemi.
Zeb,

5
No, questo richiede davvero descriptionil dispositivo Preso come dice jszumski.
Jonny

1
@Zeb Non è sicuro fare affidamento sul descriptionfatto che tu lo chiami direttamente o utilizzalo con un altro metodo, perché il formato della stringa restituita potrebbe essere modificato in qualsiasi momento. La soluzione corretta è qui: stackoverflow.com/a/16411517/108105
Tom Dalling,

10

Penso che la conversione di deviceToken in hex byte string non abbia senso. Perché? Lo invierai al tuo back-end, dove verrà trasformato nuovamente in byte per essere trasferito in APNS. Quindi, usa il metodo NSData , invialobase64EncodedStringWithOptions al server e quindi usa i dati decodificati base64 inversa :) È molto più semplice :)

NSString *tokenString = [tokenData base64EncodedStringWithOptions:NSDataBase64EncodingEndLineWithLineFeed];

@ jeet.chanchawat, per favore non aggiungere codice alle risposte degli altri utenti. Non vogliamo mettere le parole in bocca, specialmente quando si aggiunge Swift a una risposta Objective-C. Aggiungi invece la tua risposta.
JAL

2
Non volevo solo plagiare la risposta di @Oleg Shanyuk. Dato che è solo la traduzione in un'altra lingua costruita sulla sua risposta, quindi merita il futuro dei voti. Se aggiungo un'altra risposta mi darà voti per la risposta che è la ricerca di qualcun altro. Spero che ciò giustifichi l'EDIT.
jeet.chanchawat,

10

In iOS 13 descriptionsi romperà quindi usa questo

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

Per chiarezza, analizziamo questo aspetto e spieghiamo ogni parte:

Il metodo map funziona su ogni elemento di una sequenza. Poiché Data è una sequenza di byte in Swift, la chiusura passata viene valutata per ogni byte in deviceToken. L'inizializzatore String (formato :) valuta ogni byte nei dati (rappresentato dal parametro anonimo $ 0) utilizzando l'identificatore di formato% 02x, per produrre una rappresentazione esadecimale a 2 cifre con spaziatura zero dell'intero byte / 8 bit. Dopo aver raccolto ogni rappresentazione di byte creata dal metodo map, join () concatena ogni elemento in una singola stringa.

PS non usa la descrizione fornisce una stringa diversa in iOS 12 e iOS 13 e non è sicura come per ambito futuro. Gli sviluppatori non avrebbero dovuto fare affidamento su un formato specifico per la descrizione di un oggetto.

// 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 }"

Per maggiori informazioni leggi questo .


10

In iOS 13 la descrizione sarà in un formato diverso. Si prega di utilizzare il codice seguente per recuperare il token del dispositivo.

- (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];
}

Soluzione perfetta per iOS 13. Grazie Vishnu
Manish,

1
Attualmente non viene compilato: lengthnel ciclo for dovrebbe essere modificato in len. Apparentemente una modifica troppo piccola per me per fare una modifica .. Ma altrimenti funziona perfettamente!
Anders Friis,

sei un
salvavita

3

Questa è una soluzione un po 'più breve:

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])];

3

Versione funzionale di Swift

Una fodera:

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

Ecco un modulo di estensione riutilizzabile e auto-documentante:

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("")
    }
}

In alternativa, usa reduce("", combine: +)invece di joinWithSeparator("")essere visto come un maestro funzionale dai tuoi colleghi.


Modifica: ho cambiato String ($ 0, radix: 16) in String (formato: "% 02x", $ 0), perché i numeri di una cifra dovevano avere uno zero di riempimento

(Non so ancora come contrassegnare una domanda come duplicata di quest'altra , quindi ho postato di nuovo la mia risposta)


Funziona per me, grazie.
Hasya,

3

2020

token come testo ...

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

o se preferisci

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

(il risultato è lo stesso)


2

Lancio della mia risposta sulla pila. Evitare di utilizzare l'analisi delle stringhe; Non è garantito dai documenti che NSData.description funzionerà sempre in questo modo.

Implementazione di 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()
    }
}

1

Ho provato a testare due diversi metodi con formato "%02.2hhx"e"%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)

e il risultato è che il più veloce è "%02x"in media 2,0 vs 2,6 per la versione ridotta:

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

1

Utilizzando updateAccumulatingResult è più efficiente di vari altri approcci trovato qui, quindi ecco il Swiftiest modo di stringa i vostri Databyte:

func application(_ application: UIApplication,
                 didRegisterForRemoteNotificationsWithDeviceToken deviceToken: Data) {
    let token = deviceToken.reduce(into: "") { $0 += String(format: "%.2x", $1) }
    print(token)
}

Alex, non sarebbe% 02.2hhx
Fattie il

0

Per Swift:

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

println( deviceTokenString )

0

Che dire di una soluzione line?

Obiettivo C

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

veloce

let token = data.description.componentsSeparatedByCharactersInSet(NSCharacterSet.alphanumericCharacterSet().invertedSet).joinWithSeparator("")

2
Questa è la soluzione semplice e migliore. Grazie
Emmy il

0

Ecco come lo fai in 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
}

-1
NSString *tokenString = [[newDeviceToken description] stringByReplacingOccurrencesOfString:@"[<> ]" withString:@"" options:NSRegularExpressionSearch range:NSMakeRange(0, [[newDeviceToken description] length])];

ottima soluzione Ad oggi, può essere implementato in credentials.token.description.replacingOccurrences (di: "[<>]", con: "", opzioni: .regularExpression, intervallo: zero)
Frank

-1

Swift:

let tokenString = deviceToken.description.stringByReplacingOccurrencesOfString("[ <>]", withString: "", options: .RegularExpressionSearch, range: nil)

-2
-(NSString *)deviceTokenWithData:(NSData *)data
{
    NSString *deviceToken = [[data description] stringByTrimmingCharactersInSet:[NSCharacterSet characterSetWithCharactersInString:@"<>"]];
    deviceToken = [deviceToken stringByReplacingOccurrencesOfString:@" " withString:@""];
    return deviceToken;
}

-2

veloce

    // 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)")

    }

-2

Usa una categoria eccellente!

// .h file

@interface NSData (DeviceToken)

- (NSString *)stringDeviceToken;

@end    

// .m file

#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;
}

@fine

// AppDelegate.m

#import "NSData+DeviceToken.h"

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

Funziona bene!


Non fare affidamento sull'uso della "descrizione", il suo formato potrebbe cambiare in futuro. È solo a scopo di visualizzazione.
Michael Peterson,

-3

Swift 3:

Se qualcuno è alla ricerca di un modo per ottenere il token del dispositivo in Swift 3. Usa lo snippet modificato di seguito.

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

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

    print(deviceTokenString)

2
Non consiglio di usare .description in quanto non è garantito che rimanga lo stesso. Vedi la mia risposta qui: 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)

1
L'uso della descrizione non è sicuro in quanto non è garantito che fornisca gli stessi risultati in futuro.
Sahil Kapoor,

-4

La soluzione @kulss pubblicata qui, pur mancando di eleganza ma avendo la virtù della semplicità non funziona più in iOS 13, poiché descriptionfunzionerà in modo diverso per NSData. Puoi comunque usare debugDescriptioncomunque.

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


-9
NSString *tokenstring = [[NSString alloc] initWithData:token encoding:NSUTF8StringEncoding];

Funziona quando i dati sono una stringa, tuttavia deviceToken non è una stringa.
Simon Epskamp,
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.