Flutter IOS - Keep all targets' versions and build numbers up-to-date - ios

By default, only the Runner target's version and build number stay up-to-date.
I've had to add another target for a plugin I'm using, and at the moment I have to manually keep its version and build number up-to-date.
The reason I care about it is because App Store keeps warning me everytime I upload a build where the targets' versions and build numbers are not in sync.
How can I keep the second target's version and build number up-to-date automatically as well? I suppose it should somehow be done in the Podfile?

I, too, was facing the same problem and used to manually change the version every-time before archiving.
If you are still facing this issue, I have got a work around for this. You can try adding the below code into build phase of the main Target.
#!/bin/bash
buildNumber=$(cut -d'=' -f2 <<< $(cat Flutter/Generated.xcconfig | grep FLUTTER_BUILD_NUMBER))
buildName=$(cut -d'=' -f2 <<< $(cat Flutter/Generated.xcconfig | grep FLUTTER_BUILD_NAME))
target1="firstTargetName"
target2="secondTargetName"
echo "kushal___ $buildNumber"
echo "kushal___ $buildName"
#/usr/libexec/PlistBuddy -c "Set :CFBundleVersion $buildNumber" "$INFOPLIST_FILE"
#/usr/libexec/PlistBuddy -c "Set :CFBundleShortVersionString $buildName" "$INFOPLIST_FILE"
/usr/libexec/PlistBuddy -c "Set :CFBundleVersion $buildNumber" "$SRCROOT/$target1/Info.plist"
/usr/libexec/PlistBuddy -c "Set :CFBundleShortVersionString $buildName" "$SRCROOT/$target1/Info.plist"
/usr/libexec/PlistBuddy -c "Set :CFBundleVersion $buildNumber" "$SRCROOT/$target2/Info.plist"
/usr/libexec/PlistBuddy -c "Set :CFBundleShortVersionString $buildName" "$SRCROOT/$target2/Info.plist"

First you should add a new xcconfig file to you project(Version.xcconfig).
The xcconfig file should look like this(must override the OTHER_LDFLAGS variable ):
#include "Flutter/Generated.xcconfig"
OTHER_LDFLAGS=
Then set you new targets to use the configuration.
Last just set your target info.plist's version and build number to Flutter build name and number.
Build your project in flutter, then all the target's version will be just the same as you set in you pubspec.yaml file.

Related

Settings.bundle version number is updating as $(MARKETING_VERSION)

I have an app which was setting versions automatically when I incremented from
XCode > General > Version.
But recently I have updated XCode to 11.0 and seems the script is not working as expected:
version=`/usr/libexec/PlistBuddy -c "Print CFBundleShortVersionString" $SRCROOT/MyApp/Info.plist`
version+=" ("
version+=`/usr/libexec/PlistBuddy -c "Print CFBundleVersion" $SRCROOT/MyApp/Info.plist`
version+=")"
/usr/libexec/PlistBuddy "$SRCROOT/MyApp/Settings.bundle/Root.plist" -c "set PreferenceSpecifiers:1:DefaultValue $version"
Above script suppose to automatically update version and would have been visible in Settings > App.
But the question is there any change need to be done for this script to make automatically update version number from XCode?
Currently it is being replaced by scripts as $(MARKETING_VERSION) when version is incremented from XCode > General > Version which is not correct.
The version string $MARKETING_VERSION as well as build number $CURRENT_PROJECT_VERSION are now exposed as environment variable during the build process as they are now persisted in the .pbxproj configuration.
You should be able to achieve what you want like this:
version="$MARKETING_VERSION ($CURRENT_PROJECT_VERSION)"
/usr/libexec/PlistBuddy "$SRCROOT/MyApp/Settings.bundle/Root.plist" -c "set PreferenceSpecifiers:1:DefaultValue $version"
It worked by displaying MARKETING_VERSION itself: Thanks #dgimb and #Mojtaba Hosseini for your answers.
version="$MARKETING_VERSION"
version+=" ("
version+=`/usr/libexec/PlistBuddy -c "Print CFBundleVersion" $SRCROOT/MyApp/Info.plist`
version+=")"
/usr/libexec/PlistBuddy "$SRCROOT/MyApp/Settings.bundle/Root.plist" -c "set PreferenceSpecifiers:1:DefaultValue $version"

Add New Run Script Action for 300+ schemes in Xcode

I have an Xcode project which has over 300 schemes/targets and I have a custom script that I need to run before the app starts building. I've figured out how to do this by editing desired scheme, select Build->Pre-actions->New Run Script Action->Provide Build Settings From and then pasting this script (which dynamically changes version info and bundle id in my notification extension's plist file):
buildNumber=$(/usr/libexec/PlistBuddy -c "Print CFBundleVersion" "${PROJECT_DIR}/$INFOPLIST_FILE")
/usr/libexec/PlistBuddy -c "Set :CFBundleVersion $buildNumber" "${SRCROOT}/NotificationService/Info.plist"
buildVersion=$(/usr/libexec/PlistBuddy -c "Print CFBundleShortVersionString" "${PROJECT_DIR}/$INFOPLIST_FILE")
/usr/libexec/PlistBuddy -c "Set :CFBundleShortVersionString $buildVersion" "${SRCROOT}/NotificationService/Info.plist"
buildID=${PRODUCT_BUNDLE_IDENTIFIER}
/usr/libexec/PlistBuddy -c "Set :CFBundleIdentifier $buildID.NotificationService" "${SRCROOT}/NotificationService/Info.plist"
The issue is I have to do this for over 300 of my schemes and is a tedious timely effort. Is there some way to apply this to all of my schemes/targets?
Ended up solving issue by creating a fastlane script which basically adds this pre-action run script inside each of the xcscheme files in the appropriate spot in the XML.

Setting bundle version based on git in xcode

I am trying to set the bundle version in xcode based on git.
#!/bin/bash
BRANCH=${1:-'master'}
BUILD_NUMBER=$(expr $(git rev-list $BRANCH --count) - $(git rev-list HEAD..$BRANCH --count))
echo "Updating build number to $BUILD_NUMBER using branch '$BRANCH'."
APP_INFO_PLIST="${TARGET_BUILD_DIR}/${INFOPLIST_PATH}"
DSYM_INFO_PLIST="${DWARF_DSYM_FOLDER_PATH}/${DWARF_DSYM_FILE_NAME}/Contents/Info.plist"
/usr/libexec/PlistBuddy -c "Set :CFBundleVersion $BUILD_NUMBER" "$APP_INFO_PLIST"
if [ -f "$DSYM_INFO_PLIST" ] ; then
/usr/libexec/PlistBuddy -c "Set :CFBundleVersion $BUILD_NUMBER" "$DSYM_INFO_PLIST"
fi
But I run into an error on this line:
/usr/libexec/PlistBuddy -c "Set :CFBundleVersion $BUILD_NUMBER" "$DSYM_INFO_PLIST"
I get an error reading that file "Error Reading File: ...".
However, when I check for that file it does exist. Was it created after this build step maybe?
I did double check that this script is set to run after the Copy Bundle resources and after some of my cocoa pods stuff.
EDIT:
Searching around it looks like this solution was fine with xocxcodee 6. I am running xcode 7.
My problem was how I set the build version in xcode. I had it set to
<set in build phase>
The plist file is xml and xcode does not escape the text you put in the build. the '<' and '>' were screwing up the plist as it was invalid xml, which in turn threw the error when trying to read the file.
Careful the text you put in xcode for your tmp build version.

How to automatically set the version and build number of a Watchkit app target

The version and build number (or version and short version) of a Watchkit app and extension have to be set to the same value as the containing app.
I use environment variables to set the apps version in the Info.plist dynamically at build time. That also works fine for the Watchkit extension, but not for the Watchkit app.
The environment variables I use have to be provided in the plist for the main app and extension without ${} (for variable ${VERSION} I set VERSION).
if I do the same for the Watchkit app, it is taking the string itself, not the value. If I provide it with dollar & brackets there is no data in the variable.
Any idea how to set the variables for the Watchkit app?
I use this to update all the targets:
#!/bin/bash
buildNumber=$(/usr/libexec/PlistBuddy -c "Print CFBundleVersion" "$INFOPLIST_FILE")
buildNumber=$(($buildNumber + 1))
/usr/libexec/PlistBuddy -c "Set :CFBundleVersion $buildNumber" "$INFOPLIST_FILE"
/usr/libexec/PlistBuddy -c "Set :CFBundleVersion $buildNumber" "$SRCROOT/Great WatchKit App/Info.plist"
My CFBundleVersion is the number of commits on my master branch on the git repo.
On my main app target, in Build Phases > + New Run Script Phase I've added this script:
# Set the build number to the count of Git commits
buildNumber=$(git rev-list --count HEAD)
/usr/libexec/PlistBuddy -c "Set :CFBundleVersion $buildNumber" "${PROJECT_DIR}/${INFOPLIST_FILE}"
/usr/libexec/PlistBuddy -c "Set :CFBundleVersion $buildNumber" "$SRCROOT/app WatchKit Extension/Info.plist"
/usr/libexec/PlistBuddy -c "Set :CFBundleVersion $buildNumber" "$SRCROOT/app WatchKit App/Info.plist"
From app WatchKit App the app should be the name of your app, but check the exact path.
You can update the build version of all your targets without a build script. (You can also use this to update the marketing / short build version; in this case ignore changes to CFBundleVersion).
Open your project settings and set CURRENT_PROJECT_VERSION (Current Project Version) to the desired version number. In all targets make sure CURRENT_PROJECT_VERSION is empty (so that its value is inherited from the project). Then in all Info.plist files set CFBundleShortVersionString (Bundle versions string, short) and CFBundleVersion (Bundle version / build version) to $(CURRENT_PROJECT_VERSION).
Optionally, you can distinguish between the build version and the "marketing" version (this is useful if the build version is generated automatically). To do so, in your project settings, set MARKETING_VERSION (Marketing Version) to the desired version number. As before, make sure all targets inherit the value from the project. Finally, in all Info.plist files set CFBundleShortVersionString (Bundle versions string, short) to $(MARKETING_VERSION).
If you want to increment your CFBundleVersion on each build (or to have it reflect your git SHA). Use agvtool as described by dogsgod or see https://developer.apple.com/library/ios/qa/qa1827/_index.html.
stk's answer is right, but I wanted to add my findings as well.
One way to solve the problem is by using agvtools:
Create a new target in OSX > Other > External Build Sytem
Add a run script similar to this one:
#!/bin/bash
#read vesion number from version.txt in project root
VERSION=$(head -n 1 version.txt)
BUILD=`git rev-list $(git rev-parse --abbrev-ref HEAD) | wc -l | awk '{ print $1 }'`
echo "${VERSION} (${BUILD})"
agvtool new-marketing-version ${VERSION}
agvtool new-version -all ${BUILD}
exit 0
I have a version.txt file with only my version number in it (marketing version or short bundle version) that can be easily adjusted by any CI system and use the number of my git SHAs as build number (bundle version)
Adjust the sources for VERSION and BUILD to fit your requirements
Run the scheme that has been created for the new target before your build/archiving.
In case you need to have this as a dependency for your main target - this will fail, as it will stop the execution of the following targets (if somebody knows how to prevent that, I would be thankful for a hint)
But you can still achieve that with a script like the following executed for each of your plists (similar to what stk provided):
#!/bin/sh
#
# usage:
# set-version-in-plist.sh LIST VERSION BUILD
# LIST: Info.plist path & name
# VERSION: version number xxx.xxx.xxx
# BUILD: build number xxxxx
#
# Location of PlistBuddy
PLISTBUDDY="/usr/libexec/PlistBuddy"
${PLISTBUDDY} -c "Set :CFBundleShortVersionString $2" "$1";
${PLISTBUDDY} -c "Set :CFBundleVersion $3" "$1";
Save this script as a file, make it executable (chmod +x SCRIPTNAME)
Then execute it with the mentioned parameter for all your plists
This solution is not so convenient as the agvtools solution, but it should not stop your build when used in a dependency ...
I have a Run Script that I attach to my main app target. It will propagate the WatchKit Extension and the WatchKit app upon building the app.
It is completely reusable. Enjoy!
buildNumber=$(/usr/libexec/PlistBuddy -c "Print CFBundleVersion" "${PROJECT_DIR}/${INFOPLIST_FILE}")
buildNumberDec=$(($buildNumber + 1))
/usr/libexec/PlistBuddy -c "Set :CFBundleVersion $buildNumberDec" "${PROJECT_DIR}/${INFOPLIST_FILE}"
/usr/libexec/PlistBuddy -c "Set :CFBundleVersion $buildNumberDec" "$SRCROOT/${PRODUCT_NAME} WatchKit Extension/Info.plist"
/usr/libexec/PlistBuddy -c "Set :CFBundleVersion $buildNumberDec" "$SRCROOT/${PRODUCT_NAME} WatchKit App/Info.plist"
Well, if it doesn´t work like this, do it with a Run Script Build Phase. Do something like this:
#!/bin/sh
INFOPLIST="${TARGET_BUILD_DIR}/${INFOPLIST_PATH}"
echo "writing to $INFOPLIST"
PLISTCMD="Set :CFBundleVersion $(git rev-list --all|wc -l)"
echo -n "$INFOPLIST" | xargs -0 /usr/libexec/PlistBuddy -c "$PLISTCMD"
I don´t have the right paths for your WatchKit App, so you will have to change that yourself.
If it would be useful to supplement other answers with my own personal experience. These centred around build failure caused by ValidateEmbeddedBinary.
ValidateEmbeddedBinary will fail if the CFBundleVersion is not the same in the embedded WatchKit app and the parent app.
The error looks something like:
(null): error: The value of CFBundleVersion in your WatchKit app's
Info.plist (1234) does not match the value in your companion app's
Info.plist (7931). These values are required to match.
Working in XCode 7.3, the following will first update the parent app's plist. Then it updates the Debug or Release WatchKit app before PBXCp executes to copy it to the parent app directory:
#!/bin/sh
git=`sh /etc/profile; which git`
appBuild=`"$git" rev-list HEAD --count`
appPlistPath="${TARGET_BUILD_DIR}/${INFOPLIST_PATH}"
watchKitPlistPath="${BUILT_PRODUCTS_DIR}/../${CONFIGURATION}-watchos/${PRODUCT_NAME} WatchKit App.app/Info.plist"
echo "Setting App CFBundleVersion $appBuild at info plist path at ${appPlistPath}"
/usr/libexec/PlistBuddy -c "Set :CFBundleVersion $appBuild" "${appPlistPath}"
echo "Setting WatchKit App CFBundleVersion $appBuild at ${watchKitPlistPath}"
/usr/libexec/PlistBuddy -c "Set :CFBundleVersion $appBuild" "${watchKitPlistPath}"
The above uses git's commit count as CFBundleVersion.
Simple, Easy, No Scripting Needed
Tested with Xcode 13.4.1
This is by far the easiest way to sync build numbers and marketing versions across your targets.
Step 1
Make sure that the project's Current Project Version and Marketing Version are set. This will be the single source of truth for your build number and marketing version. Find these settings by clicking the project in the navigator, clicking the project (above the targets), and typing "versioning" into the filter.
Step 2
Sync Build Number / Project Version (e.g. 123)
In your targets, go to the Build Settings tab and set Current Project Version to $(CURRENT_PROJECT_VERSION).
In your .plist files set Bundle version (same as CFBundleVersion) to $(CURRENT_PROJECT_VERSION).
Sync Marketing Version (e.g. 1.2.3)
In your targets, go to the Build Settings tab and set Marketing Version to $(MARKETING_VERSION)
In your .plist files set Bundle version string (short) (same as CFBundleShortVersionString) to $(MARKETING_VERSION).
Here's a link to my blog post on the topic with more helpful screenshots.
To extend this thread, if someone encounters similar problem as me, hopefully script below will help.
For instance, I have watch target, watch extension, and an app share extension in my project.
I used run phase to update project's plist as suggested, and it works if I build the project, the .plist files are all updated as expected.
However the problem is when you archiving app(let's say all targets have different build number), the info plist in the archived projects was not updated.
After a few times of try out, I found extension's plist files were copied before this run phase, and then the run phase script(updating project's plist) won't help with the archived plist.
So I eventually changed the script to update compiled target's plist, and it works as I expected, I have same build number for all the targets in the application.
Here is how I did it:
add this script to each target's build phase:
infoPlistPath="${TARGET_BUILD_DIR}/${EXECUTABLE_FOLDER_PATH}/Info.plist"
PLISTBUDDY="/usr/libexec/PlistBuddy"
buildNumber=$(git rev-list HEAD | wc -l | tr -d ' ')
$PLISTBUDDY -c "Set :CFBundleVersion $buildNumber" "${infoPlistPath}"
For different target, this EXECUTABLE_FOLDER_PATH was different, and it will update the compiled target's info plist, instead of the project's info plist.
Just a note I checked "Run script only when installing" as well since I only need this to be run for archiving
You can also simply add the same Build Phase to EACH of your targets.
#Update build number with number of git commits if in release mode
if [ ${CONFIGURATION} == "Release" ]; then
buildNumber=$(git rev-list HEAD | wc -l | tr -d ' ')
/usr/libexec/PlistBuddy -c "Set :CFBundleVersion $buildNumber" "${PROJECT_DIR}/${INFOPLIST_FILE}"
fi;
So if you have a Watch App, a Watch App Extension, a Widget Extension, or whatever else extension, go to each of their Build Phases, add a New Run Script Phase and paste the above.

How to increase minor version by archive on <name>-AppStore scheme? And increase build version by build

I am new be sh script. I did research on how to auto increase build number and version number. I know how to create an script, and let XCode to invoke that script when I build the app. But I don't see any solution can handle my task.
My question is:
Is there any existing script can do both following two things?
1. increase minor version by archive when using -AppStore scheme.
2. increase build version by build.
P.S.
I found Version vs build in XCode 4 , I have implement #nekno 's solution.
In Xcode 4.2:
Load your Xcode project.
In the left hand pane, click on your project at the very top of the hierarchy. This will load the project settings editor.
On the left-hand side of the center window pane, click on your app under the TARGETS heading. You will need to configure this setup for each project target.
Select the Build Phases tab.
At the bottom right, click the Add Build Phase button and select Add Run Script.
Drag-and-drop the new Run Script phase to move it to just before the Copy Bundle Resources phase (when the app-info.plist file will be bundled with your app).
In the new Run Script phase, leave the Shell: /bin/sh value alone. Copy and paste the following into the script area for integer build numbers:
#!/bin/bash
buildNumber=$(/usr/libexec/PlistBuddy -c "Print CFBundleVersion" "$INFOPLIST_FILE")
buildNumber=$(($buildNumber + 1))
/usr/libexec/PlistBuddy -c "Set :CFBundleVersion $buildNumber" "$INFOPLIST_FILE"
As #AlonAmir contributed, you can use the following script instead for hex build numbers:
#!/bin/bash
buildNumber=$(/usr/libexec/PlistBuddy -c "Print CFBundleVersion" "$INFOPLIST_FILE")
buildNumber=$((0x$buildNumber))
buildNumber=$(($buildNumber + 1))
buildNumber=$(printf "%X" $buildNumber)
/usr/libexec/PlistBuddy -c "Set :CFBundleVersion $buildNumber" "$INFOPLIST_FILE"
And if you have a Settings bundle where you show the Version and Build, you can add the following to the end of the script to update the version and build. Note: Change the PreferenceSpecifiers values to match your settings. PreferenceSpecifiers:2 means look at the item at index 2 under the PreferenceSpecifiers array in your plist file, so for a 0-based index, that's the 3rd preference setting in the array.
productVersion=$(/usr/libexec/PlistBuddy -c "Print CFBundleShortVersionString" "$INFOPLIST_FILE")
/usr/libexec/PlistBuddy -c "Set PreferenceSpecifiers:2:DefaultValue $buildNumber" Settings.bundle/Root.plist
/usr/libexec/PlistBuddy -c "Set PreferenceSpecifiers:1:DefaultValue $productVersion" Settings.bundle/Root.plist
And if you have a universal app for iPad & iPhone, then you can also set the settings for the iPhone file:
/usr/libexec/PlistBuddy -c "Set PreferenceSpecifiers:2:DefaultValue $buildNumber" Settings.bundle/Root~iphone.plist
/usr/libexec/PlistBuddy -c "Set PreferenceSpecifiers:1:DefaultValue $productVersion" Settings.bundle/Root~iphone.plist
But I got error:
Script-E14D3F9517E94A3300ABC5CA.sh: line 4: 1.0.18 + 1: syntax error: invalid arithmetic operator (error token is ".0.18 + 1")
Command /bin/sh failed with exit code 1
I guess it is because that script is out of date to answer this question. I'm using XCode 4.6.3

Resources