Skip to content

Commit

Permalink
Catching UIApplicationMain exceptions
Browse files Browse the repository at this point in the history
Implemented a Ti build plugin and extended the module in
order to catch and report the description of exceptions in
UIApplicationMain.
The plugin adds some wrapping code to the main.m file automatically
generated by Titanium, which, when an exception is catch, records  its
description to NSUserDefaults. That value is picked up by HockeyApp at
the next app start, and it’s used for augmenting the crash report.
  • Loading branch information
omorandi committed Jul 29, 2014
1 parent e2c3716 commit 3fe15fc
Show file tree
Hide file tree
Showing 7 changed files with 111 additions and 6 deletions.
9 changes: 6 additions & 3 deletions ios/Classes/NlRebelicHockeyappModule.h
Expand Up @@ -5,9 +5,12 @@
* and licensed under the Apache Public License (version 2)
*/
#import "TiModule.h"
#import <HockeySDK.h>


@interface NlRebelicHockeyappModule : TiModule <BITCrashManagerDelegate>

- (NSString *)applicationLogForCrashManager:(BITCrashManager *)crashManager;

@interface NlRebelicHockeyappModule : TiModule
{
}

@end
23 changes: 22 additions & 1 deletion ios/Classes/NlRebelicHockeyappModule.m
Expand Up @@ -8,7 +8,9 @@
#import "TiBase.h"
#import "TiHost.h"
#import "TiUtils.h"
#import <HockeySDK.h>

extern NSString * const TI_APPLICATION_ID;
static NSString * appCrashInfoKey;

@implementation NlRebelicHockeyappModule

Expand All @@ -35,6 +37,8 @@ -(void)startup
[super startup];

NSLog(@"[INFO] %@ loaded",self);

appCrashInfoKey = [NSString stringWithFormat:@"%@.%@", TI_APPLICATION_ID, @"crash_info"];
}

-(void)shutdown:(id)sender
Expand Down Expand Up @@ -92,8 +96,10 @@ -(void)start:(id)appId
ENSURE_SINGLE_ARG(appId, NSString);

[[BITHockeyManager sharedHockeyManager] configureWithIdentifier:appId];
[[BITHockeyManager sharedHockeyManager].crashManager setDelegate:self];
[[BITHockeyManager sharedHockeyManager] startManager];
[[BITHockeyManager sharedHockeyManager].authenticator authenticateInstallation];

}

- (void)showFeedbackListView:(id)args
Expand All @@ -106,4 +112,19 @@ - (void)showFeedbackComposeView:(id)args
[[BITHockeyManager sharedHockeyManager].feedbackManager showFeedbackComposeView];
}


- (NSString *)applicationLogForCrashManager:(BITCrashManager *)crashManager
{

NSString *appLog = [[NSUserDefaults standardUserDefaults] valueForKey:appCrashInfoKey];
if (appLog == nil) {
return nil;
}
NSLog(@"appLog: %@", appLog);
[[NSUserDefaults standardUserDefaults] removeObjectForKey:appCrashInfoKey];
[[NSUserDefaults standardUserDefaults] synchronize];

return appLog;
}

@end
18 changes: 18 additions & 0 deletions ios/README.md
Expand Up @@ -35,3 +35,21 @@ To show the modal feedback list view on iOS:
```javascript
hockeyapp.showFeedbackListView();
```


## Getting UIApplicationMain exceptions

* Copy the Titanium build plugin named `hockeyapp` from the `plugin` directory to your Ti project directory under the
`plugins` folder.
* Add the following to `tiapp.xml`

```xml
<plugins>
<plugin>hockeyapp</plugin>
</plugins>
```

This will modify the `main.m` file generated by Titanium in order to catch NSExceptions involving UIApplicationMain.
The exception description is persisted in NSUserDefaults and used by the hockeyapp module for sending additional info
on the actual cause of the crash.

2 changes: 1 addition & 1 deletion ios/manifest
Expand Up @@ -2,7 +2,7 @@
# this is your module manifest and used by Titanium
# during compilation, packaging, distribution, etc.
#
version: 0.2
version: 0.3
apiversion: 2
description: HockeyApp module
author: Timan Rebel (Rebelcorp)
Expand Down
1 change: 1 addition & 0 deletions ios/plugin/hockeyapp/1.0/hooks/.tern-port
@@ -0,0 +1 @@
54911
62 changes: 62 additions & 0 deletions ios/plugin/hockeyapp/1.0/hooks/plugin.js
@@ -0,0 +1,62 @@
exports.cliVersion = '>=3.X';

var fs = require('fs');
var path = require('path');


var UIApplicationMainStatement = ' int retVal = UIApplicationMain(argc, argv, nil, @"TiApp");';

var replacementRows = [
' int retVal = 0;',
' @try {\n',
' retVal = UIApplicationMain(argc, argv, nil, @"TiApp");',
' }',
' @catch (NSException *e) {',
' //we save the exception description to NSUserDefaults, this value will be picked up at next app start by hockeyapp',
' [[NSUserDefaults standardUserDefaults] setObject:e.description forKey:[NSString stringWithFormat:@"%@.%@", TI_APPLICATION_ID, @"crash_info"]];',
' [[NSUserDefaults standardUserDefaults] synchronize];',
' //we need to crash here anyway for hockeyapp to notice',
' @throw e;',
' }'].join("\n");



exports.init = function(logger, config, cli, appc) {
if (cli.argv.platform != 'android') {

cli.addHook('build.pre.compile', function(build, finished) {
logger.info('Replacing the populateIosFiles() function in the iOS Build plugin');
var populateIosFiles = build.populateIosFiles;
if (populateIosFiles) {
build.populateIosFiles = function() {

var result = populateIosFiles.call(build);

var mainFile = path.join(this.buildDir, 'main.m');
var mainContents = fs.readFileSync(mainFile);
if (!mainContents) {
logger.error('Cannot find main.m in build dir');
process.exit(1);
} else {
var mainContentsStr = mainContents.toString();
var uiAppMainInvocationMatch = mainContentsStr.match(/UIApplicationMain\(argc,\sargv,\snil,\s\@\"TiApp\"\)/g);
if (!uiAppMainInvocationMatch) {
logger.error('Cannot find UIApplicationMain invocation');
process.exit(1);
}
//we inject the AppConnectUIApplication UIApplication subclass
logger.info('Modifying UIApplicationMain invocation in the generated main.m');
mainContentsStr = mainContentsStr.replace(UIApplicationMainStatement, replacementRows);
fs.writeFileSync(mainFile, mainContentsStr);
}

return result;
};
} else {
logger.error('Can\'t inject hook into ios build');
process.exit(1);
}
finished();
});
}
};
2 changes: 1 addition & 1 deletion ios/titanium.xcconfig
Expand Up @@ -4,7 +4,7 @@
// OF YOUR TITANIUM SDK YOU'RE BUILDING FOR
//
//
TITANIUM_SDK_VERSION = 3.2.2.GA
TITANIUM_SDK_VERSION = 3.2.3.GA


//
Expand Down

0 comments on commit 3fe15fc

Please sign in to comment.