On Goodreads and Books

I decided to join Goodreads in 2015, in part to see if I can get some good book recommendations from it, and in part because I really like to talk about books with folks so it seems like a network I'd like. That's my belated explanation why the Goodreads lists popped up in the sidebar recently.

Overall I'm still figuring out Goodreads. It is super-aggressive about wanting my Address Book which isn't going to happen. My rule is this: I'd be mad if a friend gave my email address to any social network so I shouldn't give out my friends email addresses. I think it's settled down a bit now, but the first few times I logged it that was like a "Here's the super-important next step!" and I definitely had to click "No" multiple times.

While rating a few books I discovered the latest Discworld novel, Raising Steam came out last November which I did not know. I'm reading that now so we can definitely put that down in the win column for Goodreads-as-recommendation-engine.

Anyway, as I said I didn't give up my contacts so if you're reading this and you have a Goodreads account ping me and I'll look you up. With only a tiny bit of judging on your reviews and ratings ;-)

UILocalNotification & Do Not Disturb Settings

File this under "Totally Obvious, no doy."

The app I'm working on these days has user-specified timers. I've been dogfooding it now for a couple of months and periodically I would get these things where I'd stop getting the audio timer notifications and I'd burn some cookies or a loaf of bread or whatever. Every time I'd dutifully mail myself the log file and the next morning I would A ) not be able to reproduce the problem and B ) not see anything wrong in the log file. Then one day I finally figured out the common factor: I had turned on "Do Not Disturb" on my phone at 10 PM. I have no idea why, it's not like I ever go to sleep at 10 PM. While iOS's Clock app can make alert sounds during Do Not Disturb my app cannot.

My immediate problem was easy: I pushed Do Not Disturb until midnight and if I need a timer after midnight I need to use Apple's Clock app. But I have a bigger problem with the app: how do I educate the user about this issue and/or alert them when it is a problem? Well, the thing is the timer mostly runs via a scheduled UILocalNotification. If you're running TodayTimer in the foreground then I'm running timers directly in order to update the UI. But as soon as TodayTimer enters the background I schedule a UILocalNotification to go off when the timer finishes. If I could see that Do Not Disturb will be active when the timer completes then I could alert the user not to rely on the timer. This wouldn't be perfect in two cases: when the user manually turn on DnD or adjusts the scheduled DnD window; or when the device travels into a new timezone (because DnD works in local time-zones but the UILocalNotification is scheduled against GMT. I might check the time, decide it was safely outside the DnD window, but the phone could travel into the DnD window before the timer expired.) Still, both of those were going to be super-rare.

This is a great theory, but the problem is the OS doesn't expose the DnD settings. So I filed a bug report (Open Radar), as a good iOS ecosystem citizen. I can understand why they aren't letting my app override DnD, I don't want Clash of Clans having the ability to play noises because I haven't clicked the cow in the last hour or whatever. But I don't see any issue with letting me know when DnD is active. I guess I can see a concern that app developers would schedule around DnD then. I'd settle for the call that schedules the notification returning an error message letting me know that my sound isn't going to happen.

Anyway, something you want to know if you have time-critical notifications with sounds. Both the sound and the vibration are silently failed by DnD and I don't see an Apple-approved way of handling the situation. I'm going to have to write some terrible end-user text that nobody will read and then just put up the with complaints & bad reviews …

Simple keyboard/text field interaction handling on iOS

EDITED TO ADD 5:15 PM PST I updated the gist because I realized I wanted to have a block run whenever I finished adjusting the view and therefore I added a completionBlock property. I'm using this to scroll a UITableView that contains text fields: whenever the keyboard finishes adjusting then I scroll the UITableView so the cell being edited is at the bottom. It works pretty well!

RoadTrip has a couple of places where if you hold the phone in landscape the keyboard can cover up a text field you're trying to edit. It's always struck me as weird that iOS can't handle this for you automatically but it doesn't, so there's a little weird piece of code you have to write to rearrange views somehow. In both places in RoadTrip I wanted to do the same thing: simply slide the main view upwards so that the field being edited is just over the top of the keyboard. I even wrote a comment saying that I should make this code some sort of generic solution, but I never went back to do so.

While working on 1.6 I noticed that the new predictive text bar on iOS 8 caused a problem with that code, so I patched it up. While doing that I noticed that the API for keyboard position changes does something other than the documentation does: if you ask for the notification curve you're supposed to get a UIViewAnimationCurve enum but sometimes you get 7, which is really a bitfield you can assign to the proper parts of the UIViewAnimationOptions. General consensus on the web seems to be to just take the "UIViewAnimationCurve", bitshift it up 16 places, and slam it into the UIViewAnimationOptions but that seems awful fragile to me.

Anyway, I fixed it up for RoadTrip, made another mental note to extract that code and submitted it.

Yesterday was the day I really did. I rewrote it into Swift and made a simple little widget that you can just create, feed it a view, and it watches for the keyboard notifications. It's a pretty simple class overall and I threw up a gist with the source. Take a look if that sounds like something you'd like to have. It uses my logger, but you can easily remove those calls.

I'm not sure what the deal is with the damn HeaderDoc comments. Sometimes they work for me in Xcode's Quick Help, sometimes they don't and I can't find anything that makes a difference. If anybody knows how to use HeaderDoc to make comments in Swift framework code that works reliably in both Objective-C and Swift let me know. I can't find anything about it.

Do not adjust your browser, we control the horizontal and we control the vertical

Hey all. This is just a quick note to say sometime in the week or so I'm going to change my website over to Squarespace 6 (it's currently still running on 5.) That should in theory be seamless but I know theory and I know my friend Murphy and his law. So if you see something weird going on please go ahead and drop me a line. Hopefully at worst there will be a day or so of whatever DNS disruption we need and then maybe some fiddling about as I settle in. (I'm not 100% sold on the theme I'm using, for example.)

Anyway, should be a fairly minor issue and then things should work a little better.

Using Adaptive Transitions in iOS 8

Hey Party Peoples! I've been writing a new app, all iOS 8-only. Gonna use Swift! Gonna use adaptive segues! It's gonna be modern, it's gonna be cutting edge! No more separate iPhone/iPad storyboards for this guy. I've watched my WWDC talks, I'm good to go! Well hold on there space cowboy!

Let's talk a bit about popovers. I'll assume you've watched 214 about View Controller Advancements, you've watched 216 about Building Adaptive Apps, and you've watched 228 about Presentation Controllers. There are several talks that all say something like "and you can use an adaptive popover segue and it will work on an iPhone. You need to add a back button which we can't go into here, but go watch 228 for more on that." 228 seems to be the endpoint for popover knowledge and 288 does something very subtle that … I don't think the intent was to mislead but it led me astray. I watched the key bit several times before I caught the omission.

The main glitch, to my mind, is that the popover example (the "Important People" app) isn't using storyboards and that they show code snippets on the slides but they don't provide the entire source to the Important People app. There is a point around 9 minutes in where they show flipping between a compact and regular horizontal size class and how the view is a popover in regular and fullscreen in compact. Of course, they couldn't talk at WWDC about why you cared but we know now: this is what happens when you rotate a iPhone 6+ into landscape.

Here's the transcript from asciiwwdc.com:

So to do adaptivity with popovers is fairly simple.

Going back to Peter's code that we left off with is that this code doesn't actually change.

All through that, this was the code that presented that popover.

But to influence how the popover actually works, we need to set a delegate on the popover Presentation Controller.

And we need to implement just two methods.

And in the slides they show us the two methods and it seems great! Except if you do that in your app it won't work. Plus the sample code is weird. They create a UINavigationController up from scratch and make the root controller the presented view. How do you ever get out back out of that? Where did that weird Dismiss button come from in the first place?

Look at that transition when they demonstrate the "Dismiss" button very carefully. It isn't a push transition, it is a Cover Vertical modal presentation. That's why the button isn't a "Back" button and it's not on the left. That's the rub: I'm pretty sure they manually built a button and stuffed it in the navigation item but they never show that code. If you blindly "just implement two methods" and drop in the code they show in the slide you will be sad. You get stuck when you run in compact horizontal (ie: any phone in portrait) where the view is full screen and there's no way to dismiss it. The missing code is simple but I think the presentation inadvertently implies the runtime provides it and it does not. Here's my Swift version:

// Clipped from viewDidLoad
    navigationItem.rightBarButtonItem = UIBarButtonItem(title: "Dismiss",
        style: .Plain,
        target: self,

func dismiss() {
    dismissViewControllerAnimated(true, nil)

I was naively assuming we had a UINavigationController at root and the popover segue adaptively became a push. It does not. I don't think you can do that. Popovers adaptively become modal presentations and thus you need to dismiss the view controller when you're done. (It doesn't have to be a Cover Vertical transition. It will use whatever modal transition you set on the destination view. You can use a flip or whatever if you'd prefer.)

I had a version of my where the storyboard had the root UINavigationController and I had both the push and the popover transition in the storyboard and checked the size class at run time to select the proper segue. It worked, but you couldn't rotate the device after presentation and have it change adaptively. The code example above, plus the code from the slides in 228 does exactly that. On the 6+ the screen will flip between a full screen with a "Dismiss" button and a popover fluidly as you rotate.