In my project I'm working with NSMutableURLRequest. Sometimes there are mistakes because of caching.
So I did
let mutableURLRequest = makeURLRequestFrom(url: url, httpMethod: "GET", httpHeaders: httpHeaders, parameters: parameters)
mutableURLRequest.cachePolicy = .ReloadIgnoringLocalAndRemoteCacheData
This is working fine. Now I don't want to set the cachePolicy for every NSMutableURLRequest. Is there a possibility to set a standard config for the cachePolicy?
For UI-elements I can set configs in the Appdelegate for the whole project, too.
Like this:
UILabel.appearance().textColor = UIColor.red()
Maybe there is a similar solution for the cachePolicy? (I can't find yet)
If you're using NSURLSession, you can specify a per-session cache policy. For NSURLConnection, I think the best you could do would be to modify the shared NSURLCache object and set its in-memory and on-disk sizes to zero, though I can't guarantee that the OS will honor that.
Related
I copy-pasted the first example of the Alamofire readme (at fa3c6d0) into main.swift:
import Foundation
import Alamofire
Alamofire.request("https://httpbin.org/get").responseJSON { response in
print(response.request) // original URL request
print(response.response) // HTTP URL response
print(response.data) // server data
print(response.result) // result of response serialization
if let JSON = response.result.value {
print("JSON: \(JSON)")
}
}
print("Done")
When I run this, all I get is Done, then the application terminates.
While I see here that I can pick a dispatch queue, this answer seems to suggest that I shouldn't have to.
Anyway, having had a similar issue with "basic" requests I tried the same solution but to no avail: the application now blocks. So, apparently, Alamofire has a different default than URLSession and wants to use the main thread.
What is the best way to have a request executed (and waited for) in an application like this?
We need to do two things.
Execute the request in the background.
Block the main thread until the request is done, i.e. the completion handler ran.
My original code does neither.
The first item is achieved by using .response(queue: DispatchQueue(label: "some-name")) (or one of its variants).
Waiting can be done in several ways.
Using a flag and active waiting, as shown here (won't scale to more than one request).
Use active waiting with countdown latch as shown here (works for multiple requests).
Use DispatchSemaphore as seen e.g. here.
And probably many more.
I'm trying to craft a very specific HTTP request to a server (ie. defining the exact set of HTTP headers), but NSURLSession keeps "helpfully" inserting a bunch of HTTP headers like Accept, Accept-Language and Accept-Encoding.
Consider the following playground (Swift 2.x) which sends a request to a service that just echos the HTTP headers that were sent:
import Foundation
import XCPlayground
XCPlaygroundPage.currentPage.needsIndefiniteExecution = true
let url = NSURL(string: "http://httpbin.org/headers")!
let request = NSMutableURLRequest(URL: url, cachePolicy: .ReloadIgnoringLocalCacheData, timeoutInterval: 30000)
let configuration = NSURLSessionConfiguration.ephemeralSessionConfiguration()
let session = NSURLSession(configuration: configuration)
let task = session.dataTaskWithRequest(request) { (data: NSData?, response: NSURLResponse?, error: NSError?) in
print(NSString(data: data!, encoding: NSUTF8StringEncoding))
XCPlaygroundPage.currentPage.finishExecution()
}
task.resume()
You can see that there are three Accept headers being sent. How can I prevent that?
I've tried setting the header using request.setValue(nil, forHTTPHeaderField: "Accept-Language") but that gets ignored. Tried setting it to "", but no good. I've also tried manipulating the HTTPAdditionalHeaders property on NSURLSessionConfiguration, but no love.
How do I get NSURLSession to not be quite so helpful?
I doubt what you're asking for is possible. NSURLSession (and NSURLConnection) automatically provide a number of headers, and that's one of them.
There's also no valid reason to remove them. All three of those headers have been part of the spec since the original HTTP/0.9 spec (https://www.w3.org/Protocols/HTTP/HTRQ_Headers.html). There's absolutely no excuse for any server not either handling those correctly or ignoring them outright.
With that said, if you provide the wrong value for those fields (and the default may be wrong), the server may refuse to give you results. To solve that problem, first figure out what type of data the server is actually going to provide, and specify that value in the Accept header instead of the default.
For example, you might set Accept to "application/json" or one of the other variants (What is the correct JSON content type?) if you're expecting JSON data.
That said, if you really must avoid having those headers sent, you can always open a socket, construct the request manually, and send it. Assuming the server doesn't require chunked encoding, it is pretty easy to do. (Chunked encoding, however, is a horror of Herculean proportions, so if your server sends that back, you'll pretty much have to resort to adding libcurl into your project and using that to make requests instead.)
I have looked at questions like this or that one but it is still not working.
Also I have a question about what should I enter into fileURL param from function multipartFormData.appendBodyPart?
Should it be a way to image from PC, or image must be added to Images.xcassets? What should I send here?
It looks like you have three issues that you need to fix.
Use .POST instead of POST.
The fileURL needs to be a valid NSURL that points to a file on the file system. You cannot just use the filename.
You are using the responseString serializer, but named the third parameter in the closure JSON. Then you are letting result into s and trying to print it out. The result parameter doesn't even exist anywhere. Instead, you should print(JSON).
Hopefully that helps clear things up a bit.
Try to use .POST not POST
As an alternative solution upload an encoded file and send it as a parameter of POST.
// `data` is NSData
let base64String = data!.base64EncodedStringWithOptions(NSDataBase64EncodingOptions.allZeros)
let parameters = ["image_data": base64String] as [String: AnyObject]
Alamofire.request(.POST, "http://your-url.com", parameters: parameters)
The cons of this method is that the data will get %33 larger due to encoding. If you have bandwidth problems it may not be a good solution.
I've asked this question over on programmers that's linked to this one. I'm trying to find a suitable header, that is unlikely to be stripped, that I can use to send back a unique Request ID with every response, even if it does not send a body.
One of the headers I considered was the Pragma header, as looking at the spec it appears to be intended not only for the additional no-cache HTTP 1.0 backwards-compatibility value, but also for application-specific values, so I should be able to use it. It should be possible, for example, to send something like no-cache; requestid=id.
So in a DelegatingHandler I tried writing to it with my ID:
//HttpResponseMessage Response;
Response.Headers.Add("pragma", "some_value");
But it arrives at the client with no-cache; always. I think WebAPI automatically sends caching headers consistent with caching being switched off, which includes the Pragma one.
So, how do I make sure my value is maintained and not overwritten?
I've cracked it, the answer is to make sure you also set the CacheControl header on the HttpResponseMessage, which then bypasses some slightly fishy logic in System.Web.Http.WebHost.HttpControllerHandler (I've opened a discussion on CodePlex about this; I think the logic needs to be changed).
So instead of
//HttpResponseMessage Response;
Response.Headers.Add("pragma", "some_value");
You have to do:
Response.Headers.CacheControl =
new System.Net.Http.Headers.CacheControlHeaderValue()
{
NoCache = true
};
Response.Headers.Add("pragma", "some_value");
(I've used NoCache since the current API default is to switch caching off for all responses).
Alright. Hopefully this will be my last post about the download manager I am writing in Objective-C. Everything seems to work well except the pause/resume functionality. My issue is that when a download tries to continue from where it left off, it appends the data it receives to the file, but it still seems that it's trying to download the entire file. This results in a file that is larger than the original file is supposed to be. Here is the code I am using for downloading files. Am I doing something wrong?
-(void)start:(unsigned int)fromByte {
NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:[NSURL URLWithString:self.url] cachePolicy:NSURLRequestUseProtocolCachePolicy timeoutInterval:DEFAULT_TIMEOUT];
// Define the bytes we wish to download.
NSString *range = [NSString stringWithFormat:#"bytes=%i-", fromByte];
[request setValue:range forHTTPHeaderField:#"Range"];
// Data should immediately start downloading after the connection is created.
self.urlConnection = [[NSURLConnection alloc] initWithRequest:request delegate:self startImmediately:TRUE];
if (!self.urlConnection) {
#warning Handle error.
}
}
I see you are specifying the Range header in the request. First thing to check is whether the server is actually honoring the Range request, by checking the headers in the response object (which should be an NSHTTPURLResponse) in connection:didReceiveResponse: for a proper Content-Range.
I finally figured this out. It turns out that the 'getFilesizeInBytes' method I had was get the NSFileSize object from the file's attributes, but I was directly casting this to an int. This caused the number to be about 20 times larger than it should have been. I was able to fix this by using [#"" intValue]. Once this was fixed, the servers were able to give me the rest of the file starting with the correct byte. It seems that before my issue was not that the server wasn't honoring my request, but that it couldn't honor my request due to me requesting data that was well beyond the final byte of the file.
There's no support for pause/resume in NSURLConnection. You can emulate it by stopping the request, then issuing a request for the rest of the content with a Range header on resume. Some support from the HTTP server is required for that, and is not guaranteed.
Looks like lack of support at the server is what you're facing.
Most download managers, however, implement their own HTTP stack on top of sockets.