I have a bunch of nested NSURLSessionDataTasks and the data downloaded persists on calls of the method. I think it might have something to do with how NSURLSession handles cache??
Is there some way I can flush the cache so I get the most recent data when I call the method (thats supposed to refresh the data)
Here's the code I'm working with if it helps at all...
--EDIT--
And a more readable excerpt of just one of the requests that I want to flush the cache from after use:
NSMutableURLRequest *homeRequest = [[NSMutableURLRequest alloc] initWithURL:[NSURL URLWithString:[NSString stringWithFormat:#"https://mistar.oakland.k12.mi.us/novi/StudentPortal/Home/Login"]]];
[homeRequest setCachePolicy:NSURLRequestReloadIgnoringCacheData];
[homeRequest setHTTPMethod:#"POST"];
NSString *postString = [NSString stringWithFormat:#"Pin=%#&Password=%#",
[self percentEscapeString:pin],
[self percentEscapeString:password]];
NSData * postBody = [postString dataUsingEncoding:NSUTF8StringEncoding];
[homeRequest setHTTPBody:postBody];
NSURLSessionDataTask *homeTask = [defaultSession dataTaskWithRequest:homeRequest
homeTask is the NSURLDataTask that I add to the defaultSession. I want to remove the cache it saves so that when I call the method that contains these lines again, it fetches from the server instead of the local cache.
Is defautSession a local variable pointing to [NSURLSession sharedSession]? I would try using an ephemeral session instead. It is almost the same except that it doesn't cache data to disk.
NSURLSessionConfiguration *config =
[NSURLSessionConfiguration ephemeralSessionConfiguration];
NSURLSession *defaultSession = [NSURLSession sessionWithConfiguration:config];
If that still doesn't fix the problem, then invalidate it before posting again. Use
[defaultSession invalidateAndCancel]
after your task has completed and then create a new session before the next time you need to post.
Related
I have a very strange problem, in Android, the API calls take 300-500 ms while in iOS it takes 1.5-2.5 sec. I have removed dependencies like my server, device specific issue, internet connectivity etc. I have a very simple sample code hitting a sample URL and for me, it takes about 2 sec, even on the simulator.
dispatch_async(dispatch_get_global_queue(0, 0), ^{
NSLog(#"start");
// 1
NSURL *url = [NSURL URLWithString:#"https://httpbin.org/get"];
NSURLSessionConfiguration *config = [NSURLSessionConfiguration defaultSessionConfiguration];
NSURLSession *session = [NSURLSession sessionWithConfiguration:config];
// 2
NSMutableURLRequest *request = [[NSMutableURLRequest alloc] initWithURL:url];
[request setValue:#"" forHTTPHeaderField:#"Accept-Encoding"];
request.HTTPMethod = #"GET";
[[session dataTaskWithRequest:request
completionHandler:^(NSData *data, NSURLResponse *response, NSError *error) {
// Handle response here
NSLog(#"end - %ld", (long)[(NSHTTPURLResponse*)response statusCode]);
}] resume];
});
I also have tried using AFNetworking and ASSIHTTP libraries, but there is no difference. I also have checked the headers and they are the same in both Android and iOS. What am I doing something wrong here?
I think your problem is not in the network but in this line:
dispatch_async(dispatch_get_global_queue(0, 0), ^{
Can you remove it and check again?
Data task is async so you don't need to wrap it in another async block.
Also you don't need to create instance of NSURLSession for every request
You can log out the timestamp (NSLog already did) when the request generate, send, callback to analyse it.
Does the server side code do any processing based on 'user-agent'?
Is there a time difference if you open the url in iOS safari and within the app ?
You can try calling the api from postman (or another REST API test tool like firefox RESTClient) and override the user-agent to use iOS values (http://www.enterpriseios.com/wiki/UserAgent). If the time difference is still the same, theres nothing you can do in your mobile code to fix this lag.
P.S. :
1. Overriding user-agent in postman needs some tweaking : https://github.com/postmanlabs/postman-app-support/wiki/Postman-Proxy
I have implemented a program to communicate http2 using NSURLSession of iOS9, And it can communicate with my server in http2.
However, I'm having a problem with receive server_push.
I found ENABLE_PUSH value is 0 in their settings and there's no delegate in receive server push in NSURLSession...
・I think NSURLSession doesn't support server_push. Is this right?
・If it support server_push,how to use?
/**
It WORKS for post data and get response.
I don't know the code should be added here
in order to get the server_push.
I suspect that NSURLSession itself cannot receive the server_push(;_;)
**/
- (void) postData
{
NSString *urlstr = self.urlArea.text;
NSURL * url = [NSURL URLWithString:urlstr];
NSDictionary *params = #{#"data":#""};
//json to query
NSData *query = [self buildQueryWithDictionary: params];
//make request
NSMutableURLRequest *request = [NSMutableURLRequest
requestWithURL:url
cachePolicy: NSURLRequestReloadIgnoringCacheData
timeoutInterval: 10.0];
[request setHTTPMethod: #"POST"];
[request setHTTPBody: query];
//prepare session
NSURLSession *session = [NSURLSession sessionWithConfiguration:[NSURLSessionConfiguration defaultSessionConfiguration] delegate:self delegateQueue:[NSOperationQueue mainQueue]];
//resume
[[session dataTaskWithRequest: request completionHandler:^(NSData *data, NSURLResponse *response, NSError *error)
{
if (response && ! error) {
NSLog(#"Data: %#", [[NSString alloc] initWithData: data encoding: NSUTF8StringEncoding]);
}else {
NSLog(#"ERR: %#", error);
}
}] resume];
}
I read this here:
The HTTP2 push mechanism is not a generic server push mechanism like
websocket or server sent events.
It is designed for a specific optimisation of HTTP conversations.
Specifically when a client asks for a resource (eg index.html) the
server can guess that it is going to next ask for a bunch of
associated resources (eg theme.css, jquery.js, logo.png, etc. etc.)
Typically a webpage can have 10s of such associated requests.
With HTTP/1.1, the server had to wait until the client actually sends
request for these associated resources, and then the client is limited
by connections to only ask for approx 6 at a time. Thus it can take
many round trips before all the associated resources that are needed
by a webpage are actually sent.
With HTTP/2, the server can send in the response to the index.html GET
push promises to tell the client that it is going to also send
theme.css, jquery.js, logo.png, etc. as if the client had requested
them. The client can then cancel those pushes or just wait for them to
be sent without incurring the extra latency of multiple round trips.
ere is a blog about the push API for HTTP2 and SPDY in jetty:
https://webtide.com/http2-push-with-experimental-servlet-api/
Solved
I received following reply from support.
iOS does not currently support this.
update
(#vin25 comment)
ios10 supports it.
I am a beginer in iOS programming. I have some problem with NSURLConnection: I have installed SWRevealViewController https://github.com/John-Lluch/SWRevealViewController and when my app is loading Data from server, I can't use interaction with screen. I can't open my SWR-menu while Data is loading.
Here is my SWR in viewDidLoad:
SWRevealViewController *revealViewController = self.revealViewController;
if ( revealViewController ) {
[self.openMenyItmet setTarget: self.revealViewController];
[self.openMenyItmet setAction: #selector( revealToggle: )];
[self.view addGestureRecognizer:self.revealViewController.panGestureRecognizer];
}
After that, I called Get method in viewDidLoad:
[self GetQUIZ];
Method detail:
- (void)GetQUIZ {
NSMutableURLRequest *request = [[NSMutableURLRequest alloc] init];
NSString *url = [NSString stringWithFormat:#"http://stringlearning.com/api/v1/user-quiz?token=%#",[[NSUserDefaults standardUserDefaults] stringForKey:#"token"]];
[request setURL:[NSURL URLWithString: url]];
[request setHTTPMethod:#"GET"];
[request setValue:#"application/x-www-form-urlencoded" forHTTPHeaderField:#"Content-Type"];
[request setValue:[UIDevice currentDevice].name forHTTPHeaderField:#"device"];
NSURLConnection *conn = [[NSURLConnection alloc] initWithRequest:request delegate:self];
NSLog(#"Left menu, User details: %#", [[NSString alloc] initWithData:[request HTTPBody] encoding:NSUTF8StringEncoding]);
NSLog(#"%#", [request allHTTPHeaderFields]);
if(conn) {
NSLog(#"Connection Successful");
} else
NSLog(#"Connection could not be made");
And then I use data in connectionDidFinishLoading:
- (void)connectionDidFinishLoading:(NSURLConnection *)connection {
NSError *deserr = nil;
NSDictionary *responseDict = [NSJSONSerialization JSONObjectWithData:responseData options: 0 error: &deserr];
I read that i should use async methods, but I never use it before. Would you write some detail solution ?
Maybe, does have different path?
I would be very grateful for the help!
I'd suggest starting with NSURLSession, which is a modern API that will accomplish the same thing, asynchronously.
To use NSURLSession, you need a few piece of the puzzle:
A web address to reach, and optionally any payload or custom headers.
An instance of NSURL: where you're downloading from and an NSURLRequest to wrap it in.
An NSURLSessionConfiguration, which handles things like caching, credentials and timeouts.
The session itself.
You need an NSURLSessionTask instance. This is the closest object to your NSURLConnection. It has callbacks via delegate or a completion block, if you just need to know when it finishes.
Here's how this would look in code:
// 1. The web address & headers
NSString *webAddress = [NSString stringWithFormat:#"http://stringlearning.com/api/v1/user-quiz?token=%#",[[NSUserDefaults standardUserDefaults] stringForKey:#"token"]];
NSDictionary <NSString *, NSString *> *headers = #{
#"device" : [UIDevice currentDevice].name,
#"Content-Type" : #"application/x-www-form-urlencoded"
};
// 2. An NSURL wrapped in an NSURLRequest
NSURL* url = [NSURL URLWithString:webAddress];
NSURLRequest *request = [NSURLRequest requestWithURL:url];
// 3. An NSURLSession Configuration
NSURLSessionConfiguration *sessionConfiguration = [NSURLSessionConfiguration defaultSessionConfiguration];
[sessionConfiguration setHTTPAdditionalHeaders:headers];
// 4. The URLSession itself.
NSURLSession *urlSession = [NSURLSession sessionWithConfiguration:sessionConfiguration];
// 5. A session task: NSURLSessionDataTask or NSURLSessionDownloadTask
NSURLSessionDataTask *dataTask = [urlSession dataTaskWithRequest:request completionHandler:^(NSData * _Nullable data, NSURLResponse * _Nullable response, NSError * _Nullable error) {
}];
// 5b. Set the delegate if you did not use the completion handler initializer
// urlSession.delegate = self;
// 6. Finally, call resume on your task.
[dataTask resume];
This will run asynchronously, allowing your UI to remain responsive as your app loads data.
When you send a request on the main thread, like you are doing now, your UI, which is always performed on the main thread, is blocked, waiting for the request to finish and process. So you should perform all your network on a background thread, asynchronously. I would recommend first to check the networking library AFNetworking , it could simplify most of your networking problems.
Welcome to SO. You should know that NSURLConnection was deprecated in iOS 9. You should be using NSURLSession instead. The approach is very similar. You can take the NSURLRequest you've created and pass it to the sharedSession object, which is set up for async requests. The simplest way to deal with it is to use the call dataTaskWithRequest:completionHandler:, which takes a completion block. In your completion block you provide code that handles both success and failure.
I am trying to upload a photo to a webservice, and I am using the following code:
NSData *imageData = UIImageJPEGRepresentation([UIImage imageNamed:imageToUpload], 0.5);
// 1
NSURLSessionConfiguration *config = [NSURLSessionConfiguration defaultSessionConfiguration];
config.HTTPMaximumConnectionsPerHost = 1;
[config setHTTPAdditionalHeaders:#{#"Content-Type":#"multipart/form-data"}];
// 2
NSURLSession *upLoadSession = [NSURLSession sessionWithConfiguration:config delegate:self delegateQueue:nil];
// for now just create a random file name, dropbox will handle it if we overwrite a file and create a new name..
NSString *urlString =[NSString stringWithFormat:#"https://ABC.co.uk/photos?api_key=MYAPIKEY" ];
NSURL *webUrl = [NSURL URLWithString:urlString];
NSMutableURLRequest *request = [[NSMutableURLRequest alloc] initWithURL:webUrl];
[request setHTTPMethod:#"POST"];
// 3
NSURLSessionUploadTask *uploadTask = [upLoadSession uploadTaskWithRequest:request fromData:imageData];
// 4
// uploadView.hidden = NO;
[[UIApplication sharedApplication] setNetworkActivityIndicatorVisible:YES];
// 5
[uploadTask resume];
I receive an error 500, and I'm not sure what to do.
I have tried implementing AFNetworking, body data appending like This, This, and This. Yet, no luck.
The code above is from Ray Wenderlich's website: Here.
All I need to provide the server is this:
file (the image)
API Key (I tried using the NSMutableURLRequest's addValue:forHTTPHeaderField: but it didn't work, so I'm using it in the URL. I know it's not the best practice in terms of safety, but I'm getting so desperate as I've been working on this for over 10 hours! One step at the time!)
The Content-Type to be multipart/form-data
That's all I need to provide, and yet I'm not getting anywhere!
Can anyone please help?
Thank you in advance
UPDATE 1: So I made some alterations to the URL structure, and added the API key as a dictionary in HTTPBody, and now I am getting error 403.
It seems really weird that apple has not made any effort to serialise this part of connection. Anyway. I really appreciate anyone helping out!
Try setting the Content-Type in the request as well:
[request addValue:#"multipart/form-data" forHTTPHeaderField:#"Content-Type"];
My organization has a number of different authentication methods used. One that I have been struggling with has been our apps that use Oath2 that will be expecting the Authorization header to have the token information to be stored in the HTTP Headers. I can manually put the Authorization header information for each request, but I would like this information to be automatically populated instead of manually adding it for every NSURLSession, NSURLConnection, UIWebView or [UIImage: imageFromUrl]. In Apples documentation for NSURLConnection class and NSURLSession it says that it will handle are designed to handle various aspects >of the HTTP protocol for you, including the Authentication header and they recommend not to alter this property, but it does not seem to get set for me. At a minimum I would expect that for the NSURLSession object I should only need to set it once by accessing the sharedSession and adding the header info via a setHTTPAdditionalHeaders call, but it does not seem to hold the info on the next time I access the sharedSession.
I there something I am missing here or do I need to manually populate the HTTP Header on all calls?
EDIT:
Below is a code snippet showing how I try to set the header for the session.
NSURLSession *session = [NSURLSession sharedSession];
NSURLSessionConfiguration *config = [NSURLSessionConfiguration defaultSessionConfiguration];
NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
id jSONObj = [defaults objectForKey: kTokenInformation];
[config setHTTPAdditionalHeaders:#{#"Authorization": [NSString stringWithFormat:#"Bearer %#",[jSONObj valueForKeyPath:#"access_token"]]}];
session = [NSURLSession sessionWithConfiguration:config];
However, it doesn't appear that the token is present the next time I call [NSURLSession sharedSession]. So right now I have to do that every time I call the shared session. I also am unsure from the documentation how you would handle apps that maintain multiple sessions which may each require separate Auth tokens. Any thoughts what I am missing hear.
Have a look at NSURLSessionConfiguration.
For example, you could set up a singleton class that provides an NSURLSession:
// Session setup
NSString *tokenString = ...;
NSURLSessionConfiguration *sessionConfiguration = [NSURLSessionConfiguration defaultSessionConfiguration];
[sessionConfiguration setHTTPAdditionalHeaders:#{ #"X-Auth-Token" : tokenString" }];
self.session = [NSURLSession sessionWithConfiguration:sessionConfiguration];
Now, every time you need to make a request you use your session:
// Send Request
NSURLSession *session = [[SessionManager sharedManager] session];
[[session dataTaskWithURL:url
completionHandler:^(NSData *data, NSURLResponse *response, NSError *error) {
}] resume];
Here's a great tutorial: http://code.tutsplus.com/tutorials/networking-with-nsurlsession-part-1--mobile-21394