Swift 開發教學:如何使用 Parse 建立註冊和登入的功能

本文是向 Rumiya Murtazina 邀稿的文章。不久前,我們曾經在文章中探討過如何在 iOS App 中整合 Parse ,並示範了如何使用其服務來建構類似於 Instagram 的 App。你不僅能夠利用 Parse 來儲存資料,其服務也提供了 PFUser 類別,可以讓你管理 App 的使用者資訊。在本文中, Rumiya 將會展示如何建構簡單的登入 App ,並帶領你走訪在 Parse 雲端中儲存使用者帳號密碼的流程。


現在就來一睹 Rumiya 的文章吧。

Parse 是與平台無關的第三方「雲端 App 解決方案」。你可以從這裡得知更多訊息。本文屬於進階的程式設計教學,將會介紹如何在 Swift 專案中運用 Parse ,並且將使用者登入資訊存放到 Parse 雲端儲存中。

login-parse-demo

開始使用

我已經預備好包含 Storyboard 和視圖控制器類別的專案範本了。此專案已經把登入( Login )、註冊( Sign Up )、重設密碼( Reset Password )和主畫面( Home Screen )等頁面都設計好了。

login-parse-storyboard

執行此專案應該會在主畫面上看到一個簡單的使用者設定檔。在開始之前,不妨花幾分鐘的時間先讓你自己熟悉一下這份專案範本。

加入 Parse 框架

首先,登入你的 Parse 帳號或是免費註冊新的帳號。來到 Dashboard 頁面,點擊「 Create a new App 」(建立新的 App )。將新的 App 命名為「 ParseDemo 」並且按下「 Create 」按鈕。

接著,選取此 App 方塊底部的「 Quickstart Guide 」(快速入門指南)。在後續的畫面中,依序點選: Data > Mobile > iOS > Swift — Existing Project 。

login-parse-create

現在應該已經準備好可以依循「 Install the SDK 」(安裝 SDK )的步驟了。將 Parse 框架連同列出的程式庫新增到你的 App 的 Frameworks 群組中。

login-parse-library

我們需要將 Parse 初始化。作法是匯入 Parse 和 Bolts 框架,並且以 QuickStart 所產生的程式碼(如下所示)來更新 AppDelegate.swift 檔案:

func application(application: UIApplication, didFinishLaunchingWithOptions launchOptions: [NSObject: AnyObject]?) -> Bool {
    
    Parse.enableLocalDatastore()
    
    // 初始化 Parse
    Parse.setApplicationId("OzyfQlVF5pHLA0OmRHB0RKsfXpdWwGWJ1mYgDTI6", clientKey: "f7Jw2X4XzSW0whm2aDtYl7wdIYn5hZVvHuPJ72zu")
    
    // [選用] 追蹤統計 App 的開啟次數
    PFAnalytics.trackAppOpenedWithLaunchOptions(launchOptions)
    
    return true
}

別忘了置換成你自己的 App ID 和用戶端金鑰。你可以在自己的 Parse 儀表板的 Settings 分頁中找到這些金鑰。

確定在下列這些類別的開頭都匯入了 Parse :

  • LoginViewController.swift
  • HomeViewController.swift
  • SignUpViewController.swift
  • ResetPasswordViewController.swift

現在,編譯並執行。你的 iOS App 在執行時應該不會在 Xcode 中看到錯誤。

顯示登入畫面

在進入使用者設定檔的主畫面之前, App 的使用者必須先登入或註冊。假使目前的訪客並未登入的話,我們需要帶出登入畫面。

在 HomeViewController.swift 檔案中加入下列的函式,以便透過 Storyboard 識別碼「 Login 」來實體化 Login View Controller :

override func viewWillAppear(animated: Bool) {
    if (PFUser.currentUser() == nil) {
        dispatch_async(dispatch_get_main_queue(), { () -> Void in

            let viewController:UIViewController = UIStoryboard(name: "Main", bundle: nil).instantiateViewControllerWithIdentifier("Login") as! UIViewController
            self.presentViewController(viewController, animated: true, completion: nil)
        })
    }
}

編譯並執行此專案。 App 啟動後應該會帶出登入畫面。目前尚未有任何註冊的使用者。我們需要能夠導覽至註冊畫面,好讓使用者能夠註冊。

註冊

要在 Storyboard 上顯示註冊畫面,必須在登入視圖控制器中點擊 Sign Up 按鈕。按住 Control 鍵將選定的按鈕拖曳到 Sign Up View Controller 。當提示視窗彈出時,選取「 present modally 」(以強制回應方式呈現)。

login-parse-control-drag

在 SignUpViewController.swift 中,針對電子郵件、使用者名稱和密碼等文字欄位宣告下列的 Outlet 變數:

@IBOutlet weak var emailField: UITextField!
@IBOutlet weak var usernameField: UITextField!
@IBOutlet weak var passwordField: UITextField!

接著,加入下列的程式碼,以便建立動作函式:

@IBAction func signUpAction(sender: AnyObject) {
    
    let username = self.usernameField.text
    let password = self.passwordField.text
    let email = self.emailField.text
    let finalEmail = email!.stringByTrimmingCharactersInSet(NSCharacterSet.whitespaceCharacterSet())
    
    // 驗證文字欄位
    if let username = username where username.characters.count < 5 {
        let alert = UIAlertView(title: "Invalid", message: "Username must be greater than 5 characters", delegate: self, cancelButtonTitle: "OK")
        alert.show()
        
    } else if let password = password where password.characters.count < 8 {
        let alert = UIAlertView(title: "Invalid", message: "Password must be greater than 8 characters", delegate: self, cancelButtonTitle: "OK")
        alert.show()
        
    } else if let email = email where email.characters.count < 8 {
        let alert = UIAlertView(title: "Invalid", message: "Please enter a valid email address", delegate: self, cancelButtonTitle: "OK")
        alert.show()
        
    } else {
        // 顯示旋轉圖示,代表工作正在進行中
        let spinner: UIActivityIndicatorView = UIActivityIndicatorView(frame: CGRectMake(0, 0, 150, 150)) as UIActivityIndicatorView
        spinner.startAnimating()
        
        let newUser = PFUser()
        
        newUser.username = username
        newUser.password = password
        newUser.email = finalEmail
        
        // 以非同步方式註冊使用者
        newUser.signUpInBackgroundWithBlock({ (succeed, error) -> Void in
            
            // 停止旋轉圖示
            spinner.stopAnimating()
            if ((error) != nil) {
                let alert = UIAlertView(title: "Error", message: "\(error)", delegate: self, cancelButtonTitle: "OK")
                alert.show()
                
            } else {
                let alert = UIAlertView(title: "Success", message: "Signed Up", delegate: self, cancelButtonTitle: "OK")
                alert.show()
                dispatch_async(dispatch_get_main_queue(), { () -> Void in
                    let viewController:UIViewController = UIStoryboard(name: "Main", bundle: nil).instantiateViewControllerWithIdentifier("Home") 
                    self.presentViewController(viewController, animated: true, completion: nil)
                })
            }
        })
    }
}

此動作函式會在使用者點擊 Sign Up 按鈕時被觸發。首先會針對文字欄位的數值進行簡單的驗證。如果驗證成功的話,便會呼叫 Parse 的 signUpInBackgroundWithBlock 函式。此 signUpInBackgroundWithBlock 函式的執行需要花費一些時間,因為它是採用非同步的方式。旋轉圖示指出工作正在進行中。當工作做完時,會有訊息通知使用者關於註冊的成敗。如果註冊成功的話,使用者將會登入並導覽至主畫面。

現在,來到 Storyboard 並且選取 Sign Up View Controller 場景。在 Connection Inspector 中,將每個 Outlet 變數連結到對應的文字欄位。

login-parse-outlets

接著,選取 Sign Up 按鈕。在 Connection Inspector 中,將此按鈕連結到 Touch Up Inside 事件。當提示視窗彈出時,選取「 signUpAction: 」項目。

login-parse-signup-action

為了能夠關閉註冊畫面,我們需要定義用來解除的 Segue 。來到 LoginViewController.swift ,加入下列的解除動作:

@IBAction func unwindToLogInScreen(segue:UIStoryboardSegue) {
}

在 Storyboard 中,選取 Sign Up View Controller 場景,按住 Control 鍵並且拖曳關閉鈕至 Exit 圖示。當提示視窗彈出時,選取 Action Segue 區段中的「 unwindToHome: 」項目。

login-parse-close-outlet

編譯並執行。現在應該能夠註冊使用者了。

來到你的 Parse 帳號,選取 ParseDemo 的 Core 。選取 User 區段以便檢視首位註冊成功的使用者:

login-parse-core-test

登入與登出

接著讓我們來實作登入和登出的部分。在 LoginViewController.swift 中,針對使用者名稱與密碼等文字欄位宣告下列的 Outlet 變數:

@IBOutlet weak var usernameField: UITextField!
@IBOutlet weak var passwordField: UITextField!

然後加入下列的動作函式:

@IBAction func loginAction(sender: AnyObject) {
    let username = self.usernameField.text
    let password = self.passwordField.text
    
    // 驗證文字欄位
    if let username = username where username.characters.count < 5 {
        let alert = UIAlertView(title: "Invalid", message: "Username must be greater than 5 characters", delegate: self, cancelButtonTitle: "OK")
        alert.show()
        
    } else if let password = password where password.characters.count < 8 {
        let alert = UIAlertView(title: "Invalid", message: "Password must be greater than 8 characters", delegate: self, cancelButtonTitle: "OK")
        alert.show()
        
    } else {
        // 顯示旋轉圖示,代表工作正在進行中
        let spinner: UIActivityIndicatorView = UIActivityIndicatorView(frame: CGRectMake(0, 0, 150, 150)) as UIActivityIndicatorView
        spinner.startAnimating()
        
        // 送出登入的要求
        PFUser.logInWithUsernameInBackground(username!, password: password!, block: { (user, error) -> Void in
            
            // 停止旋轉圖示
            spinner.stopAnimating()
            
            if ((user) != nil) {
                let alert = UIAlertView(title: "Success", message: "Logged In", delegate: self, cancelButtonTitle: "OK")
                alert.show()
                
                dispatch_async(dispatch_get_main_queue(), { () -> Void in
                    let viewController:UIViewController = UIStoryboard(name: "Main", bundle: nil).instantiateViewControllerWithIdentifier("Home") 
                    self.presentViewController(viewController, animated: true, completion: nil)
                })
                
            } else {
                let alert = UIAlertView(title: "Error", message: "\(error)", delegate: self, cancelButtonTitle: "OK")
                alert.show()
            }
        })
    }
}

上述的程式碼會在使用者點擊 Login 按鈕時被觸發。跟我們在 SignUpViewController.swift 中所實作的動作函式很類似。差別在於這回呼叫的是 logInWithUsernameInBackground (而不是 signUpInBackgroundWithBlock )函式,以便將輸入的使用者名稱與密碼傳送至 Parse 。如果登入成功的話,將會出現 Storyboard 識別碼為「 Home 」的 Home View Controller 。

現在,在 Storyboard 中,將 Login View Controller 的 Outlet 變數與對應的文字欄位相連結。選取 Login 按鈕。在 Connection Inspector 中,將此按鈕連結到 Touch Up Inside 事件。當提示視窗彈出時,選取「 loginAction: 」項目。

為了要讓使用者也能夠登出,我們必須在 HomeViewController.swift 中實作 logOutAction 動作函式:

@IBAction func logOutAction(sender: AnyObject){
    
    // 傳送使用者登出的要求
    PFUser.logOut()
    
    dispatch_async(dispatch_get_main_queue(), { () -> Void in
        let viewController:UIViewController = UIStoryboard(name: "Main", bundle: nil).instantiateViewControllerWithIdentifier("Login") 
        self.presentViewController(viewController, animated: true, completion: nil)
    })
    
}

上述的程式碼呼叫了 Parse 的 logOut 函式,並將 Login 場景實體化。

在 Storyboard 中,選取 Logout 按鈕。在 Connection Inspector 中,將此按鈕連結到 Touch Up Inside 事件。當提示視窗彈出時,選取「 logOutAction: 」項目。

在進行此部分的測試之前,我們還有一件事情要做。下列的步驟會將「 username 」文字置換成實際的使用者名稱(花幾分鐘思考一下原因吧)。現在輪到在 HomeViewController.swift 檔案中為 Username 標籤宣告 Outlet 變數了,示範如下:

@IBOutlet weak var userNameLabel: UILabel!

以下列的程式碼來更新 viewDidLoad 函式:

override func viewDidLoad() {
    super.viewDidLoad()
    
    // 顯示目前訪客的使用者名稱
    if let pUserName = PFUser.currentUser()?["username"] as? String {
        self.userNameLabel.text = "@" + pUserName
    }
}

回到 Storyboard ,將 userNameLabel Outlet 變數與 Home View Controller 中對應的 User Name 標籤相連結。

編譯並執行。主畫面上將會顯示目前的使用者名稱。

Parse Login Demo

重設密碼

在 ResetPasswordViewController.swift 中,針對 Email 文字欄位宣告如下的 Outlet 變數:

@IBOutlet weak var emailField: UITextField!

接著,加入下列的動作函式:

@IBAction func passwordReset(sender: AnyObject) {
    let email = self.emailField.text
    let finalEmail = email!.stringByTrimmingCharactersInSet(NSCharacterSet.whitespaceCharacterSet())
    
    // 傳送重設密碼的要求
    PFUser.requestPasswordResetForEmailInBackground(finalEmail)
    
    let alert = UIAlertController (title: "Password Reset", message: "An email containing information on how to reset your password has been sent to " + finalEmail + ".", preferredStyle: UIAlertControllerStyle.Alert)
    alert.addAction(UIAlertAction(title: "Ok", style: UIAlertActionStyle.Default, handler: nil))
    self.presentViewController(alert, animated: true, completion: nil)
}

在此我們只是呼叫 requestPasswordResetForEmailInBackground 函式並帶入指定的電子郵件,藉以重設密碼。隨後會由 Parse 接手處理密碼重設作業。假使電子郵件是有效的,便會將如何重設密碼的指示寄送到該電子郵件的地址。

現在你對於如何在 Storyboard 中設定 ResetPasswordViewController 的流程應該已經很熟悉了。從登入畫面設定 Segue 、將 Outlet 變數連結至文字欄位,並且將動作函式連結至按鈕。同時也別忘了賦予關閉按鈕解除的功能。

login-parse-password-reset

這樣就完成了!供你參考,請從這裡下載本文的完整 Xcode 專案。別忘了更新 AppDelegate.swift 裡面的 Parse 金鑰。

還沒有完,需要做的事情其實還有很多。舉例而言,可以嘗試找尋驗證電子郵件文字欄位正確性的方法。使用者的設定檔還沒有放照片。可以考慮實作上傳和儲存設定檔大頭照的功能。也可以加入以 Facebook 登入的選項。

如你所見, Parse 幫我們完成了許多繁雜的工作,讓你可以專注於製作漂亮又實用的 App 。試著用用看 Parse 吧,祝福你寫程式愉快!

本文作者是 Rumiya 。她是獨立的 iOS 開發者。在她的職業生涯中曾經擔任過許多角色,從 IT 技術支援、到 Visual FoxPro 資料庫專家、再到網頁開發者。她最近在閱讀完 Appcoda 的書之後,愛上了 Swift 。 Rumiya 深信精進程式設計技術之道,就是學習新主題,並且撰寫成教學文章。她把自己的 App 開發探險歷程發表在 abearablecode.com 部落格。 Rumiya 的非程式活動包括繪畫、攝影,以及將品嚐美食的過程記錄在 elegantpantry.me 部落格。
譯者簡介:陳佳新 – 奇步應用共同創辦人,開發自有 App 和網站之外,也承包各式案件。譯有多本電腦書籍,包括 O’Reilly 出版的 iOS 、 Android 、 Agile 和 Google Cloud 等主題,也在報紙上寫過小說。現與妻兒居住在故鄉彰化。歡迎造訪 https://chibuapp.com ,來信請寄到 [email protected]

原文How to Create Sign Up and Login in iOS Apps with Parse


軟件工程師,AppCoda 創辦人。著有《iOS 13 App 程式設計實力超進化實戰攻略》、《iOS 13 App 程式設計實力超進化實戰攻略》以及《精通 SwiftUI》。曾任職於HSBC, FedEx等公司,專責軟體開發、系統設計。2012年創立AppCoda技術部落格,定期發表iOS程式教學文章。現時專注發展AppCoda,致力於iOS程式教學,產品設計及開發。

blog comments powered by Disqus
Shares
Share This