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


157

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

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

代码的第一行显示为空。第二个打印令牌。如何获取newDeviceToken作为NSString?


什么是第二个的输出NSLog,在一个打印newDeviceToken
rob mayoff 2012年


不要使用描述
Fattie

Answers:


40

用这个 :

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

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

134
使用description似乎是个坏主意:没有什么可以确保iOS的更高版本不会更改此调用的实现和结果。
madewulf 2012年

16
确实,这是一个非常糟糕的主意。
David Snabel-Caunt

21
@madewulf非常感谢您指出使用描述是多么糟糕的主意..如果您提出了其他选择
那就

6
[deviceToken bytes]下的解决方案非常合适。
madewulf 2014年

37
从Swift 3 / iOS 10开始,设备令牌上的.description返回“ 32字节”。是的,不要使用它。
Victor Luft

231

如果有人正在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为字符串,可以执行以下操作:

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

118
为什么这必须如此复杂,操作系统给我们提供了一个字符串有什么问题,因为这是每个人都需要的?感谢您的解决方案。
Piwaf

3
@Sascha我希望您同意我的编辑对您的非常有用的答案:)
jrturton

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

2
我不建议使用.description,因为这不能保证稳定。点击这里,查看我的回答:stackoverflow.com/questions/9372815/...
迅速泰勒

7
你能解释一下做"%02.2hhx什么吗?
亲爱的

155

有人帮了我这个忙

- (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
这是最好的解决方案,因为将字节数压缩为十六进制,意味着您可以对其进行计数;)
loretoparisi 2012年

4
在XCode 5上,我必须转换deviceToken使其编译:const unsigned * tokenBytes =(const unsigned *)[deviceToken bytes];
Ponytech

3
令牌很快将大于32个字节,因此这将需要在每个字节上循环,而不是八个硬编码整数。
汤姆·达林”,2015年

5
这会是更好的解决方案吗?const unsigned *tokenBytes = [deviceToken bytes]; NSMutableString *hexToken = [NSMutableString string]; for (NSUInteger byteCount = 0; byteCount * 4 < [deviceToken length]; byteCount++) { [hexToken appendFormat:@"%08x", ntohl(tokenBytes[byteCount])]; }
哈罗

9
Important: APNs device tokens are of variable length. Do not hard-code their size.苹果说。
erkanyildiz

141

你可以用这个

- (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
这应该是公认的答案,因为它比使用更加安全description
DrMickeyLauer 2015年

8
这是Objective-C中唯一可以解决令牌大小即将增加的正确答案。
汤姆·达林”,2015年

同意这可能是最安全的方法,因为它不假设任何特定的令牌大小/长度。
Ryan H.

作品在iOS的10
Tjalsma

2
我曾经使用[token appendFormat:@"%02.2hhx", data[i]];Amazon SNS要求小写。
Manuel Schmitzberger

43

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

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

1
我写了相同的代码:)这是最快捷的版本,只有这样才能起作用
Quver

1
@Anand您能解释一下这段代码中的内容吗deviceToken.reduce("", {$0 + String(format: "%02X", $1)})
Ramakrishna

1
它使用swift的reduce函数将数据序列化为十六进制字符串,然后序列化为String。要了解有关减少功能的更多信息,请阅读useyourloaf.com/blog/swift-guide-to-map-filter-reduce
Anand

15

说明%02.2hhx在高投票答案

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

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


根据以上解释,我认为更改%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

感谢您的回答。iOS 12也可以使用吗?还是仅取决于Swift版本?
马库斯

1
@Markus在iOS 12中有效,仅取决于Swift版本。
jqgsninimo

14

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

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

10
这只是隐式调用-description,所以没有比接受的答案更安全的了。
jszumski 2014年

你能链接你的资料吗?我在任何地方都找不到有关它的信息。谢谢。
Zeb 2014年

找到了!我认为这有点不同。直接使用description属性是不安全的,因为它可能在将来的版本中更改,但是如果通过NSString方法使用它,几乎不会出现问题。
Zeb 2014年

5
没有这真的description像jszumski所说的那样调用deviceToken。
2015年

1
@Zeb依靠description您直接调用它还是通过其他方法使用它是不安全的,因为返回的字符串的格式可以随时更改。正确的解决方案在这里:stackoverflow.com/a/16411517/108105
Tom Dalling 2015年

10

我认为将deviceToken转换为十六进制字节字符串没有意义。为什么?您将其发送到后端,在后端将其转换回字节以发送到APNS。因此,使用NSData的方法base64EncodedStringWithOptions,将其推送到服务器,然后使用反向base64解码的数据:)如此简单:)

NSString *tokenString = [tokenData base64EncodedStringWithOptions:NSDataBase64EncodingEndLineWithLineFeed];

@ jeet.chanchawat,请不要在其他用户的答案中添加代码。我们不想在他们的嘴里说些什么,尤其是在将Swift添加到Objective-C答案中时。而是添加您自己的答案。
2017年

2
我只是不想to窃@Oleg Shanyuk的答案。因为这只是根据他的回答翻译成另一种语言的结果,所以他值得未来投票。如果我添加另一个答案,它将给我投票的答案,这是别人的研究。希望这证明编辑是正确的。
jeet.chanchawat

10

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

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

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

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

PS不使用描述在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


10

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

完美的ios 13解决方案。感谢Vishnu
Manish

1
目前尚无法编译- length在for循环中应更改为len。显然,对于我来说,更改太小了,无法进行编辑。
安德斯·弗里斯

您是
救生员

3

这是一个较短的解决方案:

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

功能性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("")被同行视为功能大师。


编辑:我将String($ 0,radix:16)更改为String(format:“%02x”,$ 0),因为一位数字需要填充零

(我还不知道如何标记一个问题为重复这个另外一个,所以我只是张贴了我的答案再次)


为我工作,谢谢。
哈西亚

3

2020年

标记为文字...

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

或者如果您愿意

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

(结果是一样的)


2

把我的答案丢到一堆。避免使用字符串解析;文档并不能保证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()
    }
}

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

1

使用updateAccumulatingResult比这里找到的其他各种方法效率更高,因此这是字符串化的最快方法Data

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

亚历克斯,那岂不是%02.2hhx
Fattie

0

对于Swift:

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

println( deviceTokenString )

0

一线解决方案呢?

目标C

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

迅速

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

2
这是最简单,最好的解决方案。谢谢
艾美奖

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
}

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

伟大的解决方案到今天为止,它可以隐含为凭据.token.description.replacingOccurrences(of:“ [<>]”,with:“”,选项:.regularExpression,范围:nil)
Frank

-1

迅速:

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

迅速

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

使用优秀的类别!

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

@结束

// AppDelegate.m

#import "NSData+DeviceToken.h"

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

工作良好!


不要依赖使用“描述”,它的格式将来可能会更改。仅用于显示目的。
迈克尔·彼得森

-3

斯威夫特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)

2
我不建议使用.description,因为这不能保证保持不变。看到这里我的答案:stackoverflow.com/questions/9372815/...
迅速泰勒


-4

解决方案@kulss在这里发布,虽然缺乏优雅,但具有简单性的优点在iOS 13中不再起作用,因为它description对于NSData的工作方式有所不同。您仍然可以使用debugDescription

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


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

当数据是字符串时,此方法有效,但是deviceToken不是字符串。
西蒙·埃斯普坎普
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.