From 6ddc91c77125fa56a5ce57667a543ff15ab9deec Mon Sep 17 00:00:00 2001 From: Braxton Ward Date: Tue, 13 Jan 2026 16:11:23 -0700 Subject: [PATCH] feat: add rn logging of native debug events --- atomicfi-transact-react-native.podspec | 4 ++-- example/package.json | 2 +- example/screens/PresentActionScreen.tsx | 19 +++++++++++++++++ example/screens/TransactScreen.tsx | 16 ++++++++++++++ ios/TransactReactNative.m | 2 ++ ios/TransactReactNative.swift | 24 ++++++++++++++------- src/index.tsx | 6 ++++++ src/ios.tsx | 28 +++++++++++++++++++++++-- 8 files changed, 89 insertions(+), 12 deletions(-) diff --git a/atomicfi-transact-react-native.podspec b/atomicfi-transact-react-native.podspec index ebc605d..eafde7b 100644 --- a/atomicfi-transact-react-native.podspec +++ b/atomicfi-transact-react-native.podspec @@ -20,13 +20,13 @@ Pod::Spec.new do |s| # See https://github.com/facebook/react-native/blob/febf6b7f33fdb4904669f99d795eba4c0f95d7bf/scripts/cocoapods/new_architecture.rb#L79. if respond_to?(:install_modules_dependencies, true) install_modules_dependencies(s) - s.dependency "AtomicSDK", "3.26.0" + s.dependency "AtomicSDK", "3.27.0" s.pod_target_xcconfig = { "DEFINES_MODULE" => "YES", } else s.dependency "React-Core" - s.dependency "AtomicSDK", "3.26.0" + s.dependency "AtomicSDK", "3.27.0" # Don't install the dependencies when we run `pod install` in the old architecture. if ENV['RCT_NEW_ARCH_ENABLED'] == '1' then diff --git a/example/package.json b/example/package.json index aef398b..a8774cf 100644 --- a/example/package.json +++ b/example/package.json @@ -7,7 +7,7 @@ "android": "npx expo run:android", "ios": "npx expo run:ios", "web": "npx expo start --web", - "clean": "npx expo prebuild --clean", + "clean": "cd .. && yarn bob build && cd example && npx expo prebuild --clean", "lint": "expo lint" }, "dependencies": { diff --git a/example/screens/PresentActionScreen.tsx b/example/screens/PresentActionScreen.tsx index 36310d9..b3713de 100644 --- a/example/screens/PresentActionScreen.tsx +++ b/example/screens/PresentActionScreen.tsx @@ -8,6 +8,7 @@ import { Alert, TextInput, Platform, + Switch, } from 'react-native'; import type { NativeStackScreenProps } from '@react-navigation/native-stack'; import type { RootStackParamList } from '../App'; @@ -31,6 +32,7 @@ const PresentActionScreen: React.FC = () => { const [isLoading, setIsLoading] = useState(false); const [presentationStyleIOS, setPresentationStyleIOS] = useState(PresentationStyles.formSheet); + const [debugEnabled, setDebugEnabled] = useState(false); const environmentOptions = [ { key: 'sandbox' as EnvironmentOption, label: 'Sandbox' }, @@ -85,6 +87,7 @@ const PresentActionScreen: React.FC = () => { id: actionId.trim(), environment: getEnvironment(), presentationStyleIOS, + setDebug: debugEnabled, onLaunch: () => { console.log('Action launched'); setIsLoading(false); @@ -172,6 +175,22 @@ const PresentActionScreen: React.FC = () => { + + Debug Mode + + + Off + + On + + + + How it works diff --git a/example/screens/TransactScreen.tsx b/example/screens/TransactScreen.tsx index 9f79fae..fea04af 100644 --- a/example/screens/TransactScreen.tsx +++ b/example/screens/TransactScreen.tsx @@ -46,6 +46,7 @@ const TransactScreen: React.FC = () => { const [deeplinkStep, setDeeplinkStep] = useState( Step.LOGIN_COMPANY ); + const [debugEnabled, setDebugEnabled] = useState(false); const products = [ { key: Product.DEPOSIT, label: 'Deposit' }, @@ -141,6 +142,7 @@ const TransactScreen: React.FC = () => { config, environment: getEnvironment(), presentationStyleIOS, + setDebug: debugEnabled, onInteraction: (interaction: any) => { console.log('Interaction:', interaction); }, @@ -238,6 +240,20 @@ const TransactScreen: React.FC = () => { /> + + + Debug Mode + + Off + + On + + diff --git a/ios/TransactReactNative.m b/ios/TransactReactNative.m index 4bbd2a5..8a24cc2 100644 --- a/ios/TransactReactNative.m +++ b/ios/TransactReactNative.m @@ -7,12 +7,14 @@ @interface RCT_EXTERN_MODULE(TransactReactNative, RCTEventEmitter) RCT_EXTERN_METHOD(presentTransact:(NSDictionary *)config environment:(NSDictionary *)environment presentationStyle:(nullable NSString *)presentationStyle + setDebug:(nullable NSNumber *)setDebug withResolver:(RCTPromiseResolveBlock)resolve withRejecter:(RCTPromiseRejectBlock)reject) RCT_EXTERN_METHOD(presentAction:(NSString *)id environment:(NSDictionary *)environment presentationStyle:(nullable NSString *)presentationStyle + setDebug:(nullable NSNumber *)setDebug withResolver:(RCTPromiseResolveBlock)resolve withRejecter:(RCTPromiseRejectBlock)reject) diff --git a/ios/TransactReactNative.swift b/ios/TransactReactNative.swift index ca302fc..2d17dab 100644 --- a/ios/TransactReactNative.swift +++ b/ios/TransactReactNative.swift @@ -37,8 +37,8 @@ class TransactReactNative: RCTEventEmitter { } } - @objc(presentTransact:environment:presentationStyle:withResolver:withRejecter:) - func presentTransact(config: [String: Any], environment: [String: Any], presentationStyle: String?, resolve: @escaping RCTPromiseResolveBlock, reject: @escaping RCTPromiseRejectBlock) -> Void { + @objc(presentTransact:environment:presentationStyle:setDebug:withResolver:withRejecter:) + func presentTransact(config: [String: Any], environment: [String: Any], presentationStyle: String?, setDebug: NSNumber?, resolve: @escaping RCTPromiseResolveBlock, reject: @escaping RCTPromiseRejectBlock) -> Void { DispatchQueue.main.async { guard let source = RCTPresentedViewController() else { return } @@ -48,6 +48,11 @@ class TransactReactNative: RCTEventEmitter { do { var json = config + let debugEnabled = setDebug?.boolValue ?? false + Atomic.setDebug(isEnabled: debugEnabled, forwardLogs: { logMessage in + self.sendEvent(withName: "onDebugLog", body: ["message": logMessage]) + }) + let parsedPresentationStyle = self.parsePresentationStyle(presentationStyle) if var platform = AtomicConfig.Platform().encode() as? [String: Any] { @@ -130,14 +135,19 @@ class TransactReactNative: RCTEventEmitter { } } - @objc(presentAction:environment:presentationStyle:withResolver:withRejecter:) - func presentAction(id: String, environment: [String: Any], presentationStyle: String?, resolve: @escaping RCTPromiseResolveBlock, reject: @escaping RCTPromiseRejectBlock) -> Void { + @objc(presentAction:environment:presentationStyle:setDebug:withResolver:withRejecter:) + func presentAction(id: String, environment: [String: Any], presentationStyle: String?, setDebug: NSNumber?, resolve: @escaping RCTPromiseResolveBlock, reject: @escaping RCTPromiseRejectBlock) -> Void { DispatchQueue.main.async { guard let source = RCTPresentedViewController() else { return } - + let parsedEnvironment = self.parseEnvironment(environment) let parsedPresentationStyle = self.parsePresentationStyle(presentationStyle) - + + let debugEnabled = setDebug?.boolValue ?? false + Atomic.setDebug(isEnabled: debugEnabled, forwardLogs: { logMessage in + self.sendEvent(withName: "onDebugLog", body: ["message": logMessage]) + }) + Atomic.presentAction( from: source, id: id, @@ -182,7 +192,7 @@ class TransactReactNative: RCTEventEmitter { } @objc override func supportedEvents() -> [String] { - return ["onInteraction", "onDataRequest", "onLaunch", "onCompletion", "onAuthStatusUpdate", "onTaskStatusUpdate"] + return ["onInteraction", "onDataRequest", "onLaunch", "onCompletion", "onAuthStatusUpdate", "onTaskStatusUpdate", "onDebugLog"] } @objc override static func requiresMainQueueSetup() -> Bool { diff --git a/src/index.tsx b/src/index.tsx index 2a01e1c..5b19f84 100644 --- a/src/index.tsx +++ b/src/index.tsx @@ -102,6 +102,7 @@ export const Atomic = { onAuthStatusUpdate, onTaskStatusUpdate, presentationStyleIOS, + setDebug, }: { config: Config; environment?: CONSTANTS.TransactEnvironment; @@ -112,6 +113,7 @@ export const Atomic = { onFinish?: Function; onClose?: Function; presentationStyleIOS?: PresentationStyleIOS; + setDebug?: boolean; }): void { config.language = config.language || 'en'; config.theme = config.theme || {}; @@ -131,6 +133,7 @@ export const Atomic = { onAuthStatusUpdate, onTaskStatusUpdate, presentationStyleIOS, + setDebug, }; switch (Platform.OS) { @@ -153,6 +156,7 @@ export const Atomic = { onClose, onAuthStatusUpdate, onTaskStatusUpdate, + setDebug, }: { id: String; environment?: CONSTANTS.TransactEnvironment; @@ -162,6 +166,7 @@ export const Atomic = { onClose?: Function; onAuthStatusUpdate?: Function; onTaskStatusUpdate?: Function; + setDebug?: boolean; }): void { const args = { TransactReactNative, @@ -173,6 +178,7 @@ export const Atomic = { onClose, onAuthStatusUpdate, onTaskStatusUpdate, + setDebug, }; switch (Platform.OS) { diff --git a/src/ios.tsx b/src/ios.tsx index 48563e9..7b474bd 100644 --- a/src/ios.tsx +++ b/src/ios.tsx @@ -13,6 +13,7 @@ export const AtomicIOS = { onAuthStatusUpdate, onTaskStatusUpdate, presentationStyleIOS, + setDebug, }: { TransactReactNative: any; config: any; @@ -24,6 +25,7 @@ export const AtomicIOS = { onAuthStatusUpdate?: Function; onTaskStatusUpdate?: Function; presentationStyleIOS?: CONSTANTS.PresentationStyleIOS; + setDebug?: boolean; }): void { const TransactReactNativeEvents = new NativeEventEmitter( TransactReactNative @@ -31,13 +33,22 @@ export const AtomicIOS = { let onInteractionListener: any; let onDataRequestListener: any; let onAuthStatusUpdateListener: any; + let onDebugLogListener: any; const removeListeners = () => { if (onInteractionListener) onInteractionListener.remove(); if (onDataRequestListener) onDataRequestListener.remove(); if (onAuthStatusUpdateListener) onAuthStatusUpdateListener.remove(); + if (onDebugLogListener) onDebugLogListener.remove(); }; + onDebugLogListener = TransactReactNativeEvents.addListener( + 'onDebugLog', + (log) => { + console.warn('[TransactNative]', log.message); + } + ); + if (onInteraction) { onInteractionListener = TransactReactNativeEvents.addListener( 'onInteraction', @@ -84,7 +95,8 @@ export const AtomicIOS = { TransactReactNative.presentTransact( config, environment, - presentationStyleIOS + presentationStyleIOS, + setDebug ).then((event: any) => { if (event.finished && onFinish) { removeListeners(); @@ -105,6 +117,7 @@ export const AtomicIOS = { onClose, onAuthStatusUpdate, onTaskStatusUpdate, + setDebug, }: { TransactReactNative: any; id: String; @@ -115,18 +128,28 @@ export const AtomicIOS = { onClose?: Function; onAuthStatusUpdate?: Function; onTaskStatusUpdate?: Function; + setDebug?: boolean; }): void { const TransactReactNativeEvents = new NativeEventEmitter( TransactReactNative ); let onLaunchListener: any; let onAuthStatusUpdateListener: any; + let onDebugLogListener: any; const removeListeners = () => { if (onLaunchListener) onLaunchListener.remove(); if (onAuthStatusUpdateListener) onAuthStatusUpdateListener.remove(); + if (onDebugLogListener) onDebugLogListener.remove(); }; + onDebugLogListener = TransactReactNativeEvents.addListener( + 'onDebugLog', + (log) => { + console.warn('[TransactNative]', log.message); + } + ); + if (onLaunch) { onLaunchListener = TransactReactNativeEvents.addListener('onLaunch', () => onLaunch() @@ -150,7 +173,8 @@ export const AtomicIOS = { TransactReactNative.presentAction( id, environment, - presentationStyleIOS + presentationStyleIOS, + setDebug ).then((event: any) => { if (event.finished && onFinish) { removeListeners();