將iOS項目進行子工程化

將iOS項目進行子工程化

摘要: 使用靜態(tài)庫或動態(tài)庫來將開發(fā)中的獨立模塊抽離的方法列疗。

將iOS項目進行子工程化

在iOS項目開發(fā)中赘艳,隨著項目的越來越大丁稀,工程的結構化會變差应民,編譯的速度也會越來越慢话原。使用靜態(tài)庫或動態(tài)庫的方式來構建子工程不僅可以加快項目的編譯速度夕吻,從結構上,也優(yōu)化了項目的組織繁仁。有兩種方式來來對項目進行子工程化涉馅,可以在項目中創(chuàng)建子項目,也可以創(chuàng)建并列的項目黄虱,建立項目依賴稚矿。需要注意,無論哪種方式捻浦,你都應該盡量保證子工程不要用到主工程中的內容晤揣,如果必須這樣做,你可以采用代理或其他回調編程方式來轉交給主工程自己處理默勾。

一碉渡、創(chuàng)建子工程的一個示例

使用Xcode新建一個命名為ProjectDemo的工程聚谁,在ProjectDemo工程中再次新建一個framework庫工程母剥,點擊新建文件中的Project...選項,選擇其中的Cocoa Touch Framework工程(創(chuàng)建Cocoa Touch Static Library則會打包為靜態(tài)庫)形导。


image

將新創(chuàng)建的工程命名為LoginLib环疼,用來模擬項目中的登錄模塊。需要注意朵耕,新建工程時炫隶,需要將其加入ProjectDemo組,如下圖:


image

對于創(chuàng)建的LoginLib工程阎曹,你可以創(chuàng)建一個LoginLib.h頭文件用來公開外界需要使用到的類伪阶,便于演示,我在里面創(chuàng)建一個視圖控制器和一個類別工具類处嫌,結構如下:


image

配置LoginLib的頭文件選項栅贴,將外界需要用到的進行公開,如下:


image

現(xiàn)在熏迹,分別編譯LoginLib工程和ProjectDemo工程檐薯,都沒有問題,但是你依然無法在ProjectDemo工程中使用LoginLib庫中的內容注暗,你需要建立主子工程的關聯(lián)坛缕,在ProjectDemo工程中建立依賴工程并接入動態(tài)庫,如下所示:


image

配置Target Dependencies的作用是確保每次主工程編譯前都會先對所依賴的工程進行編譯捆昏。之后赚楚,在ProjectDemo工程中導入LoginLib相關頭文件即可使用其中功能。

注意骗卜,如果報錯找不到頭文件直晨,你需要設置一下頭文件的尋找路徑搀军,在ProjectDemo的Build Setting中搜索header,如下圖


image

設置Header Search Paths如下即可勇皇。


image

二罩句、創(chuàng)建依賴模塊工程的一個示例

開發(fā)中還有一種場景,公司可能有一組App敛摘,這些App中可能有很多相似的模塊门烂,例如某些應用程序分為用戶端和老板端,他們都有相同的登錄模塊兄淫,我們可以使用workspace來進行項目和模塊的管理屯远。新建一個文件夾命名為Projects,在其中創(chuàng)建一個workspace文件捕虽,也命名為Projects慨丐。在workspace文件中新建兩個項目工程和一個動態(tài)庫工程,在創(chuàng)建時泄私,注意選擇加入workspace房揭,如下圖:


image

創(chuàng)建的3個工程分別命名為UserProject,BossProject和LoginLib晌端,結構如下:


image

類似我們的第一個示例捅暴,配置完頭文件路徑后,將動態(tài)庫引入UserProject和BossProject工程咧纠,即實現(xiàn)了LoginLib模塊的復用蓬痒。

三、如果子工程只能夠有資源文件

如果子工程中有資源文件漆羔,無論是plist文件還是圖片素材梧奢,在主工程調用動態(tài)庫時,這些文件都是沒有被打包進來的演痒。有兩種方式來處理這個問題:

1.將資源文件打包成Bundle包亲轨,從包中取資源

Xcode可以創(chuàng)建Bundle資源包,這種文件創(chuàng)建后編譯時會自動打包成Bundle文件嫡霞。需要注意瓶埋,Xcode只能創(chuàng)建MacOS下的Bundle模板,創(chuàng)建后需要將編譯選項設置為iOS诊沪。這種方式有很大的弊端养筒,首先主工程必須引入編譯后的Bundle包,如果每次新增或修改資源端姚,都要重新打包和導入晕粪。其次,在子工程中對素材進行使用時渐裸,都必須以Bundle為媒介巫湘,增加的復雜度装悲。

2.使用shell拷貝資源腳本

這種方式每次在編譯時都會將資源進行拷貝,類似CocoaPods的管理模式尚氛,推薦使用诀诊。例如,在主工程的編譯選項中新建一個腳本文件阅嘶,如圖:


image

編寫如下腳本代碼即可:

#!/bin/sh

# set -e

mkdir -p "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}"

install_resource()

{

  if [[ "$1" = /* ]] ; then

    RESOURCE_PATH="$1"

  fi

  if [[ ! -e "$RESOURCE_PATH" ]] ; then

    cat << EOM

error: Resource "$RESOURCE_PATH" not found.

EOM

    exit 1

  fi

  case $RESOURCE_PATH in

    *.storyboard)

      echo "ibtool --reference-external-strings-file --errors --warnings --notices --minimum-deployment-target ${!DEPLOYMENT_TARGET_SETTING_NAME} --output-format human-readable-text --compile ${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/`basename \"$RESOURCE_PATH\" .storyboard`.storyboardc $RESOURCE_PATH --sdk ${SDKROOT} ${TARGET_DEVICE_ARGS}"

      ibtool --reference-external-strings-file --errors --warnings --notices --minimum-deployment-target ${!DEPLOYMENT_TARGET_SETTING_NAME} --output-format human-readable-text --compile "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/`basename \"$RESOURCE_PATH\" .storyboard`.storyboardc" "$RESOURCE_PATH" --sdk "${SDKROOT}" ${TARGET_DEVICE_ARGS}

      ;;

    *.xib)

      echo "ibtool --reference-external-strings-file --errors --warnings --notices --minimum-deployment-target ${!DEPLOYMENT_TARGET_SETTING_NAME} --output-format human-readable-text --compile ${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/`basename \"$RESOURCE_PATH\" .xib`.nib $RESOURCE_PATH --sdk ${SDKROOT} ${TARGET_DEVICE_ARGS}"

      ibtool --reference-external-strings-file --errors --warnings --notices --minimum-deployment-target ${!DEPLOYMENT_TARGET_SETTING_NAME} --output-format human-readable-text --compile "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/`basename \"$RESOURCE_PATH\" .xib`.nib" "$RESOURCE_PATH" --sdk "${SDKROOT}" ${TARGET_DEVICE_ARGS}

      ;;

    *.framework)

      echo "mkdir -p ${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}"

      mkdir -p "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}"

      echo "rsync -av $RESOURCE_PATH ${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}"

      rsync -av "$RESOURCE_PATH" "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}"

      ;;

    *.xcdatamodel)

      echo "xcrun momc \"$RESOURCE_PATH\" \"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/`basename "$RESOURCE_PATH"`.mom\""

      xcrun momc "$RESOURCE_PATH" "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/`basename "$RESOURCE_PATH" .xcdatamodel`.mom"

      ;;

    *.xcdatamodeld)

      echo "xcrun momc \"$RESOURCE_PATH\" \"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/`basename "$RESOURCE_PATH" .xcdatamodeld`.momd\""

      xcrun momc "$RESOURCE_PATH" "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/`basename "$RESOURCE_PATH" .xcdatamodeld`.momd"

      ;;

    *.xcmappingmodel)

      echo "xcrun mapc \"$RESOURCE_PATH\" \"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/`basename "$RESOURCE_PATH" .xcmappingmodel`.cdm\""

      xcrun mapc "$RESOURCE_PATH" "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/`basename "$RESOURCE_PATH" .xcmappingmodel`.cdm"

      ;;

    *.xcassets)

 echo "all xcassets will compile later!"

      ;;

    *)

      echo "$RESOURCE_PATH"

      echo "$RESOURCE_PATH" >> "$RESOURCES_TO_COPY"

      ;;

  esac

}

install_project_resouces()

{

  PROJECT_RESOURCE_DIR="${PROJECT_DIR}/../$1"



  if [[ ! -e "${PROJECT_RESOURCE_DIR}" ]]; then

    cat << EOM

error: PROJECT_RESOURCE_DIR "${PROJECT_RESOURCE_DIR}" not found

EOM

    exit 1

  fi

  echo "copy resources in ${PROJECT_RESOURCE_DIR} to ${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}"

  ALL_RESOURCES=()

  FIND_ALL_RESOURCES=$(find "$PROJECT_RESOURCE_DIR" -iname "*.xcassets" -o -iname "*.xib" -o -iname "*.storyboard" -o -iname "*.plist" ! -iname "Info.plist")

  while read line; do

      ALL_RESOURCES+=("$line")

  done << "$RESOURCES_TO_COPY"

  case "${TARGETED_DEVICE_FAMILY}" in

    1,2)

      TARGET_DEVICE_ARGS="--target-device ipad --target-device iphone"

    ;;

    1)

      TARGET_DEVICE_ARGS="--target-device iphone"

    ;;

    2)

      TARGET_DEVICE_ARGS="--target-device ipad"

    ;;

    3)

      TARGET_DEVICE_ARGS="--target-device tv"

    ;;

    *)

      TARGET_DEVICE_ARGS="--target-device mac"

    ;;

  esac

  for i in ${ALL_RESOURCES[@]}; do

    install_resource "${i}"

  done

  mkdir -p "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}"

  rsync -avr --copy-links --no-relative --exclude '*/.svn/*' --files-from="$RESOURCES_TO_COPY" / "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}"

  if [[ "${ACTION}" == "install" ]] && [[ "${SKIP_INSTALL}" == "NO" ]]; then

    mkdir -p "${INSTALL_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}"

    rsync -avr --copy-links --no-relative --exclude '*/.svn/*' --files-from="$RESOURCES_TO_COPY" / "${INSTALL_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}"

  fi

  rm -f "$RESOURCES_TO_COPY"

}

for module in ${MODULES}; do

  install_project_resouces "${module}"

done

XCASSETS_SEARCH_DIR="${PROJECT_DIR}/.."

XCASSET_FILES=()

if [[ -n "${WRAPPER_EXTENSION}" ]] && [ "`xcrun --find actool`" ]

then

  # Find all other xcassets (this unfortunately includes those of path pods and other targets).

  ALL_XCASSETS=$(find "$XCASSETS_SEARCH_DIR" -iname "*.xcassets" -type d)

  while read line; do

    if [[ $line != "${PODS_ROOT}*" ]]; then

      XCASSET_FILES+=("$line")

    fi

  done <<<"$ALL_XCASSETS"

  echo "compile all xcassets: ${XCASSET_FILES[@]}"

  printf "%s\0" "${XCASSET_FILES[@]}" | xargs -0 xcrun actool --output-format human-readable-text --notices --warnings --platform "${PLATFORM_NAME}" --minimum-deployment-target "${!DEPLOYMENT_TARGET_SETTING_NAME}" ${TARGET_DEVICE_ARGS} --compress-pngs --compile "${BUILT_PRODUCTS_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}"

fi

echo "all done!"

四属瓣、一點小體悟

本文所討論的,只是從工程結構上實現(xiàn)模塊化與組件化的方式讯柔,一個公司可能會有很多個App產品抡蛙,但其中一定有某些基礎模塊是可以復用的,除了進行靜態(tài)庫封裝或動態(tài)庫封裝外魂迄,進行并列工程化也是一種很好的選擇粗截,這樣可以同步開發(fā),迭代更快捣炬。除了公用的模塊熊昌,還有一些模塊可能并不公用但是確可以獨立開發(fā),例如資訊類項目中可能會有用戶模塊遥金,社交模塊和內容模塊浴捆,將這些拆分為項目內的子工程可以使項目的結構更加清晰蒜田,模塊化測試也更容易進行稿械。

最后,僅僅項目結構上的模塊化遠遠達不到真正實現(xiàn)組件化項目的要求冲粤,遵守協(xié)議為標準美莫,以函數(shù)式編程為方式,全局著眼的接口設計與路由規(guī)劃梯捕,良好的編程習慣與統(tǒng)一的代碼風格厢呵,這種代碼層面的項目開發(fā)管理才真正任重道遠。后面有時間我會陸續(xù)通過其他博客來探討這些問題傀顾。希望一起交流襟铭,共同學習!

?著作權歸作者所有,轉載或內容合作請聯(lián)系作者
  • 序言:七十年代末短曾,一起剝皮案震驚了整個濱河市寒砖,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌嫉拐,老刑警劉巖哩都,帶你破解...
    沈念sama閱讀 218,755評論 6 507
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異婉徘,居然都是意外死亡漠嵌,警方通過查閱死者的電腦和手機咐汞,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,305評論 3 395
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來儒鹿,“玉大人化撕,你說我怎么就攤上這事≡佳祝” “怎么了侯谁?”我有些...
    開封第一講書人閱讀 165,138評論 0 355
  • 文/不壞的土叔 我叫張陵,是天一觀的道長章钾。 經常有香客問我墙贱,道長,這世上最難降的妖魔是什么贱傀? 我笑而不...
    開封第一講書人閱讀 58,791評論 1 295
  • 正文 為了忘掉前任惨撇,我火速辦了婚禮,結果婚禮上府寒,老公的妹妹穿的比我還像新娘魁衙。我一直安慰自己私植,他們只是感情好诀浪,可當我...
    茶點故事閱讀 67,794評論 6 392
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著介汹,像睡著了一般纤房。 火紅的嫁衣襯著肌膚如雪纵隔。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 51,631評論 1 305
  • 那天炮姨,我揣著相機與錄音捌刮,去河邊找鬼。 笑死舒岸,一個胖子當著我的面吹牛绅作,可吹牛的內容都是我干的。 我是一名探鬼主播蛾派,決...
    沈念sama閱讀 40,362評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼俄认,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了洪乍?” 一聲冷哼從身側響起眯杏,我...
    開封第一講書人閱讀 39,264評論 0 276
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎典尾,沒想到半個月后役拴,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經...
    沈念sama閱讀 45,724評論 1 315
  • 正文 獨居荒郊野嶺守林人離奇死亡钾埂,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 37,900評論 3 336
  • 正文 我和宋清朗相戀三年河闰,在試婚紗的時候發(fā)現(xiàn)自己被綠了科平。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 40,040評論 1 350
  • 序言:一個原本活蹦亂跳的男人離奇死亡姜性,死狀恐怖瞪慧,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情部念,我是刑警寧澤弃酌,帶...
    沈念sama閱讀 35,742評論 5 346
  • 正文 年R本政府宣布,位于F島的核電站儡炼,受9級特大地震影響妓湘,放射性物質發(fā)生泄漏。R本人自食惡果不足惜乌询,卻給世界環(huán)境...
    茶點故事閱讀 41,364評論 3 330
  • 文/蒙蒙 一榜贴、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧妹田,春花似錦唬党、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,944評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至晶衷,卻和暖如春蓝纲,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背房铭。 一陣腳步聲響...
    開封第一講書人閱讀 33,060評論 1 270
  • 我被黑心中介騙來泰國打工驻龟, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留温眉,地道東北人缸匪。 一個月前我還...
    沈念sama閱讀 48,247評論 3 371
  • 正文 我出身青樓,卻偏偏與公主長得像类溢,于是被迫代替她去往敵國和親凌蔬。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 44,979評論 2 355

推薦閱讀更多精彩內容