TauriでRailsをラップする

4 min read

Image

最近はTauriを使ってデスクトップアプリケーションを作るようになった。

プログラミングは楽しい。

直近のプロジェクトではRails + Go + Rustを組み合わせて、できるだけRailsから離れるような構成を試していた。いわゆる「脱Rails」だ。

けれど、結局またRailsに戻ってきている。

1ヶ月近くBunとTypeScriptばかり書いていたので、少し不安もあったが、それは杞憂だった。むしろRailsは相変わらず快適だった。

この時代、AIにプロンプトを投げれば目的のコードの6割くらいはすぐに出てくる。効率だけを考えれば、それを前提に量産していくのが正しいのだと思う。

ただ、自分はどうしてもそれで終われない。

AIに対して「こうじゃない」「ここは違う」と言い続けて、最終的に自分の思う100%に近づけたくなる。ブレインストーミングの相手としては本当に優秀で、設計の相談にもいくらでも付き合ってくれる。

その代わり、時間はかかる。

先月、モデルを軽くしてコードを書いていたときは、正直かなりつらかった。理解が噛み合わず、何度もやり直すことになり、頭が痛くなることもあった。

今は再び上位モデルに戻して、ようやく落ち着いている。


ただ、Railsで書いている限り、基本的には出力先はブラウザになる。

React Nativeを試したこともあったが、この前提はあまり変わらなかった。iOSやAndroidのことを考え始めると、結局どこかで止まってしまう。

Webサーバーはいくらでも作れるのに、「デスクトップを拡張する」という方向にはあまり進めていない。

ゲームを作っていた時期もあったが、それはそれで閉じた世界の話だった。もう少し実用的なものを作りたいという気持ちがある。


そんな中で、最近触っていたTauriからひとつ発想が出てきた。

普通、Tauriを使うときはViteやReact、Svelteなどを組み合わせる。フロントエンドをしっかり作り込んで、それをネイティブアプリとして配布する。

でも、別にそこまでしなくてもいいのではないかと思った。

Tauriを「UIを描画するためのフレームワーク」としてではなく、「WebViewとOS機能の橋渡し」として使う。

つまり、RailsはそのままWebアプリとして動かしつつ、Tauriはそれを表示しながら通知やOS連携だけを担当させる。

ブラウザのWeb Notificationではなく、OSの通知を使う。そのためのラッパーとしてTauriを使う、という発想だ。1

しかもTauriは src-tauri というディレクトリで管理できるので、既存のRailsアプリケーションにもそのまま載せやすい。

WebViewであればTurboもそのまま使えるし、CORSの設定も不要になる。

ほとんど摩擦なくTauriを取り込めたのが、今回の一番の発見だった。


試しにこんなコードを書いてみた。

import { Controller } from "@hotwired/stimulus"

export default class extends Controller {
  send() {
    if (!window.__TAURI__) {
      alert("Tauri環境ではありません")
      return
    }

    const {
      isPermissionGranted,
      requestPermission,
      sendNotification
    } = window.__TAURI__.notification

    isPermissionGranted().then((granted) => {
      if (granted) {
        sendNotification({ title: "My App", body: "Hello World!" })
      } else {
        requestPermission().then((permission) => {
          if (permission === "granted") {
            sendNotification({ title: "My App", body: "Hello World!" })
          }
        })
      }
    })
  }
}

これで普通に通知が出るはずだった。


しかし、実際に動かしてみると、音だけが鳴って通知は表示されなかった。

調べてみると、これは既知の問題だった。

tauri-apps/plugins-workspace[bug] no notifications in gnome 46+ (eg fedora 41 and ubuntu 24.04)Issue #2566 · Opengiovannifranco1

GNOME 46以降では通知の扱いが変わっており、Tauriの通知が表示されないケースがあるらしい。

.desktop ファイルを用意したり、identifierを揃えたりといったことも試したが、状況は変わらなかった。

DBusには通知自体は届いているようなので、TauriというよりはGNOME側の仕様に引っかかっているように見える。


#[tauri::command]
fn send_notification(title: String, body: String) {
    let identifier = "com.tauri.dev";

    let _ = std::process::Command::new("notify-send")
        .arg(format!("--app-name={}", identifier))
        .arg(format!("--hint=string:desktop-entry:{}", identifier))
        .arg(&title)
        .arg(&body)
        .spawn();
}

最終的には、Rust側から notify-send を呼ぶ形に落ち着いた。

少し拍子抜けではあるが、少なくとも目的は達成できた。


出だしからつまずいた形にはなったが、この方向性自体は悪くないと感じている。

以前書いた「デスクトップは重要」という話でやりたかったことに、少し近づいた気がする。

Webアプリケーションはもう十分に書ける。だからこそ、その外側にどう広げるかをもう少し考えてみたい。