iOS 13有新的获取设备推送通知令牌的方式吗?

34

所以我的朋友收到了来自OneSignal的这封电子邮件。

由于即将发布的iOS 13中可能会发生的更改,您必须在使用Xcode 11构建应用程序之前更新到最新版本的iOS SDK。所有OneSignal的包装器SDK,包括React Native、Unity和Flutter也已更新。 原因是Xcode 11与iOS 13一起发布,打破了像OneSignal这样的应用程序和库使用的常见技术,用于获取设备的推送令牌。如果您不使用我们的新SDK,则新用户将无法订阅来自您的应用程序的通知。

我对此感到好奇。

这是我们在iOS 12上获取设备通知令牌的方式。

func application(_ application: UIApplication, didRegisterForRemoteNotificationsWithDeviceToken deviceToken: Data) {
    var token = ""
    for i in 0..<deviceToken.count {
        token = token + String(format: "%02.2hhx", arguments: [deviceToken[i]])
    }
    print("Notification token = \(token)")
}

应该如何在iOS 13上正确实现?对于我目前开发的应用程序,是采用新方法还是旧方法仍然可行?

8个回答

51

您可以使用此方法在iOS 13及以上版本上获取设备令牌:

Objective-C:

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

Swift 5.0 (未经测试)

class func string(fromDeviceToken deviceToken: Data?) -> String? {
    let length = deviceToken?.count ?? 0
    if length == 0 {
        return nil
    }
    let buffer = UInt8(deviceToken?.bytes ?? 0)
    var hexString = String(repeating: "\0", count: length * 2)
    for i in 0..<length {
        hexString += String(format: "%02x", buffer[i])
    }
    return hexString
}

来源于OneSignal博客


转换行无法编译。修复方法如下:const unsigned char *buffer = (const unsigned char *)NSDeviceToken.bytes; - brkeyal

29
你的方法很好,应该可以在iOS 13上继续使用。但是有些开发者会这样做this。为了将Data转换成16进制字符串,他们会调用description,这个方法会返回类似于以下内容:
<124686a5 556a72ca d808f572 00c323b9 3eff9285 92445590 3225757d b83997ba>

然后他们修剪<>并删除空格。

iOS 13上调用令牌数据的description返回类似以下内容:

{ length = 32, bytes = 0xd3d997af 967d1f43 b405374a 13394d2f ... 28f10282 14af515f }

很明显,这种方式是错误的。

另一个例子是错误实现的(已经编辑以包括正确实现)。

更多示例可以在此主题中找到。


5
使用 description 将令牌转换为字符串的人是错误的。这从来就不是正确的方法。 - rmaddy
如果您使用描述将令牌转换为字符串,则不会影响存储中的版本。当您使用Xcode 11更新应用程序时,需要更新代码并删除旧的描述样式。 - PrasathBabu
1
有没有一种方法可以在iOS设备与之通信的服务器上将{length = 32, bytes = 0xd3d997af}转换为令牌作为临时修复措施? - Andy Davies
1
很遗憾,@AndyDavies,不行,因为“description”会丢失部分令牌值,并用“...”替换它。 - Eugene Berdnikov
iOS 13上获取设备令牌的新方法是否也适用于旧版本? - Sai Sunkari
1
@SaiSunkari 实际上这不是一种“新”的方式,它只是一种更正确和更稳定的方式,因为它不依赖于 description。当然,它也适用于旧版 iOS。 - Eugene Berdnikov

3

这是适用于Swift 5的相同代码,但是更简洁的变体。在iOS 13上进行了验证。

func getStringFrom(token:NSData) -> String {
    return token.reduce("") { $0 + String(format: "%02.2hhx", $1) }
}

2

在Xamarin.iOS中正确捕获iOS 13设备令牌

public override void RegisteredForRemoteNotifications(UIApplication application, NSData deviceToken)
{
    //DeviceToken = Regex.Replace(deviceToken.ToString(), "[^0-9a-zA-Z]+", "");
    //Replace the above line whick worked up to iOS12 with the code below:
    byte[] bytes = deviceToken.ToArray<byte>();
    string[] hexArray = bytes.Select(b => b.ToString("x2")).ToArray();
    DeviceToken = string.Join(string.Empty, hexArray);
}

这里正在进行的事情如下:
1. 首先,我们需要通过对设备标记调用ToArray()方法来获取所有字节。
2. 一旦我们获取了字节,它们会形成一个字节数组或byte[],然后我们使用LINQ Select函数调用内部函数,该函数将每个字节转换为前导零的两位十六进制字符串。在C#中,这可以很好地使用格式说明符x2完成。LINQ Select函数返回一个IEnumerable,因此轻松调用ToArray()即可获得一个字符串数组或string[]。
3. 现在只需在字符串数组上调用Join()方法,我们就会得到一个连接的字符串。
参考:https://dev.to/codeprototype/correctly-capture-ios-13-device-token-in-xamarin-1968 解决方案2:这也是完全可行的。
public override void RegisteredForRemoteNotifications(UIApplication application, NSData deviceToken)
{
    byte[] result = new byte[deviceToken.Length];
    Marshal.Copy(deviceToken.Bytes, result, 0, (int)deviceToken.Length);
    var token = BitConverter.ToString(result).Replace("-", "");
}

1

C#与Xamarin的绝妙解决方案:

// In AppDelegate class:
public override void RegisteredForRemoteNotifications(UIApplication application, NSData deviceToken)
{
    var bytes = deviceToken.ToArray();
    var deviceTokenString = string.Concat(bytes.Select(b => $"{b:x2}"));
    // TODO: handle deviceTokenString
}


0
func getStringFrom(deviceToken: Data) -> String {
    var token = ""
    for i in 0..<deviceToken.count {
        token += String(format: "%02.2hhx", arguments: [deviceToken[i]])
    }
    return token
}

1
这在我的iOS 13和iOS 12上似乎可以工作。但是我对printf格式有点生疏了。从文档中我收集到: x - 无符号32位整数 hh - 适用于有符号或无符号字符。你能帮我理解这个上下文中的02.2吗?它是用于数据填充吗?谢谢! - Jeremy Moyers

-3

你可以看一下下面的代码,因为我也曾经卡在这个问题上。以下是获取iOS 13及以上设备令牌的代码。

    NSString *str = [NSString stringWithFormat:@"%@", devTokendata]; // devTokendata is NSData
    str = [str stringByReplacingOccurrencesOfString:@" " withString:@""];
    str = [str stringByReplacingOccurrencesOfString:@"<" withString:@""];
    str = [str stringByReplacingOccurrencesOfString:@">" withString:@""];
    if (@available(iOS 13, *)) {
        str = [self hexadecimalStringFromData:devToken];
    NSLog(@"APNS Token: %@",str);
}

-(NSString *)deviceTokenFromData:(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];
}

-11

使用 deviceToken.debugDescription


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接