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!

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.