Why is background NSURLConnection having error when enter background? - ios

I am having this error when my app enter background.
NSURLConnection finished with error - code -1001 Task
<09B84034-9F73-4DB6-A685-D891B1B1068A>.<2> finished with error - code:
-1001
I am using this code
- (id<XCDYouTubeOperation>) getVideoWithIdentifier:(NSString *)videoIdentifier completionHandler:(void (^)(XCDYouTubeVideo * __nullable video, NSError * __nullable error))completionHandler
{
NSLog(#"Getting Video Identfifier");
if (!completionHandler)
#throw [NSException exceptionWithName:NSInvalidArgumentException reason:#"The `completionHandler` argument must not be nil." userInfo:nil];
XCDYouTubeVideoOperation *operation = [[XCDYouTubeVideoOperation alloc] initWithVideoIdentifier:videoIdentifier languageIdentifier:self.languageIdentifier];
operation.completionBlock = ^{
[[NSOperationQueue mainQueue] addOperationWithBlock:^{
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Warc-retain-cycles"
if (operation.video || operation.error)
{
NSAssert(!(operation.video && operation.error), #"One of `video` or `error` must be nil.");
completionHandler(operation.video, operation.error);
}
else
{
NSAssert(operation.isCancelled, #"Both `video` and `error` can not be nil if the operation was not canceled.");
}
operation.completionBlock = nil;
#pragma clang diagnostic pop
}];
};
NSLog(#"Operation - %#", operation ) ;
[self.queue addOperation:operation];
return operation;
}`
- (void) startRequestWithURL:(NSURL *)url type:(XCDYouTubeRequestType)requestType
{
if (self.isCancelled)
return;
// Max (age-restricted VEVO) = 2×GetVideoInfo + 1×WatchPage + 1×EmbedPage + 1×JavaScriptPlayer + 1×GetVideoInfo
if (++self.requestCount > 6)
{
// This condition should never happen but the request flow is quite complex so better abort here than go into an infinite loop of requests
[self finishWithError];
return;
}
XCDYouTubeLogDebug(#"Starting request: %#", url);
NSMutableURLRequest *request = [[NSMutableURLRequest alloc] initWithURL:url cachePolicy:NSURLRequestUseProtocolCachePolicy timeoutInterval:10];
[request setValue:self.languageIdentifier forHTTPHeaderField:#"Accept-Language"];
NSLog(#"Request Type - ",requestType);
// NSURLSessionDataTask *task = [self.session dataTaskWithRequest:request];
// [task resume];
self.dataTask = [self.session dataTaskWithRequest:request completionHandler:^(NSData *data, NSURLResponse *response, NSError *error)
{
if (self.isCancelled)
return;
if (error)
[self handleConnectionError:error];
else
[self handleConnectionSuccessWithData:data response:response requestType:requestType];
}];
[self.dataTask resume];
self.requestType = requestType;
}
#pragma mark - Response Dispatch
- (void) handleConnectionSuccessWithData:(NSData *)data response:(NSURLResponse *)response requestType:(XCDYouTubeRequestType)requestType
{
NSLog(#"XCDDRequestType - ",requestType);
CFStringEncoding encoding = CFStringConvertIANACharSetNameToEncoding((__bridge CFStringRef)response.textEncodingName ?: CFSTR(""));
// Use kCFStringEncodingMacRoman as fallback because it defines characters for every byte value and is ASCII compatible. See https://mikeash.com/pyblog/friday-qa-2010-02-19-character-encodings.html
NSString *responseString = CFBridgingRelease(CFStringCreateWithBytes(kCFAllocatorDefault, data.bytes, (CFIndex)data.length, encoding != kCFStringEncodingInvalidId ? encoding : kCFStringEncodingMacRoman, false)) ?: #"";
NSAssert(responseString.length > 0, #"Failed to decode response from %# (response.textEncodingName = %#, data.length = %#)", response.URL, response.textEncodingName, #(data.length));
XCDYouTubeLogVerbose(#"Response: %#\n%#", response, responseString);
switch (requestType)
{
case XCDYouTubeRequestTypeGetVideoInfo:
[self handleVideoInfoResponseWithInfo:XCDDictionaryWithQueryString(responseString) response:response];
break;
case XCDYouTubeRequestTypeWatchPage:
[self handleWebPageWithHTMLString:responseString];
break;
case XCDYouTubeRequestTypeEmbedPage:
[self handleEmbedWebPageWithHTMLString:responseString];
break;
case XCDYouTubeRequestTypeJavaScriptPlayer:
[self handleJavaScriptPlayerWithScript:responseString];
break;
}
}
This code will automatically run in background but after a few minutes it will stop and gives me the above error. How to fix this ?
EDIT 1 (Vinay Kiran Method) #
i changed the nsurlsessionconfiguration to background.
- (instancetype) initWithVideoIdentifier:(NSString *)videoIdentifier languageIdentifier:(NSString *)languageIdentifier
{
if (!(self = [super init]))
return nil; // LCOV_EXCL_LINE
_videoIdentifier = videoIdentifier ?: #"";
_languageIdentifier = languageIdentifier ?: #"en";
// _session = [NSURLSession sessionWithConfiguration:[NSURLSessionConfiguration ephemeralSessionConfiguration]];
NSURLSessionConfiguration *configuration = [NSURLSessionConfiguration backgroundSessionConfigurationWithIdentifier:#"YouTubeID"];
_session = [NSURLSession sessionWithConfiguration:configuration delegate:self delegateQueue:nil];
_operationStartSemaphore = dispatch_semaphore_create(0);
NSLog(#"Initialize the Video Identifier");
return self;
}
then change the completion handler since background it will give this error if i use handler
Swift: 'Completion handler blocks are not supported in background sessions. Use a delegate instead.'
- (void) startRequestWithURL:(NSURL *)url type:(XCDYouTubeRequestType)requestType
{
if (self.isCancelled)
return;
// Max (age-restricted VEVO) = 2×GetVideoInfo + 1×WatchPage + 1×EmbedPage + 1×JavaScriptPlayer + 1×GetVideoInfo
if (++self.requestCount > 6)
{
// This condition should never happen but the request flow is quite complex so better abort here than go into an infinite loop of requests
[self finishWithError];
return;
}
XCDYouTubeLogDebug(#"Starting request: %#", url);
NSMutableURLRequest *request = [[NSMutableURLRequest alloc] initWithURL:url cachePolicy:NSURLRequestUseProtocolCachePolicy timeoutInterval:10];
[request setValue:self.languageIdentifier forHTTPHeaderField:#"Accept-Language"];
NSLog(#"Request Type - ",requestType);
// NEWLY ADDED
NSURLSessionDataTask *task = [self.session dataTaskWithRequest:request];
[task resume];
// self.dataTask = [self.session dataTaskWithRequest:request completionHandler:^(NSData *data, NSURLResponse *response, NSError *error)
// {
// if (self.isCancelled)
// return;
//
// if (error)
// [self handleConnectionError:error];
// else
// [self handleConnectionSuccessWithData:data response:response requestType:requestType];
// }];
// [self.dataTask resume];
self.requestType = requestType;
}
the problem now is that i originally use this
self.dataTask = [self.session dataTaskWithRequest:request completionHandler:^(NSData *data, NSURLResponse *response, NSError *error)
{
if (self.isCancelled)
return;
if (error)
[self handleConnectionError:error];
else
[self handleConnectionSuccessWithData:data response:response requestType:requestType];
}];
[self.dataTask resume];
which handleConnectionSuccessWithData will take in data, response and request type. Now i don't know where can i get the data, response and request type if i use backgroundSessionConfigurationWithIdentifier.

Use background thread instead of the main queue
backgroundSessionConfigurationWithIdentifier:
For reference
https://developer.apple.com/documentation/foundation/nsurlsessionconfiguration/1407496-backgroundsessionconfigurationwi?language=objc

Related

Asynchronous call using xCode

I have another very beginner's question related to xCode. I am completely new to iOS development so I appreciate you guys to reply me.
I have written the following class to access the Restful API. The code in the method "makePostRequest" works fine if I write it directly in the calling method. But, I want to make it asynchronous and I don't know exactly how can I make this work asynchronous. Can somebody help me please to write this as asynchronos call?
#import <Foundation/Foundation.h>
#import "ServerRequest.h"
#import "NetworkHelper.h"
#implementation ServerRequest
#synthesize authorizationRequest=_authorizationRequest;
#synthesize responseContent=_responseContent;
#synthesize errorContent=_errorContent;
#synthesize url=_url;
#synthesize urlPart=_urlPart;
#synthesize token=_token;
- (void)makePostRequest : (NSString *) params {
NSString *urlString = [NSString stringWithFormat:#"%#%#", [self getUrl], [self getUrlPart]];
NSURL *url = [NSURL URLWithString:urlString];
NSURLSessionConfiguration *config = [NSURLSessionConfiguration defaultSessionConfiguration];
NSURLSession *session = [NSURLSession sessionWithConfiguration:config delegate:nil delegateQueue:[NSOperationQueue mainQueue]];
NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:url];
[request setHTTPMethod:#"POST"];
[request setValue:#"application/json" forHTTPHeaderField:#"Accept"];
if([self isAuthorizationRequest]) {
[request setValue:#"application/x-www-form-urlencoded" forHTTPHeaderField:#"Content-Type"];
[request setValue:#"Basic" forHTTPHeaderField:#"Authorization"];
}
else {
NSString *authorizationValue = [NSString stringWithFormat:#"Bearer %#", [self getToken]];
[request setValue:authorizationValue forHTTPHeaderField:#"Authorization"];
}
if(params.length > 0)
[request setHTTPBody:[params dataUsingEncoding:NSUTF8StringEncoding]];
#try {
NSURLSessionDataTask *dataTask = [session dataTaskWithRequest:request
completionHandler:^(NSData *data, NSURLResponse *response, NSError *error) {
if(error) {
NSLog(#"Error: %#", error);
}
if([response isKindOfClass:[NSHTTPURLResponse class]]) {
NSInteger statusCode = [(NSHTTPURLResponse *)response statusCode];
if(statusCode == [NetworkHelper HTTP_STATUS_CODE]) {
self.responseContent = [NSJSONSerialization JSONObjectWithData:data
options:NSJSONReadingMutableContainers|NSJSONReadingMutableLeaves
error:nil];
}
else {
self.errorContent = [NSJSONSerialization JSONObjectWithData:data
options:NSJSONReadingMutableContainers|NSJSONReadingMutableLeaves
error:nil];
}
}
}];
[dataTask resume];
}
#catch (NSException *exception) {
NSLog(#"Exception while making request: %#", exception);
} #finally {
NSLog(#"finally block here");
}
}
- (void)setAuthorization : (bool)value {
self.authorizationRequest = &value;
}
- (bool)isAuthorizationRequest {
return self.authorizationRequest;
}
- (NSDictionary *)getResponseContent {
return self.responseContent;
}
- (NSDictionary *)getErrorContent {
return self.errorContent;
}
- (void)setToken:(NSString *)token {
self.token = token;
}
- (NSString *)getToken {
return self.token;
}
- (void)setUrl:(NSString *)value {
//self.url = value;
_url = value;
}
- (NSString *)getUrl {
return self.url;
}
- (void)setUrlPart:(NSString *)value {
self.urlPart = value;
}
- (NSString *)getUrlPart {
if(self.urlPart.length == 0)
return #"";
return self.urlPart;
}
#end
I'm giving you an example how you can make your method serve you data when available. It's block based. So you don't have to consider asynchronous task here.
First define your completion block in your ServerRequest.h:
typedef void(^myCompletion)(NSDictionary*, NSError*);
And change your method's signature to this:
- (void) makePostRequest:(NSString *)params completion: (myCompletion)completionBlock;
Now change your method's implementation to something like this (I'm only posting your #try block, so just change your try block. Others remain same)
#try {
NSURLSessionDataTask *dataTask = [session dataTaskWithRequest:request
completionHandler:^(NSData *data, NSURLResponse *response, NSError *error) {
if(error) {
NSLog(#"Error: %#", error);
if (completionBlock) {
completionBlock(nil, error);
}
}
if([response isKindOfClass:[NSHTTPURLResponse class]]) {
NSInteger statusCode = [(NSHTTPURLResponse *)response statusCode];
if(statusCode == [NetworkHelper HTTP_STATUS_CODE]) {
NSError *error;
self.responseContent = [NSJSONSerialization JSONObjectWithData:data
options:NSJSONReadingMutableContainers|NSJSONReadingMutableLeaves
error:&error];
if (completionBlock) {
if (error == nil) {
completionBlock(self.responseContent, nil);
} else {
completionBlock(nil, error);
}
}
} else {
NSError *error;
self.errorContent = [NSJSONSerialization JSONObjectWithData:data
options:NSJSONReadingMutableContainers|NSJSONReadingMutableLeaves
error:&error];
if (completionBlock) {
if (error == nil) {
completionBlock(self.errorContent, nil);
} else {
completionBlock(nil, error);
}
}
}
}
}];
[dataTask resume];
}
Finally, when you call this method from somewhere else, use this as:
[serverRequestObject makePostRequest:#"your string" completion:^(NSDictionary *dictionary, NSError *error) {
// when your data is available after NSURLSessionDataTask's job, you will get your data here
if (error != nil) {
// Handle your error
} else {
// Use your dictionary here
}
}];

Activity Indicator while calling API

I want to display the activity indicator while waiting for the API to return. The problem is after all the result I get from API, then the spinner only display. The result I want is while waiting for API call, then the spinner will running.
I'm calling this method in here
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
[self startLoadingSpinner]
//Calling API...
[self stopLoadingSpinner]
}
Here is the method for the activity indicator
-(void)startLoadingSpinner {
self.activityIndicator = [[UIActivityIndicatorView alloc] initWithFrame:CGRectMake(0, 0, 70, 70)];
self.activityIndicator.opaque = YES;
self.activityIndicator.backgroundColor = [UIColor colorWithWhite:0.0f alpha:0.4f];
self.activityIndicator.center = self.view.center;
self.activityIndicator.activityIndicatorViewStyle = UIActivityIndicatorViewStyleGray;
[self.activityIndicator setColor:[UIColor whiteColor]];
[self.view addSubview:self.activityIndicator];
[self.activityIndicator startAnimating];
}
This is how I stop the activity indicator
-(void)stopLoadingSpinner {
[self.activityIndicator performSelector:#selector(removeFromSuperview) withObject:nil afterDelay:0.5];
}
Don't add activity indicators in tableview datasource method - numberOfRowsInSection .
Add these two functions calling in the same method where you are making an API call. Make an API call in ViewDidLoad, some life cycle method or in action methods.
Below is the example of using it.
NSURLSessionConfiguration *configuration = [NSURLSessionConfiguration defaultSessionConfiguration];
AFURLSessionManager *manager = [[AFURLSessionManager alloc] initWithSessionConfiguration:configuration];
NSURL *URL = [NSURL URLWithString:#"http://httpbin.org/get"];
NSURLRequest *request = [NSURLRequest requestWithURL:URL];
[self startLoadingSpinner]
NSURLSessionDataTask *dataTask = [manager dataTaskWithRequest:request completionHandler:^(NSURLResponse *response, id responseObject, NSError *error) {
if (error) {
NSLog(#"Error: %#", error);
} else {
NSLog(#"%# %#", response, responseObject);
}
[self stopLoadingSpinner]
}];
[dataTask resume];
In Swift
func makeAPIRequest(to endPoint: String) {
// here you can showActivetyIndicator start progressing here
self.startLoadingSpinner()
Alamofire.request(endPoint).responseJSON{ response in
if let value = response.result.value {
let responseInJSON = JSON(value)
self._responseInJSON = responseInJSON
}
// here you can hide Your ActivetyIndicator here
self.stopLoadingSpinner()
}
}
My detailed answer is below
-(void)simpleGetResponse{
#try {
//Call the Activity Indicator show method here
[self startLoadingSpinner];
NSString *strURL = #"Your URL";
NSURL *urlStr = [NSURL URLWithString:strURL];
NSMutableURLRequest *mutaURL = [NSMutableURLRequest requestWithURL:urlStr];
[mutaURL setHTTPMethod:#"GET"];
NSURLSession *session = [NSURLSession sharedSession];
NSURLSessionDataTask *dataTask = [session dataTaskWithRequest:mutaURL completionHandler:^(NSData *data, NSURLResponse *response, NSError *error)
{
NSHTTPURLResponse *httpResponse = (NSHTTPURLResponse *)response;
if(httpResponse.statusCode == 200)
{
NSError *parseError = nil;
id response = [NSJSONSerialization JSONObjectWithData:data options:0 error:&parseError];
if(response != nil){
if([response isKindOfClass:[NSDictionary class]]){
NSLog(#"response is in dictionary format %#",response);
NSDictionary *dictRes = [response copy];
NSLog(#"The dictRes is - %#",dictRes);
}
else{
NSLog(#"response is in array format %#",response);
NSDictionary *arrRes = [response copy];
NSLog(#"The arrRes is - %#",arrRes);
}
dispatch_async(dispatch_get_main_queue(), ^{
//Call the Activity Indicator hidden method inside the dispatch_main_queue method
[self stopLoadingSpinner]
[yourTableView reloadData];
});
}
}
else
{
NSLog(#"Error");
}
}];
[dataTask resume];
}
#catch (NSException *exception) {
NSLog(#"%#", [exception description]);
}
#finally {
}
}

Code Review for filling tableview cells with response returned from server

Connection Class
-(NSDictionary *)getResponseFromSearchByRoutewithUrl:(NSString *)url :(HttpCompletionBlock)completionHandler {
NSMutableURLRequest *urlRequest = [NSMutableURLRequest requestWithURL:[NSURL URLWithString:url]];
[urlRequest setHTTPMethod:#"GET"];
NSURLSessionConfiguration *configuration = [NSURLSessionConfiguration defaultSessionConfiguration];
NSDictionary *responseDictionary;
NSURLSession *session = [NSURLSession sessionWithConfiguration:configuration];
NSURLSessionDataTask *task = [session dataTaskWithRequest: urlRequest completionHandler:^(NSData *data, NSURLResponse *response, NSError *error) {
//check if we encountered an error
if(error != nil){
NSLog(#"%#", [error localizedDescription]);
}else{
//get and check the HTTP status code
NSInteger HTTPStatusCode = [(NSHTTPURLResponse *)response statusCode];
if (HTTPStatusCode != 200) {
NSLog(#"HTTP status code = %ld", (long)HTTPStatusCode);
}
[task resume];
[[NSOperationQueue mainQueue] addOperationWithBlock:^{
if(data != nil){
NSError *parseError = nil;
NSDictionary *responseDictionary = [NSJSONSerialization JSONObjectWithData:data options:0 error:&parseError];
NSLog(#"The response is - %#",responseDictionary);
return completionHandler(responseDictionary,nil);
}
else {
return completionHandler(nil,error);
}
}];
}
}];
[task resume];
return responseDictionary;
}
ViewController having Table view cells Class
-(void)viewDidLoad
{
_appDelegate= (AppDelegate *)[[UIApplication sharedApplication] delegate];
[[ConnectionManager sharedConnectionManager]getResponseFromSearchByRoutewithUrl:_urlString :^(NSDictionary *responseDictionary, NSError *error) {
if(error == nil)
{
_appDelegate.flightsArray=[responseDictionary objectForKey:#"scheduledFlights"];
NSLog(#"the flights array is%# ", _appDelegate.flightsArray);
}
else
{
NSLog(#"Error == %# ",error);
}
}];
}
Table View delegate class methods
-(NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
return _appDelegate.flightsArray.count;
}
Kindly Review the code and tell why the number of rows is zero...even when the response dictionary is returning dictionary...
try below code
-(void)viewDidLoad
{
_appDelegate= (AppDelegate *)[[UIApplication sharedApplication] delegate];
[[ConnectionManager sharedConnectionManager]getResponseFromSearchByRoutewithUrl:_urlString :^(NSDictionary *responseDictionary, NSError *error) {
if(error == nil)
{
_appDelegate.flightsArray=[responseDictionary objectForKey:#"scheduledFlights"];
NSLog(#"the flights array is%# ", _appDelegate.flightsArray);
[self performSelector:#selector(reloadTableview) withObject:nil afterDelay:0.2];
}
else
{
NSLog(#"Error == %# ",error);
}
}];
}
- (void)reloadTableview{
[self.tableview reloadData];
}

Wait for NSURLSessionDataTask to come back

I am new to Objective C and iOS development in general. I am trying to create an app that would make an http request and display the contents on a label.
When I started testing I noticed that the label was blank even though my logs showed that I had data back. Apparently this happens because the the response is not ready when the label text gets updated.
I put a loop on the top to fix this but I am almost sure there's got to be a better way to deal with this.
ViewController.m
- (IBAction)buttonSearch:(id)sender {
HttpRequest *http = [[HttpRequest alloc] init];
[http sendRequestFromURL: #"https://en.wiktionary.org/wiki/incredible"];
//I put this here to give some time for the url session to comeback.
int count;
while (http.responseText ==nil) {
self.outputLabel.text = [NSString stringWithFormat: #"Getting data %i ", count];
}
self.outputLabel.text = http.responseText;
}
HttpRequest.h
#import <Foundation/Foundation.h>
#interface HttpRequest : NSObject
#property (strong, nonatomic) NSString *responseText;
- (void) sendRequestFromURL: (NSString *) url;
- (NSString *) getElementBetweenText: (NSString *) start andText: (NSString *) end;
#end
HttpRequest.m
#implementation HttpRequest
- (void) sendRequestFromURL: (NSString *) url {
NSURL *myURL = [NSURL URLWithString: url];
NSURLRequest *request = [[NSURLRequest alloc] initWithURL: myURL];
NSURLSession *session = [NSURLSession sharedSession];
NSURLSessionDataTask *task = [session dataTaskWithRequest: request
completionHandler:^(NSData *data, NSURLResponse *response, NSError *error) {
self.responseText = [[NSString alloc] initWithData: data
encoding: NSUTF8StringEncoding];
}];
[task resume];
}
Thanks a lot for the help :)
Update
After reading a lot for the very useful comments here I realized that I was missing the whole point. So technically the NSURLSessionDataTask will add task to a queue that will make the call asynchronously and then I have to provide that call with a block of code I want to execute when the thread generated by the task has been completed.
Duncan thanks a lot for the response and the comments in the code. That helped me a lot to understand.
So I rewrote my procedures using the information provided. Note that they are a little verbose but, I wanted it like that understand the whole concept for now. (I am declaring a code block rather than nesting them)
HttpRequest.m
- (void) sendRequestFromURL: (NSString *) url
completion:(void (^)(NSString *, NSError *))completionBlock {
NSURL *myURL = [NSURL URLWithString: url];
NSURLRequest *request = [[NSURLRequest alloc] initWithURL: myURL];
NSURLSession *session = [NSURLSession sharedSession];
NSURLSessionDataTask *task = [session dataTaskWithRequest: request
completionHandler:^(NSData *data, NSURLResponse *response, NSError *error) {
//Create a block to handle the background thread in the dispatch method.
void (^runAfterCompletion)(void) = ^void (void) {
if (error) {
completionBlock (nil, error);
} else {
NSString *dataText = [[NSString alloc] initWithData: data encoding: NSUTF8StringEncoding];
completionBlock(dataText, error);
}
};
//Dispatch the queue
dispatch_async(dispatch_get_main_queue(), runAfterCompletion);
}];
[task resume];
}
ViewController.m
- (IBAction)buttonSearch:(id)sender {
NSString *const myURL = #"https://en.wiktionary.org/wiki/incredible";
HttpRequest *http = [[HttpRequest alloc] init];
[http sendRequestFromURL: myURL
completion: ^(NSString *str, NSError *error) {
if (error) {
self.outputText.text = [error localizedDescription];
} else {
self.outputText.text = str;
}
}];
}
Please feel free to comment on my new code. Style, incorrect usage, incorrect flow; feedback is very important in this stage of learning so I can become a better developer :)
Again thanks a lot for the replies.
You know what, use AFNetworking to save your life.
Or just modify your HttpRequest's sendRequestFromURL:
- (void)sendRequestFromURL:(NSString *)url completion:(void(^)(NSString *str, NSError *error))completionBlock {
NSURL *myURL = [NSURL URLWithString: url];
NSURLRequest *request = [[NSURLRequest alloc] initWithURL: myURL];
NSURLSession *session = [NSURLSession sharedSession];
NSURLSessionDataTask *task = [session dataTaskWithRequest: request
completionHandler:^(NSData *data, NSURLResponse *response, NSError *error) {
dispatch_async(dispatch_get_main_queue(), ^{
if (error) {
completionBlock(nil, error);
} else {
completionBlock([[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding], error);
}
});
}];
[task resume];
}
and invoke like this
[http sendRequestFromURL:#"https://en.wiktionary.org/wiki/incredible" completion:^(NSString *str, NSError *error) {
if (!error) {
self.outputLabel.text = str;
}
}];
Rewrite your sendRequestFromURL function to take a completion block:
- (void) sendRequestFromURL: (NSString *) url
completion: (void (^)(void)) completion
{
NSURL *myURL = [NSURL URLWithString: url];
NSURLRequest *request = [[NSURLRequest alloc] initWithURL: myURL];
NSURLSession *session = [NSURLSession sharedSession];
NSURLSessionDataTask *task = [session dataTaskWithRequest: request
completionHandler:^(NSData *data, NSURLResponse *response, NSError *error)
{
self.responseText = [[NSString alloc] initWithData: data
encoding: NSUTF8StringEncoding];
if (completion != nil)
{
//The data task's completion block runs on a background thread
//by default, so invoke the completion handler on the main thread
//for safety
dispatch_async(dispatch_get_main_queue(), completion);
}
}];
[task resume];
}
Then, when you call sendRequestFromURL, pass in the code you want to run when the request is ready as the completion block:
[self.sendRequestFromURL: #"http://www.someURL.com&blahblahblah",
completion: ^
{
//The code that you want to run when the data task is complete, using
//self.responseText
}];
//Do NOT expect the result to be ready here. It won't be.
The code above uses a completion block with no parameters because your code saved the response text to an instance variable. It would be more typical to pass the response data and the NSError as parameters to the completion block. See #Yahoho's answer for a version of sendRequestFromURL that takes a completion block with a result string and an NSError parameter).
(Note: I wrote the code above in the SO post editor. It probably has a few syntax errors, but it's intended as a guide, not code you can copy/paste into place. Objective-C block syntax is kinda nasty and I usually get it wrong the first time at least half the time.)
If you want easy way then Don't make separate class for call webservice. Just make meethod in viewController.m instead. I mean write sendRequestFromURL in your viewController.m and update your label's text in completion handler something like,
- (void) sendRequestFromURL: (NSString *) url {
NSURL *myURL = [NSURL URLWithString: url];
NSURLRequest *request = [[NSURLRequest alloc] initWithURL: myURL];
NSURLSession *session = [NSURLSession sharedSession];
NSURLSessionDataTask *task = [session dataTaskWithRequest: request
completionHandler:^(NSData *data, NSURLResponse *response, NSError *error) {
dispatch_async(dispatch_get_main_queue(), ^{
self.responseText = [[NSString alloc] initWithData: data
encoding: NSUTF8StringEncoding];
self.outputLabel.text = self.responseText;
})
}];
[task resume];
}

Make periodic server requests with NSURLSessionDataTask and dispatch_source timer

Need some help and explanation, because i'm really stuck in my question. i need to make this:
1) I make one request to the server, get some response and then i want to make another request every 7 seconds(example). also get some response. if it satisfy several conditions -> stop timer and do some stuff.
Main problem is that timer never stops, despite the fact that all in all i get response right. i assume that i use GCD incorrectly. because in debug this code behaves really strange.
What i have done:
This is my request function(it became like this after i read about 50 links how to do similar things)
-(void)makeRequestWithURL:(NSString*)urlString andParams:(NSString*)params andCompletionHandler:(void(^)(NSDictionary *responseData, NSError *error))completionHnadler{
NSURLSessionConfiguration *configuration = [NSURLSessionConfiguration defaultSessionConfiguration];
NSURLSession *session = [NSURLSession sessionWithConfiguration:configuration];
NSURL *url = [NSURL URLWithString:urlString];
NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:url
cachePolicy:NSURLRequestUseProtocolCachePolicy timeoutInterval:60.0];
request.HTTPMethod = #"POST";
request.HTTPBody = [params dataUsingEncoding:NSUTF8StringEncoding];
NSURLSessionDataTask *postDataTask = [session dataTaskWithRequest:request completionHandler:^(NSData *data, NSURLResponse *response, NSError *error) {
if (completionHnadler) {
if (error) {
dispatch_async(dispatch_get_main_queue(), ^{
completionHnadler(nil, error);
});
} else {
NSError *parseError;
json = [NSJSONSerialization JSONObjectWithData:data
options:NSJSONReadingMutableContainers
error:&parseError];
dispatch_async(dispatch_get_main_queue(), ^{
completionHnadler(json, parseError);
});
}
}
}];
[postDataTask resume]; }
I create my timer like this:
dispatch_source_t CreateDispatchTimer(uint64_t interval,
uint64_t leeway,
dispatch_queue_t queue ,
dispatch_block_t block) {
dispatch_source_t timer = dispatch_source_create(DISPATCH_SOURCE_TYPE_TIMER, 0, 0, queue);
if (timer) {
// Use dispatch_time instead of dispatch_walltime if the interval is small
dispatch_source_set_timer(timer, dispatch_walltime(NULL, 0), interval, leeway);
dispatch_source_set_event_handler(timer, block);
dispatch_resume(timer);
}
return timer; }
and called it like this:
-(void)checkForPassenger {
timerSource = CreateDispatchTimer(7ull * NSEC_PER_SEC, 1ull * NSEC_PER_SEC, dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
if([self getNotificationsRequest] == YES) {
dispatch_source_cancel(timerSource);
} else {
NSLog(#"go on timer");
}
NSLog(#"Driver checked for passenger!");
}); }
this is the code of periodic response:
-(BOOL)getNotificationsRequest {
NSString *urlString = #"http://primetime.by/temproad/do";
NSString *params = [NSString stringWithFormat:#"event={\"type\": \"in.getNotifications\"}&session_id=%#",session_id];
[self makeRequestWithURL:urlString andParams:params andCompletionHandler:^(NSDictionary *responseData, NSError *error) {
if ([[responseData objectForKey:#"rc"] intValue] == 0) {
NSArray *temp_notifications = [responseData objectForKey:#"notifications"];
if (temp_notifications.count != 0) {
notification = [[Notification alloc] initWithNotification:[[responseData objectForKey:#"notifications"] objectAtIndex:0]];
}
}
}];
if (notification) {
return YES;
} else {
return NO;
} }
and this is what i do in main request:
[self makeRequestWithURL:urlString andParams:params andCompletionHandler:^(NSDictionary *responseData, NSError *error) {
if ([[responseData objectForKey:#"rc"] intValue] == 0) {
route = [[Route alloc] initWithData:responseData];
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
[self checkForPassenger];
});
}
}];
NSLog(#"bye");
maybe explanation is bad so i can answer any question.
thx

Resources