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?