Swift 3 - токены устройств теперь анализируются как '32BYTES'

94

Я только что обновился с Xcode 7 до 8 GM и среди проблем с совместимостью Swift 3 заметил, что токены моих устройств перестали работать. Теперь они читают только 32BYTES.

    func application(_ application: UIApplication, didRegisterForRemoteNotificationsWithDeviceToken deviceToken: Data)
{
    print(deviceToken) // Prints '32BYTES'
    print(String(data: deviceToken , encoding: .utf8)) // Prints nil
}

До обновления я мог просто отправить NSData на свой сервер, но теперь у меня возникли проблемы с фактическим анализом токена.

Что мне здесь не хватает?

Изменить: я просто тестирую преобразование обратно в NSData и вижу ожидаемые результаты. Так что теперь я просто запутался в новом типе данных.

    func application(_ application: UIApplication, didRegisterForRemoteNotificationsWithDeviceToken deviceToken: Data)
{
    print(deviceToken) // Prints '32BYTES'
    print(String(data: deviceToken , encoding: .utf8)) // Prints nil

    let d = NSData(data: deviceToken)
    print(d) // Prints my device token
}
user1537360
источник
2
Переход к NSDataпросто печатает descriptionиз NSData. Вы по-прежнему не получаете ни строчки из этого.
rmaddy

Ответы:

189
func application(_ application: UIApplication, didRegisterForRemoteNotificationsWithDeviceToken deviceToken: Data) {
    let token = deviceToken.map { String(format: "%02.2hhx", $0) }.joined()
    print(token)
}
Рок Грегорич
источник
Обратная операция (шестнадцатеричная строка -> Данные) доступна здесь: stackoverflow.com/a/46663290/5252428
Рок Грегорич
4
%02xтоже хорошо.
jqgsninimo
1
@Rok, не могли бы вы объяснить свой ответ? в какой новый формат был отформатирован токен?
simo
@simo - это преобразование данных в шестнадцатеричную строку, которую вы можете передать веб-сервису / API для отправки push-уведомлений.
Rok Gregorič
Хороший пост с хорошим объяснением различий между %02.2hhxи %02x nshipster.com/apns-device-tokens/#overturned-in-ios-13
Рок Грегорич
35

У меня такая же проблема. Это мое решение:

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(token)
}
Олег
источник
1
Это альтернативный подход к кодированию NSDataв строку. В своем ответе я предложил использовать кодировку base64. Используется кодировка base16.
rmaddy
@rmaddy, твой путь тоже интересен, но было бы полезнее, если бы ты дал нам несколько советов с кодом !!!
vivek agravat
29

Вот мое расширение Swift 3 для получения шестнадцатеричной строки в кодировке base-16:

extension Data {
    var hexString: String {
        return map { String(format: "%02.2hhx", arguments: [$0]) }.joined()
    }
}
Phatmann
источник
Я заметил, что формат "% 02x" тоже работает. Я также не могу понять, что делает «хх»
Андрей
1
@andrei "hh" указывает программе форматирования строк обрабатывать ввод как символ. Однако в быстром темпе Data - это набор UInt8, поэтому он вам не нужен
wyu
27

Маркер устройства никогда не был строкой и уж тем более строкой в ​​кодировке UTF-8. Это данные. Это 32 байта непрозрачных данных.

Единственный допустимый способ преобразовать непрозрачные данные в строку - это закодировать их - обычно с помощью кодировки base64.

В Swift 3 / iOS 10 просто используйте этот Data base64EncodedString(options:)метод.

мэдди
источник
Отличие в новом типе данных. Я просто попытался преобразовать deviceToken в NSData, и теперь он печатает токен моего устройства, как и раньше. У вас есть пример того, как бы вы справились с этим без NSData? Потому что это кажется хакерским, но и более простым, чем то, что они, кажется, ожидают от нас.
user1537360
3
Я придерживаюсь того, что сказал. NSDataили Dataэто не имеет значения. Байты данных не являются и никогда не были строкой. В документации четко указано, что это непрозрачный набор данных. Тот факт, что ваш код работал, - это удача. Это всегда был неправильный способ справиться с этим. Просто преобразуйте данные в строку, кодируя данные base64. Это подходящее решение сейчас и раньше.
rmaddy
Декодирование Base64 не работает, если вы используете такой сервис, как Amazon SNS. Решения, которые преобразуют данные в шестнадцатеричные символы, такие как @satheeshwaran , создают строки токенов устройства, похожие на те, что были до изменений в SDK.
Александр
@Alexander Кто что сказал о декодировании Base64? Весь смысл вопроса и ответов заключается в том, чтобы кодировать необработанные данные, а не декодировать, в строку. Единственная причина сделать это - просмотреть необработанные данные. Конкретная схема кодирования значения не имеет. Другой ответ - использование кодировки base 16. Я упомянул использование кодировки base 64.
rmaddy
@rmaddy Я имел в виду кодировку Base64 в своем комментарии, приношу свои извинения.
Александр
15

Попробуй это:

func application(_ application: UIApplication, didRegisterForRemoteNotificationsWithDeviceToken deviceToken: Data) {

   let token = String(data: deviceToken.base64EncodedData(), encoding: .utf8)?.trimmingCharacters(in: CharacterSet.whitespaces).trimmingCharacters(in: CharacterSet(charactersIn: "<>")) 
}
пользователь
источник
2
Похоже, теперь он возвращает другой токен
ChikabuZ
8

попробуй это

if #available(iOS 10.0, *) {
   let deviceTokenString = deviceToken.reduce("", {$0 + String(format: "%02X", $1)})
}
синий где
источник
7

Swift 3

Самый лучший и простой способ.

deviceToken.base64EncodedString()
Маселко
источник
3
Это проще всего, но будьте осторожны с тем, что использует ваш сервер, если вы передаете токен. Многие API ожидают с кодировкой Hex или Base-16. Например, Django Push Notifications.
sww314
4

Этот ответ не был заявлен как официальный (видел его в комментарии), но именно это я в конечном итоге сделал, чтобы вернуть свой токен в порядок.

let tokenData = deviceToken as NSData
let token = tokenData.description

// remove any characters once you have token string if needed
token = token.replacingOccurrences(of: " ", with: "")
token = token.replacingOccurrences(of: "<", with: ""
token = token.replacingOccurrences(of: ">", with: "")
Билл Берджесс
источник
для нашего внутреннего API (Python) это оказалось «самым чистым» решением ¯ \ _ (ツ) _ / ¯
race_carr
1
Это не работает в iOS 10. Теперь описание возвращает только «32 байта».
edopelawi
@edopelawi ты забыл туда поставить as NSData. когда вы укажете NSData not Data, вернет правильное значение даже на iOS 10
Якуб
4
func application(_ application: UIApplication, didRegisterForRemoteNotificationsWithDeviceToken deviceToken: Data) {

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


}
PacoG
источник
4
Хотя этот блок кода может ответить на вопрос, было бы лучше, если бы вы могли дать небольшое объяснение того, почему он это делает.
Криспин
3

Я только что сделал это,

let token = String(format:"%@",deviceToken as CVarArg).components(separatedBy: CharacterSet.alphanumerics.inverted).joined(separator: "")

результат был таким же, как,

let token = deviceToken.map { String(format: "%02.2hhx", $0) }.joined()
Satheeshwaran
источник
0

Получите токен устройства в правильном формате.

func application(_ application: UIApplication, didRegisterForRemoteNotificationsWithDeviceToken deviceToken: Data) 
{
            var formattedToken = ""
            for i in 0..<deviceToken.count {
                formattedToken = formattedToken + String(format: "%02.2hhx", arguments: [deviceToken[i]])
            }
            print(formattedToken)
}
Басир Алам
источник