Update UI when using stringWithContentsofurl - iphone

I am using stringWithcontentsofurl to download some strings from my web server to the App, but i would like to update the UI to show A loader of some kind. I am downloading a number of strings (it can be sometimes as much as 100) so it would be neat for the user to show something so they know the App isn't crashing, because now the UI is stuck, i can't show A UILoader or something like that. Is there an option to do so? Or maybe A alternative to stringWithcontentsofurl where this is possible?
greets,
Erik

Try this. It loads stuff in background. If updating the UI later (in asyncload), be sure to do that on main thread.
-(void)loadappdetails:(NSString*)appid {
NSString* searchurl = [#"https://itunes.apple.com/lookup?id=" stringByAppendingString:appid];
[self performSelectorInBackground:#selector(asyncload:) withObject:searchurl];
}
-(void)asyncload:(NSString*)searchurl {
NSURL* url = [NSURL URLWithString:searchurl];
NSError* error = nil;
NSString* str = [NSString stringWithContentsOfURL:url encoding:NSUTF8StringEncoding error:&error];
if (error != nil) {
NSLog(#"Error: %#", error);
}
NSLog(#"str: %#", str);
}

This is a classic case for "Lazy Loading". See Apple's example code:
http://developer.apple.com/library/ios/#samplecode/LazyTableImages/Introduction/Intro.html
It should be easy to substitute "strings" for "images" as you read that code.
You want to display a placeholder in the label like "(fetching information)" or similar and then load the information in the background, either threading the fetches yourself (asynchronous fetching) or using a library like ASIHTTPRequest that handles all the asynchronous nuts and bolts for you, calling a delegate method once the fetch has completed.

Unfortunately, stringWithContentsOfURL is a synchronous method, meaning it will block your thread and you will not receive any callbacks while it is running. This is also bad for user experience.
The alternative would be to use NSURLConnection to manually setup your own request, and tap into the delegate methods of your connection to display some sort of progress bar. Specifically, you'll want to use connection:didReceiveData: and connectionDidFinishLoading:
Once all the data has been received, use the following to get your string.
NSString *theString = [[NSString alloc] initWithData:yourData encoding:UTF8StringEncoding];

To make this method asynch it is possible and recommendable to use Grand Central Dispatch. If anyone is interested I will show the few lines of code to do that.

Related

How to download things without display hanging?

Okay. So I'm creating this app and I'm really happy with it apart from one thing. It uses a lot of internet database checking etc. and every time it does this it hangs until it has finished, spoiling the fluidity of the interface. Is there anyway to avoid this? Download stuff happens when you click a button or sometimes just as a result of an NSTimer. I would love for the user to be able to go through the interface fluidly and the app to update the info when it gets there. IS there anyway to do this?
EDIT - Current Synchronous code:
This is to download a CSV file:
NSString *link=[[NSString alloc] initWithFormat:#"http://d.yimg.com/autoc.finance.yahoo.com/autoc?query=%#&callback=YAHOO.Finance.SymbolSuggest.ssCallback",timer.userInfo];
NSURL *url=[[NSURL alloc] initWithString:link];
NSData *dl=[[NSData alloc] initWithContentsOfURL:url];
NSString *str = [[NSString alloc] initWithData:dl encoding:NSUTF8StringEncoding];
This is to download a png file into a webView:
NSString *link=[[NSString alloc] initWithFormat:#"http://chart.finance.yahoo.com/z?s=%#&t=GOOG&q=m",self.Input.text];
url=[[NSURL alloc] initWithString:link];
[self.graph loadRequest:[NSURLRequest requestWithURL:url]];
It sounds like you're making synchronous requests. You shouldn't be doing that on the main thread. Your best option is probably to switch to asynchronous requests. You could make synchronous requests on a background thread, but that's more complicated and unnecessary in most situations.

get UIWebView's success loading response

I want to read the data in the UIWebView response when it succeeded loading the url.
Catching this data in this method didn't give me the response:
- (void) webViewDidFinishLoad:(UIWebView *)_webView {
EDIT:
I want to catch the NSURLResponse
Thanks,
Nur
Have you implemented –webView:didFailLoadWithError: ?
Probably an error when loading the URL.
I don't know enough about the architecture of your app to really say, but it might be useful for you to know that you can call arbitrary JavaScript on the page in your UIWebView from outside the UIWebView, and get the response back:
NSString *script = #"$('#textInputField').value";
NSString *result = [webView stringByEvaluatingJavaScriptFromString:script];
If you store the response in a JavaScript variable, you should be able to read it back up into your Obj-C this way.

iOS: ASIHTTPRequst synchronous on background selector bad idea?

I was using NSURLConnection in a synchronous way before running on a background selector, so when I moved over to ASIHTTPRequest I did the same with this framework.
So, is it a bad idea to do something like the following?
// From another method
[self performSelectorInBackground:#selector(callDatasource) withObject:nil];
- (NSData *)callDatasource {
NSAutoreleasePool *apool = [[NSAutoreleasePool alloc] init];
ASIHTTPRequest *request = [ASIHTTPRequest requestWithURL:someURLthatIamusing];
[request setTimeOutSeconds:50.0];
[request startSynchronous];
NSError *error = [request error];
NSData *returnedData;
if (!error) {
returnedData = [request responseData];
} else {
// do something with error
}
[self performSelectorOnMainThread:#selector(done) withObject:nil waitUntilDone:NO];
[apool release];
return returnedData;
}//end
What would be the advantage to use the ASIHTTPRequest and asynchronous methods along with the delegate methods?
From experience, sometimes odd things can happen when using ASIHTTPRequest synchronous requests off a secondary thread: the download activity icon in the status bar not disappearing upon download completion is one issue I've noticed from time to time. I've had no major problems in the past, but I use the asynchronous methods now rather than your approach. The ASI asynchronous methods are by the nature of being a widely used library more highly tested than my own implementation could ever be.
There are a number of advantages with using the asynchronous methods - you mention the delegate methods, but the latest release of ASI actually also supports blocks, which is a great leap forward (dealing with multiple synchronous calls used to be a bit of a pain due to the shared delegate methods (or unique delegates for each asynchronous call). But with blocks you can now get rid of the delegates entirely. I've found them to be really useful. Plus if you use multiple contributors it can make readability a lot easier.
Also, by doing it Async, you can more easily track progress through the setProgressDelegate command.

Needing a design pattern/example link for iPhone network connections

I'm sorry if this is a basic question. I've been googling, searching StackOverflow, and looking through example code for hours and haven't found anything satisfactory for my skill level.
I'm wanting something like a design pattern for handling network functions on the iPhone SDK. I have heard of people using a singleton class but have heard there are better ways for asynchronous connections. Would NSOperation be useful? I am fairly new to object oriented programming but am needing to make occasional calls to my webserver through HTTP for my current app and hope to find a solution that is easily reusable.
I've looked through the NSURLConnection docs and can get the basic functionality but the programming structure is messy and I'm not sure how to better organize it. Is there sample code for this that separates out these functions into their own class? A link to an example that does this would be greatly appreciated!
thanks!
I've been dealing with this same question for a while now...
If you're effectively doing a GET on a simple resource, and you're confident that the resource will always be there & accessible, there's an easy way to do it:
NSURL *URL=[[NSURL alloc] initWithString:#"http://www.google.com/"l];
[[UIApplication sharedApplication] setNetworkActivityIndicatorVisible:YES];
//TODO This needs to have timeouts & such set up, maybe parallelism
NSString *results = [[NSString alloc] initWithContentsOfURL :URL];
[[UIApplication sharedApplication] setNetworkActivityIndicatorVisible:NO];
That's a REALLY simple way to do it, but as my comment says, not very robust or reliable.
A slightly more robust, yet still reasonably simple, is replacing the NSString line with:
results = [[NSString alloc] initWithContentsOfURL:URL encoding:NSASCIIStringEncoding error:&err]; // possibly NSUnicodeStringEncoding
if (err!=nil) NSLog(#"Error occurred: %#", [err localizedDescription]);
That will at least TELL you if there's an error...
ASIHTTPRequest provides a lot of neat & useful network functionality for dealing with resources over the internet. http://allseeing-i.com/ASIHTTPRequest/ - the developer has been VERY responsive on his Google Group. I really wanted to use that, and may get back to it when/if it supports SSL Client Certificate authentication (which is what my project requires).
NSURLConnection, as described above - that's what I'm using now in my project. I would imagine that this will satisfy almost all needs, but it's (in my opinion) more tricky to use. And to be honest, I'm still having a little trouble wrapping my mind around how to integrate asynchronous data loading into my application. But if it will work for you - and it probably will, Apple is using it all over the OS and its apps - that's your best bet!
One possible approach is to use the NSURLConnection (as you mentioned).
Inside your .h file:
NSMutableData *connectionData;
Also add a property for connectionData...
Inside your .m file:
- (void)updateFromServer {
// You might want to display a loading indication here...
NSMutableData *connectionDataTemp = [[NSMutableData alloc] init];
self.connectionData = connectionDataTemp;
[connectionDataTemp release];
NSURLRequest *request = [[NSURLRequest alloc] initWithURL: your_url];
NSURLConnection *connection = [[NSURLConnection alloc] initWithRequest:request delegate:self];
[connection release];
[request release];
}
#pragma mark -
#pragma mark NSURLConnectionDelegate
- (void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)data {
// Add the received bulk of data to your mutable data object
[self.connectionData appendData:data];
}
- (void)connectionDidFinishLoading:(NSURLConnection *)connection {
// Use your data
// If there is a loading indication displayed then this is a good place to hide it...
}
- (void)connection:(NSURLConnection *)connection didFailWithError:(NSError *)error {
// Handle error
// If there is a loading indication displayed then this is a good place to hide it...
}

iPhone SDK: UIWebView to stop images from loading/downloading

How can I use the UIWebView in Xcode so that when it loads up pages it DOESN'T download the images (to cause a faster loading page)?
UIWebView is a pale, poor little shadow of WebKit's full WebView, for which this is easy. -webView:shouldStartLoadWithRequest:navigationType: only gets called for navigation. It doesn't get called for every request like WebPolicyDelegate does on mac. With UIWebView, here's how I would attack this problem:
Implement -webView:shouldStartLoadWithRequest:navigationType: and set it to always return NO. But you'll also take the request and spawn an NSURLConnection. When the NSURLConnection finishes fetching the data, you're going to look through it for any IMG tags and modify them to whatever placeholder you want. Then you will load the resulting string into the UIWebView using -loadHTMLString:baseURL:.
Of course parsing the HTML is not a trivial task on iPhone, and Javascript loaders are going to give you trouble, so this isn't a perfect answer, but it's the best I know of.
expanding on Rob's answer.
I noticed that when loadHTMLString:baseURL: and always returning NO, that webView:shouldStartLoadWithRequest:navigationType: just keeps getting called. (i suspect loadHTMLString invokes another shouldStartLoadWithRequest).
so what I had to do was alternate between returning YES/NO
and I used NSScanner to parse the HTML and change src="http://..." to src=""
- (BOOL)webView:(UIWebView *)webView shouldStartLoadWithRequest:(NSURLRequest *)request navigationType:(UIWebViewNavigationType)navigationType
{
if (pageHasNoImages==YES)
{
pageHasNoImages=FALSE;
return YES;
}
NSString* newHtml;
NSString* oldHtml;
NSData *urlData;
NSURLResponse *response;
NSError *error;
urlData = [NSURLConnection sendSynchronousRequest:request returningResponse:&response error:&error];
oldHtml=[[NSString alloc] initWithData:urlData encoding:NSUTF8StringEncoding];
newHtml=[self.detail scannerReplaceImg:oldHtml]; // my own function to parse HTML
newHtml=[self.detail scannerReplaceAds:newHtml]; // my own function to parse HTML
if (newHtml==nil)
{
NSLog(#"newHtml is nil");
newHtml=oldHtml;
}
[oldHtml release];
pageHasNoImages=TRUE;
[web loadHTMLString:newHtml baseURL:request.URL];
return NO;
}
Be the delegate for the UIWebView, then intercept the call:
– webView:shouldStartLoadWithRequest:navigationType:
Check the values of navigationType in the documentation. I believe you'll be best served by returning NO on navigationType == UIWebViewNavigationTypeOther.
does this actually cause the page to load faster?
it sounds like the images are still being downloaded, but we're just not feeding them to the UIWebView.
or does shouldStartLoadWithRequest just load the HTML text first?