* 출처
: https://velog.io/@gwangyonglee/iOS13-앱-푸시-장치-토큰deviceToken-관련-이슈
: https://www.mrlatte.net/code/2019/10/31/ios13-push-token.html
: https://stackoverflow.com/q/8798725
iOS13 앱 푸시 장치 토큰(deviceToken) 관련 이슈
안녕하세요.
이번 글에서는 iOS13 환경에서 deviceToken을 가져올 때 달라진 점에 대해서 공유하려고 합니다.
달라진 점
iOS12 환경에서는 deviceToken을 가져오는 메소드에서 deviceToken 파라미터 데이터 형식은
<124686a5 556a72ca d808f572 00c323b9 3eff9285 92445590 3225757d b83997ba>
iOS13 환경에서는
{length=32,bytes=0x91e27cd9ddf20c69da8fa650e2a06f55...e60b100d99d60adf}
로 변경되었습니다.
대응
iOS12 환경에서 deviceToken 데이터를 String으로 아래의 방법으로 가공하였다면,
func application(_ application: UIApplication, didRegisterForRemoteNotificationsWithDeviceToken deviceToken: Data) {
var deviceTokenString = String(format: "%@", deviceToken as CVarArg)
deviceTokenString = deviceTokenString.replacingOccurrences(of: "<", with: "")
deviceTokenString = deviceTokenString.replacingOccurrences(of: ">", with: "")
deviceTokenString = deviceTokenString.replacingOccurrences(of: " ", with: "")
...
}
iOS13 환경에서는 Base16-encoded / hexadecimal string 표현으로 변환해야 합니다.
func application(_ application: UIApplication, didRegisterForRemoteNotificationsWithDeviceToken deviceToken: Data) {
let deviceTokenString = deviceToken.map { String(format: "%02x", $0) }.joined()
...
}
map메소드를 이용하여 Data의 각 바이트를 16진수로 String으로 만들고, joined()을 이용하여 단일 문자열로 변환하는 방식입니다.
마무리
iOS12 환경에서는 푸시기능이 잘 작동하였는데, iOS13버전 대응하면서 디바이스 토큰 데이터 구조가 변경되었고, 갑작스럽게 대응하느라 미리 대응했으면 좋았을텐데에 대한 아쉬움이 남네요. iOS14버전 부터는 미리 미리 대응하는 습관을 길러야겠네요:)
iOS 13에서 푸시토큰 확인 방법
iOS 13에서 푸시토큰이 iOS 13 이하 버전에서와 다르게 확인되는 경우가 있어서 해결 방법을 공유한다.
Problem
기존 iOS의 푸시토큰을 확인할 때 아래와 같이 인터넷에서 공유된 잘못된 방법을 많이 사용했었다.
NSString *token = [[[[deviceToken description]
stringByReplacingOccurrencesOfString:@" " withString:@""]
stringByReplacingOccurrencesOfString:@"<" withString:@""]
stringByReplacingOccurrencesOfString:@">" withString:@""];
iOS 13 이전 버전까지는 보통 아래와 같은 형식으로 반환되어 <
, >
그리고 공백을 제거해서 사용했지만 적절한 방법이라고 할 수 없다.
<12345678 asdfghjk 45678900 dfb16ddd 12345678 45678900 45678900 asdfghjk>
iOS 13부터는 description
를 사용할 경우 푸시토큰이 아래와 같이 반환되는경우가 있어서 문제가 될 수 있다. 심지어 bytes
부분은 중간에 생략되기까지하여 서버에 저장할 경우 클라이언트 수정 없이 이 데이터만을 이용해서 문제를 해결하기도 힘들다.
{length=32,bytes=0x16406f0b18032ca902ed1117edd3bf6b...6221947229b7ca18}
Solution
이것은 apple의 갑작스러운 변경이 원인이라기 보다는 푸시 토큰을 가져오는 방법을 잘못 사용하고 있는것이 문제의 원인으로 아래와 같이 변경해야 한다.
+ (NSString *)stringFromDeviceToken:(NSData *)deviceToken {
NSUInteger length = deviceToken.length;
if (length == 0) {
return nil;
}
const unsigned char *buffer = deviceToken.bytes;
NSMutableString *hexString = [NSMutableString stringWithCapacity:(length * 2)];
for (int i = 0; i < length; ++i) {
[hexString appendFormat:@"%02x", buffer[i]];
}
return [hexString copy];
}
이상한점은 iOS 13이라고 항상 발생하는 것은 아니고 아래와 같은 조합의 경우 발생했음을 참고로 공유한다.
CFNetwork/1098.4 Darwin/19.0.0
CFNetwork/1098.7 Darwin/19.0.0
CFNetwork/1107.1 Darwin/19.0.0
CFNetwork/1115 Darwin/19.0.0
iOS 13인 아래의 경우는 발생하지 않았다.
CFNetwork/1120 Darwin/19.0.0
NOTE: The below solution no longer works on iOS 13+ devices - it will return garbage data.
Please use following code instead:
+ (NSString *)hexadecimalStringFromData:(NSData *)data
{
NSUInteger dataLength = data.length;
if (dataLength == 0) {
return nil;
}
const unsigned char *dataBuffer = (const unsigned char *)data.bytes;
NSMutableString *hexString = [NSMutableString stringWithCapacity:(dataLength * 2)];
for (int i = 0; i < dataLength; ++i) {
[hexString appendFormat:@"%02x", dataBuffer[i]];
}
return [hexString copy];
}
Solution that worked prior to iOS 13:
Objective-C
- (void)application:(UIApplication *)app didRegisterForRemoteNotificationsWithDeviceToken:(NSData *)deviceToken
{
NSString *token = [[deviceToken description] stringByTrimmingCharactersInSet: [NSCharacterSet characterSetWithCharactersInString:@"<>"]];
token = [token stringByReplacingOccurrencesOfString:@" " withString:@""];
NSLog(@"this will return '32 bytes' in iOS 13+ rather than the token", token);
}
Swift 3.0
func application(_ application: UIApplication, didRegisterForRemoteNotificationsWithDeviceToken deviceToken: Data)
{
let tokenString = deviceToken.reduce("", {$0 + String(format: "%02X", $1)})
print("this will return '32 bytes' in iOS 13+ rather than the token \(tokenString)")
}