Xcode-Increment build number only during ARCHIVE? - xcode

I have found a few other posts that show how to add a script to increment the build number with a script:
Better way of incrementing build number?
Xcode project's "Build number"
Can Xcode insert the version number into a library's filename when building?
But what I want to do, is only increase the build number when I use ARCHIVE (both before and after).
Example:
If current build number is 21, then when I choose Product > Archive the build number will be increased to 22, it will go through its process of building and creating the Archive file with the build number of 22, and then when it is finished archiving, it will increase the build number to 23.

Add the following script, as in the example listed in the first link that you posted, BUT do it twice. Once at the beginning of the build and once at the end:
if [ $CONFIGURATION == Release ]; then
echo "Bumping build number..."
plist=${PROJECT_DIR}/${INFOPLIST_FILE}
# increment the build number (ie 115 to 116)
buildnum=$(/usr/libexec/PlistBuddy -c "Print CFBundleVersion" "${plist}")
if [[ "${buildnum}" == "" ]]; then
echo "No build number in $plist"
exit 2
fi
buildnum=$(expr $buildnum + 1)
/usr/libexec/Plistbuddy -c "Set CFBundleVersion $buildnum" "${plist}"
echo "Bumped build number to $buildnum"
else
echo $CONFIGURATION " build - Not bumping build number."
fi
Many thanks to the authors of the questions that you have linked to in your question for the information that got me started on this answer!

This is very similar to #Inafziger's answer, but a more concise set of code, with the added benefit that the check for "Release" is done with a checkbox in XCode rather than a runtime variable:
Follow these instructions twice, dragging one to the beginning and one to the end (one to run before build and one to run after build):
# xcode-build-bump.sh
# #desc Auto-increment the build number every time the project is run.
# #usage
# 1. Select: your Target in Xcode
# 2. Select: Build Phases Tab
# 3. Select: Add Build Phase -> Add Run Script
# 4. Paste code below in to new "Run Script" section
# 5. Drag the "Run Script" below "Link Binaries With Libraries"
# 6. Ensure that your starting build number is set to a whole integer and not a float (e.g. 1, not 1.0)
# 7. Check the checkbox "Run script only when installing"
buildNumber=$(/usr/libexec/PlistBuddy -c "Print CFBundleVersion" "${PROJECT_DIR}/${INFOPLIST_FILE}")
buildNumber=$(($buildNumber + 1))
/usr/libexec/PlistBuddy -c "Set :CFBundleVersion $buildNumber" "${PROJECT_DIR}/${INFOPLIST_FILE}"
See https://gist.github.com/sekati/3172554

Xcode includes the command line tool agvtool to increase version numbers. So you don't have to do everything manually with PListBuddy.
xcrun agvtool next-version -all
increases your build number.
xcrun agvtool new-marketing-version 2.0
sets a new user visible version number.
See the full documentation for details.

Related

How to enable/disable “Show as run destination” for all simulators

Is it possible to toggle the “Show as run destination” flag for multiple iOS simulators instead of changing it one by one in the "Device and Simulators" window?
Is there a command line with that purpose?
I decided to find it by myself using fswatch. BTW, it's really useful for this kind of situations. By monitoring the changes of file while toggling the "Show as run destination" flag, I found out that Xcode was changing the ~/Library/Preferences/com.apple.dt.Xcode.plist file 🙌
After some analysis, I noticed the key that I needed to change to achieve what I had in mind. The key is DVTIgnoredDevices and it holds an array of simulators. So, each simulator UUID in that list will be ignored in Xcode.
Now, I can change the DVTIgnoredDevices key using defaults command line tool specifying the needed value type:
-array Allows the user to specify an array as the value for the given preference key:
defaults write somedomain preferenceKey -array element1 element2 element3
The specified array overwrites the value of the key if the key was present at the time of the write. If the key was not present, it is created with the new value.
Example:
defaults write com.apple.dt.Xcode DVTIgnoredDevices '(
"80E16DBC-2FE5-48AC-8A44-1F5DEFA00EA7",
"B8C4D5FF-8F1A-4895-BD16-CCAFECD71098"
)'
After setting the DVTIgnoredDevices key, you need to clean the DerivedData folder and restart Xcode. To clean the DerivedData folder, please see this answer or just run the shortcut shift+alt+cmd+k (that's what I usually do).
Tested on Xcode Version 9.4 (9F1027a).
UPDATE:
I usually like to have just a couple of Simulators in the list, so I decided to do a script using instruments -s devices and add all the current Simulators to the DVTIgnoredDevices key. Then I chose which simulator(s) will be shown 😊
Xcode-hide-all-iPhone-simulators.sh
simulatorsIdentifiers=$(instruments -s devices |
grep -o "iPhone .* (.*) \[.*\]" | #only iPhone
grep -o "\[.*\]" | #only UUID
sed "s/^\[\(.*\)\]$/\1/" | #remove square brackets
sed 's/^/"/;$!s/$/"/;$s/$/"/' | #add quotes
sed '$!s/$/,/' #add comma to separate each element
)
arrayOfSimulatorsIdentifiers=($(echo "$simulatorsIdentifiers" | tr ',' '\n'))
# Add simulators to DVTIgnoredDevices
echo "${#arrayOfSimulatorsIdentifiers[#]}"
for index in "${!arrayOfSimulatorsIdentifiers[#]}"
do
echo "$index Adding: ${arrayOfSimulatorsIdentifiers[index]}"
done
defaults write com.apple.dt.Xcode DVTIgnoredDevices -array ${arrayOfSimulatorsIdentifiers[#]}
Gist file

Test for file with a specific type exists

In fish, you can test if a specific file exists with test -e hello.txt, but I'm wondering if there's a way to use wildcard values in the file name, to check for any existence of a file type.
To be more specific, I'm making a quick function that checks for an Xcode Workspace, if found open it, if not then check for an Xcode Project, and if found it opens that, otherwise print an error.
Here is the current implementation:
function xcp
open *.xcodeproj
end
function xcw
open *.xcworkspace
end
function xc
if test -e *.xcworkspace
xcw
else if test -e *.xcodeproj
xcp
else
echo "No Xcode Workspace or Project in this directory."
end
end
It "works" at the minute, but when it doesn't find a workspace or project file, it prints an error about there being no matches. However this is done by default, and I'm wondering if I can hide this somehow.
Here is the error output, when it doesn't find a workspace, but does find a project and opens that:
No matches for wildcard '*.xcworkspace'. (Tip: empty matches are allowed in 'set', 'count', 'for'.)
~/.config/fish/functions/fish_prompt.fish (line 1): if test -e *.xcworkspace
^
in function 'xc'
called on standard input
Try counting them:
function xc
set -l workspaces *.xcworkspace
set -l projects *.xcodeproj
if test (count $workspaces) -gt 0
xcw
else if test (count $projects) -gt 0
xcp
else
echo "No Xcode Workspace or Project in this directory."
end
end

Update Info.plist values in archive

I need to be able to set a couple custom values in the Info.plist during the Xcode ARCHIVE process. This is for Xcode 6 and Xcode 7.
I already have a script in place that successfully updates these values as a post-action on the BUILD process. It works great when deploying to the simulator or to a phone from Xcode 6.
However, the Info.plist doesn't seem to be available from within the directory structures during the ARCHIVE process. After a BUILD, I can find the results under .../Build/Products in $CONFIGURATION-iphoneos and $CONFIGURATION-iphonesimulator. But after the ARCHIVE, there isn't anything there and I only find the compiled binaries under .../Build/Intermediates.
Certainly, I can see the Info.plist in the IPA itself. Yet any attempts to update and replace this file after the fact are unsuccessful; the IPA is no longer valid, I assume due to checksum changes or something.
I don't want to update these values in the source Info.plist (e.g., using a pre-action) as it will always make the source dirty every time I archive.
Figured this out. The process is nearly identical to the build -- use a post-action for the build, use a post-action for the archive -- just the path is different (all listed below) for where to find the Info.plist.
Below is my build script where I've used tokens for the "name" and the "value" to be updated in Info.plist. I just copied this script and renamed it for use with the archive post-action. Note that this script also has an example of extracting a value from Info.plist as I am deriving the web services version from the client version.
The path to the build Info.plist is either of:
"$BUILD_DIR/$CONFIGURATION-iphoneos/$PRODUCT_NAME.app/Info.plist"
"$BUILD_DIR/$CONFIGURATION-iphonesimulator/$PRODUCT_NAME.app/Info.plist"
NOTE: Both targets are being updated for a build since I've not figured out a way to identify which build it is doing.
The path to the archive Info.plist is:
"$ARCHIVE_PRODUCTS_PATH/Applications/$PRODUCT_NAME.app/Info.plist"
Build post-action:
$SRCROOT/post_build.sh <value> ~/xcode_build_$PRODUCT_NAME.out
Build script:
#!/bin/bash
# post_build.sh
#
# This script is intended for use by Xcode build process as a post-action.
# It expects the only argument is the value to be updated in Info.plist. It
# derives the WS version for the URL from the version found in Info.plist.
printf "Running $0 using scheme '$SCHEME_NAME' as '$USER'\n"
# If this is a clean operation, just leave
if [ $COPY_PHASE_STRIP == "YES" ]
then
printf "Doing a clean; exiting.\n"
exit 1
fi
# Confirm that PlistBuddy is available
PLIST_BUDDY=/usr/libexec/PlistBuddy
if ![-f "$PLIST_BUDDY"]
then
printf "Unable to access $PLIST_BUDDY\n"
exit 1
else
printf "PLIST_BUDDY=$PLIST_BUDDY\n"
fi
# Function to perform the changes
updatePlist()
{
PLIST_FILE=$1
if [ -f "$PLIST_FILE" ]
then
printf "Determing WS version...\n"
if [[ $SCHEME_NAME == *"Local"* ]]
then
WS_VER=""
else
# Determine the services version
BUILD_VER=$(${PLIST_BUDDY} -c "Print CFBundleShortVersionString" "$PLIST_FILE")
WS_VER=$(printf $BUILD_VER | sed 's/\(.*\)\..*/\1/' | sed 's/\./_/g')
fi
# Update the plist
${PLIST_BUDDY} -c "Set <name> <value>" "$PLIST_FILE"
printf "Updated plist $PLIST_FILE\n"
else
printf "Skipping -- no plist: $PLIST_FILE\n"
fi
}
# Retrieve the supplied URL
BASE_URL=$1
printf "BASE_URL=$BASE_URL\n\n"
# Record the environment settings
printenv | sort > ~/xcode_build_$PRODUCT_NAME.env
# Locate the plist in the device build
printf "Checking device build...\n"
updatePlist "$BUILD_DIR/$CONFIGURATION-iphoneos/$PRODUCT_NAME.app/Info.plist"
printf "\n"
# Locate the plist in the simulator build
printf "Checking simulator build...\n"
updatePlist "$BUILD_DIR/$CONFIGURATION-iphonesimulator/$PRODUCT_NAME.app/Info.plist"
printf "\n"

Per-file compiler flags in Xcode project template

I am building a new Xcode project template and I want to include a library that is non-ARC. But my whole project is ARC, so the only way how to build the project is to specify a compiler flag (-fno-objc-arc) on the files from that library.
How do I do that in an Xcode project template?
I tried setting it on specific files in the Definitions dictionary, both as COMPILER_FLAG and CompilerFlag. Neither of them works.
I have found absolutely no documentation on this, but I am pretty sure it can be done.
UPDATE:
Apple replied to my support request stating that there is no way of doing that right now. So unfortunately, we are out of luck, until they finally do something about the templates and their documentation.
UPDATE 2:
I've got an idea how to hack this a little bit by using a build phases script, that will check the Xcode project and add the required flags. I will post an answer soon.
I know this page is old but I wanted to offer my take on the provided scripts:
echo "Checking compiler flags."
PROJ_FILE="${PROJECT_FILE_PATH}/project.pbxproj"
REQUIRE_ARC=("File1ThatNeedsARC.m" "File2ThatNeedsARC.m")
REQUIRE_NO_ARC=("File1ThatNeedsARC.m" "File2ThatNeedsARC.m")
STR_TEST_CASE="File1ThatNeedsARC.m \*/; };"
if grep "${STR_TEST_CASE}" "$PROJ_FILE"
then
echo "Adding compiler flags."
for file in ${REQUIRE_ARC[#]}
do
STR_SRCH_ARC=$file" \*/; };"
STR_RPLC_ARC=$file" \*/; settings = {COMPILER_FLAGS = \"-fobjc-arc\"; }; };"
if grep "${STR_SRCH_ARC}" "$PROJ_FILE"
then
sed -i "" "s|${STR_SRCH_ARC}|${STR_RPLC_ARC}|g" "$PROJ_FILE"
fi
done
for file in ${REQUIRE_NO_ARC[#]}
do
STR_SRCH_NOARC=$file" \*/; };"
STR_RPLC_NOARC=$file" \*/; settings = {COMPILER_FLAGS = \"-fno-objc-arc\"; }; };"
if grep "${STR_SRCH_NOARC}" "$PROJ_FILE"
then
sed -i "" "s|${STR_SRCH_NOARC}|${STR_RPLC_NOARC}|g" "$PROJ_FILE"
fi
done
else
echo "Compiler flags already added."
exit 0
fi
just update the arrays REQUIRE_ARC and REQUIRE_NO_ARC (content separated by spaces) with the .m files appropriately.
Also update the test case STR_TEST_CASE
This script should be added to the build phases (Editor>Add Build Phase > Add Run Script Build Phase) of your xcode project (above Compile Sources).
Thank you to every one for there help that led to this script.
-
Dan
Ok, so even though I got a negative response from Apple (they don't support this in their template parser) I've found a way how to do it.
I would basically add new build phase to the template - run script. This script woudl flag the required files with the -fno-objc-arc flag and then delete itself from the project.
This is how you can add the flags:
PROJ_FILE="${PROJECT_FILE_PATH}/project.pbxproj"
STR_SRCH="\* Class.m \*\/"
STR_RPLC="\* Class.m *\/; settings = {COMPILER_FLAGS = \"-fno-objc-arc\"; };"
sed -i "" "s|${STR_SRCH};|${STR_RPLC}|g" "$PROJ_FILE"
Then in a similar manner you scan the project file and remove the build phase (with the script), so it doesn't get run each time.
I will update this answer with complete code soon.
After trying to make sure my script doesn't run after it's initial build, I've done the following:
echo "Adding compiler flags"
PROJ_FILE="${PROJECT_FILE_PATH}/project.pbxproj"
STR_SRCH="SomeClass.m \*/; };"
STR_RPLC="SomeClass.m \*/; settings = {COMPILER_FLAGS = \"-fobjc-arc\"; }; };"
if grep "${STR_SRCH}" "$PROJ_FILE"
then
sed -i "" "s|${STR_SRCH}|${STR_RPLC}|g" "$PROJ_FILE"
else
exit 0
fi
the grep will make sure that I have that exact string to replace. Otherwise, I'm assuming the fobjc-arc flag has already been added and I no longer need this script. This allowed me to run the build without removing the actual script.
You can add this flag int the project Build Phases>Compile sources as shown in the screenshot below. Put this flag in all .m files of this library:

Insert Subversion revision number in Xcode

I would like to insert the current Subversion revision number (as reported by svnversion) into my Xcode project. I managed to insert the revision number into the Info.plist in the $PROJECT_DIR, but this is not a good solution, since the file is versioned. I tried to insert the revision into the Info.plist in the build directory, but then I get an error during the code signing phase (this is an iPhone application).
Is there a simple way to get the revision number into the application using some build files, so that the changing revision does not change versioned files? I thought maybe I could create a temporary source file that would link with the others and provide a function to get the revision number. But I don’t know how to do that.
I am not much interested in other solutions, ie. the agvtool. All I want is the revision number available to the application, without too much magic.
There's a much simpler solution: using PlistBuddy, included at /usr/libexec/PlistBuddy in Leopard. See my answer to a related SO question for details.
PlistBuddy can be used in a Run Script build phase from within Xcode, and can be used to only affect the processed plist file. Just put it after the Copy Resources phase, and you don't even have to clean the target for it to run each time. You don't even have to print the value to a header file and make SVN ignore it, either.
echo -n ${TARGET_BUILD_DIR}/${INFOPLIST_PATH} \
| xargs -0 /usr/libexec/PlistBuddy -c "Set :CFBundleVersion `svnversion -n`"
Assuming you add the build phase before code signing occurs, your plist should be signed with the substituted value.
For posterity, I did something similar to zoul for iPhone applications, by adding a revision.h to my project, then adding the following as a Run Script build phase:
REV=`/usr/bin/svnversion -nc ${PROJECT_DIR} | /usr/bin/sed -e 's/^[^:]*://;s/[A-Za-z]//'`
echo "#define kRevisionNumber #\"$REV\"" > ${PROJECT_DIR}/revision.h
I did this to grab a simple revision number, as opposed to the more detailed string that svnversion produces in zoul's solution.
For Mac applications, I based my approach on this post, and instead created a buildnumber.xcconfig file. Under the build settings for the target, I changed the Based On value in the lower-right-hand corner of the dialog to buildnumber.xcconfig. Within the Info.plist, I edited the following lines:
<key>CFBundleVersion</key>
<string>${BUILD_NUMBER}</string>
<key>CFBundleShortVersionString</key>
<string>Version 1.0</string>
So that my About dialog would display a version string similar to Version 1.0 (1234), where 1234 is the Subversion revision number. Finally, I created a Run Script build phase with the following code:
REV=`/usr/bin/svnversion -nc ${PROJECT_DIR} | /usr/bin/sed -e 's/^[^:]*://;s/[A-Za-z]//'`
echo "BUILD_NUMBER = $REV" > ${PROJECT_DIR}/buildnumber.xcconfig
This may not be the cleanest way, as it requires a clean cycle before building for the new revision to take hold in the application, but it works.
As a new user to Stack Overflow, I can't comment on Quinn's post, but I have a small change to make his solution a bit more accurate if you're using a SVN repository that has multiple projects going on at once.
Using his approach, the svnversion number that is returned is the last check-in for the entire repository, not necessarily your code base. This tweak allows for the update to be specific to your code base.
REV=`svnversion -nc | /usr/bin/sed -e 's/^[^:]*://;s/[A-Za-z]//'`
/usr/libexec/PlistBuddy -c "Set :CFBundleVersion $REV" "${TARGET_BUILD_DIR}"/${INFOPLIST_PATH}
Using the -c flag will gather the last commit done in the active branch/tag/trunk for your codebase in the form of :, then chop off the bits you don't want to store as the Revision number.
Also, notice the double quotes around the ${TARGET_BUILD_DIR}. Those are needed for users that decide to place their project in a directory structure with spaces in the name.
Hope this helps others!
I found this page when trying to do a similar thing for my iPhone app and thought it might be helpful to share the code I decided on. I was trying to have a base version number set in my Target Info (for example 0.9.5) but then append my SVN revision number at the end of it. I needed this stored in CFBundleVersion so that AdHoc users would be able to update via iTunes even if I didn't remember to rev the version number in my Target Info pane. That's why I couldn't use the "revision.h" method which otherwise worked beautifully. Here's the final code I settled on which I've placed as a Run Script phase just after the "Copy Bundle Resources" build phase:
BASEVERNUM=`/usr/libexec/PlistBuddy -c "Print :CFBundleVersion" "${INFOPLIST_FILE}" | sed 's/,/, /g'`
REV=`svnversion -n`
SVNDATE=`LC_ALL=C svn info | awk '/^Last Changed Date:/ {print $4,$5}'`
/usr/libexec/PlistBuddy -c "Set :CFBundleVersion $BASEVERNUM.$REV" "${TARGET_BUILD_DIR}"/${INFOPLIST_PATH}
/usr/libexec/PlistBuddy -c "Set :BuildDateString $SVNDATE" "${TARGET_BUILD_DIR}"/${INFOPLIST_PATH}
It should append the results of svnversion to the end of whatever is set in the base Info.plist as the version. This way you can have something like 0.9.5 in your info plist and still have the .189 revision number appended at the end, giving a final version number of 0.9.5.189
Hope this helps someone else!
# Xcode auto-versioning script for Subversion
# by Axel Andersson, modified by Daniel Jalkut to add
# "--revision HEAD" to the svn info line, which allows
# the latest revision to always be used.
#
# modified by JM Marino to change only [BUILD] motif
# into CFBundleGetInfoString key.
#
# HOW TO USE IT: just add [BUILD] motif to your Info.plist key :
# CFBundleVersion
#
# EXAMPLE: version 1.3.0 copyright 2003-2009 by JM Marino
# with [BUILD] into CFBundleVersion key
use strict;
die "$0: Must be run from Xcode" unless $ENV{"BUILT_PRODUCTS_DIR"};
# Get the current subversion revision number and use it to set the CFBundleVersion value
#my $REV = `/usr/local/bin/svnversion -n ./`;
my $REV = `/usr/bin/svnversion -n ./`;
my $INFO = "$ENV{BUILT_PRODUCTS_DIR}/$ENV{WRAPPER_NAME}/Contents/Info.plist";
my $version = $REV;
# (Match the last group of digits without optional letter M | S):
($version =~ m/(\d+)[MS]*$/) && ($version = "" . $1);
die "$0: No Subversion revision found" unless $version;
open(FH, "$INFO") or die "$0: $INFO: $!";
my $info = join("", <FH>);
close(FH);
#$info =~ s/([\t ]+<key>CFBundleVersion<\/key>\n[\t ]+<string>.+)\[BUILD\](<\/string>)/$1$version$2/;
$info =~ s/([\t ]+<key>CFBundleVersion<\/key>\n[\t ]+<string>)\[BUILD\](<\/string>)/$1$version$2/;
open(FH, ">$INFO") or die "$0: $INFO: $!";
print FH $info;
close(FH);
Since I can't comment on Quinn's answer:
if you're using the MacPorts svn client, it may be necessary to include the full path of the svnversion command, /opt/local/bin/svnversion in my case. I also added the -c option to svnversion.
Also, if you're only interested in the second version number of mixed revisions, add a 'cut' command to the invocation, like this:
echo -n "${TARGET_BUILD_DIR}/${INFOPLIST_PATH}" \
| xargs -0 /usr/libexec/PlistBuddy -c "Set :CFBundleVersion `/opt/local/bin/svnversion -nc | cut -f2 -d:`"
Another version, written in the Apple Script. Regexp for the previousValue could be changed, currently it supports only versions in XX.XX.XX format (major, minor, svn rev).
Run by /usr/bin/osascript
set myVersion to do shell script "svn info | grep \"^Revision:\""
set myVersion to do shell script "echo " & quoted form of myVersion & "| sed 's/Revision: \\([0-9]\\)/\\1/'" as string
set myFile to do shell script "echo ${BUILT_PRODUCTS_DIR}/${WRAPPER_NAME}/"
set theOutputFolder to myFile as string
set thePListPath to POSIX path of (theOutputFolder & "Info.plist")
tell application "System Events"
tell property list file thePListPath
tell contents
set previousValue to value of property list item "CFBundleVersion"
set previousValue to do shell script "echo " & quoted form of previousValue & "| sed 's/\\([0-9]*\\.[0-9]*\\)\\(\\.[0-9]*\\)*/\\1/'" as string
set value of property list item "CFBundleVersion" to (previousValue & "." & myVersion)
end tell
end tell
end tell

Resources