How can I register user's ios device for recei

2019-09-16 04:58发布

问题:

Currently in my appDelegate in didFinishLaunchingWithOptions I'm calling the following methods:

let notificationTypes: UIUserNotificationType = [UIUserNotificationType.alert, UIUserNotificationType.badge, UIUserNotificationType.sound]
let pushNotificationSettings = UIUserNotificationSettings(types: notificationTypes, categories: nil)

application.registerUserNotificationSettings(pushNotificationSettings)
application.registerForRemoteNotifications()

I also have the following methods implemented in AppDelegate:

didRegisterForRemoteNotificationsWithDeviceToken

didFailToRegisterForRemoteNotificationsWithError

didReceiveRemoteNotification

When user starts my app for the first time, he sees the popup that ask him for permission about receiving push messages.

I want to postpone showing this popup until user manually chooses specific option in Settings menu.

In Settings menu I have a UISwitch and I'm checking the state of it every time user changes it:

func messagesStateChanged(_ sender: UISwitch){
    if(sender.isOn){
        defaults.set("true", forKey: "popupMessages")
        defaults.synchronize()
    } else {
        defaults.set("false", forKey: "popupMessages")
        defaults.synchronize()
    }
}

Is there a way of moving the push handling from app delegate to the settings menu, so that user sees the popup not immediately after starting the app, but when he selects the UISwitch? Also - will that work for future and all push functionality works well if I move it to Settings class?

回答1:

You can access the UIApplication instance provided to you in didFinishLaunchingWithOptions by accessing UIApplication.shared class property from anywhere in your application, where you have access to UIKit. I usually create a wrapper class that manages notifications, which would allow you to inject UIApplication dependency when you're writing unit tests later.

The register calls should happen when you're activating the switch:

func messagesStateChanged(_ sender: UISwitch){
    if(sender.isOn){
        defaults.set("true", forKey: "popupMessages")
        defaults.synchronize()

        let application = UIApplication.shared
        let notificationTypes: UIUserNotificationType = [UIUserNotificationType.alert, UIUserNotificationType.badge, UIUserNotificationType.sound]
        let pushNotificationSettings = UIUserNotificationSettings(types: notificationTypes, categories: nil)

        application.registerUserNotificationSettings(pushNotificationSettings)
        application.registerForRemoteNotifications()
    } else {
        defaults.set("false", forKey: "popupMessages")
        defaults.synchronize()
    }
}

A simplified wrapper class I use (singleton used for simplicity):

class NotificationManager {
    static var shared = NotificationManager()

    private var application : UIApplication?

    func setup(application: UIApplication) {
        self.application = application
    }

    func register () {
        guard let application = application else {
            print("Attempt to register without calling setup")
            return
        }

        let notificationTypes: UIUserNotificationType = [UIUserNotificationType.alert, UIUserNotificationType.badge, UIUserNotificationType.sound]
        let pushNotificationSettings = UIUserNotificationSettings(types: notificationTypes, categories: nil)

        application.registerUserNotificationSettings(pushNotificationSettings)
        application.registerForRemoteNotifications()
    }
}

This requires that you call setup in your didFinishLaunchingMethod:

NotificationManager.shared.setup(application: application)

But then wherever you need to register for the notifications:

func messagesStateChanged(_ sender: UISwitch){
    if(sender.isOn){
        ...
        NotificationManager.shared.register()
    }
    ...
}

This allows you to easily test NotificationManager class by injecting an UIApplication mock object later on and the class also nicely encapsulates all notification related logic. With some modifications it can easily keep track if you already registered, keep track of the push token you received and even handle iOS 8/9 backward compatibility without duplicating boilerplate code.