Cihaz simgemi (NSData) NSString'e nasıl dönüştürebilirim?


157

Anlık bildirimler uyguluyorum. APNS Jetonumu bir String olarak kaydetmek istiyorum.

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

İlk kod satırı null yazdırır. ikincisi jetonu yazdırır. NewDeviceToken'ımı NSString olarak nasıl edinebilirim?


Saniyenin çıkışı nedir NSLog, baskılar bu bir newDeviceToken?
rob mayoff


açıklama KULLANMAYIN
Fattie

Yanıtlar:


39

bunu kullan :

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

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

134
Açıklama kullanmak kötü bir fikir gibi görünüyor: hiçbir şey iOS'un sonraki sürümünün bu aramanın uygulamasını ve sonucunu değiştirmeyeceğini garanti etmiyor.
madewulf

16
Gerçekten, bu gerçekten kötü bir fikir.
David Snabel-Caunt

21
@madewulf çok güzel bir açıklama kullanmanın ne kadar korkunç bir fikir olduğunu belirtmek için çok güzel .. bir alternatif
öneriyor

6
[DeviceToken bytes] ile buradaki çözüm faturaya uyar.
madewulf

37
Swift 3 / iOS 10, cihaz belirtecindeki "32 bayt" ifadesini döndürüyor. Yani evet, bunu kullanma.
Victor Luft

231

Swift'te bunu yapmanın bir yolu arayan varsa:

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

Düzenleme: Swift 3 için

Swift 3 Data, değeri anlambilimiyle türü tanıtır . Bir Dizeye dönüştürmek için deviceTokenaşağıdakileri yapabilirsiniz:

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

118
Bunun neden bu kadar karmaşık olması gerekiyor, işletim sisteminin bize bir ip verdiği yanlış olan herkesin ihtiyacı olan şeydir? Bu çözüm için teşekkürler.
15'te Piwaf

3
@Sascha Umarım çok faydalı cevabınızdaki düzenlememi onaylarsınız :)
jrturton

16
let token = deviceToken.map { String(format: "%02.2hhx", $0) }.joined() Yeniden düzenledim
mono

2
Istikrarlı olduğu garanti edilmediğinden .description kullanmanızı önermiyorum. Cevabımı buradan kontrol edin: stackoverflow.com/questions/9372815/…
swift taylor

7
Ne yaptığını açıklayabilir misin "%02.2hhx?
Tatlım

155

Birisi bana bu konuda yardımcı oldu.

- (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
Bu en iyi çözümdür, çünkü encondig baytları onaltılık olarak sayabileceğinizi ima eder;)
loretoparisi

4
XCode 5'te, derlemesini sağlamak için deviceToken'i kullanmak zorunda kaldım: const unsigned * tokenBytes = (const unsigned *) [deviceToken bytes];
Ponytech

3
Jetonlar yakında 32 bayttan daha büyük olacak, bu yüzden sekiz sabit kodlu tam sayı yerine her bayt üzerinde bir döngü olması gerekecek.
Tom Dalling

5
Bu daha iyi bir çözüm olabilir mi? 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.Apple diyor.
erkanyildiz

141

Bunu kullanabilirsiniz

- (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
Kullanmaktan çok daha güvenli olduğu için bu kabul edilen cevap olmalıdır description.
DrMickeyLauer

8
Bu, Objective-C'de token boyutunda yaklaşan artışı ele alacak tek doğru cevaptır.
Tom Dalling

Belirli bir belirteç boyutu / uzunluğu varsaymadığından bunun muhtemelen en güvenli yol olduğunu kabul etti.
Ryan

İOS

2
Ben [token appendFormat:@"%02.2hhx", data[i]];Amazon SNS küçük harf gerektirir olarak kullandım .
Manuel Schmitzberger

43

Swift 3 ve en kolay yöntem isteyenler için

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

1
Ben aynı kodu yazdım :) Bu en swifty versiyonu ve sadece bu çalışıyor
Quver

1
@Anand bu kod happing ne u açıklayabilir miyimdeviceToken.reduce("", {$0 + String(format: "%02X", $1)})
Ramakrishna

1
Verileri onaltılık dizeye ve sonra Dize'ye serileştiren swift azaltma işlevini kullanır. Azaltma
Anand

15

Açıklanması %02.2hhxyüksek oy cevap :

  • %: xDönüşüm belirtecini tanıtır.
  • 02: Dönüştürülen değerin minimum genişliği 2'dir. Dönüştürülen değer alan genişliğinden daha az bayta sahipse 0, solda ile doldurulmalıdır .
  • .2: xDönüşüm belirtici için görünecek minimum basamak sayısını verir .
  • hh: xDönüşüm belirticinin imzalı bir char veya imzasız char argümanı için geçerli olduğunu belirtir (argüman tamsayı tanıtımlarına göre yükseltilecek, ancak değeri yazdırılmadan önce imzalı char veya unsigned char'a dönüştürülecektir).
  • x: İmzasız argüman "dddd" stilinde imzasız onaltılık biçime dönüştürülecektir; "abcdef" harfleri kullanılır. Hassasiyet, görüntülenecek minimum basamak sayısını belirtir; dönüştürülen değer daha az basamakla gösterilebiliyorsa, baştaki sıfırlarla genişletilir. Varsayılan kesinlik 1'dir. Sıfırın açık bir kesinlik ile dönüştürülmesinin sonucu karakter olmamalıdır.

Daha fazla ayrıntı için IEEE printf spesifikasyonuna bakın .


Açıklamalara dayanarak, ben değişikliğine daha iyi olduğunu düşünüyorum %02.2hhxetmek %02xveya %.2x.

Swift 5 için, aşağıdaki yöntemlerin hepsi mümkündür:

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

Test aşağıdaki gibidir:

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

Bu cevap için teşekkürler. Bu iOS 12 ile de çalışıyor mu? Yoksa sadece Swift sürümüne mi bağlı?
Markus

1
@Markus Bu, iOS 12'de çalışır, yalnızca Swift sürümüne bağlıdır.
jqgsninimo

14

Benim çözümüm ve uygulamamda iyi çalışıyor:

    NSString* newToken = [[[NSString stringWithFormat:@"%@",deviceToken] 
stringByTrimmingCharactersInSet:[NSCharacterSet characterSetWithCharactersInString:@"<>"]] stringByReplacingOccurrencesOfString:@" " withString:@""];
  • dönüştürme NSDataiçin NSStringbirliktestringWithFormat
  • "<>" kırpma
  • boşlukları kaldır

10
Bu sadece dolaylı olarak çağırır -description, bu yüzden kabul edilen cevaptan daha güvenli değildir.
jszumski

Lütfen kaynağınızı bağlayabilir misiniz? Bu konuda hiçbir yerde bilgi bulamıyorum. Teşekkürler.
Zeb

Buldum! Bence biraz farklı. Tanım niteliğini doğrudan kullanmak güvenli değildir çünkü gelecekteki sürümlerde değişebilir, ancak NSString yöntemiyle kullanırsanız sorun yaşamazsınız.
Zeb

5
Hayır, bu gerçekten descriptioncszumski'nin dediği gibi cihazda çağırıyor.
Jonny

1
@Zeb descriptionDoğrudan arayacağınıza ya da başka bir yöntemle kullanıp kullanmayacağınıza güvenmek güvenli değildir , çünkü döndürülen dizenin biçimi her zaman değiştirilebilir. Doğru çözüm burada: stackoverflow.com/a/16411517/108105
Tom Dalling

10

Onaltılık bayt dizeye deviceToken dönüştürmenin bir anlamı olmadığını düşünüyorum. Neden? Arka ucunuza göndereceksiniz, burada APNS'ye gönderilecek baytlara geri dönüştürülecek. Yani, NSData yöntemini kullanın base64EncodedStringWithOptions, sunucuya itin ve sonra ters base64decoded veri kullanın :) Bu çok daha kolay :)

NSString *tokenString = [tokenData base64EncodedStringWithOptions:NSDataBase64EncodingEndLineWithLineFeed];

@ jeet.chanchawat lütfen diğer kullanıcıların cevaplarına kod eklemeyin. Özellikle Objective-C cevabına Swift eklerken kelimeleri ağzına koymak istemiyoruz. Bunun yerine kendi cevabınızı ekleyin.
JAL

2
@Oleg Shanyuk'un cevabını intihal etmek istemedim. Sadece cevabına dayanan başka bir dildeki çeviri olduğundan, gelecekteki oyları hak ediyor. Başka bir cevap eklersem, başka birinin araştırması olan cevap için bana oy verir. Umarım bu EDIT'i haklı çıkarır.
jeet.chanchawat

10

İOS 13'te descriptionbu yüzden bunu kullanın

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

Açıklık için, bunu yıkalım ve her bir parçayı açıklayalım:

Harita yöntemi, bir dizinin her elemanı üzerinde çalışır. Veri Swift'teki bir bayt dizisi olduğundan, geçirilen kapatma, DeviceToken'deki her bayt için değerlendirilir. Dize (biçim :) başlatıcısı, bayt / 8 bit tamsayısının sıfır dolgulu, 2 basamaklı onaltılık bir gösterimini oluşturmak için verilerdeki her baytı (anonim parametre $ 0 ile temsil edilen)% 02x biçim belirticisini kullanarak değerlendirir. Map yöntemi tarafından oluşturulan her bayt gösterimini topladıktan sonra, join () her öğeyi tek bir dizeye birleştirir.

PS, açıklama kullanmaz, iOS 12 ve iOS 13'te farklı dize verir ve gelecekteki kapsama göre güvenli değildir. Geliştiriciler, bir nesnenin açıklaması için belirli bir biçime güvenmemeliydi.

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

Daha fazla bilgi için Bunu okuyun .


10

İOS 13'te açıklama farklı biçimde olacaktır. Cihaz kodunu almak için lütfen aşağıdaki kodu kullanın.

- (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 için mükemmel bir çözüm. Teşekkürler Vishnu
Manish

1
Şu anda derlenmiyor - lengthfor döngüsünde değiştirilmelidir len. Görünüşe göre bir düzenleme yapmak için çok küçük bir değişiklik .. Ama başka mükemmel çalışıyor!
Anders Friis

hayat kurtarıcısın
Moeez Akram

3

Bu biraz daha kısa bir çözümdür:

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

Fonksiyonel Swift sürümü

Bir astar:

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

İşte yeniden kullanılabilir ve kendi kendini belgeleyen bir uzantı formunda:

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

Alternatif olarak, akranlarınız tarafından işlevsel bir usta olarak görülmek reduce("", combine: +)yerine kullanın joinWithSeparator("").


Düzenleme: Dize ($ 0, sayı: 16) Dize (biçim: "% 02x", $ 0) olarak değiştirildi, çünkü bir basamak numarası sıfırlama değerine sahip olması gerekiyordu

(Bir soruyu diğerinin kopyası olarak nasıl işaretleyeceğimi henüz bilmiyorum , bu yüzden cevabımı tekrar gönderdim)


Benim için çalışıyor, teşekkürler.
Hasya

3

2020

metin olarak belirteç ...

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

ya da isterseniz

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

(sonuç aynıdır)


2

Cevabımı yığın üzerine atıyorum. Dize ayrıştırma kullanmaktan kaçının; Dokümanlar tarafından NSData.description öğesinin her zaman bu şekilde çalışacağı garanti edilmez.

Swift 3 Uygulaması:

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

Biçimle iki farklı yöntemi test etmeye çalıştım "%02.2hhx"ve"%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)

ve sonuç, "%02x"indirgenmiş versiyon için en hızlısının ortalama 2.0 vs 2.6 olmasıdır:

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

1

UpdateAccumulatingResult kullanmak , burada bulunan diğer çeşitli yaklaşımlardan daha verimlidir, bu nedenle baytlarınızı dizginlemenin en hızlı yolu Data:

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

Alex,% 02.2hhx olmaz
Fattie

0

Swift için:

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

println( deviceTokenString )

0

Tek hat çözümü ne olacak?

Hedef C

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

hızlı

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

2
Bu basit ve en iyi çözümdür. Teşekkürler
Emmy

0

İşte Xamarin'de bunu nasıl yapacağınız.

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

büyük çözüm Bugün itibariyle, credentials.token.description.replacingOccurrences (:: [<>] "için,:" "ile, seçenekler: .regularExpression, range: nil)
Frank

-1

Swift:

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

hızlı

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

Mükemmel bir kategori kullanın!

// .h dosya

@interface NSData (DeviceToken)

- (NSString *)stringDeviceToken;

@end    

// .m dosyası

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

@son

// AppDelegate.m

#import "NSData+DeviceToken.h"

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

İyi çalışıyor!


"Açıklama" kullanmaya güvenmeyin, biçimi gelecekte değişebilir. Yalnızca görüntüleme amaçlıdır.
Michael Peterson

-3

Hızlı 3:

Herhangi biri Swift 3'te cihaz belirteci almanın bir yolunu arıyorsa Aşağıdaki değiştirilmiş snippet'i kullanın.

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

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

    print(deviceTokenString)

2
Aynı kalması garanti edilmediğinden .description kullanmanızı önermiyorum. Cevabımı burada görebilirsiniz: stackoverflow.com/questions/9372815/…
swift taylor

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

print(token)

1
Gelecekte aynı sonuçları vereceği garanti edilmediğinden, açıklama kullanmak güvenli değildir.
Sahil Kapoor

-4

@Kulss, zerafetten yoksun, ancak sadelik erdemine sahip olan burada yayınlanan çözüm, iOSData'da artık descriptionfarklı olacağından iOS 13'te artık çalışmıyor. Yine de kullanabilirsiniz debugDescription.

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


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

Veriler bir dize olduğunda çalışır, ancak deviceToken bir dize değildir.
Simon Epskamp
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.