Doing a POST on Twitter's statuses/update resource using RestKit - ios

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

Related

how to turn AFNetworking GET request to soap web service request?

The project I'm working on have using AFHTTPRequestOperation to retrieve data from the web service using the down below method but now the company want to change this method and make it a soap request can i modify the current code the project have more than 100 class and so advanced and I'm still beginner can any one help me 2 modify the code to use the soap request and accept the soap message and requests with a simple edit or something plz ,this is the code that exist :
NSString *path = [NSString stringWithFormat:#"*********?mobile=%#&email=%#",mobile,email];
[self.operationManager GET:path parameters:nil success:^void(AFHTTPRequestOperation * operation, id responseObject) {
} failure:^void(AFHTTPRequestOperation * operation, NSError * error) {
}];
I have tried to follow the answer on (How can you use AFNetworking or STHTTPRequest to make a request of a SOAP web service?) but in the part where i call it in the other class that i need to use it in this error showed (No known class method for selector 'dictionaryForNSXMLParser:'). i found the problem i didn't call
NSDictionary *dict = [XMLReader dictionaryForNSXMLParser:parser error:nil];
but he did the same ,he used
NSDictionary *dict = [XMLReader dictionaryForNSXMLParser:parser];but while running the app it crashed in XMLReader.m class on this line
self.errorPointer = *error;
XMLReader *reader = [[XMLReader alloc] initWithError:error];
ther error is EC_Bad_Access.

How to fix this error in sending emails by Outh2 using MailCore?

Im using mailcore to for sending mails using access token. This work fine in case of using password but by using access tokens its not working. I have changed every combination of ports and connection types but still getting the same issue. I need some help.
MCOSMTPSession * smtpSession = [[MCOSMTPSession alloc] init];
smtpSession.hostname = #"smtp.gmail.com";
smtpSession.port = 465;
smtpSession.username = user; //saved value
smtpSession.connectionType = MCOConnectionTypeTLS;
smtpSession.OAuth2Token = token; //saved value(Validated)
smtpSession.authType = MCOAuthTypeXOAuth2;
smtpSession.checkCertificateEnabled=NO;
MCOMessageBuilder * builder = [[MCOMessageBuilder alloc] init];
MCOAddress *fromAdress=[[MCOAddress alloc]init];
fromAdress = [MCOAddress addressWithMailbox:user];
MCOAddress *toAdress=[[MCOAddress alloc]init];
toAdress = [MCOAddress addressWithMailbox:self.to.text];
[[builder header] setFrom:fromAdress];
[[builder header] setTo:#[toAdress]];
NSString *htmlbody1=#"Body";
[[builder header] setSubject:#"Some_subject"];
[builder setHTMLBody:htmlbody1];
[smtpSession setConnectionLogger:^(void * connectionID, MCOConnectionLogType type, NSData * data) {
NSString *response = [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding];
NSLog(#"Response %#",response);
}];
NSData * rfc822Data = [builder data];
MCOSMTPSendOperation *sendOperation = [smtpSession sendOperationWithData:rfc822Data];
[sendOperation start:^(NSError *error) {
if(error) {
NSLog(#"Error sending email: %#", error);
} else {
}
}];
im getting this response
Response 535-5.7.8 Username and Password not accepted. Learn more at
535 5.7.8 https://support.google.com/mail/answer/14257 gg7sm1578291wjd.10
and getting this error
Error Domain=MCOErrorDomain Code=5 "Unable to authenticate with the current session's credentials.
It's worth noting that if you want to send emails via Gmail, then you need to either:
Use Oauth (more details, see below)
For users with 2-step verification enabled, have them set up an App password that they then enter in the app
Have users enable the Less Secure Apps option on their account
Using OAuth
For OAuth, you can use a library like GTMAppAuth, which allows you to create an OAuth token to use within your app. However, when doing so, you must make sure to set the scope correctly when requesting access. Here's some code taken from the wiki:
OIDAuthorizationRequest *request =
[[OIDAuthorizationRequest alloc] initWithConfiguration:configuration
clientId:kClientID
clientSecret:kClientSecret
scopes:#[OIDScopeOpenID, OIDScopeProfile]
redirectURL:redirectURI
responseType:OIDResponseTypeCode
additionalParameters:nil];
Sending via SMTP
If you want to use SMTP to send email, then you must add #"https://mail.google.com/" to the scopes` argument.
Note that it seems that in 2022, Google really doesn't want you directly accessing SMTP. If you use the scope #"https://mail.google.com/" in your app, then your app is considered Restricted and it'll need to go through a verification process.
Sending via Google APIs
The alternative is to send via the Google APIs. If you want to do this, then you need the #"https://www.googleapis.com/auth/gmail.send" scope. This will allow you to send email e.g. using the GTLR library.
Notes:
If you try to use the #"https://www.googleapis.com/auth/gmail.send" scope then login via SMTP, you'll get the error Username and Password not accepted. You can't use that scope for SMTP, only for the googleapis.
P.S. Why Google can't better document this, is beyond me. I've just volunteered 20 hours of my time to Google for figuring this out.

RestKit on iOS - subsequent API calls for nested objects?

I'm using RestKit to pull data from the Foursquare API into my iphone app, but having trouble with the followup API call for a nested object.
Specifically:
I call the Venues search API (https://developer.foursquare.com/docs/venues/search) to retrieve a list of Venues. Each Venue has a unique ID which is included in the response. I do this with the following in my ViewController:
[objectManager loadObjectsAtResourcePath:[NSString stringWithFormat:#"%#?%#", [URL resourcePath], [URL query]] delegate:self];
I then do the RestKit mapping etc and store the Venues in a data array. Everything working fine up to here.
At this point I loop through the Venues in the data array, and have to make a followup API call to retrieve more details about each Venue. For each Venue, I use the unique ID and call the Venue detail API (https://developer.foursquare.com/docs/venues/venues). This is the part that stumps me. I am trying to get an NSArray of Photo objects returned from this second API call. So far I have tried variations of this:
for (id venue in self.data){
Venue *myvenue = (Venue *)venue;
RKURL *URL = [RKURL URLWithBaseURL:[objectManager baseURL] resourcePath:[NSString stringWithFormat:#"/venues/%#", myvenue.venueid] queryParameters:queryParams];
[objectManager loadObjectsAtResourcePath:[NSString stringWithFormat:#"%#?%#", [URL resourcePath], [URL query]] delegate:self];
}
and in my mapping:
RKObjectMapping *photosMapping = [RKObjectMapping mappingForClass:[Photo class]];
[photosMapping mapKeyPathsToAttributes:#"url", #"imageURL", nil];
[venueMapping mapRelationship:#"photos" withMapping:photosMapping];
[objectManager.mappingProvider setMapping:photosMapping forKeyPath:#"response.photos"]; // not sure if this keypath is for the second API call
and my Venue class has this:
#property (strong, nonatomic) NSArray *photos;
Venue.photos always returns empty. Any suggestions?
From the code you have posted, nowhere are you linking the response with a venue. I'm not sure on the exact JSON returned by foursquare but you will need to conceive a way to map using your venue mapping which has a nested photo object. If foursquare does not return some reference to the original Venue object in it's response, you can try setting the RKObjectLoader's targetObject property to the Venue (myVenue in your code)
Not sure if this is the 'best' way to do it, but I got it working in the end. Paul de Lange's comment about not mapping the venue and the photos correctly set me off on the correct path.
The foursquare response was not in the format that I required in my app, so I had to use RKObjectLoader's willMapData to modify the response slightly before the rest of the mapping operation.
Here it is in case it helps anybody in the future:
- (void)objectLoader:(RKObjectLoader *)loader willMapData:(inout id *)mappableData {
NSArray* origPhotosArray = [*mappableData valueForKeyPath:#"response.venue.photos.groups"];
NSMutableArray* newPhotosArray = [[NSMutableArray alloc] init];
// <snip> Some extra code goes here where I discarded photos I did not want.... </snip>
// Create copies of objects in the format that I want
NSMutableDictionary *myphoto = [[NSMutableDictionary alloc] initWithDictionary:Photo];
NSString *venueID = [*mappableData valueForKeyPath:#"response.venue.id"];
[myphoto setObject:venueID forKey:#"assignedVenueid"];
[newPhotosArray addObject:myphoto];
// Replace the new objects instead of the response objects
[*mappableData removeObjectForKey:#"response.venue.photos.groups"];
[*mappableData setObject:newPhotosArray forKey:#"response.venue.photos.groups"];
}
And in my mapping:
RKManagedObjectMapping *photosMapping = [RKManagedObjectMapping mappingForClass:[Photo class] inManagedObjectStore:objectStore];
[photosMapping mapKeyPath:#"url" toAttribute:#"imageURL"];
[photosMapping mapKeyPath:#"assignedVenueid" toAttribute:#"assignedVenueid"];
[venueMapping mapRelationship:#"photos" withMapping:photosMapping];
[objectManager.mappingProvider setMapping:photosMapping forKeyPath:#"response.venue.photos.groups"];
[photosMapping hasOne:#"assignedVenue" withMapping:venueMapping];
[photosMapping connectRelationship:#"assignedVenue" withObjectForPrimaryKeyAttribute:#"assignedVenueid"];

Does TWRequest work for the twitter streaming api?

I am trying to make a basic iphone app that shows nearby tweets. I was using the TWRequest object to accomplish this with the twitter search api. Unfortunately, I would actually like to mark the tweets on a map using their GPS coordinates and the search api doesn't seem to return the actual location that a tweet was made with any better accuracy than the city name.
As such, I think I need to switch to the streaming api. I am wondering if it is possible to continue using the TWRequest object in this case or if I need to actually switch over to using NSURLConnection? Thanks in advance!
Avtar
Yes, you can use a TWRequest object. Create your TWRequest object using the appropriate URL and parameters from the Twitter API doco, and set the TWRequest.account property to the ACAccount object for the Twitter account.
You can then use the signedURLRequest method of TWRequest to get an NSURLRequest which can be used to create an asynchronous NSURLConnection using connectionWithRequest:delegate:.
Once this is done, the delegate's connection:didReceiveData: method will be called whenever data is received from Twitter. Note that each NSData object received may contain more than one JSON object. You will need to split these up (separated by "\r\n") before converting each one from JSON using NSJSONSerialization.
It took me a bit of time to get this up and running, So I thought I aught to post my code for others. In my case I was trying to get tweets close to a certain location, so you will see that I used a locations parameter and a location struct I had in scope. You can add whatever params you want to the params dictionary.
Also note that this is bare bones, and you will want to do things such as notify the user that an account was not found and allow the user to select the twitter account they would like to use if multiple accounts exist.
Happy Streaming!
//First, we need to obtain the account instance for the user's Twitter account
ACAccountStore *store = [[ACAccountStore alloc] init];
ACAccountType *twitterAccountType = [store accountTypeWithAccountTypeIdentifier:ACAccountTypeIdentifierTwitter];
// Request permission from the user to access the available Twitter accounts
[store requestAccessToAccountsWithType:twitterAccountType
withCompletionHandler:^(BOOL granted, NSError *error) {
if (!granted) {
// The user rejected your request
NSLog(#"User rejected access to the account.");
}
else {
// Grab the available accounts
NSArray *twitterAccounts = [store accountsWithAccountType:twitterAccountType];
if ([twitterAccounts count] > 0) {
// Use the first account for simplicity
ACAccount *account = [twitterAccounts objectAtIndex:0];
NSMutableDictionary *params = [[NSMutableDictionary alloc] init];
[params setObject:#"1" forKey:#"include_entities"];
[params setObject:location forKey:#"locations"];
[params setObject:#"true" forKey:#"stall_warnings"];
//set any other criteria to track
//params setObject:#"words, to, track" forKey#"track"];
// The endpoint that we wish to call
NSURL *url = [NSURL URLWithString:#"https://stream.twitter.com/1.1/statuses/filter.json"];
// Build the request with our parameter
TWRequest *request = [[TWRequest alloc] initWithURL:url
parameters:params
requestMethod:TWRequestMethodPOST];
// Attach the account object to this request
[request setAccount:account];
NSURLRequest *signedReq = request.signedURLRequest;
// make the connection, ensuring that it is made on the main runloop
self.twitterConnection = [[NSURLConnection alloc] initWithRequest:signedReq delegate:self startImmediately: NO];
[self.twitterConnection scheduleInRunLoop:[NSRunLoop mainRunLoop]
forMode:NSDefaultRunLoopMode];
[self.twitterConnection start];
} // if ([twitterAccounts count] > 0)
} // if (granted)
}];

How do I use basic auth with RestKit's getObject?

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;

Resources