electronで作ったアプリをMacAppStoreリリースするまで

少し前にelectronMastodonクライアントを作ったのだが,ようやくそれをMacAppStoreに登録した.

Whalebird

Whalebird

  • Akira Fukushima
  • Social Networking
  • Free

ちゃんと公開されてる.

そいえば前にelectronのリリース用パッケージについても書いた. 今日はその続きということで,MacAppStoreへの公開までを書く.

証明書を用意する

今回は,通常のMac用ビルドは通ることを前提にしている.なお,もちろんAppleDeveloperProgramへの登録も済んでいることを前提にする.

AppStoreで配布するのだから当然証明書が必要になる.リリースするためには以下の証明書が必要になる.

  • Mac App Distribution
  • Mac Installer Distribution

前回同様,CertificateSigninRequestを作成しよう.

そしたら,developer.apple.comのCertificates, Identifiers & Profilesに行く. で,macOSのCertificatesとして,Mac App DistributionとMac Installer Distributionを作成し,これをダウンロードしておこう

ダウンロードした2つの証明書をダブルクリックし,キーチェーンに登録しよう.

キーチェーンを見た時に,

  • 3rd Party Mac Developer Application
  • 3rd Party Mac Developer Installer

が入っていれば問題ない.

ビルドしてpkgファイルを作る

今回はelectron-packagerを使う.前回,electron-builderを使えばelectron-packagerは今や不要と書いたが,masに関してはelectron-builderで証明書周りをクリアできなかった. もしかしたらもっと上手いやり方があったのかもしれないが,とりあえずドキュメントを読んだだけでは鍵の設定方法が謎い. なによりCodeSignされない気がする.

というわけでelectorn-packagerを使う.

$ npm run pack &&
  electron-packager ./ 'Whalebird for Mastodon'
  --platform=mas
  --arch=x64
  --electron-version=1.8.3
  --asar.unpackDir='build/sounds'
  --out=packages
  --ignore='^/src'
  --ignore='^/test'
  --ignore='^/.electron-vue'
  --ignore='^/.envrc'
  --ignore='^/packages'
  --ignore='^/plist'
  --ignore='^/static'
  --prune=true
  --icon=./build/icons/icon.icns
  --overwrite
  --app-bundle-id=org.whalebird.desktop
  --app-version=$npm_package_config_appVersion
  --build-version=$npm_package_config_buildVersion
  --extend-info='./plist/team.plist'
  --osx-sign
  --app-category-type=public.app-category.social-networking

こんなコマンドになる. 俺は,覚えるのは不可能なので,package.jsonに書いた.

github.com

大事なこと: plist

--extend-info でplistを指定している.ここではアプリに作成者の情報を渡すためにplistを用意している.

plistとしては,

  • team.plist
  • parent.plist
  • child.plist
  • loginhelper.plist

という4つのファイルを作っている. electorn-pcakgerではteam.plistしか使用していない.他のplistについてはCodeSignの段階で使用するので,とりあえずteam.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>ElectronTeamID</key>
    <string>team_id</string>
  </dict>
</plist>

こんなファイルになっている. team_idについては,証明書にも記述されているし,iTunesConnect等でも確認できる.

大事なこと: asar.unpackDir

Whalebirdは音声ファイルを使用しており,音声ファイルだけはasarに圧縮されるとアクセスできなくなってしまう. そのため,electron-builderでもextraResourcesを使ってasar圧縮から除外していた.

electron-packagerではasar.unpackDir というコマンドでこれを実現している.

出来上がったパッケージ内の, Contents/Resources/app.asar.unpacked/ にファイルが置かれるので,ビルド後にこのパスをelectron-builderで作ったものに合わせる必要がある.

github.com

これで今までとプロダクトコードを変更せずに,配布したアプリケーションで音声を再生できるようになる.

CodeSignする

electron-packagerでパッケージを作成しただけでは,AppStoreに配布することはできない. これにcodesignという処理をかけてやる必要がある.

plist

まず,先ほど説明を飛ばしたplistファイルを3つ作る.

  • parent.plist
  • child.plist
  • loginhelper.plist

parent.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>com.apple.security.app-sandbox</key>
    <true/>
    <key>com.apple.security.application-groups</key>
    <string>team_id.org.whalebird.desktop</string>
    <key>com.apple.security.files.user-selected.read-only</key>
    <true/>
    <key>com.apple.security.network.client</key>
    <true/>
  </dict>
</plist>

ここで,アプリに許可する動作を列挙する.

Whalebirdでは画像投稿を許可しているため,files.user-selected.read-only. そしてmastodonサーバと通信するための network.clientを許可している.

項目については,

developer.apple.com

こちらで確認できる.

child.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>com.apple.security.app-sandbox</key>
    <true/>
    <key>com.apple.security.inherit</key>
    <true/>
  </dict>
</plist>

childはparentを継承するので特に権限の設定をここで追加する必要はない.inheritしてあれば十分.

loginhelper.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>com.apple.security.app-sandbox</key>
    <true/>
  </dict>
</plist>

これにつても特別な権限を設定する必要はない.

CodeSign

plistが用意できたら,CodeSignしよう.

# Name of your app.
APP="Whalebird for Mastodon"
# The path of your app to sign.
APP_PATH="./packages/Whalebird for Mastodon-mas-x64/Whalebird for Mastodon.app"
# The path to the location you want to put the signed package.
RESULT_PATH="./packages/$APP.pkg"
# The name of certificates you requested.
APP_KEY="3rd Party Mac Developer Application: name (team_id)"
INSTALLER_KEY="3rd Party Mac Developer Installer: name (team_id)"
# The path of your plist files.
CHILD_PLIST="./plist/child.plist"
PARENT_PLIST="./plist/parent.plist"
LOGINHELPER_PLIST="./plist/loginhelper.plist"

FRAMEWORKS_PATH="$APP_PATH/Contents/Frameworks"

# At first, rename app.asar.unpacked directory.
# Because electron-builder does not store app.asar.unpacked directory.
# I want to store unpacked files at the same directory as electron-builder.
mv $APP_PATH/Contents/Resources/app.asar.unpacked/* $APP_PATH/Contents/Resources/

codesign -s "$APP_KEY" -f --entitlements "$CHILD_PLIST" "$FRAMEWORKS_PATH/Electron Framework.framework/Versions/A/Electron Framework"
codesign -s "$APP_KEY" -f --entitlements "$CHILD_PLIST" "$FRAMEWORKS_PATH/Electron Framework.framework/Versions/A/Libraries/libffmpeg.dylib"
codesign -s "$APP_KEY" -f --entitlements "$CHILD_PLIST" "$FRAMEWORKS_PATH/Electron Framework.framework/Versions/A/Libraries/libnode.dylib"
codesign -s "$APP_KEY" -f --entitlements "$CHILD_PLIST" "$FRAMEWORKS_PATH/Electron Framework.framework"
codesign -s "$APP_KEY" -f --entitlements "$CHILD_PLIST" "$FRAMEWORKS_PATH/$APP Helper.app/Contents/MacOS/$APP Helper"
codesign -s "$APP_KEY" -f --entitlements "$CHILD_PLIST" "$FRAMEWORKS_PATH/$APP Helper.app/"
codesign -s "$APP_KEY" -f --entitlements "$CHILD_PLIST" "$FRAMEWORKS_PATH/$APP Helper EH.app/Contents/MacOS/$APP Helper EH"
codesign -s "$APP_KEY" -f --entitlements "$CHILD_PLIST" "$FRAMEWORKS_PATH/$APP Helper EH.app/"
codesign -s "$APP_KEY" -f --entitlements "$CHILD_PLIST" "$FRAMEWORKS_PATH/$APP Helper NP.app/Contents/MacOS/$APP Helper NP"
codesign -s "$APP_KEY" -f --entitlements "$CHILD_PLIST" "$FRAMEWORKS_PATH/$APP Helper NP.app/"
codesign -s "$APP_KEY" -f --entitlements "$LOGINHELPER_PLIST" "$APP_PATH/Contents/Library/LoginItems/$APP Login Helper.app/Contents/MacOS/$APP Login Helper"
codesign -s "$APP_KEY" -f --entitlements "$LOGINHELPER_PLIST" "$APP_PATH/Contents/Library/LoginItems/$APP Login Helper.app/"
codesign -s "$APP_KEY" -f --entitlements "$CHILD_PLIST" "$APP_PATH/Contents/MacOS/$APP"
codesign -s "$APP_KEY" -f --entitlements "$PARENT_PLIST" "$APP_PATH"

productbuild --component "$APP_PATH" /Applications --sign "$INSTALLER_KEY" "$RESULT_PATH"

こんなコマンドを叩く必要がある.ここで,最初に作った証明書の名前を埋めておこう.

これを実行すると.pkg ファイルが作成される.

あとはこれをアップロードすれば良い.

動作確認

一応動作確認しておく.ここで動かなければAppStoreに登録しても動くはずがない.

まず,$APP_PATHにあるアプリケーションを消しておこう.これがあると,既にインストール済みと判定されて.pkgファイルが何もしてくれなくなる.

そうしたら.pkg ファイルをダブルクリックし,インストーラーを実行する. すると /Applications 以下にアプリケーションがインストールされるので,実行してみよう.

iTunesConnectでアプリとバージョンを作成

アプリケーションがビルドできたとして,次はアップロード先を作っておかないといけない.このへんはswift等でiOSアプリを作ったことがあると慣れっこである.

iTunesConnectに行き,新しいアプリケーションを作る.

最初はバージョンが1.0になっている.セマンティックバージョニングを使いたければ,バージョン情報から番号を編集できる.

ApplicationLoaderでアップロード

Xcodeを開く.

Xcode -> Open Developer Tool -> Application Loader でApplicationLoaderを開く.そうしたら先ほどiTunesConnectで作成したアプリ向けに.pkgファイルをアップロードすれば良い.

審査を通す

iTunesConnectでバージョン情報や連絡先等,必要な情報を全部埋めたら,審査にだそう. どうやったら審査に通るかは謎いが,変なアプリじゃなければだいたい通るんじゃないかな.

幸いなことにWhalebirdは一発で審査が通った.最近のAppleは審査が早くなったのでやりやすい.