简体   繁体   English

如何将我的设备令牌 (NSData) 转换为 NSString?

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

I am implementing push notifications.我正在实施推送通知。 I'd like to save my APNS Token as a String.我想将我的 APNS 令牌保存为字符串。

- (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.第一行代码打印 null。 the second prints the token.第二个打印令牌。 How can I get my newDeviceToken as an NSString?如何将我的 newDeviceToken 作为 NSString 获取?

If anyone is looking for a way to do this in Swift:如果有人正在寻找一种在 Swift 中执行此操作的方法:

Swift 3 introduces the Data type, with value semantics. Swift 3 引入了具有值语义的Data类型。 To convert the deviceToken to a String, you can do as follows:要将deviceToken转换为 String,您可以执行以下操作:

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

Old answer using NSData :使用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)")
}

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

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

For those who want in Swift 3 and most easier method对于那些想要Swift 3和最简单方法的人

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

Note - This will not work when compiling with the iOS 13 or later SDK注意 - 这在使用 iOS 13 或更高版本 SDK 编译时不起作用

use this :用这个 :

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

Explanation of %02.2hhx in the high vote answer :高票答案%02.2hhx解释:

  • % : Introduces the x conversion specifier. % :引入x转换说明符。
  • 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. 02 :转换值的最小宽度为2。如果转换值的字节数少于字段宽度,则应在左侧填充0
  • .2 : Gives the minimum number of digits to appear for the x conversion specifier. .2 :给出x转换说明符出现的最小位数。
  • 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). hh :指定x转换说明符适用于有符号字符或无符号字符参数(参数将根据整数提升进行提升,但其值应在打印前转换为有符号字符或无符号字符)。
  • x : The unsigned argument shall be converted to unsigned hexadecimal format in the style "dddd"; x : unsigned 参数应转换为样式“dddd”的无符号十六进制格式; the letters "abcdef" are used.使用字母“abcdef”。 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.默认精度为 1。使用显式精度零转换零的结果不应是字符。

For more details, see the IEEE printf specification .有关更多详细信息,请参阅IEEE printf 规范


Based on the above explanation, I think it is better to change %02.2hhx to %02x or %.2x .基于上面的解释,我认为最好将%02.2hhx更改为%02x%.2x

For Swift 5, the following methods are all feasible:对于Swift 5,以下方法都是可行的:

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

In iOS 13 the description will be in different format.在 iOS 13 中,描述将采用不同的格式。 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];
}

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使用stringWithFormatNSData转换为NSString
  • trim the "<>"修剪“<>”
  • remove the spaces删除空格

In iOS 13 description will break so use this在 iOS 13 中, description会中断,所以使用这个

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. map 方法对序列的每个元素进行操作。 Because Data is a sequence of bytes in Swift, the passed closure is evaluated for each byte in deviceToken.因为 Data 是 Swift 中的字节序列,所以传递的闭包会针对 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. String(format:) 初始值设定项使用 %02x 格式说明符评估数据中的每个字节(由匿名参数 $0 表示),以生成字节/8 位整数的零填充、2 位十六进制表示。 After collecting each byte representation created by the map method, joined() concatenates each element into a single string.在收集 map 方法创建的每个字节表示之后,joined() 将每个元素连接成一个字符串。

PS don't use description gives different string in iOS 12 and iOS 13 and not safe as per future scope. PS 不要使用描述在 iOS 12 和 iOS 13 中给出不同的字符串,并且根据未来的范围不安全。 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 .欲了解更多信息,请阅读本文

I think converting deviceToken to hex byte string has no sense.我认为将 deviceToken 转换为十六进制字节字符串没有意义。 Why?为什么? You will send it to your backend, where it will be transformed back to bytes to be pushed to APNS.您将把它发送到您的后端,在那里它将被转换回字节以推送到 APNS。 So, use NSData 's method base64EncodedStringWithOptions , push it to server, and then use reverse base64decoded data :) That is so much easier :)因此,使用NSData的方法base64EncodedStringWithOptions ,将其推送到服务器,然后使用反向 base64decoded 数据:) 这要容易得多:)

NSString *tokenString = [tokenData base64EncodedStringWithOptions:NSDataBase64EncodingEndLineWithLineFeed];

2020 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) (结果是一样的)

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

Functional Swift version函数式 Swift 版本

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.或者,使用reduce("", combine: +)而不是joinWithSeparator("")被同行视为功能大师。


Edit: I changed String($0, radix: 16) to String(format: "%02x", $0), because one digit numbers needed to having a padding zero编辑:我将 String($0, radix: 16) 更改为 String(format: "%02x", $0),因为一位数字需要填充零

(I don't know yet how to mark a question as a duplicate of this other one , so I just posted my answer again) (我还不知道如何标记一个问题为重复这个另外一个,所以我只是张贴了我的答案再次)

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.文档不保证 NSData.description 将始终以这种方式工作。

Swift 3 Implementation: 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()
    }
}

I've tried to test two different methods with format "%02.2hhx" and "%02x"我试图用格式"%02.2hhx""%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:结果是最快的是"%02x" ,平均为 2.0 对简化版本的 2.6:

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

Here's how you do it in Xamarin.iOS这是在 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
}

Using updateAccumulatingResult is more efficient than the various other approaches found here, so here's the Swiftiest way to stringify your Data bytes:使用updateAccumulatingResult比此处找到的各种其他方法更有效,因此这是将Data字节字符串化的最快捷方式:

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

For Swift :对于斯威夫特:

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

println( deviceTokenString )

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

迅速:

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

What about one line solution?单线解决方案怎么样?

Objective C目标 C

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

Swift迅速

let token = data.description.components(separatedBy: CharacterSet.alphanumerics.inverted).joined()
-(NSString *)deviceTokenWithData:(NSData *)data
{
    NSString *deviceToken = [[data description] stringByTrimmingCharactersInSet:[NSCharacterSet characterSetWithCharactersInString:@"<>"]];
    deviceToken = [deviceToken stringByReplacingOccurrencesOfString:@" " withString:@""];
    return deviceToken;
}

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

    }

Use excellent category!使用优秀的类别!

// .h file //.h文件

@interface NSData (DeviceToken)

- (NSString *)stringDeviceToken;

@end    

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

@end @结尾

// AppDelegate.m // AppDelegate.m

#import "NSData+DeviceToken.h"

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

Works fine!工作正常!

Swift 3:斯威夫特 3:

If any one is looking for a way to get device token in Swift 3. Use the below modified snippet.如果有人正在寻找一种在 Swift 3 中获取设备令牌的方法。请使用以下修改后的代码段。

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

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

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

print(token)

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. @kulss 在这里发布的解决方案虽然缺乏优雅但具有简单性的优点在 iOS 13 中不再适用,因为description对于 NSData 的工作方式有所不同。 You can still use debugDescription though.不过,您仍然可以使用debugDescription

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

除非数据以空值结尾,否则试试这个。

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

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

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM