Apple Support Part Two
And that’s why I love Apple!
I hand my phone to the guy and tell him that the pictures it takes now are really blurry. I know its because the back is scratched to hell but I played dumb. He looks at the back and says “the back of your phone is scratched to hell, that’s why. I’ll replace that for you.” After gathering my info he disappears in the back for 5 minutes then comes back out and hands me my phone with a brand new glass back and my receipt for $0.00.
Lets see if you notice the difference…
to
Apple Support
Last week I had two people ask me for help with their Apple products (iPhone and an iPod Touch). Both were afraid to reach out to Apple Support because people told them it would cost a lot of money.
Really?
My iPhone 4 use to take amazing pictures. The pictures it takes now are horrible. I have an appointment at the Genius Bar tomorrow. Granted I have Apple Care on my iPhone 4, but I bet my experience is awesome… because it always is.
I’ve had iPods, iPhones, iPod Touches – even faulty video cards embedded in the motherboard of my Mac Book Pro – all replaced… for free… I walk out with a brand new piece of hardware that’s *exactly* the same software-wise as the one I walked in with… for free…
It’s not really free – you do pay the price upfront – but for the support experience? It’s worth it!
I’ll post tomorrow about how my appointment goes.
I’m looking forward to it. Not dreading it.
NSNotification Selectors and Subclasses
// Don’t do this…
@interface parentViewController : UIViewController
@end
@implementation parentViewController
- (void) viewDidLoad {
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(handleTheNotification:) name:@”MyNotificationName” object:nil];
}
- (void)handleTheNotification:(NSNotification *)note
{
NSLog(@”Parent selector”);
}
@end
@interface childViewController : parentViewController
@end
@implementation childViewController
- (void)viewDidLoad {
// update from my original post – as brought up in the comments I do call super in viewDidLoad
[super viewDidLoad];
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(HandleTheNotification:) name:@”MyNotificationName” object:nil];
}
- (void)handleTheNotification:(NSNotification *)note
{
NSLog(@”Override of parent selector”);
}
- (void)HandleTheNotification:(NSNotification *)note
{
NSLog(@”Stupid child notification that shouldn’t have been added”);
}
@end
// … the runtime will randomly call one or the other selector in you child class.
Core Data and Enterprise iPhone Applications – Protecting Your Data
Introduction
NewsGator’s primary software offering is a product called Social Sites which basically adds social networking aspects to Microsoft SharePoint. My role in this product is to create the iOS device apps that interact with the server. My app is pulling in a lot of data that an enterprise customer considers *very* sensitive so data protection is the first thing many CIOs ask about when considering Social Sites. This post is to layout both how iOS protects data on the device so our prospective clients have a better idea about how it works, along with some additional steps developers can take to further protect their data.
Much of what I’ll discuss was covered in Session 209 at WWDC, but since that information is under NDA, I’ll stick to information that’s available in the public domain (including this post about iPhone 3GS and this post about iOS4). But if you have access to the WWDC videos, Session 209 is a great resource.
Data Encryption Starting with iPhone 3GS
Social Sites uses Core Data with a SQLite store type for storage and persistence. When the app first runs, it creates a Social_Sites.sqlite database file in its sand boxed documents directory on the device file system.
With the 3GS, any data written to the filesystem is encrypted using hardware encryption. By simply creating the Social_Sites.sqlite file on the file system, the data stored in it is already encrypted. This encryption also allows for the Social Sites data to be instantaneously unavailable when you use Remote Wipe. I say unavailable because the remote wipe doesn’t actually overwrite the encrypted data on the filesystem, but instead overwrites the hardware key used to encrypt/decrypt it. But still, the important thing is that the data on disk is encrypted.
This type of data encryption is also used on the current iPads.
Data Encryption on iOS 4
The way hardware encryption works on iOS4 has been greatly improved. I can’t find any public document from Apple that explains the improvements in detail, but their iOS 4: Understanding data protection support page gives a hint with “Data protection enhances the built-in hardware encryption by protecting the hardware encryption keys with your passcode.” I’ll just say that hardware data encryption on the 3GS and iPad use a single hardware encryption key. The WWDC session goes into great detail about the under the hood improvements. The main takeaway for a CIO though is that the Social Sites sqlite datafile on iOS4 has stronger encryption than it did in the past.
Further Protection – NSFileProtectionComplete
In iOS4, Apple introduced data protection. It gives you the ability to encrypt the hardware keys used to encrypt your files and to erase those keys when the system is locked – leaving your file unreadable until the phone is unlocked and the keys are regenerated.
This level of protection is not built in by default though. The user has to enable it (or an IT department can enable it and force users to have it on) and your code must set an attribute on your Core Data sqlite store as well.
Steps for the user to enable data protection are outlined on the iOS 4: Understanding data protection support page. You need to have the passcode turned on since this is what the system will use to generate the encryption for the keys.
As a developer, you need to set the NSFileProtection level of your sqlite store to NSFileProtectionComplete (by default it will be NSFileProtectionNone). You can do this right after you instantiate your persistent store coordinator:
- (NSPersistentStoreCoordinator *)persistentStoreCoordinator { if (persistentStoreCoordinator != nil) { return persistentStoreCoordinator; } NSString *storePath = [[self applicationDocumentsDirectory] stringByAppendingPathComponent: @"Social_Sites.sqlite"]; NSURL *storeUrl = [NSURL fileURLWithPath:storePath ]; NSError *error = nil; persistentStoreCoordinator = [[NSPersistentStoreCoordinator alloc] initWithManagedObjectModel: [self managedObjectModel]]; if (![persistentStoreCoordinator addPersistentStoreWithType:NSSQLiteStoreType configuration:nil URL:storeUrl options:nil error:&error]) { // Handle error } if(RSRunningOnOS4OrBetter()) { NSDictionary *fileAttributes = [NSDictionary dictionaryWithObject:NSFileProtectionComplete forKey:NSFileProtectionKey]; if (![[NSFileManager defaultManager] setAttributes:fileAttributes ofItemAtPath:storePath error:&error]) { // Handle error } } return persistentStoreCoordinator; } BOOL RSRunningOnOS4OrBetter(void) { static BOOL didCheckIfOnOS4 = NO; static BOOL runningOnOS4OrBetter = NO; if (!didCheckIfOnOS4) { NSString *systemVersion = [UIDevice currentDevice].systemVersion; NSInteger majorSystemVersion = 3; if (systemVersion != nil && [systemVersion length] > 0) { //Can't imagine it would be empty, but. NSString *firstCharacter = [systemVersion substringToIndex:1]; majorSystemVersion = [firstCharacter integerValue]; } runningOnOS4OrBetter = (majorSystemVersion >= 4); didCheckIfOnOS4 = YES; } return runningOnOS4OrBetter; }
Considerations
If your application needs your Core Data store in any background processing, then you cannot use data protection. Any attempt to access files that are NSFileProtectionComplete will cause an exception.
There are application delegates applicationProtectedDataWillBecomeUnavailable and applicationProtectedDataDidBecomeAvailable as well as notifications UIApplicationProtectedDataWillBecomeUnavailable and UIApplicationProtectedDataWillBecomeAvailable that you can use to determine what state your protected data is in.
You could then in theory keep unprotected data in memory in your background processing then write it into your Core Data store when protected data becomes available again.
You can find more information in the iPhone Application Programming Guide.
Conclusions
My answer to any CIO asking would be to ensure any device using Social Sites be a 3GS, an iPad or an iPhone 4. I would also encourage enforcing passcode and data protection for any device running iOS4. These steps ensure that the SQLite data store for Social Sites is hardware encrypted with remote wipe capabilities and further protected with iOS4 and data protection.
UPDATE:
Here’s the link to the iPhone Configuration Utility 3.0 which can be used to “create, maintain, encrypt, and install configuration profiles, track and install provisioning profiles and authorized applications, and capture device information including console logs.”
CGContextSetShadow in iOS4
What am I doing wrong? This code…
CGContextSetShadow(context, CGSizeMake(1, -2), 3.0);
[userImage drawInRect:CGRectIntegral(rUserImage) blendMode:kCGBlendModeNormal alpha:1.0];
CGContextRestoreGState(context);
Creates this (the top image) in iPhone OS 3.1.3…
Notice the drop shadows are opposite. The bottom cells I corrected for iOS4 but as you can see in 3.1 they’re wrong.
360 iDev Austin
If you’re an iPhone developer, or maybe just thinking about it, you should go to 360 iDev Austin! It won’t cost you as much as WWDC – not even close – but the quality of the sessions are right up there. Actually a few of the presenters from previous 360 iDevs are now Apple employees.
Well worth it!
(Ok this is a total shill post but honestly its a fantastic conference AND its in Austin… its going to be a blast)
Fast UITableViewCell with a UIWebView
The Problem
If you use UITableView’s you know that scrolling through your cells has to be fast. If you’ve done much research, you’ve probably found the Tweetie approach using one custom view and doing the drawing yourself. This works great for static text and image cells. It gets more difficult when you need to add hyperlinking to the text in the cell. The problem becomes how you add hyperlinking to a UITableViewCell but keep the scroll speed as fast as possible.
First Solution
I have an app that requires this type of functionality. Its an enterprise social computing app – you can think of it like Facebook or Yammer. Users can post microblog status messages that other users can “like” and comment on.
The first attempt I made was to subclass ABTableViewCell and in my drawing I would set aside certain regions that were “linkable”. In this screenshot, the light blue in the cells denotes text that is linkable – usernames in this case.

My tableview cell then waits for touchesBegan and looks to see if a touch is in one of these areas. If it is, it sets a flag denoting what area is touched and redraws the cell with a highlight behind the text. When it gets touchesEnded it tells its delegate what function needs to happen. So for the screenshot above, touching the username would navigate to a view showing that users details.
Its a pretty easy approach but it has its limits such as a status that has multiple links. I was left with either hand parsing the html and measuring where all the text should go that’s linkable, draw that text in a different color, and remember those regions. Or I could just strip out any HTML and draw flat text for the status… I took the easy route. Worked great for a 1.0 release but of course status linking was the number one feature request from users.
Second Solution
The idea came to me that I could add my own UIWebView to the cell and become its delegate so that I could react to any touches on links. As you probably know though, adding subviews – especially non-opaque subviews – can cause scrolling to be slow and to skip frames in the animations. To work around this I decided two things…
1. Only use the UIWebView when the text actually contains a link, otherwise just draw flat text
2. Only show the UIWebView when the tableview isn’t scrolling so that we don’t get in the way
This approach seems to work really well – but there are a few issues that you need to work around. Most of them I’ve figured out but there are two that I’m still stumped on which I will detail in a bit.
Issues and Workarounds
Issue one – loading an HTML string into a UIWebView takes a second
It takes a little bit for the webview to display your HTML. My workaround is to draw the HTML stripped version of the status in flat text and keep the webview hidden until you receive webViewDidFinishLoad. At that point you can remove the flat text or draw it in clear color and set hidden = NO on the webview.
Issue two – UIWebViews flash when first loading content
I honestly can’t tell you why this happens, but it does. And its a bit jarring to the user experience. The workaround for this is to simply move the code to redraw the cell and display the webview into its own method then use performSelector:withObject:afterDelay to call it. 0.35 second duration seems to work nice.
Issue three – UIWebViews will crash your app if you don’t nil its delegate
This was a weird bug. Because your popping webviews on and off cells at a pretty quick pace, you’ll run into situations where the webview hasn’t completed loading your HTML before you want to pop the webview off and release it during prepareForReuse (and you always want to pop the webview off and release it when preparing for reuse!) or dealloc. The problem is that the webview continues to live until its done loading whatever it is it was loading. When its done it will tell its delegate (your cell) that its done. If your cell was dealloced… CRASH. So make sure you set its delegate to nil.
Other Issues I Haven’t Solved
Issue one – UIWebView eats touches
If your app responds to tableView:didSelectRowAtIndexPathto show a details view, you’ll notice that if you touch within the bounds of the webview, this method will never fire. So the user is forced to touch a part of the cell thats outside the webview. If you designed your cell well, they’ll have no idea where this boundry is. Its a very frustrating user experience – one that I would love to get solved before shipping.
Issue two – status bar touches no longer scroll to top
If any of your displayed cells have a webview on them – hidden or not – the scroll to top by touching the status feature of the tableview quits working. My guess is that the scrollView child of the UIWebView is eating these as well and not passing them up the responder chain. Its another issue I haven’t resolved yet.
Code
OK – enough talk. Here’s a sample project that shows how I do this… FastTableViewCellWithWebView (or you can look at the full Mercurial repository here). Download it, run it, let me know what you think. And more important… let me know if you can resolved my two issues!
UIAlertView with an NSObject
Here’s my situation… I have an operation that gets performed in the background. If that operation fails I need to alert the user and see what they would like to do about it. If they want to retry it, I just add the operation back onto the queue.
To do this, I’m sending a notification that the operation failed and am including the operation as the notifications object. I then set up a responder on my view controller for that notification. It creates a UIAlertView to ask the user what to do.
If the user selects “Retry”, it would be nice to have the operation right there on the alert view in alertView:clickedButtonAtIndex: so that I can just add it back to the operation queue. But there doesn’t seem to be any properties on UIAlertView for an arbitrary object. Instead I’ve subclassed UIAlertView and simply added a NSObject property.
Pretty easy code wise, just wondering if I’m missing something on UIAlertView that would handle the same thing.
Finding a FavIcon URL
Being relatively new to objective-c, I was wondering what people thought of this code. The idea was to find the favIcon URL from a string of HTML. Is this the fastest/most efficient way without using RegEx?
-(NSString*)favIconUrlStringFromHtmlString:(NSString*)htmlString
{
NSScanner *htmlScanner = [NSScanner scannerWithString:htmlString];
while ([htmlScanner isAtEnd] == NO)
{
[htmlScanner scanUpToString:@"<link" intoString:NULL];
if(![htmlScanner isAtEnd])
{
NSString *linkString;
[htmlScanner scanUpToString:@"/>" intoString:&linkString];
// we have a link element. does it have rel set to anything we want?
if(([linkString rangeOfString:@"rel=\"shortcut icon\""].location != NSNotFound)||
([linkString rangeOfString:@"rel='shortcut icon'"].location != NSNotFound)||
([linkString rangeOfString:@"rel=\"icon\""].location != NSNotFound)||
([linkString rangeOfString:@"rel='icon'"].location != NSNotFound)||
([linkString rangeOfString:@"rel=icon "].location != NSNotFound))
{
// yep, grab the href
NSScanner *hrefScanner = [NSScanner scannerWithString:linkString];
[hrefScanner scanUpToString:@"href=" intoString:NULL];
if(![hrefScanner isAtEnd])
{
[hrefScanner scanString:@"href=" intoString:NULL];
NSString *hrefString;
// scan up to a space.
// if we don't hit one cause the href was the last thing in the element, we don't care
[hrefScanner scanUpToString:@" " intoString:&hrefString];
// clean up any quotes
hrefString = [hrefString stringByReplacingOccurrencesOfString:@"\"" withString:@""];
hrefString = [hrefString stringByReplacingOccurrencesOfString:@"'" withString:@""];
// we're done, return
return hrefString;
}
}
}
}
return nil;
}



