@@ -150,7 +150,7 @@ class ViewController: UIViewController {
150150 // For this demo, we'll use a mock URL that you'll need to replace
151151
152152 // REPLACE THIS with your actual backend endpoint that returns the payment link
153- let backendURL = " https://YOUR-NGROK-URL .ngrok-free.dev/api/create-order "
153+ let backendURL = " https://verdant-rowena-sigmoidally .ngrok-free.dev/api/create-order "
154154
155155 guard let url = URL ( string: backendURL) else {
156156 completion ( . failure( NSError ( domain: " Invalid backend URL " , code: - 1 , userInfo: nil ) ) )
@@ -324,15 +324,20 @@ extension ViewController: WKScriptMessageHandler {
324324
325325 guard message. name == " onramp " else { return }
326326
327+ // Log the type we're receiving
328+ logEvent ( " 📦 Message type: \( type ( of: message. body) ) " )
329+
327330 // Parse the message body
328331 if let messageBody = message. body as? String {
329332 // Try parsing as JSON string
333+ logEvent ( " 🔤 Received as String, parsing JSON... " )
330334 if let data = messageBody. data ( using: . utf8) ,
331335 let json = try ? JSONSerialization . jsonObject ( with: data) as? [ String : Any ] {
332336 handleMessageData ( json)
333337 }
334338 } else if let messageBody = message. body as? [ String : Any ] {
335339 // Already a dictionary
340+ logEvent ( " 📘 Received as Dictionary directly " )
336341 handleMessageData ( messageBody)
337342 }
338343 }
@@ -349,23 +354,35 @@ extension ViewController: WKNavigationDelegate {
349354 func webView( _ webView: WKWebView , didFinish navigation: WKNavigation ! ) {
350355 logEvent ( " 📄 WebView page loaded " )
351356
352- // Inject script to bridge postMessage to WKWebView
357+ // IMPORTANT: JavaScript Bridge for Coinbase postMessage Events
358+ // Coinbase uses standard web postMessage API, which doesn't automatically reach native iOS.
359+ // This bridge adapts web events to iOS's window.webkit.messageHandlers API.
360+ //
361+ // Two complementary approaches:
362+ // 1. postMessage override - catches direct postMessage() calls
363+ // 2. message event listener - catches iframe cross-origin messages
364+ //
365+ // Both are needed because Coinbase's architecture uses iframes internally.
366+ // Alternative: Use only #2 (less invasive but may miss some events)
367+
353368 let bridgeScript = """
354369 (function() {
355- // Override window.postMessage to send to native iOS
370+ // APPROACH 1: Override window.postMessage
371+ // Catches direct postMessage() calls made by Coinbase's page
356372 const originalPostMessage = window.postMessage;
357373 window.postMessage = function(message, targetOrigin) {
358- // Send to native
374+ // Forward to native iOS
359375 if (window.webkit && window.webkit.messageHandlers && window.webkit.messageHandlers.onramp) {
360376 window.webkit.messageHandlers.onramp.postMessage(message);
361377 }
362- // Also call original
378+ // Preserve original behavior for web compatibility
363379 originalPostMessage.apply(window, arguments);
364380 };
365381
366- // Listen for postMessage from iframe
382+ // APPROACH 2: Message Event Listener
383+ // Catches postMessage events crossing iframe boundaries (more common with Coinbase)
367384 window.addEventListener('message', function(event) {
368- // Validate origin is from Coinbase
385+ // Security: Only accept messages from Coinbase domains
369386 try {
370387 const originUrl = new URL(event.origin);
371388 const allowedHosts = ['pay.coinbase.com', 'coinbase.com'];
@@ -374,9 +391,10 @@ extension ViewController: WKNavigationDelegate {
374391 );
375392 if (!isAllowed) return;
376393 } catch (e) {
377- return;
394+ return; // Invalid origin URL
378395 }
379396
397+ // Parse and forward to native iOS
380398 const data = typeof event.data === 'string' ? JSON.parse(event.data) : event.data;
381399 if (window.webkit && window.webkit.messageHandlers && window.webkit.messageHandlers.onramp) {
382400 window.webkit.messageHandlers.onramp.postMessage(data);
0 commit comments