FlutterアプリにCognitoのソーシャルサインインを実装する【Google編】

はじめに

ソフトウェアエンジニアの谷藤です。

今回は Flutter アプリに Amazon Cognito を使って、Google のソーシャルサインインを実装する手順について解説していきます。

ソーシャルサインインをメインに解説していきたいので、Cognito 自体についてやユーザープールの解説はしません。

aws.amazon.com

ユーザープールの設定

まずはユーザープールの設定をします。

1. ユーザープールを作成する

まずはユーザープールを作成します。各項目についてはご自身の好みや作っているアプリによって適切に設定してください。

サインインの方法(メアドか電話番号かユーザー名のどれを使うかなど)や必須の標準属性については、作成後に変更ができないので、この点を注意して設定してください。

f:id:HiroTanifuji:20211001202722p:plain

2. ユーザープールにクライアントアプリを追加する

次に全般設定の「アプリクライアント」タブからユーザープールにクライアントアプリを追加します。

クライアントシークレット以外は後から変更可能なので、各項目は気にせずチェックにしちゃって良いと思います。

クライアントシークレットはご自身の環境で必要な場合のみ、チェックを入れてください。

f:id:HiroTanifuji:20211001204145p:plain

3. Cognito ドメインを作成する

次にアプリの統合の「ドメイン名」タブからドメインプレフィックスを設定して、Cognitoで使うドメインを作成してください。

f:id:HiroTanifuji:20211001212026p:plain

この後の設定でも使うので、作成したドメインをメモしておきます。

Google Cloud Platform の設定

次に Google Cloud Platform の設定をします。

1. プロジェクトを作成する

ユーザープールには一つの GCP のプロジェクト(アプリ)しか紐付けることができないので、この辺りも考慮しつつ進めてください。

2. アプリを登録する

API とサービス > OAuth 同意画面 からアプリを登録します。

社内向けのアプリ以外は外部のユーザーに使ってもらうと思うので、基本的には User Type は外部を選択してください。

アプリ名やアプリのロゴなどはご自身環境に合わせて設定してください。

スコープについてはユーザープールが Google から受け取りたい値を設定します。基本的には email は必要だと思うので、設定します。

f:id:HiroTanifuji:20211001210858p:plain

3. OAuth クライアント ID を作成する

次に OAuth のクライアント ID を作成します。

API とサービス > 認証情報 > 認証情報を作成 から OAuth クライアント ID を選択します。

f:id:HiroTanifuji:20211001211310p:plain

アプリケーションの種類はウェブ アプリケーション を選択します。

承認済みのリダイレクト URI にはこちらで作成した Cognito のドメイン'/oauth2/idpresponse' を付け足した URI を設定します。

f:id:HiroTanifuji:20211001213836p:plain

作成が完了すると、クライアント ID とクライアントシークレットが表示されます。この後に使うのでメモしておきます。

f:id:HiroTanifuji:20211001212709p:plain

ユーザープールでソーシャルサインインの設定

ユーザープールに戻って、ソーシャルサインインのための設定をしていきます。

1. ID プロバイダとして Google を追加する

フェデレーションの「ID プロバイダ」タブから Google を選択して、上でメモしたクライアント ID とクライアントシークレットを入力します。

Google アプリ ID に クライアント ID を、アプリシークレット にクライアントシークレットを入力してください。

認証スコープは、ユーザープールが Google から受け取りたい値を設定してください。

f:id:HiroTanifuji:20211001213309p:plain

その後、忘れずに属性マッピングの設定をしてください。

これを忘れると、Google から受け取った email などの値が、ユーザープールのユーザーの email とマッピングされず、アプリケーションで利用することができません。

f:id:HiroTanifuji:20211001213527p:plain

2. アプリクライアントの設定をする

次に、アプリの統合の「アプリクライアントの設定」タブから上で作成したアプリクライアントの詳細設定をしていきます。

まず、有効な ID プロバイダとして Google を選択してください。ユーザープールで設定したサインイン方法(メアド + パスワードなど)も有効にしたい場合は選択します。

サインインとサインアウトの URL を設定します。

それぞれ何でも良いと思いますが、今回は公式が解説している通り、アプリ名 + '://' を設定します。

https://docs.amplify.aws/lib/auth/signin_web_ui/q/platform/flutter/

OAuth 2.0 の設定で Authorization code grant を有効にする必要があります。

許可されている OAuth スコープにはご自身の環境に合わせて設定してほしいですが、無難に全て選択してしまっても問題ないと思います。

f:id:HiroTanifuji:20211001220904p:plain

これでユーザープール並びに Google 側の設定は終わりです。

Flutter アプリに Cognito を組み込む

1. Cognito ライブラリ

今回は、Cognito を組み込むためのライブラリは公式の amplify_flutter を使います。

非公式でも amazon_cognito_identity_dart_2 は使いやすくて、筆者も公式がリリースされるまではこちらを利用していました。

このライブラリを使って、Cognito のソーシャルサインインの方法をわかりやすく解説されている記事はこちらになります。とても参考になりました。

qiita.com

公式はまだリリースから間もないため、バグやその解決方法の解説が少ない恐れがあります。

ですので、個人的には、現時点では商用のアプリケーションで使う場合は非公式の方を使って、公式が安定したら置き換えるような対応が良いと思っています。

2. パッケージインストール

Cognito を使う場合は amplify_auth_cognito も必要なので、プロジェクトの pubspec.yaml に記載して flutter pub get します。

# pubspec.yaml

amplify_flutter: 0.2.4
amplify_auth_cognito: 0.2.4

3. Flutter アプリの設定

Flutter アプリに Cognito の設定ファイルを追加する必要があるので、以下を参考に amplifyconfiguration.dart というファイルを作成して lib の直下に配置してください。

// ./lib/amplifyconfiguration.dart

const amplifyconfig = ''' {
  "UserAgent": "aws-amplify-cli/2.0",
  "Version": "1.0",
  "auth": {
      "plugins": {
          "awsCognitoAuthPlugin": {
              "IdentityManager": {
                  "Default": {}
              },
              "CredentialsProvider": {
                  "CognitoIdentity": {
                      "Default": {
       // 今回は使わないので任意
                          "PoolId": "[COGNITO_IDENTITY_POOL_ID]",
       // 今回は使わないので任意
                          "Region": "[COGNITO_IDENTITY_POOL_REGION]"
                      }
                  }
              },
              "CognitoUserPool": {
                  "Default": {
      // ユーザープールの ID、全般設定から確認ができる
                      "PoolId": "[COGNITO_USER_POOL_ID]",
         // アプリクライアントの ID、全般設定 >アプリクライアント から確認ができる
                      "AppClientId": "[COGNITO_USER_POOL_APP_CLIENT_ID]",
                       // ユーザープールの ID のアンダースコアまでの値
                      "Region": "[COGNITO_USER_POOL_REGION]"
                  }
              },
              "Auth": {
                  "Default": {
                      "OAuth": {
                            // ユーザープールのドメイン名、アプリの統合 > ドメイン名から確認ができる
                            "WebDomain": "[COGNITO_DOMAIN_PREFIX].auth.[COGNITO_USER_POOL_REGION].amazoncognito.com",
                            // 上記と同じアプリクライアントの ID
                            "AppClientId": "[COGNITO_USER_POOL_APP_CLIENT_ID]",
                            // アプリの統合 > アプリクライアントの設定 のコールバック URL
                            "SignInRedirectURI": "[SIGN_IN_REDIRECT_URI]",
                            // アプリの統合 > アプリクライアントの設定 のサインアウト URL
                            "SignOutRedirectURI": "[SIGN_OUT_REDIRECT_URI]",
                            // アプリの統合 > アプリクライアントの設定 の許可されている OAuth スコープを配列として入れる、例)["phone", "email", ...] 
                            "Scopes": ["[OAUTH_SCOPES]"]
                       },
                      // 全般設定 > アプリクライアント の認証フローの設定で有効にしてある中からアプリで使うフローを入れる、例)"USER_PASSWORD_AUTH"
                      "authenticationFlowType": "[AUTHENTICATION_FLOW_TYPE]"
                  }
              }
          }
      }
  }
}''';

github.com

4. iOS の設定

公式にあるように iOS 側の設定も必要です。

https://docs.amplify.aws/lib/auth/social_signin_web_ui/q/platform/flutter/#android-platform-setup

サインインの際に開いた Web UI からアプリにリダイレクトするための設定です。

プロジェクトの ./ios/Runner/Info.plist に以下を追加してください。amplify-flutter の部分にはこちらで設定したサインイン URL から '://' を取り除いた値を入れてください。

...
<dict>
    ...
    <!-- amplify flutter setting -->
    <key>CFBundleURLTypes</key>
    <array>
        <dict>
            <key>CFBundleURLSchemes</key>
            <array>
                <string>amplify-flutter</string>
            </array>
        </dict>
    </array>
    ...
</dict>
...

github.com

5. Android の設定

公式にあるように Android 側の設定も必要です。

https://docs.amplify.aws/lib/auth/social_signin_web_ui/q/platform/flutter/#android-platform-setup

こちらもサインインの際に開いた Web UI からアプリにリダイレクトするための設定です。

プロジェクトの ./android/app/src/main/AndroidManifest.xml に以下を追加してください。こちらも amplify-flutter の部分にはこちらで設定したサインイン URL から '://' を取り除いた値を入れてください。

...
   <queries>
       <intent>
           <action android:name=
               "android.support.customtabs.action.CustomTabsService" />
       </intent>
   </queries>
   <application
    ...
        <activity android:name=
           "com.amplifyframework.auth.cognito.activities.HostedUIRedirectActivity">
            <intent-filter>
                <action android:name="android.intent.action.VIEW" />
                <category android:name="android.intent.category.DEFAULT" />
                <category android:name="android.intent.category.BROWSABLE" />
                <data android:scheme="amplify-flutter" />
            </intent-filter>
        </activity>
    </application>
...

github.com

6. Flutter アプリの実装

かなり簡易的に、サインイン済みならマイページを、未サインインならサインインページを開くための処理を mian.dart に書きました。

特に難しい点はないと思いますが、_sign メソッド内で呼び出している Amplify.Auth.signInWithWebUI がソーシャルサインインをするための API になります。

// ./lib/main.dart

import 'package:flutter/material.dart';

import 'package:amplify_auth_cognito/amplify_auth_cognito.dart';
import 'package:amplify_flutter/amplify.dart';

import 'package:amplify_flutter_social_signin/pages/loading_page.dart';
import 'package:amplify_flutter_social_signin/pages/mypage.dart';
import 'package:amplify_flutter_social_signin/pages/sign_in_page.dart';

import 'amplifyconfiguration.dart';

void main() {
  runApp(MyApp());
}

class MyApp extends StatefulWidget {
  @override
  State<StatefulWidget> createState() => _MyAppState();
}

class _MyAppState extends State<MyApp> {
  bool _isInitialized = false;
  AuthUser? _user;

  @override
  initState() {
    super.initState();
    _initAmplifyFlutter();
  }

  void _initAmplifyFlutter() async {
    AmplifyAuthCognito auth = AmplifyAuthCognito();
    Amplify.addPlugins([auth]);

    try {
      await Amplify.configure(amplifyconfig);

      final user = await Amplify.Auth.getCurrentUser();

      setState(() {
        _user = user;
      });
    } on AmplifyAlreadyConfiguredException catch (e) {
      print(e.message);
    } on SignedOutException catch (e) {
      print(e.message);
    }

    setState(() {
      _isInitialized = true;
    });
  }

  Widget _display() {
    if (!_isInitialized) {
      return const LoadingPage();
    } else {
      return _user != null
          ? MyPage(
              username: _user!.username,
              userId: _user!.userId,
              signOut: _signOut,
            )
          : SignIn(
              signIn: _signIn,
            );
    }
  }

  Future<void> _signOut() async {
    try {
      await Amplify.Auth.signOut();
      setState(() {
        _user = null;
      });
    } on AuthException catch (e) {
      print(e.message);
    }
  }

  Future<void> _signIn() async {
    try {
      final res = await Amplify.Auth.signInWithWebUI(provider: AuthProvider.google);
      if (res.isSignedIn) {
        try {
          final user = await Amplify.Auth.getCurrentUser();

          setState(() {
            _user = user;
          });
        } on SignedOutException catch (e) {
          print(e.message);
        }
      }
    } on AuthException catch (e) {
      print(e.message);
    }
  }

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Amplify Flutter App',
      theme: ThemeData(
        primarySwatch: Colors.blue,
        visualDensity: VisualDensity.adaptivePlatformDensity,
      ),
      home: _display(),
    );
  }
}

7. 動作確認

一応動作の確認をします。こちらがサインインページになります。

f:id:HiroTanifuji:20211003125924p:plain

サインインボタンによって Amplify.Auth.signInWithWebUI が呼び出されると、アクセスに関するダイアログが表示されます。

そこで Continue が押されると、Web UI で Google のログインページが表示されます。

f:id:HiroTanifuji:20211003130035p:plain

Google のログインが完了すると、アプリにリダイレクトされ、マイページが表示されます。

f:id:HiroTanifuji:20211003130048p:plain

さいごに

Flutter アプリに Amazon Cognito を使って、Google のソーシャルサインインを実装する手順について解説しました。

ご不明点やご指摘などがございましたら、コメントに書いてください。

読んでいただきありがとうございました。

2021年10月版 CodemagicでFlutterアプリのCI/CDを構築する【Android編】

はじめに

ソフトウェアエンジニアの谷藤です。

Codemagic で Flutter アプリの Android 版の CI/CD を構築するための設定(2021年10月時点)について解説します。

iOS 版はこちら

hirotanifuji.hatenablog.com

あくまで Codemagic 上の設定の話なので、Flutter アプリ自体の設定などについては解説しません。

codemagic.io

1. 各連携

Sign up した後に、User settingsより各サービスとの連携を設定します。

f:id:HiroTanifuji:20210926175612p:plain

  • Bitbucket、GitHub、GitLab のどれかを連携させる(Sign upのアカウントとして選択した場合は、連携済み)

  • Slack を連携する(任意)

  • Developer Portal の連携は必要なし

2. アプリを作成する

Apps タブで Add application を押します。

次に、上記で連携させたホスティングサービス(Bitbucket、GitHub、GitLabのどれか)を選択します。

Select repository には適切なレポジトリを、Select project type には Flutter App を選択して、アプリの作成を完了させます。

f:id:HiroTanifuji:20210926180916p:plain

3. ビルドの設定

Workflow settings

Workflow name にはワークフローの名前を設定します。iOS との区別ができれば何でも良いです。

Max build duration には開始から何分後にタイムアウトさせるかを設定します。こだわりがなければ、120分で問題ないと思います。

f:id:HiroTanifuji:20210927094630p:plain

Build for platforms と Run build on

Build for platforms は Android を選択します。

Run build on は Billing を有効にしていない限り macOS Standard VM しか選択できないと思いますが、好みの VM を選択します。

f:id:HiroTanifuji:20210927095739p:plain

Build triggers

まずは、Automatic build triggering で好みのトリガーを選択します。

合わせて Watched branch patterns や Watched tag patterns も設定します。

例としてmaterブランチにプッシュされたらビルドが走るように設定しました。

f:id:HiroTanifuji:20210926182815p:plain

Environment variables

次は環境変数の設定です。ご自身の環境によって変わるので、以下の中で当てはまる項目のみ参考にしてください。

■ プロジェクトで .env を使っている場合

プロジェクトで .env を使っていて、このファイルをリポジトリに含めていない場合は、次の対応が必要になります。

この後の Pre-build script で使うので、.env ファイルを base64 エンコードした値をセットします。

Macを使っている場合、base64 .env | pbcopybase64 エンコード とその内容のコピーができます。

例として ENV にこの値をセットします。

■ プロジェクトで Firebase を使っている場合

プロジェクトで Firebase を使っていて、構成ファイルをリポジトリに含めていない場合は、次の対応が必要になります。

Firebase を使っている場合は、android/app/google-services.json として構成ファイルを配置する必要があります。

こちらもこの後の Pre-build script で使うので、google-services.jsonbase64 エンコードした値をセットします。

例として、ANDROID_GOOGLE_SERVICE にこの値をセットします。

f:id:HiroTanifuji:20210927095911p:plain

Dependency caching

パッケージなどの依存物をキャッシュする場合は有効にします。

有効にすることでビルド時間を短縮できるので、頻繁に回すビルドでは有効にした方がコストの面で良さそうです。

例として、公式に記載の以下の3つをセットします。

$HOME/Library/Caches/CocoaPods(CocoaPods のキャッシュ)

$HOME/.gradle/caches(Gradle のキャッシュ)

$FLUTTER_ROOT/.pub-cacheDart のキャッシュ)

f:id:HiroTanifuji:20210926184620p:plain

Tests

ビルド前に実行されるテストについての設定です。好みに合わせて設定してください。

基本的には、 Stop build if tests or analysis fail は有効にした方が良いと思うので、有効にします。

その他は、例として、Static code analysis の Flutter analyzer のみ有効にします。

f:id:HiroTanifuji:20210926184718p:plain

Pre-build script

ビルド直前に実行させるスクリプトの設定です。

今回は主に、上の Environment variables で設定した base64 エンコード済のファイルをデコードして、適切な位置に配置する目的で使用します。

例として、以下のようなスクリプトを設定します。

#!/bin/sh

# .env を base64 エンコードした値が設定されている ENV をデコードして、配置する
echo $ENV | base64 --decode> $FCI_BUILD_DIR/.env

# google-services.json を base64 エンコードした値が設定されている ANDROID_GOOGLE_SERVICE をデコードして、配置する
echo $ANDROID_GOOGLE_SERVICE | base64 --decode> $FCI_BUILD_DIR/android/app/google-services.json

f:id:HiroTanifuji:20210927101049p:plain

Build

次に最も重要なビルドについての設定です。

Flutter version はご自身の環境に合わせて設定してください。特にこだわりがなければ、デフォルトで問題ないと思います。

Project path もご自身の環境に合わせて設定してください。基本的には . だと思います。

Android build format もご自身の環境に合わせて選択してください。

Mode には Release を設定します。

f:id:HiroTanifuji:20210927103016p:plain

Build arguments には flutter build の後に付け加えたい引数を指定します。ご自身の環境に合わせて設定してください。

例として、以下を設定しました。

--target-platform android-arm,android-arm64,android-x64 --flavor production --build-number=$(($BUILD_NUMBER + 10))

flavor を設定している場合は、指定します。

--build-number は基本的には $BUILD_NUMBER で良いと思いますが、すでにビルドを Google Play にアップロードしている場合は、ビルド番号が重複となってしまいます。

その場合は、--build-number=$(($BUILD_NUMBER + N)) のように、すでにアップロード済みの数(N)を加算したビルド番号を指定するようにしてください。

f:id:HiroTanifuji:20210927103214p:plain

Distribution

Android code signing と Google Play の設定をします。

まずは、Android code signing です。

Enable Android code signing を有効にします。

Keystore* には普段使っているアップロード鍵をアップロードしてください。

Keystore password、Key alias、Key password は上でアップロードした鍵についての情報を入力してください。

f:id:HiroTanifuji:20210927104557p:plain

次に、Google Play についてです。

まずは、Enable Google Play publishing を選択してください。

Credentials(.json)* ですが、ご自身の Google Play Console で手続きをする必要があります。手順は以下の通りです。

  1. Google Play Console > 設定 > API アクセス から新しいプロジェクトを作成
  2. 作成されたプロジェクトを Google Cloud Platform で開く
  3. IAM と管理 > サービスアカウント からサービスアカウントを作成する
  4. 作成したサービスアカウントのロールとして Service Accounts > サービスアカウント ユーザー を設定する
  5. 作成したサービスアカウントの詳細ページ > キー > 鍵を追加 > 新しい鍵を作成 > JSON タイプ で秘密鍵の作成 & ダウンロード
  6. Google Play Console に戻って、サービスアカウントが追加されたことを確認する
  7. このアカウントにアプリ権限とアカウント権限を付与する
  8. Codemagic に戻って、ダウンロードした秘密鍵をアップロード

Track はご自身の環境に合わせて選択してください。

Production を選択すると自動で審査の提出まで行ってくれますが、リリースノートの記載ができないことがあるので、Beta を選択して、審査の提出は手動で行う iOS と近い運用にした方が無難だと思います。

その他の Update priority や Rollout fraction などはご自身の環境に合わせて設定してください。

f:id:HiroTanifuji:20210927130435p:plain

4. 通知の設定

Email と Slack の通知を設定できます。

好みの設定にしてください。

f:id:HiroTanifuji:20210926214646p:plain

5. ビルド実行

全ての設定が完了したら、ビルドを実行してみましょう。

Start new build から、ビルドしたいレポジトリのブランチと、ビルドの設定をしたワークフローを選択して実行します。

f:id:HiroTanifuji:20210927130115p:plain

以下のように NDK is not installed というエラーが出る場合は android/app/build.gradle に NDK のバージョンを指定するようなコードを追加してください。

// android/app/build.gradle

android {
    ...

    ndkVersion "22.1.7171670"

    ...
}

f:id:HiroTanifuji:20210927124540p:plain

これは、Codemagic で使用している VM にインストールされている NDK のバージョンと、Androidがデフォルトで使う NDK のバージョンが一致しないことが原因です。

Android がデフォルトで使う NDK のバージョンを VM にインストールされている NDK のバージョンに合わせてあげると解決するはずです。

VM にインストールされている NDK のバージョンはこちらから確認できます。

docs.codemagic.io

ビルドが成功すると、以下のような通知が届きます。

f:id:HiroTanifuji:20210927125833p:plain

f:id:HiroTanifuji:20210927125840p:plain

さいごに

Codemagic を使って Flutter アプリの CI/CDを構築するための設定方法について解説しました。

ご自身の環境によって変更しないといけない箇所があると思いますが、基本的な設定や詰まりそうな箇所の解説はできたかなと思います。

ご不明点やご指摘などがございましたら、コメントに書いてください。

以下、私が参考にさせていただいた記事になります。

medium.com

medium.com

blog.dalt.me

読んでいただきありがとうございました。

2021年10月版 CodemagicでFlutterアプリのCI/CDを構築する【iOS編】

はじめに

ソフトウェアエンジニアの谷藤です。

Codemagic で Flutter アプリの iOS 版の CI/CD を構築するための設定(2021年10月時点)について解説します。

Android 版はこちら

hirotanifuji.hatenablog.com

あくまで Codemagic 上の設定の話なので、Flutter アプリ自体の設定などについては解説しません。

codemagic.io

1. 各連携

Sign up した後に、User settingsより各サービスとの連携を設定します。

f:id:HiroTanifuji:20210926175612p:plain

  • Bitbucket、GitHub、GitLab のどれかを連携させる(Sign upのアカウントとして選択した場合は、連携済み)

  • Developer Portal を連携する

App Store Connect のユーザとアクセスから API キーを発行します。名前はアプリ名 + codemagic key とかで良いと思います。アクセスは Developer で問題ありません。Key をダウンロードしたら大切に保管します。

Codemagicに戻り、App Store Connect API key name には発行した API キーの名前を、Issuer ID と Key ID はページに記載されている値を入力します。また、ダウンロードした API キーをアップロードします。

f:id:HiroTanifuji:20210926180208p:plain

f:id:HiroTanifuji:20210926180338p:plain

  • Slack を連携する(任意)

2. アプリを作成する

Apps タブで Add application を押します。

次に、上記で連携させたホスティングサービス(Bitbucket、GitHub、GitLabのどれか)を選択します。

Select repository には適切なレポジトリを、Select project type には Flutter App を選択して、アプリの作成を完了させます。

f:id:HiroTanifuji:20210926180916p:plain

3. ビルドの設定

Workflow settings

Workflow name にはワークフローの名前を設定します。AndroidAd hoc などとの区別ができれば何でも良いです。

Max build duration には開始から何分後にタイムアウトさせるかを設定します。こだわりがなければ、120分で問題ないと思います。

f:id:HiroTanifuji:20210926182148p:plain

Build for platforms と Run build on

Build for platforms は iOS を選択します。

Run build on は Billing を有効にしていない限り macOS Standard VM しか選択できないと思いますが、好みの VM を選択します。

f:id:HiroTanifuji:20210926182551p:plain

Build triggers

まずは、Automatic build triggering で好みのトリガーを選択します。

合わせて Watched branch patterns や Watched tag patterns も設定します。

例としてmaterブランチにプッシュされたらビルドが走るように設定しました。

f:id:HiroTanifuji:20210926182815p:plain

Environment variables

次は環境変数の設定です。ご自身の環境によって変わるので、以下の中で当てはまる項目のみ参考にしてください。

■ Flavorを設定している場合

Codemagic では、アーカイブ時にデフォルトで Runner Scheme を使っているため、Flavor を設定している場合は、それに合わせて Scheme を変更する必要があります。

ご自身の環境に合わせて、FCI_FLUTTER_SCHEME に値を設定してください。

You haven’t specified the iOS scheme to be used for the archive action of Xcode build. This applies when your app has custom iOS schemes. By default, Codemagic builds the Runner scheme, but you can use the FCI_FLUTTER_SCHEME environment variable to specify another scheme.

docs.codemagic.io

■ プロジェクトで .env を使っている場合

プロジェクトで .env を使っていて、このファイルをリポジトリに含めていない場合は、次の対応が必要になります。

この後の Pre-build script で使うので、.env ファイルを base64 エンコードした値をセットします。

Macを使っている場合、base64 .env | pbcopybase64 エンコード とその内容のコピーができます。

例として ENV にこの値をセットします。

■ プロジェクトで Firebase を使っている場合

プロジェクトで Firebase を使っていて、構成ファイルをリポジトリに含めていない場合は、次の対応が必要になります。

Firebase を使っている場合は、ios/Runner/GoogleService-info.plist として構成ファイルを配置する必要があります。

こちらもこの後の Pre-build script で使うので、GoogleService-info.plist を base64 エンコードした値をセットします。

例として、IOS_GOOGLE_SERVICE にこの値をセットします。

flutter build ipa で --export-options-plist=XXX を指定する場合

ビルド時に、明示的に export オプションファイルを指定していて、その対象のファイルをリポジトリに含めていない場合は、次の対応が必要になります。

こちらもこの後の Pre-build script で使うので、base64 エンコードした値をセットします。

例として、EXPORT_OPTIONS_PLIST にこの値をセットします。

f:id:HiroTanifuji:20210926184351p:plain

Dependency caching

パッケージなどの依存物をキャッシュする場合は有効にします。

有効にすることでビルド時間を短縮できるので、頻繁に回すビルドでは有効にした方がコストの面で良さそうです。

例として、公式に記載の以下の3つをセットします。

$HOME/Library/Caches/CocoaPods(CocoaPods のキャッシュ)

$HOME/.gradle/caches(Gradle のキャッシュ)

$FLUTTER_ROOT/.pub-cacheDart のキャッシュ)

f:id:HiroTanifuji:20210926184620p:plain

Tests

ビルド前に実行されるテストについての設定です。好みに合わせて設定してください。

基本的には、 Stop build if tests or analysis fail は有効にした方が良いと思うので、有効にします。

その他は、例として、Static code analysis の Flutter analyzer のみ有効にします。

f:id:HiroTanifuji:20210926184718p:plain

Pre-build script

ビルド直前に実行させるスクリプトの設定です。

今回は主に、上の Environment variables で設定した base64 エンコード済のファイルをデコードして、適切な位置に配置する目的で使用します。

例として、以下のようなスクリプトを設定します。

#!/bin/sh

# .env を base64 エンコードした値が設定されている ENV をデコードして、配置する
echo $ENV | base64 --decode> $FCI_BUILD_DIR/.env

# GoogleService-info.plist を base64 エンコードした値が設定されている IOS_GOOGLE_SERVICE をデコードして、配置する
echo $IOS_GOOGLE_SERVICE | base64 --decode> $FCI_BUILD_DIR/ios/Runner/GoogleService-Info.plist

# flutter build ipa 時に指定するオプションファイルを base64 エンコードした値が設定されている EXPORT_OPTIONS_PLIST をデコードして、配置する
echo $EXPORT_OPTIONS_PLIST | base64 --decode> $FCI_BUILD_DIR/ios/ExportOptions.plist

f:id:HiroTanifuji:20210926185009p:plain

Build

次に最も重要なビルドについての設定です。

Flutter version や Xcode version 、 CocoaPods version はご自身の環境に合わせて設定してください。特にこだわりがなければ、デフォルトで問題ないと思います。

Project path もご自身の環境に合わせて設定してください。基本的には . だと思います。

iOS build command の Use flutter build ipa は、使っている flutter のバージョンが1.24.0-6.0以降では、有効にした方が良いと思います。

Mode には Release を設定します。

f:id:HiroTanifuji:20210926190325p:plain

Build arguments には flutter build ipa の後に付け加えたい引数を指定します。ご自身の環境に合わせて設定してください。

例として、以下を設定しました。

--export-options-plist $FCI_BUILD_DIR/ios/ExportOptions.plist --flavor production --build-number=$(($BUILD_NUMBER + 10))

--export-options-plist には、上の Pre-build script で配置したファイルを指定します。

flavor を設定している場合は、それも指定します。

--build-number は基本的には $BUILD_NUMBER で良いと思いますが、すでにビルドを App Store Connect にアップロードしている場合は、重複となるためアップロードが失敗してしまいます。

その場合は、--build-number=$(($BUILD_NUMBER + N)) のように、すでにアップロード済みの数(N)を加算したビルド番号を指定するようにしてください。

f:id:HiroTanifuji:20210926212750p:plain

Distribution

iOS code signing と App Store Connect の設定をします。

まずは、iOS code signing です。

Select code signing method には Automatic か Manual を選択します。ご自身の環境に合わせて選択してください。

今回は例として、Manual を選択します。

Code signing certificate には、ご自身の Keychain Access の Default Keychains > login > My Certificates >Apple Distribution を .p12 のフォーマットでエクスポートしたファイルを指定します。

Certificate password には上記の .p12 ファイルをエクスポートした際に設定したパスワードを指定します。

f:id:HiroTanifuji:20210926213530p:plain

Provisioning profiles にはご自身の環境で使っている App Store 用のプロビジョニングプロファイルをアップロードしてください。こちら からダウンロードすることもできます。

f:id:HiroTanifuji:20210926214218p:plain

f:id:HiroTanifuji:20210926214627p:plain

次に、App Store Connect についてです。

まずは、Enable App Store Connect publishing を選択してください。

それ以外の設定は、すでに Developer Portal と連携済みだと思うので、基本的には必要ありません。

f:id:HiroTanifuji:20210926214637p:plain

4. 通知の設定

Email と Slack の通知を設定できます。

好みの設定にしてください。

f:id:HiroTanifuji:20210926214646p:plain

5. ビルド実行

全ての設定が完了したら、ビルドを実行してみましょう。

Start new build から、ビルドしたいレポジトリのブランチと、ビルドの設定をしたワークフローを選択して実行します。

f:id:HiroTanifuji:20210926214839p:plain

成功すると、以下のような通知が届きます。

f:id:HiroTanifuji:20210926215344p:plain

f:id:HiroTanifuji:20210926215350p:plain

さいごに

Codemagic を使って Flutter アプリの CI/CDを構築するための設定方法について解説しました。

ご自身の環境によって変更しないといけない箇所があると思いますが、基本的な設定や詰まりそうな箇所の解説はできたかなと思います。

ご不明点やご指摘などがございましたら、コメントに書いてください。

以下、私が参考にさせていただいた記事になります。

medium.com

medium.com

medium.com

blog.dalt.me

読んでいただきありがとうございました。

Flutter2 の List.firstWhere で The return type 'Null' isn't a XXX, as required by the closure's context になる問題

はじめに

ソフトウェアエンジニアの谷藤です。

今回は、タイトルに記載の問題について解説します。

問題の整理

以下のように List の firstWhere を使って特定の値を取り出す際に、値が見つからない場合の throw を防ぐために nullを返す orElse をセットすると、The return type 'Null' isn't a 'String', as required by the closure's context. というエラーが出てしまいます。

final List<String> fruits = ['apple', 'orange', 'banana', 'cherry'];

final grape = fruits.firstWhere((fruit) => fruit == 'grape', orElse: () => null); // The return type 'Null' isn't a 'String', as required by the closure's context.

原因

firstWhere のコードを見てみると、List の要素の型と orElse で返す値の型が一致している必要がありました。

E firstWhere(bool test(E element), {E orElse()?}) {
  for (E element in this) {
    if (test(element)) return element;
  }
  if (orElse != null) return orElse();
  throw IterableElementError.noElement();
}

解決方法

collection ライブラリの firstWhereOrNull を使うと素早く解決することができます。 pub.dev

上記のコードを collection を使って変更してみます。

import 'package:collection/collection.dart';

final List<String> fruits = ['apple', 'orange', 'banana', 'cherry'];

final grape = fruits.firstWhereOrNull((fruit) => fruit == 'grape');

エラーが出なくなりました。

firstWhereOrNull のコードを見てみると、値が見つからない場合は throw ではなく null を返していることがわかります。

T? firstWhereOrNull(bool Function(T element) test) {
  for (var element in this) {
    if (test(element)) return element;
  }
  return null;
}

さいごに

少しでも参考になったら嬉しいです。

質問やご指摘などがありましたら、コメントにお願いします。

読んでいただきありがとうございました。

Flutter の Null Safety 移行ツールが移行対象を正しく検知しない問題

はじめに

ソフトウェアエンジニアの谷藤です。

今回は、タイトルに記載の問題について解説します。

問題の整理

dart migrateを実行して移行ツールを起動したところ、一切 Null Safety 対応をしていないにも関わらず、以下のように移行対象として検知された箇所は0でした。

f:id:HiroTanifuji:20210919175815p:plain

解決方法

pubspec.yaml において Dart のバージョンを12以上に指定していたことが原因でした。

正しく検知するためには、12未満を指定している必要があったので、pubspec.yaml を以下のように変更しました。

environment:
  sdk: ">=2.11.0 <3.0.0"

移行対象として検知されるようになりました。 f:id:HiroTanifuji:20210919175818p:plain

さいごに

少しでも参考になったら嬉しいです。

質問やご指摘などがありましたら、コメントにお願いします。

読んでいただきありがとうございました。

Flutter の Null Safety 対応時に発生するエラー(The migration tool didn't start, due to analysis errors.)を解決する

はじめに

ソフトウェアエンジニアの谷藤です。

今回は、Flutter の Null Safety 対応時に私が遭遇したタイトルに記載のエラーについて解説します。

※ Null Safety 対応についての詳しい説明はここではしません。こちらをご確認ください。 dart.dev

問題の整理

dependencies を null safety 対応のバージョンにアップグレードした後に、dart migrateを実行すると、以下のエラーが発生しました。 f:id:HiroTanifuji:20210919172628p:plain

解決方法

dart migrate --helpを実行してヘルプを確認します。

f:id:HiroTanifuji:20210919173251p:plain

analysis errors を無視するための --ignore-errorsがあるので、dart migrate --ignore-errorsを実行する。

以下のように、無事に移行ツールが起動しました。

f:id:HiroTanifuji:20210919173937p:plain

さいごに

少しでも参考になったら嬉しいです。

質問やご指摘などがありましたら、コメントにお願いします。

読んでいただきありがとうございました。

iOSシュミレーターのビルドの時だけ発生するエラー(ld: in [path], building for iOS Simulator, but linking in object file built for iOS, file '[path]' for architecture arm64 )を解決する

はじめに

ソフトウェアエンジニアの谷藤です。

今日はiOSのシュミレーターでテストする際に発生したタイトルに記載のエラーについて解説します。

今回、解決まで参考にしたページはこちらです。

developer.apple.com

問題整理

実機デバイスのビルドやreleaseビルドでは問題ないのに、シュミレーターのdebugビルドで突然以下のエラーが発生するようになりました。

f:id:HiroTanifuji:20210916205559p:plain

解決方法

Xcode > Build Setting > Excluded Architectures の Debug > Any iOS Simulator SDK を 「arm64」に変更します。

f:id:HiroTanifuji:20210916211113p:plain

さいごに

今回はiOSシュミレーターのビルドのみで発生するタイトルのビルドエラーについて解説しました。

少しでも参考になったら嬉しいです。

質問やご指摘などがありましたら、コメントにお願いします。

読んでいただきありがとうございました。