Vincent Gable’s Blog

August 5, 2008

Simplified Logging

Filed under: Cocoa,MacOSX,Objective-C,Programming,Sample Code,Tips,Usability | , ,
― Vincent Gable on August 5, 2008

I have noticed a pattern in my Cocoa code, which I have been able to simplify. I often print out the value of a variable for debugging. 99 times out of 100, the code looks like this: NSLog(@"actionURL = %@", actionURL);, where actionURL is some variable.

But using the macros, I can say LOG_ID(actionURL);. This is shorter, and non-repetitive.

The macros I use to simplify debugging (2008-09-16):

#define FourCharCode2NSString(err) NSFileTypeForHFSTypeCode(err)

#define LOG_4CC(x) NSLog(@"%s = %@", # x, FourCharCode2NSString(x))
#define LOG_FUNCTION() NSLog(@"%s", __FUNCTION__)
#define LOG_ID(o) NSLog(@"%s = %@", # o, o)
#define LOG_INT(i) NSLog(@"%s = %d", # i, i)
#define LOG_INT64(ll) NSLog(@"%s = %lld", # ll, ll)
#define LOG_FLOAT(f) NSLog(@"%s = %f", # f, f)
#define LOG_LONG_FLOAT(f) NSLog(@"%s = %Lf", # f, f)
#define LOG_OBJECT(o) LOG_ID(o)
#define LOG_POINT(p) NSLog(@"%s = %@", # p, NSStringFromPoint(p))
#define LOG_RECT(r) NSLog(@"%s = %@", # r, NSStringFromRect(r))
#define LOG_SIZE(s) NSLog(@"%s = %@", # s, NSStringFromSize(s))

Look in assert.h for insight on how to roll your own debugging macros.

August 3, 2008

How to Implement Automatic Updating

Filed under: Cocoa,Design,MacOSX,Objective-C,Programming,Sample Code,Usability
― Vincent Gable on August 3, 2008

If you are about to add automatic updating to your application (and you should), then check out the Sparkle Framework before you start. I wish I had done this before I spent a lot of time writing my own automatic updating code.

A quick-and-dirty hack.

Get your application’s version number by:
[[[NSBundle mainBundle] infoDictionary] valueForKey:@"CFBundleVersion"];
(This articles explains setting up Xcode to bake the current subversion-number into every build.)

Get the the latest version number out of a text-file on a website:
[NSString stringWithContentsOfURL:[NSURL URLWithString:@"www.your-url-here.com/whaterver/version.txt" encoding:NSASCIIStringEncoding error:nil]];
If there is any trouble, it will return nil. CAUTION: This will block the main thread for up to 30 seconds, while -stringWithContentsOfURL: tries to access the URL.

Making it Usable

If you don’t want your program to be unresponsive while it tries to get something off the internet, try this:

- (void) updatesReady
{
   //do something....
}

- (void) synchronousCheck
{
   NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];

   NSURL* latestURL = [NSURL URLWithString:@"http://www.your-url-here.com/currentversion.txt"];
   NSString* latestVersion = [NSString stringWithContentsOfURL:latestURL encoding:NSASCIIStringEncoding error:nil];
   //latestVersion will be nil if there trouble was accessing latestURL.

   NSString* ourVersion = [[[NSBundle mainBundle] infoDictionary] valueForKey:@"CFBundleVersion"];
   if([ourVersion isLessThan: latestVersion])
      [self performSelectorOnMainThread:@selector(updatesReady) withObject:nil waitUntilDone:YES];

   [pool drain];
}

//will call updatesReady iff a new version is ready, otherwise does nothing.
- (void) checkForUpdates
{
   [NSThread detachNewThreadSelector:@selector(synchronousCheck) toTarget:self withObject:nil];
}

A few thoughts on automatic updating:

When the user explicitly runs an update-check, they should get immediate feedback that something happened. You should put up a window with a spinner. Even though checking for updates may only take 100msec in your lab, if the user is on a flakey wireless connection while traveling, it could take several seconds for them.

It feels like it takes several years for an HTTP fetch to time out, if you can’t do anything while you wait.

Anything having to do with threads is harder then it looks.

“Check for updates {daily, weekly, monthly}” is a useless preference. Just because Apple’s Software Update has it does not mean you should offer those choices! They do not give the user any real control over when and how they are annoyed by requests to update. Users don’t care when the update checks are performed — they only care when new software is announced to them. Ether go with the a simple check-box that toggles automatic updating, or go with something like:
“Let me know about new software: at 12 PM”.

Display the date of the last successful update check, never display “Last Update Monday 9AM — update failed”. (This should be obvious, but I see it all the time). Version checking should be silent and unobtrusive. Don’t tell the user about it — only tell them about results.

Automatically downloading updates in the background is a great idea if they are small, and the computer has a reasonably fast internet connection.

You might also want to check out: NiftyFeatures 0.4 By Uli Kusterer .

An example project containing two classes, UKUpdateChecker and UKFeedbackProvider that can be plugged into your applications to add “Check for Update…” and “Send Feedback…” menu items to your applications.

July 25, 2008

Trained Open Source Security

Filed under: Design,Programming,Quotes,Security | ,
― Vincent Gable on July 25, 2008

So it’s a matter of training. And that’s pretty much true of Open Source security models. Think of Open Source software. Having a bunch of random people look at the code to tell you if it’s secure won’t work. If you have well-trained people who look at the code, that will work! Open Source just means you can see it, it doesn’t guarantee that the right people will see it.

Bruce Schneier

The interview is much broader, and worth reading.

July 17, 2008

Null-Terminated Argument Lists

Filed under: Bug Bite,C++,Cocoa,Design,Objective-C,Programming,Usability | , , , , ,
― Vincent Gable on July 17, 2008

I was using +[NSDictionary dictionaryWithObjectsAndKeys:] to make a new dictionary, but one of the objects in the dictionary was the result of a call to a method that was returning nil, so the dictionary was incomplete.

This got me thinking about NULL/nil terminated argument lists. I don’t think they are a great idea (the compiler should be able to handle the list-termination for you!), but I think they are an especially bad idea in Objective-C.

The problem that it’s very common to have a nil object in Objective-C, relative to, say C++. Many Cocoa methods return nil on error. Since doing stuff with nil (generally) won’t cause an exception, these nils stick around much longer then in other languages. As you can see, nil is a pretty poor choice of a sentinel value.

It’s the 21st century! The compiler could tell an Obj-C method using a variable-argument-list how many arguments are in the list. This is trivial when all arguments are of type id. Since Obj-C methods use a radically different syntax from C functions, it shouldn’t effect existing C-code. Unfortunately, I don’t see this being added, because Objective-C is already so mature.

In the meantime. Be a little more suspicious of any objective-C methods taking a NULL-terminated list. I wish I had a perfect solution to avoid them, but I don’t! Sometimes they are the best way to do something. If you have a great work-around for constructing, say an NSDictionary with a variable number of key/values please let me know!

July 10, 2008

Money and Sales are Not a Metric for Good

Filed under: Programming,Usability | , , , , ,
― Vincent Gable on July 10, 2008

Sergey Solyanik recently explained why he left Google for Microsoft.

The second reason I left Google was because I realized that I am not excited by the individual contributor role any more, and I don’t want to become a manager at Google.

And I don’t know enough about Google or Microsoft’s management culture to offer any insight on this point.

I can’t write code for the sake of the technology alone – I need to know that the code is useful for others, and the only way to measure the usefulness is by the amount of money that the people are willing to part with to have access to my work.

He goes on to say that Microsoft measures everything in money, but at Google there are “eye candy” projects that are free, so he does not feel successful making them.

Now I feel the same way about writing code for the sake of technology alone. It is ultimately unfulfilling. And I have great respect someone who knows what they want to do, and does it. But I strongly feel that sales and profit are not the right metrics to measure how useful something is. There are several reasons for this

Customers and users are not the same thing.

Maximizing profits is distinct from, and often antagonistic to, maximizing quality.

The marketplace is too chaotic and relative to measure quality.

Ironically Microsoft Office 2008 For Mac is the perfect example. It’s the best selling version of Office for the Mac ever. If sales are a metric, it should be the best version of office. But it’s not (v5.1 may have this honor, at least for Word). Why it sucks is it’s own series ; I won’t go there for now. There are two big reasons why Mac Office 2008 sold so well in spite of it’s poor quality.

Firstly, there are more Mac users now then at any point in history. And Mac market-share is still rising. When 2.5x as many Macs are being sold today as 2 years ago it would be very difficult not to sell more copies of a popular Mac software package!

Secondly, 3 years ago, Apple switched from PowerPC to x86 microprocessors. Non-x86-native versions of office run excruciatingly slow on x86 computers. Microsoft made a “business decision” to not support Mac Office customers by denying them an x86-native update for versions of Office prior to 2008. There was a two-year period when the only Macs Apple would sell you had x86 processors, and the only version of office you could buy was not x86 native. This really sucked for users. But it paid off for Microsoft. They saved money by not supporting customers (which means higher profit, which is the only metric of good software, right?). But more importantly, it crippled older versions of office, which forced people to upgrade. People who would normally say “Well, I paid $500 for my copy of Office, and it may be old, but it does what I need, so I’ll skip the ‘upgrade’ and stick with ‘ol reliable, thankyouverymuch!” now had a horrible reason to upgrade. It isn’t that Office 2008 is so much better then other versions of Office, it’s that older versions of Office stopped working!

Customers != Users

I am a Mac Office 2008 user, but not a customer. Personally I think it’s is a shitty product, and I hate using it. But my employer’s IT department bought it for me, since it’s the only way to get Entourage, the lame Outlook clone for the Mac. And Entourage is the only way to get notifications from the company’s Exchange server whenever I get an email or calendar update, so I have to use it every work day.

You can’t look at the sale of Mac Office that I use and say “Vincent thought it was worth $500”, because that’s not what happened! What happened was that “Some company that Vincent works for thought it was worth $500…. because it played nice with their email server“. I hardly enter into the fucking equation at all! It’s more about the email servers then me, and I have absolutely no input on how the email servers were set up 10 years before I was hired.

Chaotic Market

And about that $500 figure ($499.95 is the estimated retail price of “Office 2008 for Mac
Special Media Edition”, and $500 is a nice round number so I’m sticking with it). Pricing is a black art. (That article is 5000 words, but I do think it’s worth reading). The only Office 2008 product I use every day is Entourage. But you can’t buy just Entourage, you have to buy it as part of an “Office 2008” bundle. And Microsoft loves to segment it’s pricing

So it’s unclear what you are buying, why, or how much of the money you give Microsoft is because of a clever pricing system, not clever software.

$1 does not mean the same thing to all customers. Obviously rich customers, say corporations, CEOs, etc. can pay more for something then the average Joe. Software that targets corporations or governments obviously will have a sticker price orders of magnitude higher then, say, a blog-authoring tool. But it is a mistake to conclude that just because a blogger can’t part with more money, that blogging software is less useful then “enterprise” email clients. Both fundamentally are communication tools, and both have changed the way that people interact.

Measuring “Good”

So how do you measure quality? There are several ways. User satisfaction, though hard to quantify, is probably the best. I recently attended a Red Cross training session on how to use AEDs. The teacher highly recommended the Zoll brand units we had purchased, and told us a few war-stories about how well Zoll-made equipment held up in the field, even after being dropped off the back of ambulances, and run over with a fire-engine. You only get high praise from your users by doing things right. (It’s possible to get praise from your customers by being cheap)

Another measure is productivity — how much more have people been able to do by using new software? This is quantifiable as time-to-complete-a-project or projects-completed-per-unit-time. However, it may be expensive to measure.

Unfortunately there is no silver bullet. Measurement is always difficult to do well — especially outside of a laboratory. Profit and sales are a data point. And they can tell you something — but they are not a good proxy for utility, satisfaction, or quality.

July 7, 2008

Getting OS X Icons

Filed under: Design,MacOSX,Programming,Reverse Engineering,Usability | ,
― Vincent Gable on July 7, 2008

This is what the Apple HIG has to say about icons. You should read it if you ever use icons. Even if you are not drawing your own icons, you need to understand how they should be used. (eg: icons in a toolbar should have a flat “head on” perspective, not the three-demensional look they have in the Dock.) You’ll find the icons you need faster if you know what they should look like.

Websites with icons you can use freely: IconDrawer, Iconfactory, Kombine.net.

SystemIconViewer (source included) by Noodlesoft is a useful tool. It lets you browse over 100 standard OS X icons that are available programatically.

For getting paths to private OS X icons, try poking around inside CandyBar.app — A commercial program that lets you customize just about any icon on your system. As of v2.6.1 /CandyBar.app/Contents/Resources/English.lproj/IconData.plist contains information on where icons are located. Icon locations do change completely between releases of OS X, even if the icon itself does not! I found CandyBar to be a better source of up-to-date icon locations then google. IconData.plist is pretty big and dense, but you can search it for keywords if you open it in Xcode, which helped me a lot.

(Although I haven’t used any of them personally, these are some design firms Apple recommends, if you have the cash.)

Bug Triage Priorities

Filed under: Programming,Quotes | ,
― Vincent Gable on July 7, 2008

Roughly, my triage order is:
* Data-loss bugs
* Unavoidable crashers
* Functionality-blocking bugs
* Avoidable crashers
* Avoidable bugs
* Misfeatures
* Performance issues
* Feature suggestions
* UI feedback
This is, of course, a rough ordering

Wil Shipley

I totally agree that data-loss / corruption is a higher priority then crashes. You can upgrade|downgrade a crashy program. But once data is gone, it is gone. Protecting users data should be your prime directive. I have always strongly disagreed with any bug triage that put crashes at spot #1.

Programatically Excluding Things from Time Machine Backups

Filed under: Cocoa,MacOSX,Objective-C,Programming,UNIX | , ,
― Vincent Gable on July 7, 2008

To exclude files/folders from a Time Machine backup, you can use the C-function CSBackupSetItemExcluded().

As far as I know there isn’t an official way to do this from the command-line or a shell script. As near as I can tell, the safest way to it without using compiled C-code is:

sudo defaults write /Library/Preferences/com.apple.TimeMachine \
SkipPaths -array-add "PATH-ONE" "PATH-TWO"

where "PATH-ONE" "PATH-TWO" are of course paths to items you want to exclude.

Credit to Ellis Jordan Bojar for this solution. (original article) Using defaults instead of tinkering with .plist files directly is really the way to go!

The Cost of Computing

Filed under: Programming,Quotes | , , ,
― Vincent Gable on July 7, 2008

But if you look at the history of the computer business, where we are at now in terms of costs is probably similar to where we were in the 80’s and early to mid 90s. Most of the cost was in manpower. That was the case then and it is the case now.

Hank Williams

See also, Jeff Atwood: Hardware is Cheap, Programmers are Expensive

July 6, 2008

Designers Don’t Solve Stuff

Filed under: Design,Programming,Quotes
― Vincent Gable on July 6, 2008

Designers are so low on the list of people capable of solving the problems we face … I don’t care how clever your layouts are, we, as designers, are incapable of solving global hunger, poverty or warming. We are what we have always been. The messenger, not the message.

Developers on the other hand …

Jack Shedd

Absolutely true, if “Designers” continue to limit themselves to style, and not how it works.

« Newer PostsOlder Posts »

Powered by WordPress