likes
comments
collection
share

在iOS项目中依赖Flutter Module-①本地依赖

作者站长头像
站长
· 阅读数 6
目录

"在iOS项目中依赖Flutter Module"系列文章和构建脚本都已放到了GitHub上,传送门🚪

本文整理的3种本地依赖方案都是Flutter开发文档- 章节:Adding Flutter to iOS 中推荐的,适用于在原有的iOS项目中依赖Flutter Module。依赖Flutter Module分为本地依赖和远程依赖,远程依赖会在后续的文章介绍,如果想提前看,可以进上面的传送门

在iOS项目依赖Flutter Module / 将Flutter Module添加到现有的iOS项目,要先创建一个Flutter Module。

cd some/path/
flutter create --template module flutter_module

flutter_module目录的结构如下,.../flutter_module/lib/里面放到的就是我们写的dart文件。

some/path/
├── flutter_module
│   ├── README.md
│   ├── build
│   ├── flutter_module.iml
│   ├── flutter_module_android.iml
│   ├── lib
│   ├── pubspec.lock
│   ├── pubspec.yaml
│   └── test

建好flutter_module后,随便加点flutter代码和第三方组件(比如flutter_boost),就可以测试了。

1.基于CocoaPods和podhelper.rb脚本本地依赖FlutterModule

这种接入方式是最常见的一种,容易上手,方便调试。ios_module/flutter_module /andriod_module可以放到不同的Git仓库,依赖时填写好相对的目录即可。为了方便测试代码,我把ios_module/flutter_module/andriod_module放在了一个Git仓库/目录下。ios_module就是iOS项目所在目录,整体目录结构如下:

some/path/
├── andriod_module
│   ├── ...
├── flutter_module
│   ├── README.md
│   ├── build
│   ├── flutter_module.iml
│   ├── flutter_module_android.iml
│   ├── lib
│   ├── pubspec.lock
│   ├── pubspec.yaml
│   └── test
├──ios_module
    ├── FlutterBoostPro
    ├── FlutterBoostPro.xcodeproj
    ├── FlutterBoostPro.xcworkspace
    ├── Podfile
    ├── Podfile.lock
    └── Pods

然后在iOS项目的Podfile文件中增加以下代码,执行pod install or update

  flutter_application_path = '../flutter_module/'
  load File.join(flutter_application_path, '.ios', 'Flutter', 'podhelper.rb')
  install_all_flutter_pods(flutter_application_path)

podhelper.rb脚本会将Flutter组件代码编译成.xcframework,并通过依赖本地路径导入到Pods中。

这种方式无论是Debug运行还是Release打包,都行得通,也方便单人开发调试两端,在1台电脑用2个IDE开发调试两端代码即可,模拟器也能正常运行。但也有明显的缺陷,需要所有的iOS开发人员都安装有Flutter开发环境,另外iOS项目编译时会很慢,每天编译的时间损耗还是不小的,打包时间也会增加不少。

2.编译FlutterModule,手动添加.xcframwork到iOS项目中

首先需要将FlutterModule编译成iOS的.xcframwork动态库,使用的是flutter build ios-framework --xcframework指令集。不过这个指令可以设置导出的目录,所以我们可以直接导出到ios_module/里,完整的目录结构如下,相比方案1,这里只增加了FlutterFrameworks 目录,专门用来存放Flutter的编译产物xcframework

some/path/
├── andriod_module
│   ├── ...
├── flutter_module
│   ├── README.md
│   ├── build
│   ├── flutter_module.iml
│   ├── flutter_module_android.iml
│   ├── lib
│   ├── pubspec.lock
│   ├── pubspec.yaml
│   └── test
├──ios_module
    ├── FlutterBoostPro
    ├── FlutterBoostPro.xcodeproj
    ├── FlutterBoostPro.xcworkspace
    ├── FlutterFrameworks             # <- 新增这个文件夹
    ├── Podfile
    ├── Podfile.lock
    └── Pods

执行这段完整的编译指令,即可导出Debug/Profile/Release3种不同模式的xcframework,并存放在这3个目录中。完整的指令如下:

这个过程会有点耗时

flutter build ios-framework --xcframework --no-universal --output=../ios_module/FlutterFrameworks/

在iOS项目中依赖Flutter Module-①本地依赖

然后在Xcode项目的根目录右键添加文件,即Add file to 'FlutterBoostPro',选择create groups,记得勾选Add to targets。添加好了后,xcode会自动把这些xcframework文件添加到Build PhasesLink Binary With Libraries中。这个过程在Flutter文档说明中是手动拖的,添加文件就省去拖文件的操作了。

在iOS项目中依赖Flutter Module-①本地依赖

然后framework添加到Embed Frameworks,初次添加时是找不到Embed Frameworks的,所以要到TARGETS - General 下面的 Frameworks, Libraries, and Embedded Content一栏操作。不过我们使用Add file to 'a project'添加的文件会自动加到这一栏,不用再重复拖入文件。

TARGETSBuild Settings里面设置Runpath Search Paths,添加"$(SRCROOT)/FlutterFrameworks/Release",指定相对路径。 这个时候试着运行项目,会出现报错:

dyld: Library not loaded: @rpath/Flutter.framework/Flutter
  Referenced from: /private/var/containers/Bundle/Application/0A64CC78-D8D3-433C-B794-B8F928525885/FlutterBoostPro.app/FlutterBoostPro
  Reason: image not found
dyld: launch, loading dependent libraries
DYLD_LIBRARY_PATH=/usr/lib/system/introspection
DYLD_INSERT_LIBRARIES=/Developer/usr/lib/libBacktraceRecording.dylib:/Developer/usr/lib/libMainThreadChecker.dylib:/Developer/Library/PrivateFrameworks/DTDDISupport.framework/libViewDebuggerSupport.dylib

是因为我们没有设置Embed & Sign,状态是Do Not EmbedFlutter文档说明中也指明了这个操作,都选择Embed & Sign即可,设置正确后就能正常运行项目了。

在iOS项目中依赖Flutter Module-①本地依赖

这种导入framework的方式,增加了编译Flutter、设置Target配置流程,如果需要切换Debug/Release环境,还需要重新添加framework,并重新设置FRAMEWORK_SEARCH_PATHSEmbed & Sign,在调试期间会增加不少的手动操作,当然为了方便调试,在flutter_module/.ios/下面的Runner项目中也可以依赖iOS的业务代码,也可以快速调试,只是Flutter clean后又要重新依赖,相对来说还是有点繁琐的;另外由于把编译产物直接导入到了iOS项目目录中,而Flutter.xcframework文件很大,会直接增加git的文件大小,影响git push和pull,每次编译也会影响到其他人员分支的同步。但这种导入framework的方式也有个非常大的优点,编译运行iOS项目耗时短,因为已经是编译过的xcframework文件,不用额外编译Flutter代码,相比之下能节省很多编译时间;另外其他的开发人员也不用安装Flutter开发环境,直接跑iOS项目就行。

3.编译FlutterModule,远程依赖Flutter.xcframework,本地依赖其余.xcframwork

前面2种方法都是依赖本机的编译产物,如果想把FlutterFrameworks分享给同事,直接推到Git会很耗时。Flutter.xcframework文件太大,超过了Github单个文件100M的限制,推到GitHub是行不通的,内部的Gitlab可以但很慢。为此Flutter官方特意给Flutter.xcframework实现了本地podspec中转依赖远程zip文件。这种依赖Flutter组件的方法逻辑上跟方案2一致,先把flutter_module编译成framwork,存放在FlutterFrameworks目录,再手动导入项目。区别在于Flutter.xcframework是通过cocoaPods导入,依赖了Google的远程文件,这样就避免了git无法提交或提交很慢的问题。

首先还是编译Flutter,需要注意这里增加了--cocoapods,编译后的产物包含了一个Flutter.podspec,这个Flutter.podspec依赖指向了Flutter.xcframework的远程文件。

flutter build ios-framework --cocoapods --xcframework --no-universal --output=../ios_module/FlutterFrameworks/

在iOS项目中依赖Flutter Module-①本地依赖

我们可以看下Flutter.podspec里面的具体内容,Flutter.xcframework依赖了远程zip文件,并不是我们前面用指令编译出来的,而且编译导出的目录里面也没有Flutter.xcframework(编译后就删除了),只有App.xcframeworkFlutterPluginRegistrant.xcframework第三方插件库 flutter_boost.xcframework

Pod::Spec.new do |s|
  s.name                  = 'Flutter'
  s.version               = '2.0.300' # 2.0.3
  s.summary               = 'Flutter Engine Framework'
  s.description           = <<-DESC
  	... 一些描述
	DESC
  s.homepage              = 'https://flutter.dev'
  s.license               = { :type => 'MIT', :text => <<-LICENSE
    	... 一些版权声明
  	LICENSE
  }
  s.author                = { 'Flutter Dev Team' => 'flutter-dev@googlegroups.com' }
  s.source                = { :http => 'https://storage.flutter-io.cn/flutter_infra/flutter/3459eb24361807fb186953a864cf890fa8e9d26a/ios-release/artifacts.zip' }
  s.documentation_url     = 'https://flutter.dev/docs'
  s.platform              = :ios, '8.0'
  s.vendored_frameworks   = 'Flutter.xcframework'
end

然后我们在Podfile新增依赖Flutter,执行pod install or update

pod 'Flutter', :podspec => './FlutterFrameworks/Release/Flutter.podspec'
-> Installing Flutter (2.0.300)
 > Http download
   $ /usr/bin/curl -f -L -o /var/folders/jp/4slqd1n915b7s_dm47l0mk240000gn/T/d20210706-71545-f6gido/file.zip https://storage.flutter-io.cn/flutter_infra/flutter/3459eb24361807fb186953a864cf890fa8e9d26a/ios-release/artifacts.zip
   --create-dirs --netrc-optional --retry 2 -A 'CocoaPods/1.10.1 cocoapods-downloader/1.4.0'
     % Total    % Received % Xferd  Average Speed   Time    Time     Time  Current
                                    Dload  Upload   Total   Spent    Left  Speed
   100  194M  100  194M    0     0  10.0M      0  0:00:19  0:00:19 --:--:-- 10.8M

首次安装会从云端下载Flutter.xcframework,文件大小在200M左右 (各版本Flutter对应的大小不一样),有点考验网络,解压后的Flutter.xcframework大小在480M左右,所以务必要在.gitignore中添加ios_module/Pods/Flutter/*.xcframework

如果这个时候我们运行项目,也是会报错的,因为目前为止只依赖Flutter.xcframework,其它的编译产物还是没有导入。所以我们还需要按照方案2的流程把App.xcframeworkFlutterPluginRegistrant.xcframework第三方库 flutter_boost.xcframework导入到项目中。不过这里我换了一种方式,不使用Add Files to 'a project'来添加文件了,而是把这3个文件拖到Frameworks, Libraries, and Embedded Content里面,设置Embed & Sign,然后在Build SettingsRunpath Search Paths添加"$(SRCROOT)/FlutterFrameworks/Release",就可以正常运行项目了。

相比方案2Flutter.xcframework采用了CocoaPods依赖导入,但是其它的.xcframework还是要手动导入。所以它的优缺点和方案2是基本一致的。Flutter.xcframework是远程的静态资源,如果有自定义引擎需求,就得在方案2的基础上改了。