August 2014

If I create a tool that’s good at posting content to Facebook and Twitter, it should also post to RSS feeds, which exist outside the context of any corporation. Now other generous and innovative people can build systems that work differently from Facebook and Twitter, using these feeds as the basis, and the investors will have another pile of technology they can monetize.

From the perspective of someone writing on the Internet, it’s so incredibly difficult to get someone to care about what I think, I can’t imagine making them work for it. It’s such a huge privilege to have anyone contemplate my words, that I feel obliged to roll out the welcome mat.

Marco was recently in a fight on the internet. I missed it and don’t know what it was about. I have no interest in being a spectator in these kinds of things — and if they were to happen to me (they don’t) I’d stop using my Twitter account.

Because that’s the thing — though it may have been started by a blog post, it all happens on Twitter.

Even though I follow people I like and respect, there’s no way around seeing some of the crap that happens on Twitter. Even if you don’t use Twitter at all, you will have seen articles about people being harrassed and threatened. You will have noticed the pure toxic sludge that pours through the service. (A hypothetical “Dawn of the Idiocracy” prequel would feature Twitter prominently.)

And it’s worse than any blog comments system, because if you use it, anybody can put something in front of your face whether you want it or not.

Twitter is also wonderful, and I get so much value out of it. But it’s like 51% good and 49% bad.

I don’t see it getting any better. Hopefully it can hold the line at just-barely-worth-it. (But the recent changes to the timeline make that a little less likely.)

So here’s what I do: I think of Twitter as part of my workplace. When I’m done for the night, my iPhone and laptop stay in my office. I’ll often pick up my iPad and do some reading — but there are no Twitter apps on my iPad and I don’t go to twitter.com on my iPad.

Some other things: Sheila and I eat all our meals together, but we don’t take out our phones while eating. We don’t take out our phones while going for our nightly walk.

In other words: if we’re hanging out, we’re hanging out with each other rather than with ourselves and the entire Twitter world. That world can go away for a while — it’ll still be there later, and it will still be the same stuff it is every single day.

Twitter is addicting in the same way slot machines are. You get small bits of pleasure at random intervals, and it doesn’t really change. So you keep pulling the lever or pushing the button.

And it’s cheap, too — 140 characters can’t compare to the grown-up pleasure of a good conversation with a real person.

That got me thinking about how CloudKit fits into this picture. As an iOS developer, CloudKit is immensely appealing at first glance. The API is low-level enough that you have a good deal of control over how your app interacts with the server. At the same time, you get a lot of server-side functionality for free, which leaves you with more time to focus on building a great app. But I still have a lot of misgivings about it.

I’ve written about CloudKit before. It seems well-designed, and I suspect (though not based on experience yet) that it’s better-executed than earlier broadly-similar services from Apple.

We would have been tempted to use it with Vesper — but it would have meant, for instance, that we couldn’t do a web app version of Vesper.

Vesper 2.003 came out earlier this week — and it includes a syncing performance enhancement which I thought I’d write up.

Performance enhancements aren’t always as straightforward as the one I’m about to describe. Often they require the hard work of revising the data model, adding caching, or doing your drawing the old-fashioned way (as opposed to just setting a property on a layer, for instance).

This one happens to be easy to write about, so I will.

But first I’ll say that Vesper is already fast and gets plenty of praise for its performance. I’m a speed freak with zero patience — except for the considerable patience required to make sure my software works for people like me.

So this performance enhancement isn’t something that any current users are likely to notice, but it will become important in the future as people create more and more notes.

* * *

Here’s what we noticed: the initial sync on a new device, with a large number of notes (more than almost anybody has; more than I have), seemed unexpectedly slow.

My first thought was that the server was having trouble handling this. It wasn’t — it was returning all the data quite quickly with no complaint. And the amount of data was around the same as a typical image file. A lot, sure, but not an insane amount for a first sync.

So I ruled out the server, networking, and JSON translation as issues. Next I did some poor-man’s profiling — I hit the pause button in the debugger a few times as the app was syncing.

And the same function always appeared: getUniqueID. It’s a little C function that calls SecRandomCopyBytes to generate a random unique ID for a note (VSNote object).

The answer was clear: that function either needs to get faster, or we need to not call it so often. Or both.

Not Call It So Often

The syncing system creates a VSNote object for each JSON note pulled from the server that does not exist locally. On first sync, that’s every single note.

The problem: VSNote’s init method generates a unique ID by calling getUniqueID. This is superfluous in the case of notes coming from the server — those notes already have a unique ID.

So I did the obvious thing: I created an -initWithUniqueID: method that allows the creator to specify a unique ID, which means I could avoid all those calls to getUniqueID.

Awesome. Problem solved. Done.

I could have stopped there, but I didn’t.

Make the Function Faster

It still bothered me that that function was so slow. It didn’t really matter, at this point. But why would SecRandomCopyBytes be so slow? Something like that could be a little slower than some other system APIs, but still the numbers I was getting seemed weirdly super slow.

So I did a straightforward timing test, and SecRandomCopyBytes itself is plenty fast enough. What gives?

I thought it might be the collision check. There’s an NSMutableSet of all note unique IDs, and we check the returned value of getUniqueID to make sure it’s not in that set. Profiling told me that that’s not the slowdown. (As expected, since the collision check happens in getUniqueID’s caller, not in the function itself.)

What I found was that it was the limits on unique IDs that were causing the problem.

The limits are this: it has to be a positive 53-bit integer (instead of 64-bit), and the integer has to be greater than the constant VSTutorialNoteMaxID. (Which is 100.)

The body of getUniqueID is actually a loop. It calls SecRandomCopyBytes repeatedly until it gets a uniqueID that fits within the limits.

I had thought, naively, that it would typically take one to three calls to get a suitable uniqueID — but I was wrong. Ten passes through the loop wasn’t that unusual, and it could be more.

The solution here was pretty simple: if the number is outside the range, use some arithmetic to get it inside the range.

If it’s negative, subtract it from zero to make it positive.

If it’s greater than the 53-bit limit, divide in half. (In a loop until it fits within the limit.)

If it’s less than VSTutorialNoteMaxID, add VSTutorialNoteMaxID.

This made it so that getting a unique ID that fits within the limits takes exactly one call to SecRandomCopyBytes instead of potentially many calls.

There is still the possibility of collision with an existing ID, but that would be so rare (most likely never), and the consequences are just a second call to SecRandomCopyBytes, so I didn’t worry about that.

But, again — most performance issues I run into don’t have nice straightforward solutions like this one. When they do, I don’t mind. It used to be that I’d beat myself up for not doing this better the first time, but these days I don’t. I’m just glad that I learned something and made the software better.

We’re still running a summer sale for inessential.com sponsorships — $500 for a week ($400 for each of two or more weeks), which is about one-third off the normal price.

If you’d like to support this blog and talk about your thing (app, conference, service) to inessential.com readers — who are, to a person, successful, intelligent, and curious — then get in touch with me.

The week starting Sept. 1 (this Monday) is available — as are later weeks. See the Sponsorship page for more info.

You can’t escape dependencies — even if you’re running Linux, Apache, MySQL, and PHP on a virtual machine — and so you need to evaluate everything.

Some questions to ask:

How long will this service be around? How difficult would it be to move? How much of this service’s unique features do I use? How much benefit do I get from those?

This extends to software, too. What is a given package’s reputation for security? Is it likely to be maintained in the future? Will upgrading to get a security fix also mean revising some of my code?

You have to plan for scale. Will this service and and those software packages allow room for growth? (Sharding, running multiple instances, etc.)

And you have to balance developer time. The point is to do less housekeeping and more bug fixes and features.

With Vesper we chose Azure Mobile Services on the grounds that it’s likely to be around a very long time and it’s based on Node (which is well-supported and runs in many places). The folks there were extremely helpful as we were making our decision, and that helped us decide. (You want to go where you’re wanted, for one thing.)

That said, we still have contingency plans, because anything could happen. There are no cases where you wouldn’t want to plan to be able to move. (In fact, we have two: one for moving from Mobile Services to another Node provider and one for moving to another service running Sinatra, in case we have reason to get off Node.)

Our contingency plans aren’t specified to the smallest detail, but that wouldn’t be more than a day of work, which is acceptable. (One reason not to get too detailed: the options will look different in six months, one year, three years, etc.)

I have no expectation that we’ll ever need to move. Azure is a big bet for Microsoft (the new CEO comes from the Azure group). We’ve found that the system performs wonderfully (we get praise for efficient syncing) and there’s a ton of room for growth — we’ve barely scratched the surface so far. (We run with just one instance, and that’s well more than enough.) We’re entirely happy with our choice.

That’s what works for us. But every app and every developer is unique, and there’s no way out of evaluating all the dependencies and making the best decision. I don’t think there are easy answers — it takes diligent research and thinking.

Yes I know, ha ha Null Pointer, Java, LOL. But that’s an exact line number friends. What did the user do? They tapped the subscribe button. Which page where they on? The Podcast Dialog. Zero ambiguity. Guess how many of our Android crashes we get that for? 100%. In iOS we’d be lucky if even 30% of our crashes had stack traces we can line up to actual things we can then reproduce.

Via Michael Tsai. (I’ve said before and I’ll keep saying that you should subscribe to Michael’s feed. If I had to cut my subscriptions down to two, I’d go with Michael’s blog and Daring Fireball.)

The reason I don’t like social media is that it takes two things that are polar opposites and duct tapes them together. Your own utility – to save links, to write text, to move files or materials, to keep notes, to communicate with yourself in the future, to communicate with some other specific people – and the social media outlet’s desire to fulfill its own objectives first.

I’ve heard blogs classified as a type of social media. Maybe that’s true, and maybe not — I don’t care.

What I do care about is that my blog isn’t part of a system where its usefulness is just a hook to get me to use it. It works the way I want to, and the company running the servers (DreamHost) doesn’t care one fig what I do.

My blog’s older than Twitter and Facebook, and it will outlive them. It has seen Flickr explode and then fade. It’s seen Google Wave and Google Reader come and go, and it’ll still be here as Google Plus fades. When Medium and Tumblr are gone, my blog will be here.

The things that will last on the internet are not owned. Plain old websites, blogs, RSS, irc, email.

A parent I know complained to their school that using Facebook for organizing and communicating was a bad idea, since Facebook’s mission is to know everything about everybody everywhere, and she doesn’t really want to help them reach that goal.

Guy indicated that he feels like the decoupling offered by the responder chain is too great — that sending an arbitrary message up the chain, with only the sender for context, is insufficient to convey user intent.…

The delegate-based approach also “cuts off” the chain early, and the argument seems to be that it cuts it off at the “right” level: where context first appears. But what if there’s more than one “right” level, or more than one scope of context?

I’ve always taken it as a clue that action methods take (id)sender as parameter — which I take to mean: “Here’s an action name and a thing. Figure out what to do.”

This is probably more true on Macs, since sender might be a button, menu item, whatever, and the action method may have to do some digging to figure out intent.

But if it’s deterministic and not a guess, is that wrong? I’m not sure.

When the table view cell was asked to perform an action it could simply pass it up the responder chain with itself as the sender. With the simple convention of an -(id)representedObject method (or Protocol if you want to be fancy) we can at least glean from the sender which item to act upon.

You can see, I hope, that at this point you’re not that far from a blogging engine that reads Markdown files on disk and returns HTML.

Run the Server

In Terminal, navigate to your CoolWebSite folder. Type the following:

ruby CoolApp.rb

You’ll see that WEBrick starts up, and you’ll see a message like this:

== Sinatra/1.4.5 has taken the stage on 4567 for development with backup from WEBrick

Now, in your browser, go to http://localhost:4567. You should see the It Worked! page, in glorious HTML. (You can view the page source to confirm.)

That’s it. Now you’re a web developer — and, what’s more, you have an easy-to-learn and lightweight framework plus a language that should feel very familiar. (No square brackets, but I’m confident you can get by without them.)

The costs for not using message passing, on the other hand, can be high because they make the code more rigid. You cannot retroactively make compiled code more dynamic. And yet, since dynamic is not the default, the odds are that a lot more methods will be static than need to be. Most of the time, objc_msgSend is not why your code is slow, yet Swift acts like it needs to protect you from this.

I’m no fan of swizzling and don’t care what happens to it. I throw swizzling in the same bucket as SIMBL and APE and haxies, and I’m glad they’re not screwing up my apps these days. (Newer Cocoa developers have no idea what I’m talking about, I realize.)

But I do care about KVO. Very much. (Warts and all.)

This die-hard speed freak has never been concerned with the speed of objc_msgSend. I’ve noticed it in Shark and in Instruments, but the real performance issues were elsewhere in my code.

That said, it hasn’t escaped my notice that most of the time static dispatch would be fine. And most of my classes could be marked as final. So I’m not actually outraged or anything — I keep an open mind that I would actually get noticeably better performance from Swift.

Especially given that now we do have the dynamic modifier.

Objective-C is supple by default. Amazingly so. But I shouldn’t pretend I use that suppleness more than I do. And if Swift isn’t so supple — well, that’s probably closer to what we actually need than what we think we need.

Still, though, there is code in Vesper that can’t be ported to pure Swift. (Model code.) The point that you can use something like Core Data but couldn’t write it in Swift remains important. But, then, Swift is young, not even 1.0, and part of the deal is that it’s up to us developers to communicate our needs.

…it seems to me that it should be entirely possible to make iOS apps that are mostly configurable from the server-side. This got me thinking. Why not build the app to download its configuration file (i.e. plist, or Localization.strings file) remotely?

With Vesper we use DB5 for configuring things like fonts, colors, and sizes. What’s stopping us from hosting a DB5.plist on a server, and changing the app on-the-fly?

Nothing. In fact, this is how TapLynx works. (TapLynx was two-projects-ago for me; I worked on it before Glassboard.)

TapLynx is DB5’s dad. The product itself was an Xcode project and static library. You’d configure your app using a plist (they were publication-style apps, made of RSS feeds). You’d set colors, graphics, titles, feed URLs, app structure, etc. — all without writing any code.

The configuration file would ship with the app, but the file could optionally include the URL of a configuration file online which the app would download and then use. That file could be changed at will.

The configuration file could also include URLs to graphics, which the app would also download and use. (Think of graphics for tab bar items. That kind of thing.)

We haven’t done anything like that with Vesper because we don’t anticipate needing it. All it takes a server that can host static files (S3, for instance), but it’s still more moving parts, and I’d rather limit the number of things that can go wrong.

And the kinds of bugs this system can fix are limited. It doesn’t change any actual code.

If I find a UIButton tossing some message up the responder chain, documented nowhere else except in the Interface Builder UI? That sucks. Do you think I’m being funny?

I’ve done this myself. Possibly even often. (Though it’s likely I wired it up in code rather than in IB.)

The thing is: I want the enclosing view controller to get the action message. When something needs to happen — changing the model, presenting another view controller, etc. — it’s likely that it’s something the view controller should do. Table view cells shouldn’t have that kind of power.

Guy writes:

Simply sending a message up the responder chain that some sub-item of a view has been tapped doesn’t help make the user’s intention clear except under the most superficial or specifically co-ordinated conditions.

My cases may be superficial. I expect the message only to go as far as the nearest view controller, and the only things it needs in order to know what to do are the message itself and the button that triggered it (the sender).

(If you have the button, you can find out what cell it’s in, get the index path of that cell, then look up what model object it refers to. Which sounds like a pain but actually isn’t.)

I think, though, that it’s likely I’m doing this in cases where I’m not using IB, since there’s no way to wire up the action to the view controller in code without somehow giving the table view cell a reference to the view controller, which I don’t want to do.

Is it possible in IB to wire up a button in a cell to an action method in the enclosing view controller? I would think so. (Memory is hazy on this.) If so, being explicit like that is probably a good thing, especially for the sake of the next person to look at the project.

But there’s a case that worries me: table view cells may be reused in different view controllers. In that case, I think you do want to wire up actions to first responder. (But then document them in the code, so Guy doesn’t have to go crazy.)

There’s a small case like that in Vesper: the Typography settings screen has a text preview feature, and that table view cell is the same table view cell used in the timeline.

(However, there are no buttons and no actions in this case, and so the issue of action targets doesn’t actually come up.)

* * *

Update 4:45 pm: Well, Guy’s right. The way you should do it is to create a protocol that the view controller conforms to. The cell’s delegate is the view controller.

(This way the cell doesn’t need to know about a specific class of view controller: it just needs to know about that protocol. I have real-life cases where a specific UITableViewCell subclass is used by different classes of view controllers.)

The button should be wired to an action method in the cell. That action method should call the right method on the cell’s delegate.

It’s likely, in this scenario, that you also want the cell to have a reference to a model object. I myself would make this opaque — the cell could have an id representedObject property, which is set when the view controller configures the cell.

This way, inside an action method in the cell, to actually do the thing, you’d have code like this:

Waiting to launch a product until its “magical” moment goes against the concept of MVP, or minimum viable product, which has become so trendy in business over the last few years…

…entrepreneurs need to be very careful in their interpretation of what a minimum viable product actually is. If you’re launching something in a space where there are a lot of people trying to do something similar to you — for example, with a consumer product — then the bar for MVP should be ridiculously high.

There are no redemptive moments or moments of hope. Things start out as unspeakably awful — and go south from there.

And just when you think somebody is getting somewhere, just when you think there might be a break, the darkness snaps shut tight.

It reminds me in a way of The Metamorphosis. It starts out bad — the protagonist has been turned into a giant bug — and then he suffers, and then he dies. He’s not likeable (you suspect he deserved this fate) and his family is even worse.

And there’s no explanation for why he turned into a monstrous vermin.

And there’s no explanation for why I love The Metamorphosis so much.

It’s not like other stories that are so awful. The British version of The Office was kind of like that — so sad, so yucky — but then you find to your surprise that you care about the characters, and the big and small good things that eventually happen shine even brighter for the contrast against the bad things.

The Leftovers is not like that.

It makes me think of an America that’s one terrible step away.

After September 11, after years of war with no end, after the horror of the lack of horror over our beloved nation torturing people, after the militarization of the police and the rise of the prison and surveillance state, after the worst economic shock since the Great Depression, after the realization that climate change is devastating cities now and our can-do country can’t summon the political will to save itself — it feels as if The Leftovers is just one trauma away from where we are right now.

As fiction I love it, because I love the courage of stories like The Metamorphosis — and, well, there’s probably something wrong with me.

As social commentary it’s an angry warning, and worth paying attention to, before we end up living in it.

Note that this implies that the popover presentation controller does not exist until after the call to -presentViewController:…. As such, the popover can’t be configured until it’s already been presented!

With Ita, we’ve focused on the act of marking items complete (a single tap) and reordering items (a long press). This is because when you are using a list, these are the actions that you do most often.

The article looks at Reminders, Clear, and Wunderlist as well as Ita, and explains the Mohawks’s design decisions.

Mattt Thompson, in Swift Operators, talks about overloading operators and proposes some guidelines.

He provides some examples that seem reasonable. 2 \*\* 3 could mean two-to-the-third-power, for instance.

Or =~ could mean is-regex-matchable. Or √4 could take the square root of 4.

I wasn’t a C++ developer and I don’t have any horror stories — and yet I’m still extremely wary of custom operators and operator overloading.

One of the great things about Cocoa and Objective-C is how easy it is to read other people’s code. If we’re both following the conventions and standard patterns, your code looks much like mine. This blows that up.

There’s an argument against every example I’ve seen so far (in this article and elsewhere). One or more of the following seems to apply in every case:

You can’t type the thing without copy-and-paste. (The √ character, for instance.)

You don’t know what it means without looking it up. (The ** and =~ examples.) Function names are easier to read than symbols, even mathematical symbols (except in the most common cases, which are already part of the language).

You lose out on auto-complete that you get when you use functions like pow, sqrt, and match instead of operators. Operators are thus often less convenient than functions.

Mattt’s guidelines are pretty good — especially “Don’t create an operator unless its meaning is obvious and undisputed.” I’d argue that one only of his examples (√) is obvious and undisputed, and it suffers from the copy/paste problem. (It’s way easier to type sqrt, especially with auto-complete.)

Brent’s guidelines on custom operators and operator overloading would be simple and short: don’t use them.

Maybe in a year or two we’ll nominate someone suitably diligent — Manton Reece, perhaps — to try a custom operator in his code. We’ll ask him to report monthly on his health and well-being, and after another year — if he hasn’t ripped it out of his code, if he isn’t suffering from any ill effects (blurred vision, headaches, joint pain) — we’ll ask him to post it as a gist so we can see it.

Then let’s take another year to decide who can go next. After a decade or two we’ll have an idea whether or not cautious, limited use of custom operators is okay in some rare situations.

CocoaLove is a new conference in Philadelphia taking place October 24th - 26th that’s doing something a little different. The technology and frameworks we use are only one small part of our craft, so the lineup of 11 amazing speakers are instead focusing on much more timeless topics, like:

Did design really play as big of a role in Apple’s success as we think, and how can we follow their lead to be more successful?

How can we leverage our community to make better products without needing to hire large teams?

Dealing with feedback is hard. How can we keep those conversations healthy and productive?

CocoaLove is a great opportunity to spend the weekend having interesting conversations with a bunch of people in the Apple community, and leave with some new friends.

Tickets are on sale now and include genuine Philly cheesesteaks (grab one quick, CocoaLove is limited to 150 attendees).

Louie Mantia talks about how he got started as a designer by tinkering:

Just as an engineer might. You start with something that exists and you change it to understand it. You do things on your own. But now… companies like Apple have locked down things like theming. It’s so hard today that no one even bothers. Changing icons is hard too. With some apps you can’t even do it without an app breaking because of code signing.

How will kids these days be able to tinker, Louie asks, when everything is locked-down?

I don’t know the answer, but I’m not too worried about it. A little bit, but not that much.

Here’s the thing: when the Mac came out in 1984, people asked the same question. The Apple II was a great machine for tinkering — and Macs didn’t even have BASIC. How were kids — and adults — going to write programs? It seemed like the end of tinkering as a hobby.

But it most definitely wasn’t.

It always seems like the age of tinkering is over. But, at least so far, it’s not. Tinkering is a strong human drive, and it will find a way.

Vesper 2.002 adds a new feature — when you’re viewing a single note, tap at the bottom of the screen to see the date it was created. Tap again to see the modification date. Tap again to character or word count. (Character count if the note is short; word count if not short.)

Other changes include some small cosmetic fixes, fixes for a couple crashing bugs, and a change to how photos are synced: it syncs low-resolution versions first, then high-resolution versions. (This makes photos syncing faster.)

App Camp for Girls came to Seattle, and I was lucky enough to get invited to the pitch session. (And Sheila was on the panel, which was very cool.) It was a big room and so there was enough space for not just the parents and camp counselors and volunteers but a few local folks like me.

The pitch session is kind of like the recital. At the end of the week, three teams of four girls each present their app. They talk about what it does and how it works, and they talk about competition, marketing, and how they plan to make money. (They all got the same memo about for-pay apps we did — the apps are free with in-app ads.)

It was wonderful.

I was a fan before, but actually seeing it — seeing the energy of the girls and the cool things they made, and seeing how happy (and happily tired) Jean MacDonald, Liz Marley, Kristina Sontag, and all the volunteers were at the end of the week — put me over the moon. This is the coolest thing, by far, that’s happening in our community.

If you can help, help. If you can sponsor a team, do it. If you can donate, donate. And, if you get so lucky as to get invited to a pitch session, go. Don’t miss it.

…developers would probably start to get unwrap fatigue. They inspect the code, see that there’s no way the index could not be valid (there’s no index arithmetic going on there, just use of an index that is guaranteed to be within bounds), and just force unwrap instead.

This is a slippery slope. Once you start doing that, you do it all the time.

targetViewController​ForAction:​sender: does indeed walk the view controller hierarchy, sending -canPerformAction:​withSender: on the way. But if a view controller returns YES, it will then determine whether the instance’s method for that selector is an override of a UIViewController implementation for the same selector. If not, it keeps looking up the chain.

Since August is the slow season, we’re running a summer sale on sponsorships. It’s about one-third off: just $500/week and $400 for each of two or more weeks. The sale runs through the week of Sep. 15 - 21.

Want to appear on the blog with the smartest readers in the world? See the sponsorships page for more info, or get in touch with me.

In Adopting Adaptive, Pablo Bendersky considers whether or not new iPhones would have a higher DPI.

One possibility for the higher-DPI case:

If Apple wants to have the text look the same size, they might tune the Dynamic Text default sizes on the new device to compensate for the higher DPI.

This could account for why Dynamic Text has been so strongly encouraged.

Also from Pablo:

There are some apps that rely a lot on custom fonts, such as Vesper. Since Dynamic Text does not allow you to use custom fonts, I wonder how those apps will work around a different Dynamic Text default size. There are tons of valid workarounds, like fetching the system font size and base your custom font size on it, but an Apple blessed solution would do a lot to help in this direction.

But this is just the first step: Getting young girls interested in tech cannot be our only focus. A recent New York Times article revealed that women end up leaving tech in numbers three times greater than our male counterparts. Without also addressing these issues in the workplace for adult women, our girls will simply grow up and leave in similar numbers.

I’ve written a couple times recently about wanting to use standard controls instead of custom controls — but we have a bunch of cases where we need to use our app’s embedded font, which doesn’t go well with the system font, and most standard controls don’t let you use a font other than the system font.

Going custom is more work, yes, and more to maintain. But there’s another aspect, easily overlooked: standard controls come with built-in accessibility features, and you need to roll your own with custom controls.

Which means it’s even more work than expected. And it’s a good bet that Apple’s ace accessibility teams have done a better job than I will.

So I filed a Radar asking for the ability to set the font (via attributedText or other means) on standard controls, so we can take advantage of the great work Apple has already done. It’s in nobody’s interest that we go custom.

* * *

I never really thought about accessibility that much until one day in 2003 when a NetNewsWire user sent me and Sheila an audio recording of what it was like to use the app. And, because NetNewsWire used standard controls exclusively — or lightly customized, but not from-scratch — it just worked. It was so cool. I hadn’t done a thing except to use AppKit as intended, and this user had an app that worked wonderfully for them.

I’ve grown even more attached to the issue of accessibility later as I came to understand it’s not about just one thing — it’s about a range of different things. And I can see it in my own life. At age 46 I’ve started to get far-sighted, and it’s difficult to read some text on my iPhone.

And I’ve been near-sighted since third grade — terribly near-sighted. With my contacts out I have to hold a screen so close to my face that, even on a retina display, I can see the pixels. I have to close one eye. I have to physically move an iPad to read text at the bottom of the screen, since that distance is farther than I can see.

My case is easily manageable — but still, it means that accessibility is for me too, not just some people I haven’t met.

Since I use and recommend Interface Builder, my work load is significantly less than those that are still living by the “write everything in code” mantra. If you find yourself still in that camp, I’d highly recommend using iOS 8, Xcode 6, and trait collections as an opportunity to get on the Interface Builder bandwagon.

I’d be shocked if, a year and a half from now, the separate iPad/iPhone categories don’t disappear. I suspect they’ll maintain some semblance of them for legacy apps but will start requiring all new apps be universal. Again it won’t happen quickly. Give it a year and a half. It’s coming.

Maybe. But here’s the thing — while developers should come to see it as a gradient of device sizes instead of separate families, users still see iPhones and iPads.

And it’s also a cinch that lots of iPhone apps already in the system won’t be updated to work well on iPads. And plenty of iPhone apps to come still won’t work well on iPad, even though Apple has made that much easier. (And vice versa, but to a lesser extent.)

And there may be some apps that really only make sense on small or large devices. Panic’s Status Board, for example, makes perfect sense as an iPad (big-screen) app, but much less sense as an iPhone (small-screen app).

Clark also mentions that some indie developers count on the iPad version as a separate app — it’s another SKU, another source of revenue. It’s unlikely that these same developers can go universal and raise the price of their app to compensate.

So this change, if it comes to pass, makes making a living on the App Store incrementally harder. Which is not a reason Apple shouldn’t do it.

I haven’t been in this position yet — I haven’t had a Mac app distributed outside the Mac App Store (or a Mac app at all) in a few years.

But I bet my solution will be to not use embedded frameworks.

It’s still possible to use shared code, after all. Even sub-repositories. It’s just that the files are included in the app target, rather than built as frameworks.

It may be less elegant in some ways, but it’s the way I’ve been doing things on iOS for a while now, and it’s working fine for me. It has the added benefit of allowing me to pick and choose what I need from a set of shared code.

Example: Q Branch Standard Kit includes some XML parsing stuff, but Vesper doesn’t need it. If we had a Q.framework that included everything, then Vesper would include code it doesn’t actually need.

As TN2206 indicates, custom resource rules no longer work on 10.9.5 or Yosemite Developer Preview 5. The effect, for us, is that none of our shipping apps will currently be accepted by Gatekeeper on those versions of OS X.

Apple appears to really want people to make reactive design so that one app will adjust for multiple screen sizes. Thus the loss of the extra income for a separate iPad app. I’m sure they’ll allow dual pricing for a while but I’d be shocked if that pricing will last for long. I bet by fall 2015 Apple will begin demanding a single app for all of iOS.

I would think that Apple would strongly encourage developers to publish universal apps. I doubt they’d switch to actually requiring universal apps.

But we should nevertheless shift our thinking about iOS apps in this respect: rather than think of iPhone apps and iPad apps, we should be thinking about iOS apps.

The addition of size classes and adaptability (and UISplitViewController and popovers to iPhones) means that we need to think more like web developers: we don’t know screen sizes in advance.

We shouldn’t even think about different types of devices (iPhone vs. iPad) — instead we should design for adaptability.

This gives Apple the ability to create devices at different sizes. Imagine devices midway between current iPhones and iPads. Imagine even smaller iPhones or even bigger iPads. It becomes just a range of sizes, not two separate families of devices.

Apple has redefined what a good iOS app does. A good iOS app adjusts to any screen size, and doesn’t care (or even know) if it’s an iPhone or iPad. Or something else.

Update 11:30 am: An earlier version of this post said that userInterfaceIdiom was deprecated. I got that from my notes on session 216 — but it appears I was using shorthand with myself. It might as well be deprecated — but it’s not actually deprecated.

Retina screens have once again placed typography at the forefront of design, allowing us to breathe life into words themselves as imagery. It’s a damn shame we’re not given more room to do so.

Dave asks for the ability to use custom fonts in alerts, share sheets, under-the-cell buttons, menus, and so on. I want that too — I’d be able to work more efficiently and use standard controls most of the time.

Here’s the thing, though: I want, as a user, to see apps that take longer. More interesting, richer, harder-to-make apps have value and shouldn’t disappear from the world or be the sole province of corporations (who won’t make these apps anyway, for the most part).

So I’d modify the advice to say: don’t take more than 90 days, unless you can afford to and you truly believe your app warrants it. But be sure.

In App Rot, Marco goes beyond just the issue of custom vs. standard controls and says:

Efficiency is key. And efficiency means doing more (or all) of the work yourself, writing a lot less custom code and UI, dropping support for older OSes, and providing less customer support.

Apple is greatly helping our efficiency. Every version of iOS brings new capabilities that make previously difficult features much easier. iOS 7’s redesign gave indie developers a huge advantage by making the stock UI cool again.

So true. I’m constantly thinking about ways to work more efficiently.

But, because we’re talking about software development, once you get to the concrete level of an actual app, it’s full of hard choices and dilemmas. I’ll talk about some of these choices we have to make as we work on Vesper.

The Problem of Fonts

Vesper has a ton of custom UI. Alerts, various popover-like things, the sidebar, swipe-to-reveal table cell buttons, and so on. Things that look like navigation bars are custom. Even the search bar handling — which stays pinned to the top of the screen instead of scrolling with the table — has a bunch of custom code, though the search bar itself is (almost) standard.

Some (not all) of these custom things exist only because we want to use Vesper’s font instead of the system font. (This isn’t unique to us. Plenty of iPhone apps use embedded fonts. Overcast is a recent example.)

Though we need to work efficiently, design still matters, and Vesper is designed around typography. We’re not even going to think for one second about dropping the embedded font.

But that leads to this dilemma: do we switch to the standard controls that don’t let us use our font, or do we stick with our custom controls?

If we stick with the custom controls, then we have more code to maintain, and, to a certain extent, we have to track Apple’s changes with each iOS release.

If we switch to standard controls, how do we justify the cost of switching? It may lead to less code and an easier-to-maintain app — but it’s more work right now, and doing that work doesn’t fix any bugs or add more features. And then we have that situation we didn’t want, where Vesper’s font and the system font compete with each other.

It reminds me of pre-Yosemite OS X, which distinguished between system and user fonts, and both were sans-serif. It was weird. But at least on OS X that was an established thing, where on iOS it’s most definitely not. And clearly the direction of UI design is to use just one font, not two.

(Yes, I could hack into the view hierarchy in some cases and change the font. But that’s bad engineering and I don’t like it. Vesper does it in exactly one place, so that the Cancel button next to the search bar uses our font.)

View Controller Transitions

Vesper was written during the iOS 6 days. The main screens use view controller containment, but the transitions were written before iOS 7 introduced standard ways of doing custom transitions.

iOS 8 is coming, and we’re still using iOS 6 code. Should I revise this code to use the new features in iOS 7 and 8?

That might not actually simplify things, but it’s been my experience that you don’t want to get too far behind on things like this. And there’s always the possibility that another coder could help with the app some day — and they’d be less at sea if they saw what they expect to see rather than my custom transitions system.

But it’s more work to make the change, and, again, it’s work that doesn’t actually fix any bugs or add new features.

Nevertheless, you’d have to call this technical debt, and technical debt should, in general, be dealt with.

But it’s not clear to me yet that we could actually accomplish replacing Vesper’s current transitions with the new system. And I’d hate to do all that work only to find out it’s not do-able. That would be an incredibly inefficient use of my time.

I actually don’t know know what to do. The default — doing nothing — may be the best call.

Swipe-to-Reveal Table Cell Buttons

iOS 8 lets us customize the buttons that appear when you pan a table cell to the left. This is great.

Since we can’t customize the font, we’re not sure we can use this. But the pull toward looking and feeling like a modern iOS app is very strong.

And it gets a little worse: Vesper lets you swipe a note to archive it, without having to stop and tap an Archive button. This isn’t supported by the new UITableViewRowAction feature. We’d have to take away the archive-in-one-motion feature — which would be a shame. It’s an integral part of Vesper.

And then it gets just a little bit worse still: Mail on iOS 8 does have the ability to keep swiping to get the default action. Mail must be doing this as a custom thing or using private APIs to get this.

Which means that if we adopted the standard behavior, people would ask us why we don’t “just” do what Mail does.

Which argues for not touching this at all, since we already do a custom thing which works.

But that, again, means we’ve got this custom code to maintain and a bit of UI that’s quite unlike what users expect. (Which isn’t necessarily the worst thing. There can be good reasons for doing something different.)

Lessons

You can learn a few things from my experience.

One is that doing an app with an embedded font is expensive, far more expensive than just the cost of licensing the font. If you’re willing to accept Clash of the Fonts — your font is used everywhere except in system-generated UI — it’s not so expensive. But if your app is at the place on the design curve where an embedded font is a good idea, then it’s probably at the place where you wouldn’t accept that conflict.

Another is that working efficiently from the start is a good idea. You want to avoid the situation where you’ve done custom work and are now faced with either going standard or revising your custom work — because both options require work. If you’d started with standard stuff you’d have less work to do. But there are also the cases where you created something custom that becomes a new standard part of iOS in a later release, and the dilemma is unavoidable.

A last lesson is that you can’t avoid doing some work. Even standard controls and features get deprecated and replaced, and you need to think hard about when and how to deal with iOS changes — and iOS changes come every single year. Can you give up every summer, every year, to getting your apps ready for the next version of iOS? You might have to, no matter what you do.

What do you get when you take some of the best Apple dev authors, trainers, and speakers and combine them with the most passionate, engaged developers in a region? You get a learning and networking experience that will not soon be forgotten! You get CocoaConf!

The CocoaConf Fall tour is getting started this weekend with a sell-out crowd in Columbus, Ohio. If you’re missing out on Columbus, you can still get tickets for the following tour stops:

If your plan is to make a tidy living building the next beautiful ___ for iOS nerds, then you are destined to repeat my mistakes. Don’t follow in my footsteps. Focus on a difficult problem that matters to a significant number of normal people. Don’t worry about being the prettiest or the most featured.

It is, however, a bit staggering to think that these numbers mean that there are around 300,000 apps on the Store currently that have seen an update in the past 3 months. It took over 2 years for the App Store to even reach 300k apps listed. That speaks to me of a very wide developer community clamoring for attention in the App Store.

The percentage of apps that don’t get updates may be smaller than you think. Later in the article David Smith puts the percentage of effectively-abandoned apps at 40%, which is a smaller number than I would have expected. (I would have guessed 60%.)