前回の記事で、iOSアプリ(習慣トラッカー)のガワができあがり、シミュレーターで動かすところまで完了しました。
今回は、Android編と同じように、このアプリに「Googleログイン機能(Firebase Authentication)」を組み込みます。
「Flutterなんだから、Androidの時に書いたコードそのままで動くんじゃないの?」
半分正解で、半分不正解です。Dartのコードは全く同じで動きますが、「外枠(iOS側)」の初期設定が必要になります。
1. Web・Androidとの違いをおさらい
モバイルアプリにおける認証の考え方は、Webとは大きく異なります。
APIキーをサーバー(.env)に隠せるWebと違い、モバイルアプリは設定ファイルをアプリ内に同梱して配布するため、キーを完全に隠すことができません。
そこで、OSごとに「このアプリからしかアクセスさせない」という証明書(バリア)を使います。
- Androidの場合:
google-services.json+ SHA-1(署名)で証明しました。 - iOSの場合:
GoogleService-Info.plist+ Bundle ID で証明します。
iOSの場合はSHA-1のような複雑な鍵の抽出は不要で、前回の記事で設定した「Bundle ID(例: tech.simplekits.habitTracker)」がそのまま証明書代わりになります。
2. iOS特有の「2つの重要ファイル」
作業に入る前に、今回触るiOS側のファイルの役割を理解しておきましょう。
| ファイル名 | 役割 |
|---|---|
GoogleService-Info.plist | 【Firebaseのパスポート】 プロジェクトIDやAPIキーが書かれたファイル。Androidの .json のiOS版です。 |
ios/Runner/Info.plist | 【アプリの取扱説明書】 アプリの名前や、マイク・カメラの許可メッセージなどを書き込むファイル。今回はここに「URLスキーム」という呪文を追記します。 |
3. 【準備】Firebaseコンソールでの設定
Step 1: iOSアプリを追加する
- Firebaseプロジェクトを開き、「アプリを追加」から「iOS(Appleマーク)」をクリックします。
- Apple バンドル ID に、前回Xcodeで設定したBundle IDを入力し、「アプリを登録」を押します。
Step 2: 設定ファイルを追加する(※要注意!)
ここがiOS開発初心者が100%つまずく最大の罠です。
Androidの時はVS Code上でファイルをドラッグ&ドロップするだけでOKでしたが、iOSでそれをやるとエラーになってアプリが起動しません。
- Firebaseから
GoogleService-Info.plistをダウンロードします。 - Xcode を開きます(
ios/Runner.xcworkspaceをダブルクリック)。 - ダウンロードした
GoogleService-Info.plistを、Xcodeの画面上にある「Runner」フォルダの中に直接ドラッグ&ドロップします。 - 「Copy items if needed」にチェックが入っていることを確認して「Finish」を押します。
Step 3: URLスキームの登録(Info.plist)
Googleログイン画面(ブラウザ)から、自分のアプリに「戻ってくる」ための道しるべを設定します。
- 先ほど追加した
GoogleService-Info.plistをXcode上で開き、REVERSED_CLIENT_IDという項目の値(com.googleusercontent.apps.XXX...)をコピーします。 - 次に、Xcodeで
Info.plistを開きます(またはVS Codeでios/Runner/Info.plistを開きます)。 - ファイルの一番下の
</dict>の直前に、以下のコードを貼り付けます。
※コピーしたREVERSED_CLIENT_IDに書き換えてください。
<key>CFBundleURLTypes</key>
<array>
<dict>
<key>CFBundleTypeRole</key>
<string>Editor</string>
<key>CFBundleURLSchemes</key>
<array>
<!-- ▼ ここにさっきコピーしたREVERSED_CLIENT_IDを貼り付ける -->
<string>com.googleusercontent.apps.1234567890-abcdefg...</string>
</array>
</dict>
</array>
これで外枠の準備は完了です。Xcodeを閉じてVS Codeに戻りましょう。
4. 【実装】Flutterコードの修正
VS Codeのターミナルで、必要なパッケージが追加されているか確認します(Android編で既に入れていればスキップでOKですが、念のため実行しましょう)。
flutter pub add firebase_core firebase_auth google_sign_in
そして lib/main.dart を記述します。コードはAndroid編と全く同じです。
Firebase SDKが優秀なので、私たちが「Shared Preferencesなどにログイン情報を手動で保存する」といった面倒な処理を書く必要は一切ありません。
import 'package:flutter/material.dart';
import 'package:firebase_core/firebase_core.dart';
import 'package:firebase_auth/firebase_auth.dart';
import 'package:google_sign_in/google_sign_in.dart';
void main() async {
// ▼ アプリ起動前にFirebaseを初期化
WidgetsFlutterBinding.ensureInitialized();
await Firebase.initializeApp();
runApp(const MyApp());
}
class MyApp extends StatelessWidget {
const MyApp({super.key});
@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Habit Tracker',
theme: ThemeData(
primarySwatch: Colors.indigo,
useMaterial3: true,
),
// ▼ authStateChanges() でログイン状態を監視(トークン保存処理などは不要!)
home: StreamBuilder<User?>(
stream: FirebaseAuth.instance.authStateChanges(),
builder: (context, snapshot) {
if (snapshot.connectionState == ConnectionState.waiting) {
return const Center(child: CircularProgressIndicator());
}
if (snapshot.hasData) {
return const HabitListScreen(); // ログイン済み
}
return const LoginScreen(); // 未ログイン
},
),
);
}
}
// ▼ ログイン画面
class LoginScreen extends StatelessWidget {
const LoginScreen({super.key});
Future<void> _signInWithGoogle() async {
try {
final GoogleSignInAccount? googleUser = await GoogleSignIn().signIn();
if (googleUser == null) return;
final GoogleSignInAuthentication googleAuth = await googleUser.authentication;
final credential = GoogleAuthProvider.credential(
accessToken: googleAuth.accessToken,
idToken: googleAuth.idToken,
);
await FirebaseAuth.instance.signInWithCredential(credential);
} catch (e) {
print(e);
}
}
@override
Widget build(BuildContext context) {
return Scaffold(
body: Center(
child: ElevatedButton.icon(
icon: const Icon(Icons.login),
label: const Text('Googleでログイン'),
onPressed: _signInWithGoogle,
),
),
);
}
}
// ▼ 習慣リスト画面
class HabitListScreen extends StatefulWidget {
const HabitListScreen({super.key});
@override
State<HabitListScreen> createState() => _HabitListScreenState();
}
class _HabitListScreenState extends State<HabitListScreen> {
final List<String> _habits = [];
@override
Widget build(BuildContext context) {
final user = FirebaseAuth.instance.currentUser;
return Scaffold(
appBar: AppBar(
title: Text('${user?.displayName ?? "ゲスト"}の習慣'),
actions: [
IconButton(
icon: const Icon(Icons.logout),
onPressed: () async {
await FirebaseAuth.instance.signOut();
},
)
],
),
body: const Center(child: Text('リストは次回DB連携で実装します')),
);
}
}
5. 動作確認
パッケージを追加し、iOS側のネイティブ設定(Info.plistなど)を変更したので、現在起動しているシミュレーターのアプリを一度完全に停止(■ボタン)してから、再度デバッグ実行(F5)してください。
※ここで再起動しないと、URLスキームの変更が反映されずログイン後にアプリに戻ってこれません。
- アプリが起動し「ログイン画面」が表示されます。
- 「Googleでログイン」を押すと、「"アプリ名"がサインインのために"google.com"を使用しようとしています」というiOS特有の警告が出ます。「続ける」を押します。
- ブラウザが開くので、Googleアカウントでログインします。
- 【成功の瞬間】 ブラウザが閉じ、アプリの「習慣リスト」画面に自動で戻ってくれば成功です!
アプリを一度タスクキル(上にスワイプして強制終了)し、もう一度起動してみてください。
SDKがログイン状態を記憶してくれているため、ログイン画面をスキップしていきなりリスト画面が表示されるはずです。
まとめ:iOSならではの「お作法」に慣れよう
お疲れ様でした!
DartのコードはAndroidと全く同じなのに、裏側では「Xcodeへのファイルの追加」や「Info.plistの設定」といったiOS特有の作業が必要になることが体感できたと思います。
また、今回は触れませんでしたが、Android編で解説した「Google Cloud ConsoleでのAPIキー制限」は、iOSの GoogleService-Info.plist に含まれるAPIキーに対しても同様に設定(Bundle IDでの制限)を行うべきです。リリース前には必ず設定しておきましょう。