Xcode 8 and exportPath - ios

Starting with Xcode 8, my TeamCity builds no longer create an Artifact with the proper name. Here is my command line to export archive:
xcodebuild -project "${XCODE_PROJECT}" -exportArchive \
-archivePath "${ARTIFACT_DIR}${ARTIFACT_NAME}.xcarchive" \
-exportPath "${ARTIFACT_DIR}${ARTIFACT_NAME}.ipa" \
-exportOptionsPlist ./export-appstore.plist
If ARTIFACT_DIR and ARTIFACT_NAME result in "/builds/myApp-1.0.1.ipa", for example, xcodebuild will actually create "/builds/myApp-1.0.1.ipa/myApp.ipa".
The "man" documentation for '-exportPath' says "Specifies the destination for the exported product, including the name of the exported file". But clearly it's assuming the file name from the Xcode project and using the entire parameter just for the path.
Is this a known bug that will be fixed, or did they remove functionality with this latest version of Xcode?

Related

Turn Package.swift file into binary XCFramework

Someone in my company created a Swift package SDK and now I was tasked to publish it for the customer in a binary way so that the end customers that will use the SDK will not be able to see the source code of it. This is how the SDK is built:
the SDK in xcode (p.s. the build folder is empty)
From my reading on the subject I understand that I need to export the files into an XCFramework file. However, the vast majority of guides I've encountered explain how to make this progress from a framework, and not from a package like in my case..
The only guide I found that seems exactly like what I need is this one, however I get an error right on the first relevant terminal command of xcodebuild -scheme [my scheme name] -sdk iphoneos -configuration Release ARCHS="arm64" BUILD_DIR="./Build". This is the main error line I get: xcodebuild: error: Building a Swift package requires that a destination is provided using the "-destination" option. The "-showdestinations" option can be used to list the available destinations.. Why would I need to specify a destination? I want the SDK to work for all the devices (ios 13+). Non of the forums I searched in online helped me solve this.
I also read Apple's instructions here but got very confused about how the terminal command in step 2 is supposed to look like in my case. Are some of the fields mandatory and some are not?
Any help would be much appreciated!!
Assuming you are working with iOS only and you need an xcframework for both device and simulator architectures, in order to generate an XCFramework from a swift package you need to:
Mark your Package as .dynamic (i.e. .library(name: "Foo", type: .dynamic, targets: ["Foo"]))
Archive the project for both simulator and device. This will generate a .framework file for each architecture.
Copy Modules folders (if any) into the .xcarchive files
Copy the bundles (if any) into the .xcarchive files
Create the xcframework with the frameworks created in step 2
Here's a bash script, based on the one from this swift forums post, to create an XCFramework from a swift package (in my case, I have my package inside an xcworkspace. I didn't try it as a standalone package, not sure if that can be done):
Change the input parameters as needed :), but most importantly replace <Your project name> and <your workspace>.
#!/bin/bash
PROJECT_NAME="<Your project name>"
PROJECT_DIR="./Packages/${PROJECT_NAME}" # Relative path to the directory containing the `Package.swift` file
BUILD_FOLDER="./build"
OUTPUT_DIR="${PROJECT_DIR}/Output"
SIMULATOR_ARCHIVE="${OUTPUT_DIR}/${PROJECT_NAME}-iphonesimulator.xcarchive"
DEVICE_ARCHIVE="${OUTPUT_DIR}/${PROJECT_NAME}-iphoneos.xcarchive"
rm -rf "$OUTPUT_DIR"
mkdir -p "$OUTPUT_DIR"
# 2 iterations: 1 for device arch and another for simulator arch
for PLATFORM in "iOS" "iOS Simulator"; do
case $PLATFORM in
"iOS")
ARCHIVE=$DEVICE_ARCHIVE
SDK=iphoneos
RELEASE_FOLDER="Release-iphoneos"
;;
"iOS Simulator")
ARCHIVE=$SIMULATOR_ARCHIVE
SDK=iphonesimulator
RELEASE_FOLDER="Release-iphonesimulator"
;;
esac
# Step 2
xcodebuild archive \
-workspace <your workspace>.xcworkspace \
-scheme $PROJECT_NAME \
-destination="generic/platform=${PLATFORM}" \
-archivePath $ARCHIVE \
-sdk $SDK \
-derivedDataPath $BUILD_FOLDER \
SKIP_INSTALL=NO \
BUILD_LIBRARY_FOR_DISTRIBUTION=YES
FRAMEWORK_PATH="${ARCHIVE}/Products/Library/Frameworks/${PROJECT_NAME}.framework"
MODULES_PATH="$FRAMEWORK_PATH/Modules"
mkdir -p $MODULES_PATH
BUILD_PRODUCTS_PATH="${BUILD_FOLDER}/Build/Intermediates.noindex/ArchiveIntermediates/${PROJECT_NAME}/BuildProductsPath"
RELEASE_PATH="${BUILD_PRODUCTS_PATH}/${RELEASE_FOLDER}"
SWIFT_MODULE_PATH="${RELEASE_PATH}/${PROJECT_NAME}.swiftmodule"
RESOURCES_BUNDLE_PATH="${RELEASE_PATH}/${PROJECT_NAME}_${PROJECT_NAME}.bundle"
# Step 3
if [ -d $SWIFT_MODULE_PATH ]
then
cp -r $SWIFT_MODULE_PATH $MODULES_PATH
fi
# Step 4
if [ -e $RESOURCES_BUNDLE_PATH ]
then
cp -r $RESOURCES_BUNDLE_PATH $FRAMEWORK_PATH
fi
done
# Step 5
xcodebuild -create-xcframework \
-framework "${DEVICE_ARCHIVE}/Products/Library/Frameworks/${PROJECT_NAME}.framework" \
-framework "${SIMULATOR_ARCHIVE}/Products/Library/Frameworks/${PROJECT_NAME}.framework" \
-output "${OUTPUT_DIR}/${PROJECT_NAME}.xcframework"
When the script ends, you will see 3 files in the output folder:
Device xcarchive
Simulator xcarchive
XCFramework
You can remove the xcarchives, as you won't need them anymore (you can also update the script to do that for you).

XCBuild shows error while calling fastlane gym

I have an app with multiple targets and try to configure fastlane's gym. Also, we use pretty extensively compiler flags in the app.
I get this error FIXME: Implement XCBuild support for macros in overriding parameters with condition sets: DEVELOPMENT_TEAM[config=Release] = ***.
The error appears after gym executes this xcodebuild command:
set -o pipefail && xcodebuild -workspace Workspace.xcworkspace -scheme Scheme -xcconfig Config.xcconfig -sdk 'iphoneos14.5' -destination 'generic/platform=iOS' -archivePath archivePath.xcarchive archive | tee Myapp.log | xcpretty
The app is native, no Ionic or ReactNative or any of that stuff.
Do you have any idea where does come from? Or how I can solve it?
Thanks for the help.
For anybody stumbling upon this question, I found the solution in this blog. Quoting from the blogpost:
Example:
CURRENT_PROJECT_VERSION_app = 15.3.9 // Application version number
CURRENT_PROJECT_VERSION_xctest = 1.0.0 // Unit Test version number
CURRENT_PROJECT_VERSION = $(CURRENT_PROJECT_VERSION_$(WRAPPER_EXTENSION))
where in my case WRAPPER_EXTENSION would be CONFIGURATION variable.

Define a preprocessor macro value from a shell script in iOS

We have an app that we want uploaded to a site with a different app id based on the the environment it's being built in. In the project we have a Release preprocessor macro set to MY_CONFIGURATION=$(MY_CONFIGURATION) and we set a default value to 3 in the user-defined settings. In our define where we assign the app id string value based off the macro define, we also have the fallback that if the value is not defined, use the value 1. The problem is when we build off Jenkins, the script assigned value (2) is overwritten by the default value from the user-defined settings (3).
Here's the line we are using in the build.sh file
xcodebuild -scheme ${SCHEME} -sdk ${SDK} -destination generic/platform=iOS CODE_SIGN_IDENTITY="${PROFILE}" MY_CONFIGURATION="${BUILD_ENV}" build
I know the correct value is being placed, as the console output from Jenkins shows the following
10:58:46 + xcodebuild -scheme MGO -sdk iphoneos9.0 -destination generic/platform=iOS 'CODE_SIGN_IDENTITY=X' MY_CONFIGURATION=2 build
10:58:47 Build settings from command line:
10:58:47 CODE_SIGN_IDENTITY = X
10:58:47 MY_CONFIGURATION = 2
10:58:47 SDKROOT = iphoneos9.0
The build uploads to the correct environment, build the #define within the code that sets the app id based on the value set displays the default value set (3) instead of the script value set (2). Any tips or help would be appreciated.
Note: I've also tried
xcodebuild -scheme ${SCHEME} -sdk ${SDK} -destination generic/platform=iOS CODE_SIGN_IDENTITY="${PROFILE}" OTHER_CFLAGS="-DMY_CONFIGURATION="${BUILD_ENV}"" build
I use xctool to build and it works perfect.
I think it works the same way in your situation and you can give it a try. Here's what I do.
xctool -workspace productname.xcworkspace -scheme "$scheme" archive -archivePath $archivePath GCC_PREPROCESSOR_DEFINITIONS_NOT_USED_IN_PRECOMPS="MY_CONFIGURATION=2 MY_ANOTHER_CONFIGURATION=1"
What makes it work is GCC_PREPROCESSOR_DEFINITIONS_NOT_USED_IN_PRECOMPS.
GCC_PREPROCESSOR_DEFINITIONS_NOT_USED_IN_PRECOMPS="MY_CONFIGURATION=2 MY_ANOTHER_CONFIGURATION=1"
You can try to append this line above to your command. Make sure the value is a number. I failed when tried to use a string value. And there's another related keyword GCC_PREPROCESSOR_DEFINITIONS in the doc.

Jenkins + iOS + TestFlight API

I installed a Jenkins and startet a Project with the Git Plugin, Xcode Plugin and the Testflight Plugin.
I can create automatic builds with the setup, but it is failing at the point on creating the .ipa files for Testflight.
The problem are at Debug and Release settings in different ways though…
If i try to create a .ipa via the Debug settings it will fail at the point that there is no build/Debug-iphoneos folder (i tried to turn of the clean option but it didn't helped). But the Xcode Build is not failing on the command line
When i try to switch to Release the linker is failing (ld).
Whithout the .ipa files i can't submit to Testflight and get a automated Test distributing.
Here is the script I'm using. (Obviously I removed the personal information, but you should be fine to understand it).
TARGET_NAME="-" # Target name
TARGET_SDK="iphoneos" # Target SDK: iphoneos
CONFIGURATION="Release" # Build Configuration
BUILD_DIR="build" # Directory where the build is generated
ARCHS="armv7" # Valid Architectures
APP_NAME="-" # Application name
## Provisioning configurations
BUILD_ARCHIVED_DIR="BuildArchived" # Directory with the history of builds
DEVELOPER_NAME="-" # Developer name
PROVISIONING_PROFILE=Prototype.mobileprovision # Provisioning profile file
PROVISIONING_DIR=~/Library/MobileDevice/Provisioning\ Profiles/ # Provisioning dir
## TestFlight App
TESTFLIGHT_API_TOKEN="-"
TESTFLIGHT_TEAM_TOKEN="-"
#Release Notes
BUILDSCRIPTS_DIR="build"
TESTFLIGHT_RELEASE_NOTES_FILE="ios_testflight-releasenotes"
#Distribution Lists
TESTFLIGHT_DISTRIBUTION_LISTS="Jenkins"
# Returns to the root directory of the build
cd ../ios
PROJECT_BUILDDIR="${BUILD_DIR}/${CONFIGURATION}-${TARGET_SDK}"
CURRENT_DIR=`pwd`
# fix for the newest sdk
# Only export the environment variable if the location exists,
# otherwise it breaks the signing process!
if [ -f "/Developer/Platforms/iPhoneOS.platform/Developer/usr/bin/codesign_allocate" ]
then
echo Export environment variable for codesign_allocate location
export CODESIGN_ALLOCATE=/Developer/Platforms/iPhoneOS.platform/Developer/usr/bin/codesign_allocate
fi
#changing the build version
INFO_PLIST_PATH="${CURRENT_DIR}/${TARGET_NAME}/${TARGET_NAME}-Info.plist"
/usr/libexec/PlistBuddy -c "Set :CFBundleVersion ${SVN_REVISION}" $INFO_PLIST_PATH
# compile project
echo Building Project
xcodebuild -target "${TARGET_NAME}" -sdk "${TARGET_SDK}" ARCHS=${ARCHS} -configuration "${CONFIGURATION}" clean build
#Check if build succeeded
#if [ $? != 0 ]
#then
# exit 1
#fi
# Create output dir ($x) if doesn't exist
mkdir -p $BUILD_ARCHIVED_DIR
# .ipa file generation
echo Generating .ipa file
/usr/bin/xcrun -sdk "${TARGET_SDK}" PackageApplication -v "${PROJECT_BUILDDIR}/${APP_NAME}.app" -o "${CURRENT_DIR}/${BUILD_ARCHIVED_DIR}/${APP_NAME}.ipa" --sign "${DEVELOPER_NAME}"
#zipping the .dSYM to send to Testflight
echo Generating zip file
/usr/bin/zip -r "${CURRENT_DIR}/${BUILD_ARCHIVED_DIR}/${APP_NAME}.app.dSYM.zip" "${CURRENT_DIR}/${PROJECT_BUILDDIR}/${APP_NAME}.app.dSYM"
echo Sending to TestFlight
curl http://testflightapp.com/api/builds.json -F file="#${CURRENT_DIR}/${BUILD_ARCHIVED_DIR}/${APP_NAME}.ipa" -F dsym="#${CURRENT_DIR}/${BUILD_ARCHIVED_DIR}/${APP_NAME}.app.dSYM.zip" -F api_token="${TESTFLIGHT_API_TOKEN}" -F team_token="${TESTFLIGHT_TEAM_TOKEN}" -F notes="This build was uploaded via the upload API" -F notify=False -F distribution_lists="${TESTFLIGHT_DISTRIBUTION_LISTS}"
echo Submission ended
I imagine the Scheme your build is targeting is incorrect.
Additionally, TestFlight has a plugin for Jenkins so you can script your build process and execute the upload to TestFlight using the their Jenkins Plugin. I have provided a build script example that works for me via manual command line and under Jenkins CI.
If you would like to see the full setup, you can find a iOS/Git/TestFlight tutorial here: Jenkins iOS – Git, xcodebuild, TestFlight
xcodebuild -alltargets clean
rm -rf "./JenkinsBuild/*"
xcodebuild -target HelloJenkins PROVISIONING_PROFILE="00000000-0000-0000-0000-000000000000" CONFIGURATION_BUILD_DIR=JenkinsBuild
rm -rf "./JenkinsArchive/*"
xcodebuild -scheme HelloJenkins archive PROVISIONING_PROFILE="00000000-0000-0000-0000-000000000000" CODE_SIGN_IDENTITY="iPhone Developer: Jonny Appleseed (XXXXXXXXXX)" -archivePath ./JenkinsArchive/HelloJenkins.xcarchive
rm -rf "./JenkinsIPAExport/*"
xcodebuild -exportArchive -exportFormat IPA -exportProvisioningProfile iOS\ Team\ Provisioning\ Profile:\ com.yourAPP.HelloJenkins -archivePath ./JenkinsArchive/HelloJenkins.xcarchive -exportPath ./JenkinsIPAExport/HelloJenkins.ipa

xcodebuild target name with Umlaut

I am trying to build an iOS App using Jenkins via the xcodebuild command.
The problem is that xcodebuild does not detect the target with the German Umlaut ü.
$ /usr/bin/xcodebuild -target AppNameWithUmlautü -configuration Debug clean build
gives me following error:
xcodebuild: error: The project 'AppNameWithUmlautü.xcodeproj' does not contain a target named 'AppNameWithUmlautü'.
I checked the existence of the target using following command:
xcodebuild -list
Information about project "AppNameWithUmlautü":
Targets:
AppNameWithUmlautü
AppNameWithOutUmlaut
Build Configurations:
Debug
Release
Ad Hoc
If no build configuration is specified and -scheme is not passed then "Release" is used. This project contains no schemes.
The target without Umlaut works.
Xcode uses the "decomposite" UTF-8 representation for Umlauts in the target name: "ü" is represented as 75 CC 88. 75 is "u", and CC 88 is the UTF-8 sequence for the "COMBINING DIAERESIS".
The shell, on the other hand, uses the "composite" UTF-8 representation C3 BC for "ü".
So the target that you give on the command line does not match the target name in the Xcode project (and one could argue that this is a bug in Xcode).
I have no knowledge about Jenkins, but if you somehow can use the output of the xcodebuild -list command to get the decomposite UTF-8 target name and copy that into your Makefile, shell script or whatever you have, then this should help.

Resources