Swift 3 - tokens de dispositivo agora estão sendo analisados ​​como '32BYTES'

94

Acabei de atualizar do Xcode 7 para o 8 GM e em meio aos problemas de compatibilidade do Swift 3, percebi que os tokens do meu dispositivo pararam de funcionar. Eles agora lêem apenas '32BYTES'.

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

Antes da atualização, eu era capaz de simplesmente enviar o NSData ao meu servidor, mas agora estou tendo dificuldade em analisar o token.

O que estou perdendo aqui?

Edit: Acabei de testar a conversão de volta para NSData e estou vendo os resultados esperados. Agora estou confuso sobre o novo tipo de dados.

    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
}
usuário1537360
fonte
2
Alterar para NSDatasimplesmente imprime o descriptionde NSData. Você ainda não entendeu nada disso.
rmaddy

Respostas:

189
func application(_ application: UIApplication, didRegisterForRemoteNotificationsWithDeviceToken deviceToken: Data) {
    let token = deviceToken.map { String(format: "%02.2hhx", $0) }.joined()
    print(token)
}
Rok Gregorič
fonte
A operação inversa (hexadecimal -> Dados) está disponível aqui: stackoverflow.com/a/46663290/5252428
Rok Gregorič
4
%02xestá bem também.
jqgsninimo
1
@Rok, você pode explicar sua resposta? qual é o novo formato para o qual o token foi formatado?
simo
@simo é uma conversão de Dados em string hexadecimal que você pode passar para um serviço da web / API para poder enviar notificações push.
Rok Gregorič de
Uma boa postagem com uma boa explicação das diferenças entre %02.2hhxe %02x nshipster.com/apns-device-tokens/#overturned-in-ios-13
Rok Gregorič
35

Eu tive o mesmo problema. Esta é a minha solução:

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)
}
Oleg
fonte
1
Esta é uma abordagem alternativa para codificar o NSDataem uma string. Sugeri usar a codificação base64 em minha resposta. Isso usa codificação base16.
rmaddy de
@rmaddy, seu jeito também é interessante, mas seria mais útil se você nos desse alguma orientação com o código !!!
vivek agravat
29

Aqui está minha extensão Swift 3 para obter uma string hexadecimal codificada em base 16:

extension Data {
    var hexString: String {
        return map { String(format: "%02.2hhx", arguments: [$0]) }.joined()
    }
}
Phatmann
fonte
Notei que o formato "% 02x" também funciona. Eu também não consigo entender o que "hh" significa
andrei
1
@andrei "hh" diz ao formatador de string para tratar a entrada como um char. No entanto, no swift, Data é uma coleção de UInt8, então você não precisa dele
wyu
27

O token do dispositivo nunca foi uma string e certamente não uma string codificada em UTF-8. São dados. São 32 bytes de dados opacos.

A única maneira válida de converter os dados opacos em uma string é codificá-los - normalmente por meio de uma codificação base64.

No Swift 3 / iOS 10, basta usar o Data base64EncodedString(options:)método.

rmaddy
fonte
Bem, a diferença aqui é o novo tipo de dados. Acabei de tentar converter o deviceToken em NSData e agora imprime o token do meu dispositivo como antes. Você tem um exemplo de como lidaria com isso sem o NSData? Porque parece estranho, mas também mais direto do que o que eles parecem esperar que façamos.
user1537360
3
Eu mantenho o que disse. NSDataou Datanão importa. Os bytes dos dados não são e nunca foram uma string. A documentação afirma claramente que se trata de um conjunto opaco de dados. O fato de seu código funcionar é uma sorte. Sempre foi a maneira incorreta de lidar com isso. Basta converter os dados em uma string codificando os dados em base64. Essa é a solução adequada agora e antes.
rmaddy
A decodificação Base64 não funciona se você usar um serviço como o Amazon SNS. Soluções que convertem os dados em caracteres hexadecimais, como @satheeshwaran , produzem strings de token de dispositivo que se parecem com as anteriores às alterações no SDK.
Alexander
@Alexander Quem falou em decodificação Base64? O objetivo da pergunta e das respostas é codificar os dados brutos, não decodificar, em uma string. A única razão para fazer isso é visualizar os dados brutos. O esquema de codificação específico é irrelevante. A outra resposta é usar a codificação de base 16. Mencionei o uso de codificação de base 64.
rmaddy de
@rmaddy, eu quis dizer codificação Base64 em meu comentário, minhas desculpas.
Alexandre de
15

Experimente isto:

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

   let token = String(data: deviceToken.base64EncodedData(), encoding: .utf8)?.trimmingCharacters(in: CharacterSet.whitespaces).trimmingCharacters(in: CharacterSet(charactersIn: "<>")) 
}
do utilizador
fonte
2
Parece que agora ele retorna um token diferente
ChikabuZ
8

tente isso

if #available(iOS 10.0, *) {
   let deviceTokenString = deviceToken.reduce("", {$0 + String(format: "%02X", $1)})
}
bluenowhere
fonte
7

Swift 3

A maneira melhor e mais fácil.

deviceToken.base64EncodedString()
Maselko
fonte
3
Isso é mais fácil, mas tome cuidado com o que seu servidor está usando se você estiver entregando o token. Muitos API esperam com codificação Hex ou Base-16. Por exemplo, Django Push Notifications.
sww314
4

Esta não foi declarada como uma resposta oficial (vi em um comentário), mas foi o que fiz para colocar meu token de volta em ordem.

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: "")
Bill Burgess
fonte
para fins de nossa API de back-end (Python), isso acabou sendo a solução "mais limpa" ¯ \ _ (ツ) _ / ¯
race_carr
1
Isso não funciona no iOS 10. A descrição agora retorna apenas "32 bytes".
edopelawi
@edopelawi você esqueceu de colocar as NSData lá. quando você coloca como NSData, não os dados retornam o valor correto mesmo no iOS 10
Jakub
4
func application(_ application: UIApplication, didRegisterForRemoteNotificationsWithDeviceToken deviceToken: Data) {

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


}
PacoG
fonte
4
Embora este bloco de código possa responder à pergunta, seria melhor se você pudesse fornecer uma pequena explicação de por que isso acontece.
Crispin
3

Eu acabei de fazer isso,

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

deu o mesmo resultado que,

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

Obtenha o token do dispositivo com o formato adequado.

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)
}
Basir Alam
fonte