NativeScript是用于使用XML,CSS和JavaScript构建跨平台本机移动应用程序的框架。 在本系列中,我们正在尝试使用NativeScript应用程序可以执行的一些很酷的操作:地理位置和Google Maps集成,SQLite数据库,Firebase集成和推送通知。 在此过程中,我们正在构建一个具有实时功能的健身应用程序,它将使用这些功能中的每一个。
在本教程中,您将学习如何将Facebook登录名添加到NativeScript应用程序。 您还将学习如何使用Firebase在Fitness应用程序中存储步行会议数据。
您将要创造的
从上一教程中挑选出来,现在让我们添加社交标签的内容。 默认情况下,显示用于登录Facebook的按钮,如下所示:
当用户首次登录时,Facebook应用程序会请求访问公共资料和电子邮件地址的权限:
它还要求朋友列表作为添加的权限。
用户登录后,将显示以下屏幕:
在此处显示当前登录用户的信息和步行会话的排行榜。 请注意,仅记录了最新的步行会话。
设置项目
如果您已关注本系列的上一篇文章 ,有关SQLite的信息,则只需使用相同的项目并构建我们将在本教程中添加的功能。 否则,您可以创建一个新项目,并将启动 程序文件复制到项目的应用程序文件夹中。
tns create fitApp --appid "com.yourname.fitApp"
之后,您还需要安装地理位置,Google Maps和SQLite插件:
tns plugin add nativescript-geolocation
tns plugin add nativescript-google-maps-sdk
tns plugin add nativescript-sqlite
安装后,您需要配置Google Maps插件。 您可以通过阅读本早期教程中有关安装Google Maps插件的部分来阅读有关如何执行此操作的完整说明。
之后,您还需要安装fecha (用于格式化日期的库):
npm install --save fecha
完成所有步骤后,您应该准备继续本教程。
运行项目
您可以通过执行tns run android
来运行项目。 但是由于该应用程序将使用地理定位功能,因此我建议您使用GPS模拟器来快速设置和更改位置。 您可以在之前的教程中的“ 运行应用程序 ”部分中了解有关操作方法的信息。
设置Firebase应用
使用Firebase时,您要做的第一件事是创建Firebase应用程序。 您可以通过转到console.firebase.com并单击Add project来实现 。 输入项目名称,并单击C reate项目按钮。 确保项目名称与应用程序名称相同。 在这种情况下,应用程序ID为com.yourname.fitApp
因此应用程序的名称为fitApp
。
创建应用程序后,您将被重定向到应用程序的仪表板页面。 在此处,您可以单击将Firebase添加到您的Android应用程序 ,输入应用程序ID,然后单击注册应用程序按钮。
接下来,下载google-services.json文件并将其复制到app / App_Resources / android目录。 该文件包含应用程序与Firebase通信所需的所有设置。
Firebase仪表板中所述的下一步是包括Firebase SDK。 但这已在插件中完成,因此我们不再需要这样做。
设置Facebook应用
由于我们将使用Facebook登录,因此我们还需要创建一个Facebook应用。 转到developers.facebook.com并创建一个新的Facebook应用程序:
创建应用程序后,您将被重定向到应用程序仪表板。 在此处,点击+添加产品菜单,然后选择Facebook登录 。
在“ 客户端 OAuth设置”下 ,启用“ 强制Web OAuth重新认证和从设备登录”以外的所有功能。 对于有效的OAuth重定向URI ,您可以通过返回Firebase仪表板,单击Authentication ,然后将Facebook作为身份验证方法来实现:
在启用它之前,您必须输入Facebook应用程序ID和应用程序密钥。 您可以从您先前创建的Facebook应用程序的仪表板上获取该信息。
完成后,单击“ 保存”,然后将OAuth重定向URI复制到Facebook应用程序设置中。 不要忘记保存更改。
接下来,您还需要将Android添加为平台。 您可以转到“ 基本设置”,然后单击“ 添加平台” :
将com.yourname.fitApp设置为Google Play包名称的值,并将com.tns.NativeScriptActivity设置为类名称。
请注意,如果以后要将该应用发布到应用商店,则需要为该应用的.apk文件生成一个哈希,并将其添加到关键哈希字段下。 还要注意,为了进行测试,您将只能使用创建应用程序时使用的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
和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);
}
);
});
现在,我们已经添加了用于登录用户的代码,下一步是返回到onAuthStateChanged
firebase.init()
调用,并添加onAuthStateChanged
。 每次身份验证状态更改时(用户登录或注销时),该函数都会执行。 如果用户已登录,我们想更新用户界面以显示当前用户。
请注意,我们将其包装在setTimeout()
,具有五秒钟的延迟,因为登录后需要几秒钟才能使用户数据(Firebase用户密钥,Firebase用户和Facebook Access令牌)可用。
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);
}
}
接下来,我们添加获取用户朋友的代码。 graph 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插件的文档中可能已经注意到的那样,实际上您可以使用该插件做更多的事情。 实际上,我们将使用其云消息传递功能来实现此应用程序的最后一个功能:推送通知。 因此,请继续关注!