Application Development for the iPhone using Apple's official SDK.
Core Dump
I've decided to create a second blog at http://jeff-lamarche.blogspot.com where I'll put any posts that are not related to writing iOS software, Apple, or the mobile software industry.
My first substantive post should show up some time this evening if all goes well.
http://iphonedevelopment.blogspot.com
Posted on 29 August 2010 | 1:38 pm
Mea Culpa
The original version of my Briefs.app blog post contained a snarky comment about the Director of the App Review team and a link to a Wired article about his past. A friend of mine, who happens to work at One Infinite Loop, privately took me to task for including that link in my post.
It took me about two seconds, now that I'm a little further away from it and not as angry, to realize that he's 100% right. It was a dick move on my part and I'm sorry for it. I have removed the link and offer my apologies to Phillip Shoemaker. The fact is, I have no visibility into what's going on and don't know where the holdup is on Briefs.app. For all I know, Mr. Shoemaker could be valiantly fighting for Briefs.app against higher up forces in the company, so casting an ad hominem attack like that was juvenile and uncool.
My basic feelings about the matter are unchanged, however. It was uncool when Apple left Google Voice in limbo, but that was one small product from one very large company. In this case, it's the sweat equity from one individual developer who worked very, very hard, sacrificing sleep, family time, and entertainment to create something very cool. The impact is much greater and much more personal and it's hard for me not to get angry about it.
http://iphonedevelopment.blogspot.com
Posted on 28 August 2010 | 6:48 am
Briefs.app
My co-worker and all-around good guy, Rob Rhyne has officially open sourced Briefs.app as of today. After three months of being dicked around by Apple's review team, he's finally given up on getting Briefs.app onto the App Store.
Throughout the ordeal, Rob has taken the whole thing with tremendous grace and has only good things to say about the people involved in the entire process. I hope he'll forgive me for not being quite so gracious.
I'm pissed on his behalf, since he won't be. Make no mistake: This sucks. This is no way to treat anybody, but especially him. Rob has bent over backwards throughout the process to be nice and work within the system and to avoid saying anything negative about the problems he's faced. Rob has kept the discourse on a level I think few of us could manage. He didn't go out and raise a stink the way many developers have when they felt slighted by the App Review team. Rob just calmly and patiently worked within the system trying to make his case and get a product he worked on for months onto the app store… while working a full time job, starting a new business, and being a parent to a toddler. Oh, and his wife works too. Rob's one of the few developers I know who spends more time sitting at a computer than me.
If Apple's review team had just come out and rejected the app, it would have sucked, and it would have been the wrong decision, but it would have been an acceptable situation. The app review team's job is to make tough decisions. Sometimes they're going to make bad decisions, and sometimes they're going to make decisions that we developers are going to disagree with.
But since making decisions is, in fact, their job and they've never actually made a decision about this particular app, it's not an acceptable nor a forgivable situation. Three fucking months Briefs.app has sat in the review queue, and in that time, the app review team has allowed other prototyping applications onto the app store: applications that do the same basic tasks that Briefs.app was created to do. Interface was approved several weeks after Briefs.app was submitted to the App Store. LiveView and Dapp were both updated just yesterday. iMockups was approved about a week ago.
But Briefs still sits in the queue and nobody can be bothered to even say what the exact holdup is or what needs to happen before a decision will get made.
I don't know what reason they could have for dragging it on like this and not making a decision, but it's not right. Apple owes Rob, and all the people who want to use Briefs, an apology.
http://iphonedevelopment.blogspot.com
Posted on 28 August 2010 | 6:08 am
Voices that Matter Philadelphia
Just wanted to let you know that I will be speaking at the next Voices That Matter iPhone Developer Conference in Philadelphia, PA which runs from October 16 and 17th. I was originally asked to do a talk on 4.0 Multitasking, which I will be doing. I've also recently been asked to give a second talk on OpenGL ES. I've agreed to do that presentation as well, and now I have a few days to decide on the specific topic and audience and to write up the abstract.
I was thinking of doing a "from 1.1 to 2.0" kind of talk, exploring the differences between the fixed and programmable pipeline and trying to explain some of the more daunting concepts of the programmable pipeline. The goal would be to make the jump from 1.1 to 2.0 (which Apple is pushing) less scary.
I'm not sure if that's a topic with broad enough appeal, though. Are you going to VTM? What would you like to see from an OpenGL ES talk?
http://iphonedevelopment.blogspot.com
Posted on 26 August 2010 | 6:20 am
Beta Builder
A week or two ago, Jeffrey Sambells had a blog post about using Xcode's Enterprise distribution for ad hoc distribution. It's a great idea that a lot of developers saw had great potential, but it had several manual steps.
Since that post, Hunter Hillegas of Hanchor, LLC and developer of Vegas Mate, has created an application to automate the entire process. The app is called Beta Builder, and it's free. If you do much ad hoc distribution, you should check it out.
http://iphonedevelopment.blogspot.com
Posted on 25 August 2010 | 6:48 pm
App Licensing
For all those people telling me that Google's App Licensing would put a definitive stop to piracy on Android and that Apple should implement something similar, all I can say is: I told you you were wrong and here's proof, and it didn't take even as long as I said it would.
I understand Google has to address piracy because it's a bit of a black eye for the platform. They need third party developers, and a lot of third party developers are gun-shy about developing for a platform with a reputation for rampant piracy. Although the iPhone has its own problems with piracy, it's on a completely different scale. The closed nature of the platform is actually an advantage for third party developers, much the way gaming consoles are. Sure, Apple's protection scheme has been compromised and any App posted to the app store can be pirated easily. But, because only people who Jailbreak their phones can actually install the hacked software, and Jailbreaking a phone can cause problems with future updates from Apple, there's built-in damage control.
It also helps that you can buy App Store apps in every country where you can legally buy an iPhone. On Android, you can only buy apps in 13 countries, meaning people in other countries either have to do without paid apps, or have to pirate. There's built-in incentive in that system for people who might be perfectly willing to pay for an app to go pirate it. Google would get more for their piracy-battling dollar by expanding the paid markets than by implementing more hare-brained licensing schemes that won't ever make a dent in piracy.
I don't envy Tim Bray's task of having to try and reassure Android developers that this crack isn't a problem. Of course it's a problem. It's a problem the media industries have been fighting with since the dawn of digital content. The RIAA, MPAA, and media companies have invested millions, maybe even billions of dollars into various schemes that have failed to make much of a dent in piracy.
For those who think this App Licensing can ever work, you (much like the media industry) fail to grasp the inherent technical flaw with any kind of DRM. With DRM, you have to provide the legal purchaser with the content and with the key to unlock that content. No matter how sophisticated the stuff in-between, no matter how complex the lock, a sufficiently technically knowledgeable person who has the content and the key to unlocking it can find a way to free the content from its protection. On Android, once you've freed the content from its DRM, you can distribute it to anybody because of the ability to sideload applications. So on most Android phones right now, once a single copy of a program has been hacked, it's just as easy to pirate as it was without App Licensing.
With software, finding the balance between making it inconvenient to pirate (because you can't make it impossible) without overly inconveniencing your customers is hard. It's easier on a closed platform. That's not to say there aren't downsides to a closed platform — of course there are — but this is one of the clear advantages for third party developers. Truth be told, you simply can not stop the dedicated pirate, but a closed platform does deter the bulk of the pirates who can be diverted into paying customers by making it inconvenient. Best of all, it does it without affecting the legal purchasers, unlike most DRM.
When it comes down to it, the most effective way to stop piracy is to make it easy and convenient for as many people to buy content legally as is possible and to price it fairly. This is something Google clearly doesn't get, or else you'd be able to buy paid apps everywhere you can buy an Android phone.
http://iphonedevelopment.blogspot.com
Posted on 25 August 2010 | 6:32 am
UIImage-Blur
Many moons ago, I wrote convolution kernel for Cocoa. It had convenience class functions to do many different types of filters, including blurring, embossing, outlining, edge detection, horizontal shift, LaPlacian, soften, high pass, etc. Now, this was before Core Image and long before the switch to Intel. I don't remember exactly when I first wrote it, but I'm guessing it was around 2001 or 2002. The method that actually applied the filter to an image used AltiVec if it was available, and if it wasn't, it did a brute force filter on the CPU.
Of course, once the switch to Intel happened, the AltiVec code was no longer was helpful, and then Apple came out with Core Image which includesda convolution kernel and all of the filter settings I had created and more. So, I stopped maintaining the code.
Then, when the iPhone came out and didn't have Accelerate or Core Image, I saw a potential use for the old source code. I had a project early on where I needed to be able to blur an image. So, I blew the dust off the old code. I didn't convert the entire convolution kernel – I didn't want to go through the effort if it wasn't going to work — so I created a blur category on UIImage. And it didn't work.
Pressed for time, I found another solution because I was uncertain the processor on the original iPhone would be able to apply a convolution kernel fast enough for my purposes, but I included the broken code when I released the source code for replicating the Urban Spoon effect. Today, I received an e-mail from a reader who figured out my rather boneheaded mistake. The convolution filter was fine, I just specified the wrong CGImage when specifying my provider while converting the byte data back to a CGImage.
Now, since I wrote that code, Apple has come out with the Accelerate framework for iOS, so it could definitely be made faster. It's unlikely that I will be able to spend the time converting it to use Accelerate unless I need a convolution kernel for my own client work; I've got too much on my plate right now to tackle it. If anyone's interested in doing the full convolution kernel port, you can check out the source code to Crimson FX. It's an old Cocoa project which may not work anymore, but it has, I believe, the last version of the convolution kernel before I gave up maintaining it. It shouldn't be hard to port the entire convolution kernel to iOS in the same manner. Once you get to the underlying byte data, the process is exactly 100% the same (even if byte order is different), and the code to convert to and from the byte data is this UIImage-Blur category.
So, without further ado, I've created a small scaffold project to hold the unaccelerated UIImage-Blur category. Have fun with it and let me know if you use it in anything interesting. If you improve it and would like to share your improvements, let me know, and I'll post it here.
You can find the source code right here. Here's a screenshot of the test scaffold with the original image and after several blurs have been applied. The image is from the Library of Congress Prints and Photographs Collection. Plattsburgh is where I grew up, so this public domain image struck my fancy. I don't know why, but the Army Base was spelled Plattsburg without the ending 'h' even though the city has always been Plattsburgh with the ending 'h'.
Thanks to Anthony Gonsalves for finding my error!
http://iphonedevelopment.blogspot.com
Posted on 24 August 2010 | 7:57 am
Transparent Grouped Tableviews
I ran into a problem today with a transparent grouped table view. On the table view, I set opaque to NO and set the background color to clearColor so that an image behind the table would show through:
I can't show you the actual app I was working on because I'm under NDA, but I've created a sample project that shows the problem using a really ugly background image. Notice the black around the corners of the group sections:
It took me a while to figure out why this was happening, but with a little crowd-source debugging thanks to Twitter, I've narrowed the culprit down. Even though you can clearly see in the first picture that the table view's background color has been set to clearColor in Interface Builder, that setting is either not being respected, or is being changed somewhere. Now, I'm using stock elements here - no custom views at all - so I know I'm not changing it.
My original fix for this was rather involved, but after a process of elimination where I kept commenting out bits of the fix and and seeing what impact it had, I was able to remove all of the code of my fix except for this line of code:
- (void)viewDidLoad
{
[super viewDidLoad];
self.tableView.backgroundColor = [UIColor clearColor];
}Turns out all the other parts were red herrings leftover from my various attempts to fix the problem. So, if you're having this problem, just manually re-set the table view's background color to clearColor and you should be good.
(and yes, I'm going to file a bug report now)
http://iphonedevelopment.blogspot.com
Posted on 19 August 2010 | 11:11 am
Core Data Starting Data
Yesterday, I tweeted asking for advice about the best way to provide a starter set of data in a Core Data-based application. The app I'm working on had started with just a small set of starter data, so the first time the app was run, I simply pulled in that starter data from a plist in the background and created managed objects based on the contents and all was good. The user never noticed it and the load was complete before they needed to do anything with the data.
Then the data set got quite a bit larger and that solution became too slow. So, I asked the Twitterverse for opinions about the best way to provide a large amount of starting data in a Core Data app. What I was hoping to find out was whether you could include a persistent store in your application bundle and use that instead of creating new persistent store the first time the app was launched.
The answer came back from lots and lots of people that you could, indeed, copy an existing persistent store from your app bundle. You could even create the persistent store using a Mac Cocoa Core Data application as long as it used the same xcdatamodel file as your iPhone app.
Before I go on, I want to thank all the people who responded with suggestions and advice. A special thanks to Dan Pasco from the excellent dev shop Black Pixel who gave very substantive assistance. With the help of the iOS dev community, it took me about 15 minutes to get this running in one of my current apps. Several people have asked for the details over Twitter. 140 characters isn't going to cut it for this, but here's what I did.
First, I created a new Mac / Cocoa project in Xcode. I used the regular Cocoa Application template, making sure to use Core Data. Several people also suggested that you could use a Document-Based Cocoa Application using Core DAta which would allow you to save the persistent store anywhere you wanted. I create the Xcode project in a subfolder of my main project folder and I added the data model file and all the managed object classes from my main project to the new project using project-relative references, making sure NOT to copy the files into the new project folder - I want to use the same files as my original project so any changes made in one are reflected in the other.
If my starting data was changing frequently, I'd probably make this new project a dependency of my main project and add a copy files build phase that would copy the persistent store into the main project's Resources folder, but my data isn't changing very often, so I'm just doing it manually. You definitely can automate the task within Xcode, and I heard from several people who have done so.
In the new Cocoa project, the first thing to do is modify the persistentStoreCoordinator method of the app delegate so it uses a persistent store with the same name as your iOS app. This is the line of code you need to modify:
NSURL *url = [NSURL fileURLWithPath: [applicationSupportDirectory stringByAppendingPathComponent: @"My_App_Name.sqlite"]];Make sure you add the .sqlite extension to the filename. By default, the Cocoa Application template uses an XML datastore and no file extension. The filename you enter here is used exactly, so if you want a file extension, you have to specify it.
Since the Cocoa Application project defaults to an XML-based persistent store, you also need to change the Cocoa App's store type to SQLite. That's a few lines later. Look for this line:
if (![persistentStoreCoordinator addPersistentStoreWithType:NSXMLStoreType
configuration:nil
URL:url
options:nil
error:&error]){Change NSXMLStoreType to NSSQLiteStoreType.
Optionally, you can also change the applicationSupportDirectory method to return a different location if you want to make the persistent store easier to find. By default, it's going to go in
~/Library/Application Support/[Cocoa App Name]/which can be a bit of a drag to navigate to.
Next, you need to do your data import. This code will inherently be application-specific and will depend on you data model and the data you need to import. Here's a simple pseudo-method for parsing a tab-delimited text file to give an idea what this might look like. This method creates an NSAutoreleasePool and a context so it can be launched in a thread if you desire. You can also call it directly - it won't hurt anything.
- (void)loadInitialData
{
NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
NSManagedObjectContext *context = [[NSManagedObjectContext alloc] init];
[context setPersistentStoreCoordinator:self.persistentStoreCoordinator];
NSString *path = [[NSBundle mainBundle] pathForResource:@"foo" ofType:@"txt"];
char buffer[1000];
FILE* file = fopen([path UTF8String], "r");
while(fgets(buffer, 1000, file) != NULL)
{
NSString* string = [[NSString alloc] initWithUTF8String:buffer];
NSArray *parts = [string componentsSeparatedByString:@"\t"]
MyManagedObjectClass *oneObject = [self methodToCreateObjectFromArray:parts];
[string release];
}
NSLog(@"Done initial load");
fclose(file);
NSError *error;
if (![context save:&error])
NSLog(@"Error saving: %@", error);
[context release];
[pool drain];
}You can add the delegate method applicationDidFinishLaunching: to your app delegate and put your code there. You don't even really need to worry about how long it takes - there's no watchdog process on the Mac that kills your app if it isn't responding to events after a period of time. If you prefer, you can have your data import functionality working in the background, though since the app does nothing else, there's no real benefit except the fact that it's the "right" way to code an application.
- (void)applicationDidFinishLaunching:(NSNotification *)aNotification
{
// Do import or launch threads to do import
// So, do this:
[self loadInitialData];
// or this:
[self performSelectorInBackground:@selector(loadInitialData) withObject:nil];
// But not both for the same method probably
}Now, run your app. When it's done importing, you can just copy the persistent store file into your iOS app's Xcode project in the Resources group. When you build your app, this file will automatically get copied into your application's bundle. Now, you just need to modify the app delegate of your iOS application to use this persistent store instead of creating a new, empty persistent store the first time the app is run.
To do that, you simply check for the existence of the persistent store in your application's /Documents folder, and if it's not there, you copy it from the application bundle to the the /Documents folder before creating the persistent store. In the app delegate, the persistentStoreCoordinator method should look something like this:
- (NSPersistentStoreCoordinator *)persistentStoreCoordinator
{
@synchronized (self)
{
if (persistentStoreCoordinator != nil)
return persistentStoreCoordinator;
NSString *defaultStorePath = [[NSBundle bundleForClass:[self class]] pathForResource:@"My_App_Name" ofType:@"sqlite"];
NSString *storePath = [[self applicationDocumentsDirectory] stringByAppendingPathComponent: @"My_App_Name.sqlite"];
NSError *error;
if (![[NSFileManager defaultManager] fileExistsAtPath:storePath])
{
if ([[NSFileManager defaultManager] copyItemAtPath:defaultStorePath toPath:storePath error:&error])
NSLog(@"Copied starting data to %@", storePath);
else
NSLog(@"Error copying default DB to %@ (%@)", storePath, error);
}
NSURL *storeURL = [NSURL fileURLWithPath:storePath];
persistentStoreCoordinator = [[NSPersistentStoreCoordinator alloc] initWithManagedObjectModel:[self managedObjectModel]];
NSDictionary *options = [NSDictionary dictionaryWithObjectsAndKeys:
[NSNumber numberWithBool:YES], NSMigratePersistentStoresAutomaticallyOption,
[NSNumber numberWithBool:YES], NSInferMappingModelAutomaticallyOption, nil];
if (![persistentStoreCoordinator addPersistentStoreWithType:NSSQLiteStoreType configuration:nil URL:storeURL options:options error:&error])
{
NSLog(@"Unresolved error %@, %@", error, [error userInfo]);
abort();
}
return persistentStoreCoordinator;
}
}Et voilà! If you run the app for the first time on a device, or run it on the simulator after resetting content and settings, you should start with the data that was loaded in your Cocoa application.
http://iphonedevelopment.blogspot.com
Posted on 19 August 2010 | 8:56 am
The Funny Thing About Old Code…
The funny thing about old code, and most new code, is that there's usually room for improvement. Shortly after posting NSString appendToFile:usingEncoding:, I realized I could've kill two birds with one stone by adding a category method on NSData and then calling that method from the NSString category method. That way I'd get the functionality on both classes without repeating logic.
Without further ado:
#import <Foundation/Foundation.h>
@interface NSData(MCFileAppend)
- (BOOL)appendToFile:(NSString *)path;
@end
@implementation NSData(MCFileAppend)
- (BOOL)appendToFile:(NSString *)path
{
NSFileHandle *fh = [NSFileHandle fileHandleForWritingAtPath:path];
if (fh == nil)
return [self writeToFile:path atomically:YES];
[fh truncateFileAtOffset:[fh seekToEndOfFile]];
[fh writeData:self];
[fh closeFile];
return YES;
}
@end
#pragma mark -
@interface NSString(MCFileAppend)
- (BOOL)appendToFile:(NSString *)path usingEncoding:(NSStringEncoding)encoding;
@end
@implementation NSString(MCFileAppend)
- (BOOL)appendToFile:(NSString *)path usingEncoding:(NSStringEncoding)encoding
{
NSData *encoded = [self dataUsingEncoding:encoding];
return [encoded appendToFile:path];
}
@endhttp://iphonedevelopment.blogspot.com
Posted on 18 August 2010 | 5:50 pm
NSString appendToFile:usingEncoding:
Somewhat frequently I find myself looking in Apple's documentation for a method that I'm sure exists but doesn't show up when I search using the Documentation Viewer in Xcode. Usually in that case, the method does actually exist, but it's not from Apple. Nine out of ten times it means I've got a category method I've used so many times that I've forgotten Apple didn't provide it. The other one out of ten, it's just an indication that I'm slowly growing senile.
When this happens, I try to post the category methods because I figure if I've used the method enough over the years that I though it came from Apple, then other people might be able to use it.
One bit of functionality that I've always been surprised isn't in NSString class is the ability to append the contents of a string onto an existing file. There are methods for creating or replacing a file with the contents of a string, so why not to append the contents onto an existing file? It's a fairly common and very useful task. I've used it for creating application logs, and for other situations where I need to create large files that wouldn't be practical to keep in memory. This is especially nice to have on iOS given the lack of virtual memory.
Here's the category method I use for this. I dug this up yesterday from an older project after searching for it in Apple's documentation. I had to update it a little (it was old enough that it used the old deprecated cString methods.
#import <Foundation/Foundation.h>
@interface NSString(MCFileAppend)
- (BOOL)appendToFile:(NSString *)path usingEncoding:(NSStringEncoding)encoding;
@end
@implementation NSString(MCFileAppend)
- (BOOL)appendToFile:(NSString *)path usingEncoding:(NSStringEncoding)encoding
{
NSFileHandle *fh = [NSFileHandle fileHandleForWritingAtPath:path];
if (fh == nil)
return [self writeToFile:path atomically:YES encoding:encoding error:nil];
[fh truncateFileAtOffset:[fh seekToEndOfFile]];
NSData *encoded = [self dataUsingEncoding:encoding];
if (encoded == nil) return NO;
[fh writeData:encoded];
return YES;
}
@endhttp://iphonedevelopment.blogspot.com
Posted on 18 August 2010 | 9:07 am
Fonts and Font Families
I'm not sure if Apple publishes an official list of supported fonts on iOS, but when working with designers, it's a questions that we developers often need the answer too. Fortunately, it's easy enough to determine it. In fact, the code to generate the list of fonts is the first (and so far only) thing that I've put into Xcode 4's new snippet feature. I'm generally not a big snippet fan. If I use code enough that I feel a need to have it as a snippet, more often than not, it makes sense to encapsulate that functionality generically in a class or category or as part of a common library.
But, there are definitely pieces of code that it's good to have around and that don't really lend themselves to that. Here's my snippet for generating font and family names. Cherish this; I slaved for hours over it. Well, okay… maybe not hours, but it did take me a minute or two to type it in.
for (NSString *family in [UIFont familyNames])
{
NSLog(@"%@", family);
for (NSString *font in [UIFont fontNamesForFamilyName:family])
{
NSLog(@"\t%@", font);
}
}The result of running this code on the current version of iOS 4 (4.0.2 / 8A400) follows. It's a handy list to provide the designers you work with so they know if they use a font not on the list, you'll have to substitute something else, or whoever's footing the bill will have to cough up potentially expensive font licensing fees.
If you want to see what the fonts actually look like, Michael Critz has put together this nice page that shows them all
Arial Hebrew
ArialHebrew-Bold
ArialHebrew
Zapfino
Zapfino
Oriya Sangam MN
OriyaSangamMN
OriyaSangamMN-Bold
Cochin
Cochin
Cochin-BoldItalic
Cochin-Italic
Cochin-Bold
Baskerville
Baskerville
Baskerville-BoldItalic
Baskerville-Italic
Baskerville-Bold
Verdana
Verdana-Bold
Verdana-BoldItalic
Verdana-Italic
Verdana
Gurmukhi MN
GurmukhiMN
GurmukhiMN-Bold
Palatino
Palatino-BoldItalic
Palatino-Italic
Palatino-Bold
Palatino-Roman
Tamil Sangam MN
TamilSangamMN-Bold
TamilSangamMN
Marker Felt
MarkerFelt-Wide
MarkerFelt-Thin
Courier New
CourierNewPS-BoldMT
CourierNewPS-ItalicMT
CourierNewPS-BoldItalicMT
CourierNewPSMT
Courier
Courier-Oblique
Courier
Courier-Bold
Courier-BoldOblique
DB LCD Temp
DBLCDTempBlack
Trebuchet MS
TrebuchetMS-Italic
Trebuchet-BoldItalic
TrebuchetMS
TrebuchetMS-Bold
Arial Rounded MT Bold
ArialRoundedMTBold
Bangla Sangam MN
BanglaSangamMN
BanglaSangamMN-Bold
Telugu Sangam MN
TeluguSangamMN
TeluguSangamMN-Bold
American Typewriter
AmericanTypewriter-Bold
AmericanTypewriter
Arial
ArialMT
Arial-ItalicMT
Arial-BoldMT
Arial-BoldItalicMT
Hiragino Kaku Gothic ProN
HiraKakuProN-W3
HiraKakuProN-W6
AppleGothic
AppleGothic
Heiti SC
STHeitiSC-Light
STHeitiSC-Medium
Malayalam Sangam MN
MalayalamSangamMN-Bold
MalayalamSangamMN
Thonburi
Thonburi
Thonburi-Bold
Helvetica
Helvetica-BoldOblique
Helvetica
Helvetica-Oblique
Helvetica-Bold
Futura
Futura-CondensedExtraBold
Futura-MediumItalic
Futura-Medium
Gujarati Sangam MN
GujaratiSangamMN-Bold
GujaratiSangamMN
Heiti K
STHeitiK-Medium
STHeitiK-Light
Devanagari Sangam MN
DevanagariSangamMN-Bold
DevanagariSangamMN
Heiti TC
STHeitiTC-Light
STHeitiTC-Medium
Sinhala Sangam MN
SinhalaSangamMN
SinhalaSangamMN-Bold
Kannada Sangam MN
KannadaSangamMN-Bold
KannadaSangamMN
Georgia
Georgia-BoldItalic
Georgia-Italic
Georgia-Bold
Georgia
Heiti J
STHeitiJ-Medium
STHeitiJ-Light
Times New Roman
TimesNewRomanPS-BoldMT
TimesNewRomanPS-ItalicMT
TimesNewRomanPS-BoldItalicMT
TimesNewRomanPSMT
Geeza Pro
GeezaPro
GeezaPro-Bold
Helvetica Neue
HelveticaNeue-Bold
HelveticaNeue
Arial Hebrew
ArialHebrew-Bold
ArialHebrew
Zapfino
Zapfino
Oriya Sangam MN
OriyaSangamMN
OriyaSangamMN-Bold
Cochin
Cochin
Cochin-BoldItalic
Cochin-Italic
Cochin-Bold
Baskerville
Baskerville
Baskerville-BoldItalic
Baskerville-Italic
Baskerville-Bold
Verdana
Verdana-Bold
Verdana-BoldItalic
Verdana-Italic
Verdana
Gurmukhi MN
GurmukhiMN
GurmukhiMN-Bold
Palatino
Palatino-BoldItalic
Palatino-Italic
Palatino-Bold
Palatino-Roman
Tamil Sangam MN
TamilSangamMN-Bold
TamilSangamMN
Marker Felt
MarkerFelt-Wide
MarkerFelt-Thin
Courier New
CourierNewPS-BoldMT
CourierNewPS-ItalicMT
CourierNewPS-BoldItalicMT
CourierNewPSMT
Courier
Courier-Oblique
Courier
Courier-Bold
Courier-BoldOblique
DB LCD Temp
DBLCDTempBlack
Trebuchet MS
TrebuchetMS-Italic
Trebuchet-BoldItalic
TrebuchetMS
TrebuchetMS-Bold
Arial Rounded MT Bold
ArialRoundedMTBold
Bangla Sangam MN
BanglaSangamMN
BanglaSangamMN-Bold
Telugu Sangam MN
TeluguSangamMN
TeluguSangamMN-Bold
American Typewriter
AmericanTypewriter-Bold
AmericanTypewriter
Arial
ArialMT
Arial-ItalicMT
Arial-BoldMT
Arial-BoldItalicMT
Hiragino Kaku Gothic ProN
HiraKakuProN-W3
HiraKakuProN-W6
AppleGothic
AppleGothic
Heiti SC
STHeitiSC-Light
STHeitiSC-Medium
Malayalam Sangam MN
MalayalamSangamMN-Bold
MalayalamSangamMN
Thonburi
Thonburi
Thonburi-Bold
Helvetica
Helvetica-BoldOblique
Helvetica
Helvetica-Oblique
Helvetica-Bold
Futura
Futura-CondensedExtraBold
Futura-MediumItalic
Futura-Medium
Gujarati Sangam MN
GujaratiSangamMN-Bold
GujaratiSangamMN
Heiti K
STHeitiK-Medium
STHeitiK-Light
Devanagari Sangam MN
DevanagariSangamMN-Bold
DevanagariSangamMN
Heiti TC
STHeitiTC-Light
STHeitiTC-Medium
Sinhala Sangam MN
SinhalaSangamMN
SinhalaSangamMN-Bold
Kannada Sangam MN
KannadaSangamMN-Bold
KannadaSangamMN
Georgia
Georgia-BoldItalic
Georgia-Italic
Georgia-Bold
Georgia
Heiti J
STHeitiJ-Medium
STHeitiJ-Light
Times New Roman
TimesNewRomanPS-BoldMT
TimesNewRomanPS-ItalicMT
TimesNewRomanPS-BoldItalicMT
TimesNewRomanPSMT
Geeza Pro
GeezaPro
GeezaPro-Bold
Helvetica Neue
HelveticaNeue-Bold
HelveticaNeue
Arial Hebrew
ArialHebrew-Bold
ArialHebrew
Zapfino
Zapfino
Oriya Sangam MN
OriyaSangamMN
OriyaSangamMN-Bold
Cochin
Cochin
Cochin-BoldItalic
Cochin-Italic
Cochin-Bold
Baskerville
Baskerville
Baskerville-BoldItalic
Baskerville-Italic
Baskerville-Bold
Verdana
Verdana-Bold
Verdana-BoldItalic
Verdana-Italic
Verdana
Gurmukhi MN
GurmukhiMN
GurmukhiMN-Bold
Palatino
Palatino-BoldItalic
Palatino-Italic
Palatino-Bold
Palatino-Roman
Tamil Sangam MN
TamilSangamMN-Bold
TamilSangamMN
Marker Felt
MarkerFelt-Wide
MarkerFelt-Thin
Courier New
CourierNewPS-BoldMT
CourierNewPS-ItalicMT
CourierNewPS-BoldItalicMT
CourierNewPSMT
Courier
Courier-Oblique
Courier
Courier-Bold
Courier-BoldOblique
DB LCD Temp
DBLCDTempBlack
Trebuchet MS
TrebuchetMS-Italic
Trebuchet-BoldItalic
TrebuchetMS
TrebuchetMS-Bold
Arial Rounded MT Bold
ArialRoundedMTBold
Bangla Sangam MN
BanglaSangamMN
BanglaSangamMN-Bold
Telugu Sangam MN
TeluguSangamMN
TeluguSangamMN-Bold
American Typewriter
AmericanTypewriter-Bold
AmericanTypewriter
Arial
ArialMT
Arial-ItalicMT
Arial-BoldMT
Arial-BoldItalicMT
Hiragino Kaku Gothic ProN
HiraKakuProN-W3
HiraKakuProN-W6
AppleGothic
AppleGothic
Heiti SC
STHeitiSC-Light
STHeitiSC-Medium
Malayalam Sangam MN
MalayalamSangamMN-Bold
MalayalamSangamMN
Thonburi
Thonburi
Thonburi-Bold
Helvetica
Helvetica-BoldOblique
Helvetica
Helvetica-Oblique
Helvetica-Bold
Futura
Futura-CondensedExtraBold
Futura-MediumItalic
Futura-Medium
Gujarati Sangam MN
GujaratiSangamMN-Bold
GujaratiSangamMN
Heiti K
STHeitiK-Medium
STHeitiK-Light
Devanagari Sangam MN
DevanagariSangamMN-Bold
DevanagariSangamMN
Heiti TC
STHeitiTC-Light
STHeitiTC-Medium
Sinhala Sangam MN
SinhalaSangamMN
SinhalaSangamMN-Bold
Kannada Sangam MN
KannadaSangamMN-Bold
KannadaSangamMN
Georgia
Georgia-BoldItalic
Georgia-Italic
Georgia-Bold
Georgia
Heiti J
STHeitiJ-Medium
STHeitiJ-Light
Times New Roman
TimesNewRomanPS-BoldMT
TimesNewRomanPS-ItalicMT
TimesNewRomanPS-BoldItalicMT
TimesNewRomanPSMT
Geeza Pro
GeezaPro
GeezaPro-Bold
Helvetica Neue
HelveticaNeue-Bold
HelveticaNeue
Arial Hebrew
ArialHebrew-Bold
ArialHebrew
Zapfino
Zapfino
Oriya Sangam MN
OriyaSangamMN
OriyaSangamMN-Bold
Cochin
Cochin
Cochin-BoldItalic
Cochin-Italic
Cochin-Bold
Baskerville
Baskerville
Baskerville-BoldItalic
Baskerville-Italic
Baskerville-Bold
Verdana
Verdana-Bold
Verdana-BoldItalic
Verdana-Italic
Verdana
Gurmukhi MN
GurmukhiMN
GurmukhiMN-Bold
Palatino
Palatino-BoldItalic
Palatino-Italic
Palatino-Bold
Palatino-Roman
Tamil Sangam MN
TamilSangamMN-Bold
TamilSangamMN
Marker Felt
MarkerFelt-Wide
MarkerFelt-Thin
Courier New
CourierNewPS-BoldMT
CourierNewPS-ItalicMT
CourierNewPS-BoldItalicMT
CourierNewPSMT
Courier
Courier-Oblique
Courier
Courier-Bold
Courier-BoldOblique
DB LCD Temp
DBLCDTempBlack
Trebuchet MS
TrebuchetMS-Italic
Trebuchet-BoldItalic
TrebuchetMS
TrebuchetMS-Bold
Arial Rounded MT Bold
ArialRoundedMTBold
Bangla Sangam MN
BanglaSangamMN
BanglaSangamMN-Bold
Telugu Sangam MN
TeluguSangamMN
TeluguSangamMN-Bold
American Typewriter
AmericanTypewriter-Bold
AmericanTypewriter
Arial
ArialMT
Arial-ItalicMT
Arial-BoldMT
Arial-BoldItalicMT
Hiragino Kaku Gothic ProN
HiraKakuProN-W3
HiraKakuProN-W6
AppleGothic
AppleGothic
Heiti SC
STHeitiSC-Light
STHeitiSC-Medium
Malayalam Sangam MN
MalayalamSangamMN-Bold
MalayalamSangamMN
Thonburi
Thonburi
Thonburi-Bold
Helvetica
Helvetica-BoldOblique
Helvetica
Helvetica-Oblique
Helvetica-Bold
Futura
Futura-CondensedExtraBold
Futura-MediumItalic
Futura-Medium
Gujarati Sangam MN
GujaratiSangamMN-Bold
GujaratiSangamMN
Heiti K
STHeitiK-Medium
STHeitiK-Light
Devanagari Sangam MN
DevanagariSangamMN-Bold
DevanagariSangamMN
Heiti TC
STHeitiTC-Light
STHeitiTC-Medium
Sinhala Sangam MN
SinhalaSangamMN
SinhalaSangamMN-Bold
Kannada Sangam MN
KannadaSangamMN-Bold
KannadaSangamMN
Georgia
Georgia-BoldItalic
Georgia-Italic
Georgia-Bold
Georgia
Heiti J
STHeitiJ-Medium
STHeitiJ-Light
Times New Roman
TimesNewRomanPS-BoldMT
TimesNewRomanPS-ItalicMT
TimesNewRomanPS-BoldItalicMT
TimesNewRomanPSMT
Geeza Pro
GeezaPro
GeezaPro-Bold
Helvetica Neue
HelveticaNeue-Bold
HelveticaNeue
Arial Hebrew
ArialHebrew-Bold
ArialHebrew
Zapfino
Zapfino
Oriya Sangam MN
OriyaSangamMN
OriyaSangamMN-Bold
Cochin
Cochin
Cochin-BoldItalic
Cochin-Italic
Cochin-Bold
Baskerville
Baskerville
Baskerville-BoldItalic
Baskerville-Italic
Baskerville-Bold
Verdana
Verdana-Bold
Verdana-BoldItalic
Verdana-Italic
Verdana
Gurmukhi MN
GurmukhiMN
GurmukhiMN-Bold
Palatino
Palatino-BoldItalic
Palatino-Italic
Palatino-Bold
Palatino-Roman
Tamil Sangam MN
TamilSangamMN-Bold
TamilSangamMN
Marker Felt
MarkerFelt-Wide
MarkerFelt-Thin
Courier New
CourierNewPS-BoldMT
CourierNewPS-ItalicMT
CourierNewPS-BoldItalicMT
CourierNewPSMT
Courier
Courier-Oblique
Courier
Courier-Bold
Courier-BoldOblique
DB LCD Temp
DBLCDTempBlack
Trebuchet MS
TrebuchetMS-Italic
Trebuchet-BoldItalic
TrebuchetMS
TrebuchetMS-Bold
Arial Rounded MT Bold
ArialRoundedMTBold
Bangla Sangam MN
BanglaSangamMN
BanglaSangamMN-Bold
Telugu Sangam MN
TeluguSangamMN
TeluguSangamMN-Bold
American Typewriter
AmericanTypewriter-Bold
AmericanTypewriter
Arial
ArialMT
Arial-ItalicMT
Arial-BoldMT
Arial-BoldItalicMT
Hiragino Kaku Gothic ProN
HiraKakuProN-W3
HiraKakuProN-W6
AppleGothic
AppleGothic
Heiti SC
STHeitiSC-Light
STHeitiSC-Medium
Malayalam Sangam MN
MalayalamSangamMN-Bold
MalayalamSangamMN
Thonburi
Thonburi
Thonburi-Bold
Helvetica
Helvetica-BoldOblique
Helvetica
Helvetica-Oblique
Helvetica-Bold
Futura
Futura-CondensedExtraBold
Futura-MediumItalic
Futura-Medium
Gujarati Sangam MN
GujaratiSangamMN-Bold
GujaratiSangamMN
Heiti K
STHeitiK-Medium
STHeitiK-Light
Devanagari Sangam MN
DevanagariSangamMN-Bold
DevanagariSangamMN
Heiti TC
STHeitiTC-Light
STHeitiTC-Medium
Sinhala Sangam MN
SinhalaSangamMN
SinhalaSangamMN-Bold
Kannada Sangam MN
KannadaSangamMN-Bold
KannadaSangamMN
Georgia
Georgia-BoldItalic
Georgia-Italic
Georgia-Bold
Georgia
Heiti J
STHeitiJ-Medium
STHeitiJ-Light
Times New Roman
TimesNewRomanPS-BoldMT
TimesNewRomanPS-ItalicMT
TimesNewRomanPS-BoldItalicMT
TimesNewRomanPSMT
Geeza Pro
GeezaPro
GeezaPro-Bold
Helvetica Neue
HelveticaNeue-Bold
HelveticaNeue
Arial Hebrew
ArialHebrew-Bold
ArialHebrew
Zapfino
Zapfino
Oriya Sangam MN
OriyaSangamMN
OriyaSangamMN-Bold
Cochin
Cochin
Cochin-BoldItalic
Cochin-Italic
Cochin-Bold
Baskerville
Baskerville
Baskerville-BoldItalic
Baskerville-Italic
Baskerville-Bold
Verdana
Verdana-Bold
Verdana-BoldItalic
Verdana-Italic
Verdana
Gurmukhi MN
GurmukhiMN
GurmukhiMN-Bold
Palatino
Palatino-BoldItalic
Palatino-Italic
Palatino-Bold
Palatino-Roman
Tamil Sangam MN
TamilSangamMN-Bold
TamilSangamMN
Marker Felt
MarkerFelt-Wide
MarkerFelt-Thin
Courier New
CourierNewPS-BoldMT
CourierNewPS-ItalicMT
CourierNewPS-BoldItalicMT
CourierNewPSMT
Courier
Courier-Oblique
Courier
Courier-Bold
Courier-BoldOblique
DB LCD Temp
DBLCDTempBlack
Trebuchet MS
TrebuchetMS-Italic
Trebuchet-BoldItalic
TrebuchetMS
TrebuchetMS-Bold
Arial Rounded MT Bold
ArialRoundedMTBold
Bangla Sangam MN
BanglaSangamMN
BanglaSangamMN-Bold
Telugu Sangam MN
TeluguSangamMN
TeluguSangamMN-Bold
American Typewriter
AmericanTypewriter-Bold
AmericanTypewriter
Arial
ArialMT
Arial-ItalicMT
Arial-BoldMT
Arial-BoldItalicMT
Hiragino Kaku Gothic ProN
HiraKakuProN-W3
HiraKakuProN-W6
AppleGothic
AppleGothic
Heiti SC
STHeitiSC-Light
STHeitiSC-Medium
Malayalam Sangam MN
MalayalamSangamMN-Bold
MalayalamSangamMN
Thonburi
Thonburi
Thonburi-Bold
Helvetica
Helvetica-BoldOblique
Helvetica
Helvetica-Oblique
Helvetica-Bold
Futura
Futura-CondensedExtraBold
Futura-MediumItalic
Futura-Medium
Gujarati Sangam MN
GujaratiSangamMN-Bold
GujaratiSangamMN
Heiti K
STHeitiK-Medium
STHeitiK-Light
Devanagari Sangam MN
DevanagariSangamMN-Bold
DevanagariSangamMN
Heiti TC
STHeitiTC-Light
STHeitiTC-Medium
Sinhala Sangam MN
SinhalaSangamMN
SinhalaSangamMN-Bold
Kannada Sangam MN
KannadaSangamMN-Bold
KannadaSangamMN
Georgia
Georgia-BoldItalic
Georgia-Italic
Georgia-Bold
Georgia
Heiti J
STHeitiJ-Medium
STHeitiJ-Light
Times New Roman
TimesNewRomanPS-BoldMT
TimesNewRomanPS-ItalicMT
TimesNewRomanPS-BoldItalicMT
TimesNewRomanPSMT
Geeza Pro
GeezaPro
GeezaPro-Bold
Helvetica Neue
HelveticaNeue-Bold
HelveticaNeue
Arial Hebrew
ArialHebrew-Bold
ArialHebrew
Zapfino
Zapfino
Oriya Sangam MN
OriyaSangamMN
OriyaSangamMN-Bold
Cochin
Cochin
Cochin-BoldItalic
Cochin-Italic
Cochin-Bold
Baskerville
Baskerville
Baskerville-BoldItalic
Baskerville-Italic
Baskerville-Bold
Verdana
Verdana-Bold
Verdana-BoldItalic
Verdana-Italic
Verdana
Gurmukhi MN
GurmukhiMN
GurmukhiMN-Bold
Palatino
Palatino-BoldItalic
Palatino-Italic
Palatino-Bold
Palatino-Roman
Tamil Sangam MN
TamilSangamMN-Bold
TamilSangamMN
Marker Felt
MarkerFelt-Wide
MarkerFelt-Thin
Courier New
CourierNewPS-BoldMT
CourierNewPS-ItalicMT
CourierNewPS-BoldItalicMT
CourierNewPSMT
Courier
Courier-Oblique
Courier
Courier-Bold
Courier-BoldOblique
DB LCD Temp
DBLCDTempBlack
Trebuchet MS
TrebuchetMS-Italic
Trebuchet-BoldItalic
TrebuchetMS
TrebuchetMS-Bold
Arial Rounded MT Bold
ArialRoundedMTBold
Bangla Sangam MN
BanglaSangamMN
BanglaSangamMN-Bold
Telugu Sangam MN
TeluguSangamMN
TeluguSangamMN-Bold
American Typewriter
AmericanTypewriter-Bold
AmericanTypewriter
Arial
ArialMT
Arial-ItalicMT
Arial-BoldMT
Arial-BoldItalicMT
Hiragino Kaku Gothic ProN
HiraKakuProN-W3
HiraKakuProN-W6
AppleGothic
AppleGothic
Heiti SC
STHeitiSC-Light
STHeitiSC-Medium
Malayalam Sangam MN
MalayalamSangamMN-Bold
MalayalamSangamMN
Thonburi
Thonburi
Thonburi-Bold
Helvetica
Helvetica-BoldOblique
Helvetica
Helvetica-Oblique
Helvetica-Bold
Futura
Futura-CondensedExtraBold
Futura-MediumItalic
Futura-Medium
Gujarati Sangam MN
GujaratiSangamMN-Bold
GujaratiSangamMN
Heiti K
STHeitiK-Medium
STHeitiK-Light
Devanagari Sangam MN
DevanagariSangamMN-Bold
DevanagariSangamMN
Heiti TC
STHeitiTC-Light
STHeitiTC-Medium
Sinhala Sangam MN
SinhalaSangamMN
SinhalaSangamMN-Bold
Kannada Sangam MN
KannadaSangamMN-Bold
KannadaSangamMN
Georgia
Georgia-BoldItalic
Georgia-Italic
Georgia-Bold
Georgia
Heiti J
STHeitiJ-Medium
STHeitiJ-Light
Times New Roman
TimesNewRomanPS-BoldMT
TimesNewRomanPS-ItalicMT
TimesNewRomanPS-BoldItalicMT
TimesNewRomanPSMT
Geeza Pro
GeezaPro
GeezaPro-Bold
Helvetica Neue
HelveticaNeue-Bold
HelveticaNeue
http://iphonedevelopment.blogspot.com
Posted on 16 August 2010 | 6:15 pm
Blender 2.5 Objective-C Export Script
Paul Horalek has taken my old Blender 2.49 script for exporting 3D objects to Objective-C header files and has ported it to the new Blender 2.5 Python API. I'd been meaning to do this for quite some time, as I've been using Blender 2.5 since the early alpha releases, but never found time. Now I don't have to.
You can find the new version of the script right here.
Thanks, Paul!
http://iphonedevelopment.blogspot.com
Posted on 11 August 2010 | 1:32 pm
NSOperation File Template Notes
I've had a few questions about the NSOperation subclass file template I posted earlier. Instead of individually responding, I thought I'd clarify here:
First, in order to use the templates, you'll need a couple of categories on NSInvocation that you can find here. I forgot that those methods weren't part of NSInvocation because I use them so frequently.
Also, you'll notice replacement tokens in the template, similar to those you get for parameters when you use autocomplete with a method or function. This was intentional. If I had used any of the file template replacement tokens, the delegate methods would have begun with a capital letter and the method names wouldn't have always made sense. Instead, you have to replace the tokens with a value - the same value.
In theory, typing the value for one replacement token should change the value for all replacement tokens of the same name, but it doesn't seem to work in the header file, for some reason I haven't been able to figure out yet.
Anyway, hope that helps with some of the confusion.
http://iphonedevelopment.blogspot.com
Posted on 11 August 2010 | 11:36 am
Call for Testers!
Update: All of my available device slots are gone.
Okay, I've got room in my device list for 15 testers for the Library of Congress Prints and Photographs application. I'd love to accept more, but the 100 device limit prevents me from using too many slots for a personal side project like this.
There's a catch: I need at least one device running iOS 3.1.x and at least one older device (3G, Original iPhone, 1st or 2nd gen iPod touch). Ideally, I'd have a nice spread of devices. The iPad won't be supported in the first release, so iPhone and iPod touch users only please. I'm considering adding iPad at a future date, but don't have the time to devote to it at the moment.
I expect to distribute the first beta build by the end of the week, possibly earlier, but no promises, since client work and the book have to take priority. I have one bug I still need to chase down and one feature I still want to add, and then I'll be ready to distribute.
If interested, e-mail me with what model device you have and the iOS version you're running. If I can use you, I'll respond and have you can send me your UDID. This will mostly be first-come-first-served, except I may have to skip people to make sure I get at least one older device and one older version of iOS.
P.S. My e-mail address is my twitter name at mac dot com.
http://iphonedevelopment.blogspot.com
Posted on 10 August 2010 | 4:24 pm
Testers, Maybe?
I read on Daring Fireball yesterday about the new Library of Congress iPhone application. I love libraries, museums, and any large collection of interesting data and was really excited about this app.
It's a nice app, and a really good start for the Library of Congress, but it's missing something I wanted: access to the Prints & Photographs Online Collection. For over a decade, the Library of Congress has been digitizing material from their collections and putting them online. Unfortunately, the web interface is kinda weak on a computer and nigh unusable on an iPhone. Which is too bad, because there's a lot of really cool stuff in here, including works by and photographs of many famous artists such as Ansel Adams, Alice Boughton, Edward Weston, Pablo Picasso, Henri Matisse and many others. There is also a huge amount of historical photographs, artworks, and documents going back to the founding of our country and even earlier. It's the kind of place you can easily lose hours exploring.
I actually hacked out an iPhone app for searching the Prints & Photographs Collections quite a while back. I started it originally because I wanted to play with a few things I hadn't used yet, like iAds, and I find it's easier to do so if I'm writing a "real" app rather than just playing.
And when I say "hacked out", I'm not exaggerating in this case. This wasn't architected so much as thrown together. It started out an entry page where you enter a search term, and then a single table view with the results. I wrote it in one Saturday night after several beers, scope creep increasing with each new beer. But… it works and seems to work pretty well. I spent another couple of hours this evening cleaning up the rough edges and running it through Instruments. I've got a short list of bugs I need to fix, but it probably needs not more than a day's worth of work before it could go to testers.
After the Library of Congress came out with their own app and it didn't include this functionality, I started thinking it might be worth throwing this on the app store as a free app to complement the official one. The ads might generate a little income, but I don't expect very much from what I've heard about iAds fill-rate, and it doesn't really matter, most of the work is already done. Be kinda nice if one of the many abandoned apps in my dev folder got to see the light of day.
So, what do you think? Any interest? I've attached some screenshots of the app so you can see how it works. You can click them for full iPhone 4 resolution. Let me know in the comments if you think this is an app you'd download and also let me know if you're interested in testing.
http://iphonedevelopment.blogspot.com
Posted on 5 August 2010 | 6:49 pm
Xcode 3, Instruments 4
This was a bit of a "duh" moment for me, but probably worth mentioning, despite being a "Captain Obvious" post.
I'm a big fan of what Apple's doing with Xcode 4, but in my opinion it's just not ready for production use yet. I'm using it for the book and occasional forays, but not for regular client work yet. However, I also run my machine using the 64-bit kernel, which means I can't use Shark without rebooting. I've been really looking forward to the Time Profiler template in the 4.0 version of Instruments as well as the two new OpenGL ES templates and the Energy Diagnostics template.
But, while grumbling about their lack in the current release version of Instruments, it struck me: you don't have to launch Instruments from the same folder you launch Xcode from. You can launch Instruments separately and attach it to a running process in the Simulator or on an attached iPhone. That means, you can launch Instruments from the Xcode4 folder instead of from the Developer folder. This allows you to develop and debug with the less-shiny-but-production-ready Xcode 3.x and also use the new Instruments templates like Time Profiler, OpenGL ES, and Energy Diagnostics.
http://iphonedevelopment.blogspot.com
Posted on 5 August 2010 | 2:39 pm
Core Data Odds and Ends
I've been doing a fair bit of work with Core Data lately, and have picked up a few tricks to make my life easier, so I thought I'd share.
Mo' Power
Back in the EOF days, there was a great little piece of third-party software called EOGenerator that implemented something called the generation gap design pattern. Essentially, this little program would create your custom subclasses of EOGenericRecord (EOF's equivalent of NSManagedObject) for you. It wouldn't just create that, though, it would actually create two classes for each entity in your EOModel.
One of these two classes was yours to modify as you saw fit. It would never get overwritten even if you ran the EOGenerator script again, over and over. The other class contained all the generated code derived from your EOModel and was designed not to be touched by the programmer. You had your class, EOGenerator had its class, and you could both work without any fear of stepping on each other's toes. You could add attributes and entities to your data model and regenerate the machine-readable classes without any chance of losing your custom validation or methods. It was nice.
Wolf Rentzsch has re-implemented this idea for Core Data, and the result is called MOGenerator. If you spend any time with Core Data and aren't familiar with MOGenerator, go become familiar with it. It will make your life much, much, much better.
SQLite to Check Data
The default persistent store type for Core Data under iOS is a SQLite store, which means that Core Data keeps all of its data inside of a single SQLite database in your application's /Documents folder. Now, the format of the persistent store is undocumented and you should never, ever, ever, ever, never change data in the persistent store or make any assumptions about how Core Data stores the data. That's part of why we use Core Data: It's an abstraction layer that lets us not worry about the underlying storage details.
But, sometimes, it can be really helpful to know whether data is getting into the persistent store or not. I often work on my data model before my user interface, so being able to peek in the data store to make sure all is well is really helpful.
The first thing you might want to consider doing, if you plan to do that, is to create a SQLite config file. This is a file of commands that will execute whenever you launch SQLite. To do that, simply create a file called .sqliterc in your home directory. Here's what mine looks like:
.mode columnIn mine, the first line tells SQLite to print the column names when I issue a SELECT command. The second line tells SQLite to present the data in tabular form. The last line prints out a list of all the tables in the database before it shows me the SQL command prompt. That's handy given that the table names don't usually exactly match your entity or class names.
.header on
.table
Once you have a config file in place, you can open up any persistent store using the sqlite3 command in Terminal.app, like so:
sqlite3 MyApplication.sqliteYou can't, of course, execute this directly against database files on your phone, but you can do it in the simulator as well as on files copied from your phone using Xcode's Organizer. If you're using the simulator, you can look for your SQLite databases at the following location:
/Users/[your account name]/Library/Application Support/iPhone Simulator/[SDK version #]/Applications/[application UUID]/Documents/[Application Name].sqlite
When I log into a database with my SQLite config file, this is what I see:
-- Loading resources from /Users/jeff/.sqlitercNotice that it has dumped the names of all my tables. It's pretty easy to figure out which table holds what data once they're right in front of you. The example above is the persistent store from the SuperDB application from More iPhone 3 Development. The Hero entity managed objects are stored in ZHERO and the Power entity managed objects are stored in ZPOWER. To look at the contents of a table, you just need to execute a SQL SELECT command. The simplest is just SELECT * FROM TABLENAME;, which prints the entire contents of the table to the console, like so:
ZHERO ZPOWER Z_METADATA Z_PRIMARYKEY
SQLite version 3.6.12
Enter ".help" for instructions
Enter SQL statements terminated with a ";"
sqlite>
sqlite> select * from ZPOWER;The format of the data is pretty easy to figure out but, again, you really should look and not touch. It'd be very easy to accidentally destroy a persistent store. A seemingly inconsequential change could get the metadata out of whack and prevent your app from using the persistent store any more.
Z_PK Z_ENT Z_OPT ZHERO ZSOURCE ZNAME
---------- ---------- ---------- ---------- ---------- ----------
1 2 2 1 Mutation Fire
sqlite>
Better Descrption
I noticed a few days ago that Wil Shipley was complaining about the fact that many of Apple's delivered UIKit and Foundation objects have not overridden the -description method. This method is important because it's the one that is used in format strings, NSLog(), and when you type the po command in GDB. Instead of getting a meaningful description of many delivered objects, you instead get the class name and memory address of the instance, which is what NSObject's implementation provides, which isn't very much help while debugging.
Unfortunately, NSManagedObject is one of the many, many classes that doesn't have its own meaningful implementation of -description. That doesn't mean you can't provide one, though.
One thing you can do is to create an abstract subclass of NSManagedObject and put the -description method there. If you then subclass this abstract class, all of your managed objects will inherit the method from the abstract class. If I have other common functionality, I'll often do exactly that. But if I don't (and often you won't) I personally hate to clutter up my object hierarchy to add debugging tools. So I actually will cheat and do something you're not really supposed to do, which is override a method using a category. The reason it's bad to do this is because the Objective-C language does not specify which method should be used, which can lead to unpredictable behavior. But, when a class inherits a method from a superclass, I've found that overriding it in a category always works. I'm not suggesting you should do this in a shipping app, however. I wrap my method in pre-compiler definitions so that it doesn't get compiled into the release version of my application.
My version of -description assumes you're generating properties for your managed objects (I always do) and also assumes you're using custom subclasses (but you are, because you're using MOGenerator, right??). The method iterates over all the properties of the class and all its superclasses up to NSManagedObject, then prints them to the console. The categories I use look like this:
#if DEBUG
@implementation NSObject(MCManagedObjectDebug)
+ (NSMutableArray *)MCproperties
{
NSMutableArray *properties = nil;
if ([self superclass] != [NSManagedObject class])
properties = [[self superclass] MCproperties];
else
properties = [NSMutableArray array];
unsigned int propCount;
objc_property_t * propList = class_copyPropertyList([self class], &propCount);
int i;
for (i=0; i < propCount; i++)
{
objc_property_t oneProp = propList[i];
NSString *propName = [NSString stringWithUTF8String:property_getName(oneProp)];
if (![properties containsObject:propName])
[properties addObject:propName];
}
return properties;
}
@end
@implementation NSManagedObject(MCManagedObjectDebug)
- (NSString *)description
{
NSArray *properties = [[self class] MCproperties];
NSMutableString *ret = [NSMutableString stringWithFormat:@"%@:", [self className]];
NSDictionary *myAttributes = [[self entity] attributesByName];
for (NSString *oneProperty in properties)
{
NSAttributeDescription *oneAttribute = [myAttributes valueForKey:oneProperty];
if (oneAttribute != nil) // If not, it's a relationship or fetched property
{
id value = [self valueForKey:oneProperty];
[ret appendFormat:@"\n\t%@ = %@", oneProperty, value];
}
}
return ret;
}
@end
#endifThe +MCProperties method uses the Objective-C runtime to discover and iterate over properties in the class definition, including @dynamic properties that were created at runtime. Because the property runtime calls do not include properties inherited from parent classes, the method acts recursively, iterating up the object hierarchy until it gets to NSManagedObject. The actual -description method simply prints out each of the attributes and the value currently stored for that attribute for this object instance. Once you do this, if you use NSLog() or po, the result is much richer information about the objects getting printed to the console. This version ignores relationships and fetched properties, but it wouldn't be hard to add those in if you needed them.http://iphonedevelopment.blogspot.com
Posted on 4 August 2010 | 6:06 am
Time Moves On…
Justin from CarpeAqua has an interesting rant this morning about pull-to-refresh. I don't expect everybody to like every new mode of interaction that gets introduced (I certainly don't), and I've been known to rant myself at times, but I must admit I don't quite get the fervor with which some people, like Justin, hate experimentation with new methods of interaction.
In his post, Justin sums up his feelings by quoting a tweet by Marco Arment that reads:
The problem, like gradients, reflections, animations, and PHP, is not that it's a bad idea but that many won't use it properly.This argument reminds me of the arguments I've heard against dot notation in Objective-C. It amounts to "somebody might use it poorly, therefore it's bad".
I've got news for you: There's almost nothing in life you can't say that about. If you're holding up the potential abuse of something as a reason not to use it, then you're making excuses for your own desire for things to stay as they always were. In the history of human innovation, there's almost nothing that couldn't be abused or put to an inappropriate use.
Sure, you can do this with Objective-C 2.0+:
someObject.release;That's a perfectly legal way to release an object. Most, including me, would argue that it's not a good way; it's likely to cause confusion and makes the intention of your code harder to read. But, just because you can do this doesn't mean dot notation is inherently flawed (though a vocal minority of Objective-C developers argue exactly that). The same thing goes for gradients, reflections, PHP, animations and, yes, pull-to-refresh. Actually, scratch that… PHP really is bad¹. But the rest of them aren't.
Even the greatest interaction ideas can be mis-used and over-used and, in fact, they usually are. I'd almost argue that they have to be. All ideas get refined as they are used more. It's natural behavior to explore, expand, and find the limits of any new idea. Thought experiments are all well and good, but until you get a new mode of interaction in front of a lot of people to try it, all you've got is your best guess based on your own opinion. In other words, overuse of the novel isn't, in the scheme of things, a bad thing. Over time, the overuse ends and we're left with something we understand much, much better and that works really, really well.
Basically, we don't know how to use new concepts "properly" until they've been used a lot, in a lot of different ways, by a lot of different people. There's nothing to guide the use of the novel. There's no way to find what works and what doesn't except by trying. And sometimes the things we try won't work, or won't work for all people. Sometimes they'll be downright bad.
A lot of our current desktop interaction model, things that we think of as universal and standard, weren't always standard or universal. I can remember when ⌘C, ⌘X, ⌘V, and ⌘P did not automatically mean cut, copy, paste, and print on the Mac. It took several years for key commands to truly become standard and universal on the Mac. Heck, many early versions of Mac applications didn't make much use of key shortcuts at all, and when they did, they weren't at all consistent in which keys they used to do what. You can't dictate what works in user interaction. You have to put it out there, observe and listen, and be willing to change based on what you see and hear.
Personally, I like pull-to-refresh as it's used in
In other apps, or especially on the iPad, pull-to-refresh doesn't seem like it would be a natural form of interaction, but I've never tried pull-to-refresh in other contexts, so I don't know. But developers will try it (and are trying it), and many uses of pull-to-refresh that don't work well will die. Either the application will fail to sell, or the author will decide to change it to something that works better for his or her users. But we might just discover new and wonderful uses for the concept in the process.
That's how this whole messy process works. That's how we get progress in user interaction. We try things. Sometimes they work, often they don't. If, as developers, we only use things that are tried and true, nothing will ever change.
Progress in user interaction, like anything else where hard-to-predict humans play a major role, is messy and slow and imperfect and no amount of ranting about it will change that fact. If we want progress... if we want to keep moving forward, that's the price we pay. You can get angry about it, but you can't change it. You'd have about as much luck arguing with the weather.
1 No, not really. That was a joke.
http://iphonedevelopment.blogspot.com
Posted on 2 August 2010 | 9:21 am
NSOperation Xcode File Template
Although I'm generally averse to using a lot of template or boilerplate code, there are times when it's handy to have file template beyond those that Apple provides. Something I've done a fair amount of lately is to create NSOperation subclasses, and there's enough work involved with setting up an operation that I made an Xcode file template that contains all that setup work.
This template includes a delegate and a protocol and some private methods for communicating with the delegate. Now, when I have lots of NSOperation subclasses in a single project, I'll actually move much of this stuff to an abstract parent class or a category on NSOperation, but templates don't have any way of setting up dependencies, so I've made this self-contained and you can do your own refactoring.
I added this particular template under the Objective-C class icon so that it comes up as a new option under the Subclass of popup menu. 
You can find the template files right here. The zip file contains the full path to install everything in the correct place, so if you want to install it so that you can use it from Xcode, you would use the following command:
unzip NSOperationTemplate.zip -d /
If you just unzip it regularly, you'll find the actual files nested several folders down as a result of the path information.
I've only touched one existing file, which is a property list that causes the new template to show up in Xcode's Subclass of dropdown menu. The rest are new files, so installing this shouldn't interfere with Xcode in any way. But caveat emptor. You do keep good backups, right?
Hopefully this will be useful to some of you. If you have suggestions for making it better, please let me know.
http://iphonedevelopment.blogspot.com
Posted on 1 August 2010 | 11:47 am
App Licensing
Google took an interesting step recently by adding a service called App Licensing to the Android SDK. I haven't looked at it in detail, but the gist of it is that it's a license validation system for third party apps. It allows third party apps to check with the Android Marketplace to see if it's authorized to run on the particular device. To simplify it beyond recognition: It's sort of like Steam for Android Apps.
This is a bit of a double-edged sword for Google, though. Steam-style online authentication isn't exactly warmly embraced by the proponents of "open" systems, but given how easy it is to pirate applications downloaded from the Android Marketplace (and then return them for a refund!), it's probably a necessary step to attract developers to the platform. Google has to at least look like they're trying to stop the rampant piracy.
But here's the thing: With an open source OS, I can think of a dozen different ways to try and circumvent something like this, and I'm hardly a 1337 hacker. Google can add complexity and make it harder to circumvent, but if someone with the right skills has full access and control over the hardware and software, you can't stop them from getting around any kind of licensing authentication scheme you create. It's like DRM. Within a few months (at most), there'll be an exploit or hack to allow pirated Marketplace apps that use App Licensing to be run without a license. I can almost guarantee it. Google can keep changing the process to fight the pirates, but it's a losing battle, and likely would entail a lot of inconvenience to developers in the process.
This is one area where a closed system has advantages. For us iPhone developers, only about 10% of our potential audience can possibly pirate our apps because pirating requires jailbreaking. That 10% is the starting point. The most it can be. Jailbraking is a quid pro quo, so 90% of our potential market can't, won't, or wouldn't know how to pirate an app. But the real number is even smaller than that. Not everybody who jailbreaks their phone pirates apps - there are other valid reasons to jailbreak (so I'm told, I've never been tempted myself) - and I know people who have jailbroken their phones who are ethical and wouldn't consider pirating an app.
There's no doubt that there are advantages to "open" systems, but there are also disadvantages. In this particular case, one of the most major drawbacks of "open" doesn't hurt Google or the Wireless providers, it hurts third party developers. If that wasn't true, Google wouldn't be devoting engineering hours to try and stop it with 'app licensing'.
Life as an iPhone Dev has it's problems, no doubt. When you have an app sitting in review for months, the way Briefs has been, when you get rejected on seemingly arbitrary or inconsistent grounds, or when you can't implement something that would benefit your users because of a term in the license agreement, it sucks. But, when all is said and done, a good app on the App Store properly promoted can make enough money for a development team to live on. Until that can be said about the "open" Android Marketplace, I simply can't buy into the "open is better" mantra.
If a curated platform offers a better user experience and allows third party developers to actually make money, I just don't see "curated" as a dirty word, no matter how many times Google's Android Evangelist tweets it.
http://iphonedevelopment.blogspot.com
Posted on 28 July 2010 | 9:16 am
Those Were the Days…
The Computer History Museum has recently posted the original source code for MacPaint and QuickDraw! Apple has given them permission to publish them both, and they're well worth taking a look at if for no other reason than to realize just how good we programmers have it today.
The source code is a combination of 68k assembly, resource files, and Pascal, and all of the code was written by the incomparable Bill Atkinson, one of the early heroes of Mac programming and author of Hypercard (among many other things).
As an interesting aside, Bill stopped programming several years ago to focus on photography, but started programming again after the iPhone SDK came out so that he could create PhotoCard.
If you're not really familiar with who Bill is, you might want to read some of the stories on Folklore.org, but be warned, it's easy to lose track of time at that site if you have any interest in Apple history.
Note: One question that occurred to me looking at this source code was "how could Bill could have coded this back then?" These were written before the Mac shipped, and the Mac shipped with only 128k of RAM, which is smaller than the MacPaint Pascal source. You couldn't have opened some of these source code files on an early Mac due to their size. The answer appears to be that they were written on a Lisa, which shipped with a meg of RAM. That was some crazy amount of memory for the day and part of the reason the Lisa cost $9,999 (somewhere between $19k and $40k in today's dollars. I do seem to recall reading, though, that the early versions of QuickDraw (aka LisaGraf) were written on some model of Apple 2, though.
http://iphonedevelopment.blogspot.com
Posted on 20 July 2010 | 7:18 am
A few things iOS developers ought to know about the ARM architecture
The Wandering Coder has a great post today titled A few things iOS developers ought to know about the ARM architecture. There's some really good information about the different ARM architectures in different iOS devices and different generations of certain devices. Well worth the read.
http://iphonedevelopment.blogspot.com
Posted on 18 July 2010 | 8:38 pm
On Swords, Perspective, and Spin…
Although I thought Apple started off a little too defensive yesterday, when you boil it down, I thought they did the right thing. If you're having a problem and a case can fix it, here, have a free case. If you already bought a case, they'll refund the money you paid for that case. If you bought an iPhone 4 and the problem keeps you from being able to use or enjoy your phone, they'll take it back, no restocking fee, no questions asked.
All other issues aside, I'm not sure what more Apple could or should have done. There aren't many products that come with an unconditional guarantee any more, yet reading some of the output of various so-called tech "journalists" since yesterday's press conference, it seems that there are many who think that Apple didn't do enough and didn't "do the right thing". Short of Apple's senior management taking out swords and falling on them (at least proverbially, if not literally), I don't think anything would have made certain journalists (and I use the term very loosely) happy.
Of course, it's not exactly good for the tech press if this quietly goes away or turns out to be much ado about nothing. They're having a grand old time and raking in a lot of page hits on this whole "antennagate" thing they've created, so I guess it's understandable that they don't want to accept a reasonable response from Apple. They clearly wanted nothing less than a fall from grace… an admission that Apple didn't design or test properly. Instead, they got treated to a number of facts that didn't jive with what they wanted to hear. They were shown other phones experiencing similar phenomenon. They were shown a little about the testing procedures the phone went through and the investment Apple has made toward testing their antenna designs. They were shown, in short, why Steve doesn't think they did anything wrong.
The natural reaction to cognitive dissonance is anger, so I guess I shouldn't be surprised by the angry diatribes from some sectors of the tech press, especially those who aren't fans of Apple to begin with. But anybody who expected anything else out of yesterday's conference call are either intentionally stoking the fires for their own benefit or else they're morons who fail to realize there are perspectives other than their own. Perspective is an interesting thing, and it's not quite the same thing as "spin", which it is often mistaken for. Perspective is your own world view. It's a combination of a whole bunch of things that make up the way one person thinks about things: their likes, dislikes, biases, priorities, and fears. Spin, on the other hand, is when somebody intentionally tries to conflate facts, or select facts to make their position look better.
And make no mistake, we saw some spin yesterday. Some of the statistics early on were very carefully selected. The iPhone 4 dropped call rate being 1 in 100 less more than the 3Gs call drop rate, for example, sounds small, but what was the 3Gs' dropped call rate? If it was 1 in 10,000, then that's a statistically huge change. If it was 3 in 100, then it's statistically tiny, probably within the margin of error. We weren't given enough information to know what that statistic meant. If I had to guess, I'd say the difference was probably statistically significant (though not necessarily huge) or they wouldn't have presented the data that way.
But, even with the spin, it was pretty clear that from Steve's perspective Apple went through a very rational, valid design and testing process and achieved something pretty amazing. And I tend to agree. I wouldn't easily give up my iPhone 4 now. It's an amazing piece of technology. It really is. I love just holding the damn thing in my hand. It's such a refreshing change from all the cheap plastic consumer devices in my life. Every other device that the iPhone manages to replace in my life (my point and shoot camera was the latest casualty), the happier I am. I would quite honestly give up using cell phones before I'd let somebody replace my iPhone 4 with a junky plastic Blackberry or Nokia phone with their confusing, poorly written and terribly designed software.
Now, that's not meant to downplay the antenna issue, because for some people it is real and a hassle, I'm just stating my own perspective. Regardless of your perspective, however, the evidence seems to show that statistically speaking, most people in most places are going to have better reception with this phone. All design is about tradeoffs and all products are designed to meet the needs of a wide variety of people. Moving to an external antenna system was a conscious choice that Apple made, and it appears to have been well thought-out, researched, and tested. A conscious design decision was made that would make more room for battery and additional sensors and gadgets while making the phone more solid feeling and still thinner than any previous phone. The tradeoff, other than the obvious ones of manufacturing costs, was the increased possibility for attenuation. Now, we know, thanks to Anandtech, that this attenuation can cause a reduction of up to 24dBm of signal strength. We also know that that amount makes no measurable difference in data speed or voice reception for most users in most locations. Months ago, before the iPhone 4 came out, and without the benefit of hindsight or knowing what kind of media circus would ensue, that almost certainly would have seemed like a perfectly valid tradeoff. "Wait, if we do this, most people, most of the time will have better reception and we can make the phone smaller and have better battery life and fit a gyroscope, and the only tradeoff is that some people in some situations if they hold the phone in a certain way will have one or two less bars? Great!"
Was it the right decision? Well, with the benefit of hindsight, perhaps not if you factor in the negative publicity they've gotten. But, was the decision the kind of stupid mistake that deserves payment in blood? Absolutely not. Even suggesting it is ridiculous.
The iPhone is a consumer product. It's like off-the-rack clothing. It's not custom tailored to any one individual. It's the product of a long series of design decisions trying to make the best product for the largest section of the population. If you're an outlier - if you're one of those people for whom the issue is preventing you from using the phone, put it in a free case, or bring it back. Apple will give you your money back, AT&T will let you out of your contract. No questions asked, no restocking fee. You're then free to go find a phone that works better for you.
If you want or expect more than that, you've got pretty serious entitlement issues. If you don't want to do that because you think you can't find a better phone, then maybe you need to re-assess your own perspective.
http://iphonedevelopment.blogspot.com
Posted on 17 July 2010 | 8:55 pm
iPhone 4 Press Conference
Well, the iPhone 4 press conference just ended, and I thought I'd type up my thoughts quickly before diving back into work. Overall, the end result is exactly what I thought they'd do: free cases and refunds for those who want them. Seems very like a fair response to me.
I thought Steve started the presentation sounding a little defensive, though I can understand why. By the middle of the performance, though, he really hit his stride, and by the end I'd have to say it was one of Steve's best performances to date when you factor in that he wasn't announcing a new product, but instead defending an existing one, which is never as much fun or as easy.
The main points were that yes, you can interfere with reception on the iPhone 4 by grasping it a specific way, but you can also do that on most smart phones (they demonstrated it on several models of competitor phones), and that the press was making a bigger deal out of this than customers were in search of a headline. He pointed out that both Apple and AT&T were seeing lower return rates for the iPhone 4 than for the 3GS, in fact the returns have only been a third of what they saw with the 3GS.
There were little bits of insight into life at Apple that were unusual. We don't usually hear much about their actual processes (except manufacturing processes which have been part of their PR since the unibody computers). Seeing the anechoic chamber was cool and it was interesting to hear a bit about the process they go through to test reception on the antenna designs. Hearing about the problems AT&T has with getting new cell towers approved in San Francisco was somewhat enlightening. I've been rather harsh about AT&T's signal in NYC and SF myself, but never really though about the NIMB factor. I'm not ready to become an AT&T fan boy, but I am ready to cut them a little bit of slack on that issue now.
We also heard that Apple has sold over three million iPhone 4s in roughly 3 weeks and that they're still selling every one they can make. That's impressive considering the amount of bad publicity they've gotten from this issue.
At the end of the session, Steve pulled up Bob Mansfield and Tim Cook to answer questions, and I thought that was especially well handled with some nice bits of humor, though they took off the gloves about a few reports, especially one from the NYTs about a supposed software bug contributing to the reception issue. I especially like the comment that was made when Bob Mansfield was talking about how they sometimes send engineers to a customer's house to test reception. Bob said "For the record, we notify them we're coming", and Steve chimed in with "…and we didn't bash in any doors". Just a great response, it showed a sense of humor while getting in a bit of a jab at Gizmodo (who were never once mentioned by name that I can remember - it was always "some website" when talking about things they did).
There were two other things that struck me, but I'm not sure if they are worth mentioning, they might just be be reading too much into things, but I will because it's fun to speculate about such things.
The first thing is that Steve mentioned that Apple has Verizon cells on campus in addition to AT&T cells. Now, it's not uncommon for large companies to have towers onsite to ensure their employees get good reception at work, but it seemed odd that he felt it was worth mentioning that they had Verizon cells on campus when talking about an issue that would only be affected by AT&T towers.
The other thing that struck me as odd was the fact that in one of the question responses, Steve talked about the press "going after" Google because they were successful and that he wished the they wouldn't do things like that. He talked about how great the stuff Google made was and made a weird little comment about people not appreciating innovation that's still happening inside the U.S. Again, it may not mean anything, he could have just been trying to think of a big successful U.S. tech company and that was the first one that jumped to mind, but given the state of affairs between Apple and Google lately, it seemed oddly conciliatory and defensive. Perhaps a hint that the Google/Apple competition is headed back to more civil grounds even if may never go back to the days of friendly coopetition of yore? I dunno, but maybe. We can hope.
That's all, now it's back to work. You should too, slacker. ☺
Update: Apple has just posted a new page explaining attenuation and signal loss.
http://iphonedevelopment.blogspot.com
Posted on 16 July 2010 | 11:59 am