Code Examples iOS SDK
Ready-to-use iOS code examples for common scenarios.
PaymentMethodSelected Helper Class
PaymentMethodSelected is a protocol in the Yuno SDK. To use it with startPaymentLite() and other methods, create a simple conforming class:
// Add this helper class to your project
class SelectedPaymentMethod: PaymentMethodSelected {
let paymentMethodType: String
let vaultedToken: String?
init(paymentMethodType: String, vaultedToken: String? = nil) {
self.paymentMethodType = paymentMethodType
self.vaultedToken = vaultedToken
}
}This class is used throughout the examples below.
Example 1: Basic UIKit Payment
import UIKit
import YunoSDK
class PaymentViewController: UIViewController, YunoPaymentFullDelegate {
private var _checkoutSession: String = ""
private var paymentMethodsView: UIView?
// YunoPaymentFullDelegate required properties
var checkoutSession: String { _checkoutSession }
var countryCode: String { "US" }
var language: String? { "en" }
var viewController: UIViewController? { self }
@IBOutlet weak var payButton: UIButton!
override func viewDidLoad() {
super.viewDidLoad()
Task {
// 1. Create session on backend
let session = try await createSession(amount: 2500)
_checkoutSession = session.id
// 2. Get payment methods view from SDK
paymentMethodsView = await Yuno.getPaymentMethodViewAsync(delegate: self)
// 3. Add to view hierarchy
if let methodsView = paymentMethodsView {
methodsView.translatesAutoresizingMaskIntoConstraints = false
view.addSubview(methodsView)
// Add constraints...
}
}
}
@IBAction func payButtonTapped() {
Yuno.startPayment()
}
func createSession(amount: Int) async throws -> CheckoutSession {
var request = URLRequest(url: URL(string: "https://api.example.com/checkout")!)
request.httpMethod = "POST"
request.setValue("application/json", forHTTPHeaderField: "Content-Type")
request.httpBody = try JSONSerialization.data(withJSONObject: [
"amount": ["currency": "USD", "value": amount],
"customer_id": "cus_123"
])
let (data, _) = try await URLSession.shared.data(for: request)
return try JSONDecoder().decode(CheckoutSession.self, from: data)
}
// YunoPaymentFullDelegate methods
func yunoCreatePayment(with token: String, information: [String: Any]) {
Task {
await createPayment(token: token)
Yuno.continuePayment()
}
}
func yunoPaymentResult(_ result: Yuno.Result) {
switch result.status {
case .succeeded:
performSegue(withIdentifier: "showSuccess", sender: nil)
case .fail, .reject, .internalError:
showAlert(title: "Error", message: "Payment failed")
case .processing:
showAlert(title: "Processing", message: "Payment is being processed")
case .userCancelled:
break
}
}
func yunoDidSelect(paymentMethod: PaymentMethodSelected) {
print("Selected:", paymentMethod)
}
func yunoUpdatePaymentMethodsViewHeight(_ height: CGFloat) {
// Update constraints if needed
}
func yunoDidUnenrollSuccessfully(_ success: Bool) {
print("Unenroll success:", success)
}
}Example 2: SwiftUI Payment
import SwiftUI
import YunoSDK
struct CheckoutView: View {
@StateObject private var viewModel = CheckoutViewModel()
@State private var showSuccess = false
@State private var errorMessage: String?
var body: some View {
VStack {
Text("$25.00")
.font(.largeTitle)
Button("Pay Now") {
Task { await viewModel.startPayment() }
}
.buttonStyle(.borderedProminent)
}
.task {
await viewModel.initialize()
}
.alert("Success!", isPresented: $showSuccess) {
Button("OK") { }
}
.alert("Error", isPresented: .constant(errorMessage != nil)) {
Button("OK") { errorMessage = nil }
} message: {
Text(errorMessage ?? "")
}
.onReceive(viewModel.$paymentStatus) { status in
switch status {
case .succeeded:
showSuccess = true
case .failed(let message):
errorMessage = message
default:
break
}
}
}
}
@MainActor
class CheckoutViewModel: ObservableObject, YunoPaymentFullDelegate {
enum PaymentStatus {
case idle, processing, succeeded, failed(String)
}
@Published var paymentStatus: PaymentStatus = .idle
private var _checkoutSession: String = ""
private var paymentMethodsView: UIView?
var checkoutSession: String { _checkoutSession }
var countryCode: String { "US" }
var language: String? { "en" }
var viewController: UIViewController? { nil }
func initialize() async {
let session = try? await createCheckoutSession()
_checkoutSession = session?.id ?? ""
paymentMethodsView = await Yuno.getPaymentMethodViewAsync(delegate: self)
}
func startPayment() async {
paymentStatus = .processing
Yuno.startPayment()
}
func yunoCreatePayment(with token: String, information: [String: Any]) {
Task {
await createPayment(token: token)
Yuno.continuePayment()
}
}
func yunoPaymentResult(_ result: Yuno.Result) {
switch result.status {
case .succeeded:
paymentStatus = .succeeded
case .fail, .reject, .internalError:
paymentStatus = .failed("Payment failed")
case .processing:
paymentStatus = .processing
case .userCancelled:
paymentStatus = .idle
}
}
func yunoDidSelect(paymentMethod: PaymentMethodSelected) { }
func yunoUpdatePaymentMethodsViewHeight(_ height: CGFloat) { }
func yunoDidUnenrollSuccessfully(_ success: Bool) { }
}Example 3: Enrollment (Save Card)
import UIKit
import YunoSDK
class EnrollmentViewController: UIViewController, YunoEnrollmentDelegate {
private var _customerSession: String = ""
// YunoEnrollmentDelegate required properties
var customerSession: String { _customerSession }
var countryCode: String { "US" }
var language: String? { "en" }
var viewController: UIViewController? { self }
func enrollCard() {
Task {
// Create customer session on backend
let session = try await createCustomerSession(customerId: "cus_123")
_customerSession = session.id
// Start enrollment
Yuno.enrollPayment(with: self, showPaymentStatus: true)
}
}
func yunoEnrollmentResult(_ result: Yuno.Result) {
switch result.status {
case .succeeded:
print("Card enrolled successfully")
// Fetch vaulted tokens from your backend
fetchSavedCards()
case .fail, .reject, .internalError:
showAlert("Enrollment failed")
case .processing:
showAlert("Enrollment processing")
case .userCancelled:
break
}
}
func fetchSavedCards() {
Task {
let cards = try await apiClient.get("/customer/cards")
// Update UI with saved cards
}
}
}Example 4: One-Click Payment with Saved Card
import UIKit
import YunoSDK
class FastCheckoutViewController: UIViewController, YunoPaymentDelegate {
private var _checkoutSession: String = ""
var savedCards: [SavedCard] = []
// YunoPaymentDelegate required properties
var checkoutSession: String { _checkoutSession }
var countryCode: String { "US" }
var language: String? { "en" }
var viewController: UIViewController? { self }
@IBOutlet weak var tableView: UITableView!
override func viewDidLoad() {
super.viewDidLoad()
loadSavedCards()
}
func loadSavedCards() {
Task {
savedCards = try await apiClient.get("/customer/cards")
tableView.reloadData()
}
}
func payWithSavedCard(_ card: SavedCard) {
Task {
// Create checkout session
let session = try await createCheckoutSession()
_checkoutSession = session.id
// Start payment with vaulted token
let paymentSelected = SelectedPaymentMethod(
paymentMethodType: "CARD",
vaultedToken: card.vaultedToken
)
Yuno.startPaymentLite(
with: self,
paymentSelected: paymentSelected,
showPaymentStatus: true
)
}
}
func yunoCreatePayment(with token: String) {
Task {
await createPayment(token: token)
Yuno.continuePayment()
}
}
func yunoPaymentResult(_ result: Yuno.Result) {
switch result.status {
case .succeeded:
navigateToOrderConfirmation()
case .fail, .reject, .internalError:
showAlert("Payment failed")
case .processing:
showAlert("Payment processing")
case .userCancelled:
break
}
}
}Example 5: Custom Payment Method Selection
import UIKit
import YunoSDK
class CustomSelectionViewController: UIViewController, YunoPaymentDelegate {
private var _checkoutSession: String = ""
var checkoutSession: String { _checkoutSession }
var countryCode: String { "US" }
var language: String? { "en" }
var viewController: UIViewController? { self }
@IBOutlet weak var tableView: UITableView!
var paymentMethods: [PaymentMethod] = []
func loadPaymentMethods() async {
do {
let session = try await createCheckoutSession()
_checkoutSession = session.id
paymentMethods = try await fetchPaymentMethods(session: session.id)
tableView.reloadData()
} catch {
showError(error)
}
}
func fetchPaymentMethods(session: String) async throws -> [PaymentMethod] {
let url = URL(string: "https://api.y.uno/v1/checkout/sessions/\(session)/payment-methods")!
var request = URLRequest(url: url)
request.setValue("pk_test_your_key", forHTTPHeaderField: "X-Yuno-Api-Key")
let (data, _) = try await URLSession.shared.data(for: request)
let response = try JSONDecoder().decode(PaymentMethodsResponse.self, from: data)
return response.paymentMethods
}
func didSelectPaymentMethod(_ method: PaymentMethod) {
let paymentSelected = SelectedPaymentMethod(
paymentMethodType: method.type,
vaultedToken: nil
)
Yuno.startPaymentLite(
with: self,
paymentSelected: paymentSelected,
showPaymentStatus: true
)
}
func yunoCreatePayment(with token: String) {
Task {
await createPayment(token: token)
Yuno.continuePayment()
}
}
func yunoPaymentResult(_ result: Yuno.Result) {
switch result.status {
case .succeeded:
navigateToSuccess()
default:
showError("Payment failed")
}
}
}Example 6: Render Mode Integration
import UIKit
import YunoSDK
class RenderModeViewController: UIViewController, YunoPaymentDelegate {
private var _checkoutSession: String = ""
private var paymentFlow: YunoPaymentRenderFlowProtocol?
var checkoutSession: String { _checkoutSession }
var countryCode: String { "US" }
var language: String? { "en" }
var viewController: UIViewController? { self }
@IBOutlet weak var paymentContainerView: UIView!
@IBOutlet weak var submitButton: UIButton!
func startPayment() {
Task {
let session = try await createCheckoutSession()
_checkoutSession = session.id
let paymentSelected = SelectedPaymentMethod(
paymentMethodType: "CARD",
vaultedToken: nil
)
// Create payment flow instance
paymentFlow = await Yuno.startPaymentRenderFlow(
paymentMethodSelected: paymentSelected,
with: self
)
// Get and display the form
if let formView = await paymentFlow?.formView(
paymentMethodSelected: paymentSelected,
with: self
) {
formView.translatesAutoresizingMaskIntoConstraints = false
paymentContainerView.addSubview(formView)
NSLayoutConstraint.activate([
formView.topAnchor.constraint(equalTo: paymentContainerView.topAnchor),
formView.leadingAnchor.constraint(equalTo: paymentContainerView.leadingAnchor),
formView.trailingAnchor.constraint(equalTo: paymentContainerView.trailingAnchor),
formView.bottomAnchor.constraint(equalTo: paymentContainerView.bottomAnchor)
])
}
}
}
@IBAction func submitButtonTapped() {
paymentFlow?.submitForm()
}
func yunoCreatePayment(with token: String, information: [String: Any]) {
Task {
await createPayment(token: token)
// Display additional views (e.g., 3DS) if needed
let additionalView = await paymentFlow?.continuePayment()
if let additionalView = additionalView {
// Display 3DS or additional verification views
}
}
}
func yunoPaymentResult(_ result: Yuno.Result) {
switch result.status {
case .succeeded:
navigateToSuccess()
default:
showError("Payment failed")
}
}
}Example 7: Error Handling with Retry
import UIKit
import YunoSDK
class PaymentWithRetryViewController: UIViewController, YunoPaymentDelegate {
private var _checkoutSession: String = ""
var retryCount = 0
let maxRetries = 3
var checkoutSession: String { _checkoutSession }
var countryCode: String { "US" }
var language: String? { "en" }
var viewController: UIViewController? { self }
func attemptPayment() {
Task {
do {
let session = try await createCheckoutSession()
_checkoutSession = session.id
let paymentSelected = SelectedPaymentMethod(
paymentMethodType: "CARD",
vaultedToken: nil
)
Yuno.startPaymentLite(
with: self,
paymentSelected: paymentSelected,
showPaymentStatus: true
)
} catch {
await handlePaymentError(error)
}
}
}
func handlePaymentError(_ error: Error) async {
if retryCount < maxRetries {
retryCount += 1
try? await Task.sleep(nanoseconds: 2_000_000_000) // 2s delay
attemptPayment()
} else {
await MainActor.run {
showAlert("Payment failed after \(maxRetries) attempts")
}
}
}
func yunoCreatePayment(with token: String) {
Task {
await createPayment(token: token)
Yuno.continuePayment()
}
}
func yunoPaymentResult(_ result: Yuno.Result) {
switch result.status {
case .succeeded:
retryCount = 0
navigateToSuccess()
case .fail, .internalError:
Task { await handlePaymentError(NSError(domain: "Payment", code: -1)) }
case .reject:
showAlert("Payment was rejected")
case .processing:
showAlert("Payment is processing")
case .userCancelled:
break
}
}
}Example 8: Analytics Integration
import UIKit
import YunoSDK
import FirebaseAnalytics
class PaymentWithAnalyticsViewController: UIViewController, YunoPaymentFullDelegate {
private var _checkoutSession: String = ""
private var paymentMethodsView: UIView?
var checkoutSession: String { _checkoutSession }
var countryCode: String { "US" }
var language: String? { "en" }
var viewController: UIViewController? { self }
func startPayment() {
Analytics.logEvent("checkout_started", parameters: [
"value": 25.00,
"currency": "USD"
])
Task {
let session = try await createCheckoutSession()
_checkoutSession = session.id
paymentMethodsView = await Yuno.getPaymentMethodViewAsync(delegate: self)
// Add to view hierarchy...
Yuno.startPayment()
}
}
func yunoCreatePayment(with token: String, information: [String: Any]) {
Task {
await createPayment(token: token)
Yuno.continuePayment()
}
}
func yunoPaymentResult(_ result: Yuno.Result) {
switch result.status {
case .succeeded:
Analytics.logEvent(AnalyticsEventPurchase, parameters: [
"value": 25.00,
"currency": "USD"
])
navigateToSuccess()
case .fail, .reject, .internalError:
Analytics.logEvent("payment_failed", parameters: [
"status": String(describing: result.status)
])
showError("Payment failed")
case .processing:
Analytics.logEvent("payment_processing", parameters: nil)
case .userCancelled:
Analytics.logEvent("payment_cancelled", parameters: nil)
}
}
func yunoDidSelect(paymentMethod: PaymentMethodSelected) {
Analytics.logEvent("payment_method_selected", parameters: [
"method": paymentMethod.paymentMethodType
])
}
func yunoUpdatePaymentMethodsViewHeight(_ height: CGFloat) { }
func yunoDidUnenrollSuccessfully(_ success: Bool) { }
}Example 9: Custom Styling
import UIKit
import YunoSDK
class StyledPaymentViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
configureYunoAppearance()
}
func configureYunoAppearance() {
let appearance = Yuno.Appearance(
fontFamily: "SF Pro Display",
accentColor: UIColor(red: 0.2, green: 0.4, blue: 0.8, alpha: 1.0),
buttonBackgroundColor: UIColor(red: 0.2, green: 0.4, blue: 0.8, alpha: 1.0),
buttonTitleColor: .white,
buttonBorderColor: UIColor(red: 0.2, green: 0.4, blue: 0.8, alpha: 1.0),
secondaryButtonBackgroundColor: .systemGray6,
secondaryButtonTitleColor: .label,
secondaryButtonBorderColor: .systemGray4,
disableButtonBackgroundColor: .systemGray4,
disableButtonTitleColor: .systemGray,
checkboxColor: UIColor(red: 0.2, green: 0.4, blue: 0.8, alpha: 1.0)
)
Yuno.initialize(
apiKey: "PUBLIC_API_KEY",
config: YunoConfig(appearance: appearance),
callback: {
print("SDK initialized with custom appearance")
}
)
}
}Example 10: Multi-Platform ViewModel
import Combine
import YunoSDK
@MainActor
class PaymentViewModel: ObservableObject, YunoPaymentFullDelegate {
@Published var paymentState: PaymentState = .idle
@Published var errorMessage: String?
private var _checkoutSession: String = ""
private var paymentMethodsView: UIView?
var checkoutSession: String { _checkoutSession }
var countryCode: String { "US" }
var language: String? { "en" }
var viewController: UIViewController? { nil }
enum PaymentState {
case idle, processing, succeeded, failed
}
func processPayment(amount: Int) async {
paymentState = .processing
do {
let session = try await createCheckoutSession(amount: amount)
_checkoutSession = session.id
paymentMethodsView = await Yuno.getPaymentMethodViewAsync(delegate: self)
Yuno.startPayment()
} catch {
paymentState = .failed
errorMessage = error.localizedDescription
}
}
func yunoCreatePayment(with token: String, information: [String: Any]) {
Task {
await createPayment(token: token)
Yuno.continuePayment()
}
}
func yunoPaymentResult(_ result: Yuno.Result) {
switch result.status {
case .succeeded:
paymentState = .succeeded
case .fail, .reject, .internalError:
paymentState = .failed
errorMessage = "Payment failed"
case .processing:
paymentState = .processing
case .userCancelled:
paymentState = .idle
}
}
func yunoDidSelect(paymentMethod: PaymentMethodSelected) { }
func yunoUpdatePaymentMethodsViewHeight(_ height: CGFloat) { }
func yunoDidUnenrollSuccessfully(_ success: Bool) { }
}
// Usage in SwiftUI
struct PaymentView: View {
@StateObject private var viewModel = PaymentViewModel()
var body: some View {
VStack {
switch viewModel.paymentState {
case .idle:
Button("Pay $25") {
Task { await viewModel.processPayment(amount: 2500) }
}
case .processing:
ProgressView()
case .succeeded:
Text("Payment successful!")
case .failed:
Text("Error: \(viewModel.errorMessage ?? "")")
}
}
}
}Backend API Helper
class YunoAPIClient {
static let shared = YunoAPIClient()
func createCheckoutSession(amount: Money, customerId: String) async throws -> CheckoutSession {
var request = URLRequest(url: URL(string: "https://api.example.com/checkout")!)
request.httpMethod = "POST"
request.setValue("application/json", forHTTPHeaderField: "Content-Type")
let body: [String: Any] = [
"amount": ["currency": amount.currency, "value": amount.value],
"customer_id": customerId,
"country": "US"
]
request.httpBody = try JSONSerialization.data(withJSONObject: body)
let (data, _) = try await URLSession.shared.data(for: request)
return try JSONDecoder().decode(CheckoutSession.self, from: data)
}
func createCustomerSession(customerId: String) async throws -> CustomerSession {
var request = URLRequest(url: URL(string: "https://api.example.com/customer/session")!)
request.httpMethod = "POST"
request.setValue("application/json", forHTTPHeaderField: "Content-Type")
request.httpBody = try JSONSerialization.data(withJSONObject: [
"customer_id": customerId
])
let (data, _) = try await URLSession.shared.data(for: request)
return try JSONDecoder().decode(CustomerSession.self, from: data)
}
func createPayment(token: String, checkoutSession: String) async throws {
var request = URLRequest(url: URL(string: "https://api.example.com/payments")!)
request.httpMethod = "POST"
request.setValue("application/json", forHTTPHeaderField: "Content-Type")
request.httpBody = try JSONSerialization.data(withJSONObject: [
"payment_method": ["token": token],
"checkout_session": checkoutSession
])
let (_, response) = try await URLSession.shared.data(for: request)
guard let httpResponse = response as? HTTPURLResponse,
httpResponse.statusCode == 200 || httpResponse.statusCode == 201 else {
throw NSError(domain: "Payment", code: -1, userInfo: [NSLocalizedDescriptionKey: "Payment creation failed"])
}
}
}Updated about 21 hours ago