Merge branch 'develop' into feature/#19-verifyBetaUsers

Conflicts:
	Support/HockeySDK.xcodeproj/project.pbxproj
This commit is contained in:
Stephan Diederich 2013-09-06 13:40:39 +02:00
commit b25796742e
35 changed files with 1034 additions and 557 deletions

View File

@ -31,10 +31,30 @@
#import <UIKit/UIKit.h>
#ifndef __IPHONE_6_1
#define __IPHONE_6_1 60100
#endif
/**
* Header style depending on the iOS version
*/
typedef NS_ENUM(NSUInteger, BITAppStoreHeaderStyle) {
/**
* Default is iOS 6 style
*/
BITAppStoreHeaderStyleDefault = 0,
/**
* Draw header in the iOS 7 style
*/
BITAppStoreHeaderStyleOS7 = 1
};
@interface BITAppStoreHeader : UIView
@property (nonatomic, copy) NSString *headerText;
@property (nonatomic, copy) NSString *subHeaderText;
@property (nonatomic, strong) UIImage *iconImage;
@property (nonatomic, assign) BITAppStoreHeaderStyle style;
@end

View File

@ -36,7 +36,8 @@
#define kLightGrayColor BIT_RGBCOLOR(235, 235, 235)
#define kDarkGrayColor BIT_RGBCOLOR(186, 186, 186)
#define kWhiteBackgroundColor BIT_RGBCOLOR(245, 245, 245)
#define kWhiteBackgroundColorDefault BIT_RGBCOLOR(245, 245, 245)
#define kWhiteBackgroundColorOS7 BIT_RGBCOLOR(255, 255, 255)
#define kImageHeight 72
#define kImageBorderRadius 12
#define kImageLeftMargin 14
@ -54,7 +55,8 @@
- (id)initWithFrame:(CGRect)frame {
if ((self = [super initWithFrame:frame])) {
self.autoresizingMask = UIViewAutoresizingFlexibleWidth;
self.backgroundColor = kWhiteBackgroundColor;
self.backgroundColor = kWhiteBackgroundColorDefault;
self.style = BITAppStoreHeaderStyleDefault;
}
return self;
}
@ -66,20 +68,35 @@
CGRect bounds = self.bounds;
CGContextRef context = UIGraphicsGetCurrentContext();
// draw the gradient
NSArray *colors = [NSArray arrayWithObjects:(id)kDarkGrayColor.CGColor, (id)kLightGrayColor.CGColor, nil];
CGGradientRef gradient = CGGradientCreateWithColors(CGColorGetColorSpace((__bridge CGColorRef)[colors objectAtIndex:0]), (__bridge CFArrayRef)colors, (CGFloat[2]){0, 1});
CGPoint top = CGPointMake(CGRectGetMidX(bounds), bounds.size.height - 3);
CGPoint bottom = CGPointMake(CGRectGetMidX(bounds), CGRectGetMaxY(bounds));
CGContextDrawLinearGradient(context, gradient, top, bottom, 0);
CGGradientRelease(gradient);
if (self.style == BITAppStoreHeaderStyleDefault) {
// draw the gradient
NSArray *colors = [NSArray arrayWithObjects:(id)kDarkGrayColor.CGColor, (id)kLightGrayColor.CGColor, nil];
CGGradientRef gradient = CGGradientCreateWithColors(CGColorGetColorSpace((__bridge CGColorRef)[colors objectAtIndex:0]), (__bridge CFArrayRef)colors, (CGFloat[2]){0, 1});
CGPoint top = CGPointMake(CGRectGetMidX(bounds), bounds.size.height - 3);
CGPoint bottom = CGPointMake(CGRectGetMidX(bounds), CGRectGetMaxY(bounds));
CGContextDrawLinearGradient(context, gradient, top, bottom, 0);
CGGradientRelease(gradient);
} else {
// draw the line
CGContextRef ctx = UIGraphicsGetCurrentContext();
CGContextSetLineWidth(ctx, 1.0);
CGContextSetStrokeColorWithColor(ctx, kDarkGrayColor.CGColor);
CGContextMoveToPoint(ctx, 0, CGRectGetMaxY(bounds));
CGContextAddLineToPoint( ctx, CGRectGetMaxX(bounds), CGRectGetMaxY(bounds));
CGContextStrokePath(ctx);
}
// icon
[_iconImage drawAtPoint:CGPointMake(kImageLeftMargin, kImageTopMargin)];
[super drawRect:rect];
}
- (void)layoutSubviews {
if (self.style == BITAppStoreHeaderStyleOS7)
self.backgroundColor = kWhiteBackgroundColorOS7;
[super layoutSubviews];
CGFloat globalWidth = self.frame.size.width;

View File

@ -33,13 +33,23 @@
#import "BITHockeyBaseManager.h"
// hockey crash manager status
typedef enum {
/**
* Crash Manager status
*/
typedef NS_ENUM(NSUInteger, BITCrashManagerStatus) {
/**
* Crash reporting is disabled
*/
BITCrashManagerStatusDisabled = 0,
/**
* User is asked each time before sending
*/
BITCrashManagerStatusAlwaysAsk = 1,
/**
* Each crash report is send automatically
*/
BITCrashManagerStatusAutoSend = 2
} BITCrashManagerStatus;
extern NSString *const kBITCrashManagerStatus;
};
@protocol BITCrashManagerDelegate;
@ -72,8 +82,10 @@ extern NSString *const kBITCrashManagerStatus;
very slow.
More background information on this topic can be found in the following blog post by Landon Fuller, the
developer of [PLCrashReporter](https://code.google.com/p/plcrashreporter/), about writing reliable and
developer of [PLCrashReporter](https://www.plcrashreporter.org), about writing reliable and
safe crash reporting: [Reliable Crash Reporting](http://goo.gl/WvTBR)
@warning If you start the app with the Xcode debugger attached, detecting crashes will _NOT_ be enabled!
*/
@interface BITCrashManager : BITHockeyBaseManager
@ -97,11 +109,7 @@ extern NSString *const kBITCrashManagerStatus;
Defines if the crash reporting feature should be disabled, ask the user before
sending each crash report or send crash reportings automatically without
asking.. This must be assigned one of the following:
- `BITCrashManagerStatusDisabled`: Crash reporting is disabled
- `BITCrashManagerStatusAlwaysAsk`: User is asked each time before sending
- `BITCrashManagerStatusAutoSend`: Each crash report is send automatically
asking.
The default value is `BITCrashManagerStatusAlwaysAsk`. You can allow the user
to switch from `BITCrashManagerStatusAlwaysAsk` to
@ -121,6 +129,27 @@ extern NSString *const kBITCrashManagerStatus;
@property (nonatomic, assign) BITCrashManagerStatus crashManagerStatus;
/**
* Trap fatal signals via a Mach exception server.
*
* By default the SDK is using the safe and proven in-process BSD Signals for catching crashes.
* This option provides an option to enable catching fatal signals via a Mach exception server
* instead.
*
* We strongly advice _NOT_ to enable Mach exception handler in release versions of your apps!
*
* Default: _NO_
*
* @warning The Mach exception handler executes in-process, and will interfere with debuggers when
* they attempt to suspend all active threads (which will include the Mach exception handler).
* Mach-based handling should _NOT_ be used when a debugger is attached. The SDK will not
* enabled catching exceptions if the app is started with the debugger running. If you attach
* the debugger during runtime, this may cause issues the Mach exception handler is enabled!
* @see isDebuggerAttached
*/
@property (nonatomic, assign, getter=isMachExceptionHandlerEnabled) BOOL enableMachExceptionHandler;
/**
Flag that determines if an "Always" option should be shown
@ -173,4 +202,19 @@ extern NSString *const kBITCrashManagerStatus;
*/
@property (nonatomic, readonly) NSTimeInterval timeintervalCrashInLastSessionOccured;
///-----------------------------------------------------------------------------
/// @name Helper
///-----------------------------------------------------------------------------
/**
* Detect if a debugger is attached to the app process
*
* This is only invoked once on app startup and can not detect if the debugger is being
* attached during runtime!
*
* @return BOOL if the debugger is attached on app startup
*/
- (BOOL)isDebuggerAttached;
@end

View File

@ -37,7 +37,6 @@
#import "BITHockeyManagerPrivate.h"
#import "BITHockeyBaseManagerPrivate.h"
#import "BITCrashManagerPrivate.h"
#import "BITCrashReportTextFormatter.h"
#include <sys/sysctl.h>
@ -79,6 +78,7 @@ NSString *const kBITCrashManagerStatus = @"BITCrashManagerStatus";
BOOL _sendingInProgress;
BOOL _isSetup;
BITPLCrashReporter *_plCrashReporter;
NSUncaughtExceptionHandler *_exceptionHandler;
}
@ -89,6 +89,7 @@ NSString *const kBITCrashManagerStatus = @"BITCrashManagerStatus";
_showAlwaysButton = NO;
_isSetup = NO;
_plCrashReporter = nil;
_exceptionHandler = nil;
_crashIdenticalCurrentVersion = YES;
@ -163,6 +164,11 @@ NSString *const kBITCrashManagerStatus = @"BITCrashManagerStatus";
#pragma mark - Private
/**
* Save all settings
*
* This saves the list of approved crash reports
*/
- (void)saveSettings {
NSString *errorString = nil;
@ -180,6 +186,11 @@ NSString *const kBITCrashManagerStatus = @"BITCrashManagerStatus";
}
}
/**
* Load all settings
*
* This contains the list of approved crash reports
*/
- (void)loadSettings {
NSString *errorString = nil;
NSPropertyListFormat format;
@ -202,6 +213,9 @@ NSString *const kBITCrashManagerStatus = @"BITCrashManagerStatus";
}
}
/**
* Remove all crash reports and stored meta data for each from the file system and keychain
*/
- (void)cleanCrashReports {
NSError *error = NULL;
@ -218,6 +232,16 @@ NSString *const kBITCrashManagerStatus = @"BITCrashManagerStatus";
[self saveSettings];
}
/**
* Extract all app sepcific UUIDs from the crash reports
*
* This allows us to send the UUIDs in the XML construct to the server, so the server does not need to parse the crash report for this data.
* The app specific UUIDs help to identify which dSYMs are needed to symbolicate this crash report.
*
* @param report The crash report from PLCrashReporter
*
* @return XML structure with the app sepcific UUIDs
*/
- (NSString *) extractAppUUIDs:(BITPLCrashReport *)report {
NSMutableString *uuidString = [NSMutableString string];
NSArray *uuidArray = [BITCrashReportTextFormatter arrayOfAppUUIDsForCrashReport:report];
@ -235,6 +259,11 @@ NSString *const kBITCrashManagerStatus = @"BITCrashManagerStatus";
return uuidString;
}
/**
* Get the userID from the delegate which should be stored with the crash report
*
* @return The userID value
*/
- (NSString *)userIDForCrashReport {
NSString *userID = @"";
@ -248,12 +277,15 @@ NSString *const kBITCrashManagerStatus = @"BITCrashManagerStatus";
return userID;
}
/**
* Get the userName from the delegate which should be stored with the crash report
*
* @return The userName value
*/
- (NSString *)userNameForCrashReport {
NSString *username = @"";
if (self.delegate && [self.delegate respondsToSelector:@selector(userNameForCrashManager:)]) {
if (!self.isAppStoreEnvironment)
NSLog(@"[HockeySDK] DEPRECATED: Please use BITHockeyManagerDelegate's userNameForHockeyManager:componentManager: or userIDForHockeyManager:componentManager: instead.");
username = [self.delegate userNameForCrashManager:self] ?: @"";
}
if ([BITHockeyManager sharedHockeyManager].delegate &&
@ -266,12 +298,15 @@ NSString *const kBITCrashManagerStatus = @"BITCrashManagerStatus";
return username;
}
/**
* Get the userEmail from the delegate which should be stored with the crash report
*
* @return The userEmail value
*/
- (NSString *)userEmailForCrashReport {
NSString *useremail = @"";
if (self.delegate && [self.delegate respondsToSelector:@selector(userEmailForCrashManager:)]) {
if (!self.isAppStoreEnvironment)
NSLog(@"[HockeySDK] DEPRECATED: Please use BITHockeyManagerDelegate's userEmailForHockeyManager:componentManager: instead.");
useremail = [self.delegate userEmailForCrashManager:self] ?: @"";
}
if ([BITHockeyManager sharedHockeyManager].delegate &&
@ -284,13 +319,55 @@ NSString *const kBITCrashManagerStatus = @"BITCrashManagerStatus";
return useremail;
}
#pragma mark - Public
/**
* Check if the debugger is attached
*
* Taken from https://github.com/plausiblelabs/plcrashreporter/blob/2dd862ce049e6f43feb355308dfc710f3af54c4d/Source/Crash%20Demo/main.m#L96
*
* @return `YES` if the debugger is attached to the current process, `NO` otherwise
*/
- (BOOL)isDebuggerAttached {
static BOOL debuggerIsAttached = NO;
static dispatch_once_t debuggerPredicate;
dispatch_once(&debuggerPredicate, ^{
struct kinfo_proc info;
size_t info_size = sizeof(info);
int name[4];
name[0] = CTL_KERN;
name[1] = KERN_PROC;
name[2] = KERN_PROC_PID;
name[3] = getpid();
if (sysctl(name, 4, &info, &info_size, NULL, 0) == -1) {
NSLog(@"[HockeySDK] ERROR: Checking for a running debugger via sysctl() failed: %s", strerror(errno));
debuggerIsAttached = false;
}
if (!debuggerIsAttached && (info.kp_proc.p_flag & P_TRACED) != 0)
debuggerIsAttached = true;
});
return debuggerIsAttached;
}
#pragma mark - PLCrashReporter
// Called to handle a pending crash report.
/**
* Process new crash reports provided by PLCrashReporter
*
* Parse the new crash report and gather additional meta data from the app which will be stored along the crash report
*/
- (void) handleCrashReport {
BITPLCrashReporter *crashReporter = [BITPLCrashReporter sharedReporter];
NSError *error = NULL;
if (!_plCrashReporter) return;
[self loadSettings];
// check if the next call ran successfully the last time
@ -301,7 +378,7 @@ NSString *const kBITCrashManagerStatus = @"BITCrashManagerStatus";
[self saveSettings];
// Try loading the crash report
NSData *crashData = [[NSData alloc] initWithData:[crashReporter loadPendingCrashReportDataAndReturnError: &error]];
NSData *crashData = [[NSData alloc] initWithData:[_plCrashReporter loadPendingCrashReportDataAndReturnError: &error]];
NSString *cacheFilename = [NSString stringWithFormat: @"%.0f", [NSDate timeIntervalSinceReferenceDate]];
@ -311,35 +388,39 @@ NSString *const kBITCrashManagerStatus = @"BITCrashManagerStatus";
// get the startup timestamp from the crash report, and the file timestamp to calculate the timeinterval when the crash happened after startup
BITPLCrashReport *report = [[BITPLCrashReport alloc] initWithData:crashData error:&error];
if ([report.processInfo respondsToSelector:@selector(processStartTime)]) {
if (report.systemInfo.timestamp && report.processInfo.processStartTime) {
_timeintervalCrashInLastSessionOccured = [report.systemInfo.timestamp timeIntervalSinceDate:report.processInfo.processStartTime];
}
}
[crashData writeToFile:[_crashesDir stringByAppendingPathComponent: cacheFilename] atomically:YES];
// write the meta file
NSMutableDictionary *metaDict = [NSMutableDictionary dictionaryWithCapacity:4];
NSString *applicationLog = @"";
NSString *errorString = nil;
[self addStringValueToKeychain:[self userNameForCrashReport] forKey:[NSString stringWithFormat:@"%@.%@", cacheFilename, kBITCrashMetaUserName]];
[self addStringValueToKeychain:[self userEmailForCrashReport] forKey:[NSString stringWithFormat:@"%@.%@", cacheFilename, kBITCrashMetaUserEmail]];
[self addStringValueToKeychain:[self userIDForCrashReport] forKey:[NSString stringWithFormat:@"%@.%@", cacheFilename, kBITCrashMetaUserID]];
if (self.delegate != nil && [self.delegate respondsToSelector:@selector(applicationLogForCrashManager:)]) {
applicationLog = [self.delegate applicationLogForCrashManager:self] ?: @"";
}
[metaDict setObject:applicationLog forKey:kBITCrashMetaApplicationLog];
NSData *plist = [NSPropertyListSerialization dataFromPropertyList:(id)metaDict
format:NSPropertyListBinaryFormat_v1_0
errorDescription:&errorString];
if (plist) {
[plist writeToFile:[NSString stringWithFormat:@"%@.meta", [_crashesDir stringByAppendingPathComponent: cacheFilename]] atomically:YES];
if (report == nil) {
BITHockeyLog(@"WARNING: Could not parse crash report");
} else {
BITHockeyLog(@"ERROR: Writing crash meta data failed. %@", error);
if ([report.processInfo respondsToSelector:@selector(processStartTime)]) {
if (report.systemInfo.timestamp && report.processInfo.processStartTime) {
_timeintervalCrashInLastSessionOccured = [report.systemInfo.timestamp timeIntervalSinceDate:report.processInfo.processStartTime];
}
}
[crashData writeToFile:[_crashesDir stringByAppendingPathComponent: cacheFilename] atomically:YES];
// write the meta file
NSMutableDictionary *metaDict = [NSMutableDictionary dictionaryWithCapacity:4];
NSString *applicationLog = @"";
NSString *errorString = nil;
[self addStringValueToKeychain:[self userNameForCrashReport] forKey:[NSString stringWithFormat:@"%@.%@", cacheFilename, kBITCrashMetaUserName]];
[self addStringValueToKeychain:[self userEmailForCrashReport] forKey:[NSString stringWithFormat:@"%@.%@", cacheFilename, kBITCrashMetaUserEmail]];
[self addStringValueToKeychain:[self userIDForCrashReport] forKey:[NSString stringWithFormat:@"%@.%@", cacheFilename, kBITCrashMetaUserID]];
if (self.delegate != nil && [self.delegate respondsToSelector:@selector(applicationLogForCrashManager:)]) {
applicationLog = [self.delegate applicationLogForCrashManager:self] ?: @"";
}
[metaDict setObject:applicationLog forKey:kBITCrashMetaApplicationLog];
NSData *plist = [NSPropertyListSerialization dataFromPropertyList:(id)metaDict
format:NSPropertyListBinaryFormat_v1_0
errorDescription:&errorString];
if (plist) {
[plist writeToFile:[NSString stringWithFormat:@"%@.meta", [_crashesDir stringByAppendingPathComponent: cacheFilename]] atomically:YES];
} else {
BITHockeyLog(@"ERROR: Writing crash meta data failed. %@", error);
}
}
}
}
@ -352,9 +433,14 @@ NSString *const kBITCrashManagerStatus = @"BITCrashManagerStatus";
[self saveSettings];
[crashReporter purgePendingCrashReport];
[_plCrashReporter purgePendingCrashReport];
}
/**
* Check if there are any crash reports available which the user did not approve yet
*
* @return `YES` if there are crash reports pending that are not approved, `NO` otherwise
*/
- (BOOL)hasNonApprovedCrashReports {
if (!_approvedCrashReports || [_approvedCrashReports count] == 0) return YES;
@ -367,6 +453,11 @@ NSString *const kBITCrashManagerStatus = @"BITCrashManagerStatus";
return NO;
}
/**
* Check if there are any new crash reports that are not yet processed
*
* @return `YES` if ther eis at least one new crash report found, `NO` otherwise
*/
- (BOOL)hasPendingCrashReport {
if (_crashManagerStatus == BITCrashManagerStatusDisabled) return NO;
@ -406,7 +497,13 @@ NSString *const kBITCrashManagerStatus = @"BITCrashManagerStatus";
#pragma mark - Crash Report Processing
// slightly delayed startup processing, so we don't keep the first runloop on startup busy for too long
/**
* Delayed startup processing for everything that does not to be done in the app startup runloop
*
* - Checks if there is another exception handler installed that may block ours
* - Present UI if the user has to approve new crash reports
* - Send pending approved crash reports
*/
- (void)invokeDelayedProcessing {
BITHockeyLog(@"INFO: Start delayed CrashManager processing");
@ -463,70 +560,93 @@ NSString *const kBITCrashManagerStatus = @"BITCrashManagerStatus";
}
}
// begin the startup process
/**
* Main startup sequence initializing PLCrashReporter if it wasn't disabled
*/
- (void)startManager {
if (_crashManagerStatus == BITCrashManagerStatusDisabled) return;
if (!_isSetup) {
BITPLCrashReporter *crashReporter = [BITPLCrashReporter sharedReporter];
NSError *error = NULL;
// Check if we previously crashed
if ([crashReporter hasPendingCrashReport]) {
_didCrashInLastSession = YES;
[self handleCrashReport];
}
// PLCrashReporter is throwing an NSException if it is being enabled again
// even though it already is enabled
@try {
// Multiple exception handlers can be set, but we can only query the top level error handler (uncaught exception handler).
//
// To check if PLCrashReporter's error handler is successfully added, we compare the top
// level one that is set before and the one after PLCrashReporter sets up its own.
//
// With delayed processing we can then check if another error handler was set up afterwards
// and can show a debug warning log message, that the dev has to make sure the "newer" error handler
// doesn't exit the process itself, because then all subsequent handlers would never be invoked.
//
// Note: ANY error handler setup BEFORE HockeySDK initialization will not be processed!
// get the current top level error handler
NSUncaughtExceptionHandler *initialHandler = NSGetUncaughtExceptionHandler();
// Enable the Crash Reporter
if (![crashReporter enableCrashReporterAndReturnError: &error])
NSLog(@"[HockeySDK] WARNING: Could not enable crash reporter: %@", [error localizedDescription]);
// get the new current top level error handler, which should now be the one from PLCrashReporter
NSUncaughtExceptionHandler *currentHandler = NSGetUncaughtExceptionHandler();
// do we have a new top level error handler? then we were successful
if (currentHandler && currentHandler != initialHandler) {
_exceptionHandler = currentHandler;
static dispatch_once_t plcrPredicate;
dispatch_once(&plcrPredicate, ^{
/* Configure our reporter */
BITHockeyLog(@"INFO: Exception handler successfully initialized.");
} else {
// this should never happen, theoretically only if NSSetUncaugtExceptionHandler() has some internal issues
NSLog(@"[HockeySDK] ERROR: Exception handler could not be set. Make sure there is no other exception handler set up!");
PLCrashReporterSignalHandlerType signalHandlerType = PLCrashReporterSignalHandlerTypeBSD;
if (self.isMachExceptionHandlerEnabled) {
signalHandlerType = PLCrashReporterSignalHandlerTypeMach;
}
}
@catch (NSException * e) {
NSLog(@"[HockeySDK] WARNING: %@", [e reason]);
}
_isSetup = YES;
BITPLCrashReporterConfig *config = [[BITPLCrashReporterConfig alloc] initWithSignalHandlerType: signalHandlerType
symbolicationStrategy: PLCrashReporterSymbolicationStrategyAll];
_plCrashReporter = [[BITPLCrashReporter alloc] initWithConfiguration: config];
// Check if we previously crashed
if ([_plCrashReporter hasPendingCrashReport]) {
_didCrashInLastSession = YES;
[self handleCrashReport];
}
// The actual signal and mach handlers are only registered when invoking `enableCrashReporterAndReturnError`
// So it is safe enough to only disable the following part when a debugger is attached no matter which
// signal handler type is set
// We only check for this if we are not in the App Store environment
BOOL debuggerIsAttached = NO;
if (![self isAppStoreEnvironment]) {
if ([self isDebuggerAttached]) {
debuggerIsAttached = YES;
NSLog(@"[HockeySDK] WARNING: Detecting crashes is NOT enabled due to running the app with a debugger attached.");
}
}
if (!debuggerIsAttached) {
// Multiple exception handlers can be set, but we can only query the top level error handler (uncaught exception handler).
//
// To check if PLCrashReporter's error handler is successfully added, we compare the top
// level one that is set before and the one after PLCrashReporter sets up its own.
//
// With delayed processing we can then check if another error handler was set up afterwards
// and can show a debug warning log message, that the dev has to make sure the "newer" error handler
// doesn't exit the process itself, because then all subsequent handlers would never be invoked.
//
// Note: ANY error handler setup BEFORE HockeySDK initialization will not be processed!
// get the current top level error handler
NSUncaughtExceptionHandler *initialHandler = NSGetUncaughtExceptionHandler();
// PLCrashReporter may only be initialized once. So make sure the developer
// can't break this
NSError *error = NULL;
// Enable the Crash Reporter
if (![_plCrashReporter enableCrashReporterAndReturnError: &error])
NSLog(@"[HockeySDK] WARNING: Could not enable crash reporter: %@", [error localizedDescription]);
// get the new current top level error handler, which should now be the one from PLCrashReporter
NSUncaughtExceptionHandler *currentHandler = NSGetUncaughtExceptionHandler();
// do we have a new top level error handler? then we were successful
if (currentHandler && currentHandler != initialHandler) {
_exceptionHandler = currentHandler;
BITHockeyLog(@"INFO: Exception handler successfully initialized.");
} else {
// this should never happen, theoretically only if NSSetUncaugtExceptionHandler() has some internal issues
NSLog(@"[HockeySDK] ERROR: Exception handler could not be set. Make sure there is no other exception handler set up!");
}
}
_isSetup = YES;
});
}
[self performSelector:@selector(invokeDelayedProcessing) withObject:nil afterDelay:0.5];
}
/**
* Send all approved crash reports
*
* Gathers all collected data and constructs the XML structure and starts the sending process
*/
- (void)sendCrashReports {
// send it to the next runloop
[self performSelector:@selector(performSendingCrashReports) withObject:nil afterDelay:1.0f];
}
- (void)performSendingCrashReports {
NSError *error = NULL;
NSMutableString *crashes = nil;
@ -670,6 +790,13 @@ NSString *const kBITCrashManagerStatus = @"BITCrashManagerStatus";
#pragma mark - Networking
/**
* Send the XML data to the server
*
* Wraps the XML structure into a POST body and starts sending the data asynchronously
*
* @param xml The XML data that needs to be send to the server
*/
- (void)postXML:(NSString*)xml {
NSMutableURLRequest *request = nil;
NSString *boundary = @"----FOO";

View File

@ -208,8 +208,9 @@ NSInteger binaryImageSort(id binary1, id binary2, void *context);
processPath = report.processInfo.processPath;
/* Remove username from the path */
processPath = [processPath stringByAbbreviatingWithTildeInPath];
if ([[processPath substringToIndex:1] isEqualToString:@"~"])
if ([processPath length] > 0)
processPath = [processPath stringByAbbreviatingWithTildeInPath];
if ([processPath length] > 0 && [[processPath substringToIndex:1] isEqualToString:@"~"])
processPath = [NSString stringWithFormat:@"/Users/USER%@", [processPath substringFromIndex:1]];
}
@ -412,8 +413,10 @@ NSInteger binaryImageSort(id binary1, id binary2, void *context);
}
/* Remove username from the image path */
NSString *imageName = [imageInfo.imageName stringByAbbreviatingWithTildeInPath];
if ([[imageName substringToIndex:1] isEqualToString:@"~"])
NSString *imageName = @"";
if (imageInfo.imageName && [imageInfo.imageName length] > 0)
imageName = [imageInfo.imageName stringByAbbreviatingWithTildeInPath];
if ([imageName length] > 0 && [[imageName substringToIndex:1] isEqualToString:@"~"])
imageName = [NSString stringWithFormat:@"/Users/USER%@", [imageName substringFromIndex:1]];
[text appendFormat: fmt,

View File

@ -13,6 +13,8 @@
#import "BITHockeyHelper.h"
#import "BITFeedbackManagerPrivate.h"
#import "BITHockeyBaseManagerPrivate.h"
@interface BITFeedbackActivity()
@ -87,14 +89,14 @@
- (UIViewController *)activityViewController {
// TODO: return compose controller with activity content added
BITFeedbackComposeViewController *composeViewController = [[BITHockeyManager sharedHockeyManager].feedbackManager feedbackComposeViewController];
BITFeedbackManager *manager = [BITHockeyManager sharedHockeyManager].feedbackManager;
BITFeedbackComposeViewController *composeViewController = [manager feedbackComposeViewController];
composeViewController.delegate = self;
[composeViewController prepareWithItems:_items];
UINavigationController *navController = [[UINavigationController alloc] initWithRootViewController: composeViewController];
navController.navigationBar.barStyle = [[[BITHockeyManager sharedHockeyManager] feedbackManager] barStyle];
navController.navigationBar.tintColor = [[[BITHockeyManager sharedHockeyManager] feedbackManager] tintColor];
navController.modalPresentationStyle = UIModalPresentationFormSheet;
UINavigationController *navController = [manager customNavigationControllerWithRootViewController:composeViewController
presentationStyle:UIModalPresentationFormSheet];
navController.modalTransitionStyle = UIModalTransitionStyleCrossDissolve;
return navController;

View File

@ -34,6 +34,8 @@
#import "BITFeedbackComposeViewController.h"
#import "BITFeedbackUserDataViewController.h"
#import "BITHockeyBaseManagerPrivate.h"
#import "BITHockeyHelper.h"
@ -222,10 +224,8 @@
BITFeedbackUserDataViewController *userController = [[BITFeedbackUserDataViewController alloc] initWithStyle:UITableViewStyleGrouped];
userController.delegate = self;
UINavigationController *navController = [[UINavigationController alloc] initWithRootViewController:userController];
navController.navigationBar.barStyle = [self.manager barStyle];
navController.navigationBar.tintColor = [self.manager tintColor];
navController.modalPresentationStyle = UIModalPresentationFormSheet;
UINavigationController *navController = [self.manager customNavigationControllerWithRootViewController:userController
presentationStyle:UIModalPresentationFormSheet];
[self presentViewController:navController animated:YES completion:nil];
}

View File

@ -31,15 +31,40 @@
#import "BITFeedbackMessage.h"
#import "BITAttributedLabel.h"
typedef enum {
/**
* Cell style depending on the iOS version
*/
typedef NS_ENUM(NSUInteger, BITFeedbackListViewCellPresentatationStyle) {
/**
* Default is iOS 6 style
*/
BITFeedbackListViewCellPresentatationStyleDefault = 0,
/**
* Draw cells in the iOS 7 style
*/
BITFeedbackListViewCellPresentatationStyleOS7 = 1
};
/**
* Cell background style
*/
typedef NS_ENUM(NSUInteger, BITFeedbackListViewCellBackgroundStyle) {
/**
* For even rows
*/
BITFeedbackListViewCellBackgroundStyleNormal = 0,
/**
* For uneven rows
*/
BITFeedbackListViewCellBackgroundStyleAlternate = 1
} BITFeedbackListViewCellBackgroundStyle;
};
@interface BITFeedbackListViewCell : UITableViewCell
@property (nonatomic, strong) BITFeedbackMessage *message;
@property (nonatomic) BITFeedbackListViewCellPresentatationStyle style;
@property (nonatomic) BITFeedbackListViewCellBackgroundStyle backgroundStyle;
@property (nonatomic, strong) BITAttributedLabel *labelText;

View File

@ -33,6 +33,9 @@
#define BACKGROUNDCOLOR_DEFAULT BIT_RGBCOLOR(245, 245, 245)
#define BACKGROUNDCOLOR_ALTERNATE BIT_RGBCOLOR(235, 235, 235)
#define BACKGROUNDCOLOR_DEFAULT_OS7 BIT_RGBCOLOR(255, 255, 255)
#define BACKGROUNDCOLOR_ALTERNATE_OS7 BIT_RGBCOLOR(255, 255, 255)
#define TEXTCOLOR_TITLE BIT_RGBCOLOR(75, 75, 75)
#define TEXTCOLOR_DEFAULT BIT_RGBCOLOR(25, 25, 25)
@ -69,6 +72,7 @@
if (self) {
// Initialization code
_backgroundStyle = BITFeedbackListViewCellBackgroundStyleNormal;
_style = BITFeedbackListViewCellPresentatationStyleDefault;
_message = nil;
@ -99,6 +103,22 @@
#pragma mark - Private
- (UIColor *)backgroundColor {
if (self.backgroundStyle == BITFeedbackListViewCellBackgroundStyleNormal) {
if (self.style == BITFeedbackListViewCellPresentatationStyleDefault) {
return BACKGROUNDCOLOR_DEFAULT;
} else {
return BACKGROUNDCOLOR_DEFAULT_OS7;
}
} else {
if (self.style == BITFeedbackListViewCellPresentatationStyleDefault) {
return BACKGROUNDCOLOR_ALTERNATE;
} else {
return BACKGROUNDCOLOR_ALTERNATE_OS7;
}
}
}
- (BOOL)isSameDayWithDate1:(NSDate*)date1 date2:(NSDate*)date2 {
NSCalendar* calendar = [NSCalendar currentCalendar];
@ -126,17 +146,11 @@
accessoryViewBackground.clipsToBounds = YES;
// colors
if (_backgroundStyle == BITFeedbackListViewCellBackgroundStyleNormal) {
accessoryViewBackground.backgroundColor = BACKGROUNDCOLOR_DEFAULT;
self.contentView.backgroundColor = BACKGROUNDCOLOR_DEFAULT;
self.labelTitle.backgroundColor = BACKGROUNDCOLOR_DEFAULT;
self.labelText.backgroundColor = BACKGROUNDCOLOR_DEFAULT;
} else {
accessoryViewBackground.backgroundColor = BACKGROUNDCOLOR_ALTERNATE;
self.contentView.backgroundColor = BACKGROUNDCOLOR_ALTERNATE;
self.labelTitle.backgroundColor = BACKGROUNDCOLOR_ALTERNATE;
self.labelText.backgroundColor = BACKGROUNDCOLOR_ALTERNATE;
}
accessoryViewBackground.backgroundColor = [self backgroundColor];
self.contentView.backgroundColor = [self backgroundColor];
self.labelTitle.backgroundColor = [self backgroundColor];
self.labelText.backgroundColor = [self backgroundColor];
self.labelTitle.textColor = TEXTCOLOR_TITLE;
if (_message.status == BITFeedbackMessageStatusSendPending || _message.status == BITFeedbackMessageStatusSendInProgress) {
[self.labelText setTextColor:TEXTCOLOR_PENDING];

View File

@ -38,11 +38,14 @@
#import "BITFeedbackMessage.h"
#import "BITAttributedLabel.h"
#import "BITHockeyBaseManagerPrivate.h"
#import "BITHockeyHelper.h"
#import <QuartzCore/QuartzCore.h>
#define DEFAULT_BACKGROUNDCOLOR BIT_RGBCOLOR(245, 245, 245)
#define DEFAULT_BACKGROUNDCOLOR_OS7 BIT_RGBCOLOR(255, 255, 255)
#define DEFAULT_TEXTCOLOR BIT_RGBCOLOR(75, 75, 75)
#define BUTTON_BORDERCOLOR BIT_RGBCOLOR(175, 175, 175)
@ -118,11 +121,19 @@
self.tableView.dataSource = self;
self.tableView.separatorStyle = UITableViewCellSeparatorStyleNone;
[self.tableView setAutoresizingMask:UIViewAutoresizingFlexibleHeight | UIViewAutoresizingFlexibleWidth];
[self.tableView setBackgroundColor:[UIColor colorWithRed:0.82 green:0.84 blue:0.84 alpha:1]];
[self.tableView setSeparatorColor:[UIColor colorWithRed:0.79 green:0.79 blue:0.79 alpha:1]];
self.view.backgroundColor = DEFAULT_BACKGROUNDCOLOR;
if ([self.manager isPreiOS7Environment]) {
[self.tableView setBackgroundColor:[UIColor colorWithRed:0.82 green:0.84 blue:0.84 alpha:1]];
[self.tableView setSeparatorColor:[UIColor colorWithRed:0.79 green:0.79 blue:0.79 alpha:1]];
} else {
[self.tableView setBackgroundColor:[UIColor colorWithRed:1.0 green:1.0 blue:1.0 alpha:1]];
}
if ([self.manager isPreiOS7Environment]) {
self.view.backgroundColor = DEFAULT_BACKGROUNDCOLOR;
} else {
self.view.backgroundColor = DEFAULT_BACKGROUNDCOLOR_OS7;
}
id refreshClass = NSClassFromString(@"UIRefreshControl");
if (refreshClass) {
self.refreshControl = [[UIRefreshControl alloc] init];
@ -234,21 +245,17 @@
BITFeedbackUserDataViewController *userController = [[BITFeedbackUserDataViewController alloc] initWithStyle:UITableViewStyleGrouped];
userController.delegate = self;
UINavigationController *navController = [[UINavigationController alloc] initWithRootViewController:userController];
navController.navigationBar.barStyle = [self.manager barStyle];
navController.navigationBar.tintColor = [self.manager tintColor];
navController.modalPresentationStyle = UIModalPresentationFormSheet;
UINavigationController *navController = [self.manager customNavigationControllerWithRootViewController:userController
presentationStyle:UIModalPresentationFormSheet];
[self presentViewController:navController animated:YES completion:nil];
}
- (void)newFeedbackAction:(id)sender {
BITFeedbackComposeViewController *composeController = [[BITFeedbackComposeViewController alloc] init];
UINavigationController *navController = [[UINavigationController alloc] initWithRootViewController:composeController];
navController.navigationBar.barStyle = [self.manager barStyle];
navController.navigationBar.tintColor = [self.manager tintColor];
navController.modalPresentationStyle = UIModalPresentationFormSheet;
UINavigationController *navController = [self.manager customNavigationControllerWithRootViewController:composeController
presentationStyle:UIModalPresentationFormSheet];
[self presentViewController:navController animated:YES completion:nil];
}
@ -449,18 +456,26 @@
}
// button
UIButton *button = [UIButton buttonWithType:UIButtonTypeCustom];
button.autoresizingMask = UIViewAutoresizingFlexibleWidth;
UIImage *stretchableButton = [bit_imageNamed(@"buttonRoundedRegular.png", BITHOCKEYSDK_BUNDLE) stretchableImageWithLeftCapWidth:10 topCapHeight:0];
UIImage *stretchableHighlightedButton = [bit_imageNamed(@"buttonRoundedRegularHighlighted.png", BITHOCKEYSDK_BUNDLE) stretchableImageWithLeftCapWidth:10 topCapHeight:0];
[button setBackgroundImage:stretchableButton forState:UIControlStateNormal];
[button setBackgroundImage:stretchableHighlightedButton forState:UIControlStateHighlighted];
UIButton *button = nil;
if ([self.manager isPreiOS7Environment]) {
button = [UIButton buttonWithType:UIButtonTypeCustom];
button.autoresizingMask = UIViewAutoresizingFlexibleWidth;
UIImage *stretchableButton = [bit_imageNamed(@"buttonRoundedRegular.png", BITHOCKEYSDK_BUNDLE) stretchableImageWithLeftCapWidth:10 topCapHeight:0];
UIImage *stretchableHighlightedButton = [bit_imageNamed(@"buttonRoundedRegularHighlighted.png", BITHOCKEYSDK_BUNDLE) stretchableImageWithLeftCapWidth:10 topCapHeight:0];
[button setBackgroundImage:stretchableButton forState:UIControlStateNormal];
[button setBackgroundImage:stretchableHighlightedButton forState:UIControlStateHighlighted];
[[button titleLabel] setShadowOffset:CGSizeMake(0, 1)];
[[button titleLabel] setFont:[UIFont boldSystemFontOfSize:14.0]];
[button setTitleColor:BUTTON_TEXTCOLOR forState:UIControlStateNormal];
[button setTitleShadowColor:BUTTON_TEXTCOLOR_SHADOW forState:UIControlStateNormal];
} else {
#if __IPHONE_OS_VERSION_MAX_ALLOWED > __IPHONE_6_1
button = [UIButton buttonWithType:UIButtonTypeSystem];
#endif
}
[[button titleLabel] setShadowOffset:CGSizeMake(0, 1)];
[[button titleLabel] setFont:[UIFont boldSystemFontOfSize:14.0]];
[button setTitleColor:BUTTON_TEXTCOLOR forState:UIControlStateNormal];
[button setTitleShadowColor:BUTTON_TEXTCOLOR_SHADOW forState:UIControlStateNormal];
if (indexPath.section == 0) {
topGap = 22;
if ([self.manager numberOfMessages] == 0) {
@ -489,15 +504,19 @@
[button addTarget:self action:@selector(setUserDataAction:) forControlEvents:UIControlEventTouchUpInside];
} else {
topGap = 0.0f;
[[button titleLabel] setShadowOffset:CGSizeMake(0, -1)];
UIImage *stretchableDeleteButton = [bit_imageNamed(@"buttonRoundedDelete.png", BITHOCKEYSDK_BUNDLE) stretchableImageWithLeftCapWidth:10 topCapHeight:0];
UIImage *stretchableDeleteHighlightedButton = [bit_imageNamed(@"buttonRoundedDeleteHighlighted.png", BITHOCKEYSDK_BUNDLE) stretchableImageWithLeftCapWidth:10 topCapHeight:0];
[button setBackgroundImage:stretchableDeleteButton forState:UIControlStateNormal];
[button setBackgroundImage:stretchableDeleteHighlightedButton forState:UIControlStateHighlighted];
[button setTitleColor:BUTTON_DELETE_TEXTCOLOR forState:UIControlStateNormal];
[button setTitleShadowColor:BUTTON_DELETE_TEXTCOLOR_SHADOW forState:UIControlStateNormal];
if ([self.manager isPreiOS7Environment]) {
[[button titleLabel] setShadowOffset:CGSizeMake(0, -1)];
UIImage *stretchableDeleteButton = [bit_imageNamed(@"buttonRoundedDelete.png", BITHOCKEYSDK_BUNDLE) stretchableImageWithLeftCapWidth:10 topCapHeight:0];
UIImage *stretchableDeleteHighlightedButton = [bit_imageNamed(@"buttonRoundedDeleteHighlighted.png", BITHOCKEYSDK_BUNDLE) stretchableImageWithLeftCapWidth:10 topCapHeight:0];
[button setBackgroundImage:stretchableDeleteButton forState:UIControlStateNormal];
[button setBackgroundImage:stretchableDeleteHighlightedButton forState:UIControlStateHighlighted];
[button setTitleColor:BUTTON_DELETE_TEXTCOLOR forState:UIControlStateNormal];
[button setTitleShadowColor:BUTTON_DELETE_TEXTCOLOR_SHADOW forState:UIControlStateNormal];
} else {
[button setTitleColor:BUTTON_DELETE_BACKGROUNDCOLOR forState:UIControlStateNormal];
}
[button setTitle:BITHockeyLocalizedString(@"HockeyFeedbackListButonDeleteAllMessages") forState:UIControlStateNormal];
[button addTarget:self action:@selector(deleteAllMessagesAction:) forControlEvents:UIControlEventTouchUpInside];
}
@ -513,7 +532,11 @@
statusLabel.font = [UIFont systemFontOfSize:10];
statusLabel.textColor = DEFAULT_TEXTCOLOR;
statusLabel.textAlignment = kBITTextLabelAlignmentCenter;
statusLabel.backgroundColor = DEFAULT_BACKGROUNDCOLOR;
if ([self.manager isPreiOS7Environment]) {
statusLabel.backgroundColor = DEFAULT_BACKGROUNDCOLOR;
} else {
statusLabel.backgroundColor = DEFAULT_BACKGROUNDCOLOR_OS7;
}
statusLabel.autoresizingMask = UIViewAutoresizingFlexibleWidth;
statusLabel.text = [NSString stringWithFormat:BITHockeyLocalizedString(@"HockeyFeedbackListLastUpdated"),
@ -543,6 +566,12 @@
cell.backgroundStyle = BITFeedbackListViewCellBackgroundStyleNormal;
}
if ([self.manager isPreiOS7Environment]) {
cell.style = BITFeedbackListViewCellPresentatationStyleDefault;
} else {
cell.style = BITFeedbackListViewCellPresentatationStyleOS7;
}
BITFeedbackMessage *message = [self.manager messageAtIndex:indexPath.row];
cell.message = message;
cell.labelText.delegate = self;

View File

@ -59,20 +59,20 @@
The UIBarStyle of the update user interface navigation bar.
Default is UIBarStyleBlackOpaque
@see tintColor
@see navigationBarTintColor
*/
@property (nonatomic, assign) UIBarStyle barStyle;
/**
The tint color of the update user interface navigation bar.
The navigationbar tint color of the update user interface navigation bar.
The tintColor is used by default, you can either overwrite it `tintColor`
The navigationBarTintColor is used by default, you can either overwrite it `navigationBarTintColor`
or define another `barStyle` instead.
Default is RGB(25, 25, 25)
@see barStyle
*/
@property (nonatomic, strong) UIColor *tintColor;
@property (nonatomic, strong) UIColor *navigationBarTintColor;
/**
The UIModalPresentationStyle for showing the update user interface when invoked

View File

@ -43,6 +43,10 @@
#import <mach-o/ldsyms.h>
#endif
#ifndef __IPHONE_6_1
#define __IPHONE_6_1 60100
#endif
@implementation BITHockeyBaseManager {
UINavigationController *_navController;
@ -56,8 +60,12 @@
if ((self = [super init])) {
_serverURL = BITHOCKEYSDK_URL;
_barStyle = UIBarStyleBlackOpaque;
self.tintColor = BIT_RGBCOLOR(25, 25, 25);
if ([self isPreiOS7Environment]) {
_barStyle = UIBarStyleBlackOpaque;
self.navigationBarTintColor = BIT_RGBCOLOR(25, 25, 25);
} else {
_barStyle = UIBarStyleDefault;
}
_modalPresentationStyle = UIModalPresentationFormSheet;
NSLocale *enUSPOSIXLocale = [[NSLocale alloc] initWithLocaleIdentifier:@"en_US_POSIX"];
@ -92,6 +100,28 @@
return (_appIdentifier ? bit_URLEncodedString(_appIdentifier) : bit_URLEncodedString([self mainBundleIdentifier]));
}
- (BOOL)isPreiOS7Environment {
static BOOL isPreiOS7Environment = YES;
static dispatch_once_t checkOS;
dispatch_once(&checkOS, ^{
// we only perform this runtime check if this is build against at least iOS7 base SDK
#if __IPHONE_OS_VERSION_MAX_ALLOWED > __IPHONE_6_1
// runtime check according to
// https://developer.apple.com/library/prerelease/ios/documentation/UserExperience/Conceptual/TransitionGuide/SupportingEarlieriOS.html
if (floor(NSFoundationVersionNumber) <= NSFoundationVersionNumber_iOS_6_1) {
isPreiOS7Environment = YES;
} else {
isPreiOS7Environment = NO;
}
#else
isPreiOS7Environment = YES;
#endif
});
return isPreiOS7Environment;
}
- (NSString *)getDevicePlatform {
size_t size;
sysctlbyname("hw.machine", NULL, &size, NULL, 0);
@ -152,6 +182,24 @@
return visibleWindow;
}
/**
* Provide a custom UINavigationController with customized appearance settings
*
* @param viewController The root viewController
* @param modalPresentationStyle The modal presentation style
*
* @return A UINavigationController
*/
- (UINavigationController *)customNavigationControllerWithRootViewController:(UIViewController *)viewController presentationStyle:(UIModalPresentationStyle)modalPresentationStyle {
UINavigationController *navController = [[UINavigationController alloc] initWithRootViewController:viewController];
navController.navigationBar.barStyle = self.barStyle;
if (self.navigationBarTintColor)
navController.navigationBar.tintColor = self.navigationBarTintColor;
navController.modalPresentationStyle = self.modalPresentationStyle;
return navController;
}
- (void)showView:(UIViewController *)viewController {
UIViewController *parentViewController = nil;
@ -166,8 +214,8 @@
}
// use topmost modal view
while (parentViewController.modalViewController) {
parentViewController = parentViewController.modalViewController;
while (parentViewController.presentedViewController) {
parentViewController = parentViewController.presentedViewController;
}
// special addition to get rootViewController from three20 which has it's own controller handling
@ -183,10 +231,7 @@
if (_navController != nil) _navController = nil;
_navController = [[UINavigationController alloc] initWithRootViewController:viewController];
_navController.navigationBar.barStyle = _barStyle;
_navController.navigationBar.tintColor = _tintColor;
_navController.modalPresentationStyle = _modalPresentationStyle;
_navController = [self customNavigationControllerWithRootViewController:viewController presentationStyle:_modalPresentationStyle];
if (parentViewController) {
_navController.modalTransitionStyle = UIModalTransitionStyleCoverVertical;

View File

@ -23,6 +23,9 @@
/** the value this object was initialized with */
- (BOOL)isAppStoreEnvironment;
/** Check if the device is running an iOS version previous to iOS 7 */
- (BOOL)isPreiOS7Environment;
/** by default, just logs the message
can be overriden by subclasses to do their own error handling,
@ -42,6 +45,7 @@
- (NSString *)executableUUID;
/** UI helpers */
- (UINavigationController *)customNavigationControllerWithRootViewController:(UIViewController *)viewController presentationStyle:(UIModalPresentationStyle)presentationStyle;
- (UIWindow *)findVisibleWindow;
- (void)showView:(UIViewController *)viewController;

View File

@ -32,7 +32,6 @@
#import "BITHockeyManagerPrivate.h"
#import "BITHockeyBaseManagerPrivate.h"
#import "BITCrashManagerPrivate.h"
#import "BITUpdateManagerPrivate.h"
#import "BITStoreUpdateManagerPrivate.h"
#import "BITFeedbackManagerPrivate.h"
@ -356,9 +355,11 @@
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wundeclared-selector"
#pragma clang diagnostic ignored "-Warc-performSelector-leaks"
+ (void)disableJMCCrashReporter {
id jmcInstance = [self jmcInstance];
id jmcOptions = [jmcInstance performSelector:@selector(options)];
SEL optionsSelector = @selector(options);
id jmcOptions = [jmcInstance performSelector:optionsSelector];
SEL crashReporterSelector = @selector(setCrashReportingEnabled:);
BOOL value = NO;
@ -374,13 +375,14 @@
+ (BOOL)checkJMCConfiguration:(NSDictionary *)configuration {
return (([configuration isKindOfClass:[NSDictionary class]]) &&
([[configuration valueForKey:@"enabled"] boolValue]) &&
([[configuration valueForKey:@"url"] length] > 0) &&
([[configuration valueForKey:@"key"] length] > 0) &&
([[configuration valueForKey:@"project"] length] > 0));
([(NSString *)[configuration valueForKey:@"url"] length] > 0) &&
([(NSString *)[configuration valueForKey:@"key"] length] > 0) &&
([(NSString *)[configuration valueForKey:@"project"] length] > 0));
}
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wundeclared-selector"
#pragma clang diagnostic ignored "-Warc-performSelector-leaks"
+ (void)applyJMCConfiguration:(NSDictionary *)configuration {
id jmcInstance = [self jmcInstance];
SEL configureSelector = @selector(configureJiraConnect:projectKey:apiKey:);

View File

@ -48,12 +48,31 @@
@end
#ifndef __IPHONE_6_1
#define __IPHONE_6_1 60100
#endif
/**
* Button style depending on the iOS version
*/
typedef NS_ENUM(NSUInteger, BITStoreButtonStyle) {
/**
* Default is iOS 6 style
*/
BITStoreButtonStyleDefault = 0,
/**
* Draw buttons in the iOS 7 style
*/
BITStoreButtonStyleOS7 = 1
};
// Simulate the Paymeny-Button from the AppStore
// The interface is flexible, so there is now fixed order
@interface BITStoreButton : UIButton
- (id)initWithFrame:(CGRect)frame;
- (id)initWithPadding:(CGPoint)padding;
- (id)initWithPadding:(CGPoint)padding style:(BITStoreButtonStyle)style;
// action delegate
@property (nonatomic, weak) id<BITStoreButtonDelegate> buttonDelegate;
@ -64,6 +83,11 @@
// align helper
@property (nonatomic, assign) CGPoint customPadding;
// align helper
@property (nonatomic, assign) BITStoreButtonStyle style;
- (void)alignToSuperview;
@end

View File

@ -58,7 +58,10 @@
@end
@implementation BITStoreButton
@implementation BITStoreButton {
CALayer *_defaultBorderLayer;
CALayer *_inActiveBorderLayer;
}
#pragma mark - private
@ -90,9 +93,19 @@
// show white or gray text, depending on the state
if (self.buttonData.isEnabled) {
[self setTitleColor:BIT_RGBCOLOR(106, 106, 106) forState:UIControlStateNormal];
if (self.style == BITStoreButtonStyleDefault) {
[self setTitleColor:BIT_RGBCOLOR(106, 106, 106) forState:UIControlStateNormal];
} else {
[self setTitleColor:BIT_RGBCOLOR(35, 111, 251) forState:UIControlStateNormal];
[_defaultBorderLayer setHidden:NO];
[_inActiveBorderLayer setHidden:YES];
}
} else {
[self setTitleColor:BIT_RGBCOLOR(148, 150, 151) forState:UIControlStateNormal];
if (self.style == BITStoreButtonStyleOS7) {
[_defaultBorderLayer setHidden:YES];
[_inActiveBorderLayer setHidden:NO];
}
}
// calculate optimal new size
@ -149,32 +162,53 @@
// register for touch events
[self addTarget:self action:@selector(buttonPressed:) forControlEvents:UIControlEventTouchUpInside];
// main gradient layer
CAGradientLayer *gradient = [CAGradientLayer layer];
gradient.colors = @[(id)BIT_RGBCOLOR(243, 243, 243).CGColor, (id)BIT_RGBCOLOR(222, 222, 222).CGColor];
gradient.locations = @[[NSNumber numberWithFloat:0.0], [NSNumber numberWithFloat:1.0]];
gradient.frame = CGRectMake(0.0, 0.0, CGRectGetWidth(frame), CGRectGetHeight(frame));
gradient.cornerRadius = 2.5;
gradient.needsDisplayOnBoundsChange = YES;
[self.layer addSublayer:gradient];
// border layers for more sex!
CALayer *borderLayer = [CALayer layer];
borderLayer.borderColor = [BIT_RGBCOLOR(191, 191, 191) CGColor];
borderLayer.borderWidth = 1.0;
borderLayer.frame = CGRectMake(0.0, 0.0, CGRectGetWidth(frame), CGRectGetHeight(frame));
borderLayer.cornerRadius = 2.5;
borderLayer.needsDisplayOnBoundsChange = YES;
[self.layer addSublayer:borderLayer];
[self bringSubviewToFront:self.titleLabel];
}
return self;
}
- (id)initWithPadding:(CGPoint)padding {
if ((self = [self initWithFrame:CGRectMake(0, 0, 40, BIT_MIN_HEIGHT)])) {
- (id)initWithPadding:(CGPoint)padding style:(BITStoreButtonStyle)style {
CGRect frame = CGRectMake(0, 0, 40, BIT_MIN_HEIGHT);
if ((self = [self initWithFrame:frame])) {
_customPadding = padding;
_style = style;
if (style == BITStoreButtonStyleDefault) {
// main gradient layer
CAGradientLayer *gradient = [CAGradientLayer layer];
gradient.colors = @[(id)BIT_RGBCOLOR(243, 243, 243).CGColor, (id)BIT_RGBCOLOR(222, 222, 222).CGColor];
gradient.locations = @[[NSNumber numberWithFloat:0.0], [NSNumber numberWithFloat:1.0]];
gradient.frame = CGRectMake(0.0, 0.0, CGRectGetWidth(frame), CGRectGetHeight(frame));
gradient.cornerRadius = 2.5;
gradient.needsDisplayOnBoundsChange = YES;
[self.layer addSublayer:gradient];
}
// border layers for more sex!
_defaultBorderLayer = [CALayer layer];
if (style == BITStoreButtonStyleDefault) {
_defaultBorderLayer.borderColor = [BIT_RGBCOLOR(191, 191, 191) CGColor];
} else {
_defaultBorderLayer.borderColor = [BIT_RGBCOLOR(35, 111, 251) CGColor];
}
_defaultBorderLayer.borderWidth = 1.0;
_defaultBorderLayer.frame = CGRectMake(0.0, 0.0, CGRectGetWidth(frame), CGRectGetHeight(frame));
_defaultBorderLayer.cornerRadius = 2.5;
_defaultBorderLayer.needsDisplayOnBoundsChange = YES;
[self.layer addSublayer:_defaultBorderLayer];
if (style == BITStoreButtonStyleOS7) {
_inActiveBorderLayer = [CALayer layer];
_inActiveBorderLayer.borderColor = [BIT_RGBCOLOR(148, 150, 151) CGColor];
_inActiveBorderLayer.borderWidth = 1.0;
_inActiveBorderLayer.frame = CGRectMake(0.0, 0.0, CGRectGetWidth(frame), CGRectGetHeight(frame));
_inActiveBorderLayer.cornerRadius = 2.5;
_inActiveBorderLayer.needsDisplayOnBoundsChange = YES;
[self.layer addSublayer:_inActiveBorderLayer];
[_inActiveBorderLayer setHidden:YES];
}
[self bringSubviewToFront:self.titleLabel];
}
return self;
}

View File

@ -33,12 +33,6 @@
#import "BITHockeyBaseManager.h"
typedef enum {
BITUpdateAuthorizationDenied,
BITUpdateAuthorizationAllowed,
BITUpdateAuthorizationPending
} BITUpdateAuthorizationState;
typedef enum {
BITUpdateCheckStartup = 0,
BITUpdateCheckDaily = 1,
@ -189,39 +183,6 @@ typedef enum {
@property (nonatomic, assign, getter=isShowingDirectInstallOption) BOOL showDirectInstallOption;
///-----------------------------------------------------------------------------
/// @name Authorization
///-----------------------------------------------------------------------------
/**
Flag that determines if each update should be authenticated
If enabled each update will be authenticated on startup against the HockeyApp servers.
The process will basically validate if the current device is part of the provisioning
profile on the server. If not, it will present a blocking view on top of the apps UI
so that no interaction is possible.
When running the app from the App Store, this setting is ignored.
*Default*: _NO_
@see authenticationSecret
@warning This only works when using Ad-Hoc provisioning profiles!
*/
@property (nonatomic, assign, getter=isRequireAuthorization) BOOL requireAuthorization;
/**
The authentication token from HockeyApp.
Set the token to the `Secret ID` which HockeyApp provides for every app.
When running the app from the App Store, this setting is ignored.
@see requireAuthorization
*/
@property (nonatomic, strong) NSString *authenticationSecret;
///-----------------------------------------------------------------------------
/// @name Expiry
///-----------------------------------------------------------------------------

View File

@ -216,34 +216,6 @@ typedef NS_ENUM(NSInteger, BITUpdateAlertViewTag) {
return @"invalid";
}
#pragma mark - Authorization
- (NSString *)authenticationToken {
return [BITHockeyMD5([NSString stringWithFormat:@"%@%@%@%@",
_authenticationSecret,
[[NSBundle mainBundle] objectForInfoDictionaryKey:@"CFBundleVersion"],
[[NSBundle mainBundle] objectForInfoDictionaryKey:@"CFBundleIdentifier"],
[self deviceIdentifier]
]
) lowercaseString];
}
- (BITUpdateAuthorizationState)authorizationState {
NSString *version = [[NSUserDefaults standardUserDefaults] objectForKey:kBITUpdateAuthorizedVersion];
NSString *token = [self stringValueFromKeychainForKey:kBITUpdateAuthorizedToken];
if (version != nil && token != nil) {
if ([version compare:[[NSBundle mainBundle] objectForInfoDictionaryKey:@"CFBundleVersion"]] == NSOrderedSame) {
// if it is denied, block the screen permanently
if ([token compare:[self authenticationToken]] != NSOrderedSame) {
return BITUpdateAuthorizationDenied;
} else {
return BITUpdateAuthorizationAllowed;
}
}
}
return BITUpdateAuthorizationPending;
}
#pragma mark - Cache
@ -361,8 +333,6 @@ typedef NS_ENUM(NSInteger, BITUpdateAlertViewTag) {
_lastCheckFailed = NO;
_currentAppVersion = [[NSBundle mainBundle] objectForInfoDictionaryKey:@"CFBundleVersion"];
_blockingView = nil;
_requireAuthorization = NO;
_authenticationSecret = nil;
_lastCheck = nil;
_uuid = [[self executableUUID] copy];
_versionUUID = nil;
@ -439,7 +409,8 @@ typedef NS_ENUM(NSInteger, BITUpdateAlertViewTag) {
return;
}
self.barStyle = UIBarStyleBlack;
if ([self isPreiOS7Environment])
self.barStyle = UIBarStyleBlack;
[self showView:[self hockeyViewController:YES]];
}
@ -560,87 +531,9 @@ typedef NS_ENUM(NSInteger, BITUpdateAlertViewTag) {
return checkForUpdate;
}
- (void)checkForAuthorization {
NSMutableString *parameter = [NSMutableString stringWithFormat:@"api/2/apps/%@", [self encodedAppIdentifier]];
[parameter appendFormat:@"?format=json&authorize=yes&app_version=%@&udid=%@&sdk=%@&sdk_version=%@&uuid=%@",
bit_URLEncodedString([[NSBundle mainBundle] objectForInfoDictionaryKey:@"CFBundleVersion"]),
([self isAppStoreEnvironment] ? @"appstore" : bit_URLEncodedString([self deviceIdentifier])),
BITHOCKEY_NAME,
BITHOCKEY_VERSION,
_uuid
];
// build request & send
NSString *url = [NSString stringWithFormat:@"%@%@", self.serverURL, parameter];
BITHockeyLog(@"INFO: Sending api request to %@", url);
NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:[NSURL URLWithString:url] cachePolicy:1 timeoutInterval:10.0];
[request setHTTPMethod:@"GET"];
[request setValue:@"Hockey/iOS" forHTTPHeaderField:@"User-Agent"];
NSURLResponse *response = nil;
NSError *error = NULL;
BOOL failed = YES;
NSData *responseData = [NSURLConnection sendSynchronousRequest:request returningResponse:&response error:&error];
if ([responseData length]) {
NSString *responseString = [[NSString alloc] initWithBytes:[responseData bytes] length:[responseData length] encoding: NSUTF8StringEncoding];
if (responseString && [responseString dataUsingEncoding:NSUTF8StringEncoding]) {
NSDictionary *feedDict = (NSDictionary *)[NSJSONSerialization JSONObjectWithData:[responseString dataUsingEncoding:NSUTF8StringEncoding] options:kNilOptions error:&error];
// server returned empty response?
if (![feedDict count]) {
[self reportError:[NSError errorWithDomain:kBITUpdateErrorDomain
code:BITUpdateAPIServerReturnedEmptyResponse
userInfo:[NSDictionary dictionaryWithObjectsAndKeys:@"Server returned empty response.", NSLocalizedDescriptionKey, nil]]];
return;
} else {
BITHockeyLog(@"INFO: Received API response: %@", responseString);
NSString *token = [[feedDict objectForKey:@"authcode"] lowercaseString];
failed = NO;
if ([[self authenticationToken] compare:token] == NSOrderedSame) {
// identical token, activate this version
// store the new data
[[NSUserDefaults standardUserDefaults] setObject:[[NSBundle mainBundle] objectForInfoDictionaryKey:@"CFBundleVersion"] forKey:kBITUpdateAuthorizedVersion];
[self addStringValueToKeychain:token forKey:kBITUpdateAuthorizedToken];
[[NSUserDefaults standardUserDefaults] synchronize];
self.requireAuthorization = NO;
self.blockingView = nil;
// now continue with an update check right away
if (self.checkForUpdateOnLaunch) {
[self checkForUpdate];
}
} else {
// different token, block this version
BITHockeyLog(@"INFO: AUTH FAILURE: %@", [self authenticationToken]);
// store the new data
[[NSUserDefaults standardUserDefaults] setObject:[[NSBundle mainBundle] objectForInfoDictionaryKey:@"CFBundleVersion"] forKey:kBITUpdateAuthorizedVersion];
[self addStringValueToKeychain:token forKey:kBITUpdateAuthorizedToken];
[[NSUserDefaults standardUserDefaults] synchronize];
[self showBlockingScreen:BITHockeyLocalizedString(@"UpdateAuthorizationDenied") image:@"authorize_denied.png"];
}
}
}
}
if (failed) {
[self showBlockingScreen:BITHockeyLocalizedString(@"UpdateAuthorizationOffline") image:@"authorize_request.png"];
}
}
- (void)checkForUpdate {
if (![self isAppStoreEnvironment] && ![self isUpdateManagerDisabled]) {
if ([self expiryDateReached]) return;
if (self.requireAuthorization) return;
if (self.isUpdateAvailable && [self hasNewerMandatoryVersion]) {
[self showCheckForUpdateAlert];
@ -735,38 +628,6 @@ typedef NS_ENUM(NSInteger, BITUpdateAlertViewTag) {
}
// checks whether this app version is authorized
- (BOOL)appVersionIsAuthorized {
if (self.requireAuthorization && !_authenticationSecret) {
[self reportError:[NSError errorWithDomain:kBITUpdateErrorDomain
code:BITUpdateAPIClientAuthorizationMissingSecret
userInfo:[NSDictionary dictionaryWithObjectsAndKeys:@"Authentication secret is not set but required.", NSLocalizedDescriptionKey, nil]]];
return NO;
}
if (!self.requireAuthorization) {
self.blockingView = nil;
return YES;
}
#if TARGET_IPHONE_SIMULATOR
NSLog(@"Authentication checks only work on devices. Using the simulator will always return being authorized.");
return YES;
#endif
BITUpdateAuthorizationState state = [self authorizationState];
if (state == BITUpdateAuthorizationDenied) {
[self showBlockingScreen:BITHockeyLocalizedString(@"UpdateAuthorizationDenied") image:@"authorize_denied.png"];
} else if (state == BITUpdateAuthorizationAllowed) {
self.requireAuthorization = NO;
return YES;
}
return NO;
}
// begin the startup process
- (void)startManager {
if (![self isAppStoreEnvironment]) {
@ -776,16 +637,8 @@ typedef NS_ENUM(NSInteger, BITUpdateAlertViewTag) {
[self checkExpiryDateReached];
if (![self expiryDateReached]) {
if (![self appVersionIsAuthorized]) {
if ([self authorizationState] == BITUpdateAuthorizationPending) {
[self showBlockingScreen:BITHockeyLocalizedString(@"UpdateAuthorizationProgress") image:@"authorize_request.png"];
[self performSelector:@selector(checkForAuthorization) withObject:nil afterDelay:0.0f];
}
} else {
if ([self checkForTracker] || ([self isCheckForUpdateOnLaunch] && [self shouldCheckForUpdates])) {
[self performSelector:@selector(checkForUpdate) withObject:nil afterDelay:1.0f];
}
if ([self checkForTracker] || ([self isCheckForUpdateOnLaunch] && [self shouldCheckForUpdates])) {
[self performSelector:@selector(checkForUpdate) withObject:nil afterDelay:1.0f];
}
}
} else {

View File

@ -74,12 +74,6 @@
// initiates app-download call. displays an system UIAlertView
- (BOOL)initiateAppDownload;
// checks whether this app version is authorized
- (BOOL)appVersionIsAuthorized;
// start checking for an authorization key
- (void)checkForAuthorization;
// get/set current active hockey view controller
@property (nonatomic, strong) BITUpdateViewController *currentHockeyViewController;

View File

@ -40,6 +40,7 @@
#import "BITUpdateManagerPrivate.h"
#import "BITUpdateViewControllerPrivate.h"
#import "BITHockeyBaseManagerPrivate.h"
#define kWebCellIdentifier @"PSWebTableViewCell"
@ -62,6 +63,14 @@
#pragma mark - Private
- (UIColor *)backgroundColor {
if ([self.updateManager isPreiOS7Environment]) {
return BIT_RGBCOLOR(235, 235, 235);
} else {
return BIT_RGBCOLOR(255, 255, 255);
}
}
- (void)restoreStoreButtonStateAnimated:(BOOL)animated {
if (_isAppStoreEnvironment) {
[self setAppStoreButtonState:AppStoreButtonStateOffline animated:animated];
@ -169,7 +178,7 @@
[self realignPreviousVersionButton];
} else {
self.tableView.tableFooterView = nil;
self.tableView.backgroundColor = BIT_RGBCOLOR(235, 235, 235);
self.tableView.backgroundColor = [self backgroundColor];
}
}
@ -200,8 +209,7 @@
} else {
cell.webViewContent = [NSString stringWithFormat:@"<p><b>%@</b>%@<br/><small>%@</small></p><p>%@</p>", [appVersion versionString], installed, dateAndSizeString, [appVersion notesOrEmptyString]];
}
cell.cellBackgroundColor = BIT_RGBCOLOR(235, 235, 235);
cell.cellBackgroundColor = [self backgroundColor];
[cell addWebView];
// hack
cell.textLabel.text = @"";
@ -269,6 +277,11 @@
[self.tableView addSubview:topView];
_appStoreHeader = [[BITAppStoreHeader alloc] initWithFrame:CGRectMake(0, 0, self.view.frame.size.width, kAppStoreViewHeight)];
if ([self.updateManager isPreiOS7Environment]) {
_appStoreHeader.style = BITAppStoreHeaderStyleDefault;
} else {
_appStoreHeader.style = BITAppStoreHeaderStyleOS7;
}
[self updateAppStoreHeader];
NSString *iconString = nil;
@ -323,7 +336,11 @@
self.tableView.tableHeaderView = _appStoreHeader;
BITStoreButton *storeButton = [[BITStoreButton alloc] initWithPadding:CGPointMake(5, 58)];
BITStoreButtonStyle buttonStyle = BITStoreButtonStyleDefault;
if (![self.updateManager isPreiOS7Environment]) {
buttonStyle = BITStoreButtonStyleOS7;
}
BITStoreButton *storeButton = [[BITStoreButton alloc] initWithPadding:CGPointMake(5, 58) style:buttonStyle];
storeButton.autoresizingMask = UIViewAutoresizingFlexibleLeftMargin;
storeButton.buttonDelegate = self;
[self.tableView.tableHeaderView addSubview:storeButton];
@ -430,7 +447,7 @@
if (rowHeight == 0) {
rowHeight = indexPath.row == 0 ? 250 : 44; // fill screen on startup
self.tableView.backgroundColor = BIT_RGBCOLOR(235, 235, 235);
self.tableView.backgroundColor = [self backgroundColor];
}
return rowHeight;

View File

@ -17,22 +17,10 @@ Pod::Spec.new do |s|
'yourself when the network becomse reachable.'
s.source_files = 'Classes'
s.resource_bundle = { 'HockeySDKResources' => ['Resources/*.png', 'Resources/*.lproj'] }
s.requires_arc = true
s.preserve_paths = 'Resources', 'Support', 'Vendor'
s.frameworks = 'CoreText', 'QuartzCore', 'SystemConfiguration', 'CrashReporter', 'CoreGraphics', 'UIKit'
s.xcconfig = { 'FRAMEWORK_SEARCH_PATHS' => '"$(PODS_ROOT)/HockeySDK/Vendor"',
'GCC_PREPROCESSOR_DEFINITIONS' => %{$(inherited) BITHOCKEY_VERSION="@\\"#{s.version}\\""} }
s.post_install do |library_representation|
Dir.chdir File.join(library_representation.sandbox_dir, 'HockeySDK/Support') do
command = "xcodebuild -project HockeySDK.xcodeproj -target HockeySDKResources CONFIGURATION_BUILD_DIR=../Resources"
command << " 2>&1 > /dev/null"
unless system(command)
raise ::Pod::Informative, "Failed to generate HockeySDK resources bundle"
end
end
File.open(library_representation.copy_resources_script_path, 'a') do |file|
file.puts "install_resource 'HockeySDK/Resources/HockeySDKResources.bundle'"
end
end
end

View File

@ -43,9 +43,3 @@ This documentation provides integrated help in Xcode for all public APIs and a s
3. Copy the content into ~`/Library/Developer/Shared/Documentation/DocSet`
The documentation is also available via the following URL: [http://hockeyapp.net/help/sdk/ios/3.0.0/](http://hockeyapp.net/help/sdk/ios/3.0.0/)
## Nightly Builds
You can download nightly builds of the develop branch from [cisimple.com](https://www.cisimple.com/jobs/qsx70pbsrjlf60nlq). Builds are triggered at midnight (UTC) if we had pushed commits to our repository in the past 24 hours.
<a href="https://www.cisimple.com/jobs/qsx70pbsrjlf60nlq"><img src='https://www.cisimple.com/jobs/qsx70pbsrjlf60nlq/build_status.png'/></a>

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.4 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.7 KiB

View File

@ -1,3 +1,3 @@
OTHER_LDFLAGS=$(inherited) -framework CoreText -framework CoreGraphics -framework Foundation -framework QuartzCore -framework SystemConfiguration -framework UIKit -framework Security
HOCKEYSDK_DOCSET_NAME=HockeySDK-iOS
GCC_PREPROCESSOR_DEFINITIONS=$(inherited) CONFIGURATION_$(CONFIGURATION)
GCC_PREPROCESSOR_DEFINITIONS=$(inherited) CONFIGURATION_$(CONFIGURATION)

View File

@ -88,8 +88,6 @@
1E59559B15B6FDA500A03429 /* HockeySDK.h in Headers */ = {isa = PBXBuildFile; fileRef = 1E71509A15B5C76F004E88FF /* HockeySDK.h */; settings = {ATTRIBUTES = (Public, ); }; };
1E5955C615B71C8600A03429 /* authorize_denied.png in Resources */ = {isa = PBXBuildFile; fileRef = 1E5955BB15B71C8600A03429 /* authorize_denied.png */; };
1E5955C715B71C8600A03429 /* authorize_denied@2x.png in Resources */ = {isa = PBXBuildFile; fileRef = 1E5955BC15B71C8600A03429 /* authorize_denied@2x.png */; };
1E5955C815B71C8600A03429 /* authorize_request.png in Resources */ = {isa = PBXBuildFile; fileRef = 1E5955BD15B71C8600A03429 /* authorize_request.png */; };
1E5955C915B71C8600A03429 /* authorize_request@2x.png in Resources */ = {isa = PBXBuildFile; fileRef = 1E5955BE15B71C8600A03429 /* authorize_request@2x.png */; };
1E5955CA15B71C8600A03429 /* bg.png in Resources */ = {isa = PBXBuildFile; fileRef = 1E5955BF15B71C8600A03429 /* bg.png */; };
1E5955CB15B71C8600A03429 /* buttonHighlight.png in Resources */ = {isa = PBXBuildFile; fileRef = 1E5955C015B71C8600A03429 /* buttonHighlight.png */; };
1E5955CC15B71C8600A03429 /* buttonHighlight@2x.png in Resources */ = {isa = PBXBuildFile; fileRef = 1E5955C115B71C8600A03429 /* buttonHighlight@2x.png */; };
@ -104,7 +102,6 @@
1E754E5C1621FBB70070AB92 /* BITCrashManager.h in Headers */ = {isa = PBXBuildFile; fileRef = 1E754E561621FBB70070AB92 /* BITCrashManager.h */; settings = {ATTRIBUTES = (Public, ); }; };
1E754E5D1621FBB70070AB92 /* BITCrashManager.m in Sources */ = {isa = PBXBuildFile; fileRef = 1E754E571621FBB70070AB92 /* BITCrashManager.m */; };
1E754E5E1621FBB70070AB92 /* BITCrashManagerDelegate.h in Headers */ = {isa = PBXBuildFile; fileRef = 1E754E581621FBB70070AB92 /* BITCrashManagerDelegate.h */; settings = {ATTRIBUTES = (Public, ); }; };
1E754E5F1621FBB70070AB92 /* BITCrashManagerPrivate.h in Headers */ = {isa = PBXBuildFile; fileRef = 1E754E591621FBB70070AB92 /* BITCrashManagerPrivate.h */; };
1E754E601621FBB70070AB92 /* BITCrashReportTextFormatter.h in Headers */ = {isa = PBXBuildFile; fileRef = 1E754E5A1621FBB70070AB92 /* BITCrashReportTextFormatter.h */; };
1E754E611621FBB70070AB92 /* BITCrashReportTextFormatter.m in Sources */ = {isa = PBXBuildFile; fileRef = 1E754E5B1621FBB70070AB92 /* BITCrashReportTextFormatter.m */; };
1E7A45FC16F54FB5005B08F1 /* OCHamcrestIOS.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 1E7A45FA16F54FB5005B08F1 /* OCHamcrestIOS.framework */; };
@ -231,8 +228,6 @@
1E59557215B6F84D00A03429 /* ru */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.strings; name = ru; path = ru.lproj/HockeySDK.strings; sourceTree = "<group>"; };
1E5955BB15B71C8600A03429 /* authorize_denied.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = authorize_denied.png; sourceTree = "<group>"; };
1E5955BC15B71C8600A03429 /* authorize_denied@2x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "authorize_denied@2x.png"; sourceTree = "<group>"; };
1E5955BD15B71C8600A03429 /* authorize_request.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = authorize_request.png; sourceTree = "<group>"; };
1E5955BE15B71C8600A03429 /* authorize_request@2x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "authorize_request@2x.png"; sourceTree = "<group>"; };
1E5955BF15B71C8600A03429 /* bg.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = bg.png; sourceTree = "<group>"; };
1E5955C015B71C8600A03429 /* buttonHighlight.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = buttonHighlight.png; sourceTree = "<group>"; };
1E5955C115B71C8600A03429 /* buttonHighlight@2x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "buttonHighlight@2x.png"; sourceTree = "<group>"; };
@ -254,7 +249,6 @@
1E754E561621FBB70070AB92 /* BITCrashManager.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = BITCrashManager.h; sourceTree = "<group>"; };
1E754E571621FBB70070AB92 /* BITCrashManager.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = BITCrashManager.m; sourceTree = "<group>"; };
1E754E581621FBB70070AB92 /* BITCrashManagerDelegate.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = BITCrashManagerDelegate.h; sourceTree = "<group>"; };
1E754E591621FBB70070AB92 /* BITCrashManagerPrivate.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = BITCrashManagerPrivate.h; sourceTree = "<group>"; };
1E754E5A1621FBB70070AB92 /* BITCrashReportTextFormatter.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = BITCrashReportTextFormatter.h; sourceTree = "<group>"; };
1E754E5B1621FBB70070AB92 /* BITCrashReportTextFormatter.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = BITCrashReportTextFormatter.m; sourceTree = "<group>"; };
1E7A45FA16F54FB5005B08F1 /* OCHamcrestIOS.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; path = OCHamcrestIOS.framework; sourceTree = "<group>"; };
@ -334,8 +328,6 @@
children = (
1E5955BB15B71C8600A03429 /* authorize_denied.png */,
1E5955BC15B71C8600A03429 /* authorize_denied@2x.png */,
1E5955BD15B71C8600A03429 /* authorize_request.png */,
1E5955BE15B71C8600A03429 /* authorize_request@2x.png */,
1E5955BF15B71C8600A03429 /* bg.png */,
1E5955C015B71C8600A03429 /* buttonHighlight.png */,
1E5955C115B71C8600A03429 /* buttonHighlight@2x.png */,
@ -463,7 +455,6 @@
1E754E561621FBB70070AB92 /* BITCrashManager.h */,
1E754E571621FBB70070AB92 /* BITCrashManager.m */,
1E754E581621FBB70070AB92 /* BITCrashManagerDelegate.h */,
1E754E591621FBB70070AB92 /* BITCrashManagerPrivate.h */,
1E754E5A1621FBB70070AB92 /* BITCrashReportTextFormatter.h */,
1E754E5B1621FBB70070AB92 /* BITCrashReportTextFormatter.m */,
);
@ -622,7 +613,6 @@
1E49A4D0161222B900463151 /* BITWebTableViewCell.h in Headers */,
1E49A4D8161222D400463151 /* HockeySDKPrivate.h in Headers */,
1EC69F601615001500808FD9 /* BITHockeyManagerPrivate.h in Headers */,
1E754E5F1621FBB70070AB92 /* BITCrashManagerPrivate.h in Headers */,
E48A3DEC17B3ED1C00924C3D /* BITAuthenticator.h in Headers */,
1E754E601621FBB70070AB92 /* BITCrashReportTextFormatter.h in Headers */,
1EACC97B162F041E007578C5 /* BITAttributedLabel.h in Headers */,
@ -743,8 +733,6 @@
files = (
1E5955C615B71C8600A03429 /* authorize_denied.png in Resources */,
1E5955C715B71C8600A03429 /* authorize_denied@2x.png in Resources */,
1E5955C815B71C8600A03429 /* authorize_request.png in Resources */,
1E5955C915B71C8600A03429 /* authorize_request@2x.png in Resources */,
1E5955CA15B71C8600A03429 /* bg.png in Resources */,
1E5955CB15B71C8600A03429 /* buttonHighlight.png in Resources */,
1E5955CC15B71C8600A03429 /* buttonHighlight@2x.png in Resources */,

View File

@ -34,7 +34,7 @@
// This must be included before any other PLCrashReporter includes, as
// it redefines symbol names
#import "PLCrashReporterNamespace.h"
#import "PLCrashNamespace.h"
#import "PLCrashReporter.h"
#import "PLCrashReport.h"
@ -93,6 +93,9 @@ typedef enum {
/** The crash report log file is corrupt or invalid */
PLCrashReporterErrorCrashReportInvalid = 2,
/** An attempt to use a resource which was in use at the time in a manner which would have conflicted with the request. */
PLCrashReporterErrorResourceBusy = 3
} PLCrashReporterError;
@ -233,7 +236,65 @@ typedef enum {
*/
/**
* @page mach_exceptions Mach Exceptions on iOS
* @page mach_exceptions Mach Exceptions on Mac OS X and iOS
*
* PLCrashReporter includes support for monitoring crashes via an in-process Mach exception handler. On Mac OS X, the
* Mach exception implementation is fully supported using entirely public API. On iOS, the APIs required are not fully
* public -- more details on the implications of this for exception handling on iOS may be found in
* @ref mach_exceptions_ios below.
*
* It is worth noting that even where the Mach exception APIs are fully supported, kernel-internal constants, as well
* as architecture-specific trap information, may be required to fully interpret a Mach exception's root cause.
*
* For example, the EXC_SOFTWARE exception is dispatched for four different failure types, using the exception
* code to differentiate failure types:
* - Non-existent system call invoked (SIGSYS)
* - Write on a pipe with no reader (SIGPIPE)
* - Abort program (SIGABRT)
* - Kill program (SIGKILL)
*
* Of those four types, only the constant required to interpret the SIGKILL behavior (EXC_SOFT_SIGNAL) is publicly defined.
* Of the remaining three failure types, the constant values are kernel implementation-private, defined only in the available
* kernel sources. On iOS, these sources are unavailable, and while they generally do match the Mac OS X implementation, there
* are no gaurantees that this is -- or will remain -- the case in the future.
*
* Likewise, interpretation of particular fault types requires information regarding the underlying machine traps
* that triggered the Mach exceptions. For example, a floating point trap on x86/x86-64 will trigger an EXC_ARITHMETIC,
* with a subcode value containing the value of the FPU status register. Determining the exact FPU cause requires
* extracting the actual exception flags from status register as per the x86 architecture documentation. The exact format
* of this subcode value is not actually documented outside the kernel, and may change in future releases.
*
* While we have the advantage of access to the x86 kernel sources, the situation on ARM is even less clear. The actual
* use of the Mach exception codes and subcodes is largely undefined by both headers and publicly available documentation,
* and the available x86 kernel sources are of little use in interpreting this data.
*
* As such, while Mach exceptions may catch some cases that BSD signals can not, they are not a perfect solution,
* and may also provide less insight into the actual failures that occur. By comparison, the BSD signal interface
* is both fully defined and architecture independent, with any necessary interpretation of the Mach exception
* codes handled in-kernel at the time of exception dispatch. It is generally recommended by Apple as the preferred
* interface, and should generally be preferred by PLCrashReporter API clients.
*
* @section mach_exceptions_compatibility Compatibility Issues
*
* @subsection Debuggers
*
* Enabling in-process Mach exception handlers will conflict with any attached debuggers; the debugger
* may suspend the processes Mach exception handling thread, which will result in any exception messages
* sent via the debugger being lost, as the in-process handler will be unable to receive and forward
* the messages.
*
* @subsection Managed Runtimes (Xamarin, Unity)
*
* A Mach exception handler may conflict with any managed runtime that registers a BSD signal handler that
* can safely handle otherwise fatal signals, allowing execution to proceed. This includes products
* such as Xamarin for iOS.
*
* In such a case, PLCrashReporter will write a crash report for non-fatal signals, as there is no
* immediate mechanism for determining whether a signal handler exists and that it can safely
* handle the failure. This can result in unexpected delays in application execution, increased I/O to
* disk, and other undesirable operations.
*
* @section mach_exceptions_ios Mach Exceptions on iOS
*
* The APIs required for Mach exception handling are not fully public on iOS. Unfortunately, there are a number
* of crash states that can only be handled with Mach exception handlers:

View File

@ -0,0 +1,105 @@
/*
* Author: Landon Fuller <landonf@plausible.coop>
*
* Copyright (c) 2012-2013 Plausible Labs Cooperative, Inc.
* All rights reserved.
*
* Permission is hereby granted, free of charge, to any person
* obtaining a copy of this software and associated documentation
* files (the "Software"), to deal in the Software without
* restriction, including without limitation the rights to use,
* copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the
* Software is furnished to do so, subject to the following
* conditions:
*
* The above copyright notice and this permission notice shall be
* included in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
* OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
* HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
* WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
* OTHER DEALINGS IN THE SOFTWARE.
*/
#ifndef PLCRASH_FEATURE_CONFIG_H
#define PLCRASH_FEATURE_CONFIG_H
#include <TargetConditionals.h>
/**
* @internal
*
* Build-time configuration for PLCrashReporter.
*
* This is used to automatically enable/disable features on a per-platform and per-configuration
* basis; it may also be used by third-party vendors to configure a custom build of PLCrashReporter.
*
* @defgroup build_config Build Configuration
* @ingroup constants
* @{
*/
/*
* Defaults
*/
/*
* For release builds, disable unused unwind implementations on targets that do not use them. For non-release
* builds, we include the unwind implementations to allow testing on a broader range of targets; it's possible
* that both compact and/or DWARF unwinding could be implemented by Apple for iOS/ARM in the future.
*/
#ifdef PLCF_RELEASE_BUILD
# if TARGET_OS_IPHONE && !TARGET_IPHONE_SIMULATOR
# ifndef PLCRASH_FEATURE_UNWIND_DWARF
# define PLCRASH_FEATURE_UNWIND_DWARF 0
# endif
# ifndef PLCRASH_FEATURE_UNWIND_COMPACT
# define PLCRASH_FEATURE_UNWIND_COMPACT 0
# endif
# endif
#endif
/*
* Configuration Flags
*/
#ifndef PLCRASH_FEATURE_MACH_EXCEPTIONS
/**
* If true, enable Mach exception support. On Mac OS X, the Mach exception implementation is fully supported,
* using publicly available API. On iOS, the APIs required for a complete implementation are not public. However, a
* popular commercial crash reporter is now shipping with support for Mach exceptions, which implies that either
* they've received special dispensation to use private APIs / private structures, they've found another way to do
* it, or they're just using undocumented functionality and hoping for the best.
*
* The exposed surface of undocumented API usage is relatively low, and there has been strong user demand to
* implement Mach exception handling regardless of concerns over API visiblity. Given this, we've enabled
* Mach exception handling by default, and provided both build-time and runtime configuration
* to disable its use.
*
* For more information on the potential issues with enabling mach exception support, @sa @ref mach_exceptions.
*/
# define PLCRASH_FEATURE_MACH_EXCEPTIONS 1
#endif
#ifndef PLCRASH_FEATURE_UNWIND_DWARF
/** If true, enable DWARF unwinding support. DWARF unwinding is currently only used by Mac OS X. */
# define PLCRASH_FEATURE_UNWIND_DWARF 1
#endif
#ifndef PLCRASH_FEATURE_UNWIND_COMPACT
/** If true, enable compact unwinding support. This is only used by Mac OS X. */
# define PLCRASH_FEATURE_UNWIND_COMPACT 1
#endif
/**
* @}
*/
#endif /* PLCRASH_FEATURE_CONFIG_H */

View File

@ -35,6 +35,7 @@
* This may be used to avoid symbol conflicts between multiple libraries
* that may both incorporate PLCrashReporter.
*/
// #define PLCRASHREPORTER_PREFIX AcmeCo
#define PLCRASHREPORTER_PREFIX BIT
#ifdef PLCRASHREPORTER_PREFIX
@ -66,5 +67,14 @@
#define PLCrashReportHostOperatingSystem PLNS(PLCrashReportHostOperatingSystem)
#define PLCrashReporterErrorDomain PLNS(PLCrashReporterErrorDomain)
#define PLCrashReporterException PLNS(PLCrashReporterException)
#define PLCrashHostInfo PLNS(PLCrashHostInfo)
#define PLCrashMachExceptionPort PLNS(PLCrashMachExceptionPort)
#define PLCrashMachExceptionPortSet PLNS(PLCrashMachExceptionPortSet)
#define PLCrashProcessInfo PLNS(PLCrashProcessInfo)
#define PLCrashReporterConfig PLNS(PLCrashReporterConfig)
#define PLCrashUncaughtExceptionHandler PLNS(PLCrashUncaughtExceptionHandler)
#define PLCrashMachExceptionForward PLNS(PLCrashMachExceptionForward)
#define PLCrashSignalHandlerForward PLNS(PLCrashSignalHandlerForward)
#define plcrash_signal_handler PLNS(plcrash_signal_handler)
#endif

View File

@ -32,6 +32,7 @@
#import "PLCrashReportBinaryImageInfo.h"
#import "PLCrashReportExceptionInfo.h"
#import "PLCrashReportMachineInfo.h"
#import "PLCrashReportMachExceptionInfo.h"
#import "PLCrashReportProcessInfo.h"
#import "PLCrashReportProcessorInfo.h"
#import "PLCrashReportRegisterInfo.h"
@ -98,6 +99,9 @@ typedef struct _PLCrashReportDecoder _PLCrashReportDecoder;
/** Signal info */
PLCrashReportSignalInfo *_signalInfo;
/** Mach exception info */
PLCrashReportMachExceptionInfo *_machExceptionInfo;
/** Thread info (PLCrashReportThreadInfo instances) */
NSArray *_threads;
@ -153,6 +157,18 @@ typedef struct _PLCrashReportDecoder _PLCrashReportDecoder;
*/
@property(nonatomic, readonly) PLCrashReportSignalInfo *signalInfo;
/**
* Mach exception information, if available. This will only be included in the
* case that encoding crash reporter's exception-based reporting was enabled, and a Mach
* exception was caught.
*
* @warning If Mach exception information is available, the legacy signalInfo property will also be provided; this
* s required to maintain backwards compatibility with the established API. Note, however, that the signal info may be derived from the
* Mach exception info by the encoding crash reporter, and thus may not exactly match the kernel exception-to-signal
* mappings implemented in xnu. As such, when Mach exception info is available, its use should be preferred.
*/
@property(nonatomic, readonly) PLCrashReportMachExceptionInfo *machExceptionInfo;
/**
* Thread information. Returns a list of PLCrashReportThreadInfo instances.
*/

View File

@ -1,9 +1,7 @@
/*
* Author: Andreas Linde <mail@andreaslinde.de>
* Kent Sutherland
* Author: Landon Fuller <landonf@plausiblelabs.com>
*
* Copyright (c) 2012-2013 HockeyApp, Bit Stadium GmbH.
* Copyright (c) 2011 Andreas Linde & Kent Sutherland.
* Copyright (c) 2013 Plausible Labs Cooperative, Inc.
* All rights reserved.
*
* Permission is hereby granted, free of charge, to any person
@ -30,15 +28,21 @@
#import <Foundation/Foundation.h>
@interface BITCrashManager () {
@interface PLCrashReportMachExceptionInfo : NSObject {
@private
/** The Mach exception type. */
uint64_t _type;
/** The Mach exception codes, represented as an ordered array of NSNumber instances. */
NSArray *_codes;
}
//// set the server URL
//@property (nonatomic, retain) NSString *serverURL;
//
//- (id)initWithAppIdentifier:(NSString *)appIdentifier;
//
//- (void)startManager;
- (id) initWithType: (uint64_t) type codes: (NSArray *) codes;
/** The Mach exception type. */
@property(nonatomic, readonly) uint64_t type;
/** The Mach exception codes, represented as an ordered array of 64-bit unsigned NSNumber instances. */
@property(nonatomic, readonly) NSArray *codes;
@end

View File

@ -29,6 +29,11 @@
#import <Foundation/Foundation.h>
#import <mach/mach.h>
#import "PLCrashReporterConfig.h"
@class PLCrashMachExceptionServer;
@class PLCrashMachExceptionPortSet;
/**
* @ingroup functions
*
@ -60,15 +65,37 @@ typedef struct PLCrashReporterCallbacks {
/** An arbitrary user-supplied context value. This value may be NULL. */
void *context;
/** The callback used to report caught signal information. In version 0 of this structure, all crashes will be
* reported via this function. */
/**
* The callback used to report caught signal information. In version 0 of this structure, all crashes will be
* reported via this function.
*
* @warning When using PLCrashReporterSignalHandlerTypeMach, the siginfo_t argument to this function will be derived
* from the Mach exception data, and may be incorrect, or may otherwise not match the expected data as provided via
* PLCrashReporterSignalHandlerTypeBSD. In addition, the provided ucontext_t value will be zero-initialized, and will
* not provide valid thread state.
*
* This callback will be deprecated in favor of a Mach-compatible replacement in a future release; support is maintained
* here to allow clients that rely on post-crash callbacks without thread state to make use of Mach exceptions.
*/
PLCrashReporterPostCrashSignalCallback handleSignal;
} PLCrashReporterCallbacks;
@interface PLCrashReporter : NSObject {
@private
/** Reporter configuration */
PLCrashReporterConfig *_config;
/** YES if the crash reporter has been enabled */
BOOL _enabled;
#if PLCRASH_FEATURE_MACH_EXCEPTIONS
/** The backing Mach exception server, if any. Nil if the reporter has not been enabled, or if
* the configured signal handler type is not PLCrashReporterSignalHandlerTypeMach. */
PLCrashMachExceptionServer *_machServer;
/** Previously registered Mach exception ports, if any. */
PLCrashMachExceptionPortSet *_previousMachPorts;
#endif /* PLCRASH_FEATURE_MACH_EXCEPTIONS */
/** Application identifier */
NSString *_applicationIdentifier;
@ -82,6 +109,8 @@ typedef struct PLCrashReporterCallbacks {
+ (PLCrashReporter *) sharedReporter;
- (instancetype) initWithConfiguration: (PLCrashReporterConfig *) config;
- (BOOL) hasPendingCrashReport;
- (NSData *) loadPendingCrashReportData;

View File

@ -0,0 +1,165 @@
/*
* Author: Landon Fuller <landonf@plausible.coop>
*
* Copyright (c) 2013 Plausible Labs Cooperative, Inc.
* All rights reserved.
*
* Permission is hereby granted, free of charge, to any person
* obtaining a copy of this software and associated documentation
* files (the "Software"), to deal in the Software without
* restriction, including without limitation the rights to use,
* copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the
* Software is furnished to do so, subject to the following
* conditions:
*
* The above copyright notice and this permission notice shall be
* included in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
* OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
* HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
* WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
* OTHER DEALINGS IN THE SOFTWARE.
*/
#import <Foundation/Foundation.h>
#import "PLCrashFeatureConfig.h"
/**
* @ingroup enums
* Supported mechanisms for trapping and handling crashes.
*/
typedef NS_ENUM(NSUInteger, PLCrashReporterSignalHandlerType) {
/**
* Trap fatal signals via a sigaction(2)-registered BSD signal handler.
*
* PLCrashReporter's signal handler will supersede previously registered handlers; existing
* handlers will not be called. This behavior may be modified in a future release, and should
* not be relied upon as a mechanism to prevent existing signal handlers from being called.
*
* There are some limitations to signal-based crash handling on Mac OS X and iOS; specifically:
*
* - On Mac OS X, stack overflows will only be handled on the thread on which
* the crash reporter was initialized. This should generally be the main thread.
* - On iOS 6.0 and later, any stack overflows will not be handled due to sigaltstack() being
* non-functional on the device. (see rdar://13002712 - SA_ONSTACK/sigaltstack() ignored on iOS).
* - Some exit paths in Apple's Libc will deregister a signal handler before firing SIGABRT, resulting
* in the signal handler never being called (see rdar://14313497 - ___abort() disables SIGABRT signal
* handlers prior to raising SIGABRT). These __abort()-based checks are:
* - Implemented for unsafe memcpy/strcpy/snprintf C functions.
* - Only enabled when operating on a fixed-width target buffer (in which case the
* compiler rewrites the function calls to the built-in variants, and provides the fixed-width length as an argument).
* - Only trigger in the case that the source data exceeds the size of the fixed width target
* buffer, and the maximum length argument either isn't supplied by the caller (eg, when using strcpy),
* or a too-long argument is supplied (eg, strncpy with a length argument longer than the target buffer),
* AND that argument can't be checked at compile-time.
*/
PLCrashReporterSignalHandlerTypeBSD = 0,
#if PLCRASH_FEATURE_MACH_EXCEPTIONS
/**
* Trap fatal signals via a Mach exception server.
*
* If any existing Mach exception server has been registered for the task, exceptions will be forwarded to that
* exception handler. Should the exceptions be handled by an existing handler, no report will be generated
* by PLCrashReporter.
*
* @par Mac OS X
*
* On Mac OS X, the Mach exception implementation is fully supported, using publicly available API -- note,
* however, that some kernel-internal constants, as well as architecture-specific trap information,
* may be required to fully interpret a Mach exception's root cause.
*
* @par iOS
*
* On iOS, the APIs required for a complete implementation are not fully public.
*
* The exposed surface of undocumented API usage is relatively low, and there has been strong user demand to
* implement Mach exception handling regardless of concerns over API visiblity. Given this, we've included
* Mach exception handling as an optional feature, with both build-time and runtime configuration
* to disable its inclusion or use, respectively.
*
* @par Debugger Incompatibility
*
* The Mach exception handler executes in-process, and will interfere with debuggers when they attempt to
* suspend all active threads (which will include the Mach exception handler). Mach-based handling
* should not be used when a debugger is attached.
*
* @par More Details
*
* For more information, refer to @ref mach_exceptions.
*/
PLCrashReporterSignalHandlerTypeMach = 1
#endif /* PLCRASH_FEATURE_MACH_EXCEPTIONS */
};
/**
* @ingroup enums
* Supported mechanisms for performing local symbolication.
*
* Local symbolication is performed using inexact heuristics and symbol data available at runtime; it may
* return information that is incorrect. This may still be useful in the case where DWARF data is unavailable
* for a given build; in that case, it can provide function and method names (though not line numbers) for a
* crash report that may otherwise be unusable.
*
* Note, however, this comes at the cost of a significant increase in code that must run within the critical
* crash reporting section, where failures may result in crash reports being corrupted or left unwritten. In
* addition, some of the provided symbolication strategies rely on knowledge of runtime internals that may
* change in future iOS releases. Given that DWARF symbolication data will <em>always</em> be more accurate, and
* the risks inherent in executing considerably more code at crash time, it is strongly recommended that local
* symbolication only be enabled for non-release builds.
*
* Multiple symbolication strategies may be enabled, in which case a best-match heuristic will be applied to the
* results.
*/
typedef NS_OPTIONS(NSUInteger, PLCrashReporterSymbolicationStrategy) {
/** No symbolication. */
PLCrashReporterSymbolicationStrategyNone = 0,
/**
* Use the standard binary symbol table. On iOS, this alone will return
* incomplete results, as most symbols are rewritten to the common '\<redacted>' string.
*/
PLCrashReporterSymbolicationStrategySymbolTable = 1 << 0,
/**
* Use Objective-C metadata to find method and class names. This relies on detailed parsing
* of the Objective-C runtime data, including undefined flags and other runtime internals. As such,
* it may return incorrect data should the runtime be changed incompatibly.
*/
PLCrashReporterSymbolicationStrategyObjC = 1 << 1,
/**
* Enable all available symbolication strategies.
*/
PLCrashReporterSymbolicationStrategyAll = (PLCrashReporterSymbolicationStrategySymbolTable|PLCrashReporterSymbolicationStrategyObjC)
};
@interface PLCrashReporterConfig : NSObject {
@private
/** The configured signal handler type. */
PLCrashReporterSignalHandlerType _signalHandlerType;
/** The configured symbolication strategy. */
PLCrashReporterSymbolicationStrategy _symbolicationStrategy;
}
+ (instancetype) defaultConfiguration;
- (instancetype) init;
- (instancetype) initWithSignalHandlerType: (PLCrashReporterSignalHandlerType) signalHandlerType
symbolicationStrategy: (PLCrashReporterSymbolicationStrategy) symbolicationStrategy;
/** The configured signal handler type. */
@property(nonatomic, readonly) PLCrashReporterSignalHandlerType signalHandlerType;
/** The configured symbolication strategy. */
@property(nonatomic, readonly) PLCrashReporterSymbolicationStrategy symbolicationStrategy;
@end

View File

@ -95,101 +95,3 @@ This example code is based on CocoaLumberjack logging into log files:
}
@end
## Advanced HowTo
If you want to restrict the log files to contain only the data from the last application run (from app start until it crashed, ignoring all suspend and resume actions), you follow these steps. (Thanks to Michael Tyson for this hint!)
4. Adjust CocoaLumberjack to initialize a new log file per application start
[_fileLogger performSelector:@selector(currentLogFileHandle)]; // init log file prior to rolling
[_fileLogger rollLogFile];
[_fileLogger performSelector:@selector(currentLogFileHandle)]; // re-init log file to apply roll
5. And when loading the prior (crashed) application run log file, stop the iteration over the log files before the most current one (which is the new session, the one after the crash):
// we start from the last (oldest) one, and stop just before the newest (which is the log for the session after the crash)
for (int index = count - 1; index > 0; index--) {
## Example
@interface BITAppDelegate () <BITCrashManagerDelegate> {}
@property (nonatomic) DDFileLogger *fileLogger;
@end
@implementation BITAppDelegate
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
[self.window makeKeyAndVisible];
// initialize before HockeySDK, so the delegate can access the file logger!
_fileLogger = [[DDFileLogger alloc] init];
_fileLogger.maximumFileSize = (1024 * 64); // 64 KByte
_fileLogger.logFileManager.maximumNumberOfLogFiles = 1;
[_fileLogger performSelector:@selector(currentLogFileHandle)]; // init log file prior to rolling
[_fileLogger rollLogFile];
[_fileLogger performSelector:@selector(currentLogFileHandle)]; // re-init log file to apply roll
[DDLog addLogger:_fileLogger];
[[BITHockeyManager sharedHockeyManager] configureWithIdentifier:@"<>"
delegate:nil];
[[BITHockeyManager sharedHockeyManager] startManager];
// add Xcode console logger if not running in the App Store
if (![[BITHockeyManager sharedHockeyManager] isAppStoreEnvironment]) {
PSDDFormatter *psLogger = [[[PSDDFormatter alloc] init] autorelease];
[[DDTTYLogger sharedInstance] setLogFormatter:psLogger];
[DDLog addLogger:[DDTTYLogger sharedInstance]];
[DDLog addLogger:[DDNSLoggerLogger sharedInstance]];
}
return YES;
}
// get the log content with a maximum byte size
- (NSString *) getLogFilesContentWithMaxSize:(NSInteger)maxSize {
NSMutableString *description = [NSMutableString string];
NSArray *sortedLogFileInfos = [[_fileLogger logFileManager] sortedLogFileInfos];
NSUInteger count = [sortedLogFileInfos count];
// we start from the last (oldest) one, and stop just before the newest (which is the log for the session after the crash)
for (int index = count - 1; index > 0; index--) {
DDLogFileInfo *logFileInfo = [sortedLogFileInfos objectAtIndex:index];
NSData *logData = [[NSFileManager defaultManager] contentsAtPath:[logFileInfo filePath]];
if ([logData length] > 0) {
NSString *result = [[NSString alloc] initWithBytes:[logData bytes]
length:[logData length]
encoding: NSUTF8StringEncoding];
[description appendString:result];
[result release];
}
}
if ([description length] > maxSize) {
description = (NSMutableString *)[description substringWithRange:NSMakeRange([description length]-maxSize-1, maxSize)];
}
return description;
}
#pragma mark - BITCrashManagerDelegate
- (NSString *)applicationLogForCrashManager:(BITCrashManager *)crashManager {
NSString *description = [self getLogFilesContentWithMaxSize:5000]; // 5000 bytes should be enough!
if ([description length] == 0) {
return nil;
} else {
return description;
}
}
@end