SwiftとNode.jsを用いた WebSocket通信

Swift

こんにちは。新人プログラマーの岩本です。

今回はNode.jsとSwiftを使って、WebSocket通信を行いたいと思います。

本記事ではWebSocketの基本から、応用として簡易的なチャットアプリを作ります。

ぜひ最後までご覧ください。

この記事は新卒エンジニアが執筆しています。
そのため内容に間違いや不備がある場合があります。
もし間違いを発見しましたら、どんどん指摘していただけると幸いです。

WebSocket通信とは

WebSocket通信とは、サーバとクライアントの相互通信を低コストで行うためのプロトコルです。

HTTP通信との違いは、サーバとクライアントのどちらからでも通信を始めることが可能なことです。

HTTPでは、クライアントしかリクエストを送ることができません。なのでSNSなどリアルタイム性を求められるアプリの場合、クライアントから高頻度でリクエストを送る必要があります。

ですが、データの更新がない場合もリクエストが送られるので、サーバの負担になってしまいます。

そこでWebSocket通信が登場しました。WebSocketではどちらからでも通信を開始することができるので、リアルタイムな動作が可能になりました。

Node.jsとSwiftでWebSocket通信を行う

ではここから実際にWebSocket通信を行なっていましょう。

今回サーバーはNode.js、クライアントはSwift(iOSアプリ)で実装します。

またライブラリは「SocketIO」を使用します。

Node.jsの実装

Node.jsをインストールしていることが前提です。

まず適当なフォルダを作り、SocketIOをインストールします。

Bash
npm install socket.io

次にapp.jsを作成し、以下のコードを記述します。

app.js
var http = require("http");
var server = http.createServer((req, res) => {
    res.write("Create server!!");
    res.end();
});

// socket.ioの準備
var io = require('socket.io')(server);

// クライアント接続時の処理(io.on)
io.on('connection', (socket) => {
    console.log("Client connected!!");

    // クライアント切断時の処理
    socket.on('disconnect', () => {
        console.log("Client disconnected!!");
    });

    // クライアントからの受信を受ける
    socket.on("from_client", (obj) => {
        console.log(obj);
    });
});

// 一定間隔でサーバ時刻を全クライアントに送る(io.emit)
var send_servertime = () => {
    var now = new Date();
    io.emit("from_server", now.toLocaleString());
    console.log(now.toLocaleString());
    setTimeout(send_servertime, 1000); // 1秒ごとにsend_servertimeを実行する
};
send_servertime();

server.listen(8080);

最後に以下のコマンドでサーバを起動し、毎秒ログが表示されていればOKです。

Bash
node app.js

iOSアプリの実装

まずSPMで、SocketIO(https://github.com/socketio/socket.io-client-swift)をインストールします。

次にContentView.swiftに以下のコードを記述します。

ContentView.swift
import SwiftUI
import SocketIO

struct ContentView: View {
    private let manager = SocketManager(socketURL: URL(string: "http://127.0.0.1:8080/")!)
    private var socket: SocketIOClient!
    
    @State private var messages: [String] = []
    
    init() {
        socket = manager.defaultSocket
    }
    
    var body: some View {
        VStack {
            List {
                ForEach(messages, id: \\.self) { message in
                    Text(message)
                }
            }
            .listStyle(.plain)
            
            Button {
                socket.emit("from_client", "button_tapped!") // イベントの送信
            } label: {
                Rectangle()
                    .frame(width: 150, height: 50)
                    .foregroundStyle(.blue)
                    .overlay {
                        Text("From Client")
                            .foregroundStyle(.white)
                    }
            }
        }
        .padding()
        .onAppear {
            socket.on(clientEvent: .connect) { data, ack in
                print("socket connected!")
            }
            
            socket.on(clientEvent: .disconnect) { data, ack in
                print("socket disconnected")
            }
            
            socket.on("from_server") { data, ack in // イベントの受信
                if let message = data as? [String] {
                    messages.append(message[0])
                }
            }
            
            socket.connect() // WebSocket通信開始
        }
        .onDisappear {
            socket.disconnect() // 通信を切断
        }
    }
}

アプリを立ち上げ、毎秒ログ情報が取得できていればOKです。

【ステップアップ】簡易的なチャットアプリを作る

ではここから学んだWebSocketの知識を使って、簡易的なチャットアプリを作成していきます。

Node.jsの実装

先ほど使ったフォルダの中にwebsocket_server_chat.jsを作成し、以下のコードを記述します。

websocket_server_chat.js
var http = require("http");
var server = http.createServer((req, res) => {
    res.write("server起動!");
    res.end();
});

var io = require("socket.io")(server);
var newMessages = []; // 新たなメッセージを格納する配列

io.on('connection', (socket) => {
    console.log("Client connected!!");

    // クライアント切断時の処理
    socket.on('disconnect', function() {
        console.log("Client disconnected!!");
    });

    // クライアントからの受信を受ける
    socket.on("from_client", (message) => {
        console.log(message);
        newMessages.push(message);
        send_new_messages();
    });
});

// 新たなメッセージを全クライアントに送る(io.emit)
var send_new_messages = () => {
    io.emit("from_server", newMessages);
    newMessages = [];
};

server.listen(8080);

新たなメッセージを受信したタイミングで、メッセージをクライアントに送る処理を追加しました。

iOSアプリの実装

ChatView.swiftを作成し、以下のコードを記述します。

ContentView.swift
struct ChatView: View {
    private let manager = SocketManager(socketURL: URL(string: "http://127.0.0.1:8080/")!)
    private var socket: SocketIOClient!
    
    @State private var messages: [String] = []
    @State private var inputMessage = ""
    
    init() {
        socket = manager.defaultSocket
    }
    
    var body: some View {
        VStack {
            LazyVStack {
                ScrollView {
                    ForEach(messages, id: \\.self) { message in
                        Text(message)
                            .padding()
                    }
                }
            }
            .frame(maxWidth: .infinity, maxHeight: .infinity)
            
            HStack {
                TextField("", text: $inputMessage)
                    .padding()
                    .textFieldStyle(.roundedBorder)
                
                Button {
                    socket.emit("from_client", inputMessage)
                    inputMessage = ""
                } label: {
                    Text("送信")
                        .padding()
                        .background(Color.blue)
                        .foregroundStyle(.white)
                }
            }
        }
        .onAppear {
            socket.on(clientEvent: .connect) { data, ack in
                print("socket connected!")
            }
            
            socket.on(clientEvent: .disconnect) { data, ack in
                print("socket disconnected")
            }
            
            socket.on("from_server") { data, ack in
                if let newMessages = data as? [[String]] {
                    newMessages
                        .flatMap { $0 }
                        .forEach { messages.append($0) }
                }
            }
            
            socket.connect()
        }
    }
}

成功すると、チャットでやり取りできるようになります。

まとめ

今回はNode.jsとSwiftを用いて、WebSocket通信を行う方法を解説しました。

この技術を用いると、チャットアプリやオンラインゲームなど、リアルタイムの処理が必要となるアプリを作成できるようになります。

ぜひご自身の手で色々と試してみてください。

ここまでのご閲覧ありがとうございました!

参考にした記事

【swift】swift+node.jsでWebSocket - Qiita
WebSocketとは...双方向通信を低コストで行うための仕組みのこと平たく言えばチャットとか、常に通信が開いたままの状態でサーバとやりとりするような通信方式の事ws://とwss://とい…
今さら聞けないWebSocket~WebSocketとは~ - Qiita
#WebSocketとはWebにおいて双方向通信を低コストで行うための仕組み。プロトコルの一種。#WebSocketの必要性###Web通信といえばHTTP。主にHTMLで書かれた文書を転…

コメント

タイトルとURLをコピーしました