From NSURLConnection to NSURLSession - ios

What is the best way to convert NSURLConnection to NSURLSession ?
error message: (Xcode)
ViewController.m:342:45: 'stringByAddingPercentEscapesUsingEncoding:' is deprecated: first deprecated in iOS 9.0 - Use -stringByAddingPercentEncodingWithAllowedCharacters: instead, which always uses the recommended UTF-8 encoding, and which encodes for a specific URL component or subcomponent since each URL component or subcomponent has different rules for what characters are valid.
my code :
-(void)downloadZip
{
NSLog(#"Start Downloading Zip File");
NSDate *myDate = (NSDate *)[[NSUserDefaults standardUserDefaults] objectForKey:#"LastUpdate"];
NSString *path = [NSString stringWithFormat:phpLinkgetZip, myDate];
NSURL *url = [NSURL URLWithString:[path stringByAddingPercentEscapesUsingEncoding:NSUTF8StringEncoding]];
NSLog(#"Checking update at Zip File : %#", path);
NSLog(#"Checking update Time : %#", myDate);
responseData = [[NSMutableData alloc] init];
NSURLRequest* updateRequest = [NSURLRequest requestWithURL: url];
NSURLConnection* connection = [[NSURLConnection alloc] initWithRequest:updateRequest delegate:self];
[connection start];
NSLog(#"Zip Downloading start...");
}
- (void)connection:(NSURLConnection *)connection didReceiveResponse:(NSURLResponse *)response {
[responseData setLength:0];
filesize = [[NSNumber numberWithLong: [response expectedContentLength] ] retain];
}
- (void)connectionDidFinishLoading:(NSURLConnection *)connection
{
[self updateZipDownloaded];
[filesize release];
[connection release];
}
-(void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)data
{
[responseData appendData:data];
NSNumber* curLength = [NSNumber numberWithLong:[responseData length] ];
float progress = [curLength floatValue] / [filesize floatValue] ;
}
- (void)connection:(NSURLConnection *)connection didFailWithError:(NSError *)error
{
NSLog(#"Zip Downloading error");
}

If you want to overcome above error please use below code
NSCharacterSet *setPath = [NSCharacterSet URLPathAllowedCharacterSet];
NSString *strURL = [path stringByAddingPercentEncodingWithAllowedCharacters:setPath];
stringByAddingPercentEncodingWithAllowedCharacters:
Returns a new string made from the receiver by replacing all
characters not in the specified set with percent-encoded characters.
Characters passed to set below methods
(NSCharacterSet *)URLUserAllowedCharacterSet;
(NSCharacterSet *)URLPasswordAllowedCharacterSet;
(NSCharacterSet *)URLHostAllowedCharacterSet;
(NSCharacterSet *)URLPathAllowedCharacterSet;
(NSCharacterSet *)URLQueryAllowedCharacterSet;
(NSCharacterSet *)URLFragmentAllowedCharacterSet;
If you want to go NSURLSession from NSURLConnetion,do the following things
NSURL *URL = [NSURL URLWithString:#"http://example.com"];
NSURLRequest *request = [NSURLRequest requestWithURL:URL];
NSURLSession *session = [NSURLSession sharedSession];
NSURLSessionDataTask *task = [session dataTaskWithRequest:request
completionHandler:
^(NSData *data, NSURLResponse *response, NSError *error) {
// ...
}];
[task resume];
NSURLSession class and related classes provide an API for downloading content via HTTP. This API provides a rich set of delegate methods for supporting authentication and gives your app the ability to perform background downloads when your app is not running or, in iOS, while your app is suspended.
To use the NSURLSession API, your app creates a series of sessions,
each of which coordinates a group of related data transfer tasks. For
example, if you are writing a web browser, your app might create one
session per tab or window. Within each session, your app adds a series
of tasks, each of which represents a request for a specific URL (and
for any follow-on URLs if the original URL returned an HTTP redirect).
Like most networking APIs, the NSURLSession API is highly
asynchronous. If you use the default, system-provided delegate, you
must provide a completion handler block that returns data to your app
when a transfer finishes successfully or with an error. Alternatively,
if you provide your own custom delegate objects, the task objects call
those delegates’ methods with data as it is received from the server
(or, for file downloads, when the transfer is complete).

Related

NSURLSession not downloading full data, but finnish with success

Scenario: I am downloading some big attachments (30-50 mb) from EWS API, by using NSURLSession. And saving the downloded xml data into files.
I made HTTP class which uses NSURLSession, handles delegate callbacks and has a completion handler. The HTTP class creates its own NSURLSession and start downloading the data. Here is my HTTP.m
//
// HTTP.m
// Download
//
// Created by Ankush Kushwaha on 7/6/18.
// Copyright © 2018 Ankush Kushwaha. All rights reserved.
//
#import "HTTP.h"
typedef void (^httpCompletionBlock)(NSData* result);
#interface HTTP()
#property (nonatomic) NSMutableData * data;
#property (nonatomic) NSString *fileNametoSaved;
#property (nonatomic) httpCompletionBlock completion;
#end
#implementation HTTP
- (instancetype)initWithAttachmntId:(NSString *)attachmentId
fileName:(NSString *)fileName
completion:(void (^)(NSData* result))completion
{
self = [super init];
if (self) {
self.data = [NSMutableData data];
self.completion = completion;
self.fileNametoSaved = fileName;
NSURL *requestUrl = [NSURL URLWithString:#"https://outlook.office365.com/EWS/Exchange.asmx"];
NSMutableURLRequest *request = [[NSMutableURLRequest alloc] initWithURL:requestUrl];
request.HTTPMethod = #"POST";
NSString *soapXmlString = [NSString stringWithFormat:#"<?xml version=\"1.0\" encoding=\"utf-8\"?>\n"
"<soap:Envelope xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n"
"xmlns:m=\"http://schemas.microsoft.com/exchange/services/2006/messages\"\n"
"xmlns:t=\"http://schemas.microsoft.com/exchange/services/2006/types\"\n"
"xmlns:soap=\"http://schemas.xmlsoap.org/soap/envelope/\">\n"
"<soap:Body>\n"
"<m:GetAttachment>\n"
"<m:AttachmentIds>\n"
"<t:AttachmentId Id=\"%#\"/>\n"
"</m:AttachmentIds>\n"
"</m:GetAttachment>\n"
"</soap:Body>\n"
"</soap:Envelope>\n",attachmentId];
if (soapXmlString)
{
NSString *xmlLength = [NSString stringWithFormat:#"%ld", (unsigned long)soapXmlString.length];
request.HTTPBody = [soapXmlString dataUsingEncoding:NSUTF8StringEncoding];
[request addValue:#"text/xml; charset=utf-8" forHTTPHeaderField:#"Content-Type"];
[request addValue:xmlLength forHTTPHeaderField:#"Content-Length"];
}
dispatch_async(dispatch_get_main_queue(), ^{
NSURLSessionConfiguration *defaultConfiguration = [NSURLSessionConfiguration ephemeralSessionConfiguration];
NSURLSession *defaultSession = [NSURLSession sessionWithConfiguration:defaultConfiguration
delegate:self
delegateQueue:nil];
NSURLSessionDataTask *dataTask = [defaultSession dataTaskWithRequest:request];
[dataTask resume];
});
}
return self;
}
-(void)URLSession:(NSURLSession *)session
task:(NSURLSessionTask *)task
didReceiveChallenge:(NSURLAuthenticationChallenge *)challenge
completionHandler:(void (^)(NSURLSessionAuthChallengeDisposition, NSURLCredential *))completionHandler
{
if (challenge.previousFailureCount == 0)
{
NSURLCredential* credential;
credential = [NSURLCredential credentialWithUser:#"MY_OUTLOOK.COM EMAIL" password:#"PASSWORD" persistence:NSURLCredentialPersistenceForSession];
[[challenge sender] useCredential:credential forAuthenticationChallenge:challenge];
completionHandler(NSURLSessionAuthChallengeUseCredential,credential);
}
else
{
// URLSession:task:didCompleteWithError delegate would be called as we are cancelling the request, due to wrong credentials.
completionHandler(NSURLSessionAuthChallengeCancelAuthenticationChallenge, nil);
}
}
-(void)URLSession:(NSURLSession *)session
dataTask:(NSURLSessionDataTask *)dataTask
didReceiveResponse:(NSURLResponse *)response
completionHandler:(void (^)(NSURLSessionResponseDisposition))completionHandler
{
completionHandler(NSURLSessionResponseAllow);
}
-(void)URLSession:(NSURLSession *)session
dataTask:(NSURLSessionDataTask *)dataTask
didReceiveData:(NSData *)data
{
[self.data appendData:data];
// NSLog(#"data : %lu", (unsigned long)self.data.length);
}
-(void)URLSession:(NSURLSession *)session
task:(NSURLSessionTask *)task
didCompleteWithError:(NSError *)error
{
NSLog(#"didCompleteWithError: %#", error);
if (error)
{
NSLog(#"Error: %#", error);
}
else
{
NSData *data;
if (self.data)
{
data = [NSData dataWithData:self.data];
}
NSLog(#"Success : %lu", (unsigned long)self.data.length);
NSString *filePath = [NSString stringWithFormat:#"/Users/startcut/Desktop/xxx/%#",
self.fileNametoSaved];
NSString *xmlString = [[NSString alloc] initWithData:self.data encoding:NSUTF8StringEncoding];
[xmlString writeToFile:filePath atomically:YES encoding:NSUTF8StringEncoding error:nil];
self.completion ? self.completion(self.data) : nil;
}
[session finishTasksAndInvalidate]; // We must release the session, else it holds strong referance for it's delegate (in our case EWSHTTPRequest).
// And it wont allow the delegate object to free -> cause memory leak
}
- (void)URLSession:(NSURLSession *)session
task:(NSURLSessionTask *)task
willPerformHTTPRedirection:(NSHTTPURLResponse *)response
newRequest:(NSURLRequest *)request
completionHandler:(void (^)(NSURLRequest * _Nullable))completionHandler;
{
NSString *redirectLocation = request.URL.absoluteString;
if (response)
{
completionHandler(nil);
}
else
{
completionHandler(request); // new redirect request
}
}
#end
In My ViewController I am making 5 HTTP requests, to download 5 diffrent attachments.
HTTP *http = [[HTTP alloc] initWithAttachmntId:#"AAAaAGFua3VzaC5zdGFyY3V0QG91dGxvb2suY29tAEYAAAAAACd30qZd6oFAvoaMby5vOMUHAOsTbManU6VPoeQkUTl4/J0AAWUo5o0AAOsTbManU6VPoeQkUTl4/J0AAWruTj0AAAESABAAWGs6REUQc02OHF0x6uYJ+g=="
fileName:#"http1"
completion:^(NSData *result) {
NSLog(#"Completion 1");
}];
HTTP *http2 = [[HTTP alloc] initWithAttachmntId:#"AAAaAGFua3VzaC5zdGFyY3V0QG91dGxvb2suY29tAEYAAAAAACd30qZd6oFAvoaMby5vOMUHAOsTbManU6VPoeQkUTl4/J0AAWUo5o0AAOsTbManU6VPoeQkUTl4/J0AAWruTjsAAAESABAAP8zebUI1fkSiE8tQ+RtwiQ=="
fileName:#"http2"
completion:^(NSData *result) {
NSLog(#"Completion 2");
}];
HTTP *http3 = [[HTTP alloc] initWithAttachmntId:#"AAAaAGFua3VzaC5zdGFyY3V0QG91dGxvb2suY29tAEYAAAAAACd30qZd6oFAvoaMby5vOMUHAOsTbManU6VPoeQkUTl4/J0AAWUo5o0AAOsTbManU6VPoeQkUTl4/J0AAWruTjkAAAESABAAiPaJIPjp/k6iQHSMpi6aDw=="
fileName:#"http3"
completion:^(NSData *result) {
NSLog(#"Completion 3");
}];
HTTP *http4 = [[HTTP alloc] initWithAttachmntId:#"AAAaAGFua3VzaC5zdGFyY3V0QG91dGxvb2suY29tAEYAAAAAACd30qZd6oFAvoaMby5vOMUHAOsTbManU6VPoeQkUTl4/J0AAWUo5o0AAOsTbManU6VPoeQkUTl4/J0AAWruTjwAAAESABAA86vBkFlTNU2oEVq/eRtLGQ=="
fileName:#"http4"
completion:^(NSData *result) {
NSLog(#"Completion 4");
}];
HTTP *http5 = [[HTTP alloc] initWithAttachmntId:#"AAAaAGFua3VzaC5zdGFyY3V0QG91dGxvb2suY29tAEYAAAAAACd30qZd6oFAvoaMby5vOMUHAOsTbManU6VPoeQkUTl4/J0AAWUo5o0AAOsTbManU6VPoeQkUTl4/J0AAWruTjoAAAESABAAND6qbOQbnkyoyg0K17T9/Q=="
fileName:#"http5"
completion:^(NSData *result) {
NSLog(#"Completion 5");
}];
Problem: As the files or data are being downloaded parallelly with 5 separate HTTP objects, At the end when NSUrlSession session delegate gets called I save data into files in my HTTP.m's -(void)URLSession (NSURLSession *)session task:(NSURLSessionTask *)task didCompleteWithError:(NSError *)error
Method. Most of the times the downloaded data (files) does not contain the full data (e.g If the Size of the attachment is 30 mb, my code downloads the data 4 mb or 10 mb or 3.2 mb etc. The numbers are not consistent). It seems that NSURLSession terminates or stop the data downloading in between and close the connection successfully. If I download 1 attachment at a time (Instead of making 5 HTTP objects in my view controller, I just make 1 object at a time) in most of the cases it works and downloads full data content.
Any help is appreciated guys. I am stuck in this from 2 days.
In no particular order:
You should not be creating a new session for each request. That prevents the OS from limiting the number of simultaneous requests correctly, and will likely cause other issues down the road. Similarly, you should not be calling finishTasksAndInvalidate after each task completes.
You must retain a reference to the session until there are no more outstanding requests. If that doesn't fit easily into your app's architecture, you might consider using the default session instead of providing your own session.
Your Content-Length header value is incorrect. It should be a byte count, not a character count. Convert the string to an NSData with encoding first, and send the length of that as Content-Length. Otherwise, it will fail as soon as you get a single multi-byte character in the body.
Your didReceiveResponse: method should ideally be clearing your data storage so that it handles multipart responses correctly (with the last one winning), rather than concatenating them.
Your authentication challenge handler, as written, is likely to cause serious problems. You should be checking the protection space of the challenge to see if it is one that you care about, and if not, you should be triggering default handling. Without that your app will fail if the user is behind any sort of proxy, among other things.
Fix those issues, and if it still isn't working, ask a new question about whatever is still not working. :-)
Finally. I found the cause. Not the solution :(
It was not from iOS code. There might be some code improvement needed as #dgatwood mentioned (Thanks), but even after improvements then I was facing the same problem.
Actually, the EWS exchange is getting throttled by large data download. Due to which EWS server terminates the connection in between. Here is the blog

Continue webservice call after transition from foreground to background in iOS objective C

Suppose I call a webservice when the app is in foreground. Now if the user sends the app to background then how do I make sure that this webservice call keeps executing in the background.
This is the piece of code that I am using in my app.
Login* login = [[Login alloc]init];
[login initiateSignInProcess];
initiateSignInProcess has 4 web service calls. they are normal
functions. I am using AFNetworking.
If any of the services fail, I call it again with a delay in the failure block of afnetworking code like below:-
failure:^(AFHTTPRequestOperation *operation, NSError *error)
{
[self performSelector:#selector(getUserId) withObject:nil afterDelay:5];
}
Now I want to know that if the user sends the app to background, then how will the code execute? Will it call this function in bakcground till it succeeds?
Best to use Background Process for fetch. Here is great tutorial for solution [ http://code.tutsplus.com/tutorials/ios-7-sdk-working-with-background-fetch--mobile-20520
Not possible in iOS6.x or lesser unless your application is has specific requirement to run in background like locations, Voip, music etc...
However this is possible with iOS7, please consider having a look at this
http://redth.codes/ios7-recipe-background-fetching/
**For(large FIle Downloade use Asynchronous Method)**
NSURL *myUrl = [NSURL URLWithString:#"Enter URL HERE"];
NSURLRequest *myRequest = [NSURLRequest requestWithURL:myUrl cachePolicy:NSURLRequestReloadIgnoringLocalCacheData timeoutInterval:60];
NSMutableData *myData = [[NSMutableData alloc] initWithLength:0];
NSURLConnection *myConnection = [[NSURLConnection alloc] initWithRequest:myRequest delegate:self startImmediately:YES];
**For(Small FIle Downloade use Synchronous Method)**
NSURL *myUrl = [NSURL URLWithString:#"Enter URl HERE"];
NSData *myData = [NSData dataWithContentsOfURL:myUrl];
UIImage *img = [UIImage imageWithData:myData];
add NSURLConnection Delegate in .h File
- (void) connection:(NSURLConnection *)connection didReceiveResponse:(NSURLResponse *)response {
[UIApplication sharedApplication].networkActivityIndicatorVisible = YES;
[myData setLength:0];
}
- (void) connection:(NSURLConnection *)connection didReceiveData:(NSData *)data {
[myData appendData:data];
}
- (void) connection:(NSURLConnection *)connection didFailWithError:(NSError *)error {
[UIApplication sharedApplication].networkActivityIndicatorVisible = NO;
[connection release];
}
- (void) connectionDidFinishLoading:(NSURLConnection *)connection {
[UIApplication sharedApplication].networkActivityIndicatorVisible = NO;
[connection release];
//download finished - data is available in myData.
}
This is depends on OS scheduling whether it allows continue to run the services in background or kill it.
Best to use Background Fetch. Here is nice tutorial http://code.tutsplus.com/tutorials/ios-7-sdk-working-with-background-fetch--mobile-20520
Hope this solve your issue.

Pause a download in objective-c

I'm unsure of what the best approach is for pausing a download. I have seen this question asked on stack over flow before, but doesn't seem to achieve the results I am looking for. For example: I understand inside of the AppDelegate, the - (void)applicationWillResignActive:(UIApplication *)application is called if a phone call comes on the phone for example. It is crucial for my app to successfully download the database to function appropriately. Here's the code I use to download the database:
NSString *urlDb = [NSString stringWithFormat:#"someURl'];
//---Create URL from where DB has to be download-----
NSURL *url = [NSURL URLWithString:urlDb];
NSError *error = nil;
NSData *data = [NSData dataWithContentsOfURL:url options:0 error:&error];
Pausing a download may not be critical for example, AT&T, but folks with Verizon cannot talk and download at the same time. Any help would be greatly appreciated. Thanks!
If you want pause/resume functionality, then you must use either old NSURLConnection, and implement all required delegate methods, or you can use new NSURLSession API.
Here is a basic implementation with NSURLConnection:
#property NSFileHandle *fileHandle;
#property NSURLConnection *connection;
// start download
- (IBAction)downloadButtonPressed
{
NSURL *url = [NSURL URLWithString:#"myUrl"];
NSURLRequest *dataRequest = [NSURLRequest requestWithURL:url];
self.connection = [NSURLConnection connectionWithRequest:dataRequest delegate:self];
[connection start];
}
Implement delegate methods
- (void)connection:(NSURLConnection *)connection didReceiveResponse:(NSURLResponse *)response
{
self.fileHandle = [NSFileHandle fileHandleForWritingAtPath:#“pathToYourFile”];
[self.fileHandle seekToEndOfFile];
}
- (void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)data
{
[self.fileHandle writeData:data];
}
If you want to pause - just cancel connection like this:
[self.connection cancel];
Then you can resume like this:
- (void)resumeDownload
{
NSURL *url = [NSURL URLWithString:#"myUrl"];
NSMutableURLRequest *dataRequest = [NSMutableURLRequest requestWithURL:url];
NSDictionary *fileAttributes = [[NSFileManager defaultManager] attributesOfItemAtPath:pathToYourFile error:&attributesError];
NSNumber *fileSizeNumber = [fileAttributes objectForKey:NSFileSize];
NSString *bytesRangeHeader = [NSString stringWithFormat:#"bytes=%lld-", [fileSizeNumber longLongValue];
[_request setValue:bytesRangeHeader forHTTPHeaderField:#"Range"];
self.connection = [NSURLConnection connectionWithRequest:dataRequest delegate:self];
[connection start];
}

how to send http request from non ui thread in ios

I am looking for a sample to send and receive http GET request in iOS. All I want to
do is handle communication in background thread such that it does not block main thread
and also want to handle http standard error code. Can anyone suggest me reference code or
example to handle http response data and handle proper memory management?
Any help will be thankful.
Two methods to achieve it:
1) NSURLCOnnection sendAsynchronousRequest method:
NSString *strURL= [NSString stringWithFormat:#"http://www.google.com/"];
NSURL *URL = [NSURL URLWithString:[strURL stringByAddingPercentEscapesUsingEncoding:NSUTF8StringEncoding]];
NSURLRequest *requestURL = [[NSURLRequest alloc] initWithURL:URL];
[NSURLConnection sendAsynchronousRequest:requestURL
queue:[NSOperationQueue mainQueue]
completionHandler:^(NSURLResponse *response, NSData *data, NSError *error)
{
NSLog(#"Response is:%#",[[NSString alloc] initWithData:data encoding:NSASCIIStringEncoding]);
}];
2) Create and fire request then NSURLConnection Delegate Methods to get the response:
// Create the request.
NSURLRequest *request = [NSURLRequest requestWithURL:[NSURL URLWithString:#"http://google.com"]];
// Create url connection and fire request
NSURLConnection *conn = [[NSURLConnection alloc] initWithRequest:request delegate:self];
#pragma mark NSURLConnection Delegate Methods
- (void)connection:(NSURLConnection *)connection didReceiveResponse:(NSURLResponse *)response {
// A response has been received, this is where we initialize the instance var you created
// so that we can append data to it in the didReceiveData method
// Furthermore, this method is called each time there is a redirect so reinitializing it
// also serves to clear it
_responseData = [[NSMutableData alloc] init];
}
- (void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)data {
// Append the new data to the instance variable you declared
[_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;
}
- (void)connectionDidFinishLoading:(NSURLConnection *)connection {
// The request is complete and data has been received
// You can parse the stuff in your instance variable now
}
- (void)connection:(NSURLConnection *)connection didFailWithError:(NSError *)error {
// The request has failed for some reason!
// Check the error var
}

how to access json data assigned to property in user defined method

I am working on the google maps where I find the location based on the text entered in the
textfield.I have used NSConnection to find the location json and then I assign this json to
the property in connectionDidFinishLoading delegate method so that I can access the json
when it is required but unfortunately I am not getting the data in geocodeAddress
method(NSLog(#"geodata %#",geocode))
Can any one help me to fix this issue?
- (void)geocodeAddress:(NSString *)address withCallback:(SEL)callback withDelegate: (id)delegate
{
NSString *geocodingBaseUrl = #"http://maps.googleapis.com/maps/api/geocode/json?";
NSString *url = [NSString stringWithFormat:#"%#address=%#&sensor=false", geocodingBaseUrl,address];
NSLog(#"url=%#",url);
url = [url stringByAddingPercentEscapesUsingEncoding: NSUTF8StringEncoding];
NSURL *queryUrl = [NSURL URLWithString:url];
NSURLRequest *request =[NSURLRequest requestWithURL:queryUrl cachePolicy:NSURLCacheStorageNotAllowed timeoutInterval:10];
//NSURLRequest *request =[NSURLRequest requestWithURL:url];
downloaddata= [[NSMutableData alloc]init];
NSURLConnection *connection =[[NSURLConnection alloc] initWithRequest:request delegate:self];
NSLog(#"geodata %#",geocode);
//[delegate performSelector:callback];
}
- (void)connectionDidFinishLoading:(NSURLConnection *)connection
{
NSError *error;
alldata = [NSJSONSerialization
JSONObjectWithData:self->downloaddata
options:NSJSONReadingMutableContainers
error:&error];
if(error)
{
}
else
{
self.geocode =alldata;
//NSLog(#"geodata %#",geocode);
}
}
NSURLConnection's are normally asynchronous, so geocode isn't set until the connectionDidFinishLoading is called, which won't happen until sometime after geocodeAddress... has finished. In your connectionDidFinishLoading... you'll need to execute code to cause the UI to refresh with the now valid geocode data.
Alternatively you could use + (NSData *)sendSynchronousRequest:(NSURLRequest *)request returningResponse:(NSURLResponse **)response error:(NSError **)error to execute the request synchronously, but that would be bad since it would block the UI until the request completed.

Resources