1. 程式人生 > >pbxprojHelper--Xcode 工程檔案助手

pbxprojHelper--Xcode 工程檔案助手

pbxprojHelper 可以幫你快速配置 Xcode 工程檔案,省去麻煩的人工手動操作。專案開源,使用 Swift 開發,詳細介紹請見使用說明。除了 Mac App 外還提供了命令列工具 pbxproj,它集成了 pbxprojHelper 的核心功能,同樣簡易實用。

因為 README_ZH 中對使用方法已經講得很詳細了,這裡重點說的是產品方案和技術實現。

產品方案

為什麼造這個工具?

在開發公司的專案時,check out 程式碼到本地後需要修改工程檔案。比如更改證書和 Bundle Identifier、刪除一些編譯不過的 Target,修改 Build Settings 等配置。重複手動修改這些配置的場景很多:

  1. 第一次 check out 新的分支,需要使用自己的配置。
  2. 增刪程式碼檔案前會先 revert project.pbxproj 檔案,修改完成後再 commit。此時本地工程檔案需要重新配置。
  3. 沒有增刪程式碼檔案但 project.pbxproj 檔案有衝突(conflict),需要先 revert 後重新配置工程檔案。
  4. 一些自動化流程(比如 CI)每次執行都需要特定的編譯選項和證書來編包。

而我本人最常遇到的場景是 1 和 2,因為不能用公司的證書配置來編譯,一些跟蘋果開發者賬號相關的功能導致一些 target 編譯不過,還有些 debug 模式下需要設定的編譯選項。所以每次都需要手動修改 Xcode 工程配置,很是麻煩。

需求!

可以說開發這個工具一開始完全就是為了解決我個人的痛點的,基本沒考慮做成功能強大的通用工具。雖然做的事情比較小眾,但也能滿足一批蘋果開發者的需求了。我把需求分為以下幾點:

  1. 將程式設計師對工程檔案做出的配置修改記錄下來,並儲存成 JSON 檔案
  2. 下次使用時直接匯入 JSON 檔案,將配置修改應用到當前的工程檔案上
  3. 支援回滾操作
  4. 支援工程檔案內容的預覽、過濾
  5. 快速切換最近使用的工程
  6. 提供命令列工具

可以說 1 和 2 是剛需,也是常用功能。3、4 和 5 是輔助功能,6 是附加需求。我平時最常碰到的需求點就是 2 和 5 了。

技術實現

我把工程檔案相關的底層方法都封裝在 PropertyListHandler

類中,它們跟介面無關。還有一些工具類和方法寫到 Utils 檔案中。

對比工程檔案

想要記錄工程檔案的修改是很難的,所以只能是比較下兩個工程檔案的差異。這裡不是對比檔案那種簡單的 diff 操作,而是要記錄具體針對哪個配置項做了『增刪改』。

工程檔案的內容可以比作一顆多叉樹,的根節點是字典,其餘中間節點都是字典的鍵。陣列的元素肯定是字串(葉子節點),字典的鍵值對則可能繼續拓展出子樹,也可能是葉子節點。在拿到兩個工程檔案的資料後,就需要對兩棵樹的每個層級進行對比。對比兩顆樹的差異演算法不難實現,核心思想是:在對比中間節點時,如果內容相同那就遞迴比較下一層,否則就記為『增』或『刪』

而比較同一層級中間節點的差異,直接用 Set 是最方便的了。我將兩棵樹的差異儲存在字典 difference 中,在內嵌方法中又實現了個尾遞迴。遞迴過程中需要記錄中間節點作為路徑,因為生成的路徑需要儲存到對比結果中。

Objective-C
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778 /// 將 project 與 other project 做比較////// - parameter project1: 作為比較的 project/// - parameter project2: 被參照的 project////// - returns: project1 相對於 project2 的變化classfunc compare(project project1:[String: Any],withOtherProject project2:[String: Any])->Any{var difference=["insert":[String: Any](),"remove":[String: Any](),"modify":[String: Any]()]/// 將兩個資料物件作遞迴比較,將最深層次節點的差異儲存到 difference 中。////// - Parameters:///   - data1: 第一個資料物件,陣列或字典///   - data2: 第二個資料物件,陣列或字典///   - parentKeyPath: 父路徑func compare(data data1: Any?,withOtherData data2: Any?, parentKeyPath: String){iflet dictionary1=data1 as?[String: Any],let dictionary2=data2 as?[String: Any]{let set1=Set(dictionary1.keys)let set2=Set(dictionary2.keys)for key inset1.subtracting(set2){iflet value=dictionary1[key],difference["insert"]?[parentKeyPath]==nil{difference["insert"]?[parentKeyPath]=[key: value]}elseiflet value=dictionary1[key],var insertDictionary=difference["insert"]?[parentKeyPath] as?[String: Any]{insertDictionary[key]=valuedifference["insert"]?[parentKeyPath]=insertDictionary}}for key inset2.subtracting(set1){ifdifference["remove"]?[parentKeyPath]==nil{difference["remove"]?[parentKeyPath]=[key]}elseifvar removeArray=difference["remove"]?[parentKeyPath] as?[Any]{removeArray.append(key)difference["remove"]?[parentKeyPath]=removeArray}}for key inset1.intersection(set2){let keyPath=parentKeyPath==""? key :"\(parentKeyPath).\(key)"// values are both String, leaf nodeiflet str1=dictionary1[key] as?String,let str2=dictionary2[key] as?String{ifstr1!=str2{difference["modify"]?[keyPath]=str1}}else{// continue compare subtreescompare(data: dictionary1[key], withOtherData: dictionary2[key], parentKeyPath: keyPath)}}}iflet array1=data1 as?[String],let array2=data2 as?[String]{let set1=Set(array1)let set2=Set(array2)for element inset1.subtracting(set2){ifdifference["insert"]?[parentKeyPath]==nil{difference["insert"]?[parentKeyPath]=[element]}elseifvar insertArray=difference["insert"]?[parentKeyPath] as?[Any]{insertArray.append(element)difference["insert"]?[parentKeyPath]=insertArray}}for element inset2.subtracting(set1){ifdifference["remove"]?[parentKeyPath]==nil{difference["remove"]?[parentKeyPath]=[element]}elseifvar removeArray=difference["remove"]?[parentKeyPath] as?[Any]{removeArray.append(element)difference["remove"]?[parentKeyPath]=removeArray}}}}compare(data: project1,withOtherData: project2,parentKeyPath:"")returndifference}

這段看似很長的程式碼其實邏輯超級簡單,就是分別針對字典和陣列兩種情況進行比較而已,弱智的一逼。需要注意的是陣列內容作為葉子節點,只存在『增』和『刪』兩種情況。

每次遞迴都將 parentKeyPath 與當前節點的值 key. 拼接在一起。也就是說最後得到的路徑是 A.B.C 這種格式。

可以看出生成的對比結果是個字典,包含三個鍵值對,鍵分別是 insertremovemodify,值為字典。

應用 JSON 配置

因為生成的 JSON 配置檔案具有一定格式,所以必須按照格式規則來應用這些配置到工程檔案中。最關鍵的是在上一步中生成的路徑格式為 A.B.C,且路徑內容是未知的,需要實時處理。所以我寫了個方法來解析路徑,步入到路徑最底層後提供閉包來對路徑的值進行修改。假設 keyPath 為路徑字串內容,方法實現如下:

Objective-C
123456789101112131415161718192021222324252627 let keys=keyPath.components(separatedBy:".")/// 假如 command 為 "modify" keyPath 為 "A.B.C",目的是讓 value[A][B][C] = data。需要沿著路徑深入,使用閉包修改葉子節點的資料,遞迴過程中逐級向上返回修改後的結果,完成整個路徑上資料的更新。////// - parameter index:    路徑深度/// - parameter value:    當前路徑對應的值/// - parameter complete: 路徑終點所要做的操作////// - returns: 當前路徑層級修改後的值func walkIn(atIndex index: Int,withCurrentValue value: Any, complete:(Any)->Any?)->Any?{ifindex keys.count{let key=keys[index]        iflet dicValue=value as?[String: Any],let nextValue=dicValue[key]{var resultValue=dicValueresultValue[key]=walkIn(atIndex: index+1, withCurrentValue: nextValue, complete: complete)returnresultValue}else{print("Wrong KeyPath")}}else{returncomplete(value)}returnvalue}

這個方法會將當前層級(index)路徑的節點作為鍵(key),並查詢字典中該鍵對應的值(nextValue)。然後遞迴遍歷下一層,直至步入到路徑(keypath)最末端。此時會執行傳入的 complete 閉包,並將結果作為該方法的返回值。這樣在對路徑最末端的節點值做出修改後就可以逐層同步上去,最後完成對整條路徑的修改。

如果能直接給 value[A][B][C] 賦值就好了,但是這是不可能的。因為路徑內容是未知的,這樣的程式碼不可能寫死的,只能動態地遞迴進去,並在呼叫後將修改內容返回上層。

之前提到過 JSON 檔案格式中包含三種命令:insertremovemodify。所以在實現 complete 方法的時候需要針對這三種命令分別處理,每種命令還要區分字典和陣列兩種資料型別。這裡處理的邏輯基本是上一步的逆邏輯,很容易理解。

Objective-C
1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253

相關推薦

pbxprojHelper--Xcode 工程檔案助手

pbxprojHelper 可以幫你快速配置 Xcode 工程檔案,省去麻煩的人工手動操作。專案開源,使用 Swift 開發,詳細介紹請見使用說明。除了 Mac App 外還提供了命令列工具 pbxproj,它集成了 pbxprojHelper 的核心功能,同樣簡易實用。 因為

Xcode】提交svn以後xcode工程檔案打不開

已經兩次遇到這個錯誤了,目測以後還會再遇到,整理一下。 提交svn時有版本或檔案衝突,更新svn後會出現這個錯誤,啥話也不說,啥提示也不給,就是告訴你“本大爺打不開” 解決方法如下: 1、找

Qt工程生成xcode工程檔案

前言 xcode 是 Mac 下重要的開發工具,若是用 Qt 開發 Mac/ios 的 App 時,可以使用 xcode 軟體進行除錯或者配置一些平臺屬性等等,畢竟是蘋果自己的開發工具,使用肯定會比較方便,那麼,該如何在 xcode 軟體中開啟 Qt 的工程的

通過Xcodeproj深入探究Xcode工程檔案

前言上文介紹了Xcode的配置檔案project.pbxproj裡面的內容並且提到了Cocoapods正是利用Xcodeproj這個元件實現修改該檔案達到改變Xcode工程結構的效果。本文將著重介紹Xcodeproj這個元件,通過本文你將會了解這個元件的內容、原理和使用該元件

通過Xcodeproj深入探究Xcode工程檔案

你是否好奇Cocoapods是如何修改掉Xcode工程的結構?你也是否曾被Xcode工程的配置檔案裡面雜亂的內容搞得摸不清頭腦?你又是否知道Xcodeproj這個神奇的Ruby庫?下面我將通過這個系列來解除你的困惑。 Cocoapods是如何修改Xcode工程結構的?

Mac下SVN提交xcode工程程式碼需要過濾的檔案

SVN客戶端工具我用的是cornerstone 在.subversion-->config中需要設定下面兩段 1.找到下面一行 ### Section for configuring automatic properties. [auto-props] 2.在後面

xcode工程檔案怎麼匯入到vs2012中

從mac上建立的工程移植到vs2012上會存在很多問題。 首先是編碼問題: mac上的編碼格式是:utf-8 vs上的編碼格式是:Uincode 所以兩種編碼格式不同,導致移植到過程中,會存在了編碼格式不正確,如果在vs上執行,一般都會修成Uincode 的編碼格式,但是我

iOS Xcode工程新增.a檔案引起錯誤的幾種修改方法(一般是微信的.a靜態庫出錯)

Ios Xcode工程新增.a檔案引起錯誤的幾種修改方法(一般是微信的.a靜態庫出錯) 一、     TARGETS -> Build Settings-> Search Paths下 1.  Library Search Paths 刪除不存在的路徑,保留.a

xcode左側不顯示工程檔案目錄,提示NO Filter Results

解決辦法:What solved was to go to Navigate > Reveal in Project Navigator. After this, the structure

Xcode-工程新增預編譯標頭檔案Prefix.pch

人生苦短,道阻且艱;修行不易,且行且努力。 【專業擅長領域】:iOS開發,遊戲開發,圖形學 【擅長平臺】:iOS平臺,Unity --------------------------------------------------------- 【個人主頁】:信厚

xcode工程編譯錯誤:No architectures to compile for

bis clear 文檔 哪些 i386 提高 href nts b2c 問題 開發環境:xcode6,iPhone6模擬器 xcode工程編譯錯誤:No architectures to compile for (ONLY_ACTIVE_ARCH=YES, active

xcode工程編譯錯誤:一般錯誤總結

content 增加 style csdn val 解決 environ 方框 ron 1.Apple LLVM 8.0 Error Group /’all-product-headers.yaml’ not found 最近升級了xcode打包後出現了個BUG,記錄解

xcode工程編譯錯誤:"An instance 0xca90200 of class UITableView was deallocated while key value observers were still registered with it"

開始 debugger eve locate obj bsp new 列表 ade An instance 0xca90200 of class UITableView was deallocated while key value observers were still

iOS Xcode工程目錄的 folder 和 group的區別

ios strip ogr get 操作 sources 本地 對話框 遇到 在使用AFN的時候遇到的一個小問題,就是因為folder和group的區別。 出現的問題在上一篇文章:AFN基本使用先看一張區別的圖: Snip20150705_1.png 藍色

iOS - 將Unity匯出的Xcode工程匯入到另一個Xcode專案, 及常見報錯的解決方法

demo下載地址 http://pan.baidu.com/s/1pLcpKpl 1.Unity匯出工程時設定bundle id要與專案一致 2.修改bit code為NO 3.刪除Main.storyboard,程式碼設定控制器(方便切

Tomcat配置虛擬路徑,使上傳檔案與伺服器及工程檔案分離開

原文連結:https://my.oschina.net/pingdy/blog/381001 摘要: 現在一般的專案都可能會涉及檔案的上傳與下載,那如何管理這些檔案呢?做法各有千秋!今天我也分享下我是怎麼去管理這些檔案的!當然了,這樣的方式只適用於檔案量小的情況下!如果檔案數量非

STM32F407VET6之IAR之ewarm7.80.4工程建立(基於官方韌體庫1.6版本) 的工程檔案目錄

最後整理結構如下所示,├─cmsis│ startup_stm32f401xx.s│ startup_stm32f40xx.s│ startup_stm32f40_41xxx.s│ startup_stm32f410xx.s│ startup_stm32f411xe.s│ startup_stm32f427x

Quartus II FPGA指定工程檔案路徑

實驗環境(藍色粗體字為特別注意內容) 1,環境:Windows 7 Ultimate 32 bit、QuartusII 13.0.1 win32、ModelSim SE 10.1a win32 2,參考文獻:https://zhidao.baidu.com/question/74947355

vs2008哪些工程檔案可以刪除

vs2008哪些工程檔案可以刪除 https://social.microsoft.com/Forums/es-ES/5fccd6b3-4992-46fa-90c9-4b93015fd197/vs20082173820123240373124325991202142148720197210243

vs工程檔案轉Qt工程檔案

以後會用到這個功能,記下一些文章資源: 如何將Visual Studio的工程轉成QT的工程 http://blog.csdn.net/nirendao/article/details/50558034 Visual Studio 2010 C++ 工程檔案解讀 http