用React Native构建一个应用程序剪辑
当App Clip在iOS 14中被引入时,我们立即意识到这是一个可能成为商店应用的大机会。由于App Clip的设计是一个可以即时下载的轻量级应用,我们想研究一下它对我们来说意味着什么。能够立即向用户展示商店应用程序的功能,而不必从App Store下载并通过入职培训,我们认为这可能有巨大的增长潜力。
应用程序夹的一个关键特征和限制是尺寸限制。为了使事情变得更加有趣,我们想用React Native来构建它。据我们所知,以前从来没有人做过这种规模的事情。
作为第一个在React Native中建立App Clip的人,每天要面对数百万用户,这被证明是一项具有挑战性的任务。
什么是App Clip?
App Clip是一个迷你版的应用程序,它的目的是轻量级的,可以 "在路上 "下载。为了提供流畅的下载体验,App Clip的大小不能超过10MB。作为比较,iOS商店的应用是51MB。
App Clip不能从App Store下载,它只能被 "调用"。调用意味着用户执行一个动作,在他们的手机上打开App Clip:扫描QR码或NFC标签,点击信息应用中的链接,或点击网页上的智能应用标语。调用完成后,iOS会显示一个提示,要求用户打开App Clip,同时在后台下载App Clip的二进制文件,使其立即启动。调用的URL被传递给App Clip,这使你能够为用户提供一个上下文的体验。
我们正在努力解决什么问题?
商店应用程序帮助用户在一个地方轻松地跟踪他们所有的包裹。当买方安装应用程序时,该订单被自动导入,买方可以随时了解其状态,而无需询问卖家。
然而,我们注意到,在 "感谢 "页面和打开应用程序之间的漏斗中,用户的数量大幅下降。尽管商店应用程序有4.8颗星的评价,但通过应用程序商店的几个额外步骤意味着一些买家选择不完成这一过程。应用程序夹将解决所有这些问题。
您的浏览器不支持视频标签。
当用户在电脑上登陆 "感谢 "页面,并通过扫描二维码调用App Clip,或在移动端结账时简单地点击打开按钮,他们将立即看到他们的订单被跟踪。没有应用商店,没有入职,只是直接进入订单细节,并可选择在整个包裹旅程中接收推送通知。
为什么是React Native?
React Native应用程序并不以体积小而闻名,所以我们知道建立一个大小低于10MB的App Clip会带来一些有趣的挑战。然而,作为应用程序商店中最受欢迎的应用程序之一,以及React Native的拥护者,我们真的想看看这是否可行。
由于商店应用程序是在React Native中构建的,我们所有的开发人员都可以为App Clip做出贡献,而不仅仅是Swift开发人员,而且我们将有可能与AppClip保持代码共享和功能平等,就像我们在Android和iOS中做的那样。
简而言之,这是一个有趣的挑战,符合我们的技术选择和我们关于建立可重复使用系统的长期设计的价值观。
构建概念验证--快速失败
由于App Clip是一项非常新的技术,有大量的未知因素。我们不确定是否有可能用React Native构建它,并且低于10MB的限制。因此,我们决定制定一个技术计划,如果我们失败了,我们会快速失败。
这个计划是这样的。
- 在React Native中建立一个 "Hello World "App Clip并确定其大小
- 建立一个非常潦草的,甚至没有功能的,实际的App Clip版本,包含所有我们估计需要的代码和依赖,并确定其大小
- 清理代码,使一切正常工作
我们还希望在产品方面快速失败。App Clips是一项全新的技术,很少有人接触过。我们不确定我们的应用夹是否能使我们的用户受益,所以我们的目标是推出一个应用夹进行测试,并快速推出。如果它被证明是成功的,我们就会回去再做一次。
你好,世界
当我们开始建立App Clip时,有很多未知的因素。所以为了确定这是否可行,我们首先创建了一个 "Hello World "的App Clip,只用React Native的*和*组件。
这个 "Hello World "App Clip的重量达到了惊人的28MB。一个光秃秃的App Clip怎么会有这么大的容量?我们调查后发现,App Clip包含了Shop应用使用的所有本地依赖,尽管它只需要React Native的一个子集。我们意识到,我们必须在Podfile中明确定义App Clip需要哪些本地依赖。
定义依赖是通过查看React Native node_modules/react-native/scripts/react_native_pods来确定React Native需要的最低限度的本地依赖。确定清单后,我们计算了App Clip的大小。结果是4.3MB。这是个好消息,但我们仍然不知道加入我们想要的所有功能是否会使我们超过10MB的限制。
构建一个简单的版本
用React Native构建App Clip与构建React Native应用几乎是一样的,但有一个很大的区别。我们需要在Podfile中明确定义App Clip的依赖关系。自动链接在这种情况下是行不通的,因为它会扫描所有已安装的包,找出与自动链接兼容的包并添加它们,我们需要挑选只有App Clip使用的pod。
这个过程非常简单;在React组件中添加一个依赖,如果它有一个本地依赖,我们就把它添加到Podfile中的*"Shop App Clip "*目标。但是这样做的后果在以后会很严重。
所以基线大小是4.3MB,现在是时候开始添加我们需要的功能了。由于我们在这个阶段仍然在探索设计,我们不知道最终的结果是什么(除了显示用户的订单信息),但我们可以做一些假设。首先,我们希望尽可能多地与应用程序共享代码。商店应用程序有一个非常强大的UI库,我们想利用它,以及处理用户和订单创建的大量业务逻辑。其次,我们知道我们需要基本的功能,比如。
- 对我们的GraphQL服务进行网络调用
- 错误报告
- 推送通知
由于我们只想确定构建规模,并本着快速失败的精神,我们在没有工作的情况下实现了这些功能。代码被添加了,还有依赖性,但App Clip根本没有功能。
我们再次计算了App Clip的大小,结果是6.5MB。尽管至少可以说这是一个潦草的实现,而且在功能上还有很多未知数,但我们知道在React Native中构建它在理论上是可行的,也是我们想要追求的。
构建App Clip
我们知道用React Native构建我们的App Clip是可行的,我们的概念验证是6.5MB,给了我们一些未知因素的余地。而对于React Native App Clip来说,肯定有很多未知因素。在应用程序和App Clip之间共享代码会不会影响其大小或导致任何其他问题?如果App Clip需要一个依赖关系,使我们超过了10MB的限制,我们该怎么做?
技术驱动设计
鉴于非常严格的限制,我们决定不像大多数项目那样由设计引导技术,我们将从相反的方向来处理这个问题。在开发App Clip时,技术将驱动设计。如果有什么东西导致我们超过或接近10MB的限制,我们就会回到绘图板,寻找其他的解决方案。
商店应用程序和应用程序夹之间的代码共享
在App Clip中,我们想让用户快速了解他们的订单,并能通过推送通知收到运输的更新。我们从商店应用程序的订单视图中得到了很大的启发,最终的App Clip设计是一个重组的版本。
应用夹与商店应用
商店应用程序的结构是尽可能多地共享代码,我们希望将其纳入App Clip中。两者之间共享代码是有意义的,特别是当App Clip具有与应用程序中的订单视图类似的功能时。
我们的第一个探索是看看在应用程序和App Clip之间共享订单视图的所有代码是否可行,并通过App Clip传递的道具修改布局。
App Clip和Shop App共享组件中的所有代码
我们很快意识到这是不可行的。首先,这将给订单视图增加太多的复杂性,但主要的是,对订单视图的任何改变都会影响到App Clip。如果一个开发者给订单视图增加了一个功能,并且有很大的依赖性,那么10MB的App Clip限制就会受到威胁。
对于一个小的开发团队来说,这可能是一个有效的方法,但在我们的规模下,我们不能这样做。在对应用程序的主要订单视图进行修改时,每个开发人员都要对应用程序夹的大小限制承担额外的责任,这有悖于我们对自主权的重视。
然后我们考虑在App Clip中建立我们自己的订单视图版本,但共享其子组件。这可能是一个可行的折中方案,所有的逻辑重代码都在**中,但简单的展示组件仍然可以被共享。
App Clip和Shop App共享组件的子组件
我们想导入App Clip的第一个组件是**,它的工作是显示产品标题、价格和图片。
显示产品标题、价格和图片
这个组件的代码看起来像这样(简化)。
但是当我们把这个组件导入App Clip时,它就崩溃了。经过一番调查,我们发现我们的*组件使用了一个叫做react-native-fast-image的* 库*。* 这是一个用原生Swift代码构建的库,我们用它来以一种非常高效的方式显示大型图片列表。如前所述,为了保持App Clip的大小,我们需要在Podfile中明确定义其所有的本地依赖关系。我们没有定义react-native-fast-image的本地依赖,因此它崩溃了。解决这个问题很容易,添加依赖关系后,我们就可以使用<ProductRow /> 组件。
然而,我们的概念验证App Clip重达6.5MB,意味着我们只有3.5MB的空间。所以我们知道我们只想添加绝对必要的依赖,而且由于App Clip只显示少量的图片,我们不认为这个库是绝对必要的。
考虑到这一点,我们简要地浏览了我们想与订单视图共享的所有组件,也许这只是一个一次性的事情,我们可以创建一个变通的方法?我们发现,的大部分子组件在某处都有一个本地的依赖关系。在分析它们会如何影响App Clip的大小时,我们发现它们会把App Clip推到10MB以上,其中一个依赖性的重量达到了惊人的2.5MB。
站在一个十字路口
我们现在意识到在应用程序中的订单视图和App Clip之间共享组件是不可能的,所有的代码都是如此吗?在这个阶段,我们正站在一个十字路口。我们要复制所有的东西吗?一些东西?还是什么都不做?
为了回答这个问题,我们决定根据以下原则来决定。
- App Clip是一个实验:我们不知道它是否会成功,所以我们想尽可能快地验证这个想法。
- 对其他开发者的影响最小化。我们是一个从事App Clip的小团队,我们不想给其他从事Shop应用的开发者增加任何责任。
- 易于删除。由于实验的成功有许多未知因素,我们希望加倍努力编写容易删除的代码。
考虑到这一点,我们决定应用程序中的订单视图和App Clip之间的相似之处纯粹是巧合。这种思维方式的改变帮助我们很快地向前迈进。
构建阶段
构建App Clip与构建其他React Native应用非常相似,唯一真正不同的是我们需要不断跟踪其大小。由于检查App Clip的大小非常耗时,在我们的本地机器上每次大约需要25分钟,我们决定只在添加任何新的依赖项时进行检查,并不时地进行一些临时检查。
App Clip的所有组件都是从头开始创建的,除了在Shop应用中使用我们的共享组件和功能。在我们的shared/ 目录中,有很多强大的基础工具,我们想在App Clip中使用*;和* 以及其他一些我们非常依赖的工具,在我们的Restyle库的帮助下,在商店应用中构造我们的UI。我们还想重用共享的钩子来设置推送通知、创建用户等。如前所述,在应用程序和App Clip之间共享代码有可能导致问题。如果开发者决定在*或* 中添加一个新的本地依赖,他们往往会在不知不觉中影响到App Clip。
然而,我们认为这些共享组件足够成熟,不需要对它们进行任何大的改动。为了捕捉任何被添加到这些共享组件的新的依赖关系,我们写了一个CI脚本来检测并通知拉动请求的作者。
该脚本做了三件事。
- 遍历Podfile并创建一个所有本地依赖关系的列表。
- 遍历App Clip的所有导入,并创建一个具有本地依赖性的列表。
- 最后,比较这两个列表。如果它们不匹配,CI工作就会失败,并指示如何继续。
有几次我们偶然发现了一些依赖关系的问题,无论是我们共享的还是外部的,都给App Clip增加了一些重量。这可能是一个用于动画的第三方库,异步存储,或获取设备的信息。考虑到我们的 "技术驱动设计 "原则,我们经常删除非关键功能的依赖性,如动画库。
我们现在对如何在构建App Clip时进行思考感到更有信心,而且我们行动迅速,不断创建和合并拉动请求。
支持应用中的调用URL
应用程序总是优先于App Clip。这意味着如果你通过扫描二维码来调用App Clip,但你已经安装了应用程序,应用程序会打开,而不是App Clip。我们也必须在应用程序中建立对调用的支持,所以即使用户已经安装了应用程序,扫描QR码也会自动导入订单。
React Native使我们能够通过链接模块做到这一点。
该模块允许我们在应用内获取调用的URL,并为已经存在的应用用户创建订单。有了这个,我们现在支持在App Clip和App中通过扫描二维码导入订单。
顺利过渡到应用程序
我们想要实现的最后一个功能是向应用程序的平稳过渡。如果用户决定从App Clip升级到完整的App体验,我们希望提供一个更简单的入职体验,并神奇地在App中为他们准备好他们的订单。苹果公司为此提供了一个非常好的解决方案,即App Clip和应用程序都可以访问的共享数据容器。
现在,我们可以将用户数据存储在App Clip中,应用程序可以访问这些数据,如果用户决定升级,可以提供一个最佳的上机体验。
测试App Clip
在App Clip的整个开发和推出过程中,测试是困难的。苹果提供了一个很好的方法,通过在Xcode中对调用URL进行硬编码来模拟App Clip的调用,但没有办法测试扫描QR码、调用App Clip和下载应用程序的完整端到端流程。这在我们的本地机器或TestFlight上都是不可能的。为了验证该流程是否能按预期工作,我们决定极早地发布App Clip的第一个版本。在测试标志的帮助下,我们确保App Clip只能由团队调用。这个早期版本没有任何功能,它只是验证了App Clip收到了调用的URL,并将适当的数据传递给应用程序,以获得良好的入职体验。一旦这个流程开始工作,而且我们可以相信我们的本地模型与生产中的工作一样,测试App Clip就变得容易多了。
经过广泛的测试,我们觉得已经准备好发布App Clip了。发布过程非常相似,因为App Clip被捆绑在应用程序中,唯一需要的是在App Store Connect中为调用模式提供副本和图像资产。
应用商店连接
我们在接触这个项目时有很多未知因素--技术对我们来说是全新的。我们试图用React Native建立一个App Clip,这并不典型我们的方法(快速失败和迭代)很有效。有一个从事原生iOS开发的开发人员是非常有帮助的,因为App Clip--即使是用React Native编写的--也涉及到很多苹果的工具。
我们没有预料到的一个挑战是分享代码会有多困难。事实证明,共享代码给主应用程序带来了太多的复杂性,而我们不想影响整个Shop团队的开发过程。所以我们在有意义的地方复制了代码。
我们最终的应用程序剪辑大小为9.1MB,离10MB的限制还差一点。有这样一个硬约束是一个有趣的挑战。我们成功地建立了我们最初所想的大部分内容,而且我们还可以进一步进行优化。
Sebastian Ekström是一名驻斯德哥尔摩的高级开发人员,自2018年以来一直在Shopify工作。他目前在店铺留存团队工作。
无论你在哪里,你的下一个旅程从这里开始如果你对从头开始构建系统以解决现实世界的问题感兴趣,我们的工程博客有关于我们遇到的其他挑战的故事。有兴趣吗?请访问我们的工程职业页面,了解我们的空缺职位,并了解Digital by Default。
在您的收件箱中获得这样的故事!
来自构建和扩展Shopify的团队的故事。这个商务平台为全球数以百万计的企业提供动力。
电子邮件地址是的,给我登记
与我们分享您的电子邮件并接收每月的更新。
谢谢您的订阅。
您将很快开始收到免费的提示和资源。