TypeScript PR

Expo+React NativeでGoogle Sign Inを実装する方法

記事内に商品プロモーションを含む場合があります

個人WebアプリでログインはGoogle認証に任せてるんですが、
MobileアプリでもGoogle認証にする方が統一感あるかなぁと
今回Expo+React NativeでiOS向けに試してみたのでそのメモです。
※MacPCで、Expoの準備とXcodeはインストール済み想定

結論としては、公式にあるようにExpoだけでは実現できず、
Nativeコードも必要になるんですが、色々はまったので備忘録がてら・・・。
ちなみに使ったライブラリは「@react-native-google-signin/google-signin

The @react-native-google-signin/google-signin library can’t be used in the Expo Go app because it requires custom native code.

Custom Native CodeとGoogle Sign Inを使う準備

公式にあるように進めます。
まずはNative Codeを追加

npx expo run:ios

次にGoogle Sign In用のライブラリをインストール

npx expo install @react-native-google-signin/google-signin

ハマったのは、「npx expo run:ios」の実行時に
なんかiOSシミュレーターが起動できません。みたいなエラーが出た点↓

Error: xcrun exited with non-zero code: 60
An error was encountered processing the command (domain=NSPOSIXErrorDomain, code=60):
Unable to boot the Simulator.
launchd failed to respond.
Underlying error (domain=com.apple.SimLaunchHostService.RequestError, code=4):
        Failed to start launchd_sim: could not bind to session, launchd_sim may have crashed or quit responding

「xcrun simctl erase all」あたりのコマンド試したけどダメで、
結局、Macの設定からキャッシュをクリアしたら、解決しました!
About this Mac -> More Info… -> General
-> Storage -> Developer -> Infoアイコンと移動して、
シミュレータのキャッシュを削除した感じです。

developer-info-icon
ios-simulator-cache

Google Sign Inの実装

とりあえずGoogleログインできて
ユーザ情報が取れるとこまで確認しました。
今後、ちょっとリファクタリングするかもですが
Authのコードは以下のような感じです。

GoogleのAPIページでiOSのOAuthのClientIDは作っておいてください。
iosClientIdで指定します。

import { GoogleSignin, User } from '@react-native-google-signin/google-signin';
import { createContext, useContext, useEffect, useState } from 'react';

type Auth = {
    user: User | null;
    loading: boolean;
    googleSignIn: () => Promise<boolean>;
};

const AuthContext = createContext<Auth>({} as Auth);

export const useAuthContext = () => {
  return useContext(AuthContext);
};

const useAuthProvider = () => {
    const [user, setUser] = useState<User | null>(null);
    const [loading, setLoading] = useState(true);
    useEffect(() => {
        GoogleSignin.configure({ iosClientId: "GoogleのCliendID" });
    }, []);

    const googleSignIn = async (): Promise<boolean> => {
        setLoading(true);
        try {
            await GoogleSignin.hasPlayServices();
            const userInfo = await GoogleSignin.signIn();
            if (userInfo) {
                console.log(userInfo);
                setUser(userInfo);
            }

            setLoading(false);
            return true;
        } catch {
            setLoading(false);
            return false;
        }
    };

    return {
        user,
        loading,
        googleSignIn,
    };
}

export const AuthProvider: React.FC<{ children: React.ReactNode }> = ({ children }) => {
  const authContext = useAuthProvider();

  return <AuthContext.Provider value={authContext}>{children}</AuthContext.Provider>;
};

これを使うログイン画面は以下のような感じ

import React from 'react';
import {
  Button,
  SafeAreaView,
  Text,
} from 'react-native';
import { useAuthContext } from '../context/auth';

export const LoginScreen = () => {
  const { googleSignIn } = useAuthContext();

  return (
     <SafeAreaView>
      <Text>ログイン画面</Text>
      <Button onPress={googleSignIn} title={'Googleログイン'} />
    </SafeAreaView>
  );
};

ハマりポイントとしては、
「npx expo start」にて
今まで実機のiPhoneで動作確認してたのですが、これがNativeコード使うと
できないらしく・・・「npx expo run:ios」でiOSシミュレータを使うことに
歩数のカウントとかしてるので、実機が良かったんですが、うーむ
EASビルドとかTest Flightとか使うらしいのですが、
また記事にしますかねぇ。

ちなみに「npx expo start」で起動すると
そもそもLinkエラーになるので無理そうです。↓

ERROR  Error: RN GoogleSignin native module is not correctly linked. Please read the readme, setup and troubleshooting instructions carefully.
If you are using Expo, make sure you are using Custom dev client, not Expo go., js engine: hermes
 ERROR  Invariant Violation: "main" has not been registered. This can happen if:
* Metro (the local dev server) is run from the wrong folder. Check if Metro is running, stop it and restart it in the current project.
* A module failed to load due to an error and `AppRegistry.registerComponent` wasn't called., js engine: hermes

他にはInfo.plistに
Google OAuthの「iOS の URL スキーム」を
書かないとエラー↓になりますので書きましょう!

[CoreFoundation] *** Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: 'Your app is missing support
for the following URL schemes: com.googleusercontent.apps.XXXXXXXXXXXXXXXXXXXXXXXXXX'
*** First throw call stack:

app.jsonに以下を追記しましょう〜。

    "ios": {
      "supportsTablet": true,
      "bundleIdentifier": "com.mydomain.mywebapp",
      "infoPlist": {
        "CFBundleURLTypes": [
          {
            "CFBundleURLSchemes": ["com.googleusercontent.apps.XXXXXXXXXXXXXXXXXXXXXXXXXX"]
          }
        ]
      }
    },

まとめ

本記事では、
「Expo+React NativeでGoogle Sign Inを実装する方法」を紹介しました。

Google認証などSSOが最近主流になりつつ気がしており
React NativeとExpoでも簡単にできるかなと思ったのですが、
意外にハマったので、記事にしてみました。

あとは、Nativeコードを気にせずに書けて、
実機で簡単に動かせるのがExpoの良いところだと思うのですが、
Google SignInにはNativeコードが必要ということで少し残念でした。
それでも回避方法がなんとか見つかったので良かったです!

同じような問題にハマった誰かの参考になれば幸いです!!