My unit test target crashes on Xcode 7.
I perform unit tests on my project with a designated app delegate class, different from my regular app delegate . The UnitTestAppDelegate is not a member of the app target, only of the test target.
In Xcode 6 it is working, the correct delegate is used, But in Xcode 7 (beta 6) the app crashes when trying to load to test delegate. I test using simulators, both iOS 8 and 9.
The file is not known to the app target:
Terminating app due to uncaught exception 'NSInternalInconsistencyException', reason: 'Unable to instantiate the UIApplication delegate instance. No class named UnitTestAppDelegate is loaded.'
My main.m file looks like this:
int main(int argc, char* argv[])
{
int returnValue;
#autoreleasepool
{
if (NSClassFromString(#"XCTest") != nil)
{
//use the test delegate
returnValue = UIApplicationMain(argc, argv, nil, #"UnitTestAppDelegate");
}
else
{
//use the normal delegate
returnValue = UIApplicationMain(argc, argv, nil, #"AppDelegate");
}
}
return returnValue;
}
I'm also attaching a screen shot of how my scheme is configured (the run and test are in the same scheme, although I also tried separating them).
Adding my UnitTestAppDelegate as a member of the app target fixes the crash, but I obviously don't want to add the file to my app.
Anyone else encountered this?
Any ideas?
See this related comment about the same issue as seen in Xcode 7 beta 6: http://qualitycoding.org/app-delegate-for-tests/#comment-63240
Related
When I am writing a tweak application with
[[UIATarget localTarget].frontMostApp isVisible] in main.mm main function, I get the exception saying
* exception UIAutomation is not enabled on this device. UIAutomation must be enabled in Settings. *
But I have Enabled Settings->Developer->Enable Automation UI in device. iOS version:8.1.2 and 8.0.1 jail broken.
int main(int argc, char **argv, char **envp)
{
#autoreleasepool {
#try
{
[[UIATarget localTarget].frontMostApp isVisible];
if ([UIATarget localTarget].springboard.pid == nil)
{
return 0;
}
}
#catch (NSException *exception)
{
NSLog(#"*** exception %# ***",exception);
return 0;
}
}
}
I have seen this link https://github.com/kif-framework/KIF/issues/707 and some Apple reference documents for UIATarget frontMostApp but I found no solution so far.
Is this problem with the iOS version ? How can I solve this? Any help is appreciated.
Your UIAuomation settings plist will be separately created for each application in case of tweaks.
Enable it in /private/var/mobile/Containers/Data/Application/XXXXXXXX-ACAB-4FC9-AE3E-XXXXXX/Library/Preferences/com.apple.UIAutomation.plist and Reboot your device to get rid of this error.
So I'm not sure if this is an issue with Unity or with the Facebook Unity SDK, or something I might be doing? It only started appearing recently, it was working perfectly fine up until I had to update Unity for iOS9 font issues.
The point at which it crashes in Xcode is:
+ (instancetype)instanceWithRequestID:(int)requestID
{
FBUnitySDKDelegate *instance = [[FBUnitySDKDelegate alloc] init];
instance->_requestID = requestID;
[g_instances addObject:instance]; // Breaks on this line. instance is nil
return instance;
}
And the code I am using for the AppRequest is
public void RequestLivesFromFriends(string[] friendIds)
{
if(!FB.IsLoggedIn)
{
LoginToFacebook ();
return;
}
FB.AppRequest(
"Please send me a life!",
Facebook.Unity.OGActionType.ASKFOR,
livesIdValue,
friendIds,
"RequestLife",
"Request a life from your friends",
requestLifeCallback
);
}
Is there currently an issue with the SDK's? Or am I just doing something wrong?
Well, I found the solution myself in the end.
I had -fno-objc-arc set as the Compiler Flag on FBUnitySDKDelegate.m
Apparently, having that on with the more recent versions of the SDK (or maybe something else was causing it, I'm not exactly sure) causes the NSMutableArray g_instances to be converted to an NSString. So when the code tries to add the FBUnitySDKDelegate object 'instance' to g_instances, it is trying to call addObject on an NSString, passing in an FBUnitySDKDelegate, which obviously doesn't work.
So yeah, if you have this problem, check for Compiler Flags on the file.
So, I had an idea to catch unanticipated exceptions in main and try to cleanup and exit gracefully:
int main(int argc, char *argv[])
{
#autoreleasepool
{
//return UIApplicationMain(argc, argv, nil, NSStringFromClass([GRWAppDelegate class]));
#try
{
int retVal = UIApplicationMain(argc, argv, nil, NSStringFromClass([GRWAppDelegate class]));
return retVal;
}
#catch (NSException *exception)
{
[Utilities setPreferencesDefaults];
}
}
}
This does catch exceptions and updates the preference defaults.
I then thought, why exit at all, just cleanup and relaunch, so I wrapped everything in a while loop:
int main(int argc, char *argv[])
{
while (YES)
{
#autoreleasepool
{
...
}
}
}
Of course I wouldn't be here if that actually worked. Problem is, once it again executes
retVal = UIApplicationMain(argc, argv, nil, NSStringFromClass([GRWAppDelegate class]));
it immediately throws a new exception:
Assertion failure in void UIApplicationInstantiateSingleton(Class)(), /SourceCache/UIKit/UIKit-2380.17/UIApplication.m:2037
NSInternalInconsistencyException
#"There can only be one UIApplication instance."
Makes sense, so is there a way I can discard the existing singleton and replace it with a new one? (although I guess it's not really a singleton if I can)
Purpose is, I don't ever want the app to crash giving a bad user experience. Even if their state isn't completely restored, I would think that would still be better than just unexpectedly exiting.
I can try to handle possible expected exceptions, but this is to try to catch things that I haven't foreseen.
This should really only catch VERY unusual circumstances, so if it can't be done it's not that big of a deal, but I was wondering how best to deal with this type of situation.
That won't work because the exception mechanism does not cleanup properly when thrown across stack frames. Since you are catching the exception in main the exception has crossed several stack frames.
Apple explicitly states that exceptions are only to be used for unrecoverable programming errors.
See SO answer by bbum:
"Any exception that passes through system framework code will leave said framework in an undefined state.. Catching said exceptions and trying to recover from it will lead to memory leaks, undefined behaviour and crashing."
Also by bbum.
From Apple docs:
Important: You should reserve the use of exceptions for programming or unexpected runtime errors such as out-of-bounds collection access, attempts to mutate immutable objects, sending an invalid message, and losing the connection to the window server. You usually take care of these sorts of errors with exceptions when an application is being created rather than at runtime.
You can create a top level exception handler and set it as your default exception handler when you app first launches. The best place to do this would be the AppDelegate's applicationDidFinishLaunching: method.
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
NSSetUncaughtExceptionHandler(&exceptionHandler);
// some UI configuration here..
return YES;
}
void exceptionHandler(NSException *exception)
{
[Utilities setPreferencesDefaults];
// You can also write exception message into a log file as well.
NSLog(#"Stack trace: %#", [exception callStackReturnAddresses]);
}
The iOS application i took over has a way of handling NSExceptions differently than what i've seen before and wanted to know why it isn't working now.
In the main.m file the old developer has this logic in it:
int main(int argc, char *argv[])
{
#autoreleasepool {
int retVal = 0;
#try {
retVal = UIApplicationMain(argc, argv, nil, NSStringFromClass([AppDelegate class]));
}
#catch (NSException *exception) {
//
//Logic to save Exception in DataStore
//
NSLog(#"Exception - %#",[exception description]);
exit(EXIT_FAILURE);
}
return retVal;
}
}
When the app would get launched again you would receive a prompt to send the exception to us and if you confirmed it would sent it to our server.
I recently pushed an update more optimized for iOS 7 of the application and noticed that i don't get any of these error reports from crashed apps anymore.
So i tested it via the following code which i know gets called:
NSArray *array = [NSArray new];
id object = [array objectAtIndex:4];
To which i receive this:
2014-05-12 14:55:57.575 APPNAME[17989:60b] *** Terminating app due to uncaught exception 'NSRangeException', reason: '*** -[__NSArrayI objectAtIndex:]: index 4 beyond bounds for empty array'
*** First throw call stack:
(0x18ab0b09c 0x196a89d78 0x18aa0c680 0x10007f498 0x18d9e02c0 0x18da997d0 0x18da996cc 0x18da98ba0
0x18da98840 0x18da98564 0x18da984e4 0x18d9dad78 0x18d5d70cc 0x18d5d1c94 0x18d5d1b4c
0x18d5d13d4 0x18d5d1178 0x18d5caa30 0x18aacb7e0 0x18aac8a68 0x18aac8df4 0x18aa09b38
0x19042f830 0x18da480e8 0x100036b98 0x197073aa0)
libc++abi.dylib: terminating with uncaught exception of type NSException
As you can see the exception is not being logged or saved but rather it is completely uncaught.
2 Questions:
Is this bad practice? I don't think it is but i am a jr. developer and not really sure and if there is a better way to do this w/o third party service?
Do you know of any changes in iOS 7 that would affect this (UNTOUCHED) Logic?
Is this bad practice? I don't think it is but i am a jr. developer and
not really sure and if there is a better way to do this w/o third
party service?
Yes, it is bad practice. UIApplicationMain() is a black hole; once control is passed to that function, it is unlikely that any code beyond that call in main() will ever be invoked again.
It is also a naive implementation; it logs less information to stdout than the normal exception handling mechanism (which is the output you are seeing). It hides information that would be useful to debugging and would also, likely, bypass the standard crash reporting mechanism.
Note that there is also a global unhandled exception handling hook. I wouldn't recommend using it, but it does exist.
Do you know of any changes in iOS 7 that would affect this (UNTOUCHED)
Logic?
Not off hand, but I haven't looked. Again, though, UIApplicationMain() is a black hole; it doesn't typically return ever.
According to Exception Programming Topics you can use NSSetUncaughtExceptionHandler function to handle uncaught exceptions. Try the code below:
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
NSSetUncaughtExceptionHandler(handleUncaughtException);
#throw [NSException exceptionWithName:NSGenericException
reason:#"Test uncaught exception handling"
userInfo:nil];
return YES;
}
void handleUncaughtException(NSException *exception)
{
NSLog(#"Exception - %#",[exception description]);
exit(EXIT_FAILURE);
}
I have c++ code in my iOS project, and in the parts wrapped with extern "C" {}, exceptions that I throw are not being caught, and crashes the application.
this happens only when project deployment target is set to 7.0 and above.
happens only in the xcode simulator, works well on devices with iOS7.
for example, this code crashes and doesn't get to the catch clause, when calling func1():
extern "C" {
bool func2(){
throw OpenSSLException("abc"); //crashes here
}
bool func1(){
try {
func2(); //calls func2 and crashes
} catch (OpenSSLException& e) {
//we never get here
}
}
}
crashes with: libc++abi.dylib: terminating with uncaught exception of type OpenSSLException: abc
Could be related to the fact that I'm catching c++ exceptions from a code wrapped with extern C? This has worked until now
tried all conventions of throwing and catching the exceptions as : catch (OpenSSLException& e) , catch (...), catch (std::exception), and this has nothing to do with the problem
this code is called from a seperate static library that my project uses (which I compile by myself and than link it)