From 54109570afd574e4244cfb8d5a5f23d09021f221 Mon Sep 17 00:00:00 2001 From: Ahmed-Ali Date: Sun, 16 Jun 2024 23:33:23 +0400 Subject: [PATCH] chore: Native logging bridge --- ios/wrappers/logging/CioLoggingEmitter.mm | 4 + ios/wrappers/logging/CioLoggingEmitter.swift | 91 ++++++++++++++++++++ src/native-logger-listener.ts | 49 +++++++++++ 3 files changed, 144 insertions(+) create mode 100644 ios/wrappers/logging/CioLoggingEmitter.mm create mode 100644 ios/wrappers/logging/CioLoggingEmitter.swift create mode 100644 src/native-logger-listener.ts diff --git a/ios/wrappers/logging/CioLoggingEmitter.mm b/ios/wrappers/logging/CioLoggingEmitter.mm new file mode 100644 index 00000000..779e1a32 --- /dev/null +++ b/ios/wrappers/logging/CioLoggingEmitter.mm @@ -0,0 +1,4 @@ +#import + +@interface RCT_EXTERN_REMAP_MODULE(CioLoggingEmitter, CioLoggingEmitter, RCTEventEmitter) +@end diff --git a/ios/wrappers/logging/CioLoggingEmitter.swift b/ios/wrappers/logging/CioLoggingEmitter.swift new file mode 100644 index 00000000..a5b2527d --- /dev/null +++ b/ios/wrappers/logging/CioLoggingEmitter.swift @@ -0,0 +1,91 @@ + +import React +import CioInternalCommon + +typealias CioLogger = CioInternalCommon.Logger + +@objc(CioLoggingEmitter) +class CioLoggingEmitter: RCTEventEmitter { + + fileprivate static var eventName = "CioLogEvent" + + fileprivate var hasObservers = false + + override func startObserving() { + hasObservers = true + } + + override func stopObserving() { + hasObservers = false + } + + override func supportedEvents() -> [String] { + [Self.eventName] + } + + override var methodQueue: dispatch_queue_t! { + DispatchQueue(label: Self.moduleName()) + } +} + + +class CioLoggerWrapper: CioLogger { + private(set) var logLevel: CioLogLevel + private var moduleRegistry: RCTModuleRegistry + + static func getInstance (moduleRegistry: RCTModuleRegistry, logLevel: CioLogLevel) -> CioLogger { + if let wrapper = DIGraphShared.shared.logger as? Self { + wrapper.logLevel = logLevel + wrapper.moduleRegistry = moduleRegistry + return wrapper + } + + let wrapper = CioLoggerWrapper(moduleRegistry: moduleRegistry, logLevel: logLevel) + DIGraphShared.shared.override(value: wrapper, forType: CioLogger.self) + return wrapper + } + + private init(moduleRegistry: RCTModuleRegistry, logLevel: CioLogLevel?) { + self.logLevel = logLevel ?? .none + self.moduleRegistry = moduleRegistry + } + + func setLogLevel(_ level: CioLogLevel) { + logLevel = level + } + + func debug(_ message: String) { + emit(message, level: .debug) + } + + func info(_ message: String) { + emit(message, level: .info) + } + + func error(_ message: String) { + emit(message, level: .error) + } + + private func emit(_ message: String, level: CioLogLevel) { + if shouldEmit(level: level), let emitter, emitter.hasObservers { + emitter.sendEvent(withName: CioLoggingEmitter.eventName, body: ["logLevel": level.rawValue, "message": message]) + } + } + + private func shouldEmit(level: CioLogLevel) -> Bool { + switch self.logLevel { + case .none: return false + case .error: + return level == .error + case .info: + return level == .error || level == .info + case .debug: + return true + } + } + + private var emitter: CioLoggingEmitter? { + moduleRegistry.module(forName: "CioLoggingEmitter") as? CioLoggingEmitter + } + +} diff --git a/src/native-logger-listener.ts b/src/native-logger-listener.ts new file mode 100644 index 00000000..29b38371 --- /dev/null +++ b/src/native-logger-listener.ts @@ -0,0 +1,49 @@ +import { NativeEventEmitter, NativeModules, Platform } from 'react-native'; +import { CioLogLevel } from './cio-config'; + +const LINKING_ERROR = + `The package 'customerio-reactnative' doesn't seem to be linked. Make sure: ` + + Platform.select({ ios: "- You have run 'pod install'\n", default: '' }) + + '- You rebuilt the app after installing the package\n' + + '- You are not using Expo Go\n'; + +const CioLoggingEmitter = NativeModules.CioLoggingEmitter + ? NativeModules.CioLoggingEmitter + : new Proxy( + {}, + { + get() { + throw new Error(LINKING_ERROR); + }, + } + ); + +export class NativeLoggerListener { + static initialize() { + const bridge = new NativeEventEmitter(CioLoggingEmitter); + bridge.addListener( + 'CioLogEvent', + (event: { logLevel: CioLogLevel; message: string }) => { + // if we just use console.log, it will log to the JS side but it will prevent RN default behavior of redirecting logs to the native side + // doing it async allows to be logged to the JS side and then to the native side + async function log() { + switch (event.logLevel) { + case CioLogLevel.Debug: + console.debug(event.message); + break; + case CioLogLevel.Info: + console.info(event.message); + break; + case CioLogLevel.Error: + console.error(event.message); + break; + default: + console.log(event); + break; + } + } + log(); + } + ); + } +}