diff --git a/Classes/BITCrashAttachment.h b/Classes/BITCrashAttachment.h new file mode 100644 index 0000000000..482e0c004c --- /dev/null +++ b/Classes/BITCrashAttachment.h @@ -0,0 +1,66 @@ +/* + * Author: Andreas Linde + * + * Copyright (c) 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 + +/** + * Provides support to add binary attachments to crash reports + * + * This is used by `[BITCrashManagerDelegate attachmentForCrashManager:]` + */ +@interface BITCrashAttachment : NSObject + +/** + * The filename the attachment should get + */ +@property (nonatomic, readonly, strong) NSString *filename; + +/** + * The attachment data as NSData object + */ +@property (nonatomic, readonly, strong) NSData *attachmentData; + +/** + * The content type of your data as MIME type + */ +@property (nonatomic, readonly, strong) NSString *contentType; + +/** + * Create an BITCrashAttachment instance with a given filename and NSData object + * + * @param filename The filename the attachment should get + * @param attachmentData The attachment data as NSData + * @param contentType The content type of your data as MIME type + * + * @return An instsance of BITCrashAttachment + */ +- (instancetype)initWithFilename:(NSString *)filename + attachmentData:(NSData *)attachmentData + contentType:(NSString *)contentType; + +@end diff --git a/Classes/BITCrashAttachment.m b/Classes/BITCrashAttachment.m new file mode 100644 index 0000000000..def32d2583 --- /dev/null +++ b/Classes/BITCrashAttachment.m @@ -0,0 +1,64 @@ +/* + * Author: Andreas Linde + * + * Copyright (c) 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 "BITCrashAttachment.h" + +@implementation BITCrashAttachment + +- (instancetype)initWithFilename:(NSString *)filename + attachmentData:(NSData *)attachmentData + contentType:(NSString *)contentType +{ + if (self = [super init]) { + _filename = filename; + _attachmentData = attachmentData; + _contentType = contentType; + } + + return self; +} + + +#pragma mark - NSCoder + +- (void)encodeWithCoder:(NSCoder *)encoder { + [encoder encodeObject:self.filename forKey:@"filename"]; + [encoder encodeObject:self.attachmentData forKey:@"data"]; + [encoder encodeObject:self.contentType forKey:@"contentType"]; +} + +- (id)initWithCoder:(NSCoder *)decoder { + if ((self = [super init])) { + _filename = [decoder decodeObjectForKey:@"filename"]; + _attachmentData = [decoder decodeObjectForKey:@"data"]; + _contentType = [decoder decodeObjectForKey:@"contentType"]; + } + return self; +} + +@end diff --git a/Classes/BITCrashManager.m b/Classes/BITCrashManager.m index b0d3b33783..18b3aaf270 100644 --- a/Classes/BITCrashManager.m +++ b/Classes/BITCrashManager.m @@ -37,7 +37,9 @@ #import "HockeySDKPrivate.h" #import "BITHockeyHelper.h" +#import "BITHockeyAppClient.h" +#import "BITCrashAttachment.h" #import "BITHockeyBaseManagerPrivate.h" #import "BITCrashManagerPrivate.h" #import "BITCrashReportTextFormatter.h" @@ -52,6 +54,11 @@ #define kBITCrashMetaUserEmail @"BITCrashMetaUserEmail" #define kBITCrashMetaUserID @"BITCrashMetaUserID" #define kBITCrashMetaApplicationLog @"BITCrashMetaApplicationLog" +#define kBITCrashMetaAttachment @"BITCrashMetaAttachment" + +// internal keys +NSString *const KBITAttachmentDictIndex = @"index"; +NSString *const KBITAttachmentDictAttachment = @"attachment"; NSString *const kBITCrashManagerStatus = @"BITCrashManagerStatus"; @@ -215,6 +222,7 @@ NSString *const kBITCrashManagerStatus = @"BITCrashManagerStatus"; 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]; @@ -228,9 +236,57 @@ NSString *const kBITCrashManagerStatus = @"BITCrashManagerStatus"; [self saveSettings]; } + +- (void)persistAttachment:(BITCrashAttachment *)attachment withFilename:(NSString *)filename { + NSString *attachmentFilename = [filename stringByAppendingString:@".data"]; + NSMutableData *data = [[NSMutableData alloc] init]; + NSKeyedArchiver *archiver = [[NSKeyedArchiver alloc] initForWritingWithMutableData:data]; + + [archiver encodeObject:attachment forKey:kBITCrashMetaAttachment]; + + [archiver finishEncoding]; + + [data writeToFile:attachmentFilename atomically:YES]; +} + +/** + * Read the attachment data from the stored file + * + * @param filename The crash report id + * + * @return an BITCrashAttachment instance or nil + */ +- (BITCrashAttachment *)attachmentForCrashReport:(NSString *)filename { + NSString *attachmentFilename = [filename stringByAppendingString:@".data"]; + + if (![_fileManager fileExistsAtPath:attachmentFilename]) + return nil; + + + NSData *codedData = [[NSData alloc] initWithContentsOfFile:attachmentFilename]; + if (!codedData) + return nil; + + NSKeyedUnarchiver *unarchiver = nil; + + @try { + unarchiver = [[NSKeyedUnarchiver alloc] initForReadingWithData:codedData]; + } + @catch (NSException *exception) { + return nil; + } + + if ([unarchiver containsValueForKey:kBITCrashMetaAttachment]) { + BITCrashAttachment *attachment = [unarchiver decodeObjectForKey:kBITCrashMetaAttachment]; + return attachment; + } + + return nil; +} + /** * Extract all app sepcific UUIDs from the crash reports - * + * * This allows us to send the UUIDs in the XML construct to the server, so the server does not need to parse the crash report for this data. * The app specific UUIDs help to identify which dSYMs are needed to symbolicate this crash report. * @@ -489,6 +545,14 @@ NSString *const kBITCrashManagerStatus = @"BITCrashManagerStatus"; } [metaDict setObject:applicationLog forKey:kBITCrashMetaApplicationLog]; + if (self.delegate != nil && [self.delegate respondsToSelector:@selector(attachmentForCrashManager:)]) { + BITCrashAttachment *attachment = [self.delegate attachmentForCrashManager:self]; + + if (attachment) { + [self persistAttachment:attachment withFilename:[_crashesDir stringByAppendingPathComponent: cacheFilename]]; + } + } + NSData *plist = [NSPropertyListSerialization dataFromPropertyList:(id)metaDict format:NSPropertyListBinaryFormat_v1_0 errorDescription:&errorString]; @@ -549,6 +613,7 @@ NSString *const kBITCrashManagerStatus = @"BITCrashManagerStatus"; ![file hasSuffix:@".DS_Store"] && ![file hasSuffix:@".analyzer"] && ![file hasSuffix:@".plist"] && + ![file hasSuffix:@".data"] && ![file hasSuffix:@".meta"]) { [_crashFiles addObject:[_crashesDir stringByAppendingPathComponent: file]]; } @@ -747,6 +812,7 @@ NSString *const kBITCrashManagerStatus = @"BITCrashManagerStatus"; NSError *error = NULL; NSMutableString *crashes = nil; + NSMutableArray *attachments = [NSMutableArray array]; _crashIdenticalCurrentVersion = NO; for (NSUInteger i=0; i < [_crashFiles count]; i++) { @@ -761,6 +827,7 @@ NSString *const kBITCrashManagerStatus = @"BITCrashManagerStatus"; 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]]; @@ -805,6 +872,13 @@ NSString *const kBITCrashManagerStatus = @"BITCrashManagerStatus"; useremail = [self stringValueFromKeychainForKey:[NSString stringWithFormat:@"%@.%@", cacheFilename, kBITCrashMetaUserEmail]] ?: @""; 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]; + } } else { BITHockeyLog(@"ERROR: Reading crash meta data. %@", error); } @@ -835,6 +909,7 @@ NSString *const kBITCrashManagerStatus = @"BITCrashManagerStatus"; } 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]]; @@ -847,7 +922,7 @@ NSString *const kBITCrashManagerStatus = @"BITCrashManagerStatus"; if (crashes != nil) { BITHockeyLog(@"INFO: Sending crash reports:\n%@", crashes); - [self postXML:[NSString stringWithFormat:@"%@", crashes]]; + [self postXML:[NSString stringWithFormat:@"%@", crashes] attachments:attachments]; } } @@ -895,7 +970,7 @@ NSString *const kBITCrashManagerStatus = @"BITCrashManagerStatus"; * * @param xml The XML data that needs to be send to the server */ -- (void)postXML:(NSString*)xml { +- (void)postXML:(NSString*)xml attachments:(NSArray *)attachments { NSMutableURLRequest *request = nil; NSString *boundary = @"----FOO"; @@ -917,12 +992,28 @@ NSString *const kBITCrashManagerStatus = @"BITCrashManagerStatus"; [request setValue:contentType forHTTPHeaderField:@"Content-type"]; NSMutableData *postBody = [NSMutableData data]; - [postBody appendData:[[NSString stringWithFormat:@"--%@\r\n", boundary] dataUsingEncoding:NSUTF8StringEncoding]]; - [postBody appendData:[@"Content-Disposition: form-data; name=\"xml\"; filename=\"crash.xml\"\r\n" dataUsingEncoding:NSUTF8StringEncoding]]; - [postBody appendData:[[NSString stringWithFormat:@"Content-Type: text/xml\r\n\r\n"] dataUsingEncoding:NSUTF8StringEncoding]]; - [postBody appendData:[xml dataUsingEncoding:NSUTF8StringEncoding]]; - [postBody appendData:[[NSString stringWithFormat:@"\r\n--%@--\r\n", boundary] dataUsingEncoding:NSUTF8StringEncoding]]; + [postBody appendData:[BITHockeyAppClient dataWithPostValue:[xml dataUsingEncoding:NSUTF8StringEncoding] + forKey:@"xml" + contentType:@"text/xml" + 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]; + + [postBody appendData:[BITHockeyAppClient dataWithPostValue:attachment.attachmentData + forKey:key + contentType:attachment.contentType + boundary:boundary + filename:attachment.filename]]; + } + + [postBody appendData:[[NSString stringWithFormat:@"\r\n--%@--\r\n", boundary] dataUsingEncoding:NSUTF8StringEncoding]]; + [request setHTTPBody:postBody]; _statusCode = 200; diff --git a/Classes/BITCrashManagerDelegate.h b/Classes/BITCrashManagerDelegate.h index e2792b5ce4..c88aef2c93 100644 --- a/Classes/BITCrashManagerDelegate.h +++ b/Classes/BITCrashManagerDelegate.h @@ -29,6 +29,7 @@ #import @class BITCrashManager; +@class BITCrashAttachment; /** The `BITCrashManagerDelegate` formal protocol defines methods further configuring @@ -47,17 +48,43 @@ /** Return any log string based data the crash report being processed should contain @param crashManager The `BITCrashManager` instance invoking this delegate + @see attachmentForCrashManager: @see userNameForCrashManager: @see userEmailForCrashManager: */ -(NSString *)applicationLogForCrashManager:(BITCrashManager *)crashManager; +/** Return a BITCrashAttachment object providing an NSData object the crash report + being processed should contain + + Please limit your attachments to reasonable files to avoid high traffic costs for your users. + + Example implementation: + + - (BITCrashAttachment *)attachmentForCrashManager:(BITCrashManager *)crashManager { + NSData *data = [NSData dataWithContentsOfURL:@"mydatafile"]; + + BITCrashAttachment *attachment = [[BITCrashAttachment alloc] initWithFilename:@"myfile.data" + attachmentData:data + contentType:@"'application/octet-stream"]; + return attachment; + } + + @param crashManager The `BITCrashManager` instance invoking this delegate + @see applicationLogForCrashManager: + @see userNameForCrashManager: + @see userEmailForCrashManager: + */ +-(BITCrashAttachment *)attachmentForCrashManager:(BITCrashManager *)crashManager; + + /** Return the user name or userid that should be send along each crash report @param crashManager The `BITCrashManager` instance invoking this delegate @see applicationLogForCrashManager: + @see attachmentForCrashManager: @see userEmailForCrashManager: @deprecated Please use `BITHockeyManagerDelegate userNameForHockeyManager:componentManager:` instead @warning When returning a non nil value, crash reports are not anonymous any @@ -71,6 +98,7 @@ @param crashManager The `BITCrashManager` instance invoking this delegate @see applicationLogForCrashManager: + @see attachmentForCrashManager: @see userNameForCrashManager: @deprecated Please use `BITHockeyManagerDelegate userEmailForHockeyManager:componentManager:` instead @warning When returning a non nil value, crash reports are not anonymous any diff --git a/Classes/BITHockeyAppClient.h b/Classes/BITHockeyAppClient.h index a8d5fdbd15..4de7fa24e7 100644 --- a/Classes/BITHockeyAppClient.h +++ b/Classes/BITHockeyAppClient.h @@ -118,8 +118,8 @@ #pragma mark - Helpers /** - * create a post body from the given value, key and boundary - * c/p from HockeyBaseManager + * create a post body from the given value, key and boundary. This is a convenience call to + * dataWithPostValue:forKey:contentType:boundary and aimed at NSString-content. * * @param value - * @param key - @@ -128,4 +128,18 @@ * @return NSData instance configured to be attached on a (post) URLRequest */ + (NSData *)dataWithPostValue:(NSString *)value forKey:(NSString *)key boundary:(NSString *) boundary; + +/** + * create a post body from the given value, key and boundary and content type. + * + * @param value - + * @param key - + *@param contentType - + * @param boundary - + * @param filename - + * + * @return NSData instance configured to be attached on a (post) URLRequest + */ ++ (NSData *)dataWithPostValue:(NSData *)value forKey:(NSString *)key contentType:(NSString *)contentType boundary:(NSString *) boundary filename:(NSString *)filename; + @end diff --git a/Classes/BITHockeyAppClient.m b/Classes/BITHockeyAppClient.m index 168162b47c..37a22b6407 100644 --- a/Classes/BITHockeyAppClient.m +++ b/Classes/BITHockeyAppClient.m @@ -85,12 +85,25 @@ } + (NSData *)dataWithPostValue:(NSString *)value forKey:(NSString *)key boundary:(NSString *) boundary { + return [self dataWithPostValue:[value dataUsingEncoding:NSUTF8StringEncoding] forKey:key contentType:@"text" boundary:boundary filename:nil]; +} + ++ (NSData *)dataWithPostValue:(NSData *)value forKey:(NSString *)key contentType:(NSString *)contentType boundary:(NSString *) boundary filename:(NSString *)filename { NSMutableData *postBody = [NSMutableData data]; [postBody appendData:[[NSString stringWithFormat:@"--%@\r\n", boundary] dataUsingEncoding:NSUTF8StringEncoding]]; - [postBody appendData:[[NSString stringWithFormat:@"Content-Disposition: form-data; name=\"%@\";\r\n", key] dataUsingEncoding:NSUTF8StringEncoding]]; - [postBody appendData:[[NSString stringWithFormat:@"Content-Type: text\r\n\r\n"] dataUsingEncoding:NSUTF8StringEncoding]]; - [postBody appendData:[value dataUsingEncoding:NSUTF8StringEncoding]]; + + // There's certainly a better way to check if we are supposed to send binary data here. + if (filename){ + [postBody appendData:[[NSString stringWithFormat:@"Content-Disposition: form-data; name=\"%@\"; filename=\"%@\"\r\n", key, filename] dataUsingEncoding:NSUTF8StringEncoding]]; + [postBody appendData:[[NSString stringWithFormat:@"Content-Type: %@\r\n", contentType] dataUsingEncoding:NSUTF8StringEncoding]]; + [postBody appendData:[[NSString stringWithFormat:@"Content-Transfer-Encoding: binary\r\n\r\n"] dataUsingEncoding:NSUTF8StringEncoding]]; + } else { + [postBody appendData:[[NSString stringWithFormat:@"Content-Disposition: form-data; name=\"%@\"\r\n", key] dataUsingEncoding:NSUTF8StringEncoding]]; + [postBody appendData:[[NSString stringWithFormat:@"Content-Type: %@\r\n\r\n", contentType] dataUsingEncoding:NSUTF8StringEncoding]]; + } + + [postBody appendData:value]; [postBody appendData:[@"\r\n" dataUsingEncoding:NSUTF8StringEncoding]]; return postBody; diff --git a/Classes/HockeySDK.h b/Classes/HockeySDK.h index aa1c4da3ff..c364d4eace 100644 --- a/Classes/HockeySDK.h +++ b/Classes/HockeySDK.h @@ -37,6 +37,7 @@ #if HOCKEYSDK_FEATURE_CRASH_REPORTER #import "BITCrashManager.h" +#import "BITCrashAttachment.h" #import "BITCrashManagerDelegate.h" #endif /* HOCKEYSDK_FEATURE_CRASH_REPORTER */ diff --git a/Support/HockeySDK.xcodeproj/project.pbxproj b/Support/HockeySDK.xcodeproj/project.pbxproj index 910428df15..6b6135a2f8 100644 --- a/Support/HockeySDK.xcodeproj/project.pbxproj +++ b/Support/HockeySDK.xcodeproj/project.pbxproj @@ -127,6 +127,8 @@ 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 */; }; + 1ED570C718BF878C00AB3350 /* BITCrashAttachment.h in Headers */ = {isa = PBXBuildFile; fileRef = 1ED570C518BF878C00AB3350 /* BITCrashAttachment.h */; }; + 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, ); }; }; 1EF95CA7162CB037000AE3AD /* BITFeedbackActivity.m in Sources */ = {isa = PBXBuildFile; fileRef = 1EF95CA5162CB036000AE3AD /* BITFeedbackActivity.m */; }; 1EF95CAA162CB314000AE3AD /* BITFeedbackComposeViewControllerDelegate.h in Headers */ = {isa = PBXBuildFile; fileRef = 1EF95CA9162CB313000AE3AD /* BITFeedbackComposeViewControllerDelegate.h */; settings = {ATTRIBUTES = (Public, ); }; }; @@ -283,6 +285,8 @@ 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 = ""; }; + 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 = ""; }; 1EF95CA4162CB036000AE3AD /* BITFeedbackActivity.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = BITFeedbackActivity.h; sourceTree = ""; }; 1EF95CA5162CB036000AE3AD /* BITFeedbackActivity.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = BITFeedbackActivity.m; sourceTree = ""; }; @@ -480,6 +484,8 @@ 1E754E571621FBB70070AB92 /* BITCrashManager.m */, 1EFF03D717F20F8300A5F13C /* BITCrashManagerPrivate.h */, 1E754E581621FBB70070AB92 /* BITCrashManagerDelegate.h */, + 1ED570C518BF878C00AB3350 /* BITCrashAttachment.h */, + 1ED570C618BF878C00AB3350 /* BITCrashAttachment.m */, 1E754E5A1621FBB70070AB92 /* BITCrashReportTextFormatter.h */, 1E754E5B1621FBB70070AB92 /* BITCrashReportTextFormatter.m */, ); @@ -633,6 +639,7 @@ 1E59559B15B6FDA500A03429 /* HockeySDK.h in Headers */, 1E59559A15B6FDA500A03429 /* BITHockeyManager.h in Headers */, 1E5955FD15B7877B00A03429 /* BITHockeyManagerDelegate.h in Headers */, + 1ED570C718BF878C00AB3350 /* BITCrashAttachment.h in Headers */, 1E754E5C1621FBB70070AB92 /* BITCrashManager.h in Headers */, 1E754E5E1621FBB70070AB92 /* BITCrashManagerDelegate.h in Headers */, 1E49A4731612226D00463151 /* BITUpdateManager.h in Headers */, @@ -876,6 +883,7 @@ 1E49A4511612223B00463151 /* BITFeedbackManager.m in Sources */, E4933E8117B66CDA00B11ACC /* BITHTTPOperation.m in Sources */, 1E49A45A1612223B00463151 /* BITFeedbackMessage.m in Sources */, + 1ED570C818BF878C00AB3350 /* BITCrashAttachment.m in Sources */, 1E49A4601612223B00463151 /* BITFeedbackUserDataViewController.m in Sources */, 1E49A4701612226D00463151 /* BITAppVersionMetaInfo.m in Sources */, 1E49A4761612226D00463151 /* BITUpdateManager.m in Sources */, diff --git a/Support/HockeySDKTests/BITCrashManagerTests.m b/Support/HockeySDKTests/BITCrashManagerTests.m index aa39447289..099b608e1d 100644 --- a/Support/HockeySDKTests/BITCrashManagerTests.m +++ b/Support/HockeySDKTests/BITCrashManagerTests.m @@ -201,6 +201,7 @@ [_sut handleCrashReport]; [verifyCount(delegateMock, times(1)) applicationLogForCrashManager:_sut]; + [verifyCount(delegateMock, times(1)) attachmentForCrashManager:_sut]; // we should have now 1 pending crash report assertThatBool([_sut hasPendingCrashReport], equalToBool(YES));