Apollo iOS チュートリアル (5)
今回は、サーバにログインする機能を追加し、クライアントが特定のリクエストを行うために使用できるトークンを取得します。
【注意】
特定サーバへのログイン方法は、独自サーバでのログイン方法とは異なる場合があります。ログインは多くの場合、ミドルウェア、またはGraphQLから完全に分離したレイヤーによって処理されます。
1. ミューテーション
ミューテーション は、サーバの状態を変更する操作です。クライアントの特定のユーザーに関連付けられたセッションを作成することにより、バックエンドの状態を変更します。
2. Graphiでのミューテーションの確認
GraphiQL を開き、左側のパネルに既にあるものを全て削除します。[DOCS]タブを開き、MUTATIONS の login を選択します。
このミューテーションは、ログインしている人のメールアドレスを引数として渡します。フィールドを選択するGraphQL操作とは異なり、単一文字列のみを返します。
(1) 左側のパネルで、次のミューテーション定義を追加。
・(GraphiQL)
mutation Login($email: String) {
login(email: $email)
}
「$email」の型が「String!」ではなく「String」であることに注意してください。これは、nullを渡すことができることを意味します。
(2) 下部の「QUERY VARIABLES」で、以下を追加。
・(GraphiQL)
{ "email": null }
(3) ミューテーションを実行。
nullが返されることがわかります。
(4) 「QUERY VARIABLES」のnullをメールアドレスに置き換える。
・(GraphiQL)
{ "email": "me@example.com" }
(3) ミューテーションを実行。
ログインに対してレスポンスが返されることがわかります。
3. Xcodeでのミューテーションの準備 - Login.graphql
(1) Xcodeの LaunchList.graphql と同じフォルダに Login.graphql という名前のファイルを作成し、先程のミューテーションを貼り付ける。
(2) プロジェクトをビルドして、ミューテーションが「API.swift」に追加することを確認。
4. keychain-swiftの追加
ログイン認証情報をキーチェーンに保存するパッケージ keychain-swift をインストールします。
(1) Xcode のメニュー「File → Swift Packages → Add Package Dependencies 」を選択。
(2) https://github.com/evgenyneu/keychain-swift.git を指定し、Nextボタンを押す。
(3) KeychainSwift を選択し、Nextボタンを押す。
5. ログインビューの作成
(1) Xcodeのメニュー「File → New → File → Swift File」で、新規Swiftファイルを作成し、LoginViewController.swift という名前を指定。
(2) LoginViewController.swift を以下のように編集。
・LoginViewController.swift
import UIKit
import KeychainSwift
class LoginViewController: UIViewController {
// ログインキーチェーンのキー
static let loginKeychainKey = "login"
// UI
@IBOutlet private var emailTextField: UITextField!
@IBOutlet private var errorLabel: UILabel!
@IBOutlet private var submitButton: UIButton!
// ビューロード時に呼ばれる
override func viewDidLoad() {
super.viewDidLoad()
self.errorLabel.text = nil
self.enableSubmitButton(true)
}
// サブミットボタンの有効化
private func enableSubmitButton(_ isEnabled: Bool) {
self.submitButton.isEnabled = isEnabled
if isEnabled {
self.submitButton.setTitle("Submit", for: .normal)
} else {
self.submitButton.setTitle("Submitting...", for: .normal)
}
}
// メールアドレス文字列の検証
private func validate(email: String) -> Bool {
return email.contains("@")
}
// サブミットボタンタップ時に呼ばれる
@IBAction private func submitTapped() {
// サブミットボタンの無効化
self.errorLabel.text = nil
self.enableSubmitButton(false)
// emailの取得
guard let email = self.emailTextField.text else {
self.errorLabel.text = "Please enter an email address."
self.enableSubmitButton(true)
return
}
// emailの検証
guard self.validate(email: email) else {
self.errorLabel.text = "Please enter a valid email."
self.enableSubmitButton(true)
return
}
// アクセストークンの取得
Network.shared.apollo.perform(mutation: LoginMutation(email: email)) {
[weak self] result in
// 後処理
defer {
self?.enableSubmitButton(true)
}
switch result {
// 成功時
case .success(let graphQLResult):
// アクセストークンの取得
if let token = graphQLResult.data?.login {
// キーチェーンに保存
let keychain = KeychainSwift()
keychain.set(token, forKey: LoginViewController.loginKeychainKey)
// クローズ
self?.dismiss(animated: true)
}
// エラー表示
if let errors = graphQLResult.errors {
print("Errors from server: \(errors)")
}
// 失敗時
case .failure(let error):
print("Error: \(error)")
}
}
}
// キャンセルボタンタップ時に呼ばれる
@IBAction private func cancelTapped() {
self.dismiss(animated: true)
}
}
6. ストーリーボードの編集
◎ LoginViewControllerの追加
(1) ストーリーボードの「DetailViewController」の横に、「UIViewController」を追加。
(2) 追加した「UIViewController」を選択し、「Custom Class」で「LoginViewController」を指定。
(3) Xcodeのメニュー「Editor(Editではなく) → Embed In → Navigation Controller」を選択。
「LoginViewController」のナビゲーションコントローラが表示されます。
(4) 「DetailViewController」の「Detail」から、Ctrl押しながらドラッグして、「LoginViewController」のナビゲーションコントローラにドロップし、セグエ「Present Modally」を指定。
(5) 追加したセグエを選択して、以下のように設定。
◎ StackViewの追加
(1) 「Login View Controller Scene → Login View Controller → View」に「(Vertical) StackView」を追加。
(2) 制約を、次のように指定。
◎ Labelの追加
(1) 「StackView」に「Label」を追加し、以下の属性を指定。
◎ TextFieldの追加
(1) 「StackView」に「TextField」を追加し、以下の属性を指定。
(2) 「TextField」を「emailTextField」アウトレットに接続。
◎ 2番目のLabelの追加
(1) 「StackView」に2番目の「Label」を追加し、以下の属性を指定。
(2) 2番目の「Label」を「errorLabel」アウトレットに接続。
◎ Buttonの追加
(1) 「StackView」に「Button」を追加し、以下の属性を指定。
(2) 「Button」を「submitButton」アウトレットに接続。
(3) 「Button」の「touchUpInside」を「submitTapped」にフック。
◎ BarButtonItemの追加
(1) 「BarButtonItem」をナビゲーションバーの左上にドラッグし、以下の属性を指定。
(2) 「BarButtonItem」を「cancelTapped」アクションにフック。
◎ 完成
7. 詳細ビューの編集
右上のボタン押下時の処理を追加します。未ログイン時はログインビューを表示します。
(1) DetailViewController.swift の先頭にインポートを追加。
・DetailViewController.swift
import KeychainSwift
(2) ボタン押下時の処理を追加。
・DetailViewController.swift
//ボタンタップ時に呼ばれる
@IBAction private func bookOrCancelTapped() {
// 未ログイン時は、ログイン画面の表示
guard self.isLoggedIn() else {
self.performSegue(withIdentifier: "showLogin", sender: self)
return
}
// ランチ情報の取得
guard let launch = self.launch else {
return
}
if launch.isBooked {
print("Cancel trip!")
} else {
print("Book trip!")
}
}
// ログインしているかどうか
private func isLoggedIn() -> Bool {
let keychain = KeychainSwift()
return keychain.get(LoginViewController.loginKeychainKey) != nil
}
(3) ストーリーボードで、BookCancelButton のアクションをbookOrCancelTapped にフック。
3. 実行
ビルドしてアプリを実行すると、次のようにログイン画面が表示されます。ログイン情報はキーチェーンに保存されるため、2回目以降は表示されません。
この記事が気に入ったらサポートをしてみませんか?