NSData description and NSString stringWithFormat have different return results when compiled with Xcode 11 versus Xcode 10

All the examples I've seen of using pushes use [NSData description] to obtain the push token in order to send to the server.

However when an app is built with XCode 11 the result of this is different than when built with Xcode 10, similarly for [NSString stringWithFormat].



Example:



    NSData* theToken = ...
    ....
    NSString* stringWithFormat = [NSString stringWithFormat"@"%@", theToken];
    NSString* description = [theToken description];


When compiled with Xcode 10 and run, the results for both are :



    @"<44154da7 32345001 53106883 ffc1071f a59c0d24 a70871e5 aa8dbb41>"



However when compiled with Xcode 11 and run the results are:



    @"{length =32, bytes = 0x44154da7 32345001 53106883 ffc1071f ... a70871e5 aa8dbb41}"



(This latter result does not occur if the code is compiled with Xcode 10 and then run on iOS 13.)



How can you convert the NSData to NSString when compiled with Xcode 11 that will give the same results as XCode10, or how can you extract just the bytes section into an NSString from the return result if compiled with Xcode 10?

Replies

Format specifier %@ (in stringWithFormat), by definition, uses the "description" method on the object to get the text to insert, so both of your techniques are basically the same.


Using "description" for getting your token as a string was *always* the wrong thing to do. There is no rule about how objects must format their descriptions, and the descriptions are subject to change at any time (as you've seen).


What you should actually do is convert the data byte by byte to hexadecimal characters, and concatenate those into a single string. I don't think there's any Objective-C API for this, so you'll have to write the code yourself, or search GitHub (or elsewhere) for code that does it.

1) why can't you send the NSData object to your server?

2) try (I am not sure what 'encoding' will work 'correctly' - but just convert back to NSData using that same encoding)

   NSString *theTokenString=[[NSString alloc] initWithBytes:[theToken bytes] length:[theToken length] encoding:NSUTF8StringEncoding];

What you should actually do is convert the data byte by byte to hexadecimal characters, and concatenate those into a single string.

Alternatively, you could use Base64, which is supported by

NSData
and widely supported on the server side.

Share and Enjoy

Quinn “The Eskimo!”
Apple Developer Relations, Developer Technical Support, Core OS/Hardware

let myEmail = "eskimo" + "1" + "@apple.com"

I found an interesting phenomenon:

Get `deviceToken` from `func application(_ application: UIApplication, didRegisterForRemoteNotificationsWithDeviceToken deviceToken: Data)`, if you generate an `NSData` again via `NSData(data: deviceToken)` then you will find the following print

```lldb

po deviceToken.description

"32 bytes"

po NSData.init(data: deviceToken)

<4133c878 9dcb6fd7 0265b6ea ac8198f3 3eba2b7a e7df1b4b 912a1f0b f5993b60>

```

I found an interesting phenomenon:

Right. The description for

NSData
contains a hex dump where as the description for Swift’s
Data
type just prints the count:
$ swift
…
  1> import Foundation
  2> let swiftData = Data("Hello Cruel World!".utf8) 
…
  3> print(swiftData)
18 bytes
  4> print(swiftData as NSData)
<48656c6c 6f204372 75656c20 576f726c 6421>

Regardless, the point QuinceyMorris was making is key: You should never rely on the exact format returned by the

description
property. It’s meant for human (well, developer:-) consumption and is not stable over time.

Share and Enjoy

Quinn “The Eskimo!”
Apple Developer Relations, Developer Technical Support, Core OS/Hardware

let myEmail = "eskimo" + "1" + "@apple.com"

I am just coming across this post now after hoping for an update on Apple's side. Can you add this update to the Release Notes so other developers are made aware of this change more directly?

Can you add this update to the Release Notes so other developers are made aware of this change more directly?

That sounds like a great idea to me. To get it in front of folks who can actually make the change, you should file a bug against the release notes.

Please post your bug number, just for the record.

Share and Enjoy

Quinn “The Eskimo!”
Apple Developer Relations, Developer Technical Support, Core OS/Hardware

let myEmail = "eskimo" + "1" + "@apple.com"

Hi.


I know this isn't the best answer but I think the answer you are looking for is to use debugDescription.

I had the same issue as you did.

In previous versions of xCode I would simply print out my NSData and would receive :

44154da7 32345001 53106883 ffc1071f a59c0d24 a70871e5 aa8dbb41


after the update however I now also receive :

{length =32, bytes = 0x44154da7 32345001 53106883 ffc1071f ... a70871e5 aa8dbb41}


so I tried using .description and got the same result and tried using .debugDescription and I got what I was looking for. just the full hex value.


If anybody has a better explination or answer please advise but I use this simply to double check some data and only to print to the log, I dont use it in the application per say.

- (NSString *)convertDataToHexStr:(NSData *)data
{
if (!data || [data length] == 0) {
return @"";
}
NSMutableString *string = [[NSMutableString alloc] initWithCapacity:[data length]];

[data enumerateByteRangesUsingBlock:^(const void *bytes, NSRange byteRange, BOOL *stop) {
unsigned char *dataBytes = (unsigned char*)bytes;
for (NSInteger i = 0; i < byteRange.length; i++) {
NSString *hexStr = [NSString stringWithFormat:@"%x", (dataBytes[i]) & 0xff];
if ([hexStr length] == 2) {
[string appendString:hexStr];
} else {
[string appendFormat:@"0%@", hexStr];
}
}
}];
return string;
}

this works.

FYI, you can make this code simpler and faster with:

[string appendFormat:@"%02x", dataBytes[i]];

Also, if you’re going to bother with

-initWithCapacity:
, you should get the capacity right, that is, pass in
[data length] * 2
.

Share and Enjoy

Quinn “The Eskimo!”
Apple Developer Relations, Developer Technical Support, Core OS/Hardware

let myEmail = "eskimo" + "1" + "@apple.com"

Thanks for this info. this resolved mt issue.

Tip: If you are running your own PHP "provider" server, don't worry about converting it to a string on the device. Just send the token as binary data to your registration endpoint and convert it to a string using:

$tokenString = bin2hex($tokenData);

Store it in your database as a string. This makes sense because, later when you want to send a push notification, you will be using the bin2hex string version in the url anyway.

$pushURL = "https://api.development.push.apple.com/3/device/$tokenString";