前言:iOS工程中引入flutter降允。我們第一步要做的事是,在podfile里面寫入類似下面代碼萍程。
flutter_app_path = '../my_flutter' #flutter工程目錄
load File.join(flutter_app_path, '.ios', 'Flutter', 'podhelper.rb') #導(dǎo)入podhelper.rb文件
install_all_flutter_pods(flutter_app_path) #調(diào)用podhelper.rb文件的install_all_flutter_pods方法
下面分析podhelper.rb都干了啥幢妄。
總體結(jié)構(gòu):1個(gè)require、6個(gè)方法
Part1茫负、require語(yǔ)法
require 'json'
require蕉鸳。類似于include和import,引入一個(gè)模塊忍法。模塊(Module)是一種把方法潮尝、類和常量組合在一起的方式。提供了命名空間饿序、實(shí)現(xiàn)了混入(mixin)
Load和require都能加載衍锚。模塊的狀態(tài)會(huì)頻繁地變化, 我們使用 load 進(jìn)行加載,獲取最新的狀態(tài)嗤堰。不變化的我們用require戴质。
Part2、6個(gè)方法
1踢匣、install_all_flutter_pods
def install_all_flutter_pods(flutter_application_path = nil)
flutter_application_path ||= File.join('..', '..')
install_flutter_engine_pod
install_flutter_plugin_pods(flutter_application_path)
install_flutter_application_pod(flutter_application_path)
end
1告匠、def:定義一個(gè)方法
2、||=:或等离唬,為空則賦值后专,不為空自身
3、File.join('..', '..'):用分割符(默認(rèn)是/)把'..'和'..'組合起來(lái)输莺,返回一個(gè)str戚哎。該例子返回一個(gè)'../..'
2、install_flutter_engine_pod
def install_flutter_engine_pod
current_directory = File.expand_path('..', __FILE__)
engine_dir = File.expand_path('engine', current_directory)
if !File.exist?(engine_dir)
# Copy the debug engine to have something to link against if the xcode backend script has not run yet.
# CocoaPods will not embed the framework on pod install (before any build phases can generate) if the dylib does not exist.
debug_framework_dir = File.join(flutter_root, 'bin', 'cache', 'artifacts', 'engine', 'ios')
FileUtils.mkdir_p(engine_dir)
FileUtils.cp_r(File.join(debug_framework_dir, 'Flutter.framework'), engine_dir)
FileUtils.cp(File.join(debug_framework_dir, 'Flutter.podspec'), engine_dir)
end
# Keep pod path relative so it can be checked into Podfile.lock.
# Process will be run from project directory.
engine_pathname = Pathname.new engine_dir
# defined_in_file is set by CocoaPods and is a Pathname to the Podfile.
project_directory_pathname = defined_in_file.dirname
relative = engine_pathname.relative_path_from project_directory_pathname
pod 'Flutter', :path => relative.to_s, :inhibit_warnings => true
end
1嫂用、__FILE__型凳。這個(gè)變量代表文件自己的文件名,在podhelper.rb中是podhelper.rb嘱函。
2甘畅、File.expand_path('..', 'b')。在b路徑下追加路徑'..',此例是b/..疏唾,也就是返回b的上級(jí)目錄蓄氧。
3、flutter_root槐脏。方法
4喉童、FileUtils.mkdir_p。生成目錄及其所有上級(jí)目錄顿天。例如泄朴,F(xiàn)ileUtils.mkdir_p '/usr/local/lib/ruby'將生成下列所有目錄(若沒(méi)有的話)。 * /usr * /usr/local * /usr/local/bin * /usr/local/bin/ruby
5露氮、FileUtils.cp_r(a,b)祖灰。把文件a拷貝到b,如果目錄b存在畔规,就拷貝到b里面局扶,如果b不存在,就把a(bǔ)拷貝到b叁扫,此時(shí)b是一個(gè)沒(méi)有后綴名的文件
6三妈、Pathname.new engine_dir。等同Pathname.new(engine_dir)莫绣,或Pathname(engine_dir)畴蒲。通過(guò)str生成一個(gè)路徑類Pathname的對(duì)象
7、a.relative_path_from b对室。a相對(duì)于b的相對(duì)路徑模燥,也叫b->a的相對(duì)路徑。Pathname('/Users/bigfly/Desktop').relative_path_from Pathname('/Users/bigfly/Desktop/my_flutter')是'..'
8掩宜、.to_s蔫骂。用來(lái)將對(duì)象以字符串的格式去描述、去輸出牺汤。也就是說(shuō)辽旋,所有對(duì)象都能使用字符串的描述格式。
只要像鴨子檐迟,就能當(dāng)成鴨子补胚,這就是to_x。只有它真的是鴨子追迟,才能當(dāng)成鴨子溶其,這就是to_xxx。例如調(diào)用obj的string方法時(shí)需要先強(qiáng)制轉(zhuǎn)換為string類怔匣,用to_str握联。類似的有to_i和to_int、to_a和to_ary每瞒、to_h和to_hash
總結(jié):該方法是把flutter_root目錄/Users/xxx/flutter/bin/cache/artifacts/engine/ios 下面的Flutter.framework和Flutter.podspec拷貝到flutter模塊工程的engine目錄/Users/bigfly/Desktop/my_flutter/.ios/Flutter/engine下面金闽。然后把Flutter pod進(jìn)來(lái)。如果engine存在就直接pod剿骨,如果不存在代芜,就去flutter_root拷貝一份過(guò)來(lái)再pod。
3浓利、install_flutter_plugin_pods
def install_flutter_plugin_pods(flutter_application_path)
flutter_application_path ||= File.join('..', '..')
# Keep pod path relative so it can be checked into Podfile.lock.
# Process will be run from project directory.
current_directory_pathname = Pathname.new File.expand_path('..', __FILE__)
# defined_in_file is set by CocoaPods and is a Pathname to the Podfile.
project_directory_pathname = defined_in_file.dirname
relative = current_directory_pathname.relative_path_from project_directory_pathname
pod 'FlutterPluginRegistrant', :path => File.join(relative, 'FlutterPluginRegistrant'), :inhibit_warnings => true
symlinks_dir = File.join(relative, '.symlinks')
FileUtils.mkdir_p(symlinks_dir)
plugins_file = File.expand_path('.flutter-plugins-dependencies', flutter_application_path)
plugin_pods = flutter_parse_dependencies_file_for_ios_plugin(plugins_file)
plugin_pods.each do |plugin_hash|
plugin_name = plugin_hash['name']
plugin_path = plugin_hash['path']
if (plugin_name && plugin_path)
symlink = File.join(symlinks_dir, plugin_name)
FileUtils.rm_f(symlink)
File.symlink(plugin_path, symlink)
pod plugin_name, :path => File.join(symlink, 'ios'), :inhibit_warnings => true
end
end
end
1挤庇、flutter_application_path:/Users/xxx/Desktop/my_flutter
2、current_directory_pathname:/Users/xxx/Desktop/my_flutter/.ios/Flutter
3贷掖、project_directory_pathname = /Users/xxx/Desktop/my_flutter/.ios
4嫡秕、symlinks_dir = /Users/xxx/Desktop/my_flutter/.ios/Flutter/.symlinks
5、plugins_file = /Users/xxx/Desktop/my_flutter/.flutter-plugins-dependencies
6苹威、flutter_parse_dependencies_file_for_ios_plugin昆咽。方法。解析.flutter-plugins-dependencies這個(gè)json文件牙甫,返回一個(gè)key鏈plugins/ios的值
7掷酗、FileUtils.rm_f(symlink):刪除/Users/xxx/Desktop/my_flutter/.ios/Flutter/.symlinks/shared_preferences
File.symlink(plugin_path, symlink):在symlink位置創(chuàng)建一個(gè)符號(hào)鏈接,指向plugin_path窟哺,此處plugin_path的值是"/Users/xxx/flutter/.pub-cache/hosted/pub.flutter-io.cn/shared_preferences-0.5.12/"
總結(jié):該方法是泻轰,先pod進(jìn)來(lái)/Users/xxx/Desktop/my_flutter/.ios/Flutter/FlutterPluginRegistrant。然后解析.flutter-plugins-dependencies文件且轨,得到key鏈plugins/ios的值浮声,是個(gè)依賴庫(kù)數(shù)組,遍歷數(shù)組旋奢,對(duì)每個(gè)依賴庫(kù)進(jìn)行刪除實(shí)體文件阿蝶、創(chuàng)建符號(hào)鏈接、pod進(jìn)來(lái)操作黄绩。
4羡洁、install_flutter_application_pod
def install_flutter_application_pod(flutter_application_path)
current_directory_pathname = Pathname.new File.expand_path('..', __FILE__)
app_framework_dir = File.expand_path('App.framework', current_directory_pathname.to_path)
app_framework_dylib = File.join(app_framework_dir, 'App')
if !File.exist?(app_framework_dylib)
# Fake an App.framework to have something to link against if the xcode backend script has not run yet.
# CocoaPods will not embed the framework on pod install (before any build phases can run) if the dylib does not exist.
# Create a dummy dylib.
FileUtils.mkdir_p(app_framework_dir)
`echo "static const int Moo = 88;" | xcrun clang -x c -dynamiclib -o "#{app_framework_dylib}" -`
end
# Keep pod and script phase paths relative so they can be checked into source control.
# Process will be run from project directory.
# defined_in_file is set by CocoaPods and is a Pathname to the Podfile.
project_directory_pathname = defined_in_file.dirname
relative = current_directory_pathname.relative_path_from project_directory_pathname
pod 'my_flutter', :path => relative.to_s, :inhibit_warnings => true
flutter_export_environment_path = File.join('${SRCROOT}', relative, 'flutter_export_environment.sh');
script_phase :name => 'Run Flutter Build my_flutter Script',
:script => "set -e\nset -u\nsource \"#{flutter_export_environment_path}\"\n\"$FLUTTER_ROOT\"/packages/flutter_tools/bin/xcode_backend.sh build",
:input_files => [
File.join('${SRCROOT}', flutter_application_path, '.metadata'),
File.join('${SRCROOT}', relative, 'App.framework', 'App'),
File.join('${SRCROOT}', relative, 'engine', 'Flutter.framework', 'Flutter'),
flutter_export_environment_path
],
:execution_position => :before_compile
end
1、current_directory_pathname = /Users/xxx/Desktop/my_flutter/.ios/Flutter
2爽丹、app_framework_dir = /Users/xxx/Desktop/my_flutter/.ios/Flutter/App.framework
3筑煮、app_framework_dylib = /Users/xxx/Desktop/my_flutter/.ios/Flutter/App.framework/App
4、echo "static const int Moo = 88;" | xcrun clang -x c -dynamiclib -o "#{app_framework_dylib}" -粤蝎。shell命令
5真仲、${SRCROOT},ruby中全局變量用${}引用
6初澎、project_directory_pathname = /Users/xxx/Desktop/my_flutter/.ios
7秸应、flutter_export_environment_path =/Users/xxx/Desktop/my_flutter/.ios/Flutter/flutter_export_environment.sh
8虑凛、script_phase 。給target添加編譯前的shell腳本软啼,腳本內(nèi)容是
set -e #之后的代碼桑谍,一旦出錯(cuò),停止執(zhí)行并退出祸挪,避免后續(xù)一些腳本的危險(xiǎn)操作
set -u #遇到不存在的變量就會(huì)報(bào)錯(cuò)锣披,并停止執(zhí)行
source "#{flutter_export_environment_path}"
"$FLUTTER_ROOT"/packages/flutter_tools/bin/xcode_backend.sh build
總結(jié):該方法是,生成App.framework/app文件如果不存在的話贿条,執(zhí)行了一條shell命令(調(diào)用xcrun clang -o命令生成目標(biāo)文件)雹仿。pod進(jìn)來(lái)my_flutter。給target添加編譯前的shell腳本整以,腳本主要執(zhí)行了xcode_backend.sh build命令
胧辽。
5、flutter_parse_dependencies_file_for_ios_plugin
def flutter_parse_dependencies_file_for_ios_plugin(file)
file_path = File.expand_path(file)
return [] unless File.exists? file_path
dependencies_file = File.read(file)
dependencies_hash = JSON.parse(dependencies_file)
# dependencies_hash.dig('plugins', 'ios') not available until Ruby 2.3
return [] unless dependencies_hash.has_key?('plugins')
return [] unless dependencies_hash['plugins'].has_key?('ios')
dependencies_hash['plugins']['ios'] || []
end
解析.flutter-plugins-dependencies文件公黑,并返回key鏈plugins/ios的值票顾。
6、flutter_root
def flutter_root
generated_xcode_build_settings_path = File.expand_path(File.join('..', '..', 'Flutter', 'Generated.xcconfig'), __FILE__)
unless File.exist?(generated_xcode_build_settings_path)
raise "#{generated_xcode_build_settings_path} must exist. If you're running pod install manually, make sure flutter pub get is executed first"
end
File.foreach(generated_xcode_build_settings_path) do |line|
matches = line.match(/FLUTTER_ROOT\=(.*)/)
return matches[1].strip if matches
end
# This should never happen...
raise "FLUTTER_ROOT not found in #{generated_xcode_build_settings_path}. Try deleting Generated.xcconfig, then run flutter pub get"
end
1帆调、File.foreach讀取文件的每一行并執(zhí)行一個(gè)block奠骄。File.readlines讀取文件的每一行到一個(gè)數(shù)組里面。
2番刊、line.match匹配結(jié)果是個(gè)數(shù)組含鳞,包括兩部分:0完整匹配部分、1匹配到的內(nèi)容
總結(jié):該方法芹务,解析Flutter/Generated.xcconfig文件蝉绷,遍歷每一行,找到FLUTTER_ROOT的值并返回枣抱。
總結(jié):該文件主要有三個(gè)方法:install_flutter_engine_pod熔吗、install_flutter_plugin_pods、install_flutter_application_pod佳晶。分別作用是:不存在就新生成并pod進(jìn)來(lái)engine/Flutter.framework/flutter桅狠、pod進(jìn)來(lái)FlutterPluginRegistrant+plugins、不存在就新生成app.framework/app并pod進(jìn)來(lái)my_flutter轿秧。
思考:
1中跌、更改flutter工程的dart代碼,重新運(yùn)行iOS工程(不是Runner),為什么代碼能生效菇篡?
pod install的時(shí)候漩符,執(zhí)行了podhelper.rb,執(zhí)行了install_flutter_application_pod方法驱还,給target添加了編譯前shell腳本嗜暴。所以pod install 過(guò)后凸克,每次運(yùn)行iOS工程都會(huì)執(zhí)行xcode_backend.sh build命令。