How to handle Debug/TestFlight/AppStore releases without targets? - ios

I use Cocoapods for my project and I need a specific set of frameworks for each Debug/TestFlight/AppStore release:
Debug (simulator or device): common frameworks + Calabash - Google Analytics
TestFlight: common frameworks + TestFlight SDK
App Store: common frameworks only
I could use targets, but they are annoying:
Every time you add a new file (or create one) you have to remember to add it to all targets. It makes it really easy to produce builds that don't have a required file.
Changes in build settings of one target don't propagate to other targets (i.e: changing a llvm flag).
On the other hand, cocoapods doesn't support weak frameworks, so I can't weak-link to Calabash in my main target and force-load it when building in Debug mode (because it'll still be in the framework set when building for the app store)
What would be a good balance between the very segregated option that are targets, and "all in" solution of a single target?

I have a makefile that removes certain pods then runs Pod install. We're making it much easier in the next release to do config based pods, so you will be able to do all of what your asking in version 0.34+. This is in the current HEAD on github.

Related

Integrating XCFrameworks into the development and release workflows

I have an Xcode workspace with an iOS framework target and an iOS SampleApp target that depends on and embeds the framework. The framework is closed-source and thus gets delivered in binary form to customers, in a release package alongside a copy of the sources for the entire SampleApp project, so that they can test it out with our app and see how the integration is done.
With this setup, during the development phase, I can open the workspace in Xcode and build the SampleApp target, and because it has an explicit dependency on the framework, Xcode immediately triggers a build of the framework first and then the app is built and linked correctly.
Now I want to leverage the new XCFramework format to distribute this framework to customers instead of using lipo'ed fat frameworks. I update my shell script that produces the release package to create an xcframework bundle at the end with all the various platforms supported, and also update the SampleApp project to depend on this xcframework instead of on the individual frameworks as it did before.
But here comes the problem with the updated SampleApp: After removing the dependency on the frameworks, the workspace setup described above doesn't work anymore, i.e. Xcode will no longer automatically build the framework target when the SampleApp target is built, because the explicit dependency link between the two is now broken.
My first attempt at solving this involved making the xcframework part of my regular development workflow as well. For this I added a run-script phase to the framework build target that generates a current-platform-only xcframework bundle and copies it to the place where the SampleApp expects to find it, and then added the framework target to the targets of the SampleApp build scheme, in order for the framework to be built when the SampleApp is built. Unfortunately this approach does not work because Xcode 12 always seems to build both targets in parallel, no matter what the 'Parallelize Build' setting is set to, and because of that the app will fail to build simply because the xcframework is not yet ready when it's processed by the build system, which is very early in the build phases of the SampleApp. So unless there's a way to force Xcode to honor the order in which the build targets are defined in the scheme, this approach is a no-go.
If this approach cannot work then I'd have to ship the xcframework with the SampleApp unmodified, and instruct customers wanting to run the app to manually remove the references to the universal frameworks and then add/drag the xcframework to it. This would work but it's ugly and does not seem user friendly enough to me.
I'm also considering a different approach whereby the release script programmatically modifies the SampleApp.xcodeproj to depend on the xcframework instead of on the universal frameworks, but that seems like it could be a very fragile thing to do, so would rather avoid it if there's a better way.
This scenario does not seem unique to me so I'd guess someone else must have faced this problem too. If so, how did you solve it?
So I ended up solving this by programmatically modifying the SampleApp's project file at release time to link and embed the xcframework bundle instead of the universal frameworks.
Initially tried to use the tool pbxproj but it didn't quite work, the initial fat framework removal phase was not succeeding.
Then I figured I could try and diff the project.pbxproj file after having manually replaced the frameworks with an xcframework, and turns out it takes only a few small edits to get there, so they can be done programmatically with a simple shell script like the following:
#!/bin/bash
PROJECT_FILE=SampleApp/SampleApp.xcodeproj/project.pbxproj
FRAMEWORK_NAME=MyFramework.framework
XCFRAMEWORK_NAME=MyFramework.xcframework
sed -i '' "s|FileType = wrapper.framework; path = ${FRAMEWORK_NAME}; sourceTree = BUILT_PRODUCTS_DIR;|FileType = wrapper.xcframework; path = ${XCFRAMEWORK_NAME}; sourceTree = \"<group>\";|" ${PROJECT_FILE}
sed -i '' "s|${FRAMEWORK_NAME}|${XCFRAMEWORK_NAME}|g" ${PROJECT_FILE}
There might be small differences in other project setups but mostly it boils down to that. Just beware that if the xcframework is meant to be placed somewhere other than in the project root then you need both a name and a path properties in the first sed command, e.g.:
name = "MyFramework.xcframework"; path = "relative/path/to/MyFramework.xcframework"

Swift Build Error - No such module 'Feature1'

I am getting compile error when I added a framework into my app. Framework Feature1 is built successfully but from module App import is not working.
How to solve this issue?
There are several potential misconfigurations the issue can arise for,
Please confirm that you have opened the .xcworkspace but not .xcodeproj file. Also make sure you have build Feature1 first before you build App.
Make sure that iOS Deployment Target is set same for all modules with App. For example is Apps deployment target is set to 9.0, Feature1s deployment target also need to be set to 9.0.
Make sure your main module (App) and your used framework (Feature1) have same set of configurations. i.e. If your Project has three configurations, Debug, Release, ReleasePremium than your Framework also need to have three configurations Debug, Release, ReleasePremium. Also make sure that the archive configuration is set same for both App and Feature1. i.e. if your Apps archive scheme is set to ReleasePremium, your Fearure1s archive scheme also need to be set into ReleasePremium.
Please assure that you do not need to import Feature1 in each .swift files when its already added in the Bridging-Header.h.
In case of issue came from Pod files, make sure you have uncommented #use_frameworks! into use_frameworks! from you Podfile. Sometime re installing pod works if Feature1 has any dependency on pods.
If none of the above steps works, delete your derived data folder and try re building.

Multiple platform support for Single Target in podfile

Is there any way to add multiple platform support for a single target through podfile?
For example my project is common for both iOS and Mac. They consume the same code base. So instead of creating multiple target for the same code, I added support for both iOS and MacOSX in the same target. It builds fine.
Now I want to add a dependency through Cocoapods. I create a podfile and specify my target's dependency on the pod. The pod in question here supports multiple platform in a similar way i.e. single target.
But now while building my project it fails for iOS.
Specifying multiple platforms in Podfile for single target produces an error.
And if I just specify platform as only iOS or Mac then the project in question fails to build on other platform.
Has anyone experienced this before?
How can I add multiple platform for a single target through podfile?
P.S. - I know I can achieve it by creating multiple targets in my project. But I want to keep that as my last option.
def import_pods
pod 'CorePlot'
end
target 'FirstAppWithMacOS' do
# define your platform here.
platform :ios, '9.0'
import_pods
end
target 'FirstMacOSApp' do
# define your platform here.
platform :osx, '10.10'
import_pods
end
See below image of my project :
The platform device is controlled by Base SDK and Supported Platforms in Architectures section in your target's Build Settings. If you didn't create the separated two targets for both macOS and iOS devices, there should be only one choice left for you to support those platform entries, that is, duplicate new two existing build configurations for macOS platform, then you could configure the settings separately.
For example, you have an existing iOS-based project and there are two default build configurations named Debug and Release. Now try to two new one for macOS, choose the main Xcode project -> choose project name (not target name here) -> select Info section in top -> tap + button under the Configurations section -> choose Duplicate "Debug"/"Release" Configuration -> rename it as Debug-macOS/Release-macOS or others, rename the original Debug/Release to Debug-iOS/Release-iOS, too.
Now you have two pairs of configurations to debug and archive the targeted platforms under the only one target's build settings. You could also create a new scheme for macOS development, just make sure that you choose the right configurations in different actions.
Talking to the main Podfile on cocoapods, if you maintain a cocoapods library by yourself and you want to add it as a dependency, it'd better support both macOS and iOS platform in your podspec file, of course, you need to make compatibility for those platforms, AFNetworking did it like that. Here is the main conversion task, do not use any API under the UIKit for macOS platform. To distinguish the platform difference in compiling time, you could use these macros for that,
#if TARGET_OS_IOS
// iOS supported
#elif TARGET_OS_OSX
// macOS supported
#endif
If you want to use a third-party library in your standalone project target, check the compatible issue first, if it only works for iOS platform, you could specify the dependent library for only one platform like this,
pod 'PonyDebugger', :configurations => ['Debug-iOS', 'Release-iOS']
Same with macOS supported.
Hope it helps!
Short answer
Is is not supported as of CocoaPods 1.10.2. There are reasons why it isn't supported and probably won't be supported any time soon or only when building with Xcode 13.
Long answer
When integrating a pod to a target, CocoaPods (CP) must perform a couple of steps to make that happen. I won't list all of them here but the following four are relevant to this answer:
Create an umbrella header.
Build a (dynamic/static) framework or static library.
Adding the umbrella header to the header search path of the target.
Liking the built framework/library to the target.
Let me go through these steps one by one:
1. Create an umbrella header
The content of the umbrella header depends on the platform the pod is built for. E.g. if it is macOS, it will import Cocoa, if it is iOS, it will import UIKit. As umbrella headers are treated in a special way, especially when using modules (and when dealing with Swift code as well, you do want to use modules, trust me), using #ifdef to make parts of them platform dependent is a rather bad idea. So to support multiple platforms, CP would need to create one umbrella header per platform.
Please note that this is exactly what CP does, if you integrate the same pod into two targets for two different platforms. E.g. if one target is macOS and one is iOS, there will be two umbrella headers, one ending in -macOS.h and one ending in -iOS.h. So in theory, this functionality is there, there is just no way to specify more than one platform per target so far and thus the created umbrella header will only work with one target platform. That is what Vikas Dadheech probably meant when he said:
And if I just specify platform as only iOS or Mac
then the project in question fails to build on other platform.
Currently the created header will only play nice with either platform. Please note that this also holds true for any auto-generated module map file (.modulemap).
2. Build a (dynamic/static) framework or static library
Just as before, how the library/framework is built depends on the platform as a Podspec file can specify different build options per platform and even select different files to be included in a build depending on platform, the pod must be built separately per platform, even if both platforms would use the same CPU instruction set (e.g. ARM Mac and ARM iPad) and the code otherwise contains nothing that is platform specific.
And just as before, this functionality is there when integrating the same pod in into different targets for different platforms, resulting in two libraries/frameworks with a platform suffix.
3. Adding the umbrella header to the header search path of the target
Now its getting a bit more tricky. Assuming we have two umbrella headers (and maybe also two module map files), only one of them must be visible when building the target they integrate into and which one depends the target platform. This is no problem when we are talking about different targets as different build settings per target are easy but in that case it's a single target and this comes with some implications.
Yet even that is solvable, e.g. by making settings depend on SDK, after all you will require a different SDK per platform, so you can do things like this:
HEADER_SEARCH_PATHS[sdk=macosx*] = ....
HEADER_SEARCH_PATHS[sdk=iphoneos*] = ...
to ensure that only the headers and map files for the correct platform are visible for the build.
4. Liking the built framework/library to the target
And here comes the problem: If you want to use Xcode's linking, you can either link against the macOS or the iOS version you've built in step 2 but you cannot link to either one depending on the target platform you are building for. So how do you make sure the binary links against the correct library and only one of them?
You can ...
combine all versions into a universal binary and always link against this one. This will work as long as your target platforms have different CPU architectures (e.g. x86 for Mac and ARM for iOS) but it won't work if your platforms have equal ones (e.g. ARM Mac and ARM iPhone). With the instruction of ARM Macs, this option is pretty much dead and it was already not possible before when your two targets are iOS and tvOS since both have been ARM, too.
not use Xcode's linking at all and instead set linking flags by hand as well as library search paths using the same trick as for the headers above. Well, this will only work with static libraries and frameworks out of the box and only reliably with the new Xcode build system if you manually set target dependencies. With dynamic frameworks you also manually must copy the frameworks, sometimes (re-)sign them correctly, and also manage dependencies by hand. All possible, but seems rather fragile and may have even more implications than I just named above. I guess this is a somewhat risky approach with lots of possible pitfalls and some setups will simply not work that way at all, no matter what you do.
use the selective linking feature of Xcode 13. This is exactly what is needed here and Apple has probably implemented it as the CPU architecture no longer is suitable for many linking cases. Yet Xcode 13 is currently still in beta and if the feature relies upon that capability, it will require using Xcode 13 for building and this requires a Mac running at least Big Sur.
If You want to support multiple platforms in a single target in your Podfile, You can use the platform directive to specify the platforms you want to support. For Example
platform :ios, '9.0'
platform :watchos, '3.0'
Also you can specify a minimum version and a maximum version for each platform. For Example
platform :ios, '9.0'..'14.0'
I did same kind of implementation in my one of project which has 3 targets.
I have created 'def' shared pods and then call this shared_pods in all 3 targets. In this way, It works pretty well. I did only for iOS app.
# Uncomment the next line to define a global platform for your project
platform :ios, '9.0'
def shared_pods
pod 'Stripe'
pod 'Google/SignIn'
end
target 'App_Dev' do
# Comment the next line if you're not using Swift and don't want to use dynamic frameworks
use_frameworks!
shared_pods
end
target 'App_QA' do
# Comment the next line if you're not using Swift and don't want to use dynamic frameworks
use_frameworks!
shared_pods
end
target 'App_Release' do
# Comment the next line if you're not using Swift and don't want to use dynamic frameworks
use_frameworks!
shared_pods
end

Embedding frameworks inside closed-source Swift framework

Our company wants to distribute a closed-source SDK for iOS to our clients. I've been using Cocoapods to build the framework and built an example app making use of it. Previously the app worked fine on the simulator as well as when deployed on the device. However, I was also embedding the Pods.framework file in the app itself. One other piece of information that may be of interest is that the framework is written in Swift, the included cocoapods dependencies are both Swift and Objective-C.
I wanted to make the pods requirements easier to manage so the user doesn't need to be concerned with them and tried to embed the Pods.framework file inside of the SDK we're building - so I removed the steps to Embed Pods Frameworks and Copy Pods Resources from the example app, leaving them only in the framework, I also removed Pods.framework as a dependency of the example app, leaving it only in the SDK. This seemed to work in the simulator, but the app now crashes on mobile device with dyld: Library not loaded error.
Upon researching it, I stumbled into a few related discussions:
https://github.com/CocoaPods/CocoaPods/issues/344 https://objectpartners.com/2014/06/25/developing-private-in-house-libraries-with-cocoapods/
However, the suggested solution of using private pods does not look like it would work for us, it's my understanding that the source code in the private pod would still be open, and we can't share it with our clients.
Could someone advise on a solution that would work in this case?
OK, I finally have a more durable solution. It's a modified, cleaner version of my old one now that I understand how Xcode links in my Swift sub-frameworks better
Problem that makes distribution/compilation a bit ugly:
Since Swift standard libraries aren't bundled on the device like Obj-C, nor are they guaranteed to be stable between versions yet (stable binary interface promised in Swift 3: https://github.com/apple/swift-evolution#development-major-version--swift-30) we have to make sure the entire project is compiled against the same version of Swift. That means the guy using your closed-source framework has to be using the same version of Swift in their Xcode for their project as you did for compiling the library, even if he's not using Swift in his code because ultimately it's his version of Swift that gets bundled into the app and your SDK runs against. This is only an issue for closed-source frameworks because open-source ones will always be compiled against the same version as final project. Possible workaround is to restrict clients to same version you use or distribute multiple compilations (i.e. Swift 2.1 and Swift 2.0). To remedy this, you could provide users with copies of binaries compiled against multiple versions of Swift.
Aside from that, here is what I had to do during compilation/distribution to make a binary framework that works in Swift:
When building the framework:
In project target, make sure to add Pods.framework to Linked Frameworks and Libraries (make sure this is a pre-compiled RED version of Pods.framework, I had a black compiled Pods.framework in the same directory which built fine but then resulted in a framework that would cause the project to complain about missing armv7 architecture during linker phase in later project)
In Build Settings, under User-Defined section, add a field called BITCODE_GENERATION_MODE and set it to bitcode
DO NOT #import any frameworks in your bridging header, all instructions telling you to do that are leftover from Swift 1.0-1.2 days, you don't need it anymore and it does more harm than good (the later project will complain that it can't find these headers that aren't even exposed to it)
Change build target to Generic iOS Device, Archive and Export the framework
When building the project using the framework:
Drag and drop the framework into the project, in General tab add it to Embedded Binaries and Linked Frameworks and Libraries (you only need to add the framework itself, not the sub-frameworks or the pods file)
In Build Settings tab, add a new path to Framework Search Paths: $(PROJECT_DIR)/MyFramework.framework/Frameworks
Build the project

Xcode: What is a target and scheme in plain language?

Yeah the title says it :-) What do they mean in plain English language? I really don't understand the explanation on Apple's website and I need to rename my target and I'm afraid that nothing works after that..
I've added in Workspace and Project too!
Workspace - Contains one or more projects. These projects usually relate to one another
Project - Contains code and resources, etc. (You'll be used to these!)
Target - Each project has one or more targets.
Each target defines a list of build settings for that project
Each target also defines a list of classes, resources, custom scripts etc to include/ use when building.
Targets are usually used for different distributions of the same project.
For example, my project has two targets, a "normal" build and an "office" build that has extra testing features and may contain several background music tracks and a button to change the track (as it currently does).
You'll be used to adding classes and resources to your default target as you add them.
You can pick and choose which classes / resources are added to which target.
In my example, I have a "DebugHandler" class that is added to my office build
If you add tests, this also adds a new target.
Scheme - A scheme defines what happens when you press "Build", "Test", "Profile", etc.
Usually, each target has at least one scheme
You can autocreate schemes for your targets by going to Scheme > Manage Schemes and pressing "Autocreate Schemes Now"
A target is an end product created by running "build" in Xcode. It might be an app, or a framework, or static library, or a unit test bundle. Whatever it is, it generally corresponds to a single item in the "built products" folder.
A scheme represents a collection of targets that you work with together. It defines which targets are used when you choose various actions in Xcode (Run, Test, Profile, etc.) Often, you will have just one scheme, which uses the main app target for the Run, Archive, and Profile actions, and a unit test target for the Test action. If you were building two related apps, you might have two schemes that used the same unit test bundle but different app targets.
The main benefit of schemes (introduced in Xcode 4) is that they allow you to switch between running your app and your unit tests without needing to toggle the selected target.
I am a visual person, hence to explain the concept I will be using a diagram.
When you have multiple targets they can be one-to-one matched with Xcode's Run,Test,Profile actions, this concept defines a scheme
A target is a version of your Project,i.e targets differ slightly in classes & resources to use during built time. A project can have multiple built time setting for separate distribution requirements.
Xcode structure
Workspace
-> Project
-> Target
-> Dependency
-> Scheme
-> Action
-> Build Configuration
-> Build Configuration File(.xcconfig)
Workspace (.xcworkspace) - is a container of multiple projects. It was created as a next step of cross-project references[About]
Workspace contains all schemes from included projects
Workspace handles all implicit dependencies[About]
Observations:
It is safe to work with different projects inside the same workspace and do not catch
//if you try to open two projects on two Xcode instances
Couldn't load Project.xcodeproj because it is already opened from another project or workspace
Cocoapods[About] working with workspace where creates Pods project
Project (.xcodeproj) - It is a container for targets and scheme. It defines code files, resources...
Also Projects manages Build Configuration(changed by scheme) and Build Configuration File[About]
You can convert existing Project into Workspace
File -> Save As Workspace...
[Workspace vs Project]
Target - PBXNativeTarget section. Defines a specific set of build settings that generate:
Application target
Library and framework targets
Test
Aggregate[About]. E.g. it is used to create a Universal framework or Umbrella framework
Scheme
Contains action(run, test, profile, analyze, archive) + configuration(additional arguments, [Build Configuration], diagnostic)
Scheme can be shared which helps you in CI, Carthage[Example]... and located:
<project_path>/<project_name>.xcodeproj/xcshareddata/xcschemes
Dependency - Targets can have dependencies. Dependency is a source link against. These dependencies can be linked statically or dynamically[About] There are two types of them:
Explicit Dependency[About] - Source code of the dependency that is located in the same project or nested project
Implicit Dependency[About] - Source/closed code of the dependency that is located in the project that is a part of the same workspace.
[Vocabulary]
tldr; Targets contain instructions to build a module/framework/library or an App/end Product e.g. instructions to build a watchOS app and an iOS App. Schemes know how to respond to certain actions e.g. a build action or test action or archive action.
Make sure you See WWDC16 video — Introduction to Xcode [45:13]. If you wanted to gain deeper knowledge then watch the entirety of the video. The video is simple to follow yet very foundational. My answer is mostly off of that.
Scheme
A scheme is how you specify what you want to run and it also contains
information about how you want to run it.
For example, I could have a project with an iOS app and a Watch app,
and in that case, I would have one scheme to run my iOS app and one
scheme to run my Watch app
Run will run my app in the debugger.
Test will run my tests.
Profile will run my app in instruments so I can measure its
performance.
Analyze will run Xcode's static analyzer and help catch problems I
might otherwise have missed.
And finally, the Archive action will build my app for release and put
it in the archive that I can then use to distribute to my testers or
to the App Store or to save for future crash log de-symbolication, or
symbolication.
Project
A project is a document that you use to organize your code an
resources in Xcode.
You have references to source code files and resource files on disc,
targets which actually build those files into products like your app,
Build settings which configure how your targets build their products,
and schemes which configure how you perform actions, such as Run, with
your targets.
Now, to access your project settings, you select your project in the
navigator, you select the popover at the top of the editor, and select
your project there.
Target
You have references to source code files and resource files on disc,
targets which actually build those files into products like your app,
Build settings which configure how your targets build their products,
and schemes which configure how you perform actions, such as Run [test, build], with
your targets.
A target contains the instructions to build one thing like an app or a
framework.
The thing that your target produces is called its product. The set of
steps that your target takes to build its product are called build
phases.
And lastly, a target has Build settings to configure how it builds its
product.
Now, one thing to note is that both projects and targets have Build
settings, and targets inherit the value set at the project level but
can override them with a target-specific value.
A target's build phases do things like cause dependencies to build
first, compile the source files in that target, and link the target
against libraries and frameworks.
To summarize:
Targets
Helps put a set of files together to build/run a product/module/package
Usually it ends up just being a product you ship to app store.
But often it can be a module that you just run unit-tests against it.
Like a single app can have an iOS target along with a watchOS target. Or just a single iOS Target. Or a single iOS target along with a test target, etc.
If you go to your target's Build Phase >> Compile Sources you'll see every file that's being built for that target. Example:
To explicitly quote Apple docs:
A target specifies a product to build and contains the instructions for building the product from a set of files in a project or workspace. A target defines a single product; it organizes the inputs into the build system—the source files and instructions for processing those source files—required to build that product. Projects can contain one or more targets, each of which produces one product.
The instructions for building a product take the form of build settings and build phases, which you can examine and edit in the Xcode project editor. A target inherits the project build settings, but you can override any of the project settings by specifying different settings at the target level. There can be only one active target at a time; the Xcode scheme specifies the active target.
A target and the product it creates can be related to another target. If a target requires the output of another target in order to build, the first target is said to depend upon the second. If both targets are in the same workspace, Xcode can discover the dependency, in which case it builds the products in the required order. Such a relationship is referred to as an implicit dependency. You can also specify explicit target dependencies in your build settings, and you can specify that two targets that Xcode might expect to have an implicit dependency are actually not dependent. For example, you might build both a library and an application that links against that library in the same workspace. Xcode can discover this relationship and automatically build the library first. However, if you actually want to link against a version of the library other than the one built in the workspace, you can create an explicit dependency in your build settings, which overrides this implicit dependency.
Schemes
A given target can be put through different actions.
build
run
test
profile
archive
You can have a scheme that has all the diagnostics enabled (which makes debugging slow) vs. a scheme that doesn't have any. Or a scheme that runs certain performance related tests vs. a scheme that runs both unit-tests and performance tests. You can edit a scheme so that it performs such actions as:
Building multiple targets
Executing scripts before or after any action
Sending emails before or after any action
Running with memory management diagnostics
Producing either a debug or release build for any action.
For more on that see Customizing the build schemes for a project
To put it all together:
Once you hit run, Xcode will look at the selected scheme. It will find its associated target(s). Use the Build Phases of that target and its Build Settings (any Project Settings that isn't overridden by the target settings will get included) to build a product into the selected destination (the destination can be an iPhone simulator or a physical iPhone or watchOS, etc).
AGAIN WATCH THE WWDC VIDEO!
My take:
Target -- a lower abstraction -- various kinds of builds. Each target has its own Build Settings (so if you split into several targets, take care of that huge sheet individually for each target). Targets have a convenient way of including/excluding files, so you can configure the build effectively on a per-file basis.
Scheme -- a higher abstraction -- guides a target through various ways of deployment (Run, Test, Archive). Has modest ways of configuring the build through Environment Parameters, but employs the Build Settings from the target. Creating / editing / deleting schemes is cheaper and easier than targets.
You can have several schemes guiding one target in several different ways.

Resources