SwiftUI

SwiftUIでGoogle APIを使用する 1 [Google Sign In]

GoogleにはGoogle Drive、Google Calendar、Gmail、Google Mapなど、さまざまなサービスが存在します。そこでSwiftUIでGoogle APIを利用する方法について紹介していきます。今回は、GoogleアカウントをSwiftUIで利用できるようにします。

プロジェクトの作成

まず、Xcode上でプロジェクトを作成しておきます。

Appを選択し、次に進みます。

実機でテストを行うのでTeamにAppleIDでログインしておいてください。

作成したらツールバーのFile→Add Packages…をクリックし、Swift Package Managerを開きます。

右上の検索バーにGoogle Sign-InのリポジトリのURLを入力します。

https://github.com/google/GoogleSignIn-iOS

GoogleSignIn-iOSが表示されたら選択してAdd Pakegesをクリックします。

SwiftUIで利用するのでGoogleSignInとGoogleSignInSwiftどちらも選択してAdd Pakegeをクリックします。

終わったらGoogle Cloud Consoleにログインし、Googleアカウントのログインフォームを作るためのIDを取得します。

OAuth クライアントIDの取得

https://console.cloud.google.com/

Google Cloud Consoleにログインします。

Google Cloud Platformの利用規約に同意して続行をクリックします。

https://developers.google.com/identity/sign-in/ios/start-integrating?hl=ja#get_an_oauth_client_id

上記の公式ドキュメントの「Oauth クライアントIDを作成する」というボタンをクリックすると、プロジェクトの作成から設定まで行えるので楽です。

  • STEP1
    プロジェクト名の入力
    プロジェクトの名前を入力します。適当なものを入れましょう。
  • STEP2
    プロダクト名の入力
    プロダクト名は、実際に作成したログインフォームに「[プロダクト名]」へログイン」などと表示される名前です。
  • STEP3
    プラットフォームの選択
    Google APIを利用するプラットフォームを選択します。
  • STEP4
    Bundle Identifierの入力
    Xcodeのプロジェクト作成時にしようしたバンドルIDを入力します。
  • STEP5
    plistのダウンロード
    クライアントIDなどが記載されたplistをダウンロードします。

XcodeにクライアントIDの追加

次に先ほどダウンロードしたcredentials.plistを開きます。内容はCLIENT_ID、REVERSED_CLIENT_ID、PLIST_VERSION、BUNDLE_IDで構成されてます。

REVERSED_CLIENT_IDはCLIENT_IDを逆に読んだものです。

CLIENT_ID=1234567890-abcdef.apps.googleusercontent.com

なら

REVERSED_CLIENT_ID=com.googleusercontent.apps.1234567890-abcdef

というようになります。

https://developers.google.com/identity/sign-in/ios/start-integrating?hl=ja#add_client_id

ここのREVERSED_CLIENT_IDをコピーしておきます。

次にXcodeプロジェクトを選択してInfoに移動します。

次にURL Typesにプラスボタンをクリックして新規追加し、URLSchemesに先ほどのREVERSED_CLIENT_IDを入力します。

最後に、Custom iOS Target Propertiesの中で右クリックして、AddRowを選択します。

KeyにGIDClientID、TypeはString、Valueに自分のCLIENT_IDを入力したら完了です。

App構造体の編集

次に、SwiftUIでGoogle Sign Inを利用できるようにするためにプロジェクト名App.swiftというファイルで次のように編集します。

import SwiftUI
import GoogleSignIn
@main
struct GoogleAPIApp: App {
    var body: some Scene {
        WindowGroup {
            ContentView()
                .onOpenURL{ url in
                    GIDSignIn.sharedInstance.handle(url)
                }
                .onAppear{
                    GIDSignIn.sharedInstance.restorePreviousSignIn{ user,error in
                    }
                }
        }
    }
}

ここに入力することでシーンからURLを受け取ってhandleを呼び出します。また、再度アプリを起動した時にログイン状態を維持するためにonAppearでリストアを行います。

GoogleSignInButtonの追加

続いてGoogleSignInButtonを追加します。ここをクリックすることでGoogleアカウントのログイン画面を表示させることができます。

import SwiftUI
import GoogleSignIn
import GoogleSignInSwift
struct ContentView: View {
    var body: some View {
        VStack {
            Image(systemName: "globe")
                .imageScale(.large)
                .foregroundColor(.accentColor)
            Text("Hello, world!")
            GoogleSignInButton()
            
        }
        .padding()
    }
}

struct ContentView_Previews: PreviewProvider {
    static var previews: some View {
        ContentView()
    }
}

操作はGoogleSignInとGoogleSignInSwiftをインポートした後GoogleSignInButton()というビューを追加するだけです。

このGoogleSignInButtonは「G Sign In」と、Googleでサインインできるように見えますが、ただのボタンなのでここをクリックしても何も起きません。

そのため、次のようにしてボタンの挙動を設定します。

まず、ボタンを押された時に呼び出される関数を定義します。

func handleSignInButton() {
        guard let presentingviewcontroller = (UIApplication.shared.connectedScenes.first as? UIWindowScene)?.windows.first?.rootViewController else {return}
        GIDSignIn.sharedInstance.signIn(withPresenting: presentingviewcontroller) { signInResult, error in
            guard signInResult != nil else {
                return
            }
        }
    }

そうしたら、GoogleSignInButtonを次のように変更します。

GoogleSignInButton(action: handleSignInButton)

これで、Googleアカウントにサインインするボタンを作ることができました。

Googleの様々なAPIを利用するためにはユーザーからデータの取得許可を得る必要があります。ユーザーの承諾とスコープの付与を行うために以下のコードをguard signInresult以下に入力します。

 guard let currentuser = GIDSignIn.sharedInstance.currentUser else {
                return
            }
            let scopes = ["YOUR_SCOPE"]
            currentuser.addScopes(scopes, presenting: presentingviewcontroller) { signInResult,error in
                guard error == nil else {return}
                guard signInResult != nil else {return}
            }

currentuserでログインしているユーザーを取得し、scopesで任意のスコープを入力し、アクセス権を付与します。また、https://console.cloud.google.com/で使用するスコープの設定が必要です。そのうち説明します。

ログアウトボタンを作る

Google アカウントからログアウトするためのボタンを作ります。

ログアウトボタンには特に使用できるデザインはないので自分で作成します。

GIDSignIn.sharedInstance.signOut()

を呼び出すことができれば良いので次のように作ってみました。

フレームの横幅が、使用した機種であるiPhone SE以外では必ずしも一致しないことを失念していました。対処として、先にmaxWidthで最大に横幅を設定した後、高さを設定しています。

Button(action:{
                GIDSignIn.sharedInstance.signOut()
            }){
                HStack{
                    Image(systemName: "escape")
                        .padding(.leading,10)
                        .padding(.trailing,3)
                    Text("ログアウト")
                }
                .bold()
                .font(.title3)
                    .frame(maxWidth: .infinity, alignment: .leading)
                    .frame(height: 40)
                    .foregroundColor(.gray)
                    .background(Color.white)
                    .cornerRadius(3)
                    .shadow(color: .gray,radius: 2,y:2)
            }

・・・。

実験で使うので、まあ良いことにしましょう。

データを取得してみる

それでは最後にGoogleアカウントからいくつか情報を取得してみましょう。

ログインしているユーザーはGIDSignIn.sharedInstance.currentUserで取得することができます。

そのため、以下のような書き方で名前を表示することができます。

Text(GIDSignIn.sharedInstance.currentUser?.profile?.name ?? "")

https://developers.google.com/identity/sign-in/ios/reference/Classes/GIDGoogleUser

上記のリファレンスにprofileから取得できるデータが記載されています。

しかし、これだけではリアルタイムで名前を表示することができませんでした。そこで、画面を強制的に更新するために新しい変数を作り、Textで表示することでその変数の値を変えたときに画面が更新するようにします。もっと合理的な画面の更新方法を知っている方はぜひ教えてください。

import SwiftUI
import GoogleSignIn
import GoogleSignInSwift
struct ContentView: View {
    @State var update = 0
    func handleSignInButton() {
        guard let presentingviewcontroller = (UIApplication.shared.connectedScenes.first as? UIWindowScene)?.windows.first?.rootViewController else {return}
        GIDSignIn.sharedInstance.signIn(withPresenting: presentingviewcontroller) { signInResult, error in
            update += 1
            guard signInResult != nil else {
                return
            }
        }
    }
    var body: some View {
        VStack {
            Text(GIDSignIn.sharedInstance.currentUser?.profile?.name ?? "")
            GoogleSignInButton(action: handleSignInButton)
            Button(action:{
                GIDSignIn.sharedInstance.signOut()
                update += 1
            }){
                HStack{
                    Image(systemName: "escape")
                        .padding(.leading,10)
                        .padding(.trailing,3)
                    Text("ログアウト")
                }
                .bold()
                .font(.title3)
                .frame(maxWidth: .infinity, alignment: .leading)
                .frame(height: 40) 
                .foregroundColor(.gray)
                .background(Color.white)
                .cornerRadius(3)
                .shadow(color: .gray,radius: 2,y:2)
            }
            
            if (UITraitCollection.current.userInterfaceStyle == .dark) {
                Text("\(update)")
                    .foregroundColor(.black)
            }   else {
                Text("\(update)")
                    .foregroundColor(.white)
            }
        }
        .padding()
    }
}

updateの変数をTextで表示した時、ダークモードかそうでないかを切り替えて文字色を変えることで、背景と同化して見えなくなります。ですが、アプリの起動中にモードを変更した場合は次の画面の更新まで色が変わらないため見えてしまうので少し注意が必要です。

ログイン画面とその後の画面を分ける方がいいかもしれません。

お疲れ様でした。

気になるアイテム



COMMENT

メールアドレスが公開されることはありません。 が付いている欄は必須項目です