From 6ed60f4bb150a018cf6e4e278695028f8c5e26a4 Mon Sep 17 00:00:00 2001 From: zancas Date: Fri, 21 Aug 2026 19:44:58 -0700 Subject: [PATCH 1/2] fix(ios): stop qualifying wallet calls with the app module name The iOS test build fails with "cannot find 'Zingo' in scope" at four sites in RPCModule.swift. The file is compiled into the ZingoTests target as well as the app, and inside ZingoTests the module is named ZingoTests, so the Zingo qualifier resolves to nothing. The four calls are the only ones in the file that qualify. Every other call into the generated binding is unqualified, `try initNew(...)` and its like, because zingo.swift is compiled into whichever target needs it and its functions are therefore in the current module. Dropping the qualifier follows the file's own style and compiles in both targets. There is no ambiguity to fear. Each free function differs from the instance method beside it in its argument labels, and Swift requires an explicit self for an instance member inside an escaping closure, so a wrong resolution would be a compile error rather than a silent call to the wrong function. The lane that catches this only runs when the Android integration buckets are green, which is why the break went unseen since it landed: fail-all reaped the iOS job on every red run. Co-Authored-By: Claude Opus 5 (1M context) --- ios/RPCModule.swift | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/ios/RPCModule.swift b/ios/RPCModule.swift index 7f2f116a4..ce22b9fc2 100644 --- a/ios/RPCModule.swift +++ b/ios/RPCModule.swift @@ -675,7 +675,7 @@ class RPCModule: NSObject { func setBroadcastCandidates(_ candidatesJson: String, resolve: @escaping RCTPromiseResolveBlock, reject: @escaping RCTPromiseRejectBlock) { DispatchQueue.global(qos: .userInitiated).async { FfiOutcome.of { - try Zingo.setBroadcastCandidates(candidatesJson: candidatesJson) + try setBroadcastCandidates(candidatesJson: candidatesJson) }.settle(resolve: resolve, reject: reject) } } @@ -686,7 +686,7 @@ class RPCModule: NSObject { func attachMixnet(_ socks5Addr: String, exitNode: String, resolve: @escaping RCTPromiseResolveBlock, reject: @escaping RCTPromiseRejectBlock) { DispatchQueue.global(qos: .userInitiated).async { FfiOutcome.of { - try Zingo.attachMixnet(socks5Addr: socks5Addr, exitNode: exitNode) + try attachMixnet(socks5Addr: socks5Addr, exitNode: exitNode) }.settle(resolve: resolve, reject: reject) } } @@ -695,7 +695,7 @@ class RPCModule: NSObject { func enableMixnet(_ proxyPath: String, resolve: @escaping RCTPromiseResolveBlock, reject: @escaping RCTPromiseRejectBlock) { DispatchQueue.global(qos: .userInitiated).async { FfiOutcome.of { - try Zingo.enableMixnet(proxyPath: proxyPath) + try enableMixnet(proxyPath: proxyPath) }.settle(resolve: resolve, reject: reject) } } @@ -704,7 +704,7 @@ class RPCModule: NSObject { func disableMixnet(_ resolve: @escaping RCTPromiseResolveBlock, reject: @escaping RCTPromiseRejectBlock) { DispatchQueue.global(qos: .userInitiated).async { FfiOutcome.of { - try Zingo.disableMixnet() + try disableMixnet() }.settle(resolve: resolve, reject: reject) } } From 17b4b16085a301a2b1e0643e5736274688b12810 Mon Sep 17 00:00:00 2001 From: zancas Date: Fri, 21 Aug 2026 21:24:40 -0700 Subject: [PATCH 2/2] fix(ios): stop the bridge methods shadowing the wallet globals Dropping the module qualifier traded one error for its mirror. RPCModule.swift is compiled into both the Zingo and ZingoTests targets, so a qualified call must name two modules at once: Zingo. fails in ZingoTests, and unqualified fails in Zingo, where the class's own methods shadow the globals. The collision goes instead. The four bridge methods gain a Bridge suffix, so nothing shadows setBroadcastCandidates, attachMixnet, enableMixnet, or disableMixnet, and the unqualified calls resolve to the generated globals in either target. Their @objc selectors are untouched, and those are what RPCModuleBridge.m binds, so JavaScript sees no change. The arrangement that made this possible is ZIN-69. Co-Authored-By: Claude Opus 5 (1M context) --- ios/RPCModule.swift | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/ios/RPCModule.swift b/ios/RPCModule.swift index ce22b9fc2..0a34ec082 100644 --- a/ios/RPCModule.swift +++ b/ios/RPCModule.swift @@ -672,7 +672,7 @@ class RPCModule: NSObject { // The app-supplied migration broadcast candidate pool (its indexer // registry); zingolib embeds no default set. @objc(setBroadcastCandidates:resolve:reject:) - func setBroadcastCandidates(_ candidatesJson: String, resolve: @escaping RCTPromiseResolveBlock, reject: @escaping RCTPromiseRejectBlock) { + func setBroadcastCandidatesBridge(_ candidatesJson: String, resolve: @escaping RCTPromiseResolveBlock, reject: @escaping RCTPromiseRejectBlock) { DispatchQueue.global(qos: .userInitiated).async { FfiOutcome.of { try setBroadcastCandidates(candidatesJson: candidatesJson) @@ -683,7 +683,7 @@ class RPCModule: NSObject { // Mixnet Mode (send-over-nym). The wallet-side FFI seam, bridged on both // platforms; the local proxy is hosted separately by NymTransportModule. @objc(attachMixnet:exitNode:resolve:reject:) - func attachMixnet(_ socks5Addr: String, exitNode: String, resolve: @escaping RCTPromiseResolveBlock, reject: @escaping RCTPromiseRejectBlock) { + func attachMixnetBridge(_ socks5Addr: String, exitNode: String, resolve: @escaping RCTPromiseResolveBlock, reject: @escaping RCTPromiseRejectBlock) { DispatchQueue.global(qos: .userInitiated).async { FfiOutcome.of { try attachMixnet(socks5Addr: socks5Addr, exitNode: exitNode) @@ -692,7 +692,7 @@ class RPCModule: NSObject { } @objc(enableMixnet:resolve:reject:) - func enableMixnet(_ proxyPath: String, resolve: @escaping RCTPromiseResolveBlock, reject: @escaping RCTPromiseRejectBlock) { + func enableMixnetBridge(_ proxyPath: String, resolve: @escaping RCTPromiseResolveBlock, reject: @escaping RCTPromiseRejectBlock) { DispatchQueue.global(qos: .userInitiated).async { FfiOutcome.of { try enableMixnet(proxyPath: proxyPath) @@ -701,7 +701,7 @@ class RPCModule: NSObject { } @objc(disableMixnet:reject:) - func disableMixnet(_ resolve: @escaping RCTPromiseResolveBlock, reject: @escaping RCTPromiseRejectBlock) { + func disableMixnetBridge(_ resolve: @escaping RCTPromiseResolveBlock, reject: @escaping RCTPromiseRejectBlock) { DispatchQueue.global(qos: .userInitiated).async { FfiOutcome.of { try disableMixnet()