From 443149409ef7e7a513b90eec54c28e9c13750651 Mon Sep 17 00:00:00 2001 From: Braxton Ward Date: Thu, 16 Apr 2026 14:52:00 -0600 Subject: [PATCH] feat: add debug flag support for iOS and Android Adds a `debug` parameter to `Atomic.transact()` and `Atomic.presentAction()` that enables debug logging and makes the webview inspectable. - iOS: Calls `Atomic.setDebug(isEnabled:forwardLogs:)` to enable debug logs (printed via debugPrint) and make the webview inspectable - Android: Sets `webContentsDebuggingEnabled` on Config/ActionConfig - Adds Debug Mode toggle to example app settings Resolves SDK-517 --- .../AtomicTransactFlutterPlugin.kt | 8 ++++++-- example/lib/models/app_state.dart | 4 ++++ example/lib/screens/pay_link_screen.dart | 1 + example/lib/screens/settings_screen.dart | 5 +++++ example/lib/screens/user_link_screen.dart | 1 + .../AtomicTransactFlutterPlugin.swift | 20 ++++++++++++++++--- .../atomic_method_channel.dart | 10 ++++++++++ .../atomic_platform_interface.dart | 2 ++ lib/src/atomic.dart | 6 +++++- 9 files changed, 51 insertions(+), 6 deletions(-) diff --git a/android/src/main/kotlin/atomic/financial/atomic_transact_flutter/AtomicTransactFlutterPlugin.kt b/android/src/main/kotlin/atomic/financial/atomic_transact_flutter/AtomicTransactFlutterPlugin.kt index 70e3487..9c152cb 100644 --- a/android/src/main/kotlin/atomic/financial/atomic_transact_flutter/AtomicTransactFlutterPlugin.kt +++ b/android/src/main/kotlin/atomic/financial/atomic_transact_flutter/AtomicTransactFlutterPlugin.kt @@ -34,6 +34,7 @@ class AtomicTransactFlutterPlugin: FlutterPlugin, MethodCallHandler, ActivityAwa if (call.method == "presentTransact") { val transactPath = call.argument("transactPath") as String? ?: "" val apiPath = call.argument("apiPath") as String? ?: "" + val debug = call.argument("debug") ?: false val configuration = call.argument>("configuration") val publicToken = configuration?.get("publicToken") as String val scope = configuration?.get("scope") as String @@ -66,7 +67,8 @@ class AtomicTransactFlutterPlugin: FlutterPlugin, MethodCallHandler, ActivityAwa experiments = configExperimentsFromMap(experiments), search = configSearchFromMap(search), environment = Config.Environment.CUSTOM, - environmentURL = transactPath + environmentURL = transactPath, + webContentsDebuggingEnabled = debug ) Transact.registerReceiver(activity, object: TransactBroadcastReceiver() { @@ -96,12 +98,14 @@ class AtomicTransactFlutterPlugin: FlutterPlugin, MethodCallHandler, ActivityAwa val id = call.argument("id") ?: return val transactPath = call.argument("transactPath") as String? ?: "" val apiPath = call.argument("apiPath") as String? ?: "" + val debug = call.argument("debug") ?: false var theme = call.argument>("theme") val config = ActionConfig( id = id, environment = Config.Environment.CUSTOM, environmentURL = transactPath, - theme = configThemeFromMap(theme) + theme = configThemeFromMap(theme), + webContentsDebuggingEnabled = debug ) Transact.registerReceiver(activity, object: TransactBroadcastReceiver() { diff --git a/example/lib/models/app_state.dart b/example/lib/models/app_state.dart index 41425b5..1746dc5 100644 --- a/example/lib/models/app_state.dart +++ b/example/lib/models/app_state.dart @@ -54,6 +54,10 @@ class AppState extends ChangeNotifier { bool get darkMode => _darkMode; set darkMode(bool v) { _darkMode = v; notifyListeners(); } + bool _debug = false; + bool get debug => _debug; + set debug(bool v) { _debug = v; notifyListeners(); } + // Pay Link PayLinkTask _payLinkTask = PayLinkTask.switchPayment; PayLinkTask get payLinkTask => _payLinkTask; diff --git a/example/lib/screens/pay_link_screen.dart b/example/lib/screens/pay_link_screen.dart index 62d4fad..d6401dc 100644 --- a/example/lib/screens/pay_link_screen.dart +++ b/example/lib/screens/pay_link_screen.dart @@ -28,6 +28,7 @@ class PayLinkScreen extends StatelessWidget { Atomic.transact( config: config, environment: state.environment, + debug: state.debug, onInteraction: (interaction) { eventLog.add(EventEntry( type: EventType.interaction, diff --git a/example/lib/screens/settings_screen.dart b/example/lib/screens/settings_screen.dart index 51c0378..809a37e 100644 --- a/example/lib/screens/settings_screen.dart +++ b/example/lib/screens/settings_screen.dart @@ -58,6 +58,11 @@ class SettingsScreen extends StatelessWidget { value: state.darkMode, onChanged: (v) => state.darkMode = v, ), + ToggleRow( + title: 'Debug Mode', + value: state.debug, + onChanged: (v) => state.debug = v, + ), const SizedBox(height: 32), ], ), diff --git a/example/lib/screens/user_link_screen.dart b/example/lib/screens/user_link_screen.dart index 67ac484..cce936c 100644 --- a/example/lib/screens/user_link_screen.dart +++ b/example/lib/screens/user_link_screen.dart @@ -28,6 +28,7 @@ class UserLinkScreen extends StatelessWidget { Atomic.transact( config: config, environment: state.environment, + debug: state.debug, onInteraction: (interaction) { eventLog.add(EventEntry( type: EventType.interaction, diff --git a/ios/atomic_transact_flutter/Sources/atomic_transact_flutter/AtomicTransactFlutterPlugin.swift b/ios/atomic_transact_flutter/Sources/atomic_transact_flutter/AtomicTransactFlutterPlugin.swift index c508aee..cbb5d13 100644 --- a/ios/atomic_transact_flutter/Sources/atomic_transact_flutter/AtomicTransactFlutterPlugin.swift +++ b/ios/atomic_transact_flutter/Sources/atomic_transact_flutter/AtomicTransactFlutterPlugin.swift @@ -23,10 +23,11 @@ public class AtomicTransactFlutterPlugin: NSObject, FlutterPlugin { let arguments = call.arguments as! [String: Any] let transactPath = arguments["transactPath"] as! String let apiPath = arguments["apiPath"] as! String + let debugEnabled = arguments["debug"] as? Bool ?? false let decoder = JSONDecoder() - + let presentationStyle = getPresentationStyle(from: arguments["presentationStyleIOS"] as? String) - + if let configuration = arguments["configuration"] as? [String: Any] { do { var json = configuration @@ -41,6 +42,12 @@ public class AtomicTransactFlutterPlugin: NSObject, FlutterPlugin { var config = try decoder.decode(AtomicConfig.self, from: data) Task { @MainActor in + await Atomic.setDebug(isEnabled: debugEnabled, forwardLogs: { logMessage in + DispatchQueue.main.async { + self.channel.invokeMethod("onDebugLog", arguments: ["message": logMessage]) + } + }) + if let controller = UIApplication.shared.windows.filter({$0.isKeyWindow}).first?.rootViewController { Atomic.presentTransact(from: controller, config: config, environment: .custom(transactPath: transactPath, apiPath: apiPath), presentationStyle: presentationStyle, onInteraction: onInteraction, onDataRequest: onDataRequest, onAuthStatusUpdate: onAuthStatusUpdate, onTaskStatusUpdate: onTaskStatusUpdate, onCompletion: onCompletion) result(nil) @@ -58,6 +65,7 @@ public class AtomicTransactFlutterPlugin: NSObject, FlutterPlugin { let id = arguments["id"] as! String let transactPath = arguments["transactPath"] as! String let apiPath = arguments["apiPath"] as! String + let debugEnabled = arguments["debug"] as? Bool ?? false let decoder = JSONDecoder() let theme: AtomicConfig.Theme = { if let themeData = arguments["theme"] as? [String: Any], @@ -67,10 +75,16 @@ public class AtomicTransactFlutterPlugin: NSObject, FlutterPlugin { } return AtomicConfig.Theme(dark: .system) }() - + let presentationStyle = getPresentationStyle(from: arguments["presentationStyleIOS"] as? String) Task { @MainActor in + await Atomic.setDebug(isEnabled: debugEnabled, forwardLogs: { logMessage in + DispatchQueue.main.async { + self.channel.invokeMethod("onDebugLog", arguments: ["message": logMessage]) + } + }) + if let controller = UIApplication.shared.windows.filter({$0.isKeyWindow}).first?.rootViewController { Atomic.presentAction(from: controller, id: id, environment: .custom(transactPath: transactPath, apiPath: apiPath), presentationStyle: presentationStyle, theme: theme, onLaunch: onLaunch, onAuthStatusUpdate: onAuthStatusUpdate, onTaskStatusUpdate: onTaskStatusUpdate, onCompletion: onCompletion) result(nil) diff --git a/lib/platform_interface/atomic_method_channel.dart b/lib/platform_interface/atomic_method_channel.dart index e40d01b..3ab44ac 100644 --- a/lib/platform_interface/atomic_method_channel.dart +++ b/lib/platform_interface/atomic_method_channel.dart @@ -1,4 +1,5 @@ import 'package:atomic_transact_flutter/src/events.dart'; +import 'package:flutter/foundation.dart'; import 'package:flutter/services.dart'; import '../src/config.dart'; @@ -21,6 +22,7 @@ class AtomicMethodChannel extends AtomicPlatformInterface { required AtomicConfig configuration, required TransactEnvironment environment, AtomicPresentationStyleIOS? presentationStyleIOS, + bool debug = false, }) async { await _channel.invokeMethod( 'presentTransact', @@ -29,6 +31,7 @@ class AtomicMethodChannel extends AtomicPlatformInterface { 'transactPath': environment.transactPath, 'apiPath': environment.apiPath, 'presentationStyleIOS': presentationStyleIOS?.name, + 'debug': debug, }, ); } @@ -41,6 +44,7 @@ class AtomicMethodChannel extends AtomicPlatformInterface { required TransactEnvironment environment, AtomicTheme? theme, AtomicPresentationStyleIOS? presentationStyleIOS, + bool debug = false, }) async { await _channel.invokeMethod('presentAction', { 'id': id, @@ -48,6 +52,7 @@ class AtomicMethodChannel extends AtomicPlatformInterface { 'apiPath': environment.apiPath, 'theme': theme?.toJson(), 'presentationStyleIOS': presentationStyleIOS?.name, + 'debug': debug, }); } @@ -95,6 +100,11 @@ class AtomicMethodChannel extends AtomicPlatformInterface { onLaunch?.call(); break; + case 'onDebugLog': + final message = call.arguments['message'] as String? ?? ''; + debugPrint('[AtomicTransact] $message'); + break; + case 'onAuthStatusUpdate': final authData = call.arguments['auth']; onAuthStatusUpdate?.call( diff --git a/lib/platform_interface/atomic_platform_interface.dart b/lib/platform_interface/atomic_platform_interface.dart index 269244d..c74ba1e 100644 --- a/lib/platform_interface/atomic_platform_interface.dart +++ b/lib/platform_interface/atomic_platform_interface.dart @@ -42,6 +42,7 @@ abstract class AtomicPlatformInterface extends PlatformInterface { required AtomicConfig configuration, required TransactEnvironment environment, AtomicPresentationStyleIOS? presentationStyleIOS, + bool debug = false, }) async { throw UnimplementedError('presentTransact() has not been implemented.'); } @@ -51,6 +52,7 @@ abstract class AtomicPlatformInterface extends PlatformInterface { required TransactEnvironment environment, AtomicTheme? theme, AtomicPresentationStyleIOS? presentationStyleIOS, + bool debug = false, }) async { throw UnimplementedError('presentAction() has not been implemented.'); } diff --git a/lib/src/atomic.dart b/lib/src/atomic.dart index fa35f73..55dfa80 100644 --- a/lib/src/atomic.dart +++ b/lib/src/atomic.dart @@ -24,6 +24,7 @@ class Atomic { AtomicTaskStatusUpdateHandler? onTaskStatusUpdate, AtomicCompletionHandler? onCompletion, AtomicPresentationStyleIOS? presentationStyleIOS, + bool debug = false, }) async { if (_isLoading) { return; @@ -49,6 +50,7 @@ class Atomic { configuration: config, environment: environment, presentationStyleIOS: presentationStyleIOS, + debug: debug, ); } @@ -61,6 +63,7 @@ class Atomic { AtomicCompletionHandler? onCompletion, AtomicTheme? theme, AtomicPresentationStyleIOS? presentationStyleIOS, + bool debug = false, }) async { if (_isLoading) { return; @@ -81,7 +84,8 @@ class Atomic { id: id, environment: environment, theme: theme, - presentationStyleIOS: presentationStyleIOS); + presentationStyleIOS: presentationStyleIOS, + debug: debug); } static Future close() async {