I am using NSURLConnection to download some JSON from a webservice, and then display in a UITableView. I have all the code working well in the view class, but I wondered if I could have the NSURLConnection methods available to other classes?
For example, something like the following:
NSURLConnectionClass *connection = [[NSURLConnectionClass alloc] init];
NSArray *myDataArray = [connection withURL: [NSURL URLWithString: #"http://www.example.com"]];
// Reload table with new data
I realise this wont work as NSURLConnection is asynchronous, but wondered if there was something else I could try. I'm basically trying to avoid repeating code in every view that downloads data.
You can create a delegate protocol for your custom connection class. This way it can download async and still and call back when done. Even better would be to use blocks for callbacks. This pattern is used in the popular ASIHttpRequest class.
You can even make this class the delegate and data source for the table view. This way you only have to call [tableview reloadData] when done loading. Downside is that this mixes up the MVC pattern a bit.
Related
I am creating a class for soap webservice to get some information from .net webserver.
for that i am using NSMutableURLRequest and parse the result using NSXmlParser.
Now i am calling that web services class from myviewcontroller.m class like this.
mywebserviceClass *obj=[[mywebserviceClass alloc] init];
[obj mymethod];
i am adding result to an array to use that array details in myviewcontroller.m class.
but i did n't get details into array while i am using that array immediately after this method.
i am trying like this by calling another method after 2 sec to use that array like this.
[self performSelector:#selector(myanotherMethod) withObject:nil afterDelay:2];
i know the reason why it is, it takes time to parse.
I am trying another way like i am creating object for viewcontroller and call this method like this.
myviewcontroller *obj=[[myviewcontroller alloc] init];
[obj myanothermethod];
NOw i can able to get details but i can't able to handle UIActivities like raising alerts.
While as told in the above performSelector method i can able to handle all UIActivities.
But i need to call that method after completion of parsing of result.
Can any one please help me.
Thank you.
Call your web service in viewDidLoad/ViewWillAppear methods of UIViewController. Collect your data and then call Parse method like- [self performSelectorOnMainThread:#selector(methodName) waitUntilDone:YES]; Below that use your array to display view or if you are displaying in table view then just call [mytblviewobj reloadData];
I have an application in which I am required to connect to the internet after a view is loaded. However, if I put this code in the viewDidLoad method the parent view freezes, and then unfreezes after the connection onto the new view. However, I would like the new view to load FIRST, and then to start the connection. I tried using viewDidAppear:, however I am getting the same issue.
Also, will any animations continue playing during the connection? Will the UI be responsive? If not, is multithreading the way to go?
Here is some of my code:
-(void)viewDidLoad {
[super viewDidLoad];
//Do some other view initialization
//Connect is a class I use to connect to the internet
[Connect getData:someString];
}
When I put the code in viewDidAppear the same thing happens.
Connection code:
NSMutableURLRequest *request = [[NSURLRequest alloc] initWithURL:url];
NSHTTPURLResponse *response;
NSError *error;
NSData *responseData = [NSURLConnection sendSynchronousRequest:request returningResponse:&response error:&error];
Also, I forgot to mention that I am running a regular expression as well after the connection.
As the name of the method says, the view has already been loaded when viewDidLoad executes.
Generally, be sure to use asynchronous connections to connect to the internet. Never block the main thread.
it is easier than you may think.
All you need is some thread management. On the view did Do:
[NSThread detachNewThreadSelector:#selector(yourMethod:) toTarget:yourTarget withObject:yourObject];
and later in another part do:
- (void)yourMethod:(id)sender{
//download the info but do not update the GUI
[self performSelectorOnMainThread:#selector(updatingTheGUI:) withObject:yourObject waitUntilDone:NO]
}
- (void)updatingTheGUI:(id)sender{
//Update your GUI
}
You will notice that the viewDidLoad method documentation of UIViewController states:
...Called after the controller’s view is loaded into memory.
This doesn't necessarily mean that it's called after the view is displayed on screen.
To answer your other questions, if you make your network request the way you have described, no, animations will not continue playing while the request is in progress and no, you can't guarantee that the UI will be responsive. This is because the network request will take an unknown amount of time. Therefore, if you make the request on the main thread, the main thread will be blocked for that period of time, however long it takes.
And, as for the last question, is multithreading the way to go? As others have stated, the easiest and probably most popular way of handling this is to initialize the NSURLConnection with initWithRequest:delegate:. The delegate being your UIViewController or Connect class, or whatever class you want to conform to the NSURLConnectionDelegate protocol and use the NSURLConnectionDelegate methods to process the downloaded data. NSURLConnection will do the work asynchronously and keep the main thread free to handle animations, displaying the UI, etc.
I know it sounds a bad idea for your app. performance but try giving a delay or sleep in between to check if it works that way. Later try to implement the asynchronous call as someone earlier stated..
I am writing an app that uses multithreading and cache. Quite similar to Apple's TopSongs sample code. Upon startup, I need to grab the value of an object in a JSON feed.
Now I am afraid doing so would ruin the workflow of the app, and block a thread or something if I use NSURLConnection. Can I just download that JSON feed (it only has one object) without using NSURLConnection's delegate methods? If I implement the delegate methods in my delegate file, then the app will finish up applicationDidFinishLaunching:application method and THEN go to
connection:didReceiveResponse:
connection:didReceiveData:
connection:didFailWithError:
connectionDidFinishLoading:
How can I avoid that? I need to get the timestamp (stored in that JSON file) right at the beginning of applicationDidFinishLaunching:application as the rest of that method depend on that timestamp.
Thank you,
There are two ways to achieve this:
Use the sendSynchronousRequest of NSURLConnection, but this will block until a response is received
+ (NSData *)sendSynchronousRequest:(NSURLRequest *)request returningResponse:(NSURLResponse **)response error:(NSError **)error
or (the preferable option)
Move the code which relies on the JSON to a seperate method. Use the asynchronous + delegate methods of NSURLConnection as normal, but in your applicationDidFinishLaunching method display some sort of HUD to inform the user of what's happening and then in the connection:didReceiveResponse method or connectionDidFinishLoading method call your new method which relies on the JSON (and will now have the JSON) and dismiss the HUD.
I'm not sure I understand what you mean when you said "the app will finish up applicationDidFinishLaunching and THEN go to
connection:didReceiveResponse:
connection:didReceiveData:
connection:didFailWithError:
connectionDidFinishLoading:"
Could you explain that a little more?
As for the point about the method depending on the timestamp that you need to get from the JSON file, I would suggest that you need to review your application design and what you are doing in your methods.
I have a view that includes a UIWebView as well as an iAD AdBannerView.
To optimize the experience and reduce bandwidth contention - I suspend the UIWebView's network activity when the iAd "detail view" is being loaded and resume it when the user returns from the ad. Currently, I simply do the following:
-(BOOL)bannerViewActionShouldBegin:(ADBannerView *)banner willLeaveApplication:(BOOL)willLeave
{
if (self.webView.loading) {
[self.webView stopLoading];
self.loadingWasInterrupted = TRUE;
}
return YES;
}
-(void)bannerViewActionDidFinish:(ADBannerView *)banner
{
if (self.loadingWasInterrupted) {
//Should use loadRequest: instead?
[self.webView reload];
}
}
I'm trying to understand if it there is any difference between calling reload vs. loadRequest: a second time, and if so, which is more efficient.
I'm guessing reload simply just saves you having to hold onto the request object and really does the same thing but I'd like to know for sure. The docs and header don't offer any clue.
I understand I could pick apart the network activity to understand what's happening but would appreciate any insight from someone who has looked at this before or who generally understands if reload behavior differs from loadRequest at all. Thank you.
Okay, a fairly complicated solution but never the less one that I think might work for you:
Define a custom protocol handler for http:
http://developer.apple.com/library/mac/#documentation/Cocoa/Conceptual/URLLoadingSystem/URLLoadingSystem.html#//apple_ref/doc/uid/10000165i
Using NSURLProtocol as the subclass. The handler will start a NSURLConnection, and return the data as it comes in to the client (in this case this will be the UIWebView that initiated the connection). Have it add itself as an observer to NSNotificationCenter for a "Pause" notification.
When you would like to display an iAd, you can send your pause notification, this will cancel the NSURLConnection (but not the NSURLProtocol, which will remain open and loading, and thus your webview will continue to appear as if it were loading).
Then when the add is finished you can send a "resume" notification (much the same), except in this case any active NSURLProtocol handlers receiving the notification will create new NSURLConnections, using the Range: header to resume where they left off:
iphone sdk: pausing NSURLConnection?
Some caveats:
Only will work when browsing websites that support the resume header (otherwise you might have to start the connection anew, and just ignore data prior to the latest byte received).
Your NSURLRequests should be formed so that they don't have a timeout. (if you want a timeout then it should be in the NSURLProtocol handlers NSURLConnections).
I'm guessing here, but I believe the reload is doing a loadRequest: internally. If you are really intent on testing this you cold add a temporary subclass to UIWebView and override the reload and loadRequest methods just for the sake of logging.
- (void)reload
{
NSLog(#"reload called");
[super reload];
}
- (void)loadRequest:(NSURLRequest *)request
{
NSLog(#"loadRequest called");
[super loadRequest:request];
}
When you call the method "loadRequest", you call it like
[webview loadRquest:[NSURLRequest requestWithURL:[NSURL URLWithString:#"http://www.ururl.com"]]];
But when u call "reload", u just instruct to reload whatever the current request is.
So basically in latter case , u are saving urself from creating a url from string and then a request from url and that makes it pretty convenient for use.
As per case of functionality, reload itself calls loadRequest so basically there is no difference in terms of efficiency and even in speed.
However basically for ur case and in many of my cases , the thing which we want but Apple has not given us is something like:-
[webview pauseLoading];
[webview resumeLoading];
So to sum up the whole thing , use any of them but if u r lazy like me to again specify the urlrequest just use "reload"
Apologies
Sorry guys I used to think that reload must be calling loadRequest but when I tried NWCoder's method, it doesnot call loadRequest on calling reload. But I still think reload and loadRquest follows the same method for loading the page
In my iPhone/iPad app I'm handling all network and web-API-requests through a "APIManager" (singleton, created in AppDelegate).
Currently the APIManager contains only one single ASINetworkQueue, to which APIRequests (subclass of ASIHTTPRequest) are added and executed. In the userInfo of each APIRequest some additional information to handle the request is added (like whether the response should be parsed into Core Data - and if so, which entity - or not).
When the user moves from one view (ViewA) to another (ViewX), I would like to have the possibility to cancel all the requests that ViewA have asked the APIManager to perform, while letting others continue.
Is there some functionality to find a certain request in a ASINetworkQueue (or NSOperationQueue) and send a cancellation message to it? Maybe using parameters added to the userInfo of the request?
Thanks in advance!
[queue operations] will return an NSArray of items in the queue, which you can then iterate and call 'cancel' on any you like.
Something like:
for (ASIHTTPRequest *req in [queue operations])
{
if (shouldCancel(req))
[req cancel];
}