Skip to content

Desktop App 開発

デスクトップアプリ開発において情報をまとめているページです。

各 OS ごとの開発方法は下記のページを参照してください。

関連リンク

各ディレクトリ詳細

root ディレクトリ

tailwind や TypeScript、webpack などの設定ファイルをまとめています。

Webpack の設定については、Electron Forge の Webpack Plugin に乗っかる形で実装しています。下記記事に詳細があります。

env ディレクトリ

各種 .env ファイルをまとめています。

  • .env.development : ローカルでの開発時に利用する環境変数をまとめています
  • .env.staging : ステージング環境での開発時に利用する環境変数をまとめています
  • .env.production : 本番環境での開発時に利用する環境変数をまとめています
  • .env.build : ビルド時に利用する環境変数をまとめています

各ファイルに格納する値は、以降のドキュメントで詳細に説明します。

src/@types

デスクトップアプリの TypeScript の型定義ファイルをまとめています。

特記事項として、src/@types/renderer.d.ts 内において、下記のように Window オブジェクトを拡張する形で型定義を追加しています。 これは、src/renderer 内の TypeScript ファイルで、window.coreAPIwindow.logger といったオブジェクトを参照するために必要です。

typescript
// src/@types/renderer.d.ts
// ...
declare global {
  interface Window {
    coreAPI: ICoreAPI;
    logger: ILoggerAPI;
  }
}
// ...

src/api

renderer プロセス側に公開する API を定義しています。

例:

ts
import { ipcRenderer } from "electron";
import { channelNameMap } from "@/api/constant";

export const coreAPI: ICoreAPI = {
  showVideo: () => ipcRenderer.send(channelNameMap.showVideo),
  recordVideo: (...data: any) =>
    ipcRenderer.send(channelNameMap.recordVideo, ...data),
  startRecording: (...data: any) =>
    ipcRenderer.send(channelNameMap.recording.start, ...data),
  stopRecording: (...data: any) =>
    ipcRenderer.send(channelNameMap.recording.stop, ...data),
};

export const loggerAPI: ILoggerAPI = {
  info: (...params) => ipcRenderer.send(channelNameMap.info, params),
};

src/assets

デスクトップアプリのアイコンなど、静的なファイルをまとめています。

src/common

基本的なユーティリティ関数をまとめています。 独自に定義した Error クラスや、独自に実装したユーティリティ関数を含みます。

src/libs

src/libs は外部ライブラリをラップした関数をまとめたディレクトリであり、基本的には外部ライブラリをそのまま export する形で利用することを想定しています。

💡 src/libs 層を作成している背景:

外部ライブラリをラップした関数を作成することで、外部ライブラリのバージョンアップや変更による影響を最小限に抑えることができます。

src/main

Main プロセスで動かすロジックのソースコードをまとめています。 基本的にはサービスごとにディレクトリを作成し、その中にロジックをまとめています。

2023年7月12日現在、下記のような構成になっています。

text
tree main -L 3
main
├── index.ts
└── services
    ├── apiClient
    │   ├── apiClient.ts
    │   ├── index.ts
    │   └── types.ts
    ├── recordSubscriber
    │   ├── ebmlHandler.ts
    │   ├── index.ts
    │   ├── mpdBuilder.ts
    │   ├── queue
    │   ├── recordSubscriber.ts
    │   ├── recordingChapters.ts
    │   └── types.ts
    ├── recorder
    │   └── recorder.ts
    ├── remoteStorage
    │   ├── index.ts
    │   ├── remoteStorage.ts
    │   └── types.ts
    └── taskManager
        ├── index.ts
        └── taskManager.ts

8 directories, 16 files

src/renderer

Renderer プロセスで動かすロジックのソースコードをまとめています。 基本的には React プロジェクトのような様相を呈しています。

2023年7月12日現在、下記のような構成になっています。

text
tree renderer/ -L 3
renderer/
├── components
│   ├── model
│   │   └── frontFacingCamera
│   ├── page
│   └── ui
│       ├── button
│       └── icons
├── globalState
├── hooks
├── models
├── pages
│   ├── front-facing-camera-bubble.tsx
│   └── main.tsx
├── services
│   └── recorder
│       ├── customMediaRecorder
│       └── recordPublisher
├── styles
│   └── global.css
└── usecases

18 directories, 3 files

ディレクトリの分け方は下記のような記事を参考にしつつ、それぞれに役割を持たせて分割しています (TODO: ここに関しては別途 wiki を書きたい)

src/window

このディレクトリでは、Quden の Desktop App で利用する window を定義しています。

ここでいう Window とは、アプリケーションにおける window のことを指します。 例えば、

2023年7月12日現在、以下のように構成することを想定しています。(が、変更の可能性があります)

  • Main
    • 拡張機能で右上に表示されるような、カメラ・マイクなどのメディアの設定や、保存先ワークスペースを設定するためのウィンドウ
  • Control Menu
    • 録画に関する基本操作(停止、再開など)を行うコントロールメニューを持つウィンドウ
  • Base Overlay
    • 画面全体を暗黙的にカバーしたオーバーレイ層
    • クリックの検知や、カーソルエフェクトの実装に利用する想定
  • (Front Facing Camera Bubble)
    • ビデオメッセージを実装する際に利用します
    • 先にステップガイドを実装する想定のため、今のところ作成予定はの目処は不明です

実装の詳細

ログイン機能の実装について

ログインフローは、BrowserWindow を用いて、ブラウザ上でログインを行う形で実装しています。

詳しくは下記の issue を参照してください。

開発の進め方

Quden Desktop app を支える技術スタック

Quden のデスクトップアプリは、下記のようなツール群で構成されています。

  • Electron
    • Electron は、Web 技術を用いてデスクトップアプリを開発するためのフレームワークです
  • Electron forge
    • Electron forge は、Electron アプリの開発を支援するためのツールです
    • electron-forge コマンドを利用することで、Electron アプリの開発が圧倒的に楽になります
  • React
    • メニュやコントロールメニューなど、フロントエンドの実装に React を利用しています

ビルドライフサイクル

Electron のビルドに置いては、下記のようなライフサイクルがあります。

  • Package
    • パッケージングを行います
    • パッケージングとは、Electron アプリを実行可能な形に変換することを指します
    • 例えば、macOS であれば .app ファイルに変換することを指します
  • Make
    • パッケージングされたアプリを配布可能な形に変換します
    • 例えば、macOS であれば .dmg ファイルに変換することを指します
  • Publish
    • 配布可能な形に変換されたアプリを配布します
    • 例えば、macOS であれば、配布可能な形に変換された .dmg ファイルを GitHub のリリースページにアップロードすることを指します

ライフサイクルは下記の画像、およびドキュメントがわかりやすいです。

Build flow

Platform, Architecture とは

  • Platform
    • macOS, Windows, Linux などの OS のことを指します
    • macOS は darwin という名前で表現されます
    • Windows は win32 という名前で表現されます
    • Linux は linux という名前で表現されます
  • Architecture
    • x64, arm64 などの CPU のことを指します
    • Intel mac は x64, M1/M2 mac は arm64 というアーキテクチャ名を持っています

Native module について

Quden の Desktop app では、クリックした位置の座標の取得や、クリック先の情報(アプリ名など)を取得するために、Node の addons の仕組みを利用しています。

具体的は、下記のファイルで実装しています。

  • src/main/addons/get-ui-element-info
    • アプリ名を取得するためのネイティブモジュール
  • src/main/addons/iohook
    • クリック位置を取得するためのネイティブモジュール
    • iohook というモジュールをコピーして利用している

参考:

アプリの配布

アプリを配布するにあたってやることは、大きく下記のとおりです。

  • package.json の Version を更新する
  • zipunk/quden-desktop-app の Releases で、新しい Release を作成する
  • 作成した Release に対して、アーティファクトをアップロードする
  • アプリをパッケージ化、および署名して配布可能な形に変換する
    • 署名について、macOS は make 段階で署名され、Windows は make 後に別途 signtool コマンドを実行することで署名できます
  • publish コマンドを実行し zipunk/quden-desktop-app-update-server の Releases に draft release を作成する
    • macOS と Windows 機でそれぞれ publish コマンドを実行する必要があります
    • Version number が同じであれば、同じ draft release に紐付いてアーティファクトがアップロードされます
  • zipunk/quden-desktop-app-update-server の Releases で、draft release を latest に指定して publish する

macOS と Windows の配布については、それぞれのドキュメントを参照してください。

Version の更新

Publish されるアプリのバージョンは package.json 内の version に依存します。

下記手順に従ってバージョンの更新を行ってください。

1: yarn version コマンドを実行する

下記コマンドを実行することで、次の2が実行されます:

  • package.json の version が更新される
  • Git tag が作成される
    • git tag で確認できます
sh
# prerelease バージョンを更新する場合
yarn version --prerelease
# ex: 0.0.1-beta.0 → 0.0.1-beta.1

# patch バージョンを更新する場合
yarn version --patch
# ex: 0.0.1 → 0.0.2

# major, minor バージョンを更新する場合も同様にです。詳しくはドキュメントを参照してください。

2: git push する

sh
# 通常の push です
git push origin <branch_name>

# tag の push です
git push origin <tag_name>

3: すべての変更を main ブランチにマージし、新たに作成した tag で Release を作成する

Version 毎の変更内容を管理しやすくするため、zipunk/quden-desktop-app の Releases に新しく Release を作成してください。

実際の例はこちら

アプリ配布の仕組み

アプリの配布において重要なコンポーネントは以下の2つです。

  • アプリの version を管理する update server
    • レポジトリ: zipunk/quden-desktop-app-update-server
    • Vercel が公開している Hazel をベースにしています
    • Release で公開されたアプリのバージョン情報、およびパッケージされたファイルを管理しています
  • Electron app
    • src/main/index.ts 内で update server に対して定期的に情報を fetch しにリクエストを送っています
    • 新しいバージョンが公開されていれば、ダイアログを表示し、更新を実行することができます

参考の読み物