しめ鯖日記

swift, iPhoneアプリ開発, ruby on rails等のTipsや入門記事書いてます

gRPCをSwiftで使ってみる

gRPCを使ってgoのサーバーにSwiftでアクセスする処理を実装しました。
Swiftは5.2、Go言語はgo1.14.4、protobufは3.12.3を使っています。

gRPCとは

gRPCとはGoogleが開発したRPCを実現するためのプロトコルのことです。
RPCはremote procedure callの略で、別のサーバーのメソッドなどを呼び出すことができる技術です。

要するにgRPCを使えば今までRESTなどでAPI通信した箇所を置き換えることができます。

今回はSwiftからgRPCを使ってサーバーにアクセスしてみたいと思います。

Go言語でgRPCサーバーを作る

サーバー立ち上げは下サイトを参考にさせて頂きました。

https://tech.smartcamp.co.jp/entry/2019/03/28/175137

Go言語とprotobufが入っていない場合はbrewでインストールします。
protobufは

brew install go
brew install protobuf

下コマンドで必要なライブラリをインストールします。

go get -u google.golang.org/grpc
go get -u github.com/golang/protobuf/protoc-gen-go

データ構造を示すprotoファイルを作成します。
pingerというフォルダを作ってpinger.protoというファイルを作って下のように記述します。

syntax = "proto3";

package pinger;

service Pinger {
  rpc Ping(Empty) returns (Pong) {}
}

message Empty {}

message Pong {
  string text = 1;
}

protoファイルを作ったら、下コマンドでgo言語のファイルを生成します。

protoc ./pinger/pinger.proto --go_out=plugins=grpc:.

もし下エラーになった場合、goのライブラリへのパスが足りていない可能性があります。
PATH="${PATH}:${HOME}/go/bin"でパスを追加すると解消することがあります。

protoc-gen-go: program not found or is not executable
Please specify a program using absolute path or make sure the program is available in your PATH system variable
--go_out: protoc-gen-go: Plugin failed with status code 1.

protocコマンドがうまく動けばpingerフォルダにpinger.pb.goというファイルが生成されます。

f:id:llcc:20200621212321p:plain

最後にserverを作成します。
カレントディレクトリにserver.goというファイルを作って下のように記述します。

package main

import (
    "context"
    "log"
    "net"

    "./pinger"
    "google.golang.org/grpc"
)

func main() {
    listener, err := net.Listen("tcp", ":5300")
    if err != nil {
        log.Fatalf("failed to listen: %v\n", err)
        return
    }
    grpcSrv := grpc.NewServer()
    pinger.RegisterPingerServer(grpcSrv, &server{})
    log.Printf("Pinger service is running!")
    grpcSrv.Serve(listener)
}

type server struct{}

func (s *server) Ping(ctx context.Context, req *pinger.Empty) (*pinger.Pong, error) {
    pong := &pinger.Pong{
        Text: "pong",
    }
    return pong, nil
}

ファイルを作ってから下コマンドを実行するとサーバーが起動します。

go run server.go

起動したら動作確認をします。
今いるディレクトリに下のようなcommand.goを作成します。

package main

import (
    "context"
    "fmt"
    "os"

    "./pinger"
    "google.golang.org/grpc"
)

func main() {
    conn, err := grpc.Dial("localhost:5300", grpc.WithInsecure())
    if err != nil {
        fmt.Fprintf(os.Stderr, "grpc.Dial: %v\n", err)
        return
    }
    defer conn.Close()
    client := pinger.NewPingerClient(conn)
    req := &pinger.Empty{}
    pong, err := client.Ping(context.Background(), req)
    if err != nil {
        fmt.Fprintf(os.Stderr, "Ping: %v\n", err)
        return
    }
    fmt.Fprintf(os.Stdout, "Pong: %s\n", pong.Text)
}

下コマンドを実行してPong: pongという表示になれば問題なく動いています。

go run command.go

Swiftからサーバーのメソッドを呼び出す

次はSwiftでクライアントを作ります。
適当なプロジェクトを作成したらCocoaPodsでgRPC-Swiftをインストールします。

target 'MyApp' do
  use_frameworks!

  pod 'gRPC-Swift', '1.0.0-alpha.12'
end

次は下コマンドでprotocのswiftプラグインをインストールします。

git clone https://github.com/grpc/grpc-swift.git
cd grpc-swift
make plugins

生成したプラグインはパスの通っている場所に移動します。
今回は一旦試したいだけなのでgrpc-swiftのパスを追加するだけにしました。

PATH="${PATH}:${HOME}/XXXX/grpc-swift"

ここまで終わったらクライアント用のファイルを生成します。

protoc pinger/pinger.proto --swift_out=. --grpc-swift_out=.

pinger.pb.swiftpinger.grpc.swiftというファイルが作られるので、これをプロジェクトに追加します。

追加したら実際に通信を行います。
AppDelegateを下のように修正します。

import UIKit
import GRPC
import NIO

@UIApplicationMain
class AppDelegate: UIResponder, UIApplicationDelegate {
    func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
        let group = MultiThreadedEventLoopGroup(numberOfThreads: 1)
        let channel = ClientConnection.insecure(group: group).connect(host: "localhost", port: 5300)
        let client = Pinger_PingerClient(channel: channel)
        let request = Pinger_Empty()
        let call = client.ping(request)
        print(try! call.response.wait())
        
        return true
    }
}

実行すると下のようにレスポンスが返る事がわかります。

f:id:llcc:20200628171237p:plain

参考URL

遠隔手続き呼出し - Wikipedia

iOSエンジニアの為のgrpc-swift入門 - Speaker Deck