デバイストークン(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);
}

コードの最初の行はnullを出力します。2番目はトークンを出力します。newDeviceTokenをNSStringとして取得するにはどうすればよいですか?


第二の出力は何ですかNSLog、版画、1つのnewDeviceToken
rob mayoff


説明は使用しないでください
Fattie

回答:


39

これを使って :

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

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

134
説明を使用するのは悪い考えのようです。iOSの新しいバージョンがこの呼び出しの実装と結果を変更しないことを保証するものは何もありません。
madewulf 2012年

16
確かに、これは本当に悪い考えです。
David Snabel-Caunt 2013

21
あなたは、代替を示唆している場合...それもよりよいされているだろう、それは使用の説明に、このようなひどいアイデアだか指摘して、あなたの@madewulfは非常にいい
abbood

6
以下の[deviceToken bytes]を使用したソリューションは、目的に適合します。
madewulf 2014

37
Swift 3 / iOS 10以降、デバイストークンの.descriptionは「32バイト」を返します。ええ、これは使わないでください。
Victor Luft 2016

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
なぜこれはそれほど複雑でなければならないのですか?それは誰もが必要とするものなので、文字列を与えるOSの何が問題になっているのですか?この解決策をありがとう。
ピワフ2015年

3
@Sascha私はあなたの非常に有用な答えへの私の編集を承認してくれることを願っています:)
jrturton 2016年

16
私はリファクタリングしました:let token = deviceToken.map { String(format: "%02.2hhx", $0) }.joined() qiita.com/mono0926/items/3cf0dca3029f32f54a09
mono

2
.descriptionを使用することはお勧めしません。安定しているとは限りません。ここに私の答えをチェックアウト:stackoverflow.com/questions/9372815/...
SWIFTテイラー

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
バイトを16進数としてencondigするため、これをカウントできることを意味するため、これが最良のソリューションです;)
loretoparisi

4
XCode 5では、コンパイルするためにdeviceTokenをキャストする必要がありました。const unsigned * tokenBytes =(const unsigned *)[deviceToken bytes];
Ponytech 2013年

3
トークンは間もなく32バイトより大きくなるため、8つのハードコードされた整数ではなく、各バイトをループする必要があります。
トムダリング、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])]; }
Harro

9
Important: APNs device tokens are of variable length. Do not hard-code their size.アップルは言う。
erkanyildiz 2016年

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での唯一の正解です。
Tom Dalling、2015年

特定のトークンのサイズ/長さを想定していないため、これがおそらく最も安全な方法であることに同意します。
Ryan H.

iOS 10で動作します
Tjalsma 2017

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
私は同じコードを書いた:)これは最も
迅速な

1
@アナンドは、このコードで何が起こっているのか説明できますかdeviceToken.reduce("", {$0 + String(format: "%02X", $1)})
Ramakrishna

1
データを16進数の文字列に、次に文字列にシリアル化するswiftのreduce関数を使用します。リデュース関数の詳細については、useyourloaf.com / blog / swift
Anand

15

説明%02.2hhx高い投票で答え

  • %x変換指定子を紹介します。
  • 02:変換された値の最小幅は2です。変換された値のバイト数がフィールド幅より少ない場合は0、左側が埋め込まれます。
  • .2x変換指定子に表示される最小桁数を指定します。
  • hh:を指定します x変換指定子がsigned charまたはunsigned char引数に適用されることを指定します(引数は整数の昇格に従って昇格されますが、その値は出力前にsigned charまたはunsigned charに変換されます)。
  • x:符号なし引数は、「dddd」のスタイルで符号なし16進形式に変換されます。「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のバージョンにのみ依存しますか?
Markus

1
@MarkusこれはiOS 12で機能し、Swiftのバージョンにのみ依存します。
jqgsninimo

14

それは私の解決策であり、私のアプリでうまく機能します:

    NSString* newToken = [[[NSString stringWithFormat:@"%@",deviceToken] 
stringByTrimmingCharactersInSet:[NSCharacterSet characterSetWithCharactersInString:@"<>"]] stringByReplacingOccurrencesOfString:@" " withString:@""];
  • 変換NSDataするNSStringstringWithFormat
  • 「<>」をトリム
  • スペースを削除

10
これは暗黙のうちにを呼び出すだけ-descriptionなので、受け入れられた回答よりも安全ではありません。
jszumski 2014年

ソースをリンクしていただけますか?それに関する情報がどこにも見つかりません。どうも。
Zeb 2014

それを見つけた!少し違うと思います。description属性を直接使用することは将来のバージョンで変更される可能性があるため安全ではありませんが、NSStringメソッドを介して使用する場合、問題はほとんど発生しません。
Zeb 2014

5
いいえ、これはdescriptionjszumskiが言うように実際にdeviceTokenを呼び出します。
ジョニー2015年

1
@Zeb description返される文字列の形式はいつでも変更される可能性があるため、直接呼び出すか、別のメソッドを介して使用するかは信頼できません。正しい解決策はここにあります:stackoverflow.com/a/16411517/108105
Tom Dalling '28

10

deviceTokenを16進バイト文字列に変換しても意味がないと思います。どうして?それをバックエンドに送信し、そこでバイトに変換されてAPNSにプッシュされます。したがって、NSDataのメソッドを使用しますbase64EncodedStringWithOptionsを使用し、それをサーバーにプッシュしてから、base64でデコードされた逆データを使用します:)これは非常に簡単です:)

NSString *tokenString = [tokenData base64EncodedStringWithOptions:NSDataBase64EncodingEndLineWithLineFeed];

@ jeet.chanchawatは、他のユーザーの回答にコードを追加しないでください。特にObjective-Cの回答にSwiftを追加する場合は、言葉を口にしたくありません。代わりに、独自の答えを追加してください。
JAL

2
@Oleg Shanyukの答えを盗用したくなかっただけです。それは彼の答えに基づいて構築された別の言語での翻訳なので、彼は将来の賛成票を投じるに値します。別の回答を追加すると、他の誰かの研究である回答への賛成票が与えられます。これが編集を正当化することを願っています。
jeet.chanchawat 2017

10

iOS 13ではdescription壊れるのでこれを使用してください

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

明確にするために、これを分解して各部分を説明しましょう。

mapメソッドは、シーケンスの各要素を操作します。DataはSwiftの一連のバイトであるため、渡されたクロージャーはdeviceTokenのバイトごとに評価されます。String(format :)イニシャライザは、%02x形式指定子を使用してデータ(匿名パラメータ$ 0で表される)の各バイトを評価し、バイト/ 8ビット整数のゼロ詰めの2桁の16進数表現を生成します。マップメソッドによって作成された各バイト表現を収集した後、joined()は各要素を単一の文字列に連結します。

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

詳細については、読みこれを


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
現在はコンパイルされません-for lengthループでに変更する必要がありますlen。どうやら私が編集するには小さすぎる変更です。
Anders Friis

あなたは命の恩人です
Moeez Akram

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("")を使用して、ピアから機能マスターとして表示されます。


編集:1桁の数字にはパディングゼロが必要だったため、String($ 0、radix:16)をString(format: "%02x"、$ 0)に変更しました。

(質問を他の質問の重複としてマークする方法がまだわからないので、もう一度回答を投稿しました)


ありがとうございます。
Hasya

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

私はフォーマットを持つ2つの異なるメソッドをテストしようとした"%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])];

優れたソリューション今日の時点では、credentials.token.description.replacingOccurrences(of: "[<>]"、with: ""、options:.regularExpression、range: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;
}

正常に動作します!


「説明」の使用に依存しないでください。形式は将来変更される可能性があります。表示のみを目的としています。
マイケルピーターソン2016年

-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/...
SWIFTテイラー

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

print(token)

1
説明を使用しても、将来同じ結果が得られることが保証されていないため、安全ではありません。
Sahil Kapoor

-4

ここに掲載されているソリューション@kulssは、エレガントさは欠けていますが、シンプルさの利点があるためdescription、NSDataでは動作が異なるため、iOS 13では動作しません。debugDescriptionただし、まだ使用できます。

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

-7

データがnullで終了しない限り、これを試してください。

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


私はそれを試しました、それはうまくいきません。コードスニペットでコメント化しています。
シーハンアラム

@SheehanAlamこの男はやり遂げた。文字列に変換する方法を見てみましょう。stackoverflow.com/questions/4994302/...
Naveedアフマド

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

これは、データが文字列の場合に機能しますが、deviceTokenは文字列ではありません。
Simon Epskamp
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.