Swiftgram/Classes/BITChannel.m

195 lines
5.3 KiB
Objective-C

#import "BITChannel.h"
#if HOCKEYSDK_FEATURE_TELEMETRY
#import "BITHockeyHelper.h"
#import "HockeySDK.h"
#import "BITTelemetryContext.h"
#import "BITTelemetryData.h"
#import "HockeySDKPrivate.h"
#import "BITOrderedDictionary.h"
#import "BITEnvelope.h"
#import "BITData.h"
#import "BITDevice.h"
#import "BITPersistencePrivate.h"
static char *const BITDataItemsOperationsQueue = "net.hockeyapp.senderQueue";
char *BITSafeJsonEventsString;
NSInteger const defaultMaxBatchCount = 1;
static NSInteger const schemaVersion = 2;
@implementation BITChannel
@synthesize persistence = _persistence;
#pragma mark - Initialisation
- (instancetype)init {
if(self = [super init]) {
bit_resetSafeJsonStream(&BITSafeJsonEventsString);
_dataItemCount = 0;
dispatch_queue_t serialQueue = dispatch_queue_create(BITDataItemsOperationsQueue, DISPATCH_QUEUE_SERIAL);
_dataItemsOperations = serialQueue;
}
return self;
}
- (instancetype)initWithTelemetryContext:(BITTelemetryContext *)telemetryContext persistence:(BITPersistence *) persistence {
if(self = [self init]) {
_telemetryContext = telemetryContext;
_persistence = persistence;
}
return self;
}
#pragma mark - Queue management
- (BOOL)isQueueBusy{
[self.persistence isFreeSpaceAvailable];
return true;
}
- (void)persistDataItemQueue {
if(!BITSafeJsonEventsString || strlen(BITSafeJsonEventsString) == 0) {
return;
}
NSData *bundle = [NSData dataWithBytes:BITSafeJsonEventsString length:strlen(BITSafeJsonEventsString)];
[self.persistence persistBundle:bundle];
// Reset both, the async-signal-safe and item counter.
[self resetQueue];
}
- (void)resetQueue {
bit_resetSafeJsonStream(&BITSafeJsonEventsString);
_dataItemCount = 0;
}
#pragma mark - Adding to queue
- (void)enqueueTelemetryItem:(BITTelemetryData *)item {
if(item) {
BITOrderedDictionary *dict = [self dictionaryForTelemetryData:item];
__weak typeof(self) weakSelf = self;
dispatch_async(self.dataItemsOperations, ^{
typeof(self) strongSelf = weakSelf;
// Enqueue item
[strongSelf appendDictionaryToJsonStream:dict];
if(strongSelf->_dataItemCount >= self.maxBatchCount) {
// Max batch count has been reached, so write queue to disk and delete all items.
[strongSelf persistDataItemQueue];
}
});
}
}
#pragma mark - Envelope telemerty items
- (BITOrderedDictionary *)dictionaryForTelemetryData:(BITTelemetryData *) telemetryData {
BITEnvelope *envelope = [self envelopeForTelemetryData:telemetryData];
BITOrderedDictionary *dict = [envelope serializeToDictionary];
return dict;
}
- (BITEnvelope *)envelopeForTelemetryData:(BITTelemetryData *)telemetryData {
telemetryData.version = @(schemaVersion);
BITData *data = [BITData new];
data.baseData = telemetryData;
data.baseType = telemetryData.dataTypeName;
BITEnvelope *envelope = [BITEnvelope new];
envelope.appId = bit_mainBundleIdentifier();
envelope.appVer = _telemetryContext.application.version;
envelope.time = bit_utcDateString([NSDate date]);
envelope.iKey = _telemetryContext.appIdentifier;
BITDevice *deviceContext = _telemetryContext.device;
if (deviceContext.deviceId) {
envelope.deviceId = deviceContext.deviceId;
}
if (deviceContext.os) {
envelope.os = deviceContext.os;
}
if (deviceContext.osVersion) {
envelope.osVer = deviceContext.osVersion;
}
envelope.tags = _telemetryContext.contextDictionary;
envelope.data = data;
envelope.name = telemetryData.envelopeTypeName;
return envelope;
}
#pragma mark - Serialization Helper
- (NSString *)serializeDictionaryToJSONString:(BITOrderedDictionary *)dictionary {
NSError *error;
NSData *data = [NSJSONSerialization dataWithJSONObject:dictionary options:(NSJSONWritingOptions)0 error:&error];
if (!data) {
BITHockeyLog(@"ERROR: JSONSerialization error: %@", error.localizedDescription);
return @"{}";
} else {
return [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding];
}
}
#pragma mark JSON Stream
- (void)appendDictionaryToJsonStream:(BITOrderedDictionary *)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));
_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);
}
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;
}
void bit_resetSafeJsonStream(char **string) {
if (!string) { return; }
free(*string);
*string = strdup("");
}
#pragma mark - Batching
- (NSInteger)maxBatchCount {
if(_maxBatchCount <= 0){
return defaultMaxBatchCount;
}
return _maxBatchCount;
}
@end
#endif /* HOCKEYSDK_FEATURE_TELEMETRY */