この記事は公開されてから1年以上経過しています。情報が古い可能性がありますので、ご注意ください。
1 はじめに
CX事業本部の平内(SIN)です。
AWS AppSync(以下、AppSync)は、GraphQLを利用するためのマネージド型サービスです。 今回は、このGraphQLへのMutationをAWS IoT経由で取得したデータで行うものを試してみました。
最初に動作している様子です。
デバイスからのデータをiOSで表示するだけなら、MQTTのPublishとSubscribeだけで充分だと思うのですが、ここは、今後の拡張を視野に入れて、敢えてGraphQLを挟むという事にさせて下さい。
2 構成
構成は、以下のとおりです。
エッジデバイスのデータは、MQTTでPublishされています。AWS IoTでは、ルールにより、到着したデータをすべてLambdaに送っています。
AppSyncへのMutationは、Lambdaから行われ、iOSでは、AWS Amplifyを使用して、そのデータをSubscriptionしてリアルタイムに表示しています。
なお、AppSyncのデータストアは、DynamoDBとし、認証は、APIキーのみです。
3 AppSync
AppSyncでアプリ(AppSyncSample)を作成し、スキーマーを以下のように必要最小限としました。
type Data {
id: String!
datetime: AWSDateTime
value: Float
}
type Mutation {
createData(value: Float!, datetime: AWSDateTime!): Data
}
type Query {
getData(id: String!): Data
}
type Subscription {
onCreateData: Data
@aws_subscribe(mutations: ["createData"])
}
クエリ画面でmutationの動作を確認している様子です。
上記のクエリで、データストアとして設定したDynamoDBにデータが追加されていることを確認できます。
4 データの生成
エッジデバイスでは、下記の形式のデータを生成します。
{
"value": 64.2,
"datetime": 1578878926168
}
MQTTでPublishしているコードの抜粋は、以下のとおりです。生成されたデータを300msecごとに送っています。
"use strict";
var awsIot = require('aws-iot-device-sdk');
class Mqtt {
constructor(){
this.topic = 'topic_1';
this.device = awsIot.device({
keyPath: './private.pem',
certPath: './cert.pem',
caPath: './root-CA.crt',
clientId: 'iot-data-generator',
host: 'xxxxxxxxxx.iot.ap-northeast-1.amazonaws.com'
});
}
async connect(){
return new Promise( (resolve,reject) =>{
this.device.on('connect', function() {
console.log('connected');
resolve();
});
});
}
publish(value){
const date = new Date();
const json = {
value: value,
datetime: date.getTime()
}
console.log(JSON.stringify(json))
this.device.publish(this.topic, JSON.stringify(json));
}
}
async function main() {
const mqtt = new Mqtt();
await mqtt.connect();
while(true){
const value = await adconv.read(); // 生成されたデータの取得
mqtt.publish(value);
await sleep(300);
}
}
main();
エッジデバイスについての細部は、下記のブログ記事をご参照ください。
[AWS IoT] AWS IoTのデバッグ用にデータジェネレーターを作ってみました
5 AWS IoT
AWS IoTでは、下記のような簡単なルールを設定して、トピックに到着したデータをそのまま、Lambda(AppSyncSample)に送っています。
SELECT * FROM 'topic_1'
6 Lambda
LambdaでAppSyncにMutationしているコードは、以下のとおりです。
先のルールで起動されるLambdaでは、パラメータ(event)に、Publishされたデータがそのまま入っていますので、GraphQLで定義したスキーマの型に変換を行っています。
なお、クエリは、先程、AppSyncのコンソールで動作確認したクエリをそのままコピーしています。
AppSyncへの認証は、APIキーとなっているので、特別なパーミッションは必要ありません。
index.js
require('isomorphic-fetch');
const AWSAppSyncClient = require('aws-appsync').default;
const gql = require('graphql-tag');
// クエリ
const query = gql(`mutation CreateData($value: Float!, $datetime: AWSDateTime!){
createData(value: $value, datetime: $datetime){
id
value
datetime
}
}`);
exports.handler = async (event: any) => {
// event = {
// "value": 23.5,
// "datetime": 1578879458040
// }
const url = process.env.END_POINT;
const region = process.env.REGION;
const apiKey = process.env.API_KEY;
const authType = 'API_KEY';
const client = new AWSAppSyncClient({
url: url,
region: region,
auth: {
type: authType,
apiKey: apiKey
},
disableOffline: true,
fetchPolicy: 'network-only'
});
if(event.value && event.datetime){
const dt = new Date(event.datetime);
const params = {
"datetime": dt.toISOString(), // AWSDateTimeへの変換 2011-10-05T14:48:00.000Z
"value": parseFloat(event.value) // Floatへの変換
}
try {
await client.mutate({
variables: params,
mutation: query
});
} catch (err) {
console.log(JSON.stringify(err));
}
}
}
Lambdaから、GraphQLへのクエリについての詳しい説明は、下記をご参照ください。
[AWS AppSync] LambdaからGraphQLのクエリを送ってみる
7 iOS
Xcodeでプロジェクトを作成した後の手順は、以下のとおりです。(コマンドは、プロジェクトのトップで実行しています)
※プログレスバーは、KDCircularProgressを利用させて頂きました。
(1) Cocoapods
CocoaPodsでAWSAppSyncとKDCircularProgressを追加しています。
$ pod init
$ vi Podfile
target 'AppSyncSample' do
use_frameworks!
pod 'AWSAppSync', '~> 2.15.0'
pod 'KDCircularProgress'
end
$ pod install
(2) Amplify初期化
amplify initでAmplifyの初期化(CFnのスタック生成等)を行います。
$ amplify init
(3) GrapQLドキュメントの生成
amplify add codegenでiOSからアクアセスするためのクラスが生成されます。
$ amplify add codegen --apiId xxxxxxxxxxxxxxxxxxxxx
また、 amplify pushして、反映させて置きます。
$ amplify push
$ amplify status
Current Environment: demo
| Category | Resource name | Operation | Provider plugin |
| -------- | ------------- | --------- | --------------- |
| Api | AppSyncSample | Create | |
生成されたファイル(awsconfiguration.json及び、API.swift)は、プロジェクトに追加します。
(4) AWSAppSyncClientのインスタンス生成
下記は、AppDelegate.swiftで、AWSAppSyncClientのインスタンスを生成しているコードの抜粋です。
import UIKit
import AWSAppSync
@UIApplicationMain
class AppDelegate: UIResponder, UIApplicationDelegate {
var appSyncClient: AWSAppSyncClient?
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
do {
let cacheConfiguration = try AWSAppSyncCacheConfiguration()
let appSyncConfig = try AWSAppSyncClientConfiguration(appSyncServiceConfig: AWSAppSyncServiceConfig(), cacheConfiguration: cacheConfiguration)
appSyncClient = try AWSAppSyncClient(appSyncConfig: appSyncConfig)
} catch {
print("Error initializing appsync client. \(error)")
}
return true
}
(5) subscriptionによるデータ取得
API.swift で提供されるクラスでGraphQLにアクセス可能になります。リアルタイムでデータを取得して、プログレスバーを更新しているコードの一部です。
class ViewController: UIViewController {
var appSyncClient: AWSAppSyncClient?
@IBOutlet weak var progress: KDCircularProgress!
@IBOutlet weak var label: UILabel!
override func viewDidLoad() {
super.viewDidLoad()
guard let appDelegate = UIApplication.shared.delegate as? AppDelegate else {
return
}
appSyncClient = appDelegate.appSyncClient
subscribe()
}
func setValue(value:Double) {
self.progress.angle = value/100 * 360.0
self.label.text = String(value)
}
var discard: Cancellable?
func subscribe() {
do {
discard = try appSyncClient?.subscribe(subscription: OnCreateDataSubscription(), resultHandler: { (result, transaction, error) in
if let result = result {
self.setValue(value: result.data?.onCreateData?.value as! Double)
} else if let error = error {
print(error.localizedDescription)
}
})
} catch {
print("Error starting subscription.")
}
}
}
AmprifyによるiOSからのGraphQLへのアクセスについては、下記を参考にさせて頂きました。
参考:https://aws-amplify.github.io/docs/sdk/ios/start
8 最後に
今回は、AppSyncを使用して、エッジデバイスのデータ変化をiOSで表示してみました。実際に物理的な動作(ボリュームを回す)が、リアルタイムに遠隔表示できると、ちょっと、気持ち良かったです。
すべてのコードは、下記に置きました。
https://github.com/furuya02/AppSyncSample