Integration Guide
Integrate CoinmeBridge in Your iOS App
Add Coinme's cryptocurrency purchase flow to your native iOS app in under 10 minutes.
TL;DR
import CoinmeBridge
var config = CoinmeConfig(rampId: "your-ramp-id", partnerKey: "your-partner-key")
config.destinationCurrency = "BTC"
let vc = CoinmeViewController(
orchestratorURL: URL(string: "https://widget.coinme.com")!,
config: config
)
vc.delegate = self // adopt CoinmeDelegate
present(vc, animated: true)Read on for the full step-by-step walkthrough.
Prerequisites
- Xcode 15.0+ and Swift 5.7+
- iOS 14.0+ deployment target
- Your rampId and partnerKey (provided by Coinme)
- Your Orchestrator URL (e.g.
https://widget.coinme.com)
Step 1: Add the Package
Configure the Cloudsmith Swift registry (one-time setup — your Coinme integration specialist will provide the entitlement token):
swift package-registry set https://swift.cloudsmith.io/coinme/coinme-sdk-frontend/ --global
Current Project Scope:Omit
--globalto configure for the current project only.
swift package-registry login https://swift.cloudsmith.io/coinme/coinme-sdk-frontend/ --username token --password <ENTITLEMENT_TOKEN>
Note:The login command must be a single line. Replace
<ENTITLEMENT_TOKEN>with the token from your Coinme integration specialist.
In Xcode, go to File > Add Package Dependencies. In the search field, enter the package identifier:
coinme.coinme-native-bridge-ios
Xcode resolves it from the configured registry. Set the dependency rule to your desired version range (e.g. Up to Next Major), then add the CoinmeBridge library to your app target.
Or add it directly to your Package.swift:
dependencies: [
.package(id: "coinme.coinme-native-bridge-ios", from: "1.0.0")
]And add "CoinmeBridge" to your target's dependencies.
Step 2: Import the Module
import CoinmeBridgeStep 3: Create a Configuration
Build a CoinmeConfig with your credentials:
var config = CoinmeConfig(
rampId: "your-ramp-id",
partnerKey: "your-partner-key",
environment: "prod" // "dev", "stage", or "prod"
)
// Optional: pre-select a cryptocurrency
config.destinationCurrency = "BTC"Step 4: Implement the Delegate
Adopt CoinmeDelegate to receive events and errors:
extension YourViewController: CoinmeDelegate {
func coinme(_ vc: CoinmeViewController, didReceiveEvent event: CoinmeEvent) {
switch event {
case .transactionComplete:
print("Transaction succeeded")
case .transactionFailed:
print("Transaction failed")
case .userCancelled:
vc.dismiss(animated: true)
case .kycRequired:
print("KYC verification required")
case .sheetOpened:
break // Orchestrator opened an info sheet
case .sheetClosed:
break // Orchestrator closed its info sheet
case .unknown(let type, _):
print("Unhandled event: \(type)")
}
}
func coinme(_ vc: CoinmeViewController, didFailWithError error: CoinmeError) {
print("Bridge error: \(error.localizedDescription)")
vc.dismiss(animated: true)
}
}Step 5: Present the View Controller
let coinmeVC = CoinmeViewController(
orchestratorURL: URL(string: "https://widget.coinme.com")!,
config: config
)
coinmeVC.delegate = self
present(coinmeVC, animated: true)That's it. CoinmeViewController handles the WebView, bridge handshake, and session lifecycle automatically. It presents full-screen by default.
Minimal Complete Example
import UIKit
import CoinmeBridge
class BuyViewController: UIViewController, CoinmeDelegate {
func showCoinme() {
var config = CoinmeConfig(
rampId: "your-ramp-id",
partnerKey: "your-partner-key"
)
config.destinationCurrency = "BTC"
let vc = CoinmeViewController(
orchestratorURL: URL(string: "https://widget.coinme.com")!,
config: config
)
vc.delegate = self
present(vc, animated: true)
}
// MARK: - CoinmeDelegate
func coinme(_ vc: CoinmeViewController, didReceiveEvent event: CoinmeEvent) {
if case .transactionComplete = event {
vc.dismiss(animated: true)
}
if case .userCancelled = event {
vc.dismiss(animated: true)
}
}
func coinme(_ vc: CoinmeViewController, didFailWithError error: CoinmeError) {
print("Error: \(error.localizedDescription)")
vc.dismiss(animated: true)
}
}Customization
Permission Flags
Camera and location default to true to provide the full Coinme experience. Screen capture protection is on by default (allowsCapture = false).
| Flag | Default | What it controls | Info.plist keys required |
|---|---|---|---|
allowsCamera | true | Camera, microphone, and photo library access for KYC document/selfie capture and upload | NSCameraUsageDescription, NSMicrophoneUsageDescription, NSPhotoLibraryUsageDescription |
allowsLocation | true | Browser geolocation API (e.g. store finders) | NSLocationWhenInUseUsageDescription |
allowsCapture | false | When false, screen recording and screenshots of the WebView are blocked | None |
Since allowsCamera and allowsLocation are true by default, you must add the corresponding Info.plist keys listed above. To opt out, set the flags to false:
config.allowsCamera = false // Disables camera + microphone
config.allowsLocation = false // Disables navigator.geolocation in the WebView
config.allowsCapture = true // Disables screen capture protection
Trade-offs of disabling permissions:
allowsCamera = false: TheallowsCameraflag and its corresponding Info.plist permissions (NSCameraUsageDescription,NSMicrophoneUsageDescription,NSPhotoLibraryUsageDescription) are all required for identity-document workflows. Without them, the integration cannot fall back to documentary KYC and is unavailable in jurisdictions that require it.allowsLocation = false: Users will not see nearby retail locations automatically and will have to specify their location manually.
Important:You need to communicate your decision about camera access and geolocation to your Coinme business development team so that your ramp can be properly configured. Not doing so may result in poor UX for a subset of your customers.
Theme and Language
config.theme = .dark // .light (default) or .dark
config.language = "es" // BCP 47 tag; defaults to "en"Transaction Pre-fill
Lock fields to prevent user changes:
config.walletAddress = "bc1q..."
config.walletLock = true
config.sourceAmount = 100.0
config.sourceAmountLock = true
config.destinationCurrency = "BTC"
config.destinationCurrencyLock = trueSSO / Authentication Passthrough
Pass credentials at any time — they're queued until the session is ready:
coinmeVC.setAuth(token: "jwt-token", userId: "user-123", userEmail: "[email protected]")Metadata and Tracking
var meta = CoinmeMetadata()
meta.source = "ios-app"
meta.campaign = "summer-promo"
config.metadata = meta
config.externalSessionId = "session-abc"
config.externalTransactionId = "tx-456"Optional Delegate Methods
These are optional — default implementations are no-ops:
func coinmeSessionReady(_ vc: CoinmeViewController) {
// Bridge handshake complete, session is active
}
func coinmeSessionEnded(_ vc: CoinmeViewController) {
// Orchestrator signaled session end
vc.dismiss(animated: true)
}Troubleshooting
| Problem | Cause | Fix |
|---|---|---|
| Bridge never connects | Wrong Orchestrator URL or environment | Verify the URL matches your environment (dev, stage, prod) |
| Events not received | Delegate not set, or set after presentation | Set delegate before calling present(_:animated:) |
| Camera not working | Missing Info.plist keys | Add NSCameraUsageDescription and NSMicrophoneUsageDescription |
| Photo upload not working | Missing Info.plist key | Add NSPhotoLibraryUsageDescription for photo library access |
| Blank white screen | Network or URL error | Check didFailWithError for .webViewLoadFailed and inspect the underlying error |
| Screen capture blocked unexpectedly | allowsCapture defaults to false | Set config.allowsCapture = true if capture is needed |
Debugging with Safari Web Inspector
In DEBUG builds the SDK enables WebView inspection automatically (iOS 16.4+). To inspect:
- Open Safari on your Mac
- Go to Develop > [Your Device/Simulator] > [Your App]
- Use the console to see
[CoinmeBridge]log messages and Orchestrator JavaScript output
Bridge Protocol Reference
The SDK handles all protocol details internally. This section is for reference only.
Message Flow
┌─────────────┐ ┌──────────────┐
│ Native │ │ Orchestrator │
│ App │ │ (WebView) │
└──────┬──────┘ └──────┬───────┘
│ │
│ 1. Load Orchestrator URL │
│────────────────────────────────────────>│
│ │
│ 2. Handshake │
│<────────────────────────────────────────│
│ { type: "handshake" } │
│ │
│ 3. HandshakeAck │
│────────────────────────────────────────>│
│ { type: "handshakeAck" } │
│ │
│ 4. bridge-session-ready │
│<────────────────────────────────────────│
│ { type: "event" } │
│ │
│ 5. updateConfig │
│────────────────────────────────────────>│
│ │
│ 6. mount │
│────────────────────────────────────────>│
│ │
Key Differences from Web Integration
- Message transport: Native uses
window.postMessage()with targeted origins; web uses thepost-robotlibrary - Message handler: Native registers
WKScriptMessageHandleron the"coinmeBridge"channel - Embedding: Native loads the Orchestrator directly in a WKWebView, not in an iframe
Updated 18 days ago