Google Sign-In 6.0.0をSwiftUIで使う
先日Google Sign-Inのバージョンが6.0.0に上がりSwift Package Managerに対応した。AdMobのSDKをCocoaPodを使いたくなくてバイナリを直でダウンロードして使っていた自分には、こうしてどんどんSwift Package Managerに対応してくれるのは朗報。
というわけで、早速Google Sign-In 6.0.0をSwiftUIで試してみた。
Swift Package MangerでGoogle Sign-In 6.0.0を使う
XcodeでSwift Package Managerを使ってパッケージをプロジェクトに加える方法は以下のページに詳しく載っている。
今回はGithubからGoogle Sign-In 6.0.0をダウンロードして使用する。- Xcodeでプロジェクトを作成したら File->Swift Packages->Add Package Dependency…を選択
- 表示されるダイアログでGoogle Sign-In 6.0.0のアドレス
https://github.com/google/GoogleSignIn-iOS
を入力
- RulesがUp to Next Major 6.0.0になっているのを確認して先へ進む
- GoogleSignInが表示されるのを確認してFinish。
- プロジェクトのSwift Package Dependenciesにパッケージが表示される。
- 最後にXcodeのTARGETS->InfoからURL Typesで新しくURL Schemesを加える。
入力するURL Schemeはclient IDを逆順にしたもの。(client ID作成時にダウンロードしたplistのREVERSED_CLIENT_IDをコピペするのが楽。)
- 後はコードの中で
import GoogleSignIn
すれば使用できる。
import GoogleSignIn
Google Sign-In 6.0.0の新しい部分
Google Sign-In 6.0.0の詳細は以下のページに詳しく書いてある。
色々と変更はあるが自分の既存のコードを書き換える必要がある場所は大体以下の通り。- どの操作もdelegateを使わずにcallbackを使うようになった
今まではdelegateを設定してそこで各種操作後の処理を行なっていたが、これからはdelegateなしで操作を行う場合にcallbackを渡すことになる。 - SignIn時のconfigurationは
GIDConfiguration
にまとめられた -
GIDSignInButton
が内部でGIDSignIn
を呼ばなくなった
クリックしてもサインインしてくれないただのボタンになったのでIBAction
なりなんなりで自分でsignInWithConfiguration:presentingViewController:callback:
を呼ぶ必要がある。また、SwiftUIでGIDSignInButton
を使う場合はIBAction
で繋げることはできないのでUIViewRepresentable
を使うことになる。
SwiftUIで使ってみる
前に作ったXcodeでGoogleSignInを使うのサンプルをGoogle Sign-In 6.0.0に書き換えてみた。
基本的にはGet started with Google Sign-In for iOSのコードをそのまま使用して書き換えている。
以下、前のサンプルからの変更点を幾つか。
GIDSignInButtonの処理
まずGIDSignInButton
をSwiftUIで使うためにUIViewRepresentable
プロトコルに対応した新しいクラスGoogleSignInBtn
を作成。makeView(context:)
の中でGIDSignInButton
のインスタンスを作成して見た目と挙動を設定している。
struct GoogleSignInBtn: UIViewRepresentable {
typealias UIViewType = GIDSignInButton
func makeView(context: Context) -> GIDSignInButton {
let button = GIDSignInButton()
// 見た目設定は省略
button.addAction(.init(handler: { _ in
guard let presentingViewController = (UIApplication.shared.connectedScenes.first as? UIWindowScene)?.windows.first?.rootViewController else {return}
GIDSignIn.sharedInstance.signIn(
with: self.userData.signInConfig,
presenting: presentingViewController,
callback: { user, error in
if let user = user {
self.userData.signIn(user: user)
}
else{
if let error = error {
print("error: \(error.localizedDescription)")
}
}
})
}), for: .touchUpInside)
}
func updateView(_ uiView: GIDSignInButton, context: Context) {
}
}
前のサンプルではdelegateを使ってサインイン時の処理を行なっていたが、今回はGIDSignIn
のsignIn:with:presenting:callback
を使って行うことになる。この処理はボタンの挙動をaddAction
で設定している部分で行なっている。
UserDataで情報アップデート
ビューの書き換えはUserData
を介して行う。
前のサンプルではUserData
がdelegateになってここで各種処理を行なっていたが今回は各種処理がなくなってデータの保持が主な仕事。
サインイン/アウトしたユーザはsignIn(user:)
で@Published var user: GIDGoogleUser?
に保持され画面が書き変わる。画像データはネットワークアクセスが入るのでDispatchQueue
で非同期にアップデートしている。
GIDConfiguration
はここに作成している。UserData
がenvironmentObject
でビューに渡されサインイン時に参照される。
fileprivate let clientID = "<clientID>"
fileprivate let grayAvatorImage: UIImage = UIImage(named: "PlaceholderAvatar")!
class UserData: NSObject, ObservableObject {
@Published var user: GIDGoogleUser?
@Published var avatorImage: UIImage = grayAvatorImage
var signInConfig = GIDConfiguration.init(clientID: clientID)
var isSignedIn: Bool {
return GIDSignIn.sharedInstance.currentUser?.authentication != nil
}
var userName: String {
if isSignedIn, let profile = user?.profile {
return profile.name
}
else {
return "< User name >"
}
}
var email: String {
if isSignedIn, let profile = user?.profile {
return profile.email
}
else {
return "< Email address >"
}
}
func signIn(user: GIDGoogleUser?) {
self.user = user
guard let user = user, let imageURL = user.profile?.imageURL(withDimension: 50) else {
self.avatorImage = grayAvatorImage
return
}
DispatchQueue.global().async {
let data = try? Data(contentsOf: imageURL)
DispatchQueue.main.async {
guard let data = data, let imageData = UIImage(data: data) else {
self.avatorImage = grayAvatorImage
return
}
self.avatorImage = imageData
}
}
}
}
既にサインインしている場合の処理
GIDSignIn.sharedInstance.restorePreviousSignIn(callback:)
でアプリ起動時に以前のサインインの状態を復元できるので、これをAppDelegate
のapplication(application:launchOptions:)
で行なっている。
class AppDelegate: UIResponder, UIApplicationDelegate {
var userData = UserData()
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
// Override point for customization after application launch.
GIDSignIn.sharedInstance.restorePreviousSignIn(callback: { user, error in
if error != nil || user == nil {
// Show the app's signed-out state.
print("Not sign-in")
}
else {
// Show the app's signed-in state.
if let user=user, let userID = user.userID {
self.userData.signIn(user: user)
print("Already signed-in as :\(userID)")
}
}
})
return true
}
// その他省略
}
Googleの使い方のページのコードそのままなのだがSwiftUIならSceneDelegate
でやっても良かったのかもしれない。
サンプルコード
clientID
を編集してからでないとSign inボタンを押したら落ちます。
fileprivate let clientID = "<clientID>" // https://console.cloud.google.com/ API -> Credential
最後に
Google Sign-Inが6.0.0でSwift Package Managerに対応したので早速テストサンプルを書いてみたのだけど、SwiftUIが全然書けない…。リリースされた当時に少しチュートリアルをやってみたりしてそこそこ分かった気になっていたのだけど、今回こんな簡単なサンプルを書くのでも思い切り忘れていて驚いた。
多分もう一度チュートリアルからやり直した方が良いレベルなので、コードのおかしなところとかあったら教えてください!
Discussion