LoginSignup
11
14

More than 3 years have passed since last update.

「iCloud CloudKit」と「Sign in with Apple」を使用してユーザー情報を保存します

Last updated at Posted at 2020-05-12

ユーザー情報を保存するために第三者のデータベースを使用していますか?iOSプラットフォーム向けだけに開発しているなら、"CloudKit" を "Sign in with Apple" と一緒に使用することを検討してはいかがでしょう。

短時間で77行のコードを使って、ユーザーログイン/登録の機能をiOSアプリに追加できます。

何ができるでしょうか?

  • ユーザーは、“Sign in with Apple” を使ってあなたのアプリに登録できます。
  • 登録後、ユーザーはどのデバイス(同じApple IDでサインインされている物)からでもサインインできて、あなたは氏名、Eメール、その他保存したユーザー情報を取得することが可能です。

:sunny: 早速始めてみましょう :sunny:

ステップ1. Xcodeプロジェクト設定で必要な機能をオンにする。“iCloud”の"CloudKit”をオンにする。

image.png

「CloudKit」をオンにします

image.png

既存の"Containers"のトグルボタンをどれかひとつクリックするか、プラスアイコンをクリックして新規"Container"を加える。

新規"Container"を加える:

追加ボタンをクリックします

68747470733a2f2f71696974612d696d6167652d73746f72652e73332e61702d6e6f727468656173742d312e616d617a6f6e6177732e636f6d2f302f3633353333302f39613239373166332d623134392d343962622d656466332d3661646132313237623962392e706e67.png

新しい「Container」の名前を入力してください

Screen Shot 2020-05-12 at 1.06.19 PM.png

ステップ2. ユーザー情報を保存するため、CloudKitのデータ構造を設定する。

  • 次に"CloudKit Dashboard"ボタンをクリックするか、http://icloud.developer.apple.com へ行く。
  • パネル左側にある自分のアプリケーション名をクリックする。

  • "Schema" をクリックします

image.png

"New Type" をクリックします

image.png

タイプに「userInfo」という名前を付けて選択します

右側の[Add field button]ボタンをクリックして、次の新しいフィールドを追加します

image.png

「emailAddress」と「name」はどちらも「String」です

Screen Shot 2020-05-12 at 12.54.59 PM.png

ステップ 3. “Sign in with Apple” 機能を追加する (Appleでサインイン)

image.png

“Capabilities” タブは次のようになります:

image.png

:sparkles: コードはこのページの下部にあります

ステップ 4. 新しいloginViewControllerを作成します

import Foundation
import UIKit
class loginViewController: UIViewController {
    override func viewDidLoad() {
        super.viewDidLoad()
    }
}

必要なフレームワークをインポートする

import AuthenticationServices
import CloudKit

“Sign in with Apple button”(Appleでサインインのボタン)に、プログラム上の参照を追加しましょう。

@IBOutlet weak var signInBtnView: UIView!
var authorizationButton: ASAuthorizationAppleIDButton!

ここでは、"signInBtnView" という既存ビューにサインインボタンを加えます。ここでは "UIStoryBoard" を使ってAを追加しています。

このボタンをあなたのプログラムのビューに加えましょう("UIView")。

override func viewDidLoad() {
    super.viewDidLoad()
     authorizationButton = ASAuthorizationAppleIDButton(type: .default, style: .whiteOutline)
    authorizationButton.frame = CGRect(origin: .zero, size: signInBtnView.frame.size)
    authorizationButton.addTarget(self, action: #selector(handleAppleIdRequest), for: .touchUpInside)
    signInBtnView.addSubview(authorizationButton)
}

ボタンがクリックされた際に実行するアクションを処理する

@objc func handleAppleIdRequest(){
    let appleIDProvider = ASAuthorizationAppleIDProvider()
    let request = appleIDProvider.createRequest()
    request.requestedScopes = [.fullName, .email]
    let authorizationController =     ASAuthorizationController(authorizationRequests: [request])
    authorizationController.delegate = self
    authorizationController.performRequests()
}

結果やエラーメッセージを受け取るには "delegate"(デリゲート)を設定しましょう。

extension loginViewController: ASAuthorizationControllerDelegate {
func authorizationController(controller: ASAuthorizationController, didCompleteWithAuthorization authorization: ASAuthorization) {
    //成功
}
func authorizationController(controller: ASAuthorizationController, didCompleteWithError error: Error) {
    //失敗した
    print(error.localizedDescription)
}

これで「Sign in with Apple」の運用が開始されたはずです。手元のシミュレータで試して下さい!

ただし、まだ以下の手続きが必要です:
1. サインインでユーザー情報を取得する。
2. 「iCloud」への情報のアップロードと
3. 「iCloud」からの情報の取得。

サインインからの情報取得

func authorizationController(controller: ASAuthorizationController, didCompleteWithAuthorization authorization: ASAuthorization) {
    if let appleIDCredential = authorization.credential as?    ASAuthorizationAppleIDCredential {
        let userID = appleIDCredential.user
        let name = appleIDCredential.fullName?.givenName
        let emailAddr = appleIDCredential.email
    }
}

ここで重要な情報です:

  • ユーザーがあなたのアプリで初めて"A"を使った場合、あなたはそのユーザーID、氏名、Eメールアドレスを受け取ります。その情報を保存する"B"については、この記事の後の方で扱います。

  • ユーザーが戻ってきた場合(登録ではなくサインインしている場合)、あなたは"userID"だけを受け取ります。"A"についてはこの記事の後の方で扱い、userIDを使って氏名とEメールを取得する方法を学びます。

  • ひとりのユーザーの"userID"は、常に同じです。

ユーザーが登録(新規ユーザー)するのか、サインインするのか、確認しましょう。

func authorizationController(controller: ASAuthorizationController, didCompleteWithAuthorization authorization: ASAuthorization) {
    if let appleIDCredential = authorization.credential as?  ASAuthorizationAppleIDCredential {
        let userID = appleIDCredential.user
        if let name = appleIDCredential.fullName?.givenName,
        let emailAddr = appleIDCredential.email {
            //新規ユーザー(登録)
            //この情報を「CloudKit」に保存
        } else {
            //既存ユーザー(サインイン)
            //ユーザー名/メールアドレスの取得
        }
    }
}

ステップ 5. CloudKit (iCloud)

データベースには3種類あることに注意してください。本記事では「privateDatabase(プライベートデータベース)」を使っているので、ユーザーのデータにアクセスできるのはユーザー本人のみとなります。

//TODO: アプリiCloudコンテナーの名前 (ステップ 1)
let privateDatabase = CKContainer(identifier: "iCloud.com.[アプリiCloudコンテナーの名前]").privateCloudDatabase

氏名とEメールを受け取った場合(ユーザーが登録しているということ)、新しい"CloudKit"記録を作成できます:

「recordID」は「Appleでサインイン」から取得した「userID」と同じになるように割り当てているため、後でユーザーのCloudKitレコードを取得できます。

//新規ユーザー(登録)
//この情報を「CloudKit」に保存
let record = CKRecord(recordType: "UserInfo", recordID: CKRecord.ID(recordName: userID))
record["name"] = name
record["emailAddress"] = emailAddr
//保存する他の情報を追加できます
privateDatabase.save(record) { (_, _) in
UserDefaults.standard.set(record.recordID.recordName, forKey: "userProfileID")

リピートユーザーの場合、"CloudKit" から名前とメールアドレスを取得します:

//既存ユーザー(サインイン)
//ユーザー名/メールアドレスの取得
privateDatabase.fetch(withRecordID: CKRecord.ID(recordName: userID)) { (record, error) in
    if let fetchedInfo = record {
       let name = fetchedInfo["name"] as? String
        let userEmailAddr = fetchedInfo["emailAddress"] as? String
        //読むべきその他の情報: fetchedInfo[その他の情報] as? String
        //TODO: ユーザー名とメールアドレスを使用できるようになりました(例:ローカルに保存)
        print("名前: \(name) 電子メールアドレス: \(userEmailAddr)")
        UserDefaults.standard.set(name, forKey: "userName")
}

完成したコードはこのリンクの先にあります: https://github.com/mszopensource/CloudKitAppleSignInExample/blob/master/loginViewController.swift

import Foundation
import UIKit
import AuthenticationServices
import CloudKit

class loginViewController: UIViewController {

    @IBOutlet var signInBtnView: UIView!
    var authorizationButton: ASAuthorizationAppleIDButton!

    override func viewDidLoad() {
        super.viewDidLoad()
         authorizationButton = ASAuthorizationAppleIDButton(type: .default, style: .whiteOutline)
        authorizationButton.frame = CGRect(origin: .zero, size: signInBtnView.frame.size)
        authorizationButton.addTarget(self, action: #selector(handleAppleIdRequest), for: .touchUpInside)
        signInBtnView.addSubview(authorizationButton)
    }

    @objc func handleAppleIdRequest(){
        let appleIDProvider = ASAuthorizationAppleIDProvider()
        let request = appleIDProvider.createRequest()
        request.requestedScopes = [.fullName, .email]
        let authorizationController = ASAuthorizationController(authorizationRequests: [request])
        authorizationController.delegate = self
        authorizationController.performRequests()
    }

}

extension loginViewController: ASAuthorizationControllerDelegate {

    func authorizationController(controller: ASAuthorizationController, didCompleteWithAuthorization authorization: ASAuthorization) {
        //TODO: アプリiCloudコンテナーの名前
        let privateDatabase = CKContainer(identifier: "iCloud.com.[アプリiCloudコンテナーの名前]").privateCloudDatabase
        if let appleIDCredential = authorization.credential as?  ASAuthorizationAppleIDCredential {
            let userID = appleIDCredential.user
            if let name = appleIDCredential.fullName?.givenName,
                let emailAddr = appleIDCredential.email {
                //新規ユーザー(登録)
                //この情報を「CloudKit」に保存
                let record = CKRecord(recordType: "UserInfo", recordID: CKRecord.ID(recordName: userID))
                record["name"] = name
                record["emailAddress"] = emailAddr
                privateDatabase.save(record) { (_, _) in
                    UserDefaults.standard.set(record.recordID.recordName, forKey: "userProfileID")
                }
            } else {
                //既存ユーザー(サインイン)
                //ユーザー名/メールアドレスの取得
                privateDatabase.fetch(withRecordID: CKRecord.ID(recordName: userID)) { (record, error) in
                    if let fetchedInfo = record {
                        let name = fetchedInfo["name"] as? String
                        let userEmailAddr = fetchedInfo["emailAddress"] as? String

                        //TODO: ユーザー名とメールアドレスを使用できるようになりました(例:ローカルに保存)
                        print("名前: \(name) 電子メールアドレス: \(userEmailAddr)")
                        UserDefaults.standard.set(userID, forKey: "userProfileID")
                    }
                }
            }
        }
    }

    func authorizationController(controller: ASAuthorizationController, didCompleteWithError error: Error) {
        print(error.localizedDescription)
    }

}

これでうまくいくはずです!

その他のタイプのユーザー情報の保存

データベースにはその他の情報も保存できます。例えば、ユーザーが好きな動物一覧を保存することができます。

まず、データベースの「スキーム」に「フィールド」を追加する必要があります。本記事の序盤ですでにその方法を解説しました。例として「likedAnimals(好きな動物)」というフィールドを新規追加したいことにしましょう。

Screen Shot 2020-05-12 at 12.52.43 PM.png

Screen Shot 2020-05-12 at 12.53.35 PM.png

ユーザーのIDをローカルストレージに保存している場合、このコードでユーザーが好きな動物を取得できます。

func getUserLikedAnimals() {
    let privateDatabase = CKContainer(identifier: "iCloud.com.[アプリiCloudコンテナーの名前]").privateCloudDatabase
    if let userCloudID = UserDefaults.standard.string(forKey: "userProfileID") {
        let recordID = CKRecord.ID(recordName: userCloudID)
        privateDatabase.fetch(withRecordID: recordID) { (fetchedRecord, error) in
            if error == nil {
                let likedAnimals = fetchedRecord?.value(forKey: "likedAnimals") as? [String]
                //TODO
            } else {
                print(error?.localizedDescription)
            }
        }
    }
}

データベース上のユーザーの記録に動物名を新規追加する場合は、このコードを使用します。

func updateUserLikedAnimals(newAnimal: String) {
    let privateDatabase = CKContainer(identifier: "iCloud.com.[アプリiCloudコンテナーの名前]").privateCloudDatabase
    if let userCloudID = UserDefaults.standard.string(forKey: "userProfileID") {
        let recordID = CKRecord.ID(recordName: userCloudID)
        privateDatabase.fetch(withRecordID: recordID) { (record, error) in
            guard let fetchedRecord = record else { return }
            if error == nil {
                var likedAnimals = fetchedRecord.value(forKey: "likedAnimals") as? [String] ?? []
                likedAnimals.append(newAnimal)
                //更新記録
                privateDatabase.save(fetchedRecord) { (modifiedRecord, error) in
                    if error != nil {
                        //失敗
                        print(error?.localizedDescription)
                    } else {
                        //成功
                    }
                }
            } else {
                print(error?.localizedDescription)
            }
        }
    }
}

最後にはiCloudの開発用データベースを本番環境にデプロイする必要があります。

Screen Shot 2020-05-11 at 4.32.00 PM.png


:relaxed: Twitter @MszPro

:sunny: 私の公開されているQiita記事のリストをカテゴリー別にご覧いただけます。

11
14
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
11
14