相信很多使用原生+Flutter的iOS專案都會遇到混合開發的整合問題,也有大神寫了一些解決方案,下面就記錄一下我的心路歷程:
前期準備
開始之前,我先拜讀了一些大神的文章(這裡只挑出對我幫助最大的):
方案篩選
經過探索,結合專案的實際情況(我司的專案採用模組化開發,pods方式整合),有下面的兩個方案:
- 使用google的整合方案
- 將flutter編譯後的產物打包到一個新的子模組中,並在其中實現對應的介面和互動邏輯。
無論是從使用方便的角度還是對程式碼的侵入程度來看,採用方案2都是順理成章的。
實現方式
寫指令碼之前,先來看看打包步驟(其實就是收集編譯產物)
打包步驟
打包的步驟如下:
1、flutter build ios。
2、進入對應flutter專案的../ios(或者.ios)/Flutter/資料夾,找到App.framwork和Flutter.framework以及FlutterPluginRegistrant資料夾,拷貝到子模組中。
3、進入對應flutter專案的build/ios/Debug-iphoneos(或者Release-iphoneos),拷貝各個依賴庫的.a檔案到子模組中。
4、如果專案有依賴到第三方的外掛(一般來說都會有),需要根據.flutter-plugins檔案中的路徑到對應模組的原始碼的ios/Classes中拷貝各個依賴庫的.h檔案(這一步可以說是相當繁瑣了)。
使用自動化指令碼執行上面的操作
以下程式碼均在flutter工程的目錄下操作(請確保flutter的外掛沒有原始碼依賴):
首先是打包:
echo "===清理flutter歷史編譯==="
flutter clean
echo "===重新生成plugin索引==="
flutter packages get
echo "===生成App.framework和flutter_assets==="
flutter build ios --debug
# 或者 flutter build ios --release
複製程式碼
然後是拷貝App.framework和Flutter.framework:
framework_dir=...
echo "===copy App.framework和Flutter.framework==="
cp -r "./.ios/Flutter/App.framework" $framework_dir
cp -r "./.ios/Flutter/engine/Flutter.framework" $framework_dir
複製程式碼
拷貝註冊器:
classes_dir=...
echo "===copy註冊器:FlutterPluginRegistrant==="
regist_dir="./.ios/Flutter/FlutterPluginRegistrant/Classes/"
model_class=${classes_dir}/flutter/FlutterPluginRegistrant
mkdir -p $model_class
cp -r $regist_dir $model_class
複製程式碼
接下來是各個外掛的拷貝,這裡有一個小坑,由於各個外掛是被打包成.a的形式進行引入,那麼很可能導致模擬器上無法執行的問題,需要打一個真機包和一個模擬器包,並將它們進行合併,才能在使用過程中用模擬器進行除錯:
# 合成的.a檔案快取
temp_dir=...
mkdir -p ${temp_dir}
current_path="$PWD"
# 執行clean並重新編譯pods部分
cd .ios/Pods
/usr/bin/env xcrun xcodebuild clean
/usr/bin/env xcrun xcodebuild build -configuration Release ARCHS='arm64 armv7' BUILD_AOT_ONLY=YES VERBOSE_SCRIPT_LOGGING=YES -workspace Runner.xcworkspace -scheme Runner BUILD_DIR=../build/ios -sdk iphoneos
# 遍歷.flutter-plugins檔案
cat .flutter-plugins | while read line
do
array=(${line//=/ })
plugin_name=${array[0]}
echo "===修改註冊器(修正引用)==="
perl -pi -e "s|\<${plugin_name}\/|\"|g" ${model_class}/GeneratedPluginRegistrant.m
perl -pi -e "s|.h\>|.h\"|g" ${model_class}/GeneratedPluginRegistrant.m
temp_library=${temp_dir}/lib${plugin_name}.a
echo ">>>生成lib${plugin_name}.a<<<"
cd .ios/Pods
/usr/bin/env xcrun xcodebuild build -configuration Release ARCHS='arm64 armv7' -target ${plugin_name} BUILD_DIR=../../build/ios -sdk iphoneos -quiet
/usr/bin/env xcrun xcodebuild build -configuration Debug ARCHS='x86_64' -target ${plugin_name} BUILD_DIR=../../build/ios -sdk iphonesimulator -quiet
echo ">>>合併lib${plugin_name}.a<<<"
lipo -create "../../build/ios/Debug-iphonesimulator/${plugin_name}/lib${plugin_name}.a" "../../build/ios/Release-iphoneos/${plugin_name}/lib${plugin_name}.a" -o $temp_library
cd $current_path
if [[ -f "$temp_library" ]]; then
echo "===copy ${plugin_name}==="
plugin=${framework_dir}/${plugin_name}
rm -rf $plugin
mkdir -p $plugin
cp -f $temp_library $plugin
classes=${array[1]}ios/Classes
class=$dest_dir/Classes/flutter/${plugin_name}
rm -rf $class
mkdir -p $class
for header in `find "$classes" -name *.h`; do
cp -f $header $class
done
fi
done
rm -rf ${temp_dir}
fi
複製程式碼
可能你會注意到上面的===修改註冊器(修正引用)===
內容,這是由於我們將flutter的外掛部分的檔案直接打包成靜態庫,並將其.h檔案全部匯入到模組內部了,因此自動生成的#import <xxx/xxx.h>
模式不適用了(會報錯喲),需要改成#import "xxx.h"
的形式進行引用。
到此,flutter的自動打包指令碼基本完成,上面的各個檔案路徑需要根據實際情況進行調整,改為適合自己的專案的路徑。
希望這篇能夠幫助大家少走彎路。