Appending data from downloaded from the web in NSMutableData - iphone

I'm trying to append data from a CSV file that I'm getting form the internet. Something is going wrong and I can't figure out what. My log is not returning anything for "Field" or "Value". I'm holding the data as NSMutableData and trying to append it. Am I overwriting it in any way? Something in the data storage/retrieval process must be going wrong... Could you pls take a look at this code and tell me if I'm doing something wrong:
#import "ViewController.h"
#import "CHCSVParser.h"
#interface ViewController ()
#end
#implementation ViewController
#synthesize parser = _parser;
bool isStatus;
#synthesize apendData;
-(void) connectionDidFinishLoading:(NSURLConnection *)connection
{
NSLog(#"connectionDidFinishLoading");
NSString *myString;
myString = [[NSString alloc] initWithData: apendData encoding:NSUTF8StringEncoding];
_parser = [[CHCSVParser alloc] initWithContentsOfCSVFile:[NSHomeDirectory() stringByAppendingPathComponent:myString]];
_parser.delegate = self;
[_parser parse];
}
-(void) connection:(NSURLConnection *)connection didReceiveData:(NSData *)data
{
NSLog(#"data: %#", data);
if (apendData)
[apendData appendData:data];
else
apendData = [[NSMutableData alloc] initWithData:data];
}
- (void)loadDatafromURL
{
NSURL *url = [NSURL URLWithString:#"http://www.google.com/trends/trendsReport?hl=en-US&cat=0-13&date=today%207-d&cmpt=q&content=1&export=1"];
NSURLRequest *request = [NSURLRequest requestWithURL:url];
[NSURLConnection connectionWithRequest:request delegate:self];
}
- (void) connection:(NSURLConnection *)connection didFailWithError:(NSError *)error{
NSLog(#"%#", [error description]);
}
- (void)parser:(CHCSVParser *)parser didBeginLine:(NSUInteger)recordNumber
{
if (recordNumber == 41) {
isStatus = YES;
NSLog(#"ParsingStart..."); // Not getting this
}
}
- (void)parser:(CHCSVParser *)parser didEndLine:(NSUInteger)recordNumber
{
if (recordNumber == 50) {
NSLog(#"ParsingEnd..."); // Not getting this
[parser cancelParsing];
}
}
- (void)parser:(CHCSVParser *)parser didReadField:(NSString *)field atIndex:(NSInteger)fieldIndex
{
if ((isStatus = YES)) {
NSLog(#"YES is working");
if (fieldIndex == 0) {
NSLog(#"Field = %#", field); // Just getting "Field = "
}
if (fieldIndex == 1){
NSLog(#"Value = %#", field); // Not getting this
}
}
}
- (void)viewDidLoad
{
[super viewDidLoad];
[self loadDatafromURL];
}
- (void)didReceiveMemoryWarning
{
[super didReceiveMemoryWarning];
}
#end

Related

Need to call retain for a property with retain flag specified

I'm newbie in Objective-C, and as most of the newbies I have a questions about references management.
I've written a class which downloads data using NSURLConnection. The code is similar to Apple's example in http://developer.apple.com/library/mac/#documentation/Cocoa/Conceptual/URLLoadingSystem/Tasks/UsingNSURLConnection.html. The only difference is that receivedData variable is declared as "#property (nonatomic,retain) NSMutableData *receivedData;" In .m file I have "#synthesize receivedData = _receivedData;".
I have connectionStart function which starts downloading data. In this function I have this code:
if (theConnection) {
// Create the NSMutableData to hold the received data.
// receivedData is an instance variable declared elsewhere.
self.receivedData = [NSMutableData data];
} else {
// Inform the user that the connection failed.
}
The program crashes with this message:
2011-06-12 12:47:22.298 WebGallery[1212:207] *** -[NSConcreteMutableData release]: message sent to deallocated instance 0x118a6fe0
If I change receivedData assignment to this code:
self.receivedData = [[NSMutableData data] retain];
Then the program works correctly and no memory leaks are detected.
As you see I need to call retain on NSMutableData and I'm using property, which is declared as "retain".
Why does this happen?
EDIT: Full contents of .m file:
#import "GalleryData.h"
#import "XmlParser.h"
#implementation GalleryData
#synthesize receivedData = _receivedData;
#synthesize imagesData = _imagesData;
#synthesize delegate = _delegate;
#synthesize currentObjectType = _currentObjectType;
#synthesize currentObjectIndex = _currentObjectIndex;
- (id) init
{
[super init];
_imagesData = [[NSMutableArray alloc] init];
return self;
}
- (void) dealloc
{
[_imagesData release];
_imagesData = nil;
[super dealloc];
}
- (void) connectionStart:(NSURL *)theURL
{
NSURLRequest *theRequest = [NSURLRequest requestWithURL:theURL cachePolicy:NSURLRequestUseProtocolCachePolicy timeoutInterval:60.0];
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.
//ASK: Kodėl čia reikia daryti retain jei #property jau nustatyta retain?
self.receivedData = [[NSMutableData data] retain];
} else {
// Inform the user that the connection failed.
}
}
- (void) startLoading
{
NSLog(#"Loading started");
self.currentObjectIndex = 0;
self.currentObjectType = ObjectTypeXML;
[self connectionStart:[NSURL URLWithString:#"http://www.aleksandr.lt/gallery/data.xml"]];
}
- (void)connection:(NSURLConnection *)connection didReceiveResponse:(NSURLResponse *)response
{
[self.receivedData setLength:0];
}
- (void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)data
{
[self.receivedData appendData:data];
}
- (void)connection:(NSURLConnection *)connection didFailWithError:(NSError *)error
{
[connection release];
[self.receivedData release];
NSLog(#"Connection failed! Error - %# %#",
[error localizedDescription],
[[error userInfo] objectForKey:NSURLErrorFailingURLStringErrorKey]);
}
- (void)connectionDidFinishLoading:(NSURLConnection *)connection
{
[connection release];
if (self.currentObjectType == ObjectTypeXML) {
NSXMLParser *nsXmlParser = [[NSXMLParser alloc] initWithData:self.receivedData];
XmlParser *parser = [[XmlParser alloc] initXmlParser:self.imagesData];
[nsXmlParser setDelegate:parser];
[nsXmlParser parse];
[nsXmlParser release];
[parser release];
[self.receivedData release];
self.receivedData = nil;
if ([self.imagesData count]) {
self.currentObjectIndex = 0;
self.currentObjectType = ObjectTypeThumbImage;
ImageData *firstImage = [self.imagesData objectAtIndex:0];
NSURL *theURL = [NSURL URLWithString:firstImage.thumbImageURL];
[self connectionStart:theURL];
} else {
[self.delegate loadingFinished];
return;
}
} else if (self.currentObjectType == ObjectTypeThumbImage) {
ImageData *currentImage;
currentImage = [self.imagesData objectAtIndex:self.currentObjectIndex];
UIImage *thumbImage = [[UIImage alloc] initWithData:self.receivedData];
if (thumbImage == nil) {
NSLog(#"image was not created");
}
[currentImage setThumbImageScaled:thumbImage];
[thumbImage release];
[self.receivedData release];
self.receivedData = nil;
if (self.currentObjectIndex == ([self.imagesData count] - 1)) {
[self.delegate loadingFinished];
return;
}
self.currentObjectIndex++;
currentImage = [self.imagesData objectAtIndex:self.currentObjectIndex];
NSLog(#"'%#'", currentImage.thumbImageURL);
NSURL *theURL = [NSURL URLWithString:currentImage.thumbImageURL];
[self connectionStart:theURL];
}
}
#end
Do not call [self.receivedData release] - this leaves the internal pointer dangling. The whole point of a retained property is that it releases itself. Just do self.receivedData = nil.
Here is your problem:
[self.receivedData release];
self.receivedData = nil;
You're releasing the attribute twice (first time explicitly and second time implicitly by assigning nil).

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.

How to use the JSON Parsing?

I am a fresher. I don't know how to use JSON for Parsing .I am really very confuse because the tutorial i have read are out dated because apple has deprecated all the API's and documents .Thats what i found .So anyone can give me the start to play with JSON?
You can use SBJSON
You can find code at GITHUB
Here's some links on "Picking the best JSON library":
Comparison of JSON Parser for Objective-C (JSON Framework, YAJL, TouchJSON, etc)
Best JSON library to use when developing an iPhone application?
And now for the shameless plug for my own JSON parsing solution, JSONKit:
What's the most efficient way of converting a 10 MB JSON response into an NSDictionary?
Cocoa JSON parsing libraries, part 2
You need to use third-party code to parse JSON. Three popular libraries/frameworks are:
SBJSON, aka json-framework
TouchJSON
yajl-objc
Choose one of them, download its source code and add it do your project.
You can use your custom framwork for it just as below
#import <Foundation/Foundation.h>
#import "SVProgressHUD.h"
#protocol JsonDataDelegate <NSObject>
#required
- (void) JsonprocessSuccessful: (NSString *)receivedData;
-(void) JsonprocessFail;
#end
#interface JsonRest : NSObject
{
id <JsonDataDelegate> delegate;
NSURLConnection *theConnection;
NSMutableData *webData;
BOOL HadGotResponse;
BOOL HasFailedConnection;
UIAlertView *alert;
}
#property (retain) id delegate;
#property(nonatomic,retain) NSURLConnection *theConnection;
#property (nonatomic)BOOL HadGotResponse;
#property (nonatomic)BOOL HasFailedConnection;
-(void)GetServiceCall:(NSString *)url Paraqmeter:(NSString*)parameter;
-(void)Set2Defaults;
#end
#import "JsonRest.h"
#define BASE_URL #"http://www.abc.com"
#define DOMAIN #"GETLIST"
#implementation JsonRest
#synthesize delegate,HadGotResponse,HasFailedConnection,theConnection;
- (id)init
{
if (self)
{
// Initialization code.
//appdel = (ReachCastAppDelegate*)[[UIApplication sharedApplication]delegate];
}
return self;
}
-(void)GetServiceCall:(NSString *)url Paraqmeter:(NSString*)parameter
{
[SVProgressHUD startActivity : #"Loading..."];
NSString *strUrl=[NSString stringWithFormat:#"%#%#?%#",BASE_URL,url,parameter];
NSURL *url1=[NSURL URLWithString:strUrl];
NSLog(#"domainURL :: %#",strUrl);
NSMutableURLRequest *req = [NSMutableURLRequest requestWithURL:url1];
[req setTimeoutInterval:60.0];
[req setHTTPMethod:#"GET"];
// if([HttpMethod isEqualToString:#"GET"])
// {
// [req setHTTPMethod:HttpMethod];
//
// }
// else if([HttpMethod isEqualToString:#"PUT"])
// {
// NSData *requestData = [NSData dataWithBytes: [BodyString UTF8String] length: [BodyString length]];
//
// [req setValue:valueforput forHTTPHeaderField:#"Content-type"];
// [req setHTTPMethod:HttpMethod];
// [req setHTTPBody: requestData];
//
// }
// else if ([HttpMethod isEqualToString:#"POST"]){
//
// NSData *requestData = [NSData dataWithBytes: [BodyString UTF8String] length: [BodyString length]];
//
// [req setValue:valueforput forHTTPHeaderField:#"Content-type"];
// [req setHTTPMethod:HttpMethod];
// [req setHTTPBody: requestData];
//
// }
if(theConnection)
[self Set2Defaults];
theConnection=[[NSURLConnection alloc]initWithRequest:req delegate:self];
if(theConnection)
webData=[[NSMutableData data]retain];
else
NSLog(#"Connection Failed !!!");
NSLog(#"Has got response");
}
-(void)Set2Defaults {
if (webData != nil){
[webData release];
webData = nil;
}
if (theConnection != nil) {
[theConnection release];
if (theConnection != nil)
theConnection = nil;
}
}
#pragma mark -
#pragma mark NSURL Connection Delegate methods
-(void) connection:(NSURLConnection *) connection didReceiveResponse:(NSURLResponse *) respons{
[webData setLength: 0];
NSHTTPURLResponse *httpResponse = (NSHTTPURLResponse *)respons;
int statusCode = [httpResponse statusCode];
NSLog(#"statusCode ---- >> %d",statusCode);
if (200 != statusCode) {
// if (401 == statusCode) {
}
}
-(void) connection:(NSURLConnection *) connection didReceiveData:(NSData *) data {
[webData appendData:data];
}
-(void) connection:(NSURLConnection *) connection didFailWithError:(NSError *) error {
[SVProgressHUD stopActivity];
[self Set2Defaults];
self.HadGotResponse=NO;
self.HasFailedConnection=YES;
// txtWindSpeed.text = #"";
[[self delegate] JsonprocessFail];
NSLog(#"Connection Failed!!!");
//[self HideLoadingDialogue];
alert = [[[UIAlertView alloc] initWithTitle:#"ERROR" message:#"Internet Connection Failed.Please try again." delegate:self cancelButtonTitle:nil otherButtonTitles:nil] autorelease];
//alert.frame=CGRectMake(alert.frame.origin.x, alert.frame.origin.y, alert.frame.size.width, 130);
NSLog(#"%F",alert.frame.origin.y);
[alert show];
alert.frame=CGRectMake(alert.frame.origin.x, alert.frame.origin.y+25, alert.frame.size.width, 130);
//[noConnect release];
[NSTimer scheduledTimerWithTimeInterval:2 target:self selector:#selector(timerCall) userInfo:nil repeats:NO];
}
-(void)timerCall
{
[alert dismissWithClickedButtonIndex:0 animated:TRUE];
}
-(void)connectionDidFinishLoading:(NSURLConnection *)connection
{
[SVProgressHUD stopActivity];
self.HasFailedConnection=NO;
NSString *theXML;
theXML = [[NSString alloc] initWithBytes: [webData mutableBytes] length:[webData length] encoding:NSASCIIStringEncoding];
//NSLog(#"theXML Values : %#", theXML);
//self.ResponseDict = [theXML JSONValue];
[[self delegate] JsonprocessSuccessful:theXML];
[theXML release];
}
-(void)dealloc
{
if (webData != nil){
[webData release];
webData = nil;
}
if (theConnection != nil) {
[theConnection release];
if (theConnection != nil)
theConnection = nil;
}
[super dealloc];
// [ResponseDict release];
}
#end
You can use this using below
-(void)getJsonCalling
{
#try {
NSString *strParameter=[NSString stringWithFormat:#"param1=%#&param2=%#",param1,param2];
objJsonRest=[[JsonRest alloc]init];
[objJsonRest setDelegate:self];
[objJsonRest GetServiceCall:DOMAIN Paraqmeter:strParameter];
[objJsonRest release];
}
#catch (NSException *exception) {
NSLog(#"Exception in %s %#",__FUNCTION__,exception);
}
}
- (void) JsonprocessSuccessful: (NSString *)receivedData
{
}
-(void) JsonprocessFail;
{
NSLog(#"FAIL");
}

Downloading a Large File - iPhone SDK

I am using Erica Sadun's method of Asynchronous Downloads (link here for the project file: download), however her method does not work with files that have a big size (50 mb or above). If I try to download a file above 50 mb, it will usually crash due to a memory crash. Is there anyway I can tweak this code so that it works with large files as well? Here is the code I have in the DownloadHelper Classes (which is already in the download link):
.h
#protocol DownloadHelperDelegate <NSObject>
#optional
- (void) didReceiveData: (NSData *) theData;
- (void) didReceiveFilename: (NSString *) aName;
- (void) dataDownloadFailed: (NSString *) reason;
- (void) dataDownloadAtPercent: (NSNumber *) aPercent;
#end
#interface DownloadHelper : NSObject
{
NSURLResponse *response;
NSMutableData *data;
NSString *urlString;
NSURLConnection *urlconnection;
id <DownloadHelperDelegate> delegate;
BOOL isDownloading;
}
#property (retain) NSURLResponse *response;
#property (retain) NSURLConnection *urlconnection;
#property (retain) NSMutableData *data;
#property (retain) NSString *urlString;
#property (retain) id delegate;
#property (assign) BOOL isDownloading;
+ (DownloadHelper *) sharedInstance;
+ (void) download:(NSString *) aURLString;
+ (void) cancel;
#end
.m
#define DELEGATE_CALLBACK(X, Y) if (sharedInstance.delegate && [sharedInstance.delegate respondsToSelector:#selector(X)]) [sharedInstance.delegate performSelector:#selector(X) withObject:Y];
#define NUMBER(X) [NSNumber numberWithFloat:X]
static DownloadHelper *sharedInstance = nil;
#implementation DownloadHelper
#synthesize response;
#synthesize data;
#synthesize delegate;
#synthesize urlString;
#synthesize urlconnection;
#synthesize isDownloading;
- (void) start
{
self.isDownloading = NO;
NSURL *url = [NSURL URLWithString:self.urlString];
if (!url)
{
NSString *reason = [NSString stringWithFormat:#"Could not create URL from string %#", self.urlString];
DELEGATE_CALLBACK(dataDownloadFailed:, reason);
return;
}
NSMutableURLRequest *theRequest = [NSMutableURLRequest requestWithURL:url];
if (!theRequest)
{
NSString *reason = [NSString stringWithFormat:#"Could not create URL request from string %#", self.urlString];
DELEGATE_CALLBACK(dataDownloadFailed:, reason);
return;
}
self.urlconnection = [[NSURLConnection alloc] initWithRequest:theRequest delegate:self];
if (!self.urlconnection)
{
NSString *reason = [NSString stringWithFormat:#"URL connection failed for string %#", self.urlString];
DELEGATE_CALLBACK(dataDownloadFailed:, reason);
return;
}
self.isDownloading = YES;
// Create the new data object
self.data = [NSMutableData data];
self.response = nil;
[self.urlconnection scheduleInRunLoop:[NSRunLoop currentRunLoop] forMode:NSRunLoopCommonModes];
}
- (void) cleanup
{
self.data = nil;
self.response = nil;
self.urlconnection = nil;
self.urlString = nil;
self.isDownloading = NO;
}
- (void)connection:(NSURLConnection *)connection didReceiveResponse:(NSURLResponse *)aResponse
{
// store the response information
self.response = aResponse;
// Check for bad connection
if ([aResponse expectedContentLength] < 0)
{
NSString *reason = [NSString stringWithFormat:#"Invalid URL [%#]", self.urlString];
DELEGATE_CALLBACK(dataDownloadFailed:, reason);
[connection cancel];
[self cleanup];
return;
}
if ([aResponse suggestedFilename])
DELEGATE_CALLBACK(didReceiveFilename:, [aResponse suggestedFilename]);
}
- (void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)theData
{
// append the new data and update the delegate
[self.data appendData:theData];
if (self.response)
{
float expectedLength = [self.response expectedContentLength];
float currentLength = self.data.length;
float percent = currentLength / expectedLength;
DELEGATE_CALLBACK(dataDownloadAtPercent:, NUMBER(percent));
}
}
- (void)connectionDidFinishLoading:(NSURLConnection *)connection
{
// finished downloading the data, cleaning up
self.response = nil;
// Delegate is responsible for releasing data
if (self.delegate)
{
NSData *theData = [self.data retain];
DELEGATE_CALLBACK(didReceiveData:, theData);
}
[self.urlconnection unscheduleFromRunLoop:[NSRunLoop currentRunLoop] forMode:NSRunLoopCommonModes];
[self cleanup];
}
- (void)connection:(NSURLConnection *)connection didFailWithError:(NSError *)error
{
self.isDownloading = NO;
NSLog(#"Error: Failed connection, %#", [error localizedDescription]);
DELEGATE_CALLBACK(dataDownloadFailed:, #"Failed Connection");
[self cleanup];
}
+ (DownloadHelper *) sharedInstance
{
if(!sharedInstance) sharedInstance = [[self alloc] init];
return sharedInstance;
}
+ (void) download:(NSString *) aURLString
{
if (sharedInstance.isDownloading)
{
NSLog(#"Error: Cannot start new download until current download finishes");
DELEGATE_CALLBACK(dataDownloadFailed:, #"");
return;
}
sharedInstance.urlString = aURLString;
[sharedInstance start];
}
+ (void) cancel
{
if (sharedInstance.isDownloading) [sharedInstance.urlconnection cancel];
}
#end
And finally this is how I write the file with the two classes above it:
- (void) didReceiveData: (NSData *) theData
{
if (![theData writeToFile:self.savePath atomically:YES])
[self doLog:#"Error writing data to file"];
[theData release];
}
If someone could help me out I would be so glad!
Thanks,
Kevin
Replace the in-memory NSData *data with an NSOutputStream *stream. In -start create the stream to append and open it:
stream = [[NSOutputStream alloc] initToFileAtPath:path append:YES];
[stream open];
As data comes in, write it to the stream:
NSUInteger left = [theData length];
NSUInteger nwr = 0;
do {
nwr = [stream write:[theData bytes] maxLength:left];
if (-1 == nwr) break;
left -= nwr;
} while (left > 0);
if (left) {
NSLog(#"stream error: %#", [stream streamError]);
}
When you're done, close the stream:
[stream close];
A better approach would be to add the stream in addition to the data ivar, set the helper as the stream's delegate, buffer incoming data in the data ivar, then dump the data ivar's contents to the helper whenever the stream sends the helper its space-available event and clear it out of the data ivar.
I have a slight modification to the above code.
Use this function, it works fine for me.
- (void) didReceiveData: (NSData*) theData
{
NSOutputStream *stream=[[NSOutputStream alloc] initToFileAtPath:self.savePath append:YES];
[stream open];
percentage.hidden=YES;
NSString *str=(NSString *)theData;
NSUInteger left = [str length];
NSUInteger nwr = 0;
do {
nwr = [stream write:[theData bytes] maxLength:left];
if (-1 == nwr) break;
left -= nwr;
} while (left > 0);
if (left) {
NSLog(#"stream error: %#", [stream streamError]);
}
[stream close];
}
Try AFNetworking. And:
NSString *yourFileURL=#"http://yourFileURL.zip";
NSURLRequest *request = [NSURLRequest requestWithURL:[NSURL URLWithString:yourFileURL]];
AFURLConnectionOperation *operation = [[AFHTTPRequestOperation alloc] initWithRequest:request];
NSString *cacheDir = [NSSearchPathForDirectoriesInDomains
(NSCachesDirectory, NSUserDomainMask, YES) objectAtIndex:0];
NSString *filePath = [cacheDir stringByAppendingPathComponent:
#"youFile.zip"];
operation.outputStream = [NSOutputStream outputStreamToFileAtPath:filePath append:NO];
[operation setDownloadProgressBlock:^(NSUInteger bytesRead, long long totalBytesRead, long long totalBytesExpectedToRead) {
//show here your downloading progress if needed
}];
[operation setCompletionBlock:^{
NSLog(#"File successfully downloaded");
}];
[operation start];

iphone NSURLConnection NSTimer works in initWithRequest but causes unrecognized selector error in sendSynchronousRequest

I need to use an NSTimer to cancel my NSURLRequest before 75 seconds (the time I measured regardless of the timeout I set). I'm using the XMLRPC classes by Eric Czarny. The XMLRPCConnection is basically an image of the NSURLConnection class.
Here's the interface and the implementation file:
#import <Foundation/Foundation.h>
#class XMLRPCRequest, XMLRPCResponse;
/* XML-RPC Connecion Notifications */
extern NSString *XMLRPCRequestFailedNotification;
extern NSString *XMLRPCSentRequestNotification;
extern NSString *XMLRPCReceivedResponseNotification;
#interface XMLRPCConnection : NSObject {
NSURLConnection *_connection;
NSString *_method;
NSMutableData *_data;
id _delegate;
UIViewController* _requester;
}
#property(nonatomic, retain) NSMutableData* _data;
- (id)initWithXMLRPCRequest: (XMLRPCRequest *)request delegate: (id)delegate;
- (id)initWithXMLRPCRequest: (XMLRPCRequest *)request delegate: (id)delegate requester:(UIViewController*)requester;
- (void) timedOut;
#pragma mark -
+ (XMLRPCResponse *)sendSynchronousXMLRPCRequest: (XMLRPCRequest *)request;
#pragma mark -
- (void)cancel;
#end
#pragma mark -
#interface NSObject (XMLRPCConnectionDelegate)
- (void)connection: (XMLRPCConnection *)connection didReceiveResponse: (XMLRPCResponse *)response
forMethod: (NSString *)method;
- (void)connection: (XMLRPCConnection *)connection didFailWithError: (NSError *)error
forMethod: (NSString *)method;
#end
Implementation file:
#import "XMLRPCConnection.h"
#import "XMLRPCRequest.h"
#import "XMLRPCResponse.h"
NSString *XMLRPCRequestFailedNotification = #"XML-RPC Failed Receiving Response";
NSString *XMLRPCSentRequestNotification = #"XML-RPC Sent Request";
NSString *XMLRPCReceivedResponseNotification = #"XML-RPC Successfully Received Response";
#interface XMLRPCConnection (XMLRPCConnectionPrivate)
- (void)connection: (NSURLConnection *)connection didReceiveData: (NSData *)data;
- (void)connection: (NSURLConnection *)connection didFailWithError: (NSError *)error;
- (void)connectionDidFinishLoading: (NSURLConnection *)connection;
- (void) timedOut;
#end
#pragma mark -
#implementation XMLRPCConnection
#synthesize _data;
- (id)initWithXMLRPCRequest: (XMLRPCRequest *)request delegate: (id)delegate requester:(UIViewController*)requester {
if (self = [super init])
{
_connection = [[NSURLConnection alloc] initWithRequest: [request request] delegate: self];
_delegate = delegate;
_requester = requester;
// set the timer for timed out requests here
NSTimer* connectionTimer = [NSTimer timerWithTimeInterval:10.0f target:self selector:#selector(timedOut) userInfo:nil repeats:NO];
[[NSRunLoop currentRunLoop] addTimer:connectionTimer forMode:NSDefaultRunLoopMode];
if (_connection != nil)
{
_method = [[NSString alloc] initWithString: [request method]];
_data = [[NSMutableData alloc] init];
[[NSNotificationCenter defaultCenter] postNotificationName:
XMLRPCSentRequestNotification object: nil];
}
else
{
if ([_delegate respondsToSelector: #selector(connection:didFailWithError:forMethod:)])
{
[_delegate connection: self didFailWithError: nil forMethod: [request method]];
}
return nil;
}
}
return self;
}
- (void) timedOut {
NSLog(#"connection timed out now!");
}
#pragma mark -
+ (XMLRPCResponse *)sendSynchronousXMLRPCRequest: (XMLRPCRequest *)request
{
NSURLResponse *urlres;
//NSHTTPURLResponse *urlres;
NSError *err = NULL;
// set the timer for timed out requests here
NSTimer* connectionTimer = [NSTimer timerWithTimeInterval:10.0f target:self selector:#selector(timedOut) userInfo:nil repeats:NO];
[[NSRunLoop currentRunLoop] addTimer:connectionTimer forMode:NSDefaultRunLoopMode];
NSData *data = [NSURLConnection sendSynchronousRequest: [request request]
returningResponse: &urlres error: &err];
if ([urlres isKindOfClass:[NSHTTPURLResponse class]]) {
NSLog(#"Received status code: %d %#", [(NSHTTPURLResponse *) urlres statusCode],
[NSHTTPURLResponse localizedStringForStatusCode:[(NSHTTPURLResponse *) urlres statusCode]]) ;
NSDictionary* headerFields = [(NSHTTPURLResponse*)urlres allHeaderFields];
NSArray* cookie = [NSHTTPCookie cookiesWithResponseHeaderFields:headerFields forURL:[request host]];
if ([cookie count] != 0) {
NSString* cookieName = [[cookie objectAtIndex:0] name];
NSString* cookieValue = [[cookie objectAtIndex:0] value];
NSLog(#"cookie name=value: %#=%#", cookieName, cookieValue );
[[User getInstance] setCookieID:[NSString stringWithFormat:#"%#=%#", cookieName, cookieValue] ];
} else {
NSLog(#"cookie array empty!");
}
}
// if an error occured while processing the request, this variable will be set
if( err != NULL )
{
//TODO: we may need to create a XMLRPCResponse with the error. and return
return (id) err;
}
if (data != nil)
{
NSString *str = [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding];
NSLog(#"response is: %#",str);
if ( ! str ) {
str = [[NSString alloc] initWithData:data encoding:[NSString defaultCStringEncoding]];
data = [str dataUsingEncoding:NSUTF8StringEncoding allowLossyConversion:YES];
}
//Check for HTML code 400 or greater in response statusCode (from header) and throw error if so
if ([urlres isKindOfClass:[NSHTTPURLResponse class]]) {
// HTTP codes equal or higher than 400 signifies an error
if ([(NSHTTPURLResponse *) urlres statusCode] >= 400) {
// NSLog(#"Received status code: %d %#", [(NSHTTPURLResponse *) urlres statusCode],
// [NSHTTPURLResponse localizedStringForStatusCode:[(NSHTTPURLResponse *) urlres statusCode]]) ;
NSString *errorIntString = [NSString stringWithFormat:#"%d", [(NSHTTPURLResponse *) urlres statusCode]];
NSString *stringForStatusCode = [NSHTTPURLResponse localizedStringForStatusCode:[(NSHTTPURLResponse *) urlres statusCode]];
NSString *errorString = [[errorIntString stringByAppendingString:#" "] stringByAppendingString:stringForStatusCode];
NSInteger code = -1; //This is not significant, just a number with no meaning
NSDictionary *usrInfo = [NSDictionary dictionaryWithObject:errorString forKey:NSLocalizedDescriptionKey];
err = [NSError errorWithDomain:#"org.wordpress.iphone" code:code userInfo:usrInfo];
return (id) err;
}
}
//[str release];
return [[[XMLRPCResponse alloc] initWithData: data] autorelease];
}
return nil;
}
#pragma mark -
- (void)cancel
{
[_connection cancel];
[_connection autorelease];
}
#pragma mark -
- (void)dealloc
{
[_method autorelease];
[_data autorelease];
[super dealloc];
}
#end
#pragma mark -
#implementation XMLRPCConnection (XMLRPCConnectionPrivate)
....
#end
The timer set in the initWithXMLRPCRequest method works fine, but if it's set in the sendSycnhronousXMLRPCRequest method, I get the following error:
2010-01-29 11:36:40.442 ActiveE[1071:207] *** Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: '*** +[XMLRPCConnection timedOut]: unrecognized selector sent to class 0x32610'
2010-01-29 11:36:40.443 ActiveE[1071:207] Stack: (
31044699,
2497855305,
31426811,
30996086,
30848706,
609709,
30829248,
30825544,
39135117,
39135314,
3100675
)
I don't understand, I did declare the timeOut method in the implementation file?
I think your timedOut method should be a public one. Not hidden in a category with private methods.
The method that the timer calls has to be of the form:
- (void)timerFireMethod:(NSTimer*)theTimer;
The name is arbitrary but you must return void and must accept a timer. I suspect that is the source of your error. In any case, you should change it to the standard form.