【Flutter】如何寫一個Flutter自動打包成iOS程式碼模組的指令碼

gzhongcheng發表於2019-06-05

相信很多使用原生+Flutter的iOS專案都會遇到混合開發的整合問題,也有大神寫了一些解決方案,下面就記錄一下我的心路歷程:

前期準備

開始之前,我先拜讀了一些大神的文章(這裡只挑出對我幫助最大的):

Flutter混合開發元件化與工程化架構

混沌初始,iOS現有專案整合Flutter

方案篩選

經過探索,結合專案的實際情況(我司的專案採用模組化開發,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的自動打包指令碼基本完成,上面的各個檔案路徑需要根據實際情況進行調整,改為適合自己的專案的路徑。

希望這篇能夠幫助大家少走彎路。

相關文章