iOS アプリからイーサリアムにつないでテストするまでの簡単な手順をまとめてみました。
イーサリアムクライアントとして、web3swift(matter-labs) を利用しています。
「ややこしい話は抜きだ!」という方は、GitHub に手順後のプロジェクトが置いてあるのでお使いくださいませ。
では、ちゃっちゃといきましょう。
プロジェクトの作成
Xcode を起動して Create a new Xcode project から新規プロジェクトを作成します。
このアプリは画面タップでテストを実行させたいので、タップ処理のひな型のある Game を選択。
プロダクト名は StudyWeb3Swift、チームと組織名&IDはご自身の開発者登録内容にて設定してください。
プロジェクトフォルダの出力先をお好みで設定して Create。
web3swift の導入
ライブラリの導入は CocoaPods にて行います。
CocoaPods はプロジェクトフォルダ内にファイルを出力するので、Xcode を終了して StudyWeb3Swift プロジェクトを閉じておきます。
まず、StudyWeb3Swift プロジェクトフォルダの直下(StudyWeb3Swift.xcodeproj と同じ階層)に、インストールするライブラリを指定する Podfile を、下記の内容で作成します(※ここでは、iOS のバージョンを 9.0 にしていますが、この値はプロジェクトのデプロイ先のバージョン指定とともに、ご自身の環境に応じて調整してください)。
source 'https://github.com/CocoaPods/Specs.git'
platform :ios, '9.0'
target 'StudyWeb3Swift' do
use_frameworks!
pod 'web3swift'
end
ターミナルを開いて、下記コマンドにて CocoaPods をインストールします(※すでに、CocoaPods をインストール済みの方は、この手順はスキップできます)。
$ sudo gem install cocoapods
つづいて、ターミナル上で、StudyWeb3Swift プロジェクトフォルダへ移動し、下記コマンドにて、Podfile で指定した web3swift ライブラリをインストールします。
インストールが正常に終了すると、StudyWeb3Swift フォルダ直下に、Podfile.lock、Pods/、StudyWeb3Swift.xcworkspace が生成されます。
生成された StudyWeb3Swift.xcworkspace が StudyWeb3Swift.xcodeproj と Pods/ 内のライブラリを協調させてくれる環境となり、以降の作業は、このワークスペース上で行うことになります。
では、StudyWeb3Swift.xcworkspace を Xcode で開いて、Pods 内の様子を確認し、web3swift が導入されていることをチェックしましょう(※間違えて StudyWeb3Swift.xcodeproj を開いてしまわないようご注意ください)。
テストコードの組み込み
下記の3ファイルをソースフォルダ StudyWeb3Swift/StudyWeb3Swift にコピーします。
・ContractHelloWorld.swift
・TestWeb3.swift
・Web3Helper.swift
つづいて、コピーしたソースコードを、StudyWeb3Swift プロジェクトにインポートします。StudyWeb3Swift → StudyWeb3Swift を右クリックして Add Files to"StudyWeb3Swift"... を選択し...
先ほどコピーした3つのファイルを選択して Add します。
Xcode 上で追加したソースが認識されていたらインポートは完了です。
追加されたソースコードの役割は下記となります。
・ContractHelloWorld … イーサリアムにデプロイされたコントラクトをラップするクラス
・TestWeb3 … テスト処理を担当するクラス
・Web3Helper … イーサリアムアカウントを管理するヘルパークラス
では早速、アプリのタップ処理にテストコードを紐づけましょう。
StudyWeb3Swift → StudyWeb3Swift → GameScene.swift を開き、GameScene クラスのメンバーに、TestWeb3 クラスのインスタンスを追加します。
private let testWeb3 = TestWeb3()
つづいて、画面がタップされた際に、TestWeb3 のインスタンスがテストを実行するように、func touchDown(atPoint pos : CGPoint) ブロック内に、下記のコードを追加します。
testWeb3.test()
これで、コーディングは終了です。
あとは、ワークスペースをビルドして実行するだけですが、その前に、iOS の対応バージョンを確認しておきましょう。このテストでは iOS 9.0 を選択していますが、この値はご自身のテスト環境に応じて調整してください。
また、実機で動作させる際は、署名関連の設定もご自身の環境に応じて調整してください。
さていよいよテストです。
iOS端末をPCと接続してスキームの実行対象に設定したら、Product → Run(command+R) でビルド&実行します。
動作確認
アプリが起動したら Xcode 上でログビューを表示し、iOS端末の画面をタップしてテストを開始しましょう。
**一回目のログ(タップで開閉します)**
@--------------------------
@ TestWeb3: start...
@--------------------------
@------------------
@ setTarget
@------------------
@ target: rinkeby
@------------------
@ setKeystore
@------------------
@ CREATE NEW KEYSTORE
@ Write down below json code to import generated account into your wallet apps(e.g. MetaMask)
{"version":3,"id":"2359f351-edce-4f48-9741-45dae1f36917","crypto":{"ciphertext":"0ec98990843f81524362a77a5f0df1fad42373d2209b8c5c20b5290b94afee99","cipherparams":{"iv":"9fa217c4b40c255cd9fe2c8cfae82392"},"kdf":"scrypt","kdfparams":{"r":6,"p":1,"n":4096,"dklen":32,"salt":"e6c1aad4d16d0e9c48bb4dc2445216bed6895fe0d5e5db752b62cc2f2c39bd1a"},"mac":"6455f877dd71d9378d1ad7068be65603fbc9a5daab47db2d2e799f9249675584","cipher":"aes-128-ctr"},"address":"0x87ba44ed65039cba432ae3e8b701b5c17b421245"}
@ privateKey: f992d3e4f3925ddaba0f7130ff7e418d1d0a2bae4cf4f5d196284f8f19c40029
@ saveKeystoreJson: result= true
@ CURRENT KEYSTORE
@ ethereumAddress: 0x87bA44Ed65039cBA432ae3e8b701B5C17b421245
@------------------
@ checkBalance
@------------------
@ balance: 0 wei
@------------------
@ checkSend
@------------------
@ try writeTx.sendPromise().wait()
@ TestWeb3: error: nodeError(desc: "insufficient funds for gas * price + value")
@--------------------------
@ TestWeb3: finished
@--------------------------
テストが開始されると、アプリは Rinkeby テストネットへ接続します。
その後、アプリの Documents 領域にアカウント情報(jsonファイル)があるかをチェックし、有効なファイルが無い場合(初回起動時)、アカウントを新規作成します。この時、作成されたアカウント情報は、key.json の名前でアプリの Documents 領域に保存され、次回以降のテストに利用されます。
ログを見ると、新規アカウント 0x87bA44Ed65039cBA432ae3e8b701B5C17b421245 が作成されていることがわかります。
また、作成直後のアカウントには残高がないので、送金テストが残高不足のエラー "insufficient funds for gas * price + value" により、失敗している様子がうかがえます。
お次は、上記のアカウントへイーサを送金した状況で再テストしてみましょう(※イーサの送金はMetaMask 等で行ってください)。
**二回目のログ(タップで開閉します)**
@--------------------------
@ TestWeb3: start...
@--------------------------
@------------------
@ setTarget
@------------------
@ target: rinkeby
@------------------
@ setKeystore
@------------------
@ loadKeystoreJson: json= {"version":3,"id":"2359f351-edce-4f48-9741-45dae1f36917","crypto":{"ciphertext":"0ec98990843f81524362a77a5f0df1fad42373d2209b8c5c20b5290b94afee99","cipherparams":{"iv":"9fa217c4b40c255cd9fe2c8cfae82392"},"kdf":"scrypt","kdfparams":{"r":6,"p":1,"n":4096,"dklen":32,"salt":"e6c1aad4d16d0e9c48bb4dc2445216bed6895fe0d5e5db752b62cc2f2c39bd1a"},"mac":"6455f877dd71d9378d1ad7068be65603fbc9a5daab47db2d2e799f9249675584","cipher":"aes-128-ctr"},"address":"0x87ba44ed65039cba432ae3e8b701b5c17b421245"}
@ loadKeystore: result= true
@ CURRENT KEYSTORE
@ ethereumAddress: 0x87bA44Ed65039cBA432ae3e8b701B5C17b421245
@------------------
@ checkBalance
@------------------
@ balance: 200000000000000000 wei
@------------------
@ checkSend
@------------------
@ try writeTx.sendPromise().wait()
@ result: TransactionSendingResult(transaction: Transaction
Nonce: 0
Gas price: 1000000000
Gas limit: 21000
To: 0xebfCB28c530a9aAD2e5819d873EB5Cc7b215d1E1
Value: 10000000000000000
Data: 0x
v: 44
r: 87422810951677431415523287396190154043506098492265568492767307684265100910143
s: 5842696374747228610494556784274260904960349939000190988355515076602550996278
Intrinsic chainID: Optional(4)
Infered chainID: Optional(4)
sender: Optional("0x87bA44Ed65039cBA432ae3e8b701B5C17b421245")
hash: Optional("0x49058ce4761b248f10cfbbd3dd7b8c18b35ed7538ebfe5d43d0de2331d75a615")
, hash: "0x49058ce4761b248f10cfbbd3dd7b8c18b35ed7538ebfe5d43d0de2331d75a615")
@------------------
@ checkHelloWorld
@------------------
@ [HelloWorld].getWord: Greeting from web3swift at Mar 2, 2020 22:47:59
@ [HelloWorld].sendWord: Greeting from web3swift at Mar 4, 2020 13:31:02
@ tx: TransactionSendingResult(transaction: Transaction
Nonce: 1
Gas price: 1000000000
Gas limit: 34900
To: 0xd21CE6F369F8281B7D39B47372c8F4A8A77841fc
Value: 0
Data: 0xcd048de60000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000002f4772656574696e672066726f6d20776562337377696674206174204d617220342c20323032302031333a33313a30320000000000000000000000000000000000
v: 43
r: 45934615592327133422040104710195506263029144129299763619641629997681409834635
s: 14105001248363733140301949226320798518699267713878394389330067776277426590357
Intrinsic chainID: Optional(4)
Infered chainID: Optional(4)
sender: Optional("0x87bA44Ed65039cBA432ae3e8b701B5C17b421245")
hash: Optional("0x4517fc343aaf4636d64e1660e89103bd4f542c488aa93c392bc630eba35481f1")
, hash: "0x4517fc343aaf4636d64e1660e89103bd4f542c488aa93c392bc630eba35481f1")
@--------------------------
@ TestWeb3: finished
@--------------------------
今回は残高が十分(0.2 eth)あるので、イーサの送金 や コントラクトへの書き込みテストが成功しているのがわかります。
補足
実装環境
・macOS Mojave 10.14.4
・Xcode 11.3.1(11C504)
確認端末
・iPhoneX iOS 11.2
・iPhone7plus iOS 13.3.1
・iPhone6plus iOS 10.3.3
・iPhone6 iOS 11.2.6
・iPhone5s iOS 10.2
・iPad(第六世代) iOS 12.2
以上です。
この記事が少しでも、誰かのお役に立ってくれたら嬉しいです。