Blog

Run your iOS app without overwriting the App Store version

30 Jan, 2015

Sometimes when you’re developing a new version of your iOS app, you’d like to run it on your iPhone or iPad and still be able to run the current version that is released on the App Store. Normally when you run your app from Xcode on your device, it will overwrite any existing version. If you then want to switch back to the version from the App Store, you’ll have to delete the development version and download it again from the App Store.
In this blog post I’ll describe how you can run a test version of your app next to your production version. This method also works when you have embedded extensions (like the Today Widget or WatchKit app) in your app or when you want to beta test your app with Apple’s TestFlight.

There are two different ways to accomplish this. The first method is to create a new target within your project that runs the same app but with a different name and identifier. With iOS 8 it is now possible to embed extensions in your app. Since these extensions are embedded in the app target, this approach doesn’t work when you have extensions and therefore I’ll only describe the second method which is based on User-Defined Settings in the build configuration.
Before going into detail, here is a quick explanation of how this works. Xcode already creates two build configurations for us: Release and Debug. By adding some User-Defined Settings to the build configurations we can run the Debug configuration with a different Bundle identifier than the Release configuration. This essentially creates a separate app on your device, keeping the one from the App Store (which used the Release configuration) intact.
To beta distribution of the app built with debug configuration easier we’ll create multiple schemes.

The basics

Follow these steps exactly to run a test version on your device next to your App Store version. These steps are based on Xcode 6.1.1 with an Apple Developer Account with admin privileges.
Click on your project in the Project Navigator and make sure the main target is selected. Under both the General and Info tabs you will find the Bundle identifier. If you change this name and run your app, it will create a new app next to the old one. Add -test to your current Bundle identifier so you get something like com.example.myapp-test. Xcode will probably tell you that you don’t have a provisioning profile. Let Xcode fix this issue for you and it will create a new Development profile for you named something like iOSTeam Provisioning Profile: com.example.myapp-test.
So we’re already able to run a test version of our app next to the App Store version. However, manually changing the Bundle identifier all the time isn’t very practical. The Bundle identifier is part of the Info.plist and it’s not derived from a Build configuration property like for example the Bundle name (which uses ${PRODUCT_NAME} from the build configuration by default). Therefore we can’t simply specify a different Bundle identifier for a different Build configuration using an existing property. But, we can use a User-Defined Setting for this.
Go to the Build Settings tab of your project and click the + button to add a User-Defined Setting. Name the new setting BUNDLE_ID and give it the value of the test Bundle identifier you created earlier (com.example.myapp-test). Then click the small triangle in front of the setting to expand the setting. Remove the -test part from the Release configuration. You should end up with something like this:
Screen Shot 2015-01-30 at 11.29.43
Now go to the Info tab and change the value of the Bundle identifier to ${BUNDLE_ID}. In the General tab you will still see the correct Bundle Identifier, but if you click on it you see the text is slightly grayed out, which means it’s taken from a Build configuration setting.
Screen Shot 2015-01-30 at 11.35.52
To test if this works, you can change edit the current Scheme and change the Build Configuration of the Run action from Debug to Release. Close the Scheme window and go to the General tab again to see that the Bundle Identifier has changed to the Release BUNDLE_ID. (If you still had the General tab open and don’t see the change then switch to another tab and back; the panel will reload the identifier). Make sure to change the Build configuration back to Debug in your scheme afterwards.
When you now Archive an app before you release it to the App Store, it will use the correct identifier from the Release configuration and when you run the app from Xcode on your device, it will use the identifier for testing. That way it no longer overwrites your App Store version on your device.

App name

Both our App Store app and test app still have the same name. This makes it hard to know which one is which. To solve this, find the Product Name in the Build Settings and change the name for the Debug configuration to something else, like MyApp Test. You can even use another app icon for your test build. Just change the Asset Catalog App Icon Set Name for the Debug configuration.

Beta distribution

What if you want to distribute a version of the app for Beta testing (through TestFlight or something similar) to other people that also shouldn’t overwrite their Apple Store version? Our Archive action is using the Release build configuration. We could change that manually to Debug to have the test Bundle identifier but then we would be getting all of the Debug settings in our app, which is not something we want. We need to create another Build configuration.
Go to the project settings of your project (so not the target settings). Click the + button under Configurations and duplicate the Release configuration. Call the new configuration AdHoc. (You might already have such a Build configuration for other reasons, in that case you can skip this step and use that one for the next steps.)
Now go to the Build Settings of your main target and change the AdHoc value of the User-Defined Setting BUNDLE_ID to the same as the Debug value. Do the same for the Product name is you changed that in the previous step.
We could already make a Beta test Archive now by manually changing the configuration of the Archive action to Debug. But it makes it easier if we create a new scheme to do this. So go to the Manage Schemes and click to + button at the bottom left to create a new scheme. Make sure that your main target is selected as Target and add ” Test” to the name so you end up with something like “MyApp Test”. Also check the Shared checkbox if you are sharing your schemes on your version control system.
Double click the new scheme to edit it and change the build configuration of the Archive action to AdHoc. Now you can Archive with the MyApp Test scheme selected to create a test version of your app that you can distribute to your testers without it overwriting their App Store version.
To avoid confusion about which build configuration is used by which scheme action, you should also change the configuration of the Profile action to AdHoc. And in your normal non-test scheme, you can change the build configuration of all actions to Release. This allows you to run the app in both modes of your device, which sometimes might be necessary, for example when you need to test push notifications that only work for the normal Bundle identifier.

Extensions

As mentioned in the intro, the main reason to use multiple schemes with different build configurations and User-Defined settings as opposed to creating multiple targets with different Bundle identifiers is because of Extensions, like the Today extension or a WatchKit extension. An extension can only be part of a single target.
Extensions make things even more complex since their Bundle identifier needs to be prefixed with the parent app’s bundle identifier. And since we just made that one dynamic, we need to make the Bundle identifier of our extensions dynamic as well.
If you don’t already have an existing extension, create a new one now. I’ve tested the approach described below with Today extensions and WatchKit extensions but it should work with any other extension as well.
The steps for getting a dynamic Bundle identifier for the extensions is very similar as the once for the main target so I won’t go into too much detail here.
First open the Info tab of the new target that was created for the extension, e.g. MyAppToday. You’ll see here that the Bundle display name is not derived from the PRODUCT_NAME. This is probably because the product name (which is derived from the target name) is something not very user friendly like MyAppToday and it is assumed that you will change it. In case of the Today extension, running a test version of the app next to the App Store version will also create 2 Today extensions in the Today view of the phone. To be able to differentiate between the two we’ll also make the Bundle display name dynamic.
Change the value of it to ${BUNDLE_DISPLAY_NAME} and then add a User-Defined Setting for BUNDLE_DISPLAY_NAME with different names for Debug/AdHoc and Release.
You might have noticed that the Bundle identifier of the extension is already party dynamic, something like com.example.myapp.$(PRODUCT_NAME:rfc1034identifier). Change this to ${PARENT_BUNDLE_ID}.$(PRODUCT_NAME:rfc1034identifier) and add a User-Defined Setting for PARENT_BUNDLE_ID to your Build Settings. The values of the PARENT_BUNDLE_ID should be exactly the same as the ones you used in your main target, e.g. com.example.myapp for Release and com.example.myapp-test for Debug and AdHoc.
That’s it, you can now Run and Archive your app with extensions who’s Bundle identifier are prefixed with the parent’s Bundle identifier.

App Groups entitlements

You might have an extension that shares UserDefaults data or Core Data stores with the main app. In that case you need to have matching App Groups entitlements in both your main app and extensions. Since we have dynamic Bundle identifiers that use different provisioning profiles, we also have to make our App Groups dynamic.
If you don’t have App Groups entitlements (or other entitlements) yet, go to the Capabilities tab of your main target and switch on App Groups. Add an app group in the form off group.[bundle identifier], e.g. group.com.example.myapp. This will generate an entitlements file for your project (MyApp.entitlements) and set the Code Signing Entitlements of your Build Settings to something like MyApp/MyApp.entitlements. Locate the entitlements file in Finder and duplicate it. Change the name of the copy by replacing ” Copy” with “Test” (MyAppTest.entitlements). Drag the copy into your project. You should now have two entitlement files in your project. Open the Test entitlements file in Xcode’s Property List editor and add “-test” to the value of Item 0 under com.apple.security.application-groups to match it with the Bundle identifier we used for testing, e.g. com.example.myapp-test. Now go back to the Build Settings and change the Debug and AdHoc values of Code Signing Entitlements to match with the file name of the Test entitlements.
Repeat all these steps for the Extension target. Xcode will also generate the entitlements file in the extension folder. You should end up with two entitlements files for your main target and two entitlements files for every extension.
The Application Groups for testing need to be added to the provisioning profiles which Xcode will handle automatically for you. It might warn/ask you for this while building.

Conclusion

It might be quite some work to follow all these steps and to get everything to work, but if you use your normal iPhone for development and still want to use or show a stable version of your app at the same time, it’s definitely worth doing. And your Beta testers will thank your for it as well.

guest
7 Comments
Oldest
Newest Most Voted
Inline Feedbacks
View all comments
Max_B
Max_B
7 years ago

Brilliant.
Even if I do not need this setup, this is a useful repository of informational tricks.

Pawel
Pawel
6 years ago

Hi. Is ig going to work with Apple’s TestFlight? As far as I know, I can’t change bundleID of my app, because iTunesConnect identifies uploaded binary by bundleID and it attaches the app to appropriate container. If I would change apps bundleID, iTunesConnect won’t know to which container attach uploaded app. Am I correct? Is there any walk around for this? Or I have to create separate container in iTunesConnect, just for beta testing? Thanks in advance!

Aleksey
Aleksey
6 years ago

I followed your steps and after all (while submitting to App Store for using with TestFlight) got this error: «iTunes Store operation failed. No suitable application records were found. Verify your bundle identifier ‘…….-test’ is correct».
Does it mean i have to create another AppID? I hope no. I hope i’m doing something wrong.

Victor
Victor
6 years ago

If you do this, you cannot test push notifications, can you? because push notifications certificates are bound to the App ID (and hence the Bundle Identifier)

Antonio
Antonio
6 years ago

Hello, this was a great article, the part about the extensions wasn’t very clear for me, do i need to create a group for each context? on my test my entitlement have the same group for all the contexts i created and i don’t get any errors? should i make an entitlement file for each and a group for each context? (development, staging, production)?

Explore related posts