将Expo Apps分离到ExpoKit

在我的《 使用Expo更轻松地进行React Native开发》一文中 ,您了解了Expo如何使初学者更轻松地开始使用React Native创建应用。 您还了解到,Expo使开发人员可以更快地启动和运行React Native应用程序,因为不再需要设置Android Studio,Xcode或其他开发工具。

但是,您也已经看到,Expo不支持应用程序可能需要的所有本机功能。 尽管Expo团队一直在努力支持更多的本机功能 ,但是最好学习如何将现有的Expo项目转换为标准的本机项目,以便在需要时可以轻松进行过渡。

拆卸到ExpoKit

为了分离到ExpoKit,首先必须编辑app.jsonpackage.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软件包。

如果成功,您应该看到类似于以下的输出:

将Expo Apps分离到ExpoKit_第1张图片

如果要部署到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界面更改位置:

将Expo Apps分离到ExpoKit_第2张图片

(如果您有运行在设备上的应用程序的问题,一定要看看这个 堆栈溢出 的问题上取得的Android Studio能够识别您的设备 。)

完成后,使用Xcode打开ios / ocdmom .xcworkspace文件。 Xcode将文件编入索引后,您应该可以单击该大播放按钮,它将自动在您选择的iOS模拟器上运行该应用程序。

Xcode还允许您模拟位置,但是仅当您构建用于在模拟器中运行的应用程序时。 更改代码并让开发服务器刷新应用程序实际上不会更改位置。 要更改位置,请单击发送图标,然后选择要使用的位置:

将Expo Apps分离到ExpoKit_第3张图片

继续编写应用程式

现在,我们准备继续为该应用程序编写代码。 这次,我们将添加在后台运行应用程序时运行一些代码的功能。

添加后台任务

导入先前安装的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_IDprocess.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:

将Expo Apps分离到ExpoKit_第4张图片

将该网址复制到 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

你可能感兴趣的:(将Expo Apps分离到ExpoKit)