From a6fc40d7f0a306a69a56983f79d0d3e9a1034b4e Mon Sep 17 00:00:00 2001 From: Anton Yarmolenko Date: Wed, 24 Jan 2024 15:22:26 +0100 Subject: [PATCH 1/7] chore: added abstract layer for push notifications --- Core/Core.xcodeproj/project.pbxproj | 4 ++ .../Configuration/Config/BrazeConfig.swift | 32 +++++++++ Core/Core/Configuration/Config/Config.swift | 1 + .../CoreTests/Configuration/ConfigTests.swift | 10 +++ OpenEdX.xcodeproj/project.pbxproj | 64 ++++++++++++++++- OpenEdX/AppDelegate.swift | 14 ++++ OpenEdX/DI/AppAssembly.swift | 6 ++ .../AnalyticsManager}/AnalyticsManager.swift | 0 .../MainScreenAnalytics.swift | 0 .../Listeners/BrazeListener.swift | 14 ++++ .../Listeners/FCMListener.swift | 14 ++++ .../Providers/BrazeProvider.swift | 17 +++++ .../Providers/FCMProvider.swift | 17 +++++ .../PushNotificationsManager.swift | 68 +++++++++++++++++++ 14 files changed, 259 insertions(+), 2 deletions(-) create mode 100644 Core/Core/Configuration/Config/BrazeConfig.swift rename OpenEdX/{ => Managers/AnalyticsManager}/AnalyticsManager.swift (100%) rename OpenEdX/{ => Managers/AnalyticsManager}/MainScreenAnalytics.swift (100%) create mode 100644 OpenEdX/Managers/PushNotificationsManager/Listeners/BrazeListener.swift create mode 100644 OpenEdX/Managers/PushNotificationsManager/Listeners/FCMListener.swift create mode 100644 OpenEdX/Managers/PushNotificationsManager/Providers/BrazeProvider.swift create mode 100644 OpenEdX/Managers/PushNotificationsManager/Providers/FCMProvider.swift create mode 100644 OpenEdX/Managers/PushNotificationsManager/PushNotificationsManager.swift diff --git a/Core/Core.xcodeproj/project.pbxproj b/Core/Core.xcodeproj/project.pbxproj index 8b9dae2f0..0f61e2d47 100644 --- a/Core/Core.xcodeproj/project.pbxproj +++ b/Core/Core.xcodeproj/project.pbxproj @@ -125,6 +125,7 @@ 07DDFCBD29A780BB00572595 /* UINavigationController+Animation.swift in Sources */ = {isa = PBXBuildFile; fileRef = 07DDFCBC29A780BB00572595 /* UINavigationController+Animation.swift */; }; 07E0939F2B308D2800F1E4B2 /* Data_Certificate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 07E0939E2B308D2800F1E4B2 /* Data_Certificate.swift */; }; A53A32352B233DEC005FE38A /* ThemeConfig.swift in Sources */ = {isa = PBXBuildFile; fileRef = A53A32342B233DEC005FE38A /* ThemeConfig.swift */; }; + A5F4E7B52B61544A00ACD166 /* BrazeConfig.swift in Sources */ = {isa = PBXBuildFile; fileRef = A5F4E7B42B61544A00ACD166 /* BrazeConfig.swift */; }; BA30427F2B20B320009B64B7 /* SocialAuthError.swift in Sources */ = {isa = PBXBuildFile; fileRef = BA30427D2B20B299009B64B7 /* SocialAuthError.swift */; }; BA593F1C2AF8E498009ADB51 /* ScrollSlidingTabBar.swift in Sources */ = {isa = PBXBuildFile; fileRef = BA593F1B2AF8E498009ADB51 /* ScrollSlidingTabBar.swift */; }; BA593F1E2AF8E4A0009ADB51 /* FrameReader.swift in Sources */ = {isa = PBXBuildFile; fileRef = BA593F1D2AF8E4A0009ADB51 /* FrameReader.swift */; }; @@ -294,6 +295,7 @@ 60153262DBC2F9E660D7E11B /* Pods-App-Core.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-App-Core.release.xcconfig"; path = "Target Support Files/Pods-App-Core/Pods-App-Core.release.xcconfig"; sourceTree = ""; }; 9D5B06CAA99EA5CD49CBE2BB /* Pods-App-Core.debugdev.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-App-Core.debugdev.xcconfig"; path = "Target Support Files/Pods-App-Core/Pods-App-Core.debugdev.xcconfig"; sourceTree = ""; }; A53A32342B233DEC005FE38A /* ThemeConfig.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ThemeConfig.swift; sourceTree = ""; }; + A5F4E7B42B61544A00ACD166 /* BrazeConfig.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = BrazeConfig.swift; sourceTree = ""; }; BA30427D2B20B299009B64B7 /* SocialAuthError.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SocialAuthError.swift; sourceTree = ""; }; BA593F1B2AF8E498009ADB51 /* ScrollSlidingTabBar.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ScrollSlidingTabBar.swift; sourceTree = ""; }; BA593F1D2AF8E4A0009ADB51 /* FrameReader.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = FrameReader.swift; sourceTree = ""; }; @@ -749,6 +751,7 @@ 0604C9A92B22FACF00AD5DBF /* UIComponentsConfig.swift */, 0727876F28D23411002E9142 /* Config.swift */, DBF6F2402B014ADA0098414B /* FirebaseConfig.swift */, + A5F4E7B42B61544A00ACD166 /* BrazeConfig.swift */, DBF6F2492B0380E00098414B /* FeaturesConfig.swift */, DBF6F2452B01DAFE0098414B /* AgreementConfig.swift */, BAFB99812B0E2354007D09F9 /* FacebookConfig.swift */, @@ -1054,6 +1057,7 @@ DBF6F2412B014ADA0098414B /* FirebaseConfig.swift in Sources */, 072787B628D37A0E002E9142 /* Validator.swift in Sources */, 0236961D28F9A2D200EEF206 /* Data_AuthResponse.swift in Sources */, + A5F4E7B52B61544A00ACD166 /* BrazeConfig.swift in Sources */, 02AFCC182AEFDB24000360F0 /* ThirdPartyMailClient.swift in Sources */, 0233D5712AF13EC800BAC8BD /* SelectMailClientView.swift in Sources */, BAFB99842B0E282E007D09F9 /* MicrosoftConfig.swift in Sources */, diff --git a/Core/Core/Configuration/Config/BrazeConfig.swift b/Core/Core/Configuration/Config/BrazeConfig.swift new file mode 100644 index 000000000..8410bf3f8 --- /dev/null +++ b/Core/Core/Configuration/Config/BrazeConfig.swift @@ -0,0 +1,32 @@ +// +// BrazeConfig.swift +// Core +// +// Created by Anton Yarmolenka on 24/01/2024. +// + +import Foundation + +private enum BrazeKeys: String { + case enabled = "ENABLED" + case pushNotificationsEnabled = "PUSH_NOTIFICATIONS_ENABLED" +} + +public final class BrazeConfig: NSObject { + public var enabled: Bool = false + public var pushNotificationsEnabled: Bool = false + + init(dictionary: [String: AnyObject]) { + super.init() + enabled = dictionary[BrazeKeys.enabled.rawValue] as? Bool == true + let pushNotificationsEnabled = dictionary[BrazeKeys.pushNotificationsEnabled.rawValue] as? Bool ?? false + self.pushNotificationsEnabled = enabled && pushNotificationsEnabled + } +} + +private let brazeKey = "BRAZE" +extension Config { + public var braze: BrazeConfig { + BrazeConfig(dictionary: self[brazeKey] as? [String: AnyObject] ?? [:]) + } +} diff --git a/Core/Core/Configuration/Config/Config.swift b/Core/Core/Configuration/Config/Config.swift index 7fcbe1c94..5c1693c97 100644 --- a/Core/Core/Configuration/Config/Config.swift +++ b/Core/Core/Configuration/Config/Config.swift @@ -25,6 +25,7 @@ public protocol ConfigProtocol { var theme: ThemeConfig { get } var uiComponents: UIComponentsConfig { get } var discovery: DiscoveryConfig { get } + var braze: BrazeConfig { get } } public enum TokenType: String { diff --git a/Core/CoreTests/Configuration/ConfigTests.swift b/Core/CoreTests/Configuration/ConfigTests.swift index 32638e484..e9b8a0ce7 100644 --- a/Core/CoreTests/Configuration/ConfigTests.swift +++ b/Core/CoreTests/Configuration/ConfigTests.swift @@ -52,6 +52,10 @@ class ConfigTests: XCTestCase { ], "APPLE_SIGNIN": [ "ENABLED": true + ], + "BRAZE": [ + "ENABLED": true, + "PUSH_NOTIFICATIONS_ENABLED": true ] ] @@ -115,4 +119,10 @@ class ConfigTests: XCTestCase { XCTAssertTrue(config.appleSignIn.enabled) } + + func testBrazeConfigInitialization() { + let config = Config(properties: properties) + + XCTAssertTrue(config.braze.pushNotificationsEnabled) + } } diff --git a/OpenEdX.xcodeproj/project.pbxproj b/OpenEdX.xcodeproj/project.pbxproj index 072473af5..b02ea1814 100644 --- a/OpenEdX.xcodeproj/project.pbxproj +++ b/OpenEdX.xcodeproj/project.pbxproj @@ -45,6 +45,11 @@ 07D5DA3E28D075AB00752FD9 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 07D5DA3D28D075AB00752FD9 /* Assets.xcassets */; }; 07D5DA4128D075AB00752FD9 /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 07D5DA3F28D075AB00752FD9 /* LaunchScreen.storyboard */; }; 95C140F3BDF778364986E83B /* Pods_App_OpenEdX.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = F138C15C3A2515F8F94DAA8B /* Pods_App_OpenEdX.framework */; }; + A500668B2B613ED10024680B /* PushNotificationsManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = A500668A2B613ED10024680B /* PushNotificationsManager.swift */; }; + A500668D2B6143000024680B /* FCMProvider.swift in Sources */ = {isa = PBXBuildFile; fileRef = A500668C2B6143000024680B /* FCMProvider.swift */; }; + A50066912B61467B0024680B /* BrazeProvider.swift in Sources */ = {isa = PBXBuildFile; fileRef = A50066902B61467B0024680B /* BrazeProvider.swift */; }; + A50066932B614DCD0024680B /* FCMListener.swift in Sources */ = {isa = PBXBuildFile; fileRef = A50066922B614DCD0024680B /* FCMListener.swift */; }; + A50066952B614DEF0024680B /* BrazeListener.swift in Sources */ = {isa = PBXBuildFile; fileRef = A50066942B614DEF0024680B /* BrazeListener.swift */; }; BA3042792B1F7147009B64B7 /* MSAL in Frameworks */ = {isa = PBXBuildFile; productRef = BA3042782B1F7147009B64B7 /* MSAL */; }; E0D6E6A32B1626B10089F9C9 /* Theme.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = E0D6E6A22B1626B10089F9C9 /* Theme.framework */; }; E0D6E6A42B1626D60089F9C9 /* Theme.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = E0D6E6A22B1626B10089F9C9 /* Theme.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; @@ -115,6 +120,11 @@ 6F54C19C823A769E18923FA8 /* Pods-App-OpenEdX.debugstage.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-App-OpenEdX.debugstage.xcconfig"; path = "Target Support Files/Pods-App-OpenEdX/Pods-App-OpenEdX.debugstage.xcconfig"; sourceTree = ""; }; 8284179FC05AEE2591573E20 /* Pods-App-OpenEdX.debugdev.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-App-OpenEdX.debugdev.xcconfig"; path = "Target Support Files/Pods-App-OpenEdX/Pods-App-OpenEdX.debugdev.xcconfig"; sourceTree = ""; }; A24D6A8E1BC4DF46AD68904C /* Pods-App-OpenEdX.releaseprod.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-App-OpenEdX.releaseprod.xcconfig"; path = "Target Support Files/Pods-App-OpenEdX/Pods-App-OpenEdX.releaseprod.xcconfig"; sourceTree = ""; }; + A500668A2B613ED10024680B /* PushNotificationsManager.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PushNotificationsManager.swift; sourceTree = ""; }; + A500668C2B6143000024680B /* FCMProvider.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FCMProvider.swift; sourceTree = ""; }; + A50066902B61467B0024680B /* BrazeProvider.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BrazeProvider.swift; sourceTree = ""; }; + A50066922B614DCD0024680B /* FCMListener.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FCMListener.swift; sourceTree = ""; }; + A50066942B614DEF0024680B /* BrazeListener.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BrazeListener.swift; sourceTree = ""; }; A89AD827F52CF6A6B903606E /* Pods-App-OpenEdX.releasestage.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-App-OpenEdX.releasestage.xcconfig"; path = "Target Support Files/Pods-App-OpenEdX/Pods-App-OpenEdX.releasestage.xcconfig"; sourceTree = ""; }; BB08ACD2CCA33D6DDDDD31B4 /* Pods-App-OpenEdX.releasedev.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-App-OpenEdX.releasedev.xcconfig"; path = "Target Support Files/Pods-App-OpenEdX/Pods-App-OpenEdX.releasedev.xcconfig"; sourceTree = ""; }; E0D6E6A22B1626B10089F9C9 /* Theme.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; path = Theme.framework; sourceTree = BUILT_PRODUCTS_DIR; }; @@ -200,8 +210,7 @@ 07D5DA3428D075AA00752FD9 /* AppDelegate.swift */, 0770DE1628D080A1006D8A5D /* RouteController.swift */, 0770DE1F28D0858A006D8A5D /* Router.swift */, - 0298DF2F2A4EF7230023A257 /* AnalyticsManager.swift */, - 02F175302A4DA95B0019CD70 /* MainScreenAnalytics.swift */, + A50066882B613E800024680B /* Managers */, 0293A2012A6FC9E30090A336 /* Data */, 0727878C28D347B2002E9142 /* View */, 0770DE1A28D084BC006D8A5D /* DI */, @@ -248,6 +257,52 @@ path = Pods; sourceTree = ""; }; + A50066872B613E4B0024680B /* PushNotificationsManager */ = { + isa = PBXGroup; + children = ( + A500668A2B613ED10024680B /* PushNotificationsManager.swift */, + A50066962B614F0C0024680B /* Providers */, + A50066972B614F2B0024680B /* Listeners */, + ); + path = PushNotificationsManager; + sourceTree = ""; + }; + A50066882B613E800024680B /* Managers */ = { + isa = PBXGroup; + children = ( + A50066872B613E4B0024680B /* PushNotificationsManager */, + A50066892B613E990024680B /* AnalyticsManager */, + ); + path = Managers; + sourceTree = ""; + }; + A50066892B613E990024680B /* AnalyticsManager */ = { + isa = PBXGroup; + children = ( + 0298DF2F2A4EF7230023A257 /* AnalyticsManager.swift */, + 02F175302A4DA95B0019CD70 /* MainScreenAnalytics.swift */, + ); + path = AnalyticsManager; + sourceTree = ""; + }; + A50066962B614F0C0024680B /* Providers */ = { + isa = PBXGroup; + children = ( + A500668C2B6143000024680B /* FCMProvider.swift */, + A50066902B61467B0024680B /* BrazeProvider.swift */, + ); + path = Providers; + sourceTree = ""; + }; + A50066972B614F2B0024680B /* Listeners */ = { + isa = PBXGroup; + children = ( + A50066922B614DCD0024680B /* FCMListener.swift */, + A50066942B614DEF0024680B /* BrazeListener.swift */, + ); + path = Listeners; + sourceTree = ""; + }; /* End PBXGroup section */ /* Begin PBXNativeTarget section */ @@ -413,9 +468,11 @@ isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; files = ( + A500668B2B613ED10024680B /* PushNotificationsManager.swift in Sources */, 0293A2052A6FCD430090A336 /* CoursePersistence.swift in Sources */, 020CA5D92AA0A25300970AAF /* AppStorage.swift in Sources */, 0298DF302A4EF7230023A257 /* AnalyticsManager.swift in Sources */, + A50066912B61467B0024680B /* BrazeProvider.swift in Sources */, 0293A2072A6FCDA30090A336 /* DiscoveryPersistence.swift in Sources */, 07D5DA3528D075AA00752FD9 /* AppDelegate.swift in Sources */, 02F175312A4DA95B0019CD70 /* MainScreenAnalytics.swift in Sources */, @@ -423,11 +480,14 @@ 0770DE5028D0A707006D8A5D /* NetworkAssembly.swift in Sources */, 0293A2032A6FCA590090A336 /* CorePersistence.swift in Sources */, 0770DE1E28D084E8006D8A5D /* AppAssembly.swift in Sources */, + A50066932B614DCD0024680B /* FCMListener.swift in Sources */, + A500668D2B6143000024680B /* FCMProvider.swift in Sources */, 025AD4AC2A6FB95C00AB8FA7 /* DatabaseManager.swift in Sources */, 024E69202AEFC3FB00FA0B59 /* MainScreenViewModel.swift in Sources */, 0770DE2028D0858A006D8A5D /* Router.swift in Sources */, 0293A2092A6FCDE50090A336 /* DashboardPersistence.swift in Sources */, 0770DE1728D080A1006D8A5D /* RouteController.swift in Sources */, + A50066952B614DEF0024680B /* BrazeListener.swift in Sources */, 071009C928D1DB3F00344290 /* ScreenAssembly.swift in Sources */, ); runOnlyForDeploymentPostprocessing = 0; diff --git a/OpenEdX/AppDelegate.swift b/OpenEdX/AppDelegate.swift index 8b6b5bd1f..0a8a1c5b1 100644 --- a/OpenEdX/AppDelegate.swift +++ b/OpenEdX/AppDelegate.swift @@ -123,4 +123,18 @@ class AppDelegate: UIResponder, UIApplicationDelegate { window?.rootViewController = RouteController() } + // Push Notifications + func application(_ application: UIApplication, didRegisterForRemoteNotificationsWithDeviceToken deviceToken: Data) { + let pushManager = Container.shared.resolve(PushNotificationsManager.self)! + pushManager.didRegisterForRemoteNotificationsWithDeviceToken(deviceToken: deviceToken) + } + func application(_ application: UIApplication, didFailToRegisterForRemoteNotificationsWithError error: Error) { + let pushManager = Container.shared.resolve(PushNotificationsManager.self)! + pushManager.didFailToRegisterForRemoteNotificationsWithError(error: error) + } + func application(_ application: UIApplication, didReceiveRemoteNotification userInfo: [AnyHashable : Any], fetchCompletionHandler completionHandler: @escaping (UIBackgroundFetchResult) -> Void) { + let pushManager = Container.shared.resolve(PushNotificationsManager.self)! + pushManager.didReceiveRemoteNotification(userInfo: userInfo) + completionHandler(.newData) + } } diff --git a/OpenEdX/DI/AppAssembly.swift b/OpenEdX/DI/AppAssembly.swift index e5a491fc7..4e24e6924 100644 --- a/OpenEdX/DI/AppAssembly.swift +++ b/OpenEdX/DI/AppAssembly.swift @@ -157,6 +157,12 @@ class AppAssembly: Assembly { container.register(Validator.self) { _ in Validator() }.inObjectScope(.container) + + container.register(PushNotificationsManager.self) { r in + PushNotificationsManager( + config: r.resolve(ConfigProtocol.self)! + ) + }.inObjectScope(.container) } } // swiftlint:enable function_body_length diff --git a/OpenEdX/AnalyticsManager.swift b/OpenEdX/Managers/AnalyticsManager/AnalyticsManager.swift similarity index 100% rename from OpenEdX/AnalyticsManager.swift rename to OpenEdX/Managers/AnalyticsManager/AnalyticsManager.swift diff --git a/OpenEdX/MainScreenAnalytics.swift b/OpenEdX/Managers/AnalyticsManager/MainScreenAnalytics.swift similarity index 100% rename from OpenEdX/MainScreenAnalytics.swift rename to OpenEdX/Managers/AnalyticsManager/MainScreenAnalytics.swift diff --git a/OpenEdX/Managers/PushNotificationsManager/Listeners/BrazeListener.swift b/OpenEdX/Managers/PushNotificationsManager/Listeners/BrazeListener.swift new file mode 100644 index 000000000..a705953e0 --- /dev/null +++ b/OpenEdX/Managers/PushNotificationsManager/Listeners/BrazeListener.swift @@ -0,0 +1,14 @@ +// +// BrazeListener.swift +// OpenEdX +// +// Created by Anton Yarmolenka on 24/01/2024. +// + +import Foundation + +class BrazeListener: PushNotificationsListener { + func didReceiveRemoteNotification(userInfo: [AnyHashable: Any]) { + + } +} diff --git a/OpenEdX/Managers/PushNotificationsManager/Listeners/FCMListener.swift b/OpenEdX/Managers/PushNotificationsManager/Listeners/FCMListener.swift new file mode 100644 index 000000000..3c0725e04 --- /dev/null +++ b/OpenEdX/Managers/PushNotificationsManager/Listeners/FCMListener.swift @@ -0,0 +1,14 @@ +// +// FCMListener.swift +// OpenEdX +// +// Created by Anton Yarmolenka on 24/01/2024. +// + +import Foundation + +class FCMListener: PushNotificationsListener { + func didReceiveRemoteNotification(userInfo: [AnyHashable: Any]) { + + } +} diff --git a/OpenEdX/Managers/PushNotificationsManager/Providers/BrazeProvider.swift b/OpenEdX/Managers/PushNotificationsManager/Providers/BrazeProvider.swift new file mode 100644 index 000000000..043e7d62b --- /dev/null +++ b/OpenEdX/Managers/PushNotificationsManager/Providers/BrazeProvider.swift @@ -0,0 +1,17 @@ +// +// BrazeProvider.swift +// OpenEdX +// +// Created by Anton Yarmolenka on 24/01/2024. +// + +import Foundation + +class BrazeProvider: PushNotificationsProvider { + func didRegisterForRemoteNotificationsWithDeviceToken(deviceToken: Data) { + + } + func didFailToRegisterForRemoteNotificationsWithError(error: Error) { + + } +} diff --git a/OpenEdX/Managers/PushNotificationsManager/Providers/FCMProvider.swift b/OpenEdX/Managers/PushNotificationsManager/Providers/FCMProvider.swift new file mode 100644 index 000000000..739659e12 --- /dev/null +++ b/OpenEdX/Managers/PushNotificationsManager/Providers/FCMProvider.swift @@ -0,0 +1,17 @@ +// +// FCMProvider.swift +// OpenEdX +// +// Created by Anton Yarmolenka on 24/01/2024. +// + +import Foundation + +class FCMProvider: PushNotificationsProvider { + func didRegisterForRemoteNotificationsWithDeviceToken(deviceToken: Data) { + + } + func didFailToRegisterForRemoteNotificationsWithError(error: Error) { + + } +} diff --git a/OpenEdX/Managers/PushNotificationsManager/PushNotificationsManager.swift b/OpenEdX/Managers/PushNotificationsManager/PushNotificationsManager.swift new file mode 100644 index 000000000..23e747ccf --- /dev/null +++ b/OpenEdX/Managers/PushNotificationsManager/PushNotificationsManager.swift @@ -0,0 +1,68 @@ +// +// PushNotificationManager.swift +// OpenEdX +// +// Created by Anton Yarmolenka on 24/01/2024. +// + +import Foundation +import Core + +public protocol PushNotificationsProvider { + func didRegisterForRemoteNotificationsWithDeviceToken(deviceToken: Data) + func didFailToRegisterForRemoteNotificationsWithError(error: Error) +} + +protocol PushNotificationsListener { + func didReceiveRemoteNotification(userInfo: [AnyHashable: Any]) +} + +class PushNotificationsManager { + private var providers: [PushNotificationsProvider] = [] + private var listeners: [PushNotificationsListener] = [] + + // Init manager + public init(config: ConfigProtocol) { + self.providers = self.providersFor(config: config) + self.listeners = self.listenersFor(config: config) + } + + private func providersFor(config: ConfigProtocol) -> [PushNotificationsProvider] { + var rProviders: [PushNotificationsProvider] = [] + if config.firebase.cloudMessagingEnabled { + rProviders.append(FCMProvider()) + } + if config.braze.pushNotificationsEnabled { + rProviders.append(BrazeProvider()) + } + return rProviders + } + + private func listenersFor(config: ConfigProtocol) -> [PushNotificationsListener] { + var rListeners: [PushNotificationsListener] = [] + if config.firebase.cloudMessagingEnabled { + rListeners.append(FCMListener()) + } + if config.braze.pushNotificationsEnabled { + rListeners.append(BrazeListener()) + } + return rListeners + } + + // Proccess functions from app delegate + public func didRegisterForRemoteNotificationsWithDeviceToken(deviceToken: Data) { + for provider in providers { + provider.didRegisterForRemoteNotificationsWithDeviceToken(deviceToken: deviceToken) + } + } + public func didFailToRegisterForRemoteNotificationsWithError(error: Error) { + for provider in providers { + provider.didFailToRegisterForRemoteNotificationsWithError(error: error) + } + } + public func didReceiveRemoteNotification(userInfo: [AnyHashable: Any]) { + for listener in listeners { + listener.didReceiveRemoteNotification(userInfo: userInfo) + } + } +} From c87f7cb837eea0e9c00bbf2ceb86840c313decd2 Mon Sep 17 00:00:00 2001 From: Anton Yarmolenko Date: Fri, 26 Jan 2024 13:22:03 +0100 Subject: [PATCH 2/7] chore: abstract layer for deep linking --- Core/Core.xcodeproj/project.pbxproj | 4 ++ .../Configuration/Config/BranchConfig.swift | 31 ++++++++ Core/Core/Configuration/Config/Config.swift | 3 +- .../CoreTests/Configuration/ConfigTests.swift | 11 +++ OpenEdX.xcodeproj/project.pbxproj | 32 +++++++++ OpenEdX/AppDelegate.swift | 40 +++++++++-- OpenEdX/DI/AppAssembly.swift | 6 ++ .../DeepLinkManager/BranchService.swift | 35 +++++++++ .../DeepLinkManager/DeepLinkManager.swift | 72 +++++++++++++++++++ .../DeepLinkManager/Link/DeepLink.swift | 18 +++++ .../DeepLinkManager/Link/PushLink.swift | 30 ++++++++ .../Listeners/BrazeListener.swift | 4 +- .../Listeners/FCMListener.swift | 4 +- .../PushNotificationsManager.swift | 28 ++++++++ 14 files changed, 304 insertions(+), 14 deletions(-) create mode 100644 Core/Core/Configuration/Config/BranchConfig.swift create mode 100644 OpenEdX/Managers/DeepLinkManager/BranchService.swift create mode 100644 OpenEdX/Managers/DeepLinkManager/DeepLinkManager.swift create mode 100644 OpenEdX/Managers/DeepLinkManager/Link/DeepLink.swift create mode 100644 OpenEdX/Managers/DeepLinkManager/Link/PushLink.swift diff --git a/Core/Core.xcodeproj/project.pbxproj b/Core/Core.xcodeproj/project.pbxproj index 0f61e2d47..4382f8035 100644 --- a/Core/Core.xcodeproj/project.pbxproj +++ b/Core/Core.xcodeproj/project.pbxproj @@ -125,6 +125,7 @@ 07DDFCBD29A780BB00572595 /* UINavigationController+Animation.swift in Sources */ = {isa = PBXBuildFile; fileRef = 07DDFCBC29A780BB00572595 /* UINavigationController+Animation.swift */; }; 07E0939F2B308D2800F1E4B2 /* Data_Certificate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 07E0939E2B308D2800F1E4B2 /* Data_Certificate.swift */; }; A53A32352B233DEC005FE38A /* ThemeConfig.swift in Sources */ = {isa = PBXBuildFile; fileRef = A53A32342B233DEC005FE38A /* ThemeConfig.swift */; }; + A595689B2B6173DF00ED4F90 /* BranchConfig.swift in Sources */ = {isa = PBXBuildFile; fileRef = A595689A2B6173DF00ED4F90 /* BranchConfig.swift */; }; A5F4E7B52B61544A00ACD166 /* BrazeConfig.swift in Sources */ = {isa = PBXBuildFile; fileRef = A5F4E7B42B61544A00ACD166 /* BrazeConfig.swift */; }; BA30427F2B20B320009B64B7 /* SocialAuthError.swift in Sources */ = {isa = PBXBuildFile; fileRef = BA30427D2B20B299009B64B7 /* SocialAuthError.swift */; }; BA593F1C2AF8E498009ADB51 /* ScrollSlidingTabBar.swift in Sources */ = {isa = PBXBuildFile; fileRef = BA593F1B2AF8E498009ADB51 /* ScrollSlidingTabBar.swift */; }; @@ -295,6 +296,7 @@ 60153262DBC2F9E660D7E11B /* Pods-App-Core.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-App-Core.release.xcconfig"; path = "Target Support Files/Pods-App-Core/Pods-App-Core.release.xcconfig"; sourceTree = ""; }; 9D5B06CAA99EA5CD49CBE2BB /* Pods-App-Core.debugdev.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-App-Core.debugdev.xcconfig"; path = "Target Support Files/Pods-App-Core/Pods-App-Core.debugdev.xcconfig"; sourceTree = ""; }; A53A32342B233DEC005FE38A /* ThemeConfig.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ThemeConfig.swift; sourceTree = ""; }; + A595689A2B6173DF00ED4F90 /* BranchConfig.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BranchConfig.swift; sourceTree = ""; }; A5F4E7B42B61544A00ACD166 /* BrazeConfig.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = BrazeConfig.swift; sourceTree = ""; }; BA30427D2B20B299009B64B7 /* SocialAuthError.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SocialAuthError.swift; sourceTree = ""; }; BA593F1B2AF8E498009ADB51 /* ScrollSlidingTabBar.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ScrollSlidingTabBar.swift; sourceTree = ""; }; @@ -752,6 +754,7 @@ 0727876F28D23411002E9142 /* Config.swift */, DBF6F2402B014ADA0098414B /* FirebaseConfig.swift */, A5F4E7B42B61544A00ACD166 /* BrazeConfig.swift */, + A595689A2B6173DF00ED4F90 /* BranchConfig.swift */, DBF6F2492B0380E00098414B /* FeaturesConfig.swift */, DBF6F2452B01DAFE0098414B /* AgreementConfig.swift */, BAFB99812B0E2354007D09F9 /* FacebookConfig.swift */, @@ -1030,6 +1033,7 @@ 021D925728DCF12900ACC565 /* AlertView.swift in Sources */, 027BD3A82909474200392132 /* KeyboardAvoidingViewController.swift in Sources */, 02E93F852AEBAEBC006C4750 /* AppReviewViewModel.swift in Sources */, + A595689B2B6173DF00ED4F90 /* BranchConfig.swift in Sources */, 0770DE2528D08FBA006D8A5D /* CoreStorage.swift in Sources */, BA8FA6612AD5974300EA029A /* AppleAuthProvider.swift in Sources */, BA8FA6702AD59EA300EA029A /* MicrosoftAuthProvider.swift in Sources */, diff --git a/Core/Core/Configuration/Config/BranchConfig.swift b/Core/Core/Configuration/Config/BranchConfig.swift new file mode 100644 index 000000000..33440a3a0 --- /dev/null +++ b/Core/Core/Configuration/Config/BranchConfig.swift @@ -0,0 +1,31 @@ +// +// BranchConfig.swift +// Core +// +// Created by Anton Yarmolenka on 24/01/2024. +// + +import Foundation + +private enum BranchKeys: String { + case enabled = "ENABLED" + case key = "KEY" +} + +public final class BranchConfig: NSObject { + public var enabled: Bool = false + public var key: String? + + init(dictionary: [String: AnyObject]) { + super.init() + enabled = dictionary[BranchKeys.enabled.rawValue] as? Bool == true + key = dictionary[BranchKeys.key.rawValue] as? String + } +} + +private let branchKey = "BRANCH" +extension Config { + public var branch: BranchConfig { + BranchConfig(dictionary: self[branchKey] as? [String: AnyObject] ?? [:]) + } +} diff --git a/Core/Core/Configuration/Config/Config.swift b/Core/Core/Configuration/Config/Config.swift index 5c1693c97..6aca3e6ca 100644 --- a/Core/Core/Configuration/Config/Config.swift +++ b/Core/Core/Configuration/Config/Config.swift @@ -26,6 +26,7 @@ public protocol ConfigProtocol { var uiComponents: UIComponentsConfig { get } var discovery: DiscoveryConfig { get } var braze: BrazeConfig { get } + var branch: BranchConfig { get } } public enum TokenType: String { @@ -65,7 +66,7 @@ public class Config { let dict = try? PropertyListSerialization.propertyList( from: data, options: [], - format: nil) as? [String: Any] + format: nil) as? [String: Any] else { return } properties = dict diff --git a/Core/CoreTests/Configuration/ConfigTests.swift b/Core/CoreTests/Configuration/ConfigTests.swift index e9b8a0ce7..21ab17ddf 100644 --- a/Core/CoreTests/Configuration/ConfigTests.swift +++ b/Core/CoreTests/Configuration/ConfigTests.swift @@ -56,6 +56,10 @@ class ConfigTests: XCTestCase { "BRAZE": [ "ENABLED": true, "PUSH_NOTIFICATIONS_ENABLED": true + ], + "BRANCH": [ + "ENABLED": true, + "KEY": "testBranchKey" ] ] @@ -125,4 +129,11 @@ class ConfigTests: XCTestCase { XCTAssertTrue(config.braze.pushNotificationsEnabled) } + + func testBranchConfigInitialization() { + let config = Config(properties: properties) + + XCTAssertTrue(config.branch.enabled) + XCTAssertEqual(config.branch.key, "testBranchKey") + } } diff --git a/OpenEdX.xcodeproj/project.pbxproj b/OpenEdX.xcodeproj/project.pbxproj index b02ea1814..c0c357d62 100644 --- a/OpenEdX.xcodeproj/project.pbxproj +++ b/OpenEdX.xcodeproj/project.pbxproj @@ -50,6 +50,10 @@ A50066912B61467B0024680B /* BrazeProvider.swift in Sources */ = {isa = PBXBuildFile; fileRef = A50066902B61467B0024680B /* BrazeProvider.swift */; }; A50066932B614DCD0024680B /* FCMListener.swift in Sources */ = {isa = PBXBuildFile; fileRef = A50066922B614DCD0024680B /* FCMListener.swift */; }; A50066952B614DEF0024680B /* BrazeListener.swift in Sources */ = {isa = PBXBuildFile; fileRef = A50066942B614DEF0024680B /* BrazeListener.swift */; }; + A59568952B61630500ED4F90 /* DeepLinkManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = A59568942B61630500ED4F90 /* DeepLinkManager.swift */; }; + A59568972B61653700ED4F90 /* DeepLink.swift in Sources */ = {isa = PBXBuildFile; fileRef = A59568962B61653700ED4F90 /* DeepLink.swift */; }; + A59568992B616D9400ED4F90 /* PushLink.swift in Sources */ = {isa = PBXBuildFile; fileRef = A59568982B616D9400ED4F90 /* PushLink.swift */; }; + A59585AF2B62A07100A35A20 /* BranchService.swift in Sources */ = {isa = PBXBuildFile; fileRef = A59585AE2B62A07100A35A20 /* BranchService.swift */; }; BA3042792B1F7147009B64B7 /* MSAL in Frameworks */ = {isa = PBXBuildFile; productRef = BA3042782B1F7147009B64B7 /* MSAL */; }; E0D6E6A32B1626B10089F9C9 /* Theme.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = E0D6E6A22B1626B10089F9C9 /* Theme.framework */; }; E0D6E6A42B1626D60089F9C9 /* Theme.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = E0D6E6A22B1626B10089F9C9 /* Theme.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; @@ -125,6 +129,10 @@ A50066902B61467B0024680B /* BrazeProvider.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BrazeProvider.swift; sourceTree = ""; }; A50066922B614DCD0024680B /* FCMListener.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FCMListener.swift; sourceTree = ""; }; A50066942B614DEF0024680B /* BrazeListener.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BrazeListener.swift; sourceTree = ""; }; + A59568942B61630500ED4F90 /* DeepLinkManager.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DeepLinkManager.swift; sourceTree = ""; }; + A59568962B61653700ED4F90 /* DeepLink.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DeepLink.swift; sourceTree = ""; }; + A59568982B616D9400ED4F90 /* PushLink.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PushLink.swift; sourceTree = ""; }; + A59585AE2B62A07100A35A20 /* BranchService.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BranchService.swift; sourceTree = ""; }; A89AD827F52CF6A6B903606E /* Pods-App-OpenEdX.releasestage.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-App-OpenEdX.releasestage.xcconfig"; path = "Target Support Files/Pods-App-OpenEdX/Pods-App-OpenEdX.releasestage.xcconfig"; sourceTree = ""; }; BB08ACD2CCA33D6DDDDD31B4 /* Pods-App-OpenEdX.releasedev.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-App-OpenEdX.releasedev.xcconfig"; path = "Target Support Files/Pods-App-OpenEdX/Pods-App-OpenEdX.releasedev.xcconfig"; sourceTree = ""; }; E0D6E6A22B1626B10089F9C9 /* Theme.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; path = Theme.framework; sourceTree = BUILT_PRODUCTS_DIR; }; @@ -270,6 +278,7 @@ A50066882B613E800024680B /* Managers */ = { isa = PBXGroup; children = ( + A59568932B6162E400ED4F90 /* DeepLinkManager */, A50066872B613E4B0024680B /* PushNotificationsManager */, A50066892B613E990024680B /* AnalyticsManager */, ); @@ -303,6 +312,25 @@ path = Listeners; sourceTree = ""; }; + A59568932B6162E400ED4F90 /* DeepLinkManager */ = { + isa = PBXGroup; + children = ( + A59568942B61630500ED4F90 /* DeepLinkManager.swift */, + A59585AE2B62A07100A35A20 /* BranchService.swift */, + A59585AD2B62677B00A35A20 /* Link */, + ); + path = DeepLinkManager; + sourceTree = ""; + }; + A59585AD2B62677B00A35A20 /* Link */ = { + isa = PBXGroup; + children = ( + A59568962B61653700ED4F90 /* DeepLink.swift */, + A59568982B616D9400ED4F90 /* PushLink.swift */, + ); + path = Link; + sourceTree = ""; + }; /* End PBXGroup section */ /* Begin PBXNativeTarget section */ @@ -472,6 +500,7 @@ 0293A2052A6FCD430090A336 /* CoursePersistence.swift in Sources */, 020CA5D92AA0A25300970AAF /* AppStorage.swift in Sources */, 0298DF302A4EF7230023A257 /* AnalyticsManager.swift in Sources */, + A59585AF2B62A07100A35A20 /* BranchService.swift in Sources */, A50066912B61467B0024680B /* BrazeProvider.swift in Sources */, 0293A2072A6FCDA30090A336 /* DiscoveryPersistence.swift in Sources */, 07D5DA3528D075AA00752FD9 /* AppDelegate.swift in Sources */, @@ -487,8 +516,11 @@ 0770DE2028D0858A006D8A5D /* Router.swift in Sources */, 0293A2092A6FCDE50090A336 /* DashboardPersistence.swift in Sources */, 0770DE1728D080A1006D8A5D /* RouteController.swift in Sources */, + A59568952B61630500ED4F90 /* DeepLinkManager.swift in Sources */, A50066952B614DEF0024680B /* BrazeListener.swift in Sources */, 071009C928D1DB3F00344290 /* ScreenAssembly.swift in Sources */, + A59568972B61653700ED4F90 /* DeepLink.swift in Sources */, + A59568992B616D9400ED4F90 /* PushLink.swift in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; diff --git a/OpenEdX/AppDelegate.swift b/OpenEdX/AppDelegate.swift index 0a8a1c5b1..2bc95b2ed 100644 --- a/OpenEdX/AppDelegate.swift +++ b/OpenEdX/AppDelegate.swift @@ -48,6 +48,7 @@ class AppDelegate: UIResponder, UIApplicationDelegate { didFinishLaunchingWithOptions: launchOptions ) } + configureDeepLinkServices(launchOptions: launchOptions) } Theme.Fonts.registerFonts() @@ -62,6 +63,10 @@ class AppDelegate: UIResponder, UIApplicationDelegate { object: nil ) + if let pushManager = Container.shared.resolve(PushNotificationsManager.self) { + pushManager.performRegistration() + } + return true } @@ -70,6 +75,13 @@ class AppDelegate: UIResponder, UIApplicationDelegate { open url: URL, options: [UIApplication.OpenURLOptionsKey: Any] = [:] ) -> Bool { if let config = Container.shared.resolve(ConfigProtocol.self) { + if let deepLinkManager = Container.shared.resolve(DeepLinkManager.self), + deepLinkManager.serviceEnabled { + if deepLinkManager.handledURLWith(app: app, open: url, options: options) { + return true + } + } + if config.facebook.enabled { ApplicationDelegate.shared.application( app, @@ -125,16 +137,30 @@ class AppDelegate: UIResponder, UIApplicationDelegate { // Push Notifications func application(_ application: UIApplication, didRegisterForRemoteNotificationsWithDeviceToken deviceToken: Data) { - let pushManager = Container.shared.resolve(PushNotificationsManager.self)! - pushManager.didRegisterForRemoteNotificationsWithDeviceToken(deviceToken: deviceToken) + if let pushManager = Container.shared.resolve(PushNotificationsManager.self) { + pushManager.didRegisterForRemoteNotificationsWithDeviceToken(deviceToken: deviceToken) + } } func application(_ application: UIApplication, didFailToRegisterForRemoteNotificationsWithError error: Error) { - let pushManager = Container.shared.resolve(PushNotificationsManager.self)! - pushManager.didFailToRegisterForRemoteNotificationsWithError(error: error) + if let pushManager = Container.shared.resolve(PushNotificationsManager.self) { + pushManager.didFailToRegisterForRemoteNotificationsWithError(error: error) + } } - func application(_ application: UIApplication, didReceiveRemoteNotification userInfo: [AnyHashable : Any], fetchCompletionHandler completionHandler: @escaping (UIBackgroundFetchResult) -> Void) { - let pushManager = Container.shared.resolve(PushNotificationsManager.self)! - pushManager.didReceiveRemoteNotification(userInfo: userInfo) + func application( + _ application: UIApplication, + didReceiveRemoteNotification userInfo: [AnyHashable: Any], + fetchCompletionHandler completionHandler: @escaping (UIBackgroundFetchResult) -> Void + ) { + if let pushManager = Container.shared.resolve(PushNotificationsManager.self) { + pushManager.didReceiveRemoteNotification(userInfo: userInfo) + } completionHandler(.newData) } + + // Deep link + func configureDeepLinkServices(launchOptions: [UIApplication.LaunchOptionsKey: Any]?) { + if let deepLinkManager = Container.shared.resolve(DeepLinkManager.self) { + deepLinkManager.configureDeepLinkService(launchOptions: launchOptions) + } + } } diff --git a/OpenEdX/DI/AppAssembly.swift b/OpenEdX/DI/AppAssembly.swift index 4e24e6924..74101e5a2 100644 --- a/OpenEdX/DI/AppAssembly.swift +++ b/OpenEdX/DI/AppAssembly.swift @@ -163,6 +163,12 @@ class AppAssembly: Assembly { config: r.resolve(ConfigProtocol.self)! ) }.inObjectScope(.container) + + container.register(DeepLinkManager.self) { r in + DeepLinkManager( + config: r.resolve(ConfigProtocol.self)! + ) + }.inObjectScope(.container) } } // swiftlint:enable function_body_length diff --git a/OpenEdX/Managers/DeepLinkManager/BranchService.swift b/OpenEdX/Managers/DeepLinkManager/BranchService.swift new file mode 100644 index 000000000..4d36fb27d --- /dev/null +++ b/OpenEdX/Managers/DeepLinkManager/BranchService.swift @@ -0,0 +1,35 @@ +// +// BranchService.swift +// OpenEdX +// +// Created by Anton Yarmolenka on 25/01/2024. +// + +import Foundation +import UIKit + +class BranchService: DeepLinkService { + // configure service + func configureWith(launchOptions: [UIApplication.LaunchOptionsKey: Any]?) { + + } + + // handle url + func handledURLWith( + app: UIApplication, + open url: URL, + options: [UIApplication.OpenURLOptionsKey: Any] + ) -> Bool { + false + } + + // This method process push notification with the link object + func processNotification(with link: PushLink) { + + } + + // This method process the deep link with response parameters + func processDeepLink(with params: [String: Any]) { + + } +} diff --git a/OpenEdX/Managers/DeepLinkManager/DeepLinkManager.swift b/OpenEdX/Managers/DeepLinkManager/DeepLinkManager.swift new file mode 100644 index 000000000..ac945cec4 --- /dev/null +++ b/OpenEdX/Managers/DeepLinkManager/DeepLinkManager.swift @@ -0,0 +1,72 @@ +// +// DeepLinkManager.swift +// OpenEdX +// +// Created by Anton Yarmolenka on 24/01/2024. +// + +import Foundation +import Core +import UIKit + +public protocol DeepLinkService { + func processNotification(with link: PushLink) + func processDeepLink(with params: [String: Any]) + func configureWith(launchOptions: [UIApplication.LaunchOptionsKey: Any]?) + func handledURLWith(app: UIApplication, open url: URL, options: [UIApplication.OpenURLOptionsKey: Any]) -> Bool +} + +class DeepLinkManager { + private var service: DeepLinkService? + + // Init manager + public init(config: ConfigProtocol) { + self.service = self.serviceFor(config: config) + } + + private func serviceFor(config: ConfigProtocol) -> DeepLinkService? { + if config.branch.enabled { + return BranchService() + } + return nil + } + + // check if service is added (means enabled) + var serviceEnabled: Bool { + service != nil + } + + // Configure services + func configureDeepLinkService(launchOptions: [UIApplication.LaunchOptionsKey: Any]?) { + if let service = service { + service.configureWith(launchOptions: launchOptions) + } + } + + // Handle open url + func handledURLWith( + app: UIApplication, + open url: URL, + options: [UIApplication.OpenURLOptionsKey: Any] = [:] + ) -> Bool { + if let service = service { + return service.handledURLWith(app: app, open: url, options: options) + } + return false + } + + // This method process push notification with the link object + func processNotification(with link: PushLink) { + if let service = service { + service.processNotification(with: link) + } + } + + // This method process the deep link with response parameters + func processDeepLink(with params: [String: Any]) { + if let service = service { + service.processDeepLink(with: params) + } + } + +} diff --git a/OpenEdX/Managers/DeepLinkManager/Link/DeepLink.swift b/OpenEdX/Managers/DeepLinkManager/Link/DeepLink.swift new file mode 100644 index 000000000..3df8a21e6 --- /dev/null +++ b/OpenEdX/Managers/DeepLinkManager/Link/DeepLink.swift @@ -0,0 +1,18 @@ +// +// DeepLink.swift +// OpenEdX +// +// Created by Anton Yarmolenka on 24/01/2024. +// + +import Foundation + +enum DeepLinkType: String { + case none +} + +public class DeepLink { + init(dictionary: [String: Any]) { + + } +} diff --git a/OpenEdX/Managers/DeepLinkManager/Link/PushLink.swift b/OpenEdX/Managers/DeepLinkManager/Link/PushLink.swift new file mode 100644 index 000000000..262bda304 --- /dev/null +++ b/OpenEdX/Managers/DeepLinkManager/Link/PushLink.swift @@ -0,0 +1,30 @@ +// +// PushLink.swift +// OpenEdX +// +// Created by Anton Yarmolenka on 24/01/2024. +// + +import Foundation + +enum DataKeys: String { + case title + case body + case aps + case alert +} + +// This link will have information of course and screen type which will be use to route on particular screen. +public class PushLink: DeepLink { + let title: String? + let body: String? + + override init(dictionary: [String: Any]) { + let aps = dictionary[DataKeys.aps.rawValue] as? [String: Any] + let alert = aps?[DataKeys.alert.rawValue] as? [String: Any] + title = alert?[DataKeys.title.rawValue] as? String + body = alert?[DataKeys.body.rawValue] as? String + + super.init(dictionary: dictionary) + } +} diff --git a/OpenEdX/Managers/PushNotificationsManager/Listeners/BrazeListener.swift b/OpenEdX/Managers/PushNotificationsManager/Listeners/BrazeListener.swift index a705953e0..7da4ddc4d 100644 --- a/OpenEdX/Managers/PushNotificationsManager/Listeners/BrazeListener.swift +++ b/OpenEdX/Managers/PushNotificationsManager/Listeners/BrazeListener.swift @@ -8,7 +8,5 @@ import Foundation class BrazeListener: PushNotificationsListener { - func didReceiveRemoteNotification(userInfo: [AnyHashable: Any]) { - - } + func notificationToThisListener(userinfo: [AnyHashable: Any]) -> Bool { false } } diff --git a/OpenEdX/Managers/PushNotificationsManager/Listeners/FCMListener.swift b/OpenEdX/Managers/PushNotificationsManager/Listeners/FCMListener.swift index 3c0725e04..ce965be51 100644 --- a/OpenEdX/Managers/PushNotificationsManager/Listeners/FCMListener.swift +++ b/OpenEdX/Managers/PushNotificationsManager/Listeners/FCMListener.swift @@ -8,7 +8,5 @@ import Foundation class FCMListener: PushNotificationsListener { - func didReceiveRemoteNotification(userInfo: [AnyHashable: Any]) { - - } + func notificationToThisListener(userinfo: [AnyHashable: Any]) -> Bool { false } } diff --git a/OpenEdX/Managers/PushNotificationsManager/PushNotificationsManager.swift b/OpenEdX/Managers/PushNotificationsManager/PushNotificationsManager.swift index 23e747ccf..4f6338e14 100644 --- a/OpenEdX/Managers/PushNotificationsManager/PushNotificationsManager.swift +++ b/OpenEdX/Managers/PushNotificationsManager/PushNotificationsManager.swift @@ -7,6 +7,8 @@ import Foundation import Core +import UIKit +import Swinject public protocol PushNotificationsProvider { func didRegisterForRemoteNotificationsWithDeviceToken(deviceToken: Data) @@ -14,9 +16,20 @@ public protocol PushNotificationsProvider { } protocol PushNotificationsListener { + func notificationToThisListener(userinfo: [AnyHashable: Any]) -> Bool func didReceiveRemoteNotification(userInfo: [AnyHashable: Any]) } +extension PushNotificationsListener { + func didReceiveRemoteNotification(userInfo: [AnyHashable: Any]) { + guard let dictionary = userInfo as? [String: Any], notificationToThisListener(userinfo: userInfo) else { return } + let link = PushLink(dictionary: dictionary) + if let deepLinkManager = Container.shared.resolve(DeepLinkManager.self) { + deepLinkManager.processNotification(with: link) + } + } +} + class PushNotificationsManager { private var providers: [PushNotificationsProvider] = [] private var listeners: [PushNotificationsListener] = [] @@ -49,6 +62,21 @@ class PushNotificationsManager { return rListeners } + // Register for push notifications + public func performRegistration() { + UNUserNotificationCenter.current().requestAuthorization(options: [.alert, .sound, .badge]) { (granted, error) in + if granted { + DispatchQueue.main.async { + UIApplication.shared.registerForRemoteNotifications() + } + } else if let error = error { + debugLog("Push notifications permission error: \(error.localizedDescription)") + } else { + debugLog("Permission for push notifications denied.") + } + } + } + // Proccess functions from app delegate public func didRegisterForRemoteNotificationsWithDeviceToken(deviceToken: Data) { for provider in providers { From 3659d5ccbfdc7df1fcd6fe3a5a6ef7b12d6907e8 Mon Sep 17 00:00:00 2001 From: Anton Yarmolenko Date: Tue, 30 Jan 2024 14:15:16 +0100 Subject: [PATCH 3/7] chore: renamed some func --- OpenEdX.xcodeproj/project.pbxproj | 10 +++++++++- OpenEdX/Managers/DeepLinkManager/DeepLinkManager.swift | 1 + .../DeepLinkManager/{ => Services}/BranchService.swift | 0 .../Listeners/BrazeListener.swift | 3 ++- .../Listeners/FCMListener.swift | 3 ++- .../Providers/BrazeProvider.swift | 2 +- .../Providers/FCMProvider.swift | 2 +- .../PushNotificationsManager.swift | 8 ++++---- 8 files changed, 20 insertions(+), 9 deletions(-) rename OpenEdX/Managers/DeepLinkManager/{ => Services}/BranchService.swift (100%) diff --git a/OpenEdX.xcodeproj/project.pbxproj b/OpenEdX.xcodeproj/project.pbxproj index c0c357d62..85704b35b 100644 --- a/OpenEdX.xcodeproj/project.pbxproj +++ b/OpenEdX.xcodeproj/project.pbxproj @@ -316,7 +316,7 @@ isa = PBXGroup; children = ( A59568942B61630500ED4F90 /* DeepLinkManager.swift */, - A59585AE2B62A07100A35A20 /* BranchService.swift */, + A5F46FD02B692B140003EEEF /* Services */, A59585AD2B62677B00A35A20 /* Link */, ); path = DeepLinkManager; @@ -331,6 +331,14 @@ path = Link; sourceTree = ""; }; + A5F46FD02B692B140003EEEF /* Services */ = { + isa = PBXGroup; + children = ( + A59585AE2B62A07100A35A20 /* BranchService.swift */, + ); + path = Services; + sourceTree = ""; + }; /* End PBXGroup section */ /* Begin PBXNativeTarget section */ diff --git a/OpenEdX/Managers/DeepLinkManager/DeepLinkManager.swift b/OpenEdX/Managers/DeepLinkManager/DeepLinkManager.swift index ac945cec4..8fb311c43 100644 --- a/OpenEdX/Managers/DeepLinkManager/DeepLinkManager.swift +++ b/OpenEdX/Managers/DeepLinkManager/DeepLinkManager.swift @@ -25,6 +25,7 @@ class DeepLinkManager { } private func serviceFor(config: ConfigProtocol) -> DeepLinkService? { + // init deep link service if config.branch.enabled { return BranchService() } diff --git a/OpenEdX/Managers/DeepLinkManager/BranchService.swift b/OpenEdX/Managers/DeepLinkManager/Services/BranchService.swift similarity index 100% rename from OpenEdX/Managers/DeepLinkManager/BranchService.swift rename to OpenEdX/Managers/DeepLinkManager/Services/BranchService.swift diff --git a/OpenEdX/Managers/PushNotificationsManager/Listeners/BrazeListener.swift b/OpenEdX/Managers/PushNotificationsManager/Listeners/BrazeListener.swift index 7da4ddc4d..c9172c404 100644 --- a/OpenEdX/Managers/PushNotificationsManager/Listeners/BrazeListener.swift +++ b/OpenEdX/Managers/PushNotificationsManager/Listeners/BrazeListener.swift @@ -8,5 +8,6 @@ import Foundation class BrazeListener: PushNotificationsListener { - func notificationToThisListener(userinfo: [AnyHashable: Any]) -> Bool { false } + // check if userinfo contains data for this Listener + func shouldListenNotification(userinfo: [AnyHashable: Any]) -> Bool { false } } diff --git a/OpenEdX/Managers/PushNotificationsManager/Listeners/FCMListener.swift b/OpenEdX/Managers/PushNotificationsManager/Listeners/FCMListener.swift index ce965be51..b0ed7f5f8 100644 --- a/OpenEdX/Managers/PushNotificationsManager/Listeners/FCMListener.swift +++ b/OpenEdX/Managers/PushNotificationsManager/Listeners/FCMListener.swift @@ -8,5 +8,6 @@ import Foundation class FCMListener: PushNotificationsListener { - func notificationToThisListener(userinfo: [AnyHashable: Any]) -> Bool { false } + // check if userinfo contains data for this Listener + func shouldListenNotification(userinfo: [AnyHashable: Any]) -> Bool { false } } diff --git a/OpenEdX/Managers/PushNotificationsManager/Providers/BrazeProvider.swift b/OpenEdX/Managers/PushNotificationsManager/Providers/BrazeProvider.swift index 043e7d62b..2655ea30e 100644 --- a/OpenEdX/Managers/PushNotificationsManager/Providers/BrazeProvider.swift +++ b/OpenEdX/Managers/PushNotificationsManager/Providers/BrazeProvider.swift @@ -8,7 +8,7 @@ import Foundation class BrazeProvider: PushNotificationsProvider { - func didRegisterForRemoteNotificationsWithDeviceToken(deviceToken: Data) { + func registerWithDeviceToken(deviceToken: Data) { } func didFailToRegisterForRemoteNotificationsWithError(error: Error) { diff --git a/OpenEdX/Managers/PushNotificationsManager/Providers/FCMProvider.swift b/OpenEdX/Managers/PushNotificationsManager/Providers/FCMProvider.swift index 739659e12..30fc24983 100644 --- a/OpenEdX/Managers/PushNotificationsManager/Providers/FCMProvider.swift +++ b/OpenEdX/Managers/PushNotificationsManager/Providers/FCMProvider.swift @@ -8,7 +8,7 @@ import Foundation class FCMProvider: PushNotificationsProvider { - func didRegisterForRemoteNotificationsWithDeviceToken(deviceToken: Data) { + func registerWithDeviceToken(deviceToken: Data) { } func didFailToRegisterForRemoteNotificationsWithError(error: Error) { diff --git a/OpenEdX/Managers/PushNotificationsManager/PushNotificationsManager.swift b/OpenEdX/Managers/PushNotificationsManager/PushNotificationsManager.swift index 4f6338e14..461de35d2 100644 --- a/OpenEdX/Managers/PushNotificationsManager/PushNotificationsManager.swift +++ b/OpenEdX/Managers/PushNotificationsManager/PushNotificationsManager.swift @@ -11,18 +11,18 @@ import UIKit import Swinject public protocol PushNotificationsProvider { - func didRegisterForRemoteNotificationsWithDeviceToken(deviceToken: Data) + func registerWithDeviceToken(deviceToken: Data) func didFailToRegisterForRemoteNotificationsWithError(error: Error) } protocol PushNotificationsListener { - func notificationToThisListener(userinfo: [AnyHashable: Any]) -> Bool + func shouldListenNotification(userinfo: [AnyHashable: Any]) -> Bool func didReceiveRemoteNotification(userInfo: [AnyHashable: Any]) } extension PushNotificationsListener { func didReceiveRemoteNotification(userInfo: [AnyHashable: Any]) { - guard let dictionary = userInfo as? [String: Any], notificationToThisListener(userinfo: userInfo) else { return } + 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) { deepLinkManager.processNotification(with: link) @@ -80,7 +80,7 @@ class PushNotificationsManager { // Proccess functions from app delegate public func didRegisterForRemoteNotificationsWithDeviceToken(deviceToken: Data) { for provider in providers { - provider.didRegisterForRemoteNotificationsWithDeviceToken(deviceToken: deviceToken) + provider.registerWithDeviceToken(deviceToken: deviceToken) } } public func didFailToRegisterForRemoteNotificationsWithError(error: Error) { From 931643abd7f49ced8edf73248ffd6444a9a2d6be Mon Sep 17 00:00:00 2001 From: Anton Yarmolenko Date: Tue, 30 Jan 2024 14:55:34 +0100 Subject: [PATCH 4/7] chore: small refactor --- .../Managers/DeepLinkManager/DeepLinkManager.swift | 8 +++----- .../DeepLinkManager/Services/BranchService.swift | 12 +----------- 2 files changed, 4 insertions(+), 16 deletions(-) diff --git a/OpenEdX/Managers/DeepLinkManager/DeepLinkManager.swift b/OpenEdX/Managers/DeepLinkManager/DeepLinkManager.swift index 8fb311c43..ace93dfb1 100644 --- a/OpenEdX/Managers/DeepLinkManager/DeepLinkManager.swift +++ b/OpenEdX/Managers/DeepLinkManager/DeepLinkManager.swift @@ -10,8 +10,6 @@ import Core import UIKit public protocol DeepLinkService { - func processNotification(with link: PushLink) - func processDeepLink(with params: [String: Any]) func configureWith(launchOptions: [UIApplication.LaunchOptionsKey: Any]?) func handledURLWith(app: UIApplication, open url: URL, options: [UIApplication.OpenURLOptionsKey: Any]) -> Bool } @@ -47,7 +45,7 @@ class DeepLinkManager { // Handle open url func handledURLWith( app: UIApplication, - open url: URL, + open url: URL, options: [UIApplication.OpenURLOptionsKey: Any] = [:] ) -> Bool { if let service = service { @@ -59,14 +57,14 @@ class DeepLinkManager { // This method process push notification with the link object func processNotification(with link: PushLink) { if let service = service { - service.processNotification(with: link) + // redirect if possible } } // This method process the deep link with response parameters func processDeepLink(with params: [String: Any]) { if let service = service { - service.processDeepLink(with: params) + // redirect if possible } } diff --git a/OpenEdX/Managers/DeepLinkManager/Services/BranchService.swift b/OpenEdX/Managers/DeepLinkManager/Services/BranchService.swift index 4d36fb27d..07a402a60 100644 --- a/OpenEdX/Managers/DeepLinkManager/Services/BranchService.swift +++ b/OpenEdX/Managers/DeepLinkManager/Services/BranchService.swift @@ -14,7 +14,7 @@ class BranchService: DeepLinkService { } - // handle url + // handle url and call DeepLinkanager.processDeepLink() with params func handledURLWith( app: UIApplication, open url: URL, @@ -22,14 +22,4 @@ class BranchService: DeepLinkService { ) -> Bool { false } - - // This method process push notification with the link object - func processNotification(with link: PushLink) { - - } - - // This method process the deep link with response parameters - func processDeepLink(with params: [String: Any]) { - - } } From a1c2a493cfdf9304f37bb133cb01ce5bf1cead8f Mon Sep 17 00:00:00 2001 From: Anton Yarmolenko Date: Fri, 2 Feb 2024 12:35:22 +0100 Subject: [PATCH 5/7] chore: added RawStringExtractable to configs --- Core/Core/Configuration/Config/BranchConfig.swift | 6 +++--- Core/Core/Configuration/Config/BrazeConfig.swift | 6 +++--- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/Core/Core/Configuration/Config/BranchConfig.swift b/Core/Core/Configuration/Config/BranchConfig.swift index 33440a3a0..43faec57f 100644 --- a/Core/Core/Configuration/Config/BranchConfig.swift +++ b/Core/Core/Configuration/Config/BranchConfig.swift @@ -7,7 +7,7 @@ import Foundation -private enum BranchKeys: String { +private enum BranchKeys: String, RawStringExtractable { case enabled = "ENABLED" case key = "KEY" } @@ -18,8 +18,8 @@ public final class BranchConfig: NSObject { init(dictionary: [String: AnyObject]) { super.init() - enabled = dictionary[BranchKeys.enabled.rawValue] as? Bool == true - key = dictionary[BranchKeys.key.rawValue] as? String + enabled = dictionary[BranchKeys.enabled] as? Bool == true + key = dictionary[BranchKeys.key] as? String } } diff --git a/Core/Core/Configuration/Config/BrazeConfig.swift b/Core/Core/Configuration/Config/BrazeConfig.swift index 8410bf3f8..0cbc10db8 100644 --- a/Core/Core/Configuration/Config/BrazeConfig.swift +++ b/Core/Core/Configuration/Config/BrazeConfig.swift @@ -7,7 +7,7 @@ import Foundation -private enum BrazeKeys: String { +private enum BrazeKeys: String, RawStringExtractable { case enabled = "ENABLED" case pushNotificationsEnabled = "PUSH_NOTIFICATIONS_ENABLED" } @@ -18,8 +18,8 @@ public final class BrazeConfig: NSObject { init(dictionary: [String: AnyObject]) { super.init() - enabled = dictionary[BrazeKeys.enabled.rawValue] as? Bool == true - let pushNotificationsEnabled = dictionary[BrazeKeys.pushNotificationsEnabled.rawValue] as? Bool ?? false + enabled = dictionary[BrazeKeys.enabled] as? Bool == true + let pushNotificationsEnabled = dictionary[BrazeKeys.pushNotificationsEnabled] as? Bool ?? false self.pushNotificationsEnabled = enabled && pushNotificationsEnabled } } From 2b3f9a5931d7a227ab5bf05f3795be59f99f0d2c Mon Sep 17 00:00:00 2001 From: Anton Yarmolenko Date: Thu, 8 Feb 2024 14:19:54 +0100 Subject: [PATCH 6/7] chore: address review feedback --- OpenEdX/AppDelegate.swift | 56 +++++++++---------- .../DeepLinkManager/DeepLinkManager.swift | 33 +++++------ .../Providers/BrazeProvider.swift | 3 +- .../Providers/FCMProvider.swift | 3 +- .../PushNotificationsManager.swift | 35 ++++++------ 5 files changed, 66 insertions(+), 64 deletions(-) diff --git a/OpenEdX/AppDelegate.swift b/OpenEdX/AppDelegate.swift index 5d4eed500..e2386a8e7 100644 --- a/OpenEdX/AppDelegate.swift +++ b/OpenEdX/AppDelegate.swift @@ -74,33 +74,33 @@ class AppDelegate: UIResponder, UIApplicationDelegate { _ app: UIApplication, open url: URL, options: [UIApplication.OpenURLOptionsKey: Any] = [:] ) -> Bool { - if let config = Container.shared.resolve(ConfigProtocol.self) { - if let deepLinkManager = Container.shared.resolve(DeepLinkManager.self), - deepLinkManager.serviceEnabled { - if deepLinkManager.handledURLWith(app: app, open: url, options: options) { - return true - } - } + guard let config = Container.shared.resolve(ConfigProtocol.self) else { return false } - if config.facebook.enabled { - ApplicationDelegate.shared.application( - app, - open: url, - sourceApplication: options[UIApplication.OpenURLOptionsKey.sourceApplication] as? String, - annotation: options[UIApplication.OpenURLOptionsKey.annotation] - ) + if let deepLinkManager = Container.shared.resolve(DeepLinkManager.self), + deepLinkManager.anyServiceEnabled { + if deepLinkManager.handledURLWith(app: app, open: url, options: options) { + return true } + } - if config.google.enabled { - return GIDSignIn.sharedInstance.handle(url) - } + if config.facebook.enabled { + ApplicationDelegate.shared.application( + app, + open: url, + sourceApplication: options[UIApplication.OpenURLOptionsKey.sourceApplication] as? String, + annotation: options[UIApplication.OpenURLOptionsKey.annotation] + ) + } - if config.microsoft.enabled { - return MSALPublicClientApplication.handleMSALResponse( - url, - sourceApplication: options[UIApplication.OpenURLOptionsKey.sourceApplication] as? String - ) - } + if config.google.enabled { + return GIDSignIn.sharedInstance.handle(url) + } + + if config.microsoft.enabled { + return MSALPublicClientApplication.handleMSALResponse( + url, + sourceApplication: options[UIApplication.OpenURLOptionsKey.sourceApplication] as? String + ) } return false @@ -139,14 +139,12 @@ class AppDelegate: UIResponder, UIApplicationDelegate { // Push Notifications func application(_ application: UIApplication, didRegisterForRemoteNotificationsWithDeviceToken deviceToken: Data) { - if let pushManager = Container.shared.resolve(PushNotificationsManager.self) { - pushManager.didRegisterForRemoteNotificationsWithDeviceToken(deviceToken: deviceToken) - } + guard let pushManager = Container.shared.resolve(PushNotificationsManager.self) else { return } + pushManager.didRegisterForRemoteNotificationsWithDeviceToken(deviceToken: deviceToken) } func application(_ application: UIApplication, didFailToRegisterForRemoteNotificationsWithError error: Error) { - if let pushManager = Container.shared.resolve(PushNotificationsManager.self) { - pushManager.didFailToRegisterForRemoteNotificationsWithError(error: error) - } + guard let pushManager = Container.shared.resolve(PushNotificationsManager.self) else { return } + pushManager.didFailToRegisterForRemoteNotificationsWithError(error: error) } func application( _ application: UIApplication, diff --git a/OpenEdX/Managers/DeepLinkManager/DeepLinkManager.swift b/OpenEdX/Managers/DeepLinkManager/DeepLinkManager.swift index ace93dfb1..28399e812 100644 --- a/OpenEdX/Managers/DeepLinkManager/DeepLinkManager.swift +++ b/OpenEdX/Managers/DeepLinkManager/DeepLinkManager.swift @@ -15,29 +15,30 @@ public protocol DeepLinkService { } class DeepLinkManager { - private var service: DeepLinkService? + private var services: [DeepLinkService] = [] // Init manager public init(config: ConfigProtocol) { - self.service = self.serviceFor(config: config) + services = servicesFor(config: config) } - private func serviceFor(config: ConfigProtocol) -> DeepLinkService? { - // init deep link service + private func servicesFor(config: ConfigProtocol) -> [DeepLinkService] { + var deepServices: [DeepLinkService] = [] + // init deep link services if config.branch.enabled { - return BranchService() + deepServices.append(BranchService()) } - return nil + return deepServices } - // check if service is added (means enabled) - var serviceEnabled: Bool { - service != nil + // check if any service is added (means enabled) + var anyServiceEnabled: Bool { + services.count > 0 } // Configure services func configureDeepLinkService(launchOptions: [UIApplication.LaunchOptionsKey: Any]?) { - if let service = service { + for service in services { service.configureWith(launchOptions: launchOptions) } } @@ -48,22 +49,22 @@ class DeepLinkManager { open url: URL, options: [UIApplication.OpenURLOptionsKey: Any] = [:] ) -> Bool { - if let service = service { - return service.handledURLWith(app: app, open: url, options: options) + for service in services where service.handledURLWith(app: app, open: url, options: options) { + return true } return false } - // This method process push notification with the link object - func processNotification(with link: PushLink) { - if let service = service { + // This method do redirect with link from push notification + func processLinkFromNotification(_ link: PushLink) { + if anyServiceEnabled { // redirect if possible } } // This method process the deep link with response parameters func processDeepLink(with params: [String: Any]) { - if let service = service { + if anyServiceEnabled { // redirect if possible } } diff --git a/OpenEdX/Managers/PushNotificationsManager/Providers/BrazeProvider.swift b/OpenEdX/Managers/PushNotificationsManager/Providers/BrazeProvider.swift index 2655ea30e..2bb1d11f2 100644 --- a/OpenEdX/Managers/PushNotificationsManager/Providers/BrazeProvider.swift +++ b/OpenEdX/Managers/PushNotificationsManager/Providers/BrazeProvider.swift @@ -8,9 +8,10 @@ import Foundation class BrazeProvider: PushNotificationsProvider { - func registerWithDeviceToken(deviceToken: Data) { + func didRegisterWithDeviceToken(deviceToken: Data) { } + func didFailToRegisterForRemoteNotificationsWithError(error: Error) { } diff --git a/OpenEdX/Managers/PushNotificationsManager/Providers/FCMProvider.swift b/OpenEdX/Managers/PushNotificationsManager/Providers/FCMProvider.swift index 30fc24983..4e66a2a30 100644 --- a/OpenEdX/Managers/PushNotificationsManager/Providers/FCMProvider.swift +++ b/OpenEdX/Managers/PushNotificationsManager/Providers/FCMProvider.swift @@ -8,9 +8,10 @@ import Foundation class FCMProvider: PushNotificationsProvider { - func registerWithDeviceToken(deviceToken: Data) { + func didRegisterWithDeviceToken(deviceToken: Data) { } + func didFailToRegisterForRemoteNotificationsWithError(error: Error) { } diff --git a/OpenEdX/Managers/PushNotificationsManager/PushNotificationsManager.swift b/OpenEdX/Managers/PushNotificationsManager/PushNotificationsManager.swift index 461de35d2..130818dd5 100644 --- a/OpenEdX/Managers/PushNotificationsManager/PushNotificationsManager.swift +++ b/OpenEdX/Managers/PushNotificationsManager/PushNotificationsManager.swift @@ -11,7 +11,7 @@ import UIKit import Swinject public protocol PushNotificationsProvider { - func registerWithDeviceToken(deviceToken: Data) + func didRegisterWithDeviceToken(deviceToken: Data) func didFailToRegisterForRemoteNotificationsWithError(error: Error) } @@ -22,11 +22,12 @@ protocol PushNotificationsListener { extension PushNotificationsListener { 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) { - deepLinkManager.processNotification(with: link) - } + guard let dictionary = userInfo as? [String: Any], + shouldListenNotification(userinfo: userInfo), + let deepLinkManager = Container.shared.resolve(DeepLinkManager.self) + else { return } + let link = PushLink(dictionary: dictionary) + deepLinkManager.processLinkFromNotification(link) } } @@ -36,30 +37,30 @@ class PushNotificationsManager { // Init manager public init(config: ConfigProtocol) { - self.providers = self.providersFor(config: config) - self.listeners = self.listenersFor(config: config) + providers = providersFor(config: config) + listeners = listenersFor(config: config) } private func providersFor(config: ConfigProtocol) -> [PushNotificationsProvider] { - var rProviders: [PushNotificationsProvider] = [] + var pushProviders: [PushNotificationsProvider] = [] if config.firebase.cloudMessagingEnabled { - rProviders.append(FCMProvider()) + pushProviders.append(FCMProvider()) } if config.braze.pushNotificationsEnabled { - rProviders.append(BrazeProvider()) + pushProviders.append(BrazeProvider()) } - return rProviders + return pushProviders } private func listenersFor(config: ConfigProtocol) -> [PushNotificationsListener] { - var rListeners: [PushNotificationsListener] = [] + var pushListeners: [PushNotificationsListener] = [] if config.firebase.cloudMessagingEnabled { - rListeners.append(FCMListener()) + pushListeners.append(FCMListener()) } if config.braze.pushNotificationsEnabled { - rListeners.append(BrazeListener()) + pushListeners.append(BrazeListener()) } - return rListeners + return pushListeners } // Register for push notifications @@ -80,7 +81,7 @@ class PushNotificationsManager { // Proccess functions from app delegate public func didRegisterForRemoteNotificationsWithDeviceToken(deviceToken: Data) { for provider in providers { - provider.registerWithDeviceToken(deviceToken: deviceToken) + provider.didRegisterWithDeviceToken(deviceToken: deviceToken) } } public func didFailToRegisterForRemoteNotificationsWithError(error: Error) { From 3b93284d934c9074c4574e89a35636ee9865c345 Mon Sep 17 00:00:00 2001 From: Anton Yarmolenko Date: Fri, 9 Feb 2024 13:29:21 +0100 Subject: [PATCH 7/7] chore: address review feedback --- OpenEdX/AppDelegate.swift | 28 ++++++++++++------- .../DeepLinkManager/DeepLinkManager.swift | 4 +-- 2 files changed, 19 insertions(+), 13 deletions(-) diff --git a/OpenEdX/AppDelegate.swift b/OpenEdX/AppDelegate.swift index e2386a8e7..e10373d43 100644 --- a/OpenEdX/AppDelegate.swift +++ b/OpenEdX/AppDelegate.swift @@ -84,23 +84,29 @@ class AppDelegate: UIResponder, UIApplicationDelegate { } if config.facebook.enabled { - ApplicationDelegate.shared.application( + if ApplicationDelegate.shared.application( app, open: url, sourceApplication: options[UIApplication.OpenURLOptionsKey.sourceApplication] as? String, annotation: options[UIApplication.OpenURLOptionsKey.annotation] - ) + ) { + return true + } } if config.google.enabled { - return GIDSignIn.sharedInstance.handle(url) + if GIDSignIn.sharedInstance.handle(url) { + return true + } } if config.microsoft.enabled { - return MSALPublicClientApplication.handleMSALResponse( + if MSALPublicClientApplication.handleMSALResponse( url, sourceApplication: options[UIApplication.OpenURLOptionsKey.sourceApplication] as? String - ) + ) { + return true + } } return false @@ -151,16 +157,18 @@ class AppDelegate: UIResponder, UIApplicationDelegate { didReceiveRemoteNotification userInfo: [AnyHashable: Any], fetchCompletionHandler completionHandler: @escaping (UIBackgroundFetchResult) -> Void ) { - if let pushManager = Container.shared.resolve(PushNotificationsManager.self) { - pushManager.didReceiveRemoteNotification(userInfo: userInfo) + guard let pushManager = Container.shared.resolve(PushNotificationsManager.self) + else { + completionHandler(.newData) + return } + pushManager.didReceiveRemoteNotification(userInfo: userInfo) completionHandler(.newData) } // Deep link func configureDeepLinkServices(launchOptions: [UIApplication.LaunchOptionsKey: Any]?) { - if let deepLinkManager = Container.shared.resolve(DeepLinkManager.self) { - deepLinkManager.configureDeepLinkService(launchOptions: launchOptions) - } + guard let deepLinkManager = Container.shared.resolve(DeepLinkManager.self) else { return } + deepLinkManager.configureDeepLinkService(launchOptions: launchOptions) } } diff --git a/OpenEdX/Managers/DeepLinkManager/DeepLinkManager.swift b/OpenEdX/Managers/DeepLinkManager/DeepLinkManager.swift index 28399e812..157bf2bb7 100644 --- a/OpenEdX/Managers/DeepLinkManager/DeepLinkManager.swift +++ b/OpenEdX/Managers/DeepLinkManager/DeepLinkManager.swift @@ -57,9 +57,7 @@ class DeepLinkManager { // This method do redirect with link from push notification func processLinkFromNotification(_ link: PushLink) { - if anyServiceEnabled { - // redirect if possible - } + // redirect if possible } // This method process the deep link with response parameters