Search code examples
apple-push-notificationsazure-notificationhubdevicetokenremote-notifications

How to deal with old iOS notification tokens remaining valid after new token and vendorId are generated?


I am using Azure Notification Hub to send remote notifications, at the moment only to iOS.

I initially realised that my device was receiving 5 notifications of the same notification event. I checked and debugged my code and it indeed correctly checks if the provided token to register is not already registered.

I then checked my database and realised that I do not have duplicates, but 5 different device tokens for the one device and user account. They each had been generated every-time I uninstall and then re-install the app on my device.

Given that, I would think that when an app is uninstalled, the token generated before is made invalid by Apples systems? I assumed this because upon re-installation, a new token is generated, different to the previous one. Why would the older tokens remain valid; whats the use case for such.

It seems other people are having this issue for iOS development, including Android too.

Using VendorId to identify a device is proving pointless, as some are finding out, that it also changes upon fresh installation. When I uninstall the app and reinstall I get a new vendorId plus a new notification token, leaving the previous ones still valid, opening up the ability to receive multiple of one instance of a notification (since the back end cannot match the new two values to anything existing in the database).

Any advice on this please? I am absolutely lost on this?

Update: I certainly need a user to have more than one device token at a time, in case they are logged in on multiple devices.


Solution

  • You are basically going to have to identify a device yourself, by saving an ID, using UUID, into Keychain. Keychain because the data persists over multiple installs.

    Below is a quick rushed implementation using Locksmith:

    import UIKit
    import Locksmith
    
    
    class Keychain
    {
        private static let AccountName = "MyAppName";
        private static let CustomDeviceIdKey = "CustomDeviceIdKey";
        
        private static func AccountData() -> Dictionary<String, Any>?
        {
            return Locksmith.loadDataForUserAccount(userAccount: AccountName)
        }
        
        private static func GenerateCustomDeviceId() -> Bool
        {
            do{
                try Locksmith.saveData(data: [CustomDeviceIdKey: UUID().uuidString], forUserAccount: AccountName)
                return true
            }catch{
                print("Failed to save custom device id: \(error.localizedDescription)")
                return false
            }
        }
        
        static func CustomDeviceId() -> String
        {
            let data = AccountData()
            if let result = data?[CustomDeviceIdKey] as? String{
                return result
            }else{
                if GenerateCustomDeviceId(){
                    return CustomDeviceId()
                }
                return ""
            }
        }
    }
    

    And then later you just go:

    func SomeMethodThatNeedsDeviceId(){
        var customDeviceIdThatPersistsOverInstalls = Keychain.CustomDeviceId()
    }
    

    And that is what you use in place of VendorId