iPhone - NSURLConnection asynchronous download using URLs in NSArray - iphone

I have seen almost all the posts about NSURL on this site, and I am still stuck. I am using Xcode 4.5.
I am trying to download images and display them in a UIScrollView.
I want to download asynchronously download images using URLs, that get stored in an array populated using JSON. I get the URLs from a JSON grab off of my database. That works quite well and I can see the URL's being placed into the urlArray, but making the URLConnection to get the image, seems to fail.
I can't get any of the images to download, or at least they don't show up in my imageArray.
Here is my code and thank you for any help!! Let me know what else is needed
- (void)viewDidLoad
{
[super viewDidLoad];
//show network activity to user.... very useful
[UIApplication sharedApplication].networkActivityIndicatorVisible = YES;
//call getJSON. getJSON does not parse, but it connects and gets the data.
[self getJSON];
}
- (void)didReceiveMemoryWarning
{
[super didReceiveMemoryWarning];
// Dispose of any resources that can be recreated.
}
- (void)getJSON
{
NSURL *url = [NSURL URLWithString:#"http://"My server goes here/json.php"];
NSURLRequest *request = [NSURLRequest requestWithURL:url];
//just initialize the connection, do not
[[NSURLConnection alloc] initWithRequest:request delegate:self]; //"Ecression result unused" warning here
}
- (void)getNextImage
{
[UIApplication sharedApplication].networkActivityIndicatorVisible = YES;
for (int y = 0; y < urlArray.count; y++)
{
NSString *urlString = [urlArray objectAtIndex:y];
NSLog(#"Array String is: %# ", urlString);
NSURL *arrayURL = [NSURL URLWithString:urlString];
NSURLRequest *imageRequest = [NSURLRequest requestWithURL:arrayURL];
NSData *imgData = [[NSURLConnection alloc] initWithRequest:imageRequest delegate:self]; //"Incompatible pointer types initializing ..." warning here
imageData = [UIImage imageWithData:imgData];
[imageArray addObject:imageData];
}
NSLog(#"LEAVING getNextImage");
}
- (void)connection:(NSURLConnection *)connection didReceiveResponse:(NSURLResponse *)response
{
theJsonData = [[NSMutableData alloc] init];
}
- (void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)data
{
[theJsonData appendData:data];
}
- (void)connectionDidFinishLoading:(NSURLConnection *)connection
{
urlArray = [[NSMutableArray alloc] init];
//This is where all the JSON Parsing is being done.
//Turn off the data indicator, because the download is complete.
[UIApplication sharedApplication].networkActivityIndicatorVisible = NO;
jsonArray = [NSJSONSerialization JSONObjectWithData:theJsonData options:nil error:nil]; //"Incompatible pointer types initializing ..." warning here
//get the URL strings out of the jsonArray
for (int x = 0; x < jsonArray.count; x++)
{
NSString *urlString = [[jsonArray objectAtIndex:x] objectForKey:#"image_URL"];
NSLog(#"String is %# ", urlString);
[urlArray addObject:urlString];
}
[self getNextImage];
//display the images..... Not sure why this is in connectionDidFinishLoading.
for (int x = 0; x < imageArray.count; x++)
{
CGRect frame;
frame.origin.x = self.mainScroll.frame.size.width * x;
frame.origin.y = 0;
frame.size = self.mainScroll.frame.size;
UIImageView *nextIV = [[UIImageView alloc] initWithFrame:frame];
[nextIV setImage:imageData];
[self.mainScroll addSubview:nextIV];
//NSLog(#"Pass %d", x);
}
self.mainScroll.contentSize = CGSizeMake(self.mainScroll.frame.size.width * imageArray.count,1.0);
NSLog(#"!!!!!!leaving connection did finnish loading!!!!!");
}
- (void)connection:(NSURLConnection *)connection didFailWithError:(NSError *)error
{
//show error message to user if there is a connection error.
UIAlertView *errorView = [[UIAlertView alloc] initWithTitle:#"Error" message:#"The Download could not complete - please make sure you're connected to the internet." delegate:nil cancelButtonTitle:#"Dismiss" otherButtonTitles:nil];
[errorView show];
//turn off the network activity indicatior
[UIApplication sharedApplication].networkActivityIndicatorVisible = NO;
}

you never download imageData. you assign it the request object . thats why you get the warning too. a NSURLConnection object is not a NSData object: NSData *imgData = [[NSURLConnection alloc] initWithRequest:imageRequest delegate:self]; //"Incompatible pointer types initializing ..." warning here
I would today rewrite it using the startAsyncConnection method. sec
-- there you go, untested and written in text edit but it should get you started (I reused most of your code but cut it down a lot too)
#import "RootViewController.h"
#interface RootViewController ()
#property(assign) IBOutlet UIScrollView *mainScroll;
#end
#implementation RootViewController
- (void)viewDidLoad
{
[super viewDidLoad];
[UIApplication sharedApplication].networkActivityIndicatorVisible = YES;
[self getJSONAndImageData];
}
- (void)getJSONAndImageData
{
NSURL *url = [NSURL URLWithString:#"http://My server goes here/json.php"];
NSURLRequest *request = [NSURLRequest requestWithURL:url];
[NSURLConnection sendAsynchronousRequest:request queue:[NSOperationQueue mainQueue] completionHandler:^(NSURLResponse*r, NSData*d, NSError*e) {
[self parseJSONAndGetImages:d];
}];
}
- (void)parseJSONAndGetImages:(NSData*)data
{
NSMutableArray *urlArray = [[NSMutableArray alloc] init];
//This is where all the JSON Parsing is being done.
//Turn off the data indicator, because the download is complete.
[UIApplication sharedApplication].networkActivityIndicatorVisible = NO;
NSArray *jsonArray = (NSArray*)[NSJSONSerialization JSONObjectWithData:data options:nil error:nil]; //"Incompatible pointer types initializing ..." warning here => likely not an array then
assert([jsonArray isKindOfClass:[NSArray class]]);
//could be made in one liner with KVC
//get the URL strings out of the jsonArray
for (int x = 0; x < jsonArray.count; x++)
{
NSString *urlString = [[jsonArray objectAtIndex:x] objectForKey:#"image_URL"];
NSLog(#"String is %# ", urlString);
[urlArray addObject:urlString];
}
[self loadImageArray:urlArray handler:^(NSArray* imageArray) {
[UIApplication sharedApplication].networkActivityIndicatorVisible = NO;
for (int x = 0; x < imageArray.count; x++)
{
CGRect frame;
frame.origin.x = self.mainScroll.frame.size.width * x;
frame.origin.y = 0;
frame.size = self.mainScroll.frame.size;
UIImageView *nextIV = [[UIImageView alloc] initWithFrame:frame];
[nextIV setImage:imageArray[x]];
[self.mainScroll addSubview:nextIV];
//NSLog(#"Pass %d", x);
}
self.mainScroll.contentSize = CGSizeMake(self.mainScroll.frame.size.width * imageArray.count,1.0);
}];
}
//for SIMPLICITY I do synchronous networking here!
- (void)loadImageArray:(NSArray *)urlArray handler:(void(^)())handler {
dispatch_async(dispatch_get_global_queue(0, 0), ^{
NSMutableArray *imageArray = [NSMutableArray array];
for (int y = 0; y < urlArray.count; y++)
{
NSString *urlString = [urlArray objectAtIndex:y];
NSLog(#"Array String is: %# ", urlString);
NSURL *arrayURL = [NSURL URLWithString:urlString];
NSURLRequest *imageRequest = [NSURLRequest requestWithURL:arrayURL];
NSData *imgData = [NSURLConnection sendSynchronousRequest:imageRequest returningResponse:nil error:nil];
UIImage *image = [UIImage imageWithData:imgData];
[imageArray addObject:image];
}
dispatch_async(dispatch_get_main_queue(),^ {
handler(imageArray);
});
});
}
#end

Related

Thumbnail for youtube video

I am trying to create a thumbnail for youtube video,but me getting below error:-
Error Domain=AVFoundationErrorDomain Code=-11850 "Operation Stopped" UserInfo=0xa07ac00 NSLocalizedDescription=Operation Stopped, NSUnderlyingError=0xa07e580 "The operation couldn’t be completed. (OSStatus error -12939.)", NSLocalizedFailureReason=The server is not correctly configured.
Please help me out to create a thumbnail of youtube video from youtube link.
The Code am using to create thumbnail is as follows:-
NSURL *url = [NSURL URLWithString:strVideoURL];
AVURLAsset *asset=[[AVURLAsset alloc] initWithURL:url options:nil];
AVAssetImageGenerator *generator = [[AVAssetImageGenerator alloc] initWithAsset:asset];
generator.appliesPreferredTrackTransform=TRUE;
CMTime thumbTime = CMTimeMakeWithSeconds(0,30);
AVAssetImageGeneratorCompletionHandler handler = ^(CMTime requestedTime, CGImageRef im, CMTime actualTime, AVAssetImageGeneratorResult result, NSError *error)
{
if (result != AVAssetImageGeneratorSucceeded) {
NSLog(#"couldn't generate thumbnail, error:%#", error);
}
// TODO Do something with the image
NSLog(#"CGImage---- %#", im);
UIImage *thumbnail=[UIImage imageWithCGImage:im];
UIImageView *imageView=[[UIImageView alloc] initWithFrame:CGRectMake(30, 50, 120, 120)];
imageView.backgroundColor=[UIColor redColor];
imageView.image=thumbnail;
[image addSubview:imageView];
UIView *view=(UIView*)[self.view viewWithTag:10];
[view removeFromSuperview];
};
CGSize maxSize = CGSizeMake(128, 128);
generator.maximumSize = maxSize;
[generator generateCGImagesAsynchronouslyForTimes:[NSArray arrayWithObject:[NSValue valueWithCMTime:thumbTime]] completionHandler:handler];
Although your question doesn't have any source code of yours so i have no idea where have you gone wrong. Try this, its working for me.
#define YOUTUBE_URL #"https://gdata.youtube.com/feeds/api/videos?v=2&author=%#&orderby=published"
NSString *userName = #"ipl";
_url = [NSURL URLWithString:[NSString stringWithFormat:YOUTUBE_URL,userName]];
-(void) fetchYoutubeThumbnails {
NSURLRequest *_request = [[NSURLRequest alloc] initWithURL: _url];
NSURLConnection *connection = [[NSURLConnection alloc] initWithRequest:_request delegate:self];
[connection start];
}
- (void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)data {
[recievedData appendData:data]; //recievedData is a class variable of type NSMutableData
}
- (void)connectionDidFinishLoading:(NSURLConnection *)connection {
NSString *xmlString = [[NSString alloc] initWithData:recievedData encoding:NSUTF8StringEncoding];
NSDictionary *result = [XMLReader dictionaryForXMLString:xmlString error:Nil];
if(result.count > 0) {
NSMutableArray *items = [[result objectForKey:#"feed"] objectForKey:#"entry"];
for(int i = items.count -1; i >= 0; i--) {
NSDictionary *item = [items objectAtIndex:i];
NSString *videoThumbnaulUrl = [[[[item valueForKey:#"media:group"] valueForKey:#"media:thumbnail"] objectAtIndex:0] valueForKey:#"url"];
// Use this url to get the thumbnail.
}
}

Calls to NSURLConnectionDataDelegate methods to download images only works occasionally

I currently have two UIImageViews in my storyboard, one of which downloads my own Facebook profile picture, and the other a friend's profile picture.
However, my issue is that only 60% of the time this works as expected, while the other 40% of the times my own profile picture appears where my friend's picture should show in the bottom, while the top box remains empty. I am unsure whether this is the result of how I call the NSURLConnectionDataDelegate methods as it downloads or completes view, or the nature of my requests called to Facebook.
I've pasted the condensed version of my two requests to Facebook in viewDidLoad, one which gets my own profile pic, and the other to get my friend's pic:
// ----- Gets my own profile picture, using requestForMe -----
FBRequest *request = [FBRequest requestForMe];
[request startWithCompletionHandler:^(FBRequestConnection *connection, id result, NSError *error) {
//handle response
if(!error){
//Some methods not included for breveity, includes facebookID used below
NSURL *pictureURL = [NSURL URLWithString:[NSString stringWithFormat:#"https://graph.facebook.com/%#/picture?type=large&return_ssl_resources=1", facebookID]];
self.imageData = [[NSMutableData alloc] init];
switcher = 1;
NSMutableURLRequest *urlRequest = [NSMutableURLRequest requestWithURL:pictureURL cachePolicy:NSURLRequestUseProtocolCachePolicy timeoutInterval:2.0f];
NSURLConnection *urlConnection = [[NSURLConnection alloc] initWithRequest:urlRequest delegate:self];
if (!urlConnection){
NSLog(#"Failed to download picture");
}
}
}];
// ----- Gets a profile picture of my friend, using requestForMyFriends -----
FBRequest *requestForFriends = [FBRequest requestForMyFriends];
[requestForFriends startWithCompletionHandler:^(FBRequestConnection *connection, id result, NSError *error) {
if(!error){
//Other methods not included, including facebookFriendID
NSURL *friendPictureURL = [NSURL URLWithString:[NSString stringWithFormat:#"https://graph.facebook.com/%#/picture?type=large&return_ssl_resources=1", facebookFriendID]];
self.supportImageData = [[NSMutableData alloc] init];
switcher = 2;
NSMutableURLRequest *urlRequest = [NSMutableURLRequest requestWithURL:friendPictureURL cachePolicy:NSURLRequestUseProtocolCachePolicy timeoutInterval:2.0f];
NSURLConnection *urlConnection = [[NSURLConnection alloc] initWithRequest:urlRequest delegate:self];
if (!urlConnection){
NSLog(#"Failed to download picture");
}
}
}];
Both requests make calls to the NSURLConnectionDataDelegate methods, and I use the switcher to decide when to load which picture:
- (void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)data {
// As chuncks of the image are received, we build our data file
if (switcher == 1) [self.imageData appendData:data];
if (switcher == 2)[self.supportImageData appendData:data];
}
- (void)connectionDidFinishLoading:(NSURLConnection *)connection
{
//When the entire image is finished downloading
if (switcher == 1) {
UIImage *image = [UIImage imageWithData:self.imageData]; //puts the completed picture into the UI
self.titleImageView.image = image;
[self.titleImageView setClipsToBounds:YES];
}
if (switcher == 2) {
UIImage *supportImage = [UIImage imageWithData:self.supportImageData];
self.supportImageView.image = supportImage;
[self.titleImageView setClipsToBounds:YES];
}
}
You have two asynchronous processes, both of which can result in having your NSURLConnectionDataDelegate methods in self being called. But if they happen at the same time, they're going to step on top of each other (you're presumably using a single NSMutableData variable to reference the data being downloaded).
Either create dedicated class that you can instantiate once for each of your two NSURLConnection requests (a NSOperation based approach, like AFNetworking, is ideal), or use sendAsynchronousRequest instead. But don't use a single object as the delegate for two concurrent NSURLConnection requests at the same time.
If you wanted to see a minimalist download operation, it might look like:
// NetworkOperation.h
#import <Foundation/Foundation.h>
typedef void(^DownloadCompletion)(NSData *data, NSError *error);
#interface NetworkOperation : NSOperation
- (id)initWithURL:(NSURL *)url completion:(DownloadCompletion)completionBlock;
#property (nonatomic, copy) NSURL *url;
#property (nonatomic, copy) DownloadCompletion downloadCompletionBlock;
#end
and
// NetworkOperation.m
#import "NetworkOperation.h"
#interface NetworkOperation () <NSURLConnectionDataDelegate>
#property (nonatomic, readwrite, getter = isExecuting) BOOL executing;
#property (nonatomic, readwrite, getter = isFinished) BOOL finished;
#property (nonatomic, strong) NSMutableData *data;
#end
#implementation NetworkOperation
#synthesize finished = _finished;
#synthesize executing = _executing;
- (id)initWithURL:(NSURL *)url completion:(DownloadCompletion)downloadCompletionBlock
{
self = [super init];
if (self) {
self.url = url;
self.downloadCompletionBlock = downloadCompletionBlock;
_executing = NO;
_finished = NO;
}
return self;
}
#pragma mark - NSOperation related stuff
- (void)start
{
if ([self isCancelled]) {
self.finished = YES;
return;
}
self.executing = YES;
NSURLRequest *request = [NSURLRequest requestWithURL:self.url];
NSAssert(request, #"%s: requestWithURL failed for URL '%#'", __FUNCTION__, [self.url absoluteString]);
NSURLConnection *connection = [[NSURLConnection alloc] initWithRequest:request delegate:self startImmediately:NO];
[connection scheduleInRunLoop:[NSRunLoop mainRunLoop] forMode:NSDefaultRunLoopMode];
[connection start];
}
- (void)setExecuting:(BOOL)executing
{
[self willChangeValueForKey:#"isExecuting"];
_executing = executing;
[self didChangeValueForKey:#"isExecuting"];
}
- (void)setFinished:(BOOL)finished
{
[self willChangeValueForKey:#"isFinished"];
_finished = finished;
[self didChangeValueForKey:#"isFinished"];
}
- (BOOL)isConcurrent
{
return YES;
}
#pragma mark NSURLConnectionDataDelegate methods
- (void)connection:(NSURLConnection *)connection didReceiveResponse:(NSURLResponse *)response
{
self.data = [NSMutableData data];
}
- (void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)data
{
if ([self isCancelled]) {
[connection cancel];
self.executing = NO;
self.finished = YES;
return;
}
[self.data appendData:data];
}
- (void)connectionDidFinishLoading:(NSURLConnection *)connection
{
if (self.downloadCompletionBlock) {
[[NSOperationQueue mainQueue] addOperationWithBlock:^{
self.downloadCompletionBlock(self.data, nil);
self.downloadCompletionBlock = nil;
}];
}
self.executing = NO;
self.finished = YES;
}
- (void)connection:(NSURLConnection *)connection didFailWithError:(NSError *)error
{
if (self.downloadCompletionBlock) {
[[NSOperationQueue mainQueue] addOperationWithBlock:^{
self.downloadCompletionBlock(nil, error);
self.downloadCompletionBlock = nil;
}];
}
self.executing = NO;
self.finished = YES;
}
#end
And then, when you wanted to use it, it might look like:
NSOperationQueue *networkQueue = [[NSOperationQueue alloc] init];
queue.maxConcurrentOperationCount = 4;
// ----- Gets my own profile picture, using requestForMe -----
FBRequest *request = [FBRequest requestForMe];
[request startWithCompletionHandler:^(FBRequestConnection *connection, id result, NSError *error) {
//handle response
if(!error) {
//Some methods not included for breveity, includes facebookID used below
NSURL *pictureURL = [NSURL URLWithString:[NSString stringWithFormat:#"https://graph.facebook.com/%#/picture?type=large&return_ssl_resources=1", facebookID]];
[networkQueue addOperation:[[NetworkOperation alloc] requestWithURL:pictureURL completion:^(NSData *data, NSError *error) {
if (!error) {
self.meImageView.image = [UIImage imageWithData:data];
}
}]];
}
}];
// ----- Gets a profile picture of my friend, using requestForMyFriends -----
FBRequest *requestForFriends = [FBRequest requestForMyFriends];
[requestForFriends startWithCompletionHandler:^(FBRequestConnection *connection, id result, NSError *error) {
if(!error){
//Other methods not included, including facebookFriendID
NSURL *friendPictureURL = [NSURL URLWithString:[NSString stringWithFormat:#"https://graph.facebook.com/%#/picture?type=large&return_ssl_resources=1", facebookFriendID]];
[networkQueue addOperation:[[NetworkOperation alloc] requestWithURL:friendPictureURL completion:^(NSData *data, NSError *error) {
if (!error) {
self.friendImageView.image = [UIImage imageWithData:data];
}
}]];
}
}];

UIWbView multiple URL loading with UIProgressView in iPhone SDK

I am trying to create an iPhone app as in the fig below:
In this i am dynamically adding some imageviews dynamically into a uiscroll view. Each imageview contains a UIButton and UIProgressView. When click on each it should different urls and the loading of each url should appear on the corresponding UIProgressView. I am using asynchronous method and it should load multiple progress views at the same time.
Here is the code i have used,
- (void)startDownload:(id)sender {
UIButton *btn = (UIButton *)sender;
int btnTag = btn.tag;
selectedTag = btn.tag;
for (int x=0; x < [urlArray count]; x++) {
if (btnTag == x) {
NSURL *url = [NSURL URLWithString:[urlArray objectAtIndex:x]];
NSURLRequest *request = [NSURLRequest requestWithURL:url];
NSURLConnection *theConnection = [[NSURLConnection alloc]initWithRequest:request delegate:self];
[NSURLConnection sendAsynchronousRequest:request
queue:[NSOperationQueue mainQueue]
completionHandler:^(NSURLResponse *response, NSData *data, NSError *error) {
NSLog(#"Expected length: %lld ",response.expectedContentLength);
}];
if (theConnection) {
receivedData = [NSMutableData data];
}else {
NSLog(#"Connection failed");
}
}
}
}
- (void)makeMyProgressMove{
UIImageView *image = (UIImageView *)[mainView viewWithTag:selectedTag];
UIProgressView *currentProgress = (UIProgressView *)[image viewWithTag:selectedTag];
NSLog(#"Prog tag: %d",currentProgress.tag);
if(currentProgress)
{
float actualProgress = (_receivedDataBytes / (float)_totalFileSize);
currentProgress.progress = actualProgress;
} else {
NSLog( #"couldn't get the progress view for the image with tag: %d", selectedTag );
}
}
- (void)connection:(NSURLConnection *)connection didReceiveResponse:(NSURLResponse *)response {
_totalFileSize = response.expectedContentLength;
receivedData = [[NSMutableData alloc] init];
}
- (void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)data {
_receivedDataBytes += [data length];
NSLog(#"Receiving data: %2f", _receivedDataBytes / (float)_totalFileSize);
if ((_receivedDataBytes / (float)_totalFileSize) < 1) {
[self performSelectorOnMainThread: #selector(makeMyProgressMove) withObject: NULL waitUntilDone:NO];
}
else if ((_receivedDataBytes / (float)_totalFileSize) == 1){
[self performSelectorOnMainThread: #selector(makeMyProgressMove) withObject: NULL waitUntilDone:NO];
_receivedDataBytes = 0;
_totalFileSize = 0;
}
[receivedData appendData:data];
}
- (void)connectionDidFinishLoading:(NSURLConnection *)connection
{
NSLog(#"Succeeded! Received %d bytes of data",[receivedData length]);
}
But its not working.
Any idea?
allocate each UIWebView with different url and and addSubView UIActivityIndicatorView to every UIWebView.Give tag for each UIActivityIndicator ..
-(void)action {
//Create a URL object.
NSURL *url = [NSURL URLWithString:urlAddress];
//URL Request Object
NSURLRequest *requestObj = [NSURLRequest requestWithURL:url];
UIWebView *webView = [[UIWebView alloc]initWithFrame:frame];//give y value incremented frame.
UIActivityIndicatorView *av = [[[UIActivityIndicatorView alloc]initWithActivityIndicatorStyle:UIActivityIndicatorViewStyleGray] autorelease];
av.frame=CGRectMake(145, 160, 25, 25);
av.center = webView.center;
av.tag = INDICATOR_TAG;
[webView addSubview:av];
webView.delegate = self;
[av startAnimating];
//Load the request in the UIWebView.
[webView loadRequest:requestObj];
}
- (void)webViewDidFinishLoad:(UIWebView *)webView {
UIActivityIndicatorView *indicator = [webView viewWithTag:INDICATOR_TAG];
indicator.hidden = YES;
}
call this action{} method for showing multiple times.
EDIT
UIWebView doesn't give you any progress information in the normal mode. What you need to do is first fetch your data asynchronously using an NSURLConnection. When the NSURLConnection delegate method connection:didReceiveResponse, you're going to take the number you get from expectedContentLength and use that as your max value. Then, inside the delegate method connection:didReceiveData, you're going to use the length property of the NSData instance to tell you how far along you are, so your progress fraction will be length / maxLength , normalized to between 0 and 1.
Finally, you're going to init the webview with data instead of a URL (in your connection:didFinishLoading delegate method).
Reference :
How to use UIProgressView while loading of a UIWebView?

i want to save my pdf into my iphone, pdfs url are with me through parsing

I have parsed my xml and i got some images and their corresponding urls of pdf from server.so whenever i click on image i have their corresponding url of pdf.I am giving an alertView on click of images and when user select the download button of alertView it should download the pdf from url into my iphone device
CODE:-
#implementation SecondViewController
#synthesize scrollView,receivedData;
- (void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)data
{
[receivedData appendData:data];
}
// Implement viewDidLoad to do additional setup after loading the view, typically from a nib.
- (void)viewDidLoad {
[super viewDidLoad];
[myIndicator setActivityIndicatorViewStyle:UIActivityIndicatorViewStyleWhiteLarge];
myIndicator.hidesWhenStopped = YES;
[myIndicator startAnimating];
UIColor *background = [[UIColor alloc] initWithPatternImage:[UIImage imageNamed:#"iphone_landscape.png"]];
self.view.backgroundColor = background;
[background release];
NSURLRequest *theRequest=[NSURLRequest requestWithURL:[NSURL URLWithString:#"http://litofinter.es.milfoil.arvixe.com/displayxml1.aspx"] cachePolicy:NSURLRequestUseProtocolCachePolicy timeoutInterval:150.0];
NSURLConnection *theConnection=[[NSURLConnection alloc] initWithRequest:theRequest delegate:self];
if (theConnection) {
receivedData = [[NSMutableData data] retain];
}
}
- (void)connectionDidFinishLoading:(NSURLConnection *)connection
{
int x=20,y=50;
appDelegate = (AppDelegate_iPhone *)[[UIApplication sharedApplication] delegate];
scrollView = [[UIScrollView alloc] initWithFrame:CGRectMake(0, 45,320, 480)];
scrollView.contentSize = CGSizeMake(320,5000);
scrollView.showsVerticalScrollIndicator = YES;
for (Litofinter *lito in appDelegate.bookArray) {
if([appDelegate.currentButtonPressed isEqualToString:lito.cName])
{
NSLog(#"Count == %d ===",[lito.productsArray count]);
for (Products *prod in lito.productsArray) {
NSString * urlString = [prod.thumbnail stringByAddingPercentEscapesUsingEncoding:NSASCIIStringEncoding];
NSURL * imageURL = [NSURL URLWithString:urlString];
NSData * imageData = [NSData dataWithContentsOfURL:imageURL];
UIImage * image = [UIImage imageWithData:imageData];
[myIndicator stopAnimating];
[myIndicator removeFromSuperview];
UIButton *imageButton = [[UIButton buttonWithType:UIButtonTypeCustom]retain];
[imageButton setFrame:CGRectMake(x, y, 150, 200)];
[imageButton setImage:image forState:UIControlStateNormal];
[imageButton setTitle:prod.pdf forState:UIControlStateNormal];
[imageButton addTarget:self action:#selector(onTapBook:) forControlEvents:UIControlEventTouchUpInside];
[scrollView addSubview:imageButton];
x = x + 150;
if(x >300)
{
y = y +250;
x = 20;
}
}
}
}
[self.view addSubview:scrollView];
[connection release];
[receivedData release];
}
-(void)onTapBook:(id)sender{
UIButton *button = (UIButton *) sender;
appDelegate.currentBookPressed = [button currentTitle];
// viewController2 = [[PdfShowViewController alloc]initWithNibName:#"PdfShowViewController" bundle:nil];
// [self presentModalViewController:viewController2 animated:YES];
UIAlertView *alert = [[UIAlertView alloc]initWithTitle:#"Ver Catalogo!" message:#"" delegate:self cancelButtonTitle:#"Cancelar" otherButtonTitles:#"Ver on-line",#"Descargar",nil];
[alert show];
/*[NSString stringWithFormat:#"%#",appDelegate.currentBookPressed] */
}
- (void)alertView:(UIAlertView *)alertView clickedButtonAtIndex:(NSInteger)buttonIndex
{
NSString *title = [alertView buttonTitleAtIndex:buttonIndex];
if([title isEqualToString:#"Ver on-line"])
{
// i will show the pdf online here
}
else if([title isEqualToString:#"Descargar"])
{
// what to write to download the pdf
}
}
-(IBAction)onTapBack{
[self dismissModalViewControllerAnimated:YES];
}
// Override to allow orientations other than the default portrait orientation.
- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation {
return YES;
}
- (void)didReceiveMemoryWarning {
[super didReceiveMemoryWarning];
}
- (void)viewDidUnload {
[super viewDidUnload];
}
- (void)dealloc {
[super dealloc];
[scrollView release];
}
#end
I would do it with NSURLConnection and then I would reuse same code above, because you have it already declared properly.
Save data to NSData and then with writeToFile save it to main bundle.
So here is some more explanation how I would do it.
There are several ways to do it.
Here is how to do it with NSData
NSData *myFile = [NSData dataWithContentsOfURL:[NSURL URLWithString:#"your_url"]]; [myFile writeToFile:[NSString stringWithFormat:#"%#/%#", [[NSBundle mainBundle] resourcePath], #"yourfilename.pdf"] atomically:YES];
Also you can use ASIHTTPRequest library which has been discontinued by author, but still works like it should.
ASIHTTPRequest *myDownloadRequest = [ASIHTTPRequest requestWithURL:fileUrl];
[request setDownloadDestinationPath:[NSString stringWithFormat:#"%#/%#", [[NSBundle mainBundle] resourcePath], #"yourfilename.pdf"]];
But maybe easiest way of all because as I can see you have displayed pdf already, so it's contents are in receivedData is just to call
[receivedData writeToFile:[NSString stringWithFormat:#"%#/%#", [[NSBundle mainBundle] resourcePath], #"yourfilename.pdf"] atomically:YES];
So actually you can reuse code that you have already wrote in viewDidLoad, replace url if necessary and after connection is closed save file to disk.
NSURLRequest *theRequest=[NSURLRequest requestWithURL:[NSURL URLWithString:#"http://litofinter.es.milfoil.arvixe.com/displayxml1.aspx"] cachePolicy:NSURLRequestUseProtocolCachePolicy timeoutInterval:150.0];
NSURLConnection *theConnection=[[NSURLConnection alloc] initWithRequest:theRequest delegate:self];
if (theConnection) {
receivedData = [[NSMutableData data] retain];
}

NSArray with multiple nested requests

I have an app that uses a segmentedControl. First item is an "All" item, where the rest is created from an array based on result from webservice. When "All" is selected I want to request all the request.
How can I go about this,
NSArray *urls = [NSArray arrayWithObjects:#"http://service/group/1/",
#"http://service/group/2/", nil];
I want to collect all result from the calls into a collection and display it in a UITableView when the "All" item is selected and probably in viewDidLoad.
For the other segments only one of the request is issued and callback with an array that then is used in:
-(UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
I have tried to look at this example for making the request from the array MultipleDownloads
Thanks,
The method in my viewController to initiate the multiple download:
- (void)requestChildrenInBackground {
queue = [[NSOperationQueue alloc] init];
//Todo remove hard coded and get from previous request respons
NSArray *urls = [NSArray arrayWithObjects: #"http://service/1/children",
#"http://service/2/children",
#"http://service/3/children", nil];
NSLog(#"%#", urls);
for (NSString * url in urls)
{
GetSchedule *operation =
[GetSchedule urlDownloaderWithUrlString:url];
[queue addOperation:operation];
}
}
This is how the multiple request gets handled:
#import "GetSchedule.h"
#import "JSON.h"
#import "Authentication.h"
#import "AttendanceReportViewController.h"
#interface GetSchedule ()
- (void)finish;
#end
#implementation GetSchedule
#synthesize appDelegate;
#synthesize username;
#synthesize password;
#synthesize authenticationString;
#synthesize encodedLoginData;
#synthesize schedulesArray;
#synthesize url = _url;
#synthesize statusCode = _statusCode;
#synthesize data = _data;
#synthesize error = _error;
#synthesize isExecuting = _isExecuting;
#synthesize isFinished = _isFinished;
+ (id)urlDownloaderWithUrlString:(NSString *)urlString {
NSURL * url = [NSURL URLWithString:urlString];
GetSchedule *operation = [[self alloc] initWithUrl:url];
return [operation autorelease];
}
- (id)initWithUrl:(NSURL *)url {
self = [super init];
if (self == nil)
return nil;
_url = [url copy];
_isExecuting = NO;
_isFinished = NO;
return self;
}
- (void)dealloc
{
[username release];
[password release];
[encodedLoginData release];
[_url release];
[_connection release];
[_data release];
[_error release];
[super dealloc];
}
- (BOOL)isConcurrent
{
return YES;
}
- (void)start
{
if (![NSThread isMainThread])
{
[self performSelectorOnMainThread:#selector(start) withObject:nil waitUntilDone:NO];
return;
}
self.username = appDelegate.username;
self.password = appDelegate.password;
Authentication *auth = [[Authentication alloc] init];
authenticationString = (NSMutableString*)[#"" stringByAppendingFormat:#"%#:%#", username, password];
self.encodedLoginData = [auth encodedAuthentication:authenticationString];
[auth release];
NSLog(#"operation for <%#> started.", _url);
[self willChangeValueForKey:#"isExecuting"];
_isExecuting = YES;
[self didChangeValueForKey:#"isExecuting"];
// Setup up the request with the url
NSMutableURLRequest *request = [[NSMutableURLRequest alloc]
initWithURL:_url];
[request setHTTPMethod:#"GET"];
[request setValue:[NSString stringWithFormat:#"Basic %#", encodedLoginData] forHTTPHeaderField:#"Authorization"];
_connection = [[NSURLConnection alloc] initWithRequest:request
delegate:self];
if (_connection == nil)
[self finish];
else {
_data = [[NSMutableData alloc] init];
}
}
- (void)finish
{
NSLog(#"operation for <%#> finished. "
#"status code: %d, error: %#, data size: %u",
_url, _statusCode, _error, [_data length]);
[_connection release];
_connection = nil;
[self willChangeValueForKey:#"isExecuting"];
[self willChangeValueForKey:#"isFinished"];
_isExecuting = NO;
_isFinished = YES;
[self didChangeValueForKey:#"isExecuting"];
[self didChangeValueForKey:#"isFinished"];
}
#pragma mark -
#pragma mark NSURLConnection delegate
- (void)connection:(NSURLConnection *)connection
didReceiveResponse:(NSURLResponse *)response
{
//[_data release];
//_data = [[NSMutableData alloc] init];
[_data setLength:0];
NSHTTPURLResponse * httpResponse = (NSHTTPURLResponse *)response;
_statusCode = [httpResponse statusCode];
}
- (void)connection:(NSURLConnection *)connection
didReceiveData:(NSData *)data
{
[_data appendData:data];
}
- (void)connectionDidFinishLoading:(NSURLConnection *)connection
{
// Parse the responseData of json objects retrieved from the service
SBJSON *parser = [[SBJSON alloc] init];
NSString *jsonString = [[NSString alloc] initWithData:_data encoding:NSUTF8StringEncoding];
NSDictionary *jsonData = [parser objectWithString:jsonString error:nil];
NSMutableArray *array = [jsonData objectForKey:#"Children"];
schedulesArray = [NSMutableArray array];
[schedulesArray addObject:array];
// Callback to AttendanceReportViewController that the responseData finished loading
[attendanceReportViewController loadSchedule];
[self finish];
}
- (void)connection:(NSURLConnection *)connection
didFailWithError:(NSError *)error
{
_error = [error copy];
[self finish];
}
#end
When all data is received I want to call back to my ViewController and get an array with all data from all request made.
Create a downloader
Create a downloader class using NSURLConnection.
Keep a member variable called downloaderObjectId.
Write a delegate method for this object. This method will pass the downloaded data and downloaderObjectId back to the delegate.
In the delegate.
Create multiple downloader objects(As per your ncessity) with unique value for the downloaderObjectId.
Store these objects in a NSMutableDictionary
Key for each object will be downloaderObjectId. so that when the delegate method is called after download you take the exact object back from the NSMutableDictionary using this key.
Main point.
Each time delegate is called. You should remove the object from dictionary(The object who is done with the download and called his delgate. You can identify this object by the key downloaderObjectId he holds. )
Then check the count of dictionary. If it is zero you can make sure that your downloads are completed. So you can call your viewcontroller.