This is a guest post from Karl MacMillan, founder of Rakkoon and a happy Bugfender user.
Explaining this is going to cause a little bit of embarrassment as I admit how clueless I was when I started iOS development. We’ll get to that in a minute. First the background.
When I started beta testing my first iOS app, at some point I pushed an update that caused some users who were previously logged into the app to not be logged in anymore. Note that I said “some users”. Not all. The worst kind of bug.
Of course I had been testing the app, including wiping a device to test first run experience. And I had forced my co-workers to test the app. None of us had seen this behavior and I could not reproduce the bug intentionally.
Now this app requires an Internet connection and connects to it’s backend on startup (and stays connected). So up until this point I had debugged with the help of backend server logs, which was working fine. But this bug was, of course, different. What I really needed to know was what was going on before the app connected to the backend.
So here's where I have to admit my ignorance. Up until this point I had not even thought about how I was going to collect logs from end users for my app. I had seen mentions of crash reporting in iTunes connect and I just assumed that there would be some way to look at logs.
So I opened up iTunes connect and searched around for how to get logs from my app running on users’ devices. Nothing. Then I saw that you view crash logs in Xcode. So I dug into Xcode, found the crash organizer, and there was nothing. Yay for Swift - no crashes! But no logs either.
I have to admit, at this point I panicked a little bit. It wasn’t a lot, but it was more than just hyperbole to make a blog post more interesting. I imagined having to get users to somehow manually collect logs. Would they have to use Xcode!?!?
You see, logging is the primary way that I debug and manage systems. I’ve been staring at logs since my first job as a Unix system administrator back when I was preparing for Y2K bugs. I’m a die-hard “debug by logging things” kind of guy and only crack open an actual debugger when I have to. So I had to have some way to get logs from end users.
At this point I remembered that Google existed and found Bugfender.
Once I found Bugfender it didn’t take long to get running. I was already using CocoaPods, so getting the SDK was a â€˜pod install’ away. I was also using XCGLogger to automatically get a little more information into the logs. It didn’t take much code to connect the two together:
And just like that I had what I needed – logs from user devices.
My method of debugging – which is hardly unique – is to verify what I know must be true. Usually one of the “must be true” things actually isn’t, and that makes the bug apparent. It’s really just a way of checking assumptions.
So I instrumented my app to verify that:
a) The login token that I was storing in the iOS Keychain was somehow missing or
b) The login token wasn’t valid or
c) I somehow wasn’t getting to the code path that would send the login token to the server even though I had the token (I knew that the app wasn’t sending the token from the backend server logs and had stared at the code enough to convince myself that if we had the token we would send it)
I did the build and released a new beta.
After viewing the logs from the beta testers that reported the problem it was clear that the token was present in the Keychain and was valid, but the app wasn’t sending the token. I had included that possibility just to be comprehensive – I didn’t think it would actually be the case. But it was.
So I went back to staring at the code but with some certainty of where the problem was. That’s the beauty of this method: once you’ve verified that the “impossible” is true it’s easier to look at the code hard enough to see the problem.
And soon enough the bug popped out. The app, which is aimed at families, has two modes: parent and child. In the child mode we do some additional configuration on the device. The branch that would determine if the token would be sent said “do I have a login token AND is this device configured”. That second part – checking that the device had been configured – was hidden behind a functional call that I thought would short-circuit and return true on parent devices.
And then it hit me. For myself, my co-workers, and even the case where I tested on a wiped phone, we would test the app in the child mode first and then run it as a parent. So on all of those devices the extra child configuration was present even if it wasn’t used in the parent mode, meaning that the expression was true and the login token was sent. Looking at that function call it was clear that I was always checking for the configuration, even on parent devices where it should never be present.
Definitely one of those bugs that is impossible to NOT see once you’ve seen it. A quick bug fix and beta release verified that this fixed the problem for the beta testers.
Once this initial bug was fixed, I kept Bugfender on and even added some additional logging. I’m usually a “defensive logger”, meaning that I do a little extra logging to help track down future bugs without having to push a new build. Since then, I’ve used it many times to find solutions to problems. Some more examples of where Bugfender has saved me:
Background refresh – the great thing about background refresh – from a user’s perspective at least – is that iOS only runs them in a way that won’t kill my battery. But from a developer perspective it can be a nightmare to figure out when and why (or why not) background refresh tasks are running. Bugfender logs definitely helped here.
App controlled VPN – being able to tunnel traffic using a VPN configured by your app is great when you need it. But there is nothing like completely screwing up the networking on a device to make things hard to debug. Bugfender has let me verify exactly what configuration was set on a device and – remember those kids I mentioned earlier – try and figure out how they were intentionally breaking the app that their parents forced them to use.
I also use Bugfender to get a feel for how often my app is getting used and on what device / OS versions. I have other tools for when I really need to dig into that kind of data, but Bugfender gives me a good overall feel for what’s going on and having the data right there when I’m reviewing logs is super helpful.
I’ve since launched this app into production and I can’t imagine supporting it without Bugfender.
This was a guest post from Karl MacMillan, founder of Rakkoon and a happy Bugfender user.