/home/mbrennek

A mostly-fresh start

T-minus 1 Week?

I made my prediction on Twitter a few weeks ago, but I am going to make it slightly more formal here. My guess is WWDC2013 announcement & ticket sales are happening on April 24th, 2013.

Why? It is the day after the next earnings call. I predicted that about a month before it happened last year, and I think it will be same this year. The bean counter’s laywers are scared shitless of Sarbox, and prevent anything non-business news from happening anywhere except in the post earnings call free-for-all.

We’ll see. I’ve got no insight, I’m just some guy with a hunch. If I’m right, I look like a psychic, if I’m wrong I delete this post and no one ever cared.

Update: Nailed the announcement, did not see the delay in ticket sales coming.

Nodedub Walkthrough

A brief(?) introduction to the source for Is WWDC Sold Out Yet?

As I said in the release announcement, the site was a way for me to get some experience with a new toolset, so don't expect to see a lot of best practices or even correct practices when looking through the code. I had used nodejs on the job briefly before, but it was someone else's existing design, and I believe you don't really know a language/framework until you have built something in it from scratch yourself.

This is also not a How to get started with Heroku or How to get started with Node.js article. There are plenty of those on the web. I used several building it. This is just me trying to hit the highlights of what makes this site unique.

Before I start, I would like to thank @esigler for jumping on the domain registration and setting up a github repo and heroku instance as soon as we started joking about it on twitter. I definately would not have actually written this if he hadn't taken it seriously and created them right away.

dubstate.js

The app has 1 main piece of state. What is the current status of WWDC?

1
2
3
4
5
var DUBSTATES = {
  NO : {name: "NOPE", description: "Tickets are not on sale yet." },
  MAYBE : {name: "ALMOST", description: "Tickets have gone on sale. Go!"},
  YES : {name: "YEP", description: "You are too late, they sold out." }
};

apple.js

This file provides the meat of the application. Is WWDC actually sold out yet? It gets https://developer.apple.com/wwdc/, counts the number of instances of 2012 and 2013, and if 2013 is mentioned more, it assumes WWDC 2013 has been announced and triggers the state change from NO to MAYBE. This function is called every 5 minutes via a simple setTimeout() call.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
var checkYear = function () {
  https.get('https://developer.apple.com/wwdc/', function(res) {
    var wwdcContent = "";
    res.setEncoding('utf8');
    res.on('data', function (d) {
      wwdcContent = wwdcContent.concat(d);
    });
    res.on('end', function() {
      var oldyear = wwdcContent.match(/2012/g);
      var newyear = wwdcContent.match(/2013/g);
      ...snip error checking...

      if ((dubstate.currentState == dubstate.DUBSTATES.NO) && (newyear > oldyear)) {
        dubstate.trigger("MAYBE");
      }
    });
  });
};

That trigger action at the end checks the database to see if it has already happened (restart, multiple nodes, etc) and if it hasn't, then it sends and email to everyone who has registered their email address.

Handling email

This part was tricky. Registrations trickle in slowly, but when the site changes, everyone expects to receive an email immediately. So to ensure we get as many emails out as we can and still keep it cheap/free to run, we:

  1. Confirm email addresses. Nothing too hard, just a simple encryption of the email address & a nonce bundled in a url for the user to click to make sure the recipient really wanted to be added. We send this confirmation through gmail since they happen slowly.
  2. Cull uncomfirmed addresses regularly. Lots of people will test with fake addresses for some reason, or bail when they find out it is a two step process. This keeps the record count low, and keeps us in the free rowcount for heroku.
  3. Once confirmed, add users to a mailgun mailing list. This allows us to send a single email when Apple's site is updated and ensures everyone gets it in a reasonable timeframe.

building the confirmation url

1
2
3
4
5
6
var confirmationString = email + ":" + nonce;
var cipher = crypto.createCipher('aes-256-cbc', credentials.aesKey);
var crypted = cipher.update(confirmationString, 'utf8', 'hex');
crypted += cipher.final('hex');
var url = "http://" + req.headers.host + "/confirm?confirmationString=" + crypted;
emailer.sendConfirmationEmail(email, url);

credentials.js

At one point in development I had most third party credentials (gmail, mailgun) hardcoded in the source, which is why the github repo has no history. It has since been changed to read these values from environment variables. Heroku and other cloud hosting services allow you to set these env vars easily based on environment (dev/test/production), and reading them in with javascript is extremely straightforward.

1
2
3
4
5
/* for encoding/decoding confirmation string */
exports.aesKey = process.env.CONFIRM_AES_KEY || "";
if (exports.aesKey == "") {
  console.log("Error: Missing AES key.  Won't be able to process confirmation emails.");
}

route/index

By default, the express framework uses the jade templating engine to reduce the amount of HTML you have to write. It takes a little getting used to, and I'll admit I barely learned enough to get a functional page displayed. This is one of many problems I have with ruby as well, I don't want or need a shorter HTML, I just want some defined way of inserting templated variables like with underscore.js or jquery.

the index javascript where variables are passed

1
2
3
4
5
exports.index = function(req, res) {
  res.render('index', { title: 'Is WWDC Sold Out Yet?',
                        value: dubstate.currentState.name,
                  description: dubstate.currentState.description });
};

the jade index template file

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
extends layout

block content
  h1= value
  p= description

  if value == 'NOPE'
    p Register your email below to be notified when WWDC is announced.
    form(name="registration", action="/add", method="get")
      input(type="text", name="email", size="35", maxlength="320")
      br
      input(type="submit", value="Submit")

  if value == 'ALMOST'
    a(href="https://developer.apple.com/wwdc") WWDC at Apple

  br
  br
  a(href="/privacy") privacy policy

fin

That's it as far as the unique bits I think. There are some helper functions I wrote for accessing the database and emailing, but they are pretty self explanatory, and the app.js (int main() if you will) is mostly boilerplate stuff to get the server running and set up the routes/pages. If you want further clarification hit me up on email/twitter and I'll be glad to expand this post.

Is WWDC Sold Out Yet? Open Sourced!

Is WWDC Sold Out Yet? is Open Source!

Get the source here. It's wrtten completely in JavaScript using Node.js and hosted on Heroku. A short walkthrough is coming soon (Update: here ). It was an experiment to learn how Nodejs and these new alternative hosting solutions work. I had a lot of fun trying something new and I think it helped me appriciate closures a lot more. If you want to contribute, or find any glaring errors please let me know or open a pull request and I'll get it fixed asap.

Note: While it was developed from scratch in git, this is a fresh repository with zero history of how it got here, since some of the intermediate stages had credentials for various external services hardcoded.

End of iOS 3, 4.0, 4.1, 4.2 Support

All the tech nerd/mac nerd sites keep pointing to the UDID ban as the big news of the day. While that is indeed important, and every developer should make sure none of their code or 3rd party dependencies like crash reporters or ad networks use the UDID anymore, I think the most overlooked item of the day is Apple's second post.

As of May 1, iPhone 5 support is required. While this may be great news for those iPhone 5 users who are tired of apps not using those extra 88 points of vertical realestate, it is the final nail in the coffin for those still using iOS < 4.3 and iPhone 3G or iPod touch 1G/2G devices.

While most app developers had already set the minimum version supported of their products to 4, 5, or even 6, it was still techinically possible to support all the way back to 3.2 by using Xcode 4.4. That's because Xcode 4.4 still allowed you to target iOS 3.2 and create armv6 binaries. With Xcode 4.5, apple stripped armv6 support and set the minimum iOS target at 4.3. Apple has also decreed that if you want to support the iPhone 5, you must build with the iOS 6 SDK and at least Xcode 4.5, even though it still technically works with the iOS 5 SDK on Xcode 4.4 (Apple ties SDK versions to Xcode releases).

Yes, I know if you google hard enough there are ways to partially build in Xcode 4.5, take those results and re-build and archive them on Xcode 4.4 and submit THAT franken-binary, but that's way outside supported territory getting into submission rejection land for doing funky things. Bye Bye iOS 3 and iPhone 3G owners. It was nice knowing you, but at least now everyone can transition to ARC without feeling like it was their decision to cut off older customers.

iOS 6.1 Screen Flickering / UIAlertView

With the release of iOS 6.1, my main product at work started flickering pretty badly. Sometimes the whole screen, and sometimes just small regions unrelated to view boundaries. I initially had no idea what was going on, but woke up at 4am the next morning with one thought keeping me awake. UIKit.

A few hours of hunting later and I found it. An UIAlertView was being created and shown from a background thread early in the app setup process, and once you triggered it the flickering continued sporadically until the app was killed with the task switcher. Once you got past this initial setup phase most users never trigger this particular alert view again, so it only affects new users (those most likely to leave App Store reviews). This bugged version is still available in the App Store at the time of this writing because of a software release process that I will rant about another day.

I knew it was almost certainly an alert view triggering this bug, so why hours? Because we have 150+ alert views in this app, and I had to try triggering nearly each and every one of the until I found the right one.

I hate the UIAlertView. How much do I hate it? Let me count the ways.

  1. It’s an ugly hack for lazy programmers to stop the user from interacting with your app on a device that should always be under the users control, 100% of the time. And it can’t even do that. The device has a giant home button that always interrupts program execution and returns the device to the home screen. At any time. Good luck recovering your uninterruptible state upon relaunch when that happens.

  2. It’s too easy to create. You don’t have to stop and think about where it belongs in you’re view hierarchy. You can create and show one anywhere with 2 lines of code and user interaction grinds to a halt. This is where my particular bug came in. My predecessor didn’t stop to think where he was calling it from.

  3. It’s design is crap. Assuming you have a need for such a concept (you don’t), then at least make sure it doesn’t cause a mountain of spaghetti code when used. This class has a single app delegate who’s main method upon return only gives you a button index. It is your responsibility to keep track of what the buttons prompted the user to do, and if you have multiple alerts which one was shown and what the appropriate action to take upon their dismissal should be. This leads to long switch statements dispatching other functions at best, and a giant didDismiss…. function at worst. Let’s face it, you’re always crunched for time and doing the awful hack way.

  4. There is no notification when you screw up. This is more of a UIKit hate in general, but if your going to require all UI state changes happen on the main thread, then by God have some way of enforcing it other than bugging out at runtime. Do some static analysis and throw a compiler warning. Abort() at runtime. Something other than random glitching on users please.

  5. It’s a modal popup. I hate popups. You hate popups. Users hate popups. The only thing worse than a popup is a popup that forces you to interact with it.

It may have been necessary durning the 2.0 days when everything was single threaded, but now UIAlertView is a rotting crutch used by the stupid and the lazy. We’ve got blocks and GCD for doing data crunching in the background while keeping the UI responsive. We’ve got simple synchronization methods for updating the UI when the work is done. Just think about user experience for two seconds before using it. Please. That’s all I ask.


tl/dr; Don’t touch UIKit from background threads. UIAlertView blows.

LLDB Autocomplete, Aka Fucking Codesense, Part 2

Way back in 2005, I bitched about Xcode’s autocomplete, because it was always guessing wrong. It was super annoying because it wasn’t just a list of suggestions, it actually filled in the line and moved your cursor, so if you paused for a split second it gave you extra work instead of being a timesaver. Thankfully someone working on the source editor is a little less masochistic and switched it to a visual studio style list of suggestions.

The old asshole must have moved to working on LLDB, because now it has the same broken behavior the Xcode source editor used to have. Pause for a half second to see which account* object you want to inspect, and it “helpfully” fills in the wrong one, every time.

Between this and the awfulness that is OCUnit, it leaves me wondering if anyone at Apple ever uses Xcode to test or debug code, or if they all just use AppCode.

GuestBook for the iPad Now Open Source

When I started the Guestbook app in 2011, I wrote it as a favor to a friend, and as a learning opportunity. A work friend was getting married and wanted to use her iPad as a Guestbook, but there weren’t any apps in the App Store that fit her needs. I had been playing around with the SDK for a couple years and looking for a project idea I could publish, so I wrote a basic one up real quick. I pushed out a 1.0 in about a month of evening & weekend work, and had all kinds of grandiose plans about where to take it.

Then life happened, as it usually does. Beth was pregnant with our son, and as soon as he was born all hobbies went out the window. I’d much rather spend time with him than writing code anyway, and I got a new job writing iOS applications. Getting paid to do what you like and all that jazz. Thirdly, it isn’t exactly setting the world on fire anyway. At the time I wrote it there wasn’t any competition, but now there are 8 guestbook type applications in the app store, and several are free. I’m going to keep charging a nominal amount for mine in the hopes that it will earn enough to keep paying for my developer license, but anyone who contributes can get a promo code just by asking.

It was a lot of fun to write, and I believe it helped me land my current job (if nothing else than just through the experience). Hopefully someone finds it useful, even if it’s in a what-not-to-do sort of way. I still have the laudry list of things I want to improve, both feature and code quality wise, but this is my acknowledgement that I’m not going to get to it any time soon.

On Creating

In the year since Samuel was born, I’ve had very little time to learn new skills or create things. Don’t get me wrong, I wouldn’t trade my time with him for anything in the world, but I do miss the act of making something from nothing, or learning a new thing that makes me better. My job for several years has just been the thing I do to get paid. I’ve learned all there is to learn in my current group, done all there is to do.

In the past, my creative outlet was writing code, building franken-boxes for special purposes like a mythtv machine or a router. These IT nerdery activities all have one thing in common. They require a large chunk of continuous time to dedicate to the task. Writing code is an especially strong example of this. It takes 15 – 20 minutes just to load the whole problem set into my memory (get in the grove), and you don’t get that kind of time to yourself, ever, when you have a small child.

Where does that time go? Mostly care and feeding of the child. They start off with zero skills in this world other than eating, sleeping, and pooping, and it is your job to turn them into a complete human being. Even the eating and sleeping part needs improvement. Sam was a horrible sleeper for much of this first year. Many hours every day were spent rocking him, so even with that free time I was supposed to have while he was asleep, I still didn’t have access to a compiler, or the ability to concentrate.

What I did have access to during these long periods of rocking was my phone. I didn’t think of much to do with it other than twitter/facebook/games though. Even the games I could play one-handed were limited. I stuck to a lot of card games. My scores are way higher than I would ever care to admit. With no creative outlet at work or at home, I’ve felt myself getting very old very fast.

I tried to correct the work situation immediately upon returning earlier this year, but economic and management constraints have pulled me back into my same old role. This lead directly to this new change in my professional life. Hopefully a new job at a new employer doing new things will satisfy that craving to be a better nerd.

The other half of fixing this creative drought is to stop playing games and start writing. Those continuous blocks of rocking I’ve wasted playing card games could have been spent putting ideas into words. I’ve switched blogging platforms so I can write offline, and then edit and publish in the minute here/ minute there computing scenario that is my home life. Conceptually, I like writing as a creative outlet because it doesn’t require the pre-load time or specialized tools that designing and writing code does. I can write a sentence here and there and then try and bring it all together into a cohesive thought later. I’m not completely happy with my new blogging workflow, but thinking about how to improve the process gives me something else to do.

What I don’t want is for this to devolve into a random bitch-about-my-day blog, so I have deleted over 300 of those types of posts from history. They are preserved in a backup, but they will probably never be seen again.

Also, since I do not have time anymore for random IT bullshit (transitioning the blog alone took 3 weeks of here-and-there time), comments are gone. If I write something that you would like to respond to, please do so via twitter, email, or your own blog.

Moving On

Yesterday I accepted an offer to advance my career at a new company. I’ll be creating iOS applications for electric coop customers at NISC.

To my friends and coworkers who remain at Boeing, it’s been a pleasure working with you, and I hope we can stay close even without the daily grind holding us together.

To my future coworkers at NISC, I look forward to meeting you, and building some great mobile experiences in the future!