Combine: Where's the Beef?

In the last couple posts, we’ve discussed how we landed on reactive programming, as well as the seven layer dip that is RxSwift. Thus far, we haven’t really spoken much about Combine, Apple’s shiny new framework that seems to ape be inspired by RxSwift.

In order to discuss a Combine, one has to discuss the main differences between it and RxSwift. To my eyes: there are three.

  • Affordances for non-reactive classes
  • Error handling
  • Backpressure

I’ll be splitting each of those into their own posts over the next week or so. Let’s start with the first one.

RxCocoa Affordances

In my prior post, we discussed that RxSwift is more than just… RxSwift. It actually includes many, many affordances for UIKit controls in the sorta-but-not-really sub-project RxCocoa. Additionally, RxSwiftCommunity steps up and provides a lot of bindings for the more remote outposts of UIKit, as well as other CocoaTouch classes that RxSwift and RxCocoa don’t cover.

This makes it impossibly easy to get an Observable stream from, say, a UIButton being tapped. From my post:

let disposeBag = DisposeBag()
let button = UIButton()
button.rx.tap
    .subscribe(onNext: { _ in
        print("Tap!")
    })
    .disposed(by: disposeBag)

Easy peasy.

Let’s [Finally] Talk About Combine

Combine is very much like RxSwift. Pulling from the documentation, Combine self-describes as such:

The Combine framework provides a declarative Swift API for processing values over time

This should sound familiar; look at how ReactiveX (the parent project of RxSwift) describes itself:

An API for asynchronous programming with observable streams

These are actually saying the same thing; the ReactiveX version is simply using some domain language. It could be rephrased as:

An API for asynchronous programming with values over time

That’s pretty much the same thing in my book.

Same As it Ever Was

As I started looking into the API, it was quickly obvious that most of the types I’m familiar with from RxSwift have approximations in Combine:

  • ObservablePublisher
  • ObserverSubscriber
  • DisposableCancellable
    This is a huge marketing win; I cannot tell you the amount of “🙄” I got from otherwise open-minded developers as soon as I started describing RxSwift’s Disposable.
  • SchedulerTypeScheduler

So far so good. I can’t help but reiterate how much I prefer “Cancellable” over "Disposable". That’s an incredibly great change not only from a marketing perspective, but also because it more accurately describes what that object is.

But things continue to get better!

  • RxCocoa’s Driver → SwiftUI’s BindableObject
    This is a little bit of a reach, but they spiritually serve the same purpose, and neither of them can error.
  • SingleFuture
  • SubjectTypeSubject
  • PublishSubjectPassthroughSubject

So far, we’re off to the races.

Let’s Take a Hot Chocolate Break

Everything takes a turn once you start diving into RxCocoa. Remember our example above, where we wanted to get an Observable stream that represents taps of a UIButton? Here it is again:

let disposeBag = DisposeBag()
let button = UIButton()
button.rx.tap
    .subscribe(onNext: { _ in
        print("Tap!")
    })
    .disposed(by: disposeBag)

To do the same in Combine requires… a lot more work.

🚨🚨🚨🚨🚨🚨🚨

Combine does not include any affordances
for binding to UIKit objects.

🚨🚨🚨🚨🚨🚨🚨

This… is a serious fucking bummer.

Here’s a generic way to get a UIControl.Event out of a UIControl using Combine:

class ControlPublisher<T: UIControl>: Publisher {
    typealias ControlEvent = (control: UIControl, event: UIControl.Event)
    typealias Output = ControlEvent
    typealias Failure = Never
    
    let subject = PassthroughSubject<Output, Failure>()
    
    convenience init(control: UIControl, event: UIControl.Event) {
        self.init(control: control, events: [event])
    }
    
    init(control: UIControl, events: [UIControl.Event]) {
        for event in events {
            control.addTarget(self, action: #selector(controlAction), for: event)
        }
    }
    
    @objc private func controlAction(sender: UIControl, forEvent event: UIControl.Event) {
        subject.send(ControlEvent(control: sender, event: event))
    }
    
    func receive<S>(subscriber: S) where S :
        Subscriber,
        ControlPublisher.Failure == S.Failure,
        ControlPublisher.Output == S.Input {
        
            subject.receive(subscriber: subscriber)
    }
}

The above is… considerably more work. On the plus side, however, the call site is reasonably similar:

ControlPublisher(control: self.button, event: .touchUpInside)
    .sink { print("Tap!") }

By comparison, RxCocoa brings us that sweet, delicious, hot chocolate, in the form of bindings to UIKit objects:

self.button.rx.tap
    .subscribe(onNext: { _ in
        print("Tap!")
    })

In and of itself, these call sites are, quite similar indeed. It’s all the work I had to do writing ControlPublisher myself to get to this point that’s the real bummer. Furthermore, RxSwift and RxCocoa are very well tested and have been deployed in projects far bigger than mine.

By comparison, my bespoke ControlPublisher hasn’t seen the light of day until… now. Just by virtue of the amount of clients (zero) and time in the real world (effectively zero compared to RxCocoa), my code is infinitely more dangerous.

Bummer.

Enter the Community?

To be fair, there is nothing stopping the community from putting together a sort of open source “CombineCocoa” that fills the gap of RxCocoa in the same way that RxSwiftCommunity works.

Nevertheless, I find this to be an exceptionally large ❌ on Combine’s scorecard. I’m not looking to rewrite all of RxCocoa simply to get bindings to UIKit objects.

If I’m willing to go all-in on SwiftUI, I suppose that would take the sting off of these missing bindings. Even my young app has a ton of UI code in it. To throw that out simply to jump on the Combine bandwagon seems foolish at best, and dangerous at worst.

More to Come

In my next post, we’ll discuss error handling in RxSwift and Combine. Some different design decisions were made between the two projects, and I could make a passionate argument that both are correct. Stay tuned.


RxSwift Quick Overview

In yesterday’s post, we walked through how one could take “Enumerable”, (or, really, Sequence) and “Enumerator” (really, Iterator), and turn them into Observable and Observer. These are the two types that underpin most everything in RxSwift.

In this post, let’s explore a little more about what RxSwift is, and what it isn’t.

Let’s Make a Sandwich

When people colloquially refer to “RxSwift”, they’re often referring to an entire group of projects and technologies. When writing an iOS app, it is certainly possible to use only RxSwift, but that’s like making a sandwich only of bread.

In reality, the beauty of RxSwift from an app developer’s perspective is not the RxSwift “bread”, but rather the meats and condiments inside. More directly, RxSwift is fine, but interaction with things like user interfaces and system frameworks is what makes RxSwift really shine.

One of my favorite uses of RxSwift is to interact with user interfaces. The taps of a UIButton are an excellent example of something that can be exposed as an Observable.

However, using only RxSwift, there isn’t a terribly straightforward way to expose a button tap as an Observable. It’s certainly doable, but would require creating an entire object just to be the receiver of the button’s .touchUpInside action. That’s a lot of housekeeping to get to one button’s tap.

Thankfully, many of these menial tasks are already taken care of. Instead of having to worry about writing an entire object to worry about a button tap, you can leverage a project that’s built upon RxSwift.

RxCocoa

RxCocoa is sort of part of RxSwift, though it’s sort of not. It’s a separate target, and must be imported on its own, but the source lives within the RxSwift repository.

It’s RxCocoa that brings all of these convenient bindings to the table. Thus, instead of having to do a whole bunch of housekeeping in order to get to a button tap, one can just do this:

let disposeBag = DisposeBag()
let button = UIButton()
button.rx.tap
    .subscribe(onNext: { _ in
        print("Tap!")
    })
    .disposed(by: disposeBag)

That is so nice, and so convenient.

Furthermore, there are equivalent bindings for mostly anything an average iOS developer runs into in a normal application. RxCocoa has bindings for UITabBar's selectedItem. For UIProgressView’s progress. For UITextField's text. The list goes on and on and on.

Let’s Join a [RxSwift]Community

RxCocoa can’t be all things to all people, though. However, the beauty of open-source is that others can join in, help, and share their code.

Enter RxSwiftCommunity.

RxSwiftCommunity is, naturally, a community-led project to extend RxSwift and RxCocoa to cover what those projects do not. A phenomenal example of this, that I’ve used in Vignette, is RxGesture. RxGesture exposes many gesture-based events by way of Observables. For example:

let someView = UIView()
let observable: Observable<UITapGestureRecognizer> = 
    someView.rx.tapGesture { (recognizer, _) in
        recognizer.numberOfTapsRequired = 3
    }
    .when(.recognized)

Just like before, it’s certainly possible to do this by hand, but it is so much nicer to be able to just grab an open-source solution to do it for you. An open-source solution that has been used many many times, and thus is far better tested than any bespoke solution would be.

Furthermore, there are a ton of RxSwiftCommunity projects. To call out just a few:

Finally, newest and perhaps most interestingly:

  • RxCombine
    Currently empty, but perhaps an attempt at an RxSwift ↔ Combine bridge?

Speaking of Testing

Another incredible feature of RxSwift, and peer to RxCocoa, is RxTest. RxTest is a wonderful suite of tools that allows you to easily test code based on Observables. This includes simulating events on a stream, at precise virtual times, in order to observe their results on the system.

I cover a lot of RxTest in my fifth and final RxSwift Primer post. Suffice it to say, RxTest makes it [almost] enjoyable to write unit tests for RxSwift-based code.

All Together Now

RxSwift in and of itself is impressive, but it is an empty sandwich. While it may be made of the most delicious bread known to man, it’s still just bread. Without the meat that is RxCocoa, the cheese that is RxCommunity, and the condiments that are RxTest, it’s just not the same.

Next week, I’ll spend some time comparing the API surface area of Combine to that of RxSwift, as well as discuss some core differences in the design of the two projects.


Building Up to Combine

I’ve been preaching the gospel about RxSwift for a year and a half now. RxSwift took me quite a while to get my head around, but once it clicked, there was no going back for me. I now have the shiniest hammer in the world, and I’ll be damned if everything doesn’t resemble a nail.

A little over a week ago, at WWDC, Apple unveiled their Combine framework. At a glance, Combine seems like little more than a first-party take on RxSwift. Before I can really get into what I do and don’t like about it, we need to understand what problem Combine is setting out to solve.

Reactive Programming? What now?

The ReactiveX community — the community which RxSwift is a part of — summarizes itself as follows:

An API for asynchronous programming with observable streams

And further:

ReactiveX is a combination of the best ideas from the Observer pattern, the Iterator pattern, and functional programming

Um… k. 👌🏻

So what the hell does that really mean?

A Foundation

In order to really understand what reactive programming is about, I find it helpful to understand how we got here. In this post, I’ll describe how one can look at existing types in any modern object-oriented programming language, twist them around, and land on reactive programming.

This post gets in the weeds fast and isn’t absolutely necessary to understanding reactive programming.

However, I do think it’s a fascinating academic exercise, especially in how strongly typed languages can lead us down the path to new discoveries.

So, feel free to wait until my next post if this goes too far off in the weeds for you.

Enumerables

The “reactive programming” that I know was borne from my old language of choice, C#. The whole premise, distilled, is fairly simple:

What if instead of enumerables where you pull values out, you instead got values pushed to you?

This push rather than pull idea was best described to me in an incredible video with Brian Beckman and Erik Meijer. The first ~36 minutes is… over my head, but starting at around 36 minutes, things get really interesting.

In short, let’s re-define the idea of a linear group of objects in Swift, as well as an object that can iterate across that linear group. We can do so by defining these fake Swift protocols:

// A linear group of objects; you could easily imagine this 
// being backed by an Array.
protocol Enumerable {
    associatedtype Enum: Enumerator
    associatedtype Element where Self.Element == Self.Enum.Element
    
    func getEnumerator() -> Self.Enum
}

// An object that can walk across a linear group of objects.
protocol Enumerator: Disposable {
    associatedtype Element
    
    func moveNext() throws -> Bool
    var current: Element { get }
}

// We may eventually need to clean up our 
// Enumerator; it could be operating on files,
// or a network resource. This is how we do so.
protocol Disposable {
    func dispose()
}

Duality

Let’s flip all of those, or make their duals. So where data was coming out, we put data in. Where data was going in, we pull data out. This sounds funny, but bear with me.

Duality of Enumerable

Starting with Enumerable:

// Exactly as seen above.
protocol Enumerable {
    associatedtype Element where Self.Element == Self.Enum.Element
    associatedtype Enum: Enumerator
    
    func getEnumerator() -> Self.Enum
}

protocol DualOfEnumerable {
    // Enumerator has:
    // getEnumerator() -> Self.Enum
    // Which could be rewritten as:
    // getEnumerator(Void) -> Enumerator
    //
    // Thus, we could summarize:
    // IN: Void; OUT: Enumerator
    // getEnumerator(Void) → Enumerator
    //
    // Thus, we are taking in Void and emitting an Enumerator.
    // For the dual of this, we should take IN whatever the
    // dual of an Enumerator is, and emit Void.
    // IN: Dual of Enumerator; OUT: Void
    func subscribe(DualOfEnumerator)
}

Again, since getEnumerator() took in Void and emitted an Enumerator, we are instead taking in [the dual of] Enumerator and emiting/returning Void.

I know this is weird. Stick with me here.

Duality of Enumerator

So what is DualOfEnumerator then?

// Exactly as seen above.
protocol Enumerator: Disposable {
    associatedtype Element
    
    // IN: Void; OUT: Bool, Error
    func moveNext() throws -> Bool
    // IN: Void; OUT: Element
    var current: Element { get }
}

protocol DualOfEnumerator {
    // IN: Bool, Error; OUT: Void
    // The previously thrown Error we will ignore for a moment
    func enumeratorIsDone(Bool)
    // IN: Element, OUT: Void
    var nextElement: Element { set }
}

Now, a few problems here:

  • Swift doesn’t have the concept of a set-only property
  • What happened to the throws on Enumerator.moveNext()?
  • What about Disposable? What happens with that?

To fix the set-only property, we can treat a set-only property as what it really is: a func. Thus, let’s slightly tweak our DualOfEnumerator:

protocol DualOfEnumerator {
    // IN: Bool; OUT: Void, Error
    // The previously thrown Error we will ignore for a moment
    func enumeratorIsDone(Bool)
    // IN: Element, OUT: Void
    func next(Element)
}

To fix the throws, let’s break out the error that could happen in moveNext() and treat it as its own separate func called error():

protocol DualOfEnumerator {
    // IN: Bool, Error; OUT: Void
    func enumeratorIsDone(Bool)
    func error(Error)
    
    // IN: Element, OUT: Void
    func next(Element)
}

There’s one other change we can make: look at the signature for when we’ve finished enumerating:

func enumeratorIsDone(Bool)

Presumably we’d have something like this over time:

enumeratorIsDone(false)
enumeratorIsDone(false)
// Now we're finally done
enumeratorIsDone(true)

At that point, why not just simplify things and only call enumeratorIsDone when… things are done? We can take that approach, and simplify the signature:

protocol DualOfEnumerator {
    func enumeratorIsDone()
    func error(Error)
    
    func next(Element)
}

Cleaning Up Our Mess

Finally, what about that Disposable? What do we do with that? Well, since Disposable is a part of the type Enumerator, when we get the dual of Enumerator, perhaps it shouldn’t be on Enumerator at all. Instead, it should be a part of DualOfEnumerable. But where?

The place where we are taking in the DualOfEnumerator is here:

func subscribe(DualOfEnumerator)

If we’re taking in the DualOfEnumerator, then shouldn’t the Disposable pop out?

Thus, here’s our final dual of everything:

protocol DualOfEnumerable {
    func subscribe(DualOfEnumerator) -> Disposable
}

protocol DualOfEnumerator {
    func enumeratorIsDone()
    func error(Error)    
    func next(Element)
}

By Any Other Name

Okay, one more time, this is what we have:

protocol DualOfEnumerable {
    func subscribe(DualOfEnumerator) -> Disposable
}

protocol DualOfEnumerator {
    func enumeratorIsDone()
    func error(Error)
    func next(Element)
}

Let’s massage these names a bit.

Starting with DualOfEnumerator, let’s use some slightly better function names, which indicate these things just happened:

protocol DualOfEnumerator {
    func onComplete()
    func onError(Error)
    func onNext(Element)
}

Cool, looking better and more consistent already.

How about these type names though? They’re straight garbage.

Let’s change them a bit.

  • The DualOfEnumerator is something that pays attention to what happens with a linear group of objects. You could say it observes the linear group.
  • The DualOfEnumerable is the subject of that attention. It’s the thing we’re observing; thus you could say it is observable.

With this in mind, let’s do some renaming:

protocol Observable {
    func subscribe(Observer) → Disposable
}

protocol Observer {
    func onComplete()
    func onError(Error)
    func onNext(Element)
}

Whoa 🤯

We just built the two foundational objects in RxSwift; you can see the real ones here and here.[1])

These two types are what drive the basis for RxSwift and reactive programming.

About Those “Fake” Protocols

The two “fake” protocols I described above aren’t really fake at all. In reality, there are analogous types in Swift:

y tho?

So why bother?

So much of modern development — particularly app development — is about being asynchronous. The user has unexpectedly tapped this button. The user has unexpectedly changed this segmented control. The user has unexpectedly selected this tab. This web socket just unexpectedly gave us new information. This download is unexpectedly, and finally, complete. This background task has just unexpectedly finished. The list goes on and on.

There are so many ways to handle these sorts of things in today’s CocoaTouch world:

  • Notifications
  • Callbacks
  • Key-Value Observation
  • Target/action

Imagine if all of those things could be reflected by one unified interface. An interface that can reflect pretty much any kind of asynchronous data or event in your entire app?

Now imagine there was an entire suite of functions that allow you to modify these streams, changing them from one type to another, extracting information from within the Elements, or even combining them with other streams?

Suddenly, there’s an entire new, universal toolbox at our disposal.

And lo, we’re back where we started:

An API for asynchronous programming with observable streams

That’s exactly what makes RxSwift so powerful. And, similarly, Combine as well.

What’s Next

If you’d like to see more about RxSwift in action, I encourage you to read my five-part blog series from late 2016. It covers the creation of the world’s dumbest CocoaTouch app, and then converts it step by step to RxSwift.

In one [or more] future post[s], I’ll cover why many of the techniques used in my RxSwift primer are not applicable to Combine, and compare and contrast Combine with RxSwift.


  1. Note in the case of Observer, the three on() functions are combined into one on(Event), where Event is an enum that specifies if the event is a completion, next, or error.


Vignette Status Update

First and most importantly, THANK YOU to anyone who has bought Vignette, told a friend about it, given it a spin, or otherwise had a think about it. The response to Vignette has far surpassed my wildest dreams and I have you to thank for that. 💙

A week after its [quiet] release, and just under a week after its public release, I wanted to spend a moment to take stock of the last several days.

Vignette Updates

By the time I started my full-court press… press… Vignette was already on its second version. Since that time, I’ve released:

  • 2019.3
    • Full resolution images from Twitter
    • Fix a problem where Instagram was not recognized when it had a trailing space
    • Fixed an accidental bait-and-switch
    • Improved security
  • 2019.4
    • Dramatically improved Facebook support; the following are now supported:
      • The hot garbage original requirement of fb://profile/1234567
      • casey.liss
      • https://www.facebook.com/casey.liss
      • https://facebook.com/casey.liss
    • Fixed a bug wherein default Twitter images were suggested
    • Vignette keeps the screen on when plugged in to prevent sleep-related issues

Forthcoming:

  • 2019.5currently in testing
    • For all services but Gravatar, Vignette will now look at any URL it can find on a contact, rather than only looking in Social Profiles
      • This will also get around Social Profiles not existing for Exchange users.
    • Improved networking detection
      • The app will automatically start a search on launch if Wi-Fi is detected
      • If no Wi-Fi is available, users can elect to perform a search over cellular
    • New close button in the selector modal
    • Fix for a layout issue in the onboarding screen
    • Fix for very long usernames in the selector modal
  • 2019.6currently in development; no guarantees here!
    • Add support for Github profile pictures
    • Fix for some of the first onboarding screen text getting cut off
    • Better Facebook default image detection
    • Accessibility escape gesture for the old avatar preview modal
    • Under-the-hood improvement to the way I’m managing queues

2019.5 will be released once I get it in front of my Test Flight users for a couple days. It’s currently sitting and waiting for review from Apple. 🙄

2019.6 will probably be a mid-June release, if all goes to plan.

Revenue

I’d love to tell you I’m diving into my Scrooge McDuck swimming pool, but… App Store Connect isn’t really telling me much of anything at the moment. 😭

No sales

I’ve reached out to Apple in a couple of different ways to attempt to get this fixed, but nothing yet.

To be honest, I don’t intend to release revenue figures, but I am hoping to call a little more attention to this extremely frustrating App Store Connect bug.

Press

I’ve been extremely lucky to get some really incredible press coverage about Vignette. Mostly for my own posterity, I wanted to capture the articles I’ve found/seen:

General Thoughts

Overall, I really am overjoyed with how the launch has gone. It hasn’t been problem-free, but it’s gone so much better than I expected. I’m hopeful to get 2019.5 out the door prior to WWDC, but either way, I suspect I have a busy summer of iOS 13 updates ahead of me.


 

As with all ideas, it starts with a question:

Could I get new pictures for all my contacts on my iPhone off Gravatar?

That was early February. I was just goofing off and seeing if I could make that work at all.

Then more questions came:

Could I do this for Twitter as well? For Instagram? For Facebook?

About three months later, Vignette is here.

Introducing Vignette

Vignette after a search is performed

Vignette allows you to add photos to your contacts by searching public social media profiles. If multiple options are found, it’s easy to select the one you wish to use. If the existing image is special or more representative of the contact, it can be kept.

By default, within a few minutes, you can add tens or even hundreds of images to your contacts.

In summary, this is the change that Vignette will enable:

CL initials → image

Vignette’s development was driven by a few core tenets. I have a clear idea of what I’d like Vignette to be. These tenets drove its development:

  • Privacy is paramount
    All the processing is done on-device; this isn’t the sort of app where your contacts are uploaded en masse to some server, and out of your control.
  • You are the customer
    It’s gross to steal users’ contacts and sell that data. My customer is you, not some business I’m selling your contacts to.
  • Keep it simple
    I already have lots of ideas for how to make Vignette more robust and even more indispensible. Over time, I hope to add a bunch of those features. But Vignette is designed to be a tool, not Facebook.

Making Money

Vignette allows you to scan your contacts and see what it can find for free. If you wish to actually save these updates to your contact list, you must pay for a one-time in-app purchase. That purchase costs $4.99, is not a subscription, and is the only in-app purchase.

My hope is to keep developing and improving Vignette over time; that is made possible by the financial support of in-app purchase. Well-wishes, kudos, congratulations, and word-of-mouth all help quite a bit, but they don’t pay the bills. :)

Using Vignette

Vignette works by scanning your contacts and seeing what information it can amass about their social media presence. That means that Vignette is only as good as the information you provide to it.

Apple’s Contacts app actually allows you to provide this information, even though it isn’t entirely obvious at first:

Adding a contact with social media

Vignette will look at the following fields:

  • Email is used for Gravatar
  • Twitter
  • Facebook
  • A custom network called Instagram

Over time, I hope to make discovery for these existing services more robust, and potentially begin to support other services, as well. That said, for now, my priority is to make all this possible without requiring you to log in. I much prefer having Vignette only use social networks anonymously to prioritize privacy.

Why Now?

Vignette is the first new project I’ve undertaken since going independent. I’ve been working on the things I had already been doing, like Casey on Cars, but Vignette is the first project both conceived and completed while indie.

I’ve been working on it feverishly for months, but a recent report really lit a fire under me. I really wanted to get Vignette out the door before WWDC, and I’m overjoyed to have made it, with over a week to spare.

Some Initial Coverage

Naturally, I have and will be talking about this on my podcasts. Myke and I have recorded episode 157 of Analog(ue) where we’ve discussed the lead-up to the launch.

Additionally, [the currently forthcoming] episode 327 of ATP will surely include some commentary.

Thank You

Please download Vignette and see what you think of it. If you’re willing, I’d love it if you’d buy the in-app purchase. Or tell your friends about Vignette. Or both!

Additionally, my thanks to Ben McCarthy, Daniel “Jelly” Farrelly, and Ste Grainer — among others — for their noteworthy and incredibly helpful contributions to Vignette.


 

One of my favorite local activities, every other Saturday, is to go to Cars and Coffee. Recently, our local paper had a nice piece about it:

A stunning new Acura NSX might be followed by a more pedestrian mid-aught Volkswagen Golf GTI or a midcentury representative of American muscle. A made-to-order Ford GT supercar mingles with its ubiquitous cousins – a bevy of Ford Mustang GTs that might be worth only a tenth of the supercar’s list price.

The thread that unites all attendees is pride in their vehicles and a desire to share them with other people who “get it.” Enthusiasts of every background are brought together by the camaraderie of the local car culture.

I’ve often documented my trips to Cars and Coffee on Instagram in the past. I really love taking the family to go check out the cars and just generally enjoy a Saturday morning together.

Perhaps it’s memories of going to Marcus Dairy when I was in high school, or perhaps it’s me trying to share with my kids what my dad shared with me. But one way or the other, Cars and Coffee has been an integral part of my family for years. I’m pleased to see it get some local recognition.


 

This week I had a blast joining my good friends Stephen Hackett and Jason Snell on this week’s episode of Download.

On this week’s show, Jason and Stephen discussed some new Fitbits, Apple hires in Qualcomm’s area of expertise, the recent Facebook news. Then I join the pair for some insights about the Geneva Motor Show.

I always cherish a time I can Stephen or Jason on a show, and any time I can talk about cars, so this was a win/win. 🎉


 

I was on this week’s episode of Clockwise, with Aleen Simms, Dan Moren, and Mikah Sargent. This week, we discussed Apple’s supposed demand of 50% of magazine subscription revenue, where money goes in the App Store, Amazon acquiring eero, and some nostalgia for fancy tech from years past.

This one was a particular hoot, as I think we were all just a little bit loopy. I have no idea how Dan was able to squeeze it into just thirty minutes, but that’s what makes him so darn good at what he does.


Gigabit Verizon FiOS with Eero

Last week our house got upgraded from 75/75 megabit internet service to gigabit.

fast.net showing 1.0 Gbps speed

Admittedly, that speed test was one time only, when only my iMac was connected, via ethernet, directly to the modem. Still though! Look at that! 1.0 Gbps!

I learned a couple things during the installation — and subsequent rework of my local network — which I thought may be worth sharing.


I originally got FiOS back in 2008. I was overjoyed to be leaving Comcast behind, and getting push-your-hair-back-like-a-Maxell-ad 15 megabit speeds. A few years ago we upgraded to 75/75. The symmetric life (having uploads as fast as downloads) is really the best.

By default, most FiOS installations bring internet into the house, from the ONT, via coax. This is way easier and way cheaper for Verizon than using ethernet would be, and for most users, it doesn’t matter. Verizon only has to pipe a line from the ONT — typically behind the house — to the crawlspace/basement or attic, where it’s plugged into the in-home coaxial network used for cable TV.

Most users wouldn’t care about that, as they happily lease Verizon’s router for an obscene amount of money each month. For me, once I realized I had (but didn’t exercise) the option of an ethernet drop instead of coax, I was really bummed. For the last ten years, I was stuck with Verizon’s just-barely-inoffensive-enough-to-be-bearable Actiontek router. I couldn’t use any other router, because no other routers can take the internet in via coax.

Gigabit installations, however, compel Verizon to bring ethernet to your router. While mostly a win, I learned that the installers have extremely strict rules about where they can put the ethernet drop. From what I could put together, they are pretty much bound to do the installation on an exterior wall if at all possible. That led to an unusual but ultimately workable installation within my in-home office, across the room from where my networking equipment lives.

Having the internet come in via ethernet opened up a very important possibility: perhaps I don’t need to use the Verizon-provided router after all? That router is $200, if you buy it outright, or something like $12 each month if you lease it! It seemed, at a glance, that I could finally switch my Eero from merely being a bridge to being the router too. In fact, this useful post seemed to indicate the same.


Except.

Despite it being extremely trendy, we are not [yet] cord cutters. That means we still have a cable box. The cable box gets its channel guide information via the internet. The cable box gets internet… via coax. The Verizon answer for this is to connect your router to ethernet (for the internet) and coax (to bridge the internet onto the coaxial network). Having ditched the Verizon router, I now needed a way to get the internet onto the coaxial network.

Enter the MoCA bridge.

A MoCA bridge… bridges… the ethernet and coaxial networks. They’re annoyingly expensive; this is the one I purchased years ago and had handy, but it appears this is the modern replacement. Regardless, after hooking the router into the MoCA bridge (via ethernet) and then connecting the bridge via coax, I was able to get our cable box back on the internet.

So, all seems well!

Looking back at the DSL Reports link above, I’ve ended up with setup #9. DSL reports lists the pros and cons as follows:

PRO:
• Can eliminate Verizon Router completely.
• Smaller, simpler device. Less power consumption.
CON:
• Additional unit to purchase.
• Does not support remote access to DVR, on-screen caller id or VZ’s CPE management interface.

What about that last one? Specifically:

Does not support remote access to DVR

That got me thinking. Before I abandoned my old Actiontek router, I took a screenshot of the port forwarding settings. Conspicuously, there were a couple of entries for IP address .100 that I didn’t recall entering. Furthermore, the IPs for every other device on my network started with .2; not all the way up at .100.

Old port forwarding rules

That made me wonder if these were special rules for the set top boxes. On a lark, I decided to re-create the rules I found in my Eero:

  • UDP 63145
  • UDP 63146
  • TCP 35000
  • TCP 35001

I suspect one or more of those aren’t necessary, but, I turned off WiFi on my phone and then I attempted to use the FiOS TV app to schedule a TV recording on my Verizon DVR. Sure enough, it worked!


So, all told, I’ve:

  • Upgraded from 75/75 → 940/880 megabits per second
  • Got rid of the stupid coaxial internet connection in favor of ethernet
  • Ditched the crummy Verizon router in favor of the far superior Eero
  • Kept my cable box happy and on the internet
  • Saved over $100/mo

All told, a good day.


How I Wash Cars
Car wash materials sitting in front of Erin's car

As a professional podcaster, I quickly learned that there are some topics you really don’t want to bring up on a show:

  • Politics
  • Religion
  • Parenting

When it comes to car enthusiasts, there’s another topic that invites argument more than almost any other: how do you like to wash your car?

Since I’m an idiot a glutton for punishment, but also since I’ve been asked several times in the past, and also because of that sweet sweet affiliate money I like to help people, here’s a basic walkthrough of the methods I use.

My car, my rules. Your car, your rules. Please come to your own conclusions.

This post is meant for the person who wants to start taking care of their car. It is not meant for the seasoned car washing enthusiast. I’m sure this list of tools will utterly horrify some, but I hope it will also help some others.

Washing the Car

When the weather is nice, I try to wash my car once a week. I don’t always get to every step in this process. I do try to do this sort of thorough cleaning once a month though.

Phase One: Wash

I am a fan of the two-bucket system for washing your car. One bucket is for soapy water; the other is for rinsing off your dirty sponge. The theory is that all the impurities and contaminants are rinsed off in the dirty bucket, so they don’t muck up the clean bucket. I’m pretty sure this is a placebo, but I like it, so that’s the way I do it.

I happen to use this Rubbermaid bucket as my dirty one; I’m not sure where I got the clean bucket. But really, any bucket will do. There’s no real science here.

For a long time I used Turtle Wax car wash, but some friends recently turned me on to Mr. Pink car wash, which is excellent. It’s super sudsy, and I’m told, easy on your car’s paint.

For a sponge, I use this mitt from Meguiars, though I never actually use it as a mitt. It’s just a nice sponge.

To wash, I fill the clean bucket with soapy water, one with plain water. Dip your sponge in the clean bucket, apply soapy water to the car, top → bottom, back → front. Do one section at a time. Rinse with the hose after each section.

Phase Two: Wheels

I really really hate brake dust. It’s the stuff that makes your wheels brown, particularly up front, which tends to be where most of the stopping happens. If I have the time, I try to always clean my wheels when I wash the rest of the car.

I have come to quite like the Black Magic No Scrub Wheel Cleaner. I spray it all over the wheel, and then simply wipe the dust off with a damp rag. Then I spray it down with the hose when I’m done. It’s important to have a dedicated wheel cleaning rag, as it will be pretty much instantly destroyed by brake dust forever. I do re-use that rag, but only for wheels, and never for anything else.

Phase Three: Exhaust

One of the few flaws of my Golf R is that the exhaust tips get really brown really quickly. In casting around for a solution to this, some friends strongly recommended Barkeeper’s Friend. I got the powdered version. I sprinkle it on a damp rag, mush it around in the rag to make a bit of a paste, and then apply to the exhaust tips. Spray clean after.

I’ve linked to this on Amazon, but you can probably find this cheaper in your local Target or equivalent. It’s also worth noting that it works well on lots of stuff around the house, such as metal sinks.

Phase Four: Dry

I don’t have particularly strong opinions here. I use bath towels purchased at Target. The only advice I have is that I’ve found 100% cotton seems to be not absorbent at all. I do advise getting towels that are in part polyester, which seems to dramatically increase how absorbent the towels are.

Before I begin to dry, I spray down the whole car one more time to try to alleviate any spotting. Then I try to dry the side of the car that faces the sun first, since it’s most likely to dry and create spots first. Otherwise I move top → bottom, back → front, just like when I wash.

Phase Five: Tires

As with clean wheels, I really love how a shiny, wet-looking tire presents. There’s many different products that can achieve this goal, but I really like Black Magic Tire Wet Foam. Once the tires are at least mostly dry, you simply spray that foam on them, and then walk away. No buffing required.

(Note the one I’ve linked here is a six-pack; you’re probably better served by going to your local store and getting just one to try.)

Since it’s so quick and easy to spray this foam on, I’ll spray all four tires from time to time, between washes, to freshen them up, when I think they need it.

Just be sure to let the foam dry up before you drive the car, lest you spray the excess foam all over your bodywork.

Twice a Year: Wax

Generally speaking, in the spring and fall I try to find the time to wax the car. I feel like this is particularly important in the fall, as it puts a protective layer on the paint prior to the harshness of winter. Yes, even here in Virginia.

There are a million and seven car waxes on the market. I grew up in a house that used Nu Finish, so that’s what I use. Nu Finish is a wet wax. The process is:

  1. Get a damp cloth
  2. Apply a little NuFinish to the cloth. A little goes a long way.
  3. Rub the wax onto the car, using a circular motion
  4. Wait for the wax to dry. Not only will it be white, but if you swipe a finger across it, you’ll take the wax right off, and it’ll feel dry.
  5. With a different, dry, cloth, also using a circular motion, rub the wax off the car.

Do the above one panel/section at a time until the whole car has been waxed.

Every Other Year: Clay

Every couple years, time permitting, I’ll clay my car.

The first time I heard about this, I thought it was absolutely bananas. Nevertheless, I tried it, and was amazed at the result.

Specially created bars of clay can be used to take invisible impurities out of your paint. I know how ridiculous this sounds. I didn’t believe it either. I thought it was the automotive equivalent of ear candling.

Believe it.

One time I clayed my wife Erin’s car. When I got to the hood, I only did half of it. I asked Erin to run her hand across the hood, from the non-clayed side to the clayed side, to see the difference. With some eye rolling, she did so. Her face quickly went from 🙄 → 😳. The difference in feeling between the side that had been clayed and the side that hadn’t was tremendous. One had the feeling of a mirror; the other felt like a veritable sand trap by comparison.

Once you clay your car, it will feel noticeably smoother to the touch.

I’ve had really good luck with Meguiar’s Clay Kit. It includes the clay bar, as well as a solution you use to dampen the car. So, the process is:

  1. Thoroughly wash and dry your car
  2. One section at a time, moisten the surface of the car using the spray
  3. Rub the clay bar directly against the car, not using very much force
  4. Dry with a dry rag
  5. Once you’re done with a section, fold the clay onto itself a few times so you’re not rubbing the old impurities against a new section of car
  6. When you’re done, wax the whole car.

This is typically a half-day affair, even for a normal-sized sedan. I haven’t yet clayed my wife’s SUV, but I assume that it will be an all-day affair.

Claying a car seems (and looks) ridiculous. I’m telling you, it’s worth it.

Every Few Years: Leather

Every few years, I also like to do a full round of leather conditioning. Another friend of mine recommended this kit from Lexol, which is expensive, but well worth it. It took a really disgusting looking steering wheel back from the brink of death, including making some cracks all but disappear.

The Lexol kit contains cleaner and conditioner for leather, and protectant for vinyl surfaces like your dashboard. For the leather, apply the cleaner, and then the conditioner. For the protectant, just use it on non-leather, plasticky surfaces, as you would Armor All.

It took me forever to do all the seats, the steering wheel, and my shift boot in my last car with all three Lexol liquids, but I was stunned by how good everything looked when it was all done.

A Closing Thought

I can’t stress enough that I am not passing off any of what I’ve said as the one true way to clean a car. I also take my car to automatic car washes from time to time. Different tools in the tool belt.

As with all instructive posts one reads on the internet, I strongly suggest careful scrutiny of the above, and for you to form your own conclusions.

That said, I find washing my car — especially with my kids — to be an extremely enjoyable way to spend some time outdoors on a nice day.