在我的《 使用Expo更轻松地进行React Native开发》一文中 ,您了解了Expo如何使初学者更轻松地开始使用React Native创建应用。 您还了解到,Expo使开发人员可以更快地启动和运行React Native应用程序,因为不再需要设置Android Studio,Xcode或其他开发工具。
但是,您也已经看到,Expo不支持应用程序可能需要的所有本机功能。 尽管Expo团队一直在努力支持更多的本机功能 ,但是最好学习如何将现有的Expo项目转换为标准的本机项目,以便在需要时可以轻松进行过渡。
拆卸到ExpoKit
为了分离到ExpoKit,首先必须编辑app.json和package.json文件。
在app.json文件中,确保已设置name
。 platforms
应该是您要构建的平台。
{
"expo": {
"name": "ocdmom",
"platforms": [
"ios",
"android"
],
如果要针对iOS构建,则必须指定ios
选项:
"ios": {
"bundleIdentifier": "com.ocd.mom"
},
如果要支持Android,请同时指定以下选项:
"android": {
"package": "com.ocd.mom"
}
创建项目时, exp
命令行工具会预填充其他选项。 但是,唯一重要的是iOS的bundleIdentifier
和Android的package
。 这些应用发布到Apple或Play商店后,将成为该应用的唯一ID。 分离需要这些细节,因为分离实际上会为要在设备上运行的应用程序生成本机代码。 您可以在 Expo文档中找到有关app.json文件的不同配置选项的更多信息。
您可以在GitHub存储库中查看文件的完整内容 。
接下来,打开package.json文件并添加项目名称:
"name": "ocdmom"
这应该是使用exp init
创建项目时使用的名称。 这一点非常重要,因为它们在编译应用程序时会使用您在package.json中指定的name
。 此名称不一致将导致错误。
现在,我们准备分离到ExpoKit。 在项目目录的根目录下执行以下命令:
exp detach
这将在本地下载适用于Android和iOS的本地Expo软件包。
如果成功,您应该看到类似于以下的输出:
如果要部署到iOS,则需要安装最新版本的Xcode。 在撰写本教程时,最新版本为9。接下来,通过执行sudo gem install cocoapods
来安装CocoaPods。 这使您可以安装项目的本机iOS依赖项。 完成后,导航到项目的ios目录并执行pod install
来安装所有本机依赖项。
安装自定义本机软件包
现在我们已经分离了,我们现在可以像在标准React Native项目中一样安装本地软件包。
对于此应用,我们需要React Native Background Timer和Pusher软件包。
首先,安装Pusher软件包,因为它更容易:
npm install --save pusher-js
这使我们可以与您之前创建的Pusher应用进行通信。
接下来,安装React Native Background Timer。 这使我们能够根据特定的时间间隔定期执行代码(即使应用程序在后台):
npm install --save react-native-background-timer
与Pusher软件包不同,这需要将本机库(iOS或Android)链接到应用程序。 执行以下命令可为您完成此操作:
react-native link
完成后,还应该在android / app / src / main / host / exp / exponent / MainApplication.java上初始化模块。 但只是要确保检查该文件中是否存在以下内容:
import com.ocetnik.timer.BackgroundTimerPackage; // check this
public List getPackages() {
return Arrays.asList(
new BackgroundTimerPackage() // also check this
);
}
如果要为iOS构建, 请在ios目录中打开Podfile ,并确保在post_install
声明之前添加了以下内容:
pod 'react-native-background-timer', :path => '../node_modules/react-native-background-timer'
完成后,在ios
目录中执行pod install
来安装本机模块。
对于Android,当您使用Android Studio运行应用程序时,此操作已自动完成。
更新Android清单文件
如果要针对Android进行构建,请打开Android清单文件( android / app / src / main / AndroidManifest.xml ),并确保添加了以下权限:
这使我们可以请求允许Pusher访问Internet和Expo,以获取用户在Android设备上的当前位置。
运行应用
我们尚未完成,但是最好立即运行该应用程序,这样您就可以查看它是否有效。 这样,您在开发应用程序时还可以看到更改。
运行该应用程序的第一步是从项目的根目录执行exp start
。 这将启动开发服务器,以便您对源代码所做的任何更改都将反映在应用程序预览中。
如果您要针对Android进行构建,请打开Android Studio,然后选择打开现有的Android Studio项目 。 在显示的目录选择器中,选择Expo项目内的android文件夹。 选择文件夹后,它应该为该文件夹中的文件建立索引。 此时,您现在应该能够通过从顶部菜单中选择“ 构建”>“重建项目”来重建项目 。 完成后,通过选择Run> Run'app'来运行应用程序 。
Android Studio可以在连接到计算机的任何Android设备上,通过Android Studio或Genymotion安装的其中一个模拟器上运行该应用程序(Android Studio自动检测正在运行的模拟器实例)。 对于此应用程序,我建议您使用Genymotion模拟器,因为它具有一个不错的GPS仿真小部件,可让您通过Google Maps界面更改位置:
(如果您有运行在设备上的应用程序的问题,一定要看看这个 堆栈溢出 的问题上取得的Android Studio能够识别您的设备 。)
完成后,使用Xcode打开ios / ocdmom .xcworkspace文件。 Xcode将文件编入索引后,您应该可以单击该大播放按钮,它将自动在您选择的iOS模拟器上运行该应用程序。
Xcode还允许您模拟位置,但是仅当您构建用于在模拟器中运行的应用程序时。 更改代码并让开发服务器刷新应用程序实际上不会更改位置。 要更改位置,请单击发送图标,然后选择要使用的位置:
继续编写应用程式
现在,我们准备继续为该应用程序编写代码。 这次,我们将添加在后台运行应用程序时运行一些代码的功能。
添加后台任务
导入先前安装的Pusher and Background Timer程序包:
import BackgroundTimer from 'react-native-background-timer';
import Pusher from 'pusher-js/react-native';
设置您先前创建的Google项目的Google API密钥的值:
const GOOGLE_API_KEY = 'YOUR GOOGLE PROJECT API KEY';
使用Expo的Location and Permissions API:
const { Location, Permissions } = Expo;
Expo的API是跨平台的,这与标准的React Native项目不同,在标准的React Native项目中,您必须安装像React Native Permissions这样的包才能访问跨平台的权限API。
接下来,设置要执行的跟踪用户当前位置的代码的时间间隔(以毫秒为单位)。 在这种情况下,我们希望它每30分钟执行一次。 请注意,在下面的代码中,我们使用location_status
变量的值来检查是否已授予访问用户当前位置的权限。 一旦安装了组件,我们稍后将设置此变量的值:
var interval_ms = 1800 * 100; // 1800 seconds = 30 minutes, times 100 to convert to milliseconds
var location_status = null; // whether accessing the user's location is allowed or not
BackgroundTimer.runBackgroundTimer(() => { // run the background task
if(location_status == 'granted'){ // if permission to access the location is granted by the user
// next: add code for getting the user's current location
}
},
interval_ms);
获取当前位置
通过使用Expo的Location API获取当前位置:
Location.getCurrentPositionAsync({ // get the user's coordinates
enableHighAccuracy: true // enable fetching of high accuracy location
})
.then((res) => {
let { latitude, longitude } = res.coords; // extract the latitude and longitude values
// next: add code for getting the address based on the coordinates
});
接下来,使用Google Maps Geocoding API,通过提供纬度和经度值向反向地理编码端点发出请求。 这将根据这些坐标返回格式化的地址:
fetch(`https://maps.googleapis.com/maps/api/geocode/json?latlng=${latitude},${longitude}&key=${GOOGLE_API_KEY}`)
.then((response) => response.json())
.then((responseJson) => {
let addr = responseJson.results[0].formatted_address;
// next: send the location with Pusher
})
.catch((error) => {
console.error(error);
});
使用推杆发送位置
下一步是使用Pusher发送位置信息。 稍后,我们将创建服务器,该服务器将用作auth端点,并同时显示显示用户当前位置的页面。
更新构造函数以为Pusher实例设置默认值:
constructor() {
/*
the code for generating unique code from earlier
*/
this.pusher = null;
}
当安装组件时,我们要初始化Pusher。 现在,您可以从之前创建的Pusher应用程序的设置中提供Pusher API密钥和集群:
componentWillMount() {
this.pusher = new Pusher('YOUR PUSHER APP KEY', {
authEndpoint: 'YOUR AUTH SERVER ENDPOINT (TO BE ADDED LATER)',
cluster: 'YOUR PUSHER CLUSTER',
encrypted: true // whether the connection will be encrypted or not. This requires https if set to true
});
}
接下来,您现在可以添加用于发送当前位置的代码。 在Pusher中,这是通过调用trigger()
方法来完成的。 第一个参数是要触发的事件的名称,第二个参数是包含要发送的数据的对象。
稍后,在服务器中,我们将订阅安装组件后将订阅的相同频道。 然后,我们将绑定到client-location
事件,以便每次从某个地方触发该事件时,服务器也会收到通知(尽管只有当它所服务的页面也订阅了相同的频道时):
fetch(...)
.then(...)
.then((responseJson) => {
let addr = responseJson.results[0].formatted_address;
current_location_channel.trigger('client-location', {
addr: addr,
lat: latitude,
lng: longitude
});
})
.catch(...);
我们唯一要获得访问用户当前位置权限的请求是在安装组件时。 然后,我们将根据用户的选择更新location_status
。 该值可以是“ granted”或“ denied”。
请记住,用于检查用户当前位置的代码是定期执行的。 这意味着location_status
变量的新值还将在以后执行该函数时使用。 之后,我们还想订阅Pusher频道,在该频道中将发送位置更新:
componentDidMount() {
try {
Permissions.askAsync(Permissions.LOCATION).then(({ status }) => {
location_status = status;
});
}catch(error){
console.log('err: ', error);
}
// subscribe to the Pusher channel
current_location_channel = this.pusher.subscribe('private-current-location-' + this.state.unique_code);
}
创建服务器
现在我们准备创建服务器。 首先,在应用程序的项目目录之外创建工作目录( ocdmom -server )。 浏览该目录并执行npm init
。 只需按Enter,直到创建package.json文件。
接下来,安装我们需要的软件包:
npm install --save express body-parser pusher
以下是每个程序包的概述:
-
express
:用于创建服务器。 这负责服务跟踪页面以及响应auth端点。 -
body-parser
:Express中间件,它解析请求主体并将其用作JavaScript对象。 -
pusher
:用于与您先前创建的Pusher应用进行通信。
完成后,您的package.json文件现在应如下所示:
{
"name": "ocdmom-server",
"version": "1.0.0",
"description": "",
"main": "server.js",
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1",
"start": "node server.js"
},
"author": "",
"license": "ISC",
"dependencies": {
"body-parser": "^1.18.2",
"express": "^4.16.2",
"pusher": "^1.5.1"
}
}
创建一个server.js文件并导入我们刚刚安装的软件包:
var express = require('express');
var bodyParser = require('body-parser');
var Pusher = require('pusher');
将服务器配置为使用body-parser
包,并设置公共文件夹的静态文件目录:
var app = express();
app.use(bodyParser.json()); // set middleware to parse request body to JavaScript object
app.use(bodyParser.urlencoded({ extended: false })); // for parsing URL encoded request body
app.use(express.static('public')); // specify the directory where the static files like css, JavaScript and image files lives
初始化推送器。 此处提供的值将来自环境变量。 我们稍后将在部署服务器时添加它们:
var pusher = new Pusher({
appId: process.env.APP_ID,
key: process.env.APP_KEY,
secret: process.env.APP_SECRET,
cluster: process.env.APP_CLUSTER,
});
在访问基本URL时提供跟踪页面:
app.get('/', function(req, res){
res.sendFile(__dirname + '/public/tracker.html');
});
接下来,创建用于响应对auth端点的请求的路由。 每次应用初始化与Pusher的连接以及访问跟踪页面时,都会点击该链接。 这是对用户进行身份验证,以便他们可以直接从客户端与Pusher应用通信。
请注意,这实际上没有任何安全措施。 这意味着任何人只要有权访问您的Pusher App密钥就可以向您的auth端点提出请求。 在生产应用程序中,您需要更强大的安全性!
app.post('/pusher/auth', function(req, res) {
var socketId = req.body.socket_id;
var channel = req.body.channel_name;
var auth = pusher.authenticate(socketId, channel);
var app_key = req.body.app_key;
var auth = pusher.authenticate(socketId, channel);
res.send(auth);
});
最后,使服务器侦听环境变量中指定的端口。 默认情况下,它是端口80,但我们还将其设置为备用值,以防万一它不存在:
var port = process.env.PORT || 80;
app.listen(port);
追踪页面
跟踪页面显示一个地图,该地图在每次从应用触发client-location
事件时都会更新。 不要忘记提供您的Google API密钥:
OCDMom Tracker
接下来,创建一个public / js / tracker.js文件并添加以下内容:
function getParameterByName(name, url) {
if (!url) url = window.location.href;
name = name.replace(/[\[\]]/g, "\\$&");
var regex = new RegExp("[?&]" + name + "(=([^]*)|&|#|$)"),
results = regex.exec(url);
if (!results) return null;
if (!results[2]) return '';
return decodeURIComponent(results[2].replace(/\+/g, " "));
}
上面的函数从URL中提取查询参数。 当在浏览器中访问服务器的基本URL时,必须将唯一代码(应用程序中显示的代码)作为查询参数包括在内。 这使我们能够跟踪用户的位置,因为它将使我们订阅与应用程序订阅的频道相同的频道。
接下来,初始化Pusher。 该代码与先前服务器中的代码相似。 唯一的区别是我们只需要指定Pusher应用程序密钥,身份验证端点和集群:
var pusher = new Pusher('YOUR PUSHER APP KEY', {
authEndpoint: 'YOUR PUSHER AUTH ENDPOINT',
cluster: 'YOUR PUSHER CLUSTER',
encrypted: true
});
检查code
是否作为查询参数提供,如果提供,则仅订阅Pusher通道:
var channel = null;
if(getParameterByName('code') == null){
alert('Make sure that the code is supplied as a query parameter, then refresh the page.');
}else{
channel = pusher.subscribe('private-current-location-' + getParameterByName('code'));
}
添加用于初始化地图的功能。 这将显示地图以及指向我们指定的默认位置的标记:
var map = null;
var marker = null;
function initMap(){
var myLatLng = { // set the default location displayed on the map
lat: -25.363,
lng: 131.044
};
map = new google.maps.Map(document.getElementById('map'), {
zoom: 16,
center: myLatLng
});
marker = new google.maps.Marker({
position: myLatLng,
map: map
});
}
绑定到client-location
事件。 每次应用触发client-location
事件时,都会执行回调函数,该事件具有与用户作为查询参数提供的唯一代码相同的唯一代码:
if(channel){
channel.bind('client-location', function(data) {
console.log('message received: ', data);
var position = new google.maps.LatLng(data.lat, data.lng); // create a new Google maps position object
// set it as the position for the marker and the map
marker.setPosition(position);
map.setCenter(position);
});
}
接下来,添加跟踪页面的样式( public / css / style.css ):
#map {
height: 100%;
}
html, body {
height: 100%;
margin: 0;
padding: 0;
}
部署服务器
我们将使用Now来部署服务器。 开源项目是免费的。
全局立即安装:
npm install now
安装完成后,您现在可以将Pusher应用程序凭据添加为secrets 。 如前所述,Now对于开源项目是免费的。 这意味着一旦服务器被部署,其源代码将在/_src
路径中可用。 这并不是很好,因为每个人都可以看到您的Pusher应用凭据。 因此,我们要做的就是将它们添加为秘密,以便可以将它们作为环境变量进行访问。
还记得服务器代码中的process.env.APP_ID
或process.env.APP_KEY
吗? 通过秘密将这些设置为环境变量。 pusher_app_id
是分配给机密的名称,而YOUR_PUSHER_APP_ID
是您的Pusher应用程序的ID。 执行以下命令以将Pusher应用程序凭据添加为机密:
now secret add pusher_app_id YOUR_PUSHER_APP_ID
now secret add pusher_app_key YOUR_PUSHER_APP_KEY
now secret add pusher_app_secret YOUR_PUSHER_APP_SECRET
now secret add pusher_app_cluster YOUR_PUSHER_APP_CLUSTER
一旦添加了这些,现在就可以部署服务器。 APP_ID
是环境变量的名称, pusher_app_id
是您要访问的机密的名称:
now -e APP_ID=@pusher_app_id -e APP_KEY=@pusher_app_key -e APP_SECRET=@pusher_app_secret APP_CLUSTER=@pusher_app_cluster
部署完成后就是这样。 它返回的URL是服务器的基本URL:
将该网址复制到 App.js文件并保存更改:
this.pusher = new Pusher('YOUR PUSHER APP KEY', {
authEndpoint: 'https://BASE-URL-OF-YOUR-SERVER/pusher/auth',
cluster: 'YOUR PUSHER APP CLUSTER',
encrypted: true
});
此时,该应用程序现在应该可以正常使用了。
结论
而已! 在这个由两部分组成的系列文章中,您学习了如何将现有的Expo项目分离到ExpoKit。 当您的应用程序已转换为标准本机项目时,ExpoKit是使用Expo平台提供的某些工具的好方法。 这使您可以将现有的本机模块用于React Native并创建自己的模块。
翻译自: https://code.tutsplus.com/tutorials/detaching-expo-apps-to-expokit--cms-30698