NativeScript AppSync
使用 AppSync 将更新热部署到您的应用中。
npm i --save nativescript-app-sync

NativeScript AppSync 插件

Build Status NPM version Downloads Twitter Follow

为您的 NativeScript 应用提供实时更新服务!

📣 注意:NativeScript AppSync 目前处于测试版,并且 不受 NativeScript 核心团队的支持。AppSync 基于 Microsoft CodePush,我们对此表示感谢,因为这个解决方案是在他们的工作基础上构建的。❤️

可选阅读:这是什么,以及它是如何工作的

NativeScript 应用由 XML/HTML、CSS 和 JavaScript 文件以及任何随附的图像组成,这些图像由 NativeScript CLI 打包,作为特定平台的二进制文件的一部分分发(例如 .ipa 或 .apk 文件)。一旦应用发布,更新代码(例如修复错误、添加新功能)或图像资源,就需要您重新编译和重新分发整个二进制文件,这当然包括您发布的应用商店所关联的任何审查时间。

AppSync 插件通过保持您的代码和图像与您发布到 AppSync 服务器的更新同步,帮助您立即将产品改进呈现给最终用户。这样,您的应用既享有离线移动体验的好处,又能够像网页一样,在更新可用时立即加载更新。这真是一举两得!

为了确保您的最终用户始终拥有您应用的正常版本,AppSync 插件会保留前一个更新的副本,以便在您意外推送包含崩溃的更新时,可以自动回滚。这样,您可以放心,您的新发布敏捷性不会导致用户在您有机会在服务器上回滚之前被阻止。这是三全其美!

解决方案的架构概述——您不必担心这些

什么可以被(并将)AppSync 化?

  • /app 文件夹中的任何内容(但不是 App_Resources 文件夹)。
  • /node_modules 文件夹中的任何内容。

💁‍♂️ 注意,我们实际上并不使用这些文件夹,但 platforms/ios/<appname>/app 中的 app 文件夹和 platforms/android/app/src/main/assets/app 中的 app 文件夹,其好处是我们不“关心”您使用 Webpack 或 Uglify 或您用于压缩或混淆应用资源的相关工具。

什么不能(并且不会)被 AppSync 化?

  • NativeScript 平台更新。例如:将 tns-android 从版本 2.5.1 更新到 2.5.2。
  • 需要依赖不同版本的本机库的插件更新。
  • App_Resources 文件夹的内容,因为这些也是本机二进制文件的一部分。

因此,只要您在 package.json 中不更改依赖项和 tns 平台的版本,您就可以愉快地推送。如果您确实提高了依赖项的版本,请确保没有更改平台库。

入门指南

全局安装 NativeScript AppSync CLI

npm i -g nativescript-app-sync-cli

💁‍♂️ 这也将添加全局 nativescript-app-sync 命令到您的机器。您可以使用 nativescript-app-sync -v 检查当前安装的版本。

登录或注册服务

检查您是否已经登录,以及使用哪个电子邮件地址

nativescript-app-sync whoami

如果您已有账户,请登录

nativescript-app-sync login

如果您还没有账户,请注册

nativescript-app-sync register

这将打开一个浏览器,您可以在其中提供凭证,之后您可以在控制台中粘贴一个访问密钥来创建访问密钥。

现在您的主目录中应该有一个.nativescript-app-sync.config文件,它将从此自动与这台机器上的服务器进行身份验证。

请注意,您可以使用该网页界面来管理您的应用程序,但CLI功能更为强大,因此建议使用命令行界面。

要注销,您可以运行nativescript-app-sync logout,这将删除配置文件。

要执行无头登录(不打开浏览器),您可以这样做:nativescript-app-sync login --accessKey <访问密钥>

将应用程序注册到服务

为每个目标平台创建一个应用程序(每个平台)。这样,您可以分别为iOS和Android发布不同的版本。

⚠️ appname必须唯一,且不应包含短横线(-)。

nativescript-app-sync app add <appname> <platform>

# examples:
nativescript-app-sync app add MyAppIOS ios
nativescript-app-sync app add MyAppAndroid android

💁‍♂️ 这将显示您在连接到AppSync服务器时所需的部署密钥。如果您想要在以后列出这些密钥,请使用nativescript-app-sync deployment ls <appName> --displayKeys

💁‍♂️ 所有新应用程序都自带两个部署(StagingProduction),以便您可以从多个渠道开始分发更新。如果您需要更多渠道/部署,只需运行:nativescript-app-sync deployment add <appName> <deploymentName>

💁‍♂️ 想要重命名应用程序吗?在任何时候,使用以下命令:nativescript-app-sync app rename <oldName> <newName>

💁‍♂️ 想要删除应用程序吗?在任何时候,使用以下命令:nativescript-app-sync app remove <appName> - 这意味着任何配置为使用它的应用程序将显然停止接收更新。

列出您的已注册应用程序

nativescript-app-sync app ls

将此插件添加到您的应用程序中

tns plugin add nativescript-app-sync

⚠️ 如果您正在限制应用程序内对互联网的访问,请确保将我们的AppSync服务器(https://appsync-server.nativescript.org)和文件服务器(https://s3.eu-west-1.amazonaws.com)列入白名单。

检查更新

在安装并配置了AppSync插件之后,剩下的只是将必要的代码添加到您的应用程序中,以控制何时检查更新。

如果可用更新,它将静默下载并安装。

然后根据提供的InstallMode,插件将等待下一次冷启动(InstallMode.ON_NEXT_RESTART)、热启动(InstallMode.ON_NEXT_RESUME)或用户响应(InstallMode.IMMEDIATE)。

请注意,苹果不希望您提示用户重启应用程序,因此仅在iOS上为Enterprise分发应用程序(或通过TestFlight等测试应用程序)时使用InstallMode.IMMEDIATE

💁‍♂️ 查看示例demo以获取示例。

// import the main plugin classes
import { AppSync } from "nativescript-app-sync";

// and at some point in your app:
AppSync.sync({
deploymentKey: "your-deployment-key" // note that this key depends on the platform you're running on (see the example below)
});

您可以配置一些内容 - 此TypeScript示例包含所有可能选项

import { AppSync, InstallMode, SyncStatus } from "nativescript-app-sync";
import { isIOS } from "tns-core-modules/platform";

AppSync.sync({
enabledWhenUsingHmr: false, // this is optional and by default false so AppSync and HMR don't fight over app updates
deploymentKey: isIOS ? "your-ios-deployment-key" : "your-android-deployment-key",
installMode: InstallMode.ON_NEXT_RESTART, // this is the default install mode; the app updates upon the next cold boot (unless the --mandatory flag was specified while pushing the update)
mandatoryInstallMode: isIOS ? InstallMode.ON_NEXT_RESUME : InstallMode.IMMEDIATE, // the default is InstallMode.ON_NEXT_RESUME which doesn'
t bother the user as long as the app is in the foreground. InstallMode.IMMEDIATE shows an installation prompt. Don't use that for iOS AppStore distributions because Apple doesn't want you to, but if you have an Enterprise-distributed app, go right ahead!
updateDialog: { // only used for InstallMode.IMMEDIATE
updateTitle: "Please restart the app", // an optional title shown in the update dialog
optionalUpdateMessage: "Optional update msg", // a message shown for non-"--mandatory" releases
mandatoryUpdateMessage: "Mandatory update msg", // a message shown for "--mandatory" releases
optionalIgnoreButtonLabel: "Later", // if a user wants to continue their session, the update will be installed on next resume
mandatoryContinueButtonLabel: isIOS ? "Exit now" : "Restart now", // On Android we can kill and restart the app, but on iOS that's not possible so the user has to manually restart it. That's why we provide a different label in this example.
appendReleaseDescription: true // appends the description you (optionally) provided when releasing a new version to AppSync
}
}, (syncStatus: SyncStatus, updateLabel?: string): void => {
console.log("AppSync syncStatus: " + syncStatus);
if (syncStatus === SyncStatus.UP_TO_DATE) {
console.log(`AppSync: no pending updates; you're running the latest version, which is ${updateLabel}`);
} else if (syncStatus === SyncStatus.UPDATE_INSTALLED) {
console.log(`AppSync: update installed (${updateLabel}) - it will be activated upon next cold boot`);
}
});
点击这里查看JavaScript示例
var AppSync = require("nativescript-app-sync").AppSync;
var InstallMode = require("nativescript-app-sync").InstallMode;
var SyncStatus = require("nativescript-app-sync").SyncStatus;
var platform = require("tns-core-modules/platform");

AppSync.sync({
enabledWhenUsingHmr: false, // this is optional and by default false so AppSync and HMR don't fight over app updates
deploymentKey: platform.isIOS ? "your-ios-deployment-key" : "your-android-deployment-key",
installMode: InstallMode.ON_NEXT_RESTART,
mandatoryInstallMode: platform.isIOS ? InstallMode.ON_NEXT_RESUME : InstallMode.IMMEDIATE,
updateDialog: {
optionalUpdateMessage: "Optional update msg",
updateTitle: "Please restart the app",
mandatoryUpdateMessage: "Mandatory update msg",
optionalIgnoreButtonLabel: "Later",
mandatoryContinueButtonLabel: platform.isIOS ? "Exit now" : "Restart now",
appendReleaseDescription: true // appends the description you (optionally) provided when releasing a new version to AppSync
}
}, function (syncStatus, updateLabel) {
if (syncStatus === SyncStatus.UP_TO_DATE) {
console.log("AppSync: no pending updates; you're running the latest version, which is: " + updateLabel);
} else if (syncStatus === SyncStatus.UPDATE_INSTALLED) {
console.log("AppSync: update (" + updateLabel + ") installed - it will be activated upon next cold boot");
}
});

何时执行此检查?

建议在冷启动周期中多次检查更新,因此将此检查与resume事件(通常在应用程序启动时运行)关联可能最简单。

import * as application from "tns-core-modules/application";
import { AppSync } from "nativescript-app-sync";

// add this in some central place that's executed once in a lifecycle
application.on(application.resumeEvent, () => {
AppSync.sync(...);
});
点击这里查看JavaScript示例
var application = require("tns-core-modules/application");

application.on(application.resumeEvent, function () {
// call the sync function
});

发布更新

一旦您的应用程序已配置并分发给您用户,并且您已对代码和/或资源进行了更改,是时候将更改立即发布给用户了!

⚠️ 首先请确保创建一个发布构建,因此请使用用于应用程序商店分发的相同命令,只是不要将其发送到AppStore。您甚至可以对应用程序进行Webpack和Uglify,这一切对插件来说都是透明的。

💁‍♂️ 当发布AppSync更新时,您不需要提升应用程序的版本,因为您根本没有修改应用商店的版本。AppSync将自动为每个发布的版本生成一个“标签”(例如 v3),以便帮助您在发布历史中识别它。

完成此操作的最简单方法是使用我们AppSync CLI中的release命令。它的一些(最相关的)选项是

param alias default description
deploymentName d "Staging" 部署到“预发布”或“生产”。
description des not set 描述本次发布对应用程序所做的更改。
targetBinaryVersion t App_Resources Semver表达式,指定本次发布针对的二进制应用程序版本(例如 1.1.0,~1.2.3)。默认值是App_Resources/iOS/Info.plistApp_Resources/Android/AndroidManifest.xml中的确切版本。
mandatory m not set 这指定更新是否应被视为“紧急的”(例如,它包含一个关键的补丁)。此属性只是简单地回传到客户端,客户端可以决定是否以及如何强制执行它。如果未设置此标志,则更新被视为“非紧急”,因此您可以选择等待应用程序的下次冷启动。这并不意味着用户可以从更新中退出;所有AppSync更新最终都会安装在客户端上。

以下是两个平台的几个示例

iOS

nativescript-app-sync release <c-ios-appname> ios # deploy to Staging
nativescript-app-sync release <AppSync-ios-appname> ios --d Production # deploy to Production (default: Staging)
nativescript-app-sync release <AppSync-ios-appname> ios --targetBinaryVersion ~1.0.0 # release to users running any 1.x version (default: the exact version in Info.plist)
nativescript-app-sync release <AppSync-ios-appname> ios --mandatory --description "My mandatory iOS version" # a release for iOS that needs to be applied ASAP.

Android

nativescript-app-sync release <AppSync-android-appname> android # deploy to Staging
nativescript-app-sync release <AppSync-android-appname> android --d Production # deploy to Production (default: Staging)
nativescript-app-sync release <AppSync-android-appname> android --targetBinaryVersion ~1.0.0 # release to users running any 1.x version (default: the exact version in AndroidManifest.xml)
点击此处了解关于--targetBinaryVersion参数的更多信息`targetBinaryVersion`指定您要发布更新的应用程序的商店/二进制版本,这样只有运行该版本的用户的设备才会收到更新,而运行较旧和/或较新版本的应用程序二进制文件的用户将不会收到更新。这具有以下优点
  1. 如果用户正在运行较旧的二进制版本,那么AppSync更新中可能存在与他们运行的版本不兼容的破坏性更改。

  2. 如果用户正在运行较新的二进制版本,则假设他们运行的是更新的版本(并且可能不兼容)与AppSync更新。

如果您希望更新针对多个应用商店二进制版本的多个版本,我们还可以允许您指定参数为semver范围表达式。这样,任何运行满足范围表达式的二进制版本的客户端设备(即semver.satisfies(version, range)返回true)都会收到更新。以下是一些有效的semver范围表达式的示例

范围表达式 谁会收到更新
1.2.3 只有运行您应用程序特定二进制应用商店版本1.2.3的设备
* 任何配置为从您的AppSync应用程序接收更新的设备
1.2.x 运行主要版本1、次要版本2和任何补丁版本的您的应用程序的设备
1.2.3 - 1.2.7 运行二进制版本在1.2.3(包含)到1.2.7(包含)之间的设备
>=1.2.3 <1.2.7 运行二进制版本在1.2.3(包含)到1.2.7(不包含)之间的设备
1.2 等同于>=1.2.0 <1.3.0
~1.2.3 等同于>=1.2.3 <1.3.0
^1.2.3 等同于>=1.2.3 <2.0.0

*注意:如果您的semver表达式以特殊shell字符或操作符(如>^或** )开头,那么如果不将值用引号括起来,命令可能无法正确执行,因为shell不会向我们的CLI进程提供正确的值。因此,在调用release命令时,最好将targetBinaryVersion参数用双引号括起来,例如app-sync release MyApp-iOS updateContents ">1.2.3"

注意:如semver规范中定义,范围仅适用于非预发布版本:https://github.com/npm/node-semver#prerelease-tags。如果您想更新带有预发布标签的版本,则需要编写您想要更新的确切版本(例如1.2.3-beta)。

以下表格概述了AppSync对每个相应应用程序类型期望您的更新semver范围的版本值。

平台 应用程序商店版本的来源
NativeScript (iOS) App_Resources/iOS/Info.plist文件中的CFBundleShortVersionString
NativeScript (Android) App_Resources/Android/AndroidManifest.xml文件中的android:versionName

注意:如果元数据文件中的应用程序商店版本缺失补丁版本,例如2.0,它将被视为具有补丁版本0,即2.0 -> 2.0.0。对于等于纯整数的应用程序商店版本,1在这种情况下将被视为1.0.0

了解过去的发布情况

以下是一些您可能发现有用的AppSync CLI命令

我创建了哪些版本,以及安装指标是什么?

使用这样的命令将告诉您安装了更新的应用程序数量

nativescript-app-sync deployment history <appsync-appname> Staging

这将产生类似以下的内容

标签 发布时间 应用程序版本 强制 描述 安装指标
v2 一小时前 1.0.0 强制iOS版本! 活跃:11%(19个中的2个)
总计:2
v1 两小时前 1.0.0 出色的iOS版本! 活跃:26%(19个中的5个)
总计:5

给我当前发布的详细信息!

这将转储您的应用程序的测试和产品环境中最新的发布的详细信息

nativescript-app-sync deployment ls <appsync-appname>

如果您还想转储您的部署密钥,请使用

nativescript-app-sync deployment ls <appsync-appname> --displayKeys

这将产生类似以下的内容

名称 部署密钥 更新元数据 安装指标
产品 r1DVaLfKjc0Y5d6BzqX4.. 没有发布更新 没有记录安装
测试 YTmVMy0GLCknVu3GVIyn.. 标签:v5 活跃:11%(19个中的2个)
应用程序版本:1.0.0 总计:2
强制:是
发布时间:一小时前
发布者:[email protected]
描述:强制iOS版本!

清除发布历史记录

这不会回滚任何发布,但它会清理历史元数据(在这种情况下是测试应用程序的历史元数据)

nativescript-app-sync deployment clear <appsync-appname> Staging

高级主题

在开发过程中测试AppSync包

在将AppSync用于生产之前,您可能想先试用一下(明智之举!)。在您推送更新并添加sync命令到您的应用程序后,执行以下步骤

  • $ tns run <platform>。在iOS 设备上添加--release标志,以便LiveSync不会干扰。
  • 更新安装后,终止并重新启动应用程序

运行演示应用程序

您还可以通过使用其演示应用程序来玩AppSync。以下是您需要执行的步骤,以观察应用程序更新

  • 向服务注册(nativescript-app-sync register)并将演示应用程序添加到您的帐户(nativescript-app-sync app add <appname> <platform> nativescript
  • 应用程序注册后,您将在控制台看到其部署密钥,使用它们更新demo中的密钥
  • 转到src并运行npm run preparedemo - 这将构建插件并向演示应用程序添加引用
  • 准备一个用作“更新版本”的应用程序(例如,取消注释一个APPSYNC标签并注释APPSTORE标签),然后运行tns build <platform>
  • 发布更新(nativescript-app-sync release <appname> <platform>
  • 您可以通过使用nativescript-app-sync deployment history <appname> Staging来确保它出现在更新列表中
  • 准备一个用作“官方发布版本”的应用程序(例如,取消注释APPSTORE标签并取消注释APPSYNC标签),然后运行tns run <platform>
  • 当应用程序在设备上部署时,您应该看到“官方发布版本”以及有关已安装更新的信息
  • 关闭应用(并从设备最近使用应用列表中移除,以确保下次启动为冷启动)然后再次运行它 - 您现在应该看到应用的“更新版本”

修补更新元数据

发布更新后,可能会出现需要修改与之关联的元数据属性之一或多个的情况(例如,您忘记将关键错误修复标记为强制性的)。

点击此处了解有关修补元数据的更多信息。

您可以通过运行以下命令来更新元数据

nativescript-app-sync patch <appName> <deploymentName>
[--label <releaseLabel>]
[--mandatory <isMandatory>]
[--description <description>]
[--targetBinaryVersion <targetBinaryVersion>]

⚠️ 此命令不允许修改发布实际更新内容。如果您需要响应已识别为损坏的发布,应使用回滚命令立即将其回滚,并在必要时,在可用的修复程序可用时发布新的更新。

除了 appNamedeploymentName,所有参数都是可选的,因此,您可以使用此命令仅更新单个属性或一次性更新所有属性。在不指定任何属性标志的情况下调用 patch 命令将不会执行任何操作。

# Mark the latest production release as mandatory
nativescript-app-sync patch MyAppiOS Production -m

# Add a "mina and max binary version" to an existing release
nativescript-app-sync patch MyAppiOS Staging -t "1.0.0 - 1.0.5"

推广更新

如果您想轻松地将发布从预发布环境推广到生产环境,请阅读此内容

一旦您针对特定部署(例如 Staging)测试了更新,并希望将其推广(例如 dev->staging, staging->production),您可以使用以下命令简单地复制发布从其中一个部署到另一个部署

nativescript-app-sync promote <appName> <sourceDeploymentName> <destDeploymentName>
[--description <description>]
[--label <label>]
[--mandatory]
[--targetBinaryVersion <targetBinaryVersion]

# example
nativescript-app-sync promote AppSyncDemoIOS Staging Production --description 'Promoted from Staging to Production'

promote 命令将为目标部署创建一个新的发布,它包括来自源部署最新版本的 确切代码和元数据(描述、强制性以及目标二进制版本)。虽然您可以使用 release 命令“手动”将更新从一个环境迁移到另一个环境,但 promote 命令具有以下优点

  1. 它更快,因为您不需要重新组装要发布的发布资产,也不需要记住与源部署的发布关联的描述/应用商店版本。

  2. 它更不容易出错,因为推广操作确保您在源部署(例如 Staging)中已经测试的确切内容将激活在目标部署(例如 Production)中。

💁‍♂️ 如果您不需要对代码进行更改,建议的工作流程是利用自动创建的 StagingProduction 环境,并将所有发布直接到 Staging,然后在执行适当的测试后,从 StagingProduction 执行 promote

回滚更新

如果您想了解有关回滚的所有信息,请阅读此内容

部署的发布历史是不可变的,因此您不能删除或删除单个更新,一旦它们发布,而不删除部署的所有发布历史。但是,如果您发布了一个损坏的或包含意外功能的更新,则可以使用 rollback 命令轻松回滚。

nativescript-app-sync rollback <appName> <deploymentName>

#example
nativescript-app-sync rollback MyAppiOS Production

这将创建一个新的部署发布,其中包含与最新版本之前的版本完全相同的 代码和元数据。例如,假设您发布了以下更新到您的应用

发布 描述 强制
v1 初始发布!
v2 添加新功能
v3 错误修复

如果您在该部署上运行了 rollback 命令,将创建一个新的发布(v4),其中包含 v2 发布的内容。

发布 描述 强制
v1 初始发布!
v2 添加新功能
v3 错误修复
v4(从v3回滚到v2) 添加新功能

已经拥有 v3 版本的最终用户,在应用执行更新检查时将被“回退”到 v2 版本。此外,仍在运行 v2 版本(因此从未获取过 v3 版本)的用户不会收到更新,因为他们已经在运行最新版本(这就是为什么我们的更新检查除了版本标签外还使用软件包哈希的原因)。

如果您希望将部署回滚到除上一个版本以外的版本(例如 v3 -> v2),可以指定可选的 --targetRelease 参数

nativescript-app-sync rollback MyAppiOS Production --targetRelease v34

⚠️ 这会将发布回滚到之前的 AppSync 版本,而不是 AppStore 版本(如果有中间版本的话)。

💁‍♂️ 回滚产生的版本将在 部署历史 命令的输出中标注,以便更容易识别。

应用协作

与多位开发者共同开发一个应用?点击这里!

如果您将与其他开发者在同一个 AppSync 应用上工作,可以使用以下命令将他们添加为协作者

nativescript-app-sync collaborator add <appName> <collaboratorEmail>

注意:这假设开发者已经使用指定的电子邮件地址在 AppSync 上注册,因此请确保他们在尝试与他们共享应用之前已经完成注册。

一旦添加,所有协作者将立即对新建共享应用拥有以下权限

  1. 查看应用、其协作者、部署和发布历史。
  2. 向应用的任何部署发布更新。
  3. 回滚应用的任何部署。

相反,这意味着应用协作者不能执行以下操作

  1. 重命名或删除应用
  2. 在应用内创建、重命名或删除新部署
  3. 清除部署的发布历史
  4. 向应用添加或删除协作者(尽管开发者可以从与他们共享的应用中删除自己作为协作者的权限)。

随着时间的推移,如果有人不再与您共同开发应用,您可以使用以下命令将他们从协作者中移除

nativescript-app-sync collaborator rm <appName> <collaboratorEmail>

如果您在任何时候想列出已添加到应用的全部协作者,只需运行以下命令

nativescript-app-sync collaborator ls <appName>

在代理后使用 AppSync

点击这里了解所有关于代理支持的信息默认情况下,`login` 命令会自动查找系统范围内的代理,通过 `HTTPS_PROXY` 或 `HTTP_PROXY` 环境变量指定,并使用该代理连接到服务器。如果您想禁用此行为,并让 CLI 建立直接连接,只需在登录时指定 `--noProxy` 参数。
nativescript-app-sync login --noProxy

如果您想明确指定 CLI 应使用的代理服务器,而不依赖于系统设置,您可以在登录时传递 --proxy 参数。

nativescript-app-sync login --proxy https://foo.com:3454

登录后,任何推断的或指定的代理设置都会与您的用户会话一起持久化。这允许您在无需重新验证或重新指定您首选代理的情况下继续使用 CLI。如果您在任何时候想开始或停止使用代理,只需注销,然后使用新设置的登录。

故障排除