创建组件工程
启动命令行,使用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玩耍