Can I cause Xcode's debugger to break programmatically? - ios

I'm looking for a way to cause the XCode debugger to break programmatically from Swift code if my iOS program is running in the debugger. This would be similar to the way System.Diagnostics.Debugger.Break() works in the Visual Studio environment. Is this possible? The point would be for any developer that hits a particular section of code to break, but not to have a fatal error that causes code execution to stop permanently.
Edit: This is a little different than the user asking to "enable" a breakpoint (though the answer provided in that question is really what I was looking for). I'm also still looking for something that can be done in Swift without interop, bridging headers, and such.

For Swift, raise(SIGINT) works for me.
I have this function:
func fail(desc: String) {
#if DEBUG
print("assertFail:\(desc)")
raise(SIGINT)
#endif
}
For DEBUG macro setup see here: In absence of preprocessor macros, is there a way to define practical scheme specific flags at project level in Xcode project

Putting asm("svc 0") in your code will stop your running application if debugging through xcode. See: https://stackoverflow.com/a/34078247/215400

I'm not 100% positive there isn't a built in one - but you can create what you want yourself using Symbolic Breakpoints in the XCode UI.
1) Create a new class to represent your Debugger Break.
#interface MYDebuggerBreak : NSObject
+(void)fireUpDebugger;
#end
#implementation MyDebuggerBreak
+(void)fireUpDebugger {
// Do Nothing
}
#end
2) Add a Symbolic Breakpoint to a method on that Class
[MYDebuggerBreak fireUpDebugger]
This is a bit roundabout, you could also put a breakpoint directly into the line "fireUpDebugger" since you control the code. Symbolic Breakpoints are more useful if you want to stop on a method call for something you don't control.

Related

When I call objective-c functions from Swift, NSLog in those functions do nothing

I have a mixed Swift and Objective-C project (one view controller is in objective C). When I try to print some debug output from that view controller (ie in the .m file using NSLog) I do not see any output.
My swift prints are just fine.
I made a new objective-c project and did some NSLogging, that works fine.
I added this code to my appdelegate (swift), just to immediately test:
NSLog("Testing 1 2 3")
print("Is this thing on?")
and I also do not see the NSLog output, but I do see output from the print statement.
What madness is this?
It seems like the property "OS_ACTIVITY_MODE": "disable" PREVENTS NSlog from showing up in the Xcode 9 log.
Uncheck this value in my scheme restored my logs.
Check this stack overflow post - iOS 10 doesn't print NSLogs
The solution teja_D proposes works, but then there's a lot of garbage in the logs.
Myself, I am just going to use printf. Either that or reach back across the obj-c / swift barrier and make a swift object call 'print' for me.
This kind of thing really makes me shake my head.

lldb breakpoint on all methods in class objective c

How can I automate setting a breakpoint on all methods in an Objective C class using lldb?
This is useful for learning the behavior of a complicated legacy class. I am using Xcode (includes lldb) for iOS development, and it is cumbersome to manually go through the (large) file in Xcode and click the gutter next to each method to set breakpoints.
One option is to use regex breakpoints.
breakpoint set -r '\[ClassName .*\]$'
You can play around with the regexp to suit your needs.
The command will create a breakpoint that stops on all methods implemented by that class. However, there will be no breakpoints on methods inherited from superclasses.
To get methods on the superclass, you'll have to use a conditional breakpoint. For example, if the superclass is UIViewController, you could do something like:
br s -r '\[UIViewController .*\]$' -c '(BOOL)[(id)$arg1 isKindOfClass:[CustomVC class]]'
For x86 change (id)$arg1 to *(id*)($ebp+8).
Finally, if you really want to learn about the control flow through various classes, check out dtrace. It's probably more suited to this than a debugger.
br se -f FooViewController.m -p '^#property|^ *- *\('
"br se" is short for "breakpoint set", pass your own filename to the -f argument, and the -p argument is a crude regex for properties and methods in Objective C.
Caveats: This doesn't seem to work for .h files, so if you have properties declared in the header that you want to watch then you may need to set watchpoints on their backing instance variables.
This is the best solution I have found so far, please post alternative solutions if you think they will be helpful.

iOS - 'MyProject-Swift.h' file not found when running Unit Tests for Swift

I am trying to setup Unit Testing for my project.
It is an existing Objective-C app, that I have recently added one Swift class to. I have setup the 'MyProject-Swift.h' and Swift Bridging files (both 'MyProject' and 'MyProjectTest') and I am able to build and run the app just fine using both Objective-C and Swift code.
However, now I want to run some Unit Tests on the new Swift class.
I setup my test file and it looks like the following:
MySwiftClassTests.swift:
import UIKit
import XCTest
import MyProject
class MySwiftClassTests: XCTestCase {
override func setUp() {
super.setUp()
// Put setup code here. This method is called before the invocation of each test method in the class.
}
override func tearDown() {
// Put teardown code here. This method is called after the invocation of each test method in the class.
super.tearDown()
}
func testExample() {
// This is an example of a functional test case.
XCTAssert(true, "Pass")
}
func testPerformanceExample() {
// This is an example of a performance test case.
self.measureBlock() {
// Put the code you want to measure the time of here.
}
}
}
I get this error when running the app as Test:
'MyProject-Swift.h' file not found
I am not sure why this happens only when trying to run the Tests.
Any suggestions?
"MyProject-Swift.h" file is generated at following path:
"$(TARGET_TEMP_DIR)/../$(PROJECT_NAME).build/DerivedSources"
I end up adding this to Header Search Paths for my Unit Test target.
Also as #hyouuu pointed out about being the known issue, hopefully Apple will provide some good solution at their end. Until I believe we need to use this above solution.
https://developer.apple.com/library/content/documentation/Xcode/Conceptual/RN-Xcode-Archive/Chapters/xc6_release_notes.html
Thanks to #gagarwal for figuring this out. In our case the product name has a space, which is collapsed in $PROJECT_NAME, so I had to hard code it. Additionally, by using $CONFIGURATION_TEMP_DIR instead of $TARGET_TEMP_DIR, you can remove the parent directory (../) from the path. So the solution is to add the following to the Header Search Paths in your test target:
"$(CONFIGURATION_TEMP_DIR)/Product Name With Spaces.build/DerivedSources"
Or, if your product does not contain spaces:
"$(CONFIGURATION_TEMP_DIR)/$(PROJECT_NAME).build/DerivedSources"
Saw in the Xcode 6.1 release note, that this is a known issue... sign...
Search for "-swift.h" in the release note https://developer.apple.com/library/content/documentation/Xcode/Conceptual/RN-Xcode-Archive/Chapters/xc6_release_notes.html
Tests written in Objective-C cannot import the Swift generated interfaces header ($(PRODUCT_MODULE_NAME)-Swift.h) for application targets, and therefore cannot be used to test code that requires this header.
Tests for Swift code should be written in Swift. Tests written in Objective-C for framework targets can access the Swift generated interfaces by importing the framework module using #import FrameworkName;. (16931027)
Please see #gagarwal's workaround below which WORKS!
I had a similar issue to yours, I think; here was my setup.
I had an object defined in Swift:
// file Foo.swift
#objc public class Foo {
// ...
}
This class was then used in the initializer of an Objective-C object:
// file Bar.h
#import "MyProject-Swift.h"
#interface Bar: NSObject
- (instancetype)initWithFoo:(Foo *)foo;
#end
This made my unit tests for Bar not compile, since the MyProject-Swift.h header isn't real and the unit test target can't see it. The release note shared by #hyouuu is on point - but I'm not testing a Swift class, I'm testing an Objective-C class!
I was able to fix this by changing the header file for Bar to use a forward class reference instead:
// file Bar.h
#class Foo;
#interface Bar: NSObject
- (instancetype)initWithFoo:(Foo *)foo;
#end
I then included MyProject-Swift.h in Bar.m, and everything worked - my tests of Objective-C objects written in Objective-C compiled properly and continued running, and I could write new tests for Swift objects in Swift.
Hope this helps!
After I tried out everything I could find on the topic, the thing that worked for me was actually running the app although it was still showing the 'ModuleName-Swift.h file not found' error.
It went away and my app works perfectly fine. I guess I should have considered that earlier... The error keeps coming back, but after running the app it always just goes away again. So the issue is not really solved for me, but I can continue working on other topics for now...
A simple
#testable import MyProject
has done the job for me.
Strangely, I was seeing this same error, but only when targeting a device (not the simulator). Before running the test I would see the red exclamation point next to the import statement for "MyProjectNameTests-Swift.h".
However, funny thing is, if I just go ahead and run the test anyway (despite this apparent build error), then during the build phase that happens thereafter, XCode actually does generate the "MyProjectNameTests-Swift.h" file, and the test runs just fine!
So, at least in my case, there was no need for the other solutions here, evidently, although I believe they do work also.
I should also note that I deleted my DerivedData directory prior to this, so maybe that is a step also worth trying.
On one of my clients project they added frameworks directly in the project. I solved the errors like this;
Command 1
option command J
now type the framework name (that gives the error) in the filter
select the framework
option command 1
in Target Membership select your *UITests target
mySwiftClassTests (and any other swift classes you want to use in objective-c) needs to be marked #objc:
#objc class MySwiftClassTests: XCTestCase
I couldn't get it to work by adding that filepath mentioned by other answers, but I realized the file where it was complaining wasn't even being tested. I just had to remove it from the test target using the Right Utilities Side Bar.
Adding a .swift file to that target fixes issue on it.

Detecting unused methods iOS

Very simple question and hopefully isn't duplicated :).
The situation is following:
Project has been developing more then a year and by many developers.
From time to time I'm facing with unused methods (which are defined in .h and .m) obviously I'm not getting any warnings.
This is not critical, but I would like to have project cleared from all unnecessary staff. Of course I can search for all methods and define which are unused in project but I wonder if there is more elegant way?!
Thanks
A quick way to check while browsing your source code is the View > Standard Editor > Show Related Items menu (shortcut key: ^1). Place your cursor within a method body and then view the Callers.
AppCode (http://www.jetbrains.com/objc) can tell you if a method or an import is unused.
It's works in real time, but you can also inspect a whole project (menu code > inspect Code)
I don't think xCode can do it.
AppCode is not free, but it has a trial version.
Might I suggest adding an NSLOG to these methods. For example, if you have a View Controller called Home, you can go into the .m file for the Home View Controller and at the top of the function, write the following:
NSLOG(#"Method 1, has 3 buttons);
then watch the readout as you progress through the actions on that View Controller. The log should say something that would best describe the method in question. Step 2 would be to take the methods that you feel aren't showing up on the output and comment them out. That can be done by highlighting the method and then hitting Command + '/' on the keyboard. This will place '//' in front of each highlighted line commenting it out. Test your view controller again and if still no errors, you can delete that method. That's a free way to do it, but it does take some time.

Xcode 5 unit test seeing wrong class

I am running into a bizarre situation where a unit test's execution is behaving differently than the normal execution of a piece of code.
In particular, I am using a library called JSONModel, and when I am attempting to deserialize an object from a JSON string, one line in particular is causing problems when I step through the executing test case:
if ( [[property.type class] isSubclassOfClass:[JSONModel class]] ) ...
If I put a breakpoint before (or after) this line and execute:
expr [[property.type class] isSubclassOfClass:[JSONModel class]]
...in the debugger, it prints \x01 as the value (i.e. true), however when I actually step the instruction pointer, it behaves as though it is false, going instead into the else block. Again, typing the expression into the debugger again shows it as true even still.
I'm curious if anyone has seen similar behavior before and has any suggestions as to what could possibly be going wrong. I'm quite certain I'm not including different definitions for anything, unless Xcode has different internal cocoa class implementations for testing.
Update: Here's some even weirder evidence: I've added some NSLog statements to get an idea for how the execution is seeing things. If I log property.type.superclass I get JSONModel back (as expected); however if I log property.type.superclass == [JSONModel class] I get false!
To me this is indicating that the JSONModel the unit test execution is seeing is somehow a different JSONModel class that I'm seeing at runtime (and what it should be seeing). However, how that is happening I can't figure out.
Could this be caused by a forward class declaration or something like that?
Well I seem to have discovered a "solution" by experimentation. It turns out if I replace [JSONModel class] with NSClassFromString(#"JSONModel") it works swimmingly!
Now why this is I cannot say, and will give the answer to whoever can explain it!
I had the exact same problem, here's what was happening.
As expected with this kind of behaviour, it was an issue with the class being duplicated. As with you, [instance class] and NSClassFromString would return different values. However, the class were identical in all points : same ivar, same methods (checked with the obj runtime). No warning was displayed at compile, link and/or runtime
In my case, the tests were about a static library used in my main application (Bar.app).
Basically, I had 3 targets:
libFoo
libFooTests
Bar.app
The tests were performing on the device, and not on simulator. As such, they needed to be logic tests, and not unit tests, and had to be loaded into an application. The bundle loader was my main app, Bar.app.
Only additional linker flag was -ObjC
Now, Bar.app was linking libFoo.
It turns out, libFooTests was also linking libFoo.
When injecting libFooTests in the test host (Bar.app), symbols were duplicated, but no warning were presented to me. This is how this particular class got duplicated.
Simply removing libFoo from the list of libraries to link against in libFooTests did the trick.

Resources