I have a background upload task that is defined in the following manner:
NSURLSessionUploadTask* task = [session_ uploadTaskWithRequest:request fromFile:[NSURL fileURLWithPath:httpBody]];
I would like to deletehttpBody once the upload task is finished, which happens in my delegate in this function:
- (void)URLSession:(NSURLSession *)session task:(NSURLSessionTask *)task didCompleteWithError:(NSError *)error
{
Q_UNUSED(session);
if (error)
{
reportError(callback, error);
}
else
{
NSMutableData *responseData = self.responsesData[#(task.taskIdentifier)];
if (responseData)
{
NSDictionary *response = [NSJSONSerialization JSONObjectWithData:responseData options:0 error:nil];
if (response)
{
LOG_INFO << [NSString stringWithFormat:#"%#", response];
}
else
{
LOG_INFO << [[NSString alloc] initWithData:responseData encoding:NSUTF8StringEncoding];
}
[self.responsesData removeObjectForKey:#(task.taskIdentifier)];
}
(*callback)(boost::none);
}
}
I am not sure how to access httpBody in didCompleteWithError so I can delete it. How do I do this?
You can get back the originalRequest from the task. If there is nothing in there to use, you can create the request as an NSMutableRequest to start and use:
+ (void)setProperty:(id)value
forKey:(NSString *)key
inRequest:(NSMutableURLRequest *)request;
To add your filename to it. You can get this later with:
+ (id)propertyForKey:(NSString *)key
inRequest:(NSURLRequest *)request;
More info:
https://developer.apple.com/documentation/foundation/nsurlprotocol/1407897-setproperty?language=objc
Related
My application having around 10 to 30 webservices(both get/post).previously i have used Async NSURLconnection. which takes around 5 sec to excute all services. But now i have replaced NSurlsession which get delay in response compare to nsurlconnection ,it is delayed by 4 to 6 times . here i have attached one of the webservice method code for reference . like below i have code for all methods .
Example NSURLsession code
#pragma mark - NSURLSession
- (NSURLSession *)createSession
{
NSLog(#"DSCReturnAPI: %s",__func__);
static NSURLSession *session = nil;
NSURLSessionConfiguration *sessionConfig = [NSURLSessionConfiguration defaultSessionConfiguration];
sessionConfig.timeoutIntervalForRequest = WEBSERVICE_TIMEOUT;
session = [NSURLSession sessionWithConfiguration:sessionConfig
delegate:self
delegateQueue:[NSOperationQueue mainQueue]];
return session;
}
#pragma mark - API Callbacks
-(void)getMyData{
#try {
_webServieRequestType = HWDSCReturnGetRequestType;
NSString *countrycode = [BusinessManager sharedInstance].countryCode;//[HWUserDefaults countryCode];
NSString *userid = [BusinessManager sharedInstance].userId;// [HWUserDefaults userId];
NSString *accessToken = [BusinessManager sharedInstance].accessToken;//[HWUserDefaults accessToken];
NSString *deviceId = [HWUserDefaults deviceId];
[HelperCallbacks logText:[NSString stringWithFormat:#"AccessToken %#",accessToken]];
NSMutableURLRequest *request = [[NSMutableURLRequest alloc] init];
[request setURL:[NSURL URLWithString:API_DSCRETURN(countrycode,userid)]];
[request setHTTPMethod:#"GET"];
[request setValue:#"application/json" forHTTPHeaderField:#"Content-Type"];
[request setValue:accessToken forHTTPHeaderField:#"Authorization"];
[request setValue:deviceId forHTTPHeaderField:#"deviceId"];
[request setValue:#"ba598025146eff37e26c9150b180a78b" forHTTPHeaderField:#"If-None-Match"];
theSession = [self createSession];
theDataTask = [theSession dataTaskWithRequest:request];
[theDataTask resume];
}
#catch (NSException *exception) {
[ExceptionHandler sendException:exception];
}
}
#pragma mark - NSURLSessionDataDelegate
- (void)URLSession:(NSURLSession *)session dataTask:(NSURLSessionDataTask *)dataTask
didReceiveData:(NSData *)data {
NSLog(#"DSCReturnAPI: %s",__func__);
// NSArray *dataArray = [NSJSONSerialization JSONObjectWithData:data options:0 error:nil];
if (theSession == session) {
if (_receivedData == nil) {
_receivedData = [[NSMutableData alloc] initWithData:data];
} else {
[_receivedData appendData:data];
}
}
else
{
//NSLog(#"");
}
}
- (void)URLSession:(NSURLSession *)session dataTask:(NSURLSessionDataTask *)dataTask didReceiveResponse:(NSURLResponse *)response completionHandler:(void (^)(NSURLSessionResponseDisposition disposition))completionHandler {
NSLog(#"DSCReturnAPI: %s",__func__);
if (theSession == session) {
[BusinessManager sharedInstance].urlResponseRecieved = NO;
NSLog(#"DSCReturnAPI: httpResponse:%#",response);
NSHTTPURLResponse* httpResponse = (NSHTTPURLResponse*)response;
httpStatusCode =(int) [httpResponse statusCode]; // To track response Code:
NSLog(#"DSCReturnAPI : Http code:%d",httpStatusCode);
NSLog(#"DSCReturnAPI : Headers %#",[httpResponse allHeaderFields]);
_receivedData = [[NSMutableData alloc]init]; }
completionHandler(NSURLSessionResponseAllow);
}
#pragma mark - NSURLSessionTaskDelegate
- (void)URLSession:(NSURLSession *)session task:(NSURLSessionTask *)task didCompleteWithError:(NSError *)error{
NSLog(#"DSCReturnAPI: %s",__func__);
NSLog(#"DSCReturnAPI: didFailWithError :error : %# errorCode: %ld",error.description,(long)error.code);
#try {
if ([self.serviceDelegate conformsToProtocol:#protocol(ServiceMgrProtocol)] && [self.serviceDelegate respondsToSelector:#selector(webServiceDidFailDSCReturnWithError:)]&& error.code != 0) {
[self.serviceDelegate webServiceDidFailDSCReturnWithError:error.localizedDescription];
}
else {
NSLog(#"DSCReturnAPI:SUCCESS");
[self processResponse];
}
}
#catch (NSException *exception) {
[ExceptionHandler sendException:exception];
}
#finally {
if (theSession) {
theSession = nil;
}
_receivedData = nil;
} }
I think no need to go for such methods of NSURLSession . It has its own block which runs on main thread.
NSMutableURLRequest *request =[[NSMutableURLRequest alloc] initWithURL:requestURL];
NSLog(#"fetchAPromotionWithType = %#", requestURL);
[request setHTTPMethod:#"GET"];
NSURLSession *session = [NSURLSession sharedSession];
NSURLSessionDataTask *task = [session dataTaskWithRequest:request
completionHandler:
^(NSData *data, NSURLResponse *response, NSError *error)
{
dispatch_async(dispatch_get_main_queue(), ^{
NSHTTPURLResponse* httpResponse = (NSHTTPURLResponse*)response;
int responseStatusCode = (int)[httpResponse statusCode];
if ( responseStatusCode == OK_RESPONSE_CODE || responseStatusCode == CREATED_RESPONSE_CODE || responseStatusCode == ACCEPTED_RESPONSE_CODE || responseStatusCode == PARTIAL_RESPONSE_CODE)
{
if (data)
{
NSDictionary *dict = [NSJSONSerialization JSONObjectWithData:data options:kNilOptions error:nil];
NSLog(#"Data->%#",dict);
if (dict != nil && [dict isKindOfClass:[NSDictionary class]])
{
NSDictionary *dataDict = [dict objectForKey:#"data"];
if (dataDict != nil && [dataDict isKindOfClass:[NSDictionary class]])
{
//Get your success Data
}
}
}
}
else
{
if (responseStatusCode == 0)
{
NSDictionary *dict = [NSDictionary dictionaryWithObjectsAndKeys:#"No Connection! Please try again.", #"message", nil];
//Get Your Fail Data
}
else if (data)
{
// NSArray *tempArray = #[#"fan", #"vungle"];
//[JLSharedData sharedManager].savedWaterfallInterstitialArray = nil; //testing
NSDictionary *dict = [NSJSONSerialization JSONObjectWithData:data options:kNilOptions error:nil];
//Get Your Fail Data
}
}
});
}];
[task resume];
}];
I am trying to call a web service. I tried this
- (void)viewDidLoad
{
[super viewDidLoad];
// Do any additional setup after loading the view.
NSURLRequest *request = [NSURLRequest requestWithURL:[NSURL URLWithString:#"http://192.168.75.1:8082/projectname/public/tests"]];
NSURLSessionConfiguration *configuration = [ NSURLSessionConfiguration ephemeralSessionConfiguration];
NSURLSession *session = [NSURLSession sessionWithConfiguration:configuration];
NSURLSessionDownloadTask *task = [session downloadTaskWithRequest:request completionHandler:^(NSURL *localFile, NSURLResponse *response, NSError *error) {
if(!error){
NSLog(#"no error");
}else{
NSLog(#"error");
}
}];
[task resume];
}
as you see there are two nslog statements. I got the no error one.
when I call that web service from my safari, I got a simple string which is index printed in the browser, how can I see that string in my xcode please?
Thanks
you can implement the delegate method
- (void)URLSession:(NSURLSession *)session downloadTask:(NSURLSessionDownloadTask *)downloadTask didFinishDownloadingToURL:(NSURL *)location;
EDIT
Try This
NSHTTPURLResponse *response = nil;
NSError *error = nil;
NSMutableURLRequest *request = [[NSMutableURLRequest alloc] initWithURL:[NSURL URLWithString:YOUR URL]];
NSData *respData = [NSURLConnection sendSynchronousRequest:request returningResponse:&response error:&error];
NSLog(#"~~~~~ Status code: %d", [response statusCode]);
//Print your recived data here..
NSString *str = [[NSString alloc] initWithData:respData encoding:NSUTF8StringEncoding];
NSLog(#"str: %#", str);
You can use the delegate methods. When a NSURLSessionDownlaodTask is completed, it's delegates will be called if your class confirmed to it.
-(void)URLSession:(NSURLSession *)session downloadTask:(NSURLSessionDownloadTask *)downloadTask didFinishDownloadingToURL:(NSURL *)location
you can get your response by parsing the data in that delegate method. It will tell you the location that the URLSession stores the downloaded result.
what I would do If I were you is:
NOTE: it is based on you said you receive a simple string only from your back-end. if that is not a simple string, you may need to revise the –connectionDidFinishLoading: method's body.
.h
#interface UIRandomViewController : UIViewController {
NSURLConnection *_urlConnection;
NSMutableData *_receivedData;
// ...
}
// ...
#end
.m
#implementation UIRandomViewController {
// ...
- (void)viewDidAppear:(BOOL)animated {
[super viewDidAppear:animated];
NSURLRequest *_request = [NSURLRequest requestWithURL:[NSURL URLWithString:#"http://192.168.75.1:8082/projectname/public/tests"]];
_urlConnection = [[NSURLConnection alloc] initWithRequest:_request delegate:self startImmediately:TRUE];
// ...
}
// ...
#pragma mark - <NSURLConnectionDelegate>
- (void)connection:(NSURLConnection *)connection didFailWithError:(NSError *)error {
_receivedData = nil;
}
- (void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)data {
if (_receivedData == nil) _receivedData = [NSMutableData dataWithData:data];
else [_receivedData appendData:data];
}
- (void)connectionDidFinishLoading:(NSURLConnection *)connection {
NSString *_receivedString = [[NSString alloc] initWithData:_receivedData encoding:NSUTF8StringEncoding];
// hello beautiful...
NSLog(#"received data : %#", _receivedString);
}
}
#end
I am using NSURLSession for background image uploading. And according to uploaded image my server gives me response and I do change in my app accordingly. But I can't get my server response when my app uploading image in background because there is no completion block.
Is there way to get response without using completion block in NSURLUploadTask?
Here is my code :
self.uploadTask = [self.session uploadTaskWithRequest:request fromData:body completionHandler:^(NSData *data, NSURLResponse *response, NSError *error) {
NSString *returnString = [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding];
NSLog(#"returnString : %#",returnString);
NSLog(#"error : %#",error);
}];
[self.uploadTask resume];
But i got this error..
Terminating app due to uncaught exception 'NSGenericException', reason: 'Completion handler blocks are not supported in background sessions. Use a delegate instead.'
But if I can't use completion handler than how should I get the server response. It says use delegate but I can't find any delegate method which can gives me server response.
A couple of thoughts:
First, instantiate your session with a delegate, because background sessions must have a delegate:
NSURLSessionConfiguration *configuration = [NSURLSessionConfiguration backgroundSessionConfigurationWithIdentifier:kSessionIdentifier];
self.session = [NSURLSession sessionWithConfiguration:configuration delegate:self delegateQueue:nil];
Second, instantiate your NSURLSessionUploadTask without a completion handler, because tasks added to a background session cannot use completion blocks. Also note, I'm using a file URL rather than a NSData:
NSURLSessionTask *task = [self.session uploadTaskWithRequest:request fromFile:fileURL];
[task resume];
Third, implement the relevant delegate methods. At a minimum, that might look like:
- (void)URLSession:(NSURLSession *)session dataTask:(NSURLSessionDataTask *)dataTask didReceiveData:(NSData *)data {
NSMutableData *responseData = self.responsesData[#(dataTask.taskIdentifier)];
if (!responseData) {
responseData = [NSMutableData dataWithData:data];
self.responsesData[#(dataTask.taskIdentifier)] = responseData;
} else {
[responseData appendData:data];
}
}
- (void)URLSession:(NSURLSession *)session task:(NSURLSessionTask *)task didCompleteWithError:(NSError *)error {
if (error) {
NSLog(#"%# failed: %#", task.originalRequest.URL, error);
}
NSMutableData *responseData = self.responsesData[#(task.taskIdentifier)];
if (responseData) {
// my response is JSON; I don't know what yours is, though this handles both
NSDictionary *response = [NSJSONSerialization JSONObjectWithData:responseData options:0 error:nil];
if (response) {
NSLog(#"response = %#", response);
} else {
NSLog(#"responseData = %#", [[NSString alloc] initWithData:responseData encoding:NSUTF8StringEncoding]);
}
[self.responsesData removeObjectForKey:#(task.taskIdentifier)];
} else {
NSLog(#"responseData is nil");
}
}
Note, the above is taking advantage of a previously instantiated NSMutableDictionary called responsesData (because, much to my chagrin, these "task" delegate methods are done at the "session" level).
Finally, you want to make sure to define a property to store the completionHandler provided by handleEventsForBackgroundURLSession:
#property (nonatomic, copy) void (^backgroundSessionCompletionHandler)(void);
And obviously, have your app delegate respond to handleEventsForBackgroundURLSession, saving the completionHandler, which will be used below in the URLSessionDidFinishEventsForBackgroundURLSession method.
- (void)application:(UIApplication *)application handleEventsForBackgroundURLSession:(NSString *)identifier completionHandler:(void (^)())completionHandler {
// This instantiates the `NSURLSession` and saves the completionHandler.
// I happen to be doing this in my session manager, but you can do this any
// way you want.
[SessionManager sharedManager].backgroundSessionCompletionHandler = completionHandler;
}
And then make sure your NSURLSessionDelegate calls this handler on the main thread when the background session is done:
- (void)URLSessionDidFinishEventsForBackgroundURLSession:(NSURLSession *)session {
if (self.backgroundSessionCompletionHandler) {
dispatch_async(dispatch_get_main_queue(), ^{
self.backgroundSessionCompletionHandler();
self.backgroundSessionCompletionHandler = nil;
});
}
}
This is only called if some of the uploads finished in the background.
There are a few moving parts, as you can see, but that's basically what's entailed.
I have set up a simple Objective-C class in my iOS app which has one simple task, to download a JSON file, parse it and then pass back a NSString which contains a variable parsed from the downloaded JSON file.
The problem I have is that I am calling this class from another class and this all works great however I need to pass back the NSString to the class from which I am calling it from.
The problem is that the method passes back the empty NSString BEFORE connectionDidFinishLoading happens.... And so the NSString never gets assigned a string......
I have setup a while loop in my method but it doesn't really work.....
here is my code:
-(NSString *)get_user_icon:(NSString *)YT_ID {
// Set BOOL to 0 for initial setup.
icon_check = 0;
NSString *url_YT = [NSString stringWithFormat:YOUT_profile_part_2, YT_ID];
dispatch_queue_t downloadQueue = dispatch_queue_create("Icon downloader YouTube", NULL);
dispatch_async(downloadQueue, ^{
dispatch_async(dispatch_get_main_queue(), ^{
NSURLRequest *theRequest_YT = [NSURLRequest requestWithURL:[NSURL URLWithString:url_YT]];
NSURLConnection *theConnection_YT = [[NSURLConnection alloc] initWithRequest:theRequest_YT delegate:self];
if (theConnection_YT) {
YT_JSON_FEED = [[NSMutableData alloc] init];
NSLog(#"Respoce happening...");
}
else {
NSLog(#"failed");
}
});
});
while (icon_check == 0) {
NSLog(#"Wait");
}
return icon_url;
}
/// Data loading ///
-(void)connection:(NSURLConnection *)connection didReceiveResponse:(NSURLResponse *)response {
[YT_JSON_FEED setLength:0];
}
-(void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)data {
[YT_JSON_FEED appendData:data];
}
-(void)connection:(NSURLConnection *)connection didFailWithError:(NSError *)error {
NSString *msg = [NSString stringWithFormat:#"Failed: %#", [error description]];
NSLog(#"%#",msg);
}
-(void)connectionDidFinishLoading:(NSURLConnection *)connection {
NSError *myError = nil;
NSDictionary *feed = [NSJSONSerialization JSONObjectWithData:YT_JSON_FEED options:NSJSONReadingMutableLeaves error:&myError];
icon_url = [[[[[feed objectForKey:#"items"] valueForKey:#"snippet"] valueForKey:#"thumbnails"] valueForKey:#"default"] valueForKey:#"url"];
icon_check = 1;
}
For a synchronous request (blocking until there is something to return), use NSURLConnection's sendSynchronousRequest:returningResponse:error: instead. Like so:
-(NSString *)get_user_icon:(NSString *)YT_ID {
NSString *url_YT = [NSString stringWithFormat:YOUT_profile_part_2, YT_ID];
NSURLRequest *theRequest_YT = [NSURLRequest requestWithURL:[NSURL URLWithString:url_YT]];
NSURLResponse* response = nil;
NSError* error = nil;
NSData* data = [NSURLConnection sendSynchronousRequest:theRequest_YT returningResponse:&response error:&error];
//Check response and error for possible errors here.
//If no errors.
NSDictionary *feed = [NSJSONSerialization JSONObjectWithData:data options:NSJSONReadingMutableLeaves error:&myError];
icon_url = [[[[[feed objectForKey:#"items"] valueForKey:#"snippet"] valueForKey:#"thumbnails"] valueForKey:#"default"] valueForKey:#"url"];
return icon_url;
}
However this is not recommended. You need to change your API to be asynchronous. Either delegate-based, but more preferably, using block-based API.
- (void)connection:(NSURLConnection *)connection didReceiveResponse:(NSURLResponse *)response
{
[self.responseData setLength:0];(URL1)
self.jsonData = [[NSMutableData alloc]init];(URL2)
self.genderData = [[NSMutableData alloc]init];(URL3)
}
I want to send multiple url at a time what the process to receive response..?
- (void)connectionDidFinishLoading:(NSURLConnection *)connection {
NSLog(#"connectionDidFinishLoading");
NSLog(#"Succeeded! Received %d bytes of data",[self.responseData length]);
NSError * error;
id result = (NSMutableArray *)[NSJSONSerialization JSONObjectWithData:self.jsonData options:kNilOptions error:&error];
if (error)
{
NSLog(#"DATA LOAD ERROR");
}
else
{
if([result isKindOfClass:[NSArray class]])
{
titlesArray = result;
}
else
{
titlesDic = result;
}
}
[self genderURLMethod];
}
This is another Delegate Method
When working with NSURLConnection, I tend to go one of two ways.
Always use a different class for each delegate. That way the delegate instance only has to worry about 1 URL.
Use [[connection originalRequest] URL] to distinguish between the different URLs.
Example:
- (void)connectionDidFinishLoading:(NSURLConnection *)connection
{
NSString *URLString = [[[[connection originalRequest] URL] absoluteString];
if ([URLString isEqualToString:MY_URL_1]) {
// Handle the response for the first URL.
} else if ([URLString isEqualToString:MY_URL_2]) {
// Handle the response for the second URL/
}
}