How to run Web3.js in Swift 2 / iOS to work with Ethereum

Maksym
6 min readNov 11, 2015

--

tags: web3.js, ios, swift, xcode, geth, ethereum

Prelude: I was trying to find such article which explains how to connect iOS/Swift with web3.js, further to Ethereum node. But found nothing, so had to figure out solution on my own.

There’s no rocket science in it. Let me share with you how to build simple iOS app that executes transactions and pulls data from Ethereum.

Premise: you already have geth installed or you can connect to a public node and you have some basic skills on Swift/Xcode.

Let’s start by creating Single View Application

give it a name

and save in any folder.

I’ll use default Xcode’s setup for sake of example.

Initial ViewController.swift:

import UIKitclass ViewController: UIViewController
{
override func viewDidLoad()
{
super.viewDidLoad()
}
}

Lets create an app to set name of the car owner in ethereum contract.
Go ahead and add an input field for name and two buttons to storyboard. One button is to send the data and the other to retrieve it. And a text field to display current owner.

Next we need to assign Xcode outlets for text field, text view and actions for the buttons.

class ViewController: UIViewController
{
@IBOutlet weak var newOwnerName: UITextField!
@IBOutlet weak var requestResult: UITextView!

override func viewDidLoad()
{
super.viewDidLoad()
}

@IBAction func setOwner() {

}

@IBAction func getOwner() {

}
}

Now when we have interface, lets establish connection with Ethereum. First of all we need to create WebKit sandbox where web3.js’ code can be executed.

We have to inherit from WKNavigationDelegate protocol and import WebKit library.

import WebKitclass ViewController: UIViewController, WKNavigationDelegate
{
@IBOutlet weak var newOwnerName: UITextField!
@IBOutlet weak var requestResult: UITextView!

var webView: WKWebView?

override func viewDidLoad()
{
super.viewDidLoad()

// Creating actual WebView object. You can make it accessible
// globally and used by other view controllers / objects
webView = WKWebView()

// Adding subview to the current interface. It’s not visible.
view.addSubview(webView!)
// Load web-page that contains web3.js library
webView!.loadRequest(NSURLRequest(URL: NSURL(string:
"file:///www/eth/web3.html")!))
webView!.navigationDelegate = self
}

// Called when web-page is loaded
func webView(webView: WKWebView, didFinishNavigation navigation: WKNavigation!) {
print("Web.js is here!")
}

@IBAction func setOwner() {

}

@IBAction func getOwner() {

}
}

Also we’ll need web3.html page with web3.js library

Initial web3.html code:
<!doctype>
<html>
<head>
<script type="text/javascript" src="web3.js"></script>
<script type="text/javascript">
var web3 = new Web3();
web3.setProvider(
// Geth instance address
new web3.providers.HttpProvider("http://localhost:8110")
);
</script>
</head>
<body>
</body>
</html>

After launching the application you should see “Web.js is here!” in Xcode.

It’s time to have smart contract ready to work. Here’s source code:

contract Car
{
// Vehicle identifier, can be VIN-number, etc.
bytes public vehicleName = "Tesla";
// We’ll use public so no need to have getOwner method
bytes16 public owner;

// There’s no auth check for simplicity.
// For example we can allow change owner only to current owner
function setOwner(bytes16 newOwner)
{
owner = newOwner;
}
}

It’s assumed that you already deployed this contract on the geth-node/network that you’re using.

Next step is to add simple JS code to web3.html so we can communicate with the contract (you can do the same from swift if you like).

var abi = [
{constant: true, inputs: [], name: "owner", outputs: [{name: "", type: "bytes16"}], type: "function"},
{constant: true, inputs: [], name: "vehicleName", outputs: [{name: "", type: "bytes"}], type: "function"},
{constant: false, inputs: [{name: "newOwner", type: "bytes16"}], name: "setOwner", outputs: [], type: "function"}
];
var contractAddress = "0x497d405218018034f88c2b0a5e8cadfa3d47f9ec"
var Car = web3.eth.contract(abi).at(contractAddress);
function getNameAndOwner()
{
// Get hex values from the contract and convert them
// into ascii string
return {
name: web3.toAscii(Car.vehicleName()),
owner: web3.toAscii(Car.owner())
};
}

To make webkit object able to load from external sources project’s Info.plist file should be altered with this code in <dict> section:

<key>NSAppTransportSecurity</key> 
<dict>
<key>NSAllowsArbitraryLoads</key><true/>
</dict>

The last part is to get data from the interface and call web3.js directly.

@IBAction func setOwner() {
if let owner = newOwnerName.text
{
webView!.evaluateJavaScript("Car.setOwner(\"\(owner)\")",
completionHandler: {(res: AnyObject?, error: NSError?) in
print("Transaction: \(res!)");
}
)
}
}
@IBAction func getOwner()
{
webView!.evaluateJavaScript("getNameAndOwner()",
completionHandler: {(res: AnyObject?, error: NSError?) in
if let carData = res as? NSDictionary
{
self.requestResult.text = "The owner of the \"\(carData["name"]!)\" car is \"\(carData[“owner”]!)\""
}
})
}

From here you just need to run the Swift project, input any name and press “Set Owner” button. This will create transaction, you should see it’s hash in Xcode’s console log.

Then if you’re running your own node, just mine the transaction in geth:

miner.start(1); admin.sleepBlocks(1); miner.stop();

After transaction is mined click “Get current owner”.

Resulting ViewController.swift:

import UIKit
import WebKit
class ViewController: UIViewController, WKNavigationDelegate
{
@IBOutlet weak var newOwnerName: UITextField!
@IBOutlet weak var requestResult: UITextView!

var webView: WKWebView?

override func viewDidLoad()
{
super.viewDidLoad()

// Creating actual WebView object. You can make it accessible
// globally and used by other view controllers / objects
webView = WKWebView()

// Adding subview to the current interface. It’s not visible.
view.addSubview(webView!)
// Load web-page that contains web3.js library
webView!.loadRequest(NSURLRequest(URL: NSURL(string: "file:///www/eth/web3.html")!))
webView!.navigationDelegate = self
}

// Called when web-page is loaded
func webView(webView: WKWebView, didFinishNavigation navigation: WKNavigation!)
{
print("Web.js is here!")
webView.evaluateJavaScript("web3.isConnected()",
completionHandler: {(res: AnyObject?, error: NSError?) in
if let connected = res where connected as! NSInteger == 1
{
print(“Connected to ethereum node”)
}
else
{
print("Unable to connect ot the node. Check the setup.")
}
}
)
}


@IBAction func setOwner()
{
if let owner = newOwnerName.text
{
// Execute JS and get it’s result
webView!.evaluateJavaScript("Car.setOwner(\"\(owner)\")",
completionHandler: {(res: AnyObject?, error: NSError?) in
// Will show transaction hash
print("Transaction: \(res!)");
}
)
}
}
@IBAction func getOwner()
{
webView!.evaluateJavaScript("getNameAndOwner()",
completionHandler: {(res: AnyObject?, error: NSError?) in
if let carData = res as? NSDictionary
{
self.requestResult.text = "The owner of the \”\(carData["name"]!)\" car is \"\(carData[“owner”]!)\""
}
}
)
}
}

web3.html:

<!doctype>
<html>
<head>
<script type="text/javascript" src="web3.js"></script>
<script type="text/javascript">
var web3 = new Web3();
web3.setProvider(
// Geth instance addres
snew web3.providers.HttpProvider("http://localhost:8110")
);
// Car contract’s abi interface
var abi = [
{constant: true, inputs: [], name: “owner”, outputs: [{name: “”, type: “bytes16”}], type: “function”},
{constant: true, inputs: [], name: “vehicleName”, outputs: [{name: “”, type: “bytes”}], type: “function”},
{constant: false, inputs: [{name: “newOwner”, type: “bytes16”}], name: “setOwner”, outputs: [], type: “function”}
];
var contractAddress = “0x497d405218018034f88c2b0a5e8cadfa3d47f9ec”
var Car = web3.eth.contract(abi).at(contractAddress);
function getNameAndOwner()
{
// Get hex values from the contract and convert them into ascii string
return {
name: web3.toAscii(Car.vehicleName()),
owner: web3.toAscii(Car.owner())
};
}
</script>
</head>
<body>
</body>
</html>

If you’re interested on how to send callback from JS to Swift, for example when event occurred in a contract, let me know and I’ll describe it too.

--

--