Submitting User Feedback
Beta release
Getting feedback from users and testers is crucial in the app development process. It provides valuable insights and helps improve the overall user experience. Sauce Labs Mobile App Distribution offers an effortless way to collect feedback through its In-App Feedback feature. By integrating the Sauce Mobile Beta SDK into your app, you can enable users to report bugs, suggest improvements, and share their thoughts directly in the app.
Using In-App Feedback
If you added the Sauce Mobile Beta SDK to your app, testers can report feedback by shaking the device. Shake to report is controlled by the In-app bug reporting setting of your app, which is on by default. To check or change it:
- Open the app in the Sauce Labs Mobile App Distribution dashboard and click SDK Settings.
- Turn In-app bug reporting on or off.
- Click Save SDK Settings.
The setting applies to sessions that start after you save. Account Owners and Org Admins can change SDK settings.
When a tester shakes the device, the SDK asks "Would you like to provide feedback?" If the tester agrees, the feedback form opens, and the tester can report a bug or share a suggestion.
The feedback is added to the session the tester is running. It appears on the Feedback page of the dashboard and on the session's Feedback tab, where Seek Video jumps the session replay to the moment the tester submitted it. See Viewing Feedback.
Feedback Contents
When users provide feedback using the In-App Feedback feature, the following information is included:
- Text - The comment the tester writes to describe the issue or suggestion.
- Email - The email address the tester enters, if the form shows the email field.
- Screenshot - A screenshot of the app at the moment the feedback form opened. On iOS, the tester can edit or remove the screenshot before sending.
- Device and app information - The device model, the OS version, and the app version.
- Session - The session the tester is running, and how far into the session the tester submitted the feedback.
- Custom fields - The values of any custom fields you add to the form. See Customizing the Feedback Form.
Customizing In-App Feedback
Sauce Labs Mobile App Distribution allows you to customize the way In-App Feedback is collected. If you prefer not to use the shake gesture for feedback collection, you can programmatically invoke the feedback form using a button click or any other gesture in your app. This way, users can access the feedback form from a designated area in the app, like the help menu or after encountering unexpected errors.
If you choose to invoke the feedback form programmatically, it is shown even if In-app bug reporting is turned off in your app's SDK settings.
- Android
- iOS
- React Native
TestFairy.showFeedbackForm();
// Be sure to import the SDK
import com.testfairy.TestFairy;
// Can be invoked on a button press
// or after your app passes a given page
TestFairy.showFeedbackForm();
showFeedbackForm() requires a running session. To collect feedback without a session, call TestFairy.showFeedbackForm(context, "<sauce-mobile-beta-token>", true). For custom fields, validation and callbacks, see Customizing the Feedback Form.
[TestFairy showFeedbackForm];
// Be sure to import the SDK
#import "TestFairy.h"
// Can be invoked on a button press
// or after your app passes a given page
[TestFairy showFeedbackForm];
import TestFairy
TestFairy.showFeedbackForm()
pushFeedbackController is deprecated. Use showFeedbackForm, which requires a running session. To collect feedback without one, call [TestFairy showFeedbackForm:@"<sauce-mobile-beta-token>" takeScreenshot:YES]. To hide the email field or add custom fields, see Customizing the Feedback Form (setFeedbackEmailVisible: is deprecated).
TestFairy.pushFeedbackController();
// Be sure to import the SDK
import TestFairy from '@saucelabs/mobile-beta-react-native';
// Can be invoked on a button press
// or after your app passes a given page
TestFairy.pushFeedbackController();
// Without a running session:
TestFairy.showFeedbackForm('<sauce-mobile-beta-token>', true);
pushFeedbackController() shows the form for the running session. showFeedbackForm(appToken, takeScreenshot) also works without a session.Customizing the Feedback Form
The built-in form has an email field, a message field and, depending on the platform, buttons to attach a screenshot or a screen recording. You customize it by building a feedback options object and handing it to the SDK before the form is shown. Doing so before beginWithoutCrashHandler is fine. The feedback classes are in the TestFairy module on iOS and the com.testfairy package on Android.
The form can be shown in two ways. showFeedbackForm() with no arguments requires a running session and attaches the feedback to it. The overload that takes the app token (showFeedbackForm(context, appToken, takeScreenshot) on Android, showFeedbackForm(appToken, takeScreenshot:) on iOS and showFeedbackForm(appToken, takeScreenshot) on React Native) works without a session and optionally captures a screenshot first. The feedback appears on the Feedback page of the dashboard, without a session.
Rules that apply on Android and iOS:
- Setting a list of form fields replaces the default fields. To keep the email and message fields, add them yourself with the reserved attribute names
:userId(email or user identifier) and:text(message). Always include a:textfield. - Every other field is sent with the feedback as a feedback attribute named after the field's attribute key, so pick short, stable names.
- Three field types are built in: single-line text (String), multi-line text (TextArea) and a dropdown list (Select, a map of label to value). You can add up to 32 fields. Attribute names must be non-empty and unique (duplicates are dropped). For any other control, implement the
FeedbackFormFieldinterface (onCreateView,getAttribute,getValue) yourself. - The reserved fields are pre-filled by the SDK (last used email, unsent draft). The
defaultTextoption only applies to the built-in form and is ignored after you set custom fields. - Email is mandatory by default. Hiding the email field lifts the requirement, and so does a custom field list without a
:userIdfield. - A custom verifier replaces the SDK's default validation (email present and valid when mandatory, non-empty message), so re-implement the checks you still want.
- Android
- iOS
- React Native
import com.testfairy.FeedbackContent;
import com.testfairy.FeedbackFormField;
import com.testfairy.FeedbackOptions;
import com.testfairy.FeedbackVerifier;
import com.testfairy.SelectFeedbackFormField;
import com.testfairy.StringFeedbackFormField;
import com.testfairy.TestFairy;
import com.testfairy.TextAreaFeedbackFormField;
import java.util.ArrayList;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
Map<String, String> types = new LinkedHashMap<>(); // label -> value
types.put("Bug", "bug");
types.put("Feature request", "feature");
List<FeedbackFormField> fields = new ArrayList<>();
// Reserved names keep the built-in fields (attribute, placeholder, default value):
fields.add(new StringFeedbackFormField(":userId", "Your email", ""));
fields.add(new TextAreaFeedbackFormField(":text", "Describe the problem", ""));
// Your own fields (any attribute name):
fields.add(new StringFeedbackFormField("phone", "Phone number", ""));
fields.add(new SelectFeedbackFormField("type", "Is this a bug or a feature request?", types, "Bug")); // the default is a label
FeedbackOptions options = new FeedbackOptions.Builder()
.setFeedbackFormFields(fields)
.setEmailMandatory(false) // default true; only enforced when a :userId field exists
.setTakeScreenshotButtonVisible(true)
.setRecordVideoButtonVisible(false)
.setFeedbackInterceptor(content -> content) // optional: inspect or modify before sending; never return null
.setCallback(new FeedbackOptions.Callback() { // optional
@Override public void onFeedbackSent(FeedbackContent content) {}
@Override public void onFeedbackCancelled() {}
@Override public void onFeedbackFailed(int reason, FeedbackContent content) {}
})
.build();
TestFairy.setFeedbackOptions(options);
// Optional: custom validation (returning false blocks sending and shows your message)
TestFairy.setFeedbackVerifier(new FeedbackVerifier() {
@Override public boolean verifyFeedback(FeedbackContent content) {
return content.getEmail().endsWith("@yourcompany.com") && !content.getText().isEmpty();
}
@Override public String getVerificationFailedMessage() {
return "Please use your company email and describe the issue.";
}
});
// Show the form from a button after beginWithoutCrashHandler ...
TestFairy.showFeedbackForm();
// ... or without a running session, optionally taking a screenshot first:
TestFairy.showFeedbackForm(context, "<sauce-mobile-beta-token>", true);
FeedbackOptions.Builder options:setEmailFieldVisible(boolean)hides the email field (and lifts the mandatory requirement).setDefaultText(String)pre-fills the message of the built-in form.setBrowserUrl(String)opens your own web form in the browser instead of the native form. The SDK appendssessionUrl,timestamp,user(yoursetUserIdvalue),platform,packageName,versionName,versionCodeandscreenNameas query parameters. The other options do not apply in this mode.
TestFairy.enableFeedbackForm("shake") (Android accepts only "shake") or TestFairy.disableFeedbackForm() before beginWithoutCrashHandler.import TestFairy // the TestFairy module of the SauceMobileBeta package
let options = TestFairyFeedbackOptions.create { builder in
builder?.title = "Send us feedback"
builder?.isEmailVisible = true
builder?.isEmailMandatory = false
builder?.feedbackFormFields = [
// Reserved names keep the built-in fields:
TestFairyStringFeedbackFormField(attribute: ":userId", label: nil,
placeholder: "Your email", defaultValue: "")!,
TestFairyTextAreaFeedbackFormField(attribute: ":text",
placeholder: "Describe the problem", defaultValue: "")!,
// Your own fields (any attribute name):
TestFairyStringFeedbackFormField(attribute: "phone", label: nil,
placeholder: "Phone number", defaultValue: "")!,
TestFairySelectFeedbackFormField(attribute: "type", label: "Is this a bug or a feature request?",
values: ["Bug": "bug", "Feature": "feature"],
defaultValue: "Bug")!, // defaultValue is one of the labels
]
// Optional: validate before sending (false blocks sending and shows your message)
builder?.verifier = CompanyEmailVerifier()
// Optional: inspect or modify the content right before it is sent (never return nil)
builder?.interceptor = { content in content }
}
TestFairy.setTestFairyFeedbackOptions(options)
// Show the form from a button after beginWithoutCrashHandler ...
TestFairy.showFeedbackForm()
// ... or without a running session, optionally attaching a screenshot:
TestFairy.showFeedbackForm("<sauce-mobile-beta-token>", takeScreenshot: true)
final class CompanyEmailVerifier: NSObject, TestFairyFeedbackVerifier {
func verifyFeedback(_ content: TestFairyFeedbackContent!) -> Bool {
let email = content.email ?? ""
let text = content.text ?? ""
return email.hasSuffix("@yourcompany.com") && !text.isEmpty
}
func getVerificationFailedMessage() -> String! {
return "Please use your company email and describe the issue."
}
}
[TestFairyFeedbackOptions createWithBlock:^(TestFairyFeedbackOptionsBuilder *builder) { ... }] followed by [TestFairy setTestFairyFeedbackOptions:options]. The defaultText builder property pre-fills the message of the built-in form. The dictionary-based setFeedbackOptions: and setFeedbackEmailVisible: are deprecated. The dropdown list shows its options in dictionary order, which is not necessarily the order you typed them.Triggers: call TestFairy.enableFeedbackForm("shake"), "screenshot" or "shake|screenshot", or TestFairy.disableFeedbackForm(), before beginWithoutCrashHandler. The triggers you set this way replace the In-app bug reporting setting from the dashboard, for both shake and screenshot. Any other value is ignored, and the SDK uses the dashboard setting.import TestFairy from '@saucelabs/mobile-beta-react-native';
TestFairy.setFeedbackOptions({
defaultText: '',
isEmailMandatory: false,
isEmailVisible: true,
isTakeScreenshotButtonVisible: true, // Android only; ignored on iOS
isTakeRecordingButtonVisible: false, // Android only; ignored on iOS
// browserUrl: 'https://...', // Android only: open your own web form instead
});
TestFairy.enableFeedbackForm('shake'); // before begin; iOS also accepts 'screenshot' and 'shake|screenshot'
// With a running session:
TestFairy.pushFeedbackController();
// Without a running session (appToken, takeScreenshot); takeScreenshot defaults to true:
TestFairy.showFeedbackForm('<sauce-mobile-beta-token>', true);
TestFairy.setFeedbackOptions() from JavaScript afterwards: it rebuilds the options from scratch and replaces the ones your native module set.