I am getting tired of messing with build settings after a couple of days now...
I have an app, let's call it MyiPhoneApp
that uses around 10 private/custom Frameworks (Cocoa Touch Dynamic Framework). MyiPhoneApp
also uses Cocoapods for some dependencies (irrelevant?). My private Frameworks are themselves using themselves (being dependent on each other).
So my setup is (simplyfied here for readability):
FRAMEWORKS (Private/Custom Cocoa Touch Dynamic):
Models
Targets: ViewModels
, ViewModelsTests
Dependent on Private Frameworks: NONE
ViewModels
Targets: ViewModels
, ViewModelsTests
Dependent on Private Frameworks: Models
Views
Targets: Views
, Views
Dependent on Private Frameworks: Models
, ViewModels
APPS
MyiPhoneApp
:
Targets: MyiPhoneApp
, MyiPhoneAppTests
, MyiPhoneAppUITests
MyiPhoneApp
dependent on Private Frameworks: Models
, ViewModels
, Views
Catch 22 running Unit Tests & upload to iTunesConnect
With ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES
for ALL targets I can run all unit tests in each private framework. But then I get rejection error from iTunes Connect:
[04:06:20]: [Transporter Error Output]: ERROR ITMS-90206: "Invalid Bundle. The bundle at 'MyiPhoneApp.app/Frameworks/Models.framework' contains disallowed file 'Frameworks'." [04:06:20]: [Transporter Error Output]: ERROR ITMS-90206: "Invalid Bundle. The bundle at 'MyiPhoneApp.app/Frameworks/ViewModels.framework' contains disallowed file 'Frameworks'." [04:06:20]: [Transporter Error Output]: ERROR ITMS-90206: "Invalid Bundle. The bundle at 'MyiPhoneApp.app/Frameworks/Views.framework' contains disallowed file 'Frameworks'."
This seems similar to: (SO Q958)[Validation Error: Invalid Bundle. The bundle at ... contains disallowed file 'Frameworks'
The answer with over 240 upvotes from Q958, suggets:
Setting ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = NO
for Frameworks and Tests Targets (and Frameworks' Test Target..?) and YES
for Apps. This is also suggested in this Github comment
When trying that (ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = NO
for frameworks and their test target) I cannot run my tests.
When I try to run ModelTests
(Remember Framwork Models
is not dependent on any other framework) the test fails/dies with:
dyld: Library not loaded: @rpath/libswiftCore.dylib Referenced from: /Users/sajjon/Library/Developer/Xcode/DerivedData/Models-ahgxztzgogwcfheblqbzabdqxhtm/Build/Products/Debug-iphonesimulator/Models.framework/Models Reason: image not found
What is the proper setup?
I have tried Xcode 9.3 and Xcode 9.4. My Frameworks and my App uses this xcconfig files:
My System
Macbook Pro 15" late 2016 macOS High Sierra v 10.13.5 (17F77)
Cocoapods 1.5.3 (bundler)
Bundler version 1.16.1
bundle exec pod --version => 1.5.3
My Podfile
source 'https://github.com/CocoaPods/Specs.git'
platform :ios, '11.2'
inhibit_all_warnings!
use_frameworks!
workspace 'MyWorkspace.xcworkspace'
### PODS (sorted by name) ###
def nimble
pod 'Nimble', '~> 7.1'
end
def rxswift
pod 'RxSwift', '~> 4.1'
end
def viewcomposer
pod 'ViewComposer', :git => 'https://github.com/Sajjon/ViewComposer.git'
end
def quick
pod 'Quick', '~> 1.3'
end
# Rest of pods removed for this StackOverFlow question for readability #
# Global Pod used by ___ALL___ targets
swiftlint
#@@ Apps (sorted by name) @@#
target 'MyiPhoneApp' do
project 'Apps/MyiPhoneApp/MyiPhoneApp'
# lots of pods removed for this SO question for readability
rxswift
tinyconstraints
viewcomposer
abstract_target 'Tests' do
nimble
quick
target "MyiPhoneAppTests"
target "MyiPhoneAppUITests"
end
end
### PRIVATE FRAMEWORKS ###
abstract_target 'Framework-Models' do
project 'Common/Models/Models'
target 'Models' do
# lots of pods removed for this SO question for readability
rxswift
swiftybeaver
end
target 'ModelsTests' do
nimble
quick
end
end
abstract_target 'Framework-ViewModels' do
project 'Common/ViewModels/ViewModels'
target 'ViewModels' do
# lots of pods removed for this SO question for readability
rxswift
viewcomposer
end
target 'ViewModelsTests' do
nimble
quick
end
end
abstract_target 'Framework-Views' do
project 'Common/Views/Views'
target 'Views' do
# lots of pods removed for this SO question for readability
rxswift
tinyconstraints
viewcomposer
end
target 'ViewsTests' do
nimble
quick
end
end
My xcconfig files setup
So as you can see in the Podfile above, my private Framework uses pods. But I want to use xcconfig files to share build settings between my frameworks (I only wrote three ViewModels
, Views
, Models
, but in fact I have around 10.).
So I have investigated which xcconfig files that Xcode creates by default for Apps, for Cocoa Touch Dynamic Frameworks and how the settings for these differ between Test target and non Test target and between Debug and Release.
From that I have created these xcconfig files
base_shared.xcconfig
includes configs: NONE
Description: Used by ALL targets (App (Main, Test, UITest), Frameworks (Main, Test)).
base_debug.xcconfig
includes configs: NONE
Description: Used by ALL debug targets (App (Main, Test, UITest), Frameworks (Main, Test)).
base_release.xcconfig
includes configs: NONE
Description: Used by ALL release targets (App (Main, Test, UITest), Frameworks (Main, Test)).
base_apps.xcconfig
includes configs: base_shared
Description: Used by apps targets (Main, Test, UITest)
base_framework.xcconfig
includes configs: base_shared
Description: Used by Frameworks (Main, Test).
base_tests.xcconfig
includes configs: base_shared
Description: Used by all Test Targets (App, Framework).
base_uitests.xcconfig
includes configs: base_shared
Description: Used by all UITest Targets (App).
Then for each and app framework I include the correct combination of xcconfigs in a Common
xcconfig. So e.g. for the Framework Models
I create 6 config files: Models-Common.xcconfig
, Models-Debug.xcconfig
, Models-Release.xcconfig
, "ModelsTests-Common.xcconfig", ModelsTests-Debug.xcconfig
, ModelsTests-Release.xcconfig
.
While that results in many xcconfig files, 6 for each framework, they are all tiny. And I can shared ALL build settings between all my 10+ Frameworks. It is a trade off.
The contents of my xcconfig files
base_shared.xcconfig
:
// BuildSettings shared between both DEBUG and RELEASE and also identical for both FRAMEWORKS (Cocoa Touch Framework) and APPS (iOS)
// Vanilla Xcode 9.3 settings
ALWAYS_SEARCH_USER_PATHS = NO
CLANG_ANALYZER_NONNULL = YES
CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE
CLANG_CXX_LANGUAGE_STANDARD = gnu++14
CLANG_CXX_LIBRARY = libc++
CLANG_ENABLE_MODULES = YES
CLANG_ENABLE_OBJC_ARC = YES
CLANG_ENABLE_OBJC_WEAK = YES
CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES
CLANG_WARN_BOOL_CONVERSION = YES
CLANG_WARN_COMMA = YES
CLANG_WARN_CONSTANT_CONVERSION = YES
CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES
CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR
CLANG_WARN_DOCUMENTATION_COMMENTS = YES
CLANG_WARN_EMPTY_BODY = YES
CLANG_WARN_ENUM_CONVERSION = YES
CLANG_WARN_INFINITE_RECURSION = YES
CLANG_WARN_INT_CONVERSION = YES
CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES
CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES
CLANG_WARN_OBJC_LITERAL_CONVERSION = YES
CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR
CLANG_WARN_RANGE_LOOP_ANALYSIS = YES
CLANG_WARN_STRICT_PROTOTYPES = YES
CLANG_WARN_SUSPICIOUS_MOVE = YES
CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE
CLANG_WARN_UNREACHABLE_CODE = YES
CLANG_WARN__DUPLICATE_METHOD_MATCH = YES
COPY_PHASE_STRIP = NO
ENABLE_STRICT_OBJC_MSGSEND = YES
GCC_C_LANGUAGE_STANDARD = gnu11
GCC_NO_COMMON_BLOCKS = YES
SDKROOT = iphoneos
GCC_WARN_64_TO_32_BIT_CONVERSION = YES
GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR
GCC_WARN_UNDECLARED_SELECTOR = YES
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE
GCC_WARN_UNUSED_FUNCTION = YES
GCC_WARN_UNUSED_VARIABLE = YES
IPHONEOS_DEPLOYMENT_TARGET = 11.2
// Custom set
CODE_SIGN_STYLE = Manual
SWIFT_VERSION = 4.1
DEVELOPMENT_TEAM = MYTEAMIDGOESHERE
ENABLE_BITCODE = NO
CODE_SIGN_ENTITLEMENTS =
CODE_SIGN_IDENTITY =
base_debug.xcconfig
:
_DEBUG = DEBUG
GCC_PREPROCESSOR_DEFINITIONS = $(_DEBUG)
DEBUG_INFORMATION_FORMAT = dwarf
ENABLE_TESTABILITY = YES
GCC_DYNAMIC_NO_PIC = NO
GCC_OPTIMIZATION_LEVEL = 0
MTL_ENABLE_DEBUG_INFO = YES
ONLY_ACTIVE_ARCH = YES
SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG
SWIFT_OPTIMIZATION_LEVEL = -Onone
SWIFT_ACTIVE_COMPILATION_CONDITIONS = $(inherited) $(_DEBUG)
base_release.xcconfig
:
DEBUG_INFORMATION_FORMAT = dwarf-with-dsym
ENABLE_NS_ASSERTIONS = NO
MTL_ENABLE_DEBUG_INFO = NO
SWIFT_COMPILATION_MODE = wholemodule
SWIFT_OPTIMIZATION_LEVEL = -O
VALIDATE_PRODUCT = YES
base_apps.xcconfig
:
#include "base_shared.xcconfig"
// Apps have no specific BuildSettings that the FRAMEWORKS does not have as well. But in the future they might have, thus we have this file.
// These settings were in the TravelCompanion xcconfig
SWIFT_ACTIVE_COMPILATION_CONDITIONS[sdk=iphonesimulator*] = $(inherited) IOS_SIMULATOR
MTL_ENABLE_DEBUG_INFO = NO
SWIFT_OPTIMIZATION_LEVEL = -O
DEBUG_INFORMATION_FORMAT = dwarf-with-dsym
ENABLE_NS_ASSERTIONS = NO
VALIDATE_PRODUCT = YES
SUPPORTED_PLATFORMS = iphonesimulator iphoneos
VALID_ARCHS = arm64 armv7 armv7s
CLANG_MODULES_AUTOLINK = YES
GCC_OPTIMIZATION_LEVEL = s
CLANG_ENABLE_CODE_COVERAGE = NO
TARGETED_DEVICE_FAMILY = 1
CLANG_ANALYZER_LOCALIZABILITY_NONLOCALIZED = YES
VERSIONING_SYSTEM = apple_generic
// Custom
// The flag `ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES` should be set to `YES` for apps and `NO` for frameworks
ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES
base_framework.xcconfig
:
#include "base_shared.xcconfig"
// BuildSettings specific for FRAMEWORKS (Cocoa Touch Framework)
// Vanilla BuildSettings
CURRENT_PROJECT_VERSION = 1
VERSIONING_SYSTEM = apple-generic
VERSION_INFO_PREFIX =
// Custom Set
PRODUCT_NAME = $(TARGET_NAME:c99extidentifier)
INSTALL_PATH = $(LOCAL_LIBRARY_DIR)/Frameworks
DYLIB_COMPATIBILITY_VERSION = 1
DYLIB_CURRENT_VERSION = 1
DEFINES_MODULE = YES
SKIP_INSTALL = YES
// The flag `ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES` should be set to `YES` for apps and `NO` for frameworks
ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = NO
LD_RUNPATH_SEARCH_PATHS = $(inherited) @executable_path/Frameworks @loader_path/Frameworks
base_tests.xcconfig
:
#include "base_shared.xcconfig"
ONLY_ACTIVE_ARCH = YES
//Only used by APPS but it does not mess up build settings for test targets for FRAMEWORKS in Xcode 9.3 so will keep it here for now
BUNDLE_LOADER = $(TEST_HOST)
base_uitests.xcconfig
:
#include "base_shared.xcconfig"
TEST_TARGET_NAME = $(PRODUCT_NAME)
Models-Common.xcconfig
:
#include "../../../../Configurations/base_framework.xcconfig"
INFOPLIST_FILE = Source/SupportingFiles/Models-Info.plist
PRODUCT_BUNDLE_IDENTIFIER = com.sajjon.Models
Models-Debug.xcconfig
:
#include "Models-Common.xcconfig"
#include "../../../../Configurations/base_debug.xcconfig"
#include "../../Pods/Target Support Files/Pods-Framework-Models-Models/Pods-Framework-Models-Models.debug.xcconfig"
Models-Release.xcconfig
:
#include "Models-Common.xcconfig"
#include "../../../../Configurations/base_release.xcconfig"
#include "../../Pods/Target Support Files/Pods-Framework-Models-Models/Pods-Framework-Models-Models.release.xcconfig"
ModelsTests-Common.xcconfig
:
#include "../../../../Configurations/base_framework.xcconfig"
#include "../../../../Configurations/base_tests.xcconfig"
INFOPLIST_FILE = Tests/SupportingFiles/ModelsTests-Info.plist
ModelsTests-Debug.xcconfig
:
#include "ModelsTests-Common.xcconfig"
#include "../../../../Configurations/base_debug.xcconfig"
#include "../../Pods/Target Support Files/Pods-Framework-Models-ModelsTests/Pods-Framework-Models-ModelsTests.debug.xcconfig"
ModelsTests-Release.xcconfig
:
#include "ModelsTests-Common.xcconfig"
#include "../../../../Configurations/base_release.xcconfig"
#include "../../Pods/Target Support Files/Pods-Framework-Models-ModelsTests/Pods-Framework-Models-ModelsTests.release.xcconfig"
MyiPhoneApp-Commong.xcconfig
:
#include "../../../../Configurations/base_apps.xcconfig"
_APP_NAME_PROD = My iPhone App
_BUNDLE_IDENTIFIER_PROD = com.sajjon.myiphoneaop
INFOPLIST_FILE = Source/SupportingFiles/MyiPhoneApp-Info.plist
DEVELOPMENT_TEAM = MYTEAMGOESHERE
PRODUCT_NAME = $(_APP_NAME_PROD)
ASSETCATALOG_COMPILER_LAUNCHIMAGE_NAME = LaunchImage
CODE_SIGN_ENTITLEMENTS = Source/SupportingFiles/$(_APP_NAME_PROD).entitlements
SWIFT_OBJC_BRIDGING_HEADER = Source/SupportingFiles/MyiPhoneApp-Bridging-Header.h
PRODUCT_MODULE_NAME = TravelCompanion
// Overridden in Debug.xcconfig
CODE_SIGN_IDENTITY = iPhone Distribution
// Overridden in Release.xcconfig
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon-Alpha
ENABLE_TESTABILITY = YES
MyiPhoneApp-Debug.xcconfig
:
#include "MyiPhoneApp-Common.xcconfig"
#include "../../../../Configurations/base_debug.xcconfig"
#include "../../Pods/Target Support Files/Pods-MyiPhoneApp/Pods-MyiPhoneApp.debug.xcconfig"
_DEBUG = DEBUG
_CODE_SIGN_ID_DEBUG = iPhone Developer
_BUNDLE_IDENTIFIER_DEBUG = $(_BUNDLE_IDENTIFIER_PROD).dev
_PP_DEBUG = match Development $(_BUNDLE_IDENTIFIER_DEBUG)
CODE_SIGN_IDENTITY = $(_CODE_SIGN_ID_DEBUG)
PROVISIONING_PROFILE_SPECIFIER = $(_PP_DEBUG)
PRODUCT_BUNDLE_IDENTIFIER = $(_BUNDLE_IDENTIFIER_DEBUG)
APP_DISPLAY_NAME = MyiPhoneApp
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon-Debug
SWIFT_ACTIVE_COMPILATION_CONDITIONS = $(inherited) $(_DEBUG)
GCC_PREPROCESSOR_DEFINITIONS = $(_DEBUG)
GCC_OPTIMIZATION_LEVEL = 0
GCC_OPTIMIZATION_LEVEL = 0
MTL_ENABLE_DEBUG_INFO = YES
SWIFT_OPTIMIZATION_LEVEL = -Onone
GCC_DYNAMIC_NO_PIC = NO
ONLY_ACTIVE_ARCH = YES
ENABLE_NS_ASSERTIONS = YES
ENABLE_NS_ASSERTIONS[sdk=iphoneos*] = YES
VALIDATE_PRODUCT = NO
MyiPhoneApp-Release.xcconfig
:
#include "MyiPhoneApp-Common.xcconfig"
#include "../../../../Configurations/base_release.xcconfig"
#include "../../Pods/Target Support Files/Pods-MyiPhoneApp/Pods-MyiPhoneApp.release.xcconfig"
_PP_PROD = match AppStore $(_BUNDLE_IDENTIFIER_PROD)
PROVISIONING_PROFILE_SPECIFIER = $(_PP_PROD)
PRODUCT_BUNDLE_IDENTIFIER = $(_BUNDLE_IDENTIFIER_PROD)
APP_DISPLAY_NAME = $(_APP_NAME_PROD)
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon-Production
SWIFT_ACTIVE_COMPILATION_CONDITIONS = $(inherited) APP_STORE_BUILD
SWIFT_OPTIMIZATION_LEVEL = -Owholemodule