Snatch Bugs with This Lightweight Bug-Reporting iOS Library

  —  
 read

The truth of life of a Quality Assurance engineer is there will always be edge-cases developers might have missed in the process of creating an iOS app, the result of which are unwanted bugs.

To help our QAs fight the nasty pesty little bugs, we’ve created the bug-reporting iOS library Bugsnatch.

Improving the process of Quality Assurance

That’s where QA engineers come into play, their sight set on snatching and exterminating those bugs early on.

Having identified a bug, a QA engineer should have an intuitive and easy-to-use method of reporting it back to the developer. At Infinum, we’ve tried multiple approaches to this, and stuck with the one we’ve created – the Bugsnatch library.

Bugsnatch is a bug-reporting iOS library written in Swift, which creates a bug report template by collecting app and device information.

In most companies, QA engineers use some project management tool to create a task describing the encountered problem.

In addition to that description, the task usually contains some kind of boilerplate device and app info, including summarized debug information. That helps developers narrow down and pinpoint the problem’s root cause. If you want to become best friends with developers, learn how not to suck at writing bug reports.

All things considered, it could take an experienced QA engineer up to a few minutes to extract and write debug info by hand in the bug report, and it isn’t just a once- or twice-a-day occurrence. This means engineers spend a lot of time writing instead of testing.

“If only there was a way to make more efficient use of QA engineers’ time” – we thought and created Bugsnatch.

Meet Bugsnatch, an expert on bug reports

Bugsnatch collects all debug info and empowers QA engineers to send it via email with just a few taps. In addition to the email version, there is a version of Bugsnatch which automatically syncs with the project management tool Productive, which we use to keep track of all the work at Infinum.

Productive keeps all of the company’s business processes centralized. From resource planning and project management, to time tracking, organizing sales and profitability monitoring.

It’s also the place where QA engineers create tasks to share information about discovered bugs to developers. Let's see how Bugsnatch helps them.

What exactly Bugsnatch's great at

Extracting general debug info

The main feature of Bugsnatch is extracting debug info from your apps. It covers the basic application and device info, such as:

  • app name
  • app version and build number
  • device name
  • iOS version

To see the basic setup for the email version of Bugsnatch in AppDelegate in action, check out this example:

func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {

    let bugsnatchConfig = BugsnatchConfig(
        trigger: ScreenshotTrigger(),
        triggerActionConfig: EmailConfig(),
        extraDebugInfoDelegate: self)
    Bugsnatch.shared.setup(config: bugsnatchConfig)

    return true
}

Now that that’s done, you’re ready to go snatch some bugs.

Extracting additional debug info

Some projects require additional debugging info, e.g. the type of user (guest or registered), user's country or some other user setting. We’ve thought of that, which is why Bugsnatch features support for adding extra debug info dynamically.

To use it, all you need to do is conform to BugsnatchExtraDebugInfoDelegate and implement the computed property called extraDebugInfo.

An example of how to do this can be seen here, in the AppDelegate extension:

extension AppDelegate: BugsnatchExtraDebugInfoDelegate {

    var extraDebugInfo: String? {
        return SessionManager.shared.user.id // example dynamic info
    }
}

If you don't need any additional debug info, just leave out the extraDebugInfoDelegate argument when initializing BugsnatchConfig, and you’re good to go.

How to trigger Bugsnatch?

While your app is running, Bugsnatch can be triggered by any one of the available triggers at your disposal.

When configuring the Bugsnatch configuration, simply choose which trigger to use, since Bugsnatch comes out of the box with two kinds of triggers:

  • a screenshot trigger
  • shake gesture trigger.

If these are not your cup of tea, you can also create custom triggers, or trigger Bugsnatch directly from your code when needed.

Creating a custom trigger

To create a custom trigger, you will have to conform to the Triggerable protocol, which requires implementing a delegate of TriggerDelegate type.

Declaration of Triggerable protocol can be seen here:

public protocol Triggerable {
    var delegate: TriggerDelegate? { get set }
}

That way, when you want to trigger Bugsnatch, the custom trigger should just call the delegates’ didTrigger() method.

Here you can see how it looks like for ScreenshotTrigger implementation:

public class ScreenshotTrigger: Triggerable {

    weak public var delegate: TriggerDelegate?

    public init() {
        _setup()
    }

    private func _setup() {
        NotificationCenter.default.addObserver(
            forName: UIApplication.userDidTakeScreenshotNotification,
            object: nil,
            queue: .main
        ) { [weak self] notification in
            self?.delegate?.didTrigger()
        }
    }
}

Taking screenshots

An important feature of Bugsnatch is the possibility to use screenshots of your application when snatching bugs.

In the email version, screenshots are automatically attached to mail for quick dispatch.

In the Productive version, Bugsnatch automatically saves the screenshot to the device. The captured screenshots help reproduce a certain bug later on and make debugging sessions easier.

Supporting localization

Bugsnatch contains labels and buttons with hardcoded text values. By default, these are displayed in English. If you need more language options, we’ve made this easy by supporting String localization.

When configuring Bugsnatch, you can change the String values for every label and button that will be displayed when starting the Bugsnatch library.

For supporting localization, simply pass BugsnatchLocalizationConfig with your own data as the localization argument when initializing BugsnatchConfig.

Here you can see a simple example of changing two values:

let localization = BugsnatchLocalizationConfig(
    titlePostfix: "fancy bug report", 
    applicationName: "App name")

let bugsnatchConfig = BugsnatchConfig(
    trigger: ScreenshotTrigger(),
    triggerActionConfig: EmailConfig(),
    localization: localization)
Bugsnatch.shared.setup(config: bugsnatchConfig)

Available in two versions

Bugsnatch can be used in your project in one of the two available versions – email and Productive.

If you set up the email version, triggering Bugsnatch launches the native iOS email-sending form. The subject is filled with an auto-generated title, the text body is filled with debug info, and the screenshot is attached automatically.

The only thing left for the QA engineer to do is to fill in the recipient's email address, and optionally, add further explanation of the bug.

In case the Productive version is set up, triggering Bugsnatch launches the Productive web page for creating a new task in a web view. Debug info is then automatically entered in the task creation form, and all other information related to the task, as well as the screenshot saved to the device, can be manually added.

Create better bug reports with Bugsnatch

So, now you know about Bugsnatch, a simple but effective iOS library which helps QA engineers identify as many errors as possible and save precious minutes. It proved really useful in many of our projects and we are already exploring ways to expand and improve it.

We’re excited to share it with the community, so please test it and feel free to share your improvement suggestions!

Also introducing a plant you can't kill, courtesy of designer Marijana Šimag.