{"rendered_body":"\u003cp data-sourcepos=\"3:1-3:164\"\u003e自身の知識の整理と備忘録も兼ねて「\u003cstrong\u003eSwift UI × MVVMアーキテクチャ\u003c/strong\u003e」でログイン機能を実装する流れをまとめてみます。\u003c/p\u003e\n\u003cp data-sourcepos=\"5:1-5:192\"\u003e間違っている点やこうした方が良くなるなどの意見がありましたらコメントくださると嬉しいです。完成したものはGitHubにて公開しています！\u003c/p\u003e\n\u003cp data-sourcepos=\"7:1-7:73\"\u003e\u003ciframe id=\"qiita-embed-content__cbb3e8247ecd310c733e0b1db07cd2e3\" src=\"https://qiita.com/embed-contents/link-card#qiita-embed-content__cbb3e8247ecd310c733e0b1db07cd2e3\" data-content=\"https%3A%2F%2Fgithub.com%2Famefure%2FiOS-Login-function-following-MVVM-architecture\" frameborder=\"0\" scrolling=\"no\" loading=\"lazy\" style=\"width:100%;\" height=\"29\"\u003e\n\u003c/iframe\u003e\n\u003c/p\u003e\n\u003ch1 data-sourcepos=\"9:1-9:8\"\u003e\n\u003cspan id=\"環境\" class=\"fragment\"\u003e\u003c/span\u003e\u003ca href=\"#%E7%92%B0%E5%A2%83\"\u003e\u003ci class=\"fa fa-link\"\u003e\u003c/i\u003e\u003c/a\u003e環境\u003c/h1\u003e\n\u003cul data-sourcepos=\"11:1-16:0\"\u003e\n\u003cli data-sourcepos=\"11:1-11:11\"\u003eSwift UI\u003c/li\u003e\n\u003cli data-sourcepos=\"12:1-12:8\"\u003eXcode\u003c/li\u003e\n\u003cli data-sourcepos=\"13:1-13:12\"\u003eFirebase\u003c/li\u003e\n\u003cli data-sourcepos=\"14:1-14:13\"\u003eCocoa Pods\u003c/li\u003e\n\u003cli data-sourcepos=\"15:1-16:0\"\u003eMVVMアーキテクチャ\u003c/li\u003e\n\u003c/ul\u003e\n\u003cp data-sourcepos=\"17:1-17:212\"\u003eログイン機能を実装するにあたって全て自分で実装したわけではなくGoogleが提供しているクラウドサービスである\u003cstrong\u003eFirebaseのAuthentication\u003c/strong\u003eを利用していきます。\u003c/p\u003e\n\u003cp data-sourcepos=\"20:1-20:299\"\u003eAuthentication(以下Auth)を組み込むだけでユーザー情報の登録や操作、ログイン状態の管理、確認メールやリセットメールなどのメール送信機能を簡単に実装でき、かつ\u003cstrong\u003e無料の範囲でも1日3000人まで利用可能\u003c/strong\u003eになっています。\u003c/p\u003e\n\u003cp data-sourcepos=\"22:1-22:157\"\u003e\u003ca href=\"https://appdev-room.com/swift-firebase-realtime-authentication\" rel=\"nofollow noopener\" target=\"_blank\"\u003e【Swift UI/Firebase】Authentication導入方法と使い方！ログイン機能の実装\u003c/a\u003e\u003c/p\u003e\n\u003ch1 data-sourcepos=\"24:1-24:8\"\u003e\n\u003cspan id=\"概要\" class=\"fragment\"\u003e\u003c/span\u003e\u003ca href=\"#%E6%A6%82%E8%A6%81\"\u003e\u003ci class=\"fa fa-link\"\u003e\u003c/i\u003e\u003c/a\u003e概要\u003c/h1\u003e\n\u003cp data-sourcepos=\"25:1-25:300\"\u003e今回作成するログイン機能は以下の3つのプロバイダアカウントを使用できるように実装します。もしiOSアプリを公開するならAppleアカウントでのログインを実装しないとリジェクトされることもあるので注意してください。\u003c/p\u003e\n\u003col data-sourcepos=\"27:1-30:0\"\u003e\n\u003cli data-sourcepos=\"27:1-27:25\"\u003eE-mail/パスワード\u003c/li\u003e\n\u003cli data-sourcepos=\"28:1-28:24\"\u003eGoogleアカウント\u003c/li\u003e\n\u003cli data-sourcepos=\"29:1-30:0\"\u003eAppleアカウント\u003c/li\u003e\n\u003c/ol\u003e\n\u003cp data-sourcepos=\"31:1-31:114\"\u003eまたログイン機能を実装するにあたって必要になりそうな機能を洗い出してみます。\u003c/p\u003e\n\u003cul data-sourcepos=\"33:1-38:0\"\u003e\n\u003cli data-sourcepos=\"33:1-33:14\"\u003e新規登録\u003c/li\u003e\n\u003cli data-sourcepos=\"34:1-34:31\"\u003eログイン(サインイン)\u003c/li\u003e\n\u003cli data-sourcepos=\"35:1-35:37\"\u003eログアウト(サインアウト)\u003c/li\u003e\n\u003cli data-sourcepos=\"36:1-36:8\"\u003e退会\u003c/li\u003e\n\u003cli data-sourcepos=\"37:1-38:0\"\u003eパスワードリセット\u003c/li\u003e\n\u003c/ul\u003e\n\u003cp data-sourcepos=\"39:1-39:126\"\u003e英語の意味的にログインではなく「サインイン」が正しいようなのでサインインを使います。\u003c/p\u003e\n\u003ch1 data-sourcepos=\"42:1-42:8\"\u003e\n\u003cspan id=\"設計\" class=\"fragment\"\u003e\u003c/span\u003e\u003ca href=\"#%E8%A8%AD%E8%A8%88\"\u003e\u003ci class=\"fa fa-link\"\u003e\u003c/i\u003e\u003c/a\u003e設計\u003c/h1\u003e\n\u003cp data-sourcepos=\"44:1-44:110\"\u003e正しいかどうかわかりませんが\u003cstrong\u003eMVVMアーキテクチャに倣って整理\u003c/strong\u003eしてみました。\u003c/p\u003e\n\u003cp data-sourcepos=\"46:1-46:34\"\u003e\u003ciframe id=\"qiita-embed-content__642057e636e219376c8a9d553447fc20\" src=\"https://qiita.com/embed-contents/link-card#qiita-embed-content__642057e636e219376c8a9d553447fc20\" data-content=\"https%3A%2F%2Fappdev-room.com%2Fswift-mvvm\" frameborder=\"0\" scrolling=\"no\" loading=\"lazy\" style=\"width:100%;\" height=\"29\"\u003e\n\u003c/iframe\u003e\n\u003c/p\u003e\n\u003ch2 data-sourcepos=\"48:1-48:13\"\u003e\n\u003cspan id=\"mmodels\" class=\"fragment\"\u003e\u003c/span\u003e\u003ca href=\"#mmodels\"\u003e\u003ci class=\"fa fa-link\"\u003e\u003c/i\u003e\u003c/a\u003eM：Models\u003c/h2\u003e\n\u003cul data-sourcepos=\"49:1-51:0\"\u003e\n\u003cli data-sourcepos=\"49:1-49:21\"\u003eAuth操作モデル\u003c/li\u003e\n\u003cli data-sourcepos=\"50:1-51:0\"\u003e各プロバイダ操作モデル\u003c/li\u003e\n\u003c/ul\u003e\n\u003cp data-sourcepos=\"52:1-52:173\"\u003eモデルとして切り出すのはAuthの操作です。ユーザー登録やサインイン、退会処理などAuthを直接操作するようなAPIを実装します。\u003c/p\u003e\n\u003cp data-sourcepos=\"54:1-54:153\"\u003e\u003ca href=\"https://qiita-user-contents.imgix.net/https%3A%2F%2Fqiita-image-store.s3.ap-northeast-1.amazonaws.com%2F0%2F2264957%2Fb9f84176-0299-012b-02aa-a7fa02f54320.png?ixlib=rb-4.1.1\u0026amp;auto=format\u0026amp;gif-q=60\u0026amp;q=75\u0026amp;s=f7509c07855d4f95f86e6c412909ef8b\" target=\"_blank\" rel=\"nofollow noopener\"\u003e\u003cimg src=\"https://qiita-user-contents.imgix.net/https%3A%2F%2Fqiita-image-store.s3.ap-northeast-1.amazonaws.com%2F0%2F2264957%2Fb9f84176-0299-012b-02aa-a7fa02f54320.png?ixlib=rb-4.1.1\u0026amp;auto=format\u0026amp;gif-q=60\u0026amp;q=75\u0026amp;s=f7509c07855d4f95f86e6c412909ef8b\" alt=\"58E1AF22-AFC8-4485-AD6F-05817026612F.png\" srcset=\"https://qiita-user-contents.imgix.net/https%3A%2F%2Fqiita-image-store.s3.ap-northeast-1.amazonaws.com%2F0%2F2264957%2Fb9f84176-0299-012b-02aa-a7fa02f54320.png?ixlib=rb-4.1.1\u0026amp;auto=format\u0026amp;gif-q=60\u0026amp;q=75\u0026amp;w=1400\u0026amp;fit=max\u0026amp;s=afc6767887a1331500ae37645a930d4e 1x\" data-canonical-src=\"https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2264957/b9f84176-0299-012b-02aa-a7fa02f54320.png\" loading=\"lazy\"\u003e\u003c/a\u003e\u003c/p\u003e\n\u003ch2 data-sourcepos=\"56:1-56:11\"\u003e\n\u003cspan id=\"vview\" class=\"fragment\"\u003e\u003c/span\u003e\u003ca href=\"#vview\"\u003e\u003ci class=\"fa fa-link\"\u003e\u003c/i\u003e\u003c/a\u003eV：View\u003c/h2\u003e\n\u003cul data-sourcepos=\"57:1-64:0\"\u003e\n\u003cli data-sourcepos=\"57:1-57:23\"\u003eサインイン画面\u003c/li\u003e\n\u003cli data-sourcepos=\"58:1-58:20\"\u003e新規登録画面\u003c/li\u003e\n\u003cli data-sourcepos=\"59:1-59:35\"\u003eパスワードリセット画面\u003c/li\u003e\n\u003cli data-sourcepos=\"60:1-60:35\"\u003eGoogleアカウントでの登録\u003c/li\u003e\n\u003cli data-sourcepos=\"61:1-61:34\"\u003eAppleアカウントでの登録\u003c/li\u003e\n\u003cli data-sourcepos=\"62:1-62:47\"\u003eサインイン時のみ表示される画面\u003c/li\u003e\n\u003cli data-sourcepos=\"63:1-64:0\"\u003eサインアウト\u0026amp;退会画面\u003c/li\u003e\n\u003c/ul\u003e\n\u003cp data-sourcepos=\"65:1-65:48\"\u003e必要となる画面は上記の通りです。\u003c/p\u003e\n\u003ch2 data-sourcepos=\"67:1-67:18\"\u003e\n\u003cspan id=\"vm-viewmodel\" class=\"fragment\"\u003e\u003c/span\u003e\u003ca href=\"#vm-viewmodel\"\u003e\u003ci class=\"fa fa-link\"\u003e\u003c/i\u003e\u003c/a\u003eVM： ViewModel\u003c/h2\u003e\n\u003cul data-sourcepos=\"68:1-70:0\"\u003e\n\u003cli data-sourcepos=\"68:1-68:48\"\u003eAuth絡みのモデルを操作するViewModel\u003c/li\u003e\n\u003cli data-sourcepos=\"69:1-70:0\"\u003eバリデーション\u003c/li\u003e\n\u003c/ul\u003e\n\u003cp data-sourcepos=\"71:1-71:114\"\u003eモデルを操作するためのViewModelとバリデーションロジック用のViewModelを実装します。\u003c/p\u003e\n\u003ch1 data-sourcepos=\"73:1-73:11\"\u003e\n\u003cspan id=\"前準備\" class=\"fragment\"\u003e\u003c/span\u003e\u003ca href=\"#%E5%89%8D%E6%BA%96%E5%82%99\"\u003e\u003ci class=\"fa fa-link\"\u003e\u003c/i\u003e\u003c/a\u003e前準備\u003c/h1\u003e\n\u003cp data-sourcepos=\"74:1-74:119\"\u003eまずは「Firebase × Xcode」のセットアップを行います。流れだけざっと確認しておきます。\u003c/p\u003e\n\u003col data-sourcepos=\"76:1-83:0\"\u003e\n\u003cli data-sourcepos=\"76:1-76:30\"\u003eプロジェクトの作成\u003c/li\u003e\n\u003cli data-sourcepos=\"77:1-77:32\"\u003eFirebaseのセットアップ\u003c/li\u003e\n\u003cli data-sourcepos=\"78:1-78:53\"\u003eFirebaseプロジェクトにiOSアプリを追加\u003c/li\u003e\n\u003cli data-sourcepos=\"79:1-79:50\"\u003eCocoa Podsの組み込み\u0026amp;ライブラリ導入\u003c/li\u003e\n\u003cli data-sourcepos=\"80:1-80:60\"\u003eエントリーポイントに初期化コードを記載\u003c/li\u003e\n\u003cli data-sourcepos=\"81:1-83:0\"\u003e各プロバイダを使用可能にする\u003c/li\u003e\n\u003c/ol\u003e\n\u003ch2 data-sourcepos=\"84:1-84:30\"\u003e\n\u003cspan id=\"プロジェクトの作成\" class=\"fragment\"\u003e\u003c/span\u003e\u003ca href=\"#%E3%83%97%E3%83%AD%E3%82%B8%E3%82%A7%E3%82%AF%E3%83%88%E3%81%AE%E4%BD%9C%E6%88%90\"\u003e\u003ci class=\"fa fa-link\"\u003e\u003c/i\u003e\u003c/a\u003eプロジェクトの作成\u003c/h2\u003e\n\u003cp data-sourcepos=\"85:1-85:64\"\u003eまずはXcodeよりSwiftプロジェクトを作成します。\u003c/p\u003e\n\u003cp data-sourcepos=\"87:1-87:153\"\u003e\u003ca href=\"https://qiita-user-contents.imgix.net/https%3A%2F%2Fqiita-image-store.s3.ap-northeast-1.amazonaws.com%2F0%2F2264957%2F648af42c-5c56-b087-a079-4cf339ac4c6c.png?ixlib=rb-4.1.1\u0026amp;auto=format\u0026amp;gif-q=60\u0026amp;q=75\u0026amp;s=09ac5425c6ee8fdbaacc9feea8044692\" target=\"_blank\" rel=\"nofollow noopener\"\u003e\u003cimg src=\"https://qiita-user-contents.imgix.net/https%3A%2F%2Fqiita-image-store.s3.ap-northeast-1.amazonaws.com%2F0%2F2264957%2F648af42c-5c56-b087-a079-4cf339ac4c6c.png?ixlib=rb-4.1.1\u0026amp;auto=format\u0026amp;gif-q=60\u0026amp;q=75\u0026amp;s=09ac5425c6ee8fdbaacc9feea8044692\" alt=\"77FDFBF0-005C-4293-8AF8-FD7FAAEFE479.png\" srcset=\"https://qiita-user-contents.imgix.net/https%3A%2F%2Fqiita-image-store.s3.ap-northeast-1.amazonaws.com%2F0%2F2264957%2F648af42c-5c56-b087-a079-4cf339ac4c6c.png?ixlib=rb-4.1.1\u0026amp;auto=format\u0026amp;gif-q=60\u0026amp;q=75\u0026amp;w=1400\u0026amp;fit=max\u0026amp;s=42fb9839ddf69fa1fed0512c002ea50e 1x\" data-canonical-src=\"https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2264957/648af42c-5c56-b087-a079-4cf339ac4c6c.png\" loading=\"lazy\"\u003e\u003c/a\u003e\u003c/p\u003e\n\u003cp data-sourcepos=\"89:1-89:78\"\u003e今回は\u003cstrong\u003eインターフェースをSwift UIで構築\u003c/strong\u003eしていきます。\u003c/p\u003e\n\u003ch2 data-sourcepos=\"91:1-91:32\"\u003e\n\u003cspan id=\"firebaseのセットアップ\" class=\"fragment\"\u003e\u003c/span\u003e\u003ca href=\"#firebase%E3%81%AE%E3%82%BB%E3%83%83%E3%83%88%E3%82%A2%E3%83%83%E3%83%97\"\u003e\u003ci class=\"fa fa-link\"\u003e\u003c/i\u003e\u003c/a\u003eFirebaseのセットアップ\u003c/h2\u003e\n\u003cp data-sourcepos=\"92:1-92:80\"\u003eFirebaseにログインして\u003cstrong\u003eFirebaseプロジェクトを作成\u003c/strong\u003eします。\u003c/p\u003e\n\u003cp data-sourcepos=\"94:1-94:83\"\u003e作成したらAuthを使えるように「\u003cstrong\u003e始める\u003c/strong\u003e」をクリックします\u003c/p\u003e\n\u003cp data-sourcepos=\"96:1-96:153\"\u003e\u003ca href=\"https://qiita-user-contents.imgix.net/https%3A%2F%2Fqiita-image-store.s3.ap-northeast-1.amazonaws.com%2F0%2F2264957%2F3880c978-ef1e-0b6b-a8f8-887b0f192e23.png?ixlib=rb-4.1.1\u0026amp;auto=format\u0026amp;gif-q=60\u0026amp;q=75\u0026amp;s=7b564cf74d23bcfadccded44ffb07ed6\" target=\"_blank\" rel=\"nofollow noopener\"\u003e\u003cimg src=\"https://qiita-user-contents.imgix.net/https%3A%2F%2Fqiita-image-store.s3.ap-northeast-1.amazonaws.com%2F0%2F2264957%2F3880c978-ef1e-0b6b-a8f8-887b0f192e23.png?ixlib=rb-4.1.1\u0026amp;auto=format\u0026amp;gif-q=60\u0026amp;q=75\u0026amp;s=7b564cf74d23bcfadccded44ffb07ed6\" alt=\"6A4602BC-85D7-4B72-8690-36C01BDD708A.png\" srcset=\"https://qiita-user-contents.imgix.net/https%3A%2F%2Fqiita-image-store.s3.ap-northeast-1.amazonaws.com%2F0%2F2264957%2F3880c978-ef1e-0b6b-a8f8-887b0f192e23.png?ixlib=rb-4.1.1\u0026amp;auto=format\u0026amp;gif-q=60\u0026amp;q=75\u0026amp;w=1400\u0026amp;fit=max\u0026amp;s=8cff50fae7e4f4593dc3a55bd865b6ff 1x\" data-canonical-src=\"https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2264957/3880c978-ef1e-0b6b-a8f8-887b0f192e23.png\" loading=\"lazy\"\u003e\u003c/a\u003e\u003c/p\u003e\n\u003cp data-sourcepos=\"98:1-98:144\"\u003eサインイン方法を選択できるので「\u003cstrong\u003eメール/パスワード\u003c/strong\u003e」と「\u003cstrong\u003eGoogle\u003c/strong\u003e」「\u003cstrong\u003eApple\u003c/strong\u003e」を選択して進みます。\u003c/p\u003e\n\u003cp data-sourcepos=\"101:1-101:153\"\u003e\u003ca href=\"https://qiita-user-contents.imgix.net/https%3A%2F%2Fqiita-image-store.s3.ap-northeast-1.amazonaws.com%2F0%2F2264957%2F2ada6aa6-e2b2-d5f1-1c3d-f04583196a92.png?ixlib=rb-4.1.1\u0026amp;auto=format\u0026amp;gif-q=60\u0026amp;q=75\u0026amp;s=5a940c1253ff40f30c7f241070346103\" target=\"_blank\" rel=\"nofollow noopener\"\u003e\u003cimg src=\"https://qiita-user-contents.imgix.net/https%3A%2F%2Fqiita-image-store.s3.ap-northeast-1.amazonaws.com%2F0%2F2264957%2F2ada6aa6-e2b2-d5f1-1c3d-f04583196a92.png?ixlib=rb-4.1.1\u0026amp;auto=format\u0026amp;gif-q=60\u0026amp;q=75\u0026amp;s=5a940c1253ff40f30c7f241070346103\" alt=\"0E7223D7-F686-4436-BBD8-680EBFD1A7EE.png\" srcset=\"https://qiita-user-contents.imgix.net/https%3A%2F%2Fqiita-image-store.s3.ap-northeast-1.amazonaws.com%2F0%2F2264957%2F2ada6aa6-e2b2-d5f1-1c3d-f04583196a92.png?ixlib=rb-4.1.1\u0026amp;auto=format\u0026amp;gif-q=60\u0026amp;q=75\u0026amp;w=1400\u0026amp;fit=max\u0026amp;s=1f3d818fd344a9010b3654c2fed839ba 1x\" data-canonical-src=\"https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2264957/2ada6aa6-e2b2-d5f1-1c3d-f04583196a92.png\" loading=\"lazy\"\u003e\u003c/a\u003e\u003c/p\u003e\n\u003cp data-sourcepos=\"103:1-103:113\"\u003e\u003ca href=\"https://appdev-room.com/swift-firebase\" rel=\"nofollow noopener\" target=\"_blank\"\u003e【Swift UI/Xcode】Firebaseの導入方法！Cocoa Podsでの使い方\u003c/a\u003e\u003c/p\u003e\n\u003ch2 data-sourcepos=\"106:1-106:53\"\u003e\n\u003cspan id=\"firebaseプロジェクトにiosアプリを追加\" class=\"fragment\"\u003e\u003c/span\u003e\u003ca href=\"#firebase%E3%83%97%E3%83%AD%E3%82%B8%E3%82%A7%E3%82%AF%E3%83%88%E3%81%ABios%E3%82%A2%E3%83%97%E3%83%AA%E3%82%92%E8%BF%BD%E5%8A%A0\"\u003e\u003ci class=\"fa fa-link\"\u003e\u003c/i\u003e\u003c/a\u003eFirebaseプロジェクトにiOSアプリを追加\u003c/h2\u003e\n\u003cp data-sourcepos=\"107:1-108:153\"\u003eFirebaseプロジェクトにiOSアプリを追加します。\u003cbr\u003e\n\u003ca href=\"https://qiita-user-contents.imgix.net/https%3A%2F%2Fqiita-image-store.s3.ap-northeast-1.amazonaws.com%2F0%2F2264957%2Fc77a5681-adb0-4e0b-07aa-40e40899e29e.png?ixlib=rb-4.1.1\u0026amp;auto=format\u0026amp;gif-q=60\u0026amp;q=75\u0026amp;s=b2e17e798acd50fa22051f5ed0143384\" target=\"_blank\" rel=\"nofollow noopener\"\u003e\u003cimg src=\"https://qiita-user-contents.imgix.net/https%3A%2F%2Fqiita-image-store.s3.ap-northeast-1.amazonaws.com%2F0%2F2264957%2Fc77a5681-adb0-4e0b-07aa-40e40899e29e.png?ixlib=rb-4.1.1\u0026amp;auto=format\u0026amp;gif-q=60\u0026amp;q=75\u0026amp;s=b2e17e798acd50fa22051f5ed0143384\" alt=\"6BB50E7E-9A9B-4F7D-AF4E-AAE2BAD4B2A8.png\" srcset=\"https://qiita-user-contents.imgix.net/https%3A%2F%2Fqiita-image-store.s3.ap-northeast-1.amazonaws.com%2F0%2F2264957%2Fc77a5681-adb0-4e0b-07aa-40e40899e29e.png?ixlib=rb-4.1.1\u0026amp;auto=format\u0026amp;gif-q=60\u0026amp;q=75\u0026amp;w=1400\u0026amp;fit=max\u0026amp;s=fbf95ff93a7e2796848c1a98a275ea2b 1x\" data-canonical-src=\"https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2264957/c77a5681-adb0-4e0b-07aa-40e40899e29e.png\" loading=\"lazy\"\u003e\u003c/a\u003e\u003c/p\u003e\n\u003cp data-sourcepos=\"110:1-110:108\"\u003eSwiftプロジェクトにダウンロードしたり\u003cstrong\u003eGoogleService-Info.plist\u003c/strong\u003e」を組み込みます。\u003c/p\u003e\n\u003cp data-sourcepos=\"113:1-113:153\"\u003e\u003ca href=\"https://qiita-user-contents.imgix.net/https%3A%2F%2Fqiita-image-store.s3.ap-northeast-1.amazonaws.com%2F0%2F2264957%2Ff686ef58-73b1-e2f6-8e53-7a0a6cec6069.png?ixlib=rb-4.1.1\u0026amp;auto=format\u0026amp;gif-q=60\u0026amp;q=75\u0026amp;s=49fc1cf07ffe01df7f7195f433fa2dcc\" target=\"_blank\" rel=\"nofollow noopener\"\u003e\u003cimg src=\"https://qiita-user-contents.imgix.net/https%3A%2F%2Fqiita-image-store.s3.ap-northeast-1.amazonaws.com%2F0%2F2264957%2Ff686ef58-73b1-e2f6-8e53-7a0a6cec6069.png?ixlib=rb-4.1.1\u0026amp;auto=format\u0026amp;gif-q=60\u0026amp;q=75\u0026amp;s=49fc1cf07ffe01df7f7195f433fa2dcc\" alt=\"03411DEE-9553-4F79-B11A-C54008EA8CE5.png\" srcset=\"https://qiita-user-contents.imgix.net/https%3A%2F%2Fqiita-image-store.s3.ap-northeast-1.amazonaws.com%2F0%2F2264957%2Ff686ef58-73b1-e2f6-8e53-7a0a6cec6069.png?ixlib=rb-4.1.1\u0026amp;auto=format\u0026amp;gif-q=60\u0026amp;q=75\u0026amp;w=1400\u0026amp;fit=max\u0026amp;s=34268839b024b1cc121b902f696fbb33 1x\" data-canonical-src=\"https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2264957/f686ef58-73b1-e2f6-8e53-7a0a6cec6069.png\" loading=\"lazy\"\u003e\u003c/a\u003e\u003c/p\u003e\n\u003cp data-sourcepos=\"116:1-116:114\"\u003e「Copy items if needed」と「Create groups」にチェックを入れて「Finish」をクリックします。\u003c/p\u003e\n\u003cp data-sourcepos=\"118:1-118:153\"\u003e\u003ca href=\"https://qiita-user-contents.imgix.net/https%3A%2F%2Fqiita-image-store.s3.ap-northeast-1.amazonaws.com%2F0%2F2264957%2F0b910528-4d0e-235d-b004-66c5e224b9b5.png?ixlib=rb-4.1.1\u0026amp;auto=format\u0026amp;gif-q=60\u0026amp;q=75\u0026amp;s=098c903e27a28e900f0a5ef529bcba43\" target=\"_blank\" rel=\"nofollow noopener\"\u003e\u003cimg src=\"https://qiita-user-contents.imgix.net/https%3A%2F%2Fqiita-image-store.s3.ap-northeast-1.amazonaws.com%2F0%2F2264957%2F0b910528-4d0e-235d-b004-66c5e224b9b5.png?ixlib=rb-4.1.1\u0026amp;auto=format\u0026amp;gif-q=60\u0026amp;q=75\u0026amp;s=098c903e27a28e900f0a5ef529bcba43\" alt=\"0BAA1332-D111-4C44-9945-527A060DB90B.png\" srcset=\"https://qiita-user-contents.imgix.net/https%3A%2F%2Fqiita-image-store.s3.ap-northeast-1.amazonaws.com%2F0%2F2264957%2F0b910528-4d0e-235d-b004-66c5e224b9b5.png?ixlib=rb-4.1.1\u0026amp;auto=format\u0026amp;gif-q=60\u0026amp;q=75\u0026amp;w=1400\u0026amp;fit=max\u0026amp;s=495b122d719d25dd8604c5f37db55f10 1x\" data-canonical-src=\"https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2264957/0b910528-4d0e-235d-b004-66c5e224b9b5.png\" loading=\"lazy\"\u003e\u003c/a\u003e\u003c/p\u003e\n\u003ch2 data-sourcepos=\"120:1-120:42\"\u003e\n\u003cspan id=\"cocoa-podsの組み込みauthの導入\" class=\"fragment\"\u003e\u003c/span\u003e\u003ca href=\"#cocoa-pods%E3%81%AE%E7%B5%84%E3%81%BF%E8%BE%BC%E3%81%BFauth%E3%81%AE%E5%B0%8E%E5%85%A5\"\u003e\u003ci class=\"fa fa-link\"\u003e\u003c/i\u003e\u003c/a\u003eCocoa Podsの組み込み\u0026amp;Authの導入\u003c/h2\u003e\n\u003cp data-sourcepos=\"121:1-122:88\"\u003e続いてライブラリ管理ツールである「Cocoa Pods」をプロジェクトに組み込みながらAuthとGoogleアカウントサインインに必要なライブラリを導入します。\u003cbr\u003e\nターミナルで以下のコマンドを打ち込み「PodFile」を生成します。\u003c/p\u003e\n\u003cdiv class=\"code-frame\" data-lang=\"ruby\" data-sourcepos=\"124:1-127:3\"\u003e\n\u003cdiv class=\"code-lang\"\u003e\u003cspan class=\"bold\"\u003eターミナル\u003c/span\u003e\u003c/div\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre\u003e\u003ccode\u003e\u003cspan class=\"err\"\u003e$\u003c/span\u003e \u003cspan class=\"n\"\u003ecd\u003c/span\u003e \u003cspan class=\"n\"\u003eプロジェクトまでのパス\u003c/span\u003e\n\u003cspan class=\"err\"\u003e$\u003c/span\u003e \u003cspan class=\"n\"\u003epod\u003c/span\u003e \u003cspan class=\"n\"\u003einit\u003c/span\u003e\n\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\n\u003c/div\u003e\n\u003cp data-sourcepos=\"128:1-128:106\"\u003e生成できたらPodFile内に各ライブラリをインストールするコードを追加します。\u003c/p\u003e\n\u003cdiv class=\"code-frame\" data-lang=\"ruby\" data-sourcepos=\"129:1-141:3\"\u003e\n\u003cdiv class=\"code-lang\"\u003e\u003cspan class=\"bold\"\u003ePodFile\u003c/span\u003e\u003c/div\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre\u003e\u003ccode\u003e\u003cspan class=\"c1\"\u003e# Uncomment the next line to define a global platform for your project\u003c/span\u003e\n\u003cspan class=\"c1\"\u003e# platform :ios, '9.0'\u003c/span\u003e\n\n\u003cspan class=\"n\"\u003etarget\u003c/span\u003e \u003cspan class=\"s1\"\u003e'(プロジェクト名)'\u003c/span\u003e \u003cspan class=\"k\"\u003edo\u003c/span\u003e\n\n  \u003cspan class=\"n\"\u003euse_frameworks!\u003c/span\u003e\n\n  \u003cspan class=\"n\"\u003epod\u003c/span\u003e \u003cspan class=\"s1\"\u003e'FirebaseAuth'\u003c/span\u003e\n  \u003cspan class=\"n\"\u003epod\u003c/span\u003e \u003cspan class=\"s1\"\u003e'GoogleSignIn'\u003c/span\u003e\n\n\u003cspan class=\"k\"\u003eend\u003c/span\u003e\n\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\n\u003c/div\u003e\n\u003cp data-sourcepos=\"142:1-142:76\"\u003e最後に実行すればプロジェクトにAuthが組み込まれます。\u003c/p\u003e\n\u003cdiv class=\"code-frame\" data-lang=\"ruby\" data-sourcepos=\"144:1-146:3\"\u003e\n\u003cdiv class=\"code-lang\"\u003e\u003cspan class=\"bold\"\u003eターミナル\u003c/span\u003e\u003c/div\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre\u003e\u003ccode\u003e\u003cspan class=\"err\"\u003e$\u003c/span\u003e \u003cspan class=\"n\"\u003epod\u003c/span\u003e \u003cspan class=\"n\"\u003einstall\u003c/span\u003e\n\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\n\u003c/div\u003e\n\u003cp data-sourcepos=\"148:1-148:108\"\u003e\u003ca href=\"https://appdev-room.com/swift-cocoapods\" rel=\"nofollow noopener\" target=\"_blank\"\u003e【Swift UI】CocoaPodsのインストール方法と使い方！\u003c/a\u003e\u003c/p\u003e\n\u003ch2 data-sourcepos=\"150:1-150:60\"\u003e\n\u003cspan id=\"エントリーポイントに初期化コードを記載\" class=\"fragment\"\u003e\u003c/span\u003e\u003ca href=\"#%E3%82%A8%E3%83%B3%E3%83%88%E3%83%AA%E3%83%BC%E3%83%9D%E3%82%A4%E3%83%B3%E3%83%88%E3%81%AB%E5%88%9D%E6%9C%9F%E5%8C%96%E3%82%B3%E3%83%BC%E3%83%89%E3%82%92%E8%A8%98%E8%BC%89\"\u003e\u003ci class=\"fa fa-link\"\u003e\u003c/i\u003e\u003c/a\u003eエントリーポイントに初期化コードを記載\u003c/h2\u003e\n\u003cdiv class=\"code-frame\" data-lang=\"swift\" data-sourcepos=\"152:1-175:3\"\u003e\n\u003cdiv class=\"code-lang\"\u003e\u003cspan class=\"bold\"\u003eTestFirebaseApp\u003c/span\u003e\u003c/div\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre\u003e\u003ccode\u003e\u003cspan class=\"kd\"\u003eimport\u003c/span\u003e \u003cspan class=\"kt\"\u003eSwiftUI\u003c/span\u003e\n\u003cspan class=\"kd\"\u003eimport\u003c/span\u003e \u003cspan class=\"kt\"\u003eFirebaseCore\u003c/span\u003e \u003cspan class=\"c1\"\u003e// 追加\u003c/span\u003e\n\n\u003cspan class=\"c1\"\u003e// 追加\u003c/span\u003e\n\u003cspan class=\"kd\"\u003eclass\u003c/span\u003e \u003cspan class=\"kt\"\u003eAppDelegate\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e \u003cspan class=\"kt\"\u003eNSObject\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e \u003cspan class=\"kt\"\u003eUIApplicationDelegate\u003c/span\u003e \u003cspan class=\"p\"\u003e{\u003c/span\u003e\n    \u003cspan class=\"kd\"\u003efunc\u003c/span\u003e \u003cspan class=\"nf\"\u003eapplication\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"n\"\u003e_\u003c/span\u003e \u003cspan class=\"nv\"\u003eapplication\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e \u003cspan class=\"kt\"\u003eUIApplication\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e\n                    \u003cspan class=\"n\"\u003edidFinishLaunchingWithOptions\u003c/span\u003e \u003cspan class=\"nv\"\u003elaunchOptions\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e \u003cspan class=\"p\"\u003e[\u003c/span\u003e\u003cspan class=\"kt\"\u003eUIApplication\u003c/span\u003e\u003cspan class=\"o\"\u003e.\u003c/span\u003e\u003cspan class=\"kt\"\u003eLaunchOptionsKey\u003c/span\u003e \u003cspan class=\"p\"\u003e:\u003c/span\u003e \u003cspan class=\"kt\"\u003eAny\u003c/span\u003e\u003cspan class=\"p\"\u003e]?\u003c/span\u003e \u003cspan class=\"o\"\u003e=\u003c/span\u003e \u003cspan class=\"kc\"\u003enil\u003c/span\u003e\u003cspan class=\"p\"\u003e)\u003c/span\u003e \u003cspan class=\"o\"\u003e-\u0026gt;\u003c/span\u003e \u003cspan class=\"kt\"\u003eBool\u003c/span\u003e \u003cspan class=\"p\"\u003e{\u003c/span\u003e\n        \u003cspan class=\"kt\"\u003eFirebaseApp\u003c/span\u003e\u003cspan class=\"o\"\u003e.\u003c/span\u003e\u003cspan class=\"nf\"\u003econfigure\u003c/span\u003e\u003cspan class=\"p\"\u003e()\u003c/span\u003e\n        \u003cspan class=\"k\"\u003ereturn\u003c/span\u003e \u003cspan class=\"kc\"\u003etrue\u003c/span\u003e\n    \u003cspan class=\"p\"\u003e}\u003c/span\u003e\n\u003cspan class=\"p\"\u003e}\u003c/span\u003e\n\n\u003cspan class=\"kd\"\u003e@main\u003c/span\u003e\n\u003cspan class=\"kd\"\u003estruct\u003c/span\u003e \u003cspan class=\"kt\"\u003eTestFirebaseApp\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e \u003cspan class=\"kt\"\u003eApp\u003c/span\u003e \u003cspan class=\"p\"\u003e{\u003c/span\u003e\n    \u003cspan class=\"c1\"\u003e// 追加\u003c/span\u003e\n    \u003cspan class=\"kd\"\u003e@UIApplicationDelegateAdaptor\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"kt\"\u003eAppDelegate\u003c/span\u003e\u003cspan class=\"o\"\u003e.\u003c/span\u003e\u003cspan class=\"k\"\u003eself\u003c/span\u003e\u003cspan class=\"p\"\u003e)\u003c/span\u003e \u003cspan class=\"k\"\u003evar\u003c/span\u003e \u003cspan class=\"nv\"\u003edelegate\u003c/span\u003e\n    \u003cspan class=\"k\"\u003evar\u003c/span\u003e \u003cspan class=\"nv\"\u003ebody\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e \u003cspan class=\"kd\"\u003esome\u003c/span\u003e \u003cspan class=\"kt\"\u003eScene\u003c/span\u003e \u003cspan class=\"p\"\u003e{\u003c/span\u003e\n        \u003cspan class=\"kt\"\u003eWindowGroup\u003c/span\u003e \u003cspan class=\"p\"\u003e{\u003c/span\u003e\n            \u003cspan class=\"kt\"\u003eContentView\u003c/span\u003e\u003cspan class=\"p\"\u003e()\u003c/span\u003e\n        \u003cspan class=\"p\"\u003e}\u003c/span\u003e\n    \u003cspan class=\"p\"\u003e}\u003c/span\u003e\n\u003cspan class=\"p\"\u003e}\u003c/span\u003e\n\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\n\u003c/div\u003e\n\u003cp data-sourcepos=\"177:1-177:40\"\u003eこれで\u003cstrong\u003e下準備は完成\u003c/strong\u003eです。\u003c/p\u003e\n\u003cp data-sourcepos=\"179:1-179:157\"\u003e\u003ca href=\"https://appdev-room.com/swift-firebase-realtime-authentication\" rel=\"nofollow noopener\" target=\"_blank\"\u003e【Swift UI/Firebase】Authentication導入方法と使い方！ログイン機能の実装\u003c/a\u003e\u003c/p\u003e\n\u003ch2 data-sourcepos=\"181:1-181:51\"\u003e\n\u003cspan id=\"sign-in-with-appleを実装するために\" class=\"fragment\"\u003e\u003c/span\u003e\u003ca href=\"#sign-in-with-apple%E3%82%92%E5%AE%9F%E8%A3%85%E3%81%99%E3%82%8B%E3%81%9F%E3%82%81%E3%81%AB\"\u003e\u003ci class=\"fa fa-link\"\u003e\u003c/i\u003e\u003c/a\u003e「Sign in with Apple」を実装するために\u003c/h2\u003e\n\u003cp data-sourcepos=\"183:1-183:135\"\u003eAppleアカウントでのサインイン(= 「Sign in with Apple」)を実装するためには前準備がいくつか必要です。\u003c/p\u003e\n\u003col data-sourcepos=\"185:1-188:0\"\u003e\n\u003cli data-sourcepos=\"185:1-185:54\"\u003eApp IDの登録と「Sign in with Apple」の有効\u003c/li\u003e\n\u003cli data-sourcepos=\"186:1-186:76\"\u003eXcodeの「Signing \u0026amp; Capability」にと「Sign in with Apple」の追加\u003c/li\u003e\n\u003cli data-sourcepos=\"187:1-188:0\"\u003eAppleサインインボタンの実装\u003c/li\u003e\n\u003c/ol\u003e\n\u003cp data-sourcepos=\"189:1-189:157\"\u003e\u003ca href=\"https://appdev-room.com/swift-firebase-authentication-apple\" rel=\"nofollow noopener\" target=\"_blank\"\u003e【Swift UI/Firebase】AuthenticationでSign in with Appleでログインを実装する方法\u003c/a\u003e\u003c/p\u003e\n\u003ch2 data-sourcepos=\"191:1-191:69\"\u003e\n\u003cspan id=\"googleアカウントでのサインインを実装するために\" class=\"fragment\"\u003e\u003c/span\u003e\u003ca href=\"#google%E3%82%A2%E3%82%AB%E3%82%A6%E3%83%B3%E3%83%88%E3%81%A7%E3%81%AE%E3%82%B5%E3%82%A4%E3%83%B3%E3%82%A4%E3%83%B3%E3%82%92%E5%AE%9F%E8%A3%85%E3%81%99%E3%82%8B%E3%81%9F%E3%82%81%E3%81%AB\"\u003e\u003ci class=\"fa fa-link\"\u003e\u003c/i\u003e\u003c/a\u003eGoogleアカウントでのサインインを実装するために\u003c/h2\u003e\n\u003cp data-sourcepos=\"192:1-192:108\"\u003eGoogleアカウントでのサインインを実装するためにもいくつか前準備が必要です。\u003c/p\u003e\n\u003col data-sourcepos=\"194:1-197:0\"\u003e\n\u003cli data-sourcepos=\"194:1-194:39\"\u003eカスタムURLスキームの追加\u003c/li\u003e\n\u003cli data-sourcepos=\"195:1-195:35\"\u003eAppDelegateクラスへの追記\u003c/li\u003e\n\u003cli data-sourcepos=\"196:1-197:0\"\u003eGoogleサインインボタンの実装\u003c/li\u003e\n\u003c/ol\u003e\n\u003cp data-sourcepos=\"198:1-198:164\"\u003e\u003ca href=\"https://appdev-room.com/swift-firebase-authentication-google\" rel=\"nofollow noopener\" target=\"_blank\"\u003e【Swift UI/Firebase】AuthenticationでGoogleアカウントでログインを実装する方法！\u003c/a\u003e\u003c/p\u003e\n\u003ch1 data-sourcepos=\"200:1-200:57\"\u003e\n\u003cspan id=\"ログイン機能の処理をmvvmに分割していく\" class=\"fragment\"\u003e\u003c/span\u003e\u003ca href=\"#%E3%83%AD%E3%82%B0%E3%82%A4%E3%83%B3%E6%A9%9F%E8%83%BD%E3%81%AE%E5%87%A6%E7%90%86%E3%82%92mvvm%E3%81%AB%E5%88%86%E5%89%B2%E3%81%97%E3%81%A6%E3%81%84%E3%81%8F\"\u003e\u003ci class=\"fa fa-link\"\u003e\u003c/i\u003e\u003c/a\u003eログイン機能の処理をMVVMに分割していく\u003c/h1\u003e\n\u003cp data-sourcepos=\"201:1-201:130\"\u003e浅はかな知識ながらMVVMアーキテクチャを意識してサインイン機能の責務を分割していきました。\u003c/p\u003e\n\u003ch2 data-sourcepos=\"203:1-203:8\"\u003e\n\u003cspan id=\"model\" class=\"fragment\"\u003e\u003c/span\u003e\u003ca href=\"#model\"\u003e\u003ci class=\"fa fa-link\"\u003e\u003c/i\u003e\u003c/a\u003eModel\u003c/h2\u003e\n\u003cp data-sourcepos=\"204:1-204:114\"\u003e結果モデルは大元1つと各プロバイダごとに1つずつ、エラーモデル1つになりました。\u003c/p\u003e\n\u003col data-sourcepos=\"206:1-211:0\"\u003e\n\u003cli data-sourcepos=\"206:1-206:18\"\u003eAuthModel.swift\u003c/li\u003e\n\u003cli data-sourcepos=\"207:1-207:23\"\u003eEmailAuthModel.swift\u003c/li\u003e\n\u003cli data-sourcepos=\"208:1-208:24\"\u003eGoogleAuthModel.swift\u003c/li\u003e\n\u003cli data-sourcepos=\"209:1-209:23\"\u003eAppleAuthModel.swift\u003c/li\u003e\n\u003cli data-sourcepos=\"210:1-211:0\"\u003eAuthErrorModel.swift\u003c/li\u003e\n\u003c/ol\u003e\n\u003cdiv class=\"code-frame\" data-lang=\"swift\" data-sourcepos=\"212:1-277:3\"\u003e\n\u003cdiv class=\"code-lang\"\u003e\u003cspan class=\"bold\"\u003eAuthModel.swift\u003c/span\u003e\u003c/div\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre\u003e\u003ccode\u003e\u003cspan class=\"kd\"\u003eimport\u003c/span\u003e \u003cspan class=\"kt\"\u003eUIKit\u003c/span\u003e\n\u003cspan class=\"kd\"\u003eimport\u003c/span\u003e \u003cspan class=\"kt\"\u003eFirebaseAuth\u003c/span\u003e\n\u003cspan class=\"kd\"\u003eimport\u003c/span\u003e \u003cspan class=\"kt\"\u003eFirebaseCore\u003c/span\u003e\n\u003cspan class=\"kd\"\u003eimport\u003c/span\u003e \u003cspan class=\"kt\"\u003eAuthenticationServices\u003c/span\u003e\n\u003cspan class=\"kd\"\u003eimport\u003c/span\u003e \u003cspan class=\"kt\"\u003eSwiftUI\u003c/span\u003e\n\n\u003cspan class=\"kd\"\u003eclass\u003c/span\u003e \u003cspan class=\"kt\"\u003eAuthModel\u003c/span\u003e \u003cspan class=\"p\"\u003e{\u003c/span\u003e\n\n    \u003cspan class=\"c1\"\u003e// MARK: - シングルトン\u003c/span\u003e\n    \u003cspan class=\"kd\"\u003estatic\u003c/span\u003e \u003cspan class=\"k\"\u003elet\u003c/span\u003e \u003cspan class=\"nv\"\u003eshared\u003c/span\u003e \u003cspan class=\"o\"\u003e=\u003c/span\u003e \u003cspan class=\"kt\"\u003eAuthModel\u003c/span\u003e\u003cspan class=\"p\"\u003e()\u003c/span\u003e\n    \n    \u003cspan class=\"c1\"\u003e// MARK: - リファレンス\u003c/span\u003e\n    \u003cspan class=\"kd\"\u003eprivate\u003c/span\u003e  \u003cspan class=\"k\"\u003elet\u003c/span\u003e \u003cspan class=\"nv\"\u003eauth\u003c/span\u003e \u003cspan class=\"o\"\u003e=\u003c/span\u003e \u003cspan class=\"kt\"\u003eAuth\u003c/span\u003e\u003cspan class=\"o\"\u003e.\u003c/span\u003e\u003cspan class=\"nf\"\u003eauth\u003c/span\u003e\u003cspan class=\"p\"\u003e()\u003c/span\u003e\n\n    \n    \u003cspan class=\"c1\"\u003e// MARK: - \u003c/span\u003e\n    \u003cspan class=\"kd\"\u003epublic\u003c/span\u003e \u003cspan class=\"kd\"\u003efunc\u003c/span\u003e \u003cspan class=\"nf\"\u003egetCurrentUser\u003c/span\u003e\u003cspan class=\"p\"\u003e()\u003c/span\u003e \u003cspan class=\"o\"\u003e-\u0026gt;\u003c/span\u003e \u003cspan class=\"kt\"\u003eUser\u003c/span\u003e\u003cspan class=\"p\"\u003e?\u003c/span\u003e \u003cspan class=\"p\"\u003e{\u003c/span\u003e\n        \u003cspan class=\"k\"\u003ereturn\u003c/span\u003e \u003cspan class=\"n\"\u003eauth\u003c/span\u003e\u003cspan class=\"o\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003ecurrentUser\u003c/span\u003e\n    \u003cspan class=\"p\"\u003e}\u003c/span\u003e\n    \n    \u003cspan class=\"kd\"\u003epublic\u003c/span\u003e \u003cspan class=\"k\"\u003elet\u003c/span\u003e \u003cspan class=\"nv\"\u003edefaultName\u003c/span\u003e \u003cspan class=\"o\"\u003e=\u003c/span\u003e \u003cspan class=\"s\"\u003e\"no name\"\u003c/span\u003e\n    \n    \u003cspan class=\"c1\"\u003e// MARK: - Sign In for Credential\u003c/span\u003e\n    \u003cspan class=\"kd\"\u003epublic\u003c/span\u003e \u003cspan class=\"kd\"\u003efunc\u003c/span\u003e \u003cspan class=\"nf\"\u003ecredentialSignIn\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"nv\"\u003ecredential\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e \u003cspan class=\"kt\"\u003eAuthCredential\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e\u003cspan class=\"nv\"\u003ecompletion\u003c/span\u003e \u003cspan class=\"p\"\u003e:\u003c/span\u003e \u003cspan class=\"kd\"\u003e@escaping\u003c/span\u003e \u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"kt\"\u003eResult\u003c/span\u003e\u003cspan class=\"o\"\u003e\u0026lt;\u003c/span\u003e\u003cspan class=\"kt\"\u003eBool\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e \u003cspan class=\"kt\"\u003eError\u003c/span\u003e\u003cspan class=\"o\"\u003e\u0026gt;\u003c/span\u003e\u003cspan class=\"p\"\u003e)\u003c/span\u003e \u003cspan class=\"o\"\u003e-\u0026gt;\u003c/span\u003e  \u003cspan class=\"kt\"\u003eVoid\u003c/span\u003e \u003cspan class=\"p\"\u003e){\u003c/span\u003e\n        \u003cspan class=\"k\"\u003eself\u003c/span\u003e\u003cspan class=\"o\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003eauth\u003c/span\u003e\u003cspan class=\"o\"\u003e.\u003c/span\u003e\u003cspan class=\"nf\"\u003esignIn\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"nv\"\u003ewith\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e \u003cspan class=\"n\"\u003ecredential\u003c/span\u003e\u003cspan class=\"p\"\u003e)\u003c/span\u003e \u003cspan class=\"p\"\u003e{\u003c/span\u003e \u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"n\"\u003eauthResult\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e \u003cspan class=\"n\"\u003eerror\u003c/span\u003e\u003cspan class=\"p\"\u003e)\u003c/span\u003e \u003cspan class=\"k\"\u003ein\u003c/span\u003e\n            \u003cspan class=\"k\"\u003eif\u003c/span\u003e \u003cspan class=\"n\"\u003eerror\u003c/span\u003e \u003cspan class=\"o\"\u003e==\u003c/span\u003e \u003cspan class=\"kc\"\u003enil\u003c/span\u003e \u003cspan class=\"p\"\u003e{\u003c/span\u003e\n            \u003cspan class=\"k\"\u003eif\u003c/span\u003e \u003cspan class=\"n\"\u003eauthResult\u003c/span\u003e\u003cspan class=\"p\"\u003e?\u003c/span\u003e\u003cspan class=\"o\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003euser\u003c/span\u003e \u003cspan class=\"o\"\u003e!=\u003c/span\u003e \u003cspan class=\"kc\"\u003enil\u003c/span\u003e\u003cspan class=\"p\"\u003e{\u003c/span\u003e\n                \u003cspan class=\"k\"\u003eif\u003c/span\u003e \u003cspan class=\"n\"\u003eauthResult\u003c/span\u003e\u003cspan class=\"o\"\u003e!.\u003c/span\u003e\u003cspan class=\"n\"\u003euser\u003c/span\u003e\u003cspan class=\"o\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003edisplayName\u003c/span\u003e \u003cspan class=\"o\"\u003e==\u003c/span\u003e \u003cspan class=\"kc\"\u003enil\u003c/span\u003e\u003cspan class=\"p\"\u003e{\u003c/span\u003e\n                    \u003cspan class=\"c1\"\u003e// Appleアカウントの場合は存在しない\u003c/span\u003e\n                \u003cspan class=\"p\"\u003e}\u003c/span\u003e\n                \u003cspan class=\"nf\"\u003ecompletion\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"o\"\u003e.\u003c/span\u003e\u003cspan class=\"nf\"\u003esuccess\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"kc\"\u003etrue\u003c/span\u003e\u003cspan class=\"p\"\u003e))\u003c/span\u003e\n            \u003cspan class=\"p\"\u003e}\u003c/span\u003e\n            \u003cspan class=\"p\"\u003e}\u003c/span\u003e\u003cspan class=\"k\"\u003eelse\u003c/span\u003e\u003cspan class=\"p\"\u003e{\u003c/span\u003e\n                \u003cspan class=\"nf\"\u003ecompletion\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"o\"\u003e.\u003c/span\u003e\u003cspan class=\"nf\"\u003efailure\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"n\"\u003eerror\u003c/span\u003e\u003cspan class=\"o\"\u003e!\u003c/span\u003e\u003cspan class=\"p\"\u003e))\u003c/span\u003e\n            \u003cspan class=\"p\"\u003e}\u003c/span\u003e\n        \u003cspan class=\"p\"\u003e}\u003c/span\u003e\n    \u003cspan class=\"p\"\u003e}\u003c/span\u003e\n    \n    \u003cspan class=\"c1\"\u003e// MARK: - サインアウト処理\u003c/span\u003e\n    \u003cspan class=\"kd\"\u003epublic\u003c/span\u003e \u003cspan class=\"kd\"\u003efunc\u003c/span\u003e \u003cspan class=\"kt\"\u003eSignOut\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"nv\"\u003ecompletion\u003c/span\u003e \u003cspan class=\"p\"\u003e:\u003c/span\u003e \u003cspan class=\"kd\"\u003e@escaping\u003c/span\u003e \u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"kt\"\u003eResult\u003c/span\u003e\u003cspan class=\"o\"\u003e\u0026lt;\u003c/span\u003e\u003cspan class=\"kt\"\u003eBool\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e \u003cspan class=\"kt\"\u003eError\u003c/span\u003e\u003cspan class=\"o\"\u003e\u0026gt;\u003c/span\u003e\u003cspan class=\"p\"\u003e)\u003c/span\u003e \u003cspan class=\"o\"\u003e-\u0026gt;\u003c/span\u003e  \u003cspan class=\"kt\"\u003eVoid\u003c/span\u003e \u003cspan class=\"p\"\u003e){\u003c/span\u003e\n        \u003cspan class=\"k\"\u003edo\u003c/span\u003e\u003cspan class=\"p\"\u003e{\u003c/span\u003e\n            \u003cspan class=\"k\"\u003etry\u003c/span\u003e \u003cspan class=\"n\"\u003eauth\u003c/span\u003e\u003cspan class=\"o\"\u003e.\u003c/span\u003e\u003cspan class=\"nf\"\u003esignOut\u003c/span\u003e\u003cspan class=\"p\"\u003e()\u003c/span\u003e\n            \u003cspan class=\"nf\"\u003eprint\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"s\"\u003e\"SignOut\"\u003c/span\u003e\u003cspan class=\"p\"\u003e)\u003c/span\u003e\n            \u003cspan class=\"nf\"\u003ecompletion\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"o\"\u003e.\u003c/span\u003e\u003cspan class=\"nf\"\u003esuccess\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"kc\"\u003etrue\u003c/span\u003e\u003cspan class=\"p\"\u003e))\u003c/span\u003e\n        \u003cspan class=\"p\"\u003e}\u003c/span\u003e \u003cspan class=\"k\"\u003ecatch\u003c/span\u003e \u003cspan class=\"k\"\u003elet\u003c/span\u003e \u003cspan class=\"nv\"\u003esignOutError\u003c/span\u003e \u003cspan class=\"k\"\u003eas\u003c/span\u003e \u003cspan class=\"kt\"\u003eNSError\u003c/span\u003e \u003cspan class=\"p\"\u003e{\u003c/span\u003e\n            \u003cspan class=\"nf\"\u003ecompletion\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"o\"\u003e.\u003c/span\u003e\u003cspan class=\"nf\"\u003efailure\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"n\"\u003esignOutError\u003c/span\u003e\u003cspan class=\"p\"\u003e))\u003c/span\u003e\n        \u003cspan class=\"p\"\u003e}\u003c/span\u003e\n    \u003cspan class=\"p\"\u003e}\u003c/span\u003e\n    \n    \u003cspan class=\"c1\"\u003e// MARK: -  各プロバイダ退会処理 \u0026amp; Appleアカウントは直呼び出し\u003c/span\u003e\n    \u003cspan class=\"kd\"\u003epublic\u003c/span\u003e \u003cspan class=\"kd\"\u003efunc\u003c/span\u003e \u003cspan class=\"nf\"\u003ewithdrawal\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"nv\"\u003ecompletion\u003c/span\u003e \u003cspan class=\"p\"\u003e:\u003c/span\u003e \u003cspan class=\"kd\"\u003e@escaping\u003c/span\u003e \u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"kt\"\u003eResult\u003c/span\u003e\u003cspan class=\"o\"\u003e\u0026lt;\u003c/span\u003e\u003cspan class=\"kt\"\u003eBool\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e \u003cspan class=\"kt\"\u003eError\u003c/span\u003e\u003cspan class=\"o\"\u003e\u0026gt;\u003c/span\u003e\u003cspan class=\"p\"\u003e)\u003c/span\u003e \u003cspan class=\"o\"\u003e-\u0026gt;\u003c/span\u003e  \u003cspan class=\"kt\"\u003eVoid\u003c/span\u003e \u003cspan class=\"p\"\u003e){\u003c/span\u003e\n        \u003cspan class=\"k\"\u003eif\u003c/span\u003e \u003cspan class=\"k\"\u003elet\u003c/span\u003e \u003cspan class=\"nv\"\u003euser\u003c/span\u003e \u003cspan class=\"o\"\u003e=\u003c/span\u003e \u003cspan class=\"n\"\u003eauth\u003c/span\u003e\u003cspan class=\"o\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003ecurrentUser\u003c/span\u003e \u003cspan class=\"p\"\u003e{\u003c/span\u003e\n            \u003cspan class=\"n\"\u003euser\u003c/span\u003e\u003cspan class=\"o\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003edelete\u003c/span\u003e \u003cspan class=\"p\"\u003e{\u003c/span\u003e \u003cspan class=\"n\"\u003eerror\u003c/span\u003e \u003cspan class=\"k\"\u003ein\u003c/span\u003e\n                \u003cspan class=\"k\"\u003eif\u003c/span\u003e \u003cspan class=\"n\"\u003eerror\u003c/span\u003e \u003cspan class=\"o\"\u003e==\u003c/span\u003e \u003cspan class=\"kc\"\u003enil\u003c/span\u003e \u003cspan class=\"p\"\u003e{\u003c/span\u003e\n                    \u003cspan class=\"nf\"\u003eprint\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"s\"\u003e\"退会成功\"\u003c/span\u003e\u003cspan class=\"p\"\u003e)\u003c/span\u003e\n                    \u003cspan class=\"nf\"\u003ecompletion\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"o\"\u003e.\u003c/span\u003e\u003cspan class=\"nf\"\u003esuccess\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"kc\"\u003etrue\u003c/span\u003e\u003cspan class=\"p\"\u003e))\u003c/span\u003e\n                \u003cspan class=\"p\"\u003e}\u003c/span\u003e \u003cspan class=\"k\"\u003eelse\u003c/span\u003e \u003cspan class=\"p\"\u003e{\u003c/span\u003e\n                    \u003cspan class=\"nf\"\u003eprint\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"s\"\u003e\"退会失敗\"\u003c/span\u003e\u003cspan class=\"p\"\u003e)\u003c/span\u003e\n                    \u003cspan class=\"nf\"\u003ecompletion\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"o\"\u003e.\u003c/span\u003e\u003cspan class=\"nf\"\u003efailure\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"n\"\u003eerror\u003c/span\u003e\u003cspan class=\"o\"\u003e!\u003c/span\u003e\u003cspan class=\"p\"\u003e))\u003c/span\u003e\n                \u003cspan class=\"p\"\u003e}\u003c/span\u003e\n            \u003cspan class=\"p\"\u003e}\u003c/span\u003e\n        \u003cspan class=\"p\"\u003e}\u003c/span\u003e\n    \u003cspan class=\"p\"\u003e}\u003c/span\u003e\n\u003cspan class=\"p\"\u003e}\u003c/span\u003e\n\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\n\u003c/div\u003e\n\u003ch2 data-sourcepos=\"278:1-278:12\"\u003e\n\u003cspan id=\"viewmodel\" class=\"fragment\"\u003e\u003c/span\u003e\u003ca href=\"#viewmodel\"\u003e\u003ci class=\"fa fa-link\"\u003e\u003c/i\u003e\u003c/a\u003eViewModel\u003c/h2\u003e\n\u003cp data-sourcepos=\"280:1-280:192\"\u003eAuthに絡んだ各Modelを一括で操作するViewModelにしました。なかなか大きくなってしまいましたがここをもう少し切り分けた方が良かったのかな？\u003c/p\u003e\n\u003col data-sourcepos=\"282:1-284:0\"\u003e\n\u003cli data-sourcepos=\"282:1-282:22\"\u003eAuthViewModel.swift\u003c/li\u003e\n\u003cli data-sourcepos=\"283:1-284:0\"\u003eValidationViewModel.swift\u003c/li\u003e\n\u003c/ol\u003e\n\u003cdiv class=\"code-frame\" data-lang=\"swift\" data-sourcepos=\"285:1-449:3\"\u003e\n\u003cdiv class=\"code-lang\"\u003e\u003cspan class=\"bold\"\u003eAuthViewModel.swift\u003c/span\u003e\u003c/div\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre\u003e\u003ccode\u003e\n\u003cspan class=\"kd\"\u003eimport\u003c/span\u003e \u003cspan class=\"kt\"\u003eUIKit\u003c/span\u003e\n\u003cspan class=\"kd\"\u003eimport\u003c/span\u003e \u003cspan class=\"kt\"\u003eFirebaseAuth\u003c/span\u003e\n\u003cspan class=\"kd\"\u003eimport\u003c/span\u003e \u003cspan class=\"kt\"\u003eFirebaseCore\u003c/span\u003e\n\u003cspan class=\"kd\"\u003eimport\u003c/span\u003e \u003cspan class=\"kt\"\u003eAuthenticationServices\u003c/span\u003e \u003cspan class=\"c1\"\u003e// Apple\u003c/span\u003e\n\n\u003cspan class=\"kd\"\u003eclass\u003c/span\u003e \u003cspan class=\"kt\"\u003eAuthViewModel\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e\u003cspan class=\"kt\"\u003eObservableObject\u003c/span\u003e \u003cspan class=\"p\"\u003e{\u003c/span\u003e\n    \n    \u003cspan class=\"c1\"\u003e// MARK: - シングルトン\u003c/span\u003e\n    \u003cspan class=\"kd\"\u003estatic\u003c/span\u003e \u003cspan class=\"k\"\u003elet\u003c/span\u003e \u003cspan class=\"nv\"\u003eshared\u003c/span\u003e \u003cspan class=\"o\"\u003e=\u003c/span\u003e \u003cspan class=\"kt\"\u003eAuthViewModel\u003c/span\u003e\u003cspan class=\"p\"\u003e()\u003c/span\u003e\n    \n    \u003cspan class=\"kd\"\u003eprivate\u003c/span\u003e \u003cspan class=\"k\"\u003evar\u003c/span\u003e \u003cspan class=\"nv\"\u003eauth\u003c/span\u003e \u003cspan class=\"o\"\u003e=\u003c/span\u003e \u003cspan class=\"kt\"\u003eAuthModel\u003c/span\u003e\u003cspan class=\"o\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003eshared\u003c/span\u003e\n    \u003cspan class=\"kd\"\u003eprivate\u003c/span\u003e \u003cspan class=\"k\"\u003elet\u003c/span\u003e \u003cspan class=\"nv\"\u003eemailAuth\u003c/span\u003e \u003cspan class=\"o\"\u003e=\u003c/span\u003e \u003cspan class=\"kt\"\u003eEmailAuthModel\u003c/span\u003e\u003cspan class=\"o\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003eshared\u003c/span\u003e\n    \u003cspan class=\"kd\"\u003eprivate\u003c/span\u003e \u003cspan class=\"k\"\u003elet\u003c/span\u003e \u003cspan class=\"nv\"\u003egoogleAuth\u003c/span\u003e \u003cspan class=\"o\"\u003e=\u003c/span\u003e \u003cspan class=\"kt\"\u003eGoogleAuthModel\u003c/span\u003e\u003cspan class=\"o\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003eshared\u003c/span\u003e\n    \u003cspan class=\"kd\"\u003eprivate\u003c/span\u003e \u003cspan class=\"k\"\u003elet\u003c/span\u003e \u003cspan class=\"nv\"\u003eappleAuth\u003c/span\u003e \u003cspan class=\"o\"\u003e=\u003c/span\u003e \u003cspan class=\"kt\"\u003eAppleAuthModel\u003c/span\u003e\u003cspan class=\"o\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003eshared\u003c/span\u003e\n    \n    \u003cspan class=\"kd\"\u003eprivate\u003c/span\u003e \u003cspan class=\"k\"\u003elet\u003c/span\u003e \u003cspan class=\"nv\"\u003eerrModel\u003c/span\u003e \u003cspan class=\"o\"\u003e=\u003c/span\u003e \u003cspan class=\"kt\"\u003eAuthErrorModel\u003c/span\u003e\u003cspan class=\"p\"\u003e()\u003c/span\u003e\n    \n    \u003cspan class=\"kd\"\u003e@Published\u003c/span\u003e \u003cspan class=\"k\"\u003evar\u003c/span\u003e \u003cspan class=\"nv\"\u003eerrMessage\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e\u003cspan class=\"kt\"\u003eString\u003c/span\u003e \u003cspan class=\"o\"\u003e=\u003c/span\u003e \u003cspan class=\"s\"\u003e\"\"\u003c/span\u003e\n    \n    \u003cspan class=\"kd\"\u003eprivate\u003c/span\u003e \u003cspan class=\"kd\"\u003efunc\u003c/span\u003e \u003cspan class=\"nf\"\u003eswitchResultAndSetErrorMsg\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"n\"\u003e_\u003c/span\u003e \u003cspan class=\"nv\"\u003eresult\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e\u003cspan class=\"kt\"\u003eResult\u003c/span\u003e\u003cspan class=\"o\"\u003e\u0026lt;\u003c/span\u003e\u003cspan class=\"kt\"\u003eBool\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e\u003cspan class=\"kt\"\u003eError\u003c/span\u003e\u003cspan class=\"o\"\u003e\u0026gt;\u003c/span\u003e\u003cspan class=\"p\"\u003e)\u003c/span\u003e \u003cspan class=\"o\"\u003e-\u0026gt;\u003c/span\u003e \u003cspan class=\"kt\"\u003eBool\u003c/span\u003e\u003cspan class=\"p\"\u003e{\u003c/span\u003e\n        \u003cspan class=\"k\"\u003eswitch\u003c/span\u003e \u003cspan class=\"n\"\u003eresult\u003c/span\u003e \u003cspan class=\"p\"\u003e{\u003c/span\u003e\n        \u003cspan class=\"k\"\u003ecase\u003c/span\u003e \u003cspan class=\"o\"\u003e.\u003c/span\u003e\u003cspan class=\"nf\"\u003esuccess\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"n\"\u003e_\u003c/span\u003e\u003cspan class=\"p\"\u003e)\u003c/span\u003e \u003cspan class=\"p\"\u003e:\u003c/span\u003e\n            \u003cspan class=\"k\"\u003ereturn\u003c/span\u003e \u003cspan class=\"kc\"\u003etrue\u003c/span\u003e\n        \u003cspan class=\"k\"\u003ecase\u003c/span\u003e \u003cspan class=\"o\"\u003e.\u003c/span\u003e\u003cspan class=\"nf\"\u003efailure\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"k\"\u003elet\u003c/span\u003e \u003cspan class=\"nv\"\u003eerror\u003c/span\u003e\u003cspan class=\"p\"\u003e)\u003c/span\u003e \u003cspan class=\"p\"\u003e:\u003c/span\u003e\n            \u003cspan class=\"nf\"\u003eprint\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"n\"\u003eerror\u003c/span\u003e\u003cspan class=\"o\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003elocalizedDescription\u003c/span\u003e\u003cspan class=\"p\"\u003e)\u003c/span\u003e\n            \u003cspan class=\"k\"\u003eself\u003c/span\u003e\u003cspan class=\"o\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003eerrMessage\u003c/span\u003e \u003cspan class=\"o\"\u003e=\u003c/span\u003e \u003cspan class=\"k\"\u003eself\u003c/span\u003e\u003cspan class=\"o\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003eerrModel\u003c/span\u003e\u003cspan class=\"o\"\u003e.\u003c/span\u003e\u003cspan class=\"nf\"\u003esetErrorMessage\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"n\"\u003eerror\u003c/span\u003e\u003cspan class=\"p\"\u003e)\u003c/span\u003e\n            \u003cspan class=\"k\"\u003ereturn\u003c/span\u003e \u003cspan class=\"kc\"\u003efalse\u003c/span\u003e\n        \u003cspan class=\"p\"\u003e}\u003c/span\u003e\n    \u003cspan class=\"p\"\u003e}\u003c/span\u003e\n    \n    \u003cspan class=\"kd\"\u003epublic\u003c/span\u003e \u003cspan class=\"kd\"\u003efunc\u003c/span\u003e \u003cspan class=\"nf\"\u003eresetErrorMsg\u003c/span\u003e\u003cspan class=\"p\"\u003e(){\u003c/span\u003e\n        \u003cspan class=\"k\"\u003eself\u003c/span\u003e\u003cspan class=\"o\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003eerrMessage\u003c/span\u003e \u003cspan class=\"o\"\u003e=\u003c/span\u003e \u003cspan class=\"s\"\u003e\"\"\u003c/span\u003e\n    \u003cspan class=\"p\"\u003e}\u003c/span\u003e\n    \n    \u003cspan class=\"c1\"\u003e// MARK: -\u003c/span\u003e\n    \u003cspan class=\"kd\"\u003epublic\u003c/span\u003e \u003cspan class=\"kd\"\u003efunc\u003c/span\u003e \u003cspan class=\"nf\"\u003egetCurrentUser\u003c/span\u003e\u003cspan class=\"p\"\u003e()\u003c/span\u003e \u003cspan class=\"o\"\u003e-\u0026gt;\u003c/span\u003e \u003cspan class=\"kt\"\u003eUser\u003c/span\u003e\u003cspan class=\"p\"\u003e?\u003c/span\u003e \u003cspan class=\"p\"\u003e{\u003c/span\u003e\n        \u003cspan class=\"k\"\u003ereturn\u003c/span\u003e \u003cspan class=\"k\"\u003eself\u003c/span\u003e\u003cspan class=\"o\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003eauth\u003c/span\u003e\u003cspan class=\"o\"\u003e.\u003c/span\u003e\u003cspan class=\"nf\"\u003egetCurrentUser\u003c/span\u003e\u003cspan class=\"p\"\u003e()\u003c/span\u003e\n    \u003cspan class=\"p\"\u003e}\u003c/span\u003e\n    \n    \u003cspan class=\"c1\"\u003e/// サインアウト\u003c/span\u003e\n    \u003cspan class=\"kd\"\u003epublic\u003c/span\u003e \u003cspan class=\"kd\"\u003efunc\u003c/span\u003e \u003cspan class=\"nf\"\u003esignOut\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"nv\"\u003ecompletion\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e \u003cspan class=\"kd\"\u003e@escaping\u003c/span\u003e \u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"kt\"\u003eBool\u003c/span\u003e\u003cspan class=\"p\"\u003e)\u003c/span\u003e \u003cspan class=\"o\"\u003e-\u0026gt;\u003c/span\u003e \u003cspan class=\"kt\"\u003eVoid\u003c/span\u003e \u003cspan class=\"p\"\u003e)\u003c/span\u003e \u003cspan class=\"p\"\u003e{\u003c/span\u003e\n        \u003cspan class=\"k\"\u003eself\u003c/span\u003e\u003cspan class=\"o\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003eauth\u003c/span\u003e\u003cspan class=\"o\"\u003e.\u003c/span\u003e\u003cspan class=\"kt\"\u003eSignOut\u003c/span\u003e \u003cspan class=\"p\"\u003e{\u003c/span\u003e \u003cspan class=\"n\"\u003eresult\u003c/span\u003e \u003cspan class=\"k\"\u003ein\u003c/span\u003e\n            \u003cspan class=\"nf\"\u003ecompletion\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"k\"\u003eself\u003c/span\u003e\u003cspan class=\"o\"\u003e.\u003c/span\u003e\u003cspan class=\"nf\"\u003eswitchResultAndSetErrorMsg\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"n\"\u003eresult\u003c/span\u003e\u003cspan class=\"p\"\u003e))\u003c/span\u003e\n        \u003cspan class=\"p\"\u003e}\u003c/span\u003e\n    \u003cspan class=\"p\"\u003e}\u003c/span\u003e\n    \n    \u003cspan class=\"c1\"\u003e/// 退会\u003c/span\u003e\n    \u003cspan class=\"kd\"\u003epublic\u003c/span\u003e \u003cspan class=\"kd\"\u003efunc\u003c/span\u003e \u003cspan class=\"nf\"\u003ewithdrawal\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"nv\"\u003ecompletion\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e \u003cspan class=\"kd\"\u003e@escaping\u003c/span\u003e \u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"kt\"\u003eBool\u003c/span\u003e\u003cspan class=\"p\"\u003e)\u003c/span\u003e \u003cspan class=\"o\"\u003e-\u0026gt;\u003c/span\u003e \u003cspan class=\"kt\"\u003eVoid\u003c/span\u003e \u003cspan class=\"p\"\u003e)\u003c/span\u003e \u003cspan class=\"p\"\u003e{\u003c/span\u003e\n        \u003cspan class=\"k\"\u003eself\u003c/span\u003e\u003cspan class=\"o\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003eauth\u003c/span\u003e\u003cspan class=\"o\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003ewithdrawal\u003c/span\u003e \u003cspan class=\"p\"\u003e{\u003c/span\u003e \u003cspan class=\"n\"\u003eresult\u003c/span\u003e \u003cspan class=\"k\"\u003ein\u003c/span\u003e\n            \u003cspan class=\"nf\"\u003ecompletion\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"k\"\u003eself\u003c/span\u003e\u003cspan class=\"o\"\u003e.\u003c/span\u003e\u003cspan class=\"nf\"\u003eswitchResultAndSetErrorMsg\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"n\"\u003eresult\u003c/span\u003e\u003cspan class=\"p\"\u003e))\u003c/span\u003e\n        \u003cspan class=\"p\"\u003e}\u003c/span\u003e\n    \u003cspan class=\"p\"\u003e}\u003c/span\u003e\n    \n    \n\u003cspan class=\"p\"\u003e}\u003c/span\u003e\n\n\u003cspan class=\"c1\"\u003e// MARK: - Email\u003c/span\u003e\n\u003cspan class=\"kd\"\u003eextension\u003c/span\u003e \u003cspan class=\"kt\"\u003eAuthViewModel\u003c/span\u003e \u003cspan class=\"p\"\u003e{\u003c/span\u003e\n    \n    \u003cspan class=\"c1\"\u003e/// サインイン\u003c/span\u003e\n    \u003cspan class=\"kd\"\u003epublic\u003c/span\u003e \u003cspan class=\"kd\"\u003efunc\u003c/span\u003e \u003cspan class=\"nf\"\u003eemailSignIn\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"nv\"\u003eemail\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e\u003cspan class=\"kt\"\u003eString\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e\u003cspan class=\"nv\"\u003epassword\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e\u003cspan class=\"kt\"\u003eString\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e\u003cspan class=\"nv\"\u003ecompletion\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e \u003cspan class=\"kd\"\u003e@escaping\u003c/span\u003e \u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"kt\"\u003eBool\u003c/span\u003e\u003cspan class=\"p\"\u003e)\u003c/span\u003e \u003cspan class=\"o\"\u003e-\u0026gt;\u003c/span\u003e \u003cspan class=\"kt\"\u003eVoid\u003c/span\u003e \u003cspan class=\"p\"\u003e)\u003c/span\u003e \u003cspan class=\"p\"\u003e{\u003c/span\u003e\n        \u003cspan class=\"n\"\u003eemailAuth\u003c/span\u003e\u003cspan class=\"o\"\u003e.\u003c/span\u003e\u003cspan class=\"nf\"\u003esignIn\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"nv\"\u003eemail\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e \u003cspan class=\"n\"\u003eemail\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e \u003cspan class=\"nv\"\u003epassword\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e \u003cspan class=\"n\"\u003epassword\u003c/span\u003e\u003cspan class=\"p\"\u003e)\u003c/span\u003e \u003cspan class=\"p\"\u003e{\u003c/span\u003e \u003cspan class=\"n\"\u003eresult\u003c/span\u003e \u003cspan class=\"k\"\u003ein\u003c/span\u003e\n            \u003cspan class=\"nf\"\u003ecompletion\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"k\"\u003eself\u003c/span\u003e\u003cspan class=\"o\"\u003e.\u003c/span\u003e\u003cspan class=\"nf\"\u003eswitchResultAndSetErrorMsg\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"n\"\u003eresult\u003c/span\u003e\u003cspan class=\"p\"\u003e))\u003c/span\u003e\n        \u003cspan class=\"p\"\u003e}\u003c/span\u003e\n    \u003cspan class=\"p\"\u003e}\u003c/span\u003e\n    \n    \u003cspan class=\"c1\"\u003e/// 新規登録\u003c/span\u003e\n    \u003cspan class=\"kd\"\u003epublic\u003c/span\u003e \u003cspan class=\"kd\"\u003efunc\u003c/span\u003e \u003cspan class=\"nf\"\u003ecreateEmailUser\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"nv\"\u003eemail\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e\u003cspan class=\"kt\"\u003eString\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e\u003cspan class=\"nv\"\u003epassword\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e\u003cspan class=\"kt\"\u003eString\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e\u003cspan class=\"nv\"\u003ename\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e\u003cspan class=\"kt\"\u003eString\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e\u003cspan class=\"nv\"\u003ecompletion\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e \u003cspan class=\"kd\"\u003e@escaping\u003c/span\u003e \u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"kt\"\u003eBool\u003c/span\u003e\u003cspan class=\"p\"\u003e)\u003c/span\u003e \u003cspan class=\"o\"\u003e-\u0026gt;\u003c/span\u003e \u003cspan class=\"kt\"\u003eVoid\u003c/span\u003e \u003cspan class=\"p\"\u003e)\u003c/span\u003e \u003cspan class=\"p\"\u003e{\u003c/span\u003e\n        \u003cspan class=\"n\"\u003eemailAuth\u003c/span\u003e\u003cspan class=\"o\"\u003e.\u003c/span\u003e\u003cspan class=\"nf\"\u003ecreateUser\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"nv\"\u003eemail\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e \u003cspan class=\"n\"\u003eemail\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e \u003cspan class=\"nv\"\u003epassword\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e \u003cspan class=\"n\"\u003epassword\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e \u003cspan class=\"nv\"\u003ename\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e \u003cspan class=\"n\"\u003ename\u003c/span\u003e\u003cspan class=\"p\"\u003e)\u003c/span\u003e \u003cspan class=\"p\"\u003e{\u003c/span\u003e \u003cspan class=\"n\"\u003eresult\u003c/span\u003e \u003cspan class=\"k\"\u003ein\u003c/span\u003e\n            \u003cspan class=\"nf\"\u003ecompletion\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"k\"\u003eself\u003c/span\u003e\u003cspan class=\"o\"\u003e.\u003c/span\u003e\u003cspan class=\"nf\"\u003eswitchResultAndSetErrorMsg\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"n\"\u003eresult\u003c/span\u003e\u003cspan class=\"p\"\u003e))\u003c/span\u003e\n        \u003cspan class=\"p\"\u003e}\u003c/span\u003e\n    \u003cspan class=\"p\"\u003e}\u003c/span\u003e\n    \n    \u003cspan class=\"c1\"\u003e/// 再認証→退会\u003c/span\u003e\n    \u003cspan class=\"kd\"\u003epublic\u003c/span\u003e \u003cspan class=\"kd\"\u003efunc\u003c/span\u003e \u003cspan class=\"nf\"\u003ecredentialEmailWithdrawal\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"nv\"\u003epassword\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e\u003cspan class=\"kt\"\u003eString\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e\u003cspan class=\"nv\"\u003ecompletion\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e \u003cspan class=\"kd\"\u003e@escaping\u003c/span\u003e \u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"kt\"\u003eBool\u003c/span\u003e\u003cspan class=\"p\"\u003e)\u003c/span\u003e \u003cspan class=\"o\"\u003e-\u0026gt;\u003c/span\u003e \u003cspan class=\"kt\"\u003eVoid\u003c/span\u003e \u003cspan class=\"p\"\u003e)\u003c/span\u003e \u003cspan class=\"p\"\u003e{\u003c/span\u003e\n        \u003cspan class=\"n\"\u003eemailAuth\u003c/span\u003e\u003cspan class=\"o\"\u003e.\u003c/span\u003e\u003cspan class=\"nf\"\u003ereAuthUser\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"nv\"\u003epass\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e \u003cspan class=\"n\"\u003epassword\u003c/span\u003e\u003cspan class=\"p\"\u003e)\u003c/span\u003e \u003cspan class=\"p\"\u003e{\u003c/span\u003e \u003cspan class=\"n\"\u003eresult\u003c/span\u003e \u003cspan class=\"k\"\u003ein\u003c/span\u003e\n            \u003cspan class=\"k\"\u003eif\u003c/span\u003e \u003cspan class=\"k\"\u003eself\u003c/span\u003e\u003cspan class=\"o\"\u003e.\u003c/span\u003e\u003cspan class=\"nf\"\u003eswitchResultAndSetErrorMsg\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"n\"\u003eresult\u003c/span\u003e\u003cspan class=\"p\"\u003e)\u003c/span\u003e \u003cspan class=\"p\"\u003e{\u003c/span\u003e\n                \u003cspan class=\"k\"\u003eself\u003c/span\u003e\u003cspan class=\"o\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003ewithdrawal\u003c/span\u003e \u003cspan class=\"p\"\u003e{\u003c/span\u003e \u003cspan class=\"n\"\u003eresult\u003c/span\u003e \u003cspan class=\"k\"\u003ein\u003c/span\u003e\n                    \u003cspan class=\"nf\"\u003ecompletion\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"n\"\u003eresult\u003c/span\u003e\u003cspan class=\"p\"\u003e)\u003c/span\u003e\n                \u003cspan class=\"p\"\u003e}\u003c/span\u003e\n            \u003cspan class=\"p\"\u003e}\u003c/span\u003e\n        \u003cspan class=\"p\"\u003e}\u003c/span\u003e\n    \u003cspan class=\"p\"\u003e}\u003c/span\u003e\n    \n    \u003cspan class=\"c1\"\u003e/// リセットパスワード\u003c/span\u003e\n    \u003cspan class=\"kd\"\u003epublic\u003c/span\u003e \u003cspan class=\"kd\"\u003efunc\u003c/span\u003e \u003cspan class=\"nf\"\u003eresetPassWord\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"nv\"\u003eemail\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e\u003cspan class=\"kt\"\u003eString\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e\u003cspan class=\"nv\"\u003ecompletion\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e \u003cspan class=\"kd\"\u003e@escaping\u003c/span\u003e \u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"kt\"\u003eBool\u003c/span\u003e\u003cspan class=\"p\"\u003e)\u003c/span\u003e \u003cspan class=\"o\"\u003e-\u0026gt;\u003c/span\u003e \u003cspan class=\"kt\"\u003eVoid\u003c/span\u003e \u003cspan class=\"p\"\u003e)\u003c/span\u003e \u003cspan class=\"p\"\u003e{\u003c/span\u003e\n        \u003cspan class=\"n\"\u003eemailAuth\u003c/span\u003e\u003cspan class=\"o\"\u003e.\u003c/span\u003e\u003cspan class=\"nf\"\u003eresetPassWord\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"nv\"\u003eemail\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e \u003cspan class=\"n\"\u003eemail\u003c/span\u003e\u003cspan class=\"p\"\u003e)\u003c/span\u003e \u003cspan class=\"p\"\u003e{\u003c/span\u003e \u003cspan class=\"n\"\u003eresult\u003c/span\u003e \u003cspan class=\"k\"\u003ein\u003c/span\u003e\n            \u003cspan class=\"nf\"\u003ecompletion\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"k\"\u003eself\u003c/span\u003e\u003cspan class=\"o\"\u003e.\u003c/span\u003e\u003cspan class=\"nf\"\u003eswitchResultAndSetErrorMsg\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"n\"\u003eresult\u003c/span\u003e\u003cspan class=\"p\"\u003e))\u003c/span\u003e\n        \u003cspan class=\"p\"\u003e}\u003c/span\u003e\n    \u003cspan class=\"p\"\u003e}\u003c/span\u003e\n    \n\u003cspan class=\"p\"\u003e}\u003c/span\u003e\n\n\u003cspan class=\"c1\"\u003e// MARK: - Google\u003c/span\u003e\n\u003cspan class=\"kd\"\u003eextension\u003c/span\u003e \u003cspan class=\"kt\"\u003eAuthViewModel\u003c/span\u003e \u003cspan class=\"p\"\u003e{\u003c/span\u003e\n    \n    \u003cspan class=\"c1\"\u003e/// サインイン\u003c/span\u003e\n    \u003cspan class=\"kd\"\u003epublic\u003c/span\u003e \u003cspan class=\"kd\"\u003efunc\u003c/span\u003e \u003cspan class=\"nf\"\u003ecredentialGoogleSignIn\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"nv\"\u003ecompletion\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e \u003cspan class=\"kd\"\u003e@escaping\u003c/span\u003e \u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"kt\"\u003eBool\u003c/span\u003e\u003cspan class=\"p\"\u003e)\u003c/span\u003e \u003cspan class=\"o\"\u003e-\u0026gt;\u003c/span\u003e \u003cspan class=\"kt\"\u003eVoid\u003c/span\u003e \u003cspan class=\"p\"\u003e)\u003c/span\u003e \u003cspan class=\"p\"\u003e{\u003c/span\u003e\n        \u003cspan class=\"n\"\u003egoogleAuth\u003c/span\u003e\u003cspan class=\"o\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003egetCredential\u003c/span\u003e \u003cspan class=\"p\"\u003e{\u003c/span\u003e \u003cspan class=\"n\"\u003ecredential\u003c/span\u003e \u003cspan class=\"k\"\u003ein\u003c/span\u003e\n            \u003cspan class=\"k\"\u003eif\u003c/span\u003e \u003cspan class=\"n\"\u003ecredential\u003c/span\u003e \u003cspan class=\"o\"\u003e!=\u003c/span\u003e \u003cspan class=\"kc\"\u003enil\u003c/span\u003e \u003cspan class=\"p\"\u003e{\u003c/span\u003e\n                \u003cspan class=\"k\"\u003eself\u003c/span\u003e\u003cspan class=\"o\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003eauth\u003c/span\u003e\u003cspan class=\"o\"\u003e.\u003c/span\u003e\u003cspan class=\"nf\"\u003ecredentialSignIn\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"nv\"\u003ecredential\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e \u003cspan class=\"n\"\u003ecredential\u003c/span\u003e\u003cspan class=\"o\"\u003e!\u003c/span\u003e\u003cspan class=\"p\"\u003e)\u003c/span\u003e \u003cspan class=\"p\"\u003e{\u003c/span\u003e \u003cspan class=\"n\"\u003eresult\u003c/span\u003e \u003cspan class=\"k\"\u003ein\u003c/span\u003e\n                    \u003cspan class=\"nf\"\u003ecompletion\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"k\"\u003eself\u003c/span\u003e\u003cspan class=\"o\"\u003e.\u003c/span\u003e\u003cspan class=\"nf\"\u003eswitchResultAndSetErrorMsg\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"n\"\u003eresult\u003c/span\u003e\u003cspan class=\"p\"\u003e))\u003c/span\u003e\n                \u003cspan class=\"p\"\u003e}\u003c/span\u003e\n            \u003cspan class=\"p\"\u003e}\u003c/span\u003e\n        \u003cspan class=\"p\"\u003e}\u003c/span\u003e\n    \u003cspan class=\"p\"\u003e}\u003c/span\u003e\n    \n    \u003cspan class=\"c1\"\u003e/// 再認証→退会\u003c/span\u003e\n    \u003cspan class=\"kd\"\u003epublic\u003c/span\u003e \u003cspan class=\"kd\"\u003efunc\u003c/span\u003e \u003cspan class=\"nf\"\u003ecredentialGoogleWithdrawal\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"nv\"\u003ecompletion\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e \u003cspan class=\"kd\"\u003e@escaping\u003c/span\u003e \u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"kt\"\u003eBool\u003c/span\u003e\u003cspan class=\"p\"\u003e)\u003c/span\u003e \u003cspan class=\"o\"\u003e-\u0026gt;\u003c/span\u003e \u003cspan class=\"kt\"\u003eVoid\u003c/span\u003e \u003cspan class=\"p\"\u003e)\u003c/span\u003e \u003cspan class=\"p\"\u003e{\u003c/span\u003e\n        \u003cspan class=\"k\"\u003eself\u003c/span\u003e\u003cspan class=\"o\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003ecredentialGoogleReaAuth\u003c/span\u003e \u003cspan class=\"p\"\u003e{\u003c/span\u003e \u003cspan class=\"n\"\u003eresult\u003c/span\u003e \u003cspan class=\"k\"\u003ein\u003c/span\u003e\n            \u003cspan class=\"k\"\u003eif\u003c/span\u003e \u003cspan class=\"n\"\u003eresult\u003c/span\u003e \u003cspan class=\"p\"\u003e{\u003c/span\u003e\n                \u003cspan class=\"k\"\u003eself\u003c/span\u003e\u003cspan class=\"o\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003ewithdrawal\u003c/span\u003e \u003cspan class=\"p\"\u003e{\u003c/span\u003e \u003cspan class=\"n\"\u003eresult\u003c/span\u003e \u003cspan class=\"k\"\u003ein\u003c/span\u003e\n                    \u003cspan class=\"nf\"\u003ecompletion\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"n\"\u003eresult\u003c/span\u003e\u003cspan class=\"p\"\u003e)\u003c/span\u003e\n                \u003cspan class=\"p\"\u003e}\u003c/span\u003e\n            \u003cspan class=\"p\"\u003e}\u003c/span\u003e\n        \u003cspan class=\"p\"\u003e}\u003c/span\u003e\n    \u003cspan class=\"p\"\u003e}\u003c/span\u003e\n    \n    \u003cspan class=\"c1\"\u003e/// 再認証\u003c/span\u003e\n    \u003cspan class=\"kd\"\u003eprivate\u003c/span\u003e \u003cspan class=\"kd\"\u003efunc\u003c/span\u003e \u003cspan class=\"nf\"\u003ecredentialGoogleReaAuth\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"nv\"\u003ecompletion\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e \u003cspan class=\"kd\"\u003e@escaping\u003c/span\u003e \u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"kt\"\u003eBool\u003c/span\u003e\u003cspan class=\"p\"\u003e)\u003c/span\u003e \u003cspan class=\"o\"\u003e-\u0026gt;\u003c/span\u003e \u003cspan class=\"kt\"\u003eVoid\u003c/span\u003e \u003cspan class=\"p\"\u003e)\u003c/span\u003e \u003cspan class=\"p\"\u003e{\u003c/span\u003e\n        \u003cspan class=\"n\"\u003egoogleAuth\u003c/span\u003e\u003cspan class=\"o\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003egetCredential\u003c/span\u003e \u003cspan class=\"p\"\u003e{\u003c/span\u003e \u003cspan class=\"n\"\u003ecredential\u003c/span\u003e \u003cspan class=\"k\"\u003ein\u003c/span\u003e\n            \u003cspan class=\"k\"\u003eif\u003c/span\u003e \u003cspan class=\"n\"\u003ecredential\u003c/span\u003e \u003cspan class=\"o\"\u003e!=\u003c/span\u003e \u003cspan class=\"kc\"\u003enil\u003c/span\u003e \u003cspan class=\"p\"\u003e{\u003c/span\u003e\n                \u003cspan class=\"k\"\u003eself\u003c/span\u003e\u003cspan class=\"o\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003egoogleAuth\u003c/span\u003e\u003cspan class=\"o\"\u003e.\u003c/span\u003e\u003cspan class=\"nf\"\u003ereAuthUser\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"nv\"\u003euser\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e \u003cspan class=\"k\"\u003eself\u003c/span\u003e\u003cspan class=\"o\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003eauth\u003c/span\u003e\u003cspan class=\"o\"\u003e.\u003c/span\u003e\u003cspan class=\"nf\"\u003egetCurrentUser\u003c/span\u003e\u003cspan class=\"p\"\u003e()\u003c/span\u003e\u003cspan class=\"o\"\u003e!\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e \u003cspan class=\"nv\"\u003ecredential\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e \u003cspan class=\"n\"\u003ecredential\u003c/span\u003e\u003cspan class=\"o\"\u003e!\u003c/span\u003e\u003cspan class=\"p\"\u003e)\u003c/span\u003e \u003cspan class=\"p\"\u003e{\u003c/span\u003e \u003cspan class=\"n\"\u003eresult\u003c/span\u003e \u003cspan class=\"k\"\u003ein\u003c/span\u003e\n                    \u003cspan class=\"nf\"\u003ecompletion\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"k\"\u003eself\u003c/span\u003e\u003cspan class=\"o\"\u003e.\u003c/span\u003e\u003cspan class=\"nf\"\u003eswitchResultAndSetErrorMsg\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"n\"\u003eresult\u003c/span\u003e\u003cspan class=\"p\"\u003e))\u003c/span\u003e\n                \u003cspan class=\"p\"\u003e}\u003c/span\u003e\n            \u003cspan class=\"p\"\u003e}\u003c/span\u003e\n        \u003cspan class=\"p\"\u003e}\u003c/span\u003e\n    \u003cspan class=\"p\"\u003e}\u003c/span\u003e\n    \n\u003cspan class=\"p\"\u003e}\u003c/span\u003e\n\n\u003cspan class=\"c1\"\u003e// MARK: - Apple\u003c/span\u003e\n\u003cspan class=\"kd\"\u003eextension\u003c/span\u003e \u003cspan class=\"kt\"\u003eAuthViewModel\u003c/span\u003e \u003cspan class=\"p\"\u003e{\u003c/span\u003e\n    \n    \u003cspan class=\"c1\"\u003e///  サインイン\u003c/span\u003e\n    \u003cspan class=\"kd\"\u003epublic\u003c/span\u003e \u003cspan class=\"kd\"\u003efunc\u003c/span\u003e \u003cspan class=\"nf\"\u003ecredentialAppleSignIn\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"nv\"\u003ecredential\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e\u003cspan class=\"kt\"\u003eAuthCredential\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e\u003cspan class=\"nv\"\u003ecompletion\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e \u003cspan class=\"kd\"\u003e@escaping\u003c/span\u003e \u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"kt\"\u003eBool\u003c/span\u003e\u003cspan class=\"p\"\u003e)\u003c/span\u003e \u003cspan class=\"o\"\u003e-\u0026gt;\u003c/span\u003e \u003cspan class=\"kt\"\u003eVoid\u003c/span\u003e \u003cspan class=\"p\"\u003e)\u003c/span\u003e \u003cspan class=\"p\"\u003e{\u003c/span\u003e\n        \u003cspan class=\"k\"\u003eself\u003c/span\u003e\u003cspan class=\"o\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003eauth\u003c/span\u003e\u003cspan class=\"o\"\u003e.\u003c/span\u003e\u003cspan class=\"nf\"\u003ecredentialSignIn\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"nv\"\u003ecredential\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e \u003cspan class=\"n\"\u003ecredential\u003c/span\u003e\u003cspan class=\"p\"\u003e)\u003c/span\u003e \u003cspan class=\"p\"\u003e{\u003c/span\u003e \u003cspan class=\"n\"\u003eresult\u003c/span\u003e \u003cspan class=\"k\"\u003ein\u003c/span\u003e\n            \u003cspan class=\"nf\"\u003eprint\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"s\"\u003e\"Apple Login\"\u003c/span\u003e\u003cspan class=\"p\"\u003e)\u003c/span\u003e\n            \u003cspan class=\"nf\"\u003ecompletion\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"k\"\u003eself\u003c/span\u003e\u003cspan class=\"o\"\u003e.\u003c/span\u003e\u003cspan class=\"nf\"\u003eswitchResultAndSetErrorMsg\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"n\"\u003eresult\u003c/span\u003e\u003cspan class=\"p\"\u003e))\u003c/span\u003e\n        \u003cspan class=\"p\"\u003e}\u003c/span\u003e\n    \u003cspan class=\"p\"\u003e}\u003c/span\u003e\n    \n    \u003cspan class=\"c1\"\u003e/// Firebase\u003c/span\u003e\n    \u003cspan class=\"kd\"\u003epublic\u003c/span\u003e \u003cspan class=\"kd\"\u003efunc\u003c/span\u003e \u003cspan class=\"nf\"\u003egetHashAndSetCurrentNonce\u003c/span\u003e\u003cspan class=\"p\"\u003e()\u003c/span\u003e \u003cspan class=\"o\"\u003e-\u0026gt;\u003c/span\u003e \u003cspan class=\"kt\"\u003eString\u003c/span\u003e \u003cspan class=\"p\"\u003e{\u003c/span\u003e\n        \u003cspan class=\"k\"\u003elet\u003c/span\u003e \u003cspan class=\"nv\"\u003enonce\u003c/span\u003e \u003cspan class=\"o\"\u003e=\u003c/span\u003e \u003cspan class=\"n\"\u003eappleAuth\u003c/span\u003e\u003cspan class=\"o\"\u003e.\u003c/span\u003e\u003cspan class=\"nf\"\u003erandomNonceString\u003c/span\u003e\u003cspan class=\"p\"\u003e()\u003c/span\u003e\n        \u003cspan class=\"n\"\u003eappleAuth\u003c/span\u003e\u003cspan class=\"o\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003ecurrentNonce\u003c/span\u003e \u003cspan class=\"o\"\u003e=\u003c/span\u003e \u003cspan class=\"n\"\u003enonce\u003c/span\u003e\n        \u003cspan class=\"k\"\u003ereturn\u003c/span\u003e \u003cspan class=\"n\"\u003eappleAuth\u003c/span\u003e\u003cspan class=\"o\"\u003e.\u003c/span\u003e\u003cspan class=\"nf\"\u003esha256\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"n\"\u003enonce\u003c/span\u003e\u003cspan class=\"p\"\u003e)\u003c/span\u003e\n    \u003cspan class=\"p\"\u003e}\u003c/span\u003e\n    \n    \u003cspan class=\"c1\"\u003e/// ボタンクリック後の結果分岐処理\u003c/span\u003e\n    \u003cspan class=\"kd\"\u003epublic\u003c/span\u003e \u003cspan class=\"kd\"\u003efunc\u003c/span\u003e \u003cspan class=\"nf\"\u003eswitchAuthResult\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"nv\"\u003eresult\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e\u003cspan class=\"kt\"\u003eResult\u003c/span\u003e\u003cspan class=\"o\"\u003e\u0026lt;\u003c/span\u003e\u003cspan class=\"kt\"\u003eASAuthorization\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e \u003cspan class=\"kt\"\u003eError\u003c/span\u003e\u003cspan class=\"o\"\u003e\u0026gt;\u003c/span\u003e\u003cspan class=\"p\"\u003e)\u003c/span\u003e \u003cspan class=\"o\"\u003e-\u0026gt;\u003c/span\u003e \u003cspan class=\"kt\"\u003eAuthCredential\u003c/span\u003e\u003cspan class=\"p\"\u003e?{\u003c/span\u003e\n        \u003cspan class=\"k\"\u003ereturn\u003c/span\u003e \u003cspan class=\"n\"\u003eappleAuth\u003c/span\u003e\u003cspan class=\"o\"\u003e.\u003c/span\u003e\u003cspan class=\"nf\"\u003eswitchAuthResult\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"nv\"\u003eresult\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e \u003cspan class=\"n\"\u003eresult\u003c/span\u003e\u003cspan class=\"p\"\u003e)\u003c/span\u003e\n    \u003cspan class=\"p\"\u003e}\u003c/span\u003e\n    \n    \u003cspan class=\"c1\"\u003e//    public func editUserInfo(credential:AuthCredential,name:String,completion: @escaping (Bool) -\u0026gt; Void ) {\u003c/span\u003e\n    \u003cspan class=\"c1\"\u003e//        if let user = auth.getCurrentUser() {\u003c/span\u003e\n    \u003cspan class=\"c1\"\u003e//            appleAuth.editUserNameApple(user: user, credential: credential, name: name) { result in\u003c/span\u003e\n    \u003cspan class=\"c1\"\u003e//                completion(result)\u003c/span\u003e\n    \u003cspan class=\"c1\"\u003e//            }\u003c/span\u003e\n    \u003cspan class=\"c1\"\u003e//        }\u003c/span\u003e\n    \u003cspan class=\"c1\"\u003e//    }\u003c/span\u003e\n\u003cspan class=\"p\"\u003e}\u003c/span\u003e\n\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\n\u003c/div\u003e\n\u003ch2 data-sourcepos=\"451:1-451:46\"\u003e\n\u003cspan id=\"mvvmアーキテクチャに倣った構造\" class=\"fragment\"\u003e\u003c/span\u003e\u003ca href=\"#mvvm%E3%82%A2%E3%83%BC%E3%82%AD%E3%83%86%E3%82%AF%E3%83%81%E3%83%A3%E3%81%AB%E5%80%A3%E3%81%A3%E3%81%9F%E6%A7%8B%E9%80%A0\"\u003e\u003ci class=\"fa fa-link\"\u003e\u003c/i\u003e\u003c/a\u003eMVVMアーキテクチャに倣った構造\u003c/h2\u003e\n\u003cp data-sourcepos=\"452:1-452:148\"\u003e今回はMVVMアーキテクチャに倣った構造でログイン機能を実装したかったので以下のポイントに注意しました。\u003c/p\u003e\n\u003cul data-sourcepos=\"454:1-460:0\"\u003e\n\u003cli data-sourcepos=\"454:1-454:23\"\u003e各要素の疎結合\u003c/li\u003e\n\u003cli data-sourcepos=\"455:1-455:52\"\u003eView-ViewModel間はデータバインディング\u003c/li\u003e\n\u003cli data-sourcepos=\"456:1-456:45\"\u003eModel-View間は直接的に関わらない\u003c/li\u003e\n\u003cli data-sourcepos=\"457:1-457:47\"\u003eModel-ViewModel間はデータを渡し合う\u003c/li\u003e\n\u003cli data-sourcepos=\"458:1-458:23\"\u003eViewModelが仲介役\u003c/li\u003e\n\u003cli data-sourcepos=\"459:1-460:0\"\u003eModelは依存性をなくす\u003c/li\u003e\n\u003c/ul\u003e\n\u003cp data-sourcepos=\"461:1-461:96\"\u003eんー？意識はしてみたものの完璧にできているか自信はありません。。\u003c/p\u003e\n\u003cp data-sourcepos=\"463:1-465:81\"\u003eまだまだ勉強は必要そうです。。\u003cbr\u003e\nご覧いただきありがとうございました！\u003cbr\u003e\nこの記事で作成したログイン機能はGitHubに公開しています！\u003c/p\u003e\n\u003cp data-sourcepos=\"467:1-467:73\"\u003e\u003ciframe id=\"qiita-embed-content__62d91bf2cb645e7940abe33107336592\" src=\"https://qiita.com/embed-contents/link-card#qiita-embed-content__62d91bf2cb645e7940abe33107336592\" data-content=\"https%3A%2F%2Fgithub.com%2Famefure%2FiOS-Login-function-following-MVVM-architecture\" frameborder=\"0\" scrolling=\"no\" loading=\"lazy\" style=\"width:100%;\" height=\"29\"\u003e\n\u003c/iframe\u003e\n\u003c/p\u003e\n","body":"\n\n自身の知識の整理と備忘録も兼ねて「**Swift UI × MVVMアーキテクチャ**」でログイン機能を実装する流れをまとめてみます。\n\n間違っている点やこうした方が良くなるなどの意見がありましたらコメントくださると嬉しいです。完成したものはGitHubにて公開しています！\n\nhttps://github.com/amefure/iOS-Login-function-following-MVVM-architecture\n\n# 環境\n\n+  Swift UI\n+  Xcode\n+  Firebase \n+  Cocoa Pods\n+  MVVMアーキテクチャ\n\nログイン機能を実装するにあたって全て自分で実装したわけではなくGoogleが提供しているクラウドサービスである**FirebaseのAuthentication**を利用していきます。\n\n\nAuthentication(以下Auth)を組み込むだけでユーザー情報の登録や操作、ログイン状態の管理、確認メールやリセットメールなどのメール送信機能を簡単に実装でき、かつ**無料の範囲でも1日3000人まで利用可能**になっています。\n\n[【Swift UI/Firebase】Authentication導入方法と使い方！ログイン機能の実装](https://appdev-room.com/swift-firebase-realtime-authentication)\n\n# 概要\n今回作成するログイン機能は以下の3つのプロバイダアカウントを使用できるように実装します。もしiOSアプリを公開するならAppleアカウントでのログインを実装しないとリジェクトされることもあるので注意してください。\n\n1. E-mail/パスワード\n1. Googleアカウント\n1. Appleアカウント\n\nまたログイン機能を実装するにあたって必要になりそうな機能を洗い出してみます。\n\n+ 新規登録\n+ ログイン(サインイン)\n+ ログアウト(サインアウト)\n+ 退会\n+ パスワードリセット\n\n英語の意味的にログインではなく「サインイン」が正しいようなのでサインインを使います。\n\n\n# 設計\n\n正しいかどうかわかりませんが**MVVMアーキテクチャに倣って整理**してみました。\n\nhttps://appdev-room.com/swift-mvvm\n\n## M：Models\n+ Auth操作モデル\n+ 各プロバイダ操作モデル\n\nモデルとして切り出すのはAuthの操作です。ユーザー登録やサインイン、退会処理などAuthを直接操作するようなAPIを実装します。\n\n![58E1AF22-AFC8-4485-AD6F-05817026612F.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2264957/b9f84176-0299-012b-02aa-a7fa02f54320.png)\n\n## V：View\n+ サインイン画面\n+ 新規登録画面\n+ パスワードリセット画面\n+ Googleアカウントでの登録\n+ Appleアカウントでの登録\n+ サインイン時のみ表示される画面\n+ サインアウト\u0026退会画面\n\n必要となる画面は上記の通りです。\n\n## VM： ViewModel\n+ Auth絡みのモデルを操作するViewModel\n+ バリデーション\n\nモデルを操作するためのViewModelとバリデーションロジック用のViewModelを実装します。\n\n# 前準備\nまずは「Firebase × Xcode」のセットアップを行います。流れだけざっと確認しておきます。\n\n1. プロジェクトの作成\n1. Firebaseのセットアップ\n1. FirebaseプロジェクトにiOSアプリを追加\n1. Cocoa Podsの組み込み\u0026ライブラリ導入\n1. エントリーポイントに初期化コードを記載\n1. 各プロバイダを使用可能にする\n\n\n## プロジェクトの作成\nまずはXcodeよりSwiftプロジェクトを作成します。\n\n![77FDFBF0-005C-4293-8AF8-FD7FAAEFE479.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2264957/648af42c-5c56-b087-a079-4cf339ac4c6c.png)\n\n今回は**インターフェースをSwift UIで構築**していきます。\n\n## Firebaseのセットアップ\nFirebaseにログインして**Firebaseプロジェクトを作成**します。\n\n作成したらAuthを使えるように「**始める**」をクリックします\n\n![6A4602BC-85D7-4B72-8690-36C01BDD708A.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2264957/3880c978-ef1e-0b6b-a8f8-887b0f192e23.png)\n\nサインイン方法を選択できるので「**メール/パスワード**」と「**Google**」「**Apple**」を選択して進みます。\n\n\n![0E7223D7-F686-4436-BBD8-680EBFD1A7EE.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2264957/2ada6aa6-e2b2-d5f1-1c3d-f04583196a92.png)\n\n[【Swift UI/Xcode】Firebaseの導入方法！Cocoa Podsでの使い方](https://appdev-room.com/swift-firebase)\n\n\n## FirebaseプロジェクトにiOSアプリを追加\nFirebaseプロジェクトにiOSアプリを追加します。\n![6BB50E7E-9A9B-4F7D-AF4E-AAE2BAD4B2A8.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2264957/c77a5681-adb0-4e0b-07aa-40e40899e29e.png)\n\nSwiftプロジェクトにダウンロードしたり**GoogleService-Info.plist**」を組み込みます。\n\n\n![03411DEE-9553-4F79-B11A-C54008EA8CE5.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2264957/f686ef58-73b1-e2f6-8e53-7a0a6cec6069.png)\n\n\n「Copy items if needed」と「Create groups」にチェックを入れて「Finish」をクリックします。\n\n![0BAA1332-D111-4C44-9945-527A060DB90B.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2264957/0b910528-4d0e-235d-b004-66c5e224b9b5.png)\n\n## Cocoa Podsの組み込み\u0026Authの導入\n続いてライブラリ管理ツールである「Cocoa Pods」をプロジェクトに組み込みながらAuthとGoogleアカウントサインインに必要なライブラリを導入します。\nターミナルで以下のコマンドを打ち込み「PodFile」を生成します。\n\n```ruby:ターミナル\n$ cd プロジェクトまでのパス\n$ pod init\n```\n生成できたらPodFile内に各ライブラリをインストールするコードを追加します。\n```ruby:PodFile\n# Uncomment the next line to define a global platform for your project\n# platform :ios, '9.0'\n\ntarget '(プロジェクト名)' do\n\n  use_frameworks!\n\n  pod 'FirebaseAuth'\n  pod 'GoogleSignIn'\n\nend\n```\n最後に実行すればプロジェクトにAuthが組み込まれます。\n\n```ruby:ターミナル\n$ pod install\n```\n\n[【Swift UI】CocoaPodsのインストール方法と使い方！](https://appdev-room.com/swift-cocoapods)\n\n## エントリーポイントに初期化コードを記載\n\n```swift:TestFirebaseApp\nimport SwiftUI\nimport FirebaseCore // 追加\n\n// 追加\nclass AppDelegate: NSObject, UIApplicationDelegate {\n    func application(_ application: UIApplication,\n                    didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey : Any]? = nil) -\u003e Bool {\n        FirebaseApp.configure()\n        return true\n    }\n}\n\n@main\nstruct TestFirebaseApp: App {\n    // 追加\n    @UIApplicationDelegateAdaptor(AppDelegate.self) var delegate\n    var body: some Scene {\n        WindowGroup {\n            ContentView()\n        }\n    }\n}\n```\n\nこれで**下準備は完成**です。\n\n[【Swift UI/Firebase】Authentication導入方法と使い方！ログイン機能の実装](https://appdev-room.com/swift-firebase-realtime-authentication)\n\n## 「Sign in with Apple」を実装するために\n\nAppleアカウントでのサインイン(= 「Sign in with Apple」)を実装するためには前準備がいくつか必要です。\n\n1. App IDの登録と「Sign in with Apple」の有効\n1. Xcodeの「Signing \u0026 Capability」にと「Sign in with Apple」の追加\n1. Appleサインインボタンの実装\n\n[【Swift UI/Firebase】AuthenticationでSign in with Appleでログインを実装する方法](https://appdev-room.com/swift-firebase-authentication-apple)\n\n## Googleアカウントでのサインインを実装するために\nGoogleアカウントでのサインインを実装するためにもいくつか前準備が必要です。\n\n1. カスタムURLスキームの追加\n1. AppDelegateクラスへの追記\n1. Googleサインインボタンの実装\n\n[【Swift UI/Firebase】AuthenticationでGoogleアカウントでログインを実装する方法！](https://appdev-room.com/swift-firebase-authentication-google)\n\n# ログイン機能の処理をMVVMに分割していく\n浅はかな知識ながらMVVMアーキテクチャを意識してサインイン機能の責務を分割していきました。\n\n## Model\n結果モデルは大元1つと各プロバイダごとに1つずつ、エラーモデル1つになりました。\n\n1. AuthModel.swift\n1. EmailAuthModel.swift\n1. GoogleAuthModel.swift\n1. AppleAuthModel.swift\n1. AuthErrorModel.swift\n\n```swift:AuthModel.swift\nimport UIKit\nimport FirebaseAuth\nimport FirebaseCore\nimport AuthenticationServices\nimport SwiftUI\n\nclass AuthModel {\n\n    // MARK: - シングルトン\n    static let shared = AuthModel()\n    \n    // MARK: - リファレンス\n    private  let auth = Auth.auth()\n\n    \n    // MARK: - \n    public func getCurrentUser() -\u003e User? {\n        return auth.currentUser\n    }\n    \n    public let defaultName = \"no name\"\n    \n    // MARK: - Sign In for Credential\n    public func credentialSignIn(credential: AuthCredential,completion : @escaping (Result\u003cBool, Error\u003e) -\u003e  Void ){\n        self.auth.signIn(with: credential) { (authResult, error) in\n            if error == nil {\n            if authResult?.user != nil{\n                if authResult!.user.displayName == nil{\n                    // Appleアカウントの場合は存在しない\n                }\n                completion(.success(true))\n            }\n            }else{\n                completion(.failure(error!))\n            }\n        }\n    }\n    \n    // MARK: - サインアウト処理\n    public func SignOut(completion : @escaping (Result\u003cBool, Error\u003e) -\u003e  Void ){\n        do{\n            try auth.signOut()\n            print(\"SignOut\")\n            completion(.success(true))\n        } catch let signOutError as NSError {\n            completion(.failure(signOutError))\n        }\n    }\n    \n    // MARK: -  各プロバイダ退会処理 \u0026 Appleアカウントは直呼び出し\n    public func withdrawal(completion : @escaping (Result\u003cBool, Error\u003e) -\u003e  Void ){\n        if let user = auth.currentUser {\n            user.delete { error in\n                if error == nil {\n                    print(\"退会成功\")\n                    completion(.success(true))\n                } else {\n                    print(\"退会失敗\")\n                    completion(.failure(error!))\n                }\n            }\n        }\n    }\n}\n```\n## ViewModel\n\nAuthに絡んだ各Modelを一括で操作するViewModelにしました。なかなか大きくなってしまいましたがここをもう少し切り分けた方が良かったのかな？\n\n1. AuthViewModel.swift\n1. ValidationViewModel.swift\n\n```swift:AuthViewModel.swift\n\nimport UIKit\nimport FirebaseAuth\nimport FirebaseCore\nimport AuthenticationServices // Apple\n\nclass AuthViewModel:ObservableObject {\n    \n    // MARK: - シングルトン\n    static let shared = AuthViewModel()\n    \n    private var auth = AuthModel.shared\n    private let emailAuth = EmailAuthModel.shared\n    private let googleAuth = GoogleAuthModel.shared\n    private let appleAuth = AppleAuthModel.shared\n    \n    private let errModel = AuthErrorModel()\n    \n    @Published var errMessage:String = \"\"\n    \n    private func switchResultAndSetErrorMsg(_ result:Result\u003cBool,Error\u003e) -\u003e Bool{\n        switch result {\n        case .success(_) :\n            return true\n        case .failure(let error) :\n            print(error.localizedDescription)\n            self.errMessage = self.errModel.setErrorMessage(error)\n            return false\n        }\n    }\n    \n    public func resetErrorMsg(){\n        self.errMessage = \"\"\n    }\n    \n    // MARK: -\n    public func getCurrentUser() -\u003e User? {\n        return self.auth.getCurrentUser()\n    }\n    \n    /// サインアウト\n    public func signOut(completion: @escaping (Bool) -\u003e Void ) {\n        self.auth.SignOut { result in\n            completion(self.switchResultAndSetErrorMsg(result))\n        }\n    }\n    \n    /// 退会\n    public func withdrawal(completion: @escaping (Bool) -\u003e Void ) {\n        self.auth.withdrawal { result in\n            completion(self.switchResultAndSetErrorMsg(result))\n        }\n    }\n    \n    \n}\n\n// MARK: - Email\nextension AuthViewModel {\n    \n    /// サインイン\n    public func emailSignIn(email:String,password:String,completion: @escaping (Bool) -\u003e Void ) {\n        emailAuth.signIn(email: email, password: password) { result in\n            completion(self.switchResultAndSetErrorMsg(result))\n        }\n    }\n    \n    /// 新規登録\n    public func createEmailUser(email:String,password:String,name:String,completion: @escaping (Bool) -\u003e Void ) {\n        emailAuth.createUser(email: email, password: password, name: name) { result in\n            completion(self.switchResultAndSetErrorMsg(result))\n        }\n    }\n    \n    /// 再認証→退会\n    public func credentialEmailWithdrawal(password:String,completion: @escaping (Bool) -\u003e Void ) {\n        emailAuth.reAuthUser(pass: password) { result in\n            if self.switchResultAndSetErrorMsg(result) {\n                self.withdrawal { result in\n                    completion(result)\n                }\n            }\n        }\n    }\n    \n    /// リセットパスワード\n    public func resetPassWord(email:String,completion: @escaping (Bool) -\u003e Void ) {\n        emailAuth.resetPassWord(email: email) { result in\n            completion(self.switchResultAndSetErrorMsg(result))\n        }\n    }\n    \n}\n\n// MARK: - Google\nextension AuthViewModel {\n    \n    /// サインイン\n    public func credentialGoogleSignIn(completion: @escaping (Bool) -\u003e Void ) {\n        googleAuth.getCredential { credential in\n            if credential != nil {\n                self.auth.credentialSignIn(credential: credential!) { result in\n                    completion(self.switchResultAndSetErrorMsg(result))\n                }\n            }\n        }\n    }\n    \n    /// 再認証→退会\n    public func credentialGoogleWithdrawal(completion: @escaping (Bool) -\u003e Void ) {\n        self.credentialGoogleReaAuth { result in\n            if result {\n                self.withdrawal { result in\n                    completion(result)\n                }\n            }\n        }\n    }\n    \n    /// 再認証\n    private func credentialGoogleReaAuth(completion: @escaping (Bool) -\u003e Void ) {\n        googleAuth.getCredential { credential in\n            if credential != nil {\n                self.googleAuth.reAuthUser(user: self.auth.getCurrentUser()!, credential: credential!) { result in\n                    completion(self.switchResultAndSetErrorMsg(result))\n                }\n            }\n        }\n    }\n    \n}\n\n// MARK: - Apple\nextension AuthViewModel {\n    \n    ///  サインイン\n    public func credentialAppleSignIn(credential:AuthCredential,completion: @escaping (Bool) -\u003e Void ) {\n        self.auth.credentialSignIn(credential: credential) { result in\n            print(\"Apple Login\")\n            completion(self.switchResultAndSetErrorMsg(result))\n        }\n    }\n    \n    /// Firebase\n    public func getHashAndSetCurrentNonce() -\u003e String {\n        let nonce = appleAuth.randomNonceString()\n        appleAuth.currentNonce = nonce\n        return appleAuth.sha256(nonce)\n    }\n    \n    /// ボタンクリック後の結果分岐処理\n    public func switchAuthResult(result:Result\u003cASAuthorization, Error\u003e) -\u003e AuthCredential?{\n        return appleAuth.switchAuthResult(result: result)\n    }\n    \n    //    public func editUserInfo(credential:AuthCredential,name:String,completion: @escaping (Bool) -\u003e Void ) {\n    //        if let user = auth.getCurrentUser() {\n    //            appleAuth.editUserNameApple(user: user, credential: credential, name: name) { result in\n    //                completion(result)\n    //            }\n    //        }\n    //    }\n}\n```\n\n## MVVMアーキテクチャに倣った構造\n今回はMVVMアーキテクチャに倣った構造でログイン機能を実装したかったので以下のポイントに注意しました。\n\n+ 各要素の疎結合\n+ View-ViewModel間はデータバインディング\n+ Model-View間は直接的に関わらない\n+ Model-ViewModel間はデータを渡し合う\n+ ViewModelが仲介役\n+ Modelは依存性をなくす\n\nんー？意識はしてみたものの完璧にできているか自信はありません。。\n\nまだまだ勉強は必要そうです。。\nご覧いただきありがとうございました！\nこの記事で作成したログイン機能はGitHubに公開しています！\n\nhttps://github.com/amefure/iOS-Login-function-following-MVVM-architecture\n","coediting":false,"comments_count":0,"created_at":"2023-04-05T08:39:26+09:00","group":null,"id":"7da45a163e6f06eda6cf","likes_count":13,"private":false,"reactions_count":0,"stocks_count":8,"tags":[{"name":"iOS","versions":[]},{"name":"authentication","versions":[]},{"name":"Swift","versions":[]},{"name":"Firebase","versions":[]},{"name":"SwiftUI","versions":[]}],"title":"【SwiftUI】MVVM構造でログイン機能を実装してみた","updated_at":"2024-04-19T11:03:31+09:00","url":"https://qiita.com/amefure/items/7da45a163e6f06eda6cf","user":{"description":"20代/非IT系の会社/\r\n趣味の範囲でプログラミング学習中\r\nWebアプリケーションやiOSアプリの作成の過程などを投稿していきます。\r\n言語:PHP/JavaScript/VBA/Swift\r\nフレームワーク:Laravel/Vue.js","facebook_id":"","followees_count":3,"followers_count":14,"github_login_name":"amefure","id":"amefure","items_count":14,"linkedin_id":"","location":"","name":"ame","organization":"","permanent_id":2264957,"profile_image_url":"https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2264957/profile-images/1636463175","team_only":false,"twitter_screen_name":"ame_network","website_url":"https://appdev-room.com/"},"page_views_count":null,"team_membership":null,"organization_url_name":null,"slide":false,"posting_campaign_uuid":null}