nRF Connect for iOS: What is a Bug-fix Release?

It's been a month since we last spoke, when we announced the release of nRF Connect for iOS 2.0. What a day that was, wasn’t it? In any case, we’re here because, since then, we’ve made three successive bug-fix releases. With the last one, 2.0.3, being released over the weekendAnd bar any unexpected major issues with 2.0.3, this will be our last release before we move on to our next major version: 2.1. 

So, you must be wondering: if all we’ve been doing is “fixing bugs”, what’s the point of this blog post? 

The point is, precisely, to let you know that even these so called “bug-fix releases” carry an enormous amount of work and effort, including last-minute changes and additions following your feedback. And we want to let you know how we’re working, release by release, to perfect nRF Connect for iOS. So let’s get into it! 

2.0.1

This was our first follow-up release to 2.0, and true to its name, it started development before we even went public with 2.0, working to round-up those square edges that we weren’t able to, because we wanted to get this new app in your hands as fast as possible. 

Scanning Animation 2.0 (left) vs. 2.0.3 (right) 

When 2.0 hit the road, we began to get intriguing reports of crashes during Scanning Animation, and since we did not understand well what was going on with those crashes, we completely rewrote the animation. In 2.0, the animation was basically designed to last around 1 second, which is the time it takes for the first scanned devices to appear in the form of a list. If your device didn’t find any devices, and the animation kept going, it became a mess no one could fully make any sense of, with the blue pulses overlapping one another. 

Free from the pressure of having to release 2.0, we took the time to rework the scanning animation from scratch, in aid of doing things better to understand what was the source of the crashes, as well learning Apple’s SpriteKit APIs better. The end result was a scanning animation much closer to what we originally envisioned, one that doesn’t become a mess after multiple seconds of repeated viewing if you happen to start nRF Connect somewhere in the woods. Of course, as one of our team members likes to point out: this animation reflects not what a Scanner does, but what an Advertiser does. We’ll fix it next time  

Other than the Scanning Animation, we worked on an incredible heap of fixes. For example: 

  • Privacy “User Actions” screen was painfully slow to open in 2.0 
  • If the user tried to deselect a device they’re connected to, the app would simply swallow the user’s request. In 2.0.1, we surface a message to the user informing them why they can’t deselect a device they’re connected to. 
  • Double-Tapping on the Scanner tab deselects all devices that aren’t connected. 
  • In 2.0, we deselected all devices if the user rotated the device. In 2.0.1 this behaviour was removed. 

We also fixed the usual array of bug-fixes causing crashes. But instead of giving you a boring list with more of those, what if we speak about just one in a little bit more detail? For 2.0.1, the most interesting bug we could find was an if-clause for choosing which child UIViewController to set when the user switched tabs: 

let key = NSNumber(value: shownTab.rawValue) 

if cache.object(forKey: key) == nil { 

    cache.setObject(viewController(forTabshownTab), forKey: key) 

} 

embed(asChildViewController: cache.object(forKey: key)!) 

The cause of the crash, as you’d expect, was on the last line, due to a force-unwrap on a nil reference. It shouldn’t happen. It didn’t happen for us in all our development of 2.0, but it got reported as a crash in our Xcode instrumentation. So, we applied a bit of defensive programming, and we reversed the logic to always arrive at the embed(asChildViewController: ) line with a valid UIViewController, independently of whether there was a cache hit or not. 

How did we do that, you ask? Why, that’s homework for you, the reader! ^^ 

2.0.2

This was a very big release. Very big. So big, that we thought we’d skip the minor release naming scheme and bump its number to 2.1 straight away, because it added a lot of tiny details as well as medium-sized features that made a big difference to our users. So let’s start with one of them: Filters and Service UUIDs. 

Updated Filtering Options (left) and Advertising Services (right)

One of our users reached out, and they told us that they were missing Services to Advertise as in the Advertiser tab. And well... we went a bit overboard with it  We noticed within our code that the list of Services we were showing was hard-coded, and wasn’t a complete representation of the Service UUID database the app ships with (keep your eyes peeled for more news on this department) Since that database, essentially a JSON file, has a lot more Services, and it doesn’t make sense to translate them, we went ahead and made it so the user can advertise as any Service UUID that nRF Connect for iOS recognises. And since we were at it, we took things a little step further, and improved the appearances attached to our various Service UUIDs, and made the list more comfortable to browse through with added icons. In addition, these icons are colored with our Service color scheme, which basically follows these rules: 

  • Red: GATT Services 
  • Grey: Apple Services 
  • Blue: Nordic Services 
  • Yellow: micro:bit Services 

So now, when the user selects one of these Services to advertise as, and they see the colored-dots next to them, it makes more sense that it did before. But granted, we’ve got long ways to go regarding better display of Service information. 

Since we changed the code that allows the user which Service to Advertise as, we touched the Service filter too, to use the same names, foregoing the translated strings we were using before in the filter. This removes duplication, and reduces our code to a Single Source of Truth for Service UUIDs. And also, we realised, the names of our filters, didn’t truly make any sense. The ‘Manufacturer’ field was not filtering on the manufacturer data only, but across any kind of data advertised, including the advertising data itself. So we renamed it. The same thing happened with the “Advertising” filter name, which we changed to “Services”, since it filters for both a device’s connected and advertised services. 

Updated Settings Screen (left), DFU Options as of version 2.0.2 (right)

More additions. Thanks to the wonderful feedback in our previous blog post, it came to our attention that some key settings regarding DFU were missing, causing the process to stall or to fail before it could even begin. Given that these settings couldn’t just be explained by their names alone (or so we felt – let us know), we reworked the Settings screen to give the Scanner settings their own screen, and the same thing for the newly introduced DFU Settings. 

New “Copy to nRF Connect” pipeline for importing DFU ZIP files. 

Another piece of feedback was regarding the way many of our users used the 1.x version of nRF Connect to get their ZIP files into the app, which was importing or copying the file into the app, and then having it show up from the list. 

In our misguided frenzy to make nRF Connect 2.0 a better iOS citizen, we decided to only provide initial support for side-loading files through iTunes, or, using the new Files-app integration to pull content from third-party sources, such as Google Drive, Dropbox or Apple’s own iCloud. But it turned out that many of you did things the other way around – exporting the file into nRF Connect, rather than importing it. We brought support for this feature late into the 2.0.2 development cycle, which required the implementation of a barebones Filesystem structure to keep track of the files in nRF Connect 2.0’s protected Documents folder. Upon successful copy, we surface a message to let the user know that the new file is now available the next time you need to perform a DFU operation. 

Besides all of this, our usual host of bug-fixes came packaged-in for free. For example, we finally tracked the source of the scanning animation crashes; we gave a better look to all the crash reports, and realised they had one element in common: iOS 9. So from 2.0.2 onwards, on iOS 9 we show a static “Scanning … “ screen, instead of our SpriteKit-based animation. 

2.0.3

You’d think that, with 2.0.1 and 2.0.2 out of the way, the last bug-fix release would be minimal, right? It turned out that wasn’t the case. 

Because the RSSI Graph can take time to draw, it now reads "Loading..." before it finishes going through all the data points.

2.0.3 came about because we had a distinct #1 source for crashes: the RSSI graph. So we went about trying to reproduce its issues, and in the process introduced some new instrumentation (logging), and what we found out surprised us: once the Graph UIViewController enters memory, it never stops drawing the graph, so many of its drawing operations were not only happening when the user was not looking at it, but also, when the updated UIView(s) were not visible.  

Thus began the long process to fix all the issues that spawned from such a big change to how the RSSI graph worked and re-drew itself. First, we made sure the graph halted drawing once it was no longer visible. This had the knock-on effect of the graph, due to the way we’d written our logic, to not draw itself if the user performed a complete scan, and then switched to the graph. After fixing this, our tests began to complain that the new graph logic was not working as expected when the filter was enabled, and that took a while to sort out. 

And when that was done, came the next source of pain: drawing the RSSI graph was very slow. We believe that this is due to a combination of two factors:  

  1. Our code could be faster averaging-out the received advertisement data. Whether it’s performing these calculations ahead of time, or by implementing a much more efficient algorithm. We’ve got some popcorn ready for our binge-session of WWDC videos on how to get the best out of Instruments. 
  2. Our third-party graphing library could be faster. But preliminary analysis shows that it’s mostly our code that’s slow. Also, as a rule it’s better to make sure it’s not you who’s causing the troubles instead of always blaming your APIs. 

If the RSSI graph is slow to draw, that’s OK. But the problem was that our entire UI froze when switching to the Graph tab. And the culprit was that the RSSI graph data calculations were happening in our events (User Actions) serial queue, causing the UI Thread to wait for the calculations to be finished to display other information, such as the list of devices or their names. The solution? Move the graph calculations to a different thread / Dispatch Queue. This is still slow, and you’ll notice it the more devices and advertisements you have, but it’s much better, and a lot more energy efficient, than 2.0.2 ever was. Let's take things one step at a time. 

You can see the new date formatting APIs in User Actions (center) and Scanner Timeout (right)

That was far from all. A lot of tiny fixes were included, such as much better logic behind the selected / deselected rows for the Scanner, Graph and Settings tabs, which apply to both the iPhone and the iPad. We also got wind of other important DFU Settings that our users might need, so they were added in a pinch (as always, let us know if they work for you). Because this our first release within the iOS 13 window, we were also able to build against iOS 13 and use some of its new APIs, our favourite being the new RelativeDateTimeFormatter, which we used in the User Actions screen, and in our Scanner Timeout selection dialog.  

Oh. Cool bug alert! Remember the Scanning Animation problems in iOS 9? We still got a couple of crashes in 2.0.2. Guess why? Because our default was to always show the animation, and we checked whether the device’s iOS version we were running on was iOS 9 after showing the animation for the first time. 

Hide.

I want to help making nRF Connect better. What can I do? 

The idea behind this blog post is to show the incredible amount of work we’re doing to get the app, release by release, closer to what we all expect it to be. You, our users, are a big part of why we’re here, so we need your help. Thus far, you’ve been invaluable in helping us identify key features that you need, and we’re doing our best to tune the app to match those needs. But we can get there faster if you help us by becoming one of our Beta Testers, and/or, enable the switch inside your iOS device that allows Apple to send us crash reports when something goes wrong.  

In an ideal world, we’d like both things to happen. Especially having more people within our Beta Testers group, because those crash reports include full instrumentation data, meaning we can track a lot more accurately what happened exactly before the crash, helping us reproduce it, understand it, and fix it. 

Phew! That was a long post. Now, let’s go back to Xcode. We can’t wait to start fixing more bugs, and making nRF Connect 2.1 more awesome than all the releases that came before it combined! 

Anonymous