自作Piece/iiideaで動作確認
この章は以下の節で構成されています。
自分でアップロードしたPieceを利用してiiideaを作る
前章までで、みなさんが作成した皆さんのアプリ上だけで動作するPieceをRiiiverに登録しました。まだ登録しただけですので、他の人には作成したPiece達を見ることはできませんが、皆さんは自分で登録したPieceですのでテスト利用できます。
注意: テスト利用中のPieceを利用するには、開発者登録したEmailアカウントと同じEmailでRiiiverにログインする必要があります。開発者としての登録とRiiiverのユーザー登録は別です。開発者登録をしたEmailアカウントでそのままRiiiver環境に入ろうとして、登録されていないアカウントと言うエラーが出る場合、Riiiver環境にも同じEmailアドレスを利用して登録して下さい。開発者登録されているEmailアドレスとRiiiverのユーザー登録したEmailアドレスが同じ場合に限り、ユーザー側にテスト利用中Pieceが見えるようになります。
早速RiiiverAppを利用して、iiideaを作成しましょう。RiiiverAppの利用方法はこのクイックマニュアルを参照して下さい。今回のiiideaづくりの目的は皆さんが作ったT/S/A Piece全て含まれたiiideaを作ることです。以下の手順でiiideaを作ることになります。
- ブランドは皆さんの「企業情報」で登録した情報を選択しましょう。(最初にCITIZENと表示されている部分を横にスワイプすると次々といろいろなブランドの名称とアイコンが見えるはずです)
- ブランドを選択後、デバイスを選択しましょう。皆さんが登録したデバイスの名前とアイコンが表示されているはずです。
(下の画面の水色の部分に皆さんが登録した企業アイコン、デバイスの部分に皆さんが登録したデバイスの写真が出ているはずです。写真を登録しなかった場合は、デバイス名のみが表示されます) - T, S, Aの選択画面でみなさんが作ったPieceを選択しましょう。
S PieceのカテゴリはサンプルアプリのPieceJSONを変更していない場合は、「Communication」のカテゴリのS Pieceとして表示されます。 - 作成できましたか?以下の画面になれば作成完了です。
ここで「ストアからダウンロード」ボタンを押して、iiideaをダウンロードして下さい。
作成したiiideaをダウンロードする
前節の最後の作業で、あなたのユーザーアカウントには、作成したiiideaがダウンロードされているはずです。
iiideaのダウンロードとは?
先程、Riiiverストアからiiideaをダウンロードしましたが、どこにダウンロードされたのでしょう?
RiiiverApplyを通じて、Riiiverストアサイトにアクセスし、今回の上の例ではiiidea「さ」をダウンロードしました。上図のように、実際はRiiiverAppにダウンロードしたというよりは、Riiiverのクラウド上で管理しているユーザー情報に、このユーザーはiiidea「さ」をダウンロードしたというデータをサーバ側のデータベースに残しているだけです。ユーザーがどの様なiiideaをダウンロードしているのかは全てクラウドサイドで管理されています。同じユーザーが別のアプリを利用してiiideaを利用するときも、クラウド側からダウンロードしたiiideaのリストもらってからiiideaを利用します。今扱っているサンプルアプリも同様です。下図を見て下さい。
サンプルアプリはRiiiverにログイン後に、ログインされたユーザーがダウンロードしているiiideaの一覧を取得できます。(詳しく言うと、RiiiverSDKのgetMyApplet()というメソッドを利用することで、iiideaリストを取得することが出来ます。)ダウンロードされたiiideaリストを要求するアプリはDeviceIDやVendorIDを指定することにより、そのユーザーがダウンロードしているiiideaの中でも、指定したDeviceやVendorアプリで扱うことができるiiideaのみが見えるようになります。
それでは、実際にサンプルアプリを利用して、みなさんが作ったiiideaを取得して表示してみましょう。
サンプルアプリには、開発者アカウントと同じEmailアカウントでログインしていることを確認して下さい。もし、開発者登録したEmailアカウントとは別のアカウントでログインしている場合には、開発者登録したEmailアドレスと同じEmailアドレスでRiiiverアカウントにログインし直して下さい。
ログインすると、今まで真っ黒だった画面から、皆さんが作ったiiideaが見える下の絵のようなMy iiideaリスト画面が表示されるようになったはずです。
この様にユーザーからするとRiiiverのUXとして、
- 新しいiiideaはストアからダウンロード。
- ダウンロードしたiiideaは対応するデバイスで表示、設定する。
という流れに必ずなります。次のUXとしては、 - 対応アプリからiiideaを有効に(アクティベート)する。
- 対象のTrigger Pieceがiiideaを実行する。
という体験になります。今回は皆さんはサンプルアプリを利用して、みなさんが作成したiiideaを実行しようとしています。実際にダウンロードしたiiideaを実行ところをデバッグして確認してみましょう。
iiideaをアクティベートする
My iiideaリスト画面に出たiiideaは利用する時にはiiideaを有効にする必要があります。これをiiideaをアクティベートすると言うことにします。
サンプルアプリではOS標準提供のUIスイッチを利用して各iiideaごとに有効・無効を切り替えられる様に実装しています。
詳細についてiOS版の場合はIiideaInfoViewController.swift
のfunc activateChanged
の中身を、Android版の場合はIiideaInfoFragment.kt
のactivate.setOnCheckedChangeListener
の中身をデバッグしながら確認してみてください。
サンプルアプリでは、My iiideaリストのiiideaのリストをタップするとiiideaの詳細画面が表示され、この詳細画面でiiideaをアクティベートできる様に設計しています。
「Activate this iiidea」のスイッチで有効無効の切り替えを行います。デバッグしながらこのスイッチを切り替えてみましょう。切り替わり時にデバッグLogが
... activate succeeded. ... deactivate succeeded.
と切り替わることを確認できるはずです。(このログはiOS版ではRiiiverSDKManager.swift、Android版ではRiiiverSDKManager.ktが出力しています。)
このデバッグの確認作業で重要なことは、iiideaを利用するとき、つまりトリガーを引く前にはアクティベートしなければならないということです。
サンプルアプリの動作をデバッグする
iiideaをアクティベートした後は、いよいよiiideaを実行してみましょう。
サンプルアプリでは、アクティベートをしたiiidea詳細画面の中にExecute TriggerPieceCoreSampleというボタンを配置し、これをタップすることでiiideaを実行する様に実装しています。つまり、「Execute TriggerPieceCoreSample」ボタンを押すことが、みなさんが作成したiiideaのトリガーです。このボタンが押されるとT PieceのPieceCoreが実行されます。
Execute TriggerPieceCoreSampleがタップされると、
iOS版ではIiideaInfoViewController.swift内のtriggerPieceCoreSample関数が呼ばれ、
Android版ではIiideaInfoFragment.kt内のpieceCore1.setOnClickListener関数が呼ばれます。
...
@IBAction func triggerPieceCoreSample(_ sender: Any) {
guard let iiidea = iiidea else {
print("trigger failed. reason: No iiidea")
return
}
print("preference:\(iiidea.triggerBlockUserPref ?? [:])")
var userInfo: [String:Any] = [:]
userInfo[TriggerNotification.Key.Identifier] = iiidea.getId()
userInfo[TriggerNotification.Key.Output] = triggerInput.text
// iiidea実行の通知
NotificationCenter.default.post(name: TriggerNotification.Name.sampleTrigger, object: nil, userInfo: userInfo)
}
...
...
pieceCore1.setOnClickListener {
Timber.d("pieceCore1")
val text = output_edit!!.text.toString()
val intent = Intent(TriggerPieceCoreSample.ACTION)
intent.putExtra("identifier", iiidea!!.id)
intent.putExtra("output", text)
activity!!.sendBroadcast(intent)
}
...
このサンプルアプリではボタンが押されたら、iiideaが実行されたことを通知センターやブロードキャスト機能を利用してアプリ内で伝える仕組みをとっています。なぜそうしているかと言いますと、一つのT Pieceが複数のiiideaから利用されるときの場合を考えているからです。
ここの部分はアクティベート、Trigger Pieceのデリゲーション、そしてPieceJSONからの設定が深く関連しているので詳細な説明をしていきます。
PieceJSONとPieceCoreの関係の理解が重要です。不明点などがあればこの仕様を今一度ご覧ください。最後まで読み終わりましたら、ここに戻ってきてください。
iiidea動作時の動きを確認する
サンプルアプリの動作確認とデバッグをしていきながら、SDKが実際に何をしているのか見ていきましょう。アンプルアプリをビルドしてデバッグ状態にします。そしてアプリに以下の操作をして見ましょう。
- サンプルアプリを立ち上げます。
- サンプルiiideaをアクティベートします。
- Execution iiideaボタンの上にあるテキストフィールドに"Hello!!"と入力します。
- Execution iiideaボタンをタップします。
すると、以下のようなデバッグログが表示されるはずです。
Trigger is fired! output:["triggerSampleInfo": "Hello!!"], preference:[:]
ServicePieceCoreSample input:["sampleInfo": "Hello!!"], preference:[:]
ActionPieceCoreSample input:["sampleInfo": "Hello!!"], preference:[:]
TriggerPieceCoreSample Trigger is fired! output:{triggerSampleInfo=Hello!!}
ServicePieceCoreSample inputJson: {"sampleInfo":"Hello!!"}, preferenceJson: {}
ActionPieceCoreSample inputJson: {"sampleInfo":"Hello!!"}, preferenceJson: {}
それでは、次にそれぞれのPieceが呼ばれていく過程を見ていきましょう。このサンプルアプリではとても単純なことをやっています。
テキストフィールドに入力した"Hello!!"という文字列がTrigger Pieceから出力され、"Hello!!"はService Piece → Action Pieceの順に渡されます。iiidea実行中の仕組みは少々複雑なので、ステップごとに見ていきます。
アクティベーション
エンドユーザー目線では、スイッチをスワイプするだけでiiideaをアクティベートできますが、
実際には、サンプルアプリ内で以下のような処理が走っています。
- 各Pieceにユーザーが設定したPreferenceを確認する(プリファレンス設定についてはこの資料をご覧ください)
- PieceJSONとPieceCoreをつなげる
この処理はiOS版ではIiideaInfoViewController.swift
内のfunc activateChanged()
に記述され、Android版ではIiideaInfoFragment.kt
内のactivate.setOnCheckedChangeListener
に記述されています。
プリファレンスの部分については、iiideaのアクティベートには本質的に関係しないので、ここでは説明を割愛します。(この部分について詳しく知りたい方はこちらをご覧ください)
重要なのは、対象のiiideaのアクティベートをRiiiver SDKにお願いする部分の実装です。
func activate(iiidea: ERApplet, completion: @escaping ((_ isSuccess: Bool, _ error: Error?) -> Void)) {
// セッションが切れないように毎回トークンを更新 - Update the token to keep the auth session.
self.getAWSToken { (isSuccess, error) in
if (isSuccess == false) {
print("activate failed. reason: No token")
completion(false, error)
return
}
// Activate the iiidea via Riiiver SDK.
iiidea.activate() { unauthPermissions, serviceProxyData, error in
guard unauthPermissions == nil || unauthPermissions == [] else {
print("activate failed. reason:No permission")
completion(false, error)
return
}
guard error == nil else {
let riiiverError:ERSDKError = error as! ERSDKError
print("activate failed. reason: \(riiiverError.rawValue)")
completion(false, error)
return
}
print("activate succeeded.")
completion(true, error)
}
}
}
fun activate(iiidea: ERApplet, completion: (isSuccess: Boolean, error: ERError?) -> Uni) {
val erSdk = erSdk
if (erSdk == null) {
// initialize 未完了
Timber.e("not initialized")
completion(false, null)
return
}
// セッションが切れないように毎回トークンを更新 - Update the token to keep the auth sesion.
getAWSToken { isSuccess: Boolean, error: Exception? ->
if (!isSuccess) {
Timber.e(
"updateAWSToken failed. reason: %s",
if (error != null) error.message else ""
)
completion(false, null)
return@getAWSToken
}
iiidea.activate(context) { _: Array<String>?, _: Array<ERServiceProxyData>?, eError: ERError? ->
if (erError == null) {
Timber.d("activate succeeded.")
completion(true, null)
} else {
completion(false, erError)
}
}
}
}
iiideaをアクティベートしようとすると、Riiiver SDKは以下のようなタスクを実行します。
- iiideaと、そのiiideaを構成するPieceの確認
- 各PieceのPieceJSONの中身を読み込み、"executor"のキーを確認
- 確認した"executor"の値に基づいてPieceCoreを探し、その名前のクラスを読み込み
さらに詳細に動作を追っていきましょう。
上の図は、PieceJSONとPieceCoreをつなぐ処理です。もしPieceJSONによって指定されたクラスを見つけられなかったら、SDKはエラーを返します。図中のstep 4にあるように、アクティベーションに成功すると、Trigger PieceCoreのクラス内の関数createTrigger()
とenable()
を自動的に呼び出します。
iOS版のサンプルアプリでは、各PieceCoreはPieceCoreフォルダ内に格納されています。
実際にアプリを開発する時は、ここに皆さんが作るファイルを置くことになります。
Android版SDKでは、予めSDKに各PieceCoreを格納しているPathを設定する必要があります。
iOS版では不要です。
Android版サンプルアプリではjp.co.riiiver.sdk.sample.piececore
にPieceCoreを格納していますので以下のようにSDKに設定します。
iOS版ではこの処理は不要です。object RiiiverSDKConfig {
var pieceCorePackageName = "jp.co.riiiver.sdk.sample.piececore"
...
object RiiiverSDKManager {
private var erSdk: ERSDK? = null
var accessToken: String? = null
var idToken: String? = null
private var context: Context? = null
/**
* RiiiverSDKを初期化する
*/ fun initialize(activity: Activity, complete: (isLoggedIn: Boolean) -> Unit) {
context = activity
ERAuth.initialize(activity, ERAuthDelegateImpl(activity)) {
Timber.i("initialized")
erSdk = ERSDK.Builder.with(activity)
.includeBlockExecutorPackageName(pieceCorePackageName).build()
complete(ERAuth.getInstance().isLoggedIn)
}
// Preference関連の初期化
IiideaSettingFragment.initialize(activity)
}
...
これからT, S, Aの各PieceCoreの動作について解説していきますが、サンプルアプリとRiiiverSDKとアプリ内のクラスとして実装するPieceCoreの連携を示した図を下に先にお見せしておきます。
次からの各PieceCoreの動作の説明は上の図と照らし合わせながら読んで下さい。
Trigger Pieceの発火
Trigger PieceCoreは、ERTriggerBlockExecutor
クラスを継承することで作ることができます。ERTriggerBlockExecutor
クラスはcreateTrigger()
とenable()
、disable()
の関数を持っていますが、これらはS PieceやA PieceのPieceCoreにはありません。
T Pieceはiiideaを開始させることが目的なので、他のPieceCoreクラスとインターフェースが異なっています。
Trigger PieceCoreの各関数について簡単に説明します。
createTrigger()
iiideaをアクティベートするとPieceCoreで継承しているERTriggerBlockExecutor
クラスのインスタンスがRiiiverSDK内部で生成されます。そのタイミングでcreateTriggerが呼ばれます。引数にiiideaのIDが含まれていいるので、アクティベートされたiiideaが何なのか、T PieceCore側でも判断することができます。
T Pieceに何かユーザーが設定できる項目がある場合、ユーザーがPreference設定画面で入力した値もこの関数が呼ばれた時に引数preferenceJsonとして渡ってきます。ユーザーの設定を読み取り、処理の情報に利用することができます。
enable()
アクティベートされると呼び出されます。上記のcreateTrigger()の直後に呼ばれます。
この時にERTriggerBlockDelegate
のインスタンスが渡されます。
iiideaを実行するためにはこのインスタンスを利用してトリガーを引きます。
disable()
ディアクティベートされる(iiideaが無効化される)と呼び出されます。
enable()で渡されたERTriggerBlockDelegate
のインスタンスは使用できなくなります。
disable()を読んだ後は、対象のiiideaは実行することはできません。
サンプルアプリで実際にTriggerの発火を行っているTriggerPieceCoreSample
を見てみましょう。
@objc(TriggerPieceCoreSample)
class TriggerPieceCoreSample: NSObject, ERTriggerBlockExecutor {
weak var delegate: ERTriggerBlockDelegate?
var identifier: String?
// TriggerPieceCoreSample の実行要求通知がきたら実行される
// called when the notification of the TriggerPieceCoreSample execution comes.
@objc private func didReceiveTriggerPieceCoreSampleTrigger(_ notification: Notification) {
guard let userInfo = notification.userInfo else { return }
// Check the ID - IDのチェック
guard let identifier = userInfo[TriggerNotification.Key.Identifier] as? String else {
print("TriggerPieceCoreSample error in execution. reason:no ID")
return
}
if identifier != self.identifier {
// does not fire because the ID is wrong. - IDが異なるため発火しない
return
}
// TriggerPieceCoreSampleのアウトプット - Get the text field's string and set it as a output of the T piece.
var output = [ "triggerSampleInfo" : "" ]
output["triggerSampleInfo"] = userInfo[TriggerNotification.Key.Output] as? String
print("TriggerPieceCoreSample Trigger fire! output:\(String(describing: output))")
// Trigger with outpur information - output情報を設定してTrigger発火
delegate!.didTrigger(outputJson: output)
}
func createTrigger(blockJsonString: String, preferenceJson: Dictionary<String, Any>, applet: ERApplet) -> Bool {
print("TriggerPieceCoreSample:\(self.identifier ?? "")]")
identifier = applet.getId()
return true
}
func enable(delegate: ERTriggerBlockDelegate) -> Bool {
print("TriggerPieceCoreSample enable")
self.delegate = delegate
// sampleTrigger実行通知を監視を開始する。通知を受けたら didReceiveTriggerPieceCoreSampleTrigger() を実行する
// Starts ovserving the notification for the "sampleTrigger" execution. When received the notification, executes didReceiveTriggerPieceCoreSampleTrigger().
NotificationCenter.default.addObserver(self, selector: #selector(didReceiveTriggerPieceCoreSampleTrigger(_:)), name: TriggerNotification.Name.sampleTrigger, object: nil)
return true
}
func disable() {
print("TriggerPieceCoreSample disable")
delegate = nil
// sampleTrigger実行通知を監視を終了する。
// Stop observing the notification for the sampleTrigger execution.
NotificationCenter.default.removeObserver(self, name: TriggerNotification.Name.sampleTrigger, object: nil)
}
}
class TriggerPieceCoreSample(val context: Context) : ERTriggerBlockExecutor, BroadcastReceiver() {
var mDelegate: ERTriggerBlockDelegate? = null
var mIdentifier: String? = null
companion object {
const val ACTION = "jp.co.riiiver.sdk.sample.piececore.intent.action.TriggerPieceCoreSample"
}
override fun onReceive(context: Context?, intent: Intent?) {
intent?.also {
val identifier = intent.getStringExtra("identifier")
if (!mIdentifier.equals(identifier)) {
// does not fire because the ID is wrong. - IDが異なるため発火しない
return
}
val output = java.util.HashMap<String, Any>()
output["triggerSampleInfo"] = intent.getStringExtra("output")
mDelegate?.also { delegate ->
Timber.d("TriggerPieceCoreSample Trigger fire! output:%s", output)
delegate.didTrigger(context, output)
} ?: run {
Timber.e("TriggerPieceCoreSample createTrigger:delegate is null")
}
}
}
override fun createTrigger(
blockJsonString: String?,
preferenceJson: JSONObject?,
applet: ERApplet?
) {
Timber.d("TriggerPieceCoreSample createTrigger:%s", "")
applet?.also {
this.mIdentifier = applet.id
} ?: run {
Timber.e("TriggerPieceCoreSample createTrigger:applet is null")
}
}
override fun enable(delegate: ERTriggerBlockDelegate?): Boolean {
Timber.d("TriggerPieceCoreSample enable:%s", "")
delegate?.also {
this.mDelegate = delegate
} ?: run {
Timber.e("TriggerPieceCoreSample createTrigger:delegate is null")
}
// ブロードキャストIntentの監視を開始する
context.applicationContext.registerReceiver(this, IntentFilter(ACTION))
return true
}
override fun disable() {
Timber.d("TriggerPieceCoreSample disable:%s", "")
if (mDelegate != null) {
mDelegate = null
// ブロードキャストIntentの監視を終了する
context.applicationContext.unregisterReceiver(this)
}
}
}
TriggerPieceCoreSampleのインスタンスが生成されたときに呼ばれるcreateTrigger()
でiiideaのID情報を保持します。
理由は先程も「一つのT Pieceが複数のiiideaから利用されるときの場合を考えているから」と記述したところと関連するからです。後ほど説明します。
アクティベートされた時に呼ばれるenable()
でERTriggerBlockDelegate
のインスタンスを保持します。
サンプルアプリではiiideaを実行するボタン(Execute TriggerPieceCoreSample)をタップすると、iiideaが実行されたことを示す通知(iOSではNotificationCenter
、AndroidはIntent
)を送るように作られていますので、enable()
が呼ばれたタイミングでその通知の監視を開始します。
ディアクティベートされた時に呼ばれるdisable()
では保持していたERTriggerBlockDelegateのインスタンスを破棄し、iiidea実行通知の監視を終了します。
ここまでがERTriggerBlockExecutor
の関数に対する実装です。
次にiiidea実行ボタンがタップされた時に呼ばれる処理を見てみましょう。
@objc private func didReceiveTriggerPieceCoreSampleTrigger(_ notification: Notfication) {
guard let userInfo = notification.userInfo else { return }
// Check the ID - IDのチェック
guard let identifier = userInfo[TriggerNotification.Key.Identifier] as? Strng else {
print("TriggerPieceCoreSample error in execution. reason:no ID")
return
}
if identifier != self.identifier {
// does not fire because the ID is wrong. - IDが異なるため発火しない
return
}
// TriggerPieceCoreSampleのアウトプット - Get the text field's string and setit as a output of the T piece.
var output = [ "triggerSampleInfo" : "" ]
output["triggerSampleInfo"] = userInfo[TriggerNotification.Key.Output] as?String
print("TriggerPieceCoreSample Trigger fire! output:\(String(describing: outut))")
// Trigger with outpur information - output情報を設定してTrigger発火
delegate!.didTrigger(outputJson: output)
}
override fun onReceive(context: Context?, intent: Intent?) {
intent?.also {
val identifier = intent.getStringExtra("identifier")
if (!mIdentifier.equals(identifier)) {
// does not fire because the ID is wrong. - IDが異なるため発火しない
return
}
val output = java.util.HashMap<String, Any>()
output["triggerSampleInfo"] = intent.getStringExtra("output")
mDelegate?.also { delegate ->
Timber.d("TriggerPieceCoreSample Trigger fire! output:%s", output)
delegate.didTrigger(context, output)
} ?: run {
Timber.e("TriggerPieceCoreSample createTrigger:delegate is null")
}
}
}
この関数でiiidea実行ボタンがタップされたことを受けてiiideaを実行するまでの処理を行います。つまり、ここからがT PieceのPieceCoreの処理が開始することになります。
受け取った通知には実行対象のiiideaのIDとServicePieceに渡すoutput情報が含まれています。
まずcreateTrigger()
が呼ばれた際に保持していたiiideaのIDと実行対象のiiideaのIDを比較チェックしています。ここが一つのT Pieceが複数のiiideaから利用されることを考慮している部分です。先に示した図の通り、T Pieceも色々なiiideaのきっかけになる場合があります。例えば、「ボタンを押すとトリガー」と言うT Pieceを作った場合、ボタンを押したら天気予報を取ってくる、知り合いにメールを送る、など同じT Pieceで複数のiiideaを実行する場合が考えられます。これらのiiideaが同時に複数アクティベートされているとすると、同じT Pieceのインスタンスが複数存在することになります。(ERTriggerBlockExecutor
を継承したクラスのインスタンス)
このサンプルアプリでは、OSのブロードキャストや通知センターを利用して、例で言う全てのボタン押しT Pieceのインスタンスに対して、イベントを発行しています。それぞれのインスタンスはiiideaのIDを受け取り、自分が正しいインスタンスかどうかを判断するように実装しています。
次に通知情報からServicePieceに渡すoutput情報を取得して、渡すためのフォーマットに合わせて整形しています。
ここの情報がServicePieceのinput情報として渡されていくことになります。
必要であれば、この辺りでPreference設定の情報を読み取るような処理を入れることになりますが、このサンプルアプリ・サンプルT PieceではPreference設定できるPieceに設計していませんので、処理を入れていません。
T PieceのPieceCoreの処理が終わったら最後にdidTrigger
にoutputを設定して呼び出して下さい。didTrigger
を呼ぶことでRiiiverSDKはトリガーが発火され、T PieceCoreの処理が全て終わったと判断し、S Pieceの実行に移ります。
Service Pieceの実行
Service PieceCoreは、ERServiceBlockExecutor
クラスを継承することで作ることができます。ERServiceBlockExecutor
クラスはexecute()
関数を持っています。
Service PieceCoreの関数について簡単に説明します。
execute()
Trigger PieceのdidTrigger
をきっかけに呼び出されます。
Trigger Pieceのoutput情報がinputJsonというパラメータに含まれています。
また、Pieceに対してユーザーがPreference設定画面で設定した情報はpreferenceJsonというパラメータに含まれています。
サンプルアプリで実際にServiceの処理を行っているServicePieceCoreSample
を見てみましょう。
@objc(ServicePieceCoreSample)
class ServicePieceCoreSample: NSObject, ERServiceBlockExecutor {
func execute(appletId: String, blockJsonString: String, preferenceJson: Dictionary<String, Any>?, inputJson: Dictionary<String, Any>?, completionHandler: @escaping ((Dictionary<String, Any>?, ERSDKError?) -> Void)) {
print("ServicePieceCoreSample input:\(inputJson ?? [:]), preference:\(preferenceJson ?? [:])")
var outputJson: Dictionary<String, Any> = [:]
if let inputJson = inputJson {
// inputとして受け取った情報をそのまま次のPieceへ渡す
// Input value goes through to the next piece as an output as it is
outputJson["serviceSampleInfo"] = inputJson["sampleInfo"]
}
completionHandler(outputJson, nil)
}
}
class ServicePieceCoreSample(val context: Context) : ERServiceBlockExecutor {
override fun execute(
appletId: String?,
blockJsonString: String?,
preferenceJson: JSONObject?,
inputJson: JSONObject?,
completionHandler: Completion1<MutableMap<String, Any>>?
) {
Timber.d("ServicePieceCoreSample inputJson: $inputJson, preferenceJson: $preferenceJson")
inputJson?.also {
// iunputとして受け取った情報をそのまま次のPieceへ渡す
//Input value goes through to the next piece as an output as it is
val outputJson = mutableMapOf(Pair("serviceSampleInfo", inputJson.get("sampleInfo")))
completionHandler?.also {
completionHandler.accept(outputJson, null)
}
}
}
}
実際のアプリにService Pieceを組み込む際は、目的に合わせてexecute()
内を書き換えてください。ERServiceBlockExecutor
を継承してさえいれば、クラス名や格納場所の変更はもちろんしていただいて構いません。
Trigger Pieceから受け取ったinput情報やユーザーが設定したpreferences情報を使ってコードを作りましょう。
必要な処理が終わったらAction Pieceに渡すoutput情報を設定してcompletionHandler
を呼ぶのを忘れずに。
completionHandler()
completionHandler
を呼ぶとRiiiver SDKにS PieceのPieceCoreでの処理が終了したこと報告することになります。RiiiverSDKは続けてAction Pieceを実行してくれます。
S PieceのPieceCoreがRiiiverSDKに内蔵されている場合
対象となるiiideaに使われているService Pieceがアプリ側に存在しない場合があります。(例えば気象情報を取得するWebサービスを利用するPieceが相当します。ウェブサービスを活用してPieceを簡単に作れるように、RiiiverではRiiiverSDKを利用せずAWS Lambdaを活用することで、S PieceやA Pieceを作る仕組みも準備しています。興味がある方はこちらをご参照下さい。)この仕組みを利用する場合、S PieceのPieceCoreはRiiiver SDKの内部にありますので、皆さんは何も実装する必要はありませんし、動作について気にする必要もありません。RiiiverSDK内部のPieceCoreの処理が終わった後、次のA Pieceが皆さんのものだった場合には、S Pieceの終了後に皆さんのA PieceがRiiiverSDKから呼ばれます。
Action Pieceの実行
Action PieceCoreは、ERActionBlockExecutor
クラスを継承することで作ることができます。ERActionBlockExecutor
クラスはexecute()
関数を持っています。
Action PieceCoreの各関数について簡単に説明します。
execute()
Service PieceのcompletionHandler
をきっかけに呼び出されます。
Service Pieceのoutput情報がinputJsonというパラメータに含まれています。
また、Pieceに対してユーザーが設定した情報はpreferenceJsonというパラメータに含まれています。
サンプルアプリで実際にServiceの処理を行っているActionPieceCoreSample
を見てみましょう。
Action PieceCoreの振る舞いはService PieceCoreとそっくりです。
@objc(ActionPieceCoreSample)
class ActionPieceCoreSample: NSObject, ERActionBlockExecutor {
func execute(appletId: String, blockJsonString: String, preferenceJson: Dictionary<String, Any>?, inputJson: Dictionary<String, Any>?, completionHandler: @escaping ((Dictionary<String, Any>?, ERSDKError?) -> Void)) {
print("ActionPieceCoreSample input:\(inputJson ?? [:]), preference:\(preferenceJson ?? [:])")
var outputJson: Dictionary<String, Any> = [:]
if let inputJson = inputJson {
// iunputとして受け取った情報をそのまま次のPieceへ渡す
//Input value goes through to the next piece as an output as it is
outputJson["actionSampleInfo"] = inputJson["sampleInfo"]
}
completionHandler(outputJson, nil)
}
}
class ActionPieceCoreSample(val context: Context) : ERActionBlockExecutor {
override fun execute(
appletId: String?,
blockJsonString: String?,
preferenceJson: JSONObject?,
inputJson: JSONObject?,
completionHandler: Completion1<MutableMap<String, Any>>?
) {
Timber.d("ActionPieceCoreSample inputJson: $inputJson, preferenceJson: $preferenceJson")
inputJson?.also {
val outputJson = mutableMapOf(Pair("actionSampleInfo", inputJson.get("sampleInfo")))
completionHandler?.also {
completionHandler.accept(outputJson, null)
}
}
}
}
Service Pieceと同様に、実際のアプリにAction Pieceを組み込む際は、目的に合わせてexecute()
内を書き換えてください。ERActionBlockExecutor
を継承してさえいれば、クラス名や格納場所の変更もOKです。
Service Pieceから受け取ったinput情報やユーザーが設定したpreferences情報を使ってコードを作りましょう。
必要な処理が終わったらAction Pieceに渡すoutput情報を設定してcompletionHandler
を呼ぶのを忘れずに。completionHandler
を呼ぶとRiiiver SDKがiiideaの実行を終了します。
completionHandler()
S PieceのcompletionHandler
と同じです。A PieceでもPieceCoreの処理が終了したらこの関数を読んで、RiiiverSDKに処理が終了したことを報告しましょう。
A Pieceは最後なので、このハンドラが呼ばれるとiiideaの実行が終了することになります。
A PieceのPieceCoreがRiiiverSDKに内蔵されている場合
[S PieceのPieceCoreがRiiiverSDKに内蔵されている場合](#S PieceのPieceCoreがRiiiverSDKに内蔵されている場合)と全く同じです。RiiiverSDK内部のA PieceCoreの実行終了でも、みなさんが作ったPieceCoreの実行が終了しても、RiiiverSDKとしては、iiideaの実行が終了したことになります。
RiiiverSDKではiiideaは同時に一つしか実行することが出来ません。A Pieceが終了し、iiideaの実行が終了したら、RiiiverSDKは次のPieceの実行を受け付けるようになります。
アクティベートは複数のiiideaに対して同時にすることが出来ます。
まとめと次のステップ
ここまでで、サンプルアプリのためにみなさんが作ったT Piece、S Piece、A Piece、これらを利用したiiideaを利用した簡単なデバッグについての説明は終了です。お疲れさまでした。
サンプルアプリを利用した説明はここまでですが、次のステップとして、みなさんのアプリにRiiiverSDKを取り入れるときの方法や注意点ついて、こちらのドキュメントで説明します。
今回のサンプルアプリではRiiiverSDKに含まれていないいくつかのファイルなどをこちらで準備した状態で配布していますで、説明ができていない部分があります。例えば、みなさんが最初に目にしたFacebookやGoogleアカウントでのログイン機能についてなどです。
皆さんのアプリに取り入れる時に考えなければならない追加のUIについてなども触れていますので、ぜひご一読下さい。
用語
新名称と旧名称
旧名称 | 新名称 | 説明 |
---|---|---|
ER | Riiiver | Riiiver開発時のプロジェクト名称が-ER。当時の開発したコード内には名前が残っています。適宜読み替えてください。 |
Applet | iiidea | ユーザーがRiiiver Appを利用することでNo Codeで作成できる小さいアプリを指す。 |
Block | Piece | iiideaを構成する要素。Trigger Piece、Servie Piece、Action Pieceがある。 |