ElectronアプリをカスタムURIで起動する
はじめに
webブラウザからリンクをクリックして、KindleやiTunesを起動したことありますよね。
このときリンク先アドレスは以下のようになっています。
カスタムURIですね。
自分で作ったElectronアプリもカスタムURIで起動したいと思います。 完全なソースはGitHubにあります。
こんな感じで動きます。
カスタムURIの登録
MacでのカスタムURIの登録
Macの場合は、アプリのinfo.plistにCFBundleURLNameを登録することで、カスタムURIでアプリを起動できるようになります。 https://developer.apple.com/library/mac/documentation/General/Reference/InfoPlistKeyReference/Articles/CoreFoundationKeys.html
具体的にElectronアプリにどう組み込むかは簡単で、electron-packagerのオプション(protocol-name, protocol)にカスタムURIを指定するだけです。
// package.json { ... "scripts": { ... "pack": "npm run pack:osx && npm run pack:win32 && npm run pack:win64", "pack:osx": "electron-packager ./dist \"MyApp\" --out=dist/osx --platform=darwin --arch=x64 --version=0.36.8 --protocol-name=\"myapp-protocol\" --protocol=\"myapp\"", "pack:win32": "electron-packager ./dist \"MyApp\" --out=dist/win32 --platform=win32 --arch=ia32 --version=0.36.8, "pack:win64": "electron-packager ./dist \"MyApp\" --out=dist/win64 --platform=win32 --arch=x64 --version=0.36.8, ... }, ...
パッケージ後の出力は以下のようになります。
// MyApp.app/Contents/info.plist <?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd"> <plist version="1.0"> <dict> ... <key>CFBundleURLTypes</key> <array> <dict> <key>CFBundleURLName</key> <string>myapp-protocol</string> <key>CFBundleURLSchemes</key> <array> <string>myapp</string> </array> </dict> </array> </dict> </plist>
WindowsでのカスタムURIの登録
Windowsの場合はレジストリにカスタムURIを登録する必要があります。 https://msdn.microsoft.com/ja-jp/library/aa767914(v=vs.85).aspx
レジストリ登録(削除)のタイミングは以下がいいですね。
インストールスクリプト作成
まずNSISでインストールスクリプトを作成します。 テンプレートはhttps://github.com/loopline-systems/electron-builder/blob/master/templates/win/installer.nsi.tpl にあるので、カスタマイズしましょう。
// installer.nsi.tpl !define APP_NAME "<%= name %>" ... # default section start Section SetShellVarContext all ... DetailPrint "Register MyApp URI Handler" DeleteRegKey HKCR "myapp" WriteRegStr HKCR "myapp" "" "URL:myapp" WriteRegStr HKCR "myapp" "URL Protocol" "" WriteRegStr HKCR "myapp\DefaultIcon" "" "$INSTDIR\${APP_NAME}.exe" WriteRegStr HKCR "myapp\shell" "" "" WriteRegStr HKCR "myapp\shell\Open" "" "" WriteRegStr HKCR "myapp\shell\Open\command" "" "$INSTDIR\${APP_NAME}.exe %1" ... SectionEnd # create a section to define what the uninstaller does Section "Uninstall" ... DetailPrint "delete MyApp URI Handler" DeleteRegKey HKCR "myapp" SectionEnd
"myapp\shell\Open\command"の行を見てもらうとわかると思いますが、 カスタムURIで起動した場合、アプリには起動引数としてURIを渡します。
electron-builderの設定
次にelectron-builderのconfigファイルを用意します。nsiTemplateに先ほど作成したinstaller.nsi.tplを指定します。
// builder.json { "osx" : { ... }, "win" : { "title" : "MyApp", "icon" : "resources/win/icon.ico", "nsiTemplate" : "resources/win/installer.nsi.tpl" }
あとはelectron-builder実行時にbuilder.jsonを指定するだけです。
// package.json { ... "scripts": { ... "installer": "npm run installer:osx && npm run installer:win32 && npm run installer:win64", "installer:osx": "electron-builder \"dist/osx/MyApp-darwin-x64/MyApp.app\" --platform=osx --out=\"dist/osx\" --config=builder.json", "installer:win32": "electron-builder \"dist/win32/MyApp-win32-ia32\" --platform=win --out=\"dist/win32\" --config=builder.json", "installer:win64": "electron-builder \"dist/win64/MyApp-win32-x64\" --platform=win --out=\"dist/win64\" --config=builder.json", ... }, ...
インストール後のレジストリは以下のようになります。
アプリ側で起動URIをハンドル
起動URIのハンドルの仕方もMacとWindowsで少し違います。
イベントリスナー登録
// main.ts or main.js import {app} from 'electron'; import {ready, handleOpenUri} from './mainWindow'; app.on('window-all-closed', () => { if (process.platform !== 'darwin') { app.quit(); } }); app.on('ready', ready); app.on('will-finish-launching', () => { // For OSX app.on('open-url', (e, url) => { e.preventDefault(); handleOpenUri(url); }) // For Windows process.argv.forEach(arg => { if (/myapp:\/\//.test(arg)) { handleOpenUri(arg); } }) });
ポイントは https://github.com/atom/electron/blob/master/docs/api/app.md#user-content-event-will-finish-launching に記載されている通り、"will-finish-launching"イベントで"open-url"のイベントリスナーを登録することです。
ここまでは特に難しくないですね。
注意すること
ここで少し注意が必要です。
前提
Electron.appのイベントの発生順序は以下となります。(Macの場合)
- will-finish-launching
- open-url
- ready
BrowserWindow(≒画面、Rendererプロセス)のインスタンス作成はreadyイベントで行います。 ちなみにreadyイベントより前ではBrowserWindowのインスタンスを作成できません。
問題
open-urlイベントで起動URIを取得したときに、画面の表示を変えようとMainプロセスからRendererプロセスに通信すると、 BrowserWindowのインスタンスがまだできてないので、大抵ぬるぽで落ちます。
Windowsの場合も、will-finish-launchingイベントの中で同期的に起動URIを取得するので、同じことが起きます。
対策
サンプルでは、"BrowserWindowの準備完了"と"起動URIの取得"をそれぞれPromise化し、Promise.allで同期をとって(両方の処理が完了して)から通信するようにしています。
https://github.com/masahirompp/electron-open-url-sample/blob/master/appsrc/main/main.ts (コードが少し冗長な感じがする)
現実的には、 起動URIを取得したあとに(Mainプロセスで)サーバから必要なデータを取得している間に、 Rendererプロセスの準備ができていることがほとんどだと思います。 なので、どこまでやるかは状況に合わせて決めてください。
その他
AutoUpdaterでレジストリを書き換える方法は検証していません。
まとめ
Electronはwebと親和性の高いフレームワークです。 そのうちwebとシームレスに使えるアプリとか出てくる気がしています(・∀・)
Enjoy Electron!!