Using tweet search with Twitter+OAuth on iPhone - iphone

Originally I needed the ability to use the search API with twitter. I did this using Matt Gemmell's great MGTwitterEngine. That code was very very simple and looked something like this:
- (void)viewDidLoad {
[super viewDidLoad];
tweetArrays = nil;
tweetNameArray = nil;
NSString *username = #"<username>";
NSString *password = #"<password>";
NSString *consumerKey = #"<consumerKey>";
NSString *consumerSecret = #"<consumerSecret>";
// Most API calls require a name and password to be set...
if (! username || ! password || !consumerKey || !consumerSecret) {
NSLog(#"You forgot to specify your username/password/key/secret in AppController.m, things might not work!");
NSLog(#"And if things are mysteriously working without the username/password, it's because NSURLConnection is using a session cookie from another connection.");
}
// Create a TwitterEngine and set our login details.
twitterEngine = [[MGTwitterEngine alloc] initWithDelegate:self];
[twitterEngine setUsesSecureConnection:NO];
[twitterEngine setConsumerKey:consumerKey secret:consumerSecret];
// This has been undepreciated for the purposes of dealing with Lists.
// At present the list API calls require you to specify a user that owns the list.
[twitterEngine setUsername:username];
[twitterEngine getSearchResultsForQuery:#"#HelloWorld" sinceID:0 startingAtPage:1 count:100];
}
This would end up calling the function:
- (void)searchResultsReceived:(NSArray *)searchResults forRequest:(NSString *)connectionIdentifier
And then I could do what I wanted with the searchResults. This required me to include the yajl library.
I then wanted to expand my code to allow users to tweet. I downloaded Ben Gottlieb's great code Twitter-OAuth-iPhone
So there's only one problem. The getSearchResultsForQuery returns a requestFailed with the following error:
Error Domain=HTTP Code=400 "The operation couldn’t be completed. (HTTP error 400.)"
To call this code I simply took the demo project in Twitter-OAuth-iPhone and added a call to getSearchResultsForQuery as seen here:
- (void) viewDidAppear: (BOOL)animated {
if (_engine) return;
_engine = [[SA_OAuthTwitterEngine alloc] initOAuthWithDelegate: self];
_engine.consumerKey = kOAuthConsumerKey;
_engine.consumerSecret = kOAuthConsumerSecret;
UIViewController *controller = [SA_OAuthTwitterController controllerToEnterCredentialsWithTwitterEngine: _engine delegate: self];
if (controller)
[self presentModalViewController: controller animated: YES];
else {
[_engine getSearchResultsForQuery:#"HelloWorld"];
// [_engine sendUpdate: [NSString stringWithFormat: #"Already Updated. %#", [NSDate date]]];
}
}
This as stated above returns a 400 error. Every other twitter API call I add here does work such as:
- (NSString *)getRepliesStartingAtPage:(int)pageNum;
Am I doing anything wrong? Or does getSearchResultsForQuery no longer work? The two code bases seem to use different versions of MGTwitterEngine, could that be causing the problem?
Thanks!

The problem is that you have to instantiate the twitter engine as an instance of SA_OAuthTwitterEngine, instead of MGTwitterEngine. When you call getSearchResultsForQuery, it uses a call to _sendRequestWithMethod to actually send the search request. SA_OAuthTwitterEngine overrides the MGTwitterEngine's implementation, but its version of that function doesn't work with getSearchResultsForQuery.
So, you need to go to getSearchResultsForQuery and make it use the MGTwitterEngine's version of the function.

Related

Twit to twitter using MGTwitterEngine not Working

I have to integrate Twitter API in iPhone(4.2 and above).For this I am using SA_OAuthTwitterEngine + MGTwitterEngine classes.
I register applicaiton to www.twitter.com/apps and pass Consumer key and Consumer secret to controller my code is this.
if(!_engine){
_engine = [[SA_OAuthTwitterEngine alloc] initOAuthWithDelegate:self];
_engine.consumerKey = KCONSKEY;
_engine.consumerSecret = KCONSECKEY;
}
UIViewController *controller = [SA_OAuthTwitterController controllerToEnterCredentialsWithTwitterEngine:_engine delegate:self];
if (controller){
[self presentModalViewController: controller animated: YES];
}
I have seen this and this example also. In both example we can’t able to twit.. Also, in both example, I ‘m getting error:
failed with error: Error Domain=HTTP Code=401 "The operation couldn’t be completed. (HTTP error 401.)”
Can Please, any one give me suggestion how to overcome this problem?
There are changes for API, I guess the MGTwitterEngine is not updated and please do refer the developer site for more information.
http://www.readwriteweb.com/archives/twitter_changes_api_fails_to_notify_developers.php
https://dev.twitter.com/docs
[_engine sendUpdate: [NSString stringWithFormat: #"Already Updated. \n%#", [NSDate date]]];

How can i implement GTMOAuth in iOS

i need to implement GTMOAuth to my iOS app..for this i have downloaded the OAuth Library and write some code
- (GTMOAuthAuthentication *)myCustomAuth {
NSString *myConsumerKey = #"f964039f2d7bc82054"; // pre-registered with service
NSString *myConsumerSecret = #"c9a749c0f1e30c9246a3be7b2586434f"; // pre-assigned by service
GTMOAuthAuthentication *auth;
auth = [[[GTMOAuthAuthentication alloc] initWithSignatureMethod:kGTMOAuthSignatureMethodHMAC_SHA1
consumerKey:myConsumerKey
privateKey:myConsumerSecret] autorelease];
// setting the service name lets us inspect the auth object later to know
// what service it is for
auth.serviceProvider = #"Custom Auth Service";
return auth;
}
- (void)signInToCustomService {
NSURL *requestURL = [NSURL URLWithString:#"http://alpha.easyreceipts.com/api/v1/oauth/request_token"];
NSURL *accessURL = [NSURL URLWithString:#"http://alpha.easyreceipts.com/api/v1/oauth/access_token"];
NSURL *authorizeURL = [NSURL URLWithString:#"http://alpha.easyreceipts.com/api/v1/oauth/authorize"];
NSString *scope = #"http://alpha.easyreceipts.com/api/v1/";
GTMOAuthAuthentication *auth = [self myCustomAuth];
// set the callback URL to which the site should redirect, and for which
// the OAuth controller should look to determine when sign-in has
// finished or been canceled
//
// This URL does not need to be for an actual web page
[auth setCallback:#"http://alpha.easyreceipts.com/api/v1/"];
// Display the autentication view
GTMOAuthViewControllerTouch *viewController;
viewController = [[[GTMOAuthViewControllerTouch alloc] initWithScope:scope
language:nil
requestTokenURL:requestURL
authorizeTokenURL:authorizeURL
accessTokenURL:accessURL
authentication:auth
appServiceName:#"My App: Custom Service"
delegate:self
finishedSelector:#selector(viewController:finishedWithAuth:error:)] autorelease];
viewController = [self.storyboard instantiateViewControllerWithIdentifier:#"myID"];
[[self navigationController] pushViewController:viewController
animated:YES];
}
but further i don't know how to implement it.Please help me Thanks in advance
First, if you want to implement an OAuth authentication system with the GTM library, you should get this one.
You can find the complete sources of GTMOAuth here.
Secondly, you can find an example of how to use it in the git repository too. You should find everything you need to at least authenticate.
If you have any more problem, please provide more informations.

ASIHTTPRequest and ASIWebPageRequest loading local html file error

I'm trying to use the ASIWebPageRequest to load a local html file so that I can use the built-in caching in ASIWebPageRequest. I know this might sound a bit pointless however I want to use remote images in my local files that only get updated once a week or so. If that makes sense.
Here is what I'm doing in code:
//OUTPUT: file:///Users/ledixonuk/Library/Application%20Support/iPhone%20Simulator/4.3.2/Applications/FDF21331-CC80-4ECB-9A33-16AEC073D117/Documents/ItemOne.html
NSURL *fileUrl = [[NSURL alloc] initFileURLWithPath:[FileSystemHelper dataFilePathInDocuments:#"ItemOne.html"]];
// Assume request is a property of our controller
// First, we'll cancel any in-progress page load
[[self webRequest] setDelegate:nil];
[[self webRequest] cancel];
[self setWebRequest:[ASIWebPageRequest requestWithURL:fileUrl]];
[[self webRequest] setDelegate:self];
[[self webRequest] setDidFailSelector:#selector(webPageFailed:)];
[[self webRequest] setDidFinishSelector:#selector(webPageFinished:)];
// Tell the request to replace urls in this page with local urls
//[[self webRequest] setUrlReplacementMode:ASIReplaceExternalResourcesWithLocalURLs];
// Tell the request to embed external resources directly in the page
[[self webRequest] setUrlReplacementMode:ASIReplaceExternalResourcesWithData];
// It is strongly recommended you use a download cache with ASIWebPageRequest
// When using a cache, external resources are automatically stored in the cache
// and can be pulled from the cache on subsequent page loads
[[self webRequest] setDownloadCache:[ASIDownloadCache sharedCache]];
// Ask the download cache for a place to store the cached data
// This is the most efficient way for an ASIWebPageRequest to store a web page
[[self webRequest] setDownloadDestinationPath:[[ASIDownloadCache sharedCache] pathToStoreCachedResponseDataForRequest:[self webRequest]]];
[[self webRequest] startAsynchronous];
And here is the error I'm getting:
404 Not Found
Not Found
The requested URL /Users/ledixonuk/Library/Application Support/iPhone Simulator/4.3.2/Applications/FDF21331-CC80-4ECB-9A33-16AEC073D117/Documents/ItemOne.html was > not found on this server.
Obviously the ASIWebPageRequest can not find the ItemOne.html file in the application document folder, however I've double checked and the file is definitely there.
Has anyone else had a similar issue to this? It's driving me mad trying to sort it out!
ASIWebPageRequest is a subclass of ASIHTTPRequest, and unfortunately ASIHTTPRequest doesn't support fetching file: URLs - only http and https ones.
Basically you have all the code you need, you just need to find a way to plumb it together. You could create a subclass of ASIHTTPRequest that can load file: URLs (essentially provide your own implement of startAsyncronous that instead just calls back the delegates after setting up the right data files), or create a subclass of ASIWebPageRequest that does it. I've not yet thought about it in detail so I have no idea which way would be best.
(I think you're seeing the 404 page as ASIHTTPRequest is managing to contact a web server somewhere - perhaps it's trying to fetch from http://127.0.0.1/Users/ledixonuk/Library/....)
See below the simple implementation I have added to my subclass of ASIHTTPRequest in order to support file:// based URLs. It's by no means complete and possibly missing some properties that should be set and it will not invoke all the delegates but for my own purposes this was enough.
- (void)startRequest
{
if ([url isFileURL])
{
// ASIHTTPRequest does not support handling file:// URLs, this is my own simple implementation here
if ([self isCancelled]) {
return;
}
[self performSelectorOnMainThread:#selector(requestStarted) withObject:nil waitUntilDone:[NSThread isMainThread]];
NSFileManager *fileManager = [[[NSFileManager alloc] init] autorelease];
NSString *filePath = [url path];
BOOL isDirectory = NO;
if ([fileManager fileExistsAtPath:filePath isDirectory:&isDirectory] && !isDirectory)
{
responseStatusCode = 200;
[self setRawResponseData:[NSData dataWithContentsOfFile:filePath]];
[self setContentLength:rawResponseData.length];
[self setTotalBytesRead:[self contentLength]];
}
else
{
responseStatusCode = 404;
[self setContentLength:0];
[self setError:[NSError errorWithDomain:NetworkRequestErrorDomain code:ASIFileManagementError userInfo:[NSDictionary dictionaryWithObjectsAndKeys:[NSString stringWithFormat:#"Cannot open file at path '%#'",filePath],NSLocalizedDescriptionKey,error,NSUnderlyingErrorKey,nil]]];
}
complete = YES;
downloadComplete = YES;
[self requestFinished];
[self markAsFinished];
}
else
{
// let the original implementation deal with all the other URLs
[super startRequest];
}
}

#selector() not working? youtube API GData issues

I'm trying to integrate GData/YouTubeAPI into my project but I'm a bit stuck. I used the code that was given in this post:
Any examples/tutorials on using Google GData API - Youtube on iphone?
My problem is that the function 'request:finishedWithFeed:error:' is not called when the GData service is done grabbing data off the interwebs.
My program compiles and runs – and no runtime errors present themselves. I also checked and feedURL returns a valid URL. I debugged this and 'service' does manage to return a value.
GDataServiceGoogleYouTube *service = [self youTubeService];
// feed id for user uploads
NSString *uploadsID = kGDataYouTubeUserFeedIDUploads;
// construct the feed url
NSURL *feedURL = [GDataServiceGoogleYouTube youTubeURLForUserID:USER_NAME userFeedID:uploadsID];
// make API call
[service fetchFeedWithURL:feedURL delegate:self didFinishSelector:#selector(request:finishedWithFeed:error:)];
This is the header for the function I'm trying to get service to call when its done, it's located in the same object as the other code i've posted
- (void)request:(GDataServiceTicket *)ticket finishedWithFeed:(GDataFeedBase *)aFeed error:(NSError *)error;
SOLUTION --
make sure its running on main thread --
-(id)initWithVideosArrayURLString:(NSString*)url {
if (self = [super init]) {
NSLog(#"loading");
[self performSelectorOnMainThread:#selector(initMainThreadWithURLString:) withObject:url waitUntilDone:NO];
}
return self;
}
-(void)initMainThreadWithURLString:(NSString*)url {
GDataServiceGoogleYouTube *service = [self youTubeService];
NSString *uploadsID = kGDataYouTubeUserFeedIDUploads;
NSURL *feedURL = [GDataServiceGoogleYouTube youTubeURLForUserID:#"annoyingorange" userFeedID:uploadsID];
[service fetchFeedWithURL:feedURL delegate:self didFinishSelector:#selector(serviceTicket:finishedWithFeed:error:)];
NSLog(#"sent");
}
- (void)serviceTicket:(GDataServiceTicket *)ticket finishedWithFeed:(GDataFeedBase *)aFeed error:(NSError *)error {
self.feed = (GDataFeedYouTubeVideo *)aFeed;
NSLog(#"success.");
}
Is your code on the main thread, and returning to the run loop after initiating the fetch? Callbacks occur only when the app's run loop is spinning.

Twitter + oAuth nightmare

I'm trying to implement a custom login view to Twitter (I don't want that UIWebView).
I've downloaded many classes, and I'm so far having a nightmare with this. Now I'm trying to make Twitter + oAuth work. Here's the demo code (that works):
_engine = [[SA_OAuthTwitterEngine alloc] initOAuthWithDelegate: self];
_engine.consumerKey = kOAuthConsumerKey;
_engine.consumerSecret = kOAuthConsumerSecret;
[_engine requestRequestToken];
UIViewController *controller = [SA_OAuthTwitterController controllerToEnterCredentialsWithTwitterEngine: _engine delegate: self];
if (controller)
[self presentModalViewController: controller animated: YES];
else
[_engine sendUpdate: [NSString stringWithFormat: #"Already Updated. %#", [NSDate date]]];
Now what I wanna do is replace that SA_OAuthTwitterController with custom UITextFields. So I'm trying this:
_engine = [[SA_OAuthTwitterEngine alloc] initOAuthWithDelegate: self];
_engine.consumerKey = kOAuthConsumerKey;
_engine.consumerSecret = kOAuthConsumerSecret;
[_engine requestRequestToken];
[_engine requestAccessToken];
[_engine setUsername:#"username" password:#"password"];
[_engine sendUpdate:#"tweet"];
But I keep getting error 401. I'm probably missing a step. Anyone?
The view controller you are eliminating loads a webview that prompts the user to authenticate your application with twitter. If you don't ever have the user authenticate, you won't be able to make authenticated twitter calls. There is a way to do what you are wanting, however, you have to go through all of the OAuth steps yourself. Here are the steps as specified from a different web service (Vimeo), but the same rules apply:
Your application sends a request
with your consumer key, and signed
with your consumer secret, for a
something called a request token. If
we verify your application
correctly, we'll send you back a
request token and a token secret.
You'll then create a link for the user to click on with the request token.
When the user gets to Vimeo, they'll be prompted to allow your
application access to their account.
If they click yes, we'll send them
back to your application, along with
a verifier.
You'll then use the request token, verifier, and token secret to make
another call to us to get an access
token. The access token is what
you'll use to access the user's
information on Vimeo.
OAuth is a real pain in the rump, so good luck. ;-)
I think the bit you're missing is here, SA_OAuthTwitterEngine.m:103:
//This generates a URL request that can be passed to a UIWebView. It will open a page in which the user must enter their Twitter creds to validate
- (NSURLRequest *) authorizeURLRequest {
if (!_requestToken.key && _requestToken.secret) return nil; // we need a valid request token to generate the URL
OAMutableURLRequest *request = [[[OAMutableURLRequest alloc] initWithURL: self.authorizeURL consumer: nil token: _requestToken realm: nil signatureProvider: nil] autorelease];
[request setParameters: [NSArray arrayWithObject: [[[OARequestParameter alloc] initWithName: #"oauth_token" value: _requestToken.key] autorelease]]];
return request;
}
You will have to "fake" this user login out yourself, I believe by sending twitter the login credentials as a separate request. It appears that the setUsername method you're calling is actually called as a post operation once a valid access token has been received. See SA_OAuthTwitterEngine.m:185
//
// access token callback
// when twitter sends us an access token this callback will fire
// we store it in our ivar as well as writing it to the keychain
//
- (void) setAccessToken: (OAServiceTicket *) ticket withData: (NSData *) data {
if (!ticket.didSucceed || !data) return;
NSString *dataString = [[[NSString alloc] initWithData: data encoding: NSUTF8StringEncoding] autorelease];
if (!dataString) return;
if (self.pin.length && [dataString rangeOfString: #"oauth_verifier"].location == NSNotFound) dataString = [dataString stringByAppendingFormat: #"&oauth_verifier=%#", self.pin];
NSString *username = [self extractUsernameFromHTTPBody:dataString];
if (username.length > 0) {
[[self class] setUsername: username password: nil];
if ([_delegate respondsToSelector: #selector(storeCachedTwitterOAuthData:forUsername:)]) [(id) _delegate storeCachedTwitterOAuthData: dataString forUsername: username];
}
[_accessToken release];
_accessToken = [[OAToken alloc] initWithHTTPResponseBody:dataString];
}
So the steps are as follows:
Request request token
Fake user login to twitter and get the pin value
Set the pin on the engine
Request access token
You can see where SA_OAuthTwitterController parses the pin out of the webview content in SA_OAuthTwitterController.m:156
#pragma mark Webview Delegate stuff
- (void) webViewDidFinishLoad: (UIWebView *) webView {
NSError *error;
NSString *path = [[NSBundle mainBundle] pathForResource: #"jQueryInject" ofType: #"txt"];
NSString *dataSource = [NSString stringWithContentsOfFile:path encoding:NSUTF8StringEncoding error:&error];
if (dataSource == nil) {
NSLog(#"An error occured while processing the jQueryInject file");
}
[_webView stringByEvaluatingJavaScriptFromString:dataSource]; //This line injects the jQuery to make it look better
NSString *authPin = [[_webView stringByEvaluatingJavaScriptFromString: #"document.getElementById('oauth_pin').innerHTML"] stringByTrimmingCharactersInSet: [NSCharacterSet whitespaceAndNewlineCharacterSet]];
if (authPin.length == 0) authPin = [[_webView stringByEvaluatingJavaScriptFromString: #"document.getElementById('oauth_pin').getElementsByTagName('a')[0].innerHTML"] stringByTrimmingCharactersInSet: [NSCharacterSet whitespaceAndNewlineCharacterSet]];
[_activityIndicator stopAnimating];
if (authPin.length) {
[self gotPin: authPin];
}
if ([_webView isLoading] || authPin.length) {
[_webView setHidden:YES];
} else {
[_webView setHidden:NO];
}
}
hope this helps.
You have to use xAuth for custom user/pass controls. But it requires the permission from Twitter.