In Xcode 7 and 8, I have been able to debug a framework I've been developing by running the containing application within an xcworkspace that also contains the framework project. If I set breakpoints in the framework, they would get triggered at runtime.
In Xcode 9 beta 6, this is no longer the case. So far, execution is only stopping at the framework breakpoints when debugging on simulator. When I debug on a physical device, the framework breakpoints do not stop execution and it appears they are completely ignored.
How can I get framework breakpoints to work properly in order to debug my framework on iOS 11 in Xcode 9?
FWIW: The xcworkspace was created by running pod install
inside the framework root directory. I then added the sample application's xcodeproj to the xcworkspace. This has been functional up until testing on Xcode 9 beta 6.
Edit: Confirmed that this behavior still takes place on the GM seed of Xcode 9.0.
Navigate to a line in your code where you want execution to pause, then click the gutter or line number in the source editor to set a breakpoint. Xcode displays a breakpoint icon to indicate the location. Drag a breakpoint up or down to move it to another location; drag it away from the gutter to remove it.
The Xcode debugger provides several methods to step through your code and inspect variables. You can precisely control execution of your code from a breakpoint, stepping into and out of called functions as necessary to determine where your bug occurs.
LLDB is a debugging component used in the LLVM project which was developed by the LLVM developer group. Xcode uses the LLDB as the default debugging tool. The full form of LLDB is Low-level debugger. Breakpoints help a developer to stop the execution of the program at any point.
An exception breakpoint is a type of breakpoint that is created when some exception occurs in the code. On occurrence of such exception our application stops at that given condition causing the exception and we can access all variables in scope at that breakpoint.
TL;DR - I needed to change which directory my archive script reads from when debugging or when preparing a release. Now when debugging, I need to be sure to set my framework scheme's archive config to "Debug" if I want breakpoints to work properly at runtime. I only use "Release" when preparing a production-ready .framework.
I reached out to Apple developer support via bug report. I will paste the response below. When he mentions "lipo", he is referring to a call I make in a post-archive script that creates a universal framework from the simulator and physical device builds.
Xcode has to match up the binary that's running with the debug symbols that are still on your machine. Xcode should do that automatically, but it sounds like some stuff is moved around behind Xcode's back. To know whether the debug information matches you can look at the output of (lldb) image list and /Mac/path/to/Build/Products/Debug-iphoneos% dwarfdump --uuid iOS-App.app/iOS-App that will work on dylibs too.
Does your framework have parenthesis around the address? That's a sure sign that lldb can't find your symbols.
if the UUID in image list doesn't match the dwarfdump, then something has modified the executable before it ran and doesn't match your built products. We’re not sure if lipo might do that, which I see in your script but definitely check. Not much can be done if the debug-info no longer exists.
If you can find the right executable with a matching UUID on your disk, you can simply (lldb) image add /Mac/path/to/DerivedData-asdfasdfadf/Products/Debug-iphoneos/iOS-App.app/Frameworks/Framework
Additionally, Xcode uses Spotlight to find symbols on your machine. To avoid constantly re-indexing while building, the Intermediates/ directory containing .o files and other place where debug information is stored was blacklisted. This happened fairly late in Xcode 9.0 so may be the reason your stuff was working before.
When I ran (lldb) image list
at runtime, I saw that the UUID of my framework did not match that which was reported by dwarfdump
at /Mac/path/to/Build/Products/Debug-iphoneos
.
I ended up modifying my post-archive script to change which build directory it reads from when creating the framework. When I set my archive config to "Debug", it will read from Debug-iphoneos now. When I set it to "Release" it reads from ${BUILD_DIR}/${CONFIGURATION}${EFFECTIVE_PLATFORM_NAME}
# NOTE: This script creates a universal framework (device and simulator). However, *** for this to work: a Simulator target must be built first, and then Archive on the Device ***
BUILD_PRODUCTS="${SYMROOT}/../../../../Products"
SIM_PATH="${BUILD_PRODUCTS}/Debug-iphonesimulator/${TARGET_NAME}.framework"
if [ "${CONFIGURATION}" = "Debug" ]; then
DEV_PATH="${BUILD_PRODUCTS}/Debug-iphoneos/${TARGET_NAME}.framework"
elif [ "${CONFIGURATION}" = "Release" ]; then
DEV_PATH="${BUILD_DIR}/${CONFIGURATION}${EFFECTIVE_PLATFORM_NAME}/${TARGET_NAME}.framework"
fi
DEST_PATH="${PROJECT_DIR}/../Frameworks/${TARGET_NAME}.framework"
rm -rf "${DEST_PATH}"
mkdir "${DEST_PATH}"
cp -r "${DEV_PATH}/" "${DEST_PATH}/"
rm -f "${DEST_PATH}/${TARGET_NAME}"
cp -Rn "${SIM_PATH}/Modules/" "${DEST_PATH}/Modules/"
lipo -create "${SIM_PATH}/${TARGET_NAME}" "${DEV_PATH}/${TARGET_NAME}" -output "${DEST_PATH}/${TARGET_NAME}"
If the paths are confusing, I don't blame you. Essentially, workspace looks like:
RootDirectory
|__SampleApp
|__SampleApp.xcodeproj
|__Frameworks
|__MyFramework.framework
|__AnotherFramework.framework
|__MyFramework
|__MyFramework.xcworkspace
|__MyFramework.xcodeproj
|__Podfile (etc..)
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With