How can I convert my device token (NSData) into an NSString?

IosObjective CApple Push-NotificationsNsstringNsdata

Ios Problem Overview


I am implementing push notifications. I'd like to save my APNS Token as a String.

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

The first line of code prints null. the second prints the token. How can I get my newDeviceToken as an NSString?

Ios Solutions


Solution 1 - Ios

If anyone is looking for a way to do this in Swift:

Swift 3 introduces the Data type, with value semantics. To convert the deviceToken to a String, you can do as follows:

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

Old answer using NSData:

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

Solution 2 - Ios

Someone Helped me with this.I am just passing along

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

Solution 3 - Ios

You could use this

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

Solution 4 - Ios

For those who want in Swift 3 and most easier method

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

Solution 5 - Ios

Note - This will not work when compiling with the iOS 13 or later SDK

use this :

NSString * deviceTokenString = [[[[deviceToken description]
    					 stringByReplacingOccurrencesOfString: @"<" withString: @""] 
    					stringByReplacingOccurrencesOfString: @">" withString: @""] 
    				   stringByReplacingOccurrencesOfString: @" " withString: @""];
    	
NSLog(@"The generated device token string is : %@",deviceTokenString);

Solution 6 - Ios

Explanation of %02.2hhx in the high vote answer:

  • %: Introduces the x conversion specifier.
  • 02: The minimum width of the converted value is 2. If the converted value has fewer bytes than the field width, it shall be padded with 0 on the left.
  • .2: Gives the minimum number of digits to appear for the x conversion specifier.
  • hh: Specifies that the x conversion specifier applies to a signed char or unsigned char argument (the argument will have been promoted according to the integer promotions, but its value shall be converted to signed char or unsigned char before printing).
  • x: The unsigned argument shall be converted to unsigned hexadecimal format in the style "dddd"; the letters "abcdef" are used. The precision specifies the minimum number of digits to appear; if the value being converted can be represented in fewer digits, it shall be expanded with leading zeros. The default precision is 1. The result of converting zero with an explicit precision of zero shall be no characters.

For more details, see the IEEE printf specification.


Based on the above explanation, I think it is better to change %02.2hhx to %02x or %.2x.

For Swift 5, the following methods are all feasible:

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

The test is as follows:

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

Solution 7 - Ios

In iOS 13 the description will be in different format. Kindly use below code to fetch the device token.

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

Solution 8 - Ios

It's my solution and It works well in my app:

    NSString* newToken = [[[NSString stringWithFormat:@"%@",deviceToken] 
stringByTrimmingCharactersInSet:[NSCharacterSet characterSetWithCharactersInString:@"<>"]] stringByReplacingOccurrencesOfString:@" " withString:@""];
  • convert NSData to NSString with stringWithFormat
  • trim the "<>"
  • remove the spaces

Solution 9 - Ios

In iOS 13 description will break so use this

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

For clarity, let’s break this down and explain each part:

The map method operates on each element of a sequence. Because Data is a sequence of bytes in Swift, the passed closure is evaluated for each byte in deviceToken. The String(format:) initializer evaluates each byte in the data (represented by the anonymous parameter $0) using the %02x format specifier, to produce a zero-padded, 2-digit hexadecimal representation of the byte / 8-bit integer. After collecting each byte representation created by the map method, joined() concatenates each element into a single string.

P.S don't use description gives different string in iOS 12 and iOS 13 and not safe as per future scope. Developers shouldn’t have relied on a specific format for an object’s description.

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

For more information read This.

Solution 10 - Ios

I think converting deviceToken to hex byte string has no sense. Why? You will send it to your backend, where it will be transformed back to bytes to be pushed to APNS. So, use NSData's method base64EncodedStringWithOptions, push it to server, and then use reverse base64decoded data :) That is so much easier :)

NSString *tokenString = [tokenData base64EncodedStringWithOptions:NSDataBase64EncodingEndLineWithLineFeed];

Solution 11 - Ios

2020

token as text...

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

or if you prefer

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

(result is the same)

Solution 12 - Ios

This is a little bit shorter solution:

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

Solution 13 - Ios

Functional Swift version

One liner:

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

Here's in a reusable and self documenting extension form:

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

Alternatively, use reduce("", combine: +) instead of joinWithSeparator("") to be seen as a functional master by your peers.


Edit: I changed String($0, radix: 16) to String(format: "%02x", $0), because one digit numbers needed to having a padding zero

(I don't know yet how to mark a question as a duplicate of this other one, so I just posted my answer again)

Solution 14 - Ios

Throwing my answer on the pile. Avoid using string parsing; It's not guaranteed by the docs that NSData.description will always work that way.

Swift 3 Implementation:

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

Solution 15 - Ios

This will work for you,

NSUInteger dataLength = deviceToken.length;
    
const unsigned char *dataBuffer = (const unsigned char *)deviceToken.bytes;
NSMutableString *deviceTokenString = [NSMutableString stringWithCapacity:(dataLength * 2)];
for (int i = 0; i < dataLength; ++i) {
    [deviceTokenString appendFormat:@"%02x", dataBuffer[i]];
}
    
NSLog(@"The generated device token string is : %@",deviceTokenString);

Solution 16 - Ios

I've tried to test two different methods with format "%02.2hhx" and "%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)

and the result is that the fastest is "%02x" at average 2.0 vs 2.6 for the reduced version:

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

Solution 17 - Ios

Here's how you do it 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
}

Solution 18 - Ios

Using updateAccumulatingResult is more efficient than the various other approaches found here, so here's the Swiftiest way to stringify your Data bytes:

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

Solution 19 - Ios

For Swift :

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

println( deviceTokenString )

Solution 20 - Ios

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

Solution 21 - Ios

Swift:

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

Solution 22 - Ios

What about one line solution?

Objective C

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

Swift

let token = data.description.components(separatedBy: CharacterSet.alphanumerics.inverted).joined()

Solution 23 - Ios

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

Solution 24 - Ios

Swift

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

Solution 25 - Ios

Use excellent category!

// .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;
}

@end

// AppDelegate.m

#import "NSData+DeviceToken.h"

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

Works fine!

Solution 26 - Ios

Swift 3:

If any one is looking for a way to get device token in Swift 3. Use the below modified snippet.

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

Solution 27 - Ios

var token: String = ""
for i in 0..<deviceToken.count {
    token += String(format: "%02.2hhx", deviceToken[i] as CVarArg)
}

print(token)

Solution 28 - Ios

The solution @kulss posted here, while lacking in elegance but having the virtue of simplicity no longer works in iOS 13, since description will work differently for NSData. You can still use debugDescription though.

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

Solution 29 - Ios

Try this one unless the data is null-terminated.

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

Solution 30 - Ios

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

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestionSheehan AlamView Question on Stackoverflow
Solution 1 - IosSaschaView Answer on Stackoverflow
Solution 2 - IosShubhankView Answer on Stackoverflow
Solution 3 - IosVlad PolyanskiyView Answer on Stackoverflow
Solution 4 - IosAnandView Answer on Stackoverflow
Solution 5 - IoskulssView Answer on Stackoverflow
Solution 6 - IosjqgsninimoView Answer on Stackoverflow
Solution 7 - IosVishnu PrakashView Answer on Stackoverflow
Solution 8 - IosZebView Answer on Stackoverflow
Solution 9 - IosSuryaKantSharmaView Answer on Stackoverflow
Solution 10 - IosOleg ShanyukView Answer on Stackoverflow
Solution 11 - IosFattieView Answer on Stackoverflow
Solution 12 - Iosk06aView Answer on Stackoverflow
Solution 13 - IosNiñoScriptView Answer on Stackoverflow
Solution 14 - Iosswift taylorView Answer on Stackoverflow
Solution 15 - Iosmanishsharma93View Answer on Stackoverflow
Solution 16 - IosNicolas ManziniView Answer on Stackoverflow
Solution 17 - IosDrunken DaddyView Answer on Stackoverflow
Solution 18 - IosAlex CuryloView Answer on Stackoverflow
Solution 19 - IosAdarsh G JView Answer on Stackoverflow
Solution 20 - IosGenja GrishinView Answer on Stackoverflow
Solution 21 - IosTonyView Answer on Stackoverflow
Solution 22 - IosNikolay ShubenkovView Answer on Stackoverflow
Solution 23 - IosMallikarjuna SBView Answer on Stackoverflow
Solution 24 - IosVinod JoshiView Answer on Stackoverflow
Solution 25 - IosLLIAJLbHOuView Answer on Stackoverflow
Solution 26 - IosLaksh GandikotaView Answer on Stackoverflow
Solution 27 - IosAbdul YasinView Answer on Stackoverflow
Solution 28 - IosjohnyuView Answer on Stackoverflow
Solution 29 - IosNaveed AhmadView Answer on Stackoverflow
Solution 30 - IosRavikantView Answer on Stackoverflow