Im am trying to properly integrate MBProgressHUD in a project while i am downloading and processing some data. Forgive me my ignorance if i am asking stupid things, but i'm a complete noob...
I have a "Data" class that is handling my HTTPRequests, so i thing here is the proper place to put some stuff in:
-(void)resolve
{
// Create the request.
NSURLRequest *theRequest=[NSURLRequest requestWithURL:[[NSURL alloc] initWithString:[self url]]
cachePolicy:NSURLRequestUseProtocolCachePolicy
timeoutInterval:60.0];
NSLog(#"Resolving content from: %#",[self url]);
// create the connection with the request
// and start loading the data
NSURLConnection *theConnection=[[NSURLConnection alloc] initWithRequest:theRequest delegate:self];
if (theConnection) {
// Create the NSMutableData to hold the received data.
// receivedData is an instance variable declared elsewhere.
receivedData = [[NSMutableData data] retain];
} else {
NSLog(#"Content - resolve: connection failed");
}
// Here is the part that it makes me crazy...
HUD = [[MBProgressHUD showHUDAddedTo:self.view animated:YES] retain];
return;
}
- (void)connection:(NSURLConnection *)connection didReceiveResponse:(NSURLResponse *)response
{
// This method is called when the server has determined that it
// has enough information to create the NSURLResponse.
// It can be called multiple times, for example in the case of a
// redirect, so each time we reset the data.
// receivedData is an instance variable declared elsewhere.
expectedLength = [response expectedContentLength];
currentLength = 0;
HUD.mode = MBProgressHUDModeDeterminate;
[receivedData setLength:0];
}
- (void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)data
{
currentLength += [data length];
HUD.progress = currentLength / (float)expectedLength;
// Append the new data to receivedData.
// receivedData is an instance variable declared elsewhere.
[receivedData appendData:data];
return;
}
- (void)connection:(NSURLConnection *)connection didFailWithError:(NSError *)error
{
[HUD hide:YES];
// release the connection, and the data object
[connection release];
// receivedData is declared as a method instance elsewhere
[receivedData release];
// inform the user
NSLog(#"Content - Connection failed! Error - %# %#",
[error localizedDescription],
[[error userInfo] objectForKey:NSURLErrorFailingURLStringErrorKey]);
return;
}
Now i know that putting the "showHUDaddedTo" in the -(void)resolve is not the right way...
I am calling this data function from a view controller with an IBaction like this:
-(IBAction) btnClicked:(id) sender {
if ([[issue status] intValue] == 1 ) // issue is not downloaded
{
[(Content *)[issue content] resolve];
//HUD = [[MBProgressHUD showHUDAddedTo:self.navigationController.view animated:YES] retain];
}
else // issue is downloaded - needs to be archived
{
NSError * error = nil;
[[NSFileManager defaultManager] removeItemAtPath:[(Content *)[issue content] path] error:&error];
if (error) {
// implement error handling
}
else {
Content * c = (Content *)[issue content];
[c setPath:#""];
[issue setStatus:[NSNumber numberWithInt:1]];
[buttonView setTitle:#"Download" forState:UIControlStateNormal];
[gotoIssue setTitle:#"..." forState:UIControlStateNormal];
// notify all interested parties of the archived content
[[NSNotificationCenter defaultCenter] postNotificationName:#"contentArchived" object:self]; // make sure its persisted!
}
}
}
Simply said: i would like to call all the MBProgressHUD stuff from my Conten.m file in the moment i push the download button in my IssueController.m file. I think you see now where my problem is: i am not a coder ;-) Any help is appreciated.
Cheers,
sandor
I would try and use the HUD method showWhileExecuting. Wrap your calls up in a new method and call that to download your information. When your method finishes, so will your HUD.
Also, is there a reason why you are retaining your calls for the HUD? I have not seen that before and I'm not sure if you need to retain those as they are autoreleased.
Related
I have a PHP RESTful server that I connect to to invoke methods based on the URL path called from my objective-c program. I use the ASIHTTPRequest and SBJson.
Everything works well but I'm doing requests in different controllers and now duplicated methods are in all those controllers. What is the best way to abstract that code (requests) in a single class, so I can easily re-use the code, so I can keep the controllers more simple.
Please save me from what I feel to be a redundant and repetitive coding sequence.
I have tried to make a singleton class, but each view controller requires different request connect points, and most view controllers require specific actions to be called when the succeeded method is called and Im not sure how to handle it all.
Here is what my code template currently looks like in the Objective-c program:
//*1) Somewhere at the top of my .m file I have this*
//These are the suffixes to the URL path that I connect to at my server,
//depending on the action required
NSString *const RequestCreateCustomer = #"Create/Customer";
NSString *const RequestUpdateCustomer = #"Update/Customer";
NSString *const RequestDeleteCustomer = #"Delete/Customer";
//*2) Then I have my connection invocation code*
//Method invocation, all of them look something like this
-navigationButton{
...
[self retrieveWithRequestStringType:RequestUpdateCustomer];
}
-(void)retrieveWithRequestStringType:(NSString*)typeOfRequest{
NSLog(#"Retrieve %# method called", typeOfRequest);
NSString *urlString = [NSString stringWithFormat:#"%#/Secure/CB/%#", #"http://www.defaultStapleURLToMyServer.com/CB", typeOfRequest];
NSString *encodedUrlString = [urlString stringByAddingPercentEscapesUsingEncoding:NSUTF8StringEncoding];
NSURL *url = [[NSURL alloc] initWithString:encodedUrlString];
serverRequest = nil;
serverRequest = [ASIFormDataRequest requestWithURL:url];
[serverRequest addRequestHeader:#"Content-Type" value:#"application/json"];
[serverRequest addRequestHeader:#"Request-Method" value:#"POST"];
//Normally at this point depending on the request type, I prepare some data that needs to be sent along with the request
NSMutableDictionary *completeDataArray = [[NSMutableDictionary alloc] init];
if([typeOfRequest isEqualToString:RequestCreateCustomer]){
[serverRequest setUserInfo:[NSDictionary dictionaryWithObject:RequestCreateCustomer forKey:#"RequestType"]];
if( ! [self validateAndPrepareAllData:&completeDataArray]){
return;
}
}
else if([typeOfRequest isEqualToString:RequestUpdateCustomer]){
[serverRequest setUserInfo:[NSDictionary dictionaryWithObject:RequestUpdateCustomer forKey:#"RequestType"]];
if( ! [self validateAndPrepareAllData:&completeDataArray]){
return;
}
}
else if([typeOfRequest isEqualToString:RequestDeleteCustomer]){
[serverRequest setUserInfo:[NSDictionary dictionaryWithObject:RequestDeleteCustomer forKey:#"RequestType"]];
if( ! [self validateAndPrepareCustomerIdData:&completeDataArray]){
return;
}
}
NSString *jsonString = [completeDataArray JSONRepresentation];
[serverRequest appendPostData:[jsonString dataUsingEncoding:NSUTF8StringEncoding]];
[serverRequest setDelegate:self];
[serverRequest setDidFinishSelector:#selector(requestSucceeded:)];
[serverRequest setDidFailSelector:#selector(requestFailed:)];
[serverRequest startAsynchronous];
}
//*3) And heres the connection did succeed, and connection did fail methods*
-(void)requestSucceeded:(ASIHTTPRequest*)request{
NSInteger statusCode = [[[request responseHeaders] objectForKey:#"StatusCode"] intValue];
NSLog(#"StatusCode: %#", [[request responseHeaders] objectForKey:#"StatusCode"]);
NSString *myString = [[NSString alloc] initWithData:[request responseData] encoding:NSUTF8StringEncoding];
NSDictionary *JSONDictionary = [myString JSONValue];
switch (statusCode) {
case 400:
case 401:
{
NSLog(#"display error message");
UIAlertView *alert = [[UIAlertView alloc] initWithTitle:#"Error" message:[[request.responseString JSONValue] objectForKey:#"Message"] delegate:nil cancelButtonTitle:#"Ok" otherButtonTitles: nil];
[alert show];
break;
}
case 200:
NSLog(#"status code = 200 so successful");
//Created customer request succeeded
if([[[request userInfo] objectForKey:#"RequestType"] isEqualToString:RequestCreateCustomer]){
[self.delegate addedCustomerVCWithCustomer:[[CustomerDataModel alloc] initWithJSONData:[JSONDictionary objectForKey:#"CustomerObject"]]];
[self cancelModalView];
}
//Edit customer request succeeded
else if([[[request userInfo] objectForKey:#"RequestType"] isEqualToString:RequestUpdateCustomer]){
[self.delegate editedCustomerVCWithCustomer:[[CustomerDataModel alloc] initWithJSONData:[JSONDictionary objectForKey:#"CustomerObject"]]];
[self cancelModalView];
}
//Delete customer request succeeded
else if([[[request userInfo] objectForKey:#"RequestType"] isEqualToString:RequestDeleteCustomer]){
[self.delegate deletedCustomer:customer];
[self cancelModalView];
}
break;
default:
[SVProgressHUD showErrorWithStatus:[NSString stringWithFormat:#"went to none: %d or %#", (int)statusCode, [[request responseHeaders] objectForKey:#"StatusCode"]]];
NSLog(#"went to none: %d or %#", (int)statusCode, [[request responseHeaders] objectForKey:#"StatusCode"]);
break;
}
[SVProgressHUD dismiss];
}
-(void)requestFailed:(ASIHTTPRequest*)request{
if([[[request userInfo] objectForKey:#"RquestType"] isEqualToString:RequestCreateCustomer]){
NSLog(#"retrieving states failed so trying again");
[self addCustomerRequest];
}
else if(){
//... you get the point
}
}
Can someone help out with an alternative solution?
create a new class that has blocks/delegate (but prefers block) on it.. let's say Request.h and Request.m.. and make a public method with a block parameter.. example
this should be public methods
typedef void(^CompletionBlock)(id results);
typedef void(^ErrorBlock)(NSError *error);
- (void)setCompetionBlock:(CompletionBlock)block; and
- (void)setErrorBlock:(ErrorBlock)error;
and make a variable name.. this should be private variables
CompletionBlock completionBlock;
ErrorBlock errorBlock;
and declare this methods in your .m
- (void)setCompletionBlock:(CompletionBlock)aCompletionBlock {
completionBlock = [aCompletionBlock copy];
}
- (void)setErrorBlock:(ErrorBlock)aError {
errorBlock = [aError copy];
}
- (void)reportSuccess:(id)results {
if (completionBlock) {
dispatch_async(dispatch_get_main_queue(), ^{
completionBlock(results);
});
}
}
- (void)reportFailure:(NSError *)error {
if (errorBlock) {
dispatch_async(dispatch_get_main_queue(), ^{
errorBlock(error);
});
}
}
and call [self reportSuccess:jsonSuccess]; under your success methods, in your code it is -(void)requestSucceeded:(ASIHTTPRequest*)request{ and [self reportFailure:NSerror*] under -(void)requestFailed:(ASIHTTPRequest*)request
and to call this class
Request *req = [Request alloc]init];
[req retrieveWithRequestStringType:#"sample"];
[req setCompletion:^(id result){
}];
[req setErrorBlock:(NSError *error) {
}];
Hai here Am downloading a video and storing it to documents folder in iphone... but when i click on download button video will start downloading and progress will show nicely... but when i go back to previous view controller and came back to the downloading controller during downloading the progress view status will not show the current status... can anybody help me to solve this? thanks in advance...
Here is my code...
- (void)downLoad:(UIButton *)sender {
_downloadBtn.hidden = YES;
_videoData = [[NSMutableData alloc] init];
_resourceName = [_resourceNameArray objectAtIndex:sender.tag];
_resourceType = [_contentTypesArray objectAtIndex:sender.tag];
NSString *urlStr = [NSString stringWithFormat:#"%#",[_videoUrlArray objectAtIndex:sender.tag]];
[NSURLConnection connectionWithRequest:[NSURLRequest requestWithURL:[NSURL URLWithString:urlStr]] delegate:self ];
}
- (void)connection:(NSURLConnection *)connection didReceiveResponse:(NSURLResponse *)response {
NSLog(#"Downloading Started");
_length = [response expectedContentLength];
NSLog(#"Size:%0.2f",_length);
}
-(void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)data {
[_videoData appendData:data];
float progress = (float)[_videoData length]/(float)_length;
NSLog(#"Progress:%0.2f",progress);
//_timeLabel.text = [NSString stringWithFormat:#"%0.2F%%",progress*100];
[_progressView setProgress:progress animated:YES];
}
-(void)connection:(NSURLConnection *)connection didFailWithError:(NSError *)error {
NSLog(#"didFailWithError");
}
-(void)connectionDidFinishLoading:(NSURLConnection *)connection {
[self saveLocally:_videoData res:_resourceName type:_resourceType];
NSLog(#"File Saved !");
}
Download screen -> previous screen -> Download screen. Let's try:
-(void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)data
{
[_videoData appendData:data];
float progress = (float)[_videoData length]/(float)_length;
NSLog(#"Progress:%0.2f",progress);
NSNumer *n = [NSNumber numberWithFloatValue:progress];
[[NSNotificationCenter defaultCenter] postNotificationName:NOTIFICATION_DOWNLOAD_PROGRESS object:n];
//_timeLabel.text = [NSString stringWithFormat:#"%0.2F%%",progress*100];
[_progressView setProgress:progress animated:YES];
}
When you go back to download screen, you should update progress by receive notification
#define NOTIFICATION_DOWNLOAD_PROGRESS #"NOTIFICATION_DOWNLOAD_PROGRESS"
- (void)viewDidLoad
{
[super viewDidLoad];
[[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(processProgressBar:) name:NOTIFICATION_DOWNLOAD_PROGRESS object:nil];
}
- (void) processProgressBar:(NSNumber *) progress
{
[_progressView setProgress: progress.floatValue];
}
In this application when user presses a button a url request will be sent to a server and some corresponding data will be received by the application.
My problem is I want to receive the data completely from the server and when the connection did finished receiving the data, change the view and pass the received data to the other view.
After the conversion of data I receive error and app crashes.
I tried this approach but don't know what should I do exactly.
Problem Solved and Code is Edited
#import "ViewController.h"
#interface ViewController ()
//#property (nonatomic, strong) NSMutableData *responseData;
#end
#implementation ViewController
#synthesize responseData;
#synthesize myTableView;
- (void)viewDidLoad {
[super viewDidLoad];
NSLog(#"viewdidload");
}
- (void)didReceiveMemoryWarning
{
[super didReceiveMemoryWarning];
}
- (void)connection:(NSURLConnection *)connection didReceiveResponse:(NSURLResponse *)response {
NSLog(#"Response received From the server.");
[self.responseData setLength:0];
}
- (void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)data {
NSLog(#"appending data to the response object.");
[self.responseData appendData:data];
}
- (void)connection:(NSURLConnection *)connection didFailWithError:(NSError *)error {
NSLog(#"didFailWithError");
NSLog([NSString stringWithFormat:#"Connection failed: %#", [error description]]);
}
- (void)connectionDidFinishLoading:(NSURLConnection *)connection {
NSLog(#"Loading data succeeded! Received %d bytes of data",
[self.responseData length]);
//call the converter to convert the received
//Data to Json Packet and save to variable
[self convertDataToJson];
[self changePage];
}
-(void)changePage {
resultsTableViewController *myTableView = [[resultsTableViewController alloc]init];
myTableView.modalTransitionStyle = UIModalTransitionStyleCoverVertical;
myTableView=[myTableView init];
[self presentViewController:myTableView animated:YES completion:NULL];
}
-(void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender
{
resultsTableViewController *mytable = [segue destinationViewController];
NSMutableArray * response = self.responseArray;
mytable.responseArray = response;
}
- (IBAction)get:(id)sender {
//making an instance of data query object and passing the word and dictionary selection to it.
if ( ![[searchField text]isEqualToString:#""] )
{
word = #”test”
[self makeRequestForWord:word withDictionaryName:#""];
// NSData *query = [word dataUsingEncoding:NSUTF8StringEncoding];
}
}
-(void)makeRequestForWord:(NSString*)word withDictionaryName:(NSString*)dicSelect;
{
//creating URL Request Object using given URL object.
//It requests data using nsConnection
}
- (void)convertDataToJson
{
// some conversion and save into responseDATA
}
- (void)viewDidUnload {
[super viewDidUnload];
}
#end
And then in function related to segue I'll send the object to the next view.
You shouldn't be presenting the view controller in connectionDidFinishLoading: since it appears that you have a segue set up for the controller -- it's crashing there because you haven't created myTable yet (declaring it in the .h file doesn't create it). The segue will create the instance of your controller, so you should call performSegueWithIdentifier:sender: in connectionDidFinishLoading:
- (void)connectionDidFinishLoading:(NSURLConnection *)connection {
NSLog(#"Loading data succeeded! Received %d bytes of data",
[self.responseData length]);
//call the converter to convert the received
//Data to Json Packet and save to variable
[self convertDataToJson];
[self performSegueWithIdentifier:#"MyIdentifier" sender:self];
}
I'm creating an app which communicates alot with a server. I want to make sure I have only 1 connection at a time - only 1 request pending. I want so that if I try to send another request, It will wait until the current one is finished before sending the next.
How can I implement This?
Tnx!
I don't know of any automatic mechanism to do this. So you have to write a new class yourself (let's call it ConnectionQueue):
Basically, instead of a creating the NSURLConnection directly, you call a method of your ConnectionQueue class (which should have exactly one instance) taking the NSURLRequest and the delegate as a parameter. Both are added to a queue, i.e. a separate NSArray for the requests and the delegates.
If the arrays contains just one element, then no request is outstanding and you can create a NSURLConnection with the specified request. However, instead of the delegate passed to the method, you pass your ConnectionQueue instance as the delegate. As a result, the connection queue will be informed about all actions of the connection. In most cases, you just forward the callback to the original delegate (you'll find it in the first element of the delegate array).
However, if the outstanding connection completes (connection:didFailWithError: or connectionDidFinishLoading: is called), you first call the original delegate and then remove the connection from the two arrays. Finally, if the arrays aren't empty, you start the next connection.
Update:
Here's some code. It compiles but hasn't been tested otherwise. Furthermore, the implementation of the NSURLConnectionDelegate protocol is incomplete. If you're expecting more than implemented callbacks, you'll have to add them.
Header File:
#import <Foundation/Foundation.h>
#interface ConnectionQueue : NSObject {
NSMutableArray *requestQueue;
NSMutableArray *delegateQueue;
NSURLConnection *currentConnection;
}
// Singleton instance
+ (ConnectionQueue *)sharedInstance;
// Cleanup and release queue
+ (void)releaseShared;
// Queue a new connection
- (void)queueRequest:(NSURLRequest *)request delegate:(id)delegate;
#end
Implementation:
#import "ConnectionQueue.h"
#implementation ConnectionQueue
static ConnectionQueue *sharedInstance = nil;
+ (ConnectionQueue*)sharedInstance
{
if (sharedInstance == nil)
sharedInstance = [[ConnectionQueue alloc] init];
return sharedInstance;
}
+ (void)releaseShared
{
[sharedInstance release];
sharedInstance = nil;
}
- (id)init
{
if ((self = [super init])) {
requestQueue = [NSMutableArray arrayWithCapacity:8];
delegateQueue = [NSMutableArray arrayWithCapacity:8];
}
return self;
}
- (void)dealloc
{
[requestQueue release];
[delegateQueue release];
[currentConnection cancel];
[currentConnection release];
[super dealloc];
}
- (void)startNextConnection
{
NSURLRequest *request = [requestQueue objectAtIndex:0];
currentConnection = [[NSURLConnection alloc] initWithRequest:request delegate:self];
}
- (void)queueRequest:(NSURLRequest *)request delegate:(id)delegate
{
[requestQueue addObject:request];
[delegateQueue addObject:delegate];
if ([requestQueue count] == 1)
[self startNextConnection];
}
- (void)connection:(NSURLConnection *)connection didReceiveResponse:(NSURLResponse *)response
{
id delegate = [delegateQueue objectAtIndex:0];
[delegate connection: connection didReceiveResponse: response];
}
- (void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)data
{
id delegate = [delegateQueue objectAtIndex:0];
[delegate connection: connection didReceiveData: data];
}
- (void)connection:(NSURLConnection *)connection didFailWithError:(NSError *)error
{
id delegate = [delegateQueue objectAtIndex:0];
[delegate connection: connection didFailWithError:error];
[currentConnection release];
currentConnection = nil;
[requestQueue removeObjectAtIndex:0];
[delegateQueue removeObjectAtIndex:0];
if ([requestQueue count] >= 1)
[self startNextConnection];
}
- (void)connectionDidFinishLoading:(NSURLConnection *)connection
{
id delegate = [delegateQueue objectAtIndex:0];
[delegate connectionDidFinishLoading: connection];
[currentConnection release];
currentConnection = nil;
[requestQueue removeObjectAtIndex:0];
[delegateQueue removeObjectAtIndex:0];
if ([requestQueue count] >= 1)
[self startNextConnection];
}
#end
To use the connection queue, create an NSURLRequest instance and then call:
[[ConnectionQueue sharedInstance] queueRequest:request delegate:self];
There's no need to create the singleton instance of ConnectionQueue explicitly. It will be created automatically. However, to properly clean up, you should call [ConnectionQueue releaseShared] when the application quits, e.g. from applicationWillTerminate: of your application delegate.
I'm building an iPhone app that aggregates data from several different data sources and displays them together in a single table. For each data source, I've created a class (WeatherProvider, TwitterProvider) that handles connecting to the datasource, download the data and storing it in an object.
I have another class ConnectionManager, that instantiates and calls each of the two provider classes, and merges the results into a single NSArray which is displayed in the table.
When I run the program calling just the WeatherProvider or just the TwitterProvider, it works perfectly. I can call each of those objects again and again to keep them up-to-date without a problem. I can also call TwitterProvider and then WeatherProvider without a problem.
However, if I call WeatherProvider then TwitterProvider, the TwitterProvider hangs just as I call: [[NSURLConnection alloc] initWithRequest:request delegate:self];.
Other points:
- it doesn't seem to matter how long between when I call WeatherProvider and TwitterProvider, TwitterProvider still hangs.
- in WeatherProvider, I'm using NSXMLParser with NSAutoreleasePool to parse the output from the WeatherProvider.
- ConnectionManager creates an instance of WeatherProvider and TwitterProvider when the application is started, and reuses those instances when the user requests a data refresh.
- I ran the app with the activity monitor connected and it verifies that the app is basically just hanging. No CPU usage, or additional memory allocations or network activity seems to be happening.
There's a bunch of code spread across a couple files, so I've tried to include the relevant bits (as far as I can tell!) here. I very much appreciate any help you can provide, even if it is just additional approaches to debugging.
WeatherProvider
-(void)getCurrentWeather: (NSString*)lat lon:(NSString*)lon lastUpdate:(double)lastUpdate
{
double now = [[NSDate date] timeIntervalSince1970];
NSString *noaaApiUrl;
// don't update if current forecast is < than 1 hour old
if(now - lastUpdate < 3600)
{
[[self delegate] weatherUpdaterComplete:1];
return;
}
// if we already have a forecast, delete and refill it.
if(forecast)
{
[forecast release];
forecast = [[WeatherForecast alloc] init];
}
forecast.clickThroughUrl = [NSString stringWithFormat:#"http://forecast.weather.gov/MapClick.php?lat=%#&lon=%#",
lat, lon];
noaaApiUrl = [NSString stringWithFormat:#"http://www.weather.gov/forecasts/xml/sample_products/browser_interface/ndfdBrowserClientByDay.php?lat=%#&lon=%#&format=24+hourly",
lat, lon];
NSURLRequest *noaaUrlRequest = [NSURLRequest requestWithURL:[NSURL URLWithString:noaaApiUrl]];
[[NSURLConnection alloc] initWithRequest:noaaUrlRequest delegate:self];
}
#pragma mark -
#pragma mark NSURLConnection delegate methods
- (void)connection:(NSURLConnection *)connection didReceiveResponse:(NSURLResponse *)response
{
self.noaaData = [NSMutableData data];
}
- (void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)data
{
[noaaData appendData:data];
}
- (void)connection:(NSURLConnection *)connection didFailWithError:(NSError *)error
{
self.noaaConnection = nil;
[[self delegate] weatherUpdaterError:error];
[connection release];
}
- (void)connectionDidFinishLoading:(NSURLConnection *)connection
{
// Spawn a thread to fetch the data so UI isn't blocked while we parse
// the data. IMPORTANT - don't access UIKit objects on 2ndary threads
[NSThread detachNewThreadSelector:#selector(parseNoaaData:) toTarget:self withObject:noaaData];
// the noaaData will be retailed by the thread until parseNoaaData: has finished executing
// so, we'll no longer need a reference to it in the main thread.
self.noaaData = nil;
[connection release];
}
#pragma mark -
#pragma mark NSXMLParser delegate methods
- (void)parseNoaaData:(NSData *)data
{
NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
NSXMLParser *parser = [[NSXMLParser alloc] initWithData:data];
[parser setDelegate:self];
[parser parse];
[parser release];
[pool release];
}
TwitterProvider
-(void)getLocationTimeline:(NSString*)lat lon:(NSString*)lon lastUpdate:(double)lastUpdate refreshUrl:(NSString*)newUrl
{
NSString *updateURL;
if(tweets.count > 1)
[tweets removeAllObjects];
updateURL = [NSString stringWithFormat:#"http://search.twitter.com/search.json?geocode=%#,%#,1mi&rpp=%#&page=1", lat, lon, TWITTER_LOCAL_UPDATES_URL_RESULTS];
_serviceResponse = [[NSMutableData data] retain];
NSURLRequest *request = [NSURLRequest requestWithURL:[NSURL URLWithString:updateURL]];
[[NSURLConnection alloc] initWithRequest:request delegate:self];
}
#pragma mark -
#pragma mark NSURLConnection delegate methods
- (void)connectionDidFinishLoading:(NSURLConnection *)connection
{
NSString *responseString = [[NSString alloc] initWithData:_serviceResponse encoding:NSUTF8StringEncoding];
// parse tweets
[responseString release];
[_serviceResponse release];
[connection release];
// tell our delegate that we're done!
[[self delegate] twitterUpdaterComplete:tweets.count];
}
- (void)connection:(NSURLConnection *)connection
didReceiveResponse:(NSURLResponse *)response
{
[_serviceResponse setLength:0];
}
- (void)connection:(NSURLConnection *)connection
didReceiveData:(NSData *)data
{
[_serviceResponse appendData:data];
}
- (void)connection:(NSURLConnection *)connection
didFailWithError:(NSError *)error
{
[connection release];
[[self delegate] twitterUpdaterError:error];
}
I was able to fix this by removing the threading in WeatherUpdater that was being used for the NSXMLParser. I had 'barrowed' that code from Apple's SiesmicXML sample app.