Xcode iOS deployment target check? - iphone

On more than one occasion, I have accidentally changed the deployment target of my Xcode project to something I don't want. The latest time, I found out because a customer wasn't seeing my app. Is there a way to put something into my code somewhere to assert the value of the deployment target? I think I would be much less likely to change that by mistake.

You can check your deployment target version via __IPHONE_OS_VERSION_MIN_REQUIRED macro and compare it to some sdk version, e.g. to make sure that your deployment target is 4.0 put the following lines somewhere:
#if __IPHONE_OS_VERSION_MIN_REQUIRED != __IPHONE_4_0
#error wrong deployment target - should be 4.0
#endif

Add a New Run Script Build Phase to your target and put this script inside:
DEPLOYMENT_TARGET_VALID=`expr ${IPHONEOS_DEPLOYMENT_TARGET} \>= "4.0"`;
if [ $DEPLOYMENT_TARGET_VALID = 1 ]; then
echo "Deployment target ($IPHONEOS_DEPLOYMENT_TARGET) is too high."
exit 1
fi;
Here, build will fail if the deployment target is set to 4.0 or higher.

Related

xcodebuild ACTIVE_COMPILATION_CONDITIONS does not override target's ACTIVE_COMPILATION_CONDITIONS

We are trying to use xcodebuild to build our frameworks, instead of using manual Xcode IDE running buttons. The issue is that in our framework we use ACTIVE_COMPILATION_CONDITIONS, which have several values. Those values are then used to check at runtime if the particular framework is integrated, like checking for Sentry:
#if SENTRY_AVAILABLE
import Sentry
#endif
The problem is that, for some builds, we need to override our project settings, specifically ACTIVE_COMPILATION_CONDITIONS. However, after the following script successfully executes, the Xcode still does not override our provided ACTIVE_COMPILATION_CONDITIONS with defined in the project.
The script:
xcodebuild -workspace project.xcworkspace -scheme SDKNR1 ONLY_ACTIVE_ARCH=NO
EXCLUDED_ARCHS=arm64 ACTIVE_COMPILATION_CONDITIONS=SENTRY_AVAILABLE -configuration
release -derivedDataPath $PROJECT_DIR/../simulators/SDKNR1 -sdk iphonesimulator
ENABLE_BITCODE=YES BITCODE_GENERATION_MODE=bitcode OTHER_CFLAGS="-fembed-bitcode" clean build
As you can see we define ACTIVE_COMPILATION_CONDITIONS=SENTRY_AVAILABLE, however it will not override target's (SDKNR1) project settings. Suppose, SDKNR1 does not have any ACTIVE_COMPILATION_CONDITIONS. We expected that xcodebuild command would override target's ACTIVE_COMPILATION_CONDITIONS and would include SENTRY_AVAILABLE
Would welcome any ideas, or perhaps it is not possible?
The proper build setting key is "SWIFT_ACTIVE_COMPILATION_CONDITIONS"
You can double check this by using the command and verifying the key exists:
xcodebuild -showBuildSettings <project/scheme/target/configuration flags>
Results from -showBuildSettings (truncated, for RELEASE_CONDITION2 set in the Xcode project settings for release build [for some reason debug wont show]):
.....
SUPPORTS_TEXT_BASED_API = NO
SWIFT_ACTIVE_COMPILATION_CONDITIONS = RELEASE_CONDITION2
SWIFT_COMPILATION_MODE = wholemodule
.....
In build log, you should see (as example here, setting RELEASE_CONIDTION2, also note that the ACTIVE_COMPLIATION_CONIDTIONS gets translated into -D parameters for swiftc):
Build settings from command line:
.....
SDKROOT = iphoneos14.5
SWIFT_ACTIVE_COMPILATION_CONDITIONS = RELEASE_CONDITION2
.....
CompileSwiftSources normal arm64 com.apple.xcode.tools.swift.compiler .....
.....
/Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/bin/swiftc <most compiler options removed> -DRELEASE_CONDITION2
Above is all with Xcode 12.5

How can I make sure that builds are using v14.x iOS/tvOS simulators?

Some of the builds in our environment are failing because the builds are using a 13.X iOS/tvOS simulator and for some reason that causes issues. I made changes to our Fastfile so that the builds use a 14.2 simulator instead and now the builds are succeeding.
Here are the changes I made to the fast file.
First, I declared a dict:
XCODE_DESTINATION = {
iphoneos: "generic/platform=iOS",
iphonesimulator: "platform=iOS Simulator,OS=14.2,name=iPhone 11 Pro Max",
appletvos: "generic/platform=tvOS",
appletvsimulator: "platform=tvOS Simulator,OS=14.2,name=Apple TV 4K"
}
Then, in the run_tests lane for both iOS and tvOS, I reference the iphonesimulator and the appletvsimulator:
run_tests(
destination: XCODE_DESTINATION[:iphonesimulator],
workspace: WORKSPACE_NAME,
scheme: options[:scheme_tests]
)
run_tests(
destination: XCODE_DESTINATION[:appletvsimulator],
workspace: WORKSPACE_NAME,
scheme: options[:scheme_tests]
)
Even though this solution solves the problem, I don't really want the XCODE_DESTINATION dict to have to specifically reference an OS version and a specific device.
Is there a way I can configure this Fastfile so that it runs the tests only if a 14.X simulator is present within XCode without having to specifically indicate that in the dict?
Thank you!
Currently fastlane does not support this, so feel free to create an issue ->
https://github.com/fastlane/fastlane/issues
But you can specify the simulators and versions in the run_tests:
run_tests(
devices: ['iPhone 11 Pro Max (14.2)'],
workspace: WORKSPACE_NAME,
scheme: options[:scheme_tests],
ensure_devices_found: true
)
Additionally you can set ensure_devices_found to true, so if the specified simulator(s) not found, tests will fail.

how to setting XCode project that have two target with same bundle version

so if i upload on appstoreconnect and have different bundle version on each target it will be ignored by apple. ignored info when uploading to appstoreconnect
PLIST="${PROJECT_DIR}/${INFOPLIST_FILE}"
PLB=/usr/libexec/PlistBuddy
LAST_BUILD_VERSION=$($PLB -c "Print CFBundleVersion" "$PLIST")
IFS=. components=($LAST_BUILD_VERSION)
major=${components[0]}
minor=${components[1]}
micro=${components[2]}
build=${components[3]}
build_plus=$((build + 1))
old_build="$major.$minor.$micro.$build"
new_build="$major.$minor.$micro.$build_plus"
$PLB -c "Set :CFBundleVersion $MARKETING_VERSION.$build_plus" "$PLIST"
i already made a run-script that change the last number for one target but i dont have idea to change another target using run script
if it 5.5.10.1 then it would change it to 5.5.10.2 for main target. but the other target dont get change
I think you can use User-Defined Setting at the project level, then use that value for all of your targets.

"Module was not compiled for testing" error when using Swift Package Manager

I created a Swift library with swift package init --type library and generated an Xcode project with swift package generate-xcodeproj.
Now I'm trying to run the Test scheme in Xcode. It prints following error:
Module '<title>' was not compiled for testing
However when I run swift build and swift test in terminal, it works fine.
I have ENABLE_TESTABILITY set to YES in all of the targets. I didn't change anything in the project except this. How can I make Xcode perform unit testing?
You need to set the "Enable Testability" to Yes in build setting over your "Main Target"
I was having this issue today, it seems like #testable cannot be used with projects generated by Swift Package Manager.
Removing #testable from my import statements solved this issue. Of course, this means we can only test the public interface of our modules.
Xcode -> Product -> Scheme -> Edit Scheme
Select the Info tab.
Set the following -
Build Configuration: Debug
Add a check mark to Debug executable
Tested with Xcode 12.4(12D4e) and iOS 14.1 deployment target.

Swift Package Manager and Xcode: Retaining Xcode Settings?

I am developing a server in Swift and using the Swift Package Manager. And find it convenient when doing my development on my Mac OS system to generate a Xcode project to use Xcode as my IDE (i.e., From time to time, my package dependencies have to be updated. I've been using swift package generate-xcodeproj to do this. My problem comes in at this point-- I have created some settings in Xcode. E.g., I've set a DEBUG flag, and I have a .plist file that is in the Copy Files Phase. These get lost when I regenerate the Xcode project. It seems I cannot simply use swift package update because sometimes files change in the dependencies and these don't get propagated to the Xcode project.
What I'd like is a means to separately establish Xcode settings in a file outside of Xcode, that can be imported into Xcode when I do the swift package generate-xcodeproj. I have not seen a way to do this.
A related question is: When I do a swift build I'd like those same build settings to be used.
Suggestions?
I can't help with Copy Files Phase.
However I have just been toying with conditional compilation, like this:
swift package generate-xcodeproj --xcconfig-overrides Sandbox.xcconfig
Sandbox.xcconfig
FLAG_SANDBOX = -DSANDBOX
OTHER_SWIFT_FLAGS = $(FLAG_SANDBOX)
This creates an Xcode project where SANDBOXis definded.
This can be used in swift code like this
#if SANDBOX
print("sandbox")
#else
print("production")
#endif
I would use a script or a makefile to import your settings into the generated Xcode project, each time you regenerate it. You can use xcodeproj rubygem.
See an example script.
Regarding using your settings in swift build, can you give an example of such a setting? In general, a makefile can read your settings file and pass the corresponding parameters to swift build.
Based on #vadim's answer above, here's a xcodeproj rubygem solution to the first part of my question:
#!/usr/bin/ruby
# Tweak the .xcodeproj after creating with the swift package manager.
# Resources:
# https://stackoverflow.com/questions/41527782/swift-package-manager-and-xcode-retaining-xcode-settings/41612477#41612477
# https://stackoverflow.com/questions/20072937/add-run-script-build-phase-to-xcode-project-from-podspec
# https://github.com/IBM-Swift/Kitura-Build/blob/master/build/fix_xcode_project.rb
# http://www.rubydoc.info/github/CocoaPods/Xcodeproj/Xcodeproj%2FProject%2FObject%2FAbstractTarget%3Anew_shell_script_build_phase
# http://www.rubydoc.info/github/CocoaPods/Xcodeproj/Xcodeproj/Project/Object/AbstractTarget
# https://gist.github.com/niklasberglund/129065e2612d00c811d0
# https://github.com/CocoaPods/Xcodeproj
# https://stackoverflow.com/questions/34367048/how-do-you-automate-do-copy-files-in-build-phases-using-a-cocoapods-post-insta?rq=1
require 'xcodeproj'
path_to_project = "Server.xcodeproj"
project = Xcodeproj::Project.open(path_to_project)
# 1) Add Copy Files Phase for Server.plist to the Products directory for Server target
target = project.targets.select { |target| target.name == 'Server' }.first
puts "Add Copy Files Phase to #{target}"
phase = target.new_copy_files_build_phase()
# Contrary to the docs (see http://www.rubydoc.info/github/CocoaPods/Xcodeproj/Xcodeproj/Project/Object/PBXCopyFilesBuildPhase) I believe this is not a path, but rather a code, e.g., 16 indicates to copy the file to the Products Directory.
phase.dst_subfolder_spec = "16"
fileRef = project.new(Xcodeproj::Project::Object::PBXFileReference)
fileRef.path = 'Server.plist'
phase.add_file_reference(fileRef)
# 2) Add in script phase for testing target-- because I haven't figured out to get access to the Products directory at test-run time.
target = project.targets.select { |target| target.name == 'ServerTests' }.first
puts "Add Script Phase to #{target}"
phase = target.new_shell_script_build_phase()
phase.shell_script = "cp Server.plist /tmp"
# 3) Add in DEBUG flag
# A little overkill, but hopefully appending a DEBUG flag in the Debug configuration for each target doesn't hurt it.
project.targets.each do |target|
puts "Appending DEBUG flag to #{target}"
if target.build_settings('Debug')['OTHER_SWIFT_FLAGS'].nil?
target.build_settings('Debug')['OTHER_SWIFT_FLAGS'] = ""
end
target.build_settings('Debug')['OTHER_SWIFT_FLAGS'] << '-DDEBUG'
end
project.save()