Vincent Gable’s Blog

April 27, 2009

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.

April 10, 2009

Pre-announcing Prometheus

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

For the past month I have been working on my first iPhone application, code-named Prometheus. It’s a dedicated editor for Simple English Wikipedia.

Simple Wikipedia

The Simple English Wikipedia is a wikipedia written in simplified English, using only a few common words, simple grammar, and shorter sentences. The goal is for it to be as accessible as possible. That’s something that really resonates with me. There are many reasons why I think it’s an important project, but I’ll only briefly mention one sappy example.

Children can’t understand all of the Grown Up Wikipedia. Simple Wikipedia helps put more knowledge within their reach. I started writing weekly reports in the 5th grade. I’m sure many schools make students start sooner, and certainly I had to write infrequent reports much earlier. An encyclopedia at a 4th grade reading level would have been a fantastic tool to learn more about the world.

Prometheus

In Greek mythology Prometheus was, “the Titan god of forethought and the creator of mankind. He cheated the gods on several occasions on behalf of man, including the theft of fire.”

Similarly, the Prometheus iPhone app brings knowledge from the American-English Wiki Gods the to the majority of the world that does not speak English natively.

Given the platform, I believe enabling small quick edits is the best way to go. I want to make it easy for a native English speaker to spend 30 seconds correcting grammar while waiting in line.

With more constrained language, writing-aids become even more helpful. Surprisingly, I find myself using a thesaurus more when writing Simple English, to be sure I’m using the clearest synonym I can. There’s a lot more an application like Prometheus can do to help, because it’s not targeting a complex language.

Roadmap

I’ll be brutally honest, the first release is not going to have any fancy analytical features I originally envisioned. My goal is to get a 1.0 release out quickly, then iterate on the feedback and what I learn. I also have a selfish motivation here, because I’m looking for a job, and not having something in the App Store yet has been a big barrier.

What I can promise for 1.0 is

  • A greatly streamlined interface, as compared to editing through Safari.
  • A safe, save-free, experience, where nothing you write is lost if you suddenly have to quit and do something else.
  • Shake to load a random page. (Look, I know that’s nothing to brag about. But it is something Safari can’t do.)
  • A $0 price tag.

I’m trying to have 1.0 ready in about a moth, but that’s an aggressive goal.

Beyond that I would like to add a “simple-checker” that can flag complex terms, and a mechanism for as-you-type suggestions of more common terms. But both of these are technically challenging, and my main priority will be building on what I’ve learned by putting something in front of people.

Teaser

Here’s a picture of what I have running today.

prerelease.jpg

I have a lot to say about the evolving design in future posts. But this should an you an idea of what I’m shooting for — as minimal an interface as I can manage, hopefully condensed into one toolbar.

I don’t have an icon yet, if you can help there please let me know. Unfortunately this is a free project all around, so I’m not in a position to hire someone. My current idea for an icon is a hand
holding a fennel stalk with fire inside, much like an olympic torch. I’d love to hear your ideas.

Percent Escapes Gotcha

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

If you use stringByAddingPercentEscapesUsingEncoding: more than once on a string, the resulting string will not decode correctly from just one call to stringByReplacingPercentEscapesUsingEncoding:. (stringByAddingPercentEscapesUsingEncoding: is not indempotent).

NSString *string = @"100%";
NSString *escapedOnce = [string stringByAddingPercentEscapesUsingEncoding:NSUTF8StringEncoding];
NSString *escapedTwice = [escapedOnce stringByAddingPercentEscapesUsingEncoding:NSUTF8StringEncoding];
NSLog(@"%@ escaped once: %@, escaped twice: %@", string, escapedOnce, escapedTwice);

100% escaped once: 100%25, escaped twice: 100%2525

I thought I was programming defensively by eagerly adding percent-escapes to any string that would become part of a URL. But this caused some annoying bugs resulting form a string being percent-escaped more then once. My solution was to create an indempotent replacement for stringByAddingPercentEscapesUsingEncoding: (I also simplified things a little by removing the encoding parameter, because I never used any encoding other then NSUTF8StringEncoding),

@implementation NSString (IndempotentPercentEscapes)
- (NSString*) stringByReplacingPercentEscapesOnce;
{
	NSString *unescaped = [self stringByReplacingPercentEscapesUsingEncoding:NSUTF8StringEncoding];
	//self may be a string that looks like an invalidly escaped string,
	//eg @"100%", in that case it clearly wasn't escaped,
	//so we return it as our unescaped string.
	return unescaped ? unescaped : self;
}

- (NSString*) stringByAddingPercentEscapesOnce;
{
	return [[self stringByReplacingPercentEscapesOnce] stringByAddingPercentEscapesUsingEncoding:NSUTF8StringEncoding];
}
@end

Usage example,

NSString *string = @"100%";
NSString *escapedOnce = [string stringByAddingPercentEscapesOnce];
NSString *escapedTwice = [escapedOnce stringByAddingPercentEscapesOnce];
NSLog(@"%@ escaped once: %@, escaped twice: %@", string, escapedOnce, escapedTwice);

100% escaped once: 100%25, escaped twice: 100%25

The paranoid have probably noticed that [aBadlyEncodedString stringByReplacingPercentEscapesOnce] will return aBadlyEncodedString not nil, This could make it harder to detect an error.

But it’s not something that I’m worried about for my application. Since I only ever use a UTF8 encoding, and it can represent any unicode character, it’s not possible to have an invalid string. But it’s certainly something to be aware of in situations where you might have strings with different encodings.

« Newer PostsOlder Posts »

Powered by WordPress