Merge pull request #269 from bitstadium/feature/prepareditems-feedbackmanager-delegate

Add delegate method to FeedbackManager to add items to feedback
This commit is contained in:
Lukas Spieß
2016-04-15 02:35:00 +02:00
6 changed files with 154 additions and 70 deletions

View File

@@ -28,6 +28,9 @@
#import <Foundation/Foundation.h>
#import "HockeySDKNullability.h"
NS_ASSUME_NONNULL_BEGIN
/**
* The users action when composing a message
*/
@@ -69,3 +72,5 @@ typedef NS_ENUM(NSUInteger, BITFeedbackComposeResult) {
- (void)feedbackComposeViewController:(BITFeedbackComposeViewController *)composeViewController
didFinishWithResult:(BITFeedbackComposeResult) composeResult;
@end
NS_ASSUME_NONNULL_END

View File

@@ -33,6 +33,8 @@
#import "BITFeedbackListViewController.h"
#import "BITFeedbackComposeViewController.h"
#import "HockeySDKNullability.h"
NS_ASSUME_NONNULL_BEGIN
// Notification message which tells that loading messages finished
#define BITHockeyFeedbackMessagesLoadingStarted @"BITHockeyFeedbackMessagesLoadingStarted"
@@ -230,9 +232,11 @@ typedef NS_ENUM(NSInteger, BITFeedbackObservationMode) {
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) NSArray *feedbackComposerPreparedItems;
@property (nonatomic, copy, nullable) NSArray *feedbackComposerPreparedItems DEPRECATED_MSG_ATTRIBUTE("Use -preparedItemsForFeedbackManager: delegate method instead.");
/**
@@ -316,11 +320,16 @@ typedef NS_ENUM(NSInteger, BITFeedbackObservationMode) {
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.
Alternatively you can implement the `preparedItemsForFeedbackManager:` delegate method
and call `showFeedbackComposeView` instead. If you use both, the items from the delegate method
and the items passed with this method will be combined.
@param items an NSArray with objects that should be attached
@see `[BITFeedbackComposeViewController prepareWithItems:]`
@see `BITFeedbackManagerDelegate preparedItemsForFeedbackManager:`
@warning This methods needs to be called on the main thread!
*/
- (void)showFeedbackComposeViewWithPreparedItems:(NSArray *)items;
- (void)showFeedbackComposeViewWithPreparedItems:(nullable NSArray *)items;
/**
Presents a modal feedback compose interface with a screenshot attached which is taken at the time of calling this method.
@@ -338,6 +347,10 @@ typedef NS_ENUM(NSInteger, BITFeedbackObservationMode) {
/**
Create a feedback compose view
This method also adds items from `feedbackComposerPreparedItems` and
the `preparedItemsForFeedbackManager:` delegate methods to the instance of
`BITFeedbackComposeViewController` that will be returned.
Example to show a modal feedback compose UI with prefilled text
BITFeedbackComposeViewController *feedbackCompose = [[BITHockeyManager sharedHockeyManager].feedbackManager feedbackComposeViewController];
@@ -355,5 +368,6 @@ typedef NS_ENUM(NSInteger, BITFeedbackObservationMode) {
*/
- (BITFeedbackComposeViewController *)feedbackComposeViewController;
@end
NS_ASSUME_NONNULL_END

View File

@@ -231,7 +231,16 @@ typedef void (^BITLatestImageFetchCompletionBlock)(UIImage *_Nonnull latestImage
- (BITFeedbackComposeViewController *)feedbackComposeViewController {
BITFeedbackComposeViewController *composeViewController = [[BITFeedbackComposeViewController alloc] init];
[composeViewController prepareWithItems:self.feedbackComposerPreparedItems];
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wdeprecated"
NSArray *preparedItems = self.feedbackComposerPreparedItems;
#pragma clang diagnostic pop
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

View File

@@ -29,6 +29,9 @@
#import <Foundation/Foundation.h>
#import "BITFeedbackComposeViewControllerDelegate.h"
#import "HockeySDKNullability.h"
NS_ASSUME_NONNULL_BEGIN
@class BITFeedbackManager;
/**
@@ -40,14 +43,6 @@
@optional
/**
* can be implemented to know when new feedback from the server arrived
*
* @param feedbackManager The feedbackManager which did detect the new messages
*/
- (void) feedbackManagerDidReceiveNewFeedback:(BITFeedbackManager*) feedbackManager;
/**
* Can be implemented to control wether the feedback manager should automatically
* fetch for new messages on app startup or when becoming active.
@@ -60,6 +55,33 @@
*
* @param feedbackManager The feedbackManager which did detect the new messages
*/
- (BOOL) allowAutomaticFetchingForNewFeedbackForManager:(BITFeedbackManager *)feedbackManager;
- (BOOL)allowAutomaticFetchingForNewFeedbackForManager:(BITFeedbackManager *)feedbackManager;
/**
* can be implemented to know when new feedback from the server arrived
*
* @param feedbackManager The feedbackManager which did detect the new messages
*/
- (void)feedbackManagerDidReceiveNewFeedback:(BITFeedbackManager *)feedbackManager;
/**
* This optional method can be implemented to provide items to prefill
* the FeedbackComposeMessage user interface with the given items.
*
* If the user sends the feedback message, these items will be attached to that message.
*
* 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.
*
* @param feedbackManager The BITFeedbackManager instance that will handle sending the feedback.
*
* @return An array containing the items to be attached to the feedback message
* @see `[BITFeedbackComposeViewController prepareWithItems:]
*/
- (nullable NSArray *)preparedItemsForFeedbackManager:(BITFeedbackManager *)feedbackManager;
@end
NS_ASSUME_NONNULL_END

View File

@@ -25,27 +25,27 @@
@interface BITFeedbackManagerTests : XCTestCase
@property BITFeedbackManager *sut;
@end
@implementation BITFeedbackManagerTests {
BITFeedbackManager *_sut;
}
@implementation BITFeedbackManagerTests
- (void)setUp {
[super setUp];
BITHockeyManager *hm = [BITHockeyManager sharedHockeyManager];
hm.delegate = nil;
_sut = [[BITFeedbackManager alloc] initWithAppIdentifier:nil appEnvironment:BITEnvironmentOther];
_sut.delegate = nil;
self.sut = [[BITFeedbackManager alloc] initWithAppIdentifier:nil appEnvironment:BITEnvironmentOther];
self.sut.delegate = nil;
}
- (void)tearDown {
[_sut removeKeyFromKeychain:kBITHockeyMetaUserID];
[_sut removeKeyFromKeychain:kBITHockeyMetaUserName];
[_sut removeKeyFromKeychain:kBITHockeyMetaUserEmail];
[self.sut removeKeyFromKeychain:kBITHockeyMetaUserID];
[self.sut removeKeyFromKeychain:kBITHockeyMetaUserName];
[self.sut removeKeyFromKeychain:kBITHockeyMetaUserEmail];
_sut = nil;
self.sut = nil;
[super tearDown];
}
@@ -53,7 +53,7 @@
#pragma mark - Private
- (void)startManager {
[_sut startManager];
[self.sut startManager];
}
#pragma mark - Setup Tests
@@ -65,48 +65,48 @@
BITHockeyManager *hm = [BITHockeyManager sharedHockeyManager];
id delegateMock = mockProtocol(@protocol(BITHockeyManagerDelegate));
hm.delegate = delegateMock;
_sut.delegate = delegateMock;
self.sut.delegate = delegateMock;
BOOL dataAvailable = [_sut updateUserIDUsingKeychainAndDelegate];
BOOL dataAvailable = [self.sut updateUserIDUsingKeychainAndDelegate];
assertThatBool(dataAvailable, isFalse());
assertThat(_sut.userID, nilValue());
assertThat(self.sut.userID, nilValue());
[verifyCount(delegateMock, times(1)) userIDForHockeyManager:hm componentManager:_sut];
[verifyCount(delegateMock, times(1)) userIDForHockeyManager:hm componentManager:self.sut];
}
- (void)testUpdateUserIDWithDelegateReturningData {
BITHockeyManager *hm = [BITHockeyManager sharedHockeyManager];
NSObject <BITHockeyManagerDelegate> *classMock = mockObjectAndProtocol([NSObject class], @protocol(BITHockeyManagerDelegate));
[given([classMock userIDForHockeyManager:hm componentManager:_sut]) willReturn:@"test"];
[given([classMock userIDForHockeyManager:hm componentManager:self.sut]) willReturn:@"test"];
hm.delegate = classMock;
_sut.delegate = classMock;
self.sut.delegate = classMock;
BOOL dataAvailable = [_sut updateUserIDUsingKeychainAndDelegate];
BOOL dataAvailable = [self.sut updateUserIDUsingKeychainAndDelegate];
assertThatBool(dataAvailable, isTrue());
assertThat(_sut.userID, equalTo(@"test"));
assertThat(self.sut.userID, equalTo(@"test"));
[verifyCount(classMock, times(1)) userIDForHockeyManager:hm componentManager:_sut];
[verifyCount(classMock, times(1)) userIDForHockeyManager:hm componentManager:self.sut];
}
- (void)testUpdateUserIDWithValueInKeychain {
[_sut addStringValueToKeychain:@"test" forKey:kBITHockeyMetaUserID];
[self.sut addStringValueToKeychain:@"test" forKey:kBITHockeyMetaUserID];
BOOL dataAvailable = [_sut updateUserIDUsingKeychainAndDelegate];
BOOL dataAvailable = [self.sut updateUserIDUsingKeychainAndDelegate];
assertThatBool(dataAvailable, isTrue());
assertThat(_sut.userID, equalTo(@"test"));
assertThat(self.sut.userID, equalTo(@"test"));
}
- (void)testUpdateUserIDWithGlobalSetter {
BITHockeyManager *hm = [BITHockeyManager sharedHockeyManager];
[hm setUserID:@"test"];
BOOL dataAvailable = [_sut updateUserIDUsingKeychainAndDelegate];
BOOL dataAvailable = [self.sut updateUserIDUsingKeychainAndDelegate];
assertThatBool(dataAvailable, isTrue());
assertThat(_sut.userID, equalTo(@"test"));
assertThat(self.sut.userID, equalTo(@"test"));
}
@@ -114,48 +114,48 @@
BITHockeyManager *hm = [BITHockeyManager sharedHockeyManager];
id delegateMock = mockProtocol(@protocol(BITHockeyManagerDelegate));
hm.delegate = delegateMock;
_sut.delegate = delegateMock;
self.sut.delegate = delegateMock;
BOOL dataAvailable = [_sut updateUserNameUsingKeychainAndDelegate];
BOOL dataAvailable = [self.sut updateUserNameUsingKeychainAndDelegate];
assertThatBool(dataAvailable, isFalse());
assertThat(_sut.userName, nilValue());
assertThat(self.sut.userName, nilValue());
[verifyCount(delegateMock, times(1)) userNameForHockeyManager:hm componentManager:_sut];
[verifyCount(delegateMock, times(1)) userNameForHockeyManager:hm componentManager:self.sut];
}
- (void)testUpdateUserNameWithDelegateReturningData {
BITHockeyManager *hm = [BITHockeyManager sharedHockeyManager];
NSObject <BITHockeyManagerDelegate> *classMock = mockObjectAndProtocol([NSObject class], @protocol(BITHockeyManagerDelegate));
[given([classMock userNameForHockeyManager:hm componentManager:_sut]) willReturn:@"test"];
[given([classMock userNameForHockeyManager:hm componentManager:self.sut]) willReturn:@"test"];
hm.delegate = classMock;
_sut.delegate = classMock;
self.sut.delegate = classMock;
BOOL dataAvailable = [_sut updateUserNameUsingKeychainAndDelegate];
BOOL dataAvailable = [self.sut updateUserNameUsingKeychainAndDelegate];
assertThatBool(dataAvailable, isTrue());
assertThat(_sut.userName, equalTo(@"test"));
assertThat(self.sut.userName, equalTo(@"test"));
[verifyCount(classMock, times(1)) userNameForHockeyManager:hm componentManager:_sut];
[verifyCount(classMock, times(1)) userNameForHockeyManager:hm componentManager:self.sut];
}
- (void)testUpdateUserNameWithValueInKeychain {
[_sut addStringValueToKeychain:@"test" forKey:kBITHockeyMetaUserName];
[self.sut addStringValueToKeychain:@"test" forKey:kBITHockeyMetaUserName];
BOOL dataAvailable = [_sut updateUserNameUsingKeychainAndDelegate];
BOOL dataAvailable = [self.sut updateUserNameUsingKeychainAndDelegate];
assertThatBool(dataAvailable, isTrue());
assertThat(_sut.userName, equalTo(@"test"));
assertThat(self.sut.userName, equalTo(@"test"));
}
- (void)testUpdateUserNameWithGlobalSetter {
BITHockeyManager *hm = [BITHockeyManager sharedHockeyManager];
[hm setUserName:@"test"];
BOOL dataAvailable = [_sut updateUserNameUsingKeychainAndDelegate];
BOOL dataAvailable = [self.sut updateUserNameUsingKeychainAndDelegate];
assertThatBool(dataAvailable, isTrue());
assertThat(_sut.userName, equalTo(@"test"));
assertThat(self.sut.userName, equalTo(@"test"));
}
@@ -163,70 +163,101 @@
BITHockeyManager *hm = [BITHockeyManager sharedHockeyManager];
id delegateMock = mockProtocol(@protocol(BITHockeyManagerDelegate));
hm.delegate = delegateMock;
_sut.delegate = delegateMock;
self.sut.delegate = delegateMock;
BOOL dataAvailable = [_sut updateUserEmailUsingKeychainAndDelegate];
BOOL dataAvailable = [self.sut updateUserEmailUsingKeychainAndDelegate];
assertThatBool(dataAvailable, isFalse());
assertThat(_sut.userEmail, nilValue());
assertThat(self.sut.userEmail, nilValue());
[verifyCount(delegateMock, times(1)) userEmailForHockeyManager:hm componentManager:_sut];
[verifyCount(delegateMock, times(1)) userEmailForHockeyManager:hm componentManager:self.sut];
}
- (void)testUpdateUserEmailWithDelegateReturningData {
BITHockeyManager *hm = [BITHockeyManager sharedHockeyManager];
NSObject <BITHockeyManagerDelegate> *classMock = mockObjectAndProtocol([NSObject class], @protocol(BITHockeyManagerDelegate));
[given([classMock userEmailForHockeyManager:hm componentManager:_sut]) willReturn:@"test"];
[given([classMock userEmailForHockeyManager:hm componentManager:self.sut]) willReturn:@"test"];
hm.delegate = classMock;
_sut.delegate = classMock;
self.sut.delegate = classMock;
BOOL dataAvailable = [_sut updateUserEmailUsingKeychainAndDelegate];
BOOL dataAvailable = [self.sut updateUserEmailUsingKeychainAndDelegate];
assertThatBool(dataAvailable, isTrue());
assertThat(_sut.userEmail, equalTo(@"test"));
assertThat(self.sut.userEmail, equalTo(@"test"));
[verifyCount(classMock, times(1)) userEmailForHockeyManager:hm componentManager:_sut];
[verifyCount(classMock, times(1)) userEmailForHockeyManager:hm componentManager:self.sut];
}
- (void)testUpdateUserEmailWithValueInKeychain {
[_sut addStringValueToKeychain:@"test" forKey:kBITHockeyMetaUserEmail];
[self.sut addStringValueToKeychain:@"test" forKey:kBITHockeyMetaUserEmail];
BOOL dataAvailable = [_sut updateUserEmailUsingKeychainAndDelegate];
BOOL dataAvailable = [self.sut updateUserEmailUsingKeychainAndDelegate];
assertThatBool(dataAvailable, isTrue());
assertThat(_sut.userEmail, equalTo(@"test"));
assertThat(self.sut.userEmail, equalTo(@"test"));
}
- (void)testUpdateUserEmailWithGlobalSetter {
BITHockeyManager *hm = [BITHockeyManager sharedHockeyManager];
[hm setUserEmail:@"test"];
BOOL dataAvailable = [_sut updateUserEmailUsingKeychainAndDelegate];
BOOL dataAvailable = [self.sut updateUserEmailUsingKeychainAndDelegate];
assertThatBool(dataAvailable, isTrue());
assertThat(_sut.userEmail, equalTo(@"test"));
assertThat(self.sut.userEmail, equalTo(@"test"));
}
- (void)testAllowFetchingNewMessages {
BOOL fetchMessages = NO;
// check the default
fetchMessages = [_sut allowFetchingNewMessages];
fetchMessages = [self.sut allowFetchingNewMessages];
assertThatBool(fetchMessages, isTrue());
// check the delegate is implemented and returns NO
BITHockeyManager *hm = [BITHockeyManager sharedHockeyManager];
NSObject <BITHockeyManagerDelegate> *classMock = mockObjectAndProtocol([NSObject class], @protocol(BITHockeyManagerDelegate));
[given([classMock allowAutomaticFetchingForNewFeedbackForManager:_sut]) willReturn:@NO];
[given([classMock allowAutomaticFetchingForNewFeedbackForManager:self.sut]) willReturn:@NO];
hm.delegate = classMock;
_sut.delegate = classMock;
self.sut.delegate = classMock;
fetchMessages = [_sut allowFetchingNewMessages];
fetchMessages = [self.sut allowFetchingNewMessages];
assertThatBool(fetchMessages, isFalse());
[verifyCount(classMock, times(1)) allowAutomaticFetchingForNewFeedbackForManager:_sut];
[verifyCount(classMock, times(1)) allowAutomaticFetchingForNewFeedbackForManager:self.sut];
}
#pragma mark - FeedbackManagerDelegate Tests
- (void)testFeedbackComposeViewController {
UIImage *sampleImage1 = [UIImage new];
UIImage *sampleImage2 = [UIImage new];
NSData *sampleData1 = [NSData data];
NSData *sampleData2 = [NSData data];
self.sut.feedbackComposeHideImageAttachmentButton = YES;
XCTAssertTrue(self.sut.feedbackComposeHideImageAttachmentButton);
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wdeprecated"
self.sut.feedbackComposerPreparedItems = @[sampleImage1, sampleData1];
#pragma clang diagnostic pop
id<BITFeedbackManagerDelegate> mockDelegate = mockProtocol(@protocol(BITFeedbackManagerDelegate));
[given([mockDelegate preparedItemsForFeedbackManager:self.sut]) willReturn:@[sampleImage2, sampleData2]];
self.sut.delegate = mockDelegate;
BITFeedbackComposeViewController *composeViewController = [self.sut feedbackComposeViewController];
NSArray *attachments = [composeViewController performSelector:@selector(attachments)];
XCTAssertEqual(attachments.count, 4);
XCTAssertTrue(composeViewController.hideImageAttachmentButton);
XCTAssertEqual(composeViewController.delegate, mockDelegate);
}
@end

View File

@@ -1,3 +1,6 @@
- [NEW] Added `preparedItemsForFeedbackManager:` method in `BITFeedbackManagerDelegate` to allow to provide items with every possible method of showing the feedback compose dialog.
- [UPDATE] Deprecate `feedbackComposerPreparedItems` property in favor of the new delegate method.
## Version 4.0.0-beta.1
- [NEW] User Metrics including users and sessions data is now in public beta!