mirror of
https://github.com/Swiftgram/Telegram-iOS.git
synced 2025-11-29 11:25:38 +00:00
Add sender
This commit is contained in:
parent
60112daa2e
commit
011972245d
115
Classes/BITSender.h
Normal file
115
Classes/BITSender.h
Normal file
@ -0,0 +1,115 @@
|
|||||||
|
#import <Foundation/Foundation.h>
|
||||||
|
#import "HockeySDK.h"
|
||||||
|
|
||||||
|
#if HOCKEYSDK_FEATURE_TELEMETRY
|
||||||
|
|
||||||
|
#import "HockeySDKNullability.h"
|
||||||
|
@class BITPersistence;
|
||||||
|
|
||||||
|
NS_ASSUME_NONNULL_BEGIN
|
||||||
|
/**
|
||||||
|
* Utility class that's responsible for sending a bundle of data to the server
|
||||||
|
*/
|
||||||
|
@interface BITSender : NSObject
|
||||||
|
|
||||||
|
///-----------------------------------------------------------------------------
|
||||||
|
/// @name Initialize instance
|
||||||
|
///-----------------------------------------------------------------------------
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Initializes a sender instance with a given persistence object.
|
||||||
|
*
|
||||||
|
* @param persistence used for loading files before sending them out
|
||||||
|
* @param serverURL the endpoint URL for telemetry data
|
||||||
|
* @return an initialized sender instance
|
||||||
|
*/
|
||||||
|
- (instancetype)initWithPersistence:(BITPersistence *)persistence serverURL:(NSURL *)serverURL;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Access to the internal operation queue
|
||||||
|
*/
|
||||||
|
@property (nonatomic, strong) NSOperationQueue *operationQueue;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A queue which is used to handle BITHTTPOperation completion blocks.
|
||||||
|
*/
|
||||||
|
@property (nonatomic, strong) dispatch_queue_t senderQueue;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The endpoint url of the telemetry server.
|
||||||
|
*/
|
||||||
|
@property (nonatomic, copy) NSString *endpointPath;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The max number of request that can run at a time.
|
||||||
|
*/
|
||||||
|
@property NSUInteger maxRequestCount;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The number of requests that are currently running.
|
||||||
|
*/
|
||||||
|
@property NSUInteger runningRequestsCount;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* BaseURL to which relative paths are appended.
|
||||||
|
*/
|
||||||
|
@property (nonatomic, strong, readonly) NSURL *serverURL;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The persistence instance used for loading files before sending them out.
|
||||||
|
*/
|
||||||
|
@property (nonatomic, strong, readonly) BITPersistence *persistence;
|
||||||
|
|
||||||
|
///-----------------------------------------------------------------------------
|
||||||
|
/// @name Sending data
|
||||||
|
///-----------------------------------------------------------------------------
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Triggers sending the saved data. Does nothing if nothing has been persisted, yet. This method should be called by BITTelemetryManager on app start.
|
||||||
|
*/
|
||||||
|
- (void)sendSavedData;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a HTTP operation and puts it to the queue.
|
||||||
|
*
|
||||||
|
* @param request a request for sending a data object to the telemetry server
|
||||||
|
* @param path path to the file which should be sent
|
||||||
|
* @param urlSessionSupported a flag which determines whether to use NSURLConnection or NSURLSession for sending out data
|
||||||
|
*/
|
||||||
|
- (void)sendRequest:(NSURLRequest * __nonnull)request path:(NSString * __nonnull)path urlSessionSupported:(BOOL)isUrlSessionSupported;
|
||||||
|
|
||||||
|
///-----------------------------------------------------------------------------
|
||||||
|
/// @name Helper
|
||||||
|
///-----------------------------------------------------------------------------
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns a request for sending data to the telemetry sender.
|
||||||
|
*
|
||||||
|
* @param data the data which should be sent
|
||||||
|
*
|
||||||
|
* @return a request which contains the given data
|
||||||
|
*/
|
||||||
|
- (NSURLRequest *)requestForData:(NSData *)data withContentType:(NSString *)contentType;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns if data should be deleted based on a given status code.
|
||||||
|
*
|
||||||
|
* @param statusCode the status code which is part of the response object
|
||||||
|
*
|
||||||
|
* @return YES if data should be deleted, NO if the payload should be sent at a later time again.
|
||||||
|
*/
|
||||||
|
- (BOOL)shouldDeleteDataWithStatusCode:(NSInteger)statusCode;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This method tries to detect whether the given data object is regular JSON or JSON Stream and returns the appropriate HTTP content type.
|
||||||
|
*
|
||||||
|
* @param data The data object whose content type should be returned.
|
||||||
|
*
|
||||||
|
* @returns "application/json" if the data is regular JSON or "application/x-json-stream" if it is JSON Stream. Defaults to "application/json".
|
||||||
|
*/
|
||||||
|
- (NSString *)contentTypeForData:(NSData *)data;
|
||||||
|
|
||||||
|
@end
|
||||||
|
NS_ASSUME_NONNULL_END
|
||||||
|
|
||||||
|
#endif /* HOCKEYSDK_FEATURE_TELEMETRY */
|
||||||
186
Classes/BITSender.m
Normal file
186
Classes/BITSender.m
Normal file
@ -0,0 +1,186 @@
|
|||||||
|
#import "BITSender.h"
|
||||||
|
#import "BITPersistencePrivate.h"
|
||||||
|
#import "BITGZIP.h"
|
||||||
|
#import "HockeySDKPrivate.h"
|
||||||
|
#import "BITHTTPOperation.h"
|
||||||
|
#import "BITHockeyHelper.h"
|
||||||
|
|
||||||
|
static char const *kPersistenceQueueString = "net.hockeyapp.senderQueue";
|
||||||
|
static NSUInteger const defaultRequestLimit = 10;
|
||||||
|
|
||||||
|
@implementation BITSender
|
||||||
|
|
||||||
|
@synthesize runningRequestsCount = _runningRequestsCount;
|
||||||
|
@synthesize persistence = _persistence;
|
||||||
|
|
||||||
|
#pragma mark - Initialize instance
|
||||||
|
|
||||||
|
- (instancetype)initWithPersistence:(BITPersistence *)persistence serverURL:(NSURL *)serverURL {
|
||||||
|
if ((self = [super init])) {
|
||||||
|
_senderQueue = dispatch_queue_create(kPersistenceQueueString, DISPATCH_QUEUE_CONCURRENT);
|
||||||
|
_maxRequestCount = defaultRequestLimit;
|
||||||
|
_serverURL = serverURL;
|
||||||
|
_persistence = persistence;
|
||||||
|
[self registerObservers];
|
||||||
|
}
|
||||||
|
return self;
|
||||||
|
}
|
||||||
|
|
||||||
|
#pragma mark - Handle persistence events
|
||||||
|
|
||||||
|
- (void)registerObservers{
|
||||||
|
NSNotificationCenter *center = [NSNotificationCenter defaultCenter];
|
||||||
|
__weak typeof(self) weakSelf = self;
|
||||||
|
[center addObserverForName:BITPersistenceSuccessNotification
|
||||||
|
object:nil
|
||||||
|
queue:nil
|
||||||
|
usingBlock:^(NSNotification *notification) {
|
||||||
|
typeof(self) strongSelf = weakSelf;
|
||||||
|
[strongSelf sendSavedData];
|
||||||
|
}];
|
||||||
|
}
|
||||||
|
|
||||||
|
#pragma mark - Sending
|
||||||
|
|
||||||
|
- (void)sendSavedData{
|
||||||
|
@synchronized(self){
|
||||||
|
if(_runningRequestsCount < _maxRequestCount){
|
||||||
|
_runningRequestsCount++;
|
||||||
|
}else{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
__weak typeof(self) weakSelf = self;
|
||||||
|
dispatch_async(self.senderQueue, ^{
|
||||||
|
typeof(self) strongSelf = weakSelf;
|
||||||
|
NSString *path = [self.persistence requestNextPath];
|
||||||
|
NSData *data = [self.persistence dataAtPath:path];
|
||||||
|
[strongSelf sendData:data withPath:path];
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
- (void)sendData:(NSData * __nonnull)data withPath:(NSString * __nonnull)path {
|
||||||
|
if(data && data.length > 0) {
|
||||||
|
NSString *contentType = [self contentTypeForData:data];
|
||||||
|
|
||||||
|
NSData *gzippedData = [data gzippedData];
|
||||||
|
NSURLRequest *request = [self requestForData:gzippedData withContentType:contentType];
|
||||||
|
id nsurlsessionClass = NSClassFromString(@"NSURLSessionUploadTask");
|
||||||
|
BOOL isUrlSessionSupported = (nsurlsessionClass && !bit_isRunningInAppExtension());
|
||||||
|
|
||||||
|
[self sendRequest:request path:path urlSessionSupported:isUrlSessionSupported];
|
||||||
|
} else {
|
||||||
|
self.runningRequestsCount -= 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
- (void)sendRequest:(NSURLRequest * __nonnull)request path:(NSString * __nonnull)path urlSessionSupported:(BOOL)isUrlSessionSupported{
|
||||||
|
if(!path || !request) return;
|
||||||
|
__weak typeof(self) weakSelf = self;
|
||||||
|
|
||||||
|
if(!isUrlSessionSupported) {
|
||||||
|
NSURLSessionConfiguration *sessionConfiguration = [NSURLSessionConfiguration defaultSessionConfiguration];
|
||||||
|
NSURLSession *session = [NSURLSession sessionWithConfiguration:sessionConfiguration];
|
||||||
|
|
||||||
|
NSURLSessionDataTask *task = [session dataTaskWithRequest:request
|
||||||
|
completionHandler: ^(NSData *data, NSURLResponse *response, NSError *error) {
|
||||||
|
typeof (self) strongSelf = weakSelf;
|
||||||
|
NSHTTPURLResponse *httpResponse = (NSHTTPURLResponse *)response;
|
||||||
|
NSInteger statusCode = httpResponse.statusCode;
|
||||||
|
[strongSelf handleResponseWithStatusCode:statusCode responseData:data filePath:path error:error];
|
||||||
|
}];
|
||||||
|
[task resume];
|
||||||
|
}else{
|
||||||
|
BITHTTPOperation *operation = [BITHTTPOperation operationWithRequest:request];
|
||||||
|
[operation setCompletion:^(BITHTTPOperation *operation, NSData *responseData, NSError *error) {
|
||||||
|
typeof(self) strongSelf = weakSelf;
|
||||||
|
NSInteger statusCode = [operation.response statusCode];
|
||||||
|
[strongSelf handleResponseWithStatusCode:statusCode responseData:responseData filePath:path error:error];
|
||||||
|
}];
|
||||||
|
|
||||||
|
[self.operationQueue addOperation:operation];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
- (void)handleResponseWithStatusCode:(NSInteger)statusCode responseData:(NSData *)responseData filePath:(NSString *)filePath error:(NSError *)error{
|
||||||
|
self.runningRequestsCount -= 1;
|
||||||
|
|
||||||
|
if(responseData && [self shouldDeleteDataWithStatusCode:statusCode]) {
|
||||||
|
//we delete data that was either sent successfully or if we have a non-recoverable error
|
||||||
|
BITHockeyLog(@"Sent data with status code: %ld", (long) statusCode);
|
||||||
|
BITHockeyLog(@"Response data:\n%@", [NSJSONSerialization JSONObjectWithData:responseData options:0 error:nil]);
|
||||||
|
[self.persistence deleteFileAtPath:filePath];
|
||||||
|
[self sendSavedData];
|
||||||
|
} else {
|
||||||
|
BITHockeyLog(@"Sending telemetry data failed");
|
||||||
|
BITHockeyLog(@"Error description: %@", error.localizedDescription);
|
||||||
|
[self.persistence giveBackRequestedPath:filePath];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#pragma mark - Helper
|
||||||
|
|
||||||
|
- (NSURLRequest *)requestForData:(NSData * __nonnull)data withContentType:(NSString * __nonnull)contentType {
|
||||||
|
|
||||||
|
NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:self.serverURL];
|
||||||
|
request.HTTPMethod = @"POST";
|
||||||
|
|
||||||
|
request.HTTPBody = data;
|
||||||
|
request.cachePolicy = NSURLRequestReloadIgnoringLocalCacheData;
|
||||||
|
|
||||||
|
NSDictionary *headers = @{@"Charset": @"UTF-8",
|
||||||
|
@"Content-Encoding": @"gzip",
|
||||||
|
@"Content-Type": contentType,
|
||||||
|
@"Accept-Encoding": @"gzip"};
|
||||||
|
[request setAllHTTPHeaderFields:headers];
|
||||||
|
|
||||||
|
return request;
|
||||||
|
}
|
||||||
|
|
||||||
|
//some status codes represent recoverable error codes
|
||||||
|
//we try sending again some point later
|
||||||
|
- (BOOL)shouldDeleteDataWithStatusCode:(NSInteger)statusCode {
|
||||||
|
NSArray *recoverableStatusCodes = @[@429, @408, @500, @503, @511];
|
||||||
|
|
||||||
|
return ![recoverableStatusCodes containsObject:@(statusCode)];
|
||||||
|
}
|
||||||
|
|
||||||
|
- (NSString *)contentTypeForData:(NSData *)data {
|
||||||
|
NSString *contentType;
|
||||||
|
static const uint8_t LINEBREAK_SIGNATURE = (0x0a);
|
||||||
|
UInt8 lastByte = 0;
|
||||||
|
if (data && data.length > 0) {
|
||||||
|
[data getBytes:&lastByte range:NSMakeRange(data.length-1, 1)];
|
||||||
|
}
|
||||||
|
|
||||||
|
if (data && (data.length > sizeof(uint8_t)) && (lastByte == LINEBREAK_SIGNATURE)) {
|
||||||
|
contentType = @"application/x-json-stream";
|
||||||
|
} else {
|
||||||
|
contentType = @"application/json";
|
||||||
|
}
|
||||||
|
return contentType;
|
||||||
|
}
|
||||||
|
|
||||||
|
#pragma mark - Getter/Setter
|
||||||
|
|
||||||
|
- (NSOperationQueue *)operationQueue {
|
||||||
|
if(nil == _operationQueue) {
|
||||||
|
_operationQueue = [[NSOperationQueue alloc] init];
|
||||||
|
_operationQueue.maxConcurrentOperationCount = defaultRequestLimit;
|
||||||
|
}
|
||||||
|
return _operationQueue;
|
||||||
|
}
|
||||||
|
|
||||||
|
- (NSUInteger)runningRequestsCount {
|
||||||
|
@synchronized(self) {
|
||||||
|
return _runningRequestsCount;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
- (void)setRunningRequestsCount:(NSUInteger)runningRequestsCount {
|
||||||
|
@synchronized(self) {
|
||||||
|
_runningRequestsCount = runningRequestsCount;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@end
|
||||||
@ -69,6 +69,10 @@
|
|||||||
1B460AB71B8E72FD0000C344 /* BITTelemetryManagerTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 1B460AB31B8E72FD0000C344 /* BITTelemetryManagerTests.m */; };
|
1B460AB71B8E72FD0000C344 /* BITTelemetryManagerTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 1B460AB31B8E72FD0000C344 /* BITTelemetryManagerTests.m */; };
|
||||||
1B50A9161B9FA82800ADECD1 /* BITTelemetryContextTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 1B50A9151B9FA82800ADECD1 /* BITTelemetryContextTests.m */; };
|
1B50A9161B9FA82800ADECD1 /* BITTelemetryContextTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 1B50A9151B9FA82800ADECD1 /* BITTelemetryContextTests.m */; };
|
||||||
1B50A9171B9FA82800ADECD1 /* BITTelemetryContextTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 1B50A9151B9FA82800ADECD1 /* BITTelemetryContextTests.m */; };
|
1B50A9171B9FA82800ADECD1 /* BITTelemetryContextTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 1B50A9151B9FA82800ADECD1 /* BITTelemetryContextTests.m */; };
|
||||||
|
1B57ABE31BA09B4C0040F078 /* BITSender.h in Headers */ = {isa = PBXBuildFile; fileRef = 1B57ABE01BA09B4C0040F078 /* BITSender.h */; };
|
||||||
|
1B57ABE41BA09B4C0040F078 /* BITSender.h in Headers */ = {isa = PBXBuildFile; fileRef = 1B57ABE01BA09B4C0040F078 /* BITSender.h */; };
|
||||||
|
1B57ABE51BA09B4C0040F078 /* BITSender.m in Sources */ = {isa = PBXBuildFile; fileRef = 1B57ABE11BA09B4C0040F078 /* BITSender.m */; };
|
||||||
|
1B57ABE61BA09B4C0040F078 /* BITSender.m in Sources */ = {isa = PBXBuildFile; fileRef = 1B57ABE11BA09B4C0040F078 /* BITSender.m */; };
|
||||||
1B57ABEC1BA0C8850040F078 /* BITCategoryContainer.h in Headers */ = {isa = PBXBuildFile; fileRef = 1B57ABE91BA0C8850040F078 /* BITCategoryContainer.h */; };
|
1B57ABEC1BA0C8850040F078 /* BITCategoryContainer.h in Headers */ = {isa = PBXBuildFile; fileRef = 1B57ABE91BA0C8850040F078 /* BITCategoryContainer.h */; };
|
||||||
1B57ABED1BA0C8850040F078 /* BITCategoryContainer.h in Headers */ = {isa = PBXBuildFile; fileRef = 1B57ABE91BA0C8850040F078 /* BITCategoryContainer.h */; };
|
1B57ABED1BA0C8850040F078 /* BITCategoryContainer.h in Headers */ = {isa = PBXBuildFile; fileRef = 1B57ABE91BA0C8850040F078 /* BITCategoryContainer.h */; };
|
||||||
1B57ABEE1BA0C8850040F078 /* BITCategoryContainer.m in Sources */ = {isa = PBXBuildFile; fileRef = 1B57ABEA1BA0C8850040F078 /* BITCategoryContainer.m */; };
|
1B57ABEE1BA0C8850040F078 /* BITCategoryContainer.m in Sources */ = {isa = PBXBuildFile; fileRef = 1B57ABEA1BA0C8850040F078 /* BITCategoryContainer.m */; };
|
||||||
@ -511,6 +515,8 @@
|
|||||||
1B460AB01B8E64AF0000C344 /* libOCMock.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; path = libOCMock.a; sourceTree = "<group>"; };
|
1B460AB01B8E64AF0000C344 /* libOCMock.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; path = libOCMock.a; sourceTree = "<group>"; };
|
||||||
1B460AB31B8E72FD0000C344 /* BITTelemetryManagerTests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = BITTelemetryManagerTests.m; sourceTree = "<group>"; };
|
1B460AB31B8E72FD0000C344 /* BITTelemetryManagerTests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = BITTelemetryManagerTests.m; sourceTree = "<group>"; };
|
||||||
1B50A9151B9FA82800ADECD1 /* BITTelemetryContextTests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = BITTelemetryContextTests.m; sourceTree = "<group>"; };
|
1B50A9151B9FA82800ADECD1 /* BITTelemetryContextTests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = BITTelemetryContextTests.m; sourceTree = "<group>"; };
|
||||||
|
1B57ABE01BA09B4C0040F078 /* BITSender.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = BITSender.h; sourceTree = "<group>"; };
|
||||||
|
1B57ABE11BA09B4C0040F078 /* BITSender.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = BITSender.m; sourceTree = "<group>"; };
|
||||||
1B57ABE91BA0C8850040F078 /* BITCategoryContainer.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = BITCategoryContainer.h; sourceTree = "<group>"; };
|
1B57ABE91BA0C8850040F078 /* BITCategoryContainer.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = BITCategoryContainer.h; sourceTree = "<group>"; };
|
||||||
1B57ABEA1BA0C8850040F078 /* BITCategoryContainer.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = BITCategoryContainer.m; sourceTree = "<group>"; };
|
1B57ABEA1BA0C8850040F078 /* BITCategoryContainer.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = BITCategoryContainer.m; sourceTree = "<group>"; };
|
||||||
1B57ABEB1BA0C8850040F078 /* BITGZIP.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = BITGZIP.h; sourceTree = "<group>"; };
|
1B57ABEB1BA0C8850040F078 /* BITGZIP.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = BITGZIP.h; sourceTree = "<group>"; };
|
||||||
@ -845,6 +851,8 @@
|
|||||||
1B330A6F1B98E267007844AB /* BITTelemetryContext.m */,
|
1B330A6F1B98E267007844AB /* BITTelemetryContext.m */,
|
||||||
1BD33EAD1B950DC700C3368B /* BITChannel.h */,
|
1BD33EAD1B950DC700C3368B /* BITChannel.h */,
|
||||||
1BD33EAE1B950DC700C3368B /* BITChannel.m */,
|
1BD33EAE1B950DC700C3368B /* BITChannel.m */,
|
||||||
|
1B57ABE01BA09B4C0040F078 /* BITSender.h */,
|
||||||
|
1B57ABE11BA09B4C0040F078 /* BITSender.m */,
|
||||||
1B57ABE91BA0C8850040F078 /* BITCategoryContainer.h */,
|
1B57ABE91BA0C8850040F078 /* BITCategoryContainer.h */,
|
||||||
1B57ABEA1BA0C8850040F078 /* BITCategoryContainer.m */,
|
1B57ABEA1BA0C8850040F078 /* BITCategoryContainer.m */,
|
||||||
1B57ABEB1BA0C8850040F078 /* BITGZIP.h */,
|
1B57ABEB1BA0C8850040F078 /* BITGZIP.h */,
|
||||||
@ -1365,6 +1373,7 @@
|
|||||||
1E49A4BE161222B900463151 /* BITHockeyHelper.h in Headers */,
|
1E49A4BE161222B900463151 /* BITHockeyHelper.h in Headers */,
|
||||||
973EC8BB18BDE29800DBFFBB /* BITArrowImageAnnotation.h in Headers */,
|
973EC8BB18BDE29800DBFFBB /* BITArrowImageAnnotation.h in Headers */,
|
||||||
1E49A4C4161222B900463151 /* BITAppStoreHeader.h in Headers */,
|
1E49A4C4161222B900463151 /* BITAppStoreHeader.h in Headers */,
|
||||||
|
1B57ABE31BA09B4C0040F078 /* BITSender.h in Headers */,
|
||||||
1E49A4CA161222B900463151 /* BITStoreButton.h in Headers */,
|
1E49A4CA161222B900463151 /* BITStoreButton.h in Headers */,
|
||||||
973EC8B718BCA8A200DBFFBB /* BITRectangleImageAnnotation.h in Headers */,
|
973EC8B718BCA8A200DBFFBB /* BITRectangleImageAnnotation.h in Headers */,
|
||||||
1E49A4D0161222B900463151 /* BITWebTableViewCell.h in Headers */,
|
1E49A4D0161222B900463151 /* BITWebTableViewCell.h in Headers */,
|
||||||
@ -1446,6 +1455,7 @@
|
|||||||
1EB617A01B0A31860035A986 /* BITFeedbackManagerDelegate.h in Headers */,
|
1EB617A01B0A31860035A986 /* BITFeedbackManagerDelegate.h in Headers */,
|
||||||
1EB6179F1B0A31810035A986 /* BITFeedbackManager.h in Headers */,
|
1EB6179F1B0A31810035A986 /* BITFeedbackManager.h in Headers */,
|
||||||
1EB617691B0A30C00035A986 /* BITAppStoreHeader.h in Headers */,
|
1EB617691B0A30C00035A986 /* BITAppStoreHeader.h in Headers */,
|
||||||
|
1B57ABE41BA09B4C0040F078 /* BITSender.h in Headers */,
|
||||||
1EB6176B1B0A30C60035A986 /* BITStoreButton.h in Headers */,
|
1EB6176B1B0A30C60035A986 /* BITStoreButton.h in Headers */,
|
||||||
1EB617A21B0A318E0035A986 /* BITActivityIndicatorButton.h in Headers */,
|
1EB617A21B0A318E0035A986 /* BITActivityIndicatorButton.h in Headers */,
|
||||||
1EB617A31B0A31910035A986 /* BITAppVersionMetaInfo.h in Headers */,
|
1EB617A31B0A31910035A986 /* BITAppVersionMetaInfo.h in Headers */,
|
||||||
@ -1807,6 +1817,7 @@
|
|||||||
846A90211B20B0EB0076BB80 /* BITCrashCXXExceptionHandler.mm in Sources */,
|
846A90211B20B0EB0076BB80 /* BITCrashCXXExceptionHandler.mm in Sources */,
|
||||||
1B87EFC51B8D2FBA0007C96B /* BITTelemetryObject.m in Sources */,
|
1B87EFC51B8D2FBA0007C96B /* BITTelemetryObject.m in Sources */,
|
||||||
1EACC97C162F041E007578C5 /* BITAttributedLabel.m in Sources */,
|
1EACC97C162F041E007578C5 /* BITAttributedLabel.m in Sources */,
|
||||||
|
1B57ABE51BA09B4C0040F078 /* BITSender.m in Sources */,
|
||||||
973EC8BC18BDE29800DBFFBB /* BITArrowImageAnnotation.m in Sources */,
|
973EC8BC18BDE29800DBFFBB /* BITArrowImageAnnotation.m in Sources */,
|
||||||
973EC8B418BCA7BC00DBFFBB /* BITImageAnnotationViewController.m in Sources */,
|
973EC8B418BCA7BC00DBFFBB /* BITImageAnnotationViewController.m in Sources */,
|
||||||
1E0FEE29173BDB260061331F /* BITKeychainUtils.m in Sources */,
|
1E0FEE29173BDB260061331F /* BITKeychainUtils.m in Sources */,
|
||||||
@ -1909,6 +1920,7 @@
|
|||||||
846A90221B20B0EB0076BB80 /* BITCrashCXXExceptionHandler.mm in Sources */,
|
846A90221B20B0EB0076BB80 /* BITCrashCXXExceptionHandler.mm in Sources */,
|
||||||
1B87EFC61B8D2FBA0007C96B /* BITTelemetryObject.m in Sources */,
|
1B87EFC61B8D2FBA0007C96B /* BITTelemetryObject.m in Sources */,
|
||||||
1EB6178A1B0A31510035A986 /* BITFeedbackComposeViewController.m in Sources */,
|
1EB6178A1B0A31510035A986 /* BITFeedbackComposeViewController.m in Sources */,
|
||||||
|
1B57ABE61BA09B4C0040F078 /* BITSender.m in Sources */,
|
||||||
1EB617701B0A30D70035A986 /* BITHockeyAttachment.m in Sources */,
|
1EB617701B0A30D70035A986 /* BITHockeyAttachment.m in Sources */,
|
||||||
1EB617881B0A31510035A986 /* BITFeedbackMessage.m in Sources */,
|
1EB617881B0A31510035A986 /* BITFeedbackMessage.m in Sources */,
|
||||||
1EB617951B0A31510035A986 /* BITHockeyManager.m in Sources */,
|
1EB617951B0A31510035A986 /* BITHockeyManager.m in Sources */,
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user