From 0cfa00924c11aad16172512e9326d6c13e58acbe Mon Sep 17 00:00:00 2001 From: Andreas Linde Date: Mon, 10 Feb 2014 01:06:59 +0100 Subject: [PATCH 01/43] Add `enableDectionAppKillWhileInForeground` option to BITCrashManager This option implements basic heuristics to detect if the app got killed by the iOS watchdog while running in foreground, which can only happen if: - The app tried to allocate too much memory - Main thread doesn't respond for some time It is not possible to detect all cases where such kills can occur. --- Classes/BITCrashManager.h | 74 ++++++ Classes/BITCrashManager.m | 355 ++++++++++++++++++++++---- Classes/BITCrashReportTextFormatter.h | 1 + Classes/BITCrashReportTextFormatter.m | 117 +++++---- 4 files changed, 443 insertions(+), 104 deletions(-) diff --git a/Classes/BITCrashManager.h b/Classes/BITCrashManager.h index 64f695d60f..37ac20711c 100644 --- a/Classes/BITCrashManager.h +++ b/Classes/BITCrashManager.h @@ -166,6 +166,38 @@ typedef NS_ENUM(NSUInteger, BITCrashManagerStatus) { @property (nonatomic, assign, getter=isMachExceptionHandlerEnabled) BOOL enableMachExceptionHandler; +/** + * Enables heuristics to detect the app getting killed while being in the foreground + * + * This allows it to get a crash report if the app got killed while being in the foreground + * because of now of the following reasons: + * - The main thread was blocked for too long + * - The app took too long to start up + * - The app tried to allocate too much memory. If iOS did send a memory warning before killing the app because of this reason, `didReceiveMemoryWarningInLastSession` returns `YES`. + * - Permitted background duration if main thread is running in an endless loop + * - App failed to resume in time if main thread is running in an endless loop + * + * The following kills can _NOT_ be detected: + * - Terminating the app takes too long + * - Permitted background duration too long for all other cases + * - App failed to resume in time for all other cases + * - possibly more cases + * + * Crash reports triggered by this mechanisms do _NOT_ contain any stack traces since the time of the kill + * cannot be intercepted and hence no stack trace of the time of the kill event can't be gathered. + * + * Default: _NO_ + * + * @warning This is a heuristic and it _MAY_ report false positives! + * + * @see wasKilledInLastSession + * @see didReceiveMemoryWarningInLastSession + * @see [Apple Technical Note TN2151](https://developer.apple.com/library/ios/technotes/tn2151/_index.html) + * @see [Apple Technical Q&A QA1693](https://developer.apple.com/library/ios/qa/qa1693/_index.html) + */ +@property (nonatomic, assign, getter = isAppKillDetectionWhileInForegroundEnabled) BOOL enableAppKillDetectionWhileInForeground; + + /** * Set the callbacks that will be executed prior to program termination after a crash has occurred * @@ -224,6 +256,48 @@ typedef NS_ENUM(NSUInteger, BITCrashManagerStatus) { @property (nonatomic, readonly) BOOL didCrashInLastSession; +/** + Indicates if the app was killed while being in foreground from the iOS + + If `enableDectionAppKillWhileInForeground` is enabled, use this on startup to check if the + app starts the first time after it was killed by iOS in the previous session. + + This can happen if it consumed too much memory or the watchdog killed the app because it + took too long to startup or blocks the main thread for too long, or other reasons. See Apple + documentation: https://developer.apple.com/library/ios/qa/qa1693/_index.html + + See `enableDectionAppKillWhileInForeground` for more details about which kind of kills can be detected. + + @warning This property only has a correct value, once `[BITHockeyManager startManager]` was + invoked! In addition, it is automatically disabled while a debugger session is active! + + @see enableAppKillDetectionWhileInForeground + @see didReceiveMemoryWarningInLastSession + */ +@property (nonatomic, readonly) BOOL wasKilledInLastSession; + + +/** + Indicates if the app did receive a low memory warning in the last session + + It may happen that low memory warning where send but couldn't be logged, since iOS + killed the app before updating the flag in the filesystem did complete. + + This property may be true in case of low memory kills, but it doesn't have to be! Apps + can also be killed without the app ever receiving a low memory warning. + + Also the app could have received a low memory warning, but the reason for being killed was + actually different. + + @warning This property only has a correct value, once `[BITHockeyManager startManager]` was + invoked! + + @see enableAppKillDetectionWhileInForeground + @see wasKilledInLastSession + */ +@property (nonatomic, readonly) BOOL didReceiveMemoryWarningInLastSession; + + /** Provides the time between startup and crash in seconds diff --git a/Classes/BITCrashManager.m b/Classes/BITCrashManager.m index fa1c8bcac9..76f2a4c811 100644 --- a/Classes/BITCrashManager.m +++ b/Classes/BITCrashManager.m @@ -55,6 +55,19 @@ NSString *const kBITCrashManagerStatus = @"BITCrashManagerStatus"; +NSString *const kBITAppWentIntoBackgroundSafely = @"BITAppWentIntoBackgroundSafely"; +NSString *const kBITAppDidReceiveLowMemoryNotification = @"BITAppDidReceiveLowMemoryNotification"; +NSString *const kBITAppVersion = @"BITAppVersion"; +NSString *const kBITAppOSVersion = @"BITAppOSVersion"; +NSString *const kBITAppUUIDs = @"BITAppUUIDs"; + +NSString *const kBITFakeCrashUUID = @"BITFakeCrashUUID"; +NSString *const kBITFakeCrashAppVersion = @"BITFakeCrashAppVersion"; +NSString *const kBITFakeCrashAppBundleIdentifier = @"BITFakeCrashAppBundleIdentifier"; +NSString *const kBITFakeCrashOSVersion = @"BITFakeCrashOSVersion"; +NSString *const kBITFakeCrashDeviceModel = @"BITFakeCrashDeviceModel"; +NSString *const kBITFakeCrashAppBinaryUUID = @"BITFakeCrashAppBinaryUUID"; +NSString *const kBITFakeCrashReport = @"BITFakeCrashAppString"; @interface BITCrashManager () @@ -83,7 +96,13 @@ NSString *const kBITCrashManagerStatus = @"BITCrashManagerStatus"; BOOL _sendingInProgress; BOOL _isSetup; + BOOL _didLogLowMemoryWarning; + id _appDidBecomeActiveObserver; + id _appWillTerminateObserver; + id _appDidEnterBackgroundObserver; + id _appWillEnterForegroundObserver; + id _appDidReceiveLowMemoryWarningObserver; id _networkDidBecomeReachableObserver; } @@ -105,6 +124,7 @@ NSString *const kBITCrashManagerStatus = @"BITCrashManagerStatus"; _didCrashInLastSession = NO; _timeintervalCrashInLastSessionOccured = -1; + _didLogLowMemoryWarning = NO; _approvedCrashReports = [[NSMutableDictionary alloc] init]; @@ -287,20 +307,116 @@ NSString *const kBITCrashManagerStatus = @"BITCrashManagerStatus"; [strongSelf triggerDelayedProcessing]; }]; } + + if (nil == _appWillTerminateObserver) { + _appWillTerminateObserver = [[NSNotificationCenter defaultCenter] addObserverForName:UIApplicationWillTerminateNotification + object:nil + queue:NSOperationQueue.mainQueue + usingBlock:^(NSNotification *note) { + typeof(self) strongSelf = weakSelf; + [strongSelf leavingAppSafely]; + }]; + } + + if (nil == _appDidEnterBackgroundObserver) { + _appDidEnterBackgroundObserver = [[NSNotificationCenter defaultCenter] addObserverForName:UIApplicationDidEnterBackgroundNotification + object:nil + queue:NSOperationQueue.mainQueue + usingBlock:^(NSNotification *note) { + typeof(self) strongSelf = weakSelf; + [strongSelf leavingAppSafely]; + }]; + } + + if (nil == _appWillEnterForegroundObserver) { + _appWillEnterForegroundObserver = [[NSNotificationCenter defaultCenter] addObserverForName:UIApplicationWillEnterForegroundNotification + object:nil + queue:NSOperationQueue.mainQueue + usingBlock:^(NSNotification *note) { + typeof(self) strongSelf = weakSelf; + [strongSelf appEnteredForeground]; + }]; + } + + if (nil == _appDidReceiveLowMemoryWarningObserver) { + _appDidReceiveLowMemoryWarningObserver = [[NSNotificationCenter defaultCenter] addObserverForName:UIApplicationDidReceiveMemoryWarningNotification + object:nil + queue:NSOperationQueue.mainQueue + usingBlock:^(NSNotification *note) { + // we only need to log this once + if (!_didLogLowMemoryWarning) { + [[NSUserDefaults standardUserDefaults] setBool:YES forKey:kBITAppDidReceiveLowMemoryNotification]; + _didLogLowMemoryWarning = YES; + } + }]; + } } - (void) unregisterObservers { - if(_appDidBecomeActiveObserver) { - [[NSNotificationCenter defaultCenter] removeObserver:_appDidBecomeActiveObserver]; - _appDidBecomeActiveObserver = nil; - } + [self unregisterObserver:_appDidBecomeActiveObserver]; + [self unregisterObserver:_appWillTerminateObserver]; + [self unregisterObserver:_appDidEnterBackgroundObserver]; + [self unregisterObserver:_appWillEnterForegroundObserver]; + [self unregisterObserver:_appDidReceiveLowMemoryWarningObserver]; - if(_networkDidBecomeReachableObserver) { - [[NSNotificationCenter defaultCenter] removeObserver:_networkDidBecomeReachableObserver]; - _networkDidBecomeReachableObserver = nil; + [self unregisterObserver:_networkDidBecomeReachableObserver]; +} + +- (void) unregisterObserver:(id)observer { + if (observer) { + [[NSNotificationCenter defaultCenter] removeObserver:observer]; + observer = nil; } } +- (void)leavingAppSafely { + if (self.isAppKillDetectionWhileInForegroundEnabled) + [[NSUserDefaults standardUserDefaults] setBool:YES forKey:kBITAppWentIntoBackgroundSafely]; +} + +- (void)appEnteredForeground { + // we disable kill detection while the debugger is running, since we'd get only false positives if the app is terminated by the user using the debugger + if (self.isDebuggerAttached) { + [[NSUserDefaults standardUserDefaults] setBool:YES forKey:kBITAppWentIntoBackgroundSafely]; + } else if (self.isAppKillDetectionWhileInForegroundEnabled) { + [[NSUserDefaults standardUserDefaults] setBool:NO forKey:kBITAppWentIntoBackgroundSafely]; + + static dispatch_once_t predAppData; + + dispatch_once(&predAppData, ^{ + id bundleVersion = [[NSBundle mainBundle] objectForInfoDictionaryKey:@"CFBundleVersion"]; + if (bundleVersion && [bundleVersion isKindOfClass:[NSString class]]) + [[NSUserDefaults standardUserDefaults] setObject:bundleVersion forKey:kBITAppVersion]; + [[NSUserDefaults standardUserDefaults] setObject:[[UIDevice currentDevice] systemVersion] forKey:kBITAppOSVersion]; + + NSString *uuidString =[NSString stringWithFormat:@"%@", + [self deviceArchitecture], + [self executableUUID] + ]; + + [[NSUserDefaults standardUserDefaults] setObject:uuidString forKey:kBITAppUUIDs]; + }); + } +} + +- (NSString *)deviceArchitecture { + NSString *archName = @"???"; + + size_t size; + cpu_type_t type; + cpu_subtype_t subtype; + size = sizeof(type); + if (sysctlbyname("hw.cputype", &type, &size, NULL, 0)) + return archName; + + size = sizeof(subtype); + if (sysctlbyname("hw.cpusubtype", &subtype, &size, NULL, 0)) + return archName; + + archName = [BITCrashReportTextFormatter bit_archNameFromCPUType:type subType:subtype] ?: @"???"; + + return archName; +} /** * Get the userID from the delegate which should be stored with the crash report @@ -437,6 +553,35 @@ NSString *const kBITCrashManagerStatus = @"BITCrashManagerStatus"; } } +/** + * Write a meta file for a new crash report + * + * @param filename the crash reports temp filename + */ +- (void)storeMetaDataForCrashReportFilename:(NSString *)filename { + NSError *error = NULL; + NSMutableDictionary *metaDict = [NSMutableDictionary dictionaryWithCapacity:4]; + NSString *applicationLog = @""; + NSString *errorString = nil; + + [self addStringValueToKeychain:[self userNameForCrashReport] forKey:[NSString stringWithFormat:@"%@.%@", filename, kBITCrashMetaUserName]]; + [self addStringValueToKeychain:[self userEmailForCrashReport] forKey:[NSString stringWithFormat:@"%@.%@", filename, kBITCrashMetaUserEmail]]; + [self addStringValueToKeychain:[self userIDForCrashReport] forKey:[NSString stringWithFormat:@"%@.%@", filename, 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:[_crashesDir stringByAppendingPathComponent: [filename stringByAppendingPathExtension:@"meta"]] atomically:YES]; + } else { + BITHockeyLog(@"ERROR: Writing crash meta data failed. %@", error); + } +} #pragma mark - PLCrashReporter @@ -481,28 +626,7 @@ NSString *const kBITCrashManagerStatus = @"BITCrashManagerStatus"; [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); - } + [self storeMetaDataForCrashReportFilename:cacheFilename]; } } } @@ -734,10 +858,103 @@ NSString *const kBITCrashManagerStatus = @"BITCrashManagerStatus"; _isSetup = YES; }); } + + if ([[NSUserDefaults standardUserDefaults] valueForKey:kBITAppDidReceiveLowMemoryNotification]) + _didReceiveMemoryWarningInLastSession = [[NSUserDefaults standardUserDefaults] boolForKey:kBITAppDidReceiveLowMemoryNotification]; + + if (!_didCrashInLastSession && self.isAppKillDetectionWhileInForegroundEnabled) { + BOOL didAppSwitchToBackgroundSafely = YES; + + if ([[NSUserDefaults standardUserDefaults] valueForKey:kBITAppWentIntoBackgroundSafely]) + didAppSwitchToBackgroundSafely = [[NSUserDefaults standardUserDefaults] boolForKey:kBITAppWentIntoBackgroundSafely]; + + if (!didAppSwitchToBackgroundSafely) { + NSLog(@"AppHasBeenKilled!"); + + [self createCrashReportForAppKill]; + + _wasKilledInLastSession = YES; + _didCrashInLastSession = YES; + } + } + [self appEnteredForeground]; + [[NSUserDefaults standardUserDefaults] setBool:NO forKey:kBITAppDidReceiveLowMemoryNotification]; + [[NSUserDefaults standardUserDefaults] synchronize]; [self triggerDelayedProcessing]; } +/** + * Creates a fake crash report because the app was killed while being in foreground + */ +- (void)createCrashReportForAppKill { + NSString *fakeReportUUID = bit_UUID(); + + NSString *fakeReportAppVersion = [[NSUserDefaults standardUserDefaults] objectForKey:kBITAppVersion]; + if (!fakeReportAppVersion) + return; + + NSString *fakeReportOSVersion = [[NSUserDefaults standardUserDefaults] objectForKey:kBITAppOSVersion] ?: [[UIDevice currentDevice] systemVersion]; + NSString *fakeReportAppBundleIdentifier = [[NSBundle mainBundle] objectForInfoDictionaryKey:@"CFBundleIdentifier"]; + NSString *fakeReportDeviceModel = [self getDevicePlatform] ?: @"Unknown"; + NSString *fakeReportAppUUIDs = [[NSUserDefaults standardUserDefaults] objectForKey:kBITAppUUIDs] ?: @""; + + NSMutableString *fakeReportString = [NSMutableString string]; + + [fakeReportString appendFormat:@"Incident Identifier: %@\n", fakeReportUUID]; + [fakeReportString appendFormat:@"CrashReporter Key: %@\n", bit_appAnonID() ?: @"???"]; + [fakeReportString appendFormat:@"Hardware Model: %@\n", fakeReportDeviceModel]; + [fakeReportString appendFormat:@"Identifier: %@\n", fakeReportAppBundleIdentifier]; + [fakeReportString appendFormat:@"Version: %@\n", fakeReportAppVersion]; + [fakeReportString appendString:@"Code Type: ARM\n"]; + [fakeReportString appendString:@"\n"]; + + NSLocale *enUSPOSIXLocale = [[NSLocale alloc] initWithLocaleIdentifier:@"en_US_POSIX"]; + NSDateFormatter *rfc3339Formatter = [[NSDateFormatter alloc] init]; + [rfc3339Formatter setLocale:enUSPOSIXLocale]; + [rfc3339Formatter setDateFormat:@"yyyy'-'MM'-'dd'T'HH':'mm':'ss'Z'"]; + [rfc3339Formatter setTimeZone:[NSTimeZone timeZoneForSecondsFromGMT:0]]; + + // we use the current date, since we don't know when the kill actually happened + [fakeReportString appendFormat:@"Date/Time: %@\n", [rfc3339Formatter stringFromDate:[NSDate date]]]; + [fakeReportString appendFormat:@"OS Version: %@\n", fakeReportOSVersion]; + [fakeReportString appendString:@"Report Version: 104\n"]; + [fakeReportString appendString:@"\n"]; + [fakeReportString appendString:@"Exception Type: SIGKILL\n"]; + [fakeReportString appendString:@"Exception Codes: 00000020 at 0x8badf00d\n"]; + [fakeReportString appendString:@"\n"]; + [fakeReportString appendString:@"Application Specific Information:\n"]; + [fakeReportString appendString:@"The application was killed by the iOS watchdog."]; + if (self.didReceiveMemoryWarningInLastSession) { + [fakeReportString appendString:@" The app received at least one Low Memory Warning."]; + } + [fakeReportString appendString:@"\n"]; + + NSString *fakeReportFilename = [NSString stringWithFormat: @"%.0f", [NSDate timeIntervalSinceReferenceDate]]; + + NSString *errorString = nil; + + NSMutableDictionary *rootObj = [NSMutableDictionary dictionaryWithCapacity:2]; + [rootObj setObject:fakeReportUUID forKey:kBITFakeCrashUUID]; + [rootObj setObject:fakeReportAppVersion forKey:kBITFakeCrashAppVersion]; + [rootObj setObject:fakeReportAppBundleIdentifier forKey:kBITFakeCrashAppBundleIdentifier]; + [rootObj setObject:fakeReportOSVersion forKey:kBITFakeCrashOSVersion]; + [rootObj setObject:fakeReportDeviceModel forKey:kBITFakeCrashDeviceModel]; + [rootObj setObject:fakeReportAppUUIDs forKey:kBITFakeCrashAppBinaryUUID]; + [rootObj setObject:fakeReportString forKey:kBITFakeCrashReport]; + + NSData *plist = [NSPropertyListSerialization dataFromPropertyList:(id)rootObj + format:NSPropertyListBinaryFormat_v1_0 + errorDescription:&errorString]; + if (plist) { + if ([plist writeToFile:[_crashesDir stringByAppendingPathComponent:[fakeReportFilename stringByAppendingPathExtension:@"fake"]] atomically:YES]) { + [self storeMetaDataForCrashReportFilename:fakeReportFilename]; + } + } else { + BITHockeyLog(@"ERROR: Writing fake crash report. %@", errorString); + } +} + /** * Send all approved crash reports * @@ -755,13 +972,49 @@ NSString *const kBITCrashManagerStatus = @"BITCrashManagerStatus"; NSData *crashData = [NSData dataWithContentsOfFile:filename]; if ([crashData length] > 0) { - BITPLCrashReport *report = [[BITPLCrashReport alloc] initWithData:crashData error:&error]; + BITPLCrashReport *report = nil; + NSString *crashUUID = @""; + NSString *installString = nil; + NSString *crashLogString = nil; + NSString *appBundleIdentifier = nil; + NSString *appBundleVersion = nil; + NSString *osVersion = nil; + NSString *deviceModel = nil; + NSString *appBinaryUUIDs = nil; + NSString *metaFilename = nil; + + NSString *errorString = nil; + NSPropertyListFormat format; + + if ([[cacheFilename pathExtension] isEqualToString:@"fake"]) { + NSDictionary *fakeReportDict = (NSDictionary *)[NSPropertyListSerialization + propertyListFromData:crashData + mutabilityOption:NSPropertyListMutableContainersAndLeaves + format:&format + errorDescription:&errorString]; + + crashLogString = [fakeReportDict objectForKey:kBITFakeCrashReport]; + crashUUID = [fakeReportDict objectForKey:kBITFakeCrashUUID]; + appBundleIdentifier = [fakeReportDict objectForKey:kBITFakeCrashAppBundleIdentifier]; + appBundleVersion = [fakeReportDict objectForKey:kBITFakeCrashAppVersion]; + appBinaryUUIDs = [fakeReportDict objectForKey:kBITFakeCrashAppBinaryUUID]; + deviceModel = [fakeReportDict objectForKey:kBITFakeCrashDeviceModel]; + osVersion = [fakeReportDict objectForKey:kBITFakeCrashOSVersion]; + + metaFilename = [cacheFilename stringByReplacingOccurrencesOfString:@".fake" withString:@".meta"]; + if ([appBundleVersion compare:[[NSBundle mainBundle] objectForInfoDictionaryKey:@"CFBundleVersion"]] == NSOrderedSame) { + _crashIdenticalCurrentVersion = YES; + } + + } else { + report = [[BITPLCrashReport alloc] initWithData:crashData error:&error]; + } - if (report == nil) { + if (report == nil && crashLogString == nil) { BITHockeyLog(@"WARNING: Could not parse crash report"); // we cannot do anything with this report, so delete it [_fileManager removeItemAtPath:filename error:&error]; - [_fileManager removeItemAtPath:[NSString stringWithFormat:@"%@.meta", filename] error:&error]; + [_fileManager removeItemAtPath:[NSString stringWithFormat:@"%@.meta", metaFilename] error:&error]; [self removeKeyFromKeychain:[NSString stringWithFormat:@"%@.%@", cacheFilename, kBITCrashMetaUserName]]; [self removeKeyFromKeychain:[NSString stringWithFormat:@"%@.%@", cacheFilename, kBITCrashMetaUserEmail]]; @@ -769,16 +1022,23 @@ NSString *const kBITCrashManagerStatus = @"BITCrashManagerStatus"; continue; } - NSString *crashUUID = @""; - if (report.uuidRef != NULL) { - crashUUID = (NSString *) CFBridgingRelease(CFUUIDCreateString(NULL, report.uuidRef)); + if (report) { + if (report.uuidRef != NULL) { + crashUUID = (NSString *) CFBridgingRelease(CFUUIDCreateString(NULL, report.uuidRef)); + } + metaFilename = [filename stringByAppendingPathExtension:@"meta"]; + crashLogString = [BITCrashReportTextFormatter stringValueForCrashReport:report crashReporterKey:installString]; + appBundleIdentifier = report.applicationInfo.applicationIdentifier; + appBundleVersion = report.applicationInfo.applicationVersion; + osVersion = report.systemInfo.operatingSystemVersion; + deviceModel = [self getDevicePlatform]; + appBinaryUUIDs = [self extractAppUUIDs:report]; + if ([report.applicationInfo.applicationVersion compare:[[NSBundle mainBundle] objectForInfoDictionaryKey:@"CFBundleVersion"]] == NSOrderedSame) { + _crashIdenticalCurrentVersion = YES; + } } - NSString *installString = bit_appAnonID() ?: @""; - NSString *crashLogString = [BITCrashReportTextFormatter stringValueForCrashReport:report crashReporterKey:installString]; - if ([report.applicationInfo.applicationVersion compare:[[NSBundle mainBundle] objectForInfoDictionaryKey:@"CFBundleVersion"]] == NSOrderedSame) { - _crashIdenticalCurrentVersion = YES; - } + installString = bit_appAnonID() ?: @""; if (crashes == nil) { crashes = [NSMutableString string]; @@ -790,10 +1050,7 @@ NSString *const kBITCrashManagerStatus = @"BITCrashManagerStatus"; NSString *applicationLog = @""; NSString *description = @""; - NSString *errorString = nil; - NSPropertyListFormat format; - - NSData *plist = [NSData dataWithContentsOfFile:[filename stringByAppendingString:@".meta"]]; + NSData *plist = [NSData dataWithContentsOfFile:[_crashesDir stringByAppendingPathComponent:metaFilename]]; if (plist) { NSDictionary *metaDict = (NSDictionary *)[NSPropertyListSerialization propertyListFromData:plist @@ -815,12 +1072,12 @@ NSString *const kBITCrashManagerStatus = @"BITCrashManagerStatus"; [crashes appendFormat:@"%s%@%@%@%@%@%@%@%@%@%@%@", [[[NSBundle mainBundle] objectForInfoDictionaryKey:@"CFBundleExecutable"] UTF8String], - [self extractAppUUIDs:report], - report.applicationInfo.applicationIdentifier, - report.systemInfo.operatingSystemVersion, - [self getDevicePlatform], + appBinaryUUIDs, + appBundleIdentifier, + osVersion, + deviceModel, [[NSBundle mainBundle] objectForInfoDictionaryKey:@"CFBundleVersion"], - report.applicationInfo.applicationVersion, + appBundleVersion, crashUUID, [crashLogString stringByReplacingOccurrencesOfString:@"]]>" withString:@"]]" @"]]>" options:NSLiteralSearch range:NSMakeRange(0,crashLogString.length)], userid, diff --git a/Classes/BITCrashReportTextFormatter.h b/Classes/BITCrashReportTextFormatter.h index 13ab19f964..5ddef4a22d 100644 --- a/Classes/BITCrashReportTextFormatter.h +++ b/Classes/BITCrashReportTextFormatter.h @@ -49,5 +49,6 @@ + (NSString *)stringValueForCrashReport:(PLCrashReport *)report crashReporterKey:(NSString *)crashReporterKey; + (NSArray *)arrayOfAppUUIDsForCrashReport:(PLCrashReport *)report; ++ (NSString *)bit_archNameFromCPUType:(uint64_t)cpuType subType:(uint64_t)subType; @end diff --git a/Classes/BITCrashReportTextFormatter.m b/Classes/BITCrashReportTextFormatter.m index 6094affc23..c8b060d34c 100644 --- a/Classes/BITCrashReportTextFormatter.m +++ b/Classes/BITCrashReportTextFormatter.m @@ -464,66 +464,73 @@ static NSInteger bit_binaryImageSort(id binary1, id binary2, void *context) { { NSString *archName = @"???"; if (imageInfo.codeType != nil && imageInfo.codeType.typeEncoding == PLCrashReportProcessorTypeEncodingMach) { - switch (imageInfo.codeType.type) { - case CPU_TYPE_ARM: - /* Apple includes subtype for ARM binaries. */ - switch (imageInfo.codeType.subtype) { - case CPU_SUBTYPE_ARM_V6: - archName = @"armv6"; - break; - - case CPU_SUBTYPE_ARM_V7: - archName = @"armv7"; - break; - - case CPU_SUBTYPE_ARM_V7S: - archName = @"armv7s"; - break; - - default: - archName = @"arm-unknown"; - break; - } - break; - - case CPU_TYPE_ARM64: - /* Apple includes subtype for ARM64 binaries. */ - switch (imageInfo.codeType.subtype) { - case CPU_SUBTYPE_ARM_ALL: - archName = @"arm64"; - break; - - case CPU_SUBTYPE_ARM_V8: - archName = @"arm64"; - break; - - default: - archName = @"arm64-unknown"; - break; - } - break; - - case CPU_TYPE_X86: - archName = @"i386"; - break; - - case CPU_TYPE_X86_64: - archName = @"x86_64"; - break; - - case CPU_TYPE_POWERPC: - archName = @"powerpc"; - break; - - default: - // Use the default archName value (initialized above). - break; - } + archName = [BITCrashReportTextFormatter bit_archNameFromCPUType:imageInfo.codeType.type subType:imageInfo.codeType.subtype]; } return archName; } ++ (NSString *)bit_archNameFromCPUType:(uint64_t)cpuType subType:(uint64_t)subType { + NSString *archName = @"???"; + switch (cpuType) { + case CPU_TYPE_ARM: + /* Apple includes subtype for ARM binaries. */ + switch (subType) { + case CPU_SUBTYPE_ARM_V6: + archName = @"armv6"; + break; + + case CPU_SUBTYPE_ARM_V7: + archName = @"armv7"; + break; + + case CPU_SUBTYPE_ARM_V7S: + archName = @"armv7s"; + break; + + default: + archName = @"arm-unknown"; + break; + } + break; + + case CPU_TYPE_ARM64: + /* Apple includes subtype for ARM64 binaries. */ + switch (subType) { + case CPU_SUBTYPE_ARM_ALL: + archName = @"arm64"; + break; + + case CPU_SUBTYPE_ARM_V8: + archName = @"arm64"; + break; + + default: + archName = @"arm64-unknown"; + break; + } + break; + + case CPU_TYPE_X86: + archName = @"i386"; + break; + + case CPU_TYPE_X86_64: + archName = @"x86_64"; + break; + + case CPU_TYPE_POWERPC: + archName = @"powerpc"; + break; + + default: + // Use the default archName value (initialized above). + break; + } + + return archName; +} + /** * Format a stack frame for display in a thread backtrace. From cc480299eb9d96a702821fc944a7550568374ca6 Mon Sep 17 00:00:00 2001 From: Andreas Linde Date: Mon, 10 Feb 2014 01:52:15 +0100 Subject: [PATCH 02/43] Multiple improvements for heuristic based kill detection handling - Marked the feature as `EXPERIMENTAL` - renamed property to `enableAppNotTerminatingCleanlyDetection` - Added details about the heuristic algorithm - Added optional delegate to let the developer influence if a report should be considered as a crash or not - Adjusted the description string in the generated report to make it more clear what actually happened. --- Classes/BITCrashManager.h | 18 +++++++++++++----- Classes/BITCrashManager.m | 23 +++++++++++++++-------- Classes/BITCrashManagerDelegate.h | 21 +++++++++++++++++++++ 3 files changed, 49 insertions(+), 13 deletions(-) diff --git a/Classes/BITCrashManager.h b/Classes/BITCrashManager.h index 37ac20711c..c56d1091a8 100644 --- a/Classes/BITCrashManager.h +++ b/Classes/BITCrashManager.h @@ -167,7 +167,7 @@ typedef NS_ENUM(NSUInteger, BITCrashManagerStatus) { /** - * Enables heuristics to detect the app getting killed while being in the foreground + * EXPERIMENTAL: Enable heuristics to detect the app not terminating cleanly * * This allows it to get a crash report if the app got killed while being in the foreground * because of now of the following reasons: @@ -176,6 +176,7 @@ typedef NS_ENUM(NSUInteger, BITCrashManagerStatus) { * - The app tried to allocate too much memory. If iOS did send a memory warning before killing the app because of this reason, `didReceiveMemoryWarningInLastSession` returns `YES`. * - Permitted background duration if main thread is running in an endless loop * - App failed to resume in time if main thread is running in an endless loop + * - If `enableMachExceptionHandler` is not activated, crashed due to stackoverflow will also be reported * * The following kills can _NOT_ be detected: * - Terminating the app takes too long @@ -186,16 +187,23 @@ typedef NS_ENUM(NSUInteger, BITCrashManagerStatus) { * Crash reports triggered by this mechanisms do _NOT_ contain any stack traces since the time of the kill * cannot be intercepted and hence no stack trace of the time of the kill event can't be gathered. * + * The heuristic is implemented as follows: + * If the app never gets a `UIApplicationDidEnterBackgroundNotification` or `UIApplicationWillTerminateNotification` + * notification, PLCrashReporter doesn't detect a crash itself, and the app starts up again, it is assumed that + * the app got either killed by iOS while being in foreground or a crash occured that couldn't be detected. + * * Default: _NO_ * - * @warning This is a heuristic and it _MAY_ report false positives! + * @warning This is a heuristic and it _MAY_ report false positives! It has been tested with iOS 6.1 and iOS 7. + * Depending on Apple changing notification events, new iOS version may cause more false positives! * * @see wasKilledInLastSession * @see didReceiveMemoryWarningInLastSession + * @see `BITCrashManagerDelegate considerAppNotTerminatedCleanlyReportForCrashManager:` * @see [Apple Technical Note TN2151](https://developer.apple.com/library/ios/technotes/tn2151/_index.html) * @see [Apple Technical Q&A QA1693](https://developer.apple.com/library/ios/qa/qa1693/_index.html) */ -@property (nonatomic, assign, getter = isAppKillDetectionWhileInForegroundEnabled) BOOL enableAppKillDetectionWhileInForeground; +@property (nonatomic, assign, getter = isAppNotTerminatingCleanlyDetectionEnabled) BOOL enableAppNotTerminatingCleanlyDetection; /** @@ -271,7 +279,7 @@ typedef NS_ENUM(NSUInteger, BITCrashManagerStatus) { @warning This property only has a correct value, once `[BITHockeyManager startManager]` was invoked! In addition, it is automatically disabled while a debugger session is active! - @see enableAppKillDetectionWhileInForeground + @see enableAppNotTerminatingCleanlyDetection @see didReceiveMemoryWarningInLastSession */ @property (nonatomic, readonly) BOOL wasKilledInLastSession; @@ -292,7 +300,7 @@ typedef NS_ENUM(NSUInteger, BITCrashManagerStatus) { @warning This property only has a correct value, once `[BITHockeyManager startManager]` was invoked! - @see enableAppKillDetectionWhileInForeground + @see enableAppNotTerminatingCleanlyDetection @see wasKilledInLastSession */ @property (nonatomic, readonly) BOOL didReceiveMemoryWarningInLastSession; diff --git a/Classes/BITCrashManager.m b/Classes/BITCrashManager.m index 76f2a4c811..e7280a92ec 100644 --- a/Classes/BITCrashManager.m +++ b/Classes/BITCrashManager.m @@ -370,7 +370,7 @@ NSString *const kBITFakeCrashReport = @"BITFakeCrashAppString"; } - (void)leavingAppSafely { - if (self.isAppKillDetectionWhileInForegroundEnabled) + if (self.isAppNotTerminatingCleanlyDetectionEnabled) [[NSUserDefaults standardUserDefaults] setBool:YES forKey:kBITAppWentIntoBackgroundSafely]; } @@ -378,7 +378,7 @@ NSString *const kBITFakeCrashReport = @"BITFakeCrashAppString"; // we disable kill detection while the debugger is running, since we'd get only false positives if the app is terminated by the user using the debugger if (self.isDebuggerAttached) { [[NSUserDefaults standardUserDefaults] setBool:YES forKey:kBITAppWentIntoBackgroundSafely]; - } else if (self.isAppKillDetectionWhileInForegroundEnabled) { + } else if (self.isAppNotTerminatingCleanlyDetectionEnabled) { [[NSUserDefaults standardUserDefaults] setBool:NO forKey:kBITAppWentIntoBackgroundSafely]; static dispatch_once_t predAppData; @@ -862,19 +862,26 @@ NSString *const kBITFakeCrashReport = @"BITFakeCrashAppString"; if ([[NSUserDefaults standardUserDefaults] valueForKey:kBITAppDidReceiveLowMemoryNotification]) _didReceiveMemoryWarningInLastSession = [[NSUserDefaults standardUserDefaults] boolForKey:kBITAppDidReceiveLowMemoryNotification]; - if (!_didCrashInLastSession && self.isAppKillDetectionWhileInForegroundEnabled) { + if (!_didCrashInLastSession && self.isAppNotTerminatingCleanlyDetectionEnabled) { BOOL didAppSwitchToBackgroundSafely = YES; if ([[NSUserDefaults standardUserDefaults] valueForKey:kBITAppWentIntoBackgroundSafely]) didAppSwitchToBackgroundSafely = [[NSUserDefaults standardUserDefaults] boolForKey:kBITAppWentIntoBackgroundSafely]; if (!didAppSwitchToBackgroundSafely) { - NSLog(@"AppHasBeenKilled!"); + BOOL considerReport = YES; - [self createCrashReportForAppKill]; + if (self.delegate && + [self.delegate respondsToSelector:@selector(considerAppNotTerminatedCleanlyReportForCrashManager:)]) { + considerReport = [self.delegate considerAppNotTerminatedCleanlyReportForCrashManager:self]; + } - _wasKilledInLastSession = YES; - _didCrashInLastSession = YES; + if (considerReport) { + [self createCrashReportForAppKill]; + + _wasKilledInLastSession = YES; + _didCrashInLastSession = YES; + } } } [self appEnteredForeground]; @@ -924,7 +931,7 @@ NSString *const kBITFakeCrashReport = @"BITFakeCrashAppString"; [fakeReportString appendString:@"Exception Codes: 00000020 at 0x8badf00d\n"]; [fakeReportString appendString:@"\n"]; [fakeReportString appendString:@"Application Specific Information:\n"]; - [fakeReportString appendString:@"The application was killed by the iOS watchdog."]; + [fakeReportString appendString:@"The application did not terminate cleanly but no crash occured."]; if (self.didReceiveMemoryWarningInLastSession) { [fakeReportString appendString:@" The app received at least one Low Memory Warning."]; } diff --git a/Classes/BITCrashManagerDelegate.h b/Classes/BITCrashManagerDelegate.h index e2792b5ce4..0c3174c5a1 100644 --- a/Classes/BITCrashManagerDelegate.h +++ b/Classes/BITCrashManagerDelegate.h @@ -130,4 +130,25 @@ */ - (void)crashManagerDidFinishSendingCrashReport:(BITCrashManager *)crashManager; +///----------------------------------------------------------------------------- +/// @name Experimental +///----------------------------------------------------------------------------- + +/** Define if a report should be considered as a crash report + + Due to the risk, that these reports may be false positives, this delegates allows the + developer to influence which reports detected by the heuristic should actually be reported. + + The developer can use the following property to get more information about the crash scenario: + - `[BITCrashManager didReceiveMemoryWarningInLastSession]`: Did the app receive a low memory warning + + This allows only reports to be considered where at least one low memory warning notification was + received by the app to reduce to possibility of having false positives. + + @param crashManager The `BITCrashManager` instance invoking this delegate + @return `YES` if the heuristic based detected report should be reported, otherwise `NO` + @see `[BITCrashManager didReceiveMemoryWarningInLastSession]` + */ +-(BOOL)considerAppNotTerminatedCleanlyReportForCrashManager:(BITCrashManager *)crashManager; + @end From 4d414b78f4cd233de35357fe0149eb5a4f55c84b Mon Sep 17 00:00:00 2001 From: Andreas Linde Date: Mon, 10 Feb 2014 13:10:10 +0100 Subject: [PATCH 03/43] Make sure the flag that a memory warning was received is persisted right away NSUserDefaults may not synchronize changes immediately, so if the kill happens before it persists changes, the information is lost. --- Classes/BITCrashManager.m | 1 + 1 file changed, 1 insertion(+) diff --git a/Classes/BITCrashManager.m b/Classes/BITCrashManager.m index e7280a92ec..27b686c49c 100644 --- a/Classes/BITCrashManager.m +++ b/Classes/BITCrashManager.m @@ -346,6 +346,7 @@ NSString *const kBITFakeCrashReport = @"BITFakeCrashAppString"; // we only need to log this once if (!_didLogLowMemoryWarning) { [[NSUserDefaults standardUserDefaults] setBool:YES forKey:kBITAppDidReceiveLowMemoryNotification]; + [[NSUserDefaults standardUserDefaults] synchronize]; _didLogLowMemoryWarning = YES; } }]; From ddf16f70623078ac4540a1d55a5279f235d073e9 Mon Sep 17 00:00:00 2001 From: Andreas Linde Date: Mon, 10 Feb 2014 13:30:46 +0100 Subject: [PATCH 04/43] Add a new line at the end of the `application specific information` string to be considered by the servers grouping --- Classes/BITCrashManager.m | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Classes/BITCrashManager.m b/Classes/BITCrashManager.m index 27b686c49c..7f0457371c 100644 --- a/Classes/BITCrashManager.m +++ b/Classes/BITCrashManager.m @@ -936,7 +936,7 @@ NSString *const kBITFakeCrashReport = @"BITFakeCrashAppString"; if (self.didReceiveMemoryWarningInLastSession) { [fakeReportString appendString:@" The app received at least one Low Memory Warning."]; } - [fakeReportString appendString:@"\n"]; + [fakeReportString appendString:@"\n\n"]; NSString *fakeReportFilename = [NSString stringWithFormat: @"%.0f", [NSDate timeIntervalSinceReferenceDate]]; From 5a88cab36eff7ae130bd4fe96b79a00455ce0636 Mon Sep 17 00:00:00 2001 From: Andreas Linde Date: Wed, 19 Feb 2014 23:38:54 +0100 Subject: [PATCH 05/43] Fix referencing wrong property name --- Classes/BITCrashManager.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Classes/BITCrashManager.h b/Classes/BITCrashManager.h index 5bad86eb8c..b958cc8913 100644 --- a/Classes/BITCrashManager.h +++ b/Classes/BITCrashManager.h @@ -281,7 +281,7 @@ typedef NS_ENUM(NSUInteger, BITCrashManagerStatus) { /** Indicates if the app was killed while being in foreground from the iOS - If `enableDectionAppKillWhileInForeground` is enabled, use this on startup to check if the + If `enableAppNotTerminatingCleanlyDetection` is enabled, use this on startup to check if the app starts the first time after it was killed by iOS in the previous session. This can happen if it consumed too much memory or the watchdog killed the app because it From c62b444272645f428abfef314e607e24e0deea2e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Lukas=20Spie=C3=9F?= Date: Fri, 28 Mar 2014 12:30:45 +0100 Subject: [PATCH 06/43] start refactoring AlertView delegate --- Classes/BITCrashManager.h | 14 ++++++++++ Classes/BITCrashManager.m | 55 ++++++++++++++++++++------------------- 2 files changed, 42 insertions(+), 27 deletions(-) diff --git a/Classes/BITCrashManager.h b/Classes/BITCrashManager.h index bf651da527..a524c2b6ab 100644 --- a/Classes/BITCrashManager.h +++ b/Classes/BITCrashManager.h @@ -58,6 +58,16 @@ typedef NS_ENUM(NSUInteger, BITCrashManagerStatus) { BITCrashManagerStatusAutoSend = 2 }; +typedef NS_ENUM(NSUInteger, BITCrashManagerUserInput) { + + BITCrashManagerUserInputDontSend, + + BITCrashManagerUserInputSend, + + BITCrashManagerUserInputAlwaysSend + +}; + @protocol BITCrashManagerDelegate; @@ -163,6 +173,10 @@ typedef NS_ENUM(NSUInteger, BITCrashManagerStatus) { * the debugger during runtime, this may cause issues the Mach exception handler is enabled! * @see isDebuggerAttached */ + +@property (nonatomic, assign) BITCrashManagerUserInput crashManagerUserInput; + + @property (nonatomic, assign, getter=isMachExceptionHandlerEnabled) BOOL enableMachExceptionHandler; diff --git a/Classes/BITCrashManager.m b/Classes/BITCrashManager.m index 18b3aaf270..be655b91f6 100644 --- a/Classes/BITCrashManager.m +++ b/Classes/BITCrashManager.m @@ -930,34 +930,35 @@ NSString *const kBITCrashManagerStatus = @"BITCrashManagerStatus"; #pragma mark - UIAlertView Delegate - (void)alertView:(UIAlertView *)alertView didDismissWithButtonIndex:(NSInteger)buttonIndex { - switch (buttonIndex) { - case 0: - if (self.delegate != nil && [self.delegate respondsToSelector:@selector(crashManagerWillCancelSendingCrashReport:)]) { - [self.delegate crashManagerWillCancelSendingCrashReport:self]; - } - - _sendingInProgress = NO; - [self cleanCrashReports]; - break; - case 1: - [self sendCrashReports]; - break; - case 2: { - _crashManagerStatus = BITCrashManagerStatusAutoSend; - [[NSUserDefaults standardUserDefaults] setInteger:_crashManagerStatus forKey:kBITCrashManagerStatus]; - [[NSUserDefaults standardUserDefaults] synchronize]; - if (self.delegate != nil && [self.delegate respondsToSelector:@selector(crashManagerWillSendCrashReportsAlways:)]) { - [self.delegate crashManagerWillSendCrashReportsAlways:self]; - } - - [self sendCrashReports]; - break; + _crashManagerUserInput = buttonIndex; + [self handleUserInput]; +} + +- (void)handleUserInput { + switch (_crashManagerUserInput) { + case BITCrashManagerUserInputDontSend: + if (self.delegate != nil && [self.delegate respondsToSelector:@selector(crashManagerWillCancelSendingCrashReport:)]) { + [self.delegate crashManagerWillCancelSendingCrashReport:self]; + } + + _sendingInProgress = NO; + [self cleanCrashReports]; + break; + case BITCrashManagerUserInputSend: + [self sendCrashReports]; + break; + case BITCrashManagerUserInputAlwaysSend: + _crashManagerStatus = BITCrashManagerStatusAutoSend; + [[NSUserDefaults standardUserDefaults] setInteger:_crashManagerStatus forKey:kBITCrashManagerStatus]; + [[NSUserDefaults standardUserDefaults] synchronize]; + if (self.delegate != nil && [self.delegate respondsToSelector:@selector(crashManagerWillSendCrashReportsAlways:)]) { + [self.delegate crashManagerWillSendCrashReportsAlways:self]; + } + + [self sendCrashReports]; + break; } - default: - _sendingInProgress = NO; - [self cleanCrashReports]; - break; - } + } From 50b0eb9bd1591d625a2c6c48c9a10823a50d122e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Lukas=20Spie=C3=9F?= Date: Fri, 28 Mar 2014 12:39:06 +0100 Subject: [PATCH 07/43] remove unnecessary property --- Classes/BITCrashManager.h | 4 ---- Classes/BITCrashManager.m | 7 +++---- 2 files changed, 3 insertions(+), 8 deletions(-) diff --git a/Classes/BITCrashManager.h b/Classes/BITCrashManager.h index a524c2b6ab..dfff2f67f4 100644 --- a/Classes/BITCrashManager.h +++ b/Classes/BITCrashManager.h @@ -173,10 +173,6 @@ typedef NS_ENUM(NSUInteger, BITCrashManagerUserInput) { * the debugger during runtime, this may cause issues the Mach exception handler is enabled! * @see isDebuggerAttached */ - -@property (nonatomic, assign) BITCrashManagerUserInput crashManagerUserInput; - - @property (nonatomic, assign, getter=isMachExceptionHandlerEnabled) BOOL enableMachExceptionHandler; diff --git a/Classes/BITCrashManager.m b/Classes/BITCrashManager.m index be655b91f6..8dfff2863b 100644 --- a/Classes/BITCrashManager.m +++ b/Classes/BITCrashManager.m @@ -930,12 +930,11 @@ NSString *const kBITCrashManagerStatus = @"BITCrashManagerStatus"; #pragma mark - UIAlertView Delegate - (void)alertView:(UIAlertView *)alertView didDismissWithButtonIndex:(NSInteger)buttonIndex { - _crashManagerUserInput = buttonIndex; - [self handleUserInput]; + [self handleUserInput:buttonIndex]; } -- (void)handleUserInput { - switch (_crashManagerUserInput) { +- (void)handleUserInput:(BITCrashManagerUserInput)userInput { + switch (userInput) { case BITCrashManagerUserInputDontSend: if (self.delegate != nil && [self.delegate respondsToSelector:@selector(crashManagerWillCancelSendingCrashReport:)]) { [self.delegate crashManagerWillCancelSendingCrashReport:self]; From 1f96aaab5ee49a0b46490ab0e9065f596de34187 Mon Sep 17 00:00:00 2001 From: Andreas Linde Date: Thu, 3 Apr 2014 19:43:53 +0200 Subject: [PATCH 08/43] Fix installString not being added to the crash report --- Classes/BITCrashManager.m | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Classes/BITCrashManager.m b/Classes/BITCrashManager.m index 4164ec221d..bbdef08496 100644 --- a/Classes/BITCrashManager.m +++ b/Classes/BITCrashManager.m @@ -1029,6 +1029,8 @@ NSString *const kBITFakeCrashReport = @"BITFakeCrashAppString"; continue; } + installString = bit_appAnonID() ?: @""; + if (report) { if (report.uuidRef != NULL) { crashUUID = (NSString *) CFBridgingRelease(CFUUIDCreateString(NULL, report.uuidRef)); @@ -1045,8 +1047,6 @@ NSString *const kBITFakeCrashReport = @"BITFakeCrashAppString"; } } - installString = bit_appAnonID() ?: @""; - if (crashes == nil) { crashes = [NSMutableString string]; } From e66714afcaeb72f3bb753564ce556e62902fc08b Mon Sep 17 00:00:00 2001 From: Andreas Linde Date: Thu, 3 Apr 2014 17:34:15 +0200 Subject: [PATCH 09/43] Define our own crash callback struct, so we don't need the PLCrashReporter headers to be public any longer --- Classes/BITCrashManager.h | 44 ++++++++++++++++----- Classes/BITCrashManager.m | 39 +++++++++++++++++- Classes/BITCrashManagerPrivate.h | 2 + Support/HockeySDK.xcodeproj/project.pbxproj | 2 +- 4 files changed, 74 insertions(+), 13 deletions(-) diff --git a/Classes/BITCrashManager.h b/Classes/BITCrashManager.h index b958cc8913..13b605ec5c 100644 --- a/Classes/BITCrashManager.h +++ b/Classes/BITCrashManager.h @@ -32,14 +32,6 @@ #import "BITHockeyBaseManager.h" -// We need this check depending on integrating as a subproject or using the binary distribution -#if __has_include("CrashReporter.h") -#import "CrashReporter.h" -#else -#import -#endif - - /** * Crash Manager status */ @@ -59,6 +51,35 @@ typedef NS_ENUM(NSUInteger, BITCrashManagerStatus) { }; +/** + * Prototype of a callback function used to execute additional user code. Called upon completion of crash + * handling, after the crash report has been written to disk. + * + * @param context The API client's supplied context value. + * + * @see `BITCrashManagerCallbacks` + * @see `[BITCrashManager setCrashCallbacks:]` + */ +typedef void (*BITCrashManagerPostCrashSignalCallback)(void *context); + +/** + * This structure contains callbacks supported by `BITCrashManager` to allow the host application to perform + * additional tasks prior to program termination after a crash has occured. + * + * @see `BITCrashManagerPostCrashSignalCallback` + * @see `[BITCrashManager setCrashCallbacks:]` + */ +typedef struct BITCrashManagerCallbacks { + /** An arbitrary user-supplied context value. This value may be NULL. */ + void *context; + + /** + * The callback used to report caught signal information. + */ + BITCrashManagerPostCrashSignalCallback handleSignal; +} BITCrashManagerCallbacks; + + @protocol BITCrashManagerDelegate; /** @@ -234,15 +255,18 @@ typedef NS_ENUM(NSUInteger, BITCrashManagerStatus) { * * _Async-Safe Functions_ * - * A subset of functions are defined to be async-safe by the OS, and are safely callable from within a signal handler. If you do implement a custom post-crash handler, it must be async-safe. A table of POSIX-defined async-safe functions and additional information is available from the CERT programming guide - SIG30-C, see https://www.securecoding.cert.org/confluence/display/seccode/SIG30-C.+Call+only+asynchronous-safe+functions+within+signal+handlers + * A subset of functions are defined to be async-safe by the OS, and are safely callable from within a signal handler. If you do implement a custom post-crash handler, it must be async-safe. A table of POSIX-defined async-safe functions and additional information is available from the [CERT programming guide - SIG30-C](https://www.securecoding.cert.org/confluence/display/seccode/SIG30-C.+Call+only+asynchronous-safe+functions+within+signal+handlers). * * Most notably, the Objective-C runtime itself is not async-safe, and Objective-C may not be used within a signal handler. * * Documentation taken from PLCrashReporter: https://www.plcrashreporter.org/documentation/api/v1.2-rc2/async_safety.html * + * @see `BITCrashManagerPostCrashSignalCallback` + * @see `BITCrashManagerCallbacks` + * * @param callbacks A pointer to an initialized PLCrashReporterCallback structure, see https://www.plcrashreporter.org/documentation/api/v1.2-rc2/struct_p_l_crash_reporter_callbacks.html */ -- (void)setCrashCallbacks: (PLCrashReporterCallbacks *) callbacks; +- (void)setCrashCallbacks: (BITCrashManagerCallbacks *) callbacks; /** diff --git a/Classes/BITCrashManager.m b/Classes/BITCrashManager.m index bbdef08496..867ffc9353 100644 --- a/Classes/BITCrashManager.m +++ b/Classes/BITCrashManager.m @@ -32,6 +32,8 @@ #if HOCKEYSDK_FEATURE_CRASH_REPORTER +#import + #import #import @@ -69,6 +71,25 @@ NSString *const kBITFakeCrashDeviceModel = @"BITFakeCrashDeviceModel"; NSString *const kBITFakeCrashAppBinaryUUID = @"BITFakeCrashAppBinaryUUID"; NSString *const kBITFakeCrashReport = @"BITFakeCrashAppString"; + +static BITCrashManagerCallbacks bitCrashCallbacks = { + .context = NULL, + .handleSignal = NULL +}; + +// proxy implementation for PLCrashReporter to keep our interface stable while this can change +static void plcr_post_crash_callback (siginfo_t *info, ucontext_t *uap, void *context) { + if (bitCrashCallbacks.handleSignal != NULL) + bitCrashCallbacks.handleSignal(context); +} + +static PLCrashReporterCallbacks plCrashCallbacks = { + .version = 0, + .context = NULL, + .handleSignal = plcr_post_crash_callback +}; + + @interface BITCrashManager () @property (nonatomic, strong) NSFileManager *fileManager; @@ -498,8 +519,22 @@ NSString *const kBITFakeCrashReport = @"BITFakeCrashAppString"; #pragma mark - Public -- (void)setCrashCallbacks: (PLCrashReporterCallbacks *) callbacks { - _crashCallBacks = callbacks; +/** + * Set the callback for PLCrashReporter + * + * @param callbacks BITCrashManagerCallbacks instance + */ +- (void)setCrashCallbacks: (BITCrashManagerCallbacks *) callbacks { + if (!callbacks) return; + + // set our proxy callback struct + bitCrashCallbacks.context = callbacks->context; + bitCrashCallbacks.handleSignal = callbacks->handleSignal; + + // set the PLCrashReporterCallbacks struct + plCrashCallbacks.context = callbacks->context; + + _crashCallBacks = &plCrashCallbacks; } /** diff --git a/Classes/BITCrashManagerPrivate.h b/Classes/BITCrashManagerPrivate.h index f2231342f4..7951cc7a17 100644 --- a/Classes/BITCrashManagerPrivate.h +++ b/Classes/BITCrashManagerPrivate.h @@ -31,6 +31,8 @@ #if HOCKEYSDK_FEATURE_CRASH_REPORTER +#import + @interface BITCrashManager () { } diff --git a/Support/HockeySDK.xcodeproj/project.pbxproj b/Support/HockeySDK.xcodeproj/project.pbxproj index 910428df15..a661402c8c 100644 --- a/Support/HockeySDK.xcodeproj/project.pbxproj +++ b/Support/HockeySDK.xcodeproj/project.pbxproj @@ -833,7 +833,7 @@ ); runOnlyForDeploymentPostprocessing = 0; shellPath = /bin/sh; - shellScript = "# Sets the target folders and the final framework product.\nFMK_NAME=HockeySDK\nFMK_VERSION=A\nFMK_RESOURCE_BUNDLE=HockeySDKResources\n\n# Documentation\nHOCKEYSDK_DOCSET_VERSION_NAME=\"de.bitstadium.${HOCKEYSDK_DOCSET_NAME}-${VERSION_STRING}\"\n\n# Install dir will be the final output to the framework.\n# The following line create it in the root folder of the current project.\nPRODUCTS_DIR=${SRCROOT}/../Products\nPLCR_DIR=${SRCROOT}/../Vendor/CrashReporter.framework\nZIP_FOLDER=HockeySDK-iOS\nTEMP_DIR=${PRODUCTS_DIR}/${ZIP_FOLDER}\nINSTALL_DIR=${TEMP_DIR}/${FMK_NAME}.framework\n\n# Working dir will be deleted after the framework creation.\nWRK_DIR=build\nDEVICE_DIR=${WRK_DIR}/Release-iphoneos\nSIMULATOR_DIR=${WRK_DIR}/Release-iphonesimulator\nHEADERS_DIR=${WRK_DIR}/Release-iphoneos/usr/local/include\n\n# Building both architectures.\nxcodebuild -project \"HockeySDK.xcodeproj\" -configuration \"Release\" -target \"${FMK_NAME}\" -sdk iphoneos\nxcodebuild -project \"HockeySDK.xcodeproj\" -configuration \"Release\" -target \"${FMK_NAME}\" -sdk iphonesimulator\n\n# Cleaning the oldest.\nif [ -d \"${TEMP_DIR}\" ]\nthen\nrm -rf \"${TEMP_DIR}\"\nfi\n\n# Creates and renews the final product folder.\nmkdir -p \"${INSTALL_DIR}\"\nmkdir -p \"${INSTALL_DIR}/Versions\"\nmkdir -p \"${INSTALL_DIR}/Versions/${FMK_VERSION}\"\nmkdir -p \"${INSTALL_DIR}/Versions/${FMK_VERSION}/Resources\"\nmkdir -p \"${INSTALL_DIR}/Versions/${FMK_VERSION}/Headers\"\n\n# Creates the internal links.\n# It MUST uses relative path, otherwise will not work when the folder is copied/moved.\nln -s \"${FMK_VERSION}\" \"${INSTALL_DIR}/Versions/Current\"\nln -s \"Versions/Current/Headers\" \"${INSTALL_DIR}/Headers\"\nln -s \"Versions/Current/Resources\" \"${INSTALL_DIR}/Resources\"\nln -s \"Versions/Current/${FMK_NAME}\" \"${INSTALL_DIR}/${FMK_NAME}\"\n\n# Copies the headers and resources files to the final product folder.\ncp -R \"${SRCROOT}/build/Release-iphoneos/include/HockeySDK/\" \"${INSTALL_DIR}/Versions/${FMK_VERSION}/Headers/\"\ncp -R \"${PLCR_DIR}/Versions/A/Headers/\" \"${INSTALL_DIR}/Versions/${FMK_VERSION}/Headers/\"\ncp -R \"${DEVICE_DIR}/${FMK_RESOURCE_BUNDLE}.bundle\" \"${INSTALL_DIR}/Versions/${FMK_VERSION}/Resources/\"\ncp -f \"${SRCROOT}/${FMK_NAME}.xcconfig\" \"${INSTALL_DIR}/Versions/${FMK_VERSION}/Resources/\"\n\n# Uses the Lipo Tool to merge both binary files (i386 + armv6/armv7) into one Universal final product.\nlipo -create \"${DEVICE_DIR}/lib${FMK_NAME}.a\" \"${SIMULATOR_DIR}/lib${FMK_NAME}.a\" -output \"${INSTALL_DIR}/Versions/${FMK_VERSION}/${FMK_NAME}\"\n\n# Combine the CrashReporter static library into a new Hockey static library file if they are not already present and copy the public headers too\nif [ -z $(otool -L \"${INSTALL_DIR}/Versions/${FMK_VERSION}/${FMK_NAME}\" | grep 'libCrashReporter') ]\nthen\nlibtool -static -o \"${INSTALL_DIR}/Versions/${FMK_VERSION}/${FMK_NAME}\" \"${INSTALL_DIR}/Versions/${FMK_VERSION}/${FMK_NAME}\" \"${SRCROOT}/../Vendor/CrashReporter.framework/Versions/A/CrashReporter\"\nfi\n\nrm -r \"${WRK_DIR}\"\n\n# build embeddedframework folder and move framework into it\nmkdir \"${INSTALL_DIR}/../${FMK_NAME}.embeddedframework\"\nmv \"${INSTALL_DIR}\" \"${INSTALL_DIR}/../${FMK_NAME}.embeddedframework/${FMK_NAME}.framework\"\n\n# link Resources\nNEW_INSTALL_DIR=${TEMP_DIR}/${FMK_NAME}.embeddedframework\nmkdir \"${NEW_INSTALL_DIR}/Resources\"\nln -s \"../${FMK_NAME}.framework/Resources/${FMK_RESOURCE_BUNDLE}.bundle\" \"${NEW_INSTALL_DIR}/Resources/${FMK_RESOURCE_BUNDLE}.bundle\"\nln -s \"../${FMK_NAME}.framework/Resources/${FMK_NAME}.xcconfig\" \"${NEW_INSTALL_DIR}/Resources/${FMK_NAME}.xcconfig\"\n\n# copy license, changelog, documentation, integration json\ncp -f \"${SRCROOT}/../Docs/Changelog-template.md\" \"${TEMP_DIR}/CHANGELOG\"\ncp -f \"${SRCROOT}/../Docs/Guide-Installation-Setup-template.md\" \"${TEMP_DIR}/README.md\"\ncp -f \"${SRCROOT}/../LICENSE\" \"${TEMP_DIR}\"\nmkdir \"${TEMP_DIR}/${HOCKEYSDK_DOCSET_VERSION_NAME}.docset\"\ncp -R \"${SRCROOT}/../documentation/docset/Contents\" \"${TEMP_DIR}/${HOCKEYSDK_DOCSET_VERSION_NAME}.docset\"\n\n# build zip\ncd \"${PRODUCTS_DIR}\"\nrm -f \"${FMK_NAME}-iOS-${VERSION_STRING}.zip\"\nzip -yr \"${FMK_NAME}-iOS-${VERSION_STRING}.zip\" \"${ZIP_FOLDER}\" -x \\*/.*\n\n#copy to output dir on cisimple\nif [ $CISIMPLE ]; then\n if [ ! -d \"${CONFIGURATION_BUILD_DIR}\" ]; then\n mkdir \"${CONFIGURATION_BUILD_DIR}\"\n fi\n cd \"${PRODUCTS_DIR}\"\n cp \"${FMK_NAME}-iOS-${VERSION_STRING}.zip\" \"${CONFIGURATION_BUILD_DIR}/${FMK_NAME}-iOS-${VERSION_STRING}.zip\"\nfi"; + shellScript = "# Sets the target folders and the final framework product.\nFMK_NAME=HockeySDK\nFMK_VERSION=A\nFMK_RESOURCE_BUNDLE=HockeySDKResources\n\n# Documentation\nHOCKEYSDK_DOCSET_VERSION_NAME=\"de.bitstadium.${HOCKEYSDK_DOCSET_NAME}-${VERSION_STRING}\"\n\n# Install dir will be the final output to the framework.\n# The following line create it in the root folder of the current project.\nPRODUCTS_DIR=${SRCROOT}/../Products\nZIP_FOLDER=HockeySDK-iOS\nTEMP_DIR=${PRODUCTS_DIR}/${ZIP_FOLDER}\nINSTALL_DIR=${TEMP_DIR}/${FMK_NAME}.framework\n\n# Working dir will be deleted after the framework creation.\nWRK_DIR=build\nDEVICE_DIR=${WRK_DIR}/Release-iphoneos\nSIMULATOR_DIR=${WRK_DIR}/Release-iphonesimulator\nHEADERS_DIR=${WRK_DIR}/Release-iphoneos/usr/local/include\n\n# Building both architectures.\nxcodebuild -project \"HockeySDK.xcodeproj\" -configuration \"Release\" -target \"${FMK_NAME}\" -sdk iphoneos\nxcodebuild -project \"HockeySDK.xcodeproj\" -configuration \"Release\" -target \"${FMK_NAME}\" -sdk iphonesimulator\n\n# Cleaning the oldest.\nif [ -d \"${TEMP_DIR}\" ]\nthen\nrm -rf \"${TEMP_DIR}\"\nfi\n\n# Creates and renews the final product folder.\nmkdir -p \"${INSTALL_DIR}\"\nmkdir -p \"${INSTALL_DIR}/Versions\"\nmkdir -p \"${INSTALL_DIR}/Versions/${FMK_VERSION}\"\nmkdir -p \"${INSTALL_DIR}/Versions/${FMK_VERSION}/Resources\"\nmkdir -p \"${INSTALL_DIR}/Versions/${FMK_VERSION}/Headers\"\n\n# Creates the internal links.\n# It MUST uses relative path, otherwise will not work when the folder is copied/moved.\nln -s \"${FMK_VERSION}\" \"${INSTALL_DIR}/Versions/Current\"\nln -s \"Versions/Current/Headers\" \"${INSTALL_DIR}/Headers\"\nln -s \"Versions/Current/Resources\" \"${INSTALL_DIR}/Resources\"\nln -s \"Versions/Current/${FMK_NAME}\" \"${INSTALL_DIR}/${FMK_NAME}\"\n\n# Copies the headers and resources files to the final product folder.\ncp -R \"${SRCROOT}/build/Release-iphoneos/include/HockeySDK/\" \"${INSTALL_DIR}/Versions/${FMK_VERSION}/Headers/\"\ncp -R \"${DEVICE_DIR}/${FMK_RESOURCE_BUNDLE}.bundle\" \"${INSTALL_DIR}/Versions/${FMK_VERSION}/Resources/\"\ncp -f \"${SRCROOT}/${FMK_NAME}.xcconfig\" \"${INSTALL_DIR}/Versions/${FMK_VERSION}/Resources/\"\n\n# Uses the Lipo Tool to merge both binary files (i386 + armv6/armv7) into one Universal final product.\nlipo -create \"${DEVICE_DIR}/lib${FMK_NAME}.a\" \"${SIMULATOR_DIR}/lib${FMK_NAME}.a\" -output \"${INSTALL_DIR}/Versions/${FMK_VERSION}/${FMK_NAME}\"\n\n# Combine the CrashReporter static library into a new Hockey static library file if they are not already present and copy the public headers too\nif [ -z $(otool -L \"${INSTALL_DIR}/Versions/${FMK_VERSION}/${FMK_NAME}\" | grep 'libCrashReporter') ]\nthen\nlibtool -static -o \"${INSTALL_DIR}/Versions/${FMK_VERSION}/${FMK_NAME}\" \"${INSTALL_DIR}/Versions/${FMK_VERSION}/${FMK_NAME}\" \"${SRCROOT}/../Vendor/CrashReporter.framework/Versions/A/CrashReporter\"\nfi\n\nrm -r \"${WRK_DIR}\"\n\n# build embeddedframework folder and move framework into it\nmkdir \"${INSTALL_DIR}/../${FMK_NAME}.embeddedframework\"\nmv \"${INSTALL_DIR}\" \"${INSTALL_DIR}/../${FMK_NAME}.embeddedframework/${FMK_NAME}.framework\"\n\n# link Resources\nNEW_INSTALL_DIR=${TEMP_DIR}/${FMK_NAME}.embeddedframework\nmkdir \"${NEW_INSTALL_DIR}/Resources\"\nln -s \"../${FMK_NAME}.framework/Resources/${FMK_RESOURCE_BUNDLE}.bundle\" \"${NEW_INSTALL_DIR}/Resources/${FMK_RESOURCE_BUNDLE}.bundle\"\nln -s \"../${FMK_NAME}.framework/Resources/${FMK_NAME}.xcconfig\" \"${NEW_INSTALL_DIR}/Resources/${FMK_NAME}.xcconfig\"\n\n# copy license, changelog, documentation, integration json\ncp -f \"${SRCROOT}/../Docs/Changelog-template.md\" \"${TEMP_DIR}/CHANGELOG\"\ncp -f \"${SRCROOT}/../Docs/Guide-Installation-Setup-template.md\" \"${TEMP_DIR}/README.md\"\ncp -f \"${SRCROOT}/../LICENSE\" \"${TEMP_DIR}\"\nmkdir \"${TEMP_DIR}/${HOCKEYSDK_DOCSET_VERSION_NAME}.docset\"\ncp -R \"${SRCROOT}/../documentation/docset/Contents\" \"${TEMP_DIR}/${HOCKEYSDK_DOCSET_VERSION_NAME}.docset\"\n\n# build zip\ncd \"${PRODUCTS_DIR}\"\nrm -f \"${FMK_NAME}-iOS-${VERSION_STRING}.zip\"\nzip -yr \"${FMK_NAME}-iOS-${VERSION_STRING}.zip\" \"${ZIP_FOLDER}\" -x \\*/.*\n\n#copy to output dir on cisimple\nif [ $CISIMPLE ]; then\nif [ ! -d \"${CONFIGURATION_BUILD_DIR}\" ]; then\nmkdir \"${CONFIGURATION_BUILD_DIR}\"\nfi\ncd \"${PRODUCTS_DIR}\"\ncp \"${FMK_NAME}-iOS-${VERSION_STRING}.zip\" \"${CONFIGURATION_BUILD_DIR}/${FMK_NAME}-iOS-${VERSION_STRING}.zip\"\nfi"; }; 1E8E66B215BC3D8200632A2E /* ShellScript */ = { isa = PBXShellScriptBuildPhase; From 06adc38535ee44d79d1479bf81fccafff9c78c75 Mon Sep 17 00:00:00 2001 From: Andreas Linde Date: Thu, 3 Apr 2014 19:57:35 +0200 Subject: [PATCH 10/43] Add a generic object which contains some basic information about the crash in the last session --- Classes/BITCrashDetails.h | 35 ++++++++++++++++++ Classes/BITCrashDetails.m | 33 +++++++++++++++++ Classes/BITCrashManager.h | 13 +++---- Classes/BITCrashManager.m | 39 +++++++++++++++++++-- Classes/HockeySDK.h | 1 + Support/HockeySDK.xcodeproj/project.pbxproj | 8 +++++ 6 files changed, 120 insertions(+), 9 deletions(-) create mode 100644 Classes/BITCrashDetails.h create mode 100644 Classes/BITCrashDetails.m diff --git a/Classes/BITCrashDetails.h b/Classes/BITCrashDetails.h new file mode 100644 index 0000000000..1c5988f71b --- /dev/null +++ b/Classes/BITCrashDetails.h @@ -0,0 +1,35 @@ +// +// BITCrashDetails.h +// HockeySDK +// +// Created by Andreas Linde on 03.04.14. +// +// + +#import + +@interface BITCrashDetails : NSObject + +@property (nonatomic, readonly, strong) NSString *incidentIdentifier; + +@property (nonatomic, readonly, strong) NSString *reporterKey; + +@property (nonatomic, readonly, strong) NSString *signal; + +@property (nonatomic, readonly, strong) NSString *exceptionName; + +@property (nonatomic, readonly, strong) NSString *exceptionReason; + +@property (nonatomic, readonly, strong) NSDate *appStartTime; + +@property (nonatomic, readonly, strong) NSDate *crashTime; + +- (instancetype)initWithIncidentIdentifier:(NSString *)incidentIdentifier + reporterKey:(NSString *)reporterKey + signal:(NSString *)signal + exceptionName:(NSString *)exceptionName + exceptionReason:(NSString *)exceptionReason + appStartTime:(NSDate *)appStartTime + crashTime:(NSDate *)crashTime; + +@end diff --git a/Classes/BITCrashDetails.m b/Classes/BITCrashDetails.m new file mode 100644 index 0000000000..d201fac9c1 --- /dev/null +++ b/Classes/BITCrashDetails.m @@ -0,0 +1,33 @@ +// +// BITCrashDetails.m +// HockeySDK +// +// Created by Andreas Linde on 03.04.14. +// +// + +#import "BITCrashDetails.h" + +@implementation BITCrashDetails + +- (instancetype)initWithIncidentIdentifier:(NSString *)incidentIdentifier + reporterKey:(NSString *)reporterKey + signal:(NSString *)signal + exceptionName:(NSString *)exceptionName + exceptionReason:(NSString *)exceptionReason + appStartTime:(NSDate *)appStartTime + crashTime:(NSDate *)crashTime; +{ + if ((self = [super init])) { + _incidentIdentifier = incidentIdentifier; + _reporterKey = reporterKey; + _signal = signal; + _exceptionName = exceptionName; + _exceptionReason = exceptionReason; + _appStartTime = appStartTime; + _crashTime = crashTime; + } + return self; +} + +@end diff --git a/Classes/BITCrashManager.h b/Classes/BITCrashManager.h index b958cc8913..6025d95b7c 100644 --- a/Classes/BITCrashManager.h +++ b/Classes/BITCrashManager.h @@ -32,12 +32,7 @@ #import "BITHockeyBaseManager.h" -// We need this check depending on integrating as a subproject or using the binary distribution -#if __has_include("CrashReporter.h") -#import "CrashReporter.h" -#else -#import -#endif +@class BITCrashDetails; /** @@ -299,6 +294,12 @@ typedef NS_ENUM(NSUInteger, BITCrashManagerStatus) { @property (nonatomic, readonly) BOOL wasKilledInLastSession; +/** + * Provides details about the crash that occured in the last app session + */ +@property (nonatomic, readonly) BITCrashDetails *lastSessionCrashDetails; + + /** Indicates if the app did receive a low memory warning in the last session diff --git a/Classes/BITCrashManager.m b/Classes/BITCrashManager.m index bbdef08496..6ce744277e 100644 --- a/Classes/BITCrashManager.m +++ b/Classes/BITCrashManager.m @@ -612,8 +612,12 @@ NSString *const kBITFakeCrashReport = @"BITFakeCrashAppString"; if (report == nil) { BITHockeyLog(@"WARNING: Could not parse crash report"); } else { + NSDate *appStartTime = nil; + NSDate *appCrashTime = nil; if ([report.processInfo respondsToSelector:@selector(processStartTime)]) { if (report.systemInfo.timestamp && report.processInfo.processStartTime) { + appStartTime = report.processInfo.processStartTime; + appCrashTime =report.systemInfo.timestamp; _timeintervalCrashInLastSessionOccured = [report.systemInfo.timestamp timeIntervalSinceDate:report.processInfo.processStartTime]; } } @@ -621,6 +625,22 @@ NSString *const kBITFakeCrashReport = @"BITFakeCrashAppString"; [crashData writeToFile:[_crashesDir stringByAppendingPathComponent: cacheFilename] atomically:YES]; [self storeMetaDataForCrashReportFilename:cacheFilename]; + + NSString *incidentIdentifier = @"???"; + if (report.uuidRef != NULL) { + incidentIdentifier = (NSString *) CFBridgingRelease(CFUUIDCreateString(NULL, report.uuidRef)); + } + + NSString *reporterKey = bit_appAnonID() ?: @""; + + _lastSessionCrashDetails = [[BITCrashDetails alloc] initWithIncidentIdentifier:incidentIdentifier + reporterKey:reporterKey + signal:report.signalInfo.name + exceptionName:report.exceptionInfo.exceptionName + exceptionReason:report.exceptionInfo.exceptionReason + appStartTime:appStartTime + crashTime:appCrashTime + ]; } } } @@ -896,6 +916,7 @@ NSString *const kBITFakeCrashReport = @"BITFakeCrashAppString"; */ - (void)createCrashReportForAppKill { NSString *fakeReportUUID = bit_UUID(); + NSString *fakeReporterKey = bit_appAnonID() ?: @"???"; NSString *fakeReportAppVersion = [[NSUserDefaults standardUserDefaults] objectForKey:kBITAppVersion]; if (!fakeReportAppVersion) @@ -906,10 +927,12 @@ NSString *const kBITFakeCrashReport = @"BITFakeCrashAppString"; NSString *fakeReportDeviceModel = [self getDevicePlatform] ?: @"Unknown"; NSString *fakeReportAppUUIDs = [[NSUserDefaults standardUserDefaults] objectForKey:kBITAppUUIDs] ?: @""; + NSString *fakeSignalName = @"SIGKILL"; + NSMutableString *fakeReportString = [NSMutableString string]; [fakeReportString appendFormat:@"Incident Identifier: %@\n", fakeReportUUID]; - [fakeReportString appendFormat:@"CrashReporter Key: %@\n", bit_appAnonID() ?: @"???"]; + [fakeReportString appendFormat:@"CrashReporter Key: %@\n", fakeReporterKey]; [fakeReportString appendFormat:@"Hardware Model: %@\n", fakeReportDeviceModel]; [fakeReportString appendFormat:@"Identifier: %@\n", fakeReportAppBundleIdentifier]; [fakeReportString appendFormat:@"Version: %@\n", fakeReportAppVersion]; @@ -921,13 +944,14 @@ NSString *const kBITFakeCrashReport = @"BITFakeCrashAppString"; [rfc3339Formatter setLocale:enUSPOSIXLocale]; [rfc3339Formatter setDateFormat:@"yyyy'-'MM'-'dd'T'HH':'mm':'ss'Z'"]; [rfc3339Formatter setTimeZone:[NSTimeZone timeZoneForSecondsFromGMT:0]]; + NSString *fakeCrashTimestamp = [rfc3339Formatter stringFromDate:[NSDate date]]; // we use the current date, since we don't know when the kill actually happened - [fakeReportString appendFormat:@"Date/Time: %@\n", [rfc3339Formatter stringFromDate:[NSDate date]]]; + [fakeReportString appendFormat:@"Date/Time: %@\n", fakeCrashTimestamp]; [fakeReportString appendFormat:@"OS Version: %@\n", fakeReportOSVersion]; [fakeReportString appendString:@"Report Version: 104\n"]; [fakeReportString appendString:@"\n"]; - [fakeReportString appendString:@"Exception Type: SIGKILL\n"]; + [fakeReportString appendFormat:@"Exception Type: %@\n", fakeSignalName]; [fakeReportString appendString:@"Exception Codes: 00000020 at 0x8badf00d\n"]; [fakeReportString appendString:@"\n"]; [fakeReportString appendString:@"Application Specific Information:\n"]; @@ -950,6 +974,15 @@ NSString *const kBITFakeCrashReport = @"BITFakeCrashAppString"; [rootObj setObject:fakeReportAppUUIDs forKey:kBITFakeCrashAppBinaryUUID]; [rootObj setObject:fakeReportString forKey:kBITFakeCrashReport]; + _lastSessionCrashDetails = [[BITCrashDetails alloc] initWithIncidentIdentifier:fakeReportUUID + reporterKey:fakeReporterKey + signal:fakeSignalName + exceptionName:nil + exceptionReason:nil + appStartTime:nil + crashTime:nil + ]; + NSData *plist = [NSPropertyListSerialization dataFromPropertyList:(id)rootObj format:NSPropertyListBinaryFormat_v1_0 errorDescription:&errorString]; diff --git a/Classes/HockeySDK.h b/Classes/HockeySDK.h index aa1c4da3ff..fe9d7b9837 100644 --- a/Classes/HockeySDK.h +++ b/Classes/HockeySDK.h @@ -38,6 +38,7 @@ #if HOCKEYSDK_FEATURE_CRASH_REPORTER #import "BITCrashManager.h" #import "BITCrashManagerDelegate.h" +#import "BITCrashDetails.h" #endif /* HOCKEYSDK_FEATURE_CRASH_REPORTER */ #if HOCKEYSDK_FEATURE_UPDATES || HOCKEYSDK_FEATURE_JIRA_MOBILE_CONNECT diff --git a/Support/HockeySDK.xcodeproj/project.pbxproj b/Support/HockeySDK.xcodeproj/project.pbxproj index 910428df15..8627e93a9f 100644 --- a/Support/HockeySDK.xcodeproj/project.pbxproj +++ b/Support/HockeySDK.xcodeproj/project.pbxproj @@ -111,6 +111,8 @@ 1E7A45FC16F54FB5005B08F1 /* OCHamcrestIOS.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 1E7A45FA16F54FB5005B08F1 /* OCHamcrestIOS.framework */; }; 1E7A45FD16F54FB5005B08F1 /* OCMockitoIOS.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 1E7A45FB16F54FB5005B08F1 /* OCMockitoIOS.framework */; }; 1E84DB3417E099BA00AC83FD /* HockeySDKFeatureConfig.h in Headers */ = {isa = PBXBuildFile; fileRef = 1E84DB3317E0977C00AC83FD /* HockeySDKFeatureConfig.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 1E90FD7318EDB86400CF0417 /* BITCrashDetails.h in Headers */ = {isa = PBXBuildFile; fileRef = 1E90FD7118EDB86400CF0417 /* BITCrashDetails.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 1E90FD7418EDB86400CF0417 /* BITCrashDetails.m in Sources */ = {isa = PBXBuildFile; fileRef = 1E90FD7218EDB86400CF0417 /* BITCrashDetails.m */; }; 1E94F9E116E91330006570AD /* BITStoreUpdateManager.h in Headers */ = {isa = PBXBuildFile; fileRef = 1E94F9DF16E91330006570AD /* BITStoreUpdateManager.h */; settings = {ATTRIBUTES = (Public, ); }; }; 1E94F9E216E91330006570AD /* BITStoreUpdateManager.m in Sources */ = {isa = PBXBuildFile; fileRef = 1E94F9E016E91330006570AD /* BITStoreUpdateManager.m */; }; 1E94F9E416E9136B006570AD /* BITStoreUpdateManagerPrivate.h in Headers */ = {isa = PBXBuildFile; fileRef = 1E94F9E316E9136B006570AD /* BITStoreUpdateManagerPrivate.h */; }; @@ -268,6 +270,8 @@ 1E7A45FA16F54FB5005B08F1 /* OCHamcrestIOS.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; path = OCHamcrestIOS.framework; sourceTree = ""; }; 1E7A45FB16F54FB5005B08F1 /* OCMockitoIOS.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; path = OCMockitoIOS.framework; sourceTree = ""; }; 1E84DB3317E0977C00AC83FD /* HockeySDKFeatureConfig.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = HockeySDKFeatureConfig.h; sourceTree = ""; }; + 1E90FD7118EDB86400CF0417 /* BITCrashDetails.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = BITCrashDetails.h; sourceTree = ""; }; + 1E90FD7218EDB86400CF0417 /* BITCrashDetails.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = BITCrashDetails.m; sourceTree = ""; }; 1E94F9DF16E91330006570AD /* BITStoreUpdateManager.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = BITStoreUpdateManager.h; sourceTree = ""; }; 1E94F9E016E91330006570AD /* BITStoreUpdateManager.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = BITStoreUpdateManager.m; sourceTree = ""; }; 1E94F9E316E9136B006570AD /* BITStoreUpdateManagerPrivate.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = BITStoreUpdateManagerPrivate.h; sourceTree = ""; }; @@ -478,6 +482,8 @@ children = ( 1E754E561621FBB70070AB92 /* BITCrashManager.h */, 1E754E571621FBB70070AB92 /* BITCrashManager.m */, + 1E90FD7118EDB86400CF0417 /* BITCrashDetails.h */, + 1E90FD7218EDB86400CF0417 /* BITCrashDetails.m */, 1EFF03D717F20F8300A5F13C /* BITCrashManagerPrivate.h */, 1E754E581621FBB70070AB92 /* BITCrashManagerDelegate.h */, 1E754E5A1621FBB70070AB92 /* BITCrashReportTextFormatter.h */, @@ -635,6 +641,7 @@ 1E5955FD15B7877B00A03429 /* BITHockeyManagerDelegate.h in Headers */, 1E754E5C1621FBB70070AB92 /* BITCrashManager.h in Headers */, 1E754E5E1621FBB70070AB92 /* BITCrashManagerDelegate.h in Headers */, + 1E90FD7318EDB86400CF0417 /* BITCrashDetails.h in Headers */, 1E49A4731612226D00463151 /* BITUpdateManager.h in Headers */, 1E49A4791612226D00463151 /* BITUpdateManagerDelegate.h in Headers */, 1E49A44E1612223B00463151 /* BITFeedbackManager.h in Headers */, @@ -886,6 +893,7 @@ 1E49A4C1161222B900463151 /* BITHockeyHelper.m in Sources */, 1E49A4C7161222B900463151 /* BITAppStoreHeader.m in Sources */, 1E49A4CD161222B900463151 /* BITStoreButton.m in Sources */, + 1E90FD7418EDB86400CF0417 /* BITCrashDetails.m in Sources */, 1E49A4D3161222B900463151 /* BITWebTableViewCell.m in Sources */, E48A3DED17B3ED1C00924C3D /* BITAuthenticator.m in Sources */, 1E49A4DB161222D400463151 /* HockeySDKPrivate.m in Sources */, From 9f9f44916a1b6bb706da292c672b37ce1bee3a6f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Lukas=20Spie=C3=9F?= Date: Wed, 16 Apr 2014 12:50:45 +0200 Subject: [PATCH 11/43] adds public method to handle user input from an alert view --- Classes/BITCrashManager.h | 5 +++ Classes/BITCrashManager.m | 69 ++++++++++++++++++++++++--------------- 2 files changed, 47 insertions(+), 27 deletions(-) diff --git a/Classes/BITCrashManager.h b/Classes/BITCrashManager.h index dfff2f67f4..97d39c214a 100644 --- a/Classes/BITCrashManager.h +++ b/Classes/BITCrashManager.h @@ -247,6 +247,11 @@ typedef NS_ENUM(NSUInteger, BITCrashManagerUserInput) { */ @property (nonatomic, readonly) BOOL didCrashInLastSession; +/** +Provides an interface to handle user input from a custom alert + @return BOOl if the input is a valid option + */ +- (BOOL)handleUserInput: (BITCrashManagerUserInput) userInput; /** Provides the time between startup and crash in seconds diff --git a/Classes/BITCrashManager.m b/Classes/BITCrashManager.m index 8dfff2863b..1bc77ea7a9 100644 --- a/Classes/BITCrashManager.m +++ b/Classes/BITCrashManager.m @@ -487,6 +487,36 @@ NSString *const kBITCrashManagerStatus = @"BITCrashManagerStatus"; } } +- (BOOL)handleUserInput:(BITCrashManagerUserInput)userInput { + switch (userInput) { + case BITCrashManagerUserInputDontSend: + if (self.delegate != nil && [self.delegate respondsToSelector:@selector(crashManagerWillCancelSendingCrashReport:)]) { + [self.delegate crashManagerWillCancelSendingCrashReport:self]; + } + + [self cleanCrashReports]; + YES; + + case BITCrashManagerUserInputSend: + [self sendCrashReports]; + YES; + + case BITCrashManagerUserInputAlwaysSend: + _crashManagerStatus = BITCrashManagerStatusAutoSend; + [[NSUserDefaults standardUserDefaults] setInteger:_crashManagerStatus forKey:kBITCrashManagerStatus]; + [[NSUserDefaults standardUserDefaults] synchronize]; + if (self.delegate != nil && [self.delegate respondsToSelector:@selector(crashManagerWillSendCrashReportsAlways:)]) { + [self.delegate crashManagerWillSendCrashReportsAlways:self]; + } + + [self sendCrashReports]; + return YES; + + default: + return NO; + } + +} #pragma mark - PLCrashReporter @@ -930,35 +960,20 @@ NSString *const kBITCrashManagerStatus = @"BITCrashManagerStatus"; #pragma mark - UIAlertView Delegate - (void)alertView:(UIAlertView *)alertView didDismissWithButtonIndex:(NSInteger)buttonIndex { - [self handleUserInput:buttonIndex]; + switch (buttonIndex) { + case 0: + [self handleUserInput:BITCrashManagerUserInputDontSend]; + break; + case 1: + [self handleUserInput:BITCrashManagerUserInputSend]; + break; + case 2: + [self handleUserInput:BITCrashManagerUserInputAlwaysSend]; + break; + } } -- (void)handleUserInput:(BITCrashManagerUserInput)userInput { - switch (userInput) { - case BITCrashManagerUserInputDontSend: - if (self.delegate != nil && [self.delegate respondsToSelector:@selector(crashManagerWillCancelSendingCrashReport:)]) { - [self.delegate crashManagerWillCancelSendingCrashReport:self]; - } - - _sendingInProgress = NO; - [self cleanCrashReports]; - break; - case BITCrashManagerUserInputSend: - [self sendCrashReports]; - break; - case BITCrashManagerUserInputAlwaysSend: - _crashManagerStatus = BITCrashManagerStatusAutoSend; - [[NSUserDefaults standardUserDefaults] setInteger:_crashManagerStatus forKey:kBITCrashManagerStatus]; - [[NSUserDefaults standardUserDefaults] synchronize]; - if (self.delegate != nil && [self.delegate respondsToSelector:@selector(crashManagerWillSendCrashReportsAlways:)]) { - [self.delegate crashManagerWillSendCrashReportsAlways:self]; - } - - [self sendCrashReports]; - break; - } - -} + #pragma mark - Networking From 68c7fd95b4845e5aa3eb28857143d1ae4d791ffd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Lukas=20Spie=C3=9F?= Date: Wed, 16 Apr 2014 12:51:51 +0200 Subject: [PATCH 12/43] makes enum more explicit --- Classes/BITCrashManager.h | 22 +++++++++++++++------- 1 file changed, 15 insertions(+), 7 deletions(-) diff --git a/Classes/BITCrashManager.h b/Classes/BITCrashManager.h index 97d39c214a..c20cce6834 100644 --- a/Classes/BITCrashManager.h +++ b/Classes/BITCrashManager.h @@ -57,14 +57,22 @@ typedef NS_ENUM(NSUInteger, BITCrashManagerStatus) { */ BITCrashManagerStatusAutoSend = 2 }; - +/** + * Crash Manager alert user input + */ typedef NS_ENUM(NSUInteger, BITCrashManagerUserInput) { - - BITCrashManagerUserInputDontSend, - - BITCrashManagerUserInputSend, - - BITCrashManagerUserInputAlwaysSend + /** + * User chose not to send the crash report + */ + BITCrashManagerUserInputDontSend = 0, + /** + * User wants the crash report to be sent + */ + BITCrashManagerUserInputSend = 1, + /** + * User chose to always send crash reports + */ + BITCrashManagerUserInputAlwaysSend = 2 }; From 954e5c9ac2d54a35238c90f0e4f1281463b90009 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Lukas=20Spie=C3=9F?= Date: Wed, 16 Apr 2014 15:29:54 +0200 Subject: [PATCH 13/43] add tests for handleUserInput method --- Support/HockeySDKTests/BITCrashManagerTests.m | 34 +++++++++++++++++++ 1 file changed, 34 insertions(+) diff --git a/Support/HockeySDKTests/BITCrashManagerTests.m b/Support/HockeySDKTests/BITCrashManagerTests.m index 099b608e1d..34510ba35d 100644 --- a/Support/HockeySDKTests/BITCrashManagerTests.m +++ b/Support/HockeySDKTests/BITCrashManagerTests.m @@ -123,6 +123,40 @@ [verifyCount(delegateMock, times(1)) userEmailForHockeyManager:hm componentManager:_sut]; } +#pragma mark - Handle User Input + +- (void)testHandleUserInputDontSend { + id delegateMock = mockProtocol(@protocol(BITCrashManagerDelegate)); + _sut.delegate = delegateMock; + + assertThatBool([_sut handleUserInput:BITCrashManagerUserInputDontSend], equalToBool(YES)); + + [verify(delegateMock) crashManagerWillCancelSendingCrashReport:_sut]; + +} + +- (void)testHandleUserInputSend { + assertThatBool([_sut handleUserInput:BITCrashManagerUserInputSend], equalToBool(YES)); +} + +- (void)testHandleUserInputAlwaysSend { + id delegateMock = mockProtocol(@protocol(BITCrashManagerDelegate)); + _sut.delegate = delegateMock; + NSUserDefaults *mockUserDefaults = mock([NSUserDefaults class]); + + //Test if CrashManagerStatus is unset + [given([mockUserDefaults integerForKey:@"BITCrashManagerStatus"]) willReturn:nil]; + + //Test if method runs through + assertThatBool([_sut handleUserInput:BITCrashManagerUserInputAlwaysSend], equalToBool(YES)); + + //Test if correct CrashManagerStatus is now set + [given([mockUserDefaults integerForKey:@"BITCrashManagerStauts"]) willReturnInt:BITCrashManagerStatusAutoSend]; + + //Verify that delegate method has been called + [verify(delegateMock) crashManagerWillSendCrashReportsAlways:_sut]; + +} #pragma mark - Debugger From 91698b85a3e4a7d2dc9edcc655c805e9d06653ee Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Lukas=20Spie=C3=9F?= Date: Wed, 16 Apr 2014 17:12:17 +0200 Subject: [PATCH 14/43] updates formatting and documentation --- Classes/BITCrashManager.h | 12 +++++++++--- Classes/BITCrashManager.m | 6 +++--- 2 files changed, 12 insertions(+), 6 deletions(-) diff --git a/Classes/BITCrashManager.h b/Classes/BITCrashManager.h index c20cce6834..f52d37346f 100644 --- a/Classes/BITCrashManager.h +++ b/Classes/BITCrashManager.h @@ -57,6 +57,8 @@ typedef NS_ENUM(NSUInteger, BITCrashManagerStatus) { */ BITCrashManagerStatusAutoSend = 2 }; + + /** * Crash Manager alert user input */ @@ -220,7 +222,7 @@ typedef NS_ENUM(NSUInteger, BITCrashManagerUserInput) { * * @param callbacks A pointer to an initialized PLCrashReporterCallback structure, see https://www.plcrashreporter.org/documentation/api/v1.2-rc2/struct_p_l_crash_reporter_callbacks.html */ -- (void)setCrashCallbacks: (PLCrashReporterCallbacks *) callbacks; +- (void)setCrashCallbacks:(PLCrashReporterCallbacks *)callbacks; /** @@ -256,10 +258,14 @@ typedef NS_ENUM(NSUInteger, BITCrashManagerUserInput) { @property (nonatomic, readonly) BOOL didCrashInLastSession; /** -Provides an interface to handle user input from a custom alert + Provides an interface to handle user input from a custom alert + + On this input depends, whether crash reports are sent, always sent or not sent and deleted. + @return BOOl if the input is a valid option + @see BITCrashManagerUserInput */ -- (BOOL)handleUserInput: (BITCrashManagerUserInput) userInput; +- (BOOL)handleUserInput:(BITCrashManagerUserInput)userInput; /** Provides the time between startup and crash in seconds diff --git a/Classes/BITCrashManager.m b/Classes/BITCrashManager.m index 1bc77ea7a9..0dbca1cc0a 100644 --- a/Classes/BITCrashManager.m +++ b/Classes/BITCrashManager.m @@ -174,9 +174,9 @@ NSString *const kBITCrashManagerStatus = @"BITCrashManagerStatus"; NSString *errorString = nil; NSMutableDictionary *rootObj = [NSMutableDictionary dictionaryWithCapacity:2]; - if (_approvedCrashReports && [_approvedCrashReports count] > 0) + if (_approvedCrashReports && [_approvedCrashReports count] > 0) { [rootObj setObject:_approvedCrashReports forKey:kBITCrashApprovedReports]; - + } NSData *plist = [NSPropertyListSerialization dataFromPropertyList:(id)rootObj format:NSPropertyListBinaryFormat_v1_0 errorDescription:&errorString]; @@ -438,7 +438,7 @@ NSString *const kBITCrashManagerStatus = @"BITCrashManagerStatus"; #pragma mark - Public -- (void)setCrashCallbacks: (PLCrashReporterCallbacks *) callbacks { +- (void)setCrashCallbacks:(PLCrashReporterCallbacks *)callbacks { _crashCallBacks = callbacks; } From 973e130325aa27f63f4c6c1ce87ce75b9d293bf0 Mon Sep 17 00:00:00 2001 From: Andreas Linde Date: Thu, 17 Apr 2014 22:50:22 +0200 Subject: [PATCH 15/43] Migrate BITCrashManager to use BITHockeyAppClient --- Classes/BITCrashManager.m | 221 ++++++++++++++----------------- Classes/BITCrashManagerPrivate.h | 7 + Classes/BITHockeyManager.m | 1 + 3 files changed, 107 insertions(+), 122 deletions(-) diff --git a/Classes/BITCrashManager.m b/Classes/BITCrashManager.m index 18b3aaf270..4d1bff75d1 100644 --- a/Classes/BITCrashManager.m +++ b/Classes/BITCrashManager.m @@ -82,11 +82,6 @@ NSString *const kBITCrashManagerStatus = @"BITCrashManagerStatus"; BOOL _crashIdenticalCurrentVersion; - NSMutableData *_responseData; - NSInteger _statusCode; - - NSURLConnection *_urlConnection; - BOOL _sendingInProgress; BOOL _isSetup; @@ -106,9 +101,6 @@ NSString *const kBITCrashManagerStatus = @"BITCrashManagerStatus"; _crashCallBacks = nil; _crashIdenticalCurrentVersion = YES; - _urlConnection = nil; - _responseData = nil; - _sendingInProgress = NO; _didCrashInLastSession = NO; _timeintervalCrashInLastSessionOccured = -1; @@ -151,8 +143,6 @@ NSString *const kBITCrashManagerStatus = @"BITCrashManagerStatus"; - (void) dealloc { [self unregisterObservers]; - - [_urlConnection cancel]; } @@ -963,36 +953,35 @@ 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 attachments:(NSArray *)attachments { - NSMutableURLRequest *request = nil; - NSString *boundary = @"----FOO"; +- (NSURLRequest *)requestWithXML:(NSString*)xml attachments:(NSArray *)attachments { + NSString *postCrashPath = [NSString stringWithFormat:@"api/2/apps/%@/crashes", self.encodedAppIdentifier]; - request = [NSMutableURLRequest requestWithURL: - [NSURL URLWithString:[NSString stringWithFormat:@"%@api/2/apps/%@/crashes?sdk=%@&sdk_version=%@&feedbackEnabled=no", - self.serverURL, - [self encodedAppIdentifier], - BITHOCKEY_NAME, - BITHOCKEY_VERSION - ] - ]]; + NSMutableURLRequest *request = [self.hockeyAppClient requestWithMethod:@"POST" + path:postCrashPath + parameters:nil]; [request setCachePolicy: NSURLRequestReloadIgnoringLocalCacheData]; [request setValue:@"HockeySDK/iOS" forHTTPHeaderField:@"User-Agent"]; [request setValue:@"gzip" forHTTPHeaderField:@"Accept-Encoding"]; - [request setTimeoutInterval: 15]; - [request setHTTPMethod:@"POST"]; + + NSString *boundary = @"----FOO"; NSString *contentType = [NSString stringWithFormat:@"multipart/form-data; boundary=%@", boundary]; [request setValue:contentType forHTTPHeaderField:@"Content-type"]; NSMutableData *postBody = [NSMutableData data]; + [postBody appendData:[BITHockeyAppClient dataWithPostValue:BITHOCKEY_NAME + forKey:@"sdk" + boundary:boundary]]; + + [postBody appendData:[BITHockeyAppClient dataWithPostValue:BITHOCKEY_VERSION + forKey:@"sdk_version" + boundary:boundary]]; + + [postBody appendData:[BITHockeyAppClient dataWithPostValue:@"no" + forKey:@"feedbackEnabled" + boundary:boundary]]; + [postBody appendData:[BITHockeyAppClient dataWithPostValue:[xml dataUsingEncoding:NSUTF8StringEncoding] forKey:@"xml" contentType:@"text/xml" @@ -1011,108 +1000,96 @@ NSString *const kBITCrashManagerStatus = @"BITCrashManagerStatus"; boundary:boundary filename:attachment.filename]]; } - + [postBody appendData:[[NSString stringWithFormat:@"\r\n--%@--\r\n", boundary] dataUsingEncoding:NSUTF8StringEncoding]]; - + [request setHTTPBody:postBody]; - - _statusCode = 200; - - //Release when done in the delegate method - _responseData = [[NSMutableData alloc] init]; - - _urlConnection = [[NSURLConnection alloc] initWithRequest:request delegate:self]; - if (!_urlConnection) { - BITHockeyLog(@"INFO: Sending crash reports could not start!"); - _sendingInProgress = NO; - } else { - if (self.delegate != nil && [self.delegate respondsToSelector:@selector(crashManagerWillSendCrashReport:)]) { - [self.delegate crashManagerWillSendCrashReport:self]; - } - - BITHockeyLog(@"INFO: Sending crash reports started."); - } + return request; } -#pragma mark - NSURLConnection Delegate +/** + * 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 attachments:(NSArray *)attachments { + + NSURLRequest* request = [self requestWithXML:xml attachments:attachments]; + + __weak typeof (self) weakSelf = self; + BITHTTPOperation *operation = [self.hockeyAppClient + operationWithURLRequest:request + completion:^(BITHTTPOperation *operation, NSData* responseData, NSError *error) { + typeof (self) strongSelf = weakSelf; + + _sendingInProgress = NO; + + NSInteger statusCode = [operation.response statusCode]; -- (void)connection:(NSURLConnection *)connection didReceiveResponse:(NSURLResponse *)response { - if ([response isKindOfClass:[NSHTTPURLResponse class]]) { - _statusCode = [(NSHTTPURLResponse *)response statusCode]; - } -} - -- (void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)data { - [_responseData appendData:data]; -} - -- (void)connection:(NSURLConnection *)connection didFailWithError:(NSError *)error { - if (self.delegate != nil && [self.delegate respondsToSelector:@selector(crashManager:didFailWithError:)]) { - [self.delegate crashManager:self didFailWithError:error]; + if (nil == error) { + if (nil == responseData || [responseData length] == 0) { + error = [NSError errorWithDomain:kBITCrashErrorDomain + code:BITCrashAPIReceivedEmptyResponse + userInfo:@{ + NSLocalizedDescriptionKey: @"Sending failed with an empty response!" + } + ]; + } else if (statusCode >= 200 && statusCode < 400) { + [strongSelf cleanCrashReports]; + + // HockeyApp uses PList XML format + NSMutableDictionary *response = [NSPropertyListSerialization propertyListFromData:responseData + mutabilityOption:NSPropertyListMutableContainersAndLeaves + format:nil + errorDescription:NULL]; + BITHockeyLog(@"INFO: Received API response: %@", response); + + if (strongSelf.delegate != nil && + [strongSelf.delegate respondsToSelector:@selector(crashManagerDidFinishSendingCrashReport:)]) { + [strongSelf.delegate crashManagerDidFinishSendingCrashReport:self]; + } + } else if (statusCode == 400) { + [strongSelf cleanCrashReports]; + + error = [NSError errorWithDomain:kBITCrashErrorDomain + code:BITCrashAPIAppVersionRejected + userInfo:@{ + NSLocalizedDescriptionKey: @"The server rejected receiving crash reports for this app version!" + } + ]; + } else { + error = [NSError errorWithDomain:kBITCrashErrorDomain + code:BITCrashAPIErrorWithStatusCode + userInfo:@{ + NSLocalizedDescriptionKey: [NSString stringWithFormat:@"Sending failed with status code: %li", (long)statusCode] + } + ]; + } + } + + if (error) { + if (strongSelf.delegate != nil && + [strongSelf.delegate respondsToSelector:@selector(crashManager:didFailWithError:)]) { + [strongSelf.delegate crashManager:self didFailWithError:error]; + } + + BITHockeyLog(@"ERROR: %@", [error localizedDescription]); + } + + }]; + + if (self.delegate != nil && [self.delegate respondsToSelector:@selector(crashManagerWillSendCrashReport:)]) { + [self.delegate crashManagerWillSendCrashReport:self]; } - BITHockeyLog(@"ERROR: %@", [error localizedDescription]); - - _sendingInProgress = NO; - - _responseData = nil; - _urlConnection = nil; + BITHockeyLog(@"INFO: Sending crash reports started."); + + [self.hockeyAppClient enqeueHTTPOperation:operation]; } -- (void)connectionDidFinishLoading:(NSURLConnection *)connection { - NSError *error = nil; - - if (_statusCode >= 200 && _statusCode < 400 && _responseData != nil && [_responseData length] > 0) { - [self cleanCrashReports]; - - // HockeyApp uses PList XML format - NSMutableDictionary *response = [NSPropertyListSerialization propertyListFromData:_responseData - mutabilityOption:NSPropertyListMutableContainersAndLeaves - format:nil - errorDescription:NULL]; - BITHockeyLog(@"INFO: Received API response: %@", response); - - if (self.delegate != nil && [self.delegate respondsToSelector:@selector(crashManagerDidFinishSendingCrashReport:)]) { - [self.delegate crashManagerDidFinishSendingCrashReport:self]; - } - } else if (_statusCode == 400) { - [self cleanCrashReports]; - - error = [NSError errorWithDomain:kBITCrashErrorDomain - code:BITCrashAPIAppVersionRejected - userInfo:[NSDictionary dictionaryWithObjectsAndKeys:@"The server rejected receiving crash reports for this app version!", NSLocalizedDescriptionKey, nil]]; - - if (self.delegate != nil && [self.delegate respondsToSelector:@selector(crashManager:didFailWithError:)]) { - [self.delegate crashManager:self didFailWithError:error]; - } - - BITHockeyLog(@"ERROR: %@", [error localizedDescription]); - } else { - if (_responseData == nil || [_responseData length] == 0) { - error = [NSError errorWithDomain:kBITCrashErrorDomain - code:BITCrashAPIReceivedEmptyResponse - userInfo:[NSDictionary dictionaryWithObjectsAndKeys:@"Sending failed with an empty response!", NSLocalizedDescriptionKey, nil]]; - } else { - error = [NSError errorWithDomain:kBITCrashErrorDomain - code:BITCrashAPIErrorWithStatusCode - userInfo:[NSDictionary dictionaryWithObjectsAndKeys:[NSString stringWithFormat:@"Sending failed with status code: %li", (long)_statusCode], NSLocalizedDescriptionKey, nil]]; - } - - if (self.delegate != nil && [self.delegate respondsToSelector:@selector(crashManager:didFailWithError:)]) { - [self.delegate crashManager:self didFailWithError:error]; - } - - BITHockeyLog(@"ERROR: %@", [error localizedDescription]); - } - - _sendingInProgress = NO; - - _responseData = nil; - _urlConnection = nil; -} - - @end #endif /* HOCKEYSDK_FEATURE_CRASH_REPORTER */ diff --git a/Classes/BITCrashManagerPrivate.h b/Classes/BITCrashManagerPrivate.h index f2231342f4..90902a9d03 100644 --- a/Classes/BITCrashManagerPrivate.h +++ b/Classes/BITCrashManagerPrivate.h @@ -31,9 +31,16 @@ #if HOCKEYSDK_FEATURE_CRASH_REPORTER +@class BITHockeyAppClient; + @interface BITCrashManager () { } +/** + * must be set + */ +@property (nonatomic, strong) BITHockeyAppClient *hockeyAppClient; + @property (nonatomic) NSUncaughtExceptionHandler *exceptionHandler; @property (nonatomic, strong) BITPLCrashReporter *plCrashReporter; diff --git a/Classes/BITHockeyManager.m b/Classes/BITHockeyManager.m index baa1a7e81c..4fe3e873ca 100644 --- a/Classes/BITHockeyManager.m +++ b/Classes/BITHockeyManager.m @@ -604,6 +604,7 @@ bitstadium_info_t bitstadium_library_info __attribute__((section("__TEXT,__bit_h #if HOCKEYSDK_FEATURE_CRASH_REPORTER BITHockeyLog(@"INFO: Setup CrashManager"); _crashManager = [[BITCrashManager alloc] initWithAppIdentifier:_appIdentifier isAppStoreEnvironment:_appStoreEnvironment]; + _crashManager.hockeyAppClient = [self hockeyAppClient]; _crashManager.delegate = _delegate; #endif /* HOCKEYSDK_FEATURE_CRASH_REPORTER */ From 20c73dcdc3018a3cdade669b4c90b3ad640da137 Mon Sep 17 00:00:00 2001 From: Andreas Linde Date: Thu, 17 Apr 2014 23:47:13 +0200 Subject: [PATCH 16/43] Send each crash report using an individual network request --- Classes/BITCrashManager.m | 107 ++++++++++++++++---------------------- 1 file changed, 46 insertions(+), 61 deletions(-) diff --git a/Classes/BITCrashManager.m b/Classes/BITCrashManager.m index 4d1bff75d1..c348826e14 100644 --- a/Classes/BITCrashManager.m +++ b/Classes/BITCrashManager.m @@ -204,26 +204,39 @@ NSString *const kBITCrashManagerStatus = @"BITCrashManagerStatus"; } } + +/** + * Remove a cached crash report + * + * @param filename The base filename of the crash report + */ +- (void)cleanCrashReportWithFilename:(NSString *)filename { + if (!filename) return; + + NSError *error = NULL; + + [_fileManager removeItemAtPath:filename error:&error]; + [_fileManager removeItemAtPath:[filename stringByAppendingString:@".data"] error:&error]; + [_fileManager removeItemAtPath:[filename stringByAppendingString:@".meta"] error:&error]; + + NSString *cacheFilename = [filename lastPathComponent]; + [self removeKeyFromKeychain:[NSString stringWithFormat:@"%@.%@", cacheFilename, kBITCrashMetaUserName]]; + [self removeKeyFromKeychain:[NSString stringWithFormat:@"%@.%@", cacheFilename, kBITCrashMetaUserEmail]]; + [self removeKeyFromKeychain:[NSString stringWithFormat:@"%@.%@", cacheFilename, kBITCrashMetaUserID]]; + + [_crashFiles removeObject:filename]; + [_approvedCrashReports removeObjectForKey:filename]; + + [self saveSettings]; +} + /** * Remove all crash reports and stored meta data for each from the file system and keychain */ - (void)cleanCrashReports { - NSError *error = NULL; - for (NSUInteger i=0; i < [_crashFiles count]; i++) { - [_fileManager removeItemAtPath:[_crashFiles objectAtIndex:i] error:&error]; - [_fileManager removeItemAtPath:[[_crashFiles objectAtIndex:i] stringByAppendingString:@".data"] error:&error]; - [_fileManager removeItemAtPath:[[_crashFiles objectAtIndex:i] stringByAppendingString:@".meta"] error:&error]; - - NSString *cacheFilename = [[_crashFiles objectAtIndex:i] lastPathComponent]; - [self removeKeyFromKeychain:[NSString stringWithFormat:@"%@.%@", cacheFilename, kBITCrashMetaUserName]]; - [self removeKeyFromKeychain:[NSString stringWithFormat:@"%@.%@", cacheFilename, kBITCrashMetaUserEmail]]; - [self removeKeyFromKeychain:[NSString stringWithFormat:@"%@.%@", cacheFilename, kBITCrashMetaUserID]]; + [self cleanCrashReportWithFilename:[_crashFiles objectAtIndex:i]]; } - [_crashFiles removeAllObjects]; - [_approvedCrashReports removeAllObjects]; - - [self saveSettings]; } @@ -242,7 +255,7 @@ NSString *const kBITCrashManagerStatus = @"BITCrashManagerStatus"; /** * Read the attachment data from the stored file * - * @param filename The crash report id + * @param filename The crash report file path * * @return an BITCrashAttachment instance or nil */ @@ -800,12 +813,13 @@ NSString *const kBITCrashManagerStatus = @"BITCrashManagerStatus"; */ - (void)sendCrashReports { NSError *error = NULL; - - NSMutableString *crashes = nil; - NSMutableArray *attachments = [NSMutableArray array]; + _crashIdenticalCurrentVersion = NO; for (NSUInteger i=0; i < [_crashFiles count]; i++) { + NSString *crashXML = nil; + BITCrashAttachment *attachment = nil; + NSString *filename = [_crashFiles objectAtIndex:i]; NSString *cacheFilename = [filename lastPathComponent]; NSData *crashData = [NSData dataWithContentsOfFile:filename]; @@ -816,13 +830,7 @@ NSString *const kBITCrashManagerStatus = @"BITCrashManagerStatus"; if (report == nil) { BITHockeyLog(@"WARNING: Could not parse crash report"); // we cannot do anything with this report, so delete it - [_fileManager removeItemAtPath:filename error:&error]; - [_fileManager removeItemAtPath:[NSString stringWithFormat:@"%@.data", filename] error:&error]; - [_fileManager removeItemAtPath:[NSString stringWithFormat:@"%@.meta", filename] error:&error]; - - [self removeKeyFromKeychain:[NSString stringWithFormat:@"%@.%@", cacheFilename, kBITCrashMetaUserName]]; - [self removeKeyFromKeychain:[NSString stringWithFormat:@"%@.%@", cacheFilename, kBITCrashMetaUserEmail]]; - [self removeKeyFromKeychain:[NSString stringWithFormat:@"%@.%@", cacheFilename, kBITCrashMetaUserID]]; + [self cleanCrashReportWithFilename:filename]; continue; } @@ -837,10 +845,6 @@ NSString *const kBITCrashManagerStatus = @"BITCrashManagerStatus"; _crashIdenticalCurrentVersion = YES; } - if (crashes == nil) { - crashes = [NSMutableString string]; - } - NSString *username = @""; NSString *useremail = @""; NSString *userid = @""; @@ -863,12 +867,7 @@ NSString *const kBITCrashManagerStatus = @"BITCrashManagerStatus"; userid = [self stringValueFromKeychainForKey:[NSString stringWithFormat:@"%@.%@", cacheFilename, kBITCrashMetaUserID]] ?: @""; applicationLog = [metaDict objectForKey:kBITCrashMetaApplicationLog] ?: @""; - BITCrashAttachment *attachment = [self attachmentForCrashReport:filename]; - if (attachment) { - NSDictionary *attachmentDict = @{KBITAttachmentDictIndex: @(i), - KBITAttachmentDictAttachment: attachment}; - [attachments addObject:attachmentDict]; - } + attachment = [self attachmentForCrashReport:filename]; } else { BITHockeyLog(@"ERROR: Reading crash meta data. %@", error); } @@ -877,7 +876,7 @@ NSString *const kBITCrashManagerStatus = @"BITCrashManagerStatus"; description = [NSString stringWithFormat:@"%@", applicationLog]; } - [crashes appendFormat:@"%s%@%@%@%@%@%@%@%@%@%@%@", + crashXML = [NSString stringWithFormat:@"%s%@%@%@%@%@%@%@%@%@%@%@", [[[NSBundle mainBundle] objectForInfoDictionaryKey:@"CFBundleExecutable"] UTF8String], [self extractAppUUIDs:report], report.applicationInfo.applicationIdentifier, @@ -893,27 +892,18 @@ NSString *const kBITCrashManagerStatus = @"BITCrashManagerStatus"; installString, [description stringByReplacingOccurrencesOfString:@"]]>" withString:@"]]" @"]]>" options:NSLiteralSearch range:NSMakeRange(0,description.length)]]; - // store this crash report as user approved, so if it fails it will retry automatically [_approvedCrashReports setObject:[NSNumber numberWithBool:YES] forKey:filename]; + + BITHockeyLog(@"INFO: Sending crash reports:\n%@", crashXML); + [self sendCrashReportWithFilename:filename xml:crashXML attachment:attachment]; } else { // we cannot do anything with this report, so delete it - [_fileManager removeItemAtPath:filename error:&error]; - [_fileManager removeItemAtPath:[NSString stringWithFormat:@"%@.data", filename] error:&error]; - [_fileManager removeItemAtPath:[NSString stringWithFormat:@"%@.meta", filename] error:&error]; - - [self removeKeyFromKeychain:[NSString stringWithFormat:@"%@.%@", cacheFilename, kBITCrashMetaUserName]]; - [self removeKeyFromKeychain:[NSString stringWithFormat:@"%@.%@", cacheFilename, kBITCrashMetaUserEmail]]; - [self removeKeyFromKeychain:[NSString stringWithFormat:@"%@.%@", cacheFilename, kBITCrashMetaUserID]]; + [self cleanCrashReportWithFilename:filename]; } } [self saveSettings]; - - if (crashes != nil) { - BITHockeyLog(@"INFO: Sending crash reports:\n%@", crashes); - [self postXML:[NSString stringWithFormat:@"%@", crashes] attachments:attachments]; - } } @@ -953,7 +943,7 @@ NSString *const kBITCrashManagerStatus = @"BITCrashManagerStatus"; #pragma mark - Networking -- (NSURLRequest *)requestWithXML:(NSString*)xml attachments:(NSArray *)attachments { +- (NSURLRequest *)requestWithXML:(NSString*)xml attachment:(BITCrashAttachment *)attachment { NSString *postCrashPath = [NSString stringWithFormat:@"api/2/apps/%@/crashes", self.encodedAppIdentifier]; NSMutableURLRequest *request = [self.hockeyAppClient requestWithMethod:@"POST" @@ -988,14 +978,9 @@ NSString *const kBITCrashManagerStatus = @"BITCrashManagerStatus"; boundary:boundary filename:@"crash.xml"]]; - for (NSDictionary *dict in attachments) { - NSInteger index = [(NSNumber *)dict[KBITAttachmentDictIndex] integerValue]; - NSString *key = [NSString stringWithFormat:@"attachment%ld", (long)index]; - - BITCrashAttachment *attachment = (BITCrashAttachment *)dict[KBITAttachmentDictAttachment]; - + if (attachment) { [postBody appendData:[BITHockeyAppClient dataWithPostValue:attachment.attachmentData - forKey:key + forKey:@"attachment0" contentType:attachment.contentType boundary:boundary filename:attachment.filename]]; @@ -1015,9 +1000,9 @@ NSString *const kBITCrashManagerStatus = @"BITCrashManagerStatus"; * * @param xml The XML data that needs to be send to the server */ -- (void)postXML:(NSString*)xml attachments:(NSArray *)attachments { +- (void)sendCrashReportWithFilename:(NSString *)filename xml:(NSString*)xml attachment:(BITCrashAttachment *)attachment { - NSURLRequest* request = [self requestWithXML:xml attachments:attachments]; + NSURLRequest* request = [self requestWithXML:xml attachment:attachment]; __weak typeof (self) weakSelf = self; BITHTTPOperation *operation = [self.hockeyAppClient @@ -1038,7 +1023,7 @@ NSString *const kBITCrashManagerStatus = @"BITCrashManagerStatus"; } ]; } else if (statusCode >= 200 && statusCode < 400) { - [strongSelf cleanCrashReports]; + [strongSelf cleanCrashReportWithFilename:filename]; // HockeyApp uses PList XML format NSMutableDictionary *response = [NSPropertyListSerialization propertyListFromData:responseData @@ -1052,7 +1037,7 @@ NSString *const kBITCrashManagerStatus = @"BITCrashManagerStatus"; [strongSelf.delegate crashManagerDidFinishSendingCrashReport:self]; } } else if (statusCode == 400) { - [strongSelf cleanCrashReports]; + [strongSelf cleanCrashReportWithFilename:filename]; error = [NSError errorWithDomain:kBITCrashErrorDomain code:BITCrashAPIAppVersionRejected From d22fb02ec20a3ee7530e9a7cb7b0a57aa3dc9177 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Lukas=20Spie=C3=9F?= Date: Tue, 22 Apr 2014 17:52:14 +0200 Subject: [PATCH 17/43] Add ability to add a description to a crash report --- Classes/BITCrashManager.h | 2 +- Classes/BITCrashManager.m | 27 ++++++++++++++++++++------- 2 files changed, 21 insertions(+), 8 deletions(-) diff --git a/Classes/BITCrashManager.h b/Classes/BITCrashManager.h index f52d37346f..0682dffd85 100644 --- a/Classes/BITCrashManager.h +++ b/Classes/BITCrashManager.h @@ -265,7 +265,7 @@ typedef NS_ENUM(NSUInteger, BITCrashManagerUserInput) { @return BOOl if the input is a valid option @see BITCrashManagerUserInput */ -- (BOOL)handleUserInput:(BITCrashManagerUserInput)userInput; +- (BOOL)handleUserInput:(BITCrashManagerUserInput)userInput withCrashMetaDescription:metaDescription; /** Provides the time between startup and crash in seconds diff --git a/Classes/BITCrashManager.m b/Classes/BITCrashManager.m index 0dbca1cc0a..c1110b568a 100644 --- a/Classes/BITCrashManager.m +++ b/Classes/BITCrashManager.m @@ -55,6 +55,7 @@ #define kBITCrashMetaUserID @"BITCrashMetaUserID" #define kBITCrashMetaApplicationLog @"BITCrashMetaApplicationLog" #define kBITCrashMetaAttachment @"BITCrashMetaAttachment" +#define kBITCrashMetaDescription @"BITCrashMetaDescription" // internal keys NSString *const KBITAttachmentDictIndex = @"index"; @@ -74,6 +75,7 @@ NSString *const kBITCrashManagerStatus = @"BITCrashManagerStatus"; NSMutableArray *_crashFiles; NSString *_crashesDir; + NSString *_lastCrashFilename; NSString *_settingsFile; NSString *_analyzerInProgressFile; NSFileManager *_fileManager; @@ -487,7 +489,7 @@ NSString *const kBITCrashManagerStatus = @"BITCrashManagerStatus"; } } -- (BOOL)handleUserInput:(BITCrashManagerUserInput)userInput { +- (BOOL)handleUserInput:(BITCrashManagerUserInput)userInput withCrashMetaDescription:(NSString *)metaDescription{ switch (userInput) { case BITCrashManagerUserInputDontSend: if (self.delegate != nil && [self.delegate respondsToSelector:@selector(crashManagerWillCancelSendingCrashReport:)]) { @@ -498,6 +500,11 @@ NSString *const kBITCrashManagerStatus = @"BITCrashManagerStatus"; YES; case BITCrashManagerUserInputSend: + if (!metaDescription && [metaDescription length] > 0) { + NSError *error; + [metaDescription writeToFile:[NSString stringWithFormat:@"%@_description.meta", [_crashesDir stringByAppendingPathComponent: _lastCrashFilename]] atomically:YES encoding:NSUTF8StringEncoding error:&error]; + BITHockeyLog(@"ERROR: Writing crash meta description failed. %@", error); + } [self sendCrashReports]; YES; @@ -591,6 +598,7 @@ NSString *const kBITCrashManagerStatus = @"BITCrashManagerStatus"; } else { BITHockeyLog(@"ERROR: Writing crash meta data failed. %@", error); } + _lastCrashFilename = cacheFilename; } } } @@ -626,7 +634,7 @@ NSString *const kBITCrashManagerStatus = @"BITCrashManagerStatus"; /** * 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 + * @return `YES` if there is at least one new crash report found, `NO` otherwise */ - (BOOL)hasPendingCrashReport { if (_crashManagerStatus == BITCrashManagerStatusDisabled) return NO; @@ -902,7 +910,8 @@ NSString *const kBITCrashManagerStatus = @"BITCrashManagerStatus"; useremail = [self stringValueFromKeychainForKey:[NSString stringWithFormat:@"%@.%@", cacheFilename, kBITCrashMetaUserEmail]] ?: @""; userid = [self stringValueFromKeychainForKey:[NSString stringWithFormat:@"%@.%@", cacheFilename, kBITCrashMetaUserID]] ?: @""; applicationLog = [metaDict objectForKey:kBITCrashMetaApplicationLog] ?: @""; - + description = [NSString stringWithContentsOfFile:[NSString stringWithFormat:@"%@_description.meta", [_crashesDir stringByAppendingPathComponent: _lastCrashFilename]] encoding:NSUTF8StringEncoding error:&error]; + BITCrashAttachment *attachment = [self attachmentForCrashReport:filename]; if (attachment) { NSDictionary *attachmentDict = @{KBITAttachmentDictIndex: @(i), @@ -914,7 +923,11 @@ NSString *const kBITCrashManagerStatus = @"BITCrashManagerStatus"; } if ([applicationLog length] > 0) { - description = [NSString stringWithFormat:@"%@", applicationLog]; + if ([description length] > 0) { + description = [NSString stringWithFormat:@"%@\n\nLog:\n%@", description, applicationLog]; + } else { + description = [NSString stringWithFormat:@"Log:\n%@", applicationLog]; + } } [crashes appendFormat:@"%s%@%@%@%@%@%@%@%@%@%@%@", @@ -962,13 +975,13 @@ NSString *const kBITCrashManagerStatus = @"BITCrashManagerStatus"; - (void)alertView:(UIAlertView *)alertView didDismissWithButtonIndex:(NSInteger)buttonIndex { switch (buttonIndex) { case 0: - [self handleUserInput:BITCrashManagerUserInputDontSend]; + [self handleUserInput:BITCrashManagerUserInputDontSend withCrashMetaDescription:nil]; break; case 1: - [self handleUserInput:BITCrashManagerUserInputSend]; + [self handleUserInput:BITCrashManagerUserInputSend withCrashMetaDescription:nil]; break; case 2: - [self handleUserInput:BITCrashManagerUserInputAlwaysSend]; + [self handleUserInput:BITCrashManagerUserInputAlwaysSend withCrashMetaDescription:nil]; break; } } From 7a5077e7c15d587751f574c4ba99dd81cce26198 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Lukas=20Spie=C3=9F?= Date: Wed, 23 Apr 2014 16:09:23 +0200 Subject: [PATCH 18/43] semi-working prototype of custom Alert --- Classes/BITCrashManager.h | 6 ++++++ Classes/BITCrashManager.m | 31 ++++++++++++++++++++----------- 2 files changed, 26 insertions(+), 11 deletions(-) diff --git a/Classes/BITCrashManager.h b/Classes/BITCrashManager.h index 0682dffd85..0b7c2a7582 100644 --- a/Classes/BITCrashManager.h +++ b/Classes/BITCrashManager.h @@ -267,6 +267,12 @@ typedef NS_ENUM(NSUInteger, BITCrashManagerUserInput) { */ - (BOOL)handleUserInput:(BITCrashManagerUserInput)userInput withCrashMetaDescription:metaDescription; +/** + Property that lets you set a custom block which handles showing a custom UI and asking the user + whether he wants to send the crash report. Needs to call the `handleUserInput` method. + */ +@property (nonatomic, copy, setter = setAlertViewHandler:) void (^alertViewHandler) (); + /** Provides the time between startup and crash in seconds diff --git a/Classes/BITCrashManager.m b/Classes/BITCrashManager.m index c1110b568a..1e89ce5106 100644 --- a/Classes/BITCrashManager.m +++ b/Classes/BITCrashManager.m @@ -116,6 +116,7 @@ NSString *const kBITCrashManagerStatus = @"BITCrashManagerStatus"; _timeintervalCrashInLastSessionOccured = -1; _approvedCrashReports = [[NSMutableDictionary alloc] init]; + _alertViewHandler = nil; _fileManager = [[NSFileManager alloc] init]; _crashFiles = [[NSMutableArray alloc] init]; @@ -489,6 +490,10 @@ NSString *const kBITCrashManagerStatus = @"BITCrashManagerStatus"; } } +- (void)setAlertViewHandler:(void (^)())alertViewHandler{ + _alertViewHandler = alertViewHandler; +} + - (BOOL)handleUserInput:(BITCrashManagerUserInput)userInput withCrashMetaDescription:(NSString *)metaDescription{ switch (userInput) { case BITCrashManagerUserInputDontSend: @@ -500,7 +505,7 @@ NSString *const kBITCrashManagerStatus = @"BITCrashManagerStatus"; YES; case BITCrashManagerUserInputSend: - if (!metaDescription && [metaDescription length] > 0) { + if (metaDescription && [metaDescription length] > 0) { NSError *error; [metaDescription writeToFile:[NSString stringWithFormat:@"%@_description.meta", [_crashesDir stringByAppendingPathComponent: _lastCrashFilename]] atomically:YES encoding:NSUTF8StringEncoding error:&error]; BITHockeyLog(@"ERROR: Writing crash meta description failed. %@", error); @@ -730,17 +735,21 @@ NSString *const kBITCrashManagerStatus = @"BITCrashManagerStatus"; alertDescription = [NSString stringWithFormat:BITHockeyLocalizedString(@"CrashDataFoundDescription"), appName]; } - UIAlertView *alertView = [[UIAlertView alloc] initWithTitle:[NSString stringWithFormat:BITHockeyLocalizedString(@"CrashDataFoundTitle"), appName] - message:alertDescription - delegate:self - cancelButtonTitle:BITHockeyLocalizedString(@"CrashDontSendReport") - otherButtonTitles:BITHockeyLocalizedString(@"CrashSendReport"), nil]; - - if (self.shouldShowAlwaysButton) { - [alertView addButtonWithTitle:BITHockeyLocalizedString(@"CrashSendReportAlways")]; + if (_alertViewHandler) { + _alertViewHandler(); + } else { + UIAlertView *alertView = [[UIAlertView alloc] initWithTitle:[NSString stringWithFormat:BITHockeyLocalizedString(@"CrashDataFoundTitle"), appName] + message:alertDescription + delegate:self + cancelButtonTitle:BITHockeyLocalizedString(@"CrashDontSendReport") + otherButtonTitles:BITHockeyLocalizedString(@"CrashSendReport"), nil]; + + if (self.shouldShowAlwaysButton) { + [alertView addButtonWithTitle:BITHockeyLocalizedString(@"CrashSendReportAlways")]; + } + + [alertView show]; } - - [alertView show]; } else { [self sendCrashReports]; } From 15582b773481791ded907290736f1fe06296ba12 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Lukas=20Spie=C3=9F?= Date: Thu, 24 Apr 2014 12:32:01 +0200 Subject: [PATCH 19/43] Cleanup and small logic error fix --- Classes/BITCrashManager.m | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/Classes/BITCrashManager.m b/Classes/BITCrashManager.m index 1e89ce5106..d39c597784 100644 --- a/Classes/BITCrashManager.m +++ b/Classes/BITCrashManager.m @@ -445,6 +445,11 @@ NSString *const kBITCrashManagerStatus = @"BITCrashManagerStatus"; _crashCallBacks = callbacks; } + +- (void)setAlertViewHandler:(void (^)())alertViewHandler{ + _alertViewHandler = alertViewHandler; +} + /** * Check if the debugger is attached * @@ -490,9 +495,6 @@ NSString *const kBITCrashManagerStatus = @"BITCrashManagerStatus"; } } -- (void)setAlertViewHandler:(void (^)())alertViewHandler{ - _alertViewHandler = alertViewHandler; -} - (BOOL)handleUserInput:(BITCrashManagerUserInput)userInput withCrashMetaDescription:(NSString *)metaDescription{ switch (userInput) { @@ -508,7 +510,6 @@ NSString *const kBITCrashManagerStatus = @"BITCrashManagerStatus"; if (metaDescription && [metaDescription length] > 0) { NSError *error; [metaDescription writeToFile:[NSString stringWithFormat:@"%@_description.meta", [_crashesDir stringByAppendingPathComponent: _lastCrashFilename]] atomically:YES encoding:NSUTF8StringEncoding error:&error]; - BITHockeyLog(@"ERROR: Writing crash meta description failed. %@", error); } [self sendCrashReports]; YES; @@ -555,6 +556,7 @@ NSString *const kBITCrashManagerStatus = @"BITCrashManagerStatus"; NSData *crashData = [[NSData alloc] initWithData:[self.plCrashReporter loadPendingCrashReportDataAndReturnError: &error]]; NSString *cacheFilename = [NSString stringWithFormat: @"%.0f", [NSDate timeIntervalSinceReferenceDate]]; + _lastCrashFilename = cacheFilename; if (crashData == nil) { BITHockeyLog(@"ERROR: Could not load crash report: %@", error); @@ -603,7 +605,6 @@ NSString *const kBITCrashManagerStatus = @"BITCrashManagerStatus"; } else { BITHockeyLog(@"ERROR: Writing crash meta data failed. %@", error); } - _lastCrashFilename = cacheFilename; } } } @@ -919,7 +920,7 @@ NSString *const kBITCrashManagerStatus = @"BITCrashManagerStatus"; useremail = [self stringValueFromKeychainForKey:[NSString stringWithFormat:@"%@.%@", cacheFilename, kBITCrashMetaUserEmail]] ?: @""; userid = [self stringValueFromKeychainForKey:[NSString stringWithFormat:@"%@.%@", cacheFilename, kBITCrashMetaUserID]] ?: @""; applicationLog = [metaDict objectForKey:kBITCrashMetaApplicationLog] ?: @""; - description = [NSString stringWithContentsOfFile:[NSString stringWithFormat:@"%@_description.meta", [_crashesDir stringByAppendingPathComponent: _lastCrashFilename]] encoding:NSUTF8StringEncoding error:&error]; + description = [NSString stringWithContentsOfFile:[NSString stringWithFormat:@"%@_description.meta", [_crashesDir stringByAppendingPathComponent: cacheFilename]] encoding:NSUTF8StringEncoding error:&error]; BITCrashAttachment *attachment = [self attachmentForCrashReport:filename]; if (attachment) { From a05e15463b15da16bd594c666c55b5bb4b052bcd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Lukas=20Spie=C3=9F?= Date: Thu, 24 Apr 2014 15:37:34 +0200 Subject: [PATCH 20/43] Update tests for new method syntax --- Support/HockeySDKTests/BITCrashManagerTests.m | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/Support/HockeySDKTests/BITCrashManagerTests.m b/Support/HockeySDKTests/BITCrashManagerTests.m index 34510ba35d..4e1c09e754 100644 --- a/Support/HockeySDKTests/BITCrashManagerTests.m +++ b/Support/HockeySDKTests/BITCrashManagerTests.m @@ -129,14 +129,14 @@ id delegateMock = mockProtocol(@protocol(BITCrashManagerDelegate)); _sut.delegate = delegateMock; - assertThatBool([_sut handleUserInput:BITCrashManagerUserInputDontSend], equalToBool(YES)); + assertThatBool([_sut handleUserInput:BITCrashManagerUserInputDontSend crashMetaDescription:nil], equalToBool(YES)); [verify(delegateMock) crashManagerWillCancelSendingCrashReport:_sut]; } - (void)testHandleUserInputSend { - assertThatBool([_sut handleUserInput:BITCrashManagerUserInputSend], equalToBool(YES)); + assertThatBool([_sut handleUserInput:BITCrashManagerUserInputSend crashMetaDescription:nil], equalToBool(YES)); } - (void)testHandleUserInputAlwaysSend { @@ -148,7 +148,7 @@ [given([mockUserDefaults integerForKey:@"BITCrashManagerStatus"]) willReturn:nil]; //Test if method runs through - assertThatBool([_sut handleUserInput:BITCrashManagerUserInputAlwaysSend], equalToBool(YES)); + assertThatBool([_sut handleUserInput:BITCrashManagerUserInputAlwaysSend crashMetaDescription:nil], equalToBool(YES)); //Test if correct CrashManagerStatus is now set [given([mockUserDefaults integerForKey:@"BITCrashManagerStauts"]) willReturnInt:BITCrashManagerStatusAutoSend]; From 27cc84a84c8b96e40d432f5581192f7295f42951 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Lukas=20Spie=C3=9F?= Date: Thu, 24 Apr 2014 15:38:10 +0200 Subject: [PATCH 21/43] Maybe a switch case should return a value... --- Classes/BITCrashManager.m | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Classes/BITCrashManager.m b/Classes/BITCrashManager.m index d39c597784..e4561fac59 100644 --- a/Classes/BITCrashManager.m +++ b/Classes/BITCrashManager.m @@ -504,7 +504,7 @@ NSString *const kBITCrashManagerStatus = @"BITCrashManagerStatus"; } [self cleanCrashReports]; - YES; + return YES; case BITCrashManagerUserInputSend: if (metaDescription && [metaDescription length] > 0) { @@ -512,7 +512,7 @@ NSString *const kBITCrashManagerStatus = @"BITCrashManagerStatus"; [metaDescription writeToFile:[NSString stringWithFormat:@"%@_description.meta", [_crashesDir stringByAppendingPathComponent: _lastCrashFilename]] atomically:YES encoding:NSUTF8StringEncoding error:&error]; } [self sendCrashReports]; - YES; + return YES; case BITCrashManagerUserInputAlwaysSend: _crashManagerStatus = BITCrashManagerStatusAutoSend; From 4aee9f81b547046164bad62ad5a96de35d2e1270 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Lukas=20Spie=C3=9F?= Date: Thu, 24 Apr 2014 15:38:31 +0200 Subject: [PATCH 22/43] Small optimizations --- Classes/BITCrashManager.h | 2 +- Classes/BITCrashManager.m | 13 +++++++++---- 2 files changed, 10 insertions(+), 5 deletions(-) diff --git a/Classes/BITCrashManager.h b/Classes/BITCrashManager.h index 0b7c2a7582..76dd986676 100644 --- a/Classes/BITCrashManager.h +++ b/Classes/BITCrashManager.h @@ -265,7 +265,7 @@ typedef NS_ENUM(NSUInteger, BITCrashManagerUserInput) { @return BOOl if the input is a valid option @see BITCrashManagerUserInput */ -- (BOOL)handleUserInput:(BITCrashManagerUserInput)userInput withCrashMetaDescription:metaDescription; +- (BOOL)handleUserInput:(BITCrashManagerUserInput)userInput crashMetaDescription:metaDescription; /** Property that lets you set a custom block which handles showing a custom UI and asking the user diff --git a/Classes/BITCrashManager.m b/Classes/BITCrashManager.m index e4561fac59..8fa17d2214 100644 --- a/Classes/BITCrashManager.m +++ b/Classes/BITCrashManager.m @@ -496,7 +496,7 @@ NSString *const kBITCrashManagerStatus = @"BITCrashManagerStatus"; } -- (BOOL)handleUserInput:(BITCrashManagerUserInput)userInput withCrashMetaDescription:(NSString *)metaDescription{ +- (BOOL)handleUserInput:(BITCrashManagerUserInput)userInput crashMetaDescription:(NSString *)metaDescription{ switch (userInput) { case BITCrashManagerUserInputDontSend: if (self.delegate != nil && [self.delegate respondsToSelector:@selector(crashManagerWillCancelSendingCrashReport:)]) { @@ -522,6 +522,11 @@ NSString *const kBITCrashManagerStatus = @"BITCrashManagerStatus"; [self.delegate crashManagerWillSendCrashReportsAlways:self]; } + if (metaDescription && [metaDescription length] > 0) { + NSError *error; + [metaDescription writeToFile:[NSString stringWithFormat:@"%@_description.meta", [_crashesDir stringByAppendingPathComponent: _lastCrashFilename]] atomically:YES encoding:NSUTF8StringEncoding error:&error]; + } + [self sendCrashReports]; return YES; @@ -985,13 +990,13 @@ NSString *const kBITCrashManagerStatus = @"BITCrashManagerStatus"; - (void)alertView:(UIAlertView *)alertView didDismissWithButtonIndex:(NSInteger)buttonIndex { switch (buttonIndex) { case 0: - [self handleUserInput:BITCrashManagerUserInputDontSend withCrashMetaDescription:nil]; + [self handleUserInput:BITCrashManagerUserInputDontSend crashMetaDescription:nil]; break; case 1: - [self handleUserInput:BITCrashManagerUserInputSend withCrashMetaDescription:nil]; + [self handleUserInput:BITCrashManagerUserInputSend crashMetaDescription:nil]; break; case 2: - [self handleUserInput:BITCrashManagerUserInputAlwaysSend withCrashMetaDescription:nil]; + [self handleUserInput:BITCrashManagerUserInputAlwaysSend crashMetaDescription:nil]; break; } } From 13118462413ff8e1583ce81d7626737e62fd029b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Lukas=20Spie=C3=9F?= Date: Thu, 24 Apr 2014 16:10:12 +0200 Subject: [PATCH 23/43] update method signature --- Classes/BITCrashManager.h | 4 ++-- Classes/BITCrashManager.m | 8 ++++---- Support/HockeySDKTests/BITCrashManagerTests.m | 6 +++--- 3 files changed, 9 insertions(+), 9 deletions(-) diff --git a/Classes/BITCrashManager.h b/Classes/BITCrashManager.h index 76dd986676..476145969d 100644 --- a/Classes/BITCrashManager.h +++ b/Classes/BITCrashManager.h @@ -265,13 +265,13 @@ typedef NS_ENUM(NSUInteger, BITCrashManagerUserInput) { @return BOOl if the input is a valid option @see BITCrashManagerUserInput */ -- (BOOL)handleUserInput:(BITCrashManagerUserInput)userInput crashMetaDescription:metaDescription; +- (BOOL)handleUserInput:(BITCrashManagerUserInput)userInput withUserProvidedCrashDescription:(NSString*)metaDescription; /** Property that lets you set a custom block which handles showing a custom UI and asking the user whether he wants to send the crash report. Needs to call the `handleUserInput` method. */ -@property (nonatomic, copy, setter = setAlertViewHandler:) void (^alertViewHandler) (); +@property (nonatomic, copy, setter = setAlertViewHandler:) customAlertViewHandler alertViewHandler; /** Provides the time between startup and crash in seconds diff --git a/Classes/BITCrashManager.m b/Classes/BITCrashManager.m index 8fa17d2214..303c0d0912 100644 --- a/Classes/BITCrashManager.m +++ b/Classes/BITCrashManager.m @@ -496,7 +496,7 @@ NSString *const kBITCrashManagerStatus = @"BITCrashManagerStatus"; } -- (BOOL)handleUserInput:(BITCrashManagerUserInput)userInput crashMetaDescription:(NSString *)metaDescription{ +- (BOOL)handleUserInput:(BITCrashManagerUserInput)userInput withUserProvidedCrashDescription:(NSString *)metaDescription{ switch (userInput) { case BITCrashManagerUserInputDontSend: if (self.delegate != nil && [self.delegate respondsToSelector:@selector(crashManagerWillCancelSendingCrashReport:)]) { @@ -990,13 +990,13 @@ NSString *const kBITCrashManagerStatus = @"BITCrashManagerStatus"; - (void)alertView:(UIAlertView *)alertView didDismissWithButtonIndex:(NSInteger)buttonIndex { switch (buttonIndex) { case 0: - [self handleUserInput:BITCrashManagerUserInputDontSend crashMetaDescription:nil]; + [self handleUserInput:BITCrashManagerUserInputDontSend withUserProvidedCrashDescription:nil]; break; case 1: - [self handleUserInput:BITCrashManagerUserInputSend crashMetaDescription:nil]; + [self handleUserInput:BITCrashManagerUserInputSend withUserProvidedCrashDescription:nil]; break; case 2: - [self handleUserInput:BITCrashManagerUserInputAlwaysSend crashMetaDescription:nil]; + [self handleUserInput:BITCrashManagerUserInputAlwaysSend withUserProvidedCrashDescription:nil]; break; } } diff --git a/Support/HockeySDKTests/BITCrashManagerTests.m b/Support/HockeySDKTests/BITCrashManagerTests.m index 4e1c09e754..45f6c044db 100644 --- a/Support/HockeySDKTests/BITCrashManagerTests.m +++ b/Support/HockeySDKTests/BITCrashManagerTests.m @@ -129,14 +129,14 @@ id delegateMock = mockProtocol(@protocol(BITCrashManagerDelegate)); _sut.delegate = delegateMock; - assertThatBool([_sut handleUserInput:BITCrashManagerUserInputDontSend crashMetaDescription:nil], equalToBool(YES)); + assertThatBool([_sut handleUserInput:BITCrashManagerUserInputDontSend withUserProvidedCrashDescription:nil], equalToBool(YES)); [verify(delegateMock) crashManagerWillCancelSendingCrashReport:_sut]; } - (void)testHandleUserInputSend { - assertThatBool([_sut handleUserInput:BITCrashManagerUserInputSend crashMetaDescription:nil], equalToBool(YES)); + assertThatBool([_sut handleUserInput:BITCrashManagerUserInputSend withUserProvidedCrashDescription:nil], equalToBool(YES)); } - (void)testHandleUserInputAlwaysSend { @@ -148,7 +148,7 @@ [given([mockUserDefaults integerForKey:@"BITCrashManagerStatus"]) willReturn:nil]; //Test if method runs through - assertThatBool([_sut handleUserInput:BITCrashManagerUserInputAlwaysSend crashMetaDescription:nil], equalToBool(YES)); + assertThatBool([_sut handleUserInput:BITCrashManagerUserInputAlwaysSend withUserProvidedCrashDescription:nil], equalToBool(YES)); //Test if correct CrashManagerStatus is now set [given([mockUserDefaults integerForKey:@"BITCrashManagerStauts"]) willReturnInt:BITCrashManagerStatusAutoSend]; From f8561afacc4e47a00ceccee2f9d29908674386e2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Lukas=20Spie=C3=9F?= Date: Thu, 24 Apr 2014 16:11:05 +0200 Subject: [PATCH 24/43] Updates documentation --- Classes/BITCrashManager.h | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/Classes/BITCrashManager.h b/Classes/BITCrashManager.h index 476145969d..3afe47092b 100644 --- a/Classes/BITCrashManager.h +++ b/Classes/BITCrashManager.h @@ -260,16 +260,19 @@ typedef NS_ENUM(NSUInteger, BITCrashManagerUserInput) { /** Provides an interface to handle user input from a custom alert - On this input depends, whether crash reports are sent, always sent or not sent and deleted. + @param userInput On this input depends, whether crash reports are sent, always sent or not sent and deleted. + @param metaDescription The content of this string will be attached to the crash report as the description and allows to ask the user for e.g. additional comments or info - @return BOOl if the input is a valid option + @return Returns YES if the input is a valid option and successfully triggered further processing of the crash report @see BITCrashManagerUserInput */ - (BOOL)handleUserInput:(BITCrashManagerUserInput)userInput withUserProvidedCrashDescription:(NSString*)metaDescription; /** Property that lets you set a custom block which handles showing a custom UI and asking the user - whether he wants to send the crash report. Needs to call the `handleUserInput` method. + whether he wants to send the crash report. + + @warning Needs to call the `handleUserInput` method! */ @property (nonatomic, copy, setter = setAlertViewHandler:) customAlertViewHandler alertViewHandler; From 3fd8cdc7e0b4d4b3227302575b29dff2498e088c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Lukas=20Spie=C3=9F?= Date: Thu, 24 Apr 2014 16:11:34 +0200 Subject: [PATCH 25/43] replace ugly block notation with clean typedef --- Classes/BITCrashManager.h | 5 +++++ Classes/BITCrashManager.m | 2 +- 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/Classes/BITCrashManager.h b/Classes/BITCrashManager.h index 3afe47092b..9a6ba8a0f5 100644 --- a/Classes/BITCrashManager.h +++ b/Classes/BITCrashManager.h @@ -39,6 +39,11 @@ #import #endif +/** + * Custom block that handles the alert that prompts the user whether he wants to send crash reports + */ +typedef void(^customAlertViewHandler)(); + /** * Crash Manager status diff --git a/Classes/BITCrashManager.m b/Classes/BITCrashManager.m index 303c0d0912..f4cb6e1508 100644 --- a/Classes/BITCrashManager.m +++ b/Classes/BITCrashManager.m @@ -446,7 +446,7 @@ NSString *const kBITCrashManagerStatus = @"BITCrashManagerStatus"; } -- (void)setAlertViewHandler:(void (^)())alertViewHandler{ +- (void)setAlertViewHandler:(customAlertViewHandler)alertViewHandler{ _alertViewHandler = alertViewHandler; } From 10c5475a02b5387b006e119384e8f0ba69091d5a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Lukas=20Spie=C3=9F?= Date: Thu, 24 Apr 2014 16:32:34 +0200 Subject: [PATCH 26/43] make method signature consistent and reflect those changes in documentation --- Classes/BITCrashManager.h | 4 ++-- Classes/BITCrashManager.m | 10 +++++----- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/Classes/BITCrashManager.h b/Classes/BITCrashManager.h index 9a6ba8a0f5..825ec48913 100644 --- a/Classes/BITCrashManager.h +++ b/Classes/BITCrashManager.h @@ -266,12 +266,12 @@ typedef NS_ENUM(NSUInteger, BITCrashManagerUserInput) { Provides an interface to handle user input from a custom alert @param userInput On this input depends, whether crash reports are sent, always sent or not sent and deleted. - @param metaDescription The content of this string will be attached to the crash report as the description and allows to ask the user for e.g. additional comments or info + @param userProvidedCrashDescription The content of this string will be attached to the crash report as the description and allows to ask the user for e.g. additional comments or info @return Returns YES if the input is a valid option and successfully triggered further processing of the crash report @see BITCrashManagerUserInput */ -- (BOOL)handleUserInput:(BITCrashManagerUserInput)userInput withUserProvidedCrashDescription:(NSString*)metaDescription; +- (BOOL)handleUserInput:(BITCrashManagerUserInput)userInput withUserProvidedCrashDescription:(NSString*)userProvidedCrashDescription; /** Property that lets you set a custom block which handles showing a custom UI and asking the user diff --git a/Classes/BITCrashManager.m b/Classes/BITCrashManager.m index f4cb6e1508..8f9327649d 100644 --- a/Classes/BITCrashManager.m +++ b/Classes/BITCrashManager.m @@ -496,7 +496,7 @@ NSString *const kBITCrashManagerStatus = @"BITCrashManagerStatus"; } -- (BOOL)handleUserInput:(BITCrashManagerUserInput)userInput withUserProvidedCrashDescription:(NSString *)metaDescription{ +- (BOOL)handleUserInput:(BITCrashManagerUserInput)userInput withUserProvidedCrashDescription:(NSString *)userProvidedCrashDescription{ switch (userInput) { case BITCrashManagerUserInputDontSend: if (self.delegate != nil && [self.delegate respondsToSelector:@selector(crashManagerWillCancelSendingCrashReport:)]) { @@ -507,9 +507,9 @@ NSString *const kBITCrashManagerStatus = @"BITCrashManagerStatus"; return YES; case BITCrashManagerUserInputSend: - if (metaDescription && [metaDescription length] > 0) { + if (userProvidedCrashDescription && [userProvidedCrashDescription length] > 0) { NSError *error; - [metaDescription writeToFile:[NSString stringWithFormat:@"%@_description.meta", [_crashesDir stringByAppendingPathComponent: _lastCrashFilename]] atomically:YES encoding:NSUTF8StringEncoding error:&error]; + [userProvidedCrashDescription writeToFile:[NSString stringWithFormat:@"%@_description.meta", [_crashesDir stringByAppendingPathComponent: _lastCrashFilename]] atomically:YES encoding:NSUTF8StringEncoding error:&error]; } [self sendCrashReports]; return YES; @@ -522,9 +522,9 @@ NSString *const kBITCrashManagerStatus = @"BITCrashManagerStatus"; [self.delegate crashManagerWillSendCrashReportsAlways:self]; } - if (metaDescription && [metaDescription length] > 0) { + if (userProvidedCrashDescription && [userProvidedCrashDescription length] > 0) { NSError *error; - [metaDescription writeToFile:[NSString stringWithFormat:@"%@_description.meta", [_crashesDir stringByAppendingPathComponent: _lastCrashFilename]] atomically:YES encoding:NSUTF8StringEncoding error:&error]; + [userProvidedCrashDescription writeToFile:[NSString stringWithFormat:@"%@_description.meta", [_crashesDir stringByAppendingPathComponent: _lastCrashFilename]] atomically:YES encoding:NSUTF8StringEncoding error:&error]; } [self sendCrashReports]; From b5348f7dcbd33392164ae5e66fd085dbcab7f5b3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Lukas=20Spie=C3=9F?= Date: Thu, 24 Apr 2014 16:38:23 +0200 Subject: [PATCH 27/43] Types should start with a capital letter --- Classes/BITCrashManager.h | 2 +- Classes/BITCrashManager.m | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/Classes/BITCrashManager.h b/Classes/BITCrashManager.h index 825ec48913..8312702ae0 100644 --- a/Classes/BITCrashManager.h +++ b/Classes/BITCrashManager.h @@ -42,7 +42,7 @@ /** * Custom block that handles the alert that prompts the user whether he wants to send crash reports */ -typedef void(^customAlertViewHandler)(); +typedef void(^CustomAlertViewHandler)(); /** diff --git a/Classes/BITCrashManager.m b/Classes/BITCrashManager.m index 8f9327649d..3c546c4786 100644 --- a/Classes/BITCrashManager.m +++ b/Classes/BITCrashManager.m @@ -55,7 +55,6 @@ #define kBITCrashMetaUserID @"BITCrashMetaUserID" #define kBITCrashMetaApplicationLog @"BITCrashMetaApplicationLog" #define kBITCrashMetaAttachment @"BITCrashMetaAttachment" -#define kBITCrashMetaDescription @"BITCrashMetaDescription" // internal keys NSString *const KBITAttachmentDictIndex = @"index"; @@ -67,6 +66,7 @@ NSString *const kBITCrashManagerStatus = @"BITCrashManagerStatus"; @interface BITCrashManager () @property (nonatomic, strong) NSFileManager *fileManager; +@property (nonatomic, copy, setter = setAlertViewHandler:) CustomAlertViewHandler alertViewHandler; @end @@ -79,7 +79,7 @@ NSString *const kBITCrashManagerStatus = @"BITCrashManagerStatus"; NSString *_settingsFile; NSString *_analyzerInProgressFile; NSFileManager *_fileManager; - + PLCrashReporterCallbacks *_crashCallBacks; BOOL _crashIdenticalCurrentVersion; @@ -446,7 +446,7 @@ NSString *const kBITCrashManagerStatus = @"BITCrashManagerStatus"; } -- (void)setAlertViewHandler:(customAlertViewHandler)alertViewHandler{ +- (void)setAlertViewHandler:(CustomAlertViewHandler)alertViewHandler{ _alertViewHandler = alertViewHandler; } From e026e42664c009aed605e5eca22fafee395a0999 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Lukas=20Spie=C3=9F?= Date: Thu, 24 Apr 2014 16:41:07 +0200 Subject: [PATCH 28/43] expose setter rather than the whole property --- Classes/BITCrashManager.h | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/Classes/BITCrashManager.h b/Classes/BITCrashManager.h index 8312702ae0..0e0c69960d 100644 --- a/Classes/BITCrashManager.h +++ b/Classes/BITCrashManager.h @@ -274,12 +274,12 @@ typedef NS_ENUM(NSUInteger, BITCrashManagerUserInput) { - (BOOL)handleUserInput:(BITCrashManagerUserInput)userInput withUserProvidedCrashDescription:(NSString*)userProvidedCrashDescription; /** - Property that lets you set a custom block which handles showing a custom UI and asking the user + Lets you set a custom block which handles showing a custom UI and asking the user whether he wants to send the crash report. - @warning Needs to call the `handleUserInput` method! + @warning Block needs to call the `handleUserInput:withUserProvidedCrashDescription` method! */ -@property (nonatomic, copy, setter = setAlertViewHandler:) customAlertViewHandler alertViewHandler; +- (void) setAlertViewHandler:(CustomAlertViewHandler)alertViewHandler; /** Provides the time between startup and crash in seconds From 3b2b76b34ec6bbfc46279598a8af1bce87db8eb7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Lukas=20Spie=C3=9F?= Date: Thu, 24 Apr 2014 16:46:11 +0200 Subject: [PATCH 29/43] move persisting of userProvidedCrashDescription to own method --- Classes/BITCrashManager.m | 18 ++++++++++-------- 1 file changed, 10 insertions(+), 8 deletions(-) diff --git a/Classes/BITCrashManager.m b/Classes/BITCrashManager.m index 3c546c4786..f6b414ae10 100644 --- a/Classes/BITCrashManager.m +++ b/Classes/BITCrashManager.m @@ -252,6 +252,13 @@ NSString *const kBITCrashManagerStatus = @"BITCrashManagerStatus"; [data writeToFile:attachmentFilename atomically:YES]; } +- (void)persistUserProvidedCrashDescription:(NSString *)userProvidedCrashDescription { + if (userProvidedCrashDescription && [userProvidedCrashDescription length] > 0) { + NSError *error; + [userProvidedCrashDescription writeToFile:[NSString stringWithFormat:@"%@.desc", [_crashesDir stringByAppendingPathComponent: _lastCrashFilename]] atomically:YES encoding:NSUTF8StringEncoding error:&error]; + } +} + /** * Read the attachment data from the stored file * @@ -507,10 +514,8 @@ NSString *const kBITCrashManagerStatus = @"BITCrashManagerStatus"; return YES; case BITCrashManagerUserInputSend: - if (userProvidedCrashDescription && [userProvidedCrashDescription length] > 0) { - NSError *error; - [userProvidedCrashDescription writeToFile:[NSString stringWithFormat:@"%@_description.meta", [_crashesDir stringByAppendingPathComponent: _lastCrashFilename]] atomically:YES encoding:NSUTF8StringEncoding error:&error]; - } + [self persistUserProvidedCrashDescription:userProvidedCrashDescription]; + [self sendCrashReports]; return YES; @@ -522,10 +527,7 @@ NSString *const kBITCrashManagerStatus = @"BITCrashManagerStatus"; [self.delegate crashManagerWillSendCrashReportsAlways:self]; } - if (userProvidedCrashDescription && [userProvidedCrashDescription length] > 0) { - NSError *error; - [userProvidedCrashDescription writeToFile:[NSString stringWithFormat:@"%@_description.meta", [_crashesDir stringByAppendingPathComponent: _lastCrashFilename]] atomically:YES encoding:NSUTF8StringEncoding error:&error]; - } + [self persistUserProvidedCrashDescription:userProvidedCrashDescription]; [self sendCrashReports]; return YES; From 5442b217eb2e355cfb75cbe0a9fe3229b53e3e8e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Lukas=20Spie=C3=9F?= Date: Thu, 24 Apr 2014 16:47:47 +0200 Subject: [PATCH 30/43] Use .desc file extension for userProvidedCrashDescription --- Classes/BITCrashManager.m | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/Classes/BITCrashManager.m b/Classes/BITCrashManager.m index f6b414ae10..e0e5e3b135 100644 --- a/Classes/BITCrashManager.m +++ b/Classes/BITCrashManager.m @@ -227,6 +227,8 @@ NSString *const kBITCrashManagerStatus = @"BITCrashManagerStatus"; [_fileManager removeItemAtPath:[_crashFiles objectAtIndex:i] error:&error]; [_fileManager removeItemAtPath:[[_crashFiles objectAtIndex:i] stringByAppendingString:@".data"] error:&error]; [_fileManager removeItemAtPath:[[_crashFiles objectAtIndex:i] stringByAppendingString:@".meta"] error:&error]; + [_fileManager removeItemAtPath:[[_crashFiles objectAtIndex:i] stringByAppendingString:@".desc"] error:&error]; + NSString *cacheFilename = [[_crashFiles objectAtIndex:i] lastPathComponent]; [self removeKeyFromKeychain:[NSString stringWithFormat:@"%@.%@", cacheFilename, kBITCrashMetaUserName]]; @@ -665,7 +667,8 @@ NSString *const kBITCrashManagerStatus = @"BITCrashManagerStatus"; ![file hasSuffix:@".analyzer"] && ![file hasSuffix:@".plist"] && ![file hasSuffix:@".data"] && - ![file hasSuffix:@".meta"]) { + ![file hasSuffix:@".meta"] && + ![file hasSuffix:@".desc"]) { [_crashFiles addObject:[_crashesDir stringByAppendingPathComponent: file]]; } } @@ -884,6 +887,7 @@ NSString *const kBITCrashManagerStatus = @"BITCrashManagerStatus"; [_fileManager removeItemAtPath:filename error:&error]; [_fileManager removeItemAtPath:[NSString stringWithFormat:@"%@.data", filename] error:&error]; [_fileManager removeItemAtPath:[NSString stringWithFormat:@"%@.meta", filename] error:&error]; + [_fileManager removeItemAtPath:[NSString stringWithFormat:@"%@.desc", filename] error:&error]; [self removeKeyFromKeychain:[NSString stringWithFormat:@"%@.%@", cacheFilename, kBITCrashMetaUserName]]; [self removeKeyFromKeychain:[NSString stringWithFormat:@"%@.%@", cacheFilename, kBITCrashMetaUserEmail]]; @@ -927,7 +931,7 @@ NSString *const kBITCrashManagerStatus = @"BITCrashManagerStatus"; useremail = [self stringValueFromKeychainForKey:[NSString stringWithFormat:@"%@.%@", cacheFilename, kBITCrashMetaUserEmail]] ?: @""; userid = [self stringValueFromKeychainForKey:[NSString stringWithFormat:@"%@.%@", cacheFilename, kBITCrashMetaUserID]] ?: @""; applicationLog = [metaDict objectForKey:kBITCrashMetaApplicationLog] ?: @""; - description = [NSString stringWithContentsOfFile:[NSString stringWithFormat:@"%@_description.meta", [_crashesDir stringByAppendingPathComponent: cacheFilename]] encoding:NSUTF8StringEncoding error:&error]; + description = [NSString stringWithContentsOfFile:[NSString stringWithFormat:@"%@.desc", [_crashesDir stringByAppendingPathComponent: cacheFilename]] encoding:NSUTF8StringEncoding error:&error]; BITCrashAttachment *attachment = [self attachmentForCrashReport:filename]; if (attachment) { From 080eb3c657aa5352d3f1e08d6e4fc96e0f1d3222 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Lukas=20Spie=C3=9F?= Date: Thu, 24 Apr 2014 16:58:31 +0200 Subject: [PATCH 31/43] More detailed documentation --- Classes/BITCrashManager.h | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/Classes/BITCrashManager.h b/Classes/BITCrashManager.h index 0e0c69960d..fbcbcd958f 100644 --- a/Classes/BITCrashManager.h +++ b/Classes/BITCrashManager.h @@ -266,7 +266,7 @@ typedef NS_ENUM(NSUInteger, BITCrashManagerUserInput) { Provides an interface to handle user input from a custom alert @param userInput On this input depends, whether crash reports are sent, always sent or not sent and deleted. - @param userProvidedCrashDescription The content of this string will be attached to the crash report as the description and allows to ask the user for e.g. additional comments or info + @param userProvidedCrashDescription The content of this optional string will be attached to the crash report as the description and allows to ask the user for e.g. additional comments or info @return Returns YES if the input is a valid option and successfully triggered further processing of the crash report @see BITCrashManagerUserInput @@ -275,7 +275,9 @@ typedef NS_ENUM(NSUInteger, BITCrashManagerUserInput) { /** Lets you set a custom block which handles showing a custom UI and asking the user - whether he wants to send the crash report. + whether he wants to send the crash report. + + @param alertViewHandler A block that is responsible for loading, presenting and and dismissing your custom user interface which prompts the user if he wants to send crash reports. The block is also responsible for triggering further processing of the crash reports. @warning Block needs to call the `handleUserInput:withUserProvidedCrashDescription` method! */ From d232fe58f6cc09b9dc4814c7f1c7960537518a75 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Lukas=20Spie=C3=9F?= Date: Thu, 24 Apr 2014 18:17:46 +0200 Subject: [PATCH 32/43] move cleanup to separate method --- Classes/BITCrashManager.m | 40 ++++++++++++++++----------------------- 1 file changed, 16 insertions(+), 24 deletions(-) diff --git a/Classes/BITCrashManager.m b/Classes/BITCrashManager.m index e0e5e3b135..fd7bda8e02 100644 --- a/Classes/BITCrashManager.m +++ b/Classes/BITCrashManager.m @@ -224,16 +224,11 @@ NSString *const kBITCrashManagerStatus = @"BITCrashManagerStatus"; NSError *error = NULL; for (NSUInteger i=0; i < [_crashFiles count]; i++) { - [_fileManager removeItemAtPath:[_crashFiles objectAtIndex:i] error:&error]; - [_fileManager removeItemAtPath:[[_crashFiles objectAtIndex:i] stringByAppendingString:@".data"] error:&error]; - [_fileManager removeItemAtPath:[[_crashFiles objectAtIndex:i] stringByAppendingString:@".meta"] error:&error]; - [_fileManager removeItemAtPath:[[_crashFiles objectAtIndex:i] stringByAppendingString:@".desc"] error:&error]; - - + NSString *filename = [_crashFiles objectAtIndex:i]; NSString *cacheFilename = [[_crashFiles objectAtIndex:i] lastPathComponent]; - [self removeKeyFromKeychain:[NSString stringWithFormat:@"%@.%@", cacheFilename, kBITCrashMetaUserName]]; - [self removeKeyFromKeychain:[NSString stringWithFormat:@"%@.%@", cacheFilename, kBITCrashMetaUserEmail]]; - [self removeKeyFromKeychain:[NSString stringWithFormat:@"%@.%@", cacheFilename, kBITCrashMetaUserID]]; + + [self cleanFilesAndKeychainWithFileName:filename CacheFilename:cacheFilename Error:error]; + } [_crashFiles removeAllObjects]; [_approvedCrashReports removeAllObjects]; @@ -241,6 +236,16 @@ NSString *const kBITCrashManagerStatus = @"BITCrashManagerStatus"; [self saveSettings]; } +- (void)cleanFilesAndKeychainWithFileName:(NSString *)filename CacheFilename:(NSString *)cacheFilename Error:(NSError *)error{ + [_fileManager removeItemAtPath:filename error:&error]; + [_fileManager removeItemAtPath:[NSString stringWithFormat:@"%@.data", filename] error:&error]; + [_fileManager removeItemAtPath:[NSString stringWithFormat:@"%@.meta", filename] error:&error]; + [_fileManager removeItemAtPath:[NSString stringWithFormat:@"%@.desc", filename] error:&error]; + + [self removeKeyFromKeychain:[NSString stringWithFormat:@"%@.%@", cacheFilename, kBITCrashMetaUserName]]; + [self removeKeyFromKeychain:[NSString stringWithFormat:@"%@.%@", cacheFilename, kBITCrashMetaUserEmail]]; + [self removeKeyFromKeychain:[NSString stringWithFormat:@"%@.%@", cacheFilename, kBITCrashMetaUserID]]; +} - (void)persistAttachment:(BITCrashAttachment *)attachment withFilename:(NSString *)filename { NSString *attachmentFilename = [filename stringByAppendingString:@".data"]; @@ -884,14 +889,7 @@ NSString *const kBITCrashManagerStatus = @"BITCrashManagerStatus"; if (report == nil) { BITHockeyLog(@"WARNING: Could not parse crash report"); // we cannot do anything with this report, so delete it - [_fileManager removeItemAtPath:filename error:&error]; - [_fileManager removeItemAtPath:[NSString stringWithFormat:@"%@.data", filename] error:&error]; - [_fileManager removeItemAtPath:[NSString stringWithFormat:@"%@.meta", filename] error:&error]; - [_fileManager removeItemAtPath:[NSString stringWithFormat:@"%@.desc", filename] error:&error]; - - [self removeKeyFromKeychain:[NSString stringWithFormat:@"%@.%@", cacheFilename, kBITCrashMetaUserName]]; - [self removeKeyFromKeychain:[NSString stringWithFormat:@"%@.%@", cacheFilename, kBITCrashMetaUserEmail]]; - [self removeKeyFromKeychain:[NSString stringWithFormat:@"%@.%@", cacheFilename, kBITCrashMetaUserID]]; + [self cleanFilesAndKeychainWithFileName:filename CacheFilename:cacheFilename Error:error]; continue; } @@ -972,13 +970,7 @@ NSString *const kBITCrashManagerStatus = @"BITCrashManagerStatus"; [_approvedCrashReports setObject:[NSNumber numberWithBool:YES] forKey:filename]; } else { // we cannot do anything with this report, so delete it - [_fileManager removeItemAtPath:filename error:&error]; - [_fileManager removeItemAtPath:[NSString stringWithFormat:@"%@.data", filename] error:&error]; - [_fileManager removeItemAtPath:[NSString stringWithFormat:@"%@.meta", filename] error:&error]; - - [self removeKeyFromKeychain:[NSString stringWithFormat:@"%@.%@", cacheFilename, kBITCrashMetaUserName]]; - [self removeKeyFromKeychain:[NSString stringWithFormat:@"%@.%@", cacheFilename, kBITCrashMetaUserEmail]]; - [self removeKeyFromKeychain:[NSString stringWithFormat:@"%@.%@", cacheFilename, kBITCrashMetaUserID]]; + [self cleanFilesAndKeychainWithFileName:filename CacheFilename:cacheFilename Error:error]; } } From 654620ee3f61cd672d836b738317470f45d8736f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Lukas=20Spie=C3=9F?= Date: Thu, 24 Apr 2014 18:18:09 +0200 Subject: [PATCH 33/43] add test for persistUserProvidedCrashDescription --- Classes/BITCrashManager.m | 7 +++++++ Classes/BITCrashManagerPrivate.h | 5 +++++ Support/HockeySDKTests/BITCrashManagerTests.m | 7 +++++++ 3 files changed, 19 insertions(+) diff --git a/Classes/BITCrashManager.m b/Classes/BITCrashManager.m index fd7bda8e02..5714696ebc 100644 --- a/Classes/BITCrashManager.m +++ b/Classes/BITCrashManager.m @@ -451,6 +451,13 @@ NSString *const kBITCrashManagerStatus = @"BITCrashManagerStatus"; return useremail; } +- (NSString *)getCrashesDir { + return _crashesDir; +} + +- (void)setLastCrashFilename:(NSString *)lastCrashFilename { + _lastCrashFilename = lastCrashFilename; +} #pragma mark - Public diff --git a/Classes/BITCrashManagerPrivate.h b/Classes/BITCrashManagerPrivate.h index f2231342f4..cf4296432d 100644 --- a/Classes/BITCrashManagerPrivate.h +++ b/Classes/BITCrashManagerPrivate.h @@ -61,9 +61,14 @@ - (BOOL)hasPendingCrashReport; - (BOOL)hasNonApprovedCrashReports; +- (void)persistUserProvidedCrashDescription:(NSString *)userProvidedCrashDescription; + - (void)invokeDelayedProcessing; - (void)sendCrashReports; +- (NSString *)getCrashesDir; +- (void)setLastCrashFilename:(NSString *)lastCrashFilename; + @end diff --git a/Support/HockeySDKTests/BITCrashManagerTests.m b/Support/HockeySDKTests/BITCrashManagerTests.m index 45f6c044db..8cecb1fb9c 100644 --- a/Support/HockeySDKTests/BITCrashManagerTests.m +++ b/Support/HockeySDKTests/BITCrashManagerTests.m @@ -71,6 +71,13 @@ [self startManager]; } +- (void)testPersistUserProvidedCrashDescription { + [_sut setLastCrashFilename:@"temp"]; + [_sut persistUserProvidedCrashDescription:@"Test string"]; + NSError *error; + NSString *description = [NSString stringWithContentsOfFile:[NSString stringWithFormat:@"%@.desc", [[_sut getCrashesDir] stringByAppendingPathComponent: @"temp"]] encoding:NSUTF8StringEncoding error:&error]; + assertThat(description, equalTo(@"Test string")); +} #pragma mark - Setup Tests From 535537aa739a5e5ee43cd7c75ff42a8a4275033a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Lukas=20Spie=C3=9F?= Date: Fri, 25 Apr 2014 14:23:18 +0200 Subject: [PATCH 34/43] add additional tests --- Classes/BITCrashManager.m | 4 --- Classes/BITCrashManagerPrivate.h | 3 ++ Support/HockeySDKTests/BITCrashManagerTests.m | 30 ++++++++++++++----- 3 files changed, 25 insertions(+), 12 deletions(-) diff --git a/Classes/BITCrashManager.m b/Classes/BITCrashManager.m index 5714696ebc..99ee061400 100644 --- a/Classes/BITCrashManager.m +++ b/Classes/BITCrashManager.m @@ -455,10 +455,6 @@ NSString *const kBITCrashManagerStatus = @"BITCrashManagerStatus"; return _crashesDir; } -- (void)setLastCrashFilename:(NSString *)lastCrashFilename { - _lastCrashFilename = lastCrashFilename; -} - #pragma mark - Public diff --git a/Classes/BITCrashManagerPrivate.h b/Classes/BITCrashManagerPrivate.h index cf4296432d..c12749e7df 100644 --- a/Classes/BITCrashManagerPrivate.h +++ b/Classes/BITCrashManagerPrivate.h @@ -38,6 +38,8 @@ @property (nonatomic, strong) BITPLCrashReporter *plCrashReporter; +@property (nonatomic) NSString *lastCrashFilename; + #if HOCKEYSDK_FEATURE_AUTHENTICATOR // Only set via BITAuthenticator @@ -62,6 +64,7 @@ - (BOOL)hasNonApprovedCrashReports; - (void)persistUserProvidedCrashDescription:(NSString *)userProvidedCrashDescription; +- (void)persistAttachment:(BITCrashAttachment *)attachment withFilename:(NSString *)filename; - (void)invokeDelayedProcessing; - (void)sendCrashReports; diff --git a/Support/HockeySDKTests/BITCrashManagerTests.m b/Support/HockeySDKTests/BITCrashManagerTests.m index 8cecb1fb9c..efae8c1c48 100644 --- a/Support/HockeySDKTests/BITCrashManagerTests.m +++ b/Support/HockeySDKTests/BITCrashManagerTests.m @@ -71,14 +71,6 @@ [self startManager]; } -- (void)testPersistUserProvidedCrashDescription { - [_sut setLastCrashFilename:@"temp"]; - [_sut persistUserProvidedCrashDescription:@"Test string"]; - NSError *error; - NSString *description = [NSString stringWithContentsOfFile:[NSString stringWithFormat:@"%@.desc", [[_sut getCrashesDir] stringByAppendingPathComponent: @"temp"]] encoding:NSUTF8StringEncoding error:&error]; - assertThat(description, equalTo(@"Test string")); -} - #pragma mark - Setup Tests - (void)testThatItInstantiates { @@ -88,6 +80,24 @@ #pragma mark - Persistence tests +- (void)testPersistUserProvidedCrashDescription { + NSString *tempCrashName = @"tempCrash"; + [_sut setLastCrashFilename:tempCrashName]; + [_sut persistUserProvidedCrashDescription:@"Test string"]; + + NSError *error; + NSString *description = [NSString stringWithContentsOfFile:[NSString stringWithFormat:@"%@.desc", [[_sut getCrashesDir] stringByAppendingPathComponent: tempCrashName]] encoding:NSUTF8StringEncoding error:&error]; + assertThat(description, equalTo(@"Test string")); +} + +- (void)testPersistAttachment { + BITCrashAttachment *attachment = mock([BITCrashAttachment class]); + NSString *filename = @"testAttachment"; + + [_sut persistAttachment:attachment withFilename:filename]; + + [given([NSData dataWithContentsOfFile:[filename stringByAppendingString:@".data"]]) willReturn:[NSMutableDictionary class]]; +} #pragma mark - Helper @@ -165,6 +175,10 @@ } +- (void)testHandleUserInputWithInvalidInput { + assertThatBool([_sut handleUserInput:3 withUserProvidedCrashDescription:nil], equalToBool(NO)); +} + #pragma mark - Debugger /** From 82b217f19d774e188d6161fe3b947bee167fa9be Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Lukas=20Spie=C3=9F?= Date: Fri, 25 Apr 2014 16:23:39 +0200 Subject: [PATCH 35/43] Much better test for attachment persisting --- Classes/BITCrashManager.m | 1 - Classes/BITCrashManagerPrivate.h | 4 ++++ Support/HockeySDKTests/BITCrashManagerTests.m | 17 +++++++++++++---- 3 files changed, 17 insertions(+), 5 deletions(-) diff --git a/Classes/BITCrashManager.m b/Classes/BITCrashManager.m index 99ee061400..de9fb5dbbe 100644 --- a/Classes/BITCrashManager.m +++ b/Classes/BITCrashManager.m @@ -65,7 +65,6 @@ NSString *const kBITCrashManagerStatus = @"BITCrashManagerStatus"; @interface BITCrashManager () -@property (nonatomic, strong) NSFileManager *fileManager; @property (nonatomic, copy, setter = setAlertViewHandler:) CustomAlertViewHandler alertViewHandler; @end diff --git a/Classes/BITCrashManagerPrivate.h b/Classes/BITCrashManagerPrivate.h index c12749e7df..36e8ce9607 100644 --- a/Classes/BITCrashManagerPrivate.h +++ b/Classes/BITCrashManagerPrivate.h @@ -36,6 +36,8 @@ @property (nonatomic) NSUncaughtExceptionHandler *exceptionHandler; +@property (nonatomic, strong) NSFileManager *fileManager; + @property (nonatomic, strong) BITPLCrashReporter *plCrashReporter; @property (nonatomic) NSString *lastCrashFilename; @@ -66,6 +68,8 @@ - (void)persistUserProvidedCrashDescription:(NSString *)userProvidedCrashDescription; - (void)persistAttachment:(BITCrashAttachment *)attachment withFilename:(NSString *)filename; +- (BITCrashAttachment *)attachmentForCrashReport:(NSString *)filename; + - (void)invokeDelayedProcessing; - (void)sendCrashReports; diff --git a/Support/HockeySDKTests/BITCrashManagerTests.m b/Support/HockeySDKTests/BITCrashManagerTests.m index efae8c1c48..b6d0240921 100644 --- a/Support/HockeySDKTests/BITCrashManagerTests.m +++ b/Support/HockeySDKTests/BITCrashManagerTests.m @@ -23,6 +23,7 @@ #import "BITTestHelper.h" +#define kBITCrashMetaAttachment @"BITCrashMetaAttachment" @interface BITCrashManagerTests : SenTestCase @@ -91,12 +92,20 @@ } - (void)testPersistAttachment { - BITCrashAttachment *attachment = mock([BITCrashAttachment class]); - NSString *filename = @"testAttachment"; + NSString *filename = @"TestAttachment"; + NSData *data = [[NSData alloc] initWithBase64Encoding:@"TestData"]; + NSString* type = @"text/plain"; - [_sut persistAttachment:attachment withFilename:filename]; + BITCrashAttachment *originalAttachment = [[BITCrashAttachment alloc] initWithFilename:filename attachmentData:data contentType:type]; + NSString *attachmentFilename = [_sut.getCrashesDir stringByAppendingPathComponent:@"testAttachment"]; - [given([NSData dataWithContentsOfFile:[filename stringByAppendingString:@".data"]]) willReturn:[NSMutableDictionary class]]; + [_sut persistAttachment:originalAttachment withFilename:attachmentFilename]; + + BITCrashAttachment *decodedAttachment = [_sut attachmentForCrashReport:attachmentFilename]; + + assertThat(decodedAttachment.filename, equalTo(filename)); + assertThat(decodedAttachment.attachmentData, equalTo(data)); + assertThat(decodedAttachment.contentType, equalTo(type)); } #pragma mark - Helper From 38a59fc73f89049ddad986fa74c32a7923512fda Mon Sep 17 00:00:00 2001 From: Andreas Linde Date: Sun, 27 Apr 2014 20:20:10 +0200 Subject: [PATCH 36/43] Naming convention cleanup --- Classes/BITCrashManager.h | 4 ++-- Classes/BITCrashManager.m | 8 +------- Classes/BITCrashManagerPrivate.h | 2 ++ 3 files changed, 5 insertions(+), 9 deletions(-) diff --git a/Classes/BITCrashManager.h b/Classes/BITCrashManager.h index 2bb4f00fc2..49e32601c5 100644 --- a/Classes/BITCrashManager.h +++ b/Classes/BITCrashManager.h @@ -37,7 +37,7 @@ /** * Custom block that handles the alert that prompts the user whether he wants to send crash reports */ -typedef void(^CustomAlertViewHandler)(); +typedef void(^BITCustomAlertViewHandler)(); /** @@ -347,7 +347,7 @@ typedef NS_ENUM(NSUInteger, BITCrashManagerUserInput) { @warning Block needs to call the `handleUserInput:withUserProvidedCrashDescription` method! */ -- (void) setAlertViewHandler:(CustomAlertViewHandler)alertViewHandler; +- (void) setAlertViewHandler:(BITCustomAlertViewHandler)alertViewHandler; /** Indicates if the app was killed while being in foreground from the iOS diff --git a/Classes/BITCrashManager.m b/Classes/BITCrashManager.m index 35306955d7..093792fea6 100644 --- a/Classes/BITCrashManager.m +++ b/Classes/BITCrashManager.m @@ -95,12 +95,6 @@ static PLCrashReporterCallbacks plCrashCallbacks = { }; -@interface BITCrashManager () - -@property (nonatomic, copy, setter = setAlertViewHandler:) CustomAlertViewHandler alertViewHandler; - -@end - @implementation BITCrashManager { NSMutableDictionary *_approvedCrashReports; @@ -608,7 +602,7 @@ static PLCrashReporterCallbacks plCrashCallbacks = { } -- (void)setAlertViewHandler:(CustomAlertViewHandler)alertViewHandler{ +- (void)setAlertViewHandler:(BITCustomAlertViewHandler)alertViewHandler{ _alertViewHandler = alertViewHandler; } diff --git a/Classes/BITCrashManagerPrivate.h b/Classes/BITCrashManagerPrivate.h index eff1209fa8..b280b1ab29 100644 --- a/Classes/BITCrashManagerPrivate.h +++ b/Classes/BITCrashManagerPrivate.h @@ -51,6 +51,8 @@ @property (nonatomic) NSString *lastCrashFilename; +@property (nonatomic, copy, setter = setAlertViewHandler:) BITCustomAlertViewHandler alertViewHandler; + #if HOCKEYSDK_FEATURE_AUTHENTICATOR // Only set via BITAuthenticator From db45c6654a9acd9875199b81aac29f8f0858ef79 Mon Sep 17 00:00:00 2001 From: Andreas Linde Date: Sun, 27 Apr 2014 23:33:07 +0200 Subject: [PATCH 37/43] Minor code format update --- Classes/BITCrashManager.m | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Classes/BITCrashManager.m b/Classes/BITCrashManager.m index 093792fea6..cca18876da 100644 --- a/Classes/BITCrashManager.m +++ b/Classes/BITCrashManager.m @@ -253,7 +253,7 @@ static PLCrashReporterCallbacks plCrashCallbacks = { [_fileManager removeItemAtPath:filename error:&error]; [_fileManager removeItemAtPath:[filename stringByAppendingString:@".data"] error:&error]; [_fileManager removeItemAtPath:[filename stringByAppendingString:@".meta"] error:&error]; - [_fileManager removeItemAtPath:[filename stringByAppendingString:@".desc"]error:&error]; + [_fileManager removeItemAtPath:[filename stringByAppendingString:@".desc"] error:&error]; NSString *cacheFilename = [filename lastPathComponent]; [self removeKeyFromKeychain:[NSString stringWithFormat:@"%@.%@", cacheFilename, kBITCrashMetaUserName]]; From 2155f5f93b8dc5744f069da53aa0492f2f4fa49b Mon Sep 17 00:00:00 2001 From: Andreas Linde Date: Thu, 1 May 2014 16:57:28 +0200 Subject: [PATCH 38/43] Add app build property to BITCrashDetails --- Classes/BITCrashDetails.h | 29 ++++++++++++++++++++++++++++- Classes/BITCrashDetails.m | 4 +++- Classes/BITCrashManager.m | 2 ++ 3 files changed, 33 insertions(+), 2 deletions(-) diff --git a/Classes/BITCrashDetails.h b/Classes/BITCrashDetails.h index 1c5988f71b..b9322f35f3 100644 --- a/Classes/BITCrashDetails.h +++ b/Classes/BITCrashDetails.h @@ -10,26 +10,53 @@ @interface BITCrashDetails : NSObject +/** + * UUID for the crash report + */ @property (nonatomic, readonly, strong) NSString *incidentIdentifier; +/** + * UUID for the app installation on the device + */ @property (nonatomic, readonly, strong) NSString *reporterKey; +/** + * Signal that caused the crash + */ @property (nonatomic, readonly, strong) NSString *signal; +/** + * Exception name that triggered the crash, nil if the crash was not caused by an exception + */ @property (nonatomic, readonly, strong) NSString *exceptionName; +/** + * Exception reason, nil if the crash was not caused by an exception + */ @property (nonatomic, readonly, strong) NSString *exceptionReason; +/** + * Date and time the app started, nil if unknown + */ @property (nonatomic, readonly, strong) NSDate *appStartTime; +/** + * Date and time the crash occured, nil if unknown + */ @property (nonatomic, readonly, strong) NSDate *crashTime; +/** + * CFBundleVersion value of the app that crashed + */ +@property (nonatomic, readonly, strong) NSString *appBuild; + - (instancetype)initWithIncidentIdentifier:(NSString *)incidentIdentifier reporterKey:(NSString *)reporterKey signal:(NSString *)signal exceptionName:(NSString *)exceptionName exceptionReason:(NSString *)exceptionReason appStartTime:(NSDate *)appStartTime - crashTime:(NSDate *)crashTime; + crashTime:(NSDate *)crashTime + appBuild:(NSString *)appBuild; @end diff --git a/Classes/BITCrashDetails.m b/Classes/BITCrashDetails.m index d201fac9c1..187b7ba0bc 100644 --- a/Classes/BITCrashDetails.m +++ b/Classes/BITCrashDetails.m @@ -16,7 +16,8 @@ exceptionName:(NSString *)exceptionName exceptionReason:(NSString *)exceptionReason appStartTime:(NSDate *)appStartTime - crashTime:(NSDate *)crashTime; + crashTime:(NSDate *)crashTime + appBuild:(NSString *)appBuild { if ((self = [super init])) { _incidentIdentifier = incidentIdentifier; @@ -26,6 +27,7 @@ _exceptionReason = exceptionReason; _appStartTime = appStartTime; _crashTime = crashTime; + _appBuild = appBuild; } return self; } diff --git a/Classes/BITCrashManager.m b/Classes/BITCrashManager.m index cca18876da..226ae9bf3c 100644 --- a/Classes/BITCrashManager.m +++ b/Classes/BITCrashManager.m @@ -788,6 +788,7 @@ static PLCrashReporterCallbacks plCrashCallbacks = { exceptionReason:report.exceptionInfo.exceptionReason appStartTime:appStartTime crashTime:appCrashTime + appBuild:report.applicationInfo.applicationVersion ]; } } @@ -1135,6 +1136,7 @@ static PLCrashReporterCallbacks plCrashCallbacks = { exceptionReason:nil appStartTime:nil crashTime:nil + appBuild:fakeReportAppVersion ]; NSData *plist = [NSPropertyListSerialization dataFromPropertyList:(id)rootObj From cf926593f54978fafd14e4cb4456fece54fb4db2 Mon Sep 17 00:00:00 2001 From: Andreas Linde Date: Sun, 4 May 2014 17:55:00 +0200 Subject: [PATCH 39/43] Don't delete all crash reports when the user denies to send the latest one --- Classes/BITCrashManager.m | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/Classes/BITCrashManager.m b/Classes/BITCrashManager.m index 226ae9bf3c..83d52da796 100644 --- a/Classes/BITCrashManager.m +++ b/Classes/BITCrashManager.m @@ -268,6 +268,8 @@ static PLCrashReporterCallbacks plCrashCallbacks = { /** * Remove all crash reports and stored meta data for each from the file system and keychain + * + * This is currently only used as a helper method for tests */ - (void)cleanCrashReports { for (NSUInteger i=0; i < [_crashFiles count]; i++) { @@ -696,7 +698,7 @@ static PLCrashReporterCallbacks plCrashCallbacks = { [self.delegate crashManagerWillCancelSendingCrashReport:self]; } - [self cleanCrashReports]; + [self cleanCrashReportWithFilename:[_crashesDir stringByAppendingPathComponent: _lastCrashFilename]]; return YES; case BITCrashManagerUserInputSend: From 0f117b27c557cd931dd170fc8864489ae2465a3f Mon Sep 17 00:00:00 2001 From: Andreas Linde Date: Sun, 4 May 2014 17:56:45 +0200 Subject: [PATCH 40/43] Send each crash report individually one after another and only if the previous succeeds This reduces unnecessary network traffic and performance issues if there are lots of crashes pending for sending on the device --- Classes/BITCrashManager.m | 266 +++++++++--------- Classes/BITCrashManagerPrivate.h | 2 +- Support/HockeySDKTests/BITCrashManagerTests.m | 2 +- 3 files changed, 138 insertions(+), 132 deletions(-) diff --git a/Classes/BITCrashManager.m b/Classes/BITCrashManager.m index 83d52da796..7ac9094c36 100644 --- a/Classes/BITCrashManager.m +++ b/Classes/BITCrashManager.m @@ -704,7 +704,7 @@ static PLCrashReporterCallbacks plCrashCallbacks = { case BITCrashManagerUserInputSend: [self persistUserProvidedCrashDescription:userProvidedCrashDescription]; - [self sendCrashReports]; + [self sendNextCrashReport]; return YES; case BITCrashManagerUserInputAlwaysSend: @@ -717,7 +717,7 @@ static PLCrashReporterCallbacks plCrashCallbacks = { [self persistUserProvidedCrashDescription:userProvidedCrashDescription]; - [self sendCrashReports]; + [self sendNextCrashReport]; return YES; default: @@ -903,7 +903,7 @@ static PLCrashReporterCallbacks plCrashCallbacks = { if (!_sendingInProgress && [self hasPendingCrashReport]) { _sendingInProgress = YES; if (!BITHockeyBundle()) { - [self sendCrashReports]; + [self sendNextCrashReport]; } else if (_crashManagerStatus != BITCrashManagerStatusAutoSend && [self hasNonApprovedCrashReports]) { if (self.delegate != nil && [self.delegate respondsToSelector:@selector(crashManagerWillShowSubmitCrashReportAlert:)]) { @@ -940,7 +940,7 @@ static PLCrashReporterCallbacks plCrashCallbacks = { [alertView show]; } } else { - [self sendCrashReports]; + [self sendNextCrashReport]; } } } @@ -1158,146 +1158,149 @@ static PLCrashReporterCallbacks plCrashCallbacks = { * * Gathers all collected data and constructs the XML structure and starts the sending process */ -- (void)sendCrashReports { +- (void)sendNextCrashReport { NSError *error = NULL; _crashIdenticalCurrentVersion = NO; - for (NSUInteger i=0; i < [_crashFiles count]; i++) { - NSString *crashXML = nil; - BITCrashAttachment *attachment = nil; + if ([_crashFiles count] == 0) + return; + + NSString *crashXML = nil; + BITCrashAttachment *attachment = nil; + + NSString *filename = [_crashFiles objectAtIndex:0]; + NSString *cacheFilename = [filename lastPathComponent]; + NSData *crashData = [NSData dataWithContentsOfFile:filename]; + + if ([crashData length] > 0) { + BITPLCrashReport *report = nil; + NSString *crashUUID = @""; + NSString *installString = nil; + NSString *crashLogString = nil; + NSString *appBundleIdentifier = nil; + NSString *appBundleVersion = nil; + NSString *osVersion = nil; + NSString *deviceModel = nil; + NSString *appBinaryUUIDs = nil; + NSString *metaFilename = nil; - NSString *filename = [_crashFiles objectAtIndex:i]; - NSString *cacheFilename = [filename lastPathComponent]; - NSData *crashData = [NSData dataWithContentsOfFile:filename]; - - if ([crashData length] > 0) { - BITPLCrashReport *report = nil; - NSString *crashUUID = @""; - NSString *installString = nil; - NSString *crashLogString = nil; - NSString *appBundleIdentifier = nil; - NSString *appBundleVersion = nil; - NSString *osVersion = nil; - NSString *deviceModel = nil; - NSString *appBinaryUUIDs = nil; - NSString *metaFilename = nil; + NSString *errorString = nil; + NSPropertyListFormat format; + + if ([[cacheFilename pathExtension] isEqualToString:@"fake"]) { + NSDictionary *fakeReportDict = (NSDictionary *)[NSPropertyListSerialization + propertyListFromData:crashData + mutabilityOption:NSPropertyListMutableContainersAndLeaves + format:&format + errorDescription:&errorString]; - NSString *errorString = nil; - NSPropertyListFormat format; - - if ([[cacheFilename pathExtension] isEqualToString:@"fake"]) { - NSDictionary *fakeReportDict = (NSDictionary *)[NSPropertyListSerialization - propertyListFromData:crashData - mutabilityOption:NSPropertyListMutableContainersAndLeaves - format:&format - errorDescription:&errorString]; - - crashLogString = [fakeReportDict objectForKey:kBITFakeCrashReport]; - crashUUID = [fakeReportDict objectForKey:kBITFakeCrashUUID]; - appBundleIdentifier = [fakeReportDict objectForKey:kBITFakeCrashAppBundleIdentifier]; - appBundleVersion = [fakeReportDict objectForKey:kBITFakeCrashAppVersion]; - appBinaryUUIDs = [fakeReportDict objectForKey:kBITFakeCrashAppBinaryUUID]; - deviceModel = [fakeReportDict objectForKey:kBITFakeCrashDeviceModel]; - osVersion = [fakeReportDict objectForKey:kBITFakeCrashOSVersion]; - - metaFilename = [cacheFilename stringByReplacingOccurrencesOfString:@".fake" withString:@".meta"]; - if ([appBundleVersion compare:[[NSBundle mainBundle] objectForInfoDictionaryKey:@"CFBundleVersion"]] == NSOrderedSame) { - _crashIdenticalCurrentVersion = YES; - } - - } else { - report = [[BITPLCrashReport alloc] initWithData:crashData error:&error]; - } - - if (report == nil && crashLogString == nil) { - BITHockeyLog(@"WARNING: Could not parse crash report"); - // we cannot do anything with this report, so delete it - [self cleanCrashReportWithFilename:filename]; - continue; + crashLogString = [fakeReportDict objectForKey:kBITFakeCrashReport]; + crashUUID = [fakeReportDict objectForKey:kBITFakeCrashUUID]; + appBundleIdentifier = [fakeReportDict objectForKey:kBITFakeCrashAppBundleIdentifier]; + appBundleVersion = [fakeReportDict objectForKey:kBITFakeCrashAppVersion]; + appBinaryUUIDs = [fakeReportDict objectForKey:kBITFakeCrashAppBinaryUUID]; + deviceModel = [fakeReportDict objectForKey:kBITFakeCrashDeviceModel]; + osVersion = [fakeReportDict objectForKey:kBITFakeCrashOSVersion]; + + metaFilename = [cacheFilename stringByReplacingOccurrencesOfString:@".fake" withString:@".meta"]; + if ([appBundleVersion compare:[[NSBundle mainBundle] objectForInfoDictionaryKey:@"CFBundleVersion"]] == NSOrderedSame) { + _crashIdenticalCurrentVersion = YES; } - installString = bit_appAnonID() ?: @""; - - if (report) { - if (report.uuidRef != NULL) { - crashUUID = (NSString *) CFBridgingRelease(CFUUIDCreateString(NULL, report.uuidRef)); - } - metaFilename = [filename stringByAppendingPathExtension:@"meta"]; - crashLogString = [BITCrashReportTextFormatter stringValueForCrashReport:report crashReporterKey:installString]; - appBundleIdentifier = report.applicationInfo.applicationIdentifier; - appBundleVersion = report.applicationInfo.applicationVersion; - osVersion = report.systemInfo.operatingSystemVersion; - deviceModel = [self getDevicePlatform]; - appBinaryUUIDs = [self extractAppUUIDs:report]; - if ([report.applicationInfo.applicationVersion compare:[[NSBundle mainBundle] objectForInfoDictionaryKey:@"CFBundleVersion"]] == NSOrderedSame) { - _crashIdenticalCurrentVersion = YES; - } + } else { + report = [[BITPLCrashReport alloc] initWithData:crashData error:&error]; + } + + if (report == nil && crashLogString == nil) { + BITHockeyLog(@"WARNING: Could not parse crash report"); + // we cannot do anything with this report, so delete it + [self cleanCrashReportWithFilename:filename]; + // we don't continue with the next report here, even if there are to prevent calling sendCrashReports from itself again + // the next crash will be automatically send on the next app start/becoming active event + return; + } + + installString = bit_appAnonID() ?: @""; + + if (report) { + if (report.uuidRef != NULL) { + crashUUID = (NSString *) CFBridgingRelease(CFUUIDCreateString(NULL, report.uuidRef)); } - + metaFilename = [filename stringByAppendingPathExtension:@"meta"]; + crashLogString = [BITCrashReportTextFormatter stringValueForCrashReport:report crashReporterKey:installString]; + appBundleIdentifier = report.applicationInfo.applicationIdentifier; + appBundleVersion = report.applicationInfo.applicationVersion; + osVersion = report.systemInfo.operatingSystemVersion; + deviceModel = [self getDevicePlatform]; + appBinaryUUIDs = [self extractAppUUIDs:report]; if ([report.applicationInfo.applicationVersion compare:[[NSBundle mainBundle] objectForInfoDictionaryKey:@"CFBundleVersion"]] == NSOrderedSame) { _crashIdenticalCurrentVersion = YES; } - - NSString *username = @""; - NSString *useremail = @""; - NSString *userid = @""; - NSString *applicationLog = @""; - NSString *description = @""; - - NSData *plist = [NSData dataWithContentsOfFile:[_crashesDir stringByAppendingPathComponent:metaFilename]]; - if (plist) { - NSDictionary *metaDict = (NSDictionary *)[NSPropertyListSerialization - propertyListFromData:plist - mutabilityOption:NSPropertyListMutableContainersAndLeaves - format:&format - errorDescription:&errorString]; - - username = [self stringValueFromKeychainForKey:[NSString stringWithFormat:@"%@.%@", cacheFilename, kBITCrashMetaUserName]] ?: @""; - useremail = [self stringValueFromKeychainForKey:[NSString stringWithFormat:@"%@.%@", cacheFilename, kBITCrashMetaUserEmail]] ?: @""; - userid = [self stringValueFromKeychainForKey:[NSString stringWithFormat:@"%@.%@", cacheFilename, kBITCrashMetaUserID]] ?: @""; - applicationLog = [metaDict objectForKey:kBITCrashMetaApplicationLog] ?: @""; - description = [NSString stringWithContentsOfFile:[NSString stringWithFormat:@"%@.desc", [_crashesDir stringByAppendingPathComponent: cacheFilename]] encoding:NSUTF8StringEncoding error:&error]; - attachment = [self attachmentForCrashReport:filename]; - } else { - BITHockeyLog(@"ERROR: Reading crash meta data. %@", error); - } - - if ([applicationLog length] > 0) { - if ([description length] > 0) { - description = [NSString stringWithFormat:@"%@\n\nLog:\n%@", description, applicationLog]; - } else { - description = [NSString stringWithFormat:@"Log:\n%@", applicationLog]; - } - } - - crashXML = [NSString stringWithFormat:@"%s%@%@%@%@%@%@%@%@%@%@%@", - [[[NSBundle mainBundle] objectForInfoDictionaryKey:@"CFBundleExecutable"] UTF8String], - appBinaryUUIDs, - appBundleIdentifier, - osVersion, - deviceModel, - [[NSBundle mainBundle] objectForInfoDictionaryKey:@"CFBundleVersion"], - appBundleVersion, - crashUUID, - [crashLogString stringByReplacingOccurrencesOfString:@"]]>" withString:@"]]" @"]]>" options:NSLiteralSearch range:NSMakeRange(0,crashLogString.length)], - userid, - username, - useremail, - installString, - [description stringByReplacingOccurrencesOfString:@"]]>" withString:@"]]" @"]]>" options:NSLiteralSearch range:NSMakeRange(0,description.length)]]; - - // store this crash report as user approved, so if it fails it will retry automatically - [_approvedCrashReports setObject:[NSNumber numberWithBool:YES] forKey:filename]; - - BITHockeyLog(@"INFO: Sending crash reports:\n%@", crashXML); - [self sendCrashReportWithFilename:filename xml:crashXML attachment:attachment]; - } else { - // we cannot do anything with this report, so delete it - [self cleanCrashReportWithFilename:filename]; } + + if ([report.applicationInfo.applicationVersion compare:[[NSBundle mainBundle] objectForInfoDictionaryKey:@"CFBundleVersion"]] == NSOrderedSame) { + _crashIdenticalCurrentVersion = YES; + } + + NSString *username = @""; + NSString *useremail = @""; + NSString *userid = @""; + NSString *applicationLog = @""; + NSString *description = @""; + + NSData *plist = [NSData dataWithContentsOfFile:[_crashesDir stringByAppendingPathComponent:metaFilename]]; + if (plist) { + NSDictionary *metaDict = (NSDictionary *)[NSPropertyListSerialization + propertyListFromData:plist + mutabilityOption:NSPropertyListMutableContainersAndLeaves + format:&format + errorDescription:&errorString]; + + username = [self stringValueFromKeychainForKey:[NSString stringWithFormat:@"%@.%@", cacheFilename, kBITCrashMetaUserName]] ?: @""; + useremail = [self stringValueFromKeychainForKey:[NSString stringWithFormat:@"%@.%@", cacheFilename, kBITCrashMetaUserEmail]] ?: @""; + userid = [self stringValueFromKeychainForKey:[NSString stringWithFormat:@"%@.%@", cacheFilename, kBITCrashMetaUserID]] ?: @""; + applicationLog = [metaDict objectForKey:kBITCrashMetaApplicationLog] ?: @""; + description = [NSString stringWithContentsOfFile:[NSString stringWithFormat:@"%@.desc", [_crashesDir stringByAppendingPathComponent: cacheFilename]] encoding:NSUTF8StringEncoding error:&error]; + attachment = [self attachmentForCrashReport:filename]; + } else { + BITHockeyLog(@"ERROR: Reading crash meta data. %@", error); + } + + if ([applicationLog length] > 0) { + if ([description length] > 0) { + description = [NSString stringWithFormat:@"%@\n\nLog:\n%@", description, applicationLog]; + } else { + description = [NSString stringWithFormat:@"Log:\n%@", applicationLog]; + } + } + + crashXML = [NSString stringWithFormat:@"%s%@%@%@%@%@%@%@%@%@%@%@", + [[[NSBundle mainBundle] objectForInfoDictionaryKey:@"CFBundleExecutable"] UTF8String], + appBinaryUUIDs, + appBundleIdentifier, + osVersion, + deviceModel, + [[NSBundle mainBundle] objectForInfoDictionaryKey:@"CFBundleVersion"], + appBundleVersion, + crashUUID, + [crashLogString stringByReplacingOccurrencesOfString:@"]]>" withString:@"]]" @"]]>" options:NSLiteralSearch range:NSMakeRange(0,crashLogString.length)], + userid, + username, + useremail, + installString, + [description stringByReplacingOccurrencesOfString:@"]]>" withString:@"]]" @"]]>" options:NSLiteralSearch range:NSMakeRange(0,description.length)]]; + + // store this crash report as user approved, so if it fails it will retry automatically + [_approvedCrashReports setObject:[NSNumber numberWithBool:YES] forKey:filename]; + + BITHockeyLog(@"INFO: Sending crash reports:\n%@", crashXML); + [self sendCrashReportWithFilename:filename xml:crashXML attachment:attachment]; + } else { + // we cannot do anything with this report, so delete it + [self cleanCrashReportWithFilename:filename]; } - + [self saveSettings]; } @@ -1416,6 +1419,9 @@ static PLCrashReporterCallbacks plCrashCallbacks = { [strongSelf.delegate respondsToSelector:@selector(crashManagerDidFinishSendingCrashReport:)]) { [strongSelf.delegate crashManagerDidFinishSendingCrashReport:self]; } + + // only if sending the crash report went successfully, continue with the next one (if there are more) + [strongSelf sendNextCrashReport]; } else if (statusCode == 400) { [strongSelf cleanCrashReportWithFilename:filename]; diff --git a/Classes/BITCrashManagerPrivate.h b/Classes/BITCrashManagerPrivate.h index b280b1ab29..50158963c2 100644 --- a/Classes/BITCrashManagerPrivate.h +++ b/Classes/BITCrashManagerPrivate.h @@ -82,7 +82,7 @@ - (BITCrashAttachment *)attachmentForCrashReport:(NSString *)filename; - (void)invokeDelayedProcessing; -- (void)sendCrashReports; +- (void)sendNextCrashReport; - (NSString *)getCrashesDir; - (void)setLastCrashFilename:(NSString *)lastCrashFilename; diff --git a/Support/HockeySDKTests/BITCrashManagerTests.m b/Support/HockeySDKTests/BITCrashManagerTests.m index b6d0240921..5bb154e48f 100644 --- a/Support/HockeySDKTests/BITCrashManagerTests.m +++ b/Support/HockeySDKTests/BITCrashManagerTests.m @@ -272,7 +272,7 @@ assertThatBool([_sut hasNonApprovedCrashReports], equalToBool(YES)); // this is currently sending blindly, needs refactoring to test properly - [_sut sendCrashReports]; + [_sut sendNextCrashReport]; [verifyCount(delegateMock, times(1)) crashManagerWillSendCrashReport:_sut]; [_sut cleanCrashReports]; From 58f6854a5e8120d16fcdacfc93a548f933a7a11a Mon Sep 17 00:00:00 2001 From: Andreas Linde Date: Tue, 20 May 2014 12:03:22 +0200 Subject: [PATCH 41/43] Update BITCrashDetails and remove non needed properties in BITCrashManager cleanup :) --- Classes/BITCrashDetails.h | 63 ++++++++++++++++----- Classes/BITCrashDetails.m | 46 ++++++++++++--- Classes/BITCrashDetailsPrivate.h | 46 +++++++++++++++ Classes/BITCrashManager.h | 27 ++------- Classes/BITCrashManager.m | 4 +- Support/HockeySDK.xcodeproj/project.pbxproj | 4 ++ 6 files changed, 143 insertions(+), 47 deletions(-) create mode 100644 Classes/BITCrashDetailsPrivate.h diff --git a/Classes/BITCrashDetails.h b/Classes/BITCrashDetails.h index b9322f35f3..4cd8fb7321 100644 --- a/Classes/BITCrashDetails.h +++ b/Classes/BITCrashDetails.h @@ -1,10 +1,30 @@ -// -// BITCrashDetails.h -// HockeySDK -// -// Created by Andreas Linde on 03.04.14. -// -// +/* + * Author: Andreas Linde + * + * Copyright (c) 2012-2014 HockeyApp, Bit Stadium GmbH. + * 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 @@ -50,13 +70,26 @@ */ @property (nonatomic, readonly, strong) NSString *appBuild; -- (instancetype)initWithIncidentIdentifier:(NSString *)incidentIdentifier - reporterKey:(NSString *)reporterKey - signal:(NSString *)signal - exceptionName:(NSString *)exceptionName - exceptionReason:(NSString *)exceptionReason - appStartTime:(NSDate *)appStartTime - crashTime:(NSDate *)crashTime - appBuild:(NSString *)appBuild; +/** + Indicates if the app was killed while being in foreground from the iOS + + If `[BITCrashManager enableAppNotTerminatingCleanlyDetection]` is enabled, use this on startup + to check if the app starts the first time after it was killed by iOS in the previous session. + + This can happen if it consumed too much memory or the watchdog killed the app because it + took too long to startup or blocks the main thread for too long, or other reasons. See Apple + documentation: https://developer.apple.com/library/ios/qa/qa1693/_index.html + + See `[BITCrashManager enableDectionAppKillWhileInForeground]` for more details about which kind of kills can be detected. + + @warning This property only has a correct value, once `[BITHockeyManager startManager]` was + invoked! In addition, it is automatically disabled while a debugger session is active! + + @see `[BITCrashManager enableAppNotTerminatingCleanlyDetection]` + @see `[BITCrashManager didReceiveMemoryWarningInLastSession]` + + @return YES if the details represent an app kill instead of a crash + */ +- (BOOL)isAppKill; @end diff --git a/Classes/BITCrashDetails.m b/Classes/BITCrashDetails.m index 187b7ba0bc..b7f5b52c84 100644 --- a/Classes/BITCrashDetails.m +++ b/Classes/BITCrashDetails.m @@ -1,12 +1,35 @@ -// -// BITCrashDetails.m -// HockeySDK -// -// Created by Andreas Linde on 03.04.14. -// -// +/* + * Author: Andreas Linde + * + * Copyright (c) 2012-2014 HockeyApp, Bit Stadium GmbH. + * 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 "BITCrashDetails.h" +#import "BITCrashDetailsPrivate.h" + +NSString *const kBITCrashKillSignal = @"SIGKILL"; @implementation BITCrashDetails @@ -32,4 +55,13 @@ return self; } +- (BOOL)isAppKill { + BOOL result = NO; + + if (_signal && [[_signal uppercaseString] isEqualToString:kBITCrashKillSignal]) + result = YES; + + return result; +} + @end diff --git a/Classes/BITCrashDetailsPrivate.h b/Classes/BITCrashDetailsPrivate.h new file mode 100644 index 0000000000..6c6fa241a1 --- /dev/null +++ b/Classes/BITCrashDetailsPrivate.h @@ -0,0 +1,46 @@ +/* + * Author: Andreas Linde + * + * Copyright (c) 2012-2014 HockeyApp, Bit Stadium GmbH. + * 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 + +extern NSString *const __attribute__((unused)) kBITCrashKillSignal; + +@interface BITCrashDetails () { + +} + +- (instancetype)initWithIncidentIdentifier:(NSString *)incidentIdentifier + reporterKey:(NSString *)reporterKey + signal:(NSString *)signal + exceptionName:(NSString *)exceptionName + exceptionReason:(NSString *)exceptionReason + appStartTime:(NSDate *)appStartTime + crashTime:(NSDate *)crashTime + appBuild:(NSString *)appBuild; + +@end diff --git a/Classes/BITCrashManager.h b/Classes/BITCrashManager.h index 49e32601c5..6cb8033a66 100644 --- a/Classes/BITCrashManager.h +++ b/Classes/BITCrashManager.h @@ -259,7 +259,7 @@ typedef NS_ENUM(NSUInteger, BITCrashManagerUserInput) { * @warning This is a heuristic and it _MAY_ report false positives! It has been tested with iOS 6.1 and iOS 7. * Depending on Apple changing notification events, new iOS version may cause more false positives! * - * @see wasKilledInLastSession + * @see lastSessionCrashDetails * @see didReceiveMemoryWarningInLastSession * @see `BITCrashManagerDelegate considerAppNotTerminatedCleanlyReportForCrashManager:` * @see [Apple Technical Note TN2151](https://developer.apple.com/library/ios/technotes/tn2151/_index.html) @@ -325,6 +325,8 @@ typedef NS_ENUM(NSUInteger, BITCrashManagerUserInput) { @warning This property only has a correct value, once `[BITHockeyManager startManager]` was invoked! + + @see lastSessionCrashDetails */ @property (nonatomic, readonly) BOOL didCrashInLastSession; @@ -349,27 +351,6 @@ typedef NS_ENUM(NSUInteger, BITCrashManagerUserInput) { */ - (void) setAlertViewHandler:(BITCustomAlertViewHandler)alertViewHandler; -/** - Indicates if the app was killed while being in foreground from the iOS - - If `enableAppNotTerminatingCleanlyDetection` is enabled, use this on startup to check if the - app starts the first time after it was killed by iOS in the previous session. - - This can happen if it consumed too much memory or the watchdog killed the app because it - took too long to startup or blocks the main thread for too long, or other reasons. See Apple - documentation: https://developer.apple.com/library/ios/qa/qa1693/_index.html - - See `enableDectionAppKillWhileInForeground` for more details about which kind of kills can be detected. - - @warning This property only has a correct value, once `[BITHockeyManager startManager]` was - invoked! In addition, it is automatically disabled while a debugger session is active! - - @see enableAppNotTerminatingCleanlyDetection - @see didReceiveMemoryWarningInLastSession - */ -@property (nonatomic, readonly) BOOL wasKilledInLastSession; - - /** * Provides details about the crash that occured in the last app session */ @@ -392,7 +373,7 @@ typedef NS_ENUM(NSUInteger, BITCrashManagerUserInput) { invoked! @see enableAppNotTerminatingCleanlyDetection - @see wasKilledInLastSession + @see lastSessionCrashDetails */ @property (nonatomic, readonly) BOOL didReceiveMemoryWarningInLastSession; diff --git a/Classes/BITCrashManager.m b/Classes/BITCrashManager.m index 7ac9094c36..33a8fff617 100644 --- a/Classes/BITCrashManager.m +++ b/Classes/BITCrashManager.m @@ -43,6 +43,7 @@ #import "BITHockeyBaseManagerPrivate.h" #import "BITCrashManagerPrivate.h" #import "BITCrashReportTextFormatter.h" +#import "BITCrashDetailsPrivate.h" #include @@ -1056,7 +1057,6 @@ static PLCrashReporterCallbacks plCrashCallbacks = { if (considerReport) { [self createCrashReportForAppKill]; - _wasKilledInLastSession = YES; _didCrashInLastSession = YES; } } @@ -1084,7 +1084,7 @@ static PLCrashReporterCallbacks plCrashCallbacks = { NSString *fakeReportDeviceModel = [self getDevicePlatform] ?: @"Unknown"; NSString *fakeReportAppUUIDs = [[NSUserDefaults standardUserDefaults] objectForKey:kBITAppUUIDs] ?: @""; - NSString *fakeSignalName = @"SIGKILL"; + NSString *fakeSignalName = kBITCrashKillSignal; NSMutableString *fakeReportString = [NSMutableString string]; diff --git a/Support/HockeySDK.xcodeproj/project.pbxproj b/Support/HockeySDK.xcodeproj/project.pbxproj index 13d4da6796..e2f862b2ac 100644 --- a/Support/HockeySDK.xcodeproj/project.pbxproj +++ b/Support/HockeySDK.xcodeproj/project.pbxproj @@ -129,6 +129,7 @@ 1EAF20AA162DC0F600957B1D /* feedbackActiviy.png in Resources */ = {isa = PBXBuildFile; fileRef = 1EAF20A6162DC0F600957B1D /* feedbackActiviy.png */; }; 1EAF20AB162DC0F600957B1D /* feedbackActiviy@2x.png in Resources */ = {isa = PBXBuildFile; fileRef = 1EAF20A7162DC0F600957B1D /* feedbackActiviy@2x.png */; }; 1EB52FD5167B766100C801D5 /* HockeySDK.strings in Resources */ = {isa = PBXBuildFile; fileRef = 1E59555F15B6F80E00A03429 /* HockeySDK.strings */; }; + 1ECA8F4D192B5BD8006B9416 /* BITCrashDetailsPrivate.h in Headers */ = {isa = PBXBuildFile; fileRef = 1ECA8F4B192B5BD8006B9416 /* BITCrashDetailsPrivate.h */; }; 1ED570C718BF878C00AB3350 /* BITCrashAttachment.h in Headers */ = {isa = PBXBuildFile; fileRef = 1ED570C518BF878C00AB3350 /* BITCrashAttachment.h */; settings = {ATTRIBUTES = (Public, ); }; }; 1ED570C818BF878C00AB3350 /* BITCrashAttachment.m in Sources */ = {isa = PBXBuildFile; fileRef = 1ED570C618BF878C00AB3350 /* BITCrashAttachment.m */; }; 1EF95CA6162CB037000AE3AD /* BITFeedbackActivity.h in Headers */ = {isa = PBXBuildFile; fileRef = 1EF95CA4162CB036000AE3AD /* BITFeedbackActivity.h */; settings = {ATTRIBUTES = (Public, ); }; }; @@ -289,6 +290,7 @@ 1EAF20A6162DC0F600957B1D /* feedbackActiviy.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = feedbackActiviy.png; sourceTree = ""; }; 1EAF20A7162DC0F600957B1D /* feedbackActiviy@2x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "feedbackActiviy@2x.png"; sourceTree = ""; }; 1EB52FC3167B73D400C801D5 /* en */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = en; path = en.lproj/HockeySDK.strings; sourceTree = ""; }; + 1ECA8F4B192B5BD8006B9416 /* BITCrashDetailsPrivate.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = BITCrashDetailsPrivate.h; sourceTree = ""; }; 1ED570C518BF878C00AB3350 /* BITCrashAttachment.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = BITCrashAttachment.h; sourceTree = ""; }; 1ED570C618BF878C00AB3350 /* BITCrashAttachment.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = BITCrashAttachment.m; sourceTree = ""; }; 1EDA60CF15C2C1450032D10B /* HockeySDK-Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = "HockeySDK-Info.plist"; sourceTree = ""; }; @@ -488,6 +490,7 @@ 1E754E571621FBB70070AB92 /* BITCrashManager.m */, 1E90FD7118EDB86400CF0417 /* BITCrashDetails.h */, 1E90FD7218EDB86400CF0417 /* BITCrashDetails.m */, + 1ECA8F4B192B5BD8006B9416 /* BITCrashDetailsPrivate.h */, 1EFF03D717F20F8300A5F13C /* BITCrashManagerPrivate.h */, 1E754E581621FBB70070AB92 /* BITCrashManagerDelegate.h */, 1ED570C518BF878C00AB3350 /* BITCrashAttachment.h */, @@ -652,6 +655,7 @@ 1E49A44E1612223B00463151 /* BITFeedbackManager.h in Headers */, E4B4DB7D17B435550099C67F /* BITAuthenticationViewController.h in Headers */, 1E49A4481612223B00463151 /* BITFeedbackListViewController.h in Headers */, + 1ECA8F4D192B5BD8006B9416 /* BITCrashDetailsPrivate.h in Headers */, 1E49A47F1612226D00463151 /* BITUpdateViewController.h in Headers */, 1E49A43C1612223B00463151 /* BITFeedbackComposeViewController.h in Headers */, E40E0B0C17DA1AFF005E38C1 /* BITHockeyAppClient.h in Headers */, From 89a73989713e0d90fe1b929bc2677ce0b21c2b52 Mon Sep 17 00:00:00 2001 From: Andreas Linde Date: Tue, 20 May 2014 13:28:05 +0200 Subject: [PATCH 42/43] Allow the developer to set more than just the description when handling user alert for crashes - Also fixes a bug where crash caches dir could get deleted - Some refactoring --- Classes/BITCrashManager.h | 12 +++-- Classes/BITCrashManager.m | 44 ++++++++++----- Classes/BITCrashManagerPrivate.h | 5 +- Classes/BITCrashMetaData.h | 54 +++++++++++++++++++ Classes/BITCrashMetaData.m | 34 ++++++++++++ Classes/HockeySDK.h | 1 + Support/HockeySDK.xcodeproj/project.pbxproj | 12 ++++- Support/HockeySDKTests/BITCrashManagerTests.m | 19 ++++--- 8 files changed, 151 insertions(+), 30 deletions(-) create mode 100644 Classes/BITCrashMetaData.h create mode 100644 Classes/BITCrashMetaData.m diff --git a/Classes/BITCrashManager.h b/Classes/BITCrashManager.h index 6cb8033a66..3a72a5fecc 100644 --- a/Classes/BITCrashManager.h +++ b/Classes/BITCrashManager.h @@ -33,6 +33,8 @@ #import "BITHockeyBaseManager.h" @class BITCrashDetails; +@class BITCrashMetaData; + /** * Custom block that handles the alert that prompts the user whether he wants to send crash reports @@ -331,15 +333,17 @@ typedef NS_ENUM(NSUInteger, BITCrashManagerUserInput) { @property (nonatomic, readonly) BOOL didCrashInLastSession; /** - Provides an interface to handle user input from a custom alert + Provides an interface to pass user input from a custom alert to a crash report - @param userInput On this input depends, whether crash reports are sent, always sent or not sent and deleted. - @param userProvidedCrashDescription The content of this optional string will be attached to the crash report as the description and allows to ask the user for e.g. additional comments or info + @param userInput Defines the users action wether to send, always send, or not to send the crash report. + @param userProvidedMetaData The content of this optional BITCrashMetaData instance will be attached to the crash report and allows to ask the user for e.g. additional comments or info. @return Returns YES if the input is a valid option and successfully triggered further processing of the crash report + @see BITCrashManagerUserInput + @see BITCrashMetaData */ -- (BOOL)handleUserInput:(BITCrashManagerUserInput)userInput withUserProvidedCrashDescription:(NSString*)userProvidedCrashDescription; +- (BOOL)handleUserInput:(BITCrashManagerUserInput)userInput withUserProvidedMetaData:(BITCrashMetaData *)userProvidedMetaData; /** Lets you set a custom block which handles showing a custom UI and asking the user diff --git a/Classes/BITCrashManager.m b/Classes/BITCrashManager.m index 33a8fff617..80f31e5c70 100644 --- a/Classes/BITCrashManager.m +++ b/Classes/BITCrashManager.m @@ -100,7 +100,6 @@ static PLCrashReporterCallbacks plCrashCallbacks = { NSMutableDictionary *_approvedCrashReports; NSMutableArray *_crashFiles; - NSString *_crashesDir; NSString *_lastCrashFilename; NSString *_settingsFile; NSString *_analyzerInProgressFile; @@ -290,10 +289,26 @@ static PLCrashReporterCallbacks plCrashCallbacks = { [data writeToFile:attachmentFilename atomically:YES]; } -- (void)persistUserProvidedCrashDescription:(NSString *)userProvidedCrashDescription { - if (userProvidedCrashDescription && [userProvidedCrashDescription length] > 0) { +- (void)persistUserProvidedMetaData:(BITCrashMetaData *)userProvidedMetaData { + if (!userProvidedMetaData) return; + + if (userProvidedMetaData.userDescription && [userProvidedMetaData.userDescription length] > 0) { NSError *error; - [userProvidedCrashDescription writeToFile:[NSString stringWithFormat:@"%@.desc", [_crashesDir stringByAppendingPathComponent: _lastCrashFilename]] atomically:YES encoding:NSUTF8StringEncoding error:&error]; + [userProvidedMetaData.userDescription writeToFile:[NSString stringWithFormat:@"%@.desc", [_crashesDir stringByAppendingPathComponent: _lastCrashFilename]] atomically:YES encoding:NSUTF8StringEncoding error:&error]; + } + + if (userProvidedMetaData.userName && [userProvidedMetaData.userName length] > 0) { + [self addStringValueToKeychain:userProvidedMetaData.userName forKey:[NSString stringWithFormat:@"%@.%@", _lastCrashFilename, kBITCrashMetaUserName]]; + + } + + if (userProvidedMetaData.userEmail && [userProvidedMetaData.userEmail length] > 0) { + [self addStringValueToKeychain:userProvidedMetaData.userEmail forKey:[NSString stringWithFormat:@"%@.%@", _lastCrashFilename, kBITCrashMetaUserEmail]]; + } + + if (userProvidedMetaData.userID && [userProvidedMetaData.userID length] > 0) { + [self addStringValueToKeychain:userProvidedMetaData.userID forKey:[NSString stringWithFormat:@"%@.%@", _lastCrashFilename, kBITCrashMetaUserID]]; + } } @@ -579,9 +594,6 @@ static PLCrashReporterCallbacks plCrashCallbacks = { return useremail; } -- (NSString *)getCrashesDir { - return _crashesDir; -} #pragma mark - Public @@ -692,18 +704,21 @@ static PLCrashReporterCallbacks plCrashCallbacks = { } } -- (BOOL)handleUserInput:(BITCrashManagerUserInput)userInput withUserProvidedCrashDescription:(NSString *)userProvidedCrashDescription{ +- (BOOL)handleUserInput:(BITCrashManagerUserInput)userInput withUserProvidedMetaData:(BITCrashMetaData *)userProvidedMetaData { switch (userInput) { case BITCrashManagerUserInputDontSend: if (self.delegate != nil && [self.delegate respondsToSelector:@selector(crashManagerWillCancelSendingCrashReport:)]) { [self.delegate crashManagerWillCancelSendingCrashReport:self]; } - [self cleanCrashReportWithFilename:[_crashesDir stringByAppendingPathComponent: _lastCrashFilename]]; + if (_lastCrashFilename) + [self cleanCrashReportWithFilename:[_crashesDir stringByAppendingPathComponent: _lastCrashFilename]]; + return YES; case BITCrashManagerUserInputSend: - [self persistUserProvidedCrashDescription:userProvidedCrashDescription]; + if (userProvidedMetaData) + [self persistUserProvidedMetaData:userProvidedMetaData]; [self sendNextCrashReport]; return YES; @@ -716,7 +731,8 @@ static PLCrashReporterCallbacks plCrashCallbacks = { [self.delegate crashManagerWillSendCrashReportsAlways:self]; } - [self persistUserProvidedCrashDescription:userProvidedCrashDescription]; + if (userProvidedMetaData) + [self persistUserProvidedMetaData:userProvidedMetaData]; [self sendNextCrashReport]; return YES; @@ -1310,13 +1326,13 @@ static PLCrashReporterCallbacks plCrashCallbacks = { - (void)alertView:(UIAlertView *)alertView didDismissWithButtonIndex:(NSInteger)buttonIndex { switch (buttonIndex) { case 0: - [self handleUserInput:BITCrashManagerUserInputDontSend withUserProvidedCrashDescription:nil]; + [self handleUserInput:BITCrashManagerUserInputDontSend withUserProvidedMetaData:nil]; break; case 1: - [self handleUserInput:BITCrashManagerUserInputSend withUserProvidedCrashDescription:nil]; + [self handleUserInput:BITCrashManagerUserInputSend withUserProvidedMetaData:nil]; break; case 2: - [self handleUserInput:BITCrashManagerUserInputAlwaysSend withUserProvidedCrashDescription:nil]; + [self handleUserInput:BITCrashManagerUserInputAlwaysSend withUserProvidedMetaData:nil]; break; } } diff --git a/Classes/BITCrashManagerPrivate.h b/Classes/BITCrashManagerPrivate.h index 50158963c2..f42062d07b 100644 --- a/Classes/BITCrashManagerPrivate.h +++ b/Classes/BITCrashManagerPrivate.h @@ -53,6 +53,8 @@ @property (nonatomic, copy, setter = setAlertViewHandler:) BITCustomAlertViewHandler alertViewHandler; +@property (nonatomic, strong) NSString *crashesDir; + #if HOCKEYSDK_FEATURE_AUTHENTICATOR // Only set via BITAuthenticator @@ -76,7 +78,7 @@ - (BOOL)hasPendingCrashReport; - (BOOL)hasNonApprovedCrashReports; -- (void)persistUserProvidedCrashDescription:(NSString *)userProvidedCrashDescription; +- (void)persistUserProvidedMetaData:(BITCrashMetaData *)userProvidedMetaData; - (void)persistAttachment:(BITCrashAttachment *)attachment withFilename:(NSString *)filename; - (BITCrashAttachment *)attachmentForCrashReport:(NSString *)filename; @@ -84,7 +86,6 @@ - (void)invokeDelayedProcessing; - (void)sendNextCrashReport; -- (NSString *)getCrashesDir; - (void)setLastCrashFilename:(NSString *)lastCrashFilename; @end diff --git a/Classes/BITCrashMetaData.h b/Classes/BITCrashMetaData.h new file mode 100644 index 0000000000..ac6bb2b650 --- /dev/null +++ b/Classes/BITCrashMetaData.h @@ -0,0 +1,54 @@ +/* + * Author: Andreas Linde + * + * Copyright (c) 2012-2014 HockeyApp, Bit Stadium GmbH. + * 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 + + +@interface BITCrashMetaData : NSObject + +/** + * User provided description that should be attached to the crash report as plain text + */ +@property (nonatomic, copy) NSString *userDescription; + +/** + * User name that should be attached to the crash report + */ +@property (nonatomic, copy) NSString *userName; + +/** + * User email that should be attached to the crash report + */ +@property (nonatomic, copy) NSString *userEmail; + +/** + * User ID that should be attached to the crash report + */ +@property (nonatomic, copy) NSString *userID; + +@end diff --git a/Classes/BITCrashMetaData.m b/Classes/BITCrashMetaData.m new file mode 100644 index 0000000000..824d149b93 --- /dev/null +++ b/Classes/BITCrashMetaData.m @@ -0,0 +1,34 @@ +/* + * Author: Andreas Linde + * + * Copyright (c) 2012-2014 HockeyApp, Bit Stadium GmbH. + * 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 "BITCrashMetaData.h" + + +@implementation BITCrashMetaData + +@end diff --git a/Classes/HockeySDK.h b/Classes/HockeySDK.h index db47dec951..709333dc9f 100644 --- a/Classes/HockeySDK.h +++ b/Classes/HockeySDK.h @@ -40,6 +40,7 @@ #import "BITCrashAttachment.h" #import "BITCrashManagerDelegate.h" #import "BITCrashDetails.h" +#import "BITCrashMetaData.h" #endif /* HOCKEYSDK_FEATURE_CRASH_REPORTER */ #if HOCKEYSDK_FEATURE_UPDATES || HOCKEYSDK_FEATURE_JIRA_MOBILE_CONNECT diff --git a/Support/HockeySDK.xcodeproj/project.pbxproj b/Support/HockeySDK.xcodeproj/project.pbxproj index e2f862b2ac..73f560e05f 100644 --- a/Support/HockeySDK.xcodeproj/project.pbxproj +++ b/Support/HockeySDK.xcodeproj/project.pbxproj @@ -130,6 +130,8 @@ 1EAF20AB162DC0F600957B1D /* feedbackActiviy@2x.png in Resources */ = {isa = PBXBuildFile; fileRef = 1EAF20A7162DC0F600957B1D /* feedbackActiviy@2x.png */; }; 1EB52FD5167B766100C801D5 /* HockeySDK.strings in Resources */ = {isa = PBXBuildFile; fileRef = 1E59555F15B6F80E00A03429 /* HockeySDK.strings */; }; 1ECA8F4D192B5BD8006B9416 /* BITCrashDetailsPrivate.h in Headers */ = {isa = PBXBuildFile; fileRef = 1ECA8F4B192B5BD8006B9416 /* BITCrashDetailsPrivate.h */; }; + 1ECA8F51192B6954006B9416 /* BITCrashMetaData.h in Headers */ = {isa = PBXBuildFile; fileRef = 1ECA8F4F192B6954006B9416 /* BITCrashMetaData.h */; }; + 1ECA8F52192B6954006B9416 /* BITCrashMetaData.m in Sources */ = {isa = PBXBuildFile; fileRef = 1ECA8F50192B6954006B9416 /* BITCrashMetaData.m */; }; 1ED570C718BF878C00AB3350 /* BITCrashAttachment.h in Headers */ = {isa = PBXBuildFile; fileRef = 1ED570C518BF878C00AB3350 /* BITCrashAttachment.h */; settings = {ATTRIBUTES = (Public, ); }; }; 1ED570C818BF878C00AB3350 /* BITCrashAttachment.m in Sources */ = {isa = PBXBuildFile; fileRef = 1ED570C618BF878C00AB3350 /* BITCrashAttachment.m */; }; 1EF95CA6162CB037000AE3AD /* BITFeedbackActivity.h in Headers */ = {isa = PBXBuildFile; fileRef = 1EF95CA4162CB036000AE3AD /* BITFeedbackActivity.h */; settings = {ATTRIBUTES = (Public, ); }; }; @@ -291,6 +293,8 @@ 1EAF20A7162DC0F600957B1D /* feedbackActiviy@2x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "feedbackActiviy@2x.png"; sourceTree = ""; }; 1EB52FC3167B73D400C801D5 /* en */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = en; path = en.lproj/HockeySDK.strings; sourceTree = ""; }; 1ECA8F4B192B5BD8006B9416 /* BITCrashDetailsPrivate.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = BITCrashDetailsPrivate.h; sourceTree = ""; }; + 1ECA8F4F192B6954006B9416 /* BITCrashMetaData.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = BITCrashMetaData.h; sourceTree = ""; }; + 1ECA8F50192B6954006B9416 /* BITCrashMetaData.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = BITCrashMetaData.m; sourceTree = ""; }; 1ED570C518BF878C00AB3350 /* BITCrashAttachment.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = BITCrashAttachment.h; sourceTree = ""; }; 1ED570C618BF878C00AB3350 /* BITCrashAttachment.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = BITCrashAttachment.m; sourceTree = ""; }; 1EDA60CF15C2C1450032D10B /* HockeySDK-Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = "HockeySDK-Info.plist"; sourceTree = ""; }; @@ -488,11 +492,13 @@ children = ( 1E754E561621FBB70070AB92 /* BITCrashManager.h */, 1E754E571621FBB70070AB92 /* BITCrashManager.m */, + 1EFF03D717F20F8300A5F13C /* BITCrashManagerPrivate.h */, + 1E754E581621FBB70070AB92 /* BITCrashManagerDelegate.h */, 1E90FD7118EDB86400CF0417 /* BITCrashDetails.h */, 1E90FD7218EDB86400CF0417 /* BITCrashDetails.m */, 1ECA8F4B192B5BD8006B9416 /* BITCrashDetailsPrivate.h */, - 1EFF03D717F20F8300A5F13C /* BITCrashManagerPrivate.h */, - 1E754E581621FBB70070AB92 /* BITCrashManagerDelegate.h */, + 1ECA8F4F192B6954006B9416 /* BITCrashMetaData.h */, + 1ECA8F50192B6954006B9416 /* BITCrashMetaData.m */, 1ED570C518BF878C00AB3350 /* BITCrashAttachment.h */, 1ED570C618BF878C00AB3350 /* BITCrashAttachment.m */, 1E754E5A1621FBB70070AB92 /* BITCrashReportTextFormatter.h */, @@ -673,6 +679,7 @@ 1E49A46D1612226D00463151 /* BITAppVersionMetaInfo.h in Headers */, 1E49A47C1612226D00463151 /* BITUpdateManagerPrivate.h in Headers */, 1E49A4851612226D00463151 /* BITUpdateViewControllerPrivate.h in Headers */, + 1ECA8F51192B6954006B9416 /* BITCrashMetaData.h in Headers */, 1E49A4B5161222B900463151 /* BITHockeyBaseManagerPrivate.h in Headers */, E4933E8017B66CDA00B11ACC /* BITHTTPOperation.h in Headers */, 1E49A4BE161222B900463151 /* BITHockeyHelper.h in Headers */, @@ -891,6 +898,7 @@ 1E49A4451612223B00463151 /* BITFeedbackListViewCell.m in Sources */, 1E49A44B1612223B00463151 /* BITFeedbackListViewController.m in Sources */, 1E49A4511612223B00463151 /* BITFeedbackManager.m in Sources */, + 1ECA8F52192B6954006B9416 /* BITCrashMetaData.m in Sources */, E4933E8117B66CDA00B11ACC /* BITHTTPOperation.m in Sources */, 1E49A45A1612223B00463151 /* BITFeedbackMessage.m in Sources */, 1ED570C818BF878C00AB3350 /* BITCrashAttachment.m in Sources */, diff --git a/Support/HockeySDKTests/BITCrashManagerTests.m b/Support/HockeySDKTests/BITCrashManagerTests.m index 5bb154e48f..1f0f25870a 100644 --- a/Support/HockeySDKTests/BITCrashManagerTests.m +++ b/Support/HockeySDKTests/BITCrashManagerTests.m @@ -81,13 +81,16 @@ #pragma mark - Persistence tests -- (void)testPersistUserProvidedCrashDescription { +- (void)testPersistUserProvidedMetaData { NSString *tempCrashName = @"tempCrash"; [_sut setLastCrashFilename:tempCrashName]; - [_sut persistUserProvidedCrashDescription:@"Test string"]; + + BITCrashMetaData *metaData = [BITCrashMetaData new]; + [metaData setUserDescription:@"Test string"]; + [_sut persistUserProvidedMetaData:metaData]; NSError *error; - NSString *description = [NSString stringWithContentsOfFile:[NSString stringWithFormat:@"%@.desc", [[_sut getCrashesDir] stringByAppendingPathComponent: tempCrashName]] encoding:NSUTF8StringEncoding error:&error]; + NSString *description = [NSString stringWithContentsOfFile:[NSString stringWithFormat:@"%@.desc", [[_sut crashesDir] stringByAppendingPathComponent: tempCrashName]] encoding:NSUTF8StringEncoding error:&error]; assertThat(description, equalTo(@"Test string")); } @@ -97,7 +100,7 @@ NSString* type = @"text/plain"; BITCrashAttachment *originalAttachment = [[BITCrashAttachment alloc] initWithFilename:filename attachmentData:data contentType:type]; - NSString *attachmentFilename = [_sut.getCrashesDir stringByAppendingPathComponent:@"testAttachment"]; + NSString *attachmentFilename = [[_sut crashesDir] stringByAppendingPathComponent:@"testAttachment"]; [_sut persistAttachment:originalAttachment withFilename:attachmentFilename]; @@ -155,14 +158,14 @@ id delegateMock = mockProtocol(@protocol(BITCrashManagerDelegate)); _sut.delegate = delegateMock; - assertThatBool([_sut handleUserInput:BITCrashManagerUserInputDontSend withUserProvidedCrashDescription:nil], equalToBool(YES)); + assertThatBool([_sut handleUserInput:BITCrashManagerUserInputDontSend withUserProvidedMetaData:nil], equalToBool(YES)); [verify(delegateMock) crashManagerWillCancelSendingCrashReport:_sut]; } - (void)testHandleUserInputSend { - assertThatBool([_sut handleUserInput:BITCrashManagerUserInputSend withUserProvidedCrashDescription:nil], equalToBool(YES)); + assertThatBool([_sut handleUserInput:BITCrashManagerUserInputSend withUserProvidedMetaData:nil], equalToBool(YES)); } - (void)testHandleUserInputAlwaysSend { @@ -174,7 +177,7 @@ [given([mockUserDefaults integerForKey:@"BITCrashManagerStatus"]) willReturn:nil]; //Test if method runs through - assertThatBool([_sut handleUserInput:BITCrashManagerUserInputAlwaysSend withUserProvidedCrashDescription:nil], equalToBool(YES)); + assertThatBool([_sut handleUserInput:BITCrashManagerUserInputAlwaysSend withUserProvidedMetaData:nil], equalToBool(YES)); //Test if correct CrashManagerStatus is now set [given([mockUserDefaults integerForKey:@"BITCrashManagerStauts"]) willReturnInt:BITCrashManagerStatusAutoSend]; @@ -185,7 +188,7 @@ } - (void)testHandleUserInputWithInvalidInput { - assertThatBool([_sut handleUserInput:3 withUserProvidedCrashDescription:nil], equalToBool(NO)); + assertThatBool([_sut handleUserInput:3 withUserProvidedMetaData:nil], equalToBool(NO)); } #pragma mark - Debugger From 7c0495c401787a824e8b27cf75da1caef164fceb Mon Sep 17 00:00:00 2001 From: Andreas Linde Date: Tue, 20 May 2014 13:32:38 +0200 Subject: [PATCH 43/43] Fix unit tests that broke in last commit --- Support/HockeySDKTests/BITCrashManagerTests.m | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Support/HockeySDKTests/BITCrashManagerTests.m b/Support/HockeySDKTests/BITCrashManagerTests.m index 1f0f25870a..e276813017 100644 --- a/Support/HockeySDKTests/BITCrashManagerTests.m +++ b/Support/HockeySDKTests/BITCrashManagerTests.m @@ -99,7 +99,7 @@ NSData *data = [[NSData alloc] initWithBase64Encoding:@"TestData"]; NSString* type = @"text/plain"; - BITCrashAttachment *originalAttachment = [[BITCrashAttachment alloc] initWithFilename:filename attachmentData:data contentType:type]; + BITCrashAttachment *originalAttachment = [[BITCrashAttachment alloc] initWithFilename:filename crashAttachmentData:data contentType:type]; NSString *attachmentFilename = [[_sut crashesDir] stringByAppendingPathComponent:@"testAttachment"]; [_sut persistAttachment:originalAttachment withFilename:attachmentFilename]; @@ -107,7 +107,7 @@ BITCrashAttachment *decodedAttachment = [_sut attachmentForCrashReport:attachmentFilename]; assertThat(decodedAttachment.filename, equalTo(filename)); - assertThat(decodedAttachment.attachmentData, equalTo(data)); + assertThat(decodedAttachment.crashAttachmentData, equalTo(data)); assertThat(decodedAttachment.contentType, equalTo(type)); }