使用 NativeScript 開發(fā)實(shí)時(shí)應(yīng)用程序:實(shí)現(xiàn)推送通知

nativescript 是一個(gè)使用 xml、css 和 javascript 構(gòu)建跨平臺(tái)本機(jī)移動(dòng)應(yīng)用程序的框架。在本系列中,我們將嘗試使用 nativescript 應(yīng)用程序可以完成的一些很酷的事情:地理位置和 google 地圖集成、sqlite 數(shù)據(jù)庫、firebase 集成和推送通知。在此過程中,我們正在構(gòu)建一個(gè)具有實(shí)時(shí)功能的健身應(yīng)用程序,該應(yīng)用程序?qū)⑹褂眠@些功能。

在本教程中,您將了解使用 Firebase 云消息服務(wù)向 NativeScript 應(yīng)用添加推送通知是多么輕松。

您將要?jiǎng)?chuàng)建的內(nèi)容

繼續(xù)上一教程,您將向應(yīng)用添加推送通知。當(dāng)用戶打破當(dāng)前記錄或其中一個(gè)朋友取代他們獲得第一名時(shí),將會(huì)觸發(fā)通知。

設(shè)置項(xiàng)目

如果您已遵循之前的 Firebase 教程,則只需使用同一項(xiàng)目并構(gòu)建我們將在本教程中添加的功能即可。否則,您可以創(chuàng)建一個(gè)新項(xiàng)目并將起始文件復(fù)制到項(xiàng)目的應(yīng)用程序文件夾中。

tns create fitApp --appid "com.yourname.fitApp" 

之后,您還需要安裝地理位置、Google 地圖、SQLite 和 Firebase 插件:

tns plugin add nativescript-geolocation tns plugin add nativescript-google-maps-sdk tns plugin add nativescript-sqlite tns plugin add nativescript-plugin-firebase 

安裝后,您需要配置 Google 地圖插件。您可以閱讀之前教程中的安裝 Google 地圖插件部分,了解有關(guān)如何執(zhí)行此操作的完整說明。

接下來,安裝用于格式化日期的 fecha 庫:

npm install --save fecha 

之后,您還需要配置 Firebase 插件。請務(wù)必閱讀上一教程中的以下部分,以便您可以運(yùn)行應(yīng)用程序:

  • 運(yùn)行項(xiàng)目
  • 設(shè)置 Firebase 應(yīng)用
  • 設(shè)置 Facebook 應(yīng)用
  • 安裝 Firebase 插件
  • 配置 Facebook 集成

由于我們已經(jīng)在上一篇文章中設(shè)置了 Firebase 插件,因此只需完成少量工作即可設(shè)置推送通知。

首先,您必須進(jìn)入 node_modules/nativescript-plugin-firebase 目錄并運(yùn)行 npm run config 來重新配置插件。這次,選擇 Facebook 身份驗(yàn)證和消息傳遞。

完成后,打開項(xiàng)目根目錄中的 firebase.nativescript.json 文件,并確保 messaging 設(shè)置為 true:

{     "using_ios": false,     "using_android": true,     "remote_config": false,     "messaging": true,     "crash_reporting": false,     "storage": false,     "facebook_auth": true,     "google_auth": false,     "admob": false,     "invites": false } 

接下來,打開app/App_Resources/Android/AndroidManifest.xml并在 中添加以下服務(wù)。這將為應(yīng)用啟用 Firebase 消息傳遞服務(wù):

<application ...><service android:name="org.nativescript.plugins.firebase.MyFirebaseInstanceIDService"><intent-filter><action android:name="com.google.firebase.INSTANCE_ID_EVENT"></action></intent-filter></service><service android:name="org.nativescript.plugins.firebase.MyFirebaseMessagingService"><intent-filter><action android:name="com.google.firebase.MESSAGING_EVENT"></action></intent-filter></service></application>

運(yùn)行項(xiàng)目

可以通過執(zhí)行 tns run android 來運(yùn)行項(xiàng)目。但由于此應(yīng)用程序?qū)⒔⒃诘乩矶ㄎ还δ艿幕A(chǔ)上,我建議您使用 GPS 模擬器來快速設(shè)置和更改您的位置。您可以在之前的教程中的運(yùn)行應(yīng)用部分了解如何執(zhí)行此操作。

如果遇到任何構(gòu)建錯(cuò)誤,您可以刪除平臺(tái)并重新運(yùn)行應(yīng)用程序:

tns platform remove android tns run android 

設(shè)置 Firebase 云函數(shù)

您將使用 Firebase Cloud Functions 創(chuàng)建一個(gè)用于發(fā)送推送通知的服務(wù)器。此 Firebase 功能用于在您正在使用的 Firebase 功能中發(fā)生特定事件時(shí)運(yùn)行后端代碼 – 例如,如果實(shí)時(shí)數(shù)據(jù)庫中保存了新數(shù)據(jù),或者通過Firebase 身份驗(yàn)證服務(wù)。對于此應(yīng)用,您將使用 HTTP 觸發(fā)器在移動(dòng)應(yīng)用向特定端點(diǎn)發(fā)出請求時(shí)發(fā)送推送通知。

要使用 Firebase Cloud Functions,您首先需要全局安裝 firebase-tools?軟件包:

npm install -g firebase-tools 

接下來,創(chuàng)建一個(gè)新文件夾來存放服務(wù)器代碼。這應(yīng)該位于您的應(yīng)用程序文件夾之外。在該文件夾中,安裝 firebase-functions 軟件包:

npm install firebase-functions@latest --save 

安裝完成后,通過運(yùn)行 firebase login 登錄 Firebase。這將打開一個(gè)新的瀏覽器選項(xiàng)卡,允許您使用 Google 帳戶登錄。完成整個(gè)過程并同意所請求的所有權(quán)限。

登錄后,您現(xiàn)在可以初始化特定 Firebase 項(xiàng)目的 Firebase 函數(shù):

firebase init functions 

這將詢問您是否要設(shè)置默認(rèn)項(xiàng)目。選擇您在上一教程中創(chuàng)建的 Firebase 項(xiàng)目:

使用 NativeScript 開發(fā)實(shí)時(shí)應(yīng)用程序:實(shí)現(xiàn)推送通知

接下來,系統(tǒng)會(huì)詢問您是否要安裝依賴項(xiàng)。說是。

安裝完所有依賴項(xiàng)后,您應(yīng)該會(huì)在目錄中看到一個(gè) firebase.json 文件和一個(gè) functions 文件夾。您要處理的文件是 functions/index.js 文件。打開該文件,您將看到以下內(nèi)容:

const functions = require('firebase-functions');  // // Create and Deploy Your First Cloud Functions // // https://firebase.google.com/docs/functions/write-firebase-functions // // exports.helloWorld = functions.https.onRequest((request, response) =&gt; { //  response.send("Hello from Firebase!"); // }); 

取消注釋 helloWorld 函數(shù),您將看到正在運(yùn)行的 HTTP 觸發(fā)器。

exports.helloWorld = functions.https.onRequest((request, response) =&gt; {     response.send("Hello from Firebase!"); }); 

運(yùn)行以下命令將函數(shù)部署到云端:

firebase deploy --only functions 

部署完成后,它應(yīng)該顯示已部署該函數(shù)的 URL:

使用 NativeScript 開發(fā)實(shí)時(shí)應(yīng)用程序:實(shí)現(xiàn)推送通知

從瀏覽器訪問該網(wǎng)址即可查看輸出“Hello from Firebase!”

添加服務(wù)器代碼

現(xiàn)在您已準(zhǔn)備好添加用于實(shí)現(xiàn)推送通知的代碼。首先,您將添加服務(wù)器組件的代碼,然后添加應(yīng)用程序的代碼。

打開functions/index.js文件并清空其內(nèi)容。

創(chuàng)建 Firebase 函數(shù)

導(dǎo)入您需要的 Firebase 軟件包:

const functions = require('firebase-functions'); // for listening to http triggers const admin = require('firebase-admin'); // for accessing the realtime database admin.initializeApp(functions.config().firebase); // initialize the Firebase Admin SDK 

創(chuàng)建 init_push 函數(shù)。請注意,任何請求方法都會(huì)調(diào)用 HTTP 觸發(fā)器,因此您必須篩選要處理的請求方法。在本例中,我們只想處理 POST 請求。我們希望應(yīng)用提交 id、steps 和 friend_ids 作為請求數(shù)據(jù)。

exports.init_push = functions.https.onRequest((request, response) =&gt; {          if(request.method == 'POST'){          var id = request.body.id; // ID of the user who made the request (Firebase Auth ID)         var steps = parseInt(request.body.steps); // latest steps, not recorded yet         var friend_ids = request.body.friend_ids.split(',');           friend_ids.push(id); // also include the ID of the current user          // next: add code for getting the user and friends data     }  }); 

獲取用戶和好友數(shù)據(jù)

接下來,查詢 Firebase 數(shù)據(jù)庫以檢查用戶 ID 是否存在。這是保護(hù)端點(diǎn)的一種方式,因此并非任何人都可以觸發(fā)推送通知。 (當(dāng)然,真正的應(yīng)用程序應(yīng)該具有更好的后端安全性,以便用戶無法欺騙自己的數(shù)據(jù)或其他人的數(shù)據(jù)。)

如果用戶確實(shí)存在,則再次查詢數(shù)據(jù)庫,以便返回所有用戶。請注意,F(xiàn)irebase 目前不提供基于 ID 數(shù)組返回記錄的方法,因此我們必須自己過濾相關(guān)數(shù)據(jù):

admin.database().ref('/users')     .orderByChild('id')     .limitToFirst(1)     .equalTo(id)     .once('value').then(snapshot =&gt; {          var user_data = snapshot.val();      if(user_data){         // get all users from the database         admin.database().ref('/users')             .once('value').then(snapshot =&gt; {             // next: add code for getting the current user's data and their friends data         });     } }); 

接下來,循環(huán)遍歷從 Firebase 返回的結(jié)果并創(chuàng)建一個(gè)新數(shù)組來容納 friends_data。完成此操作后,根據(jù)每個(gè)用戶的步數(shù)對數(shù)組進(jìn)行排序。步驟數(shù)最多的索引為第一個(gè)索引。

var friends_data = []; var current_user_data = null; var notification_data = {}; var has_notification = false;  var users = snapshot.val(); for(var key in users){     var user_id = users[key].id;          if(friend_ids.indexOf(user_id) != -1 &amp;&amp; id != user_id){ // the current user's friends         friends_data.push(users[key]);     }else if(id == user_id){ // the current user         current_user_data = users[key];     } }  // sort in descending order by the number of steps var sorted_friends_data = friends_data.sort(function(a, b) {     return b.steps - a.steps; });   // next: add code for constructing the notification payload 

構(gòu)造通知負(fù)載

現(xiàn)在我們準(zhǔn)備好確定誰將接收通知并構(gòu)建通知負(fù)載。誰是第一名?是當(dāng)前用戶還是用戶的朋友之一?由于當(dāng)前用戶在打破第一名的總記錄時(shí)也打破了自己的記錄,因此我們只需要檢查該記錄是否已被打破。

if(steps &gt; sorted_friends_data[0].steps){     // notify friend who was overtaken     var diff_steps = steps - sorted_friends_data[0].steps;     notification_data = {         payload: {             title: 'One of your friends beat your record',             body: 'Too bad, your friend ' + current_user_data.user_name + ' just overtook you by ' + diff_steps + ' steps'         },         device_token: sorted_friends_data[0].device_token     };     has_notification = true;      }else if(steps &gt; current_user_data.steps){     // notify current user     var diff_steps = steps - current_user_data.steps;     notification_data = {         payload: {             title: 'You beat your record!',             body: 'Congrats! You beat your current record by ' + diff_steps + ' steps!'          },         device_token: current_user_data.device_token     };     has_notification = true; }  // next: add code for sending push notification 

發(fā)送通知

最后發(fā)出通知:

if(has_notification){          var payload = {       notification: notification_data.payload     };          // send push notification     admin.messaging().sendToDevice(notification_data.device_token, payload).then(function(res) {                  response.send(JSON.stringify({'has_notification': true})); // inform the app that a notification was sent     })     .catch(function(error) {         response.send(JSON.stringify(error)); // send the push notification error to the app     });  }else{     response.send(JSON.stringify({'has_notification': false})); // inform the app that a notification was not sent } 

更新應(yīng)用代碼

之前,您設(shè)置了應(yīng)用程序,以便它能夠接收推送通知。這次,您將添加代碼,以便您的應(yīng)用程序可以處理這些推送通知并將其顯示給用戶。

接收推送通知

為了接收推送通知,您需要做的第一件事是更新 firebase.init() 函數(shù)以包含用于接收設(shè)備令牌的偵聽器:

onPushTokenReceivedCallback: function(token) {     // temporarily save it to application settings until such time that      // the user logs in for the first time     applicationSettings.setString('device_token', token); }, 

此函數(shù)僅執(zhí)行一次,因此您必須使用應(yīng)用程序設(shè)置在本地保存令牌。稍后,這將使我們能夠在用戶首次登錄時(shí)獲取設(shè)備令牌。如果您還記得上一教程,我們會(huì)在用戶首次登錄時(shí)將其數(shù)據(jù)保存到 Firebase。

接下來,您可以添加接收通知時(shí)的偵聽器。這將顯示一個(gè)警告框,其中使用消息的標(biāo)題和正文作為內(nèi)容:

onMessageReceivedCallback: function(message) {     dialogs.alert({         title: message.title,         message: message.body,         okButtonText: "ok"     }); }, 

將設(shè)備令牌保存到 Firebase

Firebase Cloud Messaging 在向特定設(shè)備發(fā)送推送通知時(shí)需要設(shè)備令牌。由于我們已經(jīng)在使用 Firebase,因此我們只需將設(shè)備令牌與用戶數(shù)據(jù)一起保存。為此,您需要編輯用于保存用戶數(shù)據(jù)的代碼以包含我們之前獲得的設(shè)備令牌:

if(firebase_result.value == null){       var device_token = applicationSettings.getString('device_token');      var user_data = {         'uid': fb_result.uid,         'user_name': fb_result.name,         'profile_photo': fb_result.profileImageURL,         'device_token': device_token      };  } 

觸發(fā)推送通知

當(dāng)發(fā)生以下兩種情況之一時(shí),將觸發(fā)推送通知:

  • 當(dāng)用戶打破當(dāng)前記錄時(shí)
  • 當(dāng)用戶的一個(gè)朋友打破記錄并獲得第一名時(shí)

第一個(gè)很簡單,因此實(shí)際上不需要額外的設(shè)置。但對于第二個(gè),你需要做一些工作。首先,您必須編輯身份驗(yàn)證狀態(tài)更改時(shí)的代碼。從 Facebook 結(jié)果中提取好友 ID 后,您必須使用應(yīng)用程序設(shè)置保存好友 ID。

// extracting the friend IDs from the Facebook result var friends_ids = r.data.map(function(obj){     return obj.id; });  // save the friend IDs applicationSettings.setString('friends_ids', JSON.stringify(friends_ids));  friends_ids.push(user[user_key].id); 

接下來,更新用戶何時(shí)停止跟蹤其步行的代碼。在構(gòu)建更新用戶的用戶數(shù)據(jù)的代碼之后,從應(yīng)用程序設(shè)置中獲取好友 ID,并將其包含在包含觸發(fā)推送通知的請求數(shù)據(jù)的對象中。

// construct the user data for updating the user's distance and steps var user_key = applicationSettings.getString('user_key'); var user = applicationSettings.getString('user'); var user_data = JSON.parse(user); user_data[user_key].distance = total_distance; user_data[user_key].steps = total_steps;  // get friend IDs var friends_ids = JSON.parse(applicationSettings.getString('friends_ids'));  var request_data = {     'id': user_data[user_key].id,     'friend_ids': friends_ids.join(','),     'steps': total_steps }; 

向您之前創(chuàng)建的 Firebase Cloud Functions 端點(diǎn)發(fā)出請求。一旦返回成功響應(yīng),用戶的數(shù)據(jù)才會(huì)在 Firebase 數(shù)據(jù)庫上更新。

http.request({      url: "https://us-central1-pushapp-ab621.cloudfunctions.net/init_push",      method: "POST",     headers: { "Content-Type": "application/json" },     content: JSON.stringify(request_data)  }).then(function (response) {         var statusCode = response.statusCode;     if(statusCode == 200){         // update the user's data on Firebase         firebase.update(             '/users',             user_data         );       }  }, function (e) {     console.log('Error occurred while initiating push: ', e); }); 

測試推送通知

您可以通過首先從模擬器或設(shè)備中卸載應(yīng)用程序來測試推送通知的發(fā)送。這使得我們能夠正確觸發(fā)獲取設(shè)備令牌的函數(shù)。請務(wù)必添加 console.log 以輸出設(shè)備令牌:

onPushTokenReceivedCallback: function(token) {     applicationSettings.setString('device_token', token);     console.log('device token: ', device_token); //  <p>當(dāng)設(shè)備令牌在 NativeScript 控制臺(tái)中輸出時(shí),將其復(fù)制,點(diǎn)擊 Firebase 應(yīng)用信息中心上的<strong>數(shù)據(jù)庫</strong>菜單,并將其作為設(shè)備令牌添加給應(yīng)用的所有用戶。使用 device_token 作為屬性名稱。</p> <p>要觸發(fā)推送通知,您可以使用 curl 向 Firebase 函數(shù)端點(diǎn)發(fā)出 POST 請求:</p> <pre class="brush:plaintext;toolbal:false;">curl -X POST -H "Content-Type:application/json" YOUR_FIREBASE_FUNCTION_ENDPOINT -d '{"id":"ID OF A FIREBASE USER", "steps":NUMBER_OF_STEPS, "friend_ids":"COMMA,SEPARATED,FIREBASE,USER_IDs"}' 

如果您沒有安裝curl,您可以使用Postman應(yīng)用發(fā)送請求。對請求使用以下設(shè)置:

  • 請求方法:?POST
  • 網(wǎng)址:您的 Firebase 函數(shù)端點(diǎn)
  • 標(biāo)頭鍵:?內(nèi)容類型
  • 標(biāo)頭值:?application/json
  • 正文:?
{"id":"ID OF A FIREBASE USER", "steps":NUMBER_OF_STEPS, "friend_ids":"COMMA,SEPARATED,FIREBASE,USER_IDs"} 

觸發(fā)后,您將看到類似于以下內(nèi)容的輸出:

使用 NativeScript 開發(fā)實(shí)時(shí)應(yīng)用程序:實(shí)現(xiàn)推送通知

如果應(yīng)用當(dāng)前未打開,您將在通知區(qū)域中看到通知:

使用 NativeScript 開發(fā)實(shí)時(shí)應(yīng)用程序:實(shí)現(xiàn)推送通知

結(jié)論

恭喜!您終于完成了健身應(yīng)用程序。在四個(gè)教程的過程中,您已經(jīng)構(gòu)建了一個(gè)使用 Google 地圖、SQLite、Firebase 實(shí)時(shí)數(shù)據(jù)庫和 Firebase 云消息傳遞的 NativeScript 應(yīng)用。現(xiàn)在您已經(jīng)為構(gòu)建使用這些技術(shù)的 NativeScript 應(yīng)用程序奠定了良好的基礎(chǔ)。

要了解有關(guān) NativeScript 或其他跨平臺(tái)移動(dòng)技術(shù)的更多信息,請務(wù)必查看我們在 Envato Tuts+ 上的其他一些課程和教程!

? 版權(quán)聲明
THE END
喜歡就支持一下吧
點(diǎn)贊12 分享