作者:MATTHEW MAHER莫矗,原文鏈接咽弦,原文日期:2015-11-12
譯者:CoderAFI;校對:Cee胎挎;定稿:Channe
隨著移動互聯(lián)網(wǎng)快速發(fā)展沟启,想要自己做一款用戶喜歡的應用程序越來越困難。而且蘋果提供的開發(fā)工具和資源也越發(fā)不能滿足開發(fā)者在數(shù)據(jù)服務方面的需求犹菇。于是 BaaS 服務(Backend-as-a-Service)給開發(fā)者帶來了新的選擇德迹。
谷歌的 Firebase 是目前最流行的 BaaS 服務。Firebase 在性能揭芍、易用性胳搞、可維護性方面做得都非常好。Firebase 的特色服務是實時同步的 JSON 結構數(shù)據(jù)庫称杨。這種數(shù)據(jù)庫可以迅速感知數(shù)據(jù)變化并立馬同步到其他的客戶端和設備肌毅。換句話說,F(xiàn)irebase 同步功能快如閃電姑原。
Firebase 同時提供了基于加密 SSL 鏈接的用戶授權認證服務悬而。在認證方面,你可以選擇郵箱和密碼锭汛、Facebook笨奠、Twitter、Github唤殴、Google 或者自定義這些組合來進行認證般婆。
不僅僅是在 iOS 上,F(xiàn)irebase 還在 Android 和 JavaScript 平臺上提供了 SDK朵逝。全平臺共用一套實時數(shù)據(jù)庫腺兴,F(xiàn)irebase 統(tǒng)一來管理數(shù)據(jù),自動進行數(shù)據(jù)同步廉侧。
我們很難想象页响,擁有眾多功能的 Firebase 會是一個經(jīng)濟實惠的選擇。所以下面要揭露一下價格上不好的地方……
其實并沒有段誊。在寫這篇教程的時候闰蚕,F(xiàn)irebase 免費支持同時 100 個并發(fā)連接。這個并發(fā)數(shù)已經(jīng)算比較大了连舍。同時 100 個并發(fā)連接可以支持一個非常好的應用程序了没陡。當然,如果每月付費 49 美元,就不會有上面的限制了盼玄。
FirebaseJokes 簡介
今天贴彼,我就帶領大家用 Firebase 來做一款發(fā)布笑話的應用程序。這款應用允許用戶用郵箱和密碼進行注冊和登錄埃儿。登錄成功之后就可以發(fā)布笑話器仗,然后顯示笑話的 tableview 會立刻更新。同樣童番,如果其他人也發(fā)布了笑話精钮,界面也會立馬更新。為了增加應用程序的互動性剃斧,添加了點贊功能轨香。如果笑話很有意思就會得到大家的贊譽。
下面是我們即將在 FirebaseJokes 中實現(xiàn)的功能列表:
- 創(chuàng)建帳號
- 郵箱密碼登錄
- 登出
- 對于登錄用戶跳過登錄環(huán)節(jié)
- 發(fā)布新笑話
- 使用 UITableView 展示笑話幼东、作者和點贊數(shù)
- 對好笑話進行點贊
現(xiàn)在臂容,讓我們下載初始工程。
首先打開 Main.Storyboard根蟹,App 的總體框架如下:
在我們開發(fā)這款應用過程中策橘,我們會逐步了解 Firebase 的相關的用法和概念。由于 Firebase 使用起來非常簡單娜亿,所以我們只需要花時間來開發(fā) FirebaseJokes 業(yè)務部分就可以。
Firebase 概覽
我們打開 Firebase 主頁并注冊一個賬號蚌堵,或者如果你已經(jīng)有賬號了可以直接登錄买决。當然也支持谷歌賬號注冊登錄。注冊成功后吼畏,我們直接跳過官方為 JavaScript 準備的五分鐘簡明教程督赤。我們在這里只需要查看 iOS 的版本即可。
想了解 Firebase 究竟有哪些功能泻蚊,我們只需要點擊 My First App 中的 Manage App 按鈕躲舌。這個新頁面就是 Firebase 控制臺(Firebase Forge)。它是一個非承孕郏酷的可視化調(diào)試工具没卸,并且值得我們觀看新手引導教程。教程會指引你如何創(chuàng)建鍵秒旋、值數(shù)據(jù)约计,甚至可以用加號按鈕來創(chuàng)建子節(jié)點數(shù)據(jù)。這是不是很像 JSON 呢迁筛?通過點按左上角的 Dashboard 圖標便可以退出新手引導教程煤蚌。
創(chuàng)建應用程序
現(xiàn)在,讓開始創(chuàng)建 FirebaseJokes 吧。在 My First App 的左邊尉桩,有個顏色較淡的框就是用來創(chuàng)建新應用的筒占。在 APP NAME 的文本框中,輸入「Jokes」蜘犁;APP URL 的文本框中翰苫,輸入「jokes-你的名字」。這樣可以保證每個應用都有唯一的 url沽瘦。最后點擊 CREATE NEW APP 按鈕革骨,成功之后點擊 Manage App 按鈕。
接下來析恋,我們就進入了這個應用的控制中心界面良哲。在這里,我們可以看到當前應用程序數(shù)據(jù)的實時更新變化助隧。我們也可以在控制中心直接修改數(shù)據(jù)筑凫。為了更好的理解應用程序的工作流程,我們造一些假數(shù)據(jù)來模擬下并村,步驟如下:
- 在應用名稱旁邊點擊小加號按鈕巍实;
- 在文本框中輸入「jokes」;
- 在新建的「jokes」上再次點擊加號來新創(chuàng)建一行哩牍;
- 在新的文本框中輸入一些隨機數(shù)棚潦;
- 在創(chuàng)建的隨時數(shù)上點擊加號再新創(chuàng)建一行;
- 輸入「jokeText」作為鍵膝昆;
- 輸入「What did one computer say to the other? 11001001010101」作為值丸边。
上圖就是在控制中心創(chuàng)建一條笑話后的樣子。我們不僅僅需要創(chuàng)建「笑話」荚孵,也需要創(chuàng)建「用戶」妹窖,但其實兩者都非常類似。時不時的回來看下控制臺上的應用程序的數(shù)據(jù)變化是一個非常好的習慣收叶。
有一點我要指出的是 Firebase 上的數(shù)據(jù)都是以 JSON 對象的形式存儲的骄呼。而 Parse 是采用表格的形式來存儲的。當我們向 Firebase 的數(shù)據(jù)庫中添加數(shù)據(jù)其實就是向現(xiàn)有 JSON 結構中添加一組鍵值對結構判没。剛剛我們創(chuàng)建的數(shù)據(jù) JSON 結構如下:
json
{
"jokes" : {
"e32e223r44" : {
"jokeText" : "What did one computer say to the other? 11001001010101"
}
}
}
現(xiàn)在你已經(jīng)基本了解了 Firebase 數(shù)據(jù)的存儲方式蜓萄,讓我們繼續(xù)吧。
記住在我們開始驗證用戶信息之前澄峰,先把之前創(chuàng)建的假數(shù)據(jù)刪掉绕德,因為接下來我們會在 app 中編寫這些數(shù)據(jù)。
在 FirebaseJokes 中摊阀,我們將會用郵箱和密碼注冊登錄來進行授權認證耻蛇。點擊控制中心左邊的「Login & Auth」面板來開啟這個功能踪蹬。在「Email & Password」選項卡下,選擇「Enable Email & Password Authentication」復選框臣咖。選擇完成后在復選框下面會出現(xiàn)密碼重置的相關設置信息跃捣。同時,你也可以點擊其他選項卡了解下其他登錄授權方式夺蛇。
安裝 Firebase SDK 并配置 Base URL
我們返回到控制中心疚漆,就可以看到 Firebase 應用的 base url。這個 url 正是我們 app 中需要的那一個 url刁赦,所以我們打開 Xcode娶聘,將它復制并粘貼到 Constants.swift 中的 BASE_URL 處。
import Foundation
let BASE_URL = "https://jokes-matt-maher.firebaseio.com"
現(xiàn)在甚脉,我們終于可以在工程中集成 Firebase SDK 了丸升。在此之前我們需要安裝 CocoaPods。如果你沒用過 CocoaPods牺氨,你可以去 CocoaPods 的官網(wǎng)上找到一些安裝的指南狡耻。
CocoaPods 安裝好后,打開終端猴凹,在你的工程目錄下運行如下命令來初始化 Cocoapods 配置文件:
sh
cd <your-xcode-project-directory>
pod init
運行完后夷狰,會出現(xiàn)一個 Podfile 的文件。運行如下命令郊霎,在 Xcode 中打開 Podfile 文件:
sh
open -a Xcode Podfile
接下來沼头,像這樣編輯 Podfile 文件:
sh
platform :ios, '8.0'
use_frameworks!
pod 'Firebase', '>= 2.5.0'
最后,在工程目錄下運行如下命令书劝,下載安裝 Firebase SDK:
sh
pod install
跳轉(zhuǎn)到工程目錄进倍,雙擊打開 FirebaseJokes.xcworkspace
就可以開始敲代碼了。
使用Firebase SDK
首先庄撮,我們在 DataService.swift
中做一些配置,為之后的所要做的事情鋪路毙籽。最重要的就是定義一些引用洞斯。
import Foundation
import Firebase
class DataService {
static let dataService = DataService()
private var _BASE_REF = Firebase(url: "\(BASE_URL)")
private var _USER_REF = Firebase(url: "\(BASE_URL)/users")
private var _JOKE_REF = Firebase(url: "\(BASE_URL)/jokes")
var BASE_REF: Firebase {
return _BASE_REF
}
var USER_REF: Firebase {
return _USER_REF
}
var CURRENT_USER_REF: Firebase {
let userID = NSUserDefaults.standardUserDefaults().valueForKey("uid") as! String
let currentUser = Firebase(url: "\(BASE_REF)").childByAppendingPath("users").childByAppendingPath(userID)
return currentUser!
}
var JOKE_REF: Firebase {
return _JOKE_REF
}
}
導入 Firebase 框架后就可以使用 Firebase SDK 中的接口了。上面的 DataService
其實是一個和 Firebase 交互的服務類坑赡。為了能夠讀寫數(shù)據(jù)烙如,我們引用了 base url 來創(chuàng)建了一個 Firebase 的數(shù)據(jù)庫。接下來毅否,我們會將用戶和笑話當做子節(jié)點來存儲亚铁。獲取這些子節(jié)點的內(nèi)容,我們只要在 base url 后拼接上節(jié)點名稱(例如 name)就可以訪問了螟加。為了讓子節(jié)點操作更方便徘溢,我們也創(chuàng)建了對子節(jié)點的引用吞琐。
注意:創(chuàng)建一個數(shù)據(jù)庫引用并不意味著就創(chuàng)建了一個 Firebase 服務的數(shù)據(jù)庫連接。如果沒有發(fā)起讀和寫的操作是無法獲取數(shù)據(jù)的然爆。
創(chuàng)建一個新賬戶
我們從創(chuàng)建 CreateAccountViewController.swift
文件來開始站粟。對于要用 Firebase 的類,必須要在文件頂部導入 Firebase 框架曾雕。
import UIKit
import Firebase
class CreateAccountViewController: UIViewController {
在 createAccount()
方法中奴烙,我們將用戶輸入的文本用來創(chuàng)建新的用戶。該方法其實是調(diào)用了 Firebase 的 createUser()
方法剖张,代碼如下:
@IBAction func createAccount(sender: AnyObject) {
let username = usernameField.text
let email = emailField.text
let password = passwordField.text
if username != "" && email != "" && password != "" {
// Set Email and Password for the New User.
DataService.dataService.BASE_REF.createUser(email, password: password, withValueCompletionBlock: { error, result in
if error != nil {
// There was a problem.
self.signupErrorAlert("Oops!", message: "Having some trouble creating your account. Try again.")
} else {
// Create and Login the New User with authUser
DataService.dataService.BASE_REF.authUser(email, password: password, withCompletionBlock: {
err, authData in
let user = ["provider": authData.provider!, "email": email!, "username": username!]
// Seal the deal in DataService.swift.
DataService.dataService.createNewAccount(authData.uid, user: user)
})
// Store the uid for future access - handy!
NSUserDefaults.standardUserDefaults().setValue(result ["uid"], forKey: "uid")
// Enter the app.
self.performSegueWithIdentifier("NewUserLoggedIn", sender: nil)
}
})
} else {
signupErrorAlert("Oops!", message: "Don't forget to enter your email, password, and a username.")
}
}
如果用戶輸入的信息無誤切诀,就可以注冊并登錄到 app 中。如果信息缺失搔弄,就會展示一些相應的提示信息幅虑。下面的代碼來展示錯誤信息:
func signupErrorAlert(title: String, message: String) {
// Called upon signup error to let the user know signup didn't work.
let alert = UIAlertController(title: title, message: message, preferredStyle: UIAlertControllerStyle.Alert)
let action = UIAlertAction(title: "Ok", style: .Default, handler: nil)
alert.addAction(action)
presentViewController(alert, animated: true, completion: nil)
}
實際上,創(chuàng)建用戶是在 DataService.swift 的 createNewAccount()
方法中:
func createNewAccount(uid: String, user: Dictionary<String, String>) {
// A User is born.
USER_REF.childByAppendingPath(uid).setValue(user)
}
要向 Firebase 數(shù)據(jù)庫中保存數(shù)據(jù)肯污,你只需要調(diào)用 setValue
方法翘单。在上面的代碼中,將會根據(jù) uid
保存一個新的 user 對象到 users 中(例如 /users/1283834)蹦渣。
除了將用戶存儲到 Firebase 數(shù)據(jù)庫哄芜,我們還需要在本地用 NSUserDefaults 來存儲用戶的 uid
。這樣可以實時跟蹤當前登錄用戶柬唯。
用戶登錄
在我們深入探討之前认臊,我們在 LoginViewController.swift 文件中引入 Firebase 框架。在這個類中我們將會判斷用戶是否登錄過锄奢,如果沒用登錄則跳轉(zhuǎn)到登錄界面失晴。
在 viewDidAppear()
方法中,我們會檢查存儲的 uid
是否為空拘央,以此來驗證用戶是否登錄涂屁。如果檢查通過,則可以直接跳過登錄灰伟;反之進行登錄授權拆又。
override func viewDidAppear(animated: Bool) {
super.viewDidAppear(animated)
// If we have the uid stored, the user is already logger in - no need to sign in again!
if NSUserDefaults.standardUserDefaults().valueForKey("uid") != nil && DataService.dataService.CURRENT_USER_REF.authData != nil {
self.performSegueWithIdentifier("CurrentlyLoggedIn", sender: nil)
}
}
當用戶點擊 Login 按鈕時,tryLogin()
方法就會執(zhí)行栏账。在該方法中加入如下代碼并添加一個 loginErrorAlert
的輔助方法:
@IBAction func tryLogin(sender: AnyObject) {
let email = emailField.text
let password = passwordField.text
if email != "" && password != "" {
// Login with the Firebase's authUser method
DataService.dataService.BASE_REF.authUser(email, password: password, withCompletionBlock: { error, authData in
if error != nil {
print(error)
self.loginErrorAlert("Oops!", message: "Check your username and password.")
} else {
// Be sure the correct uid is stored.
NSUserDefaults.standardUserDefaults().setValue(authData.uid, forKey: "uid")
// Enter the app!
self.performSegueWithIdentifier("CurrentlyLoggedIn", sender: nil)
}
})
} else {
// There was a problem
loginErrorAlert("Oops!", message: "Don't forget to enter your email and password.")
}
}
func loginErrorAlert(title: String, message: String) {
// Called upon login error to let the user know login didn't work.
let alert = UIAlertController(title: title, message: message, preferredStyle: UIAlertControllerStyle.Alert)
let action = UIAlertAction(title: "Ok", style: .Default, handler: nil)
alert.addAction(action)
presentViewController(alert, animated: true, completion: nil)
}
Firebase 默認支持用戶郵箱和密碼進行用戶授權認證帖族。tryLogin()
方法調(diào)用了 Firebase 的 authUser()
方法來檢查當前郵箱和密碼是否已經(jīng)注冊過。如果注冊過挡爵,就能獲取用戶的 uid
竖般,登錄成功進入 app。如果沒有茶鹃,我們則給出一些其他嘗試的提示涣雕。
既然現(xiàn)在 app 已經(jīng)完成了用戶注冊和登錄功能艰亮,接下來我們就來看看如何對笑話數(shù)據(jù)進行處理。
笑話的數(shù)據(jù)模型
什么是笑話胞谭?這個哲學問題我們可以以后再回答垃杖,或者看下 FirebaseJokes 上的一個笑話就會明白了。對我們而言丈屹,抽象出一個 Joke 數(shù)據(jù)模型更加重要调俘。
我們在 Joke.swift
中導入 Firebase 框架。在我們的數(shù)據(jù)庫中旺垒,一個 joke 對象映射一個唯一編號彩库。這個自動生成的編號包含了 joke 中的如下屬性:
- jokeText
- jokeVotes
- username(笑話的作者)
我們將笑話的 id、key 和內(nèi)容先蒋,以字典的形式作為參數(shù)骇钦,通過 init() 方法來新建一個笑話對象。
class Joke {
private var _jokeRef: Firebase!
private var _jokeKey: String!
private var _jokeText: String!
private var _jokeVotes: Int!
private var _username: String!
var jokeKey: String {
return _jokeKey
}
var jokeText: String {
return _jokeText
}
var jokeVotes: Int {
return _jokeVotes
}
var username: String {
return _username
}
// Initialize the new Joke
init(key: String, dictionary: Dictionary<String, AnyObject>) {
self._jokeKey = key
// Within the Joke, or Key, the following properties are children
if let votes = dictionary["votes"] as? Int {
self._jokeVotes = votes
}
if let joke = dictionary["jokeText"] as? String {
self._jokeText = joke
}
if let user = dictionary["author"] as? String {
self._username = user
} else {
self._username = ""
}
// The above properties are assigned to their key.
self._jokeRef = DataService.dataService.JOKE_REF.childByAppendingPath(self._jokeKey)
}
}
添加新的笑話
在 AddJokeViewController.swift
文件中竞漾,是時候引入 Firebase 框架了眯搭。在這個類中,我們將完成讓用戶添加一個笑話业岁,發(fā)送到 Firebase 服務上鳞仙,然后立刻推送到各個設備上進行同步展示。
首先在 viewDidLoad()
方法中笔时,我們首先獲取當前用戶的用戶名棍好,來當做新創(chuàng)建笑話的作者,代碼如下:
override func viewDidLoad() {
super.viewDidLoad()
// Get username of the current user, and set it to currentUsername, so we can add it to the Joke.
DataService.dataService.CURRENT_USER_REF.observeEventType(FEventType.Value, withBlock: { snapshot in
let currentUser = snapshot.value.objectForKey("username") as! String
print("Username: \(currentUser)")
self.currentUsername = currentUser
}, withCancelBlock: { error in
print(error.description)
})
}
當 saveJoke()
方法被調(diào)用時,將會創(chuàng)建一個新的笑話的字典對象允耿。這個字典對象會包含由 jokeField 中的文字組成的笑話的內(nèi)容借笙、投票數(shù)(默認為 0)以及作者名(當前用戶的用戶名)。這些值將通過 DataSerivce 中的 createNewJoke()
方法發(fā)送到服務器來進行存儲较锡。
在 AddJokeViewController
類中聲明如下變量:
var currentUsername = ""
更新 saveJoke()
方法:
@IBAction func saveJoke(sender: AnyObject) {
let jokeText = jokeField.text
if jokeText != "" {
// Build the new Joke.
// AnyObject is needed because of the votes of type Int.
let newJoke: Dictionary<String, AnyObject> = [
"jokeText": jokeText!,
"votes": 0,
"author": currentUsername
]
// Send it over to DataService to seal the deal.
DataService.dataService.createNewJoke(newJoke)
if let navController = self.navigationController {
navController.popViewControllerAnimated(true)
}
}
}
我們用一個字典對象來臨時存儲笑話數(shù)據(jù)业稼。并將該字典傳遞給 DataService
中的 createNewJoke()
方法來發(fā)送到服務器端進行存儲。在 DataSerivce.swift 文件中蚂蕴,添加 createNewJoke()
方法:
func createNewJoke(joke: Dictionary<String, AnyObject>) {
// Save the Joke
// JOKE_REF is the parent of the new Joke: "jokes".
// childByAutoId() saves the joke and gives it its own ID.
let firebaseNewJoke = JOKE_REF.childByAutoId()
// setValue() saves to Firebase.
firebaseNewJoke.setValue(joke)
}
我們是通過 Firebase 的 setValue() 方法來存儲的笑話對象低散。但是也不要忽略我們調(diào)用了 Joke 數(shù)據(jù)庫引用對象的 childByAutoId
方法。通過調(diào)用此方法掂墓,F(xiàn)irebase 會為每一個笑話生成唯一的編號谦纱,來確保笑話存儲的唯一性看成。
用戶登出
這個功能通常會出現(xiàn)在設置和個人簡介部分君编。但是我們就姑且把登出功能放在 AddJokeViewController.swift
類中吧〈ɑ牛或者說吃嘿,這就是一個最大的笑話祠乃。
logout()
方法實際上是調(diào)用了 Firebase 的 unauth()
方法來讓用戶退出登錄。在用戶登出的同時兑燥,我們需要刪除在 LoginViewController 中存儲在本地的 uid
亮瓷。
像這樣更新 logout
部分的方法:
@IBAction func logout(sender: AnyObject) {
// unauth() is the logout method for the current user.
DataService.dataService.CURRENT_USER_REF.unauth()
// Remove the user's uid from storage.
NSUserDefaults.standardUserDefaults().setValue(nil, forKey: "uid")
// Head back to Login!
let loginViewController = self.storyboard!.instantiateViewControllerWithIdentifier("Login")
UIApplication.sharedApplication().keyWindow?.rootViewController = loginViewController
}
如果沒有刪除用戶的 uid
,可能會導致用戶再次登錄的時候出現(xiàn)問題降瞳,這里要注意一下嘱支。
展示所有的笑話
最后就是從 Firebase 上獲取笑話數(shù)據(jù)展示出來。我們將會在 JokesFeedTableViewController.swift 文件中用一個 UITableView 來展示所有的笑話挣饥。在這里我們還是要先導入 Firebasde 框架除师。
在 viewDidLoad()
方法中洗搂,配置 observeEventType()
方法平夜。Firebase 是通過數(shù)據(jù)庫的引用對象來異步監(jiān)聽數(shù)據(jù)變化來獲取更新數(shù)據(jù)的。這個方法是在導航到 JokesFeedTableViewController.swift
界面時畅买,在 viewDidLoad()
方法中被調(diào)用的短荐。當數(shù)據(jù)庫有任何笑話數(shù)據(jù)變化時這個監(jiān)聽方法就會被調(diào)用倚舀,代碼如下:
var jokes = [Joke]()
override func viewDidLoad() {
super.viewDidLoad()
// observeEventType is called whenever anything changes in the Firebase - new Jokes or Votes.
// It's also called here in viewDidLoad().
// It's always listening.
DataService.dataService.JOKE_REF.observeEventType(.Value, withBlock: { snapshot in
// The snapshot is a current look at our jokes data.
print(snapshot.value)
self.jokes = []
if let snapshots = snapshot.children.allObjects as? [FDataSnapshot] {
for snap in snapshots {
// Make our jokes array for the tableView.
if let postDictionary = snap.value as? Dictionary<String, AnyObject> {
let key = snap.key
let joke = Joke(key: key, dictionary: postDictionary)
// Items are returned chronologically, but it's more fun with the newest jokes first.
self.jokes.insert(joke, atIndex: 0)
}
}
}
// Be sure that the tableView updates when there is new data.
self.tableView.reloadData()
})
}
在回調(diào)方法中返回了一些笑話對象。我們用一個數(shù)組來存儲這些對象忍宋,并展示在 tableView 上痕貌。我們想讓最新的笑話保持在頂部,但是 Firebase 返回的笑話數(shù)據(jù)是按照時間排序的讶踪,所以我們必須將數(shù)據(jù)反向插入芯侥。
每次當笑話更新的時候,我們別忘了調(diào)用 tableView 的 reloadData 方法乳讥,這樣才能讓新的數(shù)據(jù)展示出來柱查。
我們剩下的工作就是在 tableView:cellForRowAtIndexPath:
方法中指定自定義 cell:JokeCellTableViewCell.swift。在 JokeCellTableViewCell.swift 文件中創(chuàng)建一個 configureCell()
方法云石,當執(zhí)行 tableView:cellForRowAtIndexPath:
時調(diào)用該方法唉工,代碼如下:
override func numberOfSectionsInTableView(tableView: UITableView) -> Int {
return 1
}
override func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return jokes.count
}
override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let joke = jokes[indexPath.row]
// We are using a custom cell.
if let cell = tableView.dequeueReusableCellWithIdentifier("JokeCellTableViewCell") as? JokeCellTableViewCell {
// Send the single joke to configureCell() in JokeCellTableViewCell.
cell.configureCell(joke)
return cell
} else {
return JokeCellTableViewCell()
}
}
在 JokeCellTableViewCell.swift 文件中的 configureCell()
方法里,我們需要設置 label 標簽以及添加監(jiān)聽投票的點按邏輯汹忠。
func configureCell(joke: Joke) {
self.joke = joke
// Set the labels and textView.
self.jokeText.text = joke.jokeText
self.totalVotesLabel.text = "Total Votes: \(joke.jokeVotes)"
self.usernameLabel.text = joke.username
// Set "votes" as a child of the current user in Firebase and save the joke's key in votes as a boolean.
voteRef = DataService.dataService.CURRENT_USER_REF.childByAppendingPath("votes").childByAppendingPath(joke.jokeKey)
// observeSingleEventOfType() listens for the thumb to be tapped, by any user, on any device.
voteRef.observeSingleEventOfType(.Value, withBlock: { snapshot in
// Set the thumb image.
if let thumbsUpDown = snapshot.value as? NSNull {
// Current user hasn't voted for the joke... yet.
print(thumbsUpDown)
self.thumbVoteImage.image = UIImage(named: "thumb-down")
} else {
// Current user voted for the joke!
self.thumbVoteImage.image = UIImage(named: "thumb-up")
}
})
}
UITapGestureRecognizer 手勢監(jiān)聽是在 awakeFromNib()
方法中添加的淋硝。在類的頂部,同時要加上 Firebase 和 Joke 對象的引用宽菜。
var joke: Joke!
var voteRef: Firebase!
override func awakeFromNib() {
super.awakeFromNib()
// UITapGestureRecognizer is set programatically.
let tap = UITapGestureRecognizer(target: self, action: "voteTapped:")
tap.numberOfTapsRequired = 1
thumbVoteImage.addGestureRecognizer(tap)
thumbVoteImage.userInteractionEnabled = true
}
在 voteTapped()
方法中谣膳,有另外一個監(jiān)聽者在等待著用戶點擊后觸發(fā)回調(diào)。在這個方法中铅乡,將會把投票數(shù)通過 configureCell()
方法中的 voteRef 對象存儲到對應笑話下鍵為 votes 的字段值中继谚。
func voteTapped(sender: UITapGestureRecognizer) {
// observeSingleEventOfType listens for a tap by the current user.
voteRef.observeSingleEventOfType(.Value, withBlock: { snapshot in
if let thumbsUpDown = snapshot.value as? NSNull {
print(thumbsUpDown)
self.thumbVoteImage.image = UIImage(named: "thumb-down")
// addSubtractVote(), in Joke.swift, handles the vote.
self.joke.addSubtractVote(true)
// setValue saves the vote as true for the current user.
// voteRef is a reference to the user's "votes" path.
self.voteRef.setValue(true)
} else {
self.thumbVoteImage.image = UIImage(named: "thumb-up")
self.joke.addSubtractVote(false)
self.voteRef.removeValue()
}
})
}
voteTapped()
方法也會通過一個布爾值調(diào)用 addSubtractVote()
函數(shù)來更新 Joke.swift 中的投票數(shù)。如果參數(shù)是 True阵幸,則代表用戶給這個笑話投票了花履;反之芽世,F(xiàn)alse 則代表沒有投過票。
// Add or Subtract a Vote from the Joke.
func addSubtractVote(addVote: Bool) {
if addVote {
_jokeVotes = _jokeVotes + 1
} else {
_jokeVotes = _jokeVotes - 1
}
// Save the new vote total.
_jokeRef.childByAppendingPath("votes").setValue(_jokeVotes)
}
在 Joke.swift 的文件中诡壁,addSubtractVote()
方法使用了這個布爾值完成對票數(shù)的加減济瓢。Firebase 所提供的 setValue()
方法用來更新當前笑話的投票數(shù)到遠端數(shù)據(jù)庫。
測試
現(xiàn)在妹卿,可以對這個應用進行測試了旺矾。創(chuàng)建一個新用戶并創(chuàng)建一些笑話。也可以對這些笑話進行投票點贊夺克。然后回到 Firebase 的控制中心宠漩,就可以看到剛剛創(chuàng)建的用戶和笑話,如下圖:
總結
完成了懊直!我們基于 Firebase 開發(fā)了一個用戶體驗不錯的小應用扒吁。很有成就感吧!
這里是完成后的代碼室囊。
這個應用只是一個小的開始雕崩,基于 Firebase 服務來開發(fā)應用程序?qū)袩o限可能。
嘗試下為 FirebaseJokes 添加用戶授權的功能融撞;也可以添加聊天的功能盼铁;你會發(fā)現(xiàn) Firebase 提供了無窮無盡的可能性。
關于圖片存儲的建議:Firebase 存儲服務也有其局限性尝偎,像圖片文件就不太適合用 Firebase 來存儲饶火,應該存儲在單獨的圖片服務器上。像上面的純文本存儲型應用使用 Firebase 是沒有問題的致扯,但是如果應用要存儲大型文件肤寝,最好做一套單獨的文件服務。
好了抖僵,現(xiàn)在就是加入 Firebase 服務陣營的最好時機鲤看,行動起來吧。
本文由 SwiftGG 翻譯組翻譯耍群,已經(jīng)獲得作者翻譯授權义桂,最新文章請訪問 http://swift.gg。