Our project are using different build configurations to separate server environments
(e.g: Production, Staging)
#if STAGE
public struct endpoint {
public static let api = URL(string: "https://stage.???.com/api")!
}
#else
public struct endpoint {
public static let api = URL(string: "https://prod.???.com/api")!
}
#endif
}
Chances are sometimes we need to switch between different configs to debug repeatedly,
however, Xcode will rebuild almost all files when switching configurations, even if we didn't change files.
Is it possible to optimize Xcode to prevent building almost every files when we change configurations, to let it build just like an incremental build?
Below is part of our configs in build settings
Environment:
Xcode 11
Swift & Objective-C mixed project
Cocoapods to install 3rd party libraries
Perhaps there is a different strategy you can use to avoid rebuilding.
I have a game which I wrote which is a server based game. I have a version of the server that runs on MacOS for easier debugging and is built with Xcode, and the game is an iOS game. Both client and server support building different environments, however I do not control them using the Build Configuration per se. Instead I use targets as the main way of controlling access to environments.
I use the following approaches:
Command line arguments to "control" environment
Using Config files to "control" environment
Using Build Target to "control" environment
All 3 are used in the case of the server (since it is a command line executable), and the last two are used in the case of the game.
The trick here is that my code allows for the dynamic switching of environments (and this is really the key element to this approach).
I use the Build Target to control the extent of switching allowed. For example, my Dev target allows switching between Dev, QA, Stage, and Prod. My QA target allows switching between QA, Stage, and Prod, and my AppStore target only supports Prod.
Of course, backing the targets are build configurations. For build configurations I use Debug, DevDebug, Release, DevRelease, and AppStore. So plain Debug configuration and Release configuration are used by the "QA Target". And DevDebug configuration and DevRelease configuration are used with the "Dev target". I use 3 targets in all.
When the app starts, if it can take a command line and an env is defined, it uses it (if it can). The next fallback is a config file (You can easily edit this if you are on the simulator or running a MacOS app). Then what's baked in.
This also implies I have an environment chooser. I have found that also allowing a chooser to be selected from network error popups is also handy (in case you are in the wrong env). Choosers are only available on non-AppStore builds.
Related
I set up multiple environments for my app by creating multiple configurations, with multiple schemes for each configuration (Article Describes in more detail my set up). Therefore I only have to have one target. This works fine for the actual app itself but I'm having trouble doing the same setup within an app widget extension. The production scheme works fine but when trying to build the dev side scheme I'm receiving this error.
/Users/ss/Library/Developer/Xcode/DerivedData/appName-hdbxzenesdshvnfgnywdjhuifcbu/Build/Products/Debug Development-iphoneos/AppNameWidgetExtension.appex error build: : No such file or directory
When I look through the derived data I can see the dev versions .appex. AppNameWidgetExtension-dev.appex but it seems to be looking for the none dev versions .appex. I would like to be able to continue to use one app target and one extension target but not sure how to get the dev widget extension to refer to the correct .appex file.
I noticed that embedded foundation extensions within the app target only referes to the dubug Production .appex. How could I change this .appex based on current scheme? Screen shot below
Scheme setup (The same setup as article I've attached)
Ive seen similar problems here but most are referring to having multiple targets and id much rather keep my current environment only using one target for multiple configurations.
Update:
So I think my issue was renaming the product name in packaging (within the build settings of app target) which would change the product module name too. Instead, I put it back to $(TARGET_NAME). I was changing the product module name to change the app display name on phone so instead I added Bundle Display name key and set the value to $(APP_NAME) (app name is from config files) to app targets plist. I also moved all my configuration settings into config files and added config files to project configurations. I used this tutorial to help me set up my project with xcconfig files.
I have 3 targets in my iOS application namely Stage, Preprod and Prod. I added NotificationServiceExtension for the Stage target for the development purpose. Since the added NotificationServiceExtension is not available in other targets i.e pre-prod and prod, hence am not able to use the NotificationServiceExtension. Do i have to added 2 more NotificationServiceExtension for Preprod and prod targets? Or there is some different solution?
Thank you.
I'd recommend you to avoid using different targets for different environments. The better way is to use different build configurations, because:
.xcodeproj file will be much smaller
You will need single app extension
You will not need to add each new file to every target
You will not need to recompile the whole app for different environment
So in your case I'd create the following configs:
StageDebug
StageRelease
PreprodDebug
PreprodRelease
ProdDebug
ProdRelease
Here is a nice article about creating build configurations, have a look.
Till now, I know there a couple of solutions for multiple environment settings.
Solution 1: Prepare different plist files for the different environment. Then, use Add Run Script Build Phase to copy the specific plist file to overwrites project plist file.
Solution 2: In PROJECT -> Info -> Configurations, duplicate a existing configuration. setup scheme -> Info -> Build Configuration. Then, in the targets -> Build settings, add user-defined settings.
$(kBaseURL)
Solution 3: Add preprocessor macros in PROJECT -> Build Settings to define the environment. Then, in source code write the macro to switch environment setting value.
#ifdef DEBUG_ENV
#define kBaseURL #"http://debug-server.a.com/api/"
#else
#define kBaseURL #"http://production-server.a.com/api/"
#endif
There are several other ways to switch environments settings.
Which way is the best practice to do this?
I think this is also a question of taste and of what you are actually trying to achieve.
For example you might want to have a debug configuration that does not talk to the internet and rather uses HTTP stubs to mock the requests. This won't be possible by simply copying plists.
Personally I prefer creating build configurations for each environment, then set a compiler flag for each config that switches precompiler macros. I also like to take some more tricky steps to modify the bundle id and app name for all configs and then create a build scheme for every config. This results in me being able to build all environments from Xcode and install all apps on the same device, as the bundle ids are different. It's a bit tricky to set up though. However once it runs: perfect for my use cases :)
The two projects I'm working on happen to both make use of XCode at some point during building.
One project is based on Reactive Native, in which case I'd need to set the Build Location to Custom with
Products: build/Build/Products
Intermediates: build/Build/Intermediates
in order for the command react-native run-ios to run properly (or else I'd run into CFBundleIdentifier", Does Not Exist error).
Another project is based on Unity and make use of a custom framework injection, in which case I'd need to set the Build Location to Legacy as there is a custom script that uses the value in the target settings.
To my astonishment I discovered that every time I work on the other project I'd need to reset the Build Location value to the right one for the project or else I'll run into errors/problems.
Is there a way to define a project-specific configuration so that I can have different values for the Build Location for different XCode projects?
One solution I can think of is to stick to Legacy: for the project based on react-native simply define the build location as build/Build/Products in the target settings. But the drawback is that react native may just override the value to the default one at each build and rendering this approach not practical (I have yet tested it out though).
So I'm wondering if there is a "standard" way of resolving this annoying problem.
I have several apps that using the same core framework, Since each app have some small differences i'm searching for the best way to build the framework with relevant code and parameters.
The best solution (that is not so good) that i found is:
For different code compilation - using targets that include different classes
For different configuration using different Swift flags for each configuration (e.g. debug, release...)
The problems are:
I'm using several targets and each target duplicate the project configuration and i need to maintain all. this approach can lead to some bugs if i change configuration in one target but forget to change it on other target.
For debug/release & stagingA/stagingB/production I'm creating specific configurations instead combining them (This solution is problematic since for each staging i need to duplicate it for release and debug):
release production
debug production
release stagingA
debug stagingA
release stagingB
debug stagingB
Apps struct is:
App A include CoreFramework with code adjustments for A
App B include CoreFramework with code adjustments for B
Each app have debug, release, production... configuration. This configurations affect the framework (these configuration run also on the framework)
==> I'm looking for other/better way to configure my apps project's
Don't make "code adjustments" in the framework based on which client is calling it.
Construct your framework as if it were something provided as a binary release by an external supplier. Any behaviour that may be variable can then only be controlled by run-time configuration through a public API.