Firebase Cloud Functions with Database and Messaging

Cloud Functions

Firebase 一直以來便是以 serverless 為主要的方向,
而 Functions 則是一個十分有趣的功能;
它可以自動地隨著事件的回應,如資料庫的異動或是收到 HTTP 的 requests 時,執行程式碼。

其中一個重點是,我們也不需要去管理或是 scale 伺服器。

Triggers

  • Cloud Firestore Triggers
  • Realtime Database Triggers
  • Firebase Authentication Triggers
  • Google Analytics for Firebase Triggers
  • Crashlytics Triggers
  • Cloud Storage Triggers
  • Cloud Pub/Sub Triggers
  • HTTP Triggers

範例

需求

Realtime Database 底下的資料有異動的話,進行推播

直接上 Code
const functions = require('firebase-functions');
const admin = require('firebase-admin');

admin.initializeApp(functions.config().firebase);

exports.updateTodayNews = functions.database.ref('/today/{newsCategory}/updatedTime').onWrite(event => {
    const newsCategory = event.params.newsCategory;

    return admin.database().ref(`/today/${newsCategory}`).once('value').then((snapshot) => {
        if (!snapshot.hasChildren()) {
            return console.log('There are no notification tokens to send to.');
        }
        const title = snapshot.child('title').val();
        const body = snapshot.child('body').val();

        // Notification details.
        const payload = {
          notification: {
            title: title,
            body: body
          }
        };
        if (!snapshot.child('fcmTokens').hasChildren()) {
            return console.log('There are no notification tokens to send to.');
        }
        const tokens = Object.keys(snapshot.child('fcmTokens').val());
        const pushTokens = tokens.filter(key => {
            return snapshot.child('fcmTokens').child(key).val();
        });
        return admin.messaging().sendToDevice(pushTokens, payload)
        .then(function (response) {
            console.log('successfully sent message:', response);
        })
        .catch(function (error) {
            console.log('Error sending message:', error);
        });
    });
});

簡單解說

exports 後面加上這支 function 的名稱,如我們這邊則為 updateTodayNews
而 {newsCategory} 則是變數,以範例來說:
只要 /today/ 底下的任何物件 /updatedTime 又被寫入的話,則會觸發 function。

宣告 admin 是為了使用 Firebase 其他的功能,如這邊是使用到 messaging 和 database。

當 admin.database 得到資料回來後,再使用 admin.messaging().sendToDevice 來進行推播的發送。

FirebaseDatabase – Read

之前有寫過 FirebaseDatabase REST API的文章,
而這篇則會是在 iOS 上的使用。

安裝套件

由於 Google 認為 Carthage 的方式不符合他們的使用模式,
畢竟 Firebase 的 framework 並非是開源的,
所以只有提供 CocoaPods 的安裝方式或是直接下載檔案;
而我這邊就以 CocoaPods 來安裝 Firebase 相關的套件,其他則用 Carthage 來管理。

設定

我們在 Firebase console 那先建立好專案並匯入 GoogleService-Info.plist,
如果你有多個 Target 要使用的話,建議放在不同的資料夾,並且設定好 Target Membership。

並且要注意 Firebase console 內的 Database rules,
若沒有做 auth 相關內容的話,記得要調整;

如我開放給 App 讀取但不可寫入的話:

{
  "rules": {
    ".write": "auth != null",
    ".read": true
  }
}

接著在 AppDelegate.swift 中加入

func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey: Any]?) -> Bool {
    FirebaseApp.configure()
    // --**--
}

順道提醒一下,若要讓 Database 的資料在離線也能使用上一次的 cache 的話,
需要在 AppDelegate.swift 裡頭加入

func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey: Any]?) -> Bool {
    // --**--
    Database.database().isPersistenceEnabled = true
    // --**--
}

官方文件中沒有特別註記,但若在其他地方執行這段程式,則會直接報錯。

讀取資料

Firebase Database 所提供的是一個可監聽的資料庫,
來做到 Realtime Database 的效果;

Reference 便是 path 的概念,
假設我的資料長這樣:

{
    "test": {
        "user": {
            "name": "Archie"
        }
    }
}

則 ref 有幾種設法;

  • 觀察全部的資料: Database.database().reference.observe
  • 只看 test 下的異動: Database.database().reference.child(“test").observe
  • 只看 user:Database.database().reference.child(“test").child(“user").observe

隨著資料的結構,我們可以讓每個地方只專注它需要監聽的部分就好。

Firebase Database REST API

這篇主要的內容會是簡單地記錄一下 Firebase Database RESTful API

所提供的相關內容和使用方式。

Firebase Database

Firebase database 的儲存資料方式是屬於 NoSQL 的方式,

利用一組 key 配對一組 value 的模式來建構資料庫;

而在 Database 的介面中,我們可以清楚地看到資料是以 JSON 的格式呈現。

在 iOS 開發的過程中,如果要使用 Firebase 的相關內容,

可以使用官方所提供的 Firebase iOS SDK

或是在使用 Python 開發的時候,我會選擇使用官方推薦的 Pyrebase

那若你目前的開發方式沒有相對應的 SDK 或是 third party 可以使用的話呢?

那麼你就只能一起用 REST API 來完成要做的事情了!

REST API

Firebase 提供了五種 Http method

  • GET
  • PUT
  • POST
  • PATCH
  • DELETE

其中 GET 和 DELETE 就沒什麼特別好說的,你就是取得一個 JSON 或是刪除一個。

PUT

PUT 就和平常使用 PUT 的方式一樣,

它會把整個 JSON 覆蓋成你目前丟上去的 JSON。

POST

POST 的話就有些不一樣,當你 POST 一個資料到某個 JSON 的時候,

它會自行建立一組 key 並回傳 name 回來。

而你所丟的資料會是那組 key 所對應的 value,

所以簡單來說 POST 就是在做新增物件時使用,且 ID 是由它所建立的。

PATCH

PATCH 則是負責更新內容,它會先找到匹配的 key,再更新其 value;

若沒有找到相對應的 key 的話,則會建立一筆新的 key-value。

如果有其中一個無法使用呢?

若遇到你所使用的開發語言或是瀏覽器等等,無法使用其中一項 method 時;

舉個例子,DELETE 無法使用的話,Firebase 有提供你覆寫 method 的功能,

method 使用 POST,而在 header 加上:

X-HTTP-Method-Override: DELETE

便可以等同於上述所說的 DELETE。

或是加在 url 裡頭也可以:

https://%5BPROJECT_ID%5D.firebaseio/%5BJSON_NAME%5D.json?x-http-method-override=DELETE

 

Firebase ETag

Firebase 也有支援 ETag,

在 header 上加上 X-Firebase-ETag: true,它便會在回傳的 headers 中加上

Access-Control-Expose-Headers = ETag

ETag = kmHkuKx9sCx742tosJOV4oH+JBQ=

而我們可以下一次的 request 中,在 header 放上 if-match:[ETAG_VALUE]

伺服器端便會驗證是否可以執行這次的要求;

若最後一次的 ETag 和 if-match 的值相符的話,Firebase 便會回傳 412 Precondition failed。

而 ETag 相關的資訊可以看這邊

 

最後

整理完這些資訊後,就可以著手寫一些 database 的存取方法到 Vapor 的專案了!

AdMob 獎勵式廣告實作

AdMob

在 Google 的廣告投放服務之中,是以 AdSense 作為主軸,整合所有廣告相關的服務;

而 AdMob 則是其中一個專門針對行動裝置的部分。

所以若 iOS 的開發者想在其應用上放置廣告單元,則需要從 AdMob 著手。

建立廣告單元

首先,需要先到 AdMob 的控制台中,新建一個新的應用程式,

並將 app 的相關資訊填寫完整。

再者,則是建立一個廣告單元,而這篇所要分享的是「獎勵式廣告」的實作。

什麼是獎勵式廣告?

獎勵式廣告的意思就如其名,是一個可以在編輯時設定,

反饋給觀看玩廣告的使用者一些獎勵的一種廣告模式;

如大部分的手機遊戲,觀看影片後可以得到一些鑽石、金幣或者特殊加成等。

設定廣告獎勵

我們可以在這個畫面中,去設定使用者觀看頻率的限制以及獎勵數量。

而在後面程式實作的時候,會講解這邊設定會影響到什麼。

Framework 安裝

你可以透過 CocoaPods 或是直接下載拉進專案之中;

這邊由於我所加入廣告的 App 有整合 Firebase 的相關服務,

而 Firebase 官方並無支援 Carthage,所以在這就一併使用 CocoaPods 來做套件的管理。

因為使用 Firebase,所以只需要在原先的 Podfile 之中加上

並且執行 pod install,即可完成安裝。

AppDelegate.swift

先在上方 import GoogleMobileAds,並需要在 FirebaseApp.configure() 之後,加上 AdMob 的廣告設定:

GADMobileAds.configure(withApplicationID: Your_Ads_ApplicationID)

這樣便可以在一開啟 app 時,和 AdMob 做連結。

獎勵式廣告的流程

獎勵式廣告和一般掛在下方的簽入式廣告不同,它的流程為

一、先和 AdMob 發送獎勵式廣告的 request

二、主動式地去詢問 AdMob 的獎勵式廣告是否就緒;

三、影片完整備看完後,它會以 delegate 的方式告知你剛剛投放的獎勵式廣告,其相關內容設定

大致上的流程就是這樣,接著回到前面所說的設定;

若你有限制使用者觀看的頻率,在觀看完後的限制時間內,會在步驟二的地方得到 false,

所以在流程上需要稍作注意。

而獎勵數量則是會在廣告結束後的 delegate 之中,以 reward 這個物件回傳回來,

它底下會有 amount 這個的參數,便是剛剛所設定的獎勵數量。