rentzsch.com: tales from the red shed

Textcast: The Story

Notes

Well, the word is finally out: I'm very pleased to announce Textcast 1.0.

hg log proves Dave Dribin and I have been working on it for the past year (first commit on Dec 10 2007), which is rattling since I remember pitching the app concept to Dave as “a two-week app”. Uhm, our schedule slipped a bit…

Textcast was born while Dave and I were plotting what we thought would be our first app. We wrote some experimental code and did some planning. It became clear that this app would be a great deal of work, so I pitched Textcast as a “simpler” app to get out the door first.

Textcast is a deceptively simple app. It’s aim is simple, but it unifies many disparate technologies and applications which leads to lots of complexity under the hood. It’s one of those projects where you can get the basics working inside a weekend, but it takes months to make it a real Mac app.

Which is worth it, since I'm probably Textcast’s heaviest user. I primarily listen to podcasts generated by Textcast while commuting (by foot, train and car) and exercising. But sometimes I use it for weird stuff like listening to legal contracts which would normally make my eyes cross. I use Textcast practically every day. Indeed, it became increasingly weird to tick off the months using this app that I loved that I couldn’t tell anyone about.

What follows is a potpourri of Textcast-related developer topics. Most of it addresses 10.5-only technologies we used in Textcast:

  • Alex: Of course Textcast relies on Alex, 10.5’s new high-quality voice. Alex makes a world of difference when listening to lots of text, and Textcast probably couldn’t exist unless we cut a deal with Cepstral to bundle their high-quality voices or something.

  • PubSub: Initially I used Textcast exclusively on one-off article basis. I'd spot a long blog posting or feature-length article and use Textcast’s “Import from Safari” feature to pop it into podcast form so I could listen to it later. Dave’s the one who pushed on adding real feed support with 10.5’s new PubSub framework. There were some weird behaviors we needed to work around, but overall I'd say that PubSub was a win. Certainly better than rolling our own feed parser.

  • 10.5-only yet no GC: Textcast isn’t garbage-collected — we use the old explicit retain-release model. Interestingly, Textcast was garbage collected for a short time — I have a commit message from Jan 17 2008 “Turn on garbage collection”. But twelve days later we turned it off for reasons Dave and I can’t remember. I think Dave and I were scared of a rumored conflict between GC and NSOperationQueue, which we were just integrating at the time. Speaking of NSOpQ…

  • NSOperationQueue: I'd call our use of NSOpQ a win, but it did bring definite pain. We definitely wanted to multithread Textcast since converting text to audio using Alex can be considerably processor-intensive. Spreading the load across many cores Just Made Sense. The main pain points were integrating NSOpQ and its worker threads with Core Data, KVO in general and NSSpeechSynthesizer (which is non-thread safe in a very specific manner).

  • Core Data: Mostly a slam-dunk when coupled with mogenerator. Core Data is Standard Operating Procedure around these parts — you need a reason not to use it. The biggest pain turned out to be handling our undo model, which doesn’t fit into Core Data’s model and is thus Very Hard to get right.

  • Unit Testing: It’s official, Dave is test infected. And that’s a good thing. While there’s considerable pain (some undocumented) in getting Xcode set-up optimally, it’s well worth it.

  • Scripting Bridge:. iTunes needs a real API. Badly. But until we get a high-quality API for this increasingly integral app, we have its poor AppleScript support. At least Scripting Bridge mostly allowed us to pretend iTunes has an Objective-C API, easing integration into our app. We also use Scripting Bridge for the “Import from Safari” functionality. It was on my TODO list to also move our NetNewsWire integration from its current AppleScript form to Scripting Bridge, but I screwed up when I sent Brent Simmons our NetNewsWire scripting dictionary patch, which means we can’t use Scripting Bridge straight-up with NetNewsWire. But that patch deserves its own bullet:

  • NetNewsWire Integration: Most of the text I feed into Textcast probably comes from NetNewsWire, so we definitely needed some nice integration with Brent’s fine app. I reached for NetNewsWire’s AppleScript support. That worked fine for importing text from the currently-selected item when in list view mode, but didn’t work if you had a web view frontmost.

    So I did what you'd probably expect from the likes of me: I reverse-engineered NetNewsWire and added the feature. I sent the resulting source code to Brent and kindly asked that he roll the new feature into the next version. He was very cool about it and rolled it into NetNewsWire 3.1.7b4. Thanks, Brent!

    Anyway, the ability to execute Javascript via AppleScript against the currently-selected web view allows me to extract the selected text, covering both avenues of textual input.

    A little polish that I'd like to call out since it’s not obvious: if you don’t have NetNewsWire installed, we don’t even show the “Import from NetNewsWire” toolbar item to reduce clutter+confusion.

  • Open Source: We used a bunch of open-source packages in Textcast. Thanks Mac indie community!

    And speaking of open source, since you've read so far, here’s a treat:

  • JRFeedbackProvider: Early in Textcast’s development, I knew it would be important to capture user feedback. I raided my ~/Applications folder and searched out the best in-app feedback mechanisms. I found two winners: Things.app’s slick-looking multipurpose feedback panel and Acorn’s useful feedback panel with its pre-populated text. So I created my own version which combines both into one nonviral open-source version you can easily drop into your own app (and website!): JRFeedbackProvider. Patches welcome. Thanks to Gus and the Things.app crew for the inspiration.

Phew, long posting. Instead of reading it, how about you listen to it?

Wednesday, December 24, 2008
12:00 AM