iOS开发之Xcode中版本号&Build版本号自增

18 篇文章 0 订阅

1. 背景

在iOS项目打包时,有两个版本号,一个是Version,即显示在App Store中的版本号,其key为CFBundleShortVersionString;另一个是Build,即编译版本号,其key为CFBundleVersion

2. 问题发现

当App准备上架时,需要打包提交至App Store审核,在这个过程中我们可能会多次上传ipa包。在Version相同的情况下,若Build还相同,则上传到App Store Connect时会提示已有该版本,不能再次上传。

3. 问题解决

为了避免这种情况,我们可以在每次打包前去手动修改Build,以保证不会重复。或者我们可以通过脚本让Build在每次打包时自动加一,省去每次手动修改的麻烦。

2023.07 功能增强:版本号和Build版本号都可自动+1(版本号中的最小数字版本号自动+1)。

  • 假如当前Version版本号为1.0.0,则+1后为1.0.1
  • 假如当前Build版本号为10,则+1后为11

1、可通过设置脚本中的VERSION_INCREASE_WHEN_BUILDBUILD_INCREASE_WHEN_BUILD两个变量来控制Version或Build是否自动+1。
2、可到Xcode项目的.xcodeproj/project.pbxproj配置文件中获取Release XCBuildConfiguration节点 和 Debug XCBuildConfiguration节点的UUID,分别赋值给该脚本中的变量VERSION_UUID_ReleaseVERSION_UUID_Debug

  • 查找XCBuildConfiguration节点UUID的方法:先搜索“isa = XCBuildConfiguration”节点,再查看该节点是否包含MARKETING_VERSIONCURRENT_PROJECT_VERSION,若包含,则取该节点的UUID。
添加脚本的流程:

1.Xcode切换到 Build Phases 选项卡;
2.点击左上角"+“号来增加一项"New Run Script Phase”;
3.添加如下脚本代码:

# ******************************************************
#
# Copyright 2015-2023 BTStudio. All rights reserved.
#
# Name          : Version Increase
# Author        : Wangzhi
# Last modified : 2023-07-03
# Email         : wzqsyk@126.com
# Description   : 每次Build或Archive后,版本号中的最小数字版本号自动+1。
#
#
# 1. 最小数字版本号:版本号中最后一个.后的数字。无.时,与版本号中的数字相同。
#
# 2. 版本号格式:(注意:版本号设置为以下格式时,该脚本才能实现其功能。)
# - 1.0.0       规范的版本号 (推荐用于上架版本号)
# - 1.0.0_Beta  规范的版本号加上以下划线分割的后缀 (推荐用于开发阶段的版本号)
# - 10          纯数字版本号 (推荐用于Build版本号)
# - 10_Beta     纯数字版本号加上以下划线分割的后缀 (推荐用于Build版本号)
#
# 3. 版本号自动+1:
# - 若当前版本号为1.0.0,则+1后为1.0.1
# - 若当前版本号为1.0.0_Beta,则+1后为1.0.1_Beta
# - 若当前版本号为1,则+1后为2
# - 若当前版本号为1_Beta,则+1后为2_Beta
# - 若当前版本号为1.0.0.00,则+1后为1.0.0.01
#
# 4. 软件版本号规范
# 软件版本号由4部分组成:主版本号.子版本号.阶段版本号.日期版本号加希腊字母版本号。
# 示例:1.0.0_Alpha、1.0.0.230701_Alpha
#
# - 主版本号:当功能模块有较大的变动(比如增加多个模块或整体架构发生变化)时进行修改。
# - 子版本号:当功能有一定的增加或变化时进行修改。
# - 阶段版本号:一般是Bug修复或是一些小的变动,要经常发布修订版时进行修改。
# - 日期版本号:用于记录修改项目的当前日期,每天对项目的修改都需要修改。
# - 希腊字母版本号:用于标注当前版本的软件处于哪个开发阶段,当软件进入到另一个阶段时需要修改。
#   (开发阶段:Base、Alpha、Beta、RC、Release)
#
# ******************************************************


# 每次编译后版本号是否+1 (0-不执行+1,1-每次Build后+1,2-每次Archive后+1)
VERSION_INCREASE_WHEN_BUILD=2
# 每次编译后编译版本号是否+1 (0-不执行+1,1-每次Build后+1,2-每次Archive后+1)
BUILD_INCREASE_WHEN_BUILD=2

# 项目配置文件project.pbxproj的路径
ProjectPbxprojPath="${PROJECT_NAME}.xcodeproj/project.pbxproj"

# 配置文件中 MARKETING_VERSION 和 CURRENT_PROJECT_VERSION 所属节点(对象)的UUID
# UUID示例:336C88122A4D5E2C003DA11D,UUID需要手动到project.pbxproj中查找确定。
# 方法:先搜索isa = XCBuildConfiguration节点,再查看该节点是否包含
# MARKETING_VERSION 和 CURRENT_PROJECT_VERSION,若包含,则取该节点的UUID。
VERSION_UUID_Release=""
VERSION_UUID_Debug=""
    
# 注意:项目设置有不同Target时,需要根据Target名称确定UUID
if [[ "${TARGET_NAME}" == "XXX" ]]; then
    VERSION_UUID_Release=""
    VERSION_UUID_Debug=""
elif [[ "${TARGET_NAME}" == "XXX_Beta" ]]; then
    VERSION_UUID_Release=""
    VERSION_UUID_Debug=""
fi


ReleaseVersion="1.0.0" # 版本号默认值
BuildVersion="1" # 编译版本号默认值

# Info.plist中的版本号(CFBundleShortVersionString)
VersionOfInfo=""
# Info.plist中的编译版本号(CFBundleVersion)
BuildVersionOfInfo=""
# 项目配置文件project.pbxproj中的版本号(MARKETING_VERSION)
VersionOfConfig=""
# 项目配置文件project.pbxproj中的编译版本号(CURRENT_PROJECT_VERSION)
BuildVersionOfConfig=""

# Build或Archive的标志,默认为空
ConfigFlagForVersion=""
ConfigFlagForBuild=""


echo "VI_当前Xcode的 Build Configuration: $CONFIGURATION"

if [ $VERSION_INCREASE_WHEN_BUILD -eq 1 ]; then
    ConfigFlagForVersion="Debug"
elif [ $VERSION_INCREASE_WHEN_BUILD -eq 2 ]; then
    ConfigFlagForVersion="Release"
fi

if [ $BUILD_INCREASE_WHEN_BUILD -eq 1 ]; then
    ConfigFlagForBuild="Debug"
elif [ $BUILD_INCREASE_WHEN_BUILD -eq 2 ]; then
    ConfigFlagForBuild="Release"
fi


# 数字版本号自动+1函数
# - 参数: version
function version_auto_increment() {
    version=$1
    version_new=$1
    
    if [ -n "$version" ]; then
        # =~     : 用于判断左边的字符串和右边的正则表达式pattern是否匹配。
        # #*"."  : 从字符串第一次出现.的位置开始,截取.右边的所有字符。
        # ##*"." : 从字符串最后一次出现.的位置开始,截取.右边的所有字符。
        # %"."*  : 从字符串最后一次出现.的位置开始,截取.左边的所有字符。
        # %%"."* : 从字符串第一次出现.的位置开始,截取.左边的所有字符。
                        
        major_version="" # 主版本号
        minor_version="" # 小版本号
        suffix_version="" # 版本号后缀
        
        # 符合格式:10
        if [[ "$version" =~ ^[0-9]+$ ]]; then
            # 示例版本号:12345
            #
            # 主版本号:无
            # 小版本号:12345
            # 版本号后缀:无
            
            minor_version=$version
            #printf "VI_版本号: ${version}=${major_version}+${minor_version}+${suffix_version}"
                                
        # 符合格式:1.0.0
        elif [[ "$version" =~ ^[0-9][0-9\.]*[0-9]$ ]]; then
            # 示例版本号:1.23.45
            #
            # 主版本号:1.23
            # 小版本号:45
            # 版本号后缀:无
            
            major_version=${version%"."*}
            
            minor_version=${version##*"."}
            minor_version=${minor_version%%"_"*}
            #printf "VI_版本号:: ${version}=${major_version}+${minor_version}+${suffix_version}"
                        
        # 符合格式:10_Beta
        elif [[ "$version" =~ ^[0-9]+_[0-9A-z_]*$ ]]; then
            # 示例版本号:12345_Beta
            #
            # 主版本号:无
            # 小版本号:12345
            # 版本号后缀:Beta
            
            minor_version=${version%%"_"*}
            suffix_version=${version#*"_"}
            #printf "VI_版本号::: ${version}=${major_version}+${minor_version}+${suffix_version}"
                        
        # 符合格式:1.0.0_Beta
        elif [[ "$version" =~ ^[0-9][0-9\.]*[0-9]_[0-9A-z_]*$ ]]; then
            # 示例版本号:1.23.45_Beta
            #
            # 主版本号:1.23
            # 小版本号:45
            # 版本号后缀:Beta
            
            major_version=${version%"."*}
            
            minor_version=${version##*"."}
            minor_version=${minor_version%%"_"*}
                        
            suffix_version=${version#*"_"}
            #printf "VI_版本号:::: ${version}=${major_version}+${minor_version}+${suffix_version}"
            
        else
            version_new=$version
            #printf "VI_版本号: ${version} 不符合格式,不做+1处理"
        fi
        
        # 小版本号不为空,再去处理小版本号+1
        if [ -n "$minor_version" ]; then
            minor_length=${#minor_version}
            # 小版本号+1 (10#表示让Shell强制将"00009"当成十进制来解释,而不是八进制)
            minor_version=$((10#$minor_version + 1))
            # 小版本号格式化
            if [ $minor_length -eq 2 ]; then
                minor_version=$(printf "%02d" $minor_version)
            elif [ $minor_length -eq 3 ]; then
                minor_version=$(printf "%03d" $minor_version)
            elif [ $minor_length -eq 4 ]; then
                minor_version=$(printf "%04d" $minor_version)
            elif [ $minor_length -eq 5 ]; then
                minor_version=$(printf "%05d" $minor_version)
            else
                minor_version=$(printf "%d" $minor_version)
            fi
                     
            # 拼接新版本号   
            # 追加 . 和 _
            if [ -n "$major_version" ]; then
                major_version="${major_version}."
            fi
            if [ -n "$suffix_version" ]; then
                suffix_version="_${suffix_version}"
            fi
            
            version_new=${major_version}${minor_version}${suffix_version}
            #printf "VI_小版本号+1后: ${version_new}"
        fi
    fi
    
    # return只能返回1-255的整数,通常只是用来供其他地方调用获取状态,一般用0表示成功,1表示失败
    #return 1
     
    # echo用于返回字符串结果
    echo ${version_new}
}


# 版本号的处理
#if [ -z "$VERSION_UUID_Release" ]; then
#    echo "VI_请到项目.xcodeproj/project.pbxproj中获取Release XCBuildConfiguration节点的UUID,并赋值给该脚本中的变量VERSION_UUID_Release"
#fi
if [[ "${ConfigFlagForVersion}" == "${CONFIGURATION}" ]]; then
    # 用于处理的版本号
    VersionOfHandle=""

    # 1. 读取版本号
    VersionOfInfo=$(/usr/libexec/PlistBuddy -c "Print :CFBundleShortVersionString" "${INFOPLIST_FILE}")
    VersionOfConfig=${MARKETING_VERSION}
    : '
    # 1.1 版本号的确定
    # 判断读取出来的VersionOfInfo是否为空字符串:
    # - 是:不做处理
    # - 否:再判断是否为字符串"$(MARKETING_VERSION)"
    # -- 是:则需从MARKETING_VERSION读取版本号
    # -- 否:则读取出来的VersionOfInfo即是版本号
    if [ -n "$VersionOfInfo" ]; then
        if [[ "$VersionOfInfo" == "\$(MARKETING_VERSION)" ]]; then
            VersionOfHandle=$VersionOfConfig
            echo "VI_Version(Info.plist): \$(MARKETING_VERSION)"
        else
            VersionOfHandle=$VersionOfInfo
            echo "VI_Version(Info.plist): ${VersionOfHandle}"
        fi
    else 
        echo "VI_Version(Info.plist): is empty"
    fi
        
    # VersionOfInfo和VersionOfConfig中以后者为优先,所以再判断
    # 读取出来的VersionOfConfig是否为空字符串:
    # - 是:不做处理
    # - 否:则读取出来的VersionOfConfig即是版本号
    if [ -n "$VersionOfConfig" ]; then
        VersionOfHandle=$VersionOfConfig
        echo "VI_Version(project.pbxproj): ${VersionOfHandle}"
    else
        echo "VI_Version(project.pbxproj): is empty"
    fi
    '
    if [ -n "$VersionOfInfo" ]; then
        if [[ "$VersionOfInfo" == "\$(MARKETING_VERSION)" ]]; then
            VersionOfHandle=$VersionOfConfig
            echo "VI_Version(Info.plist): \$(MARKETING_VERSION)=${VersionOfHandle}"
        else
            VersionOfHandle=$VersionOfInfo
            echo "VI_Version(Info.plist): ${VersionOfHandle}"
        fi
    else 
        VersionOfHandle=$VersionOfConfig
        echo "VI_Version(project.pbxproj): ${VersionOfHandle}"
    fi
    
    # 2. 版本号+1处理
    ReleaseVersion=$(version_auto_increment $VersionOfHandle)
    echo "VI_Version小版本号+1后: ${ReleaseVersion}"
    
    # 3. 写入新版本号
    # 3.1 新版本号写入Info.plist
    #if [ -n "$VersionOfInfo" ] && [[ "$VersionOfInfo" != "\$(MARKETING_VERSION)" ]]; then
    if [ -n "$VersionOfInfo" ]; then
        echo "VI_New Version写入: Info.plist"
        /usr/libexec/PlistBuddy -c "Set :CFBundleShortVersionString $ReleaseVersion" "${INFOPLIST_FILE}"
    fi
    : '
    # 3.2 新版本号写入项目配置文件project.pbxproj
    if [ -n "$VersionOfConfig" ]; then
        echo "VI_New Version写入: project.pbxproj"
        # Debug
        /usr/libexec/PlistBuddy -c "Set :objects:${VERSION_UUID_Debug}:buildSettings:MARKETING_VERSION ${ReleaseVersion}" "${ProjectPbxprojPath}"
        # Release
        /usr/libexec/PlistBuddy -c "Set :objects:${VERSION_UUID_Release}:buildSettings:MARKETING_VERSION ${ReleaseVersion}" "${ProjectPbxprojPath}"
    fi
    '
    echo "VI_Release version need increase."
else
    echo "VI_Release version don't need increase."
fi


# Build版本号的处理
#if [ -z "$VERSION_UUID_Debug" ]; then
#    echo "VI_请到项目.xcodeproj/project.pbxproj中获取Debug XCBuildConfiguration节点的UUID,并赋值给该脚本中的变量VERSION_UUID_Debug"
#fi
if [[ "${ConfigFlagForBuild}" == "${CONFIGURATION}" ]]; then
    # 用于处理的编译版本号
    BuildVersionOfHandle=""
    
    # 1. 读取Build版本号
    BuildVersionOfInfo=$(/usr/libexec/PlistBuddy -c "Print :CFBundleVersion" "${INFOPLIST_FILE}")
    BuildVersionOfConfig=${CURRENT_PROJECT_VERSION}
    : '
    # 1.1 编译版本号的确定(与版本号的确定逻辑相同)
    if [ -n "$BuildVersionOfInfo" ]; then
        if [[ "$BuildVersionOfInfo" == "\$(CURRENT_PROJECT_VERSION)" ]]; then
            BuildVersionOfHandle=$BuildVersionOfConfig
            echo "VI_Build(Info.plist): \$(CURRENT_PROJECT_VERSION)"
        else
            BuildVersionOfHandle=$BuildVersionOfInfo
            echo "VI_Build(Info.plist): ${BuildVersionOfHandle}"
        fi
    else 
        echo "VI_Build(Info.plist): is empty"
    fi
    
    if [ -n "$BuildVersionOfConfig" ]; then
        BuildVersionOfHandle=$BuildVersionOfConfig
        echo "VI_Build(project.pbxproj): ${BuildVersionOfHandle}"
    else
        echo "VI_Build(project.pbxproj): is empty"
    fi
    '
    if [ -n "$BuildVersionOfInfo" ]; then
        if [[ "$BuildVersionOfInfo" == "\$(CURRENT_PROJECT_VERSION)" ]]; then
            BuildVersionOfHandle=$BuildVersionOfConfig
            echo "VI_Build(Info.plist): \$(CURRENT_PROJECT_VERSION)=${BuildVersionOfHandle}"
        else
            BuildVersionOfHandle=$BuildVersionOfInfo
            echo "VI_Build(Info.plist): ${BuildVersionOfHandle}"
        fi
    else
        BuildVersionOfHandle=$BuildVersionOfConfig
        echo "VI_Build(project.pbxproj): ${BuildVersionOfHandle}"
    fi
    
    # 2. 版本号+1处理
    BuildVersion=$(version_auto_increment $BuildVersionOfHandle)
    echo "VI_Build小版本号+1后: ${BuildVersion}"
    
    # 3. 写入新版本号
    # 3.1 新版本号写入Info.plist
    #if [ -n "$BuildVersionOfInfo" ] && [[ "$BuildVersionOfInfo" != "\$(CURRENT_PROJECT_VERSION)" ]]; then
    if [ -n "$BuildVersionOfInfo" ]; then
        echo "VI_New Build写入: Info.plist"
        /usr/libexec/PlistBuddy -c "Set :CFBundleVersion $BuildVersion" "${INFOPLIST_FILE}"
    fi
    : '
    # 3.2 新版本号写入项目配置文件project.pbxproj
    if [ -n "$BuildVersionOfConfig" ]; then
        echo "VI_New Build写入: project.pbxproj"
        # Debug
        /usr/libexec/PlistBuddy -c "Set :objects:${VERSION_UUID_Debug}:buildSettings:CURRENT_PROJECT_VERSION ${BuildVersion}" "${ProjectPbxprojPath}"
        # Release
        /usr/libexec/PlistBuddy -c "Set :objects:${VERSION_UUID_Release}:buildSettings:CURRENT_PROJECT_VERSION ${BuildVersion}" "${ProjectPbxprojPath}"
    fi
    '
    echo "VI_Build version need increase."
else
    echo "VI_Build version don't need increase."
fi





效果如下:
Version自动+1的脚本
4.之后每次进行打包时,Version或Build会自动+1,避免了Version或Build重复的问题。

提示:

  1. 可以双击 Version Increase 进行名称的修改;
  2. 增加一项 New Run Script Phase 后,Xcode默认名称为Run Script
  3. 每次编译或打包后,可以查看脚本的执行结果,如下图所示:
    右上角搜索框中输入VI_即可过滤显示脚本的执行结果。

    脚本的执行结果



小知识

1. PlistBuddy

PlistBuddy是Mac自带的专门解析plist的小工具,由于PlistBuddy并不在Mac默认的Path里,所以我们得通过绝对路径(/usr/libexec/PlistBuddy)来引用这个工具。

2. Shell基本语法
  1. 定义变量时,变量名不加美元符号$ (PHP语言中变量需要)。如:
    your_name=“runoob.com” (注意:变量名和等号之间不能有空格)
  2. 使用一个定义过的变量,只要在变量名前面加美元符号即可。如:
    echo $your_name
  3. 读取plist中某个字段的值,并赋给变量var:
    var=$(/usr/libexec/PlistBuddy -c “Print :字段名” plist文件路径)
  4. 修改plist中某个字段相应的值:
    /usr/libexec/PlistBuddy -c “Set :字段名 值” plist文件路径
  5. Shell的echo指令与PHP的echo指令类似,都是用于字符串的输出。
    命令格式:echo string
  6. Shell脚本没有{}括号,所以用fi表示if语句块的结束。
  7. #! 是一个约定的标记,它告诉系统这个脚本需要什么解释器来执行,即使用哪一种Shell。
  • 1
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值