How to pass Xcode environment variable from Target to its dependencies - xcode11

My project has 2 App Targets (App-A and App-B, 2 different iOS apps) and a few different dependency targets (static libraries) which are being used by both app targets.
One of the dependencies is our inhouse static framework that communicates with our backend. This framework has a run script that is run (before any code is compiled) to generate swift code. This script takes an input file that is different for each app target.
I want to set an environment variable in App Target which I can use in the static framework's run script to determine which input file to use.
I have tried setting the Project-> Build Settings-> User-defined variable and setting appropriate values in App Target, but the static framework seems to get the values from Project Settings and not from Target Settings.
Is there any way where on Xcode I can pass a value from Target app to dependent static library and use it during the build?
I am using Xcode 11.3.1
Any help is appreciated, Thank you.

Related

Can an App Extension be conditionally included in a build?

I'm starting work on 2 app extensions, and I'd like to set up my builds as follows:
Builds intended for the App Store should not include either extension
Builds intended for TestFlight should include the extension that is almost ready
Local builds should include both extensions
Currently, I have a different build configuration for each of these to show and hide other aspects of the app (and compiler flags in some cases too). I use fastlane to make my builds.
As far as I can tell, the only ways to add or remove an app extension would be:
Duplicate the main target twice and use one for each of the above builds (drawback - must maintain 3 copies of the target until the extensions are fully complete)
Manually add/remove the extension before building (drawback - impossible to make both TestFlight and App Store builds from a given branch in a CI environment because manual intervention is required)
Can anyone think of a better solution for conditionally including the app extensions?

Set Deployment Info and Build Phases by script in iOS project

In order to automize the build process when using a cross platform framework (FUSE). Unless I am wrong, I coudn't find any settings in FUSE to cover this cases so that's why I am trying to set them after FUSE builds and generates the xcode project files.
One of the issues I am facing right now is about the target device and the other one is adding an entry in the build phases.
STEP BY STEP
FUSE build generates the xcode project files including a new xcodeproj file everytime with a different FUSE-XCODE classes mapping so I cannot reuse it from one build to another
I can open the project in xcode IDE
Do some settings manually (target device and add a bundle resource)
Generate a build
Publish it to TestFlight
However, I am trying to make a command line build with the following steps:
FUSE build generates the xcode project files including a new xcodeproj file everytime with a different FUSE-XCODE classes mapping so I cannot reuse it from one build to another
Set the target device and the bundle resorce path by script
Build by xcode command line tools and generate the IPA file
Publish it to TestFlight
I already achieved to writhe the app name, version and build number by PlistBuddy using an script. However I didn't manage to do the same for the target device and adding a bundle resource.
About the device, I wanna set iPhone only:
About the Build Phases, I wanna add a Bundle Resource with the license of one library we use in the project. The license file is in a path belonging to the project.

Can I link an XCUITest target to a different Project's app target OR use XCTest/XCUITest outside of a UI Test Bundle target?

I am currently building out a test suite in an Xcode workspace that contains 3 existing projects. 2 App projects and a common framework project. Both apps are very similar in regards to UI with some minor differences.
I am trying to find a way to share the XCUITest framework I made for App A with App B.
Currently, App A has a test target set inside of its AppA.xcodeproj. All of App A's tests are doing great and easy to maintain. I need to extend this to App B and refactor App A's framework to only the App A exclusive bits so App A and App B can share the common XCUITest framework code base from a central base.
I have tried to create a new project, move over the UI test code and point the new UI Test target at App A's executable, but I could not figure out how to get the option to show.
I set the App A app as a Target Dependency in the New Project's test target > Build Phases tab, but no luck. I was able to set App A's target in the new test target's scheme but still landed on an error.
Assertion Failure: CommonBasePage.swift:21: failed: caught "NSInternalInconsistencyException",
"No target application path specified via test configuration: <XCTestConfiguration: 0x60400014e910>
testBundleURL:file:///Users/user.name/Library/Developer/Xcode/DerivedData/.../Debug-iphonesimulator/NewTestTarget-Runner.app/PlugIns/NewTestTarget.xctest/
testBundleRelativePath:(null)
productModuleName:NewTestTarget
testsToSkip:(null)
testsToRun:NewBasePage/testExample
reportResultsToIDE:YES
sessionIdentifier:<>
pathToXcodeReportingSocket:(null)
disablePerformanceMetrics:no
treatMissingBaselinesAsFailures:no
baselineFileURL:(null)
baselineFileRelativePath:(null)
targetApplicationPath:(null)
targetApplicationBundleID:(null)
testApplicationDependencies:
{...
I'm assuming targetApplicationPath:(null), targetApplicationBundleID:(null) should have legit target values but I'm unsure where else to look in order to set them.
I gave up on that and tried to create a new project & target as a Cocoa Touch Framework to shift the common code to a central point but was not able to import XCTest, which my framework depends on for XCUIApplication and XCUIElement fields.
import XCTest //Causes "Cannot load underlying module for 'XCTest'"I tried adding XCTest.framework to the targets "Link Binary with Libraries" section under build phases but no luck.
Is there a way to work with the XCTest framework outside of a test target?
OR
Is there a way to point one project's test target at another project's app target?
You can link XCTest and any other framework to anything. Make sure you have $(PLATFORM_DIR)/Developer/Library/Frameworks in both LD_RUNPATH_SEARCH_PATHS and FRAMEWORK_SEARCH_PATHS.
Making framework for shared code is a good idea.

In an Xcode 4 workspace, how do I cascade build settings & configs to subprojects

Overview
I'm using static libraries and Xcode 4 workspaces to effect modularity in iOS development, an increasingly common technique. For example, I might have a workspace which contains an App project, and a Library project, like so1:
You would then have a scheme to build these that looked something like this:
What I would like to do is have the "App build" control the "Library build" it initiates, in at least a couple of ways:
Map App configurations (e.g. Debug, AdHoc) to arbitrary Library configurations
Passing through some subset of -D defines, and/or specifying these for the library build.
I'll deal with each of these in their own section, but it's worth making a few clarifications.
Clarifications
I'm using App/Library here as an easy proxy for any Superproject/Subproject relationship you may have.
From what I've seen, Xcode 3 style embedded subprojects don't seem to work any differently in Xcode 4 than workspace "peers". I'd love to be wrong about this.
I know I could do almost anything with a "Run Build Script" build phase, and xcodebuild. But I'm trying to work within the system here, where the dependencies are specified in the scheme, and otherwise somewhat loosely coupled.
The Library exists to be used in more than just this project, and so you cannot arbitrarily load it up with junk specific to this App's build, or reference anything particular to the App or Workspace. For the general case, this rules out including static .xcconfig from the App project as a way to convey build information from the App to the Library.
Building the Library outside the workspace sacrifices too much, not an option.
Configuration Mapping
As I understand it, building a particular App configuration will:
If a configuration exists in the Library of the same name, it will build the Library using that.
Otherwise, it will build the active configuration of the Library, as specified in the Library's project file.
To my knowledge, without resorting to the aforementioned run-build-script hack, that is the extent of the control one has over subproject build configurations. Please tell me different.
Ideally, I would be able to specify (in the scheme, presumably):
AppConfigA -> LibConfig1
AppConfigB -> LibConfig2
While Debug, AdHoc, & Release may be the only configurations some ever use, complex projects often outgrow that.
Defines
I've not yet found way to pass -D defines from the App build to the Library, without resorting to xcodebuild, which can take, e.g., an .xcconfig file.
The App's build settings can be accessed in Library build run-build-script phase. However, doing that introduces a dependency in the Library on the App project, which for good reason is verboten (cf. Clarifications). But even then, I haven't found a way to use those settings to directly control the Library's build (much2).
So crazy it just might...
One scheme I came up with while writing this would be:
The Library bases it's build configurations on an empty (dummy) LibraryExternals.xcconfig file within it's own project.
A clean of Library deletes that file. A standalone build of the Library will create an empty one if it does not already exist.
That file is overwritten by an App Build run-build-script phase, and contains anything the app wants to communicate to the Library build.
Seems kind of complicated, but I'm looking for anything right now. I'll push this to an answer if nothing better comes along.
1 Apps shown are Max OS X. I find command line apps make for simpler tests. Same applies.
2 Cf. Info.plist preprocessing, which I learned about during this investigation.
If you modify your project structure to use a single project with multiple targets then each target's build settings will automatically inherit from the project. From there, you can modify ones that you want to be different, or select an individual setting and press the delete key to set it to the default specified by the project.

Xcode: project settings vs. target settings

I'm creating a static lib on Mac OS X for one of our customers, as well as a small cmd line app to test the static lib. The cmd line project has 2 extra library search paths, which meant I was linking to the Debug version in Release mode and just about went crazy, so I tried to get rid of these two paths, but I couldn't find where they were specified. I was looking in the project info, but it turns out they were specified in the target info.
I don't understand the distinction?! Why there are 2 sets of settings, which are essentially the same?! Can someone please enlighten me?
A project can contain multiple targets. For example, an app I write has four - the app itself, a Quick Look plugin, a framework and a bundle that contains Mac OS 10.6-specific functionality that can be dynamically loaded in.
Project settings apply to every single target in the project. Each target can then override individual settings if they need to - for instance, my project's Target SDK is set to 10.5, but the 10.6-specific bundle has it's Target SDK set to 10.6.
In some instances, some settings don't make sense to be in Project Settings - one of these, I guess, is search paths.
You often have multiple targets in a single project - for instance, you might have a framework project with a target for building as a dynamic .framework bundle, and a target for building a static lib. Or your app might have a target for building the app itself, and a target for building some helper command-line tool that it needs to install.
Wherever possible, I'd suggest changing settings at the highest level (in the project settings, and simultaneously changing debug & release configurations), and only customizing the target settings when necessary. Even better, move as many settings as possible into xcconfig files, which seem a much more explicit way of specifying your build setup.
Preface: you ship targets. Your end products are targets. Not projects. Think of a project as the umbrella above multiple targets.
For a more realistic example assume both Uber and Lyft were being developed by the your (umbrella) company.
The company has the following three environments:
Debug
QA
Release
The Debug and Release configs come out of the box with every new project you create. You can create as many additional configs as you want
This would require 3 configurations. To add a QA configuration follow the tutorial here
Did I apply this to the target or project?
I applied it to the project.
Ok so configs are only for projects and not for targets. Right?
Incorrect! It's confusing I know. You have to think of the project as a big container where you create your configs in there.
Then for each target (not project), for following tabs:
General, Resource Tags, Build rules, Info:
There is no difference between different configs
Signing and Capabilities tab:
You can switch between teams and sign it with a different team. This is useful if you want to sign your beta builds with your enterprise certificate but sign your Appstore build with app store certificate.
Build Settings tab:
For almost every variable in this section you can give a different value based on the config. Common Build Settings to customize are:
Architectures - 'Build Active Architecture only'
Build Options 'Debug Information Format'
Packaging
Set the plist you want per configuration.
Change the bundle identifier (Packaging >> Product Bundle Identifier). If you switch values for a field then in the plist you'll see as:
Signing
Code Signing identity
Code Signing Style (Manual or Automatic)
Development team
Provisioning Profile
Apple Clang - Optimization Level
If the values are different then the row's value would be
<Multiple values>
and you basically have to expand that value to see what value is given for debug and what value is given for Release or QA config.
If all the values are the same then you'll just see the value that is given to all of them. By default the values are the same.
Build Phases:
There's no out GUI way of switching based on configuration. However you can still do run certain commands based on the configuration. Example:
if [ "${CONFIGURATION}" = "Debug" ]; then
"${PODS_ROOT}/SwiftLint/swiftlint" autocorrect
"${PODS_ROOT}/SwiftLint/swiftlint"
fi
Summary
Long story short, this allows you to have 2 different apps (targets) with the same code (project), in 3 different environments (dev, QA, release). You create the different environments using configurations.
To learn more on this I highly recommend you to read more about this in depth and understand what configuration files (xcconfig) is. It's much more simpler than you think. It's mainly a key value pair:
AppCoda - Using Xcode Configuration (.xcconfig) to Manage Different Build Settings
NSHipster - Xcode Build Configuration Files

Resources