diff --git a/Classes/Feedback/BITFeedbackManager.m b/Classes/Feedback/BITFeedbackManager.m
index 42086250..3b319972 100644
--- a/Classes/Feedback/BITFeedbackManager.m
+++ b/Classes/Feedback/BITFeedbackManager.m
@@ -865,7 +865,9 @@ - (void)fetchMessageUpdates {
withMessage:nil
completionHandler:^(NSError * __unused err){
// inform the UI to update its data in case the list is already showing
- [[NSNotificationCenter defaultCenter] postNotificationName:BITHockeyFeedbackMessagesLoadingFinished object:nil];
+ dispatch_async(dispatch_get_main_queue(),^{
+ [[NSNotificationCenter defaultCenter] postNotificationName:BITHockeyFeedbackMessagesLoadingFinished object:nil];
+ });
}];
}
@@ -908,7 +910,9 @@ - (void)submitPendingMessages {
}
// inform the UI to update its data in case the list is already showing
- [[NSNotificationCenter defaultCenter] postNotificationName:BITHockeyFeedbackMessagesLoadingFinished object:nil];
+ dispatch_async(dispatch_get_main_queue(),^{
+ [[NSNotificationCenter defaultCenter] postNotificationName:BITHockeyFeedbackMessagesLoadingFinished object:nil];
+ });
}];
}
}
diff --git a/Classes/Feedback/BITFeedbackWindowController.m b/Classes/Feedback/BITFeedbackWindowController.m
index 8003496e..899d78bb 100644
--- a/Classes/Feedback/BITFeedbackWindowController.m
+++ b/Classes/Feedback/BITFeedbackWindowController.m
@@ -399,12 +399,8 @@ - (void)startLoadingIndicator {
}
- (void)stopLoadingIndicator {
-
- // We need to update the UI on the main thread to make sure it updates right away.
- dispatch_async( dispatch_get_main_queue(), ^{
- [self.statusBarLoadingIndicator stopAnimation:self];
- [self.statusBarLoadingIndicator setHidden:YES];
- });
+ [self.statusBarLoadingIndicator stopAnimation:self];
+ [self.statusBarLoadingIndicator setHidden:YES];
[self updateLastUpdate];
}
diff --git a/Classes/Telemetry/BITChannel.h b/Classes/Telemetry/BITChannel.h
index b5b31352..bfa48874 100644
--- a/Classes/Telemetry/BITChannel.h
+++ b/Classes/Telemetry/BITChannel.h
@@ -8,7 +8,10 @@
NS_ASSUME_NONNULL_BEGIN
-FOUNDATION_EXPORT char *BITSafeJsonEventsString;
+/**
+ * Buffer of telemtry events, will be written to disk. Make sure the buffer is used in a threadsafe way.
+ */
+FOUNDATION_EXPORT char *_Nullable BITTelemetryEventBuffer;
/**
* Items get queued before they are persisted and sent out as a batch. This class managed the queue, and forwards the batch
diff --git a/Classes/Telemetry/BITChannel.m b/Classes/Telemetry/BITChannel.m
index edfc5572..8686c9a4 100644
--- a/Classes/Telemetry/BITChannel.m
+++ b/Classes/Telemetry/BITChannel.m
@@ -11,7 +11,7 @@
#import "BITPersistencePrivate.h"
static char *const BITDataItemsOperationsQueue = "net.hockeyapp.senderQueue";
-char *BITSafeJsonEventsString;
+char *BITTelemetryEventBuffer;
NSString *const BITChannelBlockedNotification = @"BITChannelBlockedNotification";
@@ -33,7 +33,7 @@ @implementation BITChannel
- (instancetype)init {
if ((self = [super init])) {
- bit_resetSafeJsonStream(&BITSafeJsonEventsString);
+ bit_resetEventBuffer(&BITTelemetryEventBuffer);
_dataItemCount = 0;
if (bit_isDebuggerAttached()) {
_maxBatchSize = BITDebugMaxBatchSize;
@@ -69,22 +69,45 @@ - (BOOL)isQueueBusy {
return self.channelBlocked;
}
-- (void)persistDataItemQueue {
+- (void)persistDataItemQueue:(char **)eventBuffer {
[self invalidateTimer];
- if (!BITSafeJsonEventsString || strlen(BITSafeJsonEventsString) == 0) {
+
+ // Make sure string (which points to BITTelemetryEventBuffer) is not changed.
+ char *previousBuffer = NULL;
+ char *newEmptyString = NULL;
+ do {
+ newEmptyString = strdup("");
+ previousBuffer = *eventBuffer;
+
+ // This swaps pointers and makes sure eventBuffer now has the balue of newEmptyString.
+ if (OSAtomicCompareAndSwapPtr(previousBuffer, newEmptyString, (void*)eventBuffer)) {
+ @synchronized(self) {
+ self.dataItemCount = 0;
+ }
+ break;
+ }
+ } while(true);
+
+ // Nothing to persist, freeing memory and existing.
+ if (!previousBuffer || strlen(previousBuffer) == 0) {
+ free(previousBuffer);
return;
}
-
- NSData *bundle = [NSData dataWithBytes:BITSafeJsonEventsString length:strlen(BITSafeJsonEventsString)];
+
+ // Persist the data
+ NSData *bundle = [NSData dataWithBytes:previousBuffer length:strlen(previousBuffer)];
[self.persistence persistBundle:bundle];
-
+ free(previousBuffer);
+
// Reset both, the async-signal-safe and item counter.
[self resetQueue];
}
- (void)resetQueue {
- bit_resetSafeJsonStream(&BITSafeJsonEventsString);
- self.dataItemCount = 0;
+ @synchronized (self) {
+ bit_resetEventBuffer(&BITTelemetryEventBuffer);
+ self.dataItemCount = 0;
+ }
}
#pragma mark - Adding to queue
@@ -92,36 +115,42 @@ - (void)resetQueue {
- (void)enqueueTelemetryItem:(BITTelemetryData *)item {
if (!item) {
- // Case 1: Item is nil: Do not enqueue item and abort operation
+
+ // Item is nil: Do not enqueue item and abort operation.
BITHockeyLogWarning(@"WARNING: TelemetryItem was nil.");
return;
}
+ // First assigning self to weakSelf and then assigning this to strongSelf in the block is not very intuitive, this
+ // blog post explains it very well: https://dhoerl.wordpress.com/2013/04/23/i-finally-figured-out-weakself-and-strongself/
__weak typeof(self) weakSelf = self;
dispatch_async(self.dataItemsOperations, ^{
+
typeof(self) strongSelf = weakSelf;
-
if (strongSelf.isQueueBusy) {
- // Case 2: Channel is in blocked state: Trigger sender, start timer to check after again after a while and abort operation.
+
+ // Case 1: Channel is in blocked state: Trigger sender, start timer to check after again after a while and abort operation.
BITHockeyLogDebug(@"INFO: The channel is saturated. %@ was dropped.", item.debugDescription);
if (![strongSelf timerIsRunning]) {
[strongSelf startTimer];
}
return;
}
-
- // Enqueue item
- NSDictionary *dict = [self dictionaryForTelemetryData:item];
- [strongSelf appendDictionaryToJsonStream:dict];
-
- if (strongSelf.dataItemCount >= self.maxBatchSize) {
- // Case 3: Max batch count has been reached, so write queue to disk and delete all items.
- [strongSelf persistDataItemQueue];
-
- } else if (strongSelf.dataItemCount == 1) {
- // Case 4: It is the first item, let's start the timer.
- if (![strongSelf timerIsRunning]) {
- [strongSelf startTimer];
+
+ // Enqueue item.
+ @synchronized(self) {
+ NSDictionary *dict = [strongSelf dictionaryForTelemetryData:item];
+ [strongSelf appendDictionaryToEventBuffer:dict];
+ if (strongSelf.dataItemCount >= strongSelf.maxBatchSize) {
+
+ // Case 2: Max batch count has been reached, so write queue to disk and delete all items.
+ [strongSelf persistDataItemQueue:&BITTelemetryEventBuffer];
+ } else if (strongSelf.dataItemCount > 0) {
+
+ // Case 3: It is the first item, let's start the timer.
+ if (![strongSelf timerIsRunning]) {
+ [strongSelf startTimer];
+ }
}
}
});
@@ -169,39 +198,72 @@ - (NSString *)serializeDictionaryToJSONString:(NSDictionary *)dictionary {
#pragma mark JSON Stream
-- (void)appendDictionaryToJsonStream:(NSDictionary *)dictionary {
+- (void)appendDictionaryToEventBuffer:(NSDictionary *)dictionary {
if (dictionary) {
NSString *string = [self serializeDictionaryToJSONString:dictionary];
-
+
// Since we can't persist every event right away, we write it to a simple C string.
// This can then be written to disk by a signal handler in case of a crash.
- bit_appendStringToSafeJsonStream(string, &(BITSafeJsonEventsString));
- self.dataItemCount += 1;
+ @synchronized (self) {
+ bit_appendStringToEventBuffer(string, &BITTelemetryEventBuffer);
+ self.dataItemCount += 1;
+ }
}
}
-void bit_appendStringToSafeJsonStream(NSString *string, char **jsonString) {
- if (jsonString == NULL) { return; }
-
- if (!string) { return; }
-
- if (*jsonString == NULL || strlen(*jsonString) == 0) {
- bit_resetSafeJsonStream(jsonString);
+void bit_appendStringToEventBuffer(NSString *string, char **eventBuffer) {
+ if (eventBuffer == NULL) {
+ return;
}
-
- if (string.length == 0) { return; }
-
- char *new_string = NULL;
- // Concatenate old string with new JSON string and add a comma.
- asprintf(&new_string, "%s%.*s\n", *jsonString, (int)MIN(string.length, (NSUInteger)INT_MAX), string.UTF8String);
- free(*jsonString);
- *jsonString = new_string;
+
+ if (!string) {
+ return;
+ }
+
+ if (*eventBuffer == NULL || strlen(*eventBuffer) == 0) {
+ bit_resetEventBuffer(eventBuffer);
+ }
+
+ if (string.length == 0) {
+ return;
+ }
+
+ do {
+ char *newBuffer = NULL;
+ char *previousBuffer = *eventBuffer;
+
+ // Concatenate old string with new JSON string and add a comma.
+ asprintf(&newBuffer, "%s%.*s\n", previousBuffer, (int)MIN(string.length, (NSUInteger)INT_MAX), string.UTF8String);
+
+ // Compare newBuffer and previousBuffer. If they point to the same address, we are safe to use them.
+ if (OSAtomicCompareAndSwapPtr(previousBuffer, newBuffer, (void*)eventBuffer)) {
+
+ // Free the intermediate pointer.
+ free(previousBuffer);
+ return;
+ } else {
+
+ // newBuffer has been changed by another thread.
+ free(newBuffer);
+ }
+ } while (true);
}
-void bit_resetSafeJsonStream(char **string) {
- if (!string) { return; }
- free(*string);
- *string = strdup("");
+void bit_resetEventBuffer(char **eventBuffer) {
+ if (!eventBuffer) { return; }
+
+ char *newEmptyString = NULL;
+ char *prevString = NULL;
+ do {
+ prevString = *eventBuffer;
+ newEmptyString = strdup("");
+
+ // Compare pointers to strings to make sure we are still threadsafe!
+ if (OSAtomicCompareAndSwapPtr(prevString, newEmptyString, (void*)eventBuffer)) {
+ free(prevString);
+ return;
+ }
+ } while(true);
}
#pragma mark - Batching
@@ -214,37 +276,43 @@ - (NSUInteger)maxBatchSize {
}
- (void)invalidateTimer {
- if ([self timerIsRunning]) {
- dispatch_source_cancel((dispatch_source_t)self.timerSource);
- self.timerSource = nil;
+ @synchronized(self) {
+ if (self.timerSource != nil) {
+ dispatch_source_cancel((dispatch_source_t)self.timerSource);
+ self.timerSource = nil;
+ }
}
}
-(BOOL)timerIsRunning {
- return self.timerSource != nil;
+ @synchronized(self) {
+ return self.timerSource != nil;
+ }
}
- (void)startTimer {
- // Reset timer, if it is already running
- if ([self timerIsRunning]) {
+ @synchronized(self) {
+
+ // Reset timer, if it is already running.
[self invalidateTimer];
- }
-
- self.timerSource = dispatch_source_create(DISPATCH_SOURCE_TYPE_TIMER, 0, 0, self.dataItemsOperations);
- dispatch_source_set_timer((dispatch_source_t)self.timerSource, dispatch_walltime(NULL, NSEC_PER_SEC * self.batchInterval), 1ull * NSEC_PER_SEC, 1ull * NSEC_PER_SEC);
- __weak typeof(self) weakSelf = self;
- dispatch_source_set_event_handler((dispatch_source_t)self.timerSource, ^{
- typeof(self) strongSelf = weakSelf;
- if (strongSelf) {
- if (strongSelf.dataItemCount > 0) {
- [strongSelf persistDataItemQueue];
- } else {
- strongSelf.channelBlocked = NO;
+
+ dispatch_source_t timerSource = dispatch_source_create(DISPATCH_SOURCE_TYPE_TIMER, 0, 0, self.dataItemsOperations);
+ dispatch_source_set_timer(timerSource, dispatch_walltime(NULL, NSEC_PER_SEC * self.batchInterval), 1ull * NSEC_PER_SEC, 1ull * NSEC_PER_SEC);
+ __weak typeof(self) weakSelf = self;
+ dispatch_source_set_event_handler(timerSource, ^{
+ typeof(self) strongSelf = weakSelf;
+ if (strongSelf) {
+ if (strongSelf.dataItemCount > 0) {
+ [strongSelf persistDataItemQueue:&BITTelemetryEventBuffer];
+ } else {
+ strongSelf.channelBlocked = NO;
+ }
+ [strongSelf invalidateTimer];
}
- [strongSelf invalidateTimer];
- }
- });
- dispatch_resume((dispatch_source_t)self.timerSource);
+ });
+ dispatch_resume(timerSource);
+ self.timerSource = timerSource;
+ }
}
/**
diff --git a/Classes/Telemetry/BITChannelPrivate.h b/Classes/Telemetry/BITChannelPrivate.h
index eadc03f8..c526b213 100644
--- a/Classes/Telemetry/BITChannelPrivate.h
+++ b/Classes/Telemetry/BITChannelPrivate.h
@@ -67,29 +67,28 @@ FOUNDATION_EXPORT NSString *const BITChannelBlockedNotification;
/**
* Manually trigger the BITChannel to persist all items currently in its data item queue.
*/
-- (void)persistDataItemQueue;
+- (void)persistDataItemQueue:(char *_Nullable*_Nullable)eventBuffer;
/**
* Adds the specified dictionary to the JSON Stream string.
*
* @param dictionary the dictionary object which is to be added to the JSON Stream queue string.
*/
-- (void)appendDictionaryToJsonStream:(NSDictionary *)dictionary;
+- (void)appendDictionaryToEventBuffer:(NSDictionary *)dictionary;
/**
* A C function that serializes a given dictionary to JSON and appends it to a char string
*
- * @param string A string which will be appended to the string.
- * @param jsonStream The C string which the dictionary's JSON representation will be appended to.
+ * @param string The C string which the dictionary's JSON representation will be appended to.
*/
-void bit_appendStringToSafeJsonStream(NSString *string, char *__nonnull*__nonnull jsonStream);
+void bit_appendStringToEventBuffer(NSString *string, char *__nonnull*__nonnull eventBuffer);
/**
- * Reset BITSafeJsonEventsString so we can start appending JSON dictionaries.
+ * Reset the event buffer so we can start appending JSON dictionaries.
*
- * @param jsonStream The string that will be reset.
+ * @param eventBuffer The string that will be reset.
*/
-void bit_resetSafeJsonStream(char *__nonnull*__nonnull jsonStream);
+void bit_resetEventBuffer(char *__nonnull*__nonnull eventBuffer);
/**
* A method which indicates whether the telemetry pipeline is busy and no new data should be enqueued.
diff --git a/Classes/Telemetry/BITMetricsManager.m b/Classes/Telemetry/BITMetricsManager.m
index 0d34b709..07265322 100644
--- a/Classes/Telemetry/BITMetricsManager.m
+++ b/Classes/Telemetry/BITMetricsManager.m
@@ -233,31 +233,39 @@ - (void)trackDataItem:(BITTelemetryData *)dataItem {
#pragma mark - Custom getter
- (BITChannel *)channel {
- if (!_channel) {
- _channel = [[BITChannel alloc] initWithTelemetryContext:self.telemetryContext persistence:self.persistence];
+ @synchronized(self) {
+ if (!_channel) {
+ _channel = [[BITChannel alloc] initWithTelemetryContext:self.telemetryContext persistence:self.persistence];
+ }
+ return _channel;
}
- return _channel;
}
- (BITTelemetryContext *)telemetryContext {
- if (!_telemetryContext) {
- _telemetryContext = [[BITTelemetryContext alloc] initWithAppIdentifier:self.appIdentifier persistence:self.persistence];
+ @synchronized(self) {
+ if (!_telemetryContext) {
+ _telemetryContext = [[BITTelemetryContext alloc] initWithAppIdentifier:self.appIdentifier persistence:self.persistence];
+ }
+ return _telemetryContext;
}
- return _telemetryContext;
}
- (BITPersistence *)persistence {
- if (!_persistence) {
- _persistence = [BITPersistence new];
+ @synchronized(self) {
+ if (!_persistence) {
+ _persistence = [BITPersistence new];
+ }
+ return _persistence;
}
- return _persistence;
}
- (NSUserDefaults *)userDefaults {
- if (!_userDefaults) {
- _userDefaults = [NSUserDefaults standardUserDefaults];
+ @synchronized(self) {
+ if (!_userDefaults) {
+ _userDefaults = [NSUserDefaults standardUserDefaults];
+ }
+ return _userDefaults;
}
- return _userDefaults;
}
@end
diff --git a/Classes/Telemetry/BITTelemetryContext.m b/Classes/Telemetry/BITTelemetryContext.m
index e043ffed..655acd90 100644
--- a/Classes/Telemetry/BITTelemetryContext.m
+++ b/Classes/Telemetry/BITTelemetryContext.m
@@ -352,12 +352,13 @@ - (void)setDeviceType:(NSString *)deviceType {
#pragma mark - Helper
- (NSDictionary *)contextDictionary {
- NSMutableDictionary *contextDictionary = [NSMutableDictionary new];
- [contextDictionary addEntriesFromDictionary:self.tags];
- [contextDictionary addEntriesFromDictionary:[self.session serializeToDictionary]];
- [contextDictionary addEntriesFromDictionary:[self.user serializeToDictionary]];
-
- return contextDictionary;
+ __block NSMutableDictionary *tmp = [NSMutableDictionary new];
+ dispatch_sync(self.operationsQueue, ^{
+ [tmp addEntriesFromDictionary:self.tags];
+ [tmp addEntriesFromDictionary:[self.session serializeToDictionary]];
+ [tmp addEntriesFromDictionary:[self.user serializeToDictionary]];
+ });
+ return tmp;
}
- (NSDictionary *)tags {
diff --git a/Documentation/Guides/Changelog.md b/Documentation/Guides/Changelog.md
index 596fe168..2783b91f 100644
--- a/Documentation/Guides/Changelog.md
+++ b/Documentation/Guides/Changelog.md
@@ -1,3 +1,10 @@
+## 5.1.0
+
+This version contains improvements around concurrency.
+
+- [BUGFIX/IMPROVEMENT] Improve concurrency for HockeyApp Metrics. [#134](https://github.com/bitstadium/HockeySDK-Mac/pull/134) [#135](https://github.com/bitstadium/HockeySDK-Mac/pull/135)
+- [IMPROVEMENT] Always update the feedback UI on the main thread. [#136](https://github.com/bitstadium/HockeySDK-Mac/pull/136)
+
## 5.0.0
- [IMPROVEMENT] Metrics can be enabled after they have been disabled. [#124](https://github.com/bitstadium/HockeySDK-Mac/pull/124)
diff --git a/Documentation/Guides/Installation & Setup.md b/Documentation/Guides/Installation & Setup.md
index 1095f105..fe19e7de 100644
--- a/Documentation/Guides/Installation & Setup.md
+++ b/Documentation/Guides/Installation & Setup.md
@@ -2,9 +2,9 @@
[![Carthage compatible](https://img.shields.io/badge/Carthage-compatible-4BC51D.svg?style=flat)](https://github.com/Carthage/Carthage)
[![Slack Status](https://slack.hockeyapp.net/badge.svg)](https://slack.hockeyapp.net)
-## Version 5.0.0
+## Version 5.1.0
-- [Changelog](http://www.hockeyapp.net/help/sdk/mac/5.0.0/docs/docs/Changelog.html)
+- [Changelog](http://www.hockeyapp.net/help/sdk/mac/5.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.
@@ -534,7 +534,7 @@ BITHockeyManager.shared().start()
## 4. Documentation
-Our documentation can be found on [HockeyApp](https://www.hockeyapp.net/help/sdk/mac/5.0.0/index.html).
+Our documentation can be found on [HockeyApp](https://www.hockeyapp.net/help/sdk/mac/5.1.0/index.html).
## 5.Troubleshooting
@@ -547,7 +547,7 @@ Make sure that the apps build setting has `LD_RUNPATH_SEARCH_PATHS` set to `@exe
Make sure there is no `All Exceptions` breakpoint active or limit it to `Objective-C` only and exclude `C++`.
-3. Feature are not working as expected
+3. Features are not working as expected
Enable debug output to the console to see additional information from the SDK initializing the modules, sending and receiving network requests and more by adding the following code before calling `startManager`:
diff --git a/Documentation/HockeySDK/.jazzy.yaml b/Documentation/HockeySDK/.jazzy.yaml
index da72a536..de86c600 100644
--- a/Documentation/HockeySDK/.jazzy.yaml
+++ b/Documentation/HockeySDK/.jazzy.yaml
@@ -3,7 +3,7 @@ clean: true
sdk: macosx
module: HockeySDK
-module_version: 5.0.0
+module_version: 5.1.0
author: Microsoft Corp
author_url: https://www.microsoft.com
diff --git a/HockeySDK-Mac.podspec b/HockeySDK-Mac.podspec
index d392db50..7c75238b 100644
--- a/HockeySDK-Mac.podspec
+++ b/HockeySDK-Mac.podspec
@@ -1,6 +1,6 @@
Pod::Spec.new do |s|
s.name = 'HockeySDK-Mac'
- s.version = '5.0.0'
+ s.version = '5.1.0'
s.summary = 'Collect live crash reports, get feedback from your users, distribute your betas, and get usage data.'
s.description = <<-DESC
diff --git a/README.md b/README.md
index 413f4ef8..b66e44bb 100644
--- a/README.md
+++ b/README.md
@@ -3,7 +3,7 @@
[![Slack Status](https://slack.hockeyapp.net/badge.svg)](https://slack.hockeyapp.net)
-# Version 5.0.0
+# Version 5.1.0
## Introduction
@@ -21,13 +21,13 @@ The following feature is currently supported:
## 1. Setup
-It is super easy to use HockeyApp in your macOS app. Have a look at our [documentation](https://www.hockeyapp.net/help/sdk/mac/5.0.0/installation--setup.html) and onboard your app within minutes.
+It is super easy to use HockeyApp in your macOS app. Have a look at our [documentation](https://www.hockeyapp.net/help/sdk/mac/5.1.0/installation--setup.html) and onboard your app within minutes.
## 2. Documentation
-Please visit [our landing page](https://www.hockeyapp.net/help/sdk/mac/5.0.0/index.html) as a starting point for all of our documentation.
+Please visit [our landing page](https://www.hockeyapp.net/help/sdk/mac/5.1.0/index.html) as a starting point for all of our documentation.
-Please check out our [changelog](http://www.hockeyapp.net/help/sdk/mac/5.0.0/changelog.html), as well as our [troubleshooting section](https://www.hockeyapp.net/help/sdk/mac/5.0.0/installation--setup.html#troubleshooting).
+Please check out our [changelog](http://www.hockeyapp.net/help/sdk/mac/5.1.0/changelog.html), as well as our [troubleshooting section](https://www.hockeyapp.net/help/sdk/mac/5.1.0/installation--setup.html#troubleshooting).
## 3. Contributing
@@ -51,4 +51,4 @@ You must sign a [Contributor License Agreement](https://cla.microsoft.com/) befo
## 4. Contact
-If you have further questions or are running into trouble that cannot be resolved by any of the steps [in our troubleshooting section](hhttps://www.hockeyapp.net/help/sdk/mac/5.0.0/installation--setup.html#troubleshooting), feel free to open an issue here, contact us at [support@hockeyapp.net](mailto:support@hockeyapp.net) or join our [Slack](https://slack.hockeyapp.net).
+If you have further questions or are running into trouble that cannot be resolved by any of the steps [in our troubleshooting section](hhttps://www.hockeyapp.net/help/sdk/mac/5.1.0/installation--setup.html#troubleshooting), feel free to open an issue here, contact us at [support@hockeyapp.net](mailto:support@hockeyapp.net) or join our [Slack](https://slack.hockeyapp.net).
diff --git a/Support/HockeySDK.xcodeproj/project.pbxproj b/Support/HockeySDK.xcodeproj/project.pbxproj
index 8c0b8812..c08f0602 100644
--- a/Support/HockeySDK.xcodeproj/project.pbxproj
+++ b/Support/HockeySDK.xcodeproj/project.pbxproj
@@ -1003,7 +1003,7 @@
);
runOnlyForDeploymentPostprocessing = 0;
shellPath = /bin/sh;
- shellScript = "#cd -> https://github.com/realm/jazzy/issues/772\n#--no-download-badge -> https://github.com/realm/jazzy/issues/824\ncd ../Documentation/HockeySDK\njazzy --no-download-badge\n";
+ shellScript = "#cd -> https://github.com/realm/jazzy/issues/772\ncd ../Documentation/HockeySDK\njazzy\n";
};
/* End PBXShellScriptBuildPhase section */
diff --git a/Support/buildnumber.xcconfig b/Support/buildnumber.xcconfig
index 0b2e5818..7c66cf76 100644
--- a/Support/buildnumber.xcconfig
+++ b/Support/buildnumber.xcconfig
@@ -1,5 +1,5 @@
#include "HockeySDK.xcconfig"
-BUILD_NUMBER = 63
-VERSION_STRING = 5.0.0
+BUILD_NUMBER = 64
+VERSION_STRING = 5.1.0
GCC_PREPROCESSOR_DEFINITIONS = $(inherited) BITHOCKEY_VERSION="@\""$(VERSION_STRING)"\"" BITHOCKEY_BUILD="@\""$(BUILD_NUMBER)"\"" BITHOCKEY_C_VERSION="\""$(VERSION_STRING)"\"" BITHOCKEY_C_BUILD="\""$(BUILD_NUMBER)"\"" $(XCODEBUILD_GCC_PREPROCESSOR_DEFINITIONS)
diff --git a/Vendor/CrashReporter/Headers/PLCrashNamespace.h b/Vendor/CrashReporter/Headers/PLCrashNamespace.h
index eb1a4630..dba0200e 100644
--- a/Vendor/CrashReporter/Headers/PLCrashNamespace.h
+++ b/Vendor/CrashReporter/Headers/PLCrashNamespace.h
@@ -35,7 +35,7 @@
* This may be used to avoid symbol conflicts between multiple libraries
* that may both incorporate PLCrashReporter.
*/
- #define PLCRASHREPORTER_PREFIX BIT
+#define PLCRASHREPORTER_PREFIX BIT
// We need two extra layers of indirection to make CPP substitute
diff --git a/Vendor/CrashReporter/Headers/PLCrashReportSystemInfo.h b/Vendor/CrashReporter/Headers/PLCrashReportSystemInfo.h
index 9ab6ef1b..8ec71e63 100644
--- a/Vendor/CrashReporter/Headers/PLCrashReportSystemInfo.h
+++ b/Vendor/CrashReporter/Headers/PLCrashReportSystemInfo.h
@@ -49,11 +49,13 @@ typedef enum {
/** Unknown operating system */
PLCrashReportOperatingSystemUnknown = 3,
- /** tvOS */
- PLCrashReportOperatingSystemtvOS = 4,
+ /** Apple tvOS */
+ PLCrashReportOperatingSystemAppleTVOS = 4,
} PLCrashReportOperatingSystem;
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wdocumentation-deprecated-sync"
/**
* @ingroup constants
*
@@ -95,7 +97,7 @@ typedef enum {
/** Unknown */
PLCrashReportArchitectureUnknown = 6
} PLCrashReportArchitecture;
-
+#pragma clang diagnostic pop
extern PLCrashReportOperatingSystem PLCrashReportHostOperatingSystem;
extern PLCrashReportArchitecture PLCrashReportHostArchitecture;
@@ -138,10 +140,13 @@ extern PLCrashReportArchitecture PLCrashReportHostArchitecture;
/** The operating system's build identifier (eg, 10J869). This may be unavailable, and this property will be nil. */
@property(nonatomic, readonly) NSString *operatingSystemBuild;
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wdocumentation-deprecated-sync"
/** Architecture. @deprecated The architecture value has been deprecated in v1.1 and later crash reports. All new reports
* include the CPU type as part of the crash report's machine info structure, using the PLCrashReportProcessorInfo
* extensible encoding. */
@property(nonatomic, readonly) PLCrashReportArchitecture architecture;
+#pragma clang diagnostic pop
/** Date and time that the crash report was generated. This may be unavailable, and this property will be nil. */
@property(nonatomic, readonly) NSDate *timestamp;
diff --git a/Vendor/CrashReporter/Headers/PLCrashReporter.h b/Vendor/CrashReporter/Headers/PLCrashReporter.h
index 455c417a..68bf5a0f 100644
--- a/Vendor/CrashReporter/Headers/PLCrashReporter.h
+++ b/Vendor/CrashReporter/Headers/PLCrashReporter.h
@@ -44,7 +44,8 @@
* @param uap The crash's threads context.
* @param context The API client's supplied context value.
*
- * @sa @ref async_safety
+ * @sa async_satefy
+ * @ref async_safety
* @sa PLCrashReporter::setPostCrashCallbacks:
*/
typedef void (*PLCrashReporterPostCrashSignalCallback)(siginfo_t *info, ucontext_t *uap, void *context);
@@ -55,7 +56,8 @@ typedef void (*PLCrashReporterPostCrashSignalCallback)(siginfo_t *info, ucontext
* This structure contains callbacks supported by PLCrashReporter to allow the host application to perform
* additional tasks prior to program termination after a crash has occured.
*
- * @sa @ref async_safety
+ * @sa async_satefy
+ * @ref async_safety
*/
typedef struct PLCrashReporterCallbacks {
/** The version number of this structure. If not one of the defined version numbers for this type, the behavior
diff --git a/Vendor/CrashReporter/Headers/PLCrashReporterConfig.h b/Vendor/CrashReporter/Headers/PLCrashReporterConfig.h
index 82f0bd9c..15c3a24b 100644
--- a/Vendor/CrashReporter/Headers/PLCrashReporterConfig.h
+++ b/Vendor/CrashReporter/Headers/PLCrashReporterConfig.h
@@ -146,6 +146,12 @@ typedef NS_OPTIONS(NSUInteger, PLCrashReporterSymbolicationStrategy) {
/** The configured symbolication strategy. */
PLCrashReporterSymbolicationStrategy _symbolicationStrategy;
+
+ /**
+ * Flag indicating if the uncaughtExceptionHandler should be initialized or not. It usually is, except in a
+ * Xamarin environment.
+ */
+ BOOL _shouldRegisterUncaughtExceptionHandler;
}
+ (instancetype) defaultConfiguration;
@@ -154,12 +160,19 @@ typedef NS_OPTIONS(NSUInteger, PLCrashReporterSymbolicationStrategy) {
- (instancetype) initWithSignalHandlerType: (PLCrashReporterSignalHandlerType) signalHandlerType
symbolicationStrategy: (PLCrashReporterSymbolicationStrategy) symbolicationStrategy;
+- (instancetype) initWithSignalHandlerType: (PLCrashReporterSignalHandlerType) signalHandlerType
+ symbolicationStrategy: (PLCrashReporterSymbolicationStrategy) symbolicationStrategy
+ shouldRegisterUncaughtExceptionHandler: (BOOL) shouldRegisterUncaughtExceptionHandler;
+
+
/** The configured signal handler type. */
@property(nonatomic, readonly) PLCrashReporterSignalHandlerType signalHandlerType;
/** The configured symbolication strategy. */
@property(nonatomic, readonly) PLCrashReporterSymbolicationStrategy symbolicationStrategy;
+/** Should PLCrashReporter regiser an uncaught exception handler? This is entended to be used in Xamarin apps */
+@property(nonatomic, readonly) BOOL shouldRegisterUncaughtExceptionHandler;
@end
diff --git a/Vendor/CrashReporter/libCrashReporter-MacOSX-Static.a b/Vendor/CrashReporter/libCrashReporter-MacOSX-Static.a
index 5d48ee0f..c1d43cfd 100644
Binary files a/Vendor/CrashReporter/libCrashReporter-MacOSX-Static.a and b/Vendor/CrashReporter/libCrashReporter-MacOSX-Static.a differ