Run Script Phase
Add this as a “Run Script” phase (need help finding this?). It will add a new BuildInfo.plist
into your app bundle, containing both the long and short git hashes, as well as the branch name. You could also use this to add other info, such as build timestamp, etc. (this has been tested with XCode 16)
# configure git and grab version
GIT_BIN=`xcrun -find git`
REVISION=`${GIT_BIN} rev-parse --short HEAD`
REVISION_LONG=`${GIT_BIN} rev-parse HEAD`
BRANCH_NAME=`${GIT_BIN} branch | grep '*' | cut -d' ' -f2`
BUILT_AT=`date -u +"%Y-%m-%dT%H:%M:%SZ"`
# output location - this is the main bundle, under "BuildInfo.plist"
MAIN_BUNDLE="${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}"
OUTPUT_FILE="${MAIN_BUNDLE}/BuildInfo.plist"
# Add new properties to a brand new plist file at our destination
/usr/libexec/PlistBuddy -c "Add :ShortVersion string ${REVISION}" "$OUTPUT_FILE"
/usr/libexec/PlistBuddy -c "Add :LongVersion string ${REVISION_LONG}" "$OUTPUT_FILE"
/usr/libexec/PlistBuddy -c "Add :BranchName string ${BRANCH_NAME}" "$OUTPUT_FILE"
/usr/libexec/PlistBuddy -c "Add :BuiltAt string ${BUILT_AT}" "$OUTPUT_FILE"
Input & Output Files
This step is required unless you have disabled sandboxing in your project for some reason. You should add these to the script build phase you added, in the fields directly beneath the script editor:
Add the following to your “Input Files” for this script phase:
$(SRCROOT)/.git/
Similarly, add the following to your “Output Files” for this script phase:
$(TARGET_BUILD_DIR)/$(UNLOCALIZED_RESOURCES_FOLDER_PATH)/BuildInfo.plist
Accessing the Version Info
This is an example of a function I will sometimes use on my AppDelegate, although it can be done anywhere. In the below example, when you call AppDelegate.versionHash()
, it returns the git revision that the current app was built from.
It works pretty similar to how you might return something such as the Bundle Version, Build Number, etc. from your app’s Info.plist.
class func versionHash () -> String {
// get path to file in bundle - skips rest if not set
if let dictionaryPath = Bundle.main.path(forResource: "BuildInfo", ofType: "plist") {
// read the BuildInfo into a dict
if let dictionary = NSDictionary(contentsOfFile: dictionaryPath) {
// retrieve the property you want.
// Can be "ShortVersion", "LongVersion", "BranchName", or any custom property you added to above build script
return dictionary["ShortVersion"] as? String ?? ""
}
}
return "unknown"
}
Where do I find this?
You can find the Build Phases tab within your project settings, see below. Click the Plus in the upper right-hand corner, then “New Run Script Phase”.