从零搭建 iOS Native Flutter 混合工程

640?wx_fmt=jpeg

黑客技术 点击右侧关注,了解黑客的世界! 640?wx_fmt=jpeg

640?wx_fmt=jpeg

Linux编程 点击右侧关注,免费入门到精通! 640?wx_fmt=jpeg

640?wx_fmt=jpeg

程序员严选 甄选正品好物,程序员生活指南! 640?wx_fmt=jpeg


作者丨CaffreySun

https://juejin.im/post/5c3ae5ef518825242165c5ca


本文来实现一个灵活、无侵入、低耦合的 iOS Flutter 混合工程。我们希望混合开发至少得保证如下特点:


  • 对Native工程无侵入

  • 对Native工程零耦合

  • 不影响Native工程的开发流程与打包流程

  • 易本地调试

一、Flutter 提供的 Native Flutter 混合工程方式

Flutter 官方提供的混合工程搭建方法:Add Flutter to existing apps文章中介绍了如何在现有 App 里加入Flutter,下面进行逐步介绍一下


1、创建 Flutter 工程


请自行 百度/Google Flutter 安装教程,安装Flutter。然后到任意目录下执行flutter create -t module my_flutter,"my_flutter" 是要创建的 Flutter 工程的名称。


2、通过 Cocoapods 将 Flutter 引入 现有 Native 工程


在Podfile添加以下下代码


然后执行pod install


3、修改 Native 工程


打开Xcode工程,选择要加入 Flutter App 的 target,选择 Build Phases,点击顶部的 + 号,选择 New Run Script Phase,然后输入以下脚本


 
 
 
 

二、分析 Native Flutter 混合工程

按照上面三个步骤进行逐一分析每一步的问题,并提供优化方案。


1、创建 Flutter 工程


这一步首先在自己电脑上安装 Flutter,然后使用flutter create。这里就存在问题,在团队开发中每个人安装的 Flutter 版本可能并不同,这样会出现Dart层Api兼容性或Flutter虚拟机不一致等问题。在团队协作中一定要保证 Flutter 工程依赖相同的 Flutter SDK,所有需要一个工具在执行 flutter 指令时可以根据当前 Flutter 工程使用对应版本的 Flutter SDK,目前有一个名叫flutter_wrapper的工具,使用 flutterw 代替 flutter  指令,工具会自动将 Flutter SDK 放在当前 Flutter 工程目录下,并执行当前工程中的 flutter 命令,这样就不再依赖本地电脑安装的 Flutter SDK。


flutter_wrapper使用:


  1. flutter create创建 Flutter 工程,这里使用的是本地的 Flutter SDK

  2. 进入 Flutter 工程目录安装 'flutter_wrapper',执行 sh -c "$(curl -fsSL https://raw.githubusercontent.com/passsy/flutter_wrapper/master/install.sh)"

  3. 此后在当前 Flutter 工程需要使用 flutter  命令的地方都使用 ./flutterw来代替


2、通过 Cocoapods 将 Flutter 引入 现有 Native 工程


这一步在 Podfile 里添加里一个 'podhelper.rb' ruby 脚本,脚本会在 pod install/update 时执行,脚本主要做4点事情:


  • 解析 'Generated.xcconfig' 文件,获取 Flutter 工程配置信息,文件在'my_flutter/.ios/Flutter/'目录下,文件中包含了 Flutter SDK 路径、Flutter 工程路径、Flutter 工程入口、编译目录等。

  • 将 Flutter SDK 中的 Flutter.framework 通过 pod 添加到 Native 工程。

  • 将 Flutter 工程依赖的插件通过 pod 添加到 Native 工程,因为有些插件有 Native 部分代码。

  • 使用post_install 这个 pod hooks 来关闭 Native 工程的 bitcode,并将 'Generated.xcconfig' 文件加入 Native 工程。


这一步存在问题是,'podhelper.rb'脚本是通过一个本地 Flutter 工程路径'flutter_application_path'来读取,在团队协作中我们很难保证每个人的本地 Flutter 工程路径都一样,在同步代码时大家可能都要频繁修改'flutter_application_path'变量,这样很不友好。


解决这个问题的思路就是将 Flutter 工程放在当前 Native 工程的目录下,我们可以再加入一个 ruby 脚本,在每次执行 pod install/update 时,将 Flutter 工程从 git 上拉取一份放在当前目录下,这样 Flutter 工程的路径就统一了。大致代码如下:



 
 


上述代码只是临时代码,为了演示将 Flutter 工程放在当前目录下这个思路,后面会有完整的实现代码。


3. 修改 Native 工程



这里执行了一个'xcode_backend.sh'脚本的两个命令build、embed,两个命令分别的作业是:

  • build: 根据当前 Xcode 工程的 'configuration' 和其他编译配置编译 Flutter 工程,'configuration'通常为'debug'或者'release'

  • embed: 将 build 出来的 framework、资源包放入 Xcode 编译目录,并签名 framework


这里存在的问题是:Flutter 工程依赖 Native工程来执行编译,并影响Native工程的开发流程与打包流程。


通常 'configuration' 里不止有 'debug' 或者 'release',可能会有自定义的名称,如果我们使用自定义的 'configuration' 编译,那么 xcode_backend.sh build就会执行失败。因为Flutter 编译模式是通过 'configuration' 获取的,Flutter 支持 Debug、Profil、Release 三种编译模式,而我们自定义的名称不在这三种之中,Flutter 就不知道该怎么编译。


每次 Native 编译时 Flutter 就需要编译,其实是产生了相互依赖:Flutter 编译依赖 Native 编译环境,Native 依赖 Flutter 编译通过。


我们希望做到:Native 依赖 Flutter 编译出来的产物,并且保留依赖 Flutter 源码进行调试的能力。


实现这个目标我们需要两部分:

  • 第一部分:Flutter 工程里创建一个打包脚本,可以一键产生 Flutter 工程产物;

  • 第二部分:在 Native 工程获取 FLutter 工程的编译产物,并通过 pod 添加到工程;并且保留依赖 Flutter 工程源码的功能。

三、实现 Native Flutter 混合工程

下面我们来实现上文提到的两个部分


第一部分实现“打包脚本”



这一部分我们需要实现脚本自动打包 Flutter 工程,拆分一下这个脚本流程,大致分为一下几个步骤:


  1. 检查 Flutter 环境,拉取 Flutter plugin

  2. 编译 Flutter 工程产物

  3. 复制 Flutter 插件中的 Native 代码

  4. 将产物同步到产物发布的服务器


下面来一步一步的分析并实现每一步:


(1) 检查 Flutter 环境,拉取 Flutter plugin


这一步做的工作是检查是否安装了 'flutter_wrapper',如果安装则进行安装,然后执行 ./flutterw packages get,Shell代码如下:



 
 


(2) 编译 Flutter 工程产物



这一步是脚本的核心,主要逻辑和上文中'xcode_backend.sh build'类似,大致代码如下:



 
 


(3) 复制 Flutter 插件中的 Native 代码


Flutter 使用的各种插件可能会包含 Native 代码,并且这些代码已经提供了podspec,可以使用 pod 直接引入。我们要做的就是把插件的 Native 代码拷贝到产物目录。Flutter 创建了一个给 Native 注册插件的 pod 库 'FlutterPluginRegistrant',这个也需要拷贝出来,在 Flutter 工程根目录下有一个 .flutter-plugins 文件,文件内部记录了插件的名字和插件的路径,格式为 pugin_name=/xx/xx/xx,解析这个文件就可以得到插件信息,代码如下:


 
 


(4) 将产物同步到保留产物的服务器


经过上面的几个步骤后会生成一个产物目录,这个目录下会有几个二级目录,每个二级目录里都包含一个 podspec 文件。


也就是说这个产物目录里存放的就是 cocoapods 库,将目录拷贝到 Native 工程,然后用 pod 'pod_name', :path=>'xx/xxx' 的形式引用就可以了。


有了产物后我们需要一个存放产物的地方, 大家可以去这个地方下载,这一步比较灵活,可以选择将产物放在git仓库、http服务器、ftp服务器等。我最终选择将产物压缩成 zip 上传到 Maven 上,原因是为了和 Android Flutter 产物放在一个地方,并且 Maven 已成做好的产物版本管理。


Maven上传代码比较简单,这里不再赘述,有兴趣可以到文末的github仓库查看代码。


Flutter 工程版本设置是在工程目录下的 'pubspec.yaml' 文件,打包脚本读取这个文件来确定产物的版本。


最后这个脚本使用方式为 ./build_ios.h -m debug ./build_ios.h -m release,上文中没有提到的一点是只有 release 模式编译的包才会上传的服务器,debug 只是编译到产物目录。


第二步 Native 依赖 Flutter 产物


这部分我们需要实现获取指定版本 Flutter 工程 release 产物,并集成到 Native 项目,并保留可以调试 Flutter 工程的能力。


也是来拆分一下脚本流程:

  • 获取 Flutter 工程产物

    • 获取 release 产物

    • 获取 debug 产物

  • 通过 pod 引入 Flutter 工程产物


(1) 获取 Flutter 工程产物


上文说到只有 release 产物放在了产物服务器上,debug 只是编译到产物目录。不上传 debug 的原因是,debug 阶段就是开发阶段,举个不太恰当的例子:哪有开发阶段就把包上传 app store 的?也就代表这 release 的产物和 debug 的产物获取逻辑不一样,并且我们的脚本支持两种方式的切换,所以在 Podfile 添加如下代码:




Podfile 其实就是 Ruby 代码,上面几个由大写字母组成的变量是全局变量,最后一句代码的作用为读取'flutterhelper.rb'里的代码并执行,在'flutterhelper.rb'里可以获取到上面定义的全局变量,根据这几个变量做不同的操作,其中选择使用 release 还是 debug 的代码如下:


 
 
 
 

install_release_flutter_app为操作 release 产物的函数,install_debug_flutter_app为操作 debug 产物的函数。


处理 release 模式主要就是获取 release 产物,代码如下:




unzip_release_flutter_app为解压zip格式产物的函数,download_release_flutter_app为从 Maven 下载产物的函数,这两个比较简单,详细代码请看文末 github 仓库。install_release_flutter_app_pod为通过 pod 将产物添加到 Native 的函数,后面会详细介绍。


处理 debug 模式的操作为,获取 Flutter 工程源代码,执行 build_ios.sh -m debug 进行打包,然后得到 debug 产物目录,详细代码如下:


 
 
 
 

update_flutter_app为从 git 拉取代码的函数也不赘述,详情见文末 github 仓库。


(2) 通过 pod 引入 Flutter 工程产物


上文两个函数执行完成后,就得到了产物的存放目录,下面只需要引入到 Native 仓库就可以了,也就是install_release_flutter_app_pod函数,从代码如下:




如果要修改 release 产物版本,则设置FLUTTER_APP_VERSION。如果想要 debug flutter,则设置 FLUTTER_DEBUG_APP=true,如果调试本地代码则设置 FLUTTER_APP_PATH="../my_flutter",负责将 FLUTTER_APP_PATH注释掉,配置 FLUTTER_APP_URL FLUTTER_APP_BRANCH

四、总结

对照上文中提到的对混合工程的要求,总结一下:


  • Flutter 工程完全不依赖 Native 工程,而是通过 'build_ios.sh' 脚本进行编译打包;

  • 通过 pod 引入 Flutter 工程对 Native 也没有浸入,不要在 Native 工程里增加 Flutter 打包脚本;

  • Native 开发工程师只需要执行 pod install 所有的 Flutter 依赖就都加入进工程,不需要工程师配置 Flutter 开发环境;也不影响 Native 打包;

  • 也保留了本地调试 Flutter 工程的功能;


Github 仓库:https://github.com/CaffreySun/iOS_Flutter_Hybrid_Project


 推荐↓↓↓ 

640?wx_fmt=jpeg

?16个技术公众号】都在这里!

涵盖:程序员大咖、源码共读、程序员共读、数据结构与算法、黑客技术和网络安全、大数据科技、编程前端、Java、Python、Web编程开发、Android、iOS开发、Linux、数据库研发、幽默程序员等。

640?wx_fmt=png万水千山总是情,点个 “ 在看” 行不行
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值