はじめに
最近Amplifyを利用し始めたのですが、Amplify CLIでちょっとコマンドを実行するだけで、アプリからAWSのリソースを利用できるようになり、すごく便利に感じています。
ここでは、Amplifyを使って、Cognito User Poolsログインする処理をSwiftUIで行う方法を説明します。
ドキュメントには、ログインやログアウトなど一つ一つの例はあるのですが、具体的にアプリにどのように組み込むかまでは記載されていません。そこで、このサンプルが具体的にアプリにどう組み込むか悩んでいる方に役に立つのではないかと思い、書きました。
前提
バージョンは次の通りです。
- Xcode 11.2.1
- Amplify iOS SDK 2.0
また、この記事では、サインインの処理を具体的にどう組み込むかのみを説明します。
CocoaPodsに依存関係を設定して、$ amplify add auth
して、...のような基本的な手順は割愛します。基本的な利用手順については、以下のドキュメントをざっと読んでいただければと思います。
https://aws-amplify.github.io/docs/sdk/ios/authentication
イメージ
アプリを起動して、ユーザーがログイン済で出ない場合、ログイン画面を開きます。
ログインしたら、ホーム画面に遷移します。
Podfile
target 'MyApp' do
use_frameworks!
pod 'AWSMobileClient', '~> 2.12.0'
target 'MyAppTests' do
inherit! :search_paths
end
target 'MyAppUITests' do
end
end
SceneDelegate
ポイントは以下の2つです。
-
AWSMobileClient.initialize(completionHandler:)
でAWSMoblieClientを初期化し、結果に応じてホーム画面またはログイン画面を開く -
AWSMobileClient.addStateListener(object:callback:)
でuserStateをobserveし、userStateの変化に応じてホーム画面またはログイン画面を開く
import UIKit
import SwiftUI
import AWSAppSync
import AWSMobileClient
class SceneDelegate: UIResponder, UIWindowSceneDelegate {
var window: UIWindow?
func scene(_ scene: UIScene, willConnectTo session: UISceneSession, options connectionOptions: UIScene.ConnectionOptions) {
if let windowScene = scene as? UIWindowScene {
let window = UIWindow(windowScene: windowScene)
AWSMobileClient.default().initialize { [weak self, window] (userState, error) in
guard let self = self else { return }
if let error = error {
print("### AWSMobileClient.initialize.error", error.localizedDescription)
return
}
if let userState = userState {
switch (userState) {
case .signedIn:
self.showHomeView(in: window) // ホームを開く
case .signedOut:
self.showSignInView(in: window) // ログインページを開く
default:
AWSMobileClient.default().signOut()
self.showSignInView(in: window) // ログインページを開く
}
}
}
AWSMobileClient.default().addUserStateListener(self) { [weak self, window] (userState, info) in
guard let self = self else { return }
switch (userState) {
case .signedIn:
self.showHomeView(in: window) // ホームを開く
case .signedOut, .signedOutUserPoolsTokenInvalid:
self.showSignInView(in: window) // ログインページを開く
default:
AWSMobileClient.default().signOut()
self.showSignInView(in: window) // ログインページを開く
}
}
}
}
private func showHomeView(in window: UIWindow) {
DispatchQueue.main.async {
window.rootViewController = UIHostingController(rootView: HomeView())
self.window = window
window.makeKeyAndVisible()
}
}
private func showSignInView(in window: UIWindow) {
DispatchQueue.main.async {
window.rootViewController = UIHostingController(rootView: SignInView(viewModel: SignInViewModel()))
self.window = window
window.makeKeyAndVisible()
}
}
}
SignInView
パスワードの初期化とサインアップは割愛させていただきます。
import SwiftUI
struct SignInView: View {
@ObservedObject var viewModel: SignInViewModel
var body: some View {
VStack(spacing: 20) {
Spacer()
Text("MyApp")
.bold()
.font(.title)
Spacer()
TextField("Username", text: $viewModel.userName)
.autocapitalization(.none)
.padding()
.overlay(
RoundedRectangle(cornerRadius: 6)
.stroke(Color.gray, lineWidth: 1)
)
SecureField("Password", text: $viewModel.password)
.padding()
.overlay(
RoundedRectangle(cornerRadius: 6)
.stroke(Color.gray, lineWidth: 1)
)
Button(action: {
print("### signinButton did tap")
self.viewModel.signIn()
}) {
HStack {
Spacer()
Text("Sign in")
Spacer()
}
.padding()
}
.accentColor(.white)
.background(Color.green)
.cornerRadius(6)
.alert(isPresented: $viewModel.showAlert) {
Alert(title: Text("Sign in error"), message: Text(viewModel.errorMessage))
}
HStack {
Spacer()
Button(action: {
fatalError("forget password hasn't be implemented.") // 省略
}) {
Text("Forget password?")
}
}
Spacer()
HStack {
Spacer()
Text("Don't have an account?")
Button(action: {
fatalError("sign up hasn't be implemented.") // 省略
}) {
Text("Sign up")
}
Spacer()
}
Spacer()
}
.padding(EdgeInsets(top: 15, leading: 15, bottom: 15, trailing: 15))
}
}
struct SignInView_Previews: PreviewProvider {
static var previews: some View {
SignInView(viewModel: SignInViewModel())
}
}
SignInViewModel
import Foundation
import AWSMobileClient
class SignInViewModel: ObservableObject {
@Published var userName: String = ""
@Published var password: String = ""
@Published var showAlert = false
@Published var errorMessage: String = ""
func signIn() {
AWSMobileClient.default().signIn(username: userName, password: password) { (result, error) in
if let error = error {
print("### signin error:", error.localizedDescription)
self.showAlert = true
self.errorMessage = error.localizedDescription
}
print("### Signin success")
}
}
}
HomeView
import SwiftUI
struct HomeView: View {
var body: some View {
Text("Home")
.font(.title)
}
}
struct HomeView_Previews: PreviewProvider {
static var previews: some View {
HomeView()
}
}
以上です。