How to use the JSON Parsing? - iphone

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");
}

Related

About how to use NSThread

I'm an iPhone developer.
I studied about how to use NSThread. So I created source code.
But, I'm not sure about my source code whether good or bad.
-(void)check_threadEnd
{
if ([_thread isFinished]) {
threadCount++;
if (threadCount == 4) {
[self performSelector:#selector(removeActivityView) withObject:nil afterDelay:0.0];
[self.tableView reloadData];
}
}
}
Sometimes, threadCount doesn't become 4.
So, ActiveView is worked continual without stopping.
Turn the timer after a period of time, remove ActiveView?
I'll give you some advice please.
-(IBAction)click_ServerSync:(id)sender
{
if ([util checkNetwork]) {
threadCount = 0 ;
[self displayActivityView];
NSOperationQueue *queue = [[[NSOperationQueue alloc] init] autorelease];
[queue setMaxConcurrentOperationCount:4];
_thread = [[NSThread alloc] initWithTarget:self selector:#selector(_th) object:nil];
[_thread start];
}
}
-(void)_th
{
[self performSelectorOnMainThread:#selector(LoadXml:) withObject:#"XML1" waitUntilDone:NO];
[self performSelectorOnMainThread:#selector(LoadXml:) withObject:#"XML2" waitUntilDone:NO];
[self performSelectorOnMainThread:#selector(LoadXml:) withObject:#"XML3" waitUntilDone:NO];
[self performSelectorOnMainThread:#selector(LoadXml:) withObject:#"XML4" waitUntilDone:NO];
}
-(void)LoadXml:(NSString*)P_VAL
{
NSString *smsURL = [NSString stringWithFormat:#"%#%#.asp", XML_URL, P_VAL];
NSString *sendAuthInfo = [NSString stringWithFormat:#"A=%#&B=%d&C=%#&D=%#" , A, B, C, D ];
NSString *val = [sendAuthInfo stringByAddingPercentEscapesUsingEncoding:NSUTF8StringEncoding];
NSMutableURLRequest *request = [[[NSMutableURLRequest alloc]initWithURL:[NSURL URLWithString:smsURL]]autorelease];
[request setURL:[NSURL URLWithString:smsURL]];
[request setHTTPMethod:#"POST"];
[request setHTTPBody: [val dataUsingEncoding: NSUTF8StringEncoding]];
[self startAsyncLoad:request tag:P_VAL];
}
- (void)startAsyncLoad:(NSMutableURLRequest*)request tag:(NSString*)tag {
CustomURLConnection *connection = [[CustomURLConnection alloc] initWithRequest:request delegate:self startImmediately:YES tag:tag];
if (connection) {
[receivedData setObject:[[NSMutableData data] retain] forKey:connection.tag];
}
}
- (NSMutableData*)dataForConnection:(CustomURLConnection*)connection {
NSMutableData *data = [receivedData objectForKey:connection.tag];
return data;
}
-(void)check_threadEnd
{
if ([_thread isFinished]) {
threadCount++;
if (threadCount == 4) {
[self performSelector:#selector(removeActivityView) withObject:nil afterDelay:0.0];
[self.tableView reloadData];
}
}
}
- (void)connection:(NSURLConnection *)connection didReceiveResponse:(NSURLResponse *)response {
NSMutableData *dataForConnection = [self dataForConnection:(CustomURLConnection*)connection];
[dataForConnection setLength:0];
}
- (void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)data {
NSMutableData *dataForConnection = [self dataForConnection:(CustomURLConnection*)connection];
[dataForConnection appendData:data];
}
-(void)connectionDidFinishLoading:(NSURLConnection*)connection
{
NSLog(#" connection connectionDidFinishLoading : %d", [connection retainCount]);
NSMutableData *dataForConnection = [self dataForConnection:(CustomURLConnection*)connection];
[connection release];
[UIApplication sharedApplication].networkActivityIndicatorVisible = NO;
NSXMLParser *xmlParser = [[[NSXMLParser alloc] initWithData:dataForConnection] autorelease];
XMLParser *parser = [[XMLParser alloc] initXMLParser];
[xmlParser setDelegate:(id)parser];
parser.viewDelegate = (id)self;
[xmlParser parse]; // xml parser
}
Do you have a reason as to why you are opting for NSThread over NSOperation? NSOperation abstracts away a lot of the lower-level thing you would have to worry about with NSThread. I would strongly recommend you read up on this concurrency programming guide from Apple.
Pay attention to the last section, Migrating Away From Threads, as it talks about additional alternatives that can help you write robust, clean code.

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.

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];

Need help: My program should react when an other class is doing something specific - Not as easy as it sounds

I´m writing an program that uses twitter. I want that my app shows an UIAlertView when the user presses on the "Tweet"-Button and the username or password is wrong.
For my Twitterfunction I use the TwitterRequest.m/h from Brandon Trebitowski. If everthing works great and the username/password is right, this happens in my app:
TwitterRequest * t = [[TwitterRequest alloc] init];
(...);
[t statuses_update:twittermessage.text delegate:self requestSelector:#selector(status_updateCallback:)];
loadingActionSheet = [[UIActionSheet alloc] initWithTitle:#"Posting to Twitter..." delegate:nil
cancelButtonTitle:nil destructiveButtonTitle:nil otherButtonTitles:nil];
[loadingActionSheet showInView:self.view];
}
- (void) status_updateCallback: (NSData *) content {
[loadingActionSheet dismissWithClickedButtonIndex:0 animated:YES];
[loadingActionSheet release];
NSLog(#"%#",[[NSString alloc] initWithData:content encoding:NSASCIIStringEncoding]);
}
But how can I show an UIAlertView when the username/password was wrong?
Here is the TwitterRequest.m:
#import "TwitterRequest.h"
#implementation TwitterRequest
#synthesize username;
#synthesize password;
#synthesize receivedData;
#synthesize delegate;
#synthesize callback;
#synthesize errorCallback;
-(void)friends_timeline:(id)requestDelegate requestSelector:(SEL)requestSelector{
isPost = NO;
// Set the delegate and selector
self.delegate = requestDelegate;
self.callback = requestSelector;
// The URL of the Twitter Request we intend to send
NSURL *url = [NSURL URLWithString:#"http://twitter.com/statuses/friends_timeline.xml"];
[self request:url];
}
-(void)statuses_update:(NSString *)status delegate:(id)requestDelegate requestSelector:(SEL)requestSelector; {
isPost = YES;
// Set the delegate and selector
self.delegate = requestDelegate;
self.callback = requestSelector;
// The URL of the Twitter Request we intend to send
NSURL *url = [NSURL URLWithString:#"http://twitter.com/statuses/update.xml"];
requestBody = [NSString stringWithFormat:#"status=%#",status];
[self request:url];
}
-(void)request:(NSURL *) url {
theRequest = [[NSMutableURLRequest alloc] initWithURL:url];
if(isPost) {
NSLog(#"ispost");
[theRequest setHTTPMethod:#"POST"];
[theRequest setValue:#"application/x-www-form-urlencoded" forHTTPHeaderField:#"Content-Type"];
[theRequest setHTTPBody:[requestBody dataUsingEncoding:NSASCIIStringEncoding allowLossyConversion:YES]];
[theRequest setValue:[NSString stringWithFormat:#"%d",[requestBody length] ] forHTTPHeaderField:#"Content-Length"];
}
theConnection = [[NSURLConnection alloc] initWithRequest:theRequest delegate:self];
if (theConnection) {
// Create the NSMutableData that will hold
// the received data
// receivedData is declared as a method instance elsewhere
receivedData=[[NSMutableData data] retain];
} else {
// inform the user that the download could not be made
}
}
- (void)connection:(NSURLConnection *)connection didReceiveAuthenticationChallenge:(NSURLAuthenticationChallenge *)challenge {
//NSLog(#"challenged %#",[challenge proposedCredential] );
if ([challenge previousFailureCount] == 0) {
NSURLCredential *newCredential;
newCredential=[NSURLCredential credentialWithUser:[self username]
password:[self password]
persistence:NSURLCredentialPersistenceNone];
[[challenge sender] useCredential:newCredential
forAuthenticationChallenge:challenge];
} else {
[[challenge sender] cancelAuthenticationChallenge:challenge];
// inform the user that the user name and password
// in the preferences are incorrect
NSLog(#"Invalid Username or Password"); //THIS MUST be important. The console shows this message, if the username/password is wrong. Here is also the place, where I set the bool to TRUE
}
}
- (void)connection:(NSURLConnection *)connection didReceiveResponse:(NSURLResponse *)response
{
// this method is called when the server has determined that it
// has enough information to create the NSURLResponse
// it can be called multiple times, for example in the case of a
// redirect, so each time we reset the data.
// receivedData is declared as a method instance elsewhere
//[receivedData setLength:0];
}
- (void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)data {
//NSLog([[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding]);
// append the new data to the receivedData
// receivedData is declared as a method instance elsewhere
[receivedData appendData:data];
}
- (void)connection:(NSURLConnection *)connection
didFailWithError:(NSError *)error
{
// release the connection, and the data object
[connection release];
// receivedData is declared as a method instance elsewhere
[receivedData release];
[theRequest release];
// inform the user
NSLog(#"Connection failed! Error - %# %#",
[error localizedDescription],
[[error userInfo] objectForKey:NSErrorFailingURLStringKey]);
if(errorCallback) {
[delegate performSelector:errorCallback withObject:error];
}
}
- (void)connectionDidFinishLoading:(NSURLConnection *)connection
{
// do something with the data
if(delegate && callback) {
if([delegate respondsToSelector:self.callback]) {
[delegate performSelector:self.callback withObject:receivedData];
} else {
NSLog(#"No response from delegate");
}
}
// release the connection, and the data object
[theConnection release];
[receivedData release];
[theRequest release];
}
-(void) dealloc {
[super dealloc];
}
#end
I know that the important line in the TwitterRequest.m is in the else-cause of the -(void)connection-methode, because the Console writes always Invalid Username or Password, when they are wrong. So I tried to make there a bool as propertey, which will be set to TRUE, when the name/password is wrong(= when the else-cause will be used). In my ViewController I made this:
if (t.stimmtNicht == TRUE) {
[loadingActionSheet dismissWithClickedButtonIndex:0 animated:YES];
[loadingActionSheet release];
UIAlertView *alert;
alert = [[UIAlertView alloc] initWithTitle:#"Ouch!"
message:#"Your Username or Password is wrong!"
delegate:self
cancelButtonTitle:#"OK"
otherButtonTitles: nil];
[alert show];
[alert release];
}
But he always doesn't use the if-cause, even if the password is wrong. I think the code go faster through the if-quere than the TwitterRequest set it to TRUE. What can I do?
Thanks for your help and sorry for this stupid question, but I'm learning Objective-C and programming in general since just one week and I don´t know correctly how to interact from my ViewController with other classes.
Also sorry for my bad English!
Two ways I can think of:
Apparently your TwitterRequest class has a "delegate" ivar; try calling the delegate to tell him (her?) that your credentials are wrong.
Send an NSNotification through the NSNotificationCenter
In both cases, the class who gets notified should display the login view (with the username and password fields), and that's it.