View Controllers Make Crummy Navigators

My fellow iOS developers, we have a problem. We’ve been led astray by Apple. Don’t get me wrong, iOS is great. I love writing code in Swift (and Objective-C!). I’m a fan of Xcode too. And UIKit really makes a lot of really powerful things quite easy. But allow me this hot take: View controllers should never have to deal with navigation.

There’s a long running joke about MVC actually standing for massive view controllers and navigation is a key reason. Even a relatively simple view controller with a couple of possible destinations, using storyboards and segues, needs to implement methods that could grow to be dozens of lines long and end up knowing about the implementation details of view controllers that come after them.

What are we to do? There must be some way that can keep our view controllers clean of navigation and doesn’t fight the UIKit frameworks completely, right?

There is!

If we treat view controllers like functional pieces, we can expose a small interface to interact with them and use delegation to get stuff out. Here’s a trivial example:

protocol PeopleViewControllerDelegate: AnyObject {
    func personSelected(_ person: Person, from viewController: PeopleViewController)
}

final class PeopleViewController: UIViewController, UITableViewDelegate {
    var delegate: PeopleViewControllerDelegate?
    private var people: [People]?

    func configureWithPeople(_ people: [Person]) {
        self.people = people
        // fill a table with people
    }

    func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
        // find the person
        self.delegate?.personSelected(person, from: self)
    }
}

This view controller (while grossly incomplete) gets configured with an array of people that will fill a table view. Tap on a row and the view controller’s job is done. It hands off the person to its delegate. What happens next is out of its hands. Perhaps we’ll show a detail screen, but maybe it’s just an API call and this view controller gets dismissed. Our PeopleViewController doesn’t know, and doesn’t care. Kind of great, right?

This begs the question of what exactly is the delegate? I’ve come to really like the coordinator pattern myself. I like having objects whose specific responsibility it is to manage the flow of my app (or just a given object’s slice of the app).

There are some things that you’ll have to give up, of course. Storyboard segues are a non-starter in this pattern. This means that there are large swaths of sample code (Apple’s and others’) that you’ll need to learn to either ignore or translate into this pattern.

So let’s trim down the size of our view controllers. Let’s narrow their scopes of responsibility, and in turn we’ll get code that is more maintainable and easier to reason about. I think we will all be better off.

Childlike Faith

And calling to him a child, he put him in the midst of them and said, "Truly, I say to you, unless you turn and become like children, you will never enter the kingdom of heaven. Whoever humbles himself like this child is the greatest in the kingdom of heaven. (Matthew 18:2-4, ESV)

I was on a walk with Finnian (my 21 month old) and as we were on the sidewalk he wanted to walk on the curb that separates the sidewalk from the road. You might have heard that kids his age don’t have the greatest of balance and I can confirm those reports. So he would reach up to grab my hand to keep steady as he walked on the curb.

But the thing is, he didn’t even look at me. He just stuck his hand up in the air and went along the curb. He was confident that I would help him stay balanced by taking his hand.

It struck me the third or fourth time he did this that he had immense faith that I would help him to not fall. And then God reminded me that this is how I’m supposed to be with him. Jesus says that I need to become like a child to enter the kingdom of heaven. What does that mean?

I should stick out my hand and ask for help more often. But I don’t. During everyday life I try to go it on my own. It doesn’t even occur to me to pray while I’m figuring out some way to make something animate just right or parse a feed. But why not? Honestly, I don’t know. But I want to.

How do I do this? I come before God and ask him to help me remember him throughout the day.

Rejoice always! Pray constantly. Give thanks in everything, for this is God's will for you in Christ Jesus. (1 Thessalonians 5:16-18, HCSB)

do not be anxious about anything, but in everything by prayer and supplication with thanksgiving let your requests be made known to God. And the peace of God, which surpasses all understanding, will guard your hearts and your minds in Christ Jesus. (Philippians 4:6-7, ESV)

Amen!

At a previous job I used to keep a post-it note on my monitor that just said “Pray”. Maybe I should bring that back and put it on the monitors I use on a day-to-day basis. That constant reminder that God is close and I’m beckoned to draw near to the throne of grace (Hebrews 4:16) could be the kickstart I need.

Reconsidering My Blogging Setup

I have a very on-again, off-again habit of writing. Sometimes I’ll have a lot to write about, and often I’ll go long stretches without writing much. I backed Micro.blog on its Kicskatarter the day it came out (happy birthday to me!), and it’s gotten me thinking about what blogging even is. Whoa.

I’ve usually considered blogging to be posts like this one. Longer things that I host on my website. But now I’ve got my microblog. I usually think of these posts as “Tweets that I own”. We all know that when you post content to a place you don’t control –  like Twitter, Facebook, or Medium – that you don’t own the post. Not the URL, the appearance, none of it. You gave them the things. But when you own the domain, you can control it all.

So my mental model of replacing tweeting on Twitter with Micro.blog was a good start. But now I see that microblogging is still blogging. Whoa.

Now I’ve got the itch for my website to feature both longer posts and microblog posts. I’ll have feeds for long content, micro content, and a mix of both. Posts should get to micro.blog through one of those feeds. And I’ll own the links to all of my content.

Now for the real question: how can I get this done?

I’m currently using Ghost to power my blog. But Ghost doesn’t seem interested in supporting microblogging (which is fine) so I’m going to look elsewhere. I don’t really want to use micro.blog itself because I don’t get enough control over things there.

What about Wordpress? I don’t like PHP, nor have I had good experience with them in the past. So that’s out.

I’ve been looking at Jekyll. That may end up being the winner but it will take some experimenting. I need to make sure existing links to ghost content will continue working and Jekyll ends entries in .html, but Ghost doesn’t. I’ll have to learn some level of nginx rules to get this done, is my guess.

But I think what I really want is something like Brent Simmons’s Wildcat system. I just wish it was written in Swift so I could understand how it’s put together. I’ve never worked in Ruby before, and while that’s not a dealbreaker it’s definitely additional friction.

I also would love to find something that can work with my text editor (Ulysses), can support image uploading automatically, and has Mac & iOS variants for me to write and publish posts easily.

This is going to take some time, and hopefully I’ll get it right.

Hawaii Trip

We spent the last week on the Oahu island of Hawaii, getting home late last night. It was an amazing trip and couldn’t have gone any better. We rented an AirBnB house in Kaneohe (which is the windward side of the island). The weather was quite good most of the time; even when it was raining, the rain was warm. Quite the change from Seattle.

View from Diamond Head

Waikiki Beach

Going into the trip I was most nervous about how the boys would do. We took them on a plane last year to Phoenix but Finnian is 20 months now and can’t be still for more than 3 seconds. We didn’t buy him a seat on the plane so he was going as a lap baby. Atticus is 4 now and we figured we could distract him with shows & movies on “his” iPad. They both did wonderfully well. A couple of screaming fits by Finnian but that was manageable by getting him to sleep.

Dole Pineapple

Tree on the Dole Train

Of course, I took along my camera. I’ve got the Sony A7II body and while I normally have a 50mm f1.8 prime on there, I wanted something that could capture the landscapes we’d be seeing and get good portrait shots of us on the trip. So I rented the Sony 24-105mm f4 zoom lens from Hawaii Camera. In short, the lens is amazing and now I want one.

Emily had done tons of research and put together a packed itinerary that got us all around the island. We hit up the Dole plantation, the North Shore, hiked Diamond Head and hung out on Waikiki beach, visited Sea Life, and went to a luau at Disney’s Aulani resort. We had a lot of fun and came home really tired.

Finnian before the Luau

Atticus on the trip home

Xcoders Talk: Practical Coordinators

Last November I gave a talk at the Seattle Xcoders meetup about the coordinator pattern. There’s been a lot of virtual ink spilled on the topic of design patterns and the coordinator pattern specifically. I’ve been using it in a refactor of a side project app as well as using it pretty heavily at zulily in some of the new projects I had been doing.

I got inspired to give the talk after last year’s Swift by Northwest conference, where Dave DeLong gave a really great talk about improving MVC. He has a different take on coordinators than I do and his solution is really interesting. But I like where I’ve landed with coordinators for the time being personally.

I decided to put a little different spin on this talk than I’ve seen in other coordinator talks, and prepped a sample app that I would refactor. We go from storyboard segues to a coordinator handling the transition between two screens. There was a lot of live coding and it worked out pretty well. I hope it’s entertaining if not useful for you and I’d love to hear your thoughts on how it all turned out.

Video: https://vimeo.com/258453512 Slides: https://d.pr/NfRMIt

Making Proper Apps From Websites

While at zulily, I used to have JIRA and GitLab inside of wrappers provided by Fluid instances. Now that I’m at Lyft I’ve done the same thing with JIRA, but we use GitHub instead. For the longest time I would take links sent to me for those pages and paste them into the apps by hand since clicking on the links would open my browser instead of the app silos that I hand-crafted for these sites.

Well today @jackbrewster taught me about something great that takes this to another level. The app Choosy will let me route URL clicks to whatever app I want. I set up rules in Choosy to look at the web address that I clicked and now have it routing my JIRA and GitHub links to their proper Fluid apps. It’s wonderful!

My default browser is Safari, I keep Chrome around as my Flash ghetto (and now only it can be used for Meet as well). I can use Choosy to send the Hangouts/Meet links straight to Chrome as well. This makes everything feel completely seamless.

One other tip I got from Jack was about 1Password. Fluid instances don’t support it directly but you can invoke the global 1Password mini, find the site you need, and press ⌘⇧C to copy to the clipboard. Focus will then switch back to your Fluid app and you can paste the value in there. I was using the mouse to do this previously. This way sure seems a lot better.

Missing Options in Swift

The default behavior of iOS is to not show a notification (like the kind that comes in through your phone’s lock screen or Notification Center) if the app is in the foreground. I’m working on a ticket for the Lyft app to show or suppress notifications that come in while the app is in the foreground. iOS 10 introduced a whole new UserNotification framework that lets us do just that!

The delegate of our notification center will get a call aptly named userNotificationCenter(_ center: UNUserNotificationCenter, willPresent notification: UNNotification, withCompletionHandler completionHandler: @escaping (UNNotificationPresentationOptions) -> Void). Since we always have to call the completion handler – and that parameter defines the type of presentation we want – we can now control the notification’s display.

In Objective-C the options are so:

  • UNNotificationPresentationOptionsNone (kind of)
  • UNNotificationPresentationOptionsAlert
  • UNNotificationPresentationOptionsBadge
  • UNNotificationPresentationOptionsSound

But in Swift, UNNotificationPresentationOptionsNone is not available. In fact, you can only really see the docs for this option at this page. It’s listed as a Global Variable, and not a member of an enumeration. There’s a separate page for the UNNotificationPresentationOptions enumeration.

So I fired up a sample project and made a method that returned None from Objective-C and bridged that over to Swift. Of course it works, and the raw value is 0. So the options for my ticket are either add Objective-C back into the project (all our code is Swift) just to bring this over, or make a local variable set to a raw value of 0. The latter is likely what I’ll end up doing.

I’ve filed radar 36166279 to address this.

Update 12/21/2017

After posting this, Jake Carter replied to me on Twitter with an idea:

When I got in to the office this morning I gave it a go and it worked! I’ve used the [] syntax before so I’m a little disappointed that I didn’t remember it. I plugged that into my sample app and its rawValue turned out to be 0. So now I don’t have to choose between two bad options.

I think what tripped me up was seeing that the Objective-C version was an enumeration coupled with me not seeing that the Swift structure conforms to OptionSet. If I had slowed down for just a couple of minutes then I might have noticed.

I think I’m going to leave the radar open, though. I will probably amend it with these findings and make it more of a documentation issue rather than needing to add a new option to the UNNotificationPresentationOptions type. Thanks Jake!

Introducing Custom iOS App Analytics

TL;DR: Check out my new iOS framework for using CloudKit to track app analytics: TPHCloudAnalytics.

In my current reworking of Scorebook I wanted to get rid of Cocoapods if I could. I don’t have anything specific against it, but I wanted some more rapid development of my own framework code. So I moved the private pods I had to git submodules. This left me with only the Fabric and Crashlytics pods leftover.

While Fabric’s dashboard is quite impressive, they’ve added almost too much for my needs lately. I just want something simple to tell me how many sessions I’m getting with some basic device info, and maybe some ability to track custom events. Plus, removing Fabric meant that I could remove all closed source frameworks from Scorebook.

I had the idea a little while back to build analytics on top of the CloudKit public database. Today I had the opportunity to make it. The source is all up on Github here.

It’s fairly rough right now, only sending data to CloudKit. In the long-term I would like to add an iOS or Mac app that can ingest the data from CloudKit and present a nice dashboard. But for now this gets you to the place of recording your data.

I’d love any feedback you have to give.

Analyzing a Failed Build

I’m getting to work on Scorebook again this week, thanks to zulily’s allowance for a hack week for tech employees. It’s really nice to be in that codebase, even if I’m now horrified by much of what I see past me doing in there. That’s a good sign of growth, right?

Anyways, I wanted to setup a CI service and we had a demo of BuddyBuild at Xcoders a few months back. So I want to give them a shot. And my build fails. And fails. And fails. I had to fix bad file locations for my prefix.pch file, as well as my bridging header. But it was still failing.

Thankfully the folks over there are great. They followed up with me and asked that I clone the repo to a clean location and try building from there. It failed. Now I had to wonder what was going on. The BuddyBuild log was giving me a super helpful message:

** BUILD FAILED **
    The following build commands failed:
    	Analyze Scorebook/Scorebook/View\ Controllers/SBHomeScreenViewController.m

Alright, that’s not actually very helpful. Xcode’s error log pointed to importing my Scorebook-Swift.h file as being problematic. The funny thing about that file is that it’s auto-generated by Xcode. So the logs point to analyzing failures, and that file doesn’t exist yet. I had the RUN_CLANG_STATIC_ANALYZER flag set to “Yes” and the order seems to matter. So instead of building the project and then running the analyzer Xcode analyzes the files as they are being built.

See the problem?

When analyzing that view controller, it’s trying to follow a file reference that can’t exist until a successful build. If I set that flag to “No”, build, then back to “Yes”, the build succeeds. But clean builds will fail. So I guess I have to run the analyzer myself by hand.

Is this worth filing a radar over?

Making NSNotifications Type Safe

A while back, the good folks at Swift Talk posted an episode about using typed notifications. As one who deals with lots of notifications in the app I build for my day job I was intrigued. However it didn’t feel like their crack at the problem of using types in notifications went quite far enough. I wanted a way to make any notification easily accessible wherever I needed to use it.

So I took a swing. Here’s what I came up with.

I started off where Swift developers usually do: a protocol.

public protocol NotificationDescriptor {
    associatedtype Payload
    var noteName: Notification.Name { get }
    func encode(payload: Payload) -> Notification
    func decode(_ note: Notification) -> Payload
}

public extension NotificationDescriptor {
    private var _modelKey: String {
        return "ModelKey"
    }
    
    public func encode(payload: Payload) -> Notification {
        let info = [_modelKey: payload]
        let note = Notification(name: noteName, object: nil, userInfo: info)
        return note
    }
    
    public func decode(_ note: Notification) -> Payload {
        let model = note.userInfo![_modelKey] as! Payload
        return model
    }
}

So, what we have here is the definition of our protocol. We have the Payload that conformers will typealias something to tell us what is going to be handled be the encode and decode functions. Then we have the notification that is posted or listened for. So far, so good.

Next is an extension to give us boilerplate implementations of encoding and decoding. This is really only useful with custom notifications that you create, and not ones given by iOS.

To get this working, we need to also extend NotificationCenter so that it can listen for one of these descriptors. But first, there’s one class we need to build.

public final class NotificationToken {
    let token: NSObjectProtocol
    let center: NotificationCenter
    init(token: NSObjectProtocol, center: NotificationCenter) {
        self.token = token
        self.center = center
    }
    
    deinit {
        center.removeObserver(token)
    }
}

This class will handle de-registering from NotificationCenter so we just need to hold onto whatever tokens are necessary. When the token goes out of scope and it deinits, then it removes the observer automatically. That really is the worst part of using block based notification observers.

Next is the NotiicationCenter extension:

public extension NotificationCenter {
    public func addObserver<A: NotificationDescriptor>(descriptor: A, queue: OperationQueue? = nil, using block: @escaping (A.Payload) -> ()) -> NotificationToken {
        let token = addObserver(forName: descriptor.noteName, object: nil, queue: queue, using: { note in
            block(descriptor.decode(note))
        })
        return NotificationToken(token: token, center: self)
    }
}

This method will add the observer for the notification’s name, and – using the specified queue – decode the notification and call the block which will have the payload as its argument. This makes call sites really nice:

struct CustomNotification: NotificationDescriptor {
    struct Output {
        let name: String
        let type: String
    }
    
    typealias Payload = Output
    let noteName = Notification.Name("CustomNotificationPosted")
}

class Foo {
    var token: NotificationToken?
    
    init() {
        token = NotificationCenter.default.addObserver(descriptor: CustomNotification(), using: { (output) in
            print("received a \(output.name) of type \(output.type)")
        })
    }
}

var myFoo: Foo? = Foo()
let output = CustomNotification.Output(name: "thing", type: "superclass")
let note = CustomNotification().encode(payload: output)
NotificationCenter.default.post(note)
myFoo = nil

If you put this all in a playground (and the files can be found here: https://gist.github.com/jsorge/a216f95d871371d1684f45519a59c5a6), you will see in your console the printed output of the block placed in Foo’s initializer. Pretty cool.

This gets a little bit more complicated if you want to use an iOS system notification. It all still works, you will just have to implement the decode function safely.

struct KeyboardDidShowNotification: NotificationDescriptor {
    typealias NotificationOutput = (begin: CGRect?, end: CGRect?)
    typealias Payload = NotificationOutput
    let noteName: Notification.Name = .UIKeyboardDidShow
    
    func decode(_ note: Notification) -> NotificationOutput {
        let begin = note.userInfo![UIKeyboardFrameBeginUserInfoKey] as? CGRect
        let end = note.userInfo![UIKeyboardFrameEndUserInfoKey] as? CGRect
        return (begin, end)
    }
}

That’s all there is to it. I hope this is as useful to you as it has been to me!