Goは、apiやwebのバックエンド開発、CLIツールの開発でよく利用されます。Goを利用してDesktopアプリを開発する方法の一つにWailsというフレームワークを利用して開発する方法について、紹介したいと思います。

Wailsとは?

Go言語とWebテクノロジー(HTML/Javascript/CSS)を利用したフレームワークとのことです。フロントエンドには、WebViewを利用することでWebテクノロジーを利用することを可能にしているとのことです。公式サイトは、こちらです。

まず触ってみる

下記コマンドでwailsをインストールします。


$ go get github.com/wailsapp/wails/cmd/wails

プロジェクトの雛形を作成します。


$ wails init

メールアドレス、フロントエンドのフレームワーク等の入力を行うと自動で雛形が生成されます。Vue3にも対応しています。作成されるものは、下記のようなものが作成されます。


$ ls
build/        frontend/     go.mod        go.sum        main.go       project.json

ビルド方法は、下記のコマンドを利用します。


$ wails build

ビルドが完了するとbuildフォルダの配下にバイナリが格納されます。

アーキテクチャーは?

フロントエンド、バックエンドとの通信は、IPC通信を利用しています。下記画像は、Conceptsにあったものを利用しています。

これ以降は、フロントエンドにVueを利用している想定でgo, webフロントとの間でのやりとりの仕組みであるBindingとEventについて簡単に紹介したいと思います。

Binding

GOが提供するインターフェスは、Bindingという機構を使って提供します。提供方法としては、method、struct methodです。先程作成したテンプレートでは、basic()というメソッドがクライントに提供されています。


package main

import (
  "github.com/leaanthony/mewn"
  "github.com/wailsapp/wails"
)

func basic() string {
  return "Hello World!"
}

func main() {

  js := mewn.String("./frontend/dist/app.js")
  css := mewn.String("./frontend/dist/app.css")

  app := wails.CreateApp(&wails.AppConfig{
    Width:  1024,
    Height: 768,
    Title:  "cpu",
    JS:     js,
    CSS:    css,
    Colour: "#131313",
  })
  app.Bind(basic)
  app.Run()
}

利用するフロントエンドでは、下記のようになっています。


import { ref, defineComponent } from "vue";

interface Backend {
  basic(): Promise;
}

declare global {
  interface Window {
    backend: Backend;
  }
}

export default defineComponent({
  name: "Home",
  components: {
    HelloWorld,
  },
  setup() {

    const message = ref("Click the Icon");

    const getMessage = () => {
      window.backend.basic().then(result => {
        message.value = result;
      });
    }

    return { message: message, getMessage: getMessage };
  },
})

Events

こちらは、nodeJSに詳しい方は、EventEmitterという言えばピンとくるかもしれないですね。
イベントを送信する場合には、runtime.Events.Emit()を利用します。下記コードは、1秒ごとにcountアップした内容を”count”というイベントで送信しています。


type Sample struct {
    runtime *wails.Runtime
}

func (p *Sample) WailsInit(runtime *wails.Runtime) error {
      p.runtime = runtime
      count := 0
      go func () {
          for {
              p.runtime.Events.Emit("count", count)
              time.Sleep(time.Second)
              count = count + 1
          }
      }()

      return nil
  }


フロント側でこのメッセージを受け取るには、mount時にイベントを監視します。



 interface Wails {
    Events: {
      Emit(eventName: string, ...optionalData: any[]): void  
     On(eventName: string, callback: (...optionalData: any[]) => void): void
    }
  }
・・・
  declare global {
    interface Window {
      wails: Wails
    }
  }
 ・・・
 onMounted(() => {
        window.wails.Events.On("count", (count: number) => {
          console.log(count)
      })

上の例では、バックエンドがイベントを発行する側としていますが、javascript側からEventsを発行することもできます。

まとめ

駆け足でさっと見てみきましたが、普段golang、vueでアプリケーションを開発している方は、さっと触りだすことができる印象があります。画面遷移などは、vue-routerを使えば、フロントエンドだけ完結します。ビジネスロジック関連の構築は、golangで行い、必要なタイミングで更新用のデータをイベントで発行するようにすればフロントエンドは、シンプルな開発ができそうです。個人的には、意外と使えるかもしれないという印象でした。お疲れ様でした。