Go言語(Gin+Lorca)+News APIで作るデスクトップアプリ

Go言語のWebフレームワークGinと、デスクトップアプリ開発用ライブラリのLorcaを使って簡易的なニュースビューワーを作ってみました。
GitHubにコードをアップしています([https://github.com/wassan128/newsman/](https://github.com/wassan128/newsman/))。

## あらまし
ニュースを見る癖が無く、世間に置いて行かれがちな自分のためにニュースビューワーを作ろうと思ったのがきっかけです。Node.js(Electron)で書こうとも思ったのですが、せっかく最近Go言語を勉強し始めたのでGoで似たようなことができないかと調べていたところ、同じようなことができるものがあることを知ったのでやってみた次第です。

作るに当たって

1. ニュースを何かAPIで取得
2. 簡易的なWebアプリとして起動
3. デスクトップアプリ風ウィンドウに描画する

ということをします。一旦完成とした画面は以下のようになっています。

![newsman.png](https://qiita-image-store.s3.amazonaws.com/0/64713/0eea5638-e1d9-bed4-bdf9-932f1be7c48c.png)


# 環境
* Ubuntu18.04 1 LTS 64bit
* Go言語バージョン `go1.10.4 linux/amd64`

## Gin
Go製のWebフレームワークです([https://github.com/gin-gonic/gin](https://github.com/gin-gonic/gin))。
> Gin is a HTTP web framework written in Go (Golang). It features a Martini-like API with much better performance -- up to 40 times faster. If you need smashing performance, get yourself some Gin. 

と、イケてるDescriptionもさることながら、初学者の私でもある程度直感に従って使えるフレームワークであるように感じたので採用しました(他に良いものがあれば知りたいです)。

## Lorca
Go製のデスクトップアプリ開発用ライブラリです([https://github.com/zserge/lorca](https://github.com/zserge/lorca))。HTML5でUIを書くことができる、軽量なライブラリとなっており、Go言語コード内でJavaScriptコードを呼び出せるなどの特徴があるようです(今回のプログラムではその機能は使っていませんが)。

## 開発①ニュースの取得
今回ニュース取得のAPIにはNews API([https://newsapi.org/](https://newsapi.org/))の[Top headlines](https://newsapi.org/docs/endpoints/top-headlines)を用いました。
News APIにデベロッパー登録するとAPIキーがもらえます(手順は割愛します)。リクエストは
`https://newsapi.org/v2/top-headlines?country=jp&apiKey=XXX`
に飛ばします。今回は日本のニュースがほしいので`country`は`jp`としています。

APIからはJSONが返されるので、これをGo言語側で受け取ります。Top headlinesのサンプルレスポンスを見るとNews APIのJSONは

```JSON
{
    "status": "ok",
    "totalResults": 36,
    "articles": [
    {
        "source": {
             "id": "cnn",
             "name": "CNN"
        },
        "author": null,
        "title": "Mueller starts to piece together Russia puzzle in most significant move yet - CNN",
        "description": null,
        "url": "https://www.cnn.com/2018/11/30/politics/donald-trump-michael-cohen-robert-mueller/index.html",
        "urlToImage": null,
        "publishedAt": "2018-11-30T06:53:00Z",
        "content": null
     }, ...
```
という構造になっているので、これをそのまま

```Go
type Source struct {
	Id   string `json:"id"`
	Name string `json:"name"`
}
type Article struct {
	Source      Source `json:"source"`
	Author      string `json:"author"`
	Title       string `json:"title"`
	Description string `json:"description"`
	Url         string `json:"url"`
	UrlToImage  string `json:"urlToImage"`
	PublishedAt string `json:"publishedAt"`
	Content     string `json:"content"`
}
type News struct {
	Status       string    `json:"status"`
	TotalResults int       `json:"totalResults"`
	Articles     []Article `json:"articles"`
}
```
と構造体に落とし込みます。これで

```Go
// ニュースをHTTPリクエストし
res, err := http.Get(EndPoint)
if err != nil {
	fmt.Println("json load error")
}
defer res.Body.Close()

// レスポンスのBodyからJSON形式のニュースを取り出し
var news News
data, _ := ioutil.ReadAll(res.Body)
// JSONをデコードしてNews型変数にデータを入れる
if err := json.Unmarshal(data, &news); err != nil {
	fmt.Println(err)
}
```
とすると変数`news`にニュースの情報を格納することができます。このJSONと構造体の対応付けなどはGo言語ならでは感があるなと思いました(初学者並感)。

## 開発②Webアプリ化
「/」にアクセスするとニュースのタイトルが出るような実装にしたいので、

```Go
func server(news News) {
	router := gin.Default()
	// cssや画像は「static」ディレクトリ配下に設置することとする
	router.Static("/static", filepath.Join(os.Getenv("GOPATH"), repo_path, "static"))
	// テンプレートファイルは「templates」ディレクトリ配下に設置することとする
	router.LoadHTMLGlob(filepath.Join(os.Getenv("GOPATH"), repo_path, "templates/*"))
	router.GET("/", func(c *gin.Context) {
		c.HTML(http.StatusOK, "index.tmpl", gin.H{
			"articles": news.Articles,
		})
	})
	go router.Run()
}
```
というような関数を用意します。`router.Run()`でポートを指定できますが今回は省略しているので、デフォルトの「`:8080`」になります。
`index.tmpl`というテンプレートファイルに`articles`という名前でニュースの記事データを渡しています。ここで渡した値はテンプレートファイル内で

```html
{{range .article}}
    <p>{{.Title}}</p>
{{end}}
```
というように参照できます。

`router.Run()`先頭に`go`をつけてgoroutineにしているのは次ステップのLorcaでの画面描画をブロックしないためです。

## 開発③デスクトップアプリにする
ここまででニュースを取得して表示するWebアプリができているので、あとはこれをLorcaの力でデスクトップアプリにするだけです。

```Go
var ui lorca.UI
// 幅320、高さ480のウィンドウを生成
ui, _ = lorca.New("", "", 320, 480)
defer ui.Close()

// Ginで動かしたWebアプリのURLを指定
ui.Load("http://localhost:8080")
<-ui.Done()
```

とても簡単にできました。

プログラム実行の時系列としては「ニュースの取得」→「サーバー起動」→「デスクトップアプリにする」なので、ループに入るサーバー起動部を非同期処理にすることでサーバーを裏で動かしつつそこでホスティングされているコンテンツを描画しています。

## まとめ
思っていたよりも簡単にデスクトップアプリ化することができました。まだまだ改良の余地もありそうですし、これからもちょっとずつ機能追加や他のアプリも作ってみたいです。

Go言語を学び始めて日が浅く、間違いやここはもっとこうした方が良いよなどあればぜひアドバイスいただきたいので、よろしくお願いします。

## 参考にさせていただいた記事
* [Electronより軽くて手軽なlorca製デスクトップガワアプリのススメ](https://qiita.com/shwld/items/a0795586bc3b9e30a540)
* [Gin(Golang)におけるHTMLテンプレート記述方法](https://qiita.com/lanevok/items/dbf591a3916070fcba0d)