Firebase 新手指南:使用 Swift 構建一款簡單的社交應用

作者: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

下面是我們即將在 FirebaseJokes 中實現(xiàn)的功能列表:

  • 創(chuàng)建帳號
  • 郵箱密碼登錄
  • 登出
  • 對于登錄用戶跳過登錄環(huán)節(jié)
  • 發(fā)布新笑話
  • 使用 UITableView 展示笑話幼东、作者和點贊數(shù)
  • 對好笑話進行點贊

現(xiàn)在臂容,讓我們下載初始工程

首先打開 Main.Storyboard根蟹,App 的總體框架如下:

Main.Storyboard

在我們開發(fā)這款應用過程中策橘,我們會逐步了解 Firebase 的相關的用法和概念。由于 Firebase 使用起來非常簡單娜亿,所以我們只需要花時間來開發(fā) FirebaseJokes 業(yè)務部分就可以。

Firebase 概覽

我們打開 Firebase 主頁并注冊一個賬號蚌堵,或者如果你已經(jīng)有賬號了可以直接登錄买决。當然也支持谷歌賬號注冊登錄。注冊成功后吼畏,我們直接跳過官方為 JavaScript 準備的五分鐘簡明教程督赤。我們在這里只需要查看 iOS 的版本即可。

Firebase Homepage

想了解 Firebase 究竟有哪些功能泻蚊,我們只需要點擊 My First App 中的 Manage App 按鈕躲舌。這個新頁面就是 Firebase 控制臺(Firebase Forge)。它是一個非承孕郏酷的可視化調(diào)試工具没卸,并且值得我們觀看新手引導教程。教程會指引你如何創(chuàng)建鍵秒旋、值數(shù)據(jù)约计,甚至可以用加號按鈕來創(chuàng)建子節(jié)點數(shù)據(jù)。這是不是很像 JSON 呢迁筛?通過點按左上角的 Dashboard 圖標便可以退出新手引導教程煤蚌。

創(chuàng)建應用程序

Create New App

現(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ù)來模擬下并村,步驟如下:

  1. 在應用名稱旁邊點擊小加號按鈕巍实;
  2. 在文本框中輸入「jokes」;
  3. 在新建的「jokes」上再次點擊加號來新創(chuàng)建一行哩牍;
  4. 在新的文本框中輸入一些隨機數(shù)棚潦;
  5. 在創(chuàng)建的隨時數(shù)上點擊加號再新創(chuàng)建一行;
  6. 輸入「jokeText」作為鍵膝昆;
  7. 輸入「What did one computer say to the other? 11001001010101」作為值丸边。
Forge Data

上圖就是在控制中心創(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)密碼重置的相關設置信息跃捣。同時,你也可以點擊其他選項卡了解下其他登錄授權方式夺蛇。

User Auth

安裝 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)建的用戶和笑話,如下圖:

Joke Firebase Data

總結

完成了懊直!我們基于 Firebase 開發(fā)了一個用戶體驗不錯的小應用扒吁。很有成就感吧!

這里是完成后的代碼室囊。

這個應用只是一個小的開始雕崩,基于 Firebase 服務來開發(fā)應用程序?qū)袩o限可能。

嘗試下為 FirebaseJokes 添加用戶授權的功能融撞;也可以添加聊天的功能盼铁;你會發(fā)現(xiàn) Firebase 提供了無窮無盡的可能性。

關于圖片存儲的建議:Firebase 存儲服務也有其局限性尝偎,像圖片文件就不太適合用 Firebase 來存儲饶火,應該存儲在單獨的圖片服務器上。像上面的純文本存儲型應用使用 Firebase 是沒有問題的致扯,但是如果應用要存儲大型文件肤寝,最好做一套單獨的文件服務。

好了抖僵,現(xiàn)在就是加入 Firebase 服務陣營的最好時機鲤看,行動起來吧。

本文由 SwiftGG 翻譯組翻譯耍群,已經(jīng)獲得作者翻譯授權义桂,最新文章請訪問 http://swift.gg

最后編輯于
?著作權歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末蹈垢,一起剝皮案震驚了整個濱河市慷吊,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌曹抬,老刑警劉巖溉瓶,帶你破解...
    沈念sama閱讀 211,348評論 6 491
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異,居然都是意外死亡嚷闭,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,122評論 2 385
  • 文/潘曉璐 我一進店門赖临,熙熙樓的掌柜王于貴愁眉苦臉地迎上來胞锰,“玉大人,你說我怎么就攤上這事兢榨⌒衢牛” “怎么了?”我有些...
    開封第一講書人閱讀 156,936評論 0 347
  • 文/不壞的土叔 我叫張陵吵聪,是天一觀的道長凌那。 經(jīng)常有香客問我,道長吟逝,這世上最難降的妖魔是什么帽蝶? 我笑而不...
    開封第一講書人閱讀 56,427評論 1 283
  • 正文 為了忘掉前任,我火速辦了婚禮块攒,結果婚禮上励稳,老公的妹妹穿的比我還像新娘。我一直安慰自己囱井,他們只是感情好驹尼,可當我...
    茶點故事閱讀 65,467評論 6 385
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著庞呕,像睡著了一般新翎。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上住练,一...
    開封第一講書人閱讀 49,785評論 1 290
  • 那天地啰,我揣著相機與錄音,去河邊找鬼讲逛。 笑死髓绽,一個胖子當著我的面吹牛,可吹牛的內(nèi)容都是我干的妆绞。 我是一名探鬼主播顺呕,決...
    沈念sama閱讀 38,931評論 3 406
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼括饶!你這毒婦竟也來了株茶?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 37,696評論 0 266
  • 序言:老撾萬榮一對情侶失蹤图焰,失蹤者是張志新(化名)和其女友劉穎启盛,沒想到半個月后,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 44,141評論 1 303
  • 正文 獨居荒郊野嶺守林人離奇死亡僵闯,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 36,483評論 2 327
  • 正文 我和宋清朗相戀三年卧抗,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片鳖粟。...
    茶點故事閱讀 38,625評論 1 340
  • 序言:一個原本活蹦亂跳的男人離奇死亡社裆,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出向图,到底是詐尸還是另有隱情泳秀,我是刑警寧澤,帶...
    沈念sama閱讀 34,291評論 4 329
  • 正文 年R本政府宣布榄攀,位于F島的核電站嗜傅,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏檩赢。R本人自食惡果不足惜吕嘀,卻給世界環(huán)境...
    茶點故事閱讀 39,892評論 3 312
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望贞瞒。 院中可真熱鬧币他,春花似錦、人聲如沸憔狞。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,741評論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽瘾敢。三九已至拍冠,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間簇抵,已是汗流浹背庆杜。 一陣腳步聲響...
    開封第一講書人閱讀 31,977評論 1 265
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留碟摆,地道東北人晃财。 一個月前我還...
    沈念sama閱讀 46,324評論 2 360
  • 正文 我出身青樓,卻偏偏與公主長得像典蜕,于是被迫代替她去往敵國和親断盛。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 43,492評論 2 348

推薦閱讀更多精彩內(nèi)容

  • 發(fā)現(xiàn) 關注 消息 iOS 第三方庫愉舔、插件钢猛、知名博客總結 作者大灰狼的小綿羊哥哥關注 2017.06.26 09:4...
    肇東周閱讀 12,059評論 4 62
  • Android 自定義View的各種姿勢1 Activity的顯示之ViewRootImpl詳解 Activity...
    passiontim閱讀 171,757評論 25 707
  • 中國人的做事習慣比較愛找關系,尤其是拿不準的找個人斟酌一下轩缤,買個車命迈,買個房都愛走走關系看看能不能拿個內(nèi)部價贩绕。 我自...
    基礎繪畫社閱讀 1,176評論 1 0
  • www.taggerin.com淑倾,主要處理日常文檔的在線編輯,以及與Markdown,PDF,html等格式的雙向...
    igool閱讀 273評論 0 1