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!

Leveling Up as an iOS Developer

I posted to my microblog recently that I feel like I’ve been leveling up as an iOS developer. The main way has been diving into programatic layout as opposed to using Interface Builder. This has been really insightful to me and I have come to love writing my views this way. But mostly I’m learning that I can do it.

You can too.

I remember hearing about the devs who didn’t use IB and thought to myself, “that sounds like so much work.” Bear in mind that those prior folks were likely using manual layout to set frames and relied on a lot of math to get things done. Recent improvements to the Auto Layout system (especially layout anchors) make writing UI code incredibly straightforward.

For instance, to pin a view to the edges of its superview (something I have to do often) all I need is this:

firstView.addSubview(secondView)
secondView.leadingAnchor.constraint(equalTo:firstView.leadingAnchor).isActive = true
secondView.trailingAnchor.constraint(equalTo:firstView.trailingAnchor).isActive = true

And that’s really it. If you’re used to thinking about views in the relationship style of Auto Layout you too can easily start writing your views in code. There are lots of benefits: no more optional properties for @IBOutlets, you can keep you layouts contained to a single file instead of a source file and a layout file, and you can use things like UILayoutGuide to line up views without resorting to dummy UIView instances.

But my main idea has really nothing to do with ditching Interface Builder. There have been lots of blog posts about that and I’m not sure I could add much to that discussion.

My main idea is that mountains can be intimidating. You look up at someone who is at a higher point than you and wonder if you have what it takes to get up there with them. Start at base camp and get going.

You’ll try new things. You’ll fail. Get back up and keep climbing.

Months or years into your journey you’ll look down and remember where you started. Take stock of things in that moment. Praise the Lord for getting you where you are. It’s a big deal!

From there it’s important to stay humble and keep going. Try to help someone climb alongside you. Reach out to the people who are where you want to be and ask questions (you may be shocked at the generous people you meet along the way). We weren’t meant to live solitarily – find your community.

I’m taking this verse completely out of context but Romans 5:3-4 (NIV) says:

Not only so, but we also glory in our sufferings, because we know that suffering produces perseverance; perseverance, character; and character, hope.

Perseverance builds character. We level up when we keep going in the face of difficulty.

Swift Function Passing & Memory Management

I’ve started using a pattern where I use a struct that has a series of optional closures on it to serve as a way to hook into default implementations of things like data source and delegate calls. We have a fairly complex way of presenting data in our collection views that requires a lot of boilerplate, but specific areas of our app needs to add behavior to fit its requirements.

The problem that I’m running into now is that I want to assign these closures to functions on my object instead of having a ton of inline closures. I really like this approach because it allows me to keep things well organized. But there’s a problem in my memory graph.

When I pass these functions into the closures, I’m capturing a strong reference to self. When using a closure syntax I could use a capture list to take either [weak self] or [unowned self] and be fine (in fact, replacing the function references with closures doing just that makes the problem go away).

Here’s a gist replicating the issue:

If you copy the file into a playground and run it you will see deinit called on the VC but not on the data source or the frame. If you uncomment the closure on lines 46-48, they will all have deinit called as you would expect.

Is there a way to capture an [unowned self] when passing a function by reference like this?

Update: I wound up going with a solution that uses a closure for the hook instead of the function reference – passing in unowned self inside the capture list. Inside that closure I call the method. It doesn't look quite as nice as what I wanted to do, but gets the job done. Here's what that would look like with the example:

hooks.numberOfSectionsIn = { [unowned self] (section) -> Int? in
    return self.numberOfSectionsIn(section)
}

I'm Microblogging

I backed Manton Reece's Kickstarter project for micro.blog back in January. Invites started rolling out this week and I got mine the other day. If you're interested, I'm setup at http://jsorge.micro.blog.

I'm not sure how I'll use this vs using Twitter but I do have cross-posting setup so my posts become tweets. I'm also thinking about writing a lightweight posting client for the Mac. but we will see.

AVAsset and ... Touch ID?

Last week I ran into a crazy bug where trying to create an AVAsset instance triggered Touch ID to come up on my screen. Not only was I baffled, I was slightly concerned I wouldn’t be able to figure out what caused this to happen or how to work around it.

I was tasked with adding support for auto-playing video on zulily’s app, a feature that we are adding to our app. I got it working in our dev environment flawlessly, got it merged into our TestFlight branch and deployed to users for testing. The first day, I got reports of Touch ID displaying before playing a video. Authenticating or cancelling made no difference – the video played either way.

I started to dig in. I still couldn’t reproduce on a debug build, only release. So debugging became painful to say the least. A short time later I realized that this is because our debug builds append “.debug” to our product bundle ID so we can run retail and debug side by side. Flipping this back got the issue happening on my device. Bingo!

At the advice of Tim Ekl I got the prompt on screen and paused in the debugger. This revealed to me that creating the asset triggered NSURLCredentialStorage, as well as CFURLCredentialStorageCopyAllCredentials. So creating the asset by a URL triggers the keychain to be accessed.

Diving into our keychain storage indicated that we are storing an item with an access control of kSecAccessControlUserPresence. This access control type is defined as “Constraint to access with either Touch ID or passcode. Touch ID does not have to be available or enrolled. Item is still accessible by Touch ID even if fingers are added or removed.” So it seems any access to these kinds of items will bring up Touch ID. Aha!

I’ve been able to narrow it down, now I needed a workaround. Thankfully the thing we are stashing in the keychain is a fairly narrowly used item to store user credentials for our login screen. Most people likely never sign out and back in again. So my solution is to delete the item at startup, just once. If they want to save their login credentials again then we won’t use the access control flag.

To trigger Touch ID, I wrote a method on our manager class to authenticate and then execute a block. This will let us authenticate the user, then retrieve their stored credentials. I was somewhat saved by the fact that we were using this in a fairly trivial manner. I opened up a DTS ticket for the issue and it is currently being looked at by Apple Engineering.

Additionally, I created a sample project to reproduce the issue (it’s on GitHub here). I also filed radar 31353719 for Apple to investigate further.

I’m curious to see what the solution to the problem is. It likely will be some bug fix in AVFoundation or even Foundation. We shall see. I’m just glad I got the videos working for our project. But it was a fun little bug to hunt down.

Connecting An Airport Extreme to FiOS

When we got FiOS installed a couple of weeks ago our network had to go through some changes. Frontier gave us not only a bridge to go from the outside connection to ethernet, but also mandated that their router be installed in the network in order for TV guide and menu functionality to remain. I wanted a way to keep my Airport Extreme router on the network, and have found a way to do just that.

This post is mostly going to go over the details I found in this forum post and give a little bit more detail into how I got things working. First of all, here’s a map of the setup:

FiOS Setup Map

The MOCA bridge connects from its ethernet port to the WAN port of the G1100 (my FiOS provided router). From there the router plugs into the WAN port of my Airport Extreme. The steps linked above will turn the G1100 into a bridge and the Airport will handle the rest of the network handling – Wifi, DHCP, port forwarding, etc.

My original crack at this was to hook the Airport directly to the MOCA bridge. This worked and provided internet, but the TV box had limited functionality. Following the directions and hooking things up in like in the illustration got the TV back up and running like we want it to be.

I’m still not sure how exactly the G1100 sends any data to the TV box (a Motorola QIP-7100), and if anyone does know I’d love to hear about it. My hunch was that the 7100 has some sort of wifi component that is hidden from any menu and talks to the G1100. However the G1100 has its wifi completely turned off.

Somehow it seems that the G1100 sends data back over the coax connection to the MOCA, which may in turn go back to the ONT? I’m grasping at straws here. If I hear of a good solution to remove the G1100 from the equation I’ll definitely follow up on that.

If you don’t care about TV functionality, you can connect the Airport (or other router) straight to the MOCA and not even bother with the G1100.

Stop Fighting UIKit

I’m working on an update to our search screen for work, and getting my hands dirty with UISearchBar. I’m placing the search bar in the titleView property of our view controller’s UINavigationItem, and I’ve run into two different cases that had me fighting with this class and doing things that UIKit probably didn’t intend. Incidentally, both of these deal with the cancel button that can be presented from a search bar.

First, iPads won’t display the cancel button at all. No idea why but there must be a good reason. A quick web search yielded a Stack Overflow answer (of course). I wrapped the search bar in a view and it worked just like I needed. But it felt weird to do so.

The second one, however, had me considering a different approach entirely. When a search term is present in the bar and a user is viewing results, I wanted the cancel button to take us back to the initial state of the screen (which is our Browse screen, displaying categories to shop in).

But that’s not what happened. I was expecting the search bar’s delegate method searchBarCancelButtonClicked(_:)[^1] to get called. When you tap on the cancel button – and the search bar is not the first responder – it just becomes the first responder. You have to then tap on the cancel button for the delegate to get called.

My first thought (ashamedly) was to implement hitTest(_:, with:) and figure out if the cancel button (which is a private API) was tapped. This actually worked but involved me writing code like this:

subviews.filter({ String(describing:$0) == “UINavigationButton” })

My idea was to see if the cancel button was being tapped. If so, I’d call the search bar’s delegate cancel method and return nil on the hit test. This all worked; kind of. There were some other things that I would have to work around. But it all sat wrong with me.

Then I decided to just to forego using the cancel button that comes with UISearchBar altogether and put the cancel button as the right bar button item. I get full control this way. I can animate it when I need it to, and I don’t have to worry about its dependence on implementation of UIKit. I like it much better.

The moral of the story is: If you’re fighting UIKit’s behaviors, there may be an easier way to get the job done.

[^1]: I also shuddered when I saw this method as referring to a “click” action.

Five Years and Counting

It was 5 years ago today that I married my best friend. It was a wonderful day with family and friends, but made all the sweeter now seeing what our life has become. I'm beyond blessed by getting to live life with Emily.

We've been through a lot over the years:

I know that there are things I've left out of that list. It's been an adventurous 5 years for the two of us and our family. And I can't wait to see what the next 5, 10, 15 years, and beyond will have in store for us.

She has been a wonderful blessing to me; a tremendous wife and mother. She is a crown on my head. I can't thank God enough for her and it's my hope that I can love her well all the days of our life together.

Happy anniversary, my love.

Say Hi to Finnian

Last night, at 6:31 PM PDT, our family grew by one. Finnian Michael Sorge was born, healthy and measuring 8 pounds, 4 ounces and 20.5” long. He was due on the 14th, and overshot his target by a day. Not bad at all (especially considering his big brother Atticus was 11 days late, and Emily had to endure a long induction process). He came of his own volition even as we had scheduled his induction for next week. Thanks kiddo!

These last 48 hours have been quite the whirlwind. I got the call from Emily on Thursday at 6:30 as I was helping to setup for the July Xcoders meeting, then booked it to the hospital. He took 24 hours to get here, and we got home just after 8 tonight.

We are so thankful for all the prayers and support we have received from friends and family. It means a great deal.

To my new son, Finnian: You are a tremendous blessing to us, your family. We love you so very much and pray that your name is written in the Lord’s book of life. Welcome to the world.

View sizing problems

I have a custom view that I’m putting together for our app at zulily. The view has a UIImageView, and a label that’s centered in its X and Y. I’m building this whole view in code.

So far, everything is great. I’ve got my class returning the proper intrinsic content size, the image view being populated correctly, and everything lays out as it should on the first pass in one of my collection view cells. As you can see below, the label is sized properly vertically on the first pass inside of my UICollectionViewCell.

And then something happens.

When the cell goes offscreen and then comes back, it looks like this:

The view’s height gets is no longer determined by its content size. I have no height constraint on this view (and when I do, it still doesn’t matter). The custom view has a high content resistance and the only outside constraints on it from its superview are positioning its X and Y and then spacing its top and bottom neighbors.

I think the thing I need to figure out is why the content size height constraint is going away, and then I’m good to go. Any ideas?

Update 5/31/16 I figured out what was going on. I was using the -constraints array property to track my custom constraints. The system also uses this array to get things done. So when I would reset the view based on the model I would empty the array. This messed everything up. Moving to a separate array to track my constraints for the image view and label fixed the issue.