diff --git a/Classes/BITFeedbackManager.h b/Classes/BITFeedbackManager.h index 3ef85c45..f00747e7 100644 --- a/Classes/BITFeedbackManager.h +++ b/Classes/BITFeedbackManager.h @@ -76,7 +76,11 @@ typedef NS_ENUM(NSInteger, BITFeedbackObservationMode) { /** * Triggers when the user taps with three fingers on the screen. Captures a screenshot and attaches it to the composer. */ - BITFeedbackObservationModeThreeFingerTap = 2 + BITFeedbackObservationModeThreeFingerTap = 2, + /** + * Allows both BITFeedbackObservationModeOnScreenshot and BITFeedbackObservationModeThreeFingerTap at the same time. + */ + BITFeedbackObservationModeAll = 3 }; @@ -219,25 +223,14 @@ typedef NS_ENUM(NSInteger, BITFeedbackObservationMode) { This will grab the latest image from the camera roll. Requires iOS 7 or later! It also requires to add a NSPhotoLibraryUsageDescription to your app's Info.plist. - `BITFeedbackObservationModeThreeFingerTap`: Triggers when the user taps on the screen with three fingers. Takes a screenshot and attaches it to the composer. It also requires to add a NSPhotoLibraryUsageDescription to your app's Info.plist. - Default is `BITFeedbackObservationNone` + Default is `BITFeedbackObservationNone`. + If BITFeedbackManger was disabled, setting a new value will be ignored. + @see `[BITHockeyManager disableFeedbackManager]` @see showFeedbackComposeViewWithGeneratedScreenshot */ @property (nonatomic, readwrite) BITFeedbackObservationMode feedbackObservationMode; -/** - Prefill feedback compose message user interface with the items given. - - All NSString-Content in the array will be concatenated and result in the message, - while all UIImage and NSData-instances will be turned into attachments. - - @deprecated This property is deprecated in favor of `BITFeedbackManagerDelegate preparedItemsForFeedbackManager:`. - - @see `[BITFeedbackComposeViewController prepareWithItems:]` - */ -@property (nonatomic, copy, nullable) NSArray *feedbackComposerPreparedItems DEPRECATED_MSG_ATTRIBUTE("Use -preparedItemsForFeedbackManager: delegate method instead."); - - /** Don't show the option to add images from the photo library diff --git a/Classes/BITFeedbackManager.m b/Classes/BITFeedbackManager.m index 7dde9347..8c7de869 100644 --- a/Classes/BITFeedbackManager.m +++ b/Classes/BITFeedbackManager.m @@ -46,7 +46,7 @@ #import "BITHockeyAppClient.h" #define kBITFeedbackUserDataAsked @"HockeyFeedbackUserDataAsked" -#define kBITFeedbackDateOfLastCheck @"HockeyFeedbackDateOfLastCheck" +#define kBITFeedbackDateOfLastCheck @"HockeyFeedbackDateOfLastCheck" #define kBITFeedbackMessages @"HockeyFeedbackMessages" #define kBITFeedbackToken @"HockeyFeedbackToken" #define kBITFeedbackUserID @"HockeyFeedbackuserID" @@ -59,25 +59,22 @@ typedef void (^BITLatestImageFetchCompletionBlock)(UIImage *_Nonnull latestImage); -@interface BITFeedbackManager() - -@property (nonatomic, strong) UITapGestureRecognizer *tapRecognizer; -@property (nonatomic) BOOL screenshotNotificationEnabled; +@interface BITFeedbackManager () @end @implementation BITFeedbackManager { - NSFileManager *_fileManager; - NSString *_settingsFile; - + NSFileManager *_fileManager; + NSString *_settingsFile; + id _appDidBecomeActiveObserver; id _appDidEnterBackgroundObserver; id _networkDidBecomeReachableObserver; - + BOOL _incomingMessagesAlertShowing; BOOL _didEnterBackgroundState; BOOL _networkRequestInProgress; - + BITFeedbackObservationMode _observationMode; } @@ -88,25 +85,25 @@ - (instancetype)init { _currentFeedbackListViewController = nil; _currentFeedbackComposeViewController = nil; _didAskUserData = NO; - + _requireUserName = BITFeedbackUserDataElementOptional; _requireUserEmail = BITFeedbackUserDataElementOptional; _showAlertOnIncomingMessages = YES; _showFirstRequiredPresentationModal = YES; - + _disableFeedbackManager = NO; _networkRequestInProgress = NO; _incomingMessagesAlertShowing = NO; _lastCheck = nil; _token = nil; _lastMessageID = nil; - + _feedbackList = [NSMutableArray array]; - + _fileManager = [[NSFileManager alloc] init]; - + _settingsFile = [bit_settingsDir() stringByAppendingPathComponent:BITHOCKEY_FEEDBACK_SETTINGS]; - + _userID = nil; _userName = nil; _userEmail = nil; @@ -121,15 +118,15 @@ - (void)dealloc { - (void)didBecomeActiveActions { if ([self isFeedbackManagerDisabled]) return; if (!_didEnterBackgroundState) return; - + _didEnterBackgroundState = NO; - + if ([_feedbackList count] == 0) { [self loadMessages]; } else { [self updateAppDefinedUserData]; } - + if ([self allowFetchingNewMessages]) { [self updateMessagesList]; } @@ -137,54 +134,55 @@ - (void)didBecomeActiveActions { - (void)didEnterBackgroundActions { _didEnterBackgroundState = NO; - + if ([[UIApplication sharedApplication] applicationState] == UIApplicationStateBackground) { _didEnterBackgroundState = YES; } } #pragma mark - Observers -- (void) registerObservers { + +- (void)registerObservers { __weak typeof(self) weakSelf = self; - if(nil == _appDidEnterBackgroundObserver) { + if (nil == _appDidEnterBackgroundObserver) { _appDidEnterBackgroundObserver = [[NSNotificationCenter defaultCenter] addObserverForName:UIApplicationDidEnterBackgroundNotification object:nil queue:NSOperationQueue.mainQueue usingBlock:^(NSNotification *note) { - typeof(self) strongSelf = weakSelf; - [strongSelf didEnterBackgroundActions]; + typeof(self) strongSelf = weakSelf; + [strongSelf didEnterBackgroundActions]; }]; } - if(nil == _appDidBecomeActiveObserver) { + if (nil == _appDidBecomeActiveObserver) { _appDidBecomeActiveObserver = [[NSNotificationCenter defaultCenter] addObserverForName:UIApplicationDidBecomeActiveNotification object:nil queue:NSOperationQueue.mainQueue usingBlock:^(NSNotification *note) { - typeof(self) strongSelf = weakSelf; - [strongSelf didBecomeActiveActions]; + typeof(self) strongSelf = weakSelf; + [strongSelf didBecomeActiveActions]; }]; } - if(nil == _networkDidBecomeReachableObserver) { + if (nil == _networkDidBecomeReachableObserver) { _networkDidBecomeReachableObserver = [[NSNotificationCenter defaultCenter] addObserverForName:BITHockeyNetworkDidBecomeReachableNotification object:nil queue:NSOperationQueue.mainQueue usingBlock:^(NSNotification *note) { - typeof(self) strongSelf = weakSelf; - [strongSelf didBecomeActiveActions]; + typeof(self) strongSelf = weakSelf; + [strongSelf didBecomeActiveActions]; }]; } } -- (void) unregisterObservers { - if(_appDidEnterBackgroundObserver) { +- (void)unregisterObservers { + if (_appDidEnterBackgroundObserver) { [[NSNotificationCenter defaultCenter] removeObserver:_appDidEnterBackgroundObserver]; _appDidEnterBackgroundObserver = nil; } - if(_appDidBecomeActiveObserver) { + if (_appDidBecomeActiveObserver) { [[NSNotificationCenter defaultCenter] removeObserver:_appDidBecomeActiveObserver]; _appDidBecomeActiveObserver = nil; } - if(_networkDidBecomeReachableObserver) { + if (_networkDidBecomeReachableObserver) { [[NSNotificationCenter defaultCenter] removeObserver:_networkDidBecomeReachableObserver]; _networkDidBecomeReachableObserver = nil; } @@ -194,9 +192,9 @@ - (void) unregisterObservers { - (NSString *)uuidString { CFUUIDRef theToken = CFUUIDCreate(NULL); - NSString *stringUUID = (__bridge_transfer NSString *)CFUUIDCreateString(NULL, theToken); + NSString *stringUUID = (__bridge_transfer NSString *) CFUUIDCreateString(NULL, theToken); CFRelease(theToken); - + return stringUUID; } @@ -224,25 +222,22 @@ - (void)showFeedbackListView { return; } dispatch_async(dispatch_get_main_queue(), ^{ - [self showView:[self feedbackListViewController:YES]]; + [self showView:[self feedbackListViewController:YES]]; }); } - (BITFeedbackComposeViewController *)feedbackComposeViewController { BITFeedbackComposeViewController *composeViewController = [[BITFeedbackComposeViewController alloc] init]; - -#pragma clang diagnostic push -#pragma clang diagnostic ignored "-Wdeprecated" - NSArray *preparedItems = self.feedbackComposerPreparedItems ?: [NSArray array]; -#pragma clang diagnostic pop + + NSArray *preparedItems = [NSArray array]; if ([self.delegate respondsToSelector:@selector(preparedItemsForFeedbackManager:)]) { preparedItems = [preparedItems arrayByAddingObjectsFromArray:[self.delegate preparedItemsForFeedbackManager:self]]; } - + [composeViewController prepareWithItems:preparedItems]; [composeViewController setHideImageAttachmentButton:self.feedbackComposeHideImageAttachmentButton]; - + // by default set the delegate to be identical to the one of BITFeedbackManager [composeViewController setDelegate:self.delegate]; return composeViewController; @@ -252,7 +247,7 @@ - (void)showFeedbackComposeView { [self showFeedbackComposeViewWithPreparedItems:nil]; } -- (void)showFeedbackComposeViewWithPreparedItems:(NSArray *)items{ +- (void)showFeedbackComposeViewWithPreparedItems:(NSArray *)items { if (_currentFeedbackComposeViewController) { BITHockeyLogDebug(@"INFO: Feedback view already visible, aborting"); return; @@ -260,7 +255,7 @@ - (void)showFeedbackComposeViewWithPreparedItems:(NSArray *)items{ BITFeedbackComposeViewController *composeView = [self feedbackComposeViewController]; [composeView prepareWithItems:items]; dispatch_async(dispatch_get_main_queue(), ^{ - [self showView:composeView]; + [self showView:composeView]; }); } @@ -273,17 +268,17 @@ - (void)showFeedbackComposeViewWithGeneratedScreenshot { - (void)startManager { if ([self isFeedbackManagerDisabled]) return; - + [self registerObservers]; - + [self isiOS10PhotoPolicySet]; - + // we are already delayed, so the notification already came in and this won't invoked twice switch ([[UIApplication sharedApplication] applicationState]) { case UIApplicationStateActive: // we did startup, so yes we are coming from background _didEnterBackgroundState = YES; - + [self didBecomeActiveActions]; break; case UIApplicationStateBackground: @@ -297,14 +292,14 @@ - (BOOL)allowFetchingNewMessages { BOOL fetchNewMessages = YES; if ([[BITHockeyManager sharedHockeyManager].delegate respondsToSelector:@selector(allowAutomaticFetchingForNewFeedbackForManager:)]) { fetchNewMessages = [[BITHockeyManager sharedHockeyManager].delegate - allowAutomaticFetchingForNewFeedbackForManager:self]; + allowAutomaticFetchingForNewFeedbackForManager:self]; } return fetchNewMessages; } - (void)updateMessagesList { if (_networkRequestInProgress) return; - + NSArray *pendingMessages = [self messagesWithStatus:BITFeedbackMessageStatusSendPending]; if ([pendingMessages count] > 0) { [self submitPendingMessages]; @@ -322,60 +317,60 @@ - (void)updateMessagesListIfRequired { - (BOOL)updateUserIDUsingKeychainAndDelegate { BOOL availableViaDelegate = NO; - + NSString *userID = [self stringValueFromKeychainForKey:kBITHockeyMetaUserID]; - + if ([[BITHockeyManager sharedHockeyManager].delegate respondsToSelector:@selector(userIDForHockeyManager:componentManager:)]) { userID = [[BITHockeyManager sharedHockeyManager].delegate - userIDForHockeyManager:[BITHockeyManager sharedHockeyManager] - componentManager:self]; + userIDForHockeyManager:[BITHockeyManager sharedHockeyManager] + componentManager:self]; } - + if (userID) { availableViaDelegate = YES; self.userID = userID; } - + return availableViaDelegate; } - (BOOL)updateUserNameUsingKeychainAndDelegate { BOOL availableViaDelegate = NO; - + NSString *userName = [self stringValueFromKeychainForKey:kBITHockeyMetaUserName]; - + if ([[BITHockeyManager sharedHockeyManager].delegate respondsToSelector:@selector(userNameForHockeyManager:componentManager:)]) { userName = [[BITHockeyManager sharedHockeyManager].delegate - userNameForHockeyManager:[BITHockeyManager sharedHockeyManager] - componentManager:self]; + userNameForHockeyManager:[BITHockeyManager sharedHockeyManager] + componentManager:self]; } - + if (userName) { availableViaDelegate = YES; self.userName = userName; self.requireUserName = BITFeedbackUserDataElementDontShow; } - + return availableViaDelegate; } - (BOOL)updateUserEmailUsingKeychainAndDelegate { BOOL availableViaDelegate = NO; - + NSString *userEmail = [self stringValueFromKeychainForKey:kBITHockeyMetaUserEmail]; - + if ([[BITHockeyManager sharedHockeyManager].delegate respondsToSelector:@selector(userEmailForHockeyManager:componentManager:)]) { userEmail = [[BITHockeyManager sharedHockeyManager].delegate - userEmailForHockeyManager:[BITHockeyManager sharedHockeyManager] - componentManager:self]; + userEmailForHockeyManager:[BITHockeyManager sharedHockeyManager] + componentManager:self]; } - + if (userEmail) { availableViaDelegate = YES; self.userEmail = userEmail; self.requireUserEmail = BITFeedbackUserDataElementDontShow; } - + return availableViaDelegate; } @@ -383,20 +378,20 @@ - (void)updateAppDefinedUserData { [self updateUserIDUsingKeychainAndDelegate]; [self updateUserNameUsingKeychainAndDelegate]; [self updateUserEmailUsingKeychainAndDelegate]; - + // if both values are shown via the delegates, we never ever did ask and will never ever ask for user data if (self.requireUserName == BITFeedbackUserDataElementDontShow && - self.requireUserEmail == BITFeedbackUserDataElementDontShow) { + self.requireUserEmail == BITFeedbackUserDataElementDontShow) { self.didAskUserData = NO; } } - (BOOL)isiOS10PhotoPolicySet { BOOL isiOS10PhotoPolicySet = [BITHockeyHelper isPhotoAccessPossible]; - if(bit_isDebuggerAttached()) { - if(!isiOS10PhotoPolicySet) { + if (bit_isDebuggerAttached()) { + if (!isiOS10PhotoPolicySet) { BITHockeyLogWarning(@"You are using HockeyApp's Feedback feature in iOS 10 or later. iOS 10 requires you to add the usage strings to your app's info.plist. " - @"Attaching screenshots to feedback is disabled. Please add the String for NSPhotoLibraryUsageDescription to your info.plist to enable screenshot attachments."); + @"Attaching screenshots to feedback is disabled. Please add the String for NSPhotoLibraryUsageDescription to your info.plist to enable screenshot attachments."); } } return isiOS10PhotoPolicySet; @@ -408,22 +403,22 @@ - (void)loadMessages { BOOL userIDViaDelegate = [self updateUserIDUsingKeychainAndDelegate]; BOOL userNameViaDelegate = [self updateUserNameUsingKeychainAndDelegate]; BOOL userEmailViaDelegate = [self updateUserEmailUsingKeychainAndDelegate]; - + if (![_fileManager fileExistsAtPath:_settingsFile]) return; - + NSData *codedData = [[NSData alloc] initWithContentsOfFile:_settingsFile]; if (codedData == nil) return; - + NSKeyedUnarchiver *unarchiver = nil; - + @try { unarchiver = [[NSKeyedUnarchiver alloc] initForReadingWithData:codedData]; } @catch (NSException *exception) { return; } - + if (!userIDViaDelegate) { if ([unarchiver containsValueForKey:kBITFeedbackUserID]) { self.userID = [unarchiver decodeObjectForKey:kBITFeedbackUserID]; @@ -431,7 +426,7 @@ - (void)loadMessages { } self.userID = [self stringValueFromKeychainForKey:kBITFeedbackUserID]; } - + if (!userNameViaDelegate) { if ([unarchiver containsValueForKey:kBITFeedbackName]) { self.userName = [unarchiver decodeObjectForKey:kBITFeedbackName]; @@ -439,7 +434,7 @@ - (void)loadMessages { } self.userName = [self stringValueFromKeychainForKey:kBITFeedbackName]; } - + if (!userEmailViaDelegate) { if ([unarchiver containsValueForKey:kBITFeedbackEmail]) { self.userEmail = [unarchiver decodeObjectForKey:kBITFeedbackEmail]; @@ -447,7 +442,7 @@ - (void)loadMessages { } self.userEmail = [self stringValueFromKeychainForKey:kBITFeedbackEmail]; } - + if ([unarchiver containsValueForKey:kBITFeedbackUserDataAsked]) _didAskUserData = YES; @@ -456,10 +451,10 @@ - (void)loadMessages { [self addStringValueToKeychain:self.token forKey:kBITFeedbackToken]; } self.token = [self stringValueFromKeychainForKey:kBITFeedbackToken]; - + if ([unarchiver containsValueForKey:kBITFeedbackAppID]) { NSString *appID = [unarchiver decodeObjectForKey:kBITFeedbackAppID]; - + // the stored thread is from another application identifier, so clear the token // which will cause the new posts to create a new thread on the server for the // current app identifier @@ -474,21 +469,21 @@ - (void)loadMessages { if ([unarchiver containsValueForKey:kBITFeedbackDateOfLastCheck]) self.lastCheck = [unarchiver decodeObjectForKey:kBITFeedbackDateOfLastCheck]; - + if ([unarchiver containsValueForKey:kBITFeedbackLastMessageID]) self.lastMessageID = [unarchiver decodeObjectForKey:kBITFeedbackLastMessageID]; - + if ([unarchiver containsValueForKey:kBITFeedbackMessages]) { [self.feedbackList setArray:[unarchiver decodeObjectForKey:kBITFeedbackMessages]]; - + [self sortFeedbackList]; - + // inform the UI to update its data in case the list is already showing [[NSNotificationCenter defaultCenter] postNotificationName:BITHockeyFeedbackMessagesLoadingFinished object:nil]; } - + [unarchiver finishDecoding]; - + if (!self.lastCheck) { self.lastCheck = [NSDate distantPast]; } @@ -497,36 +492,36 @@ - (void)loadMessages { - (void)saveMessages { [self sortFeedbackList]; - + NSMutableData *data = [[NSMutableData alloc] init]; NSKeyedArchiver *archiver = [[NSKeyedArchiver alloc] initForWritingWithMutableData:data]; - + if (_didAskUserData) [archiver encodeObject:[NSNumber numberWithBool:YES] forKey:kBITFeedbackUserDataAsked]; - + if (self.token) [self addStringValueToKeychain:self.token forKey:kBITFeedbackToken]; - + if (self.appIdentifier) [archiver encodeObject:self.appIdentifier forKey:kBITFeedbackAppID]; - + if (self.userID) [self addStringValueToKeychain:self.userID forKey:kBITFeedbackUserID]; - + if (self.userName) [self addStringValueToKeychain:self.userName forKey:kBITFeedbackName]; - + if (self.userEmail) [self addStringValueToKeychain:self.userEmail forKey:kBITFeedbackEmail]; - + if (self.lastCheck) [archiver encodeObject:self.lastCheck forKey:kBITFeedbackDateOfLastCheck]; - + if (self.lastMessageID) [archiver encodeObject:self.lastMessageID forKey:kBITFeedbackLastMessageID]; - + [archiver encodeObject:self.feedbackList forKey:kBITFeedbackMessages]; - + [archiver finishEncoding]; [data writeToFile:_settingsFile atomically:YES]; } @@ -535,7 +530,7 @@ - (void)saveMessages { - (void)updateDidAskUserData { if (!_didAskUserData) { _didAskUserData = YES; - + [self saveMessages]; } } @@ -544,24 +539,24 @@ - (void)updateDidAskUserData { - (void)sortFeedbackList { [_feedbackList sortUsingComparator:^(BITFeedbackMessage *obj1, BITFeedbackMessage *obj2) { - NSDate *date1 = [obj1 date]; - NSDate *date2 = [obj2 date]; - - // not send, in conflict and send in progress messages on top, sorted by date - // read and unread on bottom, sorted by date - // archived on the very bottom - - if ([obj1 status] >= BITFeedbackMessageStatusSendInProgress && [obj2 status] < BITFeedbackMessageStatusSendInProgress) { - return NSOrderedDescending; - } else if ([obj1 status] < BITFeedbackMessageStatusSendInProgress && [obj2 status] >= BITFeedbackMessageStatusSendInProgress) { - return NSOrderedAscending; - } else if ([obj1 status] == BITFeedbackMessageStatusArchived && [obj2 status] < BITFeedbackMessageStatusArchived) { - return NSOrderedDescending; - } else if ([obj1 status] < BITFeedbackMessageStatusArchived && [obj2 status] == BITFeedbackMessageStatusArchived) { - return NSOrderedAscending; - } else { - return (NSInteger)[date2 compare:date1]; - } + NSDate *date1 = [obj1 date]; + NSDate *date2 = [obj2 date]; + + // not send, in conflict and send in progress messages on top, sorted by date + // read and unread on bottom, sorted by date + // archived on the very bottom + + if ([obj1 status] >= BITFeedbackMessageStatusSendInProgress && [obj2 status] < BITFeedbackMessageStatusSendInProgress) { + return NSOrderedDescending; + } else if ([obj1 status] < BITFeedbackMessageStatusSendInProgress && [obj2 status] >= BITFeedbackMessageStatusSendInProgress) { + return NSOrderedAscending; + } else if ([obj1 status] == BITFeedbackMessageStatusArchived && [obj2 status] < BITFeedbackMessageStatusArchived) { + return NSOrderedDescending; + } else if ([obj1 status] < BITFeedbackMessageStatusArchived && [obj2 status] == BITFeedbackMessageStatusArchived) { + return NSOrderedAscending; + } else { + return (NSInteger) [date2 compare:date1]; + } }]; } @@ -573,64 +568,64 @@ - (BITFeedbackMessage *)messageAtIndex:(NSUInteger)index { if ([_feedbackList count] > index) { return [_feedbackList objectAtIndex:index]; } - + return nil; } - (BITFeedbackMessage *)messageWithID:(NSNumber *)messageID { __block BITFeedbackMessage *message = nil; - + [_feedbackList enumerateObjectsUsingBlock:^(BITFeedbackMessage *objMessage, NSUInteger messagesIdx, BOOL *stop) { - if ([[objMessage identifier] isEqualToNumber:messageID]) { - message = objMessage; - *stop = YES; - } + if ([[objMessage identifier] isEqualToNumber:messageID]) { + message = objMessage; + *stop = YES; + } }]; - + return message; } - (NSArray *)messagesWithStatus:(BITFeedbackMessageStatus)status { NSMutableArray *resultMessages = [[NSMutableArray alloc] initWithCapacity:[_feedbackList count]]; - + [_feedbackList enumerateObjectsUsingBlock:^(BITFeedbackMessage *objMessage, NSUInteger messagesIdx, BOOL *stop) { - if ([objMessage status] == status) { - [resultMessages addObject: objMessage]; - } + if ([objMessage status] == status) { + [resultMessages addObject:objMessage]; + } }]; - + return [NSArray arrayWithArray:resultMessages];; } - (BITFeedbackMessage *)lastMessageHavingID { __block BITFeedbackMessage *message = nil; - - + + // Note: the logic here is slightly different than in our mac SDK, as _feedbackList is sorted in different order. // Compare the implementation of - (void)sortFeedbackList; in both SDKs. [_feedbackList enumerateObjectsUsingBlock:^(BITFeedbackMessage *objMessage, NSUInteger messagesIdx, BOOL *stop) { - if ([[objMessage identifier] integerValue] != 0) { - message = objMessage; - *stop = YES; - } + if ([[objMessage identifier] integerValue] != 0) { + message = objMessage; + *stop = YES; + } }]; - + return message; } - (void)markSendInProgressMessagesAsPending { // make sure message that may have not been send successfully, get back into the right state to be send again [_feedbackList enumerateObjectsUsingBlock:^(id objMessage, NSUInteger messagesIdx, BOOL *stop) { - if ([(BITFeedbackMessage *)objMessage status] == BITFeedbackMessageStatusSendInProgress) - [(BITFeedbackMessage *)objMessage setStatus:BITFeedbackMessageStatusSendPending]; + if ([(BITFeedbackMessage *) objMessage status] == BITFeedbackMessageStatusSendInProgress) + [(BITFeedbackMessage *) objMessage setStatus:BITFeedbackMessageStatusSendPending]; }]; } - (void)markSendInProgressMessagesAsInConflict { // make sure message that may have not been send successfully, get back into the right state to be send again [_feedbackList enumerateObjectsUsingBlock:^(id objMessage, NSUInteger messagesIdx, BOOL *stop) { - if ([(BITFeedbackMessage *)objMessage status] == BITFeedbackMessageStatusSendInProgress) - [(BITFeedbackMessage *)objMessage setStatus:BITFeedbackMessageStatusInConflict]; + if ([(BITFeedbackMessage *) objMessage status] == BITFeedbackMessageStatusSendInProgress) + [(BITFeedbackMessage *) objMessage setStatus:BITFeedbackMessageStatusInConflict]; }]; } @@ -647,25 +642,24 @@ - (BOOL)deleteMessageAtIndex:(NSUInteger)index { BITFeedbackMessage *message = _feedbackList[index]; [message deleteContents]; [_feedbackList removeObjectAtIndex:index]; - + [self saveMessages]; return YES; } - + return NO; } - (void)deleteAllMessages { [_feedbackList removeAllObjects]; - + [self saveMessages]; } - (BOOL)shouldForceNewThread { if (self.delegate && [self.delegate respondsToSelector:@selector(forceNewFeedbackThreadForFeedbackManager:)]) { return [self.delegate forceNewFeedbackThreadForFeedbackManager:self]; - } - else { + } else { return NO; } } @@ -675,33 +669,33 @@ - (BOOL)shouldForceNewThread { - (BOOL)askManualUserDataAvailable { [self updateAppDefinedUserData]; - + if (self.requireUserName == BITFeedbackUserDataElementDontShow && - self.requireUserEmail == BITFeedbackUserDataElementDontShow) + self.requireUserEmail == BITFeedbackUserDataElementDontShow) return NO; - + return YES; } - (BOOL)requireManualUserDataMissing { [self updateAppDefinedUserData]; - + if (self.requireUserName == BITFeedbackUserDataElementRequired && !self.userName) return YES; - + if (self.requireUserEmail == BITFeedbackUserDataElementRequired && !self.userEmail) return YES; - + return NO; } - (BOOL)isManualUserDataAvailable { [self updateAppDefinedUserData]; - + if ((self.requireUserName != BITFeedbackUserDataElementDontShow && self.userName) || - (self.requireUserEmail != BITFeedbackUserDataElementDontShow && self.userEmail)) + (self.requireUserEmail != BITFeedbackUserDataElementDontShow && self.userEmail)) return YES; - + return NO; } @@ -711,241 +705,241 @@ - (BOOL)isManualUserDataAvailable { - (void)updateMessageListFromResponse:(NSDictionary *)jsonDictionary { if (!jsonDictionary) { // nil is used when the server returns 404, so we need to mark all existing threads as archives and delete the discussion token - + NSArray *messagesSendInProgress = [self messagesWithStatus:BITFeedbackMessageStatusSendInProgress]; NSInteger pendingMessagesCount = [messagesSendInProgress count] + [[self messagesWithStatus:BITFeedbackMessageStatusSendPending] count]; - + [self markSendInProgressMessagesAsPending]; - + [_feedbackList enumerateObjectsUsingBlock:^(id objMessage, NSUInteger messagesIdx, BOOL *stop) { - if ([(BITFeedbackMessage *)objMessage status] != BITFeedbackMessageStatusSendPending) - [(BITFeedbackMessage *)objMessage setStatus:BITFeedbackMessageStatusArchived]; + if ([(BITFeedbackMessage *) objMessage status] != BITFeedbackMessageStatusSendPending) + [(BITFeedbackMessage *) objMessage setStatus:BITFeedbackMessageStatusArchived]; }]; - + if ([self token]) { self.token = nil; } - + NSInteger pendingMessagesCountAfterProcessing = [[self messagesWithStatus:BITFeedbackMessageStatusSendPending] count]; - + [self saveMessages]; - + // check if this request was successful and we have more messages pending and continue if positive if (pendingMessagesCount > pendingMessagesCountAfterProcessing && pendingMessagesCountAfterProcessing > 0) { [self performSelector:@selector(submitPendingMessages) withObject:nil afterDelay:0.1]; } - + return; } - + NSDictionary *feedback = [jsonDictionary objectForKey:@"feedback"]; NSString *token = [jsonDictionary objectForKey:@"token"]; NSDictionary *feedbackObject = [jsonDictionary objectForKey:@"feedback"]; if (feedback && token && feedbackObject) { if ([self shouldForceNewThread]) { - self.token = nil; + self.token = nil; } else { // update the thread token, which is not available until the 1st message was successfully sent self.token = token; } - + self.lastCheck = [NSDate date]; - + // add all new messages NSArray *feedMessages = [feedbackObject objectForKey:@"messages"]; - + // get the message that was currently sent if available NSArray *messagesSendInProgress = [self messagesWithStatus:BITFeedbackMessageStatusSendInProgress]; - + NSInteger pendingMessagesCount = [messagesSendInProgress count] + [[self messagesWithStatus:BITFeedbackMessageStatusSendPending] count]; - + __block BOOL newMessage = NO; NSMutableSet *returnedMessageIDs = [[NSMutableSet alloc] init]; - + [feedMessages enumerateObjectsUsingBlock:^(id objMessage, NSUInteger messagesIdx, BOOL *stop) { - if ([(NSDictionary *)objMessage objectForKey:@"id"]) { - NSNumber *messageID = [(NSDictionary *)objMessage objectForKey:@"id"]; - [returnedMessageIDs addObject:messageID]; - - BITFeedbackMessage *thisMessage = [self messageWithID:messageID]; - if (!thisMessage) { - // check if this is a message that was sent right now - __block BITFeedbackMessage *matchingSendInProgressOrInConflictMessage = nil; - - // TODO: match messages in state conflict - - [messagesSendInProgress enumerateObjectsUsingBlock:^(id objSendInProgressMessage, NSUInteger messagesSendInProgressIdx, BOOL *stop2) { - if ([[(NSDictionary *)objMessage objectForKey:@"token"] isEqualToString:[(BITFeedbackMessage *)objSendInProgressMessage token]]) { - matchingSendInProgressOrInConflictMessage = objSendInProgressMessage; - *stop2 = YES; - } - }]; - - if (matchingSendInProgressOrInConflictMessage) { - matchingSendInProgressOrInConflictMessage.date = [self parseRFC3339Date:[(NSDictionary *)objMessage objectForKey:@"created_at"]]; - matchingSendInProgressOrInConflictMessage.identifier = messageID; - matchingSendInProgressOrInConflictMessage.status = BITFeedbackMessageStatusRead; - NSArray *feedbackAttachments =[(NSDictionary *)objMessage objectForKey:@"attachments"]; - if (matchingSendInProgressOrInConflictMessage.attachments.count == feedbackAttachments.count) { - int attachmentIndex = 0; - for (BITFeedbackMessageAttachment* attachment in matchingSendInProgressOrInConflictMessage.attachments){ - attachment.identifier =feedbackAttachments[attachmentIndex][@"id"]; - attachmentIndex++; + if ([(NSDictionary *) objMessage objectForKey:@"id"]) { + NSNumber *messageID = [(NSDictionary *) objMessage objectForKey:@"id"]; + [returnedMessageIDs addObject:messageID]; + + BITFeedbackMessage *thisMessage = [self messageWithID:messageID]; + if (!thisMessage) { + // check if this is a message that was sent right now + __block BITFeedbackMessage *matchingSendInProgressOrInConflictMessage = nil; + + // TODO: match messages in state conflict + + [messagesSendInProgress enumerateObjectsUsingBlock:^(id objSendInProgressMessage, NSUInteger messagesSendInProgressIdx, BOOL *stop2) { + if ([[(NSDictionary *) objMessage objectForKey:@"token"] isEqualToString:[(BITFeedbackMessage *) objSendInProgressMessage token]]) { + matchingSendInProgressOrInConflictMessage = objSendInProgressMessage; + *stop2 = YES; + } + }]; + + if (matchingSendInProgressOrInConflictMessage) { + matchingSendInProgressOrInConflictMessage.date = [self parseRFC3339Date:[(NSDictionary *) objMessage objectForKey:@"created_at"]]; + matchingSendInProgressOrInConflictMessage.identifier = messageID; + matchingSendInProgressOrInConflictMessage.status = BITFeedbackMessageStatusRead; + NSArray *feedbackAttachments = [(NSDictionary *) objMessage objectForKey:@"attachments"]; + if (matchingSendInProgressOrInConflictMessage.attachments.count == feedbackAttachments.count) { + int attachmentIndex = 0; + for (BITFeedbackMessageAttachment *attachment in matchingSendInProgressOrInConflictMessage.attachments) { + attachment.identifier = feedbackAttachments[attachmentIndex][@"id"]; + attachmentIndex++; + } } - } - } else { - if ([(NSDictionary *)objMessage objectForKey:@"clean_text"] || [(NSDictionary *)objMessage objectForKey:@"text"] || [(NSDictionary *)objMessage objectForKey:@"attachments"]) { - BITFeedbackMessage *message = [[BITFeedbackMessage alloc] init]; - message.text = [(NSDictionary *)objMessage objectForKey:@"clean_text"] ?: [(NSDictionary *)objMessage objectForKey:@"text"] ?: @""; - message.name = [(NSDictionary *)objMessage objectForKey:@"name"] ?: @""; - message.email = [(NSDictionary *)objMessage objectForKey:@"email"] ?: @""; - - message.date = [self parseRFC3339Date:[(NSDictionary *)objMessage objectForKey:@"created_at"]] ?: [NSDate date]; - message.identifier = [(NSDictionary *)objMessage objectForKey:@"id"]; - message.status = BITFeedbackMessageStatusUnread; - - for (NSDictionary *attachmentData in objMessage[@"attachments"]) { - BITFeedbackMessageAttachment *newAttachment = [BITFeedbackMessageAttachment new]; - newAttachment.originalFilename = attachmentData[@"file_name"]; - newAttachment.identifier = attachmentData[@"id"]; - newAttachment.sourceURL = attachmentData[@"url"]; - newAttachment.contentType = attachmentData[@"content_type"]; - [message addAttachmentsObject:newAttachment]; + } else { + if ([(NSDictionary *) objMessage objectForKey:@"clean_text"] || [(NSDictionary *) objMessage objectForKey:@"text"] || [(NSDictionary *) objMessage objectForKey:@"attachments"]) { + BITFeedbackMessage *message = [[BITFeedbackMessage alloc] init]; + message.text = [(NSDictionary *) objMessage objectForKey:@"clean_text"] ?: [(NSDictionary *) objMessage objectForKey:@"text"] ?: @""; + message.name = [(NSDictionary *) objMessage objectForKey:@"name"] ?: @""; + message.email = [(NSDictionary *) objMessage objectForKey:@"email"] ?: @""; + + message.date = [self parseRFC3339Date:[(NSDictionary *) objMessage objectForKey:@"created_at"]] ?: [NSDate date]; + message.identifier = [(NSDictionary *) objMessage objectForKey:@"id"]; + message.status = BITFeedbackMessageStatusUnread; + + for (NSDictionary *attachmentData in objMessage[@"attachments"]) { + BITFeedbackMessageAttachment *newAttachment = [BITFeedbackMessageAttachment new]; + newAttachment.originalFilename = attachmentData[@"file_name"]; + newAttachment.identifier = attachmentData[@"id"]; + newAttachment.sourceURL = attachmentData[@"url"]; + newAttachment.contentType = attachmentData[@"content_type"]; + [message addAttachmentsObject:newAttachment]; + } + + [_feedbackList addObject:message]; + + newMessage = YES; } - - [_feedbackList addObject:message]; - - newMessage = YES; } + } else { + // we should never get any messages back that are already stored locally, + // since we add the last_message_id to the request } - } else { - // we should never get any messages back that are already stored locally, - // since we add the last_message_id to the request } - } }]; - + [self markSendInProgressMessagesAsPending]; - + [self sortFeedbackList]; [self updateLastMessageID]; - + // we got a new incoming message, trigger user notification system if (newMessage) { // check if the latest message is from the users own email address, then don't show an alert since he answered using his own email BOOL latestMessageFromUser = NO; - + BITFeedbackMessage *latestMessage = [self lastMessageHavingID]; if (self.userEmail && latestMessage.email && [self.userEmail compare:latestMessage.email] == NSOrderedSame) latestMessageFromUser = YES; - + if (!latestMessageFromUser) { - if([self.delegate respondsToSelector:@selector(feedbackManagerDidReceiveNewFeedback:)]) { + if ([self.delegate respondsToSelector:@selector(feedbackManagerDidReceiveNewFeedback:)]) { [self.delegate feedbackManagerDidReceiveNewFeedback:self]; } - - if(self.showAlertOnIncomingMessages && !self.currentFeedbackListViewController && !self.currentFeedbackComposeViewController) { + + if (self.showAlertOnIncomingMessages && !self.currentFeedbackListViewController && !self.currentFeedbackComposeViewController) { dispatch_async(dispatch_get_main_queue(), ^{ - /* - // Requires iOS 8 - id uialertcontrollerClass = NSClassFromString(@"UIAlertController"); - if (uialertcontrollerClass) { - UIAlertController *alertController = [UIAlertController alertControllerWithTitle:BITHockeyLocalizedString(@"HockeyFeedbackNewMessageTitle") - message:BITHockeyLocalizedString(@"HockeyFeedbackNewMessageText") - preferredStyle:UIAlertControllerStyleAlert]; - - UIAlertAction *cancelAction = [UIAlertAction actionWithTitle:BITHockeyLocalizedString(@"HockeyFeedbackIgnore") - style:UIAlertActionStyleCancel - handler:nil]; - UIAlertAction *showAction = [UIAlertAction actionWithTitle:BITHockeyLocalizedString(@"HockeyFeedbackShow") - style:UIAlertActionStyleDefault - handler:^(UIAlertAction *__nonnull action) { - [self showFeedbackListView]; - }]; - [alertController addAction:cancelAction]; - [alertController addAction:showAction]; - - [self showAlertController:alertController]; - } else { - */ + /* + // Requires iOS 8 + id uialertcontrollerClass = NSClassFromString(@"UIAlertController"); + if (uialertcontrollerClass) { + UIAlertController *alertController = [UIAlertController alertControllerWithTitle:BITHockeyLocalizedString(@"HockeyFeedbackNewMessageTitle") + message:BITHockeyLocalizedString(@"HockeyFeedbackNewMessageText") + preferredStyle:UIAlertControllerStyleAlert]; + + UIAlertAction *cancelAction = [UIAlertAction actionWithTitle:BITHockeyLocalizedString(@"HockeyFeedbackIgnore") + style:UIAlertActionStyleCancel + handler:nil]; + UIAlertAction *showAction = [UIAlertAction actionWithTitle:BITHockeyLocalizedString(@"HockeyFeedbackShow") + style:UIAlertActionStyleDefault + handler:^(UIAlertAction *__nonnull action) { + [self showFeedbackListView]; + }]; + [alertController addAction:cancelAction]; + [alertController addAction:showAction]; + + [self showAlertController:alertController]; + } else { + */ #pragma clang diagnostic push #pragma clang diagnostic ignored "-Wdeprecated-declarations" - UIAlertView *alertView = [[UIAlertView alloc] initWithTitle:BITHockeyLocalizedString(@"HockeyFeedbackNewMessageTitle") - message:BITHockeyLocalizedString(@"HockeyFeedbackNewMessageText") - delegate:self - cancelButtonTitle:BITHockeyLocalizedString(@"HockeyFeedbackIgnore") - otherButtonTitles:BITHockeyLocalizedString(@"HockeyFeedbackShow"), nil - ]; - [alertView setTag:0]; - [alertView show]; + UIAlertView *alertView = [[UIAlertView alloc] initWithTitle:BITHockeyLocalizedString(@"HockeyFeedbackNewMessageTitle") + message:BITHockeyLocalizedString(@"HockeyFeedbackNewMessageText") + delegate:self + cancelButtonTitle:BITHockeyLocalizedString(@"HockeyFeedbackIgnore") + otherButtonTitles:BITHockeyLocalizedString(@"HockeyFeedbackShow"), nil + ]; + [alertView setTag:0]; + [alertView show]; #pragma clang diagnostic pop - /*}*/ - _incomingMessagesAlertShowing = YES; + /*}*/ + _incomingMessagesAlertShowing = YES; }); } } } - + NSInteger pendingMessagesCountAfterProcessing = [[self messagesWithStatus:BITFeedbackMessageStatusSendPending] count]; - + // check if this request was successful and we have more messages pending and continue if positive if (pendingMessagesCount > pendingMessagesCountAfterProcessing && pendingMessagesCountAfterProcessing > 0) { [self performSelector:@selector(submitPendingMessages) withObject:nil afterDelay:0.1]; } - + } else { [self markSendInProgressMessagesAsPending]; } - + [self saveMessages]; - + return; } - (void)sendNetworkRequestWithHTTPMethod:(NSString *)httpMethod withMessage:(BITFeedbackMessage *)message completionHandler:(void (^)(NSError *error))completionHandler { NSString *boundary = @"----FOO"; - + _networkRequestInProgress = YES; // inform the UI to update its data in case the list is already showing [[NSNotificationCenter defaultCenter] postNotificationName:BITHockeyFeedbackMessagesLoadingStarted object:nil]; - + NSString *tokenParameter = @""; if ([self token]) { tokenParameter = [NSString stringWithFormat:@"/%@", [self token]]; } NSMutableString *parameter = [NSMutableString stringWithFormat:@"api/2/apps/%@/feedback%@", [self encodedAppIdentifier], tokenParameter]; - + NSString *lastMessageID = @""; if (!self.lastMessageID) { [self updateLastMessageID]; } if (self.lastMessageID) { - lastMessageID = [NSString stringWithFormat:@"&last_message_id=%li", (long)[self.lastMessageID integerValue]]; + lastMessageID = [NSString stringWithFormat:@"&last_message_id=%li", (long) [self.lastMessageID integerValue]]; } - + [parameter appendFormat:@"?format=json&bundle_version=%@&sdk=%@&sdk_version=%@%@", - bit_URLEncodedString([[NSBundle mainBundle] objectForInfoDictionaryKey:@"CFBundleVersion"]), - BITHOCKEY_NAME, - BITHOCKEY_VERSION, - lastMessageID - ]; - + bit_URLEncodedString([[NSBundle mainBundle] objectForInfoDictionaryKey:@"CFBundleVersion"]), + BITHOCKEY_NAME, + BITHOCKEY_VERSION, + lastMessageID + ]; + // build request & send NSString *url = [NSString stringWithFormat:@"%@%@", self.serverURL, parameter]; BITHockeyLogDebug(@"INFO: sending api request to %@", url); - + NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:[NSURL URLWithString:url] cachePolicy:1 timeoutInterval:10.0]; [request setHTTPMethod:httpMethod]; [request setValue:@"Hockey/iOS" forHTTPHeaderField:@"User-Agent"]; [request setValue:@"gzip" forHTTPHeaderField:@"Accept-Encoding"]; - + if (message) { NSString *contentType = [NSString stringWithFormat:@"multipart/form-data; boundary=%@", boundary]; [request setValue:contentType forHTTPHeaderField:@"Content-type"]; - + NSMutableData *postBody = [NSMutableData data]; - + [postBody appendData:[BITHockeyAppClient dataWithPostValue:@"Apple" forKey:@"oem" boundary:boundary]]; [postBody appendData:[BITHockeyAppClient dataWithPostValue:[[UIDevice currentDevice] systemVersion] forKey:@"os_version" boundary:boundary]]; [postBody appendData:[BITHockeyAppClient dataWithPostValue:[self getDevicePlatform] forKey:@"model" boundary:boundary]]; @@ -953,12 +947,12 @@ - (void)sendNetworkRequestWithHTTPMethod:(NSString *)httpMethod withMessage:(BIT [postBody appendData:[BITHockeyAppClient dataWithPostValue:[[NSBundle mainBundle] objectForInfoDictionaryKey:@"CFBundleVersion"] forKey:@"bundle_version" boundary:boundary]]; [postBody appendData:[BITHockeyAppClient dataWithPostValue:[message text] forKey:@"text" boundary:boundary]]; [postBody appendData:[BITHockeyAppClient dataWithPostValue:[message token] forKey:@"message_token" boundary:boundary]]; - + NSString *installString = bit_appAnonID(NO); if (installString) { [postBody appendData:[BITHockeyAppClient dataWithPostValue:installString forKey:@"install_string" boundary:boundary]]; } - + if (self.userID) { [postBody appendData:[BITHockeyAppClient dataWithPostValue:self.userID forKey:@"user_string" boundary:boundary]]; } @@ -968,59 +962,59 @@ - (void)sendNetworkRequestWithHTTPMethod:(NSString *)httpMethod withMessage:(BIT if (self.userEmail) { [postBody appendData:[BITHockeyAppClient dataWithPostValue:self.userEmail forKey:@"email" boundary:boundary]]; } - - + + NSInteger photoIndex = 0; - - for (BITFeedbackMessageAttachment *attachment in message.attachments){ - NSString *key = [NSString stringWithFormat:@"attachment%ld", (long)photoIndex]; - + + for (BITFeedbackMessageAttachment *attachment in message.attachments) { + NSString *key = [NSString stringWithFormat:@"attachment%ld", (long) photoIndex]; + NSString *filename = attachment.originalFilename; - + if (!filename) { - filename = [NSString stringWithFormat:@"Attachment %ld", (long)photoIndex]; + filename = [NSString stringWithFormat:@"Attachment %ld", (long) photoIndex]; } - + [postBody appendData:[BITHockeyAppClient dataWithPostValue:attachment.data forKey:key contentType:attachment.contentType boundary:boundary filename:filename]]; - + photoIndex++; } - + [postBody appendData:[[NSString stringWithFormat:@"--%@--\r\n", boundary] dataUsingEncoding:NSUTF8StringEncoding]]; - - + + [request setHTTPBody:postBody]; } - __weak typeof (self) weakSelf = self; + __weak typeof(self) weakSelf = self; if ([BITHockeyHelper isURLSessionSupported]) { NSURLSessionConfiguration *sessionConfiguration = [NSURLSessionConfiguration defaultSessionConfiguration]; __block NSURLSession *session = [NSURLSession sessionWithConfiguration:sessionConfiguration]; - + NSURLSessionDataTask *task = [session dataTaskWithRequest:request - completionHandler: ^(NSData *data, NSURLResponse *response, NSError *error) { - typeof (self) strongSelf = weakSelf; - - [session finishTasksAndInvalidate]; - - [strongSelf handleFeedbackMessageResponse:response data:data error:error completion:completionHandler]; + completionHandler:^(NSData *data, NSURLResponse *response, NSError *error) { + typeof(self) strongSelf = weakSelf; + + [session finishTasksAndInvalidate]; + + [strongSelf handleFeedbackMessageResponse:response data:data error:error completion:completionHandler]; }]; [task resume]; - + } else { #pragma clang diagnostic push #pragma clang diagnostic ignored "-Wdeprecated-declarations" [NSURLConnection sendAsynchronousRequest:request queue:[NSOperationQueue mainQueue] completionHandler:^(NSURLResponse *response, NSData *responseData, NSError *error) { #pragma clang diagnostic pop - typeof (self) strongSelf = weakSelf; - [strongSelf handleFeedbackMessageResponse:response data:responseData error:error completion:completionHandler]; + typeof(self) strongSelf = weakSelf; + [strongSelf handleFeedbackMessageResponse:response data:responseData error:error completion:completionHandler]; }]; } - + } -- (void)handleFeedbackMessageResponse:(NSURLResponse *)response data:(NSData *)responseData error:(NSError * )error completion:(void (^)(NSError *error))completionHandler{ +- (void)handleFeedbackMessageResponse:(NSURLResponse *)response data:(NSData *)responseData error:(NSError *)error completion:(void (^)(NSError *error))completionHandler { _networkRequestInProgress = NO; - + if (error) { [self reportError:error]; [self markSendInProgressMessagesAsPending]; @@ -1028,41 +1022,41 @@ - (void)handleFeedbackMessageResponse:(NSURLResponse *)response data:(NSData *)r completionHandler(error); } } else { - NSInteger statusCode = [(NSHTTPURLResponse *)response statusCode]; + NSInteger statusCode = [(NSHTTPURLResponse *) response statusCode]; if (statusCode == 404) { // thread has been deleted, we archive it [self updateMessageListFromResponse:nil]; } else if (statusCode == 409) { // we submitted a message that is already on the server, mark it as being in conflict and resolve it with another fetch - + if (!self.token) { // set the token to the first message token, since this is identical __block NSString *token = nil; - + [_feedbackList enumerateObjectsUsingBlock:^(id objMessage, NSUInteger messagesIdx, BOOL *stop) { - if ([(BITFeedbackMessage *)objMessage status] == BITFeedbackMessageStatusSendInProgress) { - token = [(BITFeedbackMessage *)objMessage token]; - *stop = YES; - } + if ([(BITFeedbackMessage *) objMessage status] == BITFeedbackMessageStatusSendInProgress) { + token = [(BITFeedbackMessage *) objMessage token]; + *stop = YES; + } }]; - + if (token) { self.token = token; } } - + [self markSendInProgressMessagesAsInConflict]; [self saveMessages]; [self performSelector:@selector(fetchMessageUpdates) withObject:nil afterDelay:0.2]; } else if ([responseData length]) { - NSString *responseString = [[NSString alloc] initWithBytes:[responseData bytes] length:[responseData length] encoding: NSUTF8StringEncoding]; + NSString *responseString = [[NSString alloc] initWithBytes:[responseData bytes] length:[responseData length] encoding:NSUTF8StringEncoding]; BITHockeyLogDebug(@"INFO: Received API response: %@", responseString); - + if (responseString && [responseString dataUsingEncoding:NSUTF8StringEncoding]) { NSError *error = NULL; - - NSDictionary *feedDict = (NSDictionary *)[NSJSONSerialization JSONObjectWithData:[responseString dataUsingEncoding:NSUTF8StringEncoding] options:0 error:&error]; - + + NSDictionary *feedDict = (NSDictionary *) [NSJSONSerialization JSONObjectWithData:[responseString dataUsingEncoding:NSUTF8StringEncoding] options:0 error:&error]; + // server returned empty response? if (error) { [self reportError:error]; @@ -1083,7 +1077,7 @@ - (void)handleFeedbackMessageResponse:(NSURLResponse *)response data:(NSData *)r } } } - + [self markSendInProgressMessagesAsPending]; if (completionHandler) { completionHandler(error); @@ -1095,15 +1089,15 @@ - (void)fetchMessageUpdates { if ([_feedbackList count] == 0 && !self.token) { // inform the UI to update its data in case the list is already showing [[NSNotificationCenter defaultCenter] postNotificationName:BITHockeyFeedbackMessagesLoadingFinished object:nil]; - + return; } - + [self sendNetworkRequestWithHTTPMethod:@"GET" withMessage:nil - completionHandler:^(NSError *error){ - // inform the UI to update its data in case the list is already showing - [[NSNotificationCenter defaultCenter] postNotificationName:BITHockeyFeedbackMessagesLoadingFinished object:nil]; + completionHandler:^(NSError *error) { + // inform the UI to update its data in case the list is already showing + [[NSNotificationCenter defaultCenter] postNotificationName:BITHockeyFeedbackMessagesLoadingFinished object:nil]; }]; } @@ -1113,17 +1107,17 @@ - (void)submitPendingMessages { [self performSelector:@selector(submitPendingMessages) withObject:nil afterDelay:2.0f]; return; } - + // app defined user data may have changed, update it [self updateAppDefinedUserData]; [self saveMessages]; - + NSArray *pendingMessages = [self messagesWithStatus:BITFeedbackMessageStatusSendPending]; - + if ([pendingMessages count] > 0) { // we send one message at a time BITFeedbackMessage *messageToSend = pendingMessages[0]; - + [messageToSend setStatus:BITFeedbackMessageStatusSendInProgress]; if (self.userID) [messageToSend setUserID:self.userID]; @@ -1131,22 +1125,22 @@ - (void)submitPendingMessages { [messageToSend setName:self.userName]; if (self.userEmail) [messageToSend setEmail:self.userEmail]; - + NSString *httpMethod = @"POST"; if ([self token]) { httpMethod = @"PUT"; } - + [self sendNetworkRequestWithHTTPMethod:httpMethod withMessage:messageToSend - completionHandler:^(NSError *error){ - if (error) { - [self markSendInProgressMessagesAsPending]; - [self saveMessages]; - } - - // inform the UI to update its data in case the list is already showing - [[NSNotificationCenter defaultCenter] postNotificationName:BITHockeyFeedbackMessagesLoadingFinished object:nil]; + completionHandler:^(NSError *error) { + if (error) { + [self markSendInProgressMessagesAsPending]; + [self saveMessages]; + } + + // inform the UI to update its data in case the list is already showing + [[NSNotificationCenter defaultCenter] postNotificationName:BITHockeyFeedbackMessagesLoadingFinished object:nil]; }]; } } @@ -1158,9 +1152,9 @@ - (void)submitMessageWithText:(NSString *)text andAttachments:(NSArray *)attachm [message setToken:[self uuidAsLowerCaseAndShortened]]; [message setAttachments:attachments]; [message setUserMessage:YES]; - + [_feedbackList addObject:message]; - + [self submitPendingMessages]; } @@ -1169,85 +1163,132 @@ - (void)submitMessageWithText:(NSString *)text andAttachments:(NSArray *)attachm #pragma clang diagnostic push #pragma clang diagnostic ignored "-Wdeprecated-declarations" + // invoke the selected action from the action sheet for a location element - (void)alertView:(UIAlertView *)alertView didDismissWithButtonIndex:(NSInteger)buttonIndex { - + _incomingMessagesAlertShowing = NO; if (buttonIndex == [alertView firstOtherButtonIndex]) { // Show button has been clicked [self showFeedbackListView]; } } + #pragma clang diagnostic pop #pragma mark - Observation Handling - (void)setFeedbackObservationMode:(BITFeedbackObservationMode)feedbackObservationMode { + //Ignore if feedback manager is disabled + if ([self isFeedbackManagerDisabled]) return; + if (feedbackObservationMode != _feedbackObservationMode) { _feedbackObservationMode = feedbackObservationMode; - - if (feedbackObservationMode == BITFeedbackObservationModeOnScreenshot){ - if (NSFoundationVersionNumber > NSFoundationVersionNumber_iOS_6_1){ - [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(screenshotNotificationReceived:) name:UIApplicationUserDidTakeScreenshotNotification object:nil]; - } else { - BITHockeyLogWarning(@"WARNING: BITFeedbackObservationModeOnScreenshot requires iOS 7 or later."); + + // Reset the other observation modes. + if (feedbackObservationMode == BITFeedbackObservationNone) { + if (self.observationModeOnScreenshotEnabled) { + [self setObservationModeOnScreenshotEnabled:NO]; + } + if (self.observationModeThreeFingerTapEnabled) { + [self setObservationModeThreeFingerTapEnabled:NO]; } - - self.screenshotNotificationEnabled = YES; - - if (self.tapRecognizer){ - [[[UIApplication sharedApplication] keyWindow] removeGestureRecognizer:self.tapRecognizer]; - self.tapRecognizer = nil; + BITHockeyLogVerbose(@"Set feedbackObservationMode to BITFeedbackObservationNone"); + } + + if (feedbackObservationMode == BITFeedbackObservationModeOnScreenshot) { + [self setObservationModeOnScreenshotEnabled:YES]; + if (self.observationModeThreeFingerTapEnabled) { + [self setObservationModeThreeFingerTapEnabled:NO]; + } + } + + if (feedbackObservationMode == BITFeedbackObservationModeThreeFingerTap) { + [self setObservationModeThreeFingerTapEnabled:YES]; + if (self.observationModeOnScreenshotEnabled) { + [self setObservationModeOnScreenshotEnabled:NO]; } } - - if (feedbackObservationMode == BITFeedbackObservationModeThreeFingerTap){ - if (!self.tapRecognizer){ - self.tapRecognizer = [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(screenshotTripleTap:)]; - self.tapRecognizer.numberOfTouchesRequired = 3; - self.tapRecognizer.delegate = self; - - dispatch_async(dispatch_get_main_queue(), ^{ + + if (feedbackObservationMode == BITFeedbackObservationModeAll) { + [self setObservationModeOnScreenshotEnabled:YES]; + [self setObservationModeThreeFingerTapEnabled:YES]; + } + } +} + +- (void)setObservationModeOnScreenshotEnabled:(BOOL)observationModeOnScreenshotEnabled { + if (NSFoundationVersionNumber > NSFoundationVersionNumber_iOS_6_1) { + // Enable/disable screenshot notification + if (observationModeOnScreenshotEnabled) { + [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(screenshotNotificationReceived:) name:UIApplicationUserDidTakeScreenshotNotification object:nil]; + BITHockeyLogVerbose(@"Added observer for UIApplocationUserDidTakeScreenshotNotification."); + } else { + [[NSNotificationCenter defaultCenter] removeObserver:self name:UIApplicationUserDidTakeScreenshotNotification object:nil]; + BITHockeyLogVerbose(@"Removed observer for UIApplocationUserDidTakeScreenshotNotification."); + } + } else { + if (observationModeOnScreenshotEnabled) { + BITHockeyLogWarning(@"BITFeedbackObservationModeOnScreenshot requires iOS 7 or later."); + } + } + + _observationModeOnScreenshotEnabled = observationModeOnScreenshotEnabled; + + BITHockeyLogVerbose(@"Enabled BITFeedbackObservationModeOnScreenshot."); +} + +- (void)setObservationModeThreeFingerTapEnabled:(BOOL)observationModeThreeFingerTapEnabled { + _observationModeThreeFingerTapEnabled = observationModeThreeFingerTapEnabled; + + if (observationModeThreeFingerTapEnabled) { + if (!self.tapRecognizer) { + self.tapRecognizer = [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(screenshotTripleTap:)]; + self.tapRecognizer.numberOfTouchesRequired = 3; + self.tapRecognizer.delegate = self; + + dispatch_async(dispatch_get_main_queue(), ^{ if (self.tapRecognizer) { [[UIApplication sharedApplication].keyWindow addGestureRecognizer:self.tapRecognizer]; } - }); - } - - if (self.screenshotNotificationEnabled){ - if (NSFoundationVersionNumber > NSFoundationVersionNumber_iOS_6_1){ - [[NSNotificationCenter defaultCenter] removeObserver:self name:UIApplicationUserDidTakeScreenshotNotification object:nil]; - self.screenshotNotificationEnabled = NO; - } - } + }); } + + BITHockeyLogVerbose(@"Enabled BITFeedbackObservationModeThreeFingerTap."); + } else { + [[[UIApplication sharedApplication] keyWindow] removeGestureRecognizer:self.tapRecognizer]; + self.tapRecognizer = nil; + BITHockeyLogVerbose(@"Disabled BITFeedbackObservationModeThreeFingerTap."); } } --(void)screenshotNotificationReceived:(NSNotification *)notification { +- (void)screenshotNotificationReceived:(NSNotification *)notification { + // Don't do anything if FeedbackManager was disabled. + if ([self isFeedbackManagerDisabled]) return; + double amountOfSeconds = 1.5; - dispatch_time_t delayTime = dispatch_time(DISPATCH_TIME_NOW, (int64_t)(amountOfSeconds * NSEC_PER_SEC)); - + dispatch_time_t delayTime = dispatch_time(DISPATCH_TIME_NOW, (int64_t) (amountOfSeconds * NSEC_PER_SEC)); + dispatch_after(delayTime, dispatch_get_main_queue(), ^{ - [self extractLastPictureFromLibraryAndLaunchFeedback]; + [self extractLastPictureFromLibraryAndLaunchFeedback]; }); } --(void)extractLastPictureFromLibraryAndLaunchFeedback { +- (void)extractLastPictureFromLibraryAndLaunchFeedback { [self requestLatestImageWithCompletionHandler:^(UIImage *latestImage) { - [self showFeedbackComposeViewWithPreparedItems:@[latestImage]]; + [self showFeedbackComposeViewWithPreparedItems:@[latestImage]]; }]; } - (void)requestLatestImageWithCompletionHandler:(BITLatestImageFetchCompletionBlock)completionHandler { - if (!completionHandler) { return; } - + if (!completionHandler) {return;} + // Safeguard in case the dev hasn't set the NSPhotoLibraryUsageDescription in their Info.plist - if(![self isiOS10PhotoPolicySet]) { return; } - + if (![self isiOS10PhotoPolicySet]) {return;} + // Only available from iOS 8 up - id phimagemanagerClass = NSClassFromString(@"PHImageManager"); - if (phimagemanagerClass) { + id phImageManagerClass = NSClassFromString(@"PHImageManager"); + if (phImageManagerClass) { [self fetchLatestImageUsingPhotoLibraryWithCompletionHandler:completionHandler]; } else { [self fetchLatestImageUsingAssetsLibraryWithCompletionHandler:completionHandler]; @@ -1259,72 +1300,72 @@ - (void)fetchLatestImageUsingAssetsLibraryWithCompletionHandler:(BITLatestImageF #pragma clang diagnostic ignored "-Wdeprecated-declarations" ALAssetsLibrary *library = [[ALAssetsLibrary alloc] init]; [library enumerateGroupsWithTypes:ALAssetsGroupSavedPhotos usingBlock:^(ALAssetsGroup *group, BOOL *stop) { - - [group setAssetsFilter:[ALAssetsFilter allPhotos]]; - - [group enumerateAssetsWithOptions:NSEnumerationReverse usingBlock:^(ALAsset *alAsset, NSUInteger index, BOOL *innerStop) { - - if (alAsset) { - ALAssetRepresentation *representation = [alAsset defaultRepresentation]; - UIImage *latestPhoto = [UIImage imageWithCGImage:[representation fullScreenImage]]; - - completionHandler(latestPhoto); - - *stop = YES; - *innerStop = YES; - } - }]; - } failureBlock: nil]; + + [group setAssetsFilter:[ALAssetsFilter allPhotos]]; + + [group enumerateAssetsWithOptions:NSEnumerationReverse usingBlock:^(ALAsset *alAsset, NSUInteger index, BOOL *innerStop) { + + if (alAsset) { + ALAssetRepresentation *representation = [alAsset defaultRepresentation]; + UIImage *latestPhoto = [UIImage imageWithCGImage:[representation fullScreenImage]]; + + completionHandler(latestPhoto); + + *stop = YES; + *innerStop = YES; + } + }]; + } failureBlock:nil]; #pragma clang diagnostic pop } -- (void)fetchLatestImageUsingPhotoLibraryWithCompletionHandler:(BITLatestImageFetchCompletionBlock)completionHandler NS_AVAILABLE_IOS(8_0){ +- (void)fetchLatestImageUsingPhotoLibraryWithCompletionHandler:(BITLatestImageFetchCompletionBlock)completionHandler NS_AVAILABLE_IOS(8_0) { // Safeguard in case the dev hasn't set the NSPhotoLibraryUsageDescription in their Info.plist - if(![self isiOS10PhotoPolicySet]) { return; } - + if (![self isiOS10PhotoPolicySet]) {return;} + [PHPhotoLibrary requestAuthorization:^(PHAuthorizationStatus status) { - switch (status) { - case PHAuthorizationStatusDenied: - case PHAuthorizationStatusRestricted: - BITHockeyLogDebug(@"INFO: The latest image could not be fetched, no permissions."); - break; - - case PHAuthorizationStatusAuthorized: - [self loadLatestImageAssetWithCompletionHandler:completionHandler]; - break; - case PHAuthorizationStatusNotDetermined: - BITHockeyLogDebug(@"INFO: The Photo Library authorization status is undetermined. This should not happen."); - break; - } + switch (status) { + case PHAuthorizationStatusDenied: + case PHAuthorizationStatusRestricted: + BITHockeyLogDebug(@"INFO: The latest image could not be fetched, no permissions."); + break; + + case PHAuthorizationStatusAuthorized: + [self loadLatestImageAssetWithCompletionHandler:completionHandler]; + break; + case PHAuthorizationStatusNotDetermined: + BITHockeyLogDebug(@"INFO: The Photo Library authorization status is undetermined. This should not happen."); + break; + } }]; } -- (void)loadLatestImageAssetWithCompletionHandler:(BITLatestImageFetchCompletionBlock)completionHandler NS_AVAILABLE_IOS(8_0){ - +- (void)loadLatestImageAssetWithCompletionHandler:(BITLatestImageFetchCompletionBlock)completionHandler NS_AVAILABLE_IOS(8_0) { + // Safeguard in case the dev hasn't set the NSPhotoLibraryUsageDescription in their Info.plist - if(![self isiOS10PhotoPolicySet]) { return; } - + if (![self isiOS10PhotoPolicySet]) {return;} + PHImageManager *imageManager = PHImageManager.defaultManager; - + PHFetchOptions *fetchOptions = [PHFetchOptions new]; fetchOptions.sortDescriptors = @[[NSSortDescriptor sortDescriptorWithKey:@"creationDate" ascending:YES]]; - + PHFetchResult *fetchResult = [PHAsset fetchAssetsWithMediaType:PHAssetMediaTypeImage options:fetchOptions]; - + if (fetchResult.count > 0) { - PHAsset *latestImageAsset = (PHAsset *)fetchResult.lastObject; + PHAsset *latestImageAsset = (PHAsset *) fetchResult.lastObject; if (latestImageAsset) { PHImageRequestOptions *options = [PHImageRequestOptions new]; options.version = PHImageRequestOptionsVersionOriginal; options.deliveryMode = PHImageRequestOptionsDeliveryModeHighQualityFormat; options.resizeMode = PHImageRequestOptionsResizeModeNone; - - [imageManager requestImageDataForAsset:latestImageAsset options:options resultHandler:^(NSData * _Nullable imageData, NSString * _Nullable dataUTI, UIImageOrientation orientation, NSDictionary * _Nullable info) { - if (imageData) { - completionHandler([UIImage imageWithData:imageData]); - } else { - BITHockeyLogDebug(@"INFO: The latest image could not be fetched, requested image data was empty."); - } + + [imageManager requestImageDataForAsset:latestImageAsset options:options resultHandler:^(NSData *_Nullable imageData, NSString *_Nullable dataUTI, UIImageOrientation orientation, NSDictionary *_Nullable info) { + if (imageData) { + completionHandler([UIImage imageWithData:imageData]); + } else { + BITHockeyLogDebug(@"INFO: The latest image could not be fetched, requested image data was empty."); + } }]; } } else { @@ -1333,7 +1374,10 @@ - (void)loadLatestImageAssetWithCompletionHandler:(BITLatestImageFetchCompletion } - (void)screenshotTripleTap:(UITapGestureRecognizer *)tapRecognizer { - if (tapRecognizer.state == UIGestureRecognizerStateRecognized){ + // Don't do anything if FeedbackManager was disabled. + if ([self isFeedbackManagerDisabled]) return; + + if (tapRecognizer.state == UIGestureRecognizerStateRecognized) { [self showFeedbackComposeViewWithGeneratedScreenshot]; } } @@ -1345,5 +1389,4 @@ - (BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer shouldRecogni @end - #endif /* HOCKEYSDK_FEATURE_FEEDBACK */ diff --git a/Classes/BITFeedbackManagerPrivate.h b/Classes/BITFeedbackManagerPrivate.h index 8782a746..e89c2357 100644 --- a/Classes/BITFeedbackManagerPrivate.h +++ b/Classes/BITFeedbackManagerPrivate.h @@ -34,6 +34,8 @@ extern NSString *const kBITFeedbackUpdateAttachmentThumbnail; #import "BITFeedbackMessage.h" +@class UITapGestureRecognizer; + @interface BITFeedbackManager () { } @@ -61,6 +63,11 @@ extern NSString *const kBITFeedbackUpdateAttachmentThumbnail; // used by BITHockeyManager if disable status is changed @property (nonatomic, getter = isFeedbackManagerDisabled) BOOL disableFeedbackManager; +// TapRecognizer used in case feedback observation mode is BITFeedbackObservationModeThreeFingerTap is set. +@property(nonatomic, strong) UITapGestureRecognizer *tapRecognizer; +@property(nonatomic) BOOL observationModeOnScreenshotEnabled; +@property(nonatomic) BOOL observationModeThreeFingerTapEnabled; + @property (nonatomic, strong) BITFeedbackListViewController *currentFeedbackListViewController; @property (nonatomic, strong) BITFeedbackComposeViewController *currentFeedbackComposeViewController; @@ -75,6 +82,7 @@ extern NSString *const kBITFeedbackUpdateAttachmentThumbnail; @property (nonatomic, copy) NSString *userEmail; + // Fetch user meta data - (BOOL)updateUserIDUsingKeychainAndDelegate; - (BOOL)updateUserNameUsingKeychainAndDelegate; diff --git a/Classes/BITFeedbackMessageAttachment.m b/Classes/BITFeedbackMessageAttachment.m index e1b9c884..05d4f3ac 100644 --- a/Classes/BITFeedbackMessageAttachment.m +++ b/Classes/BITFeedbackMessageAttachment.m @@ -95,7 +95,7 @@ - (instancetype)init { - (void)setData:(NSData *)data { self->_internalData = data; self.filename = [self possibleFilename]; - [self->_internalData writeToFile:self.filename atomically:NO]; + [self->_internalData writeToFile:self.filename atomically:YES]; } - (NSData *)data { diff --git a/HockeySDK-Source.podspec b/HockeySDK-Source.podspec index e4fe2989..7371e7b6 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.3' + s.version = '4.1.4' 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="@\\"97\\"" BITHOCKEY_C_BUILD="\\"98\\""} } + s.pod_target_xcconfig = {'GCC_PREPROCESSOR_DEFINITIONS' => %{$(inherited) BITHOCKEY_VERSION="@\\"#{s.version}\\"" BITHOCKEY_C_VERSION="\\"#{s.version}\\"" BITHOCKEY_BUILD="@\\"99\\"" BITHOCKEY_C_BUILD="\\"99\\""} } 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 cd78a10d..9fa06ec3 100644 --- a/HockeySDK.podspec +++ b/HockeySDK.podspec @@ -1,6 +1,6 @@ Pod::Spec.new do |s| s.name = 'HockeySDK' - s.version = '4.1.3' + s.version = '4.1.4' 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 0e77e526..2df2cf0a 100644 --- a/README.md +++ b/README.md @@ -2,9 +2,9 @@ [![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.3 +## Version 4.1.4 -- [Changelog](http://www.hockeyapp.net/help/sdk/ios/4.1.3/docs/docs/Changelog.html) +- [Changelog](http://www.hockeyapp.net/help/sdk/ios/4.1.4/docs/docs/Changelog.html) **NOTE** If your are using the binary integration of our SDK, make sure that the `HockeySDKResources.bundle` inside the `HockeySDK.embeddedframework`-folder has been added to your application. @@ -165,7 +165,7 @@ Our examples will use the **default** SDK (`HockeySDK.embeddedframework`). 4. Add the following lines to setup and start the HockeyApp SDK: ```swift - BITHockeyManager.shared().configure(withIdentifier: "cdef2c2b4ddf420b9cdf470a9667eb27") + BITHockeyManager.shared().configure(withIdentifier: "APP_IDENTIFIER") BITHockeyManager.shared().start() BITHockeyManager.shared().authenticator.authenticateInstallation() // This line is obsolete in the crash only builds @@ -245,16 +245,22 @@ pod "HockeySDK" #### 3.2.1 Binary Distribution Options -The default and recommended distribution is a binary (static library) and a resource bundle with translations and images for all SDK Features: Crash Reporting, User Feedback, Store Updates, Authentication, AdHoc Updates. +The default and recommended distribution is a binary (static library) and a resource bundle with translations and images for all SDK Features. -You can alternative use a Crash Reporting build only by using the following line in your `Podfile`: +```ruby +platform :ios, '8.0' +pod "HockeySDK" +``` + +Will integrate the *default* configuration of the SDK, with all features except the Feedback feature. -For the SDK with all features, add +For the SDK with all features, including Feedback, add ```ruby pod "HockeySDK", :subspecs => ['AllFeaturesLib'] ``` to your podfile. + To add the variant that only includes crash reporting, use ```ruby @@ -269,7 +275,7 @@ pod "HockeySDK", :subspecs => ['CrashOnlyExtensionsLib'] #### 3.2.2 Source Integration Options -Alternatively you can integrate the SDK by source if you want to do any modifications or want a different feature set. The following entry will integrate the SDK: +Alternatively you can integrate the SDK by source if you want to do modifications or want a different feature set. The following entry will integrate the SDK: ```ruby pod "HockeySDK-Source" @@ -286,7 +292,19 @@ To add HockeySDK to your project, simply put this line into your `Cartfile`: and then follow the steps described in the [Carthage documentation](https://github.com/Carthage/Carthage#if-youre-building-for-ios-tvos-or-watchos). -For now, this will integrate the **full-featured SDK** so you must include the `NSPhotoLibraryUsageDescription` and read the [feedback section](#feedback). +This will integrate the **full-featured SDK** so you must include the `NSPhotoLibraryUsageDescription` and read the [feedback section](#feedback). If you want to include any other version of the SDK, version 4.1.4 added the ability to do that. You need to specify the configuration that you want to use + +#### Version without Feedback + +`carthage build --platform iOS --configuration Default HockeySDK-iOS` + +#### Crash-only Version + +`carthage build --platform iOS --configuration ReleaseCrashOnly HockeySDK-iOS` + +### Crash-only extension + +`carthage build --platform iOS --configuration ReleaseCrashOnlyExtension HockeySDK-iOS` ### 3.4 iOS Extensions @@ -390,7 +408,7 @@ The following options only show some of possibilities to interact and fine-tune #### 3.6.1 Disable Crash Reporting The HockeySDK enables crash reporting **per default**. Crashes will be immediately sent to the server the next time the app is launched. -To provide you with the best crash reporting, we are using [PLCrashReporter]("https://github.com/plausiblelabs/plcrashreporter") in [Version 1.3 / Commit 05d34741d3a90bbed51214983110943831ae5943]("https://github.com/plausiblelabs/plcrashreporter/commit/05d34741d3a90bbed51214983110943831ae5943"). +To provide you with the best crash reporting, we are using a build of [PLCrashReporter]("https://github.com/plausiblelabs/plcrashreporter") based on [Version 1.2.1 / Commit 356901d7f3ca3d46fbc8640f469304e2b755e461]("https://github.com/plausiblelabs/plcrashreporter/commit/356901d7f3ca3d46fbc8640f469304e2b755e461"). This feature can be disabled as follows: @@ -635,7 +653,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.3/index.html). +Our documentation can be found on [HockeyApp](http://hockeyapp.net/help/sdk/ios/4.1.4/index.html). ## 5.Troubleshooting @@ -649,7 +667,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.3.docset` + - `de.bitstadium.HockeySDK-iOS-4.1.4.docset` ### Feature are not working as expected diff --git a/Support/HockeySDK.xcodeproj/project.pbxproj b/Support/HockeySDK.xcodeproj/project.pbxproj index 05355d32..f9780d12 100644 --- a/Support/HockeySDK.xcodeproj/project.pbxproj +++ b/Support/HockeySDK.xcodeproj/project.pbxproj @@ -884,6 +884,7 @@ 8034E6B61BA3567B00D83A30 /* BITDataTests.m */, 8034E6B71BA3567B00D83A30 /* BITDeviceTests.m */, 8034E6B81BA3567B00D83A30 /* BITDomainTests.m */, + 1E61CCAE18E0585A00A5E38E /* BITFeedbackManagerTests.m */, 8034E6B91BA3567B00D83A30 /* BITInternalTests.m */, 8034E6BA1BA3567B00D83A30 /* BITPersistenceTests.m */, 8034E6BC1BA3567B00D83A30 /* BITSenderTests.m */, @@ -895,7 +896,6 @@ 8034E6C31BA3567B00D83A30 /* BITUserTests.m */, 8034E6B31BA3567B00D83A30 /* BITAuthenticatorTests.m */, 8034E6BE1BA3567B00D83A30 /* BITStoreUpdateManagerTests.m */, - 1E61CCAE18E0585A00A5E38E /* BITFeedbackManagerTests.m */, E40E0B0817DA19DC005E38C1 /* BITHockeyAppClientTests.m */, 1E70A23517F31B82001BB32D /* BITHockeyHelperTests.m */, E4507E4217F0658F00171A0D /* BITKeychainUtilsTests.m */, @@ -1612,7 +1612,7 @@ isa = PBXProject; attributes = { LastTestingUpgradeCheck = 0600; - LastUpgradeCheck = 0810; + LastUpgradeCheck = 0820; TargetAttributes = { 1EB6173E1B0A30480035A986 = { CreatedOnToolsVersion = 6.3.1; @@ -1779,7 +1779,7 @@ ); runOnlyForDeploymentPostprocessing = 0; shellPath = /bin/sh; - shellScript = "#Original\n\n# Default config\n# Sets the target folders and the final framework product.\nFMK_NAME=HockeySDK\nFMK_RESOURCE_BUNDLE=HockeySDKResources\nFMK_iOS8_NAME=\"HockeySDK Framework\"\n\n# Documentation\nHOCKEYSDK_DOCSET_VERSION_NAME=\"de.bitstadium.${HOCKEYSDK_DOCSET_NAME}-${VERSION_STRING}\"\n\n# Install dir will be the final output to the framework.\n# The following line creates it in the root folder of the current project.\nPRODUCTS_DIR=${SRCROOT}/../Products\nZIP_FOLDER=HockeySDK-iOS\nTEMP_DIR=${PRODUCTS_DIR}/${ZIP_FOLDER}\nINSTALL_DIR=${TEMP_DIR}/${FMK_NAME}.framework\nALL_FEATURES_INSTALL_DIR=${TEMP_DIR}/HockeySDKAllFeatures/${FMK_NAME}.framework\n\n# Working dir will be deleted after the framework creation.\nWRK_DIR=build\nDEVICE_DIR=${WRK_DIR}/ReleaseDefault-iphoneos\nSIMULATOR_DIR=${WRK_DIR}/ReleaseDefault-iphonesimulator\nDEVICE_DIR_ALL_FEATURES=${WRK_DIR}/Release-iphoneos\nSIMULATOR_DIR_ALL_FEATURES=${WRK_DIR}/Release-iphonesimulator\nDEVICE_CRASH_ONLY_DIR=${WRK_DIR}/ReleaseCrashOnly-iphoneos\nSIMULATOR_CRASH_ONLY_DIR=${WRK_DIR}/ReleaseCrashOnly-iphonesimulator\nDEVICE_EXTENSIONS_CRASH_ONLY_DIR=${WRK_DIR}/ReleaseCrashOnlyExtensions-iphoneos\nSIMULATOR_EXTENSIONS_CRASH_ONLY_DIR=${WRK_DIR}/ReleaseCrashOnlyExtensions-iphonesimulator\nDEVICE_WATCH_CRASH_ONLY_DIR=${WRK_DIR}/ReleaseCrashOnlyWatchOS-iphoneos\nSIMULATOR_WATCH_CRASH_ONLY_DIR=${WRK_DIR}/ReleaseCrashOnlyWatchOS-iphonesimulator\n\n# //////////////////////////////\n# Building the SDK with all features except the Feedback Feature\n# //////////////////////////////\n\n# Building both architectures.\nxcodebuild -project \"HockeySDK.xcodeproj\" -configuration \"ReleaseDefault\" -target \"${FMK_NAME}\" -sdk iphoneos\nxcodebuild -project \"HockeySDK.xcodeproj\" -configuration \"ReleaseDefault\" -target \"${FMK_NAME}\" -sdk iphonesimulator\n\n# Cleaning the oldest.\nif [ -d \"${TEMP_DIR}\" ]\nthen\nrm -rf \"${TEMP_DIR}\"\nfi\n\n# Creates and renews the final product folder.\nmkdir -p \"${INSTALL_DIR}\"\nmkdir -p \"${INSTALL_DIR}/Headers\"\nmkdir -p \"${INSTALL_DIR}/Modules\"\n\n# Copy the swift import file\ncp -f \"${SRCROOT}/module_default.modulemap\" \"${INSTALL_DIR}/Modules/module.modulemap\"\n\n# Copies the headers and resources files to the final product folder.\ncp -R \"${SRCROOT}/${DEVICE_DIR}/include/HockeySDK/BITAuthenticator.h\" \"${INSTALL_DIR}/Headers/\"\ncp -R \"${SRCROOT}/${DEVICE_DIR}/include/HockeySDK/BITCrashAttachment.h\" \"${INSTALL_DIR}/Headers/\"\ncp -R \"${SRCROOT}/${DEVICE_DIR}/include/HockeySDK/BITCrashDetails.h\" \"${INSTALL_DIR}/Headers/\"\ncp -R \"${SRCROOT}/${DEVICE_DIR}/include/HockeySDK/BITCrashManager.h\" \"${INSTALL_DIR}/Headers/\"\ncp -R \"${SRCROOT}/${DEVICE_DIR}/include/HockeySDK/BITCrashManagerDelegate.h\" \"${INSTALL_DIR}/Headers/\"\ncp -R \"${SRCROOT}/${DEVICE_DIR}/include/HockeySDK/BITCrashMetaData.h\" \"${INSTALL_DIR}/Headers/\"\ncp -R \"${SRCROOT}/${DEVICE_DIR}/include/HockeySDK/BITHockeyAttachment.h\" \"${INSTALL_DIR}/Headers/\"\ncp -R \"${SRCROOT}/${DEVICE_DIR}/include/HockeySDK/BITHockeyBaseManager.h\" \"${INSTALL_DIR}/Headers/\"\ncp -R \"${SRCROOT}/${DEVICE_DIR}/include/HockeySDK/BITHockeyBaseViewController.h\" \"${INSTALL_DIR}/Headers/\"\ncp -R \"${SRCROOT}/${DEVICE_DIR}/include/HockeySDK/BITHockeyManager.h\" \"${INSTALL_DIR}/Headers/\"\ncp -R \"${SRCROOT}/${DEVICE_DIR}/include/HockeySDK/BITHockeyManagerDelegate.h\" \"${INSTALL_DIR}/Headers/\"\ncp -R \"${SRCROOT}/${DEVICE_DIR}/include/HockeySDK/BITMetricsManager.h\" \"${INSTALL_DIR}/Headers/\"\ncp -R \"${SRCROOT}/${DEVICE_DIR}/include/HockeySDK/BITStoreUpdateManager.h\" \"${INSTALL_DIR}/Headers/\"\ncp -R \"${SRCROOT}/${DEVICE_DIR}/include/HockeySDK/BITStoreUpdateManagerDelegate.h\" \"${INSTALL_DIR}/Headers/\"\ncp -R \"${SRCROOT}/${DEVICE_DIR}/include/HockeySDK/BITUpdateManager.h\" \"${INSTALL_DIR}/Headers/\"\ncp -R \"${SRCROOT}/${DEVICE_DIR}/include/HockeySDK/BITUpdateManagerDelegate.h\" \"${INSTALL_DIR}/Headers/\"\ncp -R \"${SRCROOT}/${DEVICE_DIR}/include/HockeySDK/BITUpdateViewController.h\" \"${INSTALL_DIR}/Headers/\"\ncp -R \"${SRCROOT}/${DEVICE_DIR}/include/HockeySDK/HockeySDK.h\" \"${INSTALL_DIR}/Headers/\"\ncp -R \"${SRCROOT}/${DEVICE_DIR}/include/HockeySDK/HockeySDKEnums.h\" \"${INSTALL_DIR}/Headers/\"\ncp -R \"${SRCROOT}/${DEVICE_DIR}/include/HockeySDK/HockeySDKFeatureConfig.h\" \"${INSTALL_DIR}/Headers/\"\ncp -R \"${SRCROOT}/${DEVICE_DIR}/include/HockeySDK/HockeySDKNullability.h\" \"${INSTALL_DIR}/Headers/\"\n\n# Copy the resource bundle\n#cp -R \"${DEVICE_DIR}/${FMK_RESOURCE_BUNDLE}.bundle\" \"${INSTALL_DIR}/Resources/\"\n\n# Copy the patched feature header\ncp -f \"${SRCROOT}/HockeySDKFeatureConfigDefault.h\" \"${INSTALL_DIR}/Headers/HockeySDKFeatureConfig.h\"\n\n# Uses the Lipo Tool to merge both binary files (i386 + armv6/armv7) into one Universal final product.\nlipo -create \"${DEVICE_DIR}/lib${FMK_NAME}.a\" \"${SIMULATOR_DIR}/lib${FMK_NAME}.a\" -output \"${INSTALL_DIR}/${FMK_NAME}\"\n\n# Combine the CrashReporter static library into a new Hockey static library file if they are not already present and copy the public headers too\nif [ -z $(otool -L \"${INSTALL_DIR}/${FMK_NAME}\" | grep 'libCrashReporter') ]\nthen\nlibtool -static -o \"${INSTALL_DIR}/${FMK_NAME}\" \"${INSTALL_DIR}/${FMK_NAME}\" \"${SRCROOT}/../Vendor/CrashReporter.framework/Versions/A/CrashReporter\"\nfi\n\n# build embeddedframework folder and move framework into it\nmkdir \"${INSTALL_DIR}/../${FMK_NAME}.embeddedframework\"\nmv \"${INSTALL_DIR}\" \"${INSTALL_DIR}/../${FMK_NAME}.embeddedframework/${FMK_NAME}.framework\"\nmv \"${DEVICE_DIR}/${FMK_RESOURCE_BUNDLE}.bundle\" \"${TEMP_DIR}/${FMK_NAME}.embeddedframework/\"\n\nrm -r \"${WRK_DIR}\"\n\n# //////////////////////////////\n# Building the full featured SDK\n# //////////////////////////////\n\n# Building both architectures.\nxcodebuild -project \"HockeySDK.xcodeproj\" -configuration \"Release\" -target \"${FMK_NAME}\" -sdk iphoneos\nxcodebuild -project \"HockeySDK.xcodeproj\" -configuration \"Release\" -target \"${FMK_NAME}\" -sdk iphonesimulator\n\n# Creates and renews the final product folder.\nmkdir -p \"${ALL_FEATURES_INSTALL_DIR}\"\nmkdir -p \"${ALL_FEATURES_INSTALL_DIR}/Headers\"\nmkdir -p \"${ALL_FEATURES_INSTALL_DIR}/Modules\"\n\n# Copy the swift import file\ncp -f \"${SRCROOT}/module_allfeatures.modulemap\" \"${ALL_FEATURES_INSTALL_DIR}/Modules/module.modulemap\"\n\n# Copies the headers and resources files to the final product folder.\ncp -R \"${DEVICE_DIR_ALL_FEATURES}/include/HockeySDK/\" \"${ALL_FEATURES_INSTALL_DIR}/Headers/\"\n\n# Use the Lipo Tool to merge both binary files (i386/x86_64 + armv7/armv7s/arm64) into one Universal final product.\nlipo -create \"${DEVICE_DIR_ALL_FEATURES}/lib${FMK_NAME}.a\" \"${SIMULATOR_DIR_ALL_FEATURES}/lib${FMK_NAME}.a\" -output \"${ALL_FEATURES_INSTALL_DIR}/${FMK_NAME}\"\n\n# Combine the CrashReporter static library into a new Hockey static library file if they are not already present and copy the public headers too\nif [ -z $(otool -L \"${ALL_FEATURES_INSTALL_DIR}/${FMK_NAME}\" | grep 'libCrashReporter') ]\nthen\nlibtool -static -o \"${ALL_FEATURES_INSTALL_DIR}/${FMK_NAME}\" \"${ALL_FEATURES_INSTALL_DIR}/${FMK_NAME}\" \"${SRCROOT}/../Vendor/CrashReporter.framework/Versions/A/CrashReporter\"\nfi\n\n# build embeddedframework folder and move framework into it\nmkdir \"${ALL_FEATURES_INSTALL_DIR}/../${FMK_NAME}.embeddedframework\"\nmv \"${ALL_FEATURES_INSTALL_DIR}/\" \"${ALL_FEATURES_INSTALL_DIR}/../${FMK_NAME}.embeddedframework/${FMK_NAME}.framework\"\nmv \"${DEVICE_DIR_ALL_FEATURES}/${FMK_RESOURCE_BUNDLE}.bundle\" \"${TEMP_DIR}/HockeySDKAllFeatures/${FMK_NAME}.embeddedframework/\"\n\n# do some cleanup\nrm -r \"${WRK_DIR}\"\n\n# /////////////////////////////////////////////\n# Building the crash only SDK without resources\n# /////////////////////////////////////////////\n\n# Building both architectures.\nxcodebuild -project \"HockeySDK.xcodeproj\" -configuration \"ReleaseCrashOnly\" -target \"${FMK_NAME}\" -sdk iphoneos\nxcodebuild -project \"HockeySDK.xcodeproj\" -configuration \"ReleaseCrashOnly\" -target \"${FMK_NAME}\" -sdk iphonesimulator\n\n# Creates and renews the final product folder.\nmkdir -p \"${INSTALL_DIR}\"\nmkdir -p \"${INSTALL_DIR}/Headers\"\nmkdir -p \"${INSTALL_DIR}/Modules\"\n\n# Copy the swift import file\ncp -f \"${SRCROOT}/module_crashonly.modulemap\" \"${INSTALL_DIR}/Modules/module.modulemap\"\n\n# Copies the headers without the resources files to the final product folder.\ncp -R \"${SRCROOT}/${DEVICE_CRASH_ONLY_DIR}\"/include/HockeySDK/BITCrash*.h \"${INSTALL_DIR}/Headers/\"\ncp -R \"${SRCROOT}/${DEVICE_CRASH_ONLY_DIR}/include/HockeySDK/BITMetricsManager.h\" \"${INSTALL_DIR}/Headers/\"\ncp -R \"${SRCROOT}/${DEVICE_CRASH_ONLY_DIR}/include/HockeySDK/BITHockeyAttachment.h\" \"${INSTALL_DIR}/Headers/\"\ncp -R \"${SRCROOT}/${DEVICE_CRASH_ONLY_DIR}/include/HockeySDK/BITHockeyBaseManager.h\" \"${INSTALL_DIR}/Headers/\"\ncp -R \"${SRCROOT}/${DEVICE_CRASH_ONLY_DIR}/include/HockeySDK/BITHockeyManager.h\" \"${INSTALL_DIR}/Headers/\"\ncp -R \"${SRCROOT}/${DEVICE_CRASH_ONLY_DIR}/include/HockeySDK/BITHockeyManagerDelegate.h\" \"${INSTALL_DIR}/Headers/\"\ncp -R \"${SRCROOT}/${DEVICE_CRASH_ONLY_DIR}/include/HockeySDK/HockeySDK.h\" \"${INSTALL_DIR}/Headers/\"\ncp -R \"${SRCROOT}/${DEVICE_CRASH_ONLY_DIR}/include/HockeySDK/HockeySDKNullability.h\" \"${INSTALL_DIR}/Headers/\"\ncp -R \"${SRCROOT}/${DEVICE_CRASH_ONLY_DIR}/include/HockeySDK/HockeySDKEnums.h\" \"${INSTALL_DIR}/Headers/\"\n\n# Copy the patched feature header\ncp -f \"${SRCROOT}/HockeySDKCrashOnlyConfig.h\" \"${INSTALL_DIR}/Headers/HockeySDKFeatureConfig.h\"\n\n# Uses the Lipo Tool to merge both binary files (i386/x86_64 + armv7/armv7s/arm64) into one Universal final product.\nlipo -create \"${SRCROOT}/${DEVICE_CRASH_ONLY_DIR}/lib${FMK_NAME}.a\" \"${SRCROOT}/${SIMULATOR_CRASH_ONLY_DIR}/lib${FMK_NAME}.a\" -output \"${INSTALL_DIR}/${FMK_NAME}\"\n\n# Combine the CrashReporter static library into a new Hockey static library file if they are not already present and copy the public headers too\nif [ -z $(otool -L \"${INSTALL_DIR}/${FMK_NAME}\" | grep 'libCrashReporter') ]\nthen\nlibtool -static -o \"${INSTALL_DIR}/${FMK_NAME}\" \"${INSTALL_DIR}/${FMK_NAME}\" \"${SRCROOT}/../Vendor/CrashReporter.framework/Versions/A/CrashReporter\"\nfi\n\n# Move the crash reporting only framework into a new folder\nmkdir \"${INSTALL_DIR}/../${FMK_NAME}CrashOnly\"\nmv \"${INSTALL_DIR}\" \"${INSTALL_DIR}/../${FMK_NAME}CrashOnly/${FMK_NAME}.framework\"\n\nrm -r \"${WRK_DIR}\"\n\n# ////////////////////////////////////////////////////////\n# Building the extensions crash only SDK without resources\n# ////////////////////////////////////////////////////////\n\n# Building both architectures.\nxcodebuild -project \"HockeySDK.xcodeproj\" -configuration \"ReleaseCrashOnlyExtensions\" -target \"${FMK_NAME}\" -sdk iphoneos\nxcodebuild -project \"HockeySDK.xcodeproj\" -configuration \"ReleaseCrashOnlyExtensions\" -target \"${FMK_NAME}\" -sdk iphonesimulator\n\n# Creates and renews the final product folder.\nmkdir -p \"${INSTALL_DIR}\"\nmkdir -p \"${INSTALL_DIR}/Headers\"\nmkdir -p \"${INSTALL_DIR}/Modules\"\n\n# Copy the swift import file\ncp -f \"${SRCROOT}/module_crashonly.modulemap\" \"${INSTALL_DIR}/Modules/module.modulemap\"\n\n# Copies the headers without the resources files to the final product folder.\ncp -R \"${SRCROOT}/${DEVICE_EXTENSIONS_CRASH_ONLY_DIR}\"/include/HockeySDK/BITCrash*.h \"${INSTALL_DIR}/Headers/\"\ncp -R \"${SRCROOT}/${DEVICE_EXTENSIONS_CRASH_ONLY_DIR}/include/HockeySDK/BITHockeyAttachment.h\" \"${INSTALL_DIR}/Headers/\"\ncp -R \"${SRCROOT}/${DEVICE_EXTENSIONS_CRASH_ONLY_DIR}/include/HockeySDK/BITHockeyBaseManager.h\" \"${INSTALL_DIR}/Headers/\"\ncp -R \"${SRCROOT}/${DEVICE_EXTENSIONS_CRASH_ONLY_DIR}/include/HockeySDK/BITHockeyManager.h\" \"${INSTALL_DIR}/Headers/\"\ncp -R \"${SRCROOT}/${DEVICE_EXTENSIONS_CRASH_ONLY_DIR}/include/HockeySDK/BITHockeyManagerDelegate.h\" \"${INSTALL_DIR}/Headers/\"\ncp -R \"${SRCROOT}/${DEVICE_EXTENSIONS_CRASH_ONLY_DIR}/include/HockeySDK/HockeySDK.h\" \"${INSTALL_DIR}/Headers/\"\ncp -R \"${SRCROOT}/${DEVICE_EXTENSIONS_CRASH_ONLY_DIR}/include/HockeySDK/HockeySDKNullability.h\" \"${INSTALL_DIR}/Headers/\"\ncp -R \"${SRCROOT}/${DEVICE_EXTENSIONS_CRASH_ONLY_DIR}/include/HockeySDK/HockeySDKEnums.h\" \"${INSTALL_DIR}/Headers/\"\n\n# Copy the patched feature header\ncp -f \"${SRCROOT}/HockeySDKCrashOnlyExtensionConfig.h\" \"${INSTALL_DIR}/Headers/HockeySDKFeatureConfig.h\"\n\n# Uses the Lipo Tool to merge both binary files (i386/x86_64 + armv7/armv7s/arm64) into one Universal final product.\nlipo -create \"${SRCROOT}/${DEVICE_EXTENSIONS_CRASH_ONLY_DIR}/lib${FMK_NAME}.a\" \"${SRCROOT}/${SIMULATOR_EXTENSIONS_CRASH_ONLY_DIR}/lib${FMK_NAME}.a\" -output \"${INSTALL_DIR}/${FMK_NAME}\"\n\n# Combine the CrashReporter static library into a new Hockey static library file if they are not already present and copy the public headers too\nif [ -z $(otool -L \"${INSTALL_DIR}/${FMK_NAME}\" | grep 'libCrashReporter') ]\nthen\nlibtool -static -o \"${INSTALL_DIR}/${FMK_NAME}\" \"${INSTALL_DIR}/${FMK_NAME}\" \"${SRCROOT}/../Vendor/CrashReporter.framework/Versions/A/CrashReporter\"\nfi\n\n# Move the crash reporting only framework into a new folder\nmkdir \"${INSTALL_DIR}/../${FMK_NAME}CrashOnlyExtension\"\nmv \"${INSTALL_DIR}\" \"${INSTALL_DIR}/../${FMK_NAME}CrashOnlyExtension/${FMK_NAME}.framework\"\n\nrm -r \"${WRK_DIR}\"\n\n# //////////////////////////////\n# Final steps: move documentation and create zip-file\n# //////////////////////////////\n\n# copy license, changelog, documentation, integration json\ncp -f \"${SRCROOT}/../Docs/Changelog-template.md\" \"${TEMP_DIR}/CHANGELOG\"\ncp -f \"${SRCROOT}/../Docs/Guide-Installation-Setup-template.md\" \"${TEMP_DIR}/README.md\"\ncp -f \"${SRCROOT}/../LICENSE\" \"${TEMP_DIR}\"\nmkdir \"${TEMP_DIR}/${HOCKEYSDK_DOCSET_VERSION_NAME}.docset\"\ncp -R \"${SRCROOT}/../documentation/docset/Contents\" \"${TEMP_DIR}/${HOCKEYSDK_DOCSET_VERSION_NAME}.docset\"\n\n# build zip\ncd \"${PRODUCTS_DIR}\"\nrm -f \"${FMK_NAME}-iOS-${VERSION_STRING}.zip\"\nzip -yr \"${FMK_NAME}-iOS-${VERSION_STRING}.zip\" \"${ZIP_FOLDER}\" -x \\*/.*\n\ncd \"${ZIP_FOLDER}\"\nrm -f \"${FMK_NAME}-iOS-documentation-${VERSION_STRING}.zip\"\nzip -yr \"${FMK_NAME}-iOS-documentation-${VERSION_STRING}.zip\" \"${HOCKEYSDK_DOCSET_VERSION_NAME}.docset\" -x \\*/.*\nmv \"${FMK_NAME}-iOS-documentation-${VERSION_STRING}.zip\" \"../\"\n"; + shellScript = "# Default config\n# Sets the target folders and the final framework product.\nFMK_NAME=HockeySDK\nFMK_RESOURCE_BUNDLE=HockeySDKResources\nFMK_iOS8_NAME=\"HockeySDK Framework\"\n\n# Documentation\nHOCKEYSDK_DOCSET_VERSION_NAME=\"de.bitstadium.${HOCKEYSDK_DOCSET_NAME}-${VERSION_STRING}\"\n\n# Install dir will be the final output to the framework.\n# The following line creates it in the root folder of the current project.\nPRODUCTS_DIR=${SRCROOT}/../Products\nZIP_FOLDER=HockeySDK-iOS\nTEMP_DIR=${PRODUCTS_DIR}/${ZIP_FOLDER}\nINSTALL_DIR=${TEMP_DIR}/${FMK_NAME}.framework\nALL_FEATURES_INSTALL_DIR=${TEMP_DIR}/HockeySDKAllFeatures/${FMK_NAME}.framework\n\n# Working dir will be deleted after the framework creation.\nWRK_DIR=build\nDEVICE_DIR=${WRK_DIR}/ReleaseDefault-iphoneos\nSIMULATOR_DIR=${WRK_DIR}/ReleaseDefault-iphonesimulator\nDEVICE_DIR_ALL_FEATURES=${WRK_DIR}/Release-iphoneos\nSIMULATOR_DIR_ALL_FEATURES=${WRK_DIR}/Release-iphonesimulator\nDEVICE_CRASH_ONLY_DIR=${WRK_DIR}/ReleaseCrashOnly-iphoneos\nSIMULATOR_CRASH_ONLY_DIR=${WRK_DIR}/ReleaseCrashOnly-iphonesimulator\nDEVICE_EXTENSIONS_CRASH_ONLY_DIR=${WRK_DIR}/ReleaseCrashOnlyExtensions-iphoneos\nSIMULATOR_EXTENSIONS_CRASH_ONLY_DIR=${WRK_DIR}/ReleaseCrashOnlyExtensions-iphonesimulator\nDEVICE_WATCH_CRASH_ONLY_DIR=${WRK_DIR}/ReleaseCrashOnlyWatchOS-iphoneos\nSIMULATOR_WATCH_CRASH_ONLY_DIR=${WRK_DIR}/ReleaseCrashOnlyWatchOS-iphonesimulator\n\n# //////////////////////////////\n# Building the SDK with all features except the Feedback Feature\n# //////////////////////////////\n\n# Building both architectures.\nxcodebuild -project \"HockeySDK.xcodeproj\" -configuration \"ReleaseDefault\" -target \"${FMK_NAME}\" -sdk iphoneos\nxcodebuild -project \"HockeySDK.xcodeproj\" -configuration \"ReleaseDefault\" -target \"${FMK_NAME}\" -sdk iphonesimulator\n\n# Cleaning the oldest.\nif [ -d \"${TEMP_DIR}\" ]\nthen\nrm -rf \"${TEMP_DIR}\"\nfi\n\n# Creates and renews the final product folder.\nmkdir -p \"${INSTALL_DIR}\"\nmkdir -p \"${INSTALL_DIR}/Headers\"\nmkdir -p \"${INSTALL_DIR}/Modules\"\n\n# Copy the swift import file\ncp -f \"${SRCROOT}/module_default.modulemap\" \"${INSTALL_DIR}/Modules/module.modulemap\"\n\n# Copies the headers and resources files to the final product folder.\ncp -R \"${SRCROOT}/${DEVICE_DIR}/include/HockeySDK/BITAuthenticator.h\" \"${INSTALL_DIR}/Headers/\"\ncp -R \"${SRCROOT}/${DEVICE_DIR}/include/HockeySDK/BITCrashAttachment.h\" \"${INSTALL_DIR}/Headers/\"\ncp -R \"${SRCROOT}/${DEVICE_DIR}/include/HockeySDK/BITCrashDetails.h\" \"${INSTALL_DIR}/Headers/\"\ncp -R \"${SRCROOT}/${DEVICE_DIR}/include/HockeySDK/BITCrashManager.h\" \"${INSTALL_DIR}/Headers/\"\ncp -R \"${SRCROOT}/${DEVICE_DIR}/include/HockeySDK/BITCrashManagerDelegate.h\" \"${INSTALL_DIR}/Headers/\"\ncp -R \"${SRCROOT}/${DEVICE_DIR}/include/HockeySDK/BITCrashMetaData.h\" \"${INSTALL_DIR}/Headers/\"\ncp -R \"${SRCROOT}/${DEVICE_DIR}/include/HockeySDK/BITHockeyAttachment.h\" \"${INSTALL_DIR}/Headers/\"\ncp -R \"${SRCROOT}/${DEVICE_DIR}/include/HockeySDK/BITHockeyBaseManager.h\" \"${INSTALL_DIR}/Headers/\"\ncp -R \"${SRCROOT}/${DEVICE_DIR}/include/HockeySDK/BITHockeyBaseViewController.h\" \"${INSTALL_DIR}/Headers/\"\ncp -R \"${SRCROOT}/${DEVICE_DIR}/include/HockeySDK/BITHockeyManager.h\" \"${INSTALL_DIR}/Headers/\"\ncp -R \"${SRCROOT}/${DEVICE_DIR}/include/HockeySDK/BITHockeyManagerDelegate.h\" \"${INSTALL_DIR}/Headers/\"\ncp -R \"${SRCROOT}/${DEVICE_DIR}/include/HockeySDK/BITMetricsManager.h\" \"${INSTALL_DIR}/Headers/\"\ncp -R \"${SRCROOT}/${DEVICE_DIR}/include/HockeySDK/BITStoreUpdateManager.h\" \"${INSTALL_DIR}/Headers/\"\ncp -R \"${SRCROOT}/${DEVICE_DIR}/include/HockeySDK/BITStoreUpdateManagerDelegate.h\" \"${INSTALL_DIR}/Headers/\"\ncp -R \"${SRCROOT}/${DEVICE_DIR}/include/HockeySDK/BITUpdateManager.h\" \"${INSTALL_DIR}/Headers/\"\ncp -R \"${SRCROOT}/${DEVICE_DIR}/include/HockeySDK/BITUpdateManagerDelegate.h\" \"${INSTALL_DIR}/Headers/\"\ncp -R \"${SRCROOT}/${DEVICE_DIR}/include/HockeySDK/BITUpdateViewController.h\" \"${INSTALL_DIR}/Headers/\"\ncp -R \"${SRCROOT}/${DEVICE_DIR}/include/HockeySDK/HockeySDK.h\" \"${INSTALL_DIR}/Headers/\"\ncp -R \"${SRCROOT}/${DEVICE_DIR}/include/HockeySDK/HockeySDKEnums.h\" \"${INSTALL_DIR}/Headers/\"\ncp -R \"${SRCROOT}/${DEVICE_DIR}/include/HockeySDK/HockeySDKFeatureConfig.h\" \"${INSTALL_DIR}/Headers/\"\ncp -R \"${SRCROOT}/${DEVICE_DIR}/include/HockeySDK/HockeySDKNullability.h\" \"${INSTALL_DIR}/Headers/\"\n\n# Copy the patched feature header\ncp -f \"${SRCROOT}/HockeySDKFeatureConfigDefault.h\" \"${INSTALL_DIR}/Headers/HockeySDKFeatureConfig.h\"\n\n# Uses the Lipo Tool to merge both binary files (i386 + armv6/armv7) into one Universal final product.\nlipo -create \"${DEVICE_DIR}/lib${FMK_NAME}.a\" \"${SIMULATOR_DIR}/lib${FMK_NAME}.a\" -output \"${INSTALL_DIR}/${FMK_NAME}\"\n\n# Combine the CrashReporter static library into a new Hockey static library file if they are not already present and copy the public headers too\nif [ -z $(otool -L \"${INSTALL_DIR}/${FMK_NAME}\" | grep 'libCrashReporter') ]\nthen\nlibtool -static -o \"${INSTALL_DIR}/${FMK_NAME}\" \"${INSTALL_DIR}/${FMK_NAME}\" \"${SRCROOT}/../Vendor/CrashReporter.framework/Versions/A/CrashReporter\"\nfi\n\n# build embeddedframework folder and move framework into it\nmkdir \"${INSTALL_DIR}/../${FMK_NAME}.embeddedframework\"\nmv \"${INSTALL_DIR}\" \"${INSTALL_DIR}/../${FMK_NAME}.embeddedframework/${FMK_NAME}.framework\"\nmv \"${DEVICE_DIR}/${FMK_RESOURCE_BUNDLE}.bundle\" \"${TEMP_DIR}/${FMK_NAME}.embeddedframework/\"\n\nrm -r \"${WRK_DIR}\"\n\n# //////////////////////////////\n# Building the full featured SDK\n# //////////////////////////////\n\n# Building both architectures.\nxcodebuild -project \"HockeySDK.xcodeproj\" -configuration \"Release\" -target \"${FMK_NAME}\" -sdk iphoneos\nxcodebuild -project \"HockeySDK.xcodeproj\" -configuration \"Release\" -target \"${FMK_NAME}\" -sdk iphonesimulator\n\n# Creates and renews the final product folder.\nmkdir -p \"${ALL_FEATURES_INSTALL_DIR}\"\nmkdir -p \"${ALL_FEATURES_INSTALL_DIR}/Headers\"\nmkdir -p \"${ALL_FEATURES_INSTALL_DIR}/Modules\"\n\n# Copy the swift import file\ncp -f \"${SRCROOT}/module_allfeatures.modulemap\" \"${ALL_FEATURES_INSTALL_DIR}/Modules/module.modulemap\"\n\n# Copies the headers and resources files to the final product folder.\ncp -R \"${DEVICE_DIR_ALL_FEATURES}/include/HockeySDK/\" \"${ALL_FEATURES_INSTALL_DIR}/Headers/\"\n\n# Use the Lipo Tool to merge both binary files (i386/x86_64 + armv7/armv7s/arm64) into one Universal final product.\nlipo -create \"${DEVICE_DIR_ALL_FEATURES}/lib${FMK_NAME}.a\" \"${SIMULATOR_DIR_ALL_FEATURES}/lib${FMK_NAME}.a\" -output \"${ALL_FEATURES_INSTALL_DIR}/${FMK_NAME}\"\n\n# Combine the CrashReporter static library into a new Hockey static library file if they are not already present and copy the public headers too\nif [ -z $(otool -L \"${ALL_FEATURES_INSTALL_DIR}/${FMK_NAME}\" | grep 'libCrashReporter') ]\nthen\nlibtool -static -o \"${ALL_FEATURES_INSTALL_DIR}/${FMK_NAME}\" \"${ALL_FEATURES_INSTALL_DIR}/${FMK_NAME}\" \"${SRCROOT}/../Vendor/CrashReporter.framework/Versions/A/CrashReporter\"\nfi\n\n# build embeddedframework folder and move framework into it\nmkdir \"${ALL_FEATURES_INSTALL_DIR}/../${FMK_NAME}.embeddedframework\"\nmv \"${ALL_FEATURES_INSTALL_DIR}/\" \"${ALL_FEATURES_INSTALL_DIR}/../${FMK_NAME}.embeddedframework/${FMK_NAME}.framework\"\nmv \"${DEVICE_DIR_ALL_FEATURES}/${FMK_RESOURCE_BUNDLE}.bundle\" \"${TEMP_DIR}/HockeySDKAllFeatures/${FMK_NAME}.embeddedframework/\"\n\n# do some cleanup\nrm -r \"${WRK_DIR}\"\n\n# /////////////////////////////////////////////\n# Building the crash only SDK without resources\n# /////////////////////////////////////////////\n\n# Building both architectures.\nxcodebuild -project \"HockeySDK.xcodeproj\" -configuration \"ReleaseCrashOnly\" -target \"${FMK_NAME}\" -sdk iphoneos\nxcodebuild -project \"HockeySDK.xcodeproj\" -configuration \"ReleaseCrashOnly\" -target \"${FMK_NAME}\" -sdk iphonesimulator\n\n# Creates and renews the final product folder.\nmkdir -p \"${INSTALL_DIR}\"\nmkdir -p \"${INSTALL_DIR}/Headers\"\nmkdir -p \"${INSTALL_DIR}/Modules\"\n\n# Copy the swift import file\ncp -f \"${SRCROOT}/module_crashonly.modulemap\" \"${INSTALL_DIR}/Modules/module.modulemap\"\n\n# Copies the headers without the resources files to the final product folder.\ncp -R \"${SRCROOT}/${DEVICE_CRASH_ONLY_DIR}\"/include/HockeySDK/BITCrash*.h \"${INSTALL_DIR}/Headers/\"\ncp -R \"${SRCROOT}/${DEVICE_CRASH_ONLY_DIR}/include/HockeySDK/BITMetricsManager.h\" \"${INSTALL_DIR}/Headers/\"\ncp -R \"${SRCROOT}/${DEVICE_CRASH_ONLY_DIR}/include/HockeySDK/BITHockeyAttachment.h\" \"${INSTALL_DIR}/Headers/\"\ncp -R \"${SRCROOT}/${DEVICE_CRASH_ONLY_DIR}/include/HockeySDK/BITHockeyBaseManager.h\" \"${INSTALL_DIR}/Headers/\"\ncp -R \"${SRCROOT}/${DEVICE_CRASH_ONLY_DIR}/include/HockeySDK/BITHockeyManager.h\" \"${INSTALL_DIR}/Headers/\"\ncp -R \"${SRCROOT}/${DEVICE_CRASH_ONLY_DIR}/include/HockeySDK/BITHockeyManagerDelegate.h\" \"${INSTALL_DIR}/Headers/\"\ncp -R \"${SRCROOT}/${DEVICE_CRASH_ONLY_DIR}/include/HockeySDK/HockeySDK.h\" \"${INSTALL_DIR}/Headers/\"\ncp -R \"${SRCROOT}/${DEVICE_CRASH_ONLY_DIR}/include/HockeySDK/HockeySDKNullability.h\" \"${INSTALL_DIR}/Headers/\"\ncp -R \"${SRCROOT}/${DEVICE_CRASH_ONLY_DIR}/include/HockeySDK/HockeySDKEnums.h\" \"${INSTALL_DIR}/Headers/\"\n\n# Copy the patched feature header\ncp -f \"${SRCROOT}/HockeySDKCrashOnlyConfig.h\" \"${INSTALL_DIR}/Headers/HockeySDKFeatureConfig.h\"\n\n# Uses the Lipo Tool to merge both binary files (i386/x86_64 + armv7/armv7s/arm64) into one Universal final product.\nlipo -create \"${SRCROOT}/${DEVICE_CRASH_ONLY_DIR}/lib${FMK_NAME}.a\" \"${SRCROOT}/${SIMULATOR_CRASH_ONLY_DIR}/lib${FMK_NAME}.a\" -output \"${INSTALL_DIR}/${FMK_NAME}\"\n\n# Combine the CrashReporter static library into a new Hockey static library file if they are not already present and copy the public headers too\nif [ -z $(otool -L \"${INSTALL_DIR}/${FMK_NAME}\" | grep 'libCrashReporter') ]\nthen\nlibtool -static -o \"${INSTALL_DIR}/${FMK_NAME}\" \"${INSTALL_DIR}/${FMK_NAME}\" \"${SRCROOT}/../Vendor/CrashReporter.framework/Versions/A/CrashReporter\"\nfi\n\n# Move the crash reporting only framework into a new folder\nmkdir \"${INSTALL_DIR}/../${FMK_NAME}CrashOnly\"\nmv \"${INSTALL_DIR}\" \"${INSTALL_DIR}/../${FMK_NAME}CrashOnly/${FMK_NAME}.framework\"\n\nrm -r \"${WRK_DIR}\"\n\n# ////////////////////////////////////////////////////////\n# Building the extensions crash only SDK without resources\n# ////////////////////////////////////////////////////////\n\n# Building both architectures.\nxcodebuild -project \"HockeySDK.xcodeproj\" -configuration \"ReleaseCrashOnlyExtensions\" -target \"${FMK_NAME}\" -sdk iphoneos\nxcodebuild -project \"HockeySDK.xcodeproj\" -configuration \"ReleaseCrashOnlyExtensions\" -target \"${FMK_NAME}\" -sdk iphonesimulator\n\n# Creates and renews the final product folder.\nmkdir -p \"${INSTALL_DIR}\"\nmkdir -p \"${INSTALL_DIR}/Headers\"\nmkdir -p \"${INSTALL_DIR}/Modules\"\n\n# Copy the swift import file\ncp -f \"${SRCROOT}/module_crashonly.modulemap\" \"${INSTALL_DIR}/Modules/module.modulemap\"\n\n# Copies the headers without the resources files to the final product folder.\ncp -R \"${SRCROOT}/${DEVICE_EXTENSIONS_CRASH_ONLY_DIR}\"/include/HockeySDK/BITCrash*.h \"${INSTALL_DIR}/Headers/\"\ncp -R \"${SRCROOT}/${DEVICE_EXTENSIONS_CRASH_ONLY_DIR}/include/HockeySDK/BITHockeyAttachment.h\" \"${INSTALL_DIR}/Headers/\"\ncp -R \"${SRCROOT}/${DEVICE_EXTENSIONS_CRASH_ONLY_DIR}/include/HockeySDK/BITHockeyBaseManager.h\" \"${INSTALL_DIR}/Headers/\"\ncp -R \"${SRCROOT}/${DEVICE_EXTENSIONS_CRASH_ONLY_DIR}/include/HockeySDK/BITHockeyManager.h\" \"${INSTALL_DIR}/Headers/\"\ncp -R \"${SRCROOT}/${DEVICE_EXTENSIONS_CRASH_ONLY_DIR}/include/HockeySDK/BITHockeyManagerDelegate.h\" \"${INSTALL_DIR}/Headers/\"\ncp -R \"${SRCROOT}/${DEVICE_EXTENSIONS_CRASH_ONLY_DIR}/include/HockeySDK/HockeySDK.h\" \"${INSTALL_DIR}/Headers/\"\ncp -R \"${SRCROOT}/${DEVICE_EXTENSIONS_CRASH_ONLY_DIR}/include/HockeySDK/HockeySDKNullability.h\" \"${INSTALL_DIR}/Headers/\"\ncp -R \"${SRCROOT}/${DEVICE_EXTENSIONS_CRASH_ONLY_DIR}/include/HockeySDK/HockeySDKEnums.h\" \"${INSTALL_DIR}/Headers/\"\n\n# Copy the patched feature header\ncp -f \"${SRCROOT}/HockeySDKCrashOnlyExtensionConfig.h\" \"${INSTALL_DIR}/Headers/HockeySDKFeatureConfig.h\"\n\n# Uses the Lipo Tool to merge both binary files (i386/x86_64 + armv7/armv7s/arm64) into one Universal final product.\nlipo -create \"${SRCROOT}/${DEVICE_EXTENSIONS_CRASH_ONLY_DIR}/lib${FMK_NAME}.a\" \"${SRCROOT}/${SIMULATOR_EXTENSIONS_CRASH_ONLY_DIR}/lib${FMK_NAME}.a\" -output \"${INSTALL_DIR}/${FMK_NAME}\"\n\n# Combine the CrashReporter static library into a new Hockey static library file if they are not already present and copy the public headers too\nif [ -z $(otool -L \"${INSTALL_DIR}/${FMK_NAME}\" | grep 'libCrashReporter') ]\nthen\nlibtool -static -o \"${INSTALL_DIR}/${FMK_NAME}\" \"${INSTALL_DIR}/${FMK_NAME}\" \"${SRCROOT}/../Vendor/CrashReporter.framework/Versions/A/CrashReporter\"\nfi\n\n# Move the crash reporting only framework into a new folder\nmkdir \"${INSTALL_DIR}/../${FMK_NAME}CrashOnlyExtension\"\nmv \"${INSTALL_DIR}\" \"${INSTALL_DIR}/../${FMK_NAME}CrashOnlyExtension/${FMK_NAME}.framework\"\n\nrm -r \"${WRK_DIR}\"\n\n# //////////////////////////////\n# Final steps: move documentation and create zip-file\n# //////////////////////////////\n\n# copy license, changelog, documentation, integration json\ncp -f \"${SRCROOT}/../Docs/Changelog-template.md\" \"${TEMP_DIR}/CHANGELOG\"\ncp -f \"${SRCROOT}/../Docs/Guide-Installation-Setup-template.md\" \"${TEMP_DIR}/README.md\"\ncp -f \"${SRCROOT}/../LICENSE\" \"${TEMP_DIR}\"\nmkdir \"${TEMP_DIR}/${HOCKEYSDK_DOCSET_VERSION_NAME}.docset\"\ncp -R \"${SRCROOT}/../documentation/docset/Contents\" \"${TEMP_DIR}/${HOCKEYSDK_DOCSET_VERSION_NAME}.docset\"\n\n# build zip\ncd \"${PRODUCTS_DIR}\"\nrm -f \"${FMK_NAME}-iOS-${VERSION_STRING}.zip\"\nzip -yr \"${FMK_NAME}-iOS-${VERSION_STRING}.zip\" \"${ZIP_FOLDER}\" -x \\*/.*\n\ncd \"${ZIP_FOLDER}\"\nrm -f \"${FMK_NAME}-iOS-documentation-${VERSION_STRING}.zip\"\nzip -yr \"${FMK_NAME}-iOS-documentation-${VERSION_STRING}.zip\" \"${HOCKEYSDK_DOCSET_VERSION_NAME}.docset\" -x \\*/.*\nmv \"${FMK_NAME}-iOS-documentation-${VERSION_STRING}.zip\" \"../\"\n"; }; 1E8E66B215BC3D8200632A2E /* ShellScript */ = { isa = PBXShellScriptBuildPhase; diff --git a/Support/HockeySDK.xcodeproj/xcshareddata/xcschemes/HockeySDK Distribution.xcscheme b/Support/HockeySDK.xcodeproj/xcshareddata/xcschemes/HockeySDK Distribution.xcscheme index 0ca0b31f..5560a6f9 100644 --- a/Support/HockeySDK.xcodeproj/xcshareddata/xcschemes/HockeySDK Distribution.xcscheme +++ b/Support/HockeySDK.xcodeproj/xcshareddata/xcschemes/HockeySDK Distribution.xcscheme @@ -1,6 +1,6 @@ diff --git a/Support/HockeySDK.xcodeproj/xcshareddata/xcschemes/HockeySDK.xcscheme b/Support/HockeySDK.xcodeproj/xcshareddata/xcschemes/HockeySDK.xcscheme index 7e4b24f3..e04dd185 100644 --- a/Support/HockeySDK.xcodeproj/xcshareddata/xcschemes/HockeySDK.xcscheme +++ b/Support/HockeySDK.xcodeproj/xcshareddata/xcschemes/HockeySDK.xcscheme @@ -1,6 +1,6 @@ mockDelegate = mockProtocol(@protocol(BITFeedbackManagerDelegate)); - [given([mockDelegate preparedItemsForFeedbackManager:self.sut]) willReturn:@[sampleImage2, sampleData2]]; + [given([mockDelegate preparedItemsForFeedbackManager:self.sut]) willReturn:@[sampleImage1, sampleData1]]; self.sut.delegate = mockDelegate; - - // Test when feedbackComposerPreparedItems is also set -#pragma clang diagnostic push -#pragma clang diagnostic ignored "-Wdeprecated" - self.sut.feedbackComposerPreparedItems = @[sampleImage1, sampleData1]; -#pragma clang diagnostic pop - BITFeedbackComposeViewController *composeViewController = [self.sut feedbackComposeViewController]; NSArray *attachments = [composeViewController performSelector:@selector(attachments)]; - XCTAssertEqual(attachments.count, 4); - - - // Test when feedbackComposerPreparedItems is nil -#pragma clang diagnostic push -#pragma clang diagnostic ignored "-Wdeprecated" - self.sut.feedbackComposerPreparedItems = nil; -#pragma clang diagnostic pop - - composeViewController = [self.sut feedbackComposeViewController]; - attachments = [composeViewController performSelector:@selector(attachments)]; XCTAssertEqual(attachments.count, 2); - XCTAssertTrue(composeViewController.hideImageAttachmentButton); XCTAssertEqual(composeViewController.delegate, mockDelegate); } diff --git a/Vendor/CrashReporter.framework/Versions/A/CrashReporter b/Vendor/CrashReporter.framework/Versions/A/CrashReporter index 3b18fa05..0f06d85c 100644 Binary files a/Vendor/CrashReporter.framework/Versions/A/CrashReporter and b/Vendor/CrashReporter.framework/Versions/A/CrashReporter differ diff --git a/Vendor/CrashReporter.framework/Versions/A/Headers/CrashReporter.h b/Vendor/CrashReporter.framework/Versions/A/Headers/CrashReporter.h index 26caf3dd..c6543191 100644 --- a/Vendor/CrashReporter.framework/Versions/A/Headers/CrashReporter.h +++ b/Vendor/CrashReporter.framework/Versions/A/Headers/CrashReporter.h @@ -95,13 +95,7 @@ typedef enum { PLCrashReporterErrorCrashReportInvalid = 2, /** An attempt to use a resource which was in use at the time in a manner which would have conflicted with the request. */ - PLCrashReporterErrorResourceBusy = 3, - - /** The requested resource could not be found. */ - PLCRashReporterErrorNotFound = 4, - - /** Allocation failed. */ - PLCRashReporterErrorInsufficientMemory = 4 + PLCrashReporterErrorResourceBusy = 3 } PLCrashReporterError; diff --git a/Vendor/CrashReporter.framework/Versions/A/Headers/PLCrashFeatureConfig.h b/Vendor/CrashReporter.framework/Versions/A/Headers/PLCrashFeatureConfig.h index a4880481..58351108 100644 --- a/Vendor/CrashReporter.framework/Versions/A/Headers/PLCrashFeatureConfig.h +++ b/Vendor/CrashReporter.framework/Versions/A/Headers/PLCrashFeatureConfig.h @@ -83,8 +83,12 @@ * * For more information on the potential issues with enabling mach exception support, @sa @ref mach_exceptions. */ +#if TARGET_OS_TV +# define PLCRASH_FEATURE_MACH_EXCEPTIONS 0 +#else # define PLCRASH_FEATURE_MACH_EXCEPTIONS 1 #endif +#endif #ifndef PLCRASH_FEATURE_UNWIND_DWARF /** If true, enable DWARF unwinding support. */ diff --git a/Vendor/CrashReporter.framework/Versions/A/Headers/PLCrashMacros.h b/Vendor/CrashReporter.framework/Versions/A/Headers/PLCrashMacros.h index a933f3a8..6629bc46 100644 --- a/Vendor/CrashReporter.framework/Versions/A/Headers/PLCrashMacros.h +++ b/Vendor/CrashReporter.framework/Versions/A/Headers/PLCrashMacros.h @@ -52,12 +52,6 @@ # define PLCR_CPP_BEGIN_NS namespace plcrash { # define PLCR_CPP_END_NS } # endif -# - /** @internal Define the plcrash::async namespace, automatically inserting an inline namespace containing the configured PLCRASHREPORTER_PREFIX, if any. */ -# define PLCR_CPP_BEGIN_ASYNC_NS PLCR_CPP_BEGIN_NS namespace async { - - /** @internal Close the definition of the `plcrash::async` namespace (and the PLCRASHREPORTER_PREFIX inline namespace, if any). */ -# define PLCR_CPP_END_ASYNC_NS PLCR_CPP_END_NS } #endif #ifdef __clang__ @@ -66,56 +60,12 @@ # define PLCR_PRAGMA_CLANG(_p) #endif -#ifdef __clang__ -# define PLCR_DEPRECATED __attribute__((deprecated)) -#else -# define PLCR_DEPRECATED -#endif - #if defined(__clang__) || defined(__GNUC__) # define PLCR_UNUSED __attribute__((unused)) #else # define PLCR_UNUSED #endif -#ifdef PLCR_PRIVATE -/** - * Marks a definition as deprecated only for for external clients, allowing - * uses of it internal fo the framework. - */ -#define PLCR_EXTERNAL_DEPRECATED - -/** - * @internal - * A macro to put above a definition marked PLCR_EXTERNAL_DEPRECATED that will - * silence warnings about there being a deprecation documentation marker but the - * definition not being marked deprecated. - */ -# define PLCR_EXTERNAL_DEPRECATED_NOWARN_PUSH() \ - PLCR_PRAGMA_CLANG("clang diagnostic push"); \ - PLCR_PRAGMA_CLANG("clang diagnostic ignored \"-Wdocumentation-deprecated-sync\"") - -/** - * @internal - * A macro to put below a definition marked PLCR_EXTERNAL_DEPRECATED that will - * silence warnings about there being a deprecation documentation marker but the - * definition not being marked deprecated. - */ -# define PLCR_EXTERNAL_DEPRECATED_NOWARN_POP() PLCR_PRAGMA_CLANG("clang diagnostic pop") -#else -# define PLCR_EXTERNAL_DEPRECATED PLCR_DEPRECATED -# define PLCR_EXTERNAL_DEPRECATED_NOWARN_PUSH() -# define PLCR_EXTERNAL_DEPRECATED_NOWARN_PUSH() -#endif /* PLCR_PRIVATE */ - -#ifdef PLCR_PRIVATE -# if defined(__clang__) && __has_feature(cxx_attributes) && __has_warning("-Wimplicit-fallthrough") -# define PLCR_FALLTHROUGH [[clang::fallthrough]] -# else -# define PLCR_FALLTHROUGH do {} while (0) -# endif -#endif - #ifdef PLCR_PRIVATE /** * @internal diff --git a/Vendor/CrashReporter.framework/Versions/A/Headers/PLCrashNamespace.h b/Vendor/CrashReporter.framework/Versions/A/Headers/PLCrashNamespace.h index b352ca19..eb1a4630 100644 --- a/Vendor/CrashReporter.framework/Versions/A/Headers/PLCrashNamespace.h +++ b/Vendor/CrashReporter.framework/Versions/A/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 @@ -247,8 +247,8 @@ #define plcrash_nasync_image_list_free PLNS(plcrash_nasync_image_list_free) #define plcrash_nasync_image_list_init PLNS(plcrash_nasync_image_list_init) #define plcrash_nasync_image_list_remove PLNS(plcrash_nasync_image_list_remove) -#define plcrash_async_macho_free PLNS(plcrash_async_macho_free) -#define plcrash_async_macho_init PLNS(plcrash_async_macho_init) +#define plcrash_nasync_macho_free PLNS(plcrash_nasync_macho_free) +#define plcrash_nasync_macho_init PLNS(plcrash_nasync_macho_init) #define plcrash_populate_error PLNS(plcrash_populate_error) #define plcrash_populate_mach_error PLNS(plcrash_populate_mach_error) #define plcrash_populate_posix_error PLNS(plcrash_populate_posix_error) diff --git a/Vendor/CrashReporter.framework/Versions/A/Headers/PLCrashReportSystemInfo.h b/Vendor/CrashReporter.framework/Versions/A/Headers/PLCrashReportSystemInfo.h index 1bc59a81..9ab6ef1b 100644 --- a/Vendor/CrashReporter.framework/Versions/A/Headers/PLCrashReportSystemInfo.h +++ b/Vendor/CrashReporter.framework/Versions/A/Headers/PLCrashReportSystemInfo.h @@ -27,8 +27,6 @@ */ #import -#include "PLCrashMacros.h" -@class PLCrashReportProcessorInfo; /** * @ingroup constants @@ -50,6 +48,10 @@ typedef enum { /** Unknown operating system */ PLCrashReportOperatingSystemUnknown = 3, + + /** tvOS */ + PLCrashReportOperatingSystemtvOS = 4, + } PLCrashReportOperatingSystem; /** @@ -57,7 +59,7 @@ typedef enum { * * Indicates the architecture under which a Crash Log was generated. * - * @note The architecture value has been deprecated in v1.1 and later crash reports. All new reports + * @deprecated The architecture value has been deprecated in v1.1 and later crash reports. All new reports * will make use of the new PLCrashReportProcessorInfo CPU type encodings. * * @internal @@ -79,7 +81,7 @@ typedef enum { * values. * @sa PLCrashReportArchitectureARMv6 */ - PLCrashReportArchitectureARM PLCR_DEPRECATED = PLCrashReportArchitectureARMv6, + PLCrashReportArchitectureARM = PLCrashReportArchitectureARMv6, /** PPC */ PLCrashReportArchitecturePPC = 3, @@ -96,9 +98,7 @@ typedef enum { extern PLCrashReportOperatingSystem PLCrashReportHostOperatingSystem; -PLCR_EXTERNAL_DEPRECATED_NOWARN_PUSH(); -extern PLCrashReportArchitecture PLCrashReportHostArchitecture PLCR_EXTERNAL_DEPRECATED; -PLCR_EXTERNAL_DEPRECATED_NOWARN_PUSH(); +extern PLCrashReportArchitecture PLCrashReportHostArchitecture; @interface PLCrashReportSystemInfo : NSObject { @private @@ -116,27 +116,17 @@ PLCR_EXTERNAL_DEPRECATED_NOWARN_PUSH(); /** Date crash report was generated. May be nil if the date is unknown. */ NSDate *_timestamp; - - /** Processor information. */ - PLCrashReportProcessorInfo *_processorInfo; } - (id) initWithOperatingSystem: (PLCrashReportOperatingSystem) operatingSystem operatingSystemVersion: (NSString *) operatingSystemVersion architecture: (PLCrashReportArchitecture) architecture - timestamp: (NSDate *) timestamp PLCR_DEPRECATED; + timestamp: (NSDate *) timestamp; - (id) initWithOperatingSystem: (PLCrashReportOperatingSystem) operatingSystem operatingSystemVersion: (NSString *) operatingSystemVersion operatingSystemBuild: (NSString *) operatingSystemBuild architecture: (PLCrashReportArchitecture) architecture - timestamp: (NSDate *) timestamp PLCR_DEPRECATED; - -- (id) initWithOperatingSystem: (PLCrashReportOperatingSystem) operatingSystem - operatingSystemVersion: (NSString *) operatingSystemVersion - operatingSystemBuild: (NSString *) operatingSystemBuild - architecture: (PLCrashReportArchitecture) architecture - processorInfo: (PLCrashReportProcessorInfo *) processorInfo timestamp: (NSDate *) timestamp; /** The operating system. */ @@ -151,13 +141,9 @@ PLCR_EXTERNAL_DEPRECATED_NOWARN_PUSH(); /** 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 PLCR_DEPRECATED; +@property(nonatomic, readonly) PLCrashReportArchitecture architecture; /** Date and time that the crash report was generated. This may be unavailable, and this property will be nil. */ @property(nonatomic, readonly) NSDate *timestamp; -/** The processor type. For v1.2 reports and later, this is an alias to the machine info's processorInfo. - * For earlier reports, this will be synthesized from the deprecated architecture property. */ -@property(nonatomic, readonly) PLCrashReportProcessorInfo *processorInfo; - @end diff --git a/Vendor/CrashReporter.framework/Versions/A/Headers/PLCrashReporter.h b/Vendor/CrashReporter.framework/Versions/A/Headers/PLCrashReporter.h index fe74e27b..455c417a 100644 --- a/Vendor/CrashReporter.framework/Versions/A/Headers/PLCrashReporter.h +++ b/Vendor/CrashReporter.framework/Versions/A/Headers/PLCrashReporter.h @@ -30,7 +30,6 @@ #import #import "PLCrashReporterConfig.h" -#import "PLCrashMacros.h" @class PLCrashMachExceptionServer; @class PLCrashMachExceptionPortSet; @@ -45,7 +44,7 @@ * @param uap The crash's threads context. * @param context The API client's supplied context value. * - * @sa The @ref async_safety documentation. + * @sa @ref async_safety * @sa PLCrashReporter::setPostCrashCallbacks: */ typedef void (*PLCrashReporterPostCrashSignalCallback)(siginfo_t *info, ucontext_t *uap, void *context); @@ -56,7 +55,7 @@ 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 The @ref async_safety documentation. + * @sa @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 @@ -88,7 +87,7 @@ typedef struct PLCrashReporterCallbacks { /** YES if the crash reporter has been enabled */ BOOL _enabled; - + #if PLCRASH_FEATURE_MACH_EXCEPTIONS /** The backing Mach exception server, if any. Nil if the reporter has not been enabled, or if * the configured signal handler type is not PLCrashReporterSignalHandlerTypeMach. */ @@ -111,7 +110,7 @@ typedef struct PLCrashReporterCallbacks { NSString *_crashReportDirectory; } -+ (PLCrashReporter *) sharedReporter PLCR_DEPRECATED; ++ (PLCrashReporter *) sharedReporter; - (instancetype) initWithConfiguration: (PLCrashReporterConfig *) config; @@ -122,11 +121,9 @@ typedef struct PLCrashReporterCallbacks { - (NSData *) generateLiveReportWithThread: (thread_t) thread; - (NSData *) generateLiveReportWithThread: (thread_t) thread error: (NSError **) outError; -- (NSData *) generateLiveReportWithThread: (thread_t) thread exception: (NSException *) exception error: (NSError **) outError; - (NSData *) generateLiveReport; - (NSData *) generateLiveReportAndReturnError: (NSError **) outError; -- (NSData *) generateLiveReportWithException: (NSException *) exception error: (NSError **) outError; - (BOOL) purgePendingCrashReport; - (BOOL) purgePendingCrashReportAndReturnError: (NSError **) outError; diff --git a/Vendor/CrashReporter.framework/Versions/A/Resources/Info.plist b/Vendor/CrashReporter.framework/Versions/A/Resources/Info.plist index c4c6265f..d1377f9b 100644 --- a/Vendor/CrashReporter.framework/Versions/A/Resources/Info.plist +++ b/Vendor/CrashReporter.framework/Versions/A/Resources/Info.plist @@ -3,7 +3,7 @@ BuildMachineOSBuild - 15G1004 + 15G1217 CFBundleDevelopmentRegion English CFBundleExecutable @@ -27,16 +27,16 @@ DTCompiler com.apple.compilers.llvm.clang.1_0 DTPlatformBuild - 7D1014 + 8C1002 DTPlatformVersion GM DTSDKBuild - 15E60 + 16C58 DTSDKName - macosx10.11 + macosx10.12 DTXcode - 0731 + 0821 DTXcodeBuild - 7D1014 + 8C1002 diff --git a/docs/Changelog-template.md b/docs/Changelog-template.md index b510c078..b40461e5 100644 --- a/docs/Changelog-template.md +++ b/docs/Changelog-template.md @@ -1,3 +1,10 @@ +## 4.1.4 + +- [IMPROVEMENT] Test targets won't be build in the run phase of the framework, which makes it possible to build individual configurations when using Carthage. Thanks a lot @wiedem for your contribution! [394](https://github.com/bitstadium/HockeySDK-iOS/pull/394) +- [IMPROVEMENT] We've reverted to a build based on PLCrashReporter 1.2.1 as 1.3 comes with unintended namespace collisions in some edge cases that result in worse crash reporting than you were used to. +- [BUGFIX] Fixes a crash on iOS 9 when attaching data to feedback [#395](https://github.com/bitstadium/HockeySDK-iOS/issues/395) +- [BUGFIX] Disabling the `BITFeedbackManager` now disables the various `BITFeedbackObservationModes`. [#390](https://github.com/bitstadium/HockeySDK-iOS/pull/390) + ## 4.1.3 - [NEW] Added `forceNewFeedbackThreadForFeedbackManager:`-callback to `BITFeedbackManagerDelegate` to force a new feedback thread for each new feedback.