Swift 開發教學:如何使用 Parse 建立註冊和登入的功能
本文是向 Rumiya Murtazina 邀稿的文章。不久前,我們曾經在文章中探討過如何在 iOS App 中整合 Parse ,並示範了如何使用其服務來建構類似於 Instagram 的 App。你不僅能夠利用 Parse 來儲存資料,其服務也提供了 PFUser 類別,可以讓你管理 App 的使用者資訊。在本文中, Rumiya 將會展示如何建構簡單的登入 App ,並帶領你走訪在 Parse 雲端中儲存使用者帳號密碼的流程。
現在就來一睹 Rumiya 的文章吧。
Parse 是與平台無關的第三方「雲端 App 解決方案」。你可以從這裡得知更多訊息。本文屬於進階的程式設計教學,將會介紹如何在 Swift 專案中運用 Parse ,並且將使用者登入資訊存放到 Parse 雲端儲存中。
開始使用
我已經預備好包含 Storyboard 和視圖控制器類別的專案範本了。此專案已經把登入( Login )、註冊( Sign Up )、重設密碼( Reset Password )和主畫面( Home Screen )等頁面都設計好了。
執行此專案應該會在主畫面上看到一個簡單的使用者設定檔。在開始之前,不妨花幾分鐘的時間先讓你自己熟悉一下這份專案範本。
加入 Parse 框架
首先,登入你的 Parse 帳號或是免費註冊新的帳號。來到 Dashboard 頁面,點擊「 Create a new App 」(建立新的 App )。將新的 App 命名為「 ParseDemo 」並且按下「 Create 」按鈕。
接著,選取此 App 方塊底部的「 Quickstart Guide 」(快速入門指南)。在後續的畫面中,依序點選: Data > Mobile > iOS > Swift — Existing Project 。
現在應該已經準備好可以依循「 Install the SDK 」(安裝 SDK )的步驟了。將 Parse 框架連同列出的程式庫新增到你的 App 的 Frameworks 群組中。
我們需要將 Parse 初始化。作法是匯入 Parse 和 Bolts 框架,並且以 QuickStart 所產生的程式碼(如下所示)來更新 AppDelegate.swift 檔案:
1 2 3 4 5 6 7 8 9 10 11 12 |
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 :
1 2 3 4 5 6 7 8 9 |
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 」(以強制回應方式呈現)。
在 SignUpViewController.swift 中,針對電子郵件、使用者名稱和密碼等文字欄位宣告下列的 Outlet 變數:
1 2 3 |
@IBOutlet weak var emailField: UITextField! @IBOutlet weak var usernameField: UITextField! @IBOutlet weak var passwordField: UITextField! |
接著,加入下列的程式碼,以便建立動作函式:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 |
@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 變數連結到對應的文字欄位。
接著,選取 Sign Up 按鈕。在 Connection Inspector 中,將此按鈕連結到 Touch Up Inside 事件。當提示視窗彈出時,選取「 signUpAction: 」項目。
為了能夠關閉註冊畫面,我們需要定義用來解除的 Segue 。來到 LoginViewController.swift ,加入下列的解除動作:
1 2 |
@IBAction func unwindToLogInScreen(segue:UIStoryboardSegue) { } |
在 Storyboard 中,選取 Sign Up View Controller 場景,按住 Control 鍵並且拖曳關閉鈕至 Exit 圖示。當提示視窗彈出時,選取 Action Segue 區段中的「 unwindToHome: 」項目。
編譯並執行。現在應該能夠註冊使用者了。
來到你的 Parse 帳號,選取 ParseDemo 的 Core 。選取 User 區段以便檢視首位註冊成功的使用者:
登入與登出
接著讓我們來實作登入和登出的部分。在 LoginViewController.swift 中,針對使用者名稱與密碼等文字欄位宣告下列的 Outlet 變數:
1 2 |
@IBOutlet weak var usernameField: UITextField! @IBOutlet weak var passwordField: UITextField! |
然後加入下列的動作函式:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 |
@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 動作函式:
1 2 3 4 5 6 7 8 9 10 11 |
@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 變數了,示範如下:
1 |
@IBOutlet weak var userNameLabel: UILabel! |
以下列的程式碼來更新 viewDidLoad
函式:
1 2 3 4 5 6 7 8 |
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 標籤相連結。
編譯並執行。主畫面上將會顯示目前的使用者名稱。
重設密碼
在 ResetPasswordViewController.swift 中,針對 Email 文字欄位宣告如下的 Outlet 變數:
1 |
@IBOutlet weak var emailField: UITextField! |
接著,加入下列的動作函式:
1 2 3 4 5 6 7 8 9 10 11 |
@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 變數連結至文字欄位,並且將動作函式連結至按鈕。同時也別忘了賦予關閉按鈕解除的功能。
這樣就完成了!供你參考,請從這裡下載本文的完整 Xcode 專案。別忘了更新 AppDelegate.swift 裡面的 Parse 金鑰。
還沒有完,需要做的事情其實還有很多。舉例而言,可以嘗試找尋驗證電子郵件文字欄位正確性的方法。使用者的設定檔還沒有放照片。可以考慮實作上傳和儲存設定檔大頭照的功能。也可以加入以 Facebook 登入的選項。
如你所見, Parse 幫我們完成了許多繁雜的工作,讓你可以專注於製作漂亮又實用的 App 。試著用用看 Parse 吧,祝福你寫程式愉快!