-
Notifications
You must be signed in to change notification settings - Fork 16
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Abstract layer for Push Notifications and Deep Linking #263
Abstract layer for Push Notifications and Deep Linking #263
Conversation
chore: added abstract layer for push notifications
@rnr The suggested approach is very close to what we have on the production app, and the approach is modular and can adopt new providers very easily. We can go with this approach. |
OpenEdX/AppDelegate.swift
Outdated
@@ -70,6 +75,13 @@ class AppDelegate: UIResponder, UIApplicationDelegate { | |||
open url: URL, options: [UIApplication.OpenURLOptionsKey: Any] = [:] | |||
) -> Bool { | |||
if let config = Container.shared.resolve(ConfigProtocol.self) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
How about using guard let here for better indentation thoughts?
OpenEdX/AppDelegate.swift
Outdated
@@ -125,4 +137,32 @@ class AppDelegate: UIResponder, UIApplicationDelegate { | |||
window?.rootViewController = RouteController() | |||
} | |||
|
|||
// Push Notifications | |||
func application(_ application: UIApplication, didRegisterForRemoteNotificationsWithDeviceToken deviceToken: Data) { | |||
if let pushManager = Container.shared.resolve(PushNotificationsManager.self) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
How about using guard let here?
OpenEdX/AppDelegate.swift
Outdated
} | ||
} | ||
func application(_ application: UIApplication, didFailToRegisterForRemoteNotificationsWithError error: Error) { | ||
if let pushManager = Container.shared.resolve(PushNotificationsManager.self) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
How about using guard let here and same for the following push notifications methods?
|
||
// Init manager | ||
public init(config: ConfigProtocol) { | ||
self.service = self.serviceFor(config: config) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The DeeplinkManager can register just one service Branch service. How about configuring it to register multiple services thoughts?
|
||
// Configure services | ||
func configureDeepLinkService(launchOptions: [UIApplication.LaunchOptionsKey: Any]?) { | ||
if let service = service { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
How about using guard let here?
func didReceiveRemoteNotification(userInfo: [AnyHashable: Any]) { | ||
guard let dictionary = userInfo as? [String: Any], shouldListenNotification(userinfo: userInfo) else { return } | ||
let link = PushLink(dictionary: dictionary) | ||
if let deepLinkManager = Container.shared.resolve(DeepLinkManager.self) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
How about moving it with guard let dictionary
?
self.providers = self.providersFor(config: config) | ||
self.listeners = self.listenersFor(config: config) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
self
is redundant.
} | ||
|
||
private func providersFor(config: ConfigProtocol) -> [PushNotificationsProvider] { | ||
var rProviders: [PushNotificationsProvider] = [] |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
How about renaming it to providers, pushProviders or pProviders? as rProviders doesn't go with the feature.
} | ||
|
||
private func listenersFor(config: ConfigProtocol) -> [PushNotificationsListener] { | ||
var rListeners: [PushNotificationsListener] = [] |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
How about renaming it to listeners, pushlisteners, or pListeners? as rListeners doesn't go with the feature.
|
||
import Foundation | ||
|
||
class BrazeListener: PushNotificationsListener { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I guess it should also implement didReceiveRemoteNotification
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
OpenEdX/AppDelegate.swift
Outdated
if config.facebook.enabled { | ||
ApplicationDelegate.shared.application( | ||
app, | ||
open: url, | ||
sourceApplication: options[UIApplication.OpenURLOptionsKey.sourceApplication] as? String, | ||
annotation: options[UIApplication.OpenURLOptionsKey.annotation] | ||
) | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Although it's not part of the underlying PR, but as we are, how about fixing it as well, it can return false, whereas the method should have been called for Google or Microsoft. It would be nice if we see if the URL is for Facebook only then it will be returning the status.
OpenEdX/AppDelegate.swift
Outdated
didReceiveRemoteNotification userInfo: [AnyHashable: Any], | ||
fetchCompletionHandler completionHandler: @escaping (UIBackgroundFetchResult) -> Void | ||
) { | ||
if let pushManager = Container.shared.resolve(PushNotificationsManager.self) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It would be nice if guard let can be used here as well.
OpenEdX/AppDelegate.swift
Outdated
|
||
// Deep link | ||
func configureDeepLinkServices(launchOptions: [UIApplication.LaunchOptionsKey: Any]?) { | ||
if let deepLinkManager = Container.shared.resolve(DeepLinkManager.self) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It would be nice if guard let can be used here as well.
|
||
// Init manager | ||
public init(config: ConfigProtocol) { | ||
services = servicesFor(config: config) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
How about renaming servicesFor
to something like configureServices(_ with config)
and call it. The method will populate services
for all enabled services?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I don't agree with this name - this func doesn't configure services but just inits and returns services for current config
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
ok
|
||
// This method do redirect with link from push notification | ||
func processLinkFromNotification(_ link: PushLink) { | ||
if anyServiceEnabled { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I guess this check isn't valid because it's for deep link services not for the push notifications.
} | ||
|
||
private func servicesFor(config: ConfigProtocol) -> [DeepLinkService] { | ||
var deepServices: [DeepLinkService] = [] |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We are not going with method name renaming then how about renaming it to services?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It's just a var
to return from a function. Why is its name so important if you are asking me to rename it a second time?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I believe the names should be meaningful according to their usage. It doesn't matter whether they are local or instance variables.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It can be renamed to services or deeplinkServices as per the usage.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Do you see a big difference between deepServices
and deeplinkServices
? Is it not "meaningful" enough?
Here #263 (comment) you asked me to rename to 'pushProviders' - why not to 'pushnotificationProviders' to ' be meaningful according to their usage'? I'm just following your example.
Don't you think we're just wasting time here?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
push is a term that is mostly used for push notifications. That's why it was suggested to use a shorter name, but deep isn't used for deep links. I don't think is time wasting, proper naming enhances the code readability.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I believe var deepServices
of type [DeepLinkService]
is enough 'readable' (especially since it's not used anywhere but these 5 lines of code)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
hmm ok if you are reluctant to make this change, then lets go with it.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
LGTM 👍
Draft PR to review and discuss an abstract implementation of Push notifications and deep link processing
What was done
PushNotificationsManager
andDeepLinkManager
to DIPushNotificationsProvider
andPushNotificationsListener
DeepLinkService
DeepLink
andPushLink
classes, which are used to redirect processes from deep link and from push notifications (DeepLink does not currently contain all possible types)configurations
forBraze
andBranch
servicesFCM
andBraze
providers and listenersBranch
deep linking serviceHow
PushNotificationsManager
worksPushNotificationsManager
creates arrays of providers and listeners given the configuration (which service(s) are enabled). When any AppDelegate method is called (e.g. registerWithDeviceToken, getRemoteNotification), PushNotificationsManager translates that call to providers or listeners. Each provider/listener has its own initialization and processing functions. A listener can also call theDeepLinkManager
to process the received notification data and trigger a redirect to the appropriate view.How
DeepLinkManager
worksDeepLinkManager
contains one service that will handle deep links. The type of redirect will be determined by theDeepLinkType
from theDeepLink
orPushLink
classes. The service processes the URL and, if it is a supported deep link, callsDeepLinkManager.processDeepLink()
with parameters and the manager performs the redirect.