Arlen Anderson
Arlen Anderson

Reputation: 2496

Mutable NSHTTPURLResponse or NSURLResponse

I need to modify response headers in an NSURLResponse. Is this possible?

Upvotes: 3

Views: 7027

Answers (4)

William Kinaan
William Kinaan

Reputation: 28799

You can do that, and you'd need NSHTTPURLResponse not NSURLResponse, because, in Swift, NSURLResponse can be used with many protocols rather than just http, such as ftp, data:, or https. As a result, you can call it to get the meta data info, such as expected content type, mime type and text encoding, while NSHTTURLResponse is the one responsible of handling HTTP protocol responses. Thus, it is the one to manipulate the headers.

This is a small code that manipulates the header key Server from the response, and prints the value before and after the change.

let url = "https://www.google.com"
    let request = NSMutableURLRequest(URL: NSURL(string: url)!)
    let session = NSURLSession.sharedSession()
    let task = session.dataTaskWithRequest(request, completionHandler: {(data, response, error) in

        if let response = response {

            let nsHTTPURLResponse = response as! NSHTTPURLResponse
            var headers = nsHTTPURLResponse.allHeaderFields
            print ("The value of the Server header before is: \(headers["Server"]!)")
            headers["Server"] = "whatever goes here"
            print ("The value of the Server header after is: \(headers["Server"]!)")

        }

        })
        task.resume()

Upvotes: -3

Colin Barrett
Colin Barrett

Reputation: 4451

I was just talking about this with a friend. My suggestion would be to write a subclass of NSURLResponse. Something along these lines:

@interface MyHTTPURLResponse : NSURLResponse { NSDictionary *myDict; } 
- (void)setAllHeaderFields:(NSDictionary *)dictionary;
@end

@implementation MyHTTPURLResponse
- (NSDictionary *)allHeaderFields { return myDict ?: [super allHeaderFields]; }
- (void)setAllHeaderFields:(NSDictionary *)dict  { if (myDict != dict) { [myDict release]; myDict = [dict retain]; } }
@end

If you're dealing with an object you didn't make, you can try using object_setClass to swizzle the class out. However I don't know if that will add the necessary instance variable. You could also use objc_setAssociatedObject and stuff this all in a category instead, if you can support a new enough SDK.

Upvotes: 9

Plamen Terziev
Plamen Terziev

Reputation: 11

I had a similar problem. I wanted to modify the header fileds of http url response. I needed it because wanted to provide cached url response to UIWebView and want to fool the web view that the response is not expired (i.e. I wanted to change "Cache-Control" property of the header but to keep the rest of the headers). My solution was to use NSKeyedArchiver to encode the original http response and to intercept the serialization with delegate. In

-(id) archiver:(NSKeyedArchiver*) archiver willEncodeObject:(id) object

I check if the object is NSDictionary and if so, I returned modified dictionary (i.e with updated "Cache-Control" header). Afterwards I just deserialized the serialized response using NSKeyedUnarchiver. Of course you may hook to the unarchiver and modify the headers in its delegate.

Note that in iOS 5 Apple has added

-(id)initWithURL:(NSURL*) url statusCode:(NSInteger) statusCode HTTPVersion:(NSString*) HTTPVersion headerFields:(NSDictionary*) headerFields

which is not in the documentation (documentation bug), but it is in the public API of NSHTTPURLResponse

Upvotes: 1

shawnwall
shawnwall

Reputation: 4607

You can read them into a NSDictionary using the allHeaderFields method.

    NSHTTPURLResponse *httpResponse = (NSHTTPURLResponse *)response;
    NSDictionary *httpResponseHeaderFields = [httpResponse
allHeaderFields];

To be 100% safe you'd want to wrap it with

if ([response respondsToSelector:@selector(allHeaderFields)]) {... }

Upvotes: 2

Related Questions