I tried the following to set the basic auth username and password, but it does not seem to be passing the basic auth in the request..
secureManager = [[RKObjectManager objectManagerWithBaseURL:#"http://localhost:3000"] retain];
secureManager.client.username = uname;
secureManager.client.password = pwd;
RKObjectLoader *loader = [svc getObject:user delegate:self];
loader.userData = [NSNumber numberWithInt:RequestLogin];
UPDATE: found my problem, I needed to add the following snippet
secureManager.client.forceBasicAuthentication = YES;
You can grab an instance of the underlying RKClient before you make your request and set the Username and Password like so:
// Set the Username and Password
[RKObjectManager sharedManager].client.username = #"username";
[RKObjectManager sharedManager].client.password = #"letmein";
// Make our Request
[[RKObjectManager sharedManager] getObject:user mapResponseWith:mapping delegate:self];
As MonkeyBonkey points out in the comments, you may need to force the authentication using a flag:
[RKObjectManager sharedManager].client.forceBasicAuthentication = YES;
Related
Everything works great with the code, the problem is it says authentication failed, though the username and password is 100% correct, so not sure if there is a way to pass the login and the password and get the user authenticated
NSString *urlString = #"URL";
NSMutableArray * keyStrings = [NSMutableArray new];
NSMutableArray * valueStrings = [NSMutableArray new];
[keyStrings addObject:#"user"];
[valueStrings addObject:#"abc"];
[keyStrings addObject:#"password"];
[valueStrings addObject:#"12345"];
NSDictionary * requestDict = [[NSDictionary alloc] initWithObjects:valueStrings forKeys:keyStrings];
AFHTTPSessionManager* manager = [AFHTTPSessionManager manager];
manager.responseSerializer = [AFHTTPResponseSerializer serializer];
manager.responseSerializer.acceptableContentTypes = [NSSet setWithObject:#"application/xml"];
manager.responseSerializer.acceptableContentTypes = [NSSet setWithObject:#"text/html"];
manager.securityPolicy.allowInvalidCertificates = YES;
manager.securityPolicy.validatesDomainName = NO;
I'm guessing you're intending to use HTTP Basic Authentication in which case you should be using the Authorization header field instead of passing the username and password in plain text within the request's parameters (I made this mistake the first time I tried to do Basic Authentication with a REST API).
You will need to add it to the AFHTTPRequestSerializer's headers which you can do by utilizing the setAuthorizationHeaderFieldWithUsername:password: method or by constructing the header field value manually and setting the header field value using the setValue:forHTTPHeaderField: method.
New to iOS... I have a Rails/Devise application that is set up with token_authenticatable. I have a tokens controller that returns a token when a valid email and password is posted:
URL: http://localhost:3000/tokens.json
Body: email=example#example.com&password=foobar
Response:
{
"token": "xyzxyztoken"
}
Once created this token grants access to other sections of the site and this works in a test client (RESTClient). I have been stuck for a while connecting to it using RESTKit in iOS.
I create my RKObjectManager in AppDelegate.m:
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
RKURL *baseURL = [RKURL URLWithBaseURLString:#"http://localhost:3000"];
self.objectManager = [RKObjectManager objectManagerWithBaseURL:baseURL];
self.objectManager.client.baseURL = baseURL;
return YES;
}
I have a view controller and when you tap a button it calls this:
-(IBAction)btnLoginRegisterTapped:(UIButton*)sender
{
// get the object manager
RKObjectManager *objectManager = [RKObjectManager sharedManager];
objectManager.serializationMIMEType = RKMIMETypeJSON;
// set mapping
RKObjectMapping *nameMapping = [RKObjectMapping mappingForClass:[Token class]];
[nameMapping mapKeyPathsToAttributes:#"token", #"token", nil];
[objectManager.mappingProvider setMapping:nameMapping forKeyPath:#""];
// create url
NSDictionary *queryParams;
queryParams = [NSDictionary dictionaryWithObjectsAndKeys:#"example#example.com", #"email", #"foobar", #"password", nil];
RKURL *URL = [RKURL URLWithBaseURL:[objectManager baseURL] resourcePath:#"/tokens.json" queryParameters:queryParams];
[objectManager loadObjectsAtResourcePath:[NSString stringWithFormat:#"%#?%#", [URL resourcePath], [URL query]] delegate:self];
}
This is my error:
2013-01-10 16:32:55.554 MyApp[69314:14003] response code: 404
2013-01-10 16:32:55.555 MyApp[69314:14003] W restkit.network:RKObjectLoader.m:300 Unable to find parser for MIME Type 'text/html'
2013-01-10 16:32:55.555 MyApp[69314:14003] W restkit.network:RKObjectLoader.m:329 Encountered unexpected response with status code: 404 (MIME Type: text/html -> URL: http://localhost:3000/tokens.json?password=foobar&email=example%40example.com -- http://localhost:3000 -- http://localhost:3000)
2013-01-10 16:32:55.556 MyApp[69314:14003] Error: The operation couldn’t be completed. (org.restkit.RestKit.ErrorDomain error 4.)
This may be a simple question but I am lost. I think I just am not understanding POSTing from iOS in general. One thing to possibly note is that viewing /tokens.json in a browser returns a Routing Error because I do not actually have a view for that:
No route matches [GET] "/tokens.json"
Anyway the point of all this is for a user to log in and get a token stored and then use it to access other data from the rails app.
Please let me know if more clarification is needed.
In your rails app, open config/routes.rb
add
resources :tokens
I am trying to post a tweet using RestKit but repeatedly get a 401 back ("message":"Could not authenticate you","code":32).
RKObjectManager *twitterObjectManager = [RKObjectManager managerWithBaseURLString:#"https://api.twitter.com"];
twitterObjectManager.client.authenticationType = RKRequestAuthenticationTypeOAuth1;
twitterObjectManager.client.OAuth1ConsumerKey = #"abc";
twitterObjectManager.client.OAuth1ConsumerSecret = #"xyz";
twitterObjectManager.client.OAuth1AccessToken = #"lmn";
twitterObjectManager.client.OAuth1AccessTokenSecret = #"qrs";
NSMutableDictionary *parameters = [NSMutableDictionary dictionaryWithObject:#"Make it work" forKey:#"status"];
[twitterObjectManager.client post:#"/1.1/statuses/update.json" params:parameters delegate:self];
The same oAuth keys work when reading statuses using GET from statuses/user_timeline.
Is there anything else which needs to be done.
It is normal for twitter, trying to use API version 1, NOT 1.1
I'm attempting to map the following object:
{"walletAccepted":false,"creditCardTypesAccepted":["visa","mastercard","discover","americanexpress"],"paypalAccepted":false}
To an object with identical attributes. However, the ResourcePath is dynamic, in that it's along the lines of /paymentmethods/zone/:internalZoneCode
Every attempt I've made at mapping it always ends up with "Encountered errors during mapping: Could not find an object mapping for keyPath: ''"
I think the issue is that there is no root key/key path. I've also attempted the following with no luck:
RKURL *rkUrl = [RKURL URLWithBaseURL:[RKClient sharedClient].baseURL resourcePath:[NSString stringWithFormat:#"/paymentoptions/zone/%#", zoneNumber]];
RKObjectLoader* loader = [[RKObjectLoader alloc] initWithURL:rkUrl mappingProvider:[RKObjectManager sharedManager].mappingProvider];
loader.method = RKRequestMethodGET;
loader.delegate = self;
loader.objectMapping = [[RKObjectManager sharedManager].mappingProvider objectMappingForClass:[PMZonePaymentMethods class]];
[loader send];
I found the way to make it happen is to configure the mapping, and add the mapping to the object manager without a key path or resourcek
So like this:
RKObjectMapping* zonePaymentMethodsMapping = [RKObjectMapping mappingForClass:[PMZonePaymentMethods class]];
[zonePaymentMethodsMapping mapKeyPath:#"walletAccepted" toAttribute:#"walletAvailable"];
[zonePaymentMethodsMapping mapKeyPath:#"creditCardTypesAccepted" toAttribute:#"cards"];
[zonePaymentMethodsMapping mapKeyPath:#"paypalAccepted" toAttribute:#"paypalAvailable"];
[[RKObjectManager sharedManager].mappingProvider addObjectMapping:zonePaymentMethodsMapping];
and use this to make the request:
RKURL *rkUrl = [RKURL URLWithBaseURL:[RKClient sharedClient].baseURL resourcePath:[NSString stringWithFormat:#"/paymentoptions/zone/%#", zoneNumber]];
RKObjectLoader* loader = [[RKObjectLoader alloc] initWithURL:rkUrl mappingProvider: [RKObjectManager sharedManager].mappingProvider];
loader.method = RKRequestMethodGET;
loader.delegate = self;
loader.objectMapping = [[RKObjectManager sharedManager].mappingProvider objectMappingForClass:[PMZonePaymentMethods class]];
[loader send];
Since I couldn't figure out how to set up two different POST resource paths for the same class , I tried manually creating the RKObjectLoader request but it seems to keep sending a GET request instead of a POST even though I've set the method to POST. Here is my code
User *user = [[User alloc] init];
user.uname = uname;
user.pwd = pwd;
RKObjectManager *svc = [RKObjectManager sharedManager];
RKObjectMapping* mapping = [svc.mappingProvider objectMappingForClass:[User class]];
// what I was using before I needed two post resource paths//[svc postObject:user mapResponseWith:mapping delegate:self];
RKObjectLoader *loader = [svc loadObjectsAtResourcePath:authResourcePath objectMapping:mapping delegate:self];
[loader setMethod:RKRequestMethodPOST];
loader.userData = [NSNumber numberWithInt:RequestLogin];
loader.params = [NSDictionary dictionaryWithObjectsAndKeys:
uname, #"uname",
pwd, #"pwd",
nil];
[loader setSourceObject:user];
[loader send];
[user release];
In cases where you have more than one path to POST or PUT to, the easiest thing to do is use the block form of the postObject: invocation and specify the destination resourcePath yourself:
[[RKObjectManager sharedManager] postObject:foo delegate:bar block:^(RKObjectLoader *loader) {
loader.resourcePath = #"/my/destinationPath";
}];
We may introduce a named route concept at some point that would let you disambiguate the routes using names, but for now its purely based on the HTTP verb.
Note that you do NOT and cannot register the secondary path on the router -- you are sidestepping it completely for the secondary path.
In order to complete Blake Watters answer if the different route need different objectMapping you will need to do:
[[RKObjectManager sharedManager] postObject:query delegate:saveJobQueryHandler block:^(RKObjectLoader* loader) {
loader.objectMapping = NEW_MAPPING;
loader.resourcePath = #"/other/url";
loader.targetObject = nil; // Important
}];
For more information about loader.targetObject = nil; read sendObject:delegate:block: