Mutility 1.1.0
/This is the first update with user-facing improvements (compared to me just cleaning up my own mess on the back-end)! Overall, the look has been simplified. Mutility 1.1.0 nixes the center black bar and the white background behind the life panels in favor of a simpler, theme-able look. Selecting between the white and black themes is included with the app for free, and makes a big difference in the looks department depending on which color iPhone you're using. The center bar animations have improved and the life panels have grown slightly. Further, the subtle triangles denoting which side to tap for increasing/decreasing life have been replaced with easier to see and unambiguous +/- symbols. But wait, there's more:
- Updated UI - as described above. The center bar animation is much more fun now.
- Crash fix - Opening and closing the settings or history screens quickly with a poor network connection could cause a crash in some cases. Been trying to squash this one in the past two versions and think I finally nailed it. More on that later.
- Embarrassing bug fix - Users who had not purchased the customization & history in-app purchases were shown the wrong life totals in the history table. Yikes, fixed.
- Integrated Tapstream analytics
Example of the white theme with a dice roll. Check it out on a white iPhone.
And now on to the TL;DR part:
Crash Fix - Take 2
Finally, I think I've gotten to the bottom of the crash that can occur when opening and closing the settings screen with a spotty network connection. I've received two Crashlytics error reports about this, had it happen to me once out and about and then just recently during testing and (thankfully) in the debug environment. Although the bugs I squashed in 1.0.2 could also cause crashes, this was probably not what was happening. It was this Stack Overflow thread that finally pointed me in the right direction.
The only pieces of code I didn't write from scratch in this app were the in-app purchase methods I built up from Ray Wenderlich's excellent tutorial. The line that was crashing was always a completion handler assignment, like this:
_completionHandler(YES, skProducts);
Each time the settings or history screen is opened, I ask the App Store for the current price of the in-app purchase. If you have a poor network connection, the App Store may not have responded by the time you dismiss the screen. My original thought was that that the method being called, either request:didFailWithError: or productsRequest:didReceiveResponse:, was failing because the object that the completion handler was to be delivered to (either my Settings screen or the History screen) had been dismissed and thus deallocated. The fix in 1.0.2 was to properly un-register these objects as observers from NSNotificationCenter when they were dismissed, so the product request response wouldn't get sent to a deallocated object and crash.
But I was mis-reading the error. The error pointed the specific line in these methods where _completionHandler was being set. Here's one method that had crashed, which was directly from Ray's tutorial:
- (void)request:(SKRequest *)request didFailWithError:(NSError *)error { NSLog(@"Failed to load list of products."); _productsRequest = nil; _completionHandler(NO, nil); _completionHandler = nil; }
The SO thread pointed out that after _completionHandler is set, it's nil-ed out, and that a second call to it would cause a crash since it had been deallocated. Since I make a request each time one of these screens is opened, it is quite possible to make multiple unfulfilled requests by opening and closing the screen with a spotty network connection. Once that network connection gets its act together, multiple store requests could then come back. The first would be OK, but subsequent ones would cause a crash. I took the advice of that thread and added logic to tell if a request had been placed already and assured that I wouldn't place any more. Pfew.
Embarrassing Bug Fix in the History Screen
This is what I get for rarely testing the app without the in-app purchase, well, purchased. If you had not purchased 'The Basics', you are only shown the last life transaction in the history table and told how many more you're missing out on. Well, I was showing the correct life delta, but the life totals were stuck at whatever the first transaction set it to. If player 1 lost 2 life at the beginning of the game, this cell would report that they had 18 life permanently. Oops, fixed, eek.
Tapstream Analytics Integration
Like Crashlytics, Tapstream was another service I discovered on Marco.org. So what am I using this for?
- Real-ish-time install and app opening statistics
- Monitoring the effectiveness of the links I embed in the app and on social media
- That's it
They let you collect all sorts of information, but I've opted out of all of it. The analytics I collect don't even use the Advertiser ID. It's completely anonymous and I don't gather any information about your phone.