Firebase⼀一場我與Jink共同打的戰爭
⾃自我介紹
Henry Tseng (@tuzaiz)
• 網路暱稱 tuzaiz, 兔⼦子 或 崽⼦子兔 • 是個混了Web front-end / back-end 與 iOS 的⽶米克斯
Developer (另外還有Python...) • ⺫⽬目前在Greenhouse Apps中擔任iOS開發⼯工程師,負責
開發Jink。 • 不過快要離開了,所以...
Available
Jink幫我⽼老闆⼯工商服務⼀一下
Jink
• ⼀一個由矽⾕谷創辦⼈人與台灣團隊開發的即時位置分享APP。
• 當分享的兩⼈人相遇時會⾃自動停⽌止分享。
• 分享了太久會⾃自動暫停,避免使⽤用者忘記⾃自⼰己的位置⼀一直外洩。
• TechCrunch與⽇日韓美中台多家媒體報導
• Apple App Store與Google Play皆有Features
Firebase
什麼是Firebase• ⼀一個很像是MongoDB的雲端資料庫
• 特⾊色在於可提供Realtime資料異動通知,效果類似WebSocket,適合⽤用於即時聊天、線上遊戲等等的應⽤用。
• 可⽤用於多平台,包括iOS、Android、Web (JavaScript)、Backend (NodeJS) and Other (REST)…
• 聽作者本⼈人說這是他在通勤時在⾞車上⼀一時興起寫出來的
• 然後現在這服務就被Google給買⾛走了...
• 這是個⼤大家都聽了許多但都不會發⽣生在⾃自⼰己⾝身上的傳奇故事。
資料結構
• 資料結構類似JSON,每個節點都是Key -> Value
• Value 可以是 String、Number、Boolean、Dict、Array 或是Timestamp
• 不過它的Array⻑⾧長得很奇怪... 只要Key是 0, 1, 2, 3 這樣排列的就是Array,不過如果Key是0, 1, 4, 6, 7 就是Dictionary,5, 6, 7, 8, 9 也是Dictionary
• 上⾯面這點要⾮非常注意,我們吃了很多次悶虧
FirebaseRef
⾸首先要做的第⼀一件事情就是建⽴立Firebase的Reference,藉由Firebase後台產⽣生的
URL連線到⺫⽬目標服務上,網址後⾯面可帶要存取的⺫⽬目標節點,沒帶就是存取根⺫⽬目錄。
var firebase = Firebase(url: “https://YOUR_PROJECT_NAME.firebaseio.com/NODE_PATH”)
Firebase *firebase = [[Firebase alloc] initWithUrl:@"https://YOUR_PROJECT_NAME.firebaseio.com/NODE_PATH"];
認證
當然,只要URL就可以存取那也太恐怖了,因此可以到後台設定安全性設定,然後利⽤用API Token或是帳號密碼進⾏行認證。
firebase.authWithCustomToken(“CUSTOM_TOKEN", withCompletionBlock: { (error, authData) -> Void in // Complete callback })
[firebase authWithCustomToken:@"CUSTOM_TOKEN" withCompletionBlock:^(NSError *error, FAuthData *authData) { // Complete callback }];
使⽤用者管理• 可讓使⽤用者註冊登⼊入,並可產⽣生⾃自⼰己的uid與tokenself.firebase.createUser(email, password: password) { (error) -> Void in // Complete callback }
self.firebase.authUser(email, password: password) { (error, authData) -> Void in // Complete callback }
[firebase createUser:email password:password withCompletionBlock:^(NSError *error) { // Complete callback }];
[firebase authUser:email password:password withCompletionBlock:^(NSError *error, FAuthData *authData) { // Complete callback }];
移轉節點• 如要對Reference的⼦子階層做監聽或是寫⼊入的話,可以⽤用兩個⽅方法。
• childByAppendingPath(path) // 前往已知的⼦子節點
• childByAutoId() // 隨機建⽴立⼀一個Hash當Key的Node
firebase.childByAppendingPath(“child1/child1_1“) firebase.childByAutoId()
[firebase childByAppendingPath:@"child1/child1_1"]; [firebase childByAutoId];
Firebase有五種Event通知
• Value:回傳監聽的節點下的所有資料,並在變動時通知。
• ChildAdded:當監聽節點的⼦子階層有新增資料時回傳。
• ChildChanged:當監聽節點的⼦子階層有資料變動時回傳。
• ChildMoved:當監聽節點的⼦子階層有資料移動時回傳。
• ChildDeleted:當監聽節點的⼦子階層有資料刪除時回傳。
firebase.observeEventType(.ChildAdded, withBlock: { (snapshot) in let key = snapshot.key as String let value = snapshot.value as [String : AnyObject] })
firebase.childByAppendingPath(“child1/child1_1“).observeEventType(.ChildChanged, withBlock: { (snapshot) in let key = snapshot.key as String let value = snapshot.value as [String : AnyObject] })
[firebase observeSingleEventOfType:FEventTypeChildAdded withBlock:^(FDataSnapshot *snapshot) { NSString *key = snapshot.key; NSDictionary *value = snapshot.value; }];
[[firebase childByAppendingPath:@"child1/child1_1"] observeSingleEventOfType:FEventTypeChildChanged withBlock:^(FDataSnapshot *snapshot) { NSString *key = snapshot.key; NSDictionary *value = snapshot.value; }];
Set / Update value• 寫值的⽅方法有兩種
• setValue // 將⺫⽬目標節點整個被新值覆蓋
• updateChildValues // 只更新有指定的Key,其餘不變
firebase.childByAutoId().setValue(["content": self.text, "like": 0])firebase.childByAutoId().setValue(100)firebase.childByAppendingPath("child1/child1_1").updateChildValues(["content": "Haha", "like": 10])
[[firebase childByAutoId] setValue:@{@"content": self.text, @"like": @10}];
[[firebase childByAutoId] setValue:@10];
[[firebase childByAppendingPath:@"child1/child1_1"] updateChildValues:@{@"content": self.text, @"like": @10}];
Transaction有些時候我們需要先取回Firebase上的資料再做變更,此時可以⽤用Transaction
[[firebase childByAppendingPath:key] runTransactionBlock:^FTransactionResult *(FMutableData *currentData) { NSMutableDictionary *status = [currentData.value mutableCopy]; NSNumber *like = status[@"like"]; if (like) { [status setObject:@(like.integerValue + 1) forKey:@"like"]; } else { [status setObject:@1 forKey:@"like"]; } currentData.value = status; FTransactionResult *result = [FTransactionResult successWithValue:currentData]; return result; }];
self.firebase.childByAppendingPath("\(key)").runTransactionBlock({ (currentData:FMutableData!) -> FTransactionResult! in var status = currentData.value as [String : AnyObject] var like = status["like"] as? Int if like != nil { status["like"] = like! + 1 } else { status["like"] = 1 } currentData.value = status return FTransactionResult.successWithValue(currentData) })
Query• Query的能⼒力很基本,基本上就是EqualTo、Limit、Order幾個Query methods
• 多個Query可串接在⼀一起
• Query同樣可以⽤用來Observe
self.firebase.queryLimitedToFirst(10).queryOrderedByKey().queryEqualToValue(“SampleData”).observeEventType (.ChildChanged, withBlock: { (snapshot) -> Void in // Callback })
[[[[firebase queryLimitedToFirst:10] queryOrderedByKey] queryEqualToValue:@"SampleData"] observeSingleEventOfType:FEventTypeChildChanged withBlock:^(FDataSnapshot *snapshot) { // Callback }];
安全性管理
• 透過編輯後台Firebase Rules的JSON file來提供安全性管理。
• 可限制哪些User可讀寫哪些Node。
{ "rules": { "foo": { ".validate": "newData.isString() && newData.val().length < 100" } } }
{ "rules": { "users": { "$user_id": { ".write": "$user_id === auth.uid", } } } }
Demo
Q&A
fin
email: [email protected]
Github: https://github.com/tuzaiz
twitter: @tuzaiz
LinkedIn: http://tw.linkedin.com/in/tuzaiz/
Facebook: https://www.facebook.com/tuzaiz