Tagged

Building Ethereum Dapps on iOS with web3.swift

A new, open source library for Dapps

Matt Marshall
Sep 6, 2018

At Argent we’re committed to helping the Ethereum community flourish. We’re excited to share how we’ve built our own, open-source, Swift library to interact with the Ethereum blockchain.

Web3.swift is designed to facilitate easy access to the Ethereum blockchain via a Swift native library, primarily for the creation of decentralized iOS apps.

It’s available on Github here. We’d love feedback and ideas on how to improve this library. We welcome all contributions and pull requests.

web3.swift logo

Why web3.swift?

We faced a problem when we started building Argent 12 months ago: there were no web3 libraries available in Swift (in fact, the closest solution came to running web3.js via WebKit). This mattered as the Argent mobile wallet is Ethereum-based and we interact directly with nodes using the standardized Ethereum JSON-RPC interface.

The solution? We built web3.swift. We’re delighted to share it with the wider community.

It builds on our active use of, and contributions to, the web3.js (Javascript) and web3j (Java) libraries. We’re seeking to learn from them what features and approaches work best on each platform.

Writing software in different languages often entails different approaches to data management and application logic, and as such we’ve structured web3.swift to make building decentralized apps (Dapps) on mobile as familiar as possible.

Main features

Our web3.swift library provides functionality for core interactions with the Ethereum blockchain. You can create accounts, call functions and perform transactions against smart contracts.

The primary benefit, however, is our dual support for working with contract code (ABI’s) via JSON or statically typed Swift structs. You may wish to use either route depending on the complexity of your product and team coding style.

We’ve made security a paramount concern with this library, especially given the trust users will place in it when handling private keys. For this reason, we’re using only minimal dependencies (BigInt) and well-proven cryptographic C libraries bundled with web3.swift.

We’ve also included support for working with ERC20 tokens (view the source code for an example of developing statically typed models) and Ethereum Name Service (via dynamic JSON).

Working with ABI contracts

To illustrate how to work with web3.swift, here’s a real-world example on the Ropsten test network.

Let’s use this HelloWorld Kittens smart contract ABI example, which stores a list of kittens and their corresponding names. We can look up how many kittens have a certain name (howManyKittensCalled), or create a new kitten with a name (createKitten). The interface is described as:

howManyKittiesCalled (_name: Str) -> IntcreateKitten -> (_name: Str)

Firstly, we install via CocoaPods and import web3 to gain access to the main classes and functions. We create an instance of EthereumClient to connect to the blockchain, using any node which supports the JSON-RPC proxy. For testing we usually use Infura:

let client = EthereumClient(url: URL(string: “https://ropsten.infura.io/<YOUR API KEY>")!)

Please ignore the use of explicit bangs (!) and lack of error handling. Whilst bad practice it makes providing example code easier!

Now, let’s set up the contract code. The above ABI example we’ve saved as Contract.json, and can use to create an instance of EthereumJSONContract:

let contractAddress = EthereumAddress(“0x93919cf5981de7c24a303e07f56f4eefa57454bb”)

let abiUrl = Bundle.main.url(forResource: “Contract”, withExtension: “json”)!

let contract = EthereumJSONContract(url: abiUrl, address: contractAddress)!

Here, we’re referencing the deployed contract on the Ropsten test network.

After this, we can call the ABI methods easily to look up data from the blockchain. To see how many kittens are called “fred”, we can do the following:

let transaction = try! contract.transaction(function: “howManyKittensCalled”, args: [“fred”])

client.eth_call(transaction) { (error, data) in
   let numberOfKittens = try! ABIDecoder.decode(data!, to: UInt64.self)
   print(“There’s \(numberOfKittens) kitten(s) called fred”)
}

We should find that the number of kittens returned is > 1 at the time of writing this. Note the use of ABIDecoder to decode the single value response to UInt64, as found in the original ABI.

Statically typed structs

If we’re building a more complex app, we may wish to create Swift structs to represent the ABI methods. This allows us to work with strictly typed data structures to create a native interface to our smart contract.

Here are example structs for the same function request & response:

struct HowManyKittens: ABIFunction {
   static let name = “howManyKittens”
   let gasPrice = BigUInt(0)
   let gasLimit = BigUInt(0)
   let contract: EthereumAddress
   let from: EthereumAddress?

   let kittenName: String   func encode(to encoder: ABIFunctionEncoder) throws {
      try encoder.encode(kittenName)
   }
}

struct HowManyKittensResponse: ABIResponse {
   static var types: [ABIType.Type] = [ UInt64.self ]

   let numberOfKittens: UInt64

   init?(values: [String]) throws {
      numberOfKittens = try ABIDecoder.decode(values[0], to: UInt64.self)
   }
}

As shown, we have modeled our function into two separate structs conforming to ABIFunction and ABIResponse. The serialization and deserialization of data will look familiar to anyone who has used the native Codable protocol.

To get the above code to work, you will have to import BigInt, which is a dependency of web3.swift. BigInt is a third-party library that provides support for 256-bit integers, which is not a foundation type in Swift.

We can now execute the function like so:

let function = HowManyKittens(contract: contractAddress, from: nil, kittenName: “fred”)

function.call(withClient: client, responseType: HowManyKittensResponse.self) { (error, response) in
   print(“There’s \(response!.numberOfKittens) kitten(s) called fred”)
}

The advantages of this approach are that all encoding and decoding are hidden in the library, so you can build up a repository of function calls to match your ABI in one place. The structs conforming to ABIFunction and ABIResponse are thus very easy to follow.

Manipulating the Blockchain

What about manipulating data on the blockchain? We now want to create a new kitten, but to do so we’ll need an Ethereum account (public/private key-pair) and load it with some Ether to pay the transaction (gas) cost.

This can be done via the above approach, setting gas price and gas limit, and calling function.execute with an EthereumAccount object. I’ll provide a full example in a follow-up post.

What’s next

There’s a couple of missing features we’ll be looking to add as we build out this library, such as batch transaction support & bloom filters for searching logs. We’re also looking to automatically generate Swift structs for a given ABI JSON interface via Sourcery. We hope the community will contribute to making this the main library for building iOS apps on Ethereum.

We’re working hard at Argent to create the very best products. We see web3.swift as one of our core components. It’s still in its early days but we hope it has a bright future ahead.

Contribute to Web3.swift on Github

Ready to get started with DeFi?

Argent is a simple, secure, all in one wallet for investing in DeFi

Download Argent

Related Blogs

Using smart contracts for smart hires

Using smart contracts for smart hires

In Emojis We Trust

How Argent uses Emojis to secure your wallet

Argent: The most secure wallet

Enjoy peace of mind with multisig security and no seed phrase

Own It

We use 🍪 cookies to personalise your experience on Argent. Privacy Policy

Accept

HQ London, made with ❤️ across Europe