Xcode 8 工程的一些整理

最近對我Xcode中的工程進(jìn)行了整理,這不Xcode 9也要出來了,先總結(jié)一下做個(gè)記錄。

做這些事,是為了解決我目前遇到的問題。簡單描述下問題,一個(gè)工程,里面有多個(gè)target(20+而且還會(huì)增長),每個(gè)發(fā)行的App都用對應(yīng)的target打包。這些App,bundle id不同,icon不同,launch screen不同,以及連接的服務(wù)器不同,有個(gè)別的幾個(gè)由于特殊的需求,代碼中有宏進(jìn)行分支。這些icon,launch screen,和服務(wù)器等配置都是通過別的方式動(dòng)態(tài)提供,并不放在我工程的源碼里(因?yàn)槊總€(gè)target都不同,且隨時(shí)可能發(fā)生變化,我的源碼不需要維護(hù)這個(gè)變化)。

我原來的方法是完成下面幾步:
1,在xcode中depulicate一個(gè)模板target;
2,獲取這個(gè)新target對應(yīng)的icon等配置;
3,menu中File->Add將Assets,launchScreen.storyboard,plist等文件都添加給這個(gè)target;
4,Podfile中加入這個(gè)target,并執(zhí)行pod install
這樣的方法,很麻煩,步驟多,容易錯(cuò),而且,當(dāng)需求比較密集的時(shí)候,我真的是很煩這種重復(fù)的手工勞動(dòng)。

不得已,我必須改變現(xiàn)在工程配置的方式,現(xiàn)在問題已經(jīng)完美解決,整個(gè)工程里,只有一個(gè)target,配合一個(gè)腳本,可以進(jìn)行茫茫多target的管理了。

廢話不多說,下面是這次實(shí)踐中的一些關(guān)鍵點(diǎn)的總結(jié):
1,Cocoa Pods 中,多個(gè)target依賴相同的第三方庫,Podfile文件的寫法:

abstract_target 'defaults' do
    platform:ios,'8.0'

   # Podfile是Ruby腳本,此處列出所有需要使用第三方庫依賴的target
    targetsList = ['target1', 'target2']
    targetsList.each do |t|
        target t do
           # 這些target需要依賴的第三方庫
            pod 'AFNetworking'
        end
    end
end

或者也可以寫成這樣,方便對個(gè)別target進(jìn)行單獨(dú)依賴庫配置:

abstract_target 'defaults' do
    platform:ios,'8.0'

    pod 'AFNetworking'

    targetsList = ['target1', 'target2']
    targetsList.each do |t|
        target t do
              pod 'XXXXX'
        end
    end
end

然后在控制臺執(zhí)行:

pod install

生成靜態(tài)庫自動(dòng)加入到target中,可以通過TARGETS->General->Linked Frameworks and Libraries 中查看被加入文件libPods-default-target1.a

2,對工程文件的修改
工程文件位于MyProject/MyProject.xcodeproj中。右鍵->顯示包內(nèi)容,可以看到里面的文件project.pbxproj。這個(gè)文件就是xcode的工程文件,可以用編輯器打開。注意到其中的編譯configuration:

xxxxxyyyyyzzzzz /* Debug */ = {
            isa = XCBuildConfiguration;
            baseConfigurationReference = aaaaabbbbbbcccc /* Pods-defaults-target1.debug.xcconfig */;
            buildSettings = {
                ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
                ASSETCATALOG_COMPILER_LAUNCHIMAGE_NAME = LaunchImage;
                CODE_SIGN_ENTITLEMENTS = MyProject/target1.entitlements;
                CODE_SIGN_IDENTITY = "iPhone Developer";
                "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";
                DEVELOPMENT_TEAM = XXXXXXXX;
                ENABLE_BITCODE = NO;
                HEADER_SEARCH_PATHS = "$(inherited)";
                INFOPLIST_FILE = "$(SRCROOT)/Info.plist";
                IPHONEOS_DEPLOYMENT_TARGET = 8.0;
                LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks";
                LIBRARY_SEARCH_PATHS = "$(inherited)";
                OTHER_CFLAGS = "";
                OTHER_LDFLAGS = (
                    "$(inherited)",
                    "-ObjC",
                    "-framework",
                    "\"AVFoundation\"",
                    "-framework",
                    "\"UIKit\"",
                    "-all_load",
                );
                PRODUCT_BUNDLE_IDENTIFIER = "com.myproject.target-1";
                PRODUCT_NAME = "$(TARGET_NAME)";
                PROVISIONING_PROFILE = "";
            };
            name = Debug;
        };

以上是工程文件中的其中一段,是編譯Debug版本時(shí)使用的配置:

# App icon使用的assets文件,在TARGETS->General->App Icons and Launch Images中選中的值
ASSETCATALOG_COMPILER_APPICON_NAME 

# Launch Images,同上
ASSETCATALOG_COMPILER_LAUNCHIMAGE_NAME

# 簽名文件,如果有push這種需要單獨(dú)簽名的功能,在Capabilities中打開push開關(guān)后,即生成一個(gè)entitlements文件,與target同名。如果target要使用另外的entitlement文件,在Xcode中配置即可,配置路徑TARGETS->Build Settings->Signing->Code Signing Entitlements
CODE_SIGN_ENTITLEMENTS

# 開發(fā)者ID,TARGETS->General->Signing中配置的開發(fā)者
DEVELOPMENT_TEAM

# target使用的Info.plist,可以放在其他路徑。加入方式是在menu->File->Add添加plist文件進(jìn)工程,然后將target正在使用的plist文件刪掉,這時(shí)Xcode->TARGETS->General會(huì)提示沒有plist,點(diǎn)擊button,在彈出的列表中選擇剛才加入的plist文件即可。選中后,General中會(huì)列出plist中的信息,而且與plist有關(guān)的文件的path也會(huì)被同步更新,不需要手動(dòng)進(jìn)行修改。
INFOPLIST_FILE

# 編譯選項(xiàng),可以加入宏控制語句,寫法為-DXXXXX,在代碼中就可以使用 #ifdef XXXXX #endif 或者 #define #endif。與C相同。
OTHER_CFLAGS

# link選項(xiàng),一般使用pod第三方庫后,會(huì)自動(dòng)被加入一些鏈接選項(xiàng)
OTHER_LDFLAGS

# App bundle ID,在TARGETS->General中顯示的Bundle Identifier。Xcode 8中,Info.plist中對應(yīng)的字段為:
# <key>CFBundleIdentifier</key>
# <string>$(PRODUCT_BUNDLE_IDENTIFIER)</string>
# 即plist中使用project文件中的這個(gè)字段,所以如果要修改bundle id,最好是通過project文件修改此字段,而不要直接修改plist文件
PRODUCT_BUNDLE_IDENTIFIER

我現(xiàn)在的管理工程和打包指定target的方法就是,在終端運(yùn)行一個(gè)我自己寫的腳本,主要?jiǎng)幼魇牵?br> 1,獲取指定target的配置包(包含icon,launch screen,plist等),plist中的display name每個(gè)target不同,其它內(nèi)容都相同;
2,把配置包中的文件都拷貝到相應(yīng)的路徑,確保project文件能找到這些文件(如果找不到,在打開xcode工程后,就開始報(bào)錯(cuò),提示找不到文件);
3,替換project文件中的PRODUCT_BUNDLE_IDENTIFER, 我的經(jīng)驗(yàn)是,不要用編輯器打開后,手動(dòng)替換,那樣保存后,project可能就不能用了(沒有具體研究,猜想是編碼等的問題),使用awk和sed進(jìn)行替換,bash腳本如下:

BUNDLE_ID=com.myproject.target-x

# 從工程文件 MyProject.xcodeproj/project.pbxproj 中獲取舊的bundle id
OLD_BUNDLE_ID=$(awk -F '=' '/PRODUCT_BUNDLE_IDENTIFIER/ {print $2; exit}' MyProject.xcodeproj/project.pbxproj | awk -F'"' '{print $2}')

# 替換,注意Mac上sed -i 后需要跟一個(gè)空串"",而且如果是在腳本中,后面最好使用雙引號(在控制臺上測試語句時(shí)用單引號沒問題,但是在腳本中也用單引號就不行了,必須雙引號)
sed -i "" "s/${OLD_BUNDLE_ID}/${BUNDLE_ID}/g" MyProject.xcodeproj/project.pbxproj

# 獲取新的bundle id
NEW_BUNDLE_ID=$(awk -F '=' '/PRODUCT_BUNDLE_IDENTIFIER/ {print $2; exit}' MyProject.xcodeproj/project.pbxproj | awk -F'"' '{print $2}')

4,Xcode中的scheme是在打開Xcode時(shí)自動(dòng)創(chuàng)建的(auto create schemes),但是,如果是從SVN中新check out出來的代碼,不打開Xcode工程,要用fastlane gym, xcodebuild等工具直接編譯,尤其在有workspace,編譯時(shí)需要提供的是scheme而不是target的情況下,新拿回來的工程中沒有scheme,必須要打開一次Xcode生成scheme么?答案是,可以自己用Ruby生成一下scheme。以下是檢查有沒有scheme,如果沒有就recreate的腳本,包含一個(gè)bash和一個(gè)ruby:

# bash,用于檢查project中是否有所需的scheme

#!/bin/bash

# 此腳本用于檢查工程中是否包含指定scheme
# 參數(shù): 
# $1: 指定的scheme,待檢查的scheme
# $2: 使用的用戶
# 返回值: 0 找到scheme; -1 未找到

SCHEME=$1
USER=$2

function check_schemes {
    scheme_exist=0
    ALL_TARGETS_AND_SHCHEMS=$(sudo -u ${USER} xcodebuild -list -project MyProject.xcodeproj)
    KEY_STRING="Schemes:"
    CONTAIN_SCHEMES=$(echo ${ALL_TARGETS_AND_SHCHEMS} | grep ${KEY_STRING})
    
    if [ -z "${CONTAIN_SCHEMES}" ]; then
        echo "此工程沒有Schemes"
    else
        echo "找到全部Schemes"  
        ALL_SCHEMES=($(echo ${ALL_TARGETS_AND_SHCHEMS##*${KEY_STRING}}))        
        for one_scheme in "${ALL_SCHEMES[@]}"; do
        if [ "${one_scheme}" == "${SCHEME}" ]; then
            scheme_exist=1
            break
        fi
    done
    fi
    return ${scheme_exist}
}

check_schemes

RESULT=$?
if [ ${RESULT} -eq 0 ];then
    # 沒有找到對應(yīng)scheme, 重新生成schemes
    echo "沒有找到對應(yīng)的scheme: ${SCHEME}, 重新生成全部schemes"  
    ruby RecreateSchemes.rb
else
    echo "找到對應(yīng)的scheme: ${SCHEME}"  
    exit 0
fi

# 重新生成schemes后再次檢查
check_schemes

RESULT=$?
if [ ${RESULT} -eq 0 ];then
    # 沒有找到對應(yīng)的schemes
    echo "不存在scheme: ${SCHEME}"  
    exit -1
fi

Ruby 文件, 用于重新生成schemes。

# RecreateSchemes.rb

#!/usr/bin/env ruby

require 'xcodeproj'

xcproj = Xcodeproj::Project.open("MyProject.xcodeproj")
xcproj.recreate_user_schemes
xcproj.save

可以在build之前先使用上面的腳本檢查是否有可以編譯的scheme。

5,針對個(gè)別需要在編譯時(shí)加入CFLAG進(jìn)行條件控制的target,可以使用下面的編譯選項(xiàng):

# 1) 使用xcodebuild進(jìn)行編譯
xcodebuild -project MyProject.xcodeproj -scheme target1 OTHER_CFLAGS='${inherited} -DTARGET1=1'

########
# 輸出包含:
Build settings from command line:
    OTHER_CFLAGS = ${inherited} -DTARGET1 =1
export OTHER_CFLAGS=" -DTARGET1 =1"
# 可見${inherited}為空
# 注意:OTHER_CFLAGS的參數(shù)需要有單引號
# 2) 使用fastlane gym,代碼中使用方式是 #if TARGET1 #endif ,通過設(shè)置-DTARGET1 =1或-DTARGET1 =0進(jìn)行條件編譯
fastlane gym --workspace MyProject.xcworkspace --scheme target1 --clean --configuration Release --archive_path ~/Desktop/temp/target1 --export_method enterprise --output_directory ~/Desktop/temp --output_name target1-xxx.ipa --xcargs OTHER_CFLAGS="'${inherited} -DTARGET1 =1'"

#########
# 輸出包含:
$ xcodebuild -list -workspace MyProject.xcworkspace -configuration Release
$ xcodebuild -showBuildSettings -workspace MyProject.xcworkspace -scheme target1 -configuration Release
+------------------+---------------------------------------------------------+
|                           Summary for gym 2.57.0                           |
+------------------+---------------------------------------------------------+
| workspace        | MyProject.xcworkspace                                        |
| scheme           | target1                                                 |
| clean            | true                                                    |
| configuration    | Release                                                 |
| archive_path     | /Users/xxx/Desktop/temp/target1                           |
| export_method    | enterprise                                              |
| output_directory | /Users/xxx/Desktop/temp                                |
| output_name      | target1-xxx                                             |
| xcargs           | OTHER_CFLAGS=' -DTARGET1 =1'                        |
| destination      | generic/platform=iOS                                    |
| build_path       | /Users/xxx/Library/Developer/Xcode/Archives/2017-09-21 |
| silent           | false                                                   |
| skip_package_ipa | false                                                   |
| buildlog_path    | ~/Library/Logs/gym                                      |
| xcode_path       | /Applications/Xcode.app                                 |
+------------------+---------------------------------------------------------+

[11:30:00]: $ set -o pipefail && xcodebuild -workspace MyProject.xcworkspace -scheme target1 -configuration Release -destination 'generic/platform=iOS' -archivePath /Users/xxx/Desktop/temp/target1.xcarchive OTHER_CFLAGS=' -DTARGET1 =1' clean archive | tee /Users/xxx/Library/Logs/gym/target1-target1.log | xcpretty

# 可見fastlane最后在xcodebuild中的CFLAGS參數(shù)需要單引號才能正常執(zhí)行
# 3) 或者,代碼中使用方式是 #ifdef  TARGET1   #else  #endif
fastlane gym --workspace  MyProject.xcworkspace --scheme target1 --clean --configuration Release --archive_path ~/Desktop/temp/target1 --export_method enterprise --output_directory ~/Desktop/temp --output_name target1-xxx.ipa --xcargs OTHER_CFLAGS="'${inherited} -DTARGET1'"

注意:
1)${inherited}或者寫為${value},是繼承工程中配置的CFLAGS,如果沒有則為空串;
2)引號的使用,如果使用xcodebuild,直接使用單引號括住CFLAGS;如果使用fastlane gym則需要在單引號外再加上雙引號,保證傳入fastlane中調(diào)用的xcodebuild時(shí),依然OTHER_CFLAGS后參數(shù)有單引號。

6,最后是關(guān)于在member center上申請push證書和provisioning profile的。一直以來的操作是先在Xcode上建好target,填好新的bundle后,一選signing,自動(dòng)就在member center對應(yīng)帳號的App ID中創(chuàng)建出來了。這次反著來,在member center直接創(chuàng)建對應(yīng)的App ID,申請push證書和profile,下載profile安裝。然后再直接用腳本替換project文件中的BUNDLE_ID,直接做出對應(yīng)的app。這樣,每次添加一個(gè)新的target(即對應(yīng)添加一個(gè)新的app)只需要在member center中進(jìn)行設(shè)置-配置-下載證書-安裝證書,代碼和工程不需要發(fā)生任何改變,也不需要因?yàn)樾绿砑恿藅arget而提交代碼了:)

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 229,406評論 6 538
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 99,034評論 3 423
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事。” “怎么了?”我有些...
    開封第一講書人閱讀 177,413評論 0 382
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經(jīng)常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 63,449評論 1 316
  • 正文 為了忘掉前任,我火速辦了婚禮,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 72,165評論 6 410
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 55,559評論 1 325
  • 那天,我揣著相機(jī)與錄音,去河邊找鬼。 笑死,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 43,606評論 3 444
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 42,781評論 0 289
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 49,327評論 1 335
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 41,084評論 3 356
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 43,278評論 1 371
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 38,849評論 5 362
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 44,495評論 3 348
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 34,927評論 0 28
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 36,172評論 1 291
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 52,010評論 3 396
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 48,241評論 2 375

推薦閱讀更多精彩內(nèi)容

  • 靜態(tài)庫與動(dòng)態(tài)庫的區(qū)別 首先來看什么是庫,庫(Library)說白了就是一段編譯好的二進(jìn)制代碼,加上頭文件就可以供別...
    吃瓜群眾呀閱讀 12,023評論 3 42
  • Spring Cloud為開發(fā)人員提供了快速構(gòu)建分布式系統(tǒng)中一些常見模式的工具(例如配置管理,服務(wù)發(fā)現(xiàn),斷路器,智...
    卡卡羅2017閱讀 134,807評論 18 139
  • 【轉(zhuǎn)載】曾夢想仗劍走天涯 1.Xcode IDE概覽 說明:從左到右,依次是“導(dǎo)航窗格(Navigator)->邊...
    06a6a973d7ab閱讀 3,864評論 2 20
  • 因?yàn)橐Y(jié)局swift3.0中引用snapKit的問題,看到一篇介紹Xcode8,swift3變化的文章,覺得很詳細(xì)...
    uniapp閱讀 4,455評論 0 12
  • 難以置信的言表,恰似初夏之輕酥。 從春日里走了出來,夏日如此養(yǎng)目。 忘了曾經(jīng)的年少,忘不了曾經(jīng)的仰慕。 從你的心中...
    徐達(dá)開閱讀 332評論 0 4