Swift:巧用module.modulemap,告別Bridging-Header.h
我報名參加金石計劃1期挑戰——瓜分10萬獎池,這是我的第1篇文章,點選檢視活動詳情
前言 專案背景
專案裡面有這麼一個需求,在一個App專案中建立多個Static Library,各司其職進行模組與職責劃分。
別問為啥沒有使用私有庫Cocopods進行,反正目前就是為了方便後續各個Static Library,可以隨便拖動到其他專案中進行復用。
然後,問題來了。
問題:在Static Library無法引用友盟的framework
為了便於說明與演示,我特別建立了一個Demo,通過截圖進行講解。
我有個專案叫做TestUM,裡面包含一個SomeSDK,我希望在SomeSDK裡面,包含高德地圖和友盟統計的功能。
於是乎,我在Podfile檔案中進行了配置:
``` target 'SomeSDK' do
# Comment the next line if you don't want to use dynamic frameworks
use_frameworks!
pod 'AMapSearch', '= 8.1.0' pod 'AMapLocation', '= 2.8.0'
pod 'UMCommon', '~> 1.3.4.P' pod 'UMSPM' pod 'UMCCommonLog'
end
target 'TestUM' do
# Comment the next line if you don't want to use dynamic frameworks
use_frameworks!
# Pods for TestUM
end
``
注意,進行Pod的target是
SomeSDK而非
TestUM,**但是實際上
TestUM`也是能引用高德與友盟的庫。**
最後,根據友盟整合的檔案,需要新增橋接檔案進行處理:
在TestUM下,我通過import AMapFoundationKit
,我們可以順利的呼叫高德的相關API,因為橋接了友盟,我也可以順利的呼叫友盟的相關API:
然而,在SomeSDK下,因為可以import AMapFoundationKit
,我依舊可以呼叫高德,但是友盟卻怎麼也點不出來了:
我嘗試在SomeSDK也建立一個類似主工程中Bridging-Header.h
的檔案,對友盟進行橋接,然而得到的卻是編譯錯誤using bridging headers with framework targets is unsupported
。
不支援,這條路被堵死了。
如果橋接行不通,SomeSDK就無法使用友盟統計的功能,只能將其相關業務移植到主工程去,這明顯不符合公司要求。
領導就一句話:高德可以,友盟為什麼不行?
現在回頭看看,為何高德地圖的既可以在TestUM又可以在SomeSDK中進行引用——因為它能在工程中的*.swift
檔案中進行import
。
而友盟在通過TestUM-Bridging-Header.h
檔案進行橋接後,在TestUM
主工程的.swift
檔案中,無需import,直接呼叫即可,但是在SomeSDK
的子工程中無法呼叫。
高德與友盟的架包到底有何差異?🤔🤔🤔
AMapFoundationKit.framework與UMCommon.framework對比
其實高德與友盟的Pod引用還是非常相似的,因為都是封裝的靜態庫,Pod整合的都是非開源的.framework架包。
這裡我們將AMapFoundationKit.framework與UMCommon.framework做一下對比:
| 高德 | 友盟 | | --- | --- | | | | | | | | | |
- 通過Xcode展開工程看,Pod中,
AMapFoundationKit.framework
不僅展示了Frameworks資料夾,同時暴露的.h檔案也顯示了,而UMCommon.framework
沒有顯示.h檔案。 - 通過
AMapFoundationKit.podspec.json
與UMCommon.podspec.json
,我們會發現雖然兩者都是.framework
的pod整合方式,但是在配置引數的差異方式決定了顯示不同。 - 看.framework的檔案結構,很明顯的發現
AMapFoundationKit.framework
比UMCommon.framework
多一個Module資料夾!
就讓我們看看,這個Module資料夾下面吧。
裡面就只有一個module.modulemap
檔案,裡面長這樣:
關於umbrella header
大家可以看看參考文件What is an umbrella header?,它的功能就是將AMapFoundationKit.h
裡面暴露的.h
檔案,通過迴圈都暴露出來。
AMapFoundationKit.h
裡面長這樣:
回想一下,我們可以在*.swift
檔案中可以import AMapFoundationKit
是不是因為有module.modulemap
中的配置緣故?
帶著這個問題,我去搜索了一下module.modulemap
的相關資料。
在一篇文章中我找到相關的資訊與靈感:
As Bridging-Header can help us in App Target and App Test Target, not in static library or dynamic libraries to use the Objective C / C APIs into Swift classes, modulemap can help us here.
通過理解,Pod這種.framework
的靜態庫,在主工程的應用可以通過橋接解決,而在主工程的的static library則需要通過modulemap來進行解決。
為UMCommon.framework手搓一個module.modulemap
本著死馬當活馬醫的想法,我想為UMCommon.framework手搓一個module.modulemap
。
首先我特地看了一下UMCommon.framework中Headers裡面的檔案:
抱著試一試的態度,我新建了Modules一個資料夾,並寫了這樣一個檔案,注意我並沒新增所有的.h檔案,只是為了方便測試。
``` framework module UMCommon {
header "MobClick.h"
header "UMConfigure.h"
header "UMCommon.h"
export *
} ```
然後將其放到對應的UMCommon.framework。
見證結果的時刻來了,編譯,試著import,成功了!
我們甚至可以,點選看看這個import UMCommon
。
MobClick
類已經完美通過Swift表示了。
而且此時,我們可以把主工程裡面的Bridging-Header.h
裡面橋接檔案註釋掉(甚至將這個.h
檔案刪除),在*.swift
中import
對應的類,即可成功引入與呼叫!
總結
-
將Pod中的某些需要橋接的庫,通過手搓一個
module.modulemap
,我們完全有能力抹去橋接操作,但是同時這樣有一個問題,一旦Pod的庫,升級或者檔案進行了變更,自行寫的module.modulemap
可能也需要更改。而且更改Pod下的庫的檔案,也不太符合操作規則。
另外,大家可以嘗試把
AlipaySDK.framework
通過這種方式去除橋接試試,原理都是一樣的,就當練手。 -
還有一種方式就是自己建立一個私有的Spec,自己新增
module.modulemap
後,進行pod庫管理,但是這樣還是避免不了上游更新,私有庫也要同步更新的問題。
最好的Pod整合方式,就像高德的庫,官方將podspec
配置好,使用者直接傻瓜pod install
就好了。
參考文件
Swift Objective C interoperability, Static Libraries, Modulemap etc…
自己寫的專案,歡迎大家star⭐️
RxStudy:RxSwift/RxCocoa框架,MVVM模式編寫wanandroid客戶端。
GetXStudy:使用GetX,重構了Flutter wanandroid客戶端。
- Swift:UIScrollView UIStackView構建可滑動頁面
- Swift:通過Protocol封裝統和入參
- 解決CocoaAsyncSocket在iOS16系統上的崩潰問題
- Swift:巧用module.modulemap,告別Bridging-Header.h
- WWDC22 | session 110357 | 邂逅Swift Regex
- WWDC22 | session110354 | Swift Regex簡介
- 突然插播:Flutter與JS互調
- Swift與H5互動:跳轉攔截完成支付功能
- 遠端控制,MQTT除錯
- 遠端控制,Server與App網路通訊初步調研
- Swift:基石庫——R.swift
- iOS無感知上拉載入更多功能實現
- Swift:解包的正確姿勢
- UI = f(State),在Swift中的一點思考
- Apple登入與實際業務結合的一點開發總結
- iOS工程無用資源圖片清理軟體推薦
- RxSwift學習——Schedulers (排程器)
- 重要!後面幾個月,iOS開發需要注意的3件事情
- 高德地圖才整改完了,Bug就來了
- 個人資訊保護法頒佈了,App的高德SDK整改