Android
iOS
Flutter

flutterで本番/ステージング/開発を切り替える

前提

切り替えることに関して2018/07現在、flutterは微妙です。
簡単に切り替えられる方法としてそれぞれのosで以下を追加すればいいはずですが、flutterではできません。

  • BuildType (Android)
  • Configurations (ios)

なぜかというとflutterコマンドのオプションには、--flavor はあるのですが、--buildtype のような指定ができません。
--release と --debug があるだけです。

なので、以下を追加することになります。

  • flavor (Android)
  • shcemes (ios)

これ、知っている人にはわかると思いますが、本番/ステージング/開発環境を切り替えるには使いづらいです。
まあ、仕組みがないのでやるしかありません。

Buildは以下のように指定します。

$ flutter build ios --debug --flavor dev

この記事では、flavorに以下を指定します。

開発=dev
ステージング=qa
本番=prod

ios

次からは、XCodeで作業するので開きます。

$ open ios/Runner.xcworkspace

まずは、以下を参考にSchemeを作成しましょう。

スクリーンショット 2018-07-10 14.14.16.png

flutterで以下のコマンドを打つと何が起こるかというと

$ flutter build ios --debug --flavor dev

ConfigurationsのDebug-devが使われます。
なので、configurationsを追加します。

以下の画像のように追加します。

スクリーンショット 2018-07-09 13.36.18.png

ちなみにビルド時に自分はfastlaneを利用していますが、うまくできなかった部分があったのでこうしています。

  • ReleaseをAppstoreにしているとfastlane matchでappstore用のプロビジョニングプロファイルも見てしまい、adhocじゃないからできないというエラーが出てしまう。
  • Release-prodだけをAppStoreにする。Releaseはqaにする。そうしないとqaにしたい場合に本番環境が作成される。

できなかったことは以上です。(flutterがconfigurationsだけでできればこんなにややこしい構成にしないで済むのですが。。)

次に、ios/Flutter/ に dev.xcconfig、qa.xcconfig、prod.xcconfig ファイルを作成して以下を記述します。

ios/Flutter/dev.xcconfig
#include "Generated.xcconfig"

FLUTTER_TARGET=lib/main.dart

これでそれぞれのConfigurationsを利用するときのファイルが揃いました。
flutterコマンドでbuildできるはずです。

それぞれの環境用のアプリ

別アプリ化

次は、環境ごとに別アプリとして認識させて、同じ端末に同居させたいでしょう。

その際にflutterでの注意すべきことは、「環境ごとにアプリ名を変えると動作しない」ということです。
つまりBuild Settings -> Product Nameは変更しないでRunnerのままにしておきます。
アプリ名をRunnerから変えたい場合は、Info.plistのCFBundleName(Bundle name)を変更するだけに留めておきます。

次に別アプリとしてインストールできるようにProduct Bundle Identifierを環境ごとに変えます。

スクリーンショット 2018-07-10 13.24.06.png

環境ごとの設定

例えば、環境ごとに違う GoogleService-Info.plist の扱いです。

結論を言うとios開発での今まで通りのやり方で良いです。

自分の場合は、環境ごとのファイルを用意して、Build Phaseで入れる方法にしています。
具体的には、

  1. 以下ファイルをRunner直下におきます。
  • GoogleService-Info-dev.plist
  • GoogleService-Info-qa.plist
  • GoogleService-Info-prod.plist
  1. Targets -> Build Phases で +ボタンを押下して、「New Run Script Phase」をします。

スクリーンショット 2018-07-10 13.43.50.png

そこで以下のShellを書きます。

rm -rf "${BUILT_PRODUCTS_DIR}/${PRODUCT_NAME}.app/GoogleService-Info.plist"

echo "-----${CONFIGURATION}-----"
echo "-----${SRCROOT}-----"

if [ "${CONFIGURATION}" = "Debug" ]  || [ "${CONFIGURATION}" = "Debug-dev" ] || [ "${CONFIGURATION}" = "Release-dev" ]; then
  cp "$SRCROOT/Runner/GoogleService-Info-dev.plist" "${BUILT_PRODUCTS_DIR}/${PRODUCT_NAME}.app/GoogleService-Info.plist"
  echo "Development GoogleService-Info copied."
elif [ "${CONFIGURATION}" = "Release" ] || [ "${CONFIGURATION}" = "Debug-qa" ] || [ "${CONFIGURATION}" = "Release-qa" ]; then
  cp "$SRCROOT/Runner/GoogleService-Info-qa.plist" "${BUILT_PRODUCTS_DIR}/${PRODUCT_NAME}.app/GoogleService-Info.plist"
  echo "Integration Test GoogleService-Info copied."
elif [ "${CONFIGURATION}" = "Release-prod" ]  || [ "${CONFIGURATION}" = "Debug-prod" ]; then
  cp "$SRCROOT/Runner/GoogleService-Info-pro.plist" "${BUILT_PRODUCTS_DIR}/${PRODUCT_NAME}.app/GoogleService-Info.plist"
  echo "Production GoogleService-Info copied."
fi

Provisioning Profile

ちなみに自分の場合は、証明書やProvisioninig Profileの取得はfastlaneで以下のように行なっています。

  lane :load_certificates do
    match(type: "development", readonly: true)
    match(type: "adhoc", app_identifier: ["ko2ic.sample.qa","ko2ic.sample"], readonly: true) 
    match(type: "appstore", app_identifier: "ko2ic.sample", readonly: true) 
  end

ビルド

ステップが二つ必要です。

まず、以下のようにコマンドを叩いておく必要があります。

$ flutter build ios --release --no-codesign --flavor prod

その後、通常のios開発のようにビルドするだけです。
例えば、fastlaneを使って入れば、以下のように書けば良いでしょう。
これでipaファイルが出来上がります。

・・・
    gym(
      configuration: configuration,
      scheme: scheme,
      export_method: export_method,
      output_name: file_name,
      export_options: {
          provisioningProfiles: {
                app_identifier => "#{provisioning_profile_name} #{app_identifier}"
          }
      }
    )

・・・

Android

まずは、debug用とrelease用のkeystoreを用意して設定を書いておきましょう。(keystoreの作成は割愛)

android/app/build.gradle
    signingConfigs {
        develop {
            storePassword "fuga"
            keyAlias "sampleDebug"
            keyPassword "fugafuga"
            storeFile file("../debug.keystore")
        }
        release {
            storePassword "hoge"
            keyAlias "sampleRelease"
            keyPassword "hogehoge"
            storeFile file("../release.keystore")
        }
    }

     defaultConfig {
         applicationId "ko2ic.sample"
         ・・・
         signingConfig signingConfigs.develop
     }  

    buildTypes {
        release {
            signingConfig signingConfigs.release
            debuggable false
            minifyEnabled true
            useProguard true
            proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
        }
    }         

それぞれの環境用のアプリ

別アプリ化

次は、productFlavorsです。

android/app/build.gradle
    flavorDimensions "default"

    productFlavors {
        dev {
            dimension "default"
            applicationIdSuffix ".dev"
            versionNameSuffix ".dev"
        }
        qa {
            dimension "default"
            applicationIdSuffix ".qa"
            versionNameSuffix ".qa"
        }
        prod {
            dimension "default"
        }
    }

Androidはこれだけでそれぞれ別アプリとしてできます。

環境ごとの設定

ios同様にgoogle-services.jsonの扱いです。
これも、普通のAndroid開発と同じでそれぞれのディレクトリに用意すれば良いでしょう。

  • android/app/src/dev/google-services.json
  • android/app/src/qa/google-services.json
  • android/app/src/prod/google-services.json

ビルド

androidの場合は、以下コマンドだけでapkが作成されます。

$ flutter build apk --release --flavor prod