創(chuàng)建組件工程
啟動(dòng)命令行,使用pod命令創(chuàng)建組件
pod lib create ZTTools_Swift // 名字自己取,會(huì)自動(dòng)創(chuàng)建相應(yīng)的工程和文件夾
之后會(huì)彈出一些選項(xiàng),按需要填即可:
// 選擇平臺(tái)
What platform do you want to use?? [ iOS / macOS ]
> iOS
// 選擇語(yǔ)言
What language do you want to use?? [ Swift / ObjC ]
> Swift
// 是否創(chuàng)建demo工程(一般都是需要的)
Would you like to include a demo application with your library? [ Yes / No ]
> Yes
// 是否使用測(cè)試框架
Which testing frameworks will you use? [ Quick / None ]
> None
// 是否創(chuàng)建UI單元測(cè)試
Would you like to do view based testing? [ Yes / No ]
> No
至此,一個(gè)空的組件工程創(chuàng)建完畢。
清理單元測(cè)試
一般都用不上單元測(cè)試,使用可以把它給刪了。
- 項(xiàng)目里把
Tests文件夾刪了 - 點(diǎn)擊工程,在
TARGETS里把單元測(cè)試的target刪了 - 在
Podfile文件里,把單元測(cè)試的target全部?jī)?nèi)容刪了
創(chuàng)建私有xcframework
如果不創(chuàng)建私有庫(kù),那你組件里的東西都會(huì)被看到,這不是我們想要的。
所以,我們需要?jiǎng)?chuàng)建一個(gè)庫(kù),把代碼全放到這個(gè)庫(kù)里面,然后再把這個(gè)庫(kù)弄到組件里面。
xcframework與framework的對(duì)比
為什么我們要用.xcframework而不是.framework呢?
首先我們來(lái)看看這二者的區(qū)別:
-
.xcframework里面裝載了多個(gè)平臺(tái)的.framework,Xcode會(huì)自動(dòng)選用正確指令集的.framework,也就是說(shuō)編譯后或者上傳到App Store后只會(huì)包含單一平臺(tái),同時(shí)也省去了手動(dòng)移除動(dòng)態(tài)庫(kù)中的模擬器指令集的工作 -
.framework雖然可以用lipo指令去合并多個(gè).framework,但是,這是一個(gè)包含所有平臺(tái)的,這會(huì)造成APP體積變大,并且App Store上架時(shí)不允許有模擬器版本,還得手動(dòng)移除模擬器的指令,這顯然很麻煩
創(chuàng)建私有framework工程
我們?cè)诮M件工程里面再創(chuàng)建一個(gè)工程,選擇Framework選項(xiàng),并且把其添加到組件的xcworkspace工作空間里。
配置工程,在General里取消掉對(duì)Mac的支持,并調(diào)整支持的iOS系統(tǒng)版本。
修改Podfile文件
默認(rèn)的Podfile文件是不支持多項(xiàng)目的,需要我們修改里面的內(nèi)容。
-
platform的平臺(tái)和最低支持系統(tǒng)版本需要改為和項(xiàng)目的一致,同時(shí)項(xiàng)目里面的工程也全部保持一致 - 添加
workspace名字 - 分別設(shè)置每個(gè)
target的pod - 在
target里面聲明對(duì)應(yīng)的project路徑
project路徑是一個(gè)相對(duì)路徑,以Podfile文件所在的目錄為根目錄;一般來(lái)說(shuō),Podfile文件就在主工程那里,別的工程就以主工程做相對(duì)路徑
以下為Podfile文件示例:
#use_frameworks! # 全局配,也可以每個(gè)項(xiàng)目單獨(dú)配
platform :ios, '10.0'
# 工作空間名稱
workspace 'ZTTools_Swift.xcworkspace' # 同一個(gè)工作空間,多個(gè)Project使用pod時(shí),需要添加工作空間名稱
# 主工程(帶podfile的工程)
target 'ZTTools_Swift_Example' do # target的名字
use_frameworks! # 項(xiàng)目單獨(dú)配
project 'ZTTools_Swift.xcodeproj' # 指明target的工程路徑;使用相對(duì)路徑,相對(duì)于Podfile文件
pod 'ZTTools_Swift', :path => '../' # 組件的pod名
end
# 同一個(gè)工作空間里面別的項(xiàng)目依賴
target 'ZTTools' do
use_frameworks! # 項(xiàng)目單獨(dú)配
project '../ZTTools/ZTTools.xcodeproj'
pod 'Alamofire'
pod 'SnapKit'
end
如果遇到pod報(bào)錯(cuò),可嘗試使用
sudo gem install cocoapods更新pods解決
創(chuàng)建自動(dòng)化腳本
我們選中SDK項(xiàng)目,點(diǎn)擊File-New-Target,選中Other,然后選擇Aggregate,命名為SDKBuildScript,點(diǎn)擊完成。
舊版本的Xcode里,
Aggregate是在Cross-platform里。
點(diǎn)擊File-New-File,選擇Shell Script,命名為SDKBuild,點(diǎn)擊創(chuàng)建(不要把它添加到Targets,不然會(huì)被編譯到Framework里的)。
把這段腳本復(fù)制到SDKBuild中,然后根據(jù)注釋修改為你自己的。
完成腳本編寫后,可以把Xcode里的SDKBuild文件刪了(但不要移除到廢紙簍)
CONFIG="${CONFIGURATION}" # "Release" "${CONFIGURATION}" "Debug" 編譯模式,使用Release即可
SCHEME_NAME="${PROJECT_NAME}" # 要build的scheme名,如果和scheme名不一致,需要修改為正確的scheme名
OUTPUT_SDK="${SCHEME_NAME}" # 產(chǎn)物名字
OUTPUT_SDKNAME="${OUTPUT_SDK}.framework"
# 項(xiàng)目里存放Framework的路徑
TARGET_FOLDER="${SRCROOT}/../ZTTools_Swift/Classes"
# 工作空間路徑
WORK_FOLDER="${SRCROOT}/../Example/ZTTools_Swift.xcworkspace"
# ---------- 以上配置是可以修改的,下面的配置則不需要改 ----------
# 編譯時(shí)存放xcarchive的路徑
SIMULATOR_ARCHIVE_PATH="${SRCROOT}/build/${OUTPUT_SDK}-iphonesimulator.xcarchive"
DEVICE_ARCHIVE_PATH="${SRCROOT}/build/${OUTPUT_SDK}-iphoneos.xcarchive"
# 編譯時(shí)存放framework的路徑
SIMULATOR_DIR_PATH="${SIMULATOR_ARCHIVE_PATH}/Products/Library/Frameworks/${OUTPUT_SDKNAME}"
DEVICE_DIR_PATH="${DEVICE_ARCHIVE_PATH}/Products/Library/Frameworks/${OUTPUT_SDKNAME}"
function removeBuild()
{
if [ -d "${SRCROOT}/build" ]
then
rm -rf "${SRCROOT}/build/"
fi
}
function removeBuildFile()
{
for FILE in $(ls "${1}"|tr " " "?")
do
if [[ "${FILE}" =~ ".xcconfig" ]]
then
rm -f "${1}/${FILE}"
fi
done
}
removeBuild
rm -rf "${TARGET_FOLDER}/${OUTPUT_SDK}.xcframework"
# 分別clean模擬器和真機(jī)
xcodebuild clean -workspace "${WORK_FOLDER}" -scheme "${SCHEME_NAME}" -configuration "${CONFIG}" -derivedDataPath "./build" -archivePath "${DEVICE_ARCHIVE_PATH}" SKIP_INSTALL=NO BUILD_LIBRARY_FOR_DISTRIBUTION=YES -sdk iphoneos
xcodebuild clean -workspace "${WORK_FOLDER}" -scheme "${SCHEME_NAME}" -configuration "${CONFIG}" -derivedDataPath "./build" -archivePath "${SIMULATOR_ARCHIVE_PATH}" SKIP_INSTALL=NO BUILD_LIBRARY_FOR_DISTRIBUTION=YES -sdk iphonesimulator
# 編譯真機(jī)的Framework
xcodebuild archive -workspace "${WORK_FOLDER}" -scheme "${SCHEME_NAME}" -configuration "${CONFIG}" -derivedDataPath "./build" -archivePath "${DEVICE_ARCHIVE_PATH}" SKIP_INSTALL=NO BUILD_LIBRARY_FOR_DISTRIBUTION=YES -sdk iphoneos
# 編譯模擬器的Framework
xcodebuild archive -workspace "${WORK_FOLDER}" -scheme "${SCHEME_NAME}" -configuration "${CONFIG}" -derivedDataPath "./build" -archivePath "${SIMULATOR_ARCHIVE_PATH}" SKIP_INSTALL=NO BUILD_LIBRARY_FOR_DISTRIBUTION=YES -sdk iphonesimulator
removeBuildFile "${DEVICE_DIR_PATH}"
removeBuildFile "${SIMULATOR_DIR_PATH}"
# 合并framework,創(chuàng)建xcframework
xcodebuild -create-xcframework \
-framework "${SIMULATOR_DIR_PATH}" \
-framework "${DEVICE_DIR_PATH}" \
-output "${TARGET_FOLDER}/${OUTPUT_SDK}.xcframework"
open "${TARGET_FOLDER}" # 打開文件夾
removeBuild
點(diǎn)擊SDK項(xiàng)目,然后在TARGETS里選中SDKBuildScript,上面選中Build Phases,點(diǎn)擊左上角的“+”號(hào),選擇New Run Script Phase。
在黑框里輸入./SDKBuild.sh。
- 當(dāng)然你也可以直接把腳本寫在黑框里,這樣子就不需要?jiǎng)?chuàng)建腳本文件了
如果使用腳本文件的話,會(huì)報(bào)沒(méi)權(quán)限的錯(cuò)誤,所以需要使用命令行來(lái)打開權(quán)限(因?yàn)槭褂梦募奖愎芾砗途帉懘a,所以這里我選擇了使用文件的方式):
打開命令行,cd到SDKBuild.sh所在的目錄,然后執(zhí)行sudo chmod +x SDKBuild.sh即可解決權(quán)限問(wèn)題- 在執(zhí)行腳本前,需要配置好
.podspec文件- 第一次運(yùn)行腳本,編譯好庫(kù)后,需要自行執(zhí)行一次
pod install為demo工程安裝該SDK。此后就不需要再執(zhí)行該命令了,因?yàn)槟_本會(huì)把新打包好的SDK替換掉舊的SDK,如果配置有變,還是需要用pod命令進(jìn)行更新- 更多SDK開發(fā)知識(shí)請(qǐng)看這篇文章:iOS】使用workspace搭建SDK開發(fā)框架
配置SDK工程
- 點(diǎn)擊SDK項(xiàng)目,然后在
TARGETS里選中SDK的target,點(diǎn)擊上面的Build Settings,找到Build Active Architecture Only項(xiàng)設(shè)置為NO(意思就是當(dāng)前打包的framework支持所有的設(shè)備,否則打包時(shí)只能用當(dāng)前版本的模擬器或真機(jī)運(yùn)行) - 在
Build Settings,找到Excluded Architectures項(xiàng),點(diǎn)擊展開,在Release選項(xiàng)下點(diǎn)擊+號(hào),選擇Any iOS Simulator SDK,值設(shè)置為arm64(處理arm64架構(gòu)合并報(bào)錯(cuò)的問(wèn)題;當(dāng)然,也可以在腳本里處理) - 點(diǎn)擊
Edit Scheme..選中腳本的target,選中Run,把Build Configuration的值改為Release(設(shè)置生成的SDK為release版,當(dāng)然也可以在腳本上設(shè)置,可以看腳本里的注釋) - 對(duì)于Swift工程,需要在
Build Settings里找到Build libraries for Distribution,設(shè)置YES,否則在合并.xcframework時(shí)會(huì)報(bào)No ‘swiftinterface’ files found within xx.swiftmodule的錯(cuò)(也可以在腳本設(shè)置) - 對(duì)于Swift工程,需要在
Build Settings里找到Skip Install,設(shè)置NO,否則在歸檔的文件目錄Products下會(huì)沒(méi)有輸出文件(也可以在腳本設(shè)置) - 在
PROJECT和TARGETS的Build Settings里,找到
Allow non-modular includes in Framework Modules,并都設(shè)置為YES;當(dāng)動(dòng)態(tài)庫(kù)需要引用第三方庫(kù)的Framework,需要告訴編譯器允許這種行為
如果工程報(bào)錯(cuò)
no such module 'XXX',并且pods工程下的Products文件夾里的產(chǎn)物全是紅的,說(shuō)明了沒(méi)生成對(duì)應(yīng)的庫(kù)。解決方法為:點(diǎn)擊Pods工程,在PROJECT的Build Settings里,找到Build Active Architecture Only設(shè)置為NO,Base SDK設(shè)置為iOS;每次pod install后可能會(huì)被重置
git管理
創(chuàng)建組件的時(shí)候,已經(jīng)默認(rèn)創(chuàng)建了git,但是,這個(gè)git是組件的,現(xiàn)在得把SDK的git和組件的git進(jìn)行分開,使得SDK的git作為組件git的子模塊進(jìn)行管理。
創(chuàng)建遠(yuǎn)程私有庫(kù)
因?yàn)镾DK本身是沒(méi)有g(shù)it的,所以需要用git init命令給它創(chuàng)建一個(gè)本地git倉(cāng)庫(kù)。
創(chuàng)建好SDK工程的git后,需要編輯.gitignore文件,內(nèi)容如下:
*~
.DS_Store
# Xcode
#
# gitignore contributors: remember to update Global/Xcode.gitignore, Objective-C.gitignore & Swift.gitignore
## Build generated
DerivedData/
## Various settings
*.pbxuser
!default.pbxuser
*.mode1v3
!default.mode1v3
*.mode2v3
!default.mode2v3
*.perspectivev3
!default.perspectivev3
xcuserdata/
## Other
*.moved-aside
*.xcbkptlist
*.xccheckout
*.xcscmblueprint
## Obj-C/Swift specific
*.hmap
*.ipa
*.dSYM.zip
*.dSYM
# CocoaPods
#
# We recommend against adding the Pods directory to your .gitignore. However
# you should judge for yourself, the pros and cons are mentioned at:
# https://guides.cocoapods.org/using/using-cocoapods.html#should-i-check-the-pods-directory-into-source-control
#
# Pods/
# Carthage
#
# Add this line if you want to avoid checking in source code from Carthage dependencies.
# Carthage/Checkouts
Carthage/Build
# fastlane
#
# It is recommended to not store the screenshots in the git repo. Instead, use fastlane to re-generate the
# screenshots whenever they are needed.
# For more information about the recommended setup visit:
# https://docs.fastlane.tools/best-practices/source-control/#source-control
fastlane/report.xml
fastlane/Preview.html
fastlane/screenshots
fastlane/test_output
# Code Injection
#
# After new code Injection tools there's a generated folder /iOSInjectionProject
# https://github.com/johnno1962/injectionforxcode
iOSInjectionProject/
然后再編輯組件的.gitignore文件,同樣是上面的內(nèi)容。
隨后創(chuàng)建2個(gè)遠(yuǎn)程倉(cāng)庫(kù),并把它們和本地git倉(cāng)庫(kù)關(guān)聯(lián)起來(lái)。
需要注意的是,遠(yuǎn)程倉(cāng)庫(kù)里,SDK的是私有的,pod組件可以是公開的。(因?yàn)榇a都在SDK里,所以公開pod組件庫(kù)也不會(huì)有什么問(wèn)題,而且使用起來(lái)也更方便)
子模塊管理
我們?yōu)榻M件工程git添加子模塊,這個(gè)時(shí)候的git得是主模塊的git,即使用組件的git來(lái)執(zhí)行添加命令。
添加子模塊的命令為git submodule add <url> <path>,其中url可以是遠(yuǎn)程地址和本地地址,本地地址要用絕對(duì)對(duì)路徑,path則是該子模塊存儲(chǔ)的目錄路徑(使用相對(duì)路徑)。
- 添加模塊之前,組件和SDK的git都需要先提交到遠(yuǎn)程
- 如果提交子模塊提示
The following paths are ignored by one of your .gitignore files,則用git submodule add -f來(lái)添加
配置pod的索引文件
項(xiàng)目名.podspec文件(我這里是ZTTools_Swift.podspec),這個(gè)文件是用來(lái)描述這個(gè)pod的說(shuō)明信息的。當(dāng)pod install安裝庫(kù)時(shí),只會(huì)引入你在.podspec中配置的那些文件。
Pod::Spec.new do |s|
s.name = '組件名'
s.version = '版本號(hào)'
s.summary = '組件精簡(jiǎn)描述'
s.description = <<-DESC
組件詳細(xì)描述
DESC
s.homepage = '組件主頁(yè)'
s.license = { :type => 'MIT', :file => 'LICENSE' }
s.author = { '用戶名' => '郵箱' }
s.source = { :git => 'git地址', :tag => s.version.to_s }
# 需要設(shè)置,不然項(xiàng)目引入庫(kù)后會(huì)崩潰
s.pod_target_xcconfig = {
'BUILD_LIBRARY_FOR_DISTRIBUTION' => 'YES'
}
s.user_target_xcconfig = { 'BUILD_LIBRARY_FOR_DISTRIBUTION' => 'YES' }
s.ios.deployment_target = '12.0' # 最低系統(tǒng)版本
s.swift_versions = ['5.0'] # Swift版本
s.vendored_frameworks = 'ZTTools_Swift/Classes/ZTToolsSDK.xcframework' # 使用私有庫(kù)
s.frameworks = 'UIKit', 'Foundation', 'Photos', 'UserNotifications', 'AVFoundation', 'CoreGraphics'
s.dependency 'Alamofire' # 依賴庫(kù)
s.dependency 'SnapKit'
end
如果集成組件后,項(xiàng)目運(yùn)行報(bào)
One of the two will be used. Which one is undefined.,說(shuō)明是符號(hào)沖突了,這個(gè)是pod的問(wèn)題。實(shí)際上是因?yàn)槟愕慕M件包含了第三方pod,然后使用組件的工程也包含了這個(gè)第三方pod導(dǎo)致的,不喜歡這個(gè)提示是話,可以在使用組件的工程里,找到Pods文件夾,在該文件夾下所有的Pods-項(xiàng)目名.debug.xcconfig和Pods-項(xiàng)目名.release.xcconfig文件,找到文件里面的OTHER_LDFLAGS,把有提示重復(fù)的-l"第三方pod名"刪除即可
發(fā)布組件
校驗(yàn)文件合法性
在發(fā)布之前,需要先轉(zhuǎn)到組件所在的文件夾,使用命令校驗(yàn).podspec文件。(可能需要翻墻)
pod lib lint是基礎(chǔ)校驗(yàn)命令,用來(lái)校驗(yàn)本地.podspec文件的,如果要校驗(yàn)遠(yuǎn)程,把lib改為spec即可。(spec會(huì)同時(shí)驗(yàn)證本地和遠(yuǎn)程是否通過(guò))
- 如果使用了第三方庫(kù),需要在后面加上
--use-libraries參數(shù) - 如果因?yàn)橛芯鎸?dǎo)致報(bào)錯(cuò)的,可以加上
--allow-warnings參數(shù)解決 - 如果需要輸出詳細(xì)信息,可以加上
--verbose參數(shù) - 如果是私有的repo庫(kù)要就要加上
--sources=“私有庫(kù)的地址”
- 提示
passed validation即為校驗(yàn)通過(guò)- 提示
[!] The spec did not pass validation即為校驗(yàn)失敗- 只有校驗(yàn)通過(guò)了,才能進(jìn)行下一步操作
- 一般來(lái)說(shuō),只需要校驗(yàn)本地即可
發(fā)布
需要先轉(zhuǎn)到組件所在的文件夾,使用命令pod trunk me查看是否注冊(cè)trunk。
如果提示[!] Authentication token is invalid or unverified. Either verify it with the email that was sent or register a new session.說(shuō)明還沒(méi)注冊(cè)過(guò)trunk或者登錄已經(jīng)過(guò)期了,需要執(zhí)行pod trunk register 郵箱 '名字' --description='描述文本' --verbose。(后面2個(gè)參數(shù)是可選的)
如果輸出名字、郵箱和注冊(cè)時(shí)間等信息,說(shuō)明已經(jīng)是注冊(cè)并是登錄狀態(tài)。這個(gè)時(shí)候就可以提交組件到pod了。
使用命令pod trunk push xxx.podspec發(fā)布組件到pod,同樣的可以加上--allow-warnings和--verbose參數(shù);如果要跳過(guò)驗(yàn)證pod是否導(dǎo)入,還可以加上--skip-import-validation參數(shù)。
- 發(fā)布之前請(qǐng)先打上tag,不然會(huì)發(fā)布失敗
- tag必須和
.podspec文件的s.version一致- 如果之前打了tag并發(fā)布了,更新了文件后請(qǐng)用新的tag,不然不生效(也就是說(shuō),你修了一個(gè)小bug,想同一個(gè)版本號(hào),把本地和遠(yuǎn)程的tag刪了,再打上同樣的tag并推上遠(yuǎn)程,這種方法是不可行的)
- 提交成功后,并不一定能馬上搜索到,需要等待一天左右
更新組件
- 更新改動(dòng)推送到遠(yuǎn)程倉(cāng)庫(kù)
- 打tag,并推送到遠(yuǎn)程倉(cāng)庫(kù)
- 執(zhí)行發(fā)布命令即可
如果提示
[!] You need to register a session first.,說(shuō)明需要驗(yàn)證會(huì)話。使用pod trunk register "你之前注冊(cè)的郵箱"后,去郵箱點(diǎn)擊鏈接驗(yàn)證即可
其他
- 使用命令
pod trunk delete 組件名 版本號(hào)可以刪除已發(fā)布的庫(kù)的某一版本 - 在組件文件夾里,有一個(gè)和組件名一樣的文件夾,里面有2個(gè)文件夾,分別是放置資源的
Assets和放置源碼或者庫(kù)的Classes - 在組件文件夾里,有一個(gè)叫
Example的文件夾,里面就是demo工程 - 打開demo的工作空間后,在
Pods工程里,有一個(gè)叫ReplaceMe的文件,是創(chuàng)建組件時(shí)默認(rèn)生成的,刪除即可
歡迎來(lái)群139322447玩耍