rails actioncable + swiftでwebsocketのhello world
iOSでWebSocketを少し試す必要があったのでそのときの手順をまとめておく。サーバー側はRails5.2のAPIモードでActionCableを使う。
railsインストール
$ bundle exec rails new ac_test --api
Channel作成
channel作成
$ ./bin/rails g chnnel chat
ChannelはコントローラのWebSocket版と考える。そのままだけど、subscribeはクライアントからsubscribeのメッセージを受け取ったら呼ばれる。
app/channels/chat_channel.rb
class ChatChannel < ApplicationCable::Channel
def subscribed
stream_from "chat:message"
end
def unsubscribed
end
end
接続してみる
ここまでできたらサーバーを起動してWebSocketクライアントで接続してみる。
WebSocket Client on the Mac App Store
GUIのソフトを探したところこちらのソフトが使いやすそうだった。

ws://192.168.1.109:3000/cable を入力して「Connect」をクリック
rails serverのコンソール
Started GET "/cable" for 192.168.1.109 at 2019-01-08 18:33:16 +0900 Started GET "/cable/" [WebSocket] for 192.168.1.109 at 2019-01-08 18:33:16 +0900 Request origin not allowed: ws://192.168.1.109:3000 Failed to upgrade to WebSocket (REQUEST_METHOD: GET, HTTP_CONNECTION: Upgrade, HTTP_UPGRADE: websocket) Finished "/cable/" [WebSocket] for 192.168.1.109 at 2019-01-08 18:33:16 +0900
「Failed to upgrade to WebSocket...」と接続に失敗していることがわかる。原因は送信元のチェックで引っかかているようで、今回はテストのため全てのリクエストを許可する。
config/environments/development.rb
# すべての送信元からのリクエストを許可 config.action_cable.disable_request_forgery_protection = true
サーバーを再起動して再度接続すると今度は「Successfully upgraded to WebSocket」と成功。
Started GET "/cable" for 192.168.1.109 at 2019-01-08 18:38:06 +0900 Started GET "/cable/" [WebSocket] for 192.168.1.109 at 2019-01-08 18:38:06 +0900 Successfully upgraded to WebSocket (REQUEST_METHOD: GET, HTTP_CONNECTION: Upgrade, HTTP_UPGRADE: websocket)

クライアント側もtype:welcomのレスポンスを確認できた。
subscribeしてみる
引きつづき接続したままの状態でsubscribeする

「Body」に以下のJSONを入力して送信ボタンのアイコンをクリック
{"command":"subscribe","identifier":"{\"channel\":\"ChatChannel\"}"}railsのコンソールに以下が出力されれば成功
ChatChannel is transmitting the subscription confirmation ChatChannel is streaming from chat:message
この状態で、ChatChannelのMessageにメッセージを送信してクライアント側でレスポンスを取得できるか確認する
./bin/rails console
$ ChatChannel.broadcast_to('message', 'hello')レスポンスがあればクライアントの右側のペインに出力されるはずだが何も表示されないので失敗
ruby on rails - ActionCable.server.broadcast from the console - Stack Overflow
こちらの記事によると、別プロセスから実行する場合(rails serverとrails consoleは別プロセス)の場合、adapter: asyncは使えないらしい。記事の通り、config/cable.ymlの設定をasyncからadapterに変更する。
development: adapter: async ↓ development: adapter: redis url: redis://localhost:6379
Gemfileにredisを追加して./bin/bundle install
gem 'redis'
macにredis-serverがインストースされていれば redis-server で起動する。されてなければ「brew install redis」等でインストールする。再度、rails consoleからメッセージを送信すると今度はメッセージが受信できることが確認できた。

ここまででサーバー側の実装は完了とする。
Swiftでクライアントを実装する
GitHub - tidwall/SwiftWebSocket: Fast Websockets in Swift for iOS and OSX
シンプルに試せそうだったのこちらを使う。READMEに書かれているとおり、Carthageでインストールする。Carthageでのライブラリのインストールについてはこちらを参考に。
ViewController.swift
import UIKit
import SwiftWebSocket
class ViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
func initializeWebSocket() {
let request = URLRequest(url: URL(string: "ws://192.168.1.109:3000/cable")!)
let ws = WebSocket(request: request)
ws.event.open = {
// 接続に成功したらsubscribe発行
let json = """
{"command":"subscribe","identifier":"{\\"channel\\":\\"ChatChannel\\"}"}
"""
ws.send(json)
}
ws.event.close = {_,_,_ in
// 接続が切れたら再接続を
DispatchQueue.main.asyncAfter(deadline: .now() + 10) {
initializeWebSocket()
}
}
ws.event.message = {(response) in
// メッセージ受信
dump(response)
}
}
// 接続
initializeWebSocket()
}
}ほぼドキュメントに書かれている内容だが上記のようなプログラムを実行するとコンソールから動作が確認できた。ほとんどrails側の作業だった。。。チャンネルとストリーミングのところはあまり理解できてないのでチャットルームなり作って理解を深めたいと思う。とりあえず触りはこんなものかな。以上です。