CA Bearsfan
CA Bearsfan

Reputation: 474

Help with making NSURLConnection in a custom class

Hey all, I'm trying to use Yahoo's PlaceFinder to do reverse geocoding for an app I'm making. Problem is I need to use the NSURLConnection to call to my database. So I decided to make a custom class that is initialized with the user's latitude and longitude, and only store a string variable containing the state the user is in.

Update the following code now works fine....

Here is the .h

#import <Foundation/Foundation.h>
#import "CJSONDeserializer.h"

@interface StateFinder : NSObject 
{
    NSString *userState;
    NSURLConnection *connection;
}

-(id)initwithLatitude:(NSString *)latitude andLongitude:(NSString *)longitude;
@property (nonatomic, retain) NSString *userState;
@property (nonatomic, retain) NSURLConnection *connection;

@end

and the .m

#import "StateFinder.h"

@implementation StateFinder

@synthesize userState;
@synthesize connection;

-(id)initwithLatitude:(NSString *)latitude andLongitude:(NSString *)longitude
{
    if(self = [super init])
    {
        NSString *lat = latitude;
        NSString *lon = longitude;

        NSString *stateURLFinder = [NSString stringWithFormat:@"http://where.yahooapis.com/geocode?q=%@,+%@&gflags=R&flags=J&appid=zqoGxo7k", lat, lon];

        //NSLog(stateURLFinder);

        NSURL *stateURL = [NSURL URLWithString:stateURLFinder];


        NSURLRequest *request = [[NSURLRequest alloc] initWithURL: stateURL];

        connection = [[NSURLConnection alloc] initWithRequest:request delegate:self];

        [request release];
    }
    return self;
}


-(void)connection:(NSURLConnection *)connection didReceiveResponse:(NSURLResponse *)response
{
    NSLog(@"didReceiveResponse");
}

- (void)connectionDidFinishLoading:(NSURLConnection *)connection
{
    NSLog(@"didFinishLoading");
}

- (void)connection:(NSURLConnection *)connection didFailWithError:(NSError *)error
{
    NSLog(@"didFailWithError");
}

- (void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)data 
{
    // Store incoming data into a string
    NSString *jsonString = [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding];

    NSLog(jsonString);
    // Yes, this is incomplete, but I was waiting for the method to fire before going
        // any further. This will at least show me the JSON data being returned from yahoo 
        // in string format so I can output it to the console via NSLog
}

- (void)dealloc 
{
    [userState release];
    [connection release];
    [super dealloc];
}

@end

This is the current code I'm using and it works fine. All I did was include the connectionDidFinishLoading and didFailWithError methods to the original code. With regards to the connection being released before it was made, I used the code above as is without the previously mentioned methods and neither didReceiveData/didReceiveResponse would hit. It wasn't until those 2 methods were included that the methods began getting called. Not sure how, not sure why, but that was the only change among all of those suggested that worked. Big thanks to @Jiva DeVoe , @XJones , @jlehr and @Aby for all the tips/hints/suggestions!

Upvotes: 0

Views: 1767

Answers (4)

XJones
XJones

Reputation: 21967

Get rid of the line:

[connection scheduleInRunLoop:[NSRunLoop mainRunLoop] forMode:NSDefaultRunLoopMode];

Once you've initialized the connection with the request it runs automatically. You have other problems as well. Your implementation of the NSURLConnectionDelegate protocol is wrong. You need to incrementally add data received in connection:didReceiveResponse to an NSMutableData object. You can convert it to JSON in `connectionDidFinishLoading:'.

As for the memory leak, release the connection in connectionDidFinishLoading: or connection:didFailWithError: You are guaranteed to receive one but not both of them.

Everything you need to know is here

[EDIT: added NSURLConnection code sample]

// Attach this to the touchUpInside event of a UIButton
// Note that all objects will be autoreleased
// Note that you can comment out any or all of the NSURLConnection delegate methods
// and the request will execute

- (IBAction)initiateRequest
{
    NSURLRequest *request = [NSURLRequest requestWithURL:[NSURL urlWithString:@"http://www.google.com"]];
    NSURLConnection *connection = [NSURLConnection connectionWithRequest:request delegate:self];
}

- (void)connection:(NSURLConnection *)connection didRecieveData:(NSData *)date
{
    NSLog(@"connection:didReceiveData");
}

- (void)connection:(NSURLConnection *)connection didReceiveResponse:(NSURLResponse *)response
{
    NSLog(@"connection:didReceiveResponse:");
}

- (void)connectionDidFinishLoading:(NSURLConnection *)connection
{
    NSLog(@"connectionDidFinishLoading:");
}

- (void)connection:(NSURLConnection *)connection didFailWithError:(NSError *)error
{
    NSLog(@"connection:didFailWithError:");
}

Upvotes: 1

CA Bearsfan
CA Bearsfan

Reputation: 474

Thanks to XJones for the suggestion. I wasn't including the two methods didFinishLoading or didFailWithError once inserted into my code, both didReceiveResponse and didReceiveData both began to hit. Thank you all for the tips, advice and suggestions and hopefully this helps someone else out down the road.

Upvotes: 0

jlehr
jlehr

Reputation: 15617

You're sending a release message to the connection before it's had a chance to run; don't do that. With regards to JSON, the string you said was returned from the server is JSON. Were you expecting something else?

Upvotes: 1

Jiva DeVoe
Jiva DeVoe

Reputation: 1348

Actually, I advise that you definitely do not use sendSynchronousRequest. You should always try to use asynchronous networking unless the app you're developing is a command line app without a run loop.

I suspect your problem may be that you're releasing the connection immediately, so it's never getting a chance to run. You should add a member variable and keep it around until you've received a response or whatever.

Bonus tip:

Guessing you're probably doing this for either iOS or the Mac, and writing GUI apps. These apps have run loops. When you use synchronous networking like the prior answer suggests, you prevent that runloop from executing. This means that if the request takes longer than a few seconds, your iOS app will be killed by the OS, and your Mac app will appear non-responsive. Neither of these are good results.

Upvotes: 4

Related Questions