2025年版|Flutter|Firebase|AppleサインインをAndroidとiPhoneに実装する

Devlog
Devlog

Apple審査の際に、Appleサインインの実装を求められると聞いたので。

手順

  1. Apple Developer Programへ登録、登録料を支払いアカウントを取得
  2. Bundle IDをIdentifierに登録
  3. FirebaseコンソールでAppleサインインの設定をする
  4. XCODEとAppleアカウントを紐付け、Appleサインインを導入する
  5. パッケージを導入しサインイン処理とボタンを実装

【注意】AppleサインインはAndroid/iOSともにシミュレーターでは動作しません!実機での検証が必要です。

Apple Developer Programへ登録、登録料を支払いアカウントを取得

こちらの記事をご参照ください。

Bundle IDをIdentifierに登録

Bundle IDとは

Bundle ID/バンドルIDは、アプリを識別するためのIDで、唯一の文字列である必要があり、一度登録すると変更ができません。

com.(teamname).(appname) の逆ドメイン形式が一般的で、teamname の箇所は組織名や開発者名、appname の箇所はアプリ名とするのが慣例です。Flutterプロジェクトを作成すると、デフォルトの teamname は「example」になっています。Bundle ID自体はユーザーから見えないもので、appname は厳密にはアプリの表示名と一致している必要はないですが、 appname を表示されるアプリ名と揃えるのが慣例ですし、複数のアプリを配信する場合は管理もしやすいでしょう。

BundleIdentifierはiOSプラットフォームでの呼び方で、AndroidではApplicationIdが同じ役割のものです。Androidはパッケージ名の命名規則に準拠するため小文字しか使えません。iOSでは大文字も使えるようですが、慣例としては全て小文字となります。

項目Android(applicationId)iOS(bundleIdentifier)
大文字使用❌ 不可⭕ 可(非推奨)
表示名と関連❌ 無関係❌ 無関係
命名規則英数字
ピリオド(.)
アンダースコア(_)
英数字
ピリオド(.)
ハイフン(-)

Bundle IDをApple Developer Programへ登録

Bundle IDを登録するために、まずは組織名/作者名とアプリ名を最終決定する必要があります。パッケージ名・アプリ名の変更にはいくつか書き換える箇所がありますので、変更の際はこちらも参考にしてください。

Bundle IDが最終決定し、コード内の該当箇所の整合を確認したら、Apple Developer Programの管理画面上で登録します。

Flutterのsign_in_with_appleパッケージの公式ドキュメントの、「Create a Service ID」までを参考に進めました。(その項以降は突然難しいことを言い出しますが、もっと簡単な方法で実装できました。)
https://pub.dev/packages/sign_in_with_apple

App ID の登録

プログラムのリソース → ID

追加ボタン

App IDs のまま continue

App のまま continue

Description は説明欄なのでなんでもOK。あとから変更も可能です。Bundle ID は一度登録すると変更できないので慎重に!

下の方にある Sign In with Apple を有効化しましょう。ここもあとから変更できます。

確認画面が出てくるので Register で完了です。

Service ID の登録

Web or Android でサインイン機能を実装する場合、Service ID の設定と構成も必要です。

App ID と同じ文字列では登録できないので、適当に作成。(App ID と同じ文字列だと、2枚目のエラーになる)

登録した Service ID を再度開くと、Sign in with Apple の設定が出てくるようになるので、チェックを入れて「Configure」を押下します。

「Domains and Subdomains」は、Web でのサインインを使用しない場合も何かしら登録しなくてはいけないようです(ドキュメントより)。ドメインの認証が発生するわけではないので、ドメインを持っていない場合はexample.com で良いと思います。(Return URLsはのちに出てくるFirebaseへのAppleサインインの設定後にコピペします。後述します。)

確認画面で Done で完了です。Service ID の編集画面に戻るので、Continue → Save で保存します。

続けて認証鍵を作成します。Keys から Create a key を押下。

Key Name をつけ、Sign in with Apple にチェックを入れ「Configure」に進みます。

Primary App ID で先ほど作成したApp ID を選択すると、Grouped App IDs という項目が出てきます。(ここは複数のアプリを統合してサインイン機能を作成する場合に関係のある項目なので、そうでない場合は設定項目はありません。)Save を押下し、認証鍵作成画面に戻るので Continue → Register に進みます。

ダウンロード画面に移行します。このページは二度と開けず、また一度ダウンロードするともう一度ダウンロードすることはできません。(画像はダウンロード後。「Download」がグレーアウトしています。)確実にダウンロードし、保存できていることを確認しましょう。ダウンロードしたら「Done」で終了します。

FirebaseコンソールでAppleサインインの設定をする

公式ドキュメントはこちら。
https://firebase.google.com/docs/auth/ios/apple?hl=ja&authuser=0&_gl=1*zc0mcu*_ga*NDA3ODM5NjUuMTcyNzQzNzMwOQ..*_ga_CW55HF8NVT*czE3NDgyOTcxNjUkbzI5OCRnMSR0MTc0ODI5NzE3MCRqNTUkbDAkaDAkZGFYSVBkX0dqOV9UdU05TmJic3FaLVQ4R181QWcxOW1UakE.

ドキュメントだけだとちょっと混乱したので手順を載せます。

Firebase側の登録作業

FirebaseコンソールのAuthentication → ログイン方法 → 新しいプロバイダを追加

Appleを選択

「有効にする」をONにした状態。iPhoneのみの場合は不要ですが、Androidも実装する場合はApple Developerページで作成したサービスID(App IDではなくService ID)を入力します。「OAuth コードフローの構成(省略可)」を押下し、追加設定の欄を展開します。

チームIDは、Apple Developer Programのアカウントページをスクロールすると出てくる「メンバーシップの詳細」にあります。

キーIDは、先ほど認証鍵を作成した、アカウントトップページ → ID → Keys で確認できます。

秘密鍵は、先ほどダウンロードした「.p8」ファイルを、ブラウザにドラッグするとファイルが開き中を閲覧できるので、表示された文字列をコピーして貼り付けます

最後に、設定画面下部の認証コールバックURLをコピーして、保存を押下します。

Appleアカウント画面での紐付け作業

Apple のアカウント画面で、プログラムのリソース → IDで登録した App ID を選択し、Sign In with Apple → Edit を開きます。

Enable as a primary App ID のままでOK。エンドポイントの欄に、先ほどFirebaseでコピーしたURLを貼り付けます。

Save で設定を保存します。

続いて Service ID から先ほど登録したIDを開き、Sign In with Apple の「Configure」を押下します。追加ボタンを押下。

Return URLs に、先ほどFirebaseでコピーしたURLを貼り付けます。

保存し完了です。

XCODEへDeveloperアカウントとiPhone実機を紐付け、Appleサインインを導入する

後述しますが、Appleサインインはシミュレーターでの動作はサポートされておらず実際にもAndroid/iOSともにシミュレーターでは動きませんでした。

アカウント・実機の紐付けはこちら

続いてAppleサインインを有効化します。Runner → Signing &Capabilities で追加ボタンを押下

リスト(かなり長い)から Sign in with Apple を選択

表示されるようになれば完了!

パッケージを導入しサインイン処理とボタンを実装

pubspec.yaml:パッケージを導入

YAML
dependencies:
  # Sign in
  google_sign_in:
  google_sign_in_ios:
  sign_in_with_apple:
  sign_in_button:
  # 略

処理は他のサインイン方法とまとめて一箇所に記載しています。

Dart
import 'package:firebase_auth/firebase_auth.dart';
import 'package:google_sign_in/google_sign_in.dart';
import 'package:sign_in_with_apple/sign_in_with_apple.dart';
import 'package:dayby/infrastructure/analytics/google_analytics_tags.dart';

class AuthService {
  // ------------------------------
  // メールアドレスとパスワード
  // ------------------------------
  Future<void> mailSignUp(String email, String password) async {
    await FirebaseAuth.instance.createUserWithEmailAndPassword(
      email: email,
      password: password,
    );
    await GATags().prcAUmailSignUp();
  }

  Future<void> mailSignIn(String email, String password) async {
    await FirebaseAuth.instance.signInWithEmailAndPassword(
      email: email,
      password: password,
    );
    await GATags().prcAUmailSignIn();
  }

  Future<void> passReset(String email) async {
    await FirebaseAuth.instance.sendPasswordResetEmail(
      email: email,
    );
    await GATags().prcAUpassReset();
  }

  // ------------------------------
  // Googleで新規登録&サインイン
  // ------------------------------
  Future<void> googleSignUpIn() async {
    // アプリが知りたい情報
    const scopes = ['openid', 'profile', 'email'];

    // サインインリクエスト
    final request = GoogleSignIn(scopes: scopes);
    final response = await request.signIn();

    // 戻り値からアクセストークンを取得
    final authn = await response?.authentication;
    final accessToken = authn?.accessToken;

    // アクセストークンがnull=サインイン途中に離脱 処理を抜ける
    if (accessToken == null) return;

    // Firebaseへアクセストークンを送る
    final oAuthCredential =
        GoogleAuthProvider.credential(accessToken: accessToken);
    await FirebaseAuth.instance.signInWithCredential(oAuthCredential);

    await GATags().prcAUgoogleSignUpIn();
  }

  // ------------------------------
  // AppleIDで新規登録&ログイン
  // ------------------------------
  Future<void> appleSignUpIn() async {
    final appleProvider = AppleAuthProvider();
    if (kIsWeb) {
      await FirebaseAuth.instance.signInWithPopup(appleProvider);
    } else {
      await FirebaseAuth.instance.signInWithProvider(appleProvider);
    }
    await GATags().prcAUappleSignUpIn();
  }

  // ------------------------------
  // サインアウト
  // ------------------------------
  Future<void> signOut() async {
    await FirebaseAuth.instance.signOut();
    await GATags().prcAUsignOut();
  }

  // ------------------------------
  // 退会
  // ------------------------------
  Future<void> deleteUser() async {
    await FirebaseAuth.instance.currentUser?.delete();
  }
}

ボタンはsign_in_buttonパッケージを使うと簡単に実装できます。テキストは端末の画面サイズによって文字がオーバーしてしまうので、横幅によって文言を変えています。sign_in_with_appleパッケージにも
SignInWithAppleButtonというウィジェットが用意されているらしいので、そちらでも良いでしょう。AppleサインインもGoogleサインインも、公式によるUIのレギュレーション(決まりごと)がありますので、好きなようにデザインすることはできません。

Dart
final appleSignInButton = SizedBox(
  width: MySize.signinButtonWidth(context), // 画面サイズの7割
  height: MySize().signinButtonHeight, // 40
  child: SignInButton(
    Buttons.apple,
    text: (fieldWidth(context) < 300) ? 'Appleサインイン' : 'Appleアカウントを使う',
    onPressed: () async {
      await AuthService().appleSignUpIn();
      await userDelete();
    },
  ),
);

ビルド時の注意点

さて、悲しい事実なのですが、

  • iPhoneの場合、サポートしているのは物理デバイスのみ。シミュレーターは動作せず。(iOS14のみの不具合という記事もありましたが、iOS15, 16ともにエラーになりました🥺)
  • Androidの場合、そもそもサポートされていない模様だが実機では本記事の手順により無事動いた。シミュレーターは動作せず。
  • サポートしているのはリリースビルドのみ(普段みなさんが開発中に行なっているのはデバッグビルド。)

ということで、実機&リリースモードで動作検証が必要です。

Android実機でアプリを動かす方法はこちら

iPhone実機でアプリを動かす方法はこちら

リリースモードのビルドコマンドはこちら

flutter run --release

エラーと対処

その他に出くわしたエラーたち。この記事はエラー対処含めた手順を整理して記載していますが、実際には何度も立ち戻って対処していました•••

実機でのwirelessビルドができない

登録したデバイスは、有線接続していなくてもVS CODE上に出てくるのでビルドしてみたところ、接続エラーに。

Exception attempting to connect to the VM Service: SocketException: Connection failed (OS Error: Address already in use, errno = 48), address = xxx.xx.xx.x, port = xxxxx
This was attempt #400. Will retry in 0:00:01.600000.

有線接続した状態でのビルドに切り替えることで回避しました。

ちなみに、たまにケーブルを刺したのに認識されずVS CODE上で「iPhone名(wireless)」しか表示されないことがありますが、その状態でのビルドは通りました😇

XCODEでAppleサインインを有効化していない

Sign in with Apple errored: Error Domain=com.apple.AuthenticationServices.AuthorizationError Code=1000 "(null)"

手順を見返して実施しましょう!

Flutter側のコードのエラー

古めの記事を参照した以下のコードでは、getAppleIDCredential が通らずエラーになってしまいました。

Dart
import 'package:firebase_auth/firebase_auth.dart';
import 'package:google_sign_in/google_sign_in.dart';
import 'package:sign_in_with_apple/sign_in_with_apple.dart';
import 'package:dayby/infrastructure/analytics/google_analytics_tags.dart';

class AuthService {
  // ------------------------------
  // AppleIDで新規登録&ログイン
  // ------------------------------
  Future<void> appleSignUpIn() async {
    // アプリが知りたい情報
    const scopes = [
      AppleIDAuthorizationScopes.email,
      AppleIDAuthorizationScopes.fullName,
    ];

    // OAthCredentialインスタンス作成
    OAuthProvider oauthProvider = OAuthProvider('apple.com');
    final appleCredential =
        await SignInWithApple.getAppleIDCredential(scopes: scopes);
    final oAuthCredential = oauthProvider.credential(
      idToken: appleCredential.identityToken,
      accessToken: appleCredential.authorizationCode,
    );

    // Firebaseへアクセストークンを送る
    await FirebaseAuth.instance.signInWithCredential(oAuthCredential);
  }
}
flutter: ----------------FIREBASE CRASHLYTICS----------------
flutter: SignInWithAppleAuthorizationException(AuthorizationErrorCode.unknown, The operation couldn’t be completed. (com.apple.AuthenticationServices.AuthorizationError error 1000.))
flutter: 
#0      MethodChannelSignInWithApple.getAppleIDCredential (package:sign_in_with_apple_platform_interface/method_channel_sign_in_with_apple.dart:86:7)
<asynchronous suspension>
#1      AuthService.appleSignUpIn (package:dayby/infrastructure/auth/auth.dart:72:9)
<asynchronous suspension>
#2      Social.build.<anonymous closure> (package:dayby/presentation/pages/_boot/signup_and_signin_social.dart:36:11)
<asynchronous suspension>
flutter: ----------------------------------------------------

コメント

タイトルとURLをコピーしました