Skip to main content

Documentation Index

Fetch the complete documentation index at: https://mintlify.com/frol/near-connect-ios/llms.txt

Use this file to discover all available pages before exploring further.

Overview

This guide walks you through building a minimal iOS app that connects to NEAR wallets. You’ll implement:
  • ✅ Wallet connection with the selector UI
  • ✅ Displaying the connected account
  • ✅ Disconnecting the wallet
  • ✅ Session persistence across app launches
Prerequisites: Make sure you’ve installed NEAR Connect iOS before starting this guide.

Step 1: Create the Wallet Manager

The NEARWalletManager is the core class that manages wallet state and operations. Create it once and share it throughout your app using SwiftUI’s environment.
import SwiftUI
import NEARConnect

@main
struct NEARConnectExampleApp: App {
    @StateObject private var walletManager = NEARWalletManager()

    var body: some Scene {
        WindowGroup {
            ContentView()
                .environmentObject(walletManager)
        }
    }
}
The wallet manager automatically loads previously connected accounts from UserDefaults on initialization. If a user was signed in when they closed the app, they’ll still be signed in when they open it again.

Step 2: Build the Connection UI

Create a view that displays the connection status and allows users to connect or disconnect:
ContentView.swift
import SwiftUI
import NEARConnect

struct ContentView: View {
    @EnvironmentObject var walletManager: NEARWalletManager

    var body: some View {
        VStack(spacing: 20) {
            Text("NEAR Connect Demo")
                .font(.largeTitle)
                .fontWeight(.bold)
            
            if walletManager.isSignedIn, let account = walletManager.currentAccount {
                // Connected state
                VStack(spacing: 12) {
                    Image(systemName: "checkmark.circle.fill")
                        .font(.system(size: 60))
                        .foregroundColor(.green)
                    
                    Text("Connected")
                        .font(.headline)
                    
                    Text(account.accountId)
                        .font(.title3)
                        .fontWeight(.semibold)
                    
                    Text("via \(account.walletId)")
                        .font(.caption)
                        .foregroundColor(.secondary)
                    
                    Button("Disconnect") {
                        walletManager.disconnect()
                    }
                    .buttonStyle(.borderedProminent)
                    .tint(.red)
                }
            } else {
                // Disconnected state
                VStack(spacing: 12) {
                    Image(systemName: "wallet.pass")
                        .font(.system(size: 60))
                        .foregroundColor(.blue)
                    
                    Text("Not Connected")
                        .font(.headline)
                        .foregroundColor(.secondary)
                    
                    Button("Connect Wallet") {
                        walletManager.connect()
                    }
                    .buttonStyle(.borderedProminent)
                }
            }
        }
        .padding()
        .fullScreenCover(isPresented: $walletManager.showWalletUI) {
            WalletBridgeSheet()
                .environmentObject(walletManager)
        }
    }
}

Key Points

  • @EnvironmentObject: Access the shared wallet manager
  • isSignedIn: Reactive boolean that updates when connection state changes
  • currentAccount: Contains accountId, publicKey, and walletId when connected
  • connect(): Triggers the wallet selector UI
  • disconnect(): Signs out and clears stored credentials
  • showWalletUI: Binding that controls when to present the wallet sheet
The WalletBridgeSheet is a pre-built component that handles the WebView presentation and all wallet interactions. You don’t need to build your own UI for wallet operations.

Step 3: Run Your App

Build and run your app on a simulator or device:
1

Build & Run

Press ⌘R or click the Play button in Xcode.
2

Connect a Wallet

Tap Connect Wallet. The wallet selector UI will appear with a list of available NEAR wallets.
3

Choose a Wallet

Select a wallet from the list. For testing:
  • MyNearWallet (web): Works in simulator and device
  • HOT Wallet (Telegram): Requires device with Telegram installed
4

Approve Connection

Follow the wallet’s authentication flow. For web wallets, you’ll see a browser-like interface. For native wallets, the app will deep link to the wallet app.
5

Connected!

Once approved, the UI automatically updates to show your connected account. The wallet sheet dismisses automatically.
Try force-quitting and relaunching the app. Your connection persists across sessions!

Step 4: Send Your First Transaction

Now that you can connect a wallet, let’s send some NEAR tokens:
TransactionView.swift
import SwiftUI
import NEARConnect

struct TransactionView: View {
    @EnvironmentObject var walletManager: NEARWalletManager
    @State private var recipientId = ""
    @State private var amount = "0.01"
    @State private var isProcessing = false
    @State private var showError = false
    @State private var errorMessage = ""
    
    var body: some View {
        Form {
            Section("Transfer Details") {
                TextField("Recipient (e.g., bob.near)", text: $recipientId)
                    .textInputAutocapitalization(.never)
                
                HStack {
                    TextField("Amount", text: $amount)
                        .keyboardType(.decimalPad)
                    Text("NEAR")
                        .foregroundColor(.secondary)
                }
            }
            
            Section {
                Button("Send NEAR") {
                    Task { await sendTransaction() }
                }
                .disabled(isProcessing || recipientId.isEmpty)
            }
        }
        .fullScreenCover(isPresented: $walletManager.showWalletUI) {
            WalletBridgeSheet()
                .environmentObject(walletManager)
        }
        .alert("Error", isPresented: $showError) {
            Button("OK", role: .cancel) {}
        } message: {
            Text(errorMessage)
        }
    }
    
    private func sendTransaction() async {
        guard let yoctoAmount = NEARWalletManager.toYoctoNEAR(amount) else {
            errorMessage = "Invalid amount"
            showError = true
            return
        }
        
        isProcessing = true
        defer { isProcessing = false }
        
        do {
            let result = try await walletManager.sendNEAR(
                to: recipientId,
                amountYocto: yoctoAmount
            )
            
            print("✅ Transaction successful!")
            print("Hash: \(result.transactionHashes.first ?? "")")
            
            // Clear form
            recipientId = ""
            amount = "0.01"
        } catch {
            errorMessage = error.localizedDescription
            showError = true
        }
    }
}

Understanding the Flow

1

User Enters Details

The form collects the recipient account ID and amount in NEAR.
2

Convert to YoctoNEAR

NEARWalletManager.toYoctoNEAR(_:) converts human-readable amounts (“1.5 NEAR”) to the protocol’s base unit (“1500000000000000000000000 yoctoNEAR”).
1 NEAR = 10^24 yoctoNEAR
3

Call sendNEAR

This async method:
  • Sets showWalletUI = true (opens the wallet sheet)
  • Passes the transaction to the wallet
  • Waits for user approval
  • Returns the transaction hash
4

Wallet Approval

The wallet displays transaction details and asks the user to approve or reject.
5

Result

  • Success: TransactionResult with transaction hashes
  • Failure: Throws NEARError with a descriptive message
  • Auto-dismiss: The wallet sheet closes automatically on completion
Always convert user-entered amounts to yoctoNEAR before sending. Sending “1” as the amount means 1 yoctoNEAR (nearly zero NEAR), not 1 NEAR.

Step 5: Call a Smart Contract

Interact with NEAR smart contracts using callFunction:
ContractView.swift
import SwiftUI
import NEARConnect

struct ContractView: View {
    @EnvironmentObject var walletManager: NEARWalletManager
    @State private var message = "Hello from iOS!"
    @State private var isProcessing = false
    
    var body: some View {
        VStack(spacing: 20) {
            Text("Guest Book Demo")
                .font(.title)
            
            TextField("Your message", text: $message)
                .textFieldStyle(.roundedBorder)
                .padding()
            
            Button("Add Message to Guest Book") {
                Task { await addMessage() }
            }
            .buttonStyle(.borderedProminent)
            .disabled(isProcessing)
        }
        .padding()
        .fullScreenCover(isPresented: $walletManager.showWalletUI) {
            WalletBridgeSheet()
                .environmentObject(walletManager)
        }
    }
    
    private func addMessage() async {
        isProcessing = true
        defer { isProcessing = false }
        
        do {
            let result = try await walletManager.callFunction(
                contractId: "guest-book.near",
                methodName: "add_message",
                args: ["text": message],
                deposit: "0"  // No NEAR attached
            )
            
            print("✅ Message added!")
            print("Hash: \(result.transactionHashes.first ?? "")")
            
            message = ""  // Clear input
        } catch {
            print("❌ Error: \(error.localizedDescription)")
        }
    }
}

Method Parameters

  • contractId: The account ID of the smart contract (e.g., "guest-book.near")
  • methodName: The contract method to call (e.g., "add_message")
  • args: A dictionary of arguments encoded as JSON
  • gas: Optional, defaults to "30000000000000" (30 TGas)
  • deposit: NEAR tokens to attach (in yoctoNEAR), defaults to "0"
The SDK automatically Base64-encodes your args dictionary before sending it to the contract, matching the NEAR protocol’s requirements.

Common Patterns

Check Connection Status Before Operations

guard walletManager.isSignedIn else {
    print("Please connect a wallet first")
    return
}

// Proceed with transaction...

Handle Busy State

Button("Send Transaction") {
    Task { await sendTransaction() }
}
.disabled(walletManager.isBusy)
The isBusy property is true whenever a wallet operation is in progress.

Query Account Balance

Task {
    do {
        let accountInfo = try await walletManager.viewAccount()
        if let amountYocto = accountInfo["amount"] as? String {
            let nearAmount = NEARWalletManager.formatNEAR(yoctoNEAR: amountYocto)
            print("Balance: \(nearAmount) NEAR")
        }
    } catch {
        print("Failed to fetch balance: \(error)")
    }
}

Switch Networks

// Set before connecting
walletManager.network = .testnet

// Or reconnect on network change
if walletManager.isSignedIn {
    walletManager.disconnect()
}
walletManager.network = .mainnet
walletManager.connect()
Changing networks while connected doesn’t automatically reconnect. Always disconnect first, change the network, then reconnect.

Error Handling

All async wallet operations throw NEARError:
do {
    let result = try await walletManager.sendNEAR(
        to: "recipient.near",
        amountYocto: "1000000000000000000000000"
    )
    print("Success: \(result.transactionHashes.first ?? "")")
} catch NEARError.notSignedIn {
    print("User needs to connect a wallet first")
} catch NEARError.operationInProgress {
    print("Another operation is already running")
} catch NEARError.walletError(let message) {
    print("Wallet error: \(message)")
} catch NEARError.webViewNotReady {
    print("Bridge is still initializing")
} catch {
    print("Unexpected error: \(error)")
}

Error Types

ErrorWhen It Occurs
.notSignedInUser hasn’t connected a wallet
.operationInProgressAnother wallet operation is already running
.walletError(String)The wallet rejected the operation or encountered an error
.webViewNotReadyThe JavaScript bridge hasn’t finished loading
.rpcError(String)NEAR RPC request failed (e.g., in viewAccount())

Complete Example App

The repository includes a full-featured example app demonstrating:
  • Wallet connection and session persistence
  • Token transfers with amount validation
  • Smart contract calls with JSON arguments
  • Message signing (NEP-413)
  • Delegate actions (NEP-366 meta transactions)
  • Account balance queries
  • Error handling and loading states
Clone and run it:
git clone https://github.com/frol/near-connect-ios.git
cd near-connect-ios
open Example/NEARConnectExample.xcodeproj
Build and run the NEARConnectExample target to see all features in action.

Next Steps

API Reference

Explore the complete NEARWalletManager API with detailed examples

Message Signing

Implement authentication and authorization with off-chain message signing

Advanced Transactions

Build complex transactions with multiple actions

Example Code

Browse production-ready code samples on GitHub

Build docs developers (and LLMs) love