---
title: Making Your Version Number Super-Visible in iOS
teaser: 'If you can''t tell me what version of my app you''re running, we''re both
  stuck.

  '
tags: swift,ios,mobile,user experience
author: Louis Antonopoulos
published_on: 2019-06-26
---

**Quick Note:** This post is about increasing version number visibility in iOS, 
but Android projects would benefit from a similar approach!

## **Why do I need to know the version number?**

I can't tell you the number of times I've had this kind of conversation when
trying to process a bug report:

**Customer:** Hey, the app isn't [*doing the thing it's supposed to do*].

**Me:** That's a problem! Let me try to reproduce it.

**Me:** *cannot reproduce the problem*

**Me:** Huh. That's weird. What version are you on?

**Customer:** *silence*

**Customer:** How would I figure that out?

**Me:** *silence*

This is one example that demonstrates why you need to make your version
information accessible in as many places as possible. If you haven't provided
your users multiple ways to identify what version they're on, you can get stuck
quickly.

Knowing the version number can help with many different situations:

* If a bug has been fixed in a particular version, you can stop triaging a
  reported issue immediately if you can tell that the user is on an older version.
* If a user reports an issue and they can demonstrate conclusively that they're
  on the latest version, you don't have to waste time having them reinstall the
  app.
* If a bug has been reported and you have multiple versions that are active, 
  being able to identify the version number on a device can help you deterimine 
  which specific version introduced the bug: "Look, it worked fine here in 
  1.5.2, but 1.5.3 is broken."
* When presenting a demo, knowing absolutely that the demo device is running the
  correct version (which may not be the latest) instills confidence and improves
  the chances of a successful demo.
* If the QA team routinely adds screenshots of the version they're testing to
  bug reports, it reduces uncertainty about the reliability of a test result.
* If a user swears that they're on the latest version, but they've been
  incorrect before and you need to verify the version number, being able to
  request a screenshot or a verbal confirmation increases confidence in the
  report.

## **Where should my app display its version number?**

### **On the LaunchScreen Storyboard**

If your app crashes on launch, a user isn't going to be able to look in a
Settings menu to tell you what version it is. Showing the version number on the
launch screen storyboard is a great way to solve this problem:

![LaunchScreen.storyboard](https://images.thoughtbot.com/blog-vellum-image-uploads/H2Cz58FQsKnDp8DvzXIQ_LaunchScreenStoryboard.png)

Note that the launch storyboard can't be changed dynamically when your app 
launches. But what you *can* do is dynamically update a static label on your 
LaunchScreen.storyboard as you create your build through a little scripting 
and a custom Build Phase.

**Start by adding a label to your `LaunchScreen.storyboard`:**

1. Format the label however you like. I like to use something subtle that you
  can read when you need to but that isn't very noticeable otherwise.

1. Set `Document/Label` to `APP_VERSION`. You will use this value in the Build
  Phase script.

![Xcode Document Label](https://images.thoughtbot.com/blog-vellum-image-uploads/Dl7srSqZRvWmwe2Tu7ls_DocumentLabel.png)

**Continue by adding the custom Build Phase:**

1. Select your project in the upper-left-hand corner of Xcode.

1. Select the app target towards the middle of the screen.

You should see a strip of tabs that includes `General`, `Capabilities`, [...],
  `Build Settings`, and then `Build Phases`.

1. Click on the `+` above the list of build phases and choose
  `New Run Script Phase`

1. Rename the phase to something like "Show Version on LaunchScreen". Xcode can
  be finicky about this part; if you can't rename the phase, don't worry too much
  about it.

1. Move the phase somewhere above `Copy Bundle Resources`.

1. Paste the following into the script area:

```
# Output version & build number to the APP_VERSION label on LaunchScreen.storyboard
versionNumber=$(/usr/libexec/PlistBuddy -c "Print CFBundleShortVersionString" "${INFOPLIST_FILE}")
buildNumber=$(/usr/libexec/PlistBuddy -c "Print CFBundleVersion" "${INFOPLIST_FILE}")

sed -i "" -e "/userLabel=\"APP_VERSION\"/s/text=\"[^\"]*\"/text=\"Version: $versionNumber ($buildNumber)\"/" "$PROJECT_DIR/$PROJECT_NAME/Storyboards/Base.lproj/LaunchScreen.storyboard"
```

**Note that the path in the `sed` command must match the path to your
LaunchScreen.storyboard.**
In this project, it's saved under the main project folder in a folder 
called `Storyboards`.

![Xcode Custom Build Phase](https://images.thoughtbot.com/blog-vellum-image-uploads/9GaKqNxmRvWa60ZazOIo_XcodeCustomBuildPhase.png)

### **In any kind of Settings menu, User Profile menu, or About popup**

This is the spot that you will most likely direct your user to when they ask the
question, "How do I know what version I'm on?"

You want it to be easy to navigate to, because you will either be writing
instructions to someone in Slack or in an email, or talking them through it on
the phone.

Other than that, there aren't any guidelines. Just be sure you report all the
information you need:

* Version number (major, minor, and build)
* Any special environment information (dev, staging, production)
* Anything else that's unique to your application that will help you when
  triaging issues

### **When the app first launches**

Log your version information to the console in `AppDelegate.swift`, at the very
beginning of `application(:didFinishLaunchingWithOptions:)`.

It's good practice to log your version info to the Console on launch. Why? A few
reasons.

1. If you don't implement the storyboard custom Build Phase, or if your app
  loads so quickly that the user can't see what it says, this is yet another way
  to identify the version number on launch. If you connect your device to your
  Mac, you can use the `Console.app` to see the log message as it goes by.

1. If you're filtering a bunch of messages from the log, having a phrase you can
  search for is very helpful.

1. When you're developing, seeing that version information scroll by in Xcode
  each time you launch your app can be incredibly helpful. Personally, I've caught
  myself multiple times either testing the wrong version or seeing that I was in
  the wrong environment before taking in-app actions I would have regretted.

```
func application(_: UIApplication, didFinishLaunchingWithOptions _: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
  print("\(Bundle.main.versionString) (\(Bundle.main.bundleID))")

  [...]

  return true
}
```

### **When you check in changes to Info.plist**

This is tangential to the above, in that it's not something your users will see,
but if you use the version number as your git commit message for `Info.plist`,
it makes it incredibly easy to visually scroll through your commit history and
identify what features and fixes were present in a particular version.

![Version History](https://images.thoughtbot.com/blog-vellum-image-uploads/17yzwFeWSbfeFr46Npgd_VersionHistory_Small.png)

## **How about some code samples?**

**Extensions to retrieve and format version information:**

```
import Foundation

extension Bundle {
  private var releaseVersionNumber: String {
    return infoDictionary?["CFBundleShortVersionString"] as? String ?? ""
  }

  private var buildVersionNumber: String {
    return infoDictionary?["CFBundleVersion"] as? String ?? ""
  }

  var bundleID: String {
    return Bundle.main.bundleIdentifier?.lowercased() ?? ""
  }

  var versionString: String {
    var scheme = ""

    // If you use different bundle IDs for different environments, code like this is helpful:
    if bundleID.contains(".dev") {
      scheme = "Dev"
    } else if bundleID.contains(".staging") {
      scheme = "Staging"
    }

    let returnValue = "Version \(releaseVersionNumber).\(buildVersionNumber) \(scheme)"

    return returnValue.trimmingCharacters(in: .whitespacesAndNewlines)
  }
}
```

**Log the version info in `AppDelegate.swift`:**

```
func application(_: UIApplication, didFinishLaunchingWithOptions _: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
  print("\(Bundle.main.versionString) (\(Bundle.main.bundleID))")

  [...]

  return true
}
```

## **Can I download a working project to see all this in action?**

Of course! A working example of this post is in a 
[repository in GitHub](https://github.com/thoughtbot/louis-a-blog-samples)
that will continue to evolve and be referenced by multiple posts.

[**Get the code that corresponds to this post: Release 2019-06-26**](https://github.com/thoughtbot/louis-a-blog-samples/releases/tag/2019-06-26)

---

Thanks for reading, everyone!

May this help you and your users in all your projects.
