美文网首页iOS开发分享
【iOS】使用pod创建私有组件

【iOS】使用pod创建私有组件

作者: 子天々君 | 来源:发表于2022-03-11 18:52 被阅读0次

    创建组件工程

    启动命令行,使用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下会没有输出文件(也可以在脚本设置)
    • PROJECTTARGETSBuild Settings里,找到
      Allow non-modular includes in Framework Modules,并都设置为YES;当动态库需要引用第三方库的Framework,需要告诉编译器允许这种行为

    如果工程报错no such module 'XXX',并且pods工程下的Products文件夹里的产物全是红的,说明了没生成对应的库。解决方法为:点击Pods工程,在PROJECTBuild Settings里,找到Build Active Architecture Only设置为NOBase 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.xcconfigPods-项目名.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玩耍

    相关文章

      网友评论

        本文标题:【iOS】使用pod创建私有组件

        本文链接:https://www.haomeiwen.com/subject/relhdrtx.html