まだプログラマーですが何か?

プログラマーネタ中心。たまに作成したウェブサービス関連の話も https://twitter.com/dotnsf

前回の続きです:
Ubuntu で Swift 言語を使う


前回は Ubuntu 上に Swift アプリケーション開発環境を用意して、ハローワールド的なアプリケーションを作って実行するまでを紹介しました。今回は Swift 用の Web アプリケーションフレームワークである Kitura を使って、Ubuntu 上の Swift で Web アプリケーションを開発してみます。 以下の作業の前に前回紹介した導入作業が必要になるので、まだの場合はこちらを参照して Ubuntu に Swift をインストールしておいてください。

では改めて Kitura アプリケーションを作ってみます。前回同様にアプリケーションプロジェクト用のディレクトリ(以下の例では myFirstKitura)を作って移動し、swift コマンドでパッケージを初期化しておきます(ここまでは前回と同様です):
$ mkdir myFirstKitura
$ cd myFirstKitura
$ swift package init --type executable

このプロジェクトでは Kitura を利用するため、パッケージ情報に dependencies を記述する必要があります。Package.swift ファイルをテキストエディタで開き、以下の青字部分を追加します:
import PackageDescription

let package = Package(
    name: "myFirstKitura",
    dependencies: [
        .Package(url: "https://github.com/IBM-Swift/Kitura.git", majorVersion: 1, minor: 6)
    ]
)

そしてアプリケーションのソースコード Sources/main.swift を以下のように変更します。Kitura の HTTP Server を使って 8090 番ポートで待ちうけ、"/"(ドキュメントルート)への GET アクセスをハンドリングしてメッセージを表示する、という内容にしています:
import Kitura

// Create a new router
let router = Router()

// Handle HTTP GET requests to /
router.get("/") {
    request, response, next in
    response.send("ハローワールド!")
    next()
}

// Add an HTTP server and connect it to the router
Kitura.addHTTPServer(onPort: 8090, with: router)

// Start the Kitura runloop (this call never returns)
Kitura.run()
  ↑なんとなく Node.js の Express に似てます。。


ではこのコードをビルドして実行します:
$ swift build
$ ./build/debug/myFirstKitura (実行したままにします)

実行されている状態で、同じシステムのウェブブラウザから http://localhost:8090/ にアクセスし、コードに記述したメッセージが表示されることを確認します:
2017040402


ウェブアプリケーションとして稼働できていることが確認できました。実行したままになっているコマンドを終了するには Ctrl + C を実行します:
$ ./build/debug/myFirstKitura
^C  (Ctrl+C を実行して、コマンドプロンプトに戻る)
$


以上、簡単なサンプルでしたが、Web のルーティング機能が実装されたフレームワークで Swift アプリケーションを、macOS ではなく Ubuntu 環境で開発/実行することができるようになりました。この Swift ウェブアプリケーションは IBM BluemixSwift ランタイムとしても実装されており、作ったアプリケーションを Bluemix 上ですぐに運用することができます:
2017040403



(参考)
http://www.kitura.io/en/starter/gettingstarted.html


Apple が開発したプログラミング言語 Swift は 2016 年にオープンソース化され、macOS 以外のプラットフォームでも Swift のアプリケーション開発ができるようになりました。というわけで、実際に Ubuntu から Swift をセットアップして使ってみました。以下は自分の(VirtualBox 上の)Ubuntu 14.02 環境での作業記録です。

なお、今回は Swift 3.0.2 を使うことにします(2017/Apr/04 時点での最新バージョンは 3.1 ですが、このバージョンの場合 Xcode 併用となって環境準備が面倒だったので・・ ←訂正、コマンドラインだけでも 3.1 が使えました。「Xcode の一部として Swift が提供されている」という表現が正しいようです)。

Ubuntu で Swift をインストールしたり、Swift アプリを作成したりする作業はターミナルからのコマンド操作で行います。というわけでターミナルを起動し、まずは Swift をインストールする上での前提パッケージをインストールします:
$ sudo apt-get update
$ sudo apt-get install clang libicu-dev libcurl4-openssl-dev libssl-dev

続いて Swift のダウンロードページから Ubuntu 用の Swift 3.0.2 をダウンロードします:
2017040401


ダウンロードしたファイルを展開して、展開先にパスを通します:
$ tar xzvf swift-3.0.2-RELEASE-ubuntu14.04.tar.gz
$ export PATH=~/swift-3.0.2-RELEASE-ubuntu14.04.tar.gz/usr/bin:$PATH  (この行を ~/.bashrc などに書くのがオススメ)

これで Swift 本体のインストールは完了です。次にアプリケーションを作ってみます。開発用のディレクトリ(以下の例では myFirstSwift)を作って、そこに移動します:
$ mkdir myFirstSwift
$ cd myFirstSwift

このディレクトリの中で上記でインストールした swift コマンドを使い、Swift アプリケーションパッケージを初期化します:
$ swift package init --type executable

この時点で以下のようなファイル構成が生成されています:
myFirstProject
|- Package.swift
|- Sources
    |- main.swift
    |- Tests

いわゆる「ソースファイル」は Sources/main.swift になります。この時点での中身は以下のような Hello World 表示アプリケーションになっています:
print("Hello, world!")

必要に応じてテキストエディタで適当に書き直してみます:
print("ハロー Swift ワールド on Ubuntu!")

ソースコードが用意できたら swift コマンドでビルドします:
$ swift build

ビルド結果は .build/debug/myFirstSwift にあるので、これを実行して、期待通りの結果が出力されることを確認します:
$ ./build/debug/myFirstSwift
ハロー Swift ワールド on Ubuntu!

ごく簡単なサンプルでしたが、とりあえず Ubuntu 上で Swift アプリの開発ができることが確認できましたが。次回は Swift の Web フレームワークである Kitura を紹介する予定です。




(参考)
http://www.kitura.io/en/starter/gettingstarted.html


MQTT は IoT の仕組みの中で使われることの多いプロトコルですが、以前からそれだけに使うのは勿体ないなあと思っていました。MQTT のリアルタイム性はチャットなどのメッセージングアプリケーションにも向いていると思っており、実際に Facebook Messenger の仕組みとしても使われているとの情報もあります。というわけで、MQTT を使ったチャットアプリの開発に挑戦してみました。

加えて、IoT といえば IBM Bluemix からも提供されている Node-RED が有名です。今回は IBM Bluemix 環境上の Node-REDIBM IoT Platform サービスの quickstart エディションを使ってチャットを作ってみることに挑戦しました。

何はともあれ、まずは Node-RED 環境を用意します。IBM Bluemix にログインし、ボイラープレートから Node-RED Starter を選択して、Node-RED が使えるアプリケーションサーバーインスタンスを用意します:
2017040301


IBM Bluemix の Node-RED を使わずに、自前等で Node-RED 環境を用意する場合は npm などで node-red-contrib-scx-ibmiotapp ノードをインストールして有効にしておく必要があります:
2017040302

(↑この ibmiot ノードが使える状態にしてください)


では Node-RED でチャットアプリを作ります、といっても実はかなりシンプルです。1つ1つノードを配置してもいいのですが、まずはインポートして中身を確認し、必要に応じて説明を加えることにします。画面右上のハンバーガーメニューを開き、 読み込み→クリックボード を選択します:
2017040303

 
「フローの読み込み」画面になったら、ここの内容をそっくりそのままコピー&ペーストして「読み込み」ボタンをクリックし、フロー定義を作成します:
2017040304


正しく読み込みが完了すると以下のような3本のデータフローが再現されるはずです。上から1つ目は GET /chat 実行時のチャット画面(HTML)の定義、2つ目はチャットメッセージを POST(POST /post) した時の処理、そして3つ目は IBM IoT サービスを使って MQTT 経由でチャット参加メンバーのメッセージを取り出す処理を定義しています。いずれもシンプルな処理で実現できていることが確認できます:
2017040305


画面内に2つの IBM IoT ノードが含まれています(青いノード、INPUT/OUTPUT が1つずつ)。それぞれダブルクリックすると、どちらにも Device Id を入力する項目があり、いずれも初期状態では空になっているはずです。この Device Id にはユニークな値を指定する必要があります。以下の例では "dotnsf.mqtt.chat.001" という値を設定していますが、ここには自分の名前や日付を含めるなどして、誰とも被らないユニークな値を設定します(2つのノード両方の Device Id に同じ値を指定します)。指定後「完了」ボタンをクリックします:
2017040306


また、2つ目のフローの中にある function ノードの中身を確認します。ここはチャット参加者が自分のメッセージを投稿した時に実行されるフローで、HTTP リクエストの本文(msg.req.body)の値を取り出して、その値を IoT の Payload に変換している部分です。これも非常にシンプルな処理内容が記述されていることが確認できます:
2017040307


改めて3つそれぞれのフローの中でどのような処理が定義されているのかを確認してみましょう。1つ目のフローはウェブブラウザから(サーバー名)/chat という URL に(GET リクエストで)アクセスした時に返される HTML の定義です。実際の HTML や CSS/JavaScript 定義そのものは「チャット画面」というテンプレートノードの中で定義されています(後述します):
2017040301


2つ目のフローはチャット画面の中で利用者が自分のメッセージをチャットに流した時に実行される処理です。チャットにメッセージを流すと(サーバー名)/post という URL に名前やメッセージ内容が HTTP POST され、その内容を(上記のように)取り出して MQTT の Payload に変換し、IBM IoT に転送(MQTT の処理でいうと「パブリッシュ」)しています。転送時にユニークな Device Id を指定していることで、同じテンプレートを使っても異なるアプリケーションであるとみなし、他の人が作ったチャットと混線しないようにしています。なお、緑色のノードはデバッグノードで、POST されたメッセージの内容をこの画面内からも確認できるようにしているだけで、実際の処理には無関係です(無くても動きます):
2017040302


そして3つ目のフローは上記2つ目のフローで処理されたメッセージを取り出すフローになります。自分だけでなく、同じチャット画面を見ている他のユーザーがメッセージを流した場合もこの処理が実行され、IBM IoT 経由で送られたメッセージが(サーバー名)/ws/chat という URL の WebSocket に送信されるよう記述されています。実際には1つ目の HTML の中で /ws/chat を監視しており、ここにメッセージが送られてきた場合の処理が記述されています:
2017040303


この状態でデプロイ(画面右上のボタン)をクリックすることで実際にチャットアプリケーションを使うことができるようになります。デプロイ後、PCやスマホのウェブブラウザで https://(Node-RED の動いているホスト名)/chat にアクセスしてみてください。Node-RED の一番上のフローが呼ばれ、テンプレートノードの中で定義された内容の HTML が表示されます。初期状態では↓のように名前の入力を求められます:
2017040301


適当な名前を入力して「入室」ボタンをクリックします(入室のタイミングで IoT と接続します):
2017040302


入室すると画面が切り替わり、自分の名前とメッセージ入力フィールドが画面下に表示されます。画面の大半はチャット履歴が表示される画面ですが、まだ何もメッセージがないので何も表示されていません:
2017040303


では試しに何かメッセージを入力してみます。入力を確定するには PC からであれば Enter キーを、スマホであれば「開く」などでメッセージを確定させてください:
2017040304


入力したメッセージがチャット履歴に表示されます。これは自分のメッセージなので右側に吹き出しがついて、緑色で表示されるようにしています:
2017040305


もう1つメッセージを送ると、メッセージが下に追加されます:
2017040306


試しに別のブラウザや別のスマホなどから同じ URL にアクセスして、別の名前で入室してメッセージを送信してみます。このユーザーから見ると入室前のメッセージは見れないので、自分のメッセージが一番最初に表示されます:
2017040307


が、元のユーザーからは別のユーザーが入室してきてメッセージを送信したことになります。その場合は白背景で、左側に吹き出しがある状態でチャット履歴に記録されます(この UI 見たことありますよね。意識して CSS を作ってます(笑)):
2017040308


同様にして別のユーザーが入室してくると、そのユーザーのメッセージも白背景で左に吹き出しが付く形で表示されていく、というものです。最低限のグループチャットの機能は実現できていると思ってます:
2017040309


さて、ではこのチャット画面の HTML はどうなっているのかを説明します。具体的な内容は PC ブラウザからアクセスして HTML ソース(と埋め込まれた CSS など)を直接参照していただきたいのですが、肝心な部分の JavaScript はこのようになっています(赤字はコメント):
  :

var socket; var wsUrl = 'wss://' + location.hostname + '/ws/chat'; //. WebSocket監視先URL function connect(){ //. 「入室」時に呼ばれる処理 console.log( "connect()" ); socket = new WebSocket(wsUrl); //. WebSocket 接続 socket.onmessage = function(e) { //. WebSocket にメッセージが来たら、以下を実行 var msg = JSON.parse(e.data); //. 送信データ(POST されたデータ)を JSON で取り出し //console.log( msg ); if( msg.id != deviceid ){ //. 自分のメッセージなのか、他人のメッセージなのかを判別 //. 自分以外の発言 var box = "<div class='question_box'><p class='notmymessage'>" + msg.name + "</p><div id='arrow_question'>" + msg.message + "</div></div>"; $('#contents').append( box ); }else{ //. 自分の発言 var box = "<div class='answer_box'><p class='mymessage'>" + msg.name + "</p><div id='arrow_answer'>" + msg.message + "</div></div>" $('#contents').append( box ); } } }

:

2つ目のフローで投稿したメッセージの内容が IBM IoT ノードに(MQTT で)送られていました。自分だけでなく同じチャットルームに入室している全ての人のメッセージがこのように MQTT データとして送信されます。 そしてその内容を3つ目のフローで取得し、/ws/chat というパスに WebSocket データとして送信していました。つまりチャットで誰かがメッセージを送ると、/ws/chat に WebSocket でデータが送られるということになります。そのデータを監視して、自分のメッセージか他人のメッセージかを判別して Dynamic HTML でチャット履歴に追加する、という部分の処理が上記になります。


そしてこれだけでチャットが実現できているということは、(気付いている人もいるかもしれませんが)少なくともここまでの処理に関してはデータベースを一切使わずに実現できていることを意味しています。確かにリアルタイムデータ処理なのでデータを保存する必要はないのですが、実際に保存せずに実現できるというのはなかなか興味深いのではないかと思っています。


このページのトップヘ