Fastlane: Solving the WatchKit CFBundleVersion matching problem

Fastlane is one of the tools that a developer shouldn’t live without. At this point it must have saved me hundreds of hours.  As great as fastlane is out of the box it has a few rough edges when you introduce a WatchKit app.

Recently I added a WatchKit app into one of my projects and started getting the below error:

error: The value of CFBundleVersion in your WatchKit app’s Info.plist (262) does not match the value in your companion app’s Info.plist (263). These values are required to match.

In a nutshell, Apple requires that your WatchKit app and it’s companion app share the same version. If your fastlane build includes increment_build_number these will never match by default.

Solving this problem is pretty easy, although alittle more involved then just changing your versioning system like in your core app. Hopefully the below will help people Googling for this error in the future.

Step 1: Update your Watch App’s Versioning Configurations

The first thing you will need to do is update the versioning configuration for your Watch App’s target.

  1. In Xcode click on your Watch App’s target
  2. Go to the Build Settings tab
  3. Scroll (or search) for the Versioning section
  4. Update the “Current Project Version” to match your core app target’s version. It is important you get this correct the first time.
  5. Switch the “Versioning System” to “Apple Generic”

watchapp-versioning.png

With these changes in place fastlane increment your Watch App’s Current Project Version every time increment_build_number is called. This will work just like it does in your core app target.

Step 2: Update your Watch App’s Extension Configuration

Just like step 1, you will need to update the versioning configuration for your Watch App’s extension target.

  1. In Xcode click on your Watch App’s Extension target
  2. Go to the Build Settings tab
  3. Scroll (or search) for the Versioning section
  4. Update the “Current Project Version” to match your core app target’s version. It is important you get this correct the first time.
  5. Switch the “Versioning System” to “Apple Generic”

watchext-versioning.png

This again let’s fastlane increment your Watch App’s Extension when increment_build_number is called.

Step 3: Keeping your CFBundleVersion in sync

If you run your fastlane build process now you will notice you still get an error that your CFBundleVersion information does not match. Whereas fastlane is updating the Current Project Version in both your Watch App and Watch App Extension it doesn’t update their CFBundleVersion value.

The best way I’ve found to do this is to use PlistBuddy and update the CFBundleVersion as part of the fastlane build process. Below is a small convenience function created to manage the process of updating the build numbers.

def setBuildNumberOnExtension(build_number)
raise if build_number.nil?
puts "Setting Extension to build number #{build_number}"
sh("/usr/libexec/PlistBuddy -c 'Set CFBundleVersion #{build_number}' ../myWatchApp/Info.plist")
sh("/usr/libexec/PlistBuddy -c 'Set CFBundleVersion #{build_number}' ../myWatchAppExtension/Info.plist")
end

For my workflow I’ve incorporated the setBuildNumberOnExtension helper function into my build method that is called as part of my deployment, testing, or CI process.

desc "Create ipa"
lane :build do
increment_build_number
setBuildNumberOnExtension(
get_build_number
)
gym(scheme: "TestApp", workspace: "TestApp.xcworkspace", output_directory: "../../../Builds/TestApp", clean:true, silent: true, export_method: "enterprise")
end

The same approach should work for all extensions. Even if you only have a “Today Extension” I’d recommend keeping the version numbers in sync with your core app. This will make life easier when you are debugging.

Check of the fastlane examples for more ideas on how to create extensions to improve your workflow.