Swift 3 - tokeny urządzeń są teraz analizowane jako „32 BYTES”

94

Właśnie zaktualizowałem Xcode 7 do 8 GM i pośród problemów ze zgodnością Swift 3 zauważyłem, że moje tokeny urządzenia przestały działać. Teraz czytają tylko „32 bajty”.

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

Przed aktualizacją mogłem po prostu wysłać NSData na mój serwer, ale teraz mam problem z analizowaniem tokena.

Czego tu brakuje?

Edycja: właśnie testuję konwersję z powrotem do NSData i widzę oczekiwane wyniki. Więc teraz jestem po prostu zdezorientowany nowym typem danych.

    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
źródło
2
Zmiana na NSDatapo prostu drukuje descriptionz NSData. Nadal nie otrzymujesz z tego ciągu.
rmaddy,

Odpowiedzi:

189
func application(_ application: UIApplication, didRegisterForRemoteNotificationsWithDeviceToken deviceToken: Data) {
    let token = deviceToken.map { String(format: "%02.2hhx", $0) }.joined()
    print(token)
}
Rok Gregorič
źródło
Operacja odwrotna (ciąg szesnastkowy -> dane) jest dostępna tutaj: stackoverflow.com/a/46663290/5252428
Rok Gregorič
4
%02xteż jest w porządku.
jqgsninimo
1
@Rok czy możesz wyjaśnić swoją odpowiedź? do jakiego nowego formatu został sformatowany token?
simo
@simo to konwersja danych na ciąg szesnastkowy, który można przekazać do usługi sieciowej / interfejsu API, aby móc wysyłać powiadomienia push.
Rok Gregorič
Dobry post z dobrym wyjaśnieniem różnic między %02.2hhxi %02x nshipster.com/apns-device-tokens/#overturned-in-ios-13
Rok Gregorič
35

Miałem ten sam problem. To jest moje rozwiązanie:

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
źródło
1
Jest to alternatywne podejście do kodowania NSDataciągu. Zasugerowałem użycie kodowania base64 w mojej odpowiedzi. Używa kodowania base16.
rmaddy
@rmaddy Twój sposób jest również interesujący, ale byłoby bardziej pomocne, gdybyś dał nam wskazówki dotyczące kodu !!!
vivek agravat
29

Oto moje rozszerzenie Swift 3, aby uzyskać ciąg szesnastkowy zakodowany w base-16:

extension Data {
    var hexString: String {
        return map { String(format: "%02.2hhx", arguments: [$0]) }.joined()
    }
}
phatmann
źródło
Zauważyłem, że format „% 02x” również działa. Nie rozumiem też, co robi „hh”
andrei
1
@andrei "hh" mówi programowi formatującemu, aby traktował wejście jako znak. Jednak w szybkim tempie Data to zbiór UInt8, więc nie potrzebujesz tego
wyu
27

Token urządzenia nigdy nie był łańcuchem i na pewno nie był łańcuchem zakodowanym w UTF-8. To dane. To 32 bajty nieprzezroczystych danych.

Jedynym prawidłowym sposobem konwersji nieprzezroczystych danych na ciąg jest zakodowanie ich - zwykle za pomocą kodowania base64.

W Swift 3 / iOS 10 po prostu użyj tej Data base64EncodedString(options:)metody.

rmaddy
źródło
Cóż, różnica polega na nowym typie danych. Właśnie próbowałem przekonwertować deviceToken na NSData i teraz wyświetla token mojego urządzenia, tak jak poprzednio. Czy masz przykład, jak poradziłbyś sobie z tym bez NSData? Ponieważ wydaje się to dziwaczne, ale także prostsze niż to, czego się od nas oczekuje.
user1537360
3
Trzymam się tego, co powiedziałem. NSDatalub Datanie ma to znaczenia. Bajty danych nie są i nigdy nie były ciągiem. Dokumentacja jasno stwierdza, że ​​jest to nieprzejrzysty zestaw danych. Fakt, że Twój kod działał, to szczęście. Zawsze był to niewłaściwy sposób radzenia sobie z tym. Po prostu przekonwertuj dane na ciąg przez kodowanie base64. To właściwe rozwiązanie teraz i wcześniej.
rmaddy,
Dekodowanie Base64 nie działa, jeśli korzystasz z usługi takiej jak Amazon SNS. Rozwiązania, które konwertują dane na znaki szesnastkowe, takie jak @satheeshwaran , generują ciągi znaków tokena urządzenia, które przypominają te sprzed zmian w SDK.
Alexander
@Alexander Kto powiedział coś o dekodowaniu Base64? Głównym celem pytania i odpowiedzi jest zakodowanie surowych danych, a nie dekodowanie, w ciąg. Jedynym powodem, aby to zrobić, jest przeglądanie surowych danych. Konkretny schemat kodowania nie ma znaczenia. Inną odpowiedzią jest użycie kodowania podstawowego 16. Wspomniałem o kodowaniu base 64.
rmaddy
@rmaddy W moim komentarzu miałem na myśli kodowanie Base64, przepraszam.
Alexander
15

Spróbuj tego:

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

   let token = String(data: deviceToken.base64EncodedData(), encoding: .utf8)?.trimmingCharacters(in: CharacterSet.whitespaces).trimmingCharacters(in: CharacterSet(charactersIn: "<>")) 
}
użytkownik
źródło
2
Wygląda na to, że teraz zwraca inny token
ChikabuZ
8

Spróbuj tego

if #available(iOS 10.0, *) {
   let deviceTokenString = deviceToken.reduce("", {$0 + String(format: "%02X", $1)})
}
niebieski
źródło
7

Szybki 3

Najlepszy i najłatwiejszy sposób.

deviceToken.base64EncodedString()
Maselko
źródło
3
Jest to najłatwiejsze, ale uważaj, czego używa twój serwer, jeśli przekazujesz token. Wiele interfejsów API oczekuje z kodowaniem szesnastkowym lub Base-16. Na przykład powiadomienia wypychane Django.
sww314
4

Ta odpowiedź nie została podana jako oficjalna odpowiedź (widziałem to w komentarzu), ale ostatecznie to zrobiłem, aby przywrócić porządek w moim tokenie.

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
źródło
na potrzeby naszego zaplecza API (Python)
okazało
1
To nie działa w iOS 10. Opis teraz zwraca tylko „32 bajty”.
edopelawi
@edopelawi zapomniałeś as NSDatatam wstawić . jeśli wstawisz jako NSData not Data zwróci poprawną wartość nawet na 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
źródło
4
Chociaż ten blok kodu może odpowiedzieć na pytanie, najlepiej byłoby, gdybyś mógł podać małe wyjaśnienie, dlaczego tak się dzieje.
Crispin
3

Właśnie to zrobiłem,

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

dał wynik taki sam jak

let token = deviceToken.map { String(format: "%02.2hhx", $0) }.joined()
Satheeshwaran
źródło
0

Uzyskaj token urządzenia o odpowiednim formacie.

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
źródło