RareJob Tech Blog

レアジョブのエンジニア・デザイナーによる技術ブログです

iOS 版レアジョブアプリが Sign in with Apple に対応した話

APP/UX チームの玉置です。
今回は iOS 版レアジョブ アプリの Sign in with Apple 対応が完了しましたので、それについてエモい話をします。
はい、 Sign in with Apple にやっと対応することができました。非常に大変でした。

【目次】

Sign in with Apple とは何か

まずモバイルアプリ界隈では非常に有名ですが、その他の業界ではSign in with Appleの認知度はそこまで普及していないので簡単に説明してみます。
Sign in with AppleApple が開発した Apple アカウント( AppleID )を使ったソーシャルログインの認証システムです。
例えば、 TwitterFacebook のソーシャルログインといった感じのものですね。

レアジョブ が Sign in with Apple に対応した背景と対応後のデザイン

去年、 Apple が突如 iOS13 と一緒に Sign in with Apple を公開して「皆の者、これに対応しなさい!」という号令が発表されました。
全てのアプリがこれに対応しなければならないわけではなく、 FacebookTwitter といった Apple 以外のソーシャルログインを実装しているアプリが対象にされました。

6月末までにソーシャルログインを使う場合は Sign in with Apple 対応が必要になりました。レアジョブは Facebook ログインをサポートしています。
レアジョブ における Facebook ログインユーザーの比率が無視できないぐらい高いので、この Sign in with Apple に対応しなければならなくなりました。

もともとのレアジョブの登録・ログイン画面はこちらになります。

登録画面 ログイン画面
f:id:qed805:20200820135650p:plain
過去の登録画面
f:id:qed805:20200820135712p:plain
ログイン画面

それが Sign in with Apple の対応で iOS 13 以上と iOS 12 以下で見え方が変わるように対応しました。

iOS 13 登録 iOS 13 ログイン画面
f:id:qed805:20200820135924p:plain
リリース後のiOS13での登録画面
f:id:qed805:20200820135956p:plain
リリース後のiOS13でのログイン画面
iOS 12 登録 iOS 12 ログイン画面
f:id:qed805:20200820140048p:plain
リリース後のiOS12以下での登録画面
f:id:qed805:20200820140119p:plain
リリース後のiOS12以下でのログイン画面

スケジュールと見積もりについて

プロダクトオーナーに見積もりを提示してスケジュールを引くために Sign in with Apple の仕組みと Apple サーバーから得られる情報を確認する必要がありました。
Sign in with Apple のサンプルプロジェクトを知ったのは実際に開発が始まってからでしたので、見積もり段階ではどんな情報がどんな風に得られるかわからなかったのですね。

そのため自分のマイマシンで Sign in with Apple を実装する必要がありました。実装すること自体はそんな手間ではありませんでした。

これから Sign in with Apple を実装するのでしたら、先に Apple が公開しているサンプルプロジェクトを確認しておいた方がいいと思います。

Appleのドキュメントとサンプルプロジェクトファイルのダウンロードページ

https://developer.apple.com/documentation/authenticationservices/implementing_user_authentication_with_sign_in_with_apple

見積もりは正確な情報が分かりませんでしたので、 Sign in with Apple 自体のシステムの調査として

  1. 実装方法
  2. 得られるデータの確認
  3. 仕様の調査

の3つの項目を2回に分けて見積もりしました。

アプリ側の実装自体は5人日ぐらいのボリューム感でしたが、 Web 版や API との連携も含めた見積もりで2週間に膨れ上がってしまいました。

開発前に不明であった内容について

  1. ログイン後の挙動 (email, fullnameの扱い)
  2. メールを非公開の内容
  3. ログアウトの方法

1. ログイン後の挙動 (email, fullnameの扱い)

Sign in with Apple で有名なのがログイン後に取得できるデータについてです。
Apple サーバーから取得できるユーザー情報の一部に email と fullname の情報がありますが、この情報は最初の1回目しか取得できず2回目以降は空が渡ってくることです。

再度 email と fullname を取得する手段は残されていて、端末の設定アプリから

Apple ID > パスワードとセキュリティ > Apple IDを使用中のApp > 特定のアプリ

で表示される「Apple IDの使用を停止する」をタップして停止させるとアプリでの Sign in with Apple の使用が止まって 再度 email と fullname を取得できるようになります。

2. メールを非公開の内容

次に Apple 認証の際に Apple ID に紐づいているメールアドレスを公開するかどうかの選択できる部分の仕様について紹介します。

Apple 認証を用いるとアプリで使用する際にメールアドレスを公開するかどうかを選択できます。

  • 「メールを共有」
  • 「メールを非公開」

の2つのオプションがあります。

「メールを共有」して Sign in with Apple するとアプリ側でユーザーのメールアドレスが公開されます。
「メールを非公開」して Sign in with Apple すると Apple 側で生成されたプライベート用のメールアドレスが発行されます。

abcde12345@privaterelay.appleid.com
alk32943dw@privaterelay.appleid.com
bl31dkr03e@privaterelay.appleid.com

このような形式です。
アットマーク前は10文字の固定長のランダム文字列というのが特徴的です。

3. ログアウトの方法

こちらは Sign in with Apple のログアウト方法です。
1でApple IDの使用を停止する」をタップして使用を停止させるとそれがログアウトになる仕様のようでした。

フロント側で必要な対応

  • iOS 12 以下と iOS 13 以降での画面の切り分け
  • ユーザー情報のキーチェーンへの保存
  • アプリ起動時に Apple 認証しているかどうかのチェック

iOS 12 以下と iOS 13 以降での画面の切り分け

Sign in with AppleiOS 13 以降でした使えないので端末バージョンで処理を分けるしかありません。
iOS に ContainerView というコンポーネントがありますので ViewControler を分離させました。

    override func viewDidLoad() {
        super.viewDidLoad()
        setupSocialLoginView()
    }

    private func setupSocialLoginView() {
        if #available(iOS 13, *) {
            /// iOS 13以降で走る処理
            let socialNewVC = UIStoryboard.load("SocialRegister", idetifier: "SocialNewRegisterViewController") as! SocialNewRegisterViewController
            addSocialContainer(vc: socialNewVC)
        } else {
            /// iOS 12以下で走る処理
            let socialOldVC = UIStoryboard.load("SocialRegister", idetifier: "SocialOldRegisterViewController") as! SocialOldRegisterViewController
            addSocialContainer(vc: socialOldVC)
        }
    }

このように分岐させました。

Container 側のデザインは storyboard でデザインしています。

f:id:qed805:20200721140543p:plain
storyboard

ユーザー情報のキーチェーンへの保存

Apple 認証後に得られるユーザー情報をAPIに送信するのですが、確実に送信させないと行けませんので保存する領域を決めないといけませんでした。

ちょうどレアジョブアプリは iPhone のキーチェーン( Keychain )を使っていますので、得られたユーザー情報の一部をキーチェーンの領域に保存することにしました。

アプリ起動時に Apple 認証しているかどうかのチェック

Sign in with Apple でログイン中のレアジョブユーザーが再度レアジョブ アプリに戻ってきたときにログイン中かどうかを確認する必要がありました。

そこで AppDelegate で user をキーにしてログイン中かどうかを判別することにしました。
ログイン中であればその状態を維持して、ログアウトされていればアプリをログアウトする必要があります。

ソースコード上では次のような対応を行いました。

    private func checkAppleLoginStatus() {
        /// Apple認証後のuserの情報をキーチェーンから取得する
        guard let userIdentifier = UserDataStore.currentUserIdentifier else { return }
        /// Apple ログイン中かどうかキーチェーンの情報を使って確認する
        if #available(iOS 13.0, *) {
            let appleIDProvider = ASAuthorizationAppleIDProvider()
            appleIDProvider.getCredentialState(forUserID: userIdentifier) { (credentialState, error) in
                switch credentialState {
                case .authorized:
                    /// 端末側のApple ログイン中
                    break
                case .revoked, .notFound:
                    /// 端末側のAppleログインセッション切れ
                    // ログアウトする
                default:
                    break
                }
            }
        }
    }

Web フロントへの対応で共有すべき情報

  • Web でのログインに必要な ServiceID と Key
  • Web からのメール送信ができるようにするための対応
  • AppId と ServiceId との関係

Web でのログインに必要な ServiceID と Key

Web 版 Sign in with AppleApple 認証で必要なものがありました。 ServiceID と Key です。どちらも Apple Developer サイトで設定・取得できるものです。
ServiceID とは iOS アプリの AppID と似たものです。

iOS アプリでは BundleID を発行してユニークなIDを発行しますが、Web に該当するものが ServiceID になります。 ServiceID は AppID と同じものは指定できずさらにユニークなものにしなければなりません。 ServiceID はApplD.xxxx みたいに使っている AppID の後にユニークな文字列を追加しました。

さらに Key も設定してファイルをダウンロードして Web エンジニアに共有しました。

Web からのメール送信ができるようにするための対応

レアジョブのサービスに Web サーバーからメール送信する機能があります。例えば、レッスン予約時やレッスン開始前に送信される確認メールだったりします。

実は上で話しました「メールを非公開」にして Apple 認証すると Apple がプライベートなメールアドレスを生成します。
このメールアドレスに何も設定せずにメールを送信しようとするとエラーが発生して「メールが送れません」みたいなエラーメールが返ってきて正常にメールを送信できません。

そこでサービス元から Apple のプライベートメールアドレスにメールを送信するために Apple Developer サイトで設定を行う必要があります。
普段のアプリ開発のリリースでは触らない Certificates ページの「 More 」の項目に進みます。

f:id:qed805:20200721163337p:plain
Moreページ

「 Configure 」ボタンをクリックすると「Configure Sign in with Apple for Email Communication」ページにアクセスできます。
このページで Apple のプライベートメールアドレスに送信する「送信元のメールアドレス」を登録する必要があります。

上限があるのかまでは確認していませんが考えられるメール送信元のメールアドレスを全て登録しておきます。
この設定で Apple のプライベートメールアドレスにメールを送信できるようになります。

詳細について知りたい場合は Apple 公式のドキュメントがありますのでこちらを確認すれば把握できると思います。

Configure Private Email Relay Service

https://help.apple.com/developer-account/?lang=en#/devf822fb8fc

AppId と ServiceId との関係

最後に AppID と ServiceID との関係についてです。

さらっと説明すると
AppID は iOS アプリ側で Apple 認証に必要な ID で、 Web で Apple 認証するのに必要なものが ServiceID です。

AppID と ServiceID は密接に紐づいています。
AppID でApple 認証した AppleID アカウントであれば、それに紐づいている ServiceID を使えば同じ AppleID アカウントで Web 版の Apple 認証に成功することができます。
AppID と ServiceID が紐づいていないもので Apple 認証をしようとすると失敗してしまいます。

そこだけ注意すれば大丈夫でした。

最後に

とても長くなりましたが無事にアプリと Web とで Sign in with Apple に対応できましたので、是非とも使ってみてください。
リリースしてからしばらく経っていることもあって利用者の方も徐々に増え始めています。とても嬉しいことですね!
実際にログインするときは指紋認証か顔認証でログインできるようになりますので文字入力をする必要がないのはとても快適です。