Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add delegate for implementing custom prompting behavior #65

Merged
merged 9 commits into from
Jun 25, 2018

Conversation

NickEntin
Copy link
Collaborator

Add delegate for implementing custom prompting behavior via the ARKEmailBugReporterPromptingDelegate protocol.

Also fixes a bug where the email dialog isn't shown when no log messages are included in the report. Previously, this could happen when the log stores are empty or the emailAttachmentAdditionsDelegate returns false for all log stores. The custom prompt adds another case of this bug by offering users the chance to filter out all log store.

…mailBugReporterPromptingDelegate` protocol.

Also fixes a bug where the email dialog isn't shown when no log messages are included in the report. Previously, this could happen when the log stores are empty or the `emailAttachmentAdditionsDelegate` returns false for all log stores. The custom prompt adds another case of this bug by offering users the chance to filter out all log store.
@NickEntin NickEntin requested review from pwesten, efirestone and dfed May 9, 2018 00:46
Copy link
Collaborator

@dfed dfed left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Dig the concept! A few comments.

Note that nits/supernits are not blocking 😉

/// The log stores that will be included as attachments on the email.
@property (nonnull, nonatomic, copy) NSArray<ARKLogStore *> *logStores;

/// Controls whether or not a screenshot should be attached to the email, when available.
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

might be good to add what the default value is.

/// Controls whether or not a screenshot should be attached to the email, when available.
@property (nonatomic) BOOL includesScreenshot;

/// Controls whether or not a view hierarchy description should be attached to the email, when available.
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

might be good to add what the default value is.

@class ARKEmailAttachment;


@interface ARKEmailBugReportConfiguration : NSObject
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

triple-slash comment explaining what this object is?

/// Called on the main thread when a bug is filed to signal that a bug report prompt should be presented to the user.
/// The `completion` should be called with either an updated configuration to present the email dialog, or `nil` to signal that the prompt was cancelled.
/// When the initial `configuration` has `includesScreenshot` or `includesViewHierarchyDescription` false, setting the field to true will have no effect.
- (void)showBugReportingPromptForConfiguration:(nonnull ARKEmailBugReportConfiguration *)configuration completion:(nonnull ARKEmailBugReporterCustomPromptCompletionBlock)completion;
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

(not a blocker) any chance we can use the modern _Nonnull syntax here rather than nonnull?


@required

/// Called on the main thread when a bug is filed to signal that a bug report prompt should be presented to the user.
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

👏 for calling out what thread this is called on


dispatch_group_t logStoreRetrievalDispatchGroup = dispatch_group_create();
dispatch_group_enter(logStoreRetrievalDispatchGroup);
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: technically there's no need for this outside enter/leave. As long as you're calling notify after you've entered all your async tasks, you're fine.

that said, I'm pretty sure the surrounding enter/leave is in the style of other code here, so, we're good either way.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think theoretically it's better because we could change retrieveAllLogMessagesWithCompletionHandler to be synchronous at some point and potentially inadvertently change behavior here.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

As long as notify is called after current async work, and after you'd want it to be executed, any changes to sync/async behavior above shouldn't cause a change.

But yeah, the wrapper certainly makes things a bit more foolproof :)


// Once all log messages have been retrieved, attach the data and show the compose window.
dispatch_group_notify(logStoreRetrievalDispatchGroup, dispatch_get_main_queue(), ^{
NSMutableString *emailBody = [self _prefilledEmailBodyWithEmailBodyAdditions:emailBodyAdditions];
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

supernit: const here and... well... everywhere 😉

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Added it everywhere it seems relevant in this method.

if (appendToEmailBody) {
[emailBody appendString:emailBodyForLogStore];
}

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

supernit: whitespace

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good catch

- (void)_createBugReportWithTitle:(NSString *)title;
{
self.configuration.prefilledEmailSubject = title;
self.completion(self.configuration);
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

should we set completion to nil after this line to prevent accidentally calling it twice?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is a short-lived object, so I don't think there's any change in calling it twice. No harm in setting it to nil though, so probably better to be safe.

}]];

[alertController addAction:[UIAlertAction actionWithTitle:cancelButtonTitle style:UIAlertActionStyleDefault handler:^(UIAlertAction *action) {
self.completion(nil);
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

should we set completion to nil after this line to prevent accidentally calling it twice?

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

... or consider the no-storage approach.

@@ -23,4 +23,17 @@

@implementation ARKEmailBugReportConfiguration

- (instancetype)init;
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nice 😄

@@ -53,6 +54,20 @@
@end


typedef void (^ARKEmailBugReporterCustomPromptCompletionBlock)(ARKEmailBugReportConfiguration *_Nullable configuration);

@protocol ARKEmailBugReporterPromptingDelegate <NSObject>
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The <NSObject> specifier may be noise here. Not a strong opinion though since it's idiomatic.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks like every other protocol in Aardvark does this, so just as well to match.

if (configuration != nil) {
[self _createBugReportWithConfiguration:configuration];
} else {
// If the configuration is nil, the callee has signaled that we should not show a bug report. In the future, we can clean up any persisted state here as necessary.
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In the absence of a specific to-do item, I'd omit the else and put this comment before the if.

[self.mailComposeViewController setSubject:configuration.prefilledEmailSubject];

dispatch_group_t logStoreRetrievalDispatchGroup = dispatch_group_create();
dispatch_group_enter(logStoreRetrievalDispatchGroup);
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It would be a little better to put this outside the canSendMail check so the bug is fixed for both code paths.

{
/*
iOS 8 often fails to transfer the keyboard from a focused text field to a UIAlertView's text field.
Transfer first responder to an invisble view when a debug screenshot is captured to make bug filing itself bug-free.
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

invisible :-)


- (void)showBugReportingPromptForConfiguration:(nonnull ARKEmailBugReportConfiguration *)configuration completion:(nonnull ARKEmailBugReporterCustomPromptCompletionBlock)completion {
self.configuration = configuration;
self.completion = completion;
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It might be a little tidier to not store these in properties on the object, but instead capture them in the completion blocks inside the method below (which could then just be inlined here).

}]];

[alertController addAction:[UIAlertAction actionWithTitle:cancelButtonTitle style:UIAlertActionStyleDefault handler:^(UIAlertAction *action) {
self.completion(nil);
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

... or consider the no-storage approach.

@NickEntin
Copy link
Collaborator Author

@dfed @pwesten I think I've addressed all of the feedback. Please take another look when you get a chance.

Copy link
Collaborator

@dfed dfed left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM!

{
self = [super init];

_prefilledEmailSubject = @"";
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

it may be worth having a quick comment here stating: "If any of the below default values change, remember to update the header file"

That may also be overkill. I leave it up to you.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I feel like making behavioral changes like changing default values should imply needing to update the header docs, and if it doesn't then you don't have enough header docs. Maybe that's just optimistic thinking, but adding comments saying that things are documented feels backwards.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Works for me!

/// Controls whether or not a view hierarchy description should be attached to the email, when available. Defaults to NO.
@property (nonatomic, readonly) BOOL includesViewHierarchyDescription;

/// Additional attachments to include on the email. Defaults to nil.
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Might be a little more convenient to make it nonnull and default to an empty array?

- (void)removeScreenshot;

/// Removes the view hierarchy description from the bug report, if one is included.
- (void)removeViewHierarchyDescription;
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It's a little confusing to use the word remove when it's just going to flip a flag. This whole object represents a "configuration" of a bug report, not the actual contents. Might want to adjust comments to reflect that very clearly.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Would disable be better than remove here?

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hmmm "omit"?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Works for me 👍

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actually, exclude might be better, since it matches includesScreenshot

@NickEntin NickEntin merged commit f65d75e into square:master Jun 25, 2018
@NickEntin NickEntin deleted the entin/custom-prompting branch June 25, 2018 19:25
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants