Vincent Gable’s Blog

April 30, 2009

Acceptable Delays

This is a collection of sources on what constitutes an acceptable delay. It’s very much a work in progress, and will be updated when I stumble into new information. I’m very interested in any insights, experience, or sources you may have.

Based on some experiments I did back at IBM, delays of 1/10th of a second are roughly when people start to notice that an editor is slow. If you can respond is less than 1/10th of a second, people don’t perceive a troublesome delay.

Mark Chu-Carroll

One second … is the required response time for hypertext navigation. Users do not keep their attention on the page if downloading exceeds 10 seconds.

Jakob Nielsen, (in 1997?)

In A/B tests (at Amazon.com), we tried delaying the page in increments of 100 milliseconds and found that even very small delays would result in substantial and costly drops in revenue. (eg 20% drop in traffic when moving from 0.4 to 0.9 second load time for search results).

Greg Linden covering results disclosed by Google VP Marissa Mayer

If a user operates a control and nothing appears on the display for more than approximately 250 msec, she is likely to become uneasy, to try again, or to begin to wonder whether the system is failing.

— Jeff Raskin, The Humane Interface (page 75)

David Eagleman’s blog post Will you perceive the event that kills you? is an engaging look at how slow human perception is, compared to mechanical response time. For example, in a car crash that takes 70ms from impact until airbags begin deflating, the occupants are not aware of the collision until 150-300 milliseconds (possibly as long as 500 milliseconds) after impact.

April 27, 2009

Don’t Trust TIME

Filed under: Announcement,Security | , , ,
― Vincent Gable on April 27, 2009

Technical problems can be remediated. A dishonest corporate culture is much harder to fix.

Bruce Schneier

UPDATE 2009-06-12: See also, The Top 10 Most Absurd Time Covers of The Past 40 Years.

BREAKING NEWS 2010-08-26: The Onion: TIME Magazine Announces New Version of Magazine for Adults.

Recently The 2009 TIME 100 Finalists online-poll was manipulated with hither-to unheard of sophistication. Not only did hackers vote their choice into the #1 spot, but they stuffed the ballot so that the runners up spelled out a message!

kg9kl.jpg

Jeff Atwood called TIME’s web developers clowns, but that seems too harsh to me, since online polls are so inherently untrustworthy that spending resources trying to secure them is almost always a waste. Even if all the technical problems could be solved, the results still wouldn’t be meaningful, because they wouldn’t be a census or a random sampling. An online poll is a way to engage readers, and let them do more than passively consume. TIME’s poll succeeded there, even if it was gamed. (Arguably it was more engaging because it was gamed).

But today, April 27th, TIME’s writers disingenuously denied the hack

TIME.com’s technical team did detect and extinguish several
attempts to hack the vote.

When I first heard news of the attacks, it was already a week old, TIME’s whitewashing came two weeks after the results of the hack were published. Portraying the hack as an “attempt” that was “extinguished” is just blatantly wrong.

I’m a big believer in Hanlon’s razor: “never attribute to malice that which can be adequately explained by stupidity.” But it’s very hard to give TIME’s staff the benefit of the doubt here, since by their own admission they were aware of the hack, and the poll results were “surprising”. It takes a staggering amount of stupidity not to connect the dots, or be aware of what was being written about you for weeks.

Consequently, TIME has lost my trust. If their denial was written in stupidity, it shows an unforgivably incompetent journalistic ethic. If it was a deliberate whitewashing of the poll results, then it’s an even more egregious failure. Also, what kind of an article announcing the winner of a poll only has pictures of people who are not the winner? (Hint: something by the hacks at TIME)

A Childish Thing

Filed under: Accessibility | , , ,
― Vincent Gable on April 27, 2009

I couldn’t resist doing this after reading this snooty fashion editorial in the New York Times,

But to other writers and editors, the Kindle is the ultimate bad idea whose time has come. Anne Fadiman, the author, was relieved to learn that her essay collection, “Ex Libris
,” was not available on Kindle. “It would really be ironic if it were,” she said of the book, which evokes her abiding passion for books as objects.

“There’s a little box on Amazon that reads ‘Tell the publisher I’d like to read this book on Kindle,’ ” she said. “I hope no one tells the publisher.”

Go on, make the world a more ironic and accessible place — tell them, I’d like to read this book on Kindle

Oh, and as to the rest of the article, it turns out kindle owners are not more literarily self-important; and the Kindle will give us more data to judge people with, as it becomes more integrated with our online presence. Imagine, for example, being able to tell what books someone actually read, and how many times, on their facebook profile.

April 24, 2009

How To Check if an Application is Running With AppleScript

Filed under: Programming,Sample Code | ,
― Vincent Gable on April 24, 2009
on ApplicationIsRunning(appName)
	tell application "System Events" to set appNameIsRunning to exists (processes where name is appName)
	return appNameIsRunning
end ApplicationIsRunning

Use it like,

if ApplicationIsRunning("Mail") then
	display dialog "Don't forget to write mom!"
end if

On Mac OS X 10.5, this worked for me even when the application-file’s name was changed. On 10.4 I do not expect that it would still work if someone renamed the application, unless you used the creator type to locate the process, not the name.

You might also be interested in how to get the name of the frontmost application, here’s how:

tell application "System Events" to set FrontAppName to name of first process where frontmost is true
if FrontAppName is "DVD Player" then display dialog "Get to work!" end if

April 22, 2009

-[NSURL isEqual:] Gotcha

Filed under: Bug Bite,Cocoa,iPhone,MacOSX,Programming,Sample Code | , , , , , ,
― Vincent Gable on April 22, 2009

BREAKING UPDATE: Actually comparing the -absoluteURL or -absoluteString of two NSURLs that represent a file is not good enough. One may start file:///, and the other file://localhost/, and they will not be isEqual:! A work around is to compare the path of each NSURL. I’m still looking into the issue, but for now I am using the following method to compare NSURLs.

@implementation NSURL (IsEqualTesting)
- (BOOL) isEqualToURL:(NSURL*)otherURL;
{
	return [[self absoluteURL] isEqual:[otherURL absoluteURL]] || 
	[self isFileURL] && [otherURL isFileURL] &&
	([[self path] isEqual:[otherURL path]]);
}
@end

[a isEqual:b] may report NO for two NSURLs that both resolve to the same resource (website, file, whatever). So compare NSURLs like [[a absoluteString] isEqual:[b absoluteString]]. It’s important to be aware of this gotcha, because URLs are Apple’s preferred way to represent file paths, and APIs are starting to require them. Equality tests that worked for NSString file-paths may fail with NSURL file-paths.

The official documentation says

two NSURLs are considered equal if they both have the same base baseURL and relativeString.

Furthermore,

An NSURL object is composed of two parts—a potentially nil base URL and a string that is resolved relative to the base URL. An NSURL object whose string is fully resolved without a base is considered absolute; all others are considered relative.

In other words, two NSURL objects can resolve to the same absolute URL, but have a different base URL, and be considered !isEqual:.

An example should make this all clear,

NSURL *VGableDotCom = [NSURL URLWithString:@"http://vgable.com"];
NSURL *a = [[NSURL alloc] initWithString:@"blog" relativeToURL:VGableDotCom];
NSURL *b = [[NSURL alloc] initWithString:@"http://vgable.com/blog" relativeToURL:nil];
LOG_INT([a isEqual:b]);
LOG_INT([[a absoluteURL] isEqual:[b absoluteURL]]);
LOG_ID([a absoluteURL]);
LOG_ID([b absoluteURL]);

[a isEqual:b] = 0
[[a absoluteURL] isEqual:[b absoluteURL]] = 1
[a absoluteURL] = http://vgable.com/blog
[b absoluteURL] = http://vgable.com/blog

Remember that collections use isEqual: to determine equality, so you may have to convert an NSURL to an absoluteURL to get the behavior you expect, especially with NSSet and NSDictionary.

Getting the Current URL from a WebView

Filed under: Cocoa,iPhone,MacOSX,Objective-C,Programming,Sample Code | , , ,
― Vincent Gable on April 22, 2009

UPDATED 2009-04-30: WARNING: this method will not always give the correct result. +[NSURL URLWithString:] requires it’s argument to have unicode characters %-escaped UTF8. But stringByAddingPercentEscapesUsingEncoding:NSUTF8StringEncoding will convert # to %23, so http://example.com/index.html#s1 would become http://example.com/index.html%23s1. Unfortunately, the two URLs are not equivalent. The un-%-escaped one refers to section #s1 in the file index.html, and the other tries to fetch the file index.html#s1 (“index dot html#s1”). I have not yet implemented a workaround, although I suspect one is possible, by building the NSURL out of bits of the JavaScript location object, rather then trying to convert the whole string.


UIWebView/WebView does not provide a way to find the URL of the webpage it is showing. But there’s a simple (and neat) way to get it using embedded JavaScript.

- (NSString *)stringByEvaluatingJavaScriptFromString:(NSString *)script

Is a deceptively powerful method that can execute dynamically constructed JavaScript, and lets you embed JavaScript snippets in Cocoa programs. We can use it to embed one line of JavaScript to ask a UIWebView for the URL it’s showing.

@interface UIWebView (CurrentURLInfo)
- (NSURL*) locationURL;
@end
@implementation UIWebView (CurrentURLInfo) - (NSURL*) locationURL; { NSString *rawLocationString = [self stringByEvaluatingJavaScriptFromString:@"location.href;"]; if(!rawLocationString) return nil; //URLWithString: needs percent escapes added or it will fail with, eg. a file:// URL with spaces or any URL with unicode. locationString = [locationString stringByAddingPercentEscapesUsingEncoding:NSUTF8StringEncoding]; return [NSURL URLWithString:locationString] } @end

With the CurrentURLInfo category, you can do aWebView.locationURL to get the URL of the page a WebView is showing.


One last note, the

if(!rawLocationString)
	return nil;

special-case was only necessary, because [NSURL URLWithString:nil] throws an exception (rdar://6810626). But Apple has decided that this is correct behavior.

April 21, 2009

A Scalpel Not a Swiss Army Knife

Filed under: Design,iPhone,Programming,Quotes,Usability | ,
― Vincent Gable on April 21, 2009

Steven Frank summarizing feedback on the direction future of computer interfaces,

The other common theme was a desire to see applications become less general purpose and more specific. A good example was finding out train or bus schedules. One way to do this is to start up your all-purpose web browser, and visit a transit web site that offers a downloadable PDF of the bus schedule pamphlet. Another way is to use an iPhone application that has been built-to-task to interface with a particular city’s transit system. It’s no contest which is the better experience.

…In 2009, it’s still a chore to find out from the internet what time the grocery store down the street closes — we’ve got some work to do.

I would like to see a nice pithy term replace “very specific task-driven apps”. Perhaps “Specialty Applications” or “Focused Programs”. But I’m not enamored with ether. Whatever the term, it should emphasize excelling at something, not being limited. What are your thoughts for a name?

April 19, 2009

Beware rangeOf NSString Operations

Filed under: Bug Bite,iPhone,Objective-C,Programming,Sample Code | , , , ,
― Vincent Gable on April 19, 2009

I have repeatedly had trouble with the rageOfNSString methods, because they return a struct. Going forward I will do more to avoid them, here are some ways I plan to do it.

Sending a message that returns a struct to nil can “return” undefined values. With small structs like NSRange, you are more likely to get {0} on Intel, compared to PowerPC and iPhone/ARM. Unfortunately, this makes nil-messaging bugs hard to detect. In my experience you will miss them when running on the simulator, even if they are 100% reproducible on an actual iPhone.

This category method has helped me avoid using -rangeOfString: dangerously,

@implementation NSString  (HasSubstring)
- (BOOL) hasSubstring:(NSString*)substring;
{
	if(IsEmpty(substring))
		return NO;
	NSRange substringRange = [self rangeOfString:substring];
	return substringRange.location != NSNotFound && substringRange.length > 0;
}
@end

I choose to define [aString hasSubstring:@""] as NO. You might prefer to throw an exception, or differentiate between @"" and nil. But I don’t think a nil string is enough error to throw an exception. And even though technically any string contains the empty string, I generally treat @"" as semantically equivalent to nil.

As I’ve said before,

A few simple guidelines can help you avoid my misfortune:

  • Be especially careful using of any objective-C method that returns a double, struct, or long long
  • Don’t write methods that return a double, struct, orlong long. Return an object instead of a struct; an NSNumber* or float instead of a double or long long. If you must return a dangerous data type, then see if you can avoid it. There really isn’t a good reason to return a struct, except for efficiency. And when micro-optimizations like that matter, it makes more sense to write that procedure in straight-C, which avoids the overhead of Objective-C message-passing, and solves the undefined-return-value problem.
  • But if you absolutely must return a dangerous data type, then return it in a parameter. That way you can give it a default value of your choice, and won’t have undefined values if an object is unexpectedly nil.
    Bad:
    - (struct CStructure) evaluateThings;
    Good:
    - (void) resultOfEvaluatingThings:(struct CStructure*)result;.

It’s not a bad idea to wrap up all the rangeOf methods in functions or categories that play safer with nil.

Thanks to Greg Parker for corrections!

April 18, 2009

Too Fabulous For Me

Filed under: Announcement | ,
― Vincent Gable on April 18, 2009

I bring this up, because it’s an example of when branding has caused me to pass on buying a product I otherwise liked.

Tab Energy is the best tasting diet energy drink I’ve had. Period. To me it had a pomegranate flavor, but I’ve heard it described as “jolly rancher” and “watermelon”.

It’s expensive, even within the it’s-so-over-priced-you-should-just-take-some-pills-and-juice-instead1 energy drink market. But the real shame, for me, is the fabulous branding. It’s not just gender issues, there’s nothing about the lifestyle the advertising promotes that I can embrace. Judge harshly for yourself,

So I’ve never put a pack of it in my shopping cart. Very rarely I’ve picked up single servings from convince stores — unlike the boxes, the cans don’t have “You go, Girl!” and “Shop till you drop!” printed on the front. I haven’t had a Tab Energy in over a year and a half.

1Caffeine pills and B-vitiman supplements are an order of magnitude cheaper.

April 15, 2009

Beyond Two Page Programs

And one of the things that is disturbingly true about most novices on computers is that about 2 pages of program is the maximum they can handle. They like to spread it out, use their visual field as an extension of their short term memory

–Alan Kay From Doing With Images Makes Symbols

A few thoughts on this phenomenon.

A denser, more concise, less “English like” programming language would counter-intuitivly be easier for novices to use, if it let them keep their project below the 2-page limit.

Does this limit increase with more and bigger displays?

Do graphical programming language change anything? It seems like they might “scale” better on a very large display. But in my (albeit limited) experience they are much less compact then textual source code. And it’s not clear to me they support abstraction as well.

Older Posts »

Powered by WordPress