Archive for category objective c
For mobile, it’s all native – appcelerator vs phonegap vs native
Posted by dquail in iphone, objective c on February 5, 2012
I finally took enough time to form a stronger opinion in the html5 vs. native, vs Appcelerator, vs phone gap debate and for me, I’m all in on native. Here’s a bit of a breakdown on the different choices.
Appcelerator
- Pros: It’s main selling point is that it allows web developers to quickly create cross platform apps using html, javascript and css. Appcelerator will actually compile down to native code which in theory runs as fast as native code written in objective C
- Cons: Compiling to multiple platforms is a bit of a double edged sword. It feels like they’re constantly having to make design decisions that support the lowest common denominator between the multitude of mobile platforms. Furthermore, inherent in this model is a bit of follow mentality. Any time a mobile platform introduces changes, they have to catch up to provide support. I’ve heard anecdotes about developers experiencing performance hits when using appcelerator … but I actually think I’d call BS on that. It’s fast.
Phone Gap
- Pros: Phone gap allows web developers to write html, css, and javascript that runs within a mobile app. So your web devs can write code quickly across multiple platforms.
- Cons: IMO this is a cheap solution. Unlike appcelerator, the javascript isn’t compiled into native code. It’s simply embedded into a UIWebView (or other webkit control for other platforms). This has important implications that you have to understand:
- UIWebView is a pig and has major performance issues. Your app is NEVER gonna be as performant as a native app.
- You don’t have direct access to native controls like a UINavigationController or UIPopoverController, and native resources like the photo assets on the phone. Instead, they’ve created some admittingly pretty awesome hooks which bridge the “gap” between native controls and javascript. But these are a bit clunky from my experience. It’s difficult to persist application state between these controls. They’re also playing the same game as appcelerator is here – trying to support several cross platform controls. They’re always playing catch up, and making lowest common denominator compromises.
Simplified NSUserDefaults
Posted by dquail in objective c, programming, Uncategorized on January 15, 2012
I use NSUserDefaults all the time for storing simple users settings and application state. But there’s a whole lotta string literals and a bunch of repeating yourself that takes place. That sucks ….
So I like to create a category on NSUserDefaults so I can treat it like a concrete class, whose API is enforced by the compiler and autocompleted by Xcode. Here’s an example of how I use it to track a users email address within my app. Note the rcast_ prefix. Always a good idea to prefix your categories to prevent future name collisions.
//NSUserDefaults_RCastr.h
@interface NSUserDefaults (RCastr)
@property (assign, getter=rcast_userEmail,
setter=rcast_setUserEmail:) NSString *rcast_userEmail;
//NSUserDefaults_RCastr.m
#import "NSUserDefaults+RCastr.h"
NSString *const rcastDefaultsKeyUserEmail = @"rcast_userEmail";
@implementation NSUserDefaults (RCastr)
#pragma mark -
#pragma mark Username
- (NSString *)rcast_userEmail {
return [self stringForKey:rcastDefaultsKeyUserEmail];
}
- (void)rcast_setUserEmail:(NSString *)userEmail {
[self setObject:userEmail forKey:rcastDefaultsKeyUserEmail];
[self synchronize];
}
@end
That allows me to use user defaults like the following:
//Set the email address
NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
defaults.rcast_userEmail = @"user@domain.com";
//Get the email address
NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
if (defaults.rcast_userEmail){
[FlurryAnalytics setUserID:defaults.rcast_userEmail];
}
Dead easy change, but much cleaner and far less error prone.
oAuth authorization callback on the iPhone with WebView
Posted by dquail in iphone, objective c, programming, Uncategorized on December 7, 2009
There was a bit of chatter a while back about handling oAuth on the iPhone; in particular security balances sending a user to the website using Safari vs an embedded WebView. Mike’s blog post discusses this so I won’t repeat, other than to say that I think either solution (webView or Safari) is open for spoofing. An app developer could spoof an auth and grab creds in either case pretty easily … so I’ve chosen to optimize for user experience by implemented an oAuth webView for our app.
Here’s some of the relevant code for launching to an authorize website and handling the response.
* Our app uses oAuthConsumer library
* Our implimentation was against linkedin oauth which meant that I needed to pass the oauth_callback in our request token request by setting the oauth_callback parameter there rather than the authorize URL
In other words, our requestToken method looks something like the following:
OAMutableURLRequest *request = [[[OAMutableURLRequest alloc] initWithURL: self.requestTokenURL consumer: self.consumer token:nil realm:nil signatureProvider: nil] autorelease]; if (!request) return; [request setHTTPMethod: @"POST"]; [request setParameters: [NSArray arrayWithObject: [[[OARequestParameter alloc] initWithName: @"oauth_callback" value: kLinkedInCallbackUrl] autorelease]]]; OADataFetcher *fetcher = [[[OADataFetcher alloc] init] autorelease]; [fetcher fetchDataWithRequest: request delegate: self didFinishSelector: @selector(setRequestToken:withData:) didFailSelector: @selector(outhTicketFailed:data:)];
With the request token set, we’re able to pass this to the authorize url and it will return to our custom url
/*Create the request object for the linkedin URL*/
if (!_requestToken.key && _requestToken.secret) return nil; // we need a valid request token to generate the URL OAMutableURLRequest *request = [[[OAMutableURLRequest alloc] initWithURL: self.authorizeURL consumer: nil token: _requestToken realm: nil signatureProvider: nil] autorelease]; [request setParameters: [NSArray arrayWithObject: [[[OARequestParameter alloc] initWithName: @"oauth_token" value: _requestToken.key] autorelease]]];
/*Load the webview with this request*/
[_webView loadRequest: request];
/*Check the response for a custom uri and redirect if it's set*/
- (BOOL) webView: (UIWebView *) webView shouldStartLoadWithRequest: (NSURLRequest *) request navigationType: (UIWebViewNavigationType) navigationType {
NSData *data = [request HTTPBody];
char *raw = data ? (char *) [data bytes] : "";
NSURL* url = request.URL;
if ([url.scheme isEqualToString:@"liconnect"]) {
[_spinner stopAnimating];
if ([url.resourceSpecifier isEqualToString:@"cancel"]) {
if ([_delegate respondsToSelector:@selector(OAuthLinkedInAuthorizeViewFailed:)]) {
[_delegate OAuthLinkedInAuthorizeViewFailed:self];
}
}
else {
[self dialogDidSucceed:url];
}
return NO;
}
if (raw && strstr(raw, "cancel=Deny")) {
[self denied];
return NO;
}
if (navigationType != UIWebViewNavigationTypeOther) _webView.alpha = 0.1;
return YES;
}
/*Strip out the token and auth_verifier from the url*/
- (void)dialogDidSucceed:(NSURL*)url {
NSString* q = url.query;
NSLog(q);
NSRange verifier_start = [q rangeOfString:@"oauth_verifier="];
NSRange start = [q rangeOfString:@"auth_token="];
if (start.location != NSNotFound) {
NSRange end = [q rangeOfString:@"&"];
NSUInteger offset = start.location+start.length;
NSString* token = end.location == NSNotFound
? [q substringFromIndex:offset]
: [q substringWithRange:NSMakeRange(offset, end.location-offset)];
NSString* verifier = [q substringFromIndex:verifier_start.location+verifier_start.length];
if (token&&verifier) {
if ([_delegate respondsToSelector:@selector(requestTokenReceived:withVerifier:)]){
[_delegate requestTokenReceived:token withVerifier:verifier];
}
}
else
{
if ([_delegate respondsToSelector:@selector(OAuthLinkedInAuthorizeViewFailed:)]){
[_delegate OAuthLinkedInAuthorizeViewFailed:self];
} }
[self removeFromSuperview];
}
}
You’re done. You’ve now got your oauth_verifier and your access token all lined up to store away or make immediate calls to the api.
English word list for solving Jumble puzzle
I’ve been playing around with the iPhone SDK, and decided on a “Jumble Solver” as my first as a good learning project. It’d include a whole bunch of objective C and general programming concepts – Recursion, delegation, Interface builder, inheritence, sqlite.
My general approach was to build a decision tree with the available characters in the jumble, and compare the leaf nodes with an english dictionary. Finding a sqlite database of words … or rather a list of words was really hard. I stumbled upon a word list in text format found at the national puzzle solvers website. To dump that into a database I used the following simple ruby script. You’ll notice I limited the words to less than 7 characters. The search algorithm is n! complexity so limiting the jumble to 7 characters proved optimal performance. :
require ‘sqlite3.rb’
db=SQLite3::Database.new(“WordDatabase.sql”)
word_list=File.open(‘words.txt’).readlines
word_list.each do |word|
if word.length <8
db.execute(“INSERT INTO words (word) VALUES (?)”, word.chomp)
end
end