appcues / appcues-ios-sdk

The Appcues iOS SDK
https://www.appcues.com/mobile
MIT License
8 stars 2 forks source link

Add one line push config #526

Closed mmaatttt closed 4 months ago

mmaatttt commented 5 months ago

This PR is very much building on the approach for the UIScrollViewDelegate swizzling added and explained in https://github.com/appcues/appcues-ios-sdk/pull/479.

As noted in the Appcues.swift docs, there's 5 things that need to happen for automatic push config:

  1. Calling UIApplication.registerForRemoteNotifications().
    • This is straightforward.
  2. Implementing UIApplicationDelegate.application(_:didRegisterForRemoteNotificationsWithDeviceToken:) to call Appcues.setPushToken(_:).
    • Because we can safely assume there's always a UIApplicationDelegate, we just need to add the placeholder implementation if there is none, and then swizzle the method.
  3. Ensuring UNUserNotificationCenter.current().delegate is set.
    • This is the same approach as the UIScrollViewDelegate getter swizzling.
  4. Implementing UNUserNotificationCenterDelegate.userNotificationCenter(_:didReceive:withCompletionHandler:) to call Appcues.didReceiveNotification(response:completionHandler:).
    • This is swizzled with a placeholder implementation from the getter of the UNUserNotificationCenterDelegate (same ideas as the UIScrollView approach.
  5. Implementing UNUserNotificationCenterDelegate.userNotificationCenter(_:willPresent:withCompletionHandler:).
    • This is swizzled with a placeholder implementation from the getter of the UNUserNotificationCenterDelegate (same ideas as the UIScrollView approach.

AppcuesUNUserNotificationCenterDelegate has a static instance that the various swizzled methods call. To properly support multiple SDK instances (that each can be auto configured!) there's a list of pushMonitors that we can operate from (using the weak wrapper approach from AnalyticsPublisher).

Structural Notes

  1. I moved the implementation of Appcues.setPushToken into PushMonitor so that the swizzled implementation can also easy access it.
  2. Instead of having the almost the same long swizzling block for UIScrollViewDelegate, UIApplicationDelegate, and UNUserNotificationCenterDelegate, I created Swizzler with one extra parameter.

What's Next

  1. I've thought about adding config options to control what specifically gets automatically swizzled, but for now I'm comfortable with the all-or-nothing approach.
  2. I'd like tests for the swizzled behaviour (and the various scenarios of what's already implemented in the existing app), but I haven't yet looked into what's doable there.