How can I receive the data by NSURLConnection in other thread in Objective-C - objective-c

I write iPhone application. In this app, I use Twitter framework. In this framework, call back function made in desynchronization is in other thread.
In my view controller,
ViewController.m
[accountStore requestAccessToAccountsWithType:accountType
withCompletionHandler:^(BOOL granted, NSError *error) {
if (granted) {
if (account == nil) {
NSArray *accountArray = [accountStore accountsWithAccountType:accountType];
account = [accountArray objectAtIndex:2];
}
if (account != nil){
NSURL *url = [NSURL URLWithString:#"http://api.twitter.com/1/statuses/user_timeline.json"];
NSMutableDictionary *params = [[NSMutableDictionary alloc] init];
[params setObject:#"1" forKey:#"count"];
TWRequest *request = [[TWRequest alloc] initWithURL:url
parameters:params
requestMethod:TWRequestMethodGET];
[request setAccount:account];
[request performRequestWithHandler:^(NSData *responseData, NSHTTPURLResponse *urlResponse, NSError *error) {
if (responseData) {
//Throw response data to other Web API
[self otherAPI:responseData];
[[NSRunLoop currentRunLoop] run];
}
}];
}
}
}];
And I write these method in this class.
- (void)connection:(NSURLConnection *)connection didReceiveResponse:(NSURLResponse *)response;
- (void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)data;
- (void)connectionDidFinishLoading:(NSURLConnection *)connection;
But I cannot receive full data from other API. I can receive only first data. I think there are some problems in conducting multi thread.
Therefore I'd like to let me know what's wrong in this code.

I think I see your problem. -connection:didReceiveData: is called multiple times, you need build up a NSMutableData object which will contain the whole message.
Note: This only works for a single download per instance at one time.
- (void)connection:(NSURLConnection *)connection didReceiveResponse:(NSURLResponse *)response
{
self.responseData = [[NSMutableData dataWithCapacity:0];
}
- (void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)data
{
[self.responseData appendData:data];
}
- (void)connectionDidFinishLoading:(NSURLConnection *)connection
{
// self.responseData has all the data.
}

Related

NSURLConnection switch to NSURLSession returning 404 response

I am attempting to convert my project from using NSURLConnection to NSURLSession. However, after making the switch I cannot seem to get a response from the server. The returned response from the server is always 404.
My original code, using NSURLConnection:
#implementation RecorderManager
- (void)sendRequestToURL:(NSString*)url withData:(NSData*)postData forScreen:(NSString *)inScreenName
{
RecorderAsynchSubmit* delegate = [[RecorderAsynchSubmit alloc] initWithScreenName:inScreenName andURL:url];
NSURLConnection *urlConnection = [[NSURLConnection alloc] initWithRequest:request delegate:delegate];
}
#implementation RecorderAsynchSubmit
// Implementing NSURLConnectionDelegate, NSURLConnectionDataDelegate
- (void)connection:(NSURLConnection *)connection didFailWithError:(NSError *)error
{
// Handle error
}
- (void)connection:(NSURLConnection *)connection didReceiveResponse:(NSURLResponse *)response
{
[httpResponse setLength:0];
httpResponseCode = [((NSHTTPURLResponse *) response) statusCode];
}
- (void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)httpdata
{
[httpResponse appendData:httpdata];
}
- (void)connectionDidFinishLoading:(NSURLConnection *)connection
{
NSString *responseString = [[[NSString alloc] initWithData:httpResponse encoding:NSUTF8StringEncoding] copy];
if (httpResponseCode == STATUS_CODE_VALID) { // response code for entry submitted
responseString = #"Submit Succeeded";
} else {
responseString = [NSString stringWithFormat:#"Your entry could not be submitted. Data has been stored locally. Error Message: %#", responseString];
}
[[NSNotificationCenter defaultCenter]
postNotificationName:#"submissionCompleteEvent"
object:[[RecorderNotificationMessage alloc] initWithStatusMessage:responseString detailedMessage:responseString]];
}
My updated code using NSURLSessionTask:
#implementation RecorderManager
- (void)sendRequestToURL:(NSString*)url withData:(NSData*)postData forScreen:(NSString *)inScreenName
{
RecorderAsynchSubmit* delegate = [[RecorderAsynchSubmit alloc] initWithScreenName:inScreenName andURL:url];
NSURLSessionConfiguration *configuration = [NSURLSessionConfiguration defaultSessionConfiguration];
NSURLSession *session = [NSURLSession sessionWithConfiguration:configuration delegate:delegate delegateQueue:nil];
NSURL *URL = [NSURL URLWithString:url];
NSURLSessionTask *task = [session dataTaskWithURL:URL];
[task resume];
}
#implementation RecorderAsynchSubmit
// Implementing NSURLSessionDelegate, NSURLSessionTaskDelegate, NSURLSessionDataDelegate
- (void)URLSession:(NSURLSession *)session dataTask:(NSURLSessionDataTask *)dataTask didReceiveResponse:(NSURLResponse *)response
completionHandler:(void (^)(NSURLSessionResponseDisposition disposition))completionHandler {
[httpResponse setLength:0];
httpResponseCode = [((NSHTTPURLResponse *) response) statusCode];
completionHandler(NSURLSessionResponseAllow);
}
-(void)URLSession:(NSURLSession *)session dataTask:(NSURLSessionDataTask *)dataTask
didReceiveData:(NSData *)data {
[httpResponse appendData:data];
}
- (void)URLSession:(NSURLSession *)session task:(NSURLSessionTask *)task
didCompleteWithError:(NSError *)error {
if (error) {
// Handle error
} else {
NSString *responseString = [[[NSString alloc] initWithData:httpResponse encoding:NSUTF8StringEncoding] copy];
if (httpResponseCode == STATUS_CODE_VALID) {
responseString = #"Submit Succeeded";
} else {
responseString = [NSString stringWithFormat:#"Your entry could not be submitted. Data has been stored locally. Error Message: %#", responseString];
}
[[NSNotificationCenter defaultCenter]
postNotificationName:#"submissionCompleteEvent"
object:[[RecorderNotificationMessage alloc] initWithStatusMessage:responseString detailedMessage:responseString]];
}
}
The original code is working fine and returns the expected result, however the updated code always returns a 404 response from the server.

Retrieving images once saved to a server

I have code written that uploads an image to the server, but I am not sure how to retrieve the images after I upload them. I tried using an NSURL request, but the did receiveData delegate method is never called. Below I've included all relevant code related to uploading the picture, and then my attempt at pulling the data using an NSURL request. Is there anything conceptually that I'm doing wrong? Thank you.
- (IBAction)nextButtonPressed:(id)sender {
[self.signupController uploadProfilePicture:UIImagePNGRepresentation(self.imageView.image) completion:^(NSError *error){
[[NSOperationQueue mainQueue] addOperationWithBlock:^{
[[SyncController sharedInstance] sync];
[self performSegueWithIdentifier:#"addFriendsSegue" sender:self];
}];
}];
}
And the uploadProfilePicture method:
- (void)uploadProfilePicture:(NSData *)imageData completion:(void (^)(NSError *error))completion {
BRUser *user = [BRSession userWithContext:[[BRCoreDataManager sharedManager] mainContext]];
[self.apiClient uploadProfilePicture:imageData forUser:user parameters:nil completion:[self uploadProfilePictureHandlerWithCompletion:completion]];
}
And then, also, here is the upload profile picture method in the API client:
- (void)uploadProfilePicture:(NSData *)imageData forUser:(BRUser *)user parameters:(NSDictionary *)parameters completion:(BRAPIClientCompletionBlock)completion {
NSError *error;
NSURLRequest *request = [self.requestSerializer multiformRequestForAPIAction:BRAPIActionCreate nestedResource:#"image" parent:user data:imageData parameters:parameters error:&error];
if (error) {
completion(nil, error);
}
NSURLSessionDataTaskCompletionBlock dataTaskCompletion = [self requestHandlerWithHTTPStatusErrors:#{ #400 : #(BRAPIUnauthorized) } completion:completion];
NSURLSessionDataTask *task = [self.authURLSession dataTaskWithRequest:request completionHandler:dataTaskCompletion];
[task resume];
}
and the multiform method called in the previous method:
- (NSURLRequest *)multiformRequestForAPIAction:(BRAPIAction)action nestedResource:(id)resource parent:(id)parent data:(NSData *)data parameters:(NSDictionary *)parameters error:(NSError *__autoreleasing *)error {
NSParameterAssert(action);
NSParameterAssert(resource);
NSParameterAssert(parent);
NSParameterAssert(data);
NSString *method = [BRAPIRequestSerializer HTTPMethodForAPIAction:action];
NSURL *url = [self.apiURL nestedURLForResource:resource parent:parent];
NSLog(#"URL: %#",[url absoluteStringWithTrailingSlash]);
return [self.serializer multipartFormRequestWithMethod:method
URLString:[url absoluteStringWithTrailingSlash]
parameters:parameters
constructingBodyWithBlock:^(id<AFMultipartFormData> formData) {
[formData appendPartWithFileData:data name:#"image" fileName:#"image.png" mimeType:#"image/png"];
}
error:error];
}
And my failed attempt at retrieving the data via the url it's stored at:
-(void) downloadImageFromURL :(NSString *)imageUrlString{
// Create the request.
NSURLRequest *theRequest=[NSURLRequest requestWithURL:[NSURL URLWithString:imageUrlString]
cachePolicy:NSURLRequestUseProtocolCachePolicy
timeoutInterval:60.0];
// Create the NSMutableData to hold the received data.
// receivedData is an instance variable declared elsewhere.
NSData * receivedData = [[NSMutableData alloc] init];
// create the connection with the request
// and start loading the data
NSURLConnection *theConnection=[[NSURLConnection alloc] initWithRequest:theRequest delegate:self];
if (!theConnection) {
// Release the receivedData object.
receivedData = nil;
// Inform the user that the connection failed.
NSLog(#"connection falied");
} else {
NSLog(#"connection succesful");
};
}
- (void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)data
{
NSLog(#"data: %#",data);
}
I also made sure to include the NSURLConnectionDelegate. The didRecieveData method is never called.
Feel free to let me know if there's more code you need to see!

OS X cocoa send HTTP response to PHP page, wait for PHP response to request, Continue

id like to achive what is mentioned in the title, can anyone point me in the right direction regarding ressources or torturials? I do understand the basics of the HTTP protocol, but i am fairly new to OS X programming.
In fact you can use the NSMutableURLRequest, if you want to make a test to start you can do this:
//test.h
#import <Foundation/Foundation.h>
#interface test : NSObject<NSURLConnectionDataDelegate>{
NSMutableData* _responseData;
}
//test.m
#implementation test
//Just call this method to start the request.
-(void)testRequest{
//set request
NSURL url = [NSURL URLWithString:#"http://ip/file.php"];
NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:url
cachePolicy:NSURLCacheStorageNotAllowed
timeoutInterval:20.0];
//Start the request
NSURLConnection * connection;
connection = [[NSURLConnection alloc] initWithRequest: request delegate:self];
}
after this you have to implement all the methods as woz said but catching the response:
#pragma mark - NSURLConectionDlegate Methods
- (void)connection:(NSURLConnection *)connection didReceiveResponse:(NSURLResponse *)response
{
_responseData = [[NSMutableData alloc] init];
}
//Receive data from the server
- (void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)data {
// Append the new data to the instance variable
[_responseData appendData:data];
}
- (NSCachedURLResponse *)connection:(NSURLConnection *)connection
willCacheResponse:(NSCachedURLResponse*)cachedResponse {
// Return nil to indicate not necessary to store a cached response for this connection
return nil;
}
//in this method you can check the response.
- (void)connectionDidFinishLoading:(NSURLConnection *)connection {
// The request is complete and data has been received
NSString *receivedDataString = [[NSString alloc] initWithData:_responseData encoding:NSUTF8StringEncoding];
NSLog(#"this is reponse: %#",receivedDataString);
}
server side
//file.php
echo "hello";
I like short solutions, and using blocks.
- (void)sendRequestWithURL:(NSURL*) url {
NSURLRequest *request = [NSURLRequest requestWithURL:url];
[NSURLConnection sendAsynchronousRequest:request
queue:[NSOperationQueue mainQueue]
completionHandler:^(NSURLResponse *response, NSData *data, NSError *error) {
if (!error) {
NSLog(#"%#", [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding]);
}
else {
///log error
}
}];
}

JSON objects from URL connection displayed in tableview

#pragma mark -
#pragma mark Fetch loans from internet
-(void)loadData
{
self.responseData = [NSMutableData data];
NSURLRequest *request = [NSURLRequest requestWithURL:[NSURL URLWithString:
#"http://192.168.1.104:8080/Test/ItemGroup.jsp"]];
[[NSURLConnection alloc] initWithRequest:request delegate:self];
}
- (void)connection:(NSURLConnection *)connection didReceiveResponse:(NSURLResponse *)response {
[responseData setLength:0];
}
- (void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)data {
[responseData appendData:data];
}
- (void)connection:(NSURLConnection *)connection didFailWithError:(NSError *)error {
[connection release];
self.responseData = nil;
}
#pragma mark -
#pragma mark Process loan data
- (void)connectionDidFinishLoading:(NSURLConnection *)connection {
[connection release];
NSString *responseString = [[NSString alloc] initWithData:responseData encoding:NSUTF8StringEncoding];
self.responseData = nil;
array=[responseString JSONValue];
NSMutableString *text=[NSMutableString stringWithString:#"Values:\n"];
for (int i=0; i <[array count]; i++) {
[text appendFormat:#"%#\n",[array objectAtIndex:i]];
// NSLog(#"Values:""%#\n",array);
}
}
You can use RestKit (a Cocoa RESTful web services framework) to fetch the data and use the object mapping feature to map returned data to an array of objects.
The example given in the RESTKit Object Mapping wiki maps a JSON doc into an array of Article instances: https://github.com/RestKit/RestKit/wiki/Object-mapping

How to return data directly which was loaded by NSURLConnection if delegate functions are needed?

A short explanation what I want to do: I'm using NSURLConnection to connect to a SSL webpage which is my API. The servers certificate is a self signed one so you have to accept it, for example in a web browser. I've found a solution on Stack Overflow how to do the trick (How to use NSURLConnection to connect with SSL for an untrusted cert?)
So I've added the NSURLConnection delegate to use methods like "didReceiveAuthenticationChallenge". As a result of that I cannot use this:
NSData *response = [NSURLConnection sendSynchronousRequest:request returningResponse:nil error:&error];
because there is no possibility to use the delegate functions in this case. My question is the following: I need a function which looks like this:
- (NSDictionary *)getData : (NSArray *)parameter {
[...|
NSURLConnection *theConnection = [[NSURLConnection alloc] initWithRequest:request delegate:self];
[...]
return myDictionary;
}
how can I return a NSDictionary by using this? As far as you know the delegate function of NSURLConnection are called now and the response isn't available at this point. The problem is that the view controller depends on this response so I need to return the dictionary directly... Does anybody know a solution for this? What about a callback function?
okay, I've found a solution for that. A very good thing is to use blocks in objective-c.
First of all you have to add some methods to NSURLRequest and NSURL:
#implementation NSURLRequest (URLFetcher)
- (void)fetchDataWithResponseBlock:(void (^)(FetchResponse *response))block {
FetchResponse *response = [[FetchResponse alloc] initWithBlock:block];
[[NSURLConnection connectionWithRequest:self delegate:response] start];
[response release];
}
#end
#implementation NSURL (URLFetcher)
- (void)fetchDataWithResponseBlock:(void (^)(FetchResponse *response))block {
[[NSURLRequest requestWithURL:self] fetchDataWithResponseBlock:block];
}
#end
And than just implement the follwing class:
#implementation FetchResponse
- (id)initWithBlock:(void(^)(FetchResponse *response))block {
if ((self = [super init])) {
_block = [block copy];
}
return self;
}
- (NSData *)data {
return _data;
}
- (NSURLResponse *)response {
return _response;
}
- (NSError *)error {
return _error;
}
- (NSInteger)statusCode {
if ([_response isKindOfClass:[NSHTTPURLResponse class]]) return [(NSHTTPURLResponse *)_response statusCode];
return 0;
}
- (void)connection:(NSURLConnection *)connection didReceiveResponse:(NSURLResponse *)response {
_response = response;
}
- (void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)data {
if (!_data) _data = [[NSMutableData alloc] init];
[_data appendData:data];
}
- (void)connectionDidFinishLoading:(NSURLConnection *)connection {
_block(self);
}
- (void)connection:(NSURLConnection *)connection didFailWithError:(NSError *)error {
_error = error;
_block(self);
}
Now you can do the follwing, some kind of callback function:
NSURL *url = [NSURL URLWithString:[NSString stringWithFormat:#"http://..."];
NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:url];
[request setValue:#"application/json" forHTTPHeaderField:#"accept"];
[request fetchDataWithResponseBlock:^(FetchResponse *response) {
if (response.error || response.statusCode != 200)
NSLog(#"Error: %#", response.error);
else {
//use response.data
}
}];
Here you can find the orginal german solution by ICNH: Asynchrones I/O mit Bloecken
Thank you very much for this!
My suggestion would be to use some other delegate methods for NSURLConnection like connection:didReceiveResponse: or connection:didReceiveData:. You should probably keep a use a set up like so:
#interface MyClass : NSObject {
…
NSMutableData *responseData;
}
…
#end
- (void)startConnection {
NSURLConnection *theConnection=[[NSURLConnection alloc] initWithRequest:request delegate:self];
if (theConnection) {
responseData = [[NSMutableData data] retain];
} else {
// connection failed
}
}
- (void)connection:(NSURLConnection *)connection didReceiveResponse:(NSURLResponse *)response {
// connection could have been redirected, reset the data
[responseData setLength:0];
}
- (void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)data {
[responseData appendData:data];
}
- (void)connectionDidFinishLoading:(NSURLConnection *)connection {
// connection is done, do what you want
………
// don't leak the connection or the response when you are done with them
[connection release];
[responseData release];
}
// for your authentication challenge
- (BOOL)connection:(NSURLConnection *)connection canAuthenticateAgainstProtectionSpace (NSURLProtectionSpace *)protectionSpace {
return [protectionSpace.authenticationMethod isEqualToString:NSURLAuthenticationMethodServerTrust];
}
- (void)connection:(NSURLConnection *)connection didReceiveAuthenticationChallenge:(NSURLAuthenticationChallenge *)challenge {
if ([challenge.protectionSpace.authenticationMethod isEqualToString:NSURLAuthenticationMethodServerTrust])
if ([trustedHosts containsObject:challenge.protectionSpace.host])
[challenge.sender useCredential:[NSURLCredential credentialForTrust:challenge.protectionSpace.serverTrust] forAuthenticationChallenge:challenge];
[challenge.sender continueWithoutCredentialForAuthenticationChallenge:challenge];
}