Pod 中資源引入方式對比

2021-03-02 知識小集

寫這篇博客起因是由於上周四在知識小集發了一個 Tip :「 應用 icon 被 Cocoapods 「吃掉」的解決方式 」,講 Pod 裡面使用了 .xcassets 會導致 Xcode 9 打出來的包沒有 icon 的解決方案。然後和 @Damonwong 展開了一下討論。當然這條 Tip 講得不全對,而且表達得不是很清晰,所以這裡專門開一篇文章來講一下那個 Tip 想要表達的意思,最後也會附上對應的 Demo。也當做之後要寫的模塊化系列的文章開篇吧。

首先我們來給出結論:

下面我們先來了解一下這兩種方式的具體情況,以及各自的優劣勢,最後通過一個 Demo 來驗證我們上面給出的結論。

註:本文內容涉及的環境配置為:Xcode 9.2 、Cocoapods 1.4.0;文中描述的資源主要是指 png 格式的圖片。

resource 和 resource_bundle

resource 和 resource_bundle 是 Pod 中引入資源的兩種方式,下面我們圍繞官方文檔做進一步解釋和對比,看看兩者之間的區別。

resource

官方文檔對 resource 的描述如下:

A list of resources that should be copied into the target bundle.

這種方式會將引用的文件夾下的所有資源拷貝到 target 的 bundle 中去,可以簡單的理解為 .app 目錄下或者 .app 的 Assets.car 文件中(如果是 .xcassets 的資源)。

Tips: 注意一下上面加粗的部分。

我們來看一下官方給出的示例:


spec.resource = 'Resources/HockeySDK.bundle'


spec.resources = ['Images/\*.png', 'Sounds/\*']

resource_bundle

官方文檔對 resource_bundle 的描述如下:

This attribute allows to define the name and the file of the resource bundles which should be built for the Pod. They are specified as a hash where the keys represent the name of the bundles and the values the file patterns that they should include.

這種方式可以將指定路徑下的資源打包到以 => 之前的 key 命名的 bundle 中,這個 bundle 最終會被拷貝到 target 也就是 .app 根目錄下。如果有指定 .xcassets 資源,會被打包到以 key 命名的 bundle 裡的 Assets.car 文件中。

Tips: 注意一下上面加粗的部分。

我們來看一下官方給出的示例:


spec.ios.resource_bundle = { 'MapBox' => 'MapView/Map/Resources/*.png' }


spec.resource_bundles = {
   'MapBox' => ['MapView/Map/Resources/\*.png'],
   'OtherResources' => ['MapView/Map/OtherResources/\*.png']
}

對比

其實上面官網的描述我故意漏附了後面的幾段話,文檔上在兩種方式下都強烈(strongly)推薦使用 resource_bundle 的方式。

We strongly recommend library developers to adopt resource bundles as there can be name collisions using the resources attribute.

另外對 resource 還有如下描述:

Moreover, resources specified with this attribute are copied directly to the client target and therefore they are not optimised by Xcode.

而對 resource_bundle 還有如下描述:

The names of the bundles should at least include the name of the Pod to minimise the chance of name collisions.

綜合上述所說,就是使用 resource_bundle 主要有以下兩點好處:

我們來對比下兩種方式下最終打包出來的應用目錄結構:

可以看出 resource_bundle 的形式會生成對應的 bundle(上圖中的 Pod1.bundle),並且 .xcassets 最終會被打包到對應 bundle 下的 Assets.car 文件下(該文件可用這個工具打開:iOS-Images-Extractorhttps://github.com/devcxm/iOS-Images-Extractor )。而 resource 的形式,會把 .xcassets 打包到應用根目錄下的 Assets.car 中。

而要讀取對應的圖片時,resource 對應的代碼長下面這樣:

UIImage *image = [UIImage imageNamed:@"your-image-name"
                           inBundle:[NSBundle bundleForClass:[self class]]
      compatibleWithTraitCollection:nil];

而 resource_bundle 對應的讀取代碼如下面所示:

NSBundle *bundle = [NSBundle bundleForClass:[self class]];
NSURL *url = [bundle URLForResource:@"your-bundle-name" withExtension:@"bundle"];
NSBundle *targetBundle = [NSBundle bundleWithURL:url];
UIImage *image = [UIImage imageNamed:@"your-image-name"
                           inBundle:targetBundle
      compatibleWithTraitCollection:nil];

通過以上兩個代碼片段,相信各位已經知道代碼片段 1、2 都可能存在圖片命名衝突的問題,但是一般情況下片段 2 的衝突概率遠小於片段 1 的。因為一般情況下,模塊內的圖片命名肯定是不會衝突的,而模塊間的圖片的命名就不好說了。當然如果團隊裡有明確的命名規範,片段 1 和片段 2 都不會有問題。

雖然片段 2 代碼較片段 1 稍複雜一些,但是如果我們將其封裝成一個 NSBundle 的分類,就免去了冗長的寫法,對於 Storyboard/Xib 同樣適用。



@interface NSBundle (Pod1Bundle)

+ (NSBundle *)pod1_bundle;

@end



@interface Pod1FakeClass : NSObject
@end
@implementation Pod1FakeClass
@end

@implementation NSBundle (Pod1Bundle)

+ (NSBundle *)pod1_bundle {
   NSBundle *bundle = [self bundleForClass:[Pod1FakeClass class]];
   NSURL *url = [bundle URLForResource:@"Pod1" withExtension:@"bundle"];
   return [self bundleWithURL:url];
}

@end


UIImage *image = [UIImage imageNamed:@"your-image-name"
                           inBundle:[NSBundle pod1_bundle]
      compatibleWithTraitCollection:nil];

示例Demo

最後我們通過一個 Demo 來驗證一下,具體代碼可以查看:https://github.com/wang9262/PodResourceDemo

第一個 commit 寫好了兩個 podspec,一個用 resource_bundle(Pod1),一個用 resource(Pod2),然後都引用 .xassets 資源,裡面都有一個名為 Pod 的圖片,主工程也有。區別在於 Pod 中的圖片頂部會有該 Pod 名稱的水印。然後頁面上有3個 ImageView,目前只設置了中間那個 ImageView 的圖片為主工程的圖片。運行起來,一切表現正常,展示出來的圖片也確實是主工程的圖片。

第二個 commit 在主工程內分別讀取 Pod1 和 Pod2 的名為 Pod 的圖片,然後分別塞到上下兩個 ImageView 中,運行起來,最下面那個 ImageView 的圖片變成主工程的圖片了,而最上面的 ImageView 的圖片是正常的!!!這就是我們上面說到的那個問題,由於 Pod2 使用 resource的方式,.xcassets 中圖片是直接和主工程的 .xcassets 中圖片一樣是打包到 .app 根目錄下的 Assets.car 中,命名一致,導致被主工程的圖片給覆蓋掉了(可以使用上面說到的解壓工具解壓,查看根目錄 .car 下的文件內容,只有主工程的圖片)。

第三個 commit 及 ruby-shell 分支主要解決應用 icon 為空的問題。當我們 Home 鍵回到桌面時,會發現 Demo 的 icon 是空的,但是我們是有設置 icon 的。所以我們有兩種解決方案來解決這個問題。

方案一:不使用 .xcassets

把圖片放到 Pod2 目錄下,不再放到 .xcassets 裡面,然後 podspec 裡的寫法改成:

s.resources = ['Pod2/**/*.png']

pod install 之後再跑一下,發現 icon 回來了,但是圖片還是被主工程覆蓋了,查看 .app 文件,我們發現根目錄下多了兩張圖片,他們來自於 Pod2 目錄下。

相應的我們的讀取代碼就需要改成下面這種形式,還需要區分 @2x 和 @3x,非常麻煩,但是這個時候確實能讀到對應的圖片。


_podImage = [UIImage imageNamed:@"Pod@2x"
                      inBundle:[NSBundle bundleForClass:[self class]]
 compatibleWithTraitCollection:nil];

_podImage = [UIImage imageNamed:@"Pod@3x"
                      inBundle:[NSBundle bundleForClass:[self class]]
 compatibleWithTraitCollection:nil];

方案二:腳本

在 podfile 中加入下面這一段腳本

post_install do |installer|
copy_pods_resources_path = "Pods/Target Support Files/Pods-ResourceDemo_Example/Pods-ResourceDemo_Example-resources.sh"
string_to_replace = '--compile "${BUILT_PRODUCTS_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}"'
assets_compile_with_app_icon_arguments = '--compile "${BUILT_PRODUCTS_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}" --app-icon "${ASSETCATALOG_COMPILER_APPICON_NAME}" --output-partial-info-plist "${BUILD_DIR}/assetcatalog_generated_info.plist"'
text = File.read(copy_pods_resources_path)
new_contents = text.gsub(string_to_replace, assets_compile_with_app_icon_arguments)
File.open(copy_pods_resources_path, "w") {|file| file.puts new_contents }
end

然後 pod install 之後,運行即可。

以上兩種方式執行之前最好 clean(cmd+option+shift+k) 一下,防止 Xcode 緩存,導致以上方法執行後也會出現 icon 消失的情況。

總結

綜合以上,我們再總結一下 resource_bundle 的優勢:

至於劣勢,我覺得根本就沒有,上面那個硬編碼問題,完全可以通過我說的分類或者你自定義宏的方式把這個硬編碼問題 Cover 掉。

如果認為文章中的觀點或結論有問題,歡迎指出,一起溝通探討。

參考連結

官方文檔 https://guides.cocoapods.org/syntax/podspec.html#resources

App Icons not included in build from Xcode 9  https://github.com/CocoaPods/CocoaPods/issues/7003

關於 Pod 庫的資源引用  resource_bundles or resources http://zhoulingyu.com/2018/02/02/pod-resource-reference/

覺得好,就打個賞吧

再關注一下嘍

相關焦點

  • 提升口語的終極大殺器--Englishpod(附資源)
    所以,今天給大家推薦一個提升口語的大殺器-Englishpod。這個材料是我在大學期間偶然了解到,然後去了官網把所有的資源全部下載並且進行了認真學習。在大學期間的口語提升這個材料幫了不少忙,所以真心推薦給更多的英語學習者學習利用。這個資源是我個人覺得非常好的英語學習資料,網上的評價也非常高。
  • k8s 命令詳解(pod,label,job)
    今天和大家詳解講解下pod,label,job。kubectl 命令詳解 pod查看pod資源(簡略)kubectl get pod參數解析NAME pod名READY 準備好的副本數STATUS 狀態RESTARTS 重啟AGE 已經運行的時間查看pod資源(較詳細)kubectl
  • kubernetes-issue-1:ephemeral-storage引發的pod驅逐問題
    說明當前所在work-node資源不容許此pod部署,發生了pod驅逐。B.The node was low on resource: ephemeral-storage.說明了發生pod驅逐的原因。ephemeral-storage(短暫存儲)的概念和作用ephemeral-storage是為了管理和調度Kubernetes中運行的應用的短暫存儲。
  • 細數蘋果HomePod mini與華為AI音箱2產品特點對比
    homepod mini 在智能家居時代,智能音箱作為智慧物聯的中間樞紐,承載著企業進軍智慧家居市場的強烈厚望。華為AI音箱2 作為兩家在智慧家居領域的明星產品,兩者主要特點對比如下: 外觀方面:都極具美學簡約風格,輕盈小巧
  • 解決孩子厭學問題 教育平臺Nearpod引入VR體驗
    虛擬實境技術並不是沒什麼用,老師們已經用它在現實生活中教學了。   「你正在前往火星」,我的虛擬實境課堂「告訴」我。但我實際上是坐在舊金山的Nearpod辦公室,這是一家教育科技公司,已經為數千萬的K12(一家線上教育機構)學生提供了這種體驗。   「每個月有超過300萬的孩子登錄我們的平臺,其中大約60%的人體驗了VR內容。」Nearpod的聯合創始人兼執行長Guido Kovalskys告訴我。   這很了不起,特別是考慮到他們在六個多月前才推出了VR功能。
  • Nearpod宣布收購學生教育創新平臺Floabulary 曾受賈伯斯妻子支持
    總部位於邁阿密的Nearpod公司表示,在Flocabulary的幫助下,它將為課堂帶來更多文化相關和媒體豐富的內容。美國97%地區的教師使用了Nearpod和Flocabulary。Nearpod執行長Guido Kovalskys在一份聲明中說:「制定令人驚嘆的課程計劃是一項艱巨的工作,而用音樂來完成這項工作則要困難上百倍。」
  • 如何利用Englishpod學好英文
    Englishpod 是非常好的練習聽力的材料,相信很多英語愛好者早已有所了解,但據我所知,在材料拿到手之後真正全部學完的人並不多。Englishpod 目前共有365期內容,可以說是比較大部頭的學習資料了。
  • 超高性價比 芙洛蒂pod-100易迅好評特賣中
    第1頁超高性價比 芙洛蒂pod-100易迅好評特賣中  朗易電子廠,一直致力於耳機行業,專注於耳機性能的提高。從產品的設計製造銷售到售後服務,都力求做到最好。一直從事於把耳機性能做得更好價格更便宜的研究。
  • pea是豌豆,pod是莢果,那like two peas in a pod是什麼意思?
    我們知道,pea有「豌豆」的意思,pod有「莢果」的意思,通常豌豆莢就是指a pea pod。那like two peas in a pod是什麼意思呢?like two peas in a pod的意思是「very similar, especially
  • 【聚焦語文真問題】「語文學習資源:整合與使用」之三│李凌雲:語文學習資源引入課堂的教學策略
    【摘   要】將語文學習資源引入課堂,是達成課堂目標常用的教學手段之一。但在實施過程中,有的教師引入的時機和呈現的方式不夠恰當,影響了效果。有效地用好學習資源,需要教師對資源進行考量,對呈現時機、狀態進行設計。
  • Englishpod——中高級英語聽力材料
    今天向大家推薦中高級播講類材料——Englishpod。Englishpod 是由Praxis Language公司創立的一個播客節目,並聘請專業的聲優和音效師,製作語言學習相關的播客內容。Englishpod包含多種主題,主要為日常生活、旅遊文化、情感問題、商務面試等。
  • drag Nano pod電子菸怎麼樣
    drag nano pod電子菸問:為什麼我抽drag nano pod的時候有刺啦刺啦的聲音?問drag Nano pod電子菸工作原理?drag Nano pod電子菸用戶反饋四川廣元苟老闆說:東西很好包裝各個方面都很嚴密給男朋友買的他已經迫不及待想試試看怎樣 寧夏銀川費老闆說:買替煙產品,充好電馬上試抽了下,跟描述一樣,煙霧挺大,口感也滿意,希望耐用。
  • 俚語two peas in a pod什麼意思?
    ◆◆◆◆◆◆◆◆◆◆◆◆◆◆◆◆◆◆◆微學小貼士——想聽懂看懂美劇和電影嗎
  • 美國VR教育公司Nearpod收購音樂教育內容公司Flocabulary
    美國VR教育公司Nearpod收購音樂教育內容公司Flocabulary 作者:Power教育 發布時間:
  • 在 TKE 中使用 Velero 遷移複製集群資源
    注意事項從 1.5 版本開始,Velero 可以使用 Restic 備份所有pod卷,而不必單獨注釋每個 pod。在執行遷移過程中,請不要對兩邊集群資源做任何 CRUD 操作,以免在遷移過程中造成數據差異,最終導致遷移後的數據不一致。儘量保證集群 B 和集群 A 工作節點的 CPU、內存等規格配置相同或不要相差太大,以免出現遷移後的 Pods 因資源原因無法調度導致 Pending 的情況。
  • 引入「比率」概念,讓數據對比更嚴謹
    在數據分析中,單個欄位的對比在特殊場景下並不嚴謹,還有可能產生錯誤的分析與判斷。而引入比率概念後,我們則可以更加準確地識別數據,做數據分析。在上篇文章中,和大家一起分析了常見的「數據對比」,也通過「老王女裝店中靜默銷售額」的案例提出單個欄位的數據對比,具有迷惑性,存在陷阱。
  • 【方法】練習Englishpod時的注意點
    Englishpod裡面的生詞量和短語量,還是比較多的,這也就是為什麼建議大家要重複聽的原因。 2.Englishpod第3部分的前半部分,有對於詞和短語的英英解釋,如果你想掌握純熟一點,就可以對這一部分進行聽寫練習。 3.不用太拘泥於要聽幾遍或者是聽的順序怎麼樣,只要最終把這些陌生的內容掌握就可以了。
  • 「peas in a pod」是雙胞胎的意思嗎?哥倆好的英語口語表達
    peas in a podPea: a small round green seed,eaten as a vegetable .Several peans grow together inside a long thin Pod on a climbing plant also called
  • 讓手機變身教育工具,教育科技創企Nearpod獲2100萬美元B輪融資
    Nearpod執行長Guido Kovalskys與聯合創始人Felipe Sommer以及Emiliano Abramzon在2012年成立了這家公司,Guido Kovalskys則表示:「無需繁瑣操作,教師就可以輕鬆將軟體技術應用與交互式內容納入日常教學模式中。」此外,該平臺宣稱免費向教師群體開放,不過也有些老師以及一些學校會支付訂閱Nearpod精品課程或特色項目。