How to use Xcode's targets to manage the build of development and production builds
Imagine that you have completed the development and testing of the application and are now ready to submit the official version. The problem is that some Web service URLs point to the test server, and the API key is configured for the test environment. Before submitting the app to Apple for review, you need to modify all of these API keys and URLs to accommodate the production version. That sounds fine, doesn't it? But is there a better way to handle the build of development and production versions than to modify the relevant values back and forth between the development and production environments? This is the next Eugene to discuss with you.
Go to Eugene Tutorial
For starters, some people may wonder why two separate databases and environments are needed in the app development process. The reason is that when you continue to build new features or continue to develop your app, you want to partition existing public and production versions.
The standard software development practice is to develop different versions of the software in different development environments, as described in our case for developing iphone applications. The development version of an application typically uses a database that is different from the production environment (or other systems such as analysis). That's why we should use separate servers and databases for different environments. Developers typically use virtual images or virtual data during testing. In a test environment, it is not uncommon to use test data such as "Test Comment", "Argharghargh" and "one more Test comment". Obviously, you don't want your real users to see such messages. If your application uses an analysis system, you can even send thousands of events during the testing phase. Similarly, you will not put the test data and production data in the same database. This is why it is always recommended to differentiate between development and production environments.
When using two separate environments, your application needs to have a way to find out which environment it should connect to. A common approach is to define a global variable in your main application proxy, which initializes your application to a development or production pattern.
Enum Environmenttype {
Case development, Production
}
Let Environment:environmenttype =. Production
Switch Environment {
case. Development:
Set Web service URL to development
Set API keys to development
Print ("It s for Development")
case. Production:
Set Web service URL to production
Set API keys to production
Print ("It s for production")
}
This method requires you to change the global variables each time you switch the environment. Although this method may be fast and convenient, it has some important limitations. First, because we use a bundle ID in both the development and production environments, you can't install two versions of the app on a single device. This is inconvenient when you need to test a development version of an application and still use the production version of the app on that device. In addition, this approach is likely to upload the app's development version to the store. If you forget to change this global variable, you will upload the wrong app to your users. I remember one time I forgot to change the global variables before submitting the app to the App Store, and it was scary for the user to download the development version of the app.
In this article, I'll show you a better way to differentiate between development and production builds. Specifically, we will create a development target in Xcode. This method is suitable for new and existing large projects, so you can use an existing application against this tutorial.
By applying this approach, the development and production versions of the app will use the same underlying code, but can have different icons, bundle IDs, and points to different databases. The publish and submit process will be very simple. Most importantly, your testers and managers can install two versions of the application on the same device, so they know exactly which version they are experiencing.
How to create a new target
So how do you create a development target in Xcode? I use the sample project "Todo" to guide you through the process step-by-step. You can also use your own project and follow the steps:
1. Enter project settings in the project's navigation panel. Under the targets area, right-click an existing target and select ' Duplicate ' to replicate the existing target.
2.Xcode will ask if your new target is being developed for the ipad. For this tutorial, we just select "Duplicate only".
Tip: If your project supports generic devices, Xcode will not prompt you for the above message.
3. Now we have a new target and build scheme called ' Todo copy '. Rename and make it easier to understand.
Select the new target in the targets list. Press the ENTER key to edit the text and add a more appropriate name. I'm more inclined to "Todo Dev". You are free to choose any name you like.
Next, find "Manage schemes ...", select the shceme you created in step 1, and press "enter" to make the name of scheme the same as the name of the new target (this is the name you chose for the new target)
4. Step 4 is optional, but highly recommended. If you want to simply differentiate between development and production builds, you should use a separate icon and launch page for each version. This will give testers a clearer idea of which app is being used and prevents the development version from being uploaded.
Jump to ' assets.xcassets ' to add a new icon. Right-click icons > Apps Icons & Launch Images > New iOS app icon. Rename the new icon to "Appicon-dev" to add your own picture at the same time.
5. Now go back to project settings, select your development target, and change the bundle Identifier. You can simply append "Dev" to the original ID. If you performed step 4, make sure that you change the app app icon to set the one that you created in the previous step.
6. Xcode will automatically add a plist file (such as Todo copy-info.plist) to your target. You can find it in the root folder of the project. Rename it from "Copy" to "Dev" and place it under the original plist file. Here you will be more likely to manage the files.
7. Now open the "Build Settings" that you developed target, scroll to "packaging" and change the value to the developed Plist file (Todo dev.plist).
8. Finally, we will pre-process the macro/compiler identity for the production and development target configuration. We can then use that identity in our code to detect the version of the application that is running.
For objective-c projects, go to ' Build Settings ' under ' Apple LLVM 7.0-preprocessing '. Expand ' Preprocessor Macros ' to add a variable in the Rebug and release areas. For developing target (that is, Todo Dev), set the value to ' development = 1 '. Another, set the value to ' development=0 ' to represent the production version.
For swift projects, the compiler no longer supports preprocessing directives. As an alternative, it uses the compile-time property and build configuration. Select develop target and add an identity to represent the development version. Find ' Build Setting ' scroll down to the ' Swift compiler-custom Flags ' section. Set the value '-ddevelopment ' to indicate this target as the development version.
Now that you've created and configured the development target, what's the next step?
Using target and macro
Based on the configured macro dev_version, we can use it to dynamically compile the project in code. The following is a simple example:
Objective-c:
#if Development
#define SERVER_URL @ "http://dev.server.com/api/"
#define Api_token @ "Di2023409jf90ew"
#else
#define SERVER_URL @ "http://prod.server.com/api/"
#define Api_token @ "71a629j0f090232"
#endif
In objective-c you can use ' # if ' to check the environment of ' development ' and set the URLS/API key accordingly.
Swift:
#if Development
Let Server_url = "http://dev.server.com/api/"
Let Api_token = "Di2023409jf90ew"
#else
Let Server_url = "http://prod.server.com/api/"
Let Api_token = "71a629j0f090232"
#endif
In Swift you can still use the ' #if ' parameter to determine the build's parameters for dynamic compilation. However, in addition to using ' #define ' to define basic constants, we can also define a global constant with ' let ' in Swift.
Tip: Usually, you'll put the above code in the app delegate. But ultimately it depends on where you initialize the application settings.
Now, when you select the "Todo Dev" scheme to run the project, you create a development version that automatically sets the server's configuration to the development environment. Now you can upload the development version to TestFlight or Hockeyapp for testers and managers to test.
Then if you need to create a production version, you can simply choose "Todo" scheme. No code changes are required.
Some considerations for managing multiple target
1. When you add new files to the project, do not forget to select two target to keep your code synchronized in two versions.
2. If you are using Cocoapods, do not forget to add a new target to your podfile. You can specify multiple target using ' link_with '. You can refer to the CocoaPods documentation for further details. Your podfile looks like this:
3. If you are using a continuous integration system, such as Travis CI or Jenkins, do not forget to configure the two target build and deliver.
How to use Xcode's targets to manage the build of development and production builds