diff --git a/Classes/BITCrashManager.m b/Classes/BITCrashManager.m index 0e042fbc39..26c69f3a42 100644 --- a/Classes/BITCrashManager.m +++ b/Classes/BITCrashManager.m @@ -247,10 +247,6 @@ static void uncaught_cxx_exception_handler(const BITCrashUncaughtCXXExceptionInf _settingsFile = [_crashesDir stringByAppendingPathComponent:BITHOCKEY_CRASH_SETTINGS]; _analyzerInProgressFile = [_crashesDir stringByAppendingPathComponent:BITHOCKEY_CRASH_ANALYZER]; - if ([_fileManager fileExistsAtPath:_analyzerInProgressFile]) { - NSError *error = nil; - [_fileManager removeItemAtPath:_analyzerInProgressFile error:&error]; - } if (!BITHockeyBundle() && !bit_isRunningInAppExtension()) { BITHockeyLogWarning(@"[HockeySDK] WARNING: %@ is missing, will send reports automatically!", BITHOCKEYSDK_BUNDLE); @@ -367,7 +363,7 @@ static void uncaught_cxx_exception_handler(const BITCrashUncaughtCXXExceptionInf } } -- (void)persistAttachment:(BITHockeyAttachment *)attachment withFilename:(NSString *)filename { +- (BOOL)persistAttachment:(BITHockeyAttachment *)attachment withFilename:(NSString *)filename { NSString *attachmentFilename = [filename stringByAppendingString:@".data"]; NSMutableData *data = [[NSMutableData alloc] init]; NSKeyedArchiver *archiver = [[NSKeyedArchiver alloc] initForWritingWithMutableData:data]; @@ -376,7 +372,7 @@ static void uncaught_cxx_exception_handler(const BITCrashUncaughtCXXExceptionInf [archiver finishEncoding]; - [data writeToFile:attachmentFilename atomically:YES]; + return [data writeToFile:attachmentFilename atomically:YES]; } - (void)persistUserProvidedMetaData:(BITCrashMetaData *)userProvidedMetaData { @@ -757,6 +753,7 @@ static void uncaught_cxx_exception_handler(const BITCrashUncaughtCXXExceptionInf * @param filename the crash reports temp filename */ - (void)storeMetaDataForCrashReportFilename:(NSString *)filename { + BITHockeyLogVerbose(@"VERBOSE: Storing meta data for crash report with filename %@", filename); NSError *error = NULL; NSMutableDictionary *metaDict = [NSMutableDictionary dictionaryWithCapacity:4]; NSString *applicationLog = @""; @@ -771,10 +768,18 @@ static void uncaught_cxx_exception_handler(const BITCrashUncaughtCXXExceptionInf [metaDict setObject:applicationLog forKey:kBITCrashMetaApplicationLog]; if ([self.delegate respondsToSelector:@selector(attachmentForCrashManager:)]) { + BITHockeyLogVerbose(@"VERBOSE: Processing attachment for crash report with filename %@", filename); BITHockeyAttachment *attachment = [self.delegate attachmentForCrashManager:self]; if (attachment && attachment.hockeyAttachmentData) { - [self persistAttachment:attachment withFilename:[_crashesDir stringByAppendingPathComponent: filename]]; + BOOL success = [self persistAttachment:attachment withFilename:[_crashesDir stringByAppendingPathComponent: filename]]; + if (!success) { + BITHockeyLogError(@"ERROR: Persisting the crash attachment failed"); + } else { + BITHockeyLogVerbose(@"VERBOSE: Crash attachment successfully persisted."); + } + } else { + BITHockeyLogDebug(@"INFO: Crash attachment was nil"); } } @@ -783,10 +788,14 @@ static void uncaught_cxx_exception_handler(const BITCrashUncaughtCXXExceptionInf options:0 error:&error]; if (plist) { - [plist writeToFile:[_crashesDir stringByAppendingPathComponent: [filename stringByAppendingPathExtension:@"meta"]] atomically:YES]; + BOOL success = [plist writeToFile:[_crashesDir stringByAppendingPathComponent: [filename stringByAppendingPathExtension:@"meta"]] atomically:YES]; + if (!success) { + BITHockeyLogError(@"ERROR: Writing crash meta data failed."); + } } else { BITHockeyLogError(@"ERROR: Writing crash meta data failed. %@", error); } + BITHockeyLogVerbose(@"VERBOSE: Storing crash meta data finished."); } - (BOOL)handleUserInput:(BITCrashManagerUserInput)userInput withUserProvidedMetaData:(BITCrashMetaData *)userProvidedMetaData { @@ -837,6 +846,7 @@ static void uncaught_cxx_exception_handler(const BITCrashUncaughtCXXExceptionInf * Parse the new crash report and gather additional meta data from the app which will be stored along the crash report */ - (void) handleCrashReport { + BITHockeyLogVerbose(@"VERBOSE: Handling crash report"); NSError *error = NULL; if (!self.plCrashReporter) return; @@ -845,6 +855,7 @@ static void uncaught_cxx_exception_handler(const BITCrashUncaughtCXXExceptionInf if (![_fileManager fileExistsAtPath:_analyzerInProgressFile]) { // mark the start of the routine [_fileManager createFileAtPath:_analyzerInProgressFile contents:nil attributes:nil]; + BITHockeyLogVerbose(@"VERBOSE: AnalyzerInProgress file created"); [self saveSettings]; @@ -900,6 +911,8 @@ static void uncaught_cxx_exception_handler(const BITCrashUncaughtCXXExceptionInf [self storeMetaDataForCrashReportFilename:cacheFilename]; } } + } else { + BITHockeyLogWarning(@"WARNING: AnalyzerInProgress file found, handling crash report skipped"); } // Purge the report @@ -988,6 +1001,7 @@ static void uncaught_cxx_exception_handler(const BITCrashUncaughtCXXExceptionInf } - (void)triggerDelayedProcessing { + BITHockeyLogVerbose(@"VERBOSE: Triggering delayed crash processing."); [NSObject cancelPreviousPerformRequestsWithTarget:self selector:@selector(invokeDelayedProcessing) object:nil]; [self performSelector:@selector(invokeDelayedProcessing) withObject:nil afterDelay:0.5]; } @@ -1243,6 +1257,7 @@ static void uncaught_cxx_exception_handler(const BITCrashUncaughtCXXExceptionInf } if (considerReport) { + BITHockeyLogVerbose(@"INFO: App kill detected, creating crash report."); [self createCrashReportForAppKill]; _didCrashInLastSession = YES; @@ -1262,6 +1277,7 @@ static void uncaught_cxx_exception_handler(const BITCrashUncaughtCXXExceptionInf [[NSUserDefaults standardUserDefaults] synchronize]; [self triggerDelayedProcessing]; + BITHockeyLogVerbose(@"VERBOSE: CrashManager startManager has finished."); } /** diff --git a/Classes/BITCrashManagerPrivate.h b/Classes/BITCrashManagerPrivate.h index faf9f1ad14..75ecf3ce64 100644 --- a/Classes/BITCrashManagerPrivate.h +++ b/Classes/BITCrashManagerPrivate.h @@ -96,7 +96,7 @@ - (NSString *)firstNotApprovedCrashReport; - (void)persistUserProvidedMetaData:(BITCrashMetaData *)userProvidedMetaData; -- (void)persistAttachment:(BITHockeyAttachment *)attachment withFilename:(NSString *)filename; +- (BOOL)persistAttachment:(BITHockeyAttachment *)attachment withFilename:(NSString *)filename; - (BITHockeyAttachment *)attachmentForCrashReport:(NSString *)filename; diff --git a/Classes/BITDomain.m b/Classes/BITDomain.m index 8e15e4a8b1..dbe2adf31d 100644 --- a/Classes/BITDomain.m +++ b/Classes/BITDomain.m @@ -3,6 +3,7 @@ @implementation BITDomain @synthesize envelopeTypeName = _envelopeTypeName; @synthesize dataTypeName = _dataTypeName; +@synthesize properties = _properties; /// Initializes a new instance of the class. - (instancetype)init { diff --git a/Classes/BITEventData.h b/Classes/BITEventData.h index ed3e0b147b..50bb133330 100644 --- a/Classes/BITEventData.h +++ b/Classes/BITEventData.h @@ -4,5 +4,6 @@ @property (nonatomic, copy, readonly) NSString *envelopeTypeName; @property (nonatomic, copy, readonly) NSString *dataTypeName; +@property (nonatomic, strong) NSDictionary *measurements; @end diff --git a/Classes/BITEventData.m b/Classes/BITEventData.m index 1f86da1ba4..0c8dfd2195 100644 --- a/Classes/BITEventData.m +++ b/Classes/BITEventData.m @@ -5,6 +5,8 @@ @synthesize envelopeTypeName = _envelopeTypeName; @synthesize dataTypeName = _dataTypeName; @synthesize version = _version; +@synthesize properties = _properties; +@synthesize measurements = _measurements; /// Initializes a new instance of the class. - (instancetype)init { @@ -12,6 +14,8 @@ _envelopeTypeName = @"Microsoft.ApplicationInsights.Event"; _dataTypeName = @"EventData"; _version = @2; + _properties = [NSDictionary new]; + _measurements = [NSDictionary new]; } return self; } @@ -25,6 +29,13 @@ if (self.name != nil) { [dict setObject:self.name forKey:@"name"]; } + if (self.properties !=nil) { + [dict setObject:self.properties forKey:@"properties"]; + } + if (self.measurements) { + [dict setObject:self.measurements forKey:@"measurements"]; + } + return dict; } @@ -36,6 +47,8 @@ _envelopeTypeName = [coder decodeObjectForKey:@"self.envelopeTypeName"]; _dataTypeName = [coder decodeObjectForKey:@"self.dataTypeName"]; _version = [coder decodeObjectForKey:@"self.version"]; + _properties = [coder decodeObjectForKey:@"self.properties"]; + _measurements = [coder decodeObjectForKey:@"self.measurements"]; } return self; } @@ -45,6 +58,8 @@ [coder encodeObject:self.envelopeTypeName forKey:@"self.envelopeTypeName"]; [coder encodeObject:self.dataTypeName forKey:@"self.dataTypeName"]; [coder encodeObject:self.version forKey:@"self.version"]; + [coder encodeObject:self.properties forKey:@"self.properties"]; + [coder encodeObject:self.measurements forKey:@"self.measurements"]; } diff --git a/Classes/BITMetricsManager.h b/Classes/BITMetricsManager.h index 303143e107..6d5e3f2d5f 100644 --- a/Classes/BITMetricsManager.h +++ b/Classes/BITMetricsManager.h @@ -40,7 +40,18 @@ NS_ASSUME_NONNULL_BEGIN * * @param eventName The event's name as a string. */ -- (void)trackEventWithName:(NSString *)eventName; +- (void)trackEventWithName:(nonnull NSString *)eventName; + +/** + * This method allows to track an event that happened in your app. + * Remember to choose meaningful event names to have the best experience when diagnosing your app + * in the web portal. + * + * @param eventName the name of the event, which should be tracked. + * @param properties key value pairs with additional info about the event. + * @param measurements key value pairs, which contain custom metrics. + */ +- (void)trackEventWithName:(nonnull NSString *)eventName properties:(nullable NSDictionary *)properties measurements:(nullable NSDictionary *)measurements; @end diff --git a/Classes/BITMetricsManager.m b/Classes/BITMetricsManager.m index 508921d793..8e4798fe0b 100644 --- a/Classes/BITMetricsManager.m +++ b/Classes/BITMetricsManager.m @@ -176,7 +176,7 @@ static NSString *const BITMetricsURLPathString = @"v2/track"; #pragma mark Events -- (void)trackEventWithName:(NSString *)eventName { +- (void)trackEventWithName:(nonnull NSString *)eventName { if (!eventName) { return; } if (self.disabled) { BITHockeyLogDebug(@"INFO: BITMetricsManager is disabled, therefore this tracking call was ignored."); @@ -192,6 +192,24 @@ static NSString *const BITMetricsURLPathString = @"v2/track"; }); } +- (void)trackEventWithName:(nonnull NSString *)eventName properties:(nullable NSDictionary *)properties measurements:(nullable NSDictionary *)measurements { + if (!eventName) { return; } + if (self.disabled) { + BITHockeyLogDebug(@"INFO: BITMetricsManager is disabled, therefore this tracking call was ignored."); + return; + } + + __weak typeof(self) weakSelf = self; + dispatch_async(self.metricsEventQueue, ^{ + typeof(self) strongSelf = weakSelf; + BITEventData *eventData = [BITEventData new]; + [eventData setName:eventName]; + [eventData setProperties:properties]; + [eventData setMeasurements:measurements]; + [strongSelf trackDataItem:eventData]; + }); +} + #pragma mark Track DataItem - (void)trackDataItem:(BITTelemetryData *)dataItem { diff --git a/Classes/BITTelemetryData.h b/Classes/BITTelemetryData.h index b1a5d3d0fc..50539a1074 100644 --- a/Classes/BITTelemetryData.h +++ b/Classes/BITTelemetryData.h @@ -11,6 +11,7 @@ NS_ASSUME_NONNULL_BEGIN @property (nonatomic, copy) NSNumber *version; @property (nonatomic, copy) NSString *name; +@property (nonatomic, strong) NSDictionary *properties; @end diff --git a/Classes/BITTelemetryData.m b/Classes/BITTelemetryData.m index b0f15bf409..ee524acb2e 100644 --- a/Classes/BITTelemetryData.m +++ b/Classes/BITTelemetryData.m @@ -18,6 +18,7 @@ if(self) { _version = [coder decodeObjectForKey:@"self.version"]; _name = [coder decodeObjectForKey:@"self.name"]; + _properties = [coder decodeObjectForKey:@"self.properties"]; } return self; } @@ -26,6 +27,7 @@ [super encodeWithCoder:coder]; [coder encodeObject:self.version forKey:@"self.version"]; [coder encodeObject:self.name forKey:@"self.name"]; + [coder encodeObject:self.properties forKey:@"self.properties"]; } @end diff --git a/HockeySDK-Source.podspec b/HockeySDK-Source.podspec index 46cb8f1aec..b0fca4972d 100644 --- a/HockeySDK-Source.podspec +++ b/HockeySDK-Source.podspec @@ -1,6 +1,6 @@ Pod::Spec.new do |s| s.name = 'HockeySDK-Source' - s.version = '4.1.0-beta.2' + s.version = '4.1.0' s.summary = 'Collect live crash reports, get feedback from your users, distribute your betas, and analyze your test coverage with HockeyApp.' s.description = <<-DESC @@ -25,7 +25,7 @@ Pod::Spec.new do |s| s.frameworks = 'AssetsLibrary', 'CoreGraphics', 'CoreTelephony', 'CoreText', 'MobileCoreServices', 'Photos', 'QuartzCore', 'QuickLook', 'Security', 'SystemConfiguration', 'UIKit' s.libraries = 'c++', 'z' s.vendored_frameworks = 'Vendor/CrashReporter.framework' - s.pod_target_xcconfig = {'GCC_PREPROCESSOR_DEFINITIONS' => %{$(inherited) BITHOCKEY_VERSION="@\\"#{s.version}\\"" BITHOCKEY_C_VERSION="\\"#{s.version}\\"" BITHOCKEY_BUILD="@\\"60\\"" BITHOCKEY_C_BUILD="\\"60\\""} } + s.pod_target_xcconfig = {'GCC_PREPROCESSOR_DEFINITIONS' => %{$(inherited) BITHOCKEY_VERSION="@\\"#{s.version}\\"" BITHOCKEY_C_VERSION="\\"#{s.version}\\"" BITHOCKEY_BUILD="@\\"95\\"" BITHOCKEY_C_BUILD="\\"95\\""} } s.resource_bundle = { 'HockeySDKResources' => ['Resources/*.png', 'Resources/*.lproj'] } s.preserve_paths = 'Resources', 'Support' s.private_header_files = 'Classes/*Private.h' diff --git a/HockeySDK.podspec b/HockeySDK.podspec index f86952d802..07f5915da9 100644 --- a/HockeySDK.podspec +++ b/HockeySDK.podspec @@ -1,6 +1,6 @@ Pod::Spec.new do |s| s.name = 'HockeySDK' - s.version = '4.1.0-beta.2' + s.version = '4.1.0' s.summary = 'Collect live crash reports, get feedback from your users, distribute your betas, and analyze your test coverage with HockeyApp.' s.description = <<-DESC diff --git a/README.md b/README.md index 1698e00fe8..ec977b4dab 100644 --- a/README.md +++ b/README.md @@ -2,13 +2,13 @@ [![Carthage compatible](https://img.shields.io/badge/Carthage-compatible-4BC51D.svg?style=flat)](https://github.com/Carthage/Carthage) [![Version](http://cocoapod-badges.herokuapp.com/v/HockeySDK/badge.png)](http://cocoadocs.org/docsets/HockeySDK) -## Version 4.1.0-beta.2 +## Version 4.1.0 -- [Changelog](http://www.hockeyapp.net/help/sdk/ios/4.1.0-beta.2/docs/docs/Changelog.html) +- [Changelog](http://www.hockeyapp.net/help/sdk/ios/4.1.0/docs/docs/Changelog.html) **NOTE:** With the release of HockeySDK 4.0.0-alpha.1 a bug was introduced which lead to the exclusion of the Application Support folder from iCloud and iTunes backups. -If you have been using one of the affected versions (4.0.0-alpha.2, Version 4.0.0-beta.1, 4.0.0, 4.1.0-alpha.1, 4.1.0-alpha.2, or Version 4.1.0-beta.1), please make sure to update to at least version 4.0.1 or 4.1.0-beta.2 of our SDK as soon as you can. +If you have been using one of the affected versions (4.0.0-alpha.2, Version 4.0.0-beta.1, 4.0.0, 4.1.0-alpha.1, 4.1.0-alpha.2, or Version 4.1.0-beta.1), please make sure to update to at least version 4.0.1 of our SDK as soon as you can. ## Introduction @@ -18,7 +18,7 @@ The following features are currently supported: 1. **Collect crash reports:** If your app crashes, a crash log with the same format as from the Apple Crash Reporter is written to the device's storage. If the user starts the app again, he is asked to submit the crash report to HockeyApp. This works for both beta and live apps, i.e. those submitted to the App Store. -2. **User Metrics:** Understand user behavior to improve your app. Track usage through daily and monthly active users, monitor crash impacted users, as well as customer engagement through session count. If you are part of [Preseason](hockeyapp.net/preseason), you can now track Custom Events in your app, understand user actions and see the aggregates on the HockeyApp portal. +2. **User Metrics:** Understand user behavior to improve your app. Track usage through daily and monthly active users, monitor crash impacted users, as well as customer engagement through session count.You can now track **Custom Events** in your app, understand user actions and see the aggregates on the HockeyApp portal. 3. **Update Ad-Hoc / Enterprise apps:** The app will check with HockeyApp if a new version for your Ad-Hoc or Enterprise build is available. If yes, it will show an alert view to the user and let him see the release notes, the version history and start the installation process right away. @@ -453,6 +453,39 @@ metricsManager.trackEventWithName(eventName) - There is currently a limit of 300 unique event names per app per week. - There is _no_ limit on the number of times an event can happen. +#### 3.7.2 Attaching custom properties and measurements to a custom event + +It's possible to attach porperties and/or measurements to a custom event. + +- Properties have to be a string. +- Measurements have to be of a numeric type. + +**Objective-C** + +```objectivec +BITMetricsManager *metricsManager = [BITHockeyManager sharedHockeyManager].metricsManager; + +NSDictionary *myProperties = @{@"Property 1" : @"Something", + @"Property 2" : @"Other thing", + @"Property 3" : @"Totally different thing"}; +NSDictionary *myMeasurements = @{@"Measurement 1" : @1, + @"Measurement 2" : @2.34, + @"Measurement 3" : @2000000}; + +[metricsManager trackEventWithName:eventName properties:myProperties measurements:myMeasurements] +``` + +**Swift** + +```swift +let myProperties = ["Property 1": "Something", "Property 2": "Other thing", "Property 3" : "Totally different thing."] +let myMeasurements = ["Measurement 1": 1, "Measurement 2": 2.3, "Measurement 3" : 30000] + +let metricsManager = BITHockeyManager.sharedHockeyManager().metricsManager +metricsManager.trackEventWithName(eventName, properties: myProperties, myMeasurements: measurements) +``` + + ### 3.8 Feedback @@ -524,7 +557,7 @@ To check if data is send properly to HockeyApp and also see some additional SDK ## 4. Documentation -Our documentation can be found on [HockeyApp](http://hockeyapp.net/help/sdk/ios/4.1.0-beta.2/index.html). +Our documentation can be found on [HockeyApp](http://hockeyapp.net/help/sdk/ios/4.1.0/index.html). ## 5.Troubleshooting @@ -538,7 +571,7 @@ Our documentation can be found on [HockeyApp](http://hockeyapp.net/help/sdk/ios/ Make sure none of the following files are copied into your app bundle, check under app target, `Build Phases`, `Copy Bundle Resources` or in the `.app` bundle after building: - `HockeySDK.framework` (except if you build a dynamic framework version of the SDK yourself!) - - `de.bitstadium.HockeySDK-iOS-4.1.0-beta.2.docset` + - `de.bitstadium.HockeySDK-iOS-4.1.0.docset` ### Feature are not working as expected diff --git a/Support/buildnumber.xcconfig b/Support/buildnumber.xcconfig index 85262e6253..2e99ca24cb 100644 --- a/Support/buildnumber.xcconfig +++ b/Support/buildnumber.xcconfig @@ -1,7 +1,7 @@ #include "HockeySDK.xcconfig" -BUILD_NUMBER = 94 -VERSION_STRING = 4.1.0-beta.2 +BUILD_NUMBER = 95 +VERSION_STRING = 4.1.0 GCC_PREPROCESSOR_DEFINITIONS = $(inherited) $(HOCKEYSDK_GCC_PREPROCESSOR_DEFINITIONS) HOCKEYSDK_CONFIGURATION_$(CONFIGURATION) BITHOCKEY_VERSION="@\""$(VERSION_STRING)"\"" BITHOCKEY_BUILD="@\""$(BUILD_NUMBER)"\"" BITHOCKEY_C_VERSION="\""$(VERSION_STRING)"\"" BITHOCKEY_C_BUILD="\""$(BUILD_NUMBER)"\"" BIT_ARM_ARCHS = armv7 armv7s arm64 BIT_SIM_ARCHS = x86_64 i386 diff --git a/Support/release.xcconfig b/Support/release.xcconfig index f73473130a..433b211fc3 100644 --- a/Support/release.xcconfig +++ b/Support/release.xcconfig @@ -1,4 +1,4 @@ #include "buildnumber.xcconfig" OTHER_CFLAGS[sdk=iphoneos9.*] = $(HOCKEYSDK_WARNING_FLAGS) -fembed-bitcode -OTHER_CFLAGS[sdk=iphonesimulator9.*] = $(HOCKEYSDK_WARNING_FLAGS) +OTHER_CFLAGS[sdk=iphonesimulator9.*] = $(HOCKEYSDK_WARNING_FLAGS) -fembed-bitcode-marker diff --git a/docs/Changelog-template.md b/docs/Changelog-template.md index 0e0252696f..5a8d3b2b69 100644 --- a/docs/Changelog-template.md +++ b/docs/Changelog-template.md @@ -1,3 +1,8 @@ +## 4.1.0 + +- Includes improvements from 4.0.2 release of the SDK. +- [NEW] Additional API to track an event with properties and measurements. + ## 4.1.0-beta.2 - [BUGFIX] Fixes an issue where the whole app's Application Support directory was accidentally excluded from backups. @@ -38,6 +43,11 @@ This SDK release explicitly includes the Application Support directory into back - [IMPROVEMENT] Reuse `NSURLSession` object - [IMPROVEMENT] Under the hood improvements and cleanup +## Version 4.0.2 + +- [BUGFIX] Add Bitcode marker back to simulator slices. This is necessary because otherwise `lipo` apparently strips the Bitcode sections from the merged library completely. As a side effect, this unfortunately breaks compatibility with Xcode 6. [#310](https://github.com/bitstadium/HockeySDK-iOS/pull/310) +- [IMPROVEMENT] Improve error detection and logging during crash processing in case the app is sent to the background while crash processing hasn't finished.[#311](https://github.com/bitstadium/HockeySDK-iOS/pull/311) + ## Version 4.0.1 - [BUGFIX] Fixes an issue where the whole app's Application Support directory was accidentally excluded from backups.