I am using RestKit for my iOS app.
I would like to add a custom header for all requests.
Is it possible to add a single header in one place and have all my RestKit requests use it? If so, where do I add the code?
If not - do I have to add a header for every single request I make?
You can set the header on the client that the RKObjectManager creates, after initializing the RKObjectManager:
RKObjectManager *manager = [RKObjectManager managerWithBaseURL:#"https://mycompany.example.com/rest/"];
[[manager HTTPClient] setDefaultHeader:#"X-AUTH-TOKEN" value:#"abc123"];
You don't need to subclass the AFHTTPClient.
It is possible by using custom AFHTTPClient. Create a subclass of AFHTTPClient and rewrite requestWithMethod:path:parameters: method like this:
- (NSMutableURLRequest *)requestWithMethod:(NSString *)method
path:(NSString *)path
parameters:(NSDictionary *)parameters
{
[self setDefaultHeader:#"X-USER-TOKEN" value:userToken];
return [super requestWithMethod:method
path:path
parameters:parameters];
}
Then initialize object manager with it:
RKObjectManager *manager = [[RKObjectManager alloc]
initWithHTTPClient:customHttpClient];
Related
Is they a way to add some url parameters (like http://api.example.com/v3/object?data=123&info=test) to all restkit request witouth adding them manually to all
getObjectsAtPath:parameters:success:failure:
getObjectsAtPathForRouteNamed:object:parameters:success:failure:
...
each request should add the info parameter.
I've actually a way to do it, using Method Swizzling. Is they a way to do it directly with RestKit?
You have a couple of ways to do this:
you can either subclass the methods of RKObjectManager to something like this:
-(void)addedParamToGetObjectsAtPath:(NSString*)path parameters:(NSDictionary*)parameters success:(success:^(RKObjectRequestOperation *operation, RKMappingResult *mappingResult)successBlock failure::^(RKObjectRequestOperation *operation, NSError *error){
NSMutableDictionary* newParams = [NSMutableDictionary new];
if(parameters){
[newParams addEntriesFromDictionary:parameters];
}
newParams[#"info"]=test;
getObjectsAtPath:(NSString*)path parameters:(NSDictionary*)parameters success:(success:^(RKObjectRequestOperation *operation, RKMappingResult *mappingResult){
// Deal with the success here
successBlock(operation, mappingResult);
} failure:^(RKObjectRequestOperation *operation, NSError *error) {
//Deal with the error here
errorBlock(operation, error);
}];
Or tell Restkit to use a different RequestOperationClass
//When configuring RestKit
RKObjectManager *objectManager = [RKObjectManager managerWithBaseURL:[NSURL URLWithString:BASE_URL]];
//Some more configuration
//....
[objectManager registerRequestOperationClass:[YourObjectRequestOperation class]];
And define a subclass of RKObjectRequestOperation, YourObjectRequestOperation
#import "FBObjectRequestOperation.h"
#interface RKHTTPRequestOperation ()
#property (nonatomic, strong, readwrite) NSMutableURLRequest* request;
#end
#implementation FBObjectRequestOperation
- (id)initWithHTTPRequestOperation:(RKHTTPRequestOperation *)requestOperation responseDescriptors:(NSArray *)responseDescriptors
{
NSParameterAssert(requestOperation);
NSParameterAssert(responseDescriptors);
//your method to change the requestOperation
RKHTTPRequestOperation* myRequestOperation = [YourObjectRequestOperation addParametersToRequest:requestOperation];
self = [super initWithHTTPRequestOperation:myRequestOperation responseDescriptors:responseDescriptors];
if (self) {
//Change headers or any other thing that you need
}
return self;
}
To actually change the requestOperation you will need to get the url from the request and and add there the new parameters. That will happen in this part RKHTTPRequestOperation* myRequestOperation = [YourObjectRequestOperation addParametersToRequest:requestOperation]; and I am living up to you to complete the code.
This should work for any request you are doing with the object manager.
This technique is also very helpful is you need to calculate headers dynamically for each request.
I am new to AFNetworking and I know how to pass URL parameters. But how would I pass headers into the same call.
I am also subclassing my AFHTTPSessionManager
See my code below:
- (void)getExpenses:(NSString *)page
success:(void (^) (NSArray *myExpenses))success
failure:(RequestFailureBlock)failure
{
NSString *resourceURL = [NSString stringWithFormat:#"%#/expenses/", APIBaseURLString];
NSDictionary *parameters = #{#"page":page, #"Authorization": APIAuthorization};
[self getExpenses:resourceURL parameters:parameters success:success failure:failure];
}
setAuthorizationHeaderFieldWithToken is deprecated due to servers having different requirements about how the access token is sent (token, bearer, etc)
michaels answer otherwise is correct, use
[self.requestSerializer setValue:#"Some-Value" forHTTPHeaderField:#"Header-Field"];
or
[self.requestSerializer setAuthorizationHeaderFieldWithUsername:#"" password:#""];
for basic auth
You set header values on the requestSerializer property of AFHTTPSessionManager:
[self.requestSerializer setValue:#"Some-Value" forHTTPHeaderField:#"Header-Field"];
EDIT:
It looks like you're trying to set authorization; there is a method for that too:
[self.requestSerializer setAuthorizationHeaderFieldWithUsername:#"" password:#""];
// OR
[self.requestSerializer setAuthorizationHeaderFieldWithToken:#""];
If you need to set the Content-Type header, see this SO answer on how to do that
can one AFTHTTPClient handle both json and xml?
I have one single domain in which some services only return json, while others only return xml. How would I make a GET request and instruct AFHTTPClient to use AFJSONRequestOperation for some services and an AFXMLRequestOperation for other GET requests?
So what I would like is:
chairs.com GET customerprofile ---> returns XML (no option for json)
charis.com GET inventory ---> returns JSON (no option for xml)
Is this a job for multiple AFHTTPClients?
Thanks
Your use of AFHTTPClient indicates you're using AFNetworking 1, but I'll answer this question for both versions, for future readers.
AFNetworking 1.x
You just need to register the appropriate AFHTTPOperation subclass. This is typically done in your subclass of initWithBaseURL::
- (instancetype) initWithBaseURL:(NSURL *)url {
self = [super initWithBaseURL:url];
if (self) {
[self registerHTTPOperationClass:[AFJSONRequestOperation class]];
[self registerHTTPOperationClass:[AFXMLRequestOperation class]];
}
return self;
}
When your app makes an outgoing request, you'll need to make sure your accept headers are set appropriately (for example, to text/json or text/xml, depending on what you expect from which endpoint you hit). Otherwise, AFNetworking won't know which operation to use for which request.
There are a few ways to easily solve this Accept header requirement. If one of your endpoints is an exception to a general rule, I might do this by overriding requestWithMethod:path:parameters::
- (NSMutableURLRequest *)requestWithMethod:(NSString *)method
path:(NSString *)path
parameters:(NSDictionary *)parameters {
request = [super requestWithMethod:method path:path parameters:parameters];
if (/* the endpoint specified in path returns XML */) {
[request setValue:#"text/xml" forHTTPHeaderField:#"Accept"];
} else {
[request setValue:#"text/json" forHTTPHeaderField:#"Accept"];
}
}
This is a small violation of tell, don't ask; feel free to refactor as necessary.
If you don't plan on upgrading to AFNetworking 2, then you can stop reading here.
AFNetworking 2.x
Version 2.0 of AFNetworking makes this simpler and more intuitive. In 2.0, the serialization responsibility is broken out into a separate class. Instances of this class are called response serializers. When you upgrade, you'll want an AFCompoundResponseSerializer. The documentation describes it best:
AFCompoundSerializer is a subclass of AFHTTPSerializer that delegates the response serialization to the first AFHTTPSerializer object that returns YES to validateResponse:data:error:, falling back on the default behavior of AFHTTPSerializer. This is useful for supporting multiple potential types and structures of server responses with a single serializer.
For example:
AFJSONResponseSerializer *jsonSerializer = [AFJSONResponseSerializer serializerWithReadingOptions:0];
AFXMLDocumentSerializer *xmlSerializer = [AFXMLDocumentSerializer serializerWithXMLDocumentOptions:0];
AFCompoundResponseSerializer *compoundSerializer = [AFCompoundResponseSerializer compoundSerializerWithResponseSerializers:#[jsonSerializer, xmlSerializer]];
[AFHTTPSessionManager manager].responseSerializer = compoundSerializer;
I was using this method to POST my objects, but it's been deprecated:
- (void)postObject:(id<NSObject>)object mapResponseWith:(RKObjectMapping *)objectMapping delegate:(id<RKObjectLoaderDelegate>)delegate
What should I now use instead? How do I configure the RKObjectMapping of my response?
The selector has been deprecated in favor of - (void)postObject:(id<NSObject>)object usingBlock:(RKObjectLoaderBlock)block - thus you use the block to configure a RKObjectLoader instance. Example follows how to set an objectMapping (mapping used to map the response) in the block:
[[RKObjectManager sharedManager] postObject:object
block:^(RKObjectLoader* loader) {
loader.objectMapping = objectMapping;
loader.delegate = delegate;
}];
you could also use
[[RKObjectManager sharedManager] postObject:(id<NSObject>)object delegate:(id<RKObjectLoaderDelegate>)delegate];
This is not deprecated
I am using a subclass of NSURLProtocol to intercept all HTTP calls and modify the user agent as well as add a other http headers required by my server.
-(id)initWithRequest:(NSURLRequest *)request
cachedResponse:(NSCachedURLResponse *)cachedResponse
client:(id <NSURLProtocolClient>)client
{
NSMutableURLRequest* lInnerRequest;
//************************************************
lInnerRequest = [request mutableCopy];
[lInnerRequest setValue:#"MyUserAgent" forHTTPHeaderField:#"User-Agent"];
//************************************************
self = [super initWithRequest:lInnerRequest
cachedResponse:cachedResponse
client:client];
//************************************************
if (self)
{
self.innerRequest = lInnerRequest;
}
//***********************************00*************
[lInnerRequest release];
//************************************************
return self;
}
My protocol then uses an NSURLConnection
- (void)startLoading
{
self.URLConnection = [NSURLConnection connectionWithRequest:self.innerRequest delegate:self];
}
I then implement all the delegate method in the NSURLConnection by forwarding the call to the equivalent NSURLProtocolClient method.
This works well in general but when I am uploading data to the server, my code which is using NSURLConnection does not get called back on:
connection:didSendBodyData:totalBytesWritten:totalBytesExpectedToWrite:
I understand why this is since I didn't implement that method in the NSURLProtocol as there are no equivalent NSURLProtocolClient method which can be used to report upload progress.
Has someone found any workaround for this?
add this line to your code to make it a HTTP POST request:
[lInnerRequest setHTTPMethod:#"POST"];
Use setProperty:forKey:inRequest: to save NSURLConnection and delegate object, then use propertyForKey:inRequest: to load NSURLConnection object and delegate object in custom NSURLProtocol class, when data sent, use the NSURLConnection object and delegate to call the delegate method