Search This Blog

Friday, April 30, 2010

TextAlertView



#import

#define kUITextFieldHeight 30.0
#define kUITextFieldXPadding 12.0
#define kUITextFieldYPadding 10.0
#define kUIAlertOffset 100.0

@interface TextAlertView : UIAlertView {
UITextField *textField;
BOOL layoutDone;
}

@property (nonatomic, retain) UITextField *textField;

- (id)initWithTitle:(NSString *)title message:(NSString *)message delegate:(id)delegate
cancelButtonTitle:(NSString *)cancelButtonTitle otherButtonTitles:(NSString *)otherButtonTitles, ...;

@end



#import "TextAlertView.h"

@implementation TextAlertView

@synthesize textField;

/*
* Initialize view with maximum of two buttons
*/
- (id)initWithTitle:(NSString *)title message:(NSString *)message delegate:(id)delegate
cancelButtonTitle:(NSString *)cancelButtonTitle otherButtonTitles:(NSString *)otherButtonTitles, ... {
self = [super initWithTitle:title message:message delegate:delegate cancelButtonTitle:cancelButtonTitle
otherButtonTitles:otherButtonTitles, nil];
if (self) {
// Create and add UITextField to UIAlertView
UITextField *myTextField = [[[UITextField alloc] initWithFrame:CGRectZero] retain];
myTextField.autocorrectionType = UITextAutocorrectionTypeNo;
myTextField.alpha = 0.75;
myTextField.borderStyle = UITextBorderStyleRoundedRect;
myTextField.delegate = delegate;
[self setTextField:myTextField];
// insert UITextField before first button
BOOL inserted = NO;
for( UIView *view in self.subviews ){
if(!inserted && ![view isKindOfClass:[UILabel class]])
[self insertSubview:myTextField aboveSubview:view];
}

//[self addSubview:myTextField];
// ensure that layout for views is done once
layoutDone = NO;

// add a transform to move the UIAlertView above the keyboard
CGAffineTransform myTransform = CGAffineTransformMakeTranslation(0.0, kUIAlertOffset);
[self setTransform:myTransform];
}
return self;
}

/*
* Show alert view and make keyboard visible
*/
- (void) show {
[super show];
[[self textField] becomeFirstResponder];
}

/*
* Determine maximum y-coordinate of UILabel objects. This method assumes that only
* following objects are contained in subview list:
* - UILabel
* - UITextField
* - UIThreePartButton (Private Class)
*/
- (CGFloat) maxLabelYCoordinate {
// Determine maximum y-coordinate of labels
CGFloat maxY = 0;
for( UIView *view in self.subviews ){
if([view isKindOfClass:[UILabel class]]) {
CGRect viewFrame = [view frame];
CGFloat lowerY = viewFrame.origin.y + viewFrame.size.height;
if(lowerY > maxY)
maxY = lowerY;
}
}
return maxY;
}

/*
* Override layoutSubviews to correctly handle the UITextField
*/
- (void)layoutSubviews {
[super layoutSubviews];
CGRect frame = [self frame];
CGFloat alertWidth = frame.size.width;

// Perform layout of subviews just once
if(!layoutDone) {
CGFloat labelMaxY = [self maxLabelYCoordinate];

// Insert UITextField below labels and move other fields down accordingly
for(UIView *view in self.subviews){
if([view isKindOfClass:[UITextField class]]){
CGRect viewFrame = CGRectMake(
kUITextFieldXPadding,
labelMaxY + kUITextFieldYPadding,
alertWidth - 2.0*kUITextFieldXPadding,
kUITextFieldHeight);
[view setFrame:viewFrame];
} else if(![view isKindOfClass:[UILabel class]]) {
CGRect viewFrame = [view frame];
viewFrame.origin.y += kUITextFieldHeight;
[view setFrame:viewFrame];
}
}

// size UIAlertView frame by height of UITextField
frame.size.height += kUITextFieldHeight + 2.0;
[self setFrame:frame];
layoutDone = YES;
} else {
// reduce the x placement and width of the UITextField based on UIAlertView width
for(UIView *view in self.subviews){
if([view isKindOfClass:[UITextField class]]){
CGRect viewFrame = [view frame];
viewFrame.origin.x = kUITextFieldXPadding;
viewFrame.size.width = alertWidth - 2.0*kUITextFieldXPadding;
[view setFrame:viewFrame];
}
}
}
}
@end

Wednesday, April 28, 2010

Avoiding iPhone App Rejection From Apple

FROM http://www.mobileorchard.com/avoiding-iphone-app-rejection-from-apple/

I’ve been developing apps for the iPhone for over 6 months now. Over this time period, I’ve successfully submitted over 45 apps, the majority under my own company’s iTunes account. Given the large number of app submissions, I’ve had my share of app rejections.

As has been mentioned many many times on the various developer forums, Apple’s approval process can be very frustrating and inconsistent. However, if you are careful, you can greatly reduce your risk of getting an app rejected.

Based on my own experiences, here’s my list of things to be careful about:

1. The dreaded HIG Violation of Apple’s Human Interface Guidelines (HIG) is probably the most frequent cause for an app to be rejected. As an iPhone developer, you definitely need to read the HIG and follow the guidelines! Take every single item mentioned in the HIG seriously. Yes, many apps demonstrate gross violations of the HIG (after all, splash screens are a no-no according to the HIG), but you can never claim “App X does this and is already in the store” as an excuse when Apple rejects your app. Well, you can claim that, but Apple will not accept it as a valid justification.

There are certain items in the HIG to which Apple does seem to turn a blind eye (splash screens probably being the most obvious), but unless you like taking chances with your livelihood, avoid going against any of the HIG guidelines.

2. Matching icons Believe it or not, Apple is now requiring the 512×512 iTunes Store icon match the 57×57 icon displayed on the iPhone. As a reason for rejection, Apple will state having unmatching icons is in violation of the HIG. There’s nothing in the HIG that states these two icons must match, but since it’s Apple’s store, you basically need to play by their arbitrary rules. So, unless you want unnecessary delays in your approval, make sure your icons match. The icons don’t have to be identical, but there should be something shared between the two. Just having them both be pictures that are of a similar theme is not enough.

3. Simulating failures Apple doesn’t like anything that pretends the iPhone or iPod Touch is failing. So, simulating a cracked screen is a good way to get your app rejected. Any other idea of faking a failure of the iPhone which you might think others would find amusing will probably also be a reason for rejection from Apple.

4. Button images must be consistent If you decide to use one of the existing images Apple provides for buttons, be careful you use it for an identical function. While the HIG states you can use a standard button in a non-standard way if your app is providing a “immersive environment”, you are better off creating your own custom buttons to avoid the risk of rejection. If you use the “action button” image, make sure tapping on it brings up a menu with choices. If it doesn’t, Apple may reject the app.

5. Bandwidth usage over cellular networks If your app downloads data over the cellular network, ensure you do not use too much bandwidth. How much is too much? Well, there isn’t an exact number, but a tech support person from Apple advised me to not exceed 4.5 meg of data per 5 minutes of activity. You can test your app’s usage by going into your iPhone settings, choosing the General->Usage menu and clearing the stats. Then run your app for 5 minutes, return to this screen and see what the stats say. Also, to get the most accurate numbers, you should turn off any other network activity on your phone while you run the test (such as Email or MobileMe updates).

6. Popup for network detection If your app requires the use of the Internet, you must detect when the network is unavailable and provide a pop-up message informing the user. Just having the spinning busy icon display and a message saying “trying to connect” is not sufficient. Apple will reject your app if you don’t provide a message informing the user that they need a network connection.

7. False claims of a missing network On a related note, make sure you don’t have any false positives in your network detection. There’s a bug in the “reachability” functions provided by Apple. If you don’t first try to perform a network connection but instead just do a reachability test, the code will always report the network is unavailable. Apple will reject your app if they discover you have this false positive case.

8. Political lampooning Don’t make any jokes about political figures, past or present, in either your app or the description in iTunes. Apple will most-likely reject your app.

9. Ensure your app description is accurate Spend some time proof-reading your app description for iTunes. This description is the only information the reviewer is going to have about your app. Make sure there isn’t anything ambiguous in the description. If there is room for misunderstanding a feature, you run the risk of the reviewer rejecting the app because they felt the app does not behave as described.

10. Keep your “what’s new” descriptions brief Whenever you submit an update, Apple requires you to provide a description of what is new in the app. Related to the previous note above, try to be as clear and concise as possible. Don’t go into too much detail describing what has changed, otherwise you introduce more opportunity for the reviewer to misunderstand what has changed. I’ve had an app rejected because the reviewer misunderstood what I said had changed in the app.

11. OS compatibility If you claim your app works with OS 2.0 and higher, you better make sure you test whether your app really does work on all the OS versions between 2.0 and the current one. The reviewer most-likely will! There are some anomalies in the behavior of certain functions across the different versions of the OS (for example, reachability code returns a slightly different set of flags under 2.0 vs. 2.2, UILabels don’t respond to the Touch events under 2.1 and earlier, etc.). If the reviewer finds the app does not work properly with a certain version of the OS, the app will rightly be rejected. However, don’t expect the reviewer to mention that they were testing using a different OS. That little detail is usually not mentioned in the rejection email, leading to the potential for lost time trying to find a bug while testing with a different OS than the reviewer. Again, test the app with every version you claim to support.

This is by no means an exhaustive list. It’s just the things I’ve personally run into in my app submissions and is a laundry list of items I keep in the back of my mind whenever I’m developing a new app.

If you do find your app is rejected, the best advice I can give is try to remain calm. Remember there are thousands of other developers in your shoes. We feel your pain. It often feels unfair, and perhaps it is unfair at times. It can be a terribly frustrating experience, especially when you’ve done your best to follow every guideline Apple provides and you might be convinced Apple is wrong in their assessment of your app.

But, it’s Apple’s store. They can do whatever they want in the end and don’t have to be fair. If you feel your app has been wrongfully rejected, the best you can do is be courteous and try to outline your position, quoting from whatever relevant Apple documentation applies. But, in the end, don’t expect Apple to yield to your request, or in many cases, even acknowledge your request. Apple is generally very brief in their email responses and sometimes totally silent. The best bet for an approval is to implement a change based on Apple’s reason for rejection and move on. Trying to win an argument because you feel you are right is not going to be productive.

And if you do get a rejection, add it to your list of things to avoid for the next app.




Six weeks ago guest author Brian Stormont posted an article here titled Avoiding iPhone App Rejection From Apple. While writing a rejection story is almost a rite of passage amongst iPhone developers, Brian took a prescriptive what not to do angle.

Brian’s story elicited a big response. Dozens of people contributed comments and wrote privately to supply additional gotchas, tips and approaches. While some weren’t helpful — e.g., “Be Trent Reznor,” in reference to the rejection then approval, unchanged, of the Nine Inch Nails app — many were.

We’ve collated, consolidated, summarized and (except when the authors asked us not to) attributed the collective wisdom to present it to you here:

1. Trademarks, Particularly Icons — Numerous apps, including Bump, the Billionth App ran into delays and rejections for including icons and imagery that a Apple deemed a trademark violation. Common culprits: iPhone-like icons and Polaroid-like image frames.

2. Giveaways/Prize Apps/Contests — While not expressly forbidden in the contracts, Apple rejects prize applications and apps that contains contests or giveaways. There are exceptions to this policy. For example, Apple seems willing to let game applications tie into an on-the-web leaderboard with prizes, though an in-app/embedded leaderboard with prizes is likely verboten. However, as the policy is either unwritten or unavailable for review outside of Apple, trying to create an app that narrowly fits within the inferred acceptable parameters or operates similarly to existing giveaway apps already in the store is risky.

3. Don’t Ask, Don’t Tell — Sometimes being above-board doesn’t pay. An example: Alan Francis wrote to tell us about his experience submitting an app that included the Pinch Analytics, an package used in thousands of apps that collects anonymized usage data. As a courtesy to his users, Alan stated that he was collecting this data and provided an opt-out mechanism. Either one of these measures is unusual; combined, almost unheard of. His app was rejected until he added a giant warning label on first run, while thousands of other applications that failed to mention including analytics were allowed in.

4. Avoid Humor Where It’s Not Expected, Or Where It Violates The HIG —
An update to the Instant New York app was rejected when its developers jokingly included the phrase “extra dragons” in their release notes — though, as noted by Jeff Richardson, Apple did approve an update to Google’s app with release notes containing “longer version number” and “ninja.” Carl HerrMann’s intentionally silly BellyButton app was rejected for a disabled “lint” button, the HIG violating joke being that nobody would want link in their bellybutton.

5. Inadvertent “Objectionable Content” — Last week, the story of the rejection and then later approval of Eucalyptus — a library app featuring over 20,000 classic books — was widely reported upon. The app sourced freely available content from Project Gutenberg. Buried in the archives was a Victorian, text-only translation of the Kama Sutra of Vatsyayana. Apple rejected the app on “objectionable content” grounds. Its author, after first trying to resolve the issue with Apple, blogged about his experience whence it was picked up widely. Shortly thereafter Apple reversed its decision. Eucalyptus was the latest in a series; previous examples: Tweetie, the popular Twitter client, rejected because people swear on Twitter; Jesse Tayler’s Craig’s List Browser because, well, take your pick — it’s Craig’s list!; and Jelle Prins’s Lyrics because not all songs are PG. Wired’s story about Jelle adding a dirty-word filter, and the easter egg to disable it, is worth reading.

6. Update Spam — There’s some indication that Apple frowns upon publishing no-change updates in an attempt to keep your app appearing in the what’s new listings. Noel Llopis provided a humorous example: “I submitted an update for Tea Time. The update was just a bug fix with the images in the picker, so in the what’s new field I wrote. ‘Fixed a bug that would occasionally display the wrong image for a tea type.’ Apple rejected the update a week later saying that they ‘tried it, but the image never changed for different tea types’. I was totally baffled until I realized they were testing the countdown screen, which has a static image of a tea cup, not the images on the picker. So I had to resubmit the same binary adding ‘in the picker’ to the what’s new description.”

7. Doesn’t Work. Doesn’t Work As Advertised — Reportedly, the most common reason for rejecting an app is that it simply doesn’t work or doesn’t work as advertised. Seems obvious, and I wouldn’t have bothered to report it if it wasn’t apparently so common.

8. Public Figures — Brian’s original article included “political lampooning.” I’ll extend that to include association or portrayal of public figures. Two examples: around Obama’s inauguration, CodeMorphic created an app called Obamify that manipulated photos to appear like those iconic posters from the campaign; the app went into infinite review. Yak Apps had to remove imagery containing Mr. and Mrs. Obama before their “First Dog” app was approved.

9. Too Few Potential Consumers (Or The Appearance Thereof) — Memo Akten produced remote control software that conforms to the TUIO protocol for sending multi-touch events over WiFi. Apple rejected it on grounds that its market was too small and suggested, instead, Ad Hoc Distribution. I spent 10 minutes trying to figure out how small this niche is and was ready to write it off until I discovered that this field is connected (conceptually, at least) to Microsoft’s Surface technology and is covered by the analyst firm IDG. Would an overtaxed app reviewer at Apple spend the time to make this determination? Best bet is to save them the work by supplying them with evidence in your submission has a vast, mainstream audience — or at least a sizable niche one.

Updated: 10. Don’t Include Price In Your Description — Just minutes after I originally posted this article Michael Kaye added a very good additional tip: “Don’t mention pricing in the App Description. For example mentioning ‘now only $1.99′ will according to Apple, ‘potentially confuse users’…and they have a point as its 99 pence in the UK, €1.99 in Europe etc.” Thanks for the great comment Michael!

Monday, April 26, 2010

NSString's skill

[[devToken description] stringByTrimmingCharactersInSet:[NSCharacterSet characterSetWithCharactersInString:@"<>"]]

Bubble_Sort

- (void)bubble_sort {
int i = [finalRouteList count]-1;
bool change = true;

for (; i>=1&&change; --i) {
change = false;
for (int j = 0; j Route *myRoute1 = [finalRouteList objectAtIndex:j];
Route *myRoute2 = [finalRouteList objectAtIndex:j+1];
AvegoAppDelegate *delegate = (AvegoAppDelegate *)[[UIApplication sharedApplication] delegate];
if ((pow([myRoute1.fromLat doubleValue]-delegate.locationManager.location.coordinate.latitude, 2)+pow([myRoute1.fromLon doubleValue]-delegate.locationManager.location.coordinate.longitude, 2))>(pow([myRoute2.fromLat doubleValue]-delegate.locationManager.location.coordinate.latitude, 2)+pow([myRoute2.fromLon doubleValue]-delegate.locationManager.location.coordinate.longitude, 2))){
[finalRouteList exchangeObjectAtIndex:j withObjectAtIndex:j+1];
change = true;
}
}
}
}

Friday, April 23, 2010

10 secs hidden

CGContextRef context = UIGraphicsGetCurrentContext();
[UIView beginAnimations:nil context:context];
[UIView setAnimationCurve:UIViewAnimationCurveEaseInOut];
[UIView setAnimationDuration:0.3];
[ToolBar setAlpha:1.0];
[UIView commitAnimations];
[self performSelector:@selector(Hidden) withObject:nil afterDelay:1.0];


-(void)Hidden{
CGContextRef context = UIGraphicsGetCurrentContext();
[UIView beginAnimations:nil context:context];
[UIView setAnimationCurve:UIViewAnimationCurveEaseInOut];
[ToolBar setAlpha:0.0];
[UIView setAnimationDuration:0.3];
[UIView commitAnimations];
}

Wednesday, April 7, 2010

NSDataAdditions : 64Encoding...


@interface NSData (NSDataAdditions)
+ (NSData *) dataWithBase64EncodedString:(NSString *) string;
- (id) initWithBase64EncodedString:(NSString *) string;

- (NSString *) base64Encoding;
- (NSString *) base64EncodingWithLineLength:(unsigned int) lineLength;

- (BOOL) hasPrefix:(NSData *) prefix;
- (BOOL) hasPrefixBytes:(void *) prefix length:(unsigned int) length;
@end



// Created by khammond on Mon Oct 29 2001.
// Formatted by Timothy Hatcher on Sun Jul 4 2004.
// Copyright (c) 2001 Kyle Hammond. All rights reserved.
// Original development by Dave Winer.

#import "NSDataAdditions.h"

static char encodingTable[64] = {
'A','B','C','D','E','F','G','H','I','J','K','L','M','N','O','P',
'Q','R','S','T','U','V','W','X','Y','Z','a','b','c','d','e','f',
'g','h','i','j','k','l','m','n','o','p','q','r','s','t','u','v',
'w','x','y','z','0','1','2','3','4','5','6','7','8','9','+','/' };

@implementation NSData (NSDataAdditions)
+ (NSData *) dataWithBase64EncodedString:(NSString *) string {
return [[[NSData allocWithZone:nil] initWithBase64EncodedString:string] autorelease];
}

- (id) initWithBase64EncodedString:(NSString *) string {
NSMutableData *mutableData = nil;

if( string ) {
unsigned long ixtext = 0;
unsigned long lentext = 0;
unsigned char ch = 0;
unsigned char inbuf[4], outbuf[3];
short i = 0, ixinbuf = 0;
BOOL flignore = NO;
BOOL flendtext = NO;
NSData *base64Data = nil;
const unsigned char *base64Bytes = nil;

// Convert the string to ASCII data.
base64Data = [string dataUsingEncoding:NSASCIIStringEncoding];
base64Bytes = (const unsigned char *)[base64Data bytes];
mutableData = [NSMutableData dataWithCapacity:[base64Data length]];
lentext = [base64Data length];

while( YES ) {
if( ixtext >= lentext ) break;
ch = base64Bytes[ixtext++];
flignore = NO;

if( ( ch >= 'A' ) && ( ch <= 'Z' ) ) ch = ch - 'A';
else if( ( ch >= 'a' ) && ( ch <= 'z' ) ) ch = ch - 'a' + 26;
else if( ( ch >= '0' ) && ( ch <= '9' ) ) ch = ch - '0' + 52;
else if( ch == '+' ) ch = 62;
else if( ch == '=' ) flendtext = YES;
else if( ch == '/' ) ch = 63;
else flignore = YES;

if( ! flignore ) {
short ctcharsinbuf = 3;
BOOL flbreak = NO;

if( flendtext ) {
if( ! ixinbuf ) break;
if( ( ixinbuf == 1 ) || ( ixinbuf == 2 ) ) ctcharsinbuf = 1;
else ctcharsinbuf = 2;
ixinbuf = 3;
flbreak = YES;
}

inbuf [ixinbuf++] = ch;

if( ixinbuf == 4 ) {
ixinbuf = 0;
outbuf [0] = ( inbuf[0] << 2 ) | ( ( inbuf[1] & 0x30) >> 4 );
outbuf [1] = ( ( inbuf[1] & 0x0F ) << 4 ) | ( ( inbuf[2] & 0x3C ) >> 2 );
outbuf [2] = ( ( inbuf[2] & 0x03 ) << 6 ) | ( inbuf[3] & 0x3F );

for( i = 0; i < ctcharsinbuf; i++ )
[mutableData appendBytes:&outbuf[i] length:1];
}

if( flbreak ) break;
}
}
}

self = [self initWithData:mutableData];
return self;
}

#pragma mark -

- (NSString *) base64Encoding {
return [self base64EncodingWithLineLength:0];
}

- (NSString *) base64EncodingWithLineLength:(unsigned int) lineLength {
const unsigned char *bytes = (const unsigned char *)[self bytes];
NSMutableString *result = [NSMutableString stringWithCapacity:[self length]];
unsigned long ixtext = 0;
unsigned long lentext = [self length];
long ctremaining = 0;
unsigned char inbuf[3], outbuf[4];
unsigned short i = 0;
unsigned short charsonline = 0, ctcopy = 0;
unsigned long ix = 0;

while( YES ) {
ctremaining = lentext - ixtext;
if( ctremaining <= 0 ) break;

for( i = 0; i < 3; i++ ) {
ix = ixtext + i;
if( ix < lentext ) inbuf[i] = bytes[ix];
else inbuf [i] = 0;
}

outbuf [0] = (inbuf [0] & 0xFC) >> 2;
outbuf [1] = ((inbuf [0] & 0x03) << 4) | ((inbuf [1] & 0xF0) >> 4);
outbuf [2] = ((inbuf [1] & 0x0F) << 2) | ((inbuf [2] & 0xC0) >> 6);
outbuf [3] = inbuf [2] & 0x3F;
ctcopy = 4;

switch( ctremaining ) {
case 1:
ctcopy = 2;
break;
case 2:
ctcopy = 3;
break;
}

for( i = 0; i < ctcopy; i++ )
[result appendFormat:@"%c", encodingTable[outbuf[i]]];

for( i = ctcopy; i < 4; i++ )
[result appendString:@"="];

ixtext += 3;
charsonline += 4;

if( lineLength > 0 ) {
if( charsonline >= lineLength ) {
charsonline = 0;
[result appendString:@"\n"];
}
}
}

return [NSString stringWithString:result];
}

#pragma mark -

- (BOOL) hasPrefix:(NSData *) prefix {
unsigned int length = [prefix length];
if( ! prefix || ! length || [self length] < length ) return NO;
return ( memcmp( [self bytes], [prefix bytes], length ) == 0 );
}

- (BOOL) hasPrefixBytes:(void *) prefix length:(unsigned int) length {
if( ! prefix || ! length || [self length] < length ) return NO;
return ( memcmp( [self bytes], prefix, length ) == 0 );
}
@end



File Compress then Encoding, finally Send

NSData *fileData = [[NSData alloc] initWithContentsOfFile: filePath];
NSData *gzippedData = [fileData gzipDeflate];
NSString *base64String = [NSString stringWithFormat:@"z:%@", [gzippedData base64Encoding]];
[fileData release];

NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:[NSURL URLWithString:url]];

NSString *s = [[NSString alloc] initWithFormat:@"data=%@", base64String];
DLog(@"log is %@", s);
[request setHTTPBody:[s dataUsingEncoding:NSUTF8StringEncoding]];
[s release];

[request setHTTPMethod:@"POST"];

NSURLResponse *response;
NSError *error;
[NSURLConnection sendSynchronousRequest:request returningResponse:&response error:&error];

iPad/iPhone forward geocoding API using Google gecoding service

The iPhone (and now iPad) SDK supports reverse geocoding out of the box. Reverse geocoding means you have a coordinate and want to find out where you are located, for instance, the name of the street you are on. Forward geocoding means you know the name of a location and want to find the coordinates. For reasons not totally clear to me there’s no support for forward geocoding, I’ve read somewhere that it has something to do with license deals.
I’ve started working a little with the iPad simulator and with its large display it makes sense presenting information on a map view. Because I haven’t found a good example/API for forward geocoding I’ve decided to publish my own. There are a few players offering geocoding services, Yahoo, CloudeMade, Tele Atlas and of course Google. Yahoo and Google are both free but I’ve decided on using Google.

This sample iPad application contains a search bar and a large UIMapView. Search results are visualized on the map with a placemark and by clicking the placemark the map will zoom to the viewport returned from the geocoding service. When I started working with the geocoding service it was still in version two, this last week Google launched version three. Version two will be depricated of course but I had already written the parser for version two so I’ve included it as well in my sample. Of course the API works for iPhone as well. Because the API contains quite a lot of code you will find the code inside the sample project at the bottom of this page.



Using my Forward geocoding API

Using BSForwardGeocoder is pretty straight forward. Example below:


- (void) searchBarSearchButtonClicked:(UISearchBar *)theSearchBar {

NSLog(@"Searching for: %@", searchBar.text);
if(forwardGeocoder == nil)
{
forwardGeocoder = [[BSForwardGeocoder alloc] initWithDelegate:self];
}

// Forward geocode!
[forwardGeocoder findLocation:searchBar.text];

}
-(void)forwardGeocoderFoundLocation
{
if(forwardGeocoder.status == G_GEO_SUCCESS)
{
int searchResults = [forwardGeocoder.results count];

// Add placemarks for each result
for(int i = 0; i < searchResults; i++)
{
BSKmlResult *place = [forwardGeocoder.results objectAtIndex:i];

// Add a placemark on the map
CustomPlacemark *placemark = [[CustomPlacemark alloc] initWithRegion:place.coordinateRegion];
placemark.title = place.address;
[mapView addAnnotation:placemark];

NSArray *countryName = [place findAddressComponent:@"country"];
if([countryName count] > 0)
{
NSLog(@"Country: %@", ((BSAddressComponent*)[countryName objectAtIndex:0]).longName );
}

[countryName release];
}

if([forwardGeocoder.results count] == 1)
{
BSKmlResult *place = [forwardGeocoder.results objectAtIndex:0];

// Zoom into the location
[mapView setRegion:place.coordinateRegion animated:TRUE];
}

// Dismiss the keyboard
[searchBar resignFirstResponder];
}
}


When search has executed the geocoder contains the result and a status code. The status code is from Google and to support both version 2 and 3 of the API there’s an enum containing response codes. If everything goes well the status should be “G_GEO_SUCCESS” and the “results” property will contain an array of BSKmlResult objects which contain the location information returned for the query. Here’s an example query for my home town Stockholm (Google geocoding service version 3) : http://maps.google.com/maps/api/geocode/xml?address=stockholm&sensor=false.
The details for the service can be found here: http://code.google.com/apis/maps/documentation/geocoding/.
There are multiple arguments you can pass to the search url, you should read the geocoding documentation and check the search url in the application before you implement this in your own app.

BSForwardGeocoder

#import
#import "BSGoogleV2KmlParser.h"
#import "BSGoogleV3KmlParser.h"

// Enum for geocoding status responses
enum {
G_GEO_SUCCESS = 200,
G_GEO_BAD_REQUEST = 400,
G_GEO_SERVER_ERROR = 500,
G_GEO_MISSING_QUERY = 601,
G_GEO_UNKNOWN_ADDRESS = 602,
G_GEO_UNAVAILABLE_ADDRESS = 603,
G_GEO_UNKNOWN_DIRECTIONS = 604,
G_GEO_BAD_KEY = 610,
G_GEO_TOO_MANY_QUERIES = 620
};

@protocol BSForwardGeocoderDelegate
@required
-(void)forwardGeocoderFoundLocation;
@optional
-(void)forwardGeocoderError:(NSString *)errorMessage;
@end

@interface BSForwardGeocoder : NSObject {
NSString *searchQuery;
NSString *googleAPiKey;
int status;
NSArray *results;
id delegate;
}
-(id) initWithDelegate:(id)del;
-(void) findLocation:(NSString *)searchString;

@property (assign) id delegate;
@property (nonatomic, retain) NSString *searchQuery;
@property (nonatomic, readonly) int status;
@property (nonatomic, retain) NSArray *results;

@end


BSKmlResult
The result class is the same for both version 2 and 3 of the service. A big difference in the returned information between the versions is the way address components are returned, in version three more information is returned and therefore I’ve created another class to store the address component information. The properties: countryNameCode, countryName, subAdministrativeAreaName and localityName are for version two only. For version three all address information is stored in the “addressComponents” array (contains BSAddressComponent objects). Because there is really no reason to use version 2 anymore you probably want to remove this code for your own application.

#import
#import
#import "BSAddressComponent.h"

@interface BSKmlResult : NSObject {
NSString *address;
NSString *countryNameCode;
NSString *countryName;
NSString *subAdministrativeAreaName;
NSString *localityName;
float viewportSouthWestLat;
float viewportSouthWestLon;
float viewportNorthEastLat;
float viewportNorthEastLon;
float boundsSouthWestLat;
float boundsSouthWestLon;
float boundsNorthEastLat;
float boundsNorthEastLon;
float latitude;
float longitude;
float height;
NSInteger accuracy;
NSArray *addressComponents;
}

@property (nonatomic, retain) NSString *address;
@property (nonatomic, assign) NSInteger accuracy;
@property (nonatomic, retain) NSString *countryNameCode;
@property (nonatomic, retain) NSString *countryName;
@property (nonatomic, retain) NSString *subAdministrativeAreaName;
@property (nonatomic, retain) NSString *localityName;
@property (nonatomic, retain) NSArray *addressComponents;
@property (nonatomic, assign) float latitude;
@property (nonatomic, assign) float longitude;
@property (nonatomic, assign) float viewportSouthWestLat;
@property (nonatomic, assign) float viewportSouthWestLon;
@property (nonatomic, assign) float viewportNorthEastLat;
@property (nonatomic, assign) float viewportNorthEastLon;
@property (nonatomic, assign) float boundsSouthWestLat;
@property (nonatomic, assign) float boundsSouthWestLon;
@property (nonatomic, assign) float boundsNorthEastLat;
@property (nonatomic, assign) float boundsNorthEastLon;
@property (readonly) CLLocationCoordinate2D coordinate;
@property (readonly) MKCoordinateSpan coordinateSpan;
@property (readonly) MKCoordinateRegion coordinateRegion;

-(NSArray*)findAddressComponent:(NSString*)typeName;

@end


To make it somewhat simple to find address components I’ve added a method that will search for components for you. Using version 3 of the geocoding service you will get the country name using this code:

NSArray *countryName = [BSKmlResultPlace findAddressComponent:@"country"];
if([countryName count] > 0)
{
NSLog(@"Country: %@", ((BSAddressComponent*)[countryName objectAtIndex:0]).longName );
}
[countryName release];


There are also properties to make the result simple to use with a MKMapView. The “coordinate” property returns a CLLocationCoordinate2D object, “coordinateSpan” calculates and returns a MKCoordinateSpan object for setting the map viewport. The “coordinateRegion” combines both coordinate and coordinateSpan returning a MKCoordinateRegion object that can be used to directly move your MKMapView to the right place.

BSAddressComponent


#import


@interface BSAddressComponent : NSObject {
NSString *longName;
NSString *shortName;
NSArray *types;
}

@property (nonatomic, retain) NSString *longName;
@property (nonatomic, retain) NSString *shortName;
@property (nonatomic, retain) NSArray *types;


Learned from[url]http://blog.sallarp.com/ipad-iphone-forward-geocoding-api-google/[/url]