創建組件工程
啟動命令行,使用pod命令創建組件
pod lib create ZTTools_Swift // 名字自己取,會自動創建相應的工程和文件夾
之后會彈出一些選項,按需要填即可:
// 選擇平臺
What platform do you want to use?? [ iOS / macOS ]
> iOS
// 選擇語言
What language do you want to use?? [ Swift / ObjC ]
> Swift
// 是否創建demo工程(一般都是需要的)
Would you like to include a demo application with your library? [ Yes / No ]
> Yes
// 是否使用測試框架
Which testing frameworks will you use? [ Quick / None ]
> None
// 是否創建UI單元測試
Would you like to do view based testing? [ Yes / No ]
> No
至此,一個空的組件工程創建完畢。
清理單元測試
一般都用不上單元測試,使用可以把它給刪了。
- 項目里把
Tests
文件夾刪了 - 點擊工程,在
TARGETS
里把單元測試的target刪了 - 在
Podfile
文件里,把單元測試的target
全部內容刪了
創建私有xcframework
如果不創建私有庫,那你組件里的東西都會被看到,這不是我們想要的。
所以,我們需要創建一個庫,把代碼全放到這個庫里面,然后再把這個庫弄到組件里面。
xcframework與framework的對比
為什么我們要用.xcframework
而不是.framework
呢?
首先我們來看看這二者的區別:
-
.xcframework
里面裝載了多個平臺的.framework
,Xcode會自動選用正確指令集的.framework
,也就是說編譯后或者上傳到App Store后只會包含單一平臺,同時也省去了手動移除動態庫中的模擬器指令集的工作 -
.framework
雖然可以用lipo
指令去合并多個.framework
,但是,這是一個包含所有平臺的,這會造成APP體積變大,并且App Store上架時不允許有模擬器版本,還得手動移除模擬器的指令,這顯然很麻煩
創建私有framework工程
我們在組件工程里面再創建一個工程,選擇Framework
選項,并且把其添加到組件的xcworkspace
工作空間里。
配置工程,在General
里取消掉對Mac的支持,并調整支持的iOS系統版本。
修改Podfile文件
默認的Podfile
文件是不支持多項目的,需要我們修改里面的內容。
-
platform
的平臺和最低支持系統版本需要改為和項目的一致,同時項目里面的工程也全部保持一致 - 添加
workspace
名字 - 分別設置每個
target
的pod - 在
target
里面聲明對應的project
路徑
project
路徑是一個相對路徑,以Podfile
文件所在的目錄為根目錄;一般來說,Podfile
文件就在主工程那里,別的工程就以主工程做相對路徑
以下為Podfile
文件示例:
#use_frameworks! # 全局配,也可以每個項目單獨配
platform :ios, '10.0'
# 工作空間名稱
workspace 'ZTTools_Swift.xcworkspace' # 同一個工作空間,多個Project使用pod時,需要添加工作空間名稱
# 主工程(帶podfile的工程)
target 'ZTTools_Swift_Example' do # target的名字
use_frameworks! # 項目單獨配
project 'ZTTools_Swift.xcodeproj' # 指明target的工程路徑;使用相對路徑,相對于Podfile文件
pod 'ZTTools_Swift', :path => '../' # 組件的pod名
end
# 同一個工作空間里面別的項目依賴
target 'ZTTools' do
use_frameworks! # 項目單獨配
project '../ZTTools/ZTTools.xcodeproj'
pod 'Alamofire'
pod 'SnapKit'
end
如果遇到pod報錯,可嘗試使用
sudo gem install cocoapods
更新pods解決
創建自動化腳本
我們選中SDK項目,點擊File-New-Target
,選中Other
,然后選擇Aggregate
,命名為SDKBuildScript,點擊完成。
舊版本的Xcode里,
Aggregate
是在Cross-platform
里。
點擊File-New-File
,選擇Shell Script
,命名為SDKBuild,點擊創建(不要把它添加到Targets,不然會被編譯到Framework里的)。
把這段腳本復制到SDKBuild中,然后根據注釋修改為你自己的。
完成腳本編寫后,可以把Xcode里的SDKBuild文件刪了(但不要移除到廢紙簍)
CONFIG="${CONFIGURATION}" # "Release" "${CONFIGURATION}" "Debug" 編譯模式,使用Release即可
SCHEME_NAME="${PROJECT_NAME}" # 要build的scheme名,如果和scheme名不一致,需要修改為正確的scheme名
OUTPUT_SDK="${SCHEME_NAME}" # 產物名字
OUTPUT_SDKNAME="${OUTPUT_SDK}.framework"
# 項目里存放Framework的路徑
TARGET_FOLDER="${SRCROOT}/../ZTTools_Swift/Classes"
# 工作空間路徑
WORK_FOLDER="${SRCROOT}/../Example/ZTTools_Swift.xcworkspace"
# ---------- 以上配置是可以修改的,下面的配置則不需要改 ----------
# 編譯時存放xcarchive的路徑
SIMULATOR_ARCHIVE_PATH="${SRCROOT}/build/${OUTPUT_SDK}-iphonesimulator.xcarchive"
DEVICE_ARCHIVE_PATH="${SRCROOT}/build/${OUTPUT_SDK}-iphoneos.xcarchive"
# 編譯時存放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模擬器和真機
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
# 編譯真機的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,創建xcframework
xcodebuild -create-xcframework \
-framework "${SIMULATOR_DIR_PATH}" \
-framework "${DEVICE_DIR_PATH}" \
-output "${TARGET_FOLDER}/${OUTPUT_SDK}.xcframework"
open "${TARGET_FOLDER}" # 打開文件夾
removeBuild
點擊SDK項目,然后在TARGETS
里選中SDKBuildScript
,上面選中Build Phases
,點擊左上角的“+”號,選擇New Run Script Phase
。
在黑框里輸入./SDKBuild.sh。
- 當然你也可以直接把腳本寫在黑框里,這樣子就不需要創建腳本文件了
如果使用腳本文件的話,會報沒權限的錯誤,所以需要使用命令行來打開權限(因為使用文件方便管理和編寫代碼,所以這里我選擇了使用文件的方式):
打開命令行,cd到SDKBuild.sh所在的目錄,然后執行sudo chmod +x SDKBuild.sh
即可解決權限問題- 在執行腳本前,需要配置好
.podspec
文件- 第一次運行腳本,編譯好庫后,需要自行執行一次
pod install
為demo工程安裝該SDK。此后就不需要再執行該命令了,因為腳本會把新打包好的SDK替換掉舊的SDK,如果配置有變,還是需要用pod命令進行更新- 更多SDK開發知識請看這篇文章:iOS】使用workspace搭建SDK開發框架
配置SDK工程
- 點擊SDK項目,然后在
TARGETS
里選中SDK的target,點擊上面的Build Settings
,找到Build Active Architecture Only
項設置為NO
(意思就是當前打包的framework支持所有的設備,否則打包時只能用當前版本的模擬器或真機運行) - 在
Build Settings
,找到Excluded Architectures
項,點擊展開,在Release
選項下點擊+
號,選擇Any iOS Simulator SDK
,值設置為arm64
(處理arm64架構合并報錯的問題;當然,也可以在腳本里處理) - 點擊
Edit Scheme..
選中腳本的target,選中Run
,把Build Configuration
的值改為Release
(設置生成的SDK為release版,當然也可以在腳本上設置,可以看腳本里的注釋) - 對于Swift工程,需要在
Build Settings
里找到Build libraries for Distribution
,設置YES
,否則在合并.xcframework
時會報No ‘swiftinterface’ files found within xx.swiftmodule
的錯(也可以在腳本設置) - 對于Swift工程,需要在
Build Settings
里找到Skip Install
,設置NO
,否則在歸檔的文件目錄Products下會沒有輸出文件(也可以在腳本設置) - 在
PROJECT
和TARGETS
的Build Settings
里,找到
Allow non-modular includes in Framework Modules
,并都設置為YES
;當動態庫需要引用第三方庫的Framework,需要告訴編譯器允許這種行為
如果工程報錯
no such module 'XXX'
,并且pods
工程下的Products
文件夾里的產物全是紅的,說明了沒生成對應的庫。解決方法為:點擊Pods
工程,在PROJECT
的Build Settings
里,找到Build Active Architecture Only
設置為NO
,Base SDK
設置為iOS
;每次pod install
后可能會被重置
git管理
創建組件的時候,已經默認創建了git,但是,這個git是組件的,現在得把SDK的git和組件的git進行分開,使得SDK的git作為組件git的子模塊進行管理。
創建遠程私有庫
因為SDK本身是沒有git的,所以需要用git init
命令給它創建一個本地git倉庫。
創建好SDK工程的git后,需要編輯.gitignore
文件,內容如下:
*~
.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
文件,同樣是上面的內容。
隨后創建2個遠程倉庫,并把它們和本地git倉庫關聯起來。
需要注意的是,遠程倉庫里,SDK的是私有的,pod組件可以是公開的。(因為代碼都在SDK里,所以公開pod組件庫也不會有什么問題,而且使用起來也更方便)
子模塊管理
我們為組件工程git添加子模塊,這個時候的git得是主模塊的git,即使用組件的git來執行添加命令。
添加子模塊的命令為git submodule add <url> <path>
,其中url可以是遠程地址和本地地址,本地地址要用絕對對路徑,path則是該子模塊存儲的目錄路徑(使用相對路徑)。
- 添加模塊之前,組件和SDK的git都需要先提交到遠程
- 如果提交子模塊提示
The following paths are ignored by one of your .gitignore files
,則用git submodule add -f
來添加
配置pod的索引文件
項目名.podspec文件(我這里是ZTTools_Swift.podspec),這個文件是用來描述這個pod的說明信息的。當pod install
安裝庫時,只會引入你在.podspec中配置的那些文件。
Pod::Spec.new do |s|
s.name = '組件名'
s.version = '版本號'
s.summary = '組件精簡描述'
s.description = <<-DESC
組件詳細描述
DESC
s.homepage = '組件主頁'
s.license = { :type => 'MIT', :file => 'LICENSE' }
s.author = { '用戶名' => '郵箱' }
s.source = { :git => 'git地址', :tag => s.version.to_s }
# 需要設置,不然項目引入庫后會崩潰
s.pod_target_xcconfig = {
'BUILD_LIBRARY_FOR_DISTRIBUTION' => 'YES'
}
s.user_target_xcconfig = { 'BUILD_LIBRARY_FOR_DISTRIBUTION' => 'YES' }
s.ios.deployment_target = '12.0' # 最低系統版本
s.swift_versions = ['5.0'] # Swift版本
s.vendored_frameworks = 'ZTTools_Swift/Classes/ZTToolsSDK.xcframework' # 使用私有庫
s.frameworks = 'UIKit', 'Foundation', 'Photos', 'UserNotifications', 'AVFoundation', 'CoreGraphics'
s.dependency 'Alamofire' # 依賴庫
s.dependency 'SnapKit'
end
如果集成組件后,項目運行報
One of the two will be used. Which one is undefined.
,說明是符號沖突了,這個是pod的問題。實際上是因為你的組件包含了第三方pod,然后使用組件的工程也包含了這個第三方pod導致的,不喜歡這個提示是話,可以在使用組件的工程里,找到Pods
文件夾,在該文件夾下所有的Pods-項目名.debug.xcconfig
和Pods-項目名.release.xcconfig
文件,找到文件里面的OTHER_LDFLAGS
,把有提示重復的-l"第三方pod名"
刪除即可
發布組件
校驗文件合法性
在發布之前,需要先轉到組件所在的文件夾,使用命令校驗.podspec
文件。(可能需要翻墻)
pod lib lint
是基礎校驗命令,用來校驗本地.podspec
文件的,如果要校驗遠程,把lib
改為spec
即可。(spec
會同時驗證本地和遠程是否通過)
- 如果使用了第三方庫,需要在后面加上
--use-libraries
參數 - 如果因為有警告導致報錯的,可以加上
--allow-warnings
參數解決 - 如果需要輸出詳細信息,可以加上
--verbose
參數 - 如果是私有的repo庫要就要加上
--sources=“私有庫的地址”
- 提示
passed validation
即為校驗通過- 提示
[!] The spec did not pass validation
即為校驗失敗- 只有校驗通過了,才能進行下一步操作
- 一般來說,只需要校驗本地即可
發布
需要先轉到組件所在的文件夾,使用命令pod trunk me
查看是否注冊trunk。
如果提示[!] Authentication token is invalid or unverified. Either verify it with the email that was sent or register a new session.
說明還沒注冊過trunk或者登錄已經過期了,需要執行pod trunk register 郵箱 '名字' --description='描述文本' --verbose
。(后面2個參數是可選的)
如果輸出名字、郵箱和注冊時間等信息,說明已經是注冊并是登錄狀態。這個時候就可以提交組件到pod了。
使用命令pod trunk push xxx.podspec
發布組件到pod,同樣的可以加上--allow-warnings
和--verbose
參數;如果要跳過驗證pod是否導入,還可以加上--skip-import-validation
參數。
- 發布之前請先打上tag,不然會發布失敗
- tag必須和
.podspec
文件的s.version
一致- 如果之前打了tag并發布了,更新了文件后請用新的tag,不然不生效(也就是說,你修了一個小bug,想同一個版本號,把本地和遠程的tag刪了,再打上同樣的tag并推上遠程,這種方法是不可行的)
- 提交成功后,并不一定能馬上搜索到,需要等待一天左右
更新組件
- 更新改動推送到遠程倉庫
- 打tag,并推送到遠程倉庫
- 執行發布命令即可
如果提示
[!] You need to register a session first.
,說明需要驗證會話。使用pod trunk register "你之前注冊的郵箱"
后,去郵箱點擊鏈接驗證即可
其他
- 使用命令
pod trunk delete 組件名 版本號
可以刪除已發布的庫的某一版本 - 在組件文件夾里,有一個和組件名一樣的文件夾,里面有2個文件夾,分別是放置資源的
Assets
和放置源碼或者庫的Classes
- 在組件文件夾里,有一個叫
Example
的文件夾,里面就是demo工程 - 打開demo的工作空間后,在
Pods
工程里,有一個叫ReplaceMe
的文件,是創建組件時默認生成的,刪除即可
歡迎來群139322447玩耍