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

时间:2012-02-21 05:48:39

标签: iphone objective-c cocoa-touch nsstring nsdata

我正在实施推送通知。我想将我的APNS令牌保存为字符串。

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

第一行代码打印为null。第二个打印令牌。如何将newDeviceToken作为NSString?

30 个答案:

答案 0 :(得分:199)

如果有人想在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)")
}

编辑:对于Swift 3

Swift 3引入了Data类型,带有值语义。要将deviceToken转换为String,您可以执行以下操作:

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

答案 1 :(得分:152)

有人帮助了我。我只是在路过

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

答案 2 :(得分:99)

你可以用这个

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

答案 3 :(得分:67)

使用它:

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

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

答案 4 :(得分:37)

对于那些想要 Swift 3 和最简单方法的人

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

答案 5 :(得分:16)

这是我的解决方案,它在我的应用程序中运行良好:

    NSString* newToken = [[[NSString stringWithFormat:@"%@",deviceToken] 
stringByTrimmingCharactersInSet:[NSCharacterSet characterSetWithCharactersInString:@"<>"]] stringByReplacingOccurrencesOfString:@" " withString:@""];
  • 使用NSData
  • NSString转换为stringWithFormat
  • 修剪“&lt;&gt;”
  • 删除空格

答案 6 :(得分:8)

在iOS 13 description中会损坏,因此请使用此

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

为清楚起见,我们将其分解并解释每个部分:

map方法对序列的每个元素进行操作。由于Data是Swift中的字节序列,因此将对deviceToken中的每个字节评估传递的闭包。 String(format :)初始化程序使用%02x格式说明符评估数据中的每个字节(由匿名参数$ 0表示),以生成零填充的2位十六进制表示形式的字节/ 8位整数。 在收集了由map方法创建的每个字节表示形式之后,joind()将每个元素连接为一个字符串。

P.S请勿使用描述在iOS 12和iOS 13中给出不同的字符串,并且根据将来的范围是不安全的。 开发人员不应该依赖特定格式来描述对象。

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

有关更多信息,请阅读This

答案 7 :(得分:8)

我认为将deviceToken转换为十六进制字节字符串是没有意义的。为什么?您将它发送到您的后端,在那里它将被转换回字节以推送到APNS。所以,使用 NSData 的方法base64EncodedStringWithOptions,将其推送到服务器,然后使用反向base64decoded数据:)这样更容易:)

NSString *tokenString = [tokenData base64EncodedStringWithOptions:NSDataBase64EncodingEndLineWithLineFeed];

答案 8 :(得分:4)

在iOS 13中,说明将采用不同的格式。请使用下面的代码来获取设备令牌。

- (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 < length; ++i) {
        [hexString appendFormat:@"%02x", buffer[i]];
    }
    return [hexString copy];
}

答案 9 :(得分:4)

这是一个稍微简短的解决方案:

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

答案 10 :(得分:3)

2020

令牌为文本...

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

或者,如果您愿意

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

(结果相同)

答案 11 :(得分:3)

功能Swift版

一个班轮:

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

以可重复使用的自我记录扩展形式:

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

或者,使用reduce("", combine: +)代替joinWithSeparator("")被同行视为功能主人。

编辑:我将字符串($ 0,基数:16)更改为字符串(格式:&#34;%02x&#34;,$ 0),因为填充零所需的一位数字

(我还不知道如何将问题标记为this other one的副本,所以我只是再次发布了我的答案)

答案 12 :(得分:3)

一线解决方案怎么样?

目标C

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

夫特

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

答案 13 :(得分:1)

使用updateAccumulatingResult比这里找到的其他各种方法效率更高,因此这是 Swiftiest 用来对Data字节进行字符串化的方法:

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

答案 14 :(得分:1)

把我的答案扔在堆上。避免使用字符串解析;文档并不保证NSData.description将始终以这种方式工作。

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

答案 15 :(得分:1)

我尝试使用格式"%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)

结果是缩小版本的最快速度为"%02x"平均为2.0对2.6:

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

答案 16 :(得分:1)

%02.2hhx的高票解释answer

  • %:介绍了x转换说明符。
  • 02:转换后的值的最小宽度为2。如果转换后的值的字节数少于字段宽度,则应在其左侧填充0
  • .2:给出x转换说明符出现的最小位数。
  • hh:指定x转换说明符适用于带符号的char或无符号的char参数(该参数将根据整数提升进行提升,但其值应转换为带符号的char或未签名的字符(在打印之前)。
  • x:无符号参数应以“ dddd”样式转换为无符号十六进制格式;使用字母“ abcdef”。精度指定出现的最小位数;如果要转换的值可以用较少的数字表示,则应将其扩展为前导零。默认精度为1。以零的显式精度转换零的结果应为无字符。

有关更多详细信息,请参见IEEE printf specification


基于以上解释,我认为将%02.2hhx更改为%02x%.2x更好。

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

测试如下:

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

答案 17 :(得分:1)

对于Swift:

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

println( deviceTokenString )

答案 18 :(得分:0)

这是您在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
}

答案 19 :(得分:0)

斯威夫特

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

    }

答案 20 :(得分:0)

这将为您服务,

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

答案 21 :(得分:0)

夫特:

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

答案 22 :(得分:-1)

斯威夫特3:

如果有人正在寻找在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)

答案 23 :(得分:-1)

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

答案 24 :(得分:-1)

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

答案 25 :(得分:-2)

使用优秀的类别!

// .h文件

@interface NSData (DeviceToken)

- (NSString *)stringDeviceToken;

@end    

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

#import "NSData+DeviceToken.h"

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

工作正常!

答案 26 :(得分:-3)

解决方案@kulss在此处发布,尽管缺少优雅却具有简单性的优点在iOS 13中不再起作用,因为description对于NSData的工作方式有所不同。不过,您仍然可以使用debugDescription

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

答案 27 :(得分:-3)

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

print(token)

答案 28 :(得分:-6)

尝试这个,除非数据以空值终止。

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

答案 29 :(得分:-9)

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