nativescript致力于使用 XML、 css和javascript构建跨平台的原生移动应用程序。在本系列中,我们将尝试使用 NativeScript 应用程序可以做的一些很酷的事情:地理定位和 Google 地图集成、sqlite 数据库、firebase 集成和推送通知。在此过程中,我们正在构建一个具有实时功能的健身应用程序,该应用程序将使用这些功能中的每一个。
在本教程中,您将学习如何将 Facebook 登录添加到您的 NativeScript 应用程序。您还将学习如何使用 Firebase 在健身应用中存储步行数据。
你将要创造什么
从上一个教程开始,现在让我们添加社交标签的内容。默认情况下,使用 Facebook 登录的按钮显示如下:
当用户第一次登录时,Facebook 应用程序会请求访问公共个人资料和电子邮件地址的权限:
它还要求朋友列表作为附加权限。
用户登录后,将显示以下屏幕:
这是显示当前登录用户的信息和步行会话的排行榜的地方。请注意,仅记录最近的步行会话。
设置项目
如果您已阅读本系列的上一篇文章,在 sqlite 上,您可以简单地使用相同的项目并构建我们将在本教程中添加的功能。否则,您可以创建一个新项目并将启动文件复制到项目的app 文件夹中。
tns create fitApp --appid "com.yourname.fitApp"
之后,您还需要安装地理定位、谷歌地图和 SQLite 插件:
tns plugin add nativescript-geolocation tns plugin add nativescript-google-maps-sdk tns plugin add nativescript-sqlite
安装后,您需要配置 Google Maps 插件。您可以通过阅读早期教程 中 的安装 Google 地图插件部分来阅读有关如何执行此操作的完整说明。
之后,您还需要安装fecha,一个用于格式化日期的库:
npm install --save fecha
完成所有这些后,您应该准备好学习本教程。
运行项目
您可以通过执行来运行项目。但由于此应用程序将使用地理定位功能,我建议您使用 GPS 模拟器来快速设置和更改您的位置。您可以在前面教程的运行应用程序部分了解如何执行此操作。tns run android
设置 Firebase 应用
使用 Firebase 时需要做的第一件事是创建一个 Firebase 应用。您可以通过访问console.firebase.com 并单击Add project来做到这一点。输入项目名称,然后单击“创建项目” 按钮。确保项目的名称与应用程序的名称相同。在这种情况下,应用程序 ID 是com.yourname.fitApp
所以应用程序的名称是fitApp
。
创建应用程序后,您将被重定向到应用程序的仪表板页面。从那里您可以点击Add Firebase to your Android app,输入应用 ID,然后点击Register App 按钮。
接下来,下载google-services.json 文件并将其复制到app/App_Resources/android 目录。该文件包含应用与 Firebase 通信所需的所有设置。
Firebase 仪表板中所述的下一步是包含 Firebase SDK。但这已经在插件中完成了,所以我们不再需要这样做。
设置 Facebook 应用程序
由于我们要使用 Facebook 登录,我们还需要创建一个 Facebook 应用程序。转到developers.facebook.com 并创建一个新的 Facebook 应用程序:
创建应用程序后,您将被重定向到应用程序仪表板。从那里,单击+ 添加产品 菜单并选择Facebook 登录。
在Client oauth Settings下,启用除Force Web OAuth Reauthentication 和Login from Devices之外的所有内容。对于Valid OAuth redirect URIs,您可以通过返回 Firebase 仪表板,单击Authentication并启用 Facebook 作为身份验证方法来获取它:
在启用它之前,您必须输入 Facebook 应用 ID 和应用密钥。您可以从之前创建的 Facebook 应用程序的仪表板中获取该信息。
完成后,单击保存 并将OAuth 重定向 URI复制 到 Facebook 应用程序设置。不要忘记保存更改。
接下来,您还需要将 Android 添加为平台。您可以通过转到基本 设置并单击添加平台来做到这一点:
将com.yourname.fitApp设置 为 Google Play Package Name 的值,并将 com.tns.NativeScriptActivity 设置为 Class Name。
请注意,如果您打算稍后将应用程序发布到应用商店,则需要为应用程序的.apk 文件生成一个哈希并将其添加到key hashes 字段下。另请注意,为了进行测试,您将只能使用您用于创建应用程序的 Facebook 开发人员帐户。如果要添加其他 Facebook 帐户进行测试,可以在oles下添加。
安装 Firebase 插件
为了集成 Firebase,我们需要为 NativeScript 安装 Firebase 插件。这使得在 Firebase 中实现 Facebook 登录和实时数据库功能变得更加容易:
tns plugin add nativescript-plugin-firebase
安装完成后,安装程序会询问您一些有关您将在应用程序中使用的 Firebase 功能的问题。对 Facebook 登录回答“是”, 对其他人回答“否” 。
配置 Facebook 集成
您需要让应用知道它要与哪个 Facebook 应用对话。您可以通过打开app\App_Resources\Android\AndroidManifest.xml 文件并 <meta-data android:name="com.facebook.sdk.ApplicationId" android:value="@string/facebook_app_id"/>
在<application>
标签下添加来做到这一点:
<application android:name="com.tns.NativeScriptApplication" android:allowBackup="true" android:icon="@drawable/icon" android:label="@string/app_name" android:theme="@style/AppTheme"> <!-- add this: --> <meta-data android:name="com.facebook.sdk.ApplicationId" android:value="@string/facebook_app_id"/> <!-- other stuff --> </application>
接下来,创建一个 app\App_Resources\Android\values\facebooklogin.xml 文件并添加以下内容:
<?xml version='1.0' encoding='utf-8'?> <resources> <string name="facebook_app_id">YOUR_FACEBOOK_APP_ID</string> </resources>
请务必替换 YOUR_FACEBOOK_APP_ID
为您之前创建的 Facebook 应用的应用 ID。
解决构建错误
如果您在安装插件后遇到构建错误,请务必检查存储库自述文件中的Android 上的已知问题部分。如果您的具体问题不存在,请尝试浏览问题页面。
至于我,我遇到的主要问题是与Google Maps 插件的兼容性问题。由于该插件还使用 Google Play 服务,因此使用的不同版本存在冲突。为了解决这个问题,我必须打开app/App_Resources/Android/app.gradle 文件并指定要使用的 Google Play 服务版本:
android { //default config here project.ext { googlePlayServicesVersion = "11.0.+" } }
在编写本教程时,它是 11.0。但请务必通过 Android SDK 检查当前为您安装的版本。
完成后,您必须卸载 android 平台 ( tns platform remove android
) 并尝试再次运行该应用程序 ( tns run android
)。
如果这对您不起作用并且您仍然遇到相同的构建错误,您可能需要通过创建一个新项目重新开始。但这一次,请尝试在 Google Maps 插件之前安装 Firebase 插件。然后在尝试运行应用程序之前进行必要的配置更改。
添加代码
现在我们准备添加代码。我们将首先添加 XML,然后是 JavaScript,最后是 CSS 代码。
添加 UI 标记
我们将主要在社交标签视图中工作。首先,添加用于显示当前登录用户信息的标记以及用于注销的按钮:
<StackLayout width="140" class="profile p-20" visibility="{{ is_loggedin ? 'visible' : 'collapsed' }}"> <Image src="{{ profile_photo }}" stretch="aspectFit" /> <Label text="{{ user_name }}" textWrap="true" /> <Button text="Logout" tap="{{ logout }}" class="btn-logout" /> </StackLayout>
下面是显示排行榜的标记。这 friends_data
会循环显示用户的朋友和用户的用户名、距离和步数。
<StackLayout visibility="{{ is_loggedin && friends_data ? 'visible' : 'collapsed' }}"> <Label text="Leaderboard" class="heading" textWrap="true" /> <GridLayout columns="2*,*,*" rows="auto" class="item item-header"> <Label text="Name" textWrap="true" row="0" col="0"/> <Label text="Distance" textWrap="true" row="0" col="1" /> <Label text="Steps" textWrap="true" row="0" col="2" /> </GridLayout> <ListView items="{{ friends_data }}"> <ListView.itemTemplate> <GridLayout columns="2*,*,*" rows="auto" class="item item-row"> <Label text="{{ user_name }}" textWrap="true" row="0" col="0"/> <Label text="{{ distance }}" textWrap="true" row="0" col="1" /> <Label text="{{ steps }}" textWrap="true" row="0" col="2" /> </GridLayout> </ListView.itemTemplate> </ListView> </StackLayout>
如果当前没有用户登录,我们会显示用于登录 Facebook 的按钮:
<StackLayout class="p-20"> <Button text="Login with Facebook" tap="{{ loginFacebook }}" class="btn-facebook" visibility="{{ is_loggedin ? 'collapsed' : 'visible' }}" /> </StackLayout>
导入库
打开main-view-model.js 文件,在导入 fecha 库的代码下方添加以下内容:
var fecha = require('fecha'); var firebase = require("nativescript-plugin-firebase"); var http = require("http"); var applicationSettings = require("application-settings");
我们nativescript-plugin-firebase
用来与 Firebase 对话,http
向 Facebook 的 Graph api发出 HTTP 请求,并application-settings
保存用户的登录数据。
初始化 Firebase
接下来,使用该init()
函数初始化 Firebase。这接受一个对象,该对象包含 Firebase 支持的不同功能的选项(例如身份验证、实时数据库、云消息传递)。
下面,我们添加了一个persist
选项,它使 Firebase 将数据保存在本地,这样应用程序在离线时仍然可以使用。稍后,我们将为身份验证状态更改(用户登录或注销应用程序时)添加监听器。
firebase.init({ persist: true, // add later: code for listening when auth status changes }).then( function(instance){ console.log("firebase.init done"); }, function(error){ console.log("firebase.init error: " + error); } );
登录用户
接下来,添加当用户点击登录 Facebook 按钮时将执行的代码。这使用该login
函数,该函数接受一个包含type
and 的对象facebookOptions
。
type
是用于登录的身份验证方法。在本例中,它是 Facebook 。facebookOptions
是一个包含名为 的数组的对象 scope
。此数组的元素是 您希望应用程序向用户请求的权限 。
一旦用户登录并同意所请求的所有权限,promise 就会解析并执行传递给then()
. Facebook 用户详细信息作为参数传递给此函数,但我们唯一需要的是访问令牌。我们稍后可以使用它向 Facebook 的 Graph API 发出请求,以获取更多信息。
viewModel.loginFacebook = function() { firebase.login({ type: firebase.LoginType.FACEBOOK, facebookOptions: { scope: ['public_profile', 'email', 'user_friends'] } }).then( function(fb_result){ var fb_access_token = fb_result.providers[1].token; // next: add code for checking if user is new or not }, function (err) { console.log('error logging in to facebook: ', err); } ); }
接下来,我们将向 Firebase 数据库发出查询,以检查用户是否已经存在。为此,我们使用query()
方法。当响应作为第一个参数返回时,它接受要执行的函数。第二个参数是执行查询的路径,第三个参数是查询本身。
如果用户已经存在,query()
将返回用户的数据。然后我们使用应用程序设置将数据保存在本地。稍后当我们***身份验证状态更改以及在 Firebase 上更新用户的最新步行会话时,我们将需要访问这些数据。
firebase.query( function(firebase_result){ if(!firebase_result.error){ if(firebase_result.value == null){ //user doesn't exist yet //next: add code for saving the data for new user }else{ // user already exists for(var user_key in firebase_result.value){ // save user's data locally applicationSettings.setString('user_key', user_key); applicationSettings.setString('user', JSON.stringify(firebase_result.value)); applicationSettings.setString('fb_token', fb_access_token); } } } }, '/users', { singleevent: true, // for checking if the value exists (return the whole data) orderBy: { // the property in each of the objects in which to perform the query type: firebase.QueryOrderByType.CHILD, value: 'uid' }, range: { // the comparison operator type: firebase.QueryRangeType.EQUAL_TO, value: fb_result.uid }, limit: { // limit to only return the first result type: firebase.QueryLimitType.FIRST, value: 1 } } );
创建新用户
现在让我们添加用于为新用户保存数据的代码。首先创建包含用户数据的对象。然后向 Facebook 的 Graph API 发出请求以获取用户的 Facebook ID(仅对该特定应用有效)。
稍后,我们将使用此 ID 来检查特定 Firebase 用户是否是当前用户的朋友。Firebase 不会在您登录时返回此 ID,这就是我们需要单独请求的原因。
返回响应后,我们将使用 Firebase 的push()
方法将用户的数据保存在/users
路径中。这将返回用作此特定用户 ID 的密钥。我们稍后将使用它来更新用户的最后一次步行会话。这就是为什么我们还需要将其与用户数据和 Facebook 访问令牌一起保存在本地。
var user_data = { 'uid': fb_result.uid, 'user_name': fb_result.name, 'profile_photo': fb_result.profileImageURL }; http.getJSON('https://graph.facebook.com/me?access_token=' + fb_access_token) .then(function(r){ user_data.id = r.id; // facebook user ID for this specific app // create new user firebase.push( '/users', user_data ).then( function (result) { var user = {}; user[result.key] = user_data; // the key is the property containing the user's data // store user's data locally applicationSettings.setString('user_key', result.key); applicationSettings.setString('user', JSON.stringify(user)); applicationSettings.setString('fb_token', fb_access_token); } ); });
现在我们已经添加了用于登录用户的代码,下一步是返回 firebase.init()
调用,并添加onAuthStateChanged
. 每次身份验证状态更改(当用户登录或注销时)时,都会执行此函数。如果用户已登录,我们希望更新 UI 以显示当前用户。
请注意,我们将其包装在setTimeout()
5 秒延迟内,因为登录后用户数据(Firebase 用户密钥、Firebase 用户和 Facebook 访问令牌)需要几秒钟才能可用。
persist: true, onAuthStateChanged: function (data) { if(data.loggedIn){ // is a user logged in? setTimeout(function(){ // update UI to show the current user viewModel.set('is_loggedin', true); viewModel.set('profile_photo', data.user.profileImageURL); viewModel.set('user_name', data.user.name); // next: add code for getting friends list }, 5000); } }
接下来,我们添加获取用户好友的代码。图形 API 返回用户每个朋友的 ID 和名称,但我们只需要 ID。我们还需要推送当前用户的 ID,因为我们也将在排行榜中显示它。
var user_key = applicationSettings.getString('user_key'); var user = JSON.parse(applicationSettings.getString('user')); var fb_token = applicationSettings.getString('fb_token'); http.getJSON('https://graph.facebook.com/me/friends?access_token=' + fb_token) .then(function (r) { // extract the ID's var friends_ids = r.data.map(function(obj){ return obj.id; }); friends_ids.push(user[user_key].id); // also push the ID for the current user // next: add code for listening for changes in the database }, function(e){ console.log('err: ', e); });
显示排行榜
接下来,添加用于***数据库更改的代码。到目前为止,我们还没有真正实现这个应用程序的“实时”部分。这是我们最终添加它的时候。
为此,我们使用addValueEventListener()
方法。当您指定为第二个参数的路径发生更改时,这将接受您要执行的函数。整个值 ( result
) 作为参数传入此函数。
确实没有指定查询以仅按特定 ID 过滤结果的功能。因此,使用朋友 ID 数组 ( friends_ids
),我们遍历result
并检查当前行是当前用户还是他们的朋友之一。只有这样我们才会推送当前行的值。从那里,我们只需对数据进行排序和格式化以在 UI 中显示。
firebase.addValueEventListener(function(result){ var friends_data = []; for(var row in result.value){ var friend = result.value[row]; // check if the current row is the current user or one of their friends if(friends_ids.indexOf(friend.id) !== -1){ friends_data.push(result.value[row]); } } // sort data in descending order of the steps var sorted_friends_data = friends_data.sort(function(a, b) { return b.steps - a.steps; }); // format the data for displaying var formatted_sorted_friends_data = sorted_friends_data.map(function(obj, key){ var updated_obj = obj; updated_obj.distance = commafy(obj.distance) + 'm'; updated_obj.steps = commafy(obj.steps); return updated_obj; }); // update the UI viewModel.set('friends_data', formatted_sorted_friends_data); }, "/users");
更新最新的步行课程
当用户停止跟踪他们当前的位置时,我们会 在 Firebase 上更新用户的distance
和:steps
viewModel.set('walks', walks); if(walks.length > 0){ viewModel.set('has_walks', true); } 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; // update user's data firebase.update( '/users', user_data );
注销用户
接下来,添加用于注销用户的代码。这会将 UI 重置为用户未登录的状态,并且还会清除本地数据。
viewModel.logout = function() { firebase.logout(); viewModel.set('is_loggedin', false); viewModel.set('profile_photo', null); viewModel.set('user_name', null); applicationSettings.clear(); }
添加样式
最后,打开app/app.css 文件并在现有代码下方添加以下内容:
.btn-facebook { background-color: #365899; color: #fff; } .btn-logout { background-color: red; color: #fff; } .profile { text-align: center; }
结论
而已!在本教程中,您学习了如何将 Facebook 登录和 Firebase 集成到 NativeScript 应用程序中。正如您在 NativeScript Firebase 插件的文档中可能已经注意到的那样,您实际上可以使用这个插件做更多的事情。事实上,我们将使用它的云消息传递功能来实现这个应用程序的最后一个功能:推送通知。所以请继续关注!
- 解决构建错误
- 添加 UI 标记
- 导入库
- 初始化 Firebase
- 登录用户
- 创建新用户
- 显示排行榜
- 更新最新的步行课程
- 注销用户
- 添加样式