October 2013

That’s never, not even once, been a problem for me. Never caused a bug.

And, as a relentless cutter, a simplifier by temperament, I like it because it means I can do without a couple braces. Though it doesn’t make a difference to the generated code, it does means less actual characters in the source file, and it saves a line for the closing brace.

But I recently changed my mind. Now I write it like this:

if (something) { doAThing(); }

And for the same reason: for the reason of simplification.

It’s simpler because it removes a special case, which means I no longer have to notice that it’s one line or multiple lines and add or delete braces as needed. I’ve simplified what my brain has to do at the cost of some more braces in the source code. I can live with that trade-off.

(Second reason: it’s likely that I was the last Cocoa coder to omit the braces on these statements, and I’ve learned thehardway that other people are going to work on my code some day. Even with Vesper we’ve had Doug Russell work on the code, on accessibility.)

PS But you know what I’m never going to do? Write methods like this:

- (void)doSomething{ doAThing();}

I absolutely cannot bear that extra line for the opening brace. It gives me gas. I’ll often — usually, even — put a blank line there, but a blank line is nice and clean. A line with just an opening brace is like a fingernail on the page. (And it ruins the clean left edge of - to open and } to close.) Instead I’d write it like this:

- (void)doSomething { doAThing();}

PPS Reasonable people may disagree. It’s a cinch that unreasonable people will also disagree.

Some of you will be surprised to hear that Open Transport was included through OS X 10.8. Most of you probably have no idea what Open Transport is (was). (It was a set of flexible, stream-based networking APIs from the pre-OS-X era.)

What’s New in OS X lists some removed APIs: Open Transport, Power management (Power.h), Message.framework, ServerNotification.framework.

This came up on Twitter as a response to my post about [NSURL­Session­Data­Delegate](http://inessential.com/2013/10/12/nsurlsessiondatadelegate_and_objc_runtim).

I’ll clarify.

I have a bunch of rules-of-thumb. One is that any time you #import <objc/runtime.h> in production code, you’re making a mistake. You’re hacking rather than doing the right thing.

The runtime is fun and interesting and every Cocoa developer ought to get to know it.

But if you find yourself resorting to runtime functions rather than using the non-runtime APIs as they are designed, you’re probably fighting the frameworks — and fighting the frameworks leads only to bad software and heartbreak.

Instead, it’s better to understand the non-runtime APIs and use them as they are designed to be used. (Think of the runtime APIs as protomatter.)

That said, of all the runtime APIs, associated objects comes the closest to getting a pass. The feature is convenient and useful, and with normal care is not going to have unintended consequences.

Nevertheless, I think of associated objects as a last resort, just because #import <objc/runtime.h> is a big red flag. If the non-runtime APIs — the APIs closer to the problem at hand — provide a way to do what you want to do, then that’s the way to go.

I’ve used associated objects before, and I may again. But, every time I do, I think of it as a failure and I try to understand what failed. It’s either a non-runtime API or my own design, or both, that failed. If it’s my design, I should fix it rather than take the shortcut.

So I can’t really say that using associated objects is a hack in the sense that you’re depending on undocumented behavior. It’s documented. It’s a hack in the sense that you’re taking a shortcut. There is a more appropriate way of solving the problem.

I’ve never learned that much about Federal budget deficits and the debt. I knew that deficit refers to how short we are in a given year, and debt refers to the total amount of money owed.

So I’ve been doing research and learned a few more basic things. (Which you probably already know. Skip reading, if so. Also, it’s possible I got something wrong. Let me know and I’ll fix it.)

GDP

Debt is usually expressed as a percentage of GDP (Gross Domestic Product). There’s a reason for this: the actual number is meaningful only in relation to how rich we are.

Think of it this way: if you have a debt of $10,000, and you make $20,000 a year, then you have a serious problem.

But if you have a debt of $10,000 and you make $100,000 a year, then you can just write a check and be done with it. (You won’t enjoy it, but it’ll be done.)

Public Debt

There are two types of debt-holders: the public and the government. Most of the debt is in public hands, and when we talk about the debt we usually talk about the public debt (since the rest of the debt is money the government owes itself).

The public debt is expected to be around 76% of GDP at the end of this year.

Plenty of countries have a worse ratio: Japan’s, for instance, is well over 100%. And plenty of countries have a much better ratio. (Chile and Estonia appear to be in great shape.)

History of Public Debt to GDP

It has been higher: during World War II it was, unsurprisingly, over 100%.

It declined till the mid-’70s, started climbing quickly in the early ’80s, started declining again in the mid-’90s, started climbing again in the early 2000s, then shot up quickly when we hit the 2008 financial crisis.

There’s Always Debt

Since the founding of our nation there has been at least some debt, except for a period during 1835-1836. Some amount of debt isn’t a terrible thing.

How Debt Happens

You run a deficit, which contributes to the debt, when you spend more than you take in.

How To Lower the Debt

The goal is to spend less than you take in.

There are a few options, which are often used in some combination.

Raise taxes.

Spend less.

Make the economy stronger.

Everybody agrees on #3. When the economy is stronger, that means our GDP is higher, and the public debt is a smaller percentage of GDP. It also means that more people are making more money, which raises revenue.

When the economy goes bad, as it did in 2008, revenues go down — even without tax cuts — because corporations and people make and spend less money. We’re less rich.

In very broad strokes: Democrats and Republicans disagree on #1 and #2. Republicans believe that lowering taxes, particularly for wealthy people and corporations (Republicans call them “job-creators”), will make #3 happen. That is, the intuitively-expected loss of revenue caused by lowering taxes will be more than made-up-for by a growing economy. Republicans strongly emphasize #2: spend less.

Democrats believe that lowering taxes is a poor stimulus, though useful at times, and that the best way to achieve #3 is more direct stimulus. (Food stamps, roads and bridges, technological and scientific research, education, and so on.) Democrats emphasize #1: raise taxes.

History would seemingly allow you to argue either side. You could note that debt-to-GDP rose significantly during the Reagan and (both) Bush years, and dropped during Clinton’s administration (which ended with deficit surpluses). But it also rose dramatically during the Obama administration, and you could argue that a Republican Congress was responsible for the lowered debt during the Clinton years.

(I have my own position on this, but the point of this post is not to lay out an argument.)

Why Is Too Much Debt Bad, and How Much Is Too Much

Gus Mueller reflects on two talks from the Çingleton conference: Matt Drance’s talk on leadership and Jonathan Rentzsch’s talk on core values:

In [C4 release]; Jon described why he ended his C4 conference. In short it was over the addition of Section 3.3.1 of the iPhone Developer agreement (which basically said you could only write iPhone apps in Cocoa) and the lack of condemnation from the developer community. When Apple added that section I thought it was stupid, but hey it’s their platform they can do what they want. And so like most developers, I didn’t say anything about it.

That was a personal mistake on my part. And I didn’t realize just how big a mistake it was until Jon’s talk and the razor sharp way he described how he felt.

It’s a good reminder. The web is full of people taking stands that don’t cost them anything. What about when it might cost you something? That’s when courage is needed; that’s when taking a stand means something.

Also: when a friend of yours takes a principled stand, it’s worth your attention. You may disagree. But, if you agree, can you leave him or her to stand alone?

At the time I was apathetic. I thought it was mainly about apps developed using Flash, and I was more than happy to never see one of those. (And I’ve also said that developers who want to write iOS apps really should learn Objective-C, because they’re hindering themselves if they don’t. So I didn’t really get what Jon was talking about.)

But if you suggest that maybe it was because I lacked courage, that my explanation above is a cop-out, I won’t dispute it. I plan to do better next time.

PS My second-favorite podcast these days is Edge Cases by Andrew Pontious and Jonathan Rentzsch, the Tenspeed and Brown Shoe of the development world. Definitely worth subscribing to. (My favorite is still, God save me, Roderick on the Line.)

Example

Say you’re writing an RSS reader or podcast client. The app downloads a bunch of feeds, and passes data to a streaming XML parser, one per download, as it arrives.

How do you know which feed is associated with which task?

If each NSURLSession​DataTask had its own delegate, you could give that delegate a feed property. Easy. (That’s how you’d have done it with the old NSURL​Connection system.)

I can think of a few options:

• Overload taskDescription — give it the URL of the feed, for instance. I don’t like this, though, because that’s really supposed to be, according to the headers, a descriptive label for the task. Not a data container.

• Look at originalRequest and look up the feed object based on the URL. Feels a little fragile (though it shouldn’t be) and it’s kind of a pain.

• Subclass NSURLSession​DataTask and add a feed property. I don’t see a way to do that, though, since the NSURL​Session creates data tasks.

• Maintain some kind of map — using an NSMapTable or NSMutable​Dictionary — that maps data tasks to feeds. Do-able, but ugly. Not particularly object-oriented.

• Hack it.

The Hack

Rule of thumb: if #import <objc/runtime.h> appears anywhere in your production code, you’re probably making a mistake.

It’s fun to play with the runtime, and you absolutely should — it’s part of becoming a good Cocoa developer. But you shouldn’t ship those hacks.

This may be a case where it’s warranted, though.

I may change my mind, but right now I’m thinking that using associated objects is the way to go. Associated objects let you attach any data to any object.

Update Oct. 17, 2013

A wise man clued me in to this: there are APIs for attaching data to an NSMutableURLRequest. I’d not noticed this part in NSURLRequest.h:

NSURLRequest and NSMutableURLRequest are designed to be customized to support protocol-specific requests. Protocol implementors who need to extend the capabilities of NSURLRequest and NSMutableURLRequest are encouraged to provide categories on these classes as appropriate to support protocol-specific data. To store and retrieve data, category methods can use the +propertyForKey:​inRequest: and +setProperty:​forKey:​inRequest: class methods on NSURLProtocol. See the NSHTTPURLRequest on NSURLRequest and NSMutableHTTPURLRequest on NSMutableURLRequest for examples of such extensions.

I try — honestly, in good faith — to understand the Republican party and its leaders.

Perhaps someone can help me. Here’s one of my big questions: why doesn’t the Republican party do the traditional thing, which is to declare victory when it gets its way?

Winning, after all, looks good, and tends to be rewarded at the polls. But, more importantly, winning is winning. If you got your way, you got your way. And it makes it easier to keep getting your way.

Obvious example

The Affordable Care Act is a Republican health care plan. It preserves private medical insurance and ensures personal responsibility. It could be called the “no more freeloaders” plan.

It’s an evolution of plans by the Heritage Foundation and Bob Dole; it’s very similar to the plan that Republican Mitt Romney implemented in Massachusetts.

Were I a Republican in Congress, I would have been yelling from the hills. “Guess who figured out how to insure all Americans. Republicans! Guess who made it so even people with pre-existing conditions can get insurance. Republicans! Guess who figured out how to slow the rising cost of health care. Republicans!”

I would have voted for it and made sure everyone knew that this was a historic Republican victory. I would have made sure everyone knew that Republicans have great ideas that make life better for every single American, not just rich people.

And then, with the wind at my back, and a Democratic Congress and administration apparently keen to pass and implement Republican ideas, I would have gone on to the next one. (Perhaps regarding immigration or tax reform.)

I still don’t understand why it didn’t happen this way. It seemed like such a slam-dunk.

More recent example

Democrats in Congress reluctantly agreed to pass a CR that kept spending at sequester levels. Democrats would like to spend more money; Republicans don’t want to. Democrats caved on this completely.

Why not declare victory? “We got the Democrats to agree to our preferred level of spending, and we agreed to continue to fund the government. We won, because Republican ideas on spending are good for America and even Democrats recognized that.”

Done, right?

And then — again, with the wind at my back, with me looking like a winner and Democrats so willing to make deals — I would have taken up entitlement reform.

But that’s not how it happens

Clearly I’m naive. There is something, or many somethings, I don’t get.

Evangelicals. Social issues are central for Evangelicals and they feel a deep sense of cultural and political loss. They believe their towns, communities, and schools are suffering from a deep “culture rot” that has invaded from the outside. The central focus here is homosexuality, but also the decline of homogenous small towns. They like the Tea Party because they stand up to the Democrats.

Tea Party. Big government, Obama, the loss of liberty, and decline of responsibility are central to the Tea Party worldview. Obama’s America is an unmitigated evil based on big government, regulations, and dependency. They are not focused on social issues at all. They like the Tea Party because it is getting “back to basics” and believe it has the potential to reshape the GOP.

Moderates. Moderates are deeply concerned with the direction of the country and believe Obama has taken it down the wrong path economically. They are centrally focused on market-based economics, small government, and eliminating waste and inefficiency. They are largely open to progressive social policies, including on gay marriage and immigration. They disdain the Tea Party and have a hard time taking Fox News seriously.

Core Data

There are some apps — think of RSS readers and Twitter clients — that handle tons of new data every day. They need to be scalable, not block the main thread, and handle cases where they need to delete lots of old objects and change properties for lots of objects at once.

After hearing about this on Twitter, I immediately grabbed my phone and deleted Camera+. I was sad to do it because it is a great app — I’d used it happily for a long time — but this move really turned me off.

It pisses me off to see Tap Tap Tap — John Casasanta specifically — treat our fellow developers at Realmac Software like this. It’s wrong. We expect more from 12-year-olds, and John is an adult.

About our national debt, the Constitution says this in the 14th amendment:

Section 4. The validity of the public debt of the United States, authorized by law, including debts incurred for payment of pensions and bounties for services in suppressing insurrection or rebellion, shall not be questioned. But neither the United States nor any State shall assume or pay any debt or obligation incurred in aid of insurrection or rebellion against the United States, or any claim for the loss or emancipation of any slave; but all such debts, obligations and claims shall be held illegal and void.

This is obviously a post-Civil-War amendment; it was adopted in 1868. The parts about insurrection and rebellion can be safely ignored. Which leaves us with this:

The validity of the public debt of the United States, authorized by law, shall not be questioned.

XML-RPC isn’t, as they say, rocket science. However, it’s pretty cool that with an off-the-shelf Mac one can throw together a simple script to, for example, grab the latest post off your blog, build a link to it by its title, and copy the HTML to the pasteboard.

We need a fallback plan to save the country. There may be no choice but to attempt something that will seem radical. We face a procedural roadblock; procedures can be devised to remove it. We need to stop playing with the optics and start planning the worst-case endgame.

These may be the last days before the central catastrophe of (most of) our lives. If it comes to pass, we’ll all be poorer and we’ll never get back to where we would have been.

The world will be poorer. And it will hate the U.S.A. for very good reasons, and it will see this is a failure of democracy.

Today, I stormed out of the house as the Public Works employees dropped off the new cart in the middle of my soggy lawn (not the driveway) and then had to yell when they paid no attention to my waving… Along the way, he implied I should be happy they were dropping off the container when, after all, it was raining. Oh sugar, you won’t melt — trust me. I lived in Seattle for 24 years where rain is a way of life.

Side note: I bet you my Mom was thinking of the classic Asimov short story Rain, Rain, Go Away when she wrote “sugar, you won’t melt.” (I know that story’s in her library because that’s where I read it.)

And: the scene with the Public Works people reminds me of Jubal Harshaw in Stranger in a Strange Land when the government dudes land on his flower beds. “Get that God damned heap off my rose bushes!”

I mentioned in a previous post that I was probably going to switch Vesper to Core Data, but that I needed to test with a large number of notes first to make sure it still performed well.

Here’s the test: how long does it take for the All Notes view — the default view, what you see on launch — to show notes?

This view stresses the persistence layer the hardest, since it has to fetch all the notes that have not been archived. (It’s possible in real life that the Archive view could be larger, but with my test data that’s not nearly the case.)

Results

I’m using Core Data and NSFetched​Results​Controller, running on an iPhone 5.

Here’s how long it takes for my normal (non-test) notes to load: 0.0065 seconds. Excellent.

Here’s how long it takes to load 32,007 notes: 2.6 seconds.

The thing is, the main thread is also blocked for that entire time. My FMDB/SQLite version doesn’t block the main thread (except for an imperceptible amount of time). However, my FMDB/SQLite version takes longer to show data.

The Choice

I see two options:

Optimize my FMDB/SQLite version further so that it can do fetches in batches. (I’d already done some of the work on this, so it’s not as bad as it sounds, but it’s work I’d rather not do, since I’d rather be working on syncing.)

Assume that people aren’t going to hit tens of thousands of notes for a while, and in the meantime iPhones will get faster. (This same test probably runs faster on an iPhone 5S, after all.)

To be clear: I don’t like, at all, that it takes 2.6 seconds for 32,007 notes, and I especially don’t like that the fetch blocks the main thread. But at this point I’m willing to say that my own productivity is more important than getting that 2.6 seconds down to something smaller.

What if that number had been 10 seconds? I wouldn’t make the same trade-off — I’d go back to FMDB/SQLite. That might have been true for 5 seconds too. But for 2.6 seconds I’ll make this trade-off and stick with Core Data.

In my view, Brent has this all backwards. He is thinking up a mythical user with 30000 Vesper notes, and wants to be sure that that 1/1000th of a percent of his customers doesn’t experience any lag. What he doesn’t seem to realize, is that that choice has a big impact on the other 99.999% of his costumers, who almost certainly will pay a performance penalty.

I would have expected it would go without saying that the 99% or 99.999% come first. Performance has to be awesome for them, and I would never accept a trade-off where it got worse for most people just so it could be acceptable for the very few.

Drew also writes:

See, there’s no such thing as a free lunch in the performance game. It’s a mathematical fact. You can even read about it on Wikipedia. When you optimize a program for one scenario, you are making it less optimal for other scenarios.

Drew is absolutely correct that there’s no free lunch. There are always trade-offs.

However, there’s a trade-off Drew didn’t appear to consider: you can often beat the performance of a general system like Core Data — for both the 99% and the 1% — by creating a custom, hand-tuned system.

The trade-off means that it’s more code and more work for the developer, but the app performs better for 100% of users.

The question, then, is whether or not that particular trade-off is worth it, which must be answered on a case-by-case basis, which often requires actual measurement.

PS You should check out Drew’s project Ensembles, a new Core Data sync framework. Interesting for sure, and could turn into something quite valuable.

@brentsimmons honest question: why are you worrying about performance with 30,000 notes in Vesper? Will anyone encounter that scenario?

When I was a kid back in the ’70s and ’80s I was really into hi-fi systems. I couldn’t afford much gear, but I could borrow copies of The Absolute Sound and read them cover-to-cover. In those days the general wisdom was to buy an amplifier with way more power than you could possibly ever use, because it would sound great at low and normal volumes.

That wisdom stuck in my head, and I think it applies to software.

My guess is always wrong

I’ve learned that I’m unlikely to over-estimate the amount of data people like to keep.

Years ago (2005) I added a tabbed browser to NetNewsWire for Macintosh. I guessed that my most extreme user might have as many as 100 tabs, and I needed to make sure it scaled to that.

I released the feature and people liked it. After a while I started getting complaints about performance from people who had thousands of tabs. Some users had tens of thousands of tabs. I was way off in my estimate of what the most extreme user might do — and I’ve remembered that ever since. (The app remembered your tabs between runs, which was a fairly rare thing in 2005.)

So now I don’t try to guess what a reasonable extreme might be — I guess what an extreme extreme might be, knowing that there’s a good chance I’m still under-estimating.

But really — 30,000 notes?

I don’t know who would type in 30,000 notes. That’s a lot of typing.

Since a note can be a photo with no text, there could be someone who makes thousands of notes-that-are-photos.

But still, 30,000 notes is a highly unlikely scenario for an iPhone app.

However — what if the popular Twitter apps had a send-to-Vesper feature? Or if we added a bookmarklet to Safari for iPhone that let you add a page as a note?

Or, going even further: what if we had a public web services API? What if we had a scriptable Mac app? What if some form of scriptability was added to iOS?

Add in automation, and suddenly 30,000 notes doesn’t sound crazy.

I’m not promising or even hinting at anything. I’m just explaining what I think about when I’m making decisions regarding performance and scalability. If I assumed that there would never be a way to automate adding notes, I might be correct — but I’d be guilty of malpractice. The engineering department doesn’t have the luxury of making that assumption.

Performance Is Important

I think performance is under-rated by many developers. There are apps that I use and love that block the main thread too often, apps that are otherwise wonderful. I’m disappointed every time I notice. I sigh at the app.

I’m a developer, so I know what’s going on, and I know that it doesn’t have to be that way.

But I also think that non-developers notice these things. They don’t notice an app that performs well — it just works — but they notice an app that doesn’t. And I’m unwilling to let my app be one of those. If one person ever sighs at my app — for being unresponsive to touches, for dropping frames in animations, whatever — then I haven’t done my job.

What makes an app successful? What makes an app do well enough in the App Store so that you make enough money to be able to keep doing what you love?

I don’t know. But I think that frustrating users with non-responsiveness (and, worse, crashes) is a pretty sure way to make an app not successful.

Here’s what happened. I was adding support for multiple attachments per note (I’m not promising it as a feature, but I need to code for it) and I found myself setting up yet another lookup table to handle the to-many relationship. My code was getting more complex and less easy to maintain.

Core Data is famously good at handling relationships. I found myself wishing I could just use Core Data rather than have to do this manually again.

NSFetchedResults​Controller

Meanwhile, in the back of my head was this small UITableView​Delegate addition in iOS 7:

The great thing about NSFetchedResults​Controller is that it loads objects in batches — but for tables with variable row heights, where you need to load each object in order to calculate each row height, batching was defeated. You needed to load all the objects in order to calculate all the row heights.

With estimatedHeight​ForRow​AtIndexPath I can now use that (very nice) batching system, and just estimate row heights for unloaded objects. The row heights can be calculated as rows are about to be displayed, and not before. (I also keep an in-memory cache keyed to each object’s uniqueID.)

Suddenly NSFetchedResults​Controller was made useful.

That tipped the scales for me. I decided to take the weekend and do a Core Data implementation for the database layer, and just back out if I get spooked.

I Didn’t Get Spooked

I decided going in that I’d do things the straightforward way. No premature optimization. And this meant the scariest thing of all: I would allow database access on the main thread. (For fetches and faulting-in objects.)

I was startled by how fast it was. (On my iPhone 5. I haven’t upgraded, but it hasn’t escaped me that it should be even faster on the new iPhones.) And this reminded me that the last time I worked with Core Data on iPhones was in 2010. Devices have come a long way since then. This was good news.

I did make one exception to the do-it-straightforwardly rule. I set up two NSManagedObject​Contexts: one on the main thread and one with private queue concurrency. The main thread context is the child of the private queue context.

This way, doing a save on the main thread doesn’t hit the database: changes get saved to the private queue context, which then does a save to the database.

I am otherwise — so far, anyway — not doing multi-threaded Core Data. Which is awesome, because once you start doing that it’s just about time to switch away. (Core Data is beautiful as an intelligent persistence layer that you work with on the main thread. Once you go multi-threaded, it’s just a weird database system.)

I Could Still Throw All This Work Away

I have no doubt that Core Data is up to just about anything a consumer app would do on a Mac. But iPhones are not Macs.

I still need to run some tests. With Vesper’s FMDB/SQLite database system, I tested it by importing Daring Fireball’s Linked List archive and Dave’s tweets. (Up through early 2013.) This was about 30,000 notes and thousands of tags. It took some work, but I got it fast enough to handle this amount of data easily.

I don’t know yet how Core Data will handle this. It may handle it just fine. Or it may be that I need to do some performance work.

The worst-case scenario is that Core Data can’t handle it and no amount of performance work on my part can fix it. (Or, alternately, the necessary performance optimizations make the code too complex and too hard to maintain.)

I’m optimistic, but I’ll have to see. (If you hear nothing more from me about this, you can assume it worked out.)

Lesson Learned

Vesper is exactly the kind of app Core Data was meant for: it’s an object graph. Notes, tags, and attachments.

Now, were I writing an RSS reader (I’m not), I still wouldn’t use Core Data. The thing that took me away from Core Data years ago still applies: there are times when you need to mark 10,000 items (or whatever) as read all at once, and loading all of those via Core Data just to flip a property is not an option. Not even in a background thread. (Then there’s also the deleting problem, which is similar. You can’t let the database grow forever — you want to delete old articles periodically. That should be one SQL call; it shouldn’t mean loading in a ton of managed objects just to delete them.)

(This would probably be true were I writing a Twitter or ADN client, too. Which I’m also not doing.)

Anyway. Core Data. Even though I’ve talked about how I don’t use it, I’ve always said that you should use it, because it’s the right thing in 95% of cases. And Vesper is one of those cases.

Well. Pending my performance tests, that is.

PS I should add that I’m not using Core Data for images. Thumbnails are managed via FMDB/SQLite, and full-size images are stored as separate files on disk. I have no reason to make changes with image-handling.

UITextView Bugs

I’ve mentioned before how much I love TextKit. I do. So so much. Crazy much.

I’ve also mentioned that UITextView in iOS 7 is really a 1.0 release, since it’s the first UITextView with TextKit, and so I’m not surprised there are bugs. I understand and forgive — but I’m still eager to see them fixed.

UI Feature Requests

Since Vesper uses custom fonts for its UI, it’s a little jarring if something appears that doesn’t use the same font. There are just a few places where there is no system support for setting a custom font.

I’m talking about the “basement” menus, often opened by a “hamburger” button. We have one of these in Vesper, and my previous app Glassboard had one too. As do tons of other apps.

At this point it’s become a standard. It solves a real problem. It would be good for iPhone users were there a standard way that these all behaved. (And it would make it easy for developers to match that standard behavior.)

We get frequent bug reports that panning from the left edge of the screen is too fussy. Too easy to miss.

We’re using the standard, new-in-iOS-7 gesture recognizer for this. So I asked that it be made more forgiving or be made customizable.

(Of course, we might be able to work around it by using a regular UIPanGestureRecognizer, detecting a screen edge pan, and making it more forgiving than UIScreenEdgePanGestureRecognizer. But still, I believe we have evidence that UIScreenEdgePanGestureRecognizer should be more forgiving, so I reported it.)

Here’s what gets me about the government shutdown: if a clean continuing resolution bill came to the floor of the House, it would pass. (It’s already passed in the Senate.) Democrats would vote for it — and Republicans, aside from the few dozen hard-core radicals, would vote for it too.

In other words, a clean continuing resolution has majority support, including support from enough Republicans to pass. That’s not the problem.

The problem is that Speaker John Boehner won’t bring that bill to a vote. It’s his decision. One man’s decision.

We can speculate as to why. (He may be afraid to lose his job as Speaker.)

We can also note that Americans consider as heroes those politicians who defy their extremist wing so they can do the right thing for the nation. And we condemn those politicans as craven when they don’t.

My first thought is to assign each tag a unique ID (a UUID). This has the advantage that if a tag is renamed (not promising that feature), then it’s easy to sync that name change. Tags might look like this in the database:

name: Cars
uniqueID: 436BF0FC-E264-45EC-​ACD9-C1A363845FC7

name: Radios
uniqueID: A758512C-7B20-4F5F-9C4B-B40BE0BC2220

And then a note would refer to its tags via an array of uniqueIDs. Something like this…

So you could rename Radios to Car Radios with just a simple change. That tag would change in the database to look like this:

name: Car Radios
uniqueID: A758512C-7B20-4F5F-​9C4B-B40BE0BC2220

And the note wouldn’t have to change, since the unique IDs of its tags haven’t changed. All that’s changed is the name field. Nice.

But here’s the problem

You have a day phone and a night phone. (Imagine.)

You’re thinking about your upcoming trip to Paris. On your day phone you create a Paris tag. But your day phone is off-line, and you switch to your night phone before your day phone gets the chance to sync.

So now it’s night-time, and you’re still thinking about your upcoming trip to Paris. On your night phone you create a Paris tag too.

This makes renaming tags more difficult, though. In this scenario, a note’s list of tags looks like this:

tags: cars, radios

So when you rename Radios to Car Radios, it’s necessary to update every single note that references radios so that it looks like this:

tags: cars, car radios

That’s more work when renaming, but I think the trade-off is worth it so that your day and night phones don’t clash when you create the same tag on each device before they sync.

But what about deleting tags?

(Not promising this feature either, but I have to code for it.)

After your trip to Paris you delete the Paris tag. One way to handle this is to make the tag entry look like this:

name: Paris
uniqueID: paris
deleted: YES

Just flip the deleted bit to YES, and that’s the entire change. The app would be smart enough to know that if a note refers to the Paris tag, it should just ignore it, not show it, since that tag has been deleted.

One small change and you’re done. Nice.

But no…

Six months later you have another trip to Paris. So you’re writing a note and give it a Paris tag. What should happen then?

Well, we could flip deleted back to NO. But then all the notes from your previous trip would all of a sudden have their Paris tag resurrected.

You don’t want that, because you’d deleted the Paris tag back then. You just want new notes tagged with Paris to show that tag.

How I think I will solve the deleting problem

The right thing to do is, once again, more work. The app will visit all the notes that refer to that tag, and remove that reference.

If a note has tags like this…

tags: paris, packing, travel

…then it would be changed to look like this:

tags: packing, travel

The actual tag could remain in the database — it’s just that no notes would refer to it. In the case of tags, that’s the equivalent of deleting it.

And if you started using that tag again in the future, it would, correctly, appear just for new notes. It wouldn’t get resurrected for old notes, since those notes were changed to not refer to that tag.

Note

I’m still thinking about tags. I could change my mind (particularly if I think of a better way, or if someone tells me about a better way).

Much of the rest of syncing is conceptually nailed down. (Much of it was nailed down before I wrote the first line of Vesper code last February.) But things can change as theory meets code.