BNB Smart Chain

BNB Smart Chain

Swift

For Apple-platform developers, web3.swift from Argent is the go-to library for reaching BNB Smart Chain from native code. It targets iOS 13 and later and macOS 10.15 and later, which lines up with the OS versions that ship modern Swift concurrency, and it lets an app read BNB balances or interact with BEP-20 tokens on chain 56 without leaving Swift. Add it through Swift Package Manager by pointing Xcode at https://github.com/argentlabs/web3.swift.

The platform floor is worth keeping in mind when you set your deployment target: iOS 13+ and macOS 10.15+ are required. Installation is handled entirely by Swift Package Manager — add the argentlabs/web3.swift repository as a package dependency and Xcode resolves the rest.

Web3.swift

A single initializer spins up a client: Web3(rpcURL: "https://bsc.therpc.io/YOUR_API_KEY") gives you an object wired to BNB Smart Chain. Every network operation on it is marked async throws. You call each one with await plus try from within a Task or another async context. A balance read on chain 56 suspends rather than blocks, and any RPC failure surfaces as a thrown Swift error you handle with do/catch.

import Web3
import BigInt
class EthereumClient {
private let web3 = Web3(rpcURL: "https://bsc.therpc.io/YOUR_API_KEY")
func getBalance(address: String) async throws -> Double {
let address = try EthereumAddress(hex: address, eip55: true)
let balance = try await web3.eth.getBalance(address: address)
return balance.converted(to: .ether).value
}
func sendTransaction(
from: EthereumPrivateKey,
to: String,
amount: Double
) async throws -> String {
let toAddress = try EthereumAddress(hex: to, eip55: true)
let amount = EthereumAmount(value: amount, unit: .ether)
let transaction = try await web3.eth.prepareTransaction(
to: toAddress,
value: amount,
from: from.address
)
let signed = try transaction.sign(with: from)
return try await web3.eth.send(transaction: signed)
}
}
  • GitHub: https://github.com/argentlabs/web3.swift
  • A purely native Swift implementation with no bridging overhead
  • First-class iOS and macOS support sharing one codebase
  • Modern async/await APIs for every chain-56 call
  • Type-safe contract interactions plus wallet and key management
  • Built-in ENS resolution for human-readable addresses

SwiftUI Integration

SwiftUI makes a BNB balance feel reactive with very little plumbing. Model your view state as an ObservableObject whose @Published properties hold the balance and loading flag, then expose it to the view as a @StateObject so the UI redraws whenever chain-56 data arrives. Kick off the actual fetch from a Button action or, for load-on-appear behaviour, the .task modifier — both let you spawn a Task { } and await the web3.swift call without freezing the interface.

import SwiftUI
import Web3
struct WalletView: View {
@StateObject private var viewModel = WalletViewModel()
var body: some View {
VStack {
Text("Balance: \(viewModel.balance) BNB")
Button("Refresh") {
Task {
await viewModel.updateBalance()
}
}
}
}
}
class WalletViewModel: ObservableObject {
private let client = EthereumClient()
@Published var balance: Double = 0
func updateBalance() async {
do {
balance = try await client.getBalance(address: "YOUR_ADDRESS")
} catch {
print("Error: \(error)")
}
}
}

Smart Contract Integration

To talk to a deployed contract such as a BEP-20 token, load its ABI as JSON. Then build a contract handle through web3.eth.Contract, supplying the on-chain address. From that handle web3.swift lets you invoke the contract's functions as Swift calls. It encodes parameters and decodes return values for you, so reading a token balance or symbol on chain 56 stays type-safe.

struct Contract {
let web3 = Web3(rpcURL: "https://bsc.therpc.io/YOUR_API_KEY")
let contractAddress: EthereumAddress
func callMethod() async throws -> String {
let contract = try await web3.eth.Contract(
json: contractABI,
address: contractAddress
)
return try await contract.method(
"methodName",
parameters: [param1, param2],
extraData: Data()
).call()
}
}

Error Handling

Swift's error model rewards being explicit, so define a typed EthereumError enum that distinguishes a malformed address from an underlying network failure on BNB Smart Chain. Split into separate cases, those let the UI show a "check the address" message in one branch and a "retry, the network is unreachable" message in another. As a cheap first line of defence, validate the address format locally before you ever dispatch a request to chain 56. A typo caught client-side saves a wasted round trip.

enum EthereumError: Error {
case invalidAddress
case insufficientFunds
case networkError(String)
}
extension EthereumClient {
func safeGetBalance(address: String) async throws -> Double {
guard address.hasPrefix("0x") else {
throw EthereumError.invalidAddress
}
do {
return try await getBalance(address: address)
} catch {
throw EthereumError.networkError(error.localizedDescription)
}
}
}

Ready to call this in production?

Free tier covers personal projects. Pay-as-you-go scales without a card.