IT story

디바이스 토큰 (NSData)을 NSString으로 어떻게 변환 할 수 있습니까?

hot-time 2020. 6. 16. 08:05
반응형

디바이스 토큰 (NSData)을 NSString으로 어떻게 변환 할 수 있습니까?


푸시 알림을 구현하고 있습니다. 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으로 어떻게 얻을 수 있습니까?


이것을 사용하십시오 :

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

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

누군가 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)")
}

편집 : 스위프트 3

Swift 3는 Data값 의미론을 가진 유형을 소개합니다 . deviceToken문자열 로 변환하려면 다음과 같이하십시오.

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

누군가 나를 도와주었습니다.

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

이것을 사용할 수 있습니다

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

Swift 3 에서 가장 쉬운 방법 을 원하는 사람들을 위해

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

내 솔루션이며 내 앱에서 잘 작동합니다.

    NSString* newToken = [[[NSString stringWithFormat:@"%@",deviceToken] 
stringByTrimmingCharactersInSet:[NSCharacterSet characterSetWithCharactersInString:@"<>"]] stringByReplacingOccurrencesOfString:@" " withString:@""];
  • 변환 NSDataNSStringstringWithFormat
  • "<>"트림
  • 공백을 제거하십시오

deviceToken을 16 진수 바이트 문자열로 변환하는 것은 의미가 없다고 생각합니다. 왜? 백엔드로 보내면 APNS로 푸시하기 위해 바이트로 다시 변환됩니다. 따라서 NSData 의 방법 base64EncodedStringWithOptions을 사용하여 서버로 푸시 한 다음 역방향 base64 디코딩 데이터를 사용하십시오. :) 훨씬 쉽습니다. :)

NSString *tokenString = [tokenData base64EncodedStringWithOptions:NSDataBase64EncodingEndLineWithLineFeed];

%02.2hhx높은 투표 답변 에 대한 설명 :

  • %: x변환 지정자를 소개 합니다.
  • 02: 변환 된 값의 최소 너비는 2입니다. 변환 된 값의 필드 너비보다 바이트 수가 적은 0경우 왼쪽에 채워집니다 .
  • .2: x변환 지정자 에 표시 할 최소 자릿수를 제공 합니다.
  • hh: x변환 지정자가 부호있는 char 또는 부호없는 char 인수에 적용되도록 지정합니다 (인수는 정수 승격에 따라 승격되지만 값은 인쇄하기 전에 부호있는 char 또는 부호없는 char로 변환됩니다).
  • x: 부호없는 인수는 "dddd"스타일의 부호없는 16 진수 형식으로 변환됩니다. 문자 "abcdef"가 사용됩니다. 정밀도는 표시 할 최소 자릿수를 지정합니다. 변환되는 값을 더 적은 자릿수로 표시 할 수 있으면 앞에 오는 0으로 확장됩니다. 기본 정밀도는 1입니다. 명시 적 정밀도 0으로 0을 변환 한 결과는 문자가 아닙니다.

자세한 내용은 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

이것은 조금 더 짧은 해결책입니다.

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

기능적 스위프트 버전

짧막 한 농담:

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이 필요하기 때문에 String ($ 0, radix : 16)을 String (format : "% 02x", $ 0)으로 변경했습니다.

(아직 질문을 이 다른 질문과 중복으로 표시하는 방법을 모르므로 답을 다시 게시했습니다.)


더미에 내 대답을 던지기. 문자열 구문 분석을 사용하지 마십시오. 문서에 의해 NSData.description이 항상 그런 식으로 작동한다는 보장은 없습니다.

스위프트 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()
    }
}

iOS 13에서는 description고장이므로 이것을 사용하십시오.

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

명확하게하기 위해 이것을 분해하고 각 부분을 설명합시다.

map 메소드는 시퀀스의 각 요소에서 작동합니다. Data는 Swift에서 일련의 바이트이므로 전달 된 클로저는 deviceToken의 각 바이트에 대해 평가됩니다. String (format :) 이니셜 라이저는 % 02x 형식 지정자를 사용하여 데이터의 각 바이트 (익명 매개 변수 $ 0로 표시)를 평가하여 바이트 / 8 비트 정수의 0으로 채워진 2 자리 16 진수 표현을 생성합니다. map 메소드로 작성된 각 바이트 표시를 수집 한 후, join ()은 각 요소를 단일 문자열로 연결합니다.

P.S don't use description gives different string in iOS 12 and iOS 13 and not safe as per future scope. 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.


What about one line solution?

Objective C

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

Swift

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

I've tried to test two different methods with format "%02.2hhx" and "%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:

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

Using updateAccumulatingResult is more efficient than the various other approaches found here, so here's the Swiftiest way to stringify your Data bytes:

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 )

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

    }

Here's how you do it in 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
}

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

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

Swift:

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

Swift 3:

If any one is looking for a way to get device token in Swift 3. Use the below modified snippet.

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

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

    print(deviceTokenString)

Use excellent category!

// .h file

@interface NSData (DeviceToken)

- (NSString *)stringDeviceToken;

@end    

// .m file

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

Works fine!


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. You can still use debugDescription though.

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

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

print(token)

Try this one unless the data is null-terminated.

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


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

참고URL : https://stackoverflow.com/questions/9372815/how-can-i-convert-my-device-token-nsdata-into-an-nsstring

반응형