diff --git a/example/ios/Podfile.lock b/example/ios/Podfile.lock index 0c8818c..6d3eff7 100644 --- a/example/ios/Podfile.lock +++ b/example/ios/Podfile.lock @@ -1,6 +1,6 @@ PODS: - boost (1.83.0) - - candlefinance-push (0.2.3): + - candlefinance-push (0.4.1): - glog - RCT-Folly (= 2022.05.16.00) - React-Core @@ -1179,7 +1179,7 @@ EXTERNAL SOURCES: SPEC CHECKSUMS: boost: d3f49c53809116a5d38da093a8aa78bf551aed09 - candlefinance-push: 3b3c1f4669c2bf478c146718ca11ea923795b4fe + candlefinance-push: 1519da157c7caa87d313bbe1ec4c66920ee5109e DoubleConversion: fea03f2699887d960129cc54bba7e52542b6f953 FBLazyVector: 84f6edbe225f38aebd9deaf1540a4160b1f087d7 FBReactNativeSpec: d0086a479be91c44ce4687a962956a352d2dc697 @@ -1229,6 +1229,6 @@ SPEC CHECKSUMS: SocketRocket: f32cd54efbe0f095c4d7594881e52619cfe80b17 Yoga: 64cd2a583ead952b0315d5135bf39e053ae9be70 -PODFILE CHECKSUM: 041bf51303cf30c6e88a6d2780937e50c827ccf6 +PODFILE CHECKSUM: 07ac4e73e4895943d5bb8f9dfb2658c0d89fca69 COCOAPODS: 1.14.3 diff --git a/example/ios/PushExample.xcodeproj/project.pbxproj b/example/ios/PushExample.xcodeproj/project.pbxproj index efe6ee8..8040c09 100644 --- a/example/ios/PushExample.xcodeproj/project.pbxproj +++ b/example/ios/PushExample.xcodeproj/project.pbxproj @@ -552,7 +552,10 @@ "-DFOLLY_USE_LIBCPP=1", "-DFOLLY_CFG_NO_COROUTINES=1", ); - OTHER_LDFLAGS = "$(inherited) "; + OTHER_LDFLAGS = ( + "$(inherited)", + " ", + ); REACT_NATIVE_PATH = "${PODS_ROOT}/../../node_modules/react-native"; SDKROOT = iphoneos; USE_HERMES = false; @@ -620,7 +623,10 @@ "-DFOLLY_USE_LIBCPP=1", "-DFOLLY_CFG_NO_COROUTINES=1", ); - OTHER_LDFLAGS = "$(inherited) "; + OTHER_LDFLAGS = ( + "$(inherited)", + " ", + ); REACT_NATIVE_PATH = "${PODS_ROOT}/../../node_modules/react-native"; SDKROOT = iphoneos; USE_HERMES = false; diff --git a/ios/Push.swift b/ios/Push.swift index 8309e66..bdeb74f 100644 --- a/ios/Push.swift +++ b/ios/Push.swift @@ -2,54 +2,82 @@ import UIKit import NotificationCenter import React +enum NotificationType: String, CaseIterable { + case notificationReceived, deviceTokenReceived, errorReceived +} + +struct Action { + let type: NotificationType + let payload: Any! +} + +public protocol Logger { + func track(event: String) +} + +public class SharedPush: NSObject { + + static var sharedInstance: SharedPush? = { + let instance = SharedPush() + return instance + }() + + var didInitialize = false + var notificationCallbackDictionary: [String: () -> Void] = [:] + @objc public var emitter: RCTEventEmitter? + var queue: [Action] = [] + var logger: Logger? + + public func initPush(logger: Logger?) { + guard !didInitialize else { + logger?.track(event: "\(#function) didInitialize: \(didInitialize)") + return + } + Self.sharedInstance = self + didInitialize = true + self.logger = logger + logger?.track(event: "\(#function) didInitialize: \(true)") + } +} + @objc(Push) final public class Push: RCTEventEmitter { + public lazy var shared = SharedPush.sharedInstance - private var notificationCallbackDictionary: [String: () -> Void] = [:] - private static var isInitialized = false - @objc public static var emitter: RCTEventEmitter? - private static var queue: [Action] = [] - override public init() { - super.init() - Self.emitter = self - } - - enum NotificationType: String, CaseIterable { - case notificationReceived, deviceTokenReceived, errorReceived - } - - struct Action { - let type: NotificationType - let payload: Any! + super.init() + shared?.emitter = self + shared?.logger?.track(event: "\(#function)") } - private static func sendStoreAction(_ action: Action) { - if let emitter = self.emitter { + private func sendStoreAction(_ action: Action) { + shared?.logger?.track(event: "\(#function)") + if let emitter = shared?.emitter { emitter.sendEvent(withName: action.type.rawValue, body: action.payload) } } - @objc public static func dispatch(type: String, payload: Any!) { + @objc public func dispatch(type: String, payload: Any!) { + shared?.logger?.track(event: "\(#function): \(type)") let actionObj = Action(type: .init(rawValue: type) ?? .errorReceived, payload: payload) - if isInitialized { + if let didInitialize = shared?.didInitialize, didInitialize { sendStoreAction(actionObj) } else { - queue.append(actionObj) + shared?.queue.append(actionObj) } } @objc public override func startObserving() { - Self.isInitialized = true - for event in Self.queue { - Self.sendStoreAction(event) - } - Self.queue = [] - } - - @objc public override func stopObserving() { - Self.isInitialized = false - } + shared?.logger?.track(event: "\(#function)") + for event in (shared?.queue ?? []) { + sendStoreAction(event) + } + shared?.queue = [] + } + + @objc public override func stopObserving() { + shared?.logger?.track(event: "\(#function)") + } public override func supportedEvents() -> [String]! { return NotificationType.allCases.map { $0.rawValue } @@ -57,10 +85,11 @@ final public class Push: RCTEventEmitter { @objc(onFinish:withResolver:withRejecter:) public func onFinish(uuid: String, resolve: @escaping RCTPromiseResolveBlock, reject: @escaping RCTPromiseRejectBlock) { - if let callback = notificationCallbackDictionary[uuid] { + if let callback = shared?.notificationCallbackDictionary[uuid] { callback() - notificationCallbackDictionary.removeValue(forKey: uuid) + shared?.notificationCallbackDictionary.removeValue(forKey: uuid) } + shared?.logger?.track(event: "\(#function)") } @objc(requestPermissions:withRejecter:) @@ -77,10 +106,12 @@ final public class Push: RCTEventEmitter { } } } + shared?.logger?.track(event: "\(#function)") } @objc(getAuthorizationStatus:withRejecter:) func getAuthorizationStatus(resolve: @escaping RCTPromiseResolveBlock, reject: @escaping RCTPromiseRejectBlock) -> Void { + shared?.logger?.track(event: "\(#function)") DispatchQueue.main.async { UNUserNotificationCenter.current().getNotificationSettings { settings in resolve(settings.authorizationStatus.rawValue) @@ -94,6 +125,7 @@ final public class Push: RCTEventEmitter { UIApplication.shared.registerForRemoteNotifications() resolve(true) } + shared?.logger?.track(event: "\(#function)") } @objc(isRegisteredForRemoteNotifications:withRejecter:) @@ -102,6 +134,7 @@ final public class Push: RCTEventEmitter { let value = UIApplication.shared.isRegisteredForRemoteNotifications resolve(value) } + shared?.logger?.track(event: "\(#function)") } } @@ -113,43 +146,47 @@ extension Push: UNUserNotificationCenterDelegate { return String(format: "%02x", data) } let token = tokenParts.joined() - Self.dispatch(type: NotificationType.deviceTokenReceived.rawValue, payload: token) + dispatch(type: NotificationType.deviceTokenReceived.rawValue, payload: token) + shared?.logger?.track(event: "\(#function)") } public func application(_ application: UIApplication, didFailToRegisterForRemoteNotificationsWithError error: Error) { - Self.dispatch( + dispatch( type: NotificationType.errorReceived.rawValue, payload: error.localizedDescription ) + shared?.logger?.track(event: "\(#function)") } public func userNotificationCenter(_ center: UNUserNotificationCenter, willPresent notification: UNNotification, withCompletionHandler completionHandler: @escaping (UNNotificationPresentationOptions) -> Void) { let uuid = UUID().uuidString - Self.dispatch( + dispatch( type: NotificationType.notificationReceived.rawValue, payload: ["payload": notification.request.content.userInfo, "uuid": uuid, "kind": "foreground"] ) - notificationCallbackDictionary[uuid] = { + shared?.notificationCallbackDictionary[uuid] = { completionHandler([.badge, .banner, .sound, .list]) } - DispatchQueue.main.asyncAfter(deadline: .now() + 29) { - if let callback = self.notificationCallbackDictionary[uuid] { + DispatchQueue.main.asyncAfter(deadline: .now() + 29) { [weak self] in + if let callback = self?.shared?.notificationCallbackDictionary[uuid] { callback() - self.notificationCallbackDictionary.removeValue(forKey: uuid) + self?.shared?.notificationCallbackDictionary.removeValue(forKey: uuid) } } + shared?.logger?.track(event: "\(#function)") } public func userNotificationCenter(_ center: UNUserNotificationCenter, didReceive response: UNNotificationResponse) async { - Self.dispatch( + dispatch( type: NotificationType.notificationReceived.rawValue, payload: ["payload": response.notification.request.content.userInfo, "kind": "opened"] ) + shared?.logger?.track(event: "\(#function)") } public func application(_ application: UIApplication, didReceiveRemoteNotification userInfo: [AnyHashable: Any], fetchCompletionHandler completionHandler: @escaping (UIBackgroundFetchResult) -> Void) { if application.applicationState == .active { - Self.dispatch( + dispatch( type: NotificationType.errorReceived.rawValue, payload: "Background notification received while the app is foregrounded state, use to handle `notificationReceived` in the foreground." ) @@ -157,19 +194,20 @@ extension Push: UNUserNotificationCenterDelegate { return } let uuid = UUID().uuidString - Self.dispatch( + dispatch( type: NotificationType.notificationReceived.rawValue, payload: ["payload": userInfo, "uuid": uuid, "kind": "background"] ) - notificationCallbackDictionary[uuid] = { + shared?.notificationCallbackDictionary[uuid] = { completionHandler(.newData) } - DispatchQueue.main.asyncAfter(deadline: .now() + 29) { - if let callback = self.notificationCallbackDictionary[uuid] { + DispatchQueue.main.asyncAfter(deadline: .now() + 29) { [weak self] in + if let callback = self?.shared?.notificationCallbackDictionary[uuid] { callback() - self.notificationCallbackDictionary.removeValue(forKey: uuid) + self?.shared?.notificationCallbackDictionary.removeValue(forKey: uuid) } } + shared?.logger?.track(event: "\(#function)") } }