Merge pull request #226 from brianantonelli/master

Twitter iOS 5 Integration
This commit is contained in:
Max Ogden
2011-11-25 16:26:42 -08:00
9 changed files with 426 additions and 0 deletions

19
iPhone/Twitter/LICENSE.md Normal file
View File

@@ -0,0 +1,19 @@
Copyright (c) 2011 Brian Antonelli
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.

43
iPhone/Twitter/README.md Normal file
View File

@@ -0,0 +1,43 @@
PhoneGap Twitter Plugin
================================
The Twitter plugin for PhoneGap allows you to take advantage of the Twitter integration that ships with iOS 5. Please note that to use the APIs you must compile the application on the iOS 5 SDK. The plugin will not cause any issues if it is run on a pre-iOS 5 OS as long as you always validate that the Twitter SDK is available (see **Twitter.isTwitterAvailable()**)
This is licensed under MIT.
Getting Started
===============
Download the latest version of PhoneGap from www.phonegap.com.
Create an iOS PhoneGap project (Android not yet supported)
Check out the /example/www/index.html to see how it works.
<pre>
|-native
| |-ios
`-www
`-TwitterPlugin.js
</pre>
/native/ios is the native code for the plugin on ios.
/www/TwitterPlugin.js is the JavaScript code for the plugin
iOS (Mac OS X)
===============
1. Create a basic PhoneGap iOS application. See http://www.phonegap.com/start/#ios-x4
2. From the **PhoneGap Facebook Connect Plugin** folder copy the contents of the **native/ios** folder into your app in Xcode (usually in the **Plugins** folder group). Make sure it is added as a "group" (yellow folder)
3. Find the PhoneGap.plist file in the project navigator, expand the "Plugins" sub-tree, and add a new entry. For the key, add **com.phonegap.twitter**, and its value will be **TwitterPlugin**
4. From the **PhoneGap Twitter Plugin** folder copy the contents of the **www** folder into the **www** directory in Xcode (don't forget to add script tags in your index.html to reference any .js files copied over)
5. Click on your project's icon (the root element) in Project Navigator, select your **Target**, and the **Build Phases** tab.
6. From the **Build Phases** tab, expand **Link Binary With Libraries**, then click on the **+** button
7. Select **Twitter.framework** and click Add
8. for Xcode 4, you will need to build it once, and heed the warning - this is an Xcode 4 template limitation. The warning instructions will tell you to drag copy the **www** folder into the project in Xcode (add as a **folder reference** which is a blue folder).
9. If you wish to allow users to share URLs and/or images you need to add a whitelist wildcard since you don't know which domains they'll reference. Simply add a wildcard entry (*) to external hosts whitelist (PhoneGap.plist/ExternalHosts).
10. Run the application in Xcode.
If you have issues with the app crashing on iOS Simulator you may have a weak linking issue. For more information see: http://stackoverflow.com/questions/6738858/use-of-blocks-crashes-app-in-iphone-simulator-4-3-xcode-4-2-and-4-0-2

View File

@@ -0,0 +1,59 @@
function onBodyLoad(){
document.addEventListener("deviceready", onDeviceReady, false);
}
function onDeviceReady(){
console.log("onDeviceReady");
TwitterDemo.setup();
}
TwitterDemo = {
$:function(id){
return document.getElementById(id);
},
log:function(s){
TwitterDemo.$("log").innerHTML = s;
},
setup:function(){
var tests = ["isAvailable", "isSetup", "tweet", "timeline", "mentions"];
for(var i=0, l=tests.length; i<l; i++){
this.$(tests[i]).onclick = this[tests[i]];
}
},
isAvailable:function(){
window.plugins.twitter.isTwitterAvailable(function(r){
TwitterDemo.log("twitter available? " + r);
});
},
isSetup:function(){
window.plugins.twitter.isTwitterSetup(function(r){
TwitterDemo.log("twitter configured? " + r);
});
},
tweet:function(){
window.plugins.twitter.sendTweet(
function(s){ TwitterDemo.log("tweet success"); },
function(e){ TwitterDemo.log("tweet failure: " + e); },
"Tweety Poo",
"https://github.com/brianantonelli",
"http://zomgdinosaurs.com/zomg.jpg");
},
timeline:function(){
window.plugins.twitter.getPublicTimeline(
function(s){ TwitterDemo.log("timeline success: " + JSON.stringify(s)); },
function(e){ TwitterDemo.log("timeline failure: " + e); });
},
mentions:function(){
window.plugins.twitter.getMentions(
function(s){ TwitterDemo.log("mentions success: " + JSON.stringify(s)); },
function(e){ TwitterDemo.log("mentions failure: " + e); });
}
};

View File

@@ -0,0 +1,26 @@
<!DOCTYPE html>
<html>
<head>
<title></title>
<meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no;" />
<meta charset="utf-8">
<script type="text/javascript" charset="utf-8" src="phonegap-1.1.0.js"></script>
<script type="text/javascript" charset="utf-8" src="TwitterPlugin.js"></script>
<script type="text/javascript" charset="utf-8" src="demo.js"></script>
</head>
<body onload="onBodyLoad()">
<h1>Hey, it's Twitter on PhoneGap!</h1>
<br />
<ol>
<li><a href="#" id="isAvailable">isAvailable</a></li>
<li><a href="#" id="isSetup">isSetup</a></li>
<li><a href="#" id="tweet">tweet</a></li>
<li><a href="#" id="timeline">timeline</a></li>
<li><a href="#" id="mentions">mentions</a></li>
</ol>
<div id="log" style="width:100%;height:200px;overflow:auto;background-color:#000;color:yellow;"></div>
</body>
</html>

22
iPhone/Twitter/install Normal file
View File

@@ -0,0 +1,22 @@
#!/usr/bin/env ruby
def replace_in_file(filepath, regexp, *args, &block)
content = File.read(filepath).gsub(regexp, *args, &block)
File.open(filepath, 'wb') { |file| file.write(content) }
end
file = File.expand_path(ARGV[0])
platform = ( File.extension(file) == ".plist" ? "ios" : "android" )
if (platform == "ios")
replace_in_file(file, /\s*<key>com.phonegap.twitter<\/key>\n/mi) do |match|
""
end
replace_in_file(file, /\s*<string>TwitterPlugin<\/string>\n/mi) do |match|
""
end
replace_in_file(file, /<key>Plugins<\/key>\n\s*<dict>/mi) do |match|
"<key>Plugins</key>\n\t<dict>\n\t\t<key>com.phonegap.twitter</key>\n\t\t<string>TwitterPlugin</string>"
end
elsif (platform == "android")
end

View File

@@ -0,0 +1,32 @@
//
// TwitterPlugin.h
// TwitterPlugin
//
// Created by Antonelli Brian on 10/13/11.
//
#import <Foundation/Foundation.h>
#import <Twitter/Twitter.h>
#import <Accounts/Accounts.h>
#ifdef PHONEGAP_FRAMEWORK
#import <PhoneGap/PGPlugin.h>
#else
#import "PGPlugin.h"
#endif
@interface TwitterPlugin : PGPlugin{
}
- (void) isTwitterAvailable:(NSMutableArray*)arguments withDict:(NSMutableDictionary*)options;
- (void) isTwitterSetup:(NSMutableArray*)arguments withDict:(NSMutableDictionary*)options;
- (void) sendTweet:(NSMutableArray*)arguments withDict:(NSMutableDictionary*)options;
- (void) getPublicTimeline:(NSMutableArray*)arguments withDict:(NSMutableDictionary*)options;
- (void) getMentions:(NSMutableArray*)arguments withDict:(NSMutableDictionary*)options;
- (void) performCallbackOnMainThreadforJS:(NSString*)js;
@end

View File

@@ -0,0 +1,181 @@
//
// TwitterPlugin.m
// TwitterPlugin
//
// Created by Antonelli Brian on 10/13/11.
//
#import "TwitterPlugin.h"
#ifdef PHONEGAP_FRAMEWORK
#import <PhoneGap/JSON.h>
#else
#import "JSON.h"
#endif
#define TWITTER_URL @"http://api.twitter.com/1/"
@implementation TwitterPlugin
- (void) isTwitterAvailable:(NSMutableArray*)arguments withDict:(NSMutableDictionary*)options{
NSString *callbackId = [arguments objectAtIndex:0];
TWTweetComposeViewController *tweetViewController = [[TWTweetComposeViewController alloc] init];
BOOL twitterSDKAvailable = tweetViewController != nil;
// http://brianistech.wordpress.com/2011/10/13/ios-5-twitter-integration/
if(tweetViewController != nil){
[tweetViewController release];
}
[super writeJavascript:[[PluginResult resultWithStatus:PGCommandStatus_OK messageAsInt:twitterSDKAvailable ? 1 : 0] toSuccessCallbackString:callbackId]];
}
- (void) isTwitterSetup:(NSMutableArray*)arguments withDict:(NSMutableDictionary*)options{
NSString *callbackId = [arguments objectAtIndex:0];
BOOL canTweet = [TWTweetComposeViewController canSendTweet];
[super writeJavascript:[[PluginResult resultWithStatus:PGCommandStatus_OK messageAsInt:canTweet ? 1 : 0] toSuccessCallbackString:callbackId]];
}
- (void) sendTweet:(NSMutableArray*)arguments withDict:(NSMutableDictionary*)options{
// arguments: callback, tweet text, url attachment, image attachment
NSString *callbackId = [arguments objectAtIndex:0];
NSString *tweetText = [arguments objectAtIndex:1];
NSString *urlAttach = [arguments objectAtIndex:2];
NSString *imageAttach = [arguments objectAtIndex:3];
TWTweetComposeViewController *tweetViewController = [[TWTweetComposeViewController alloc] init];
[tweetViewController setInitialText:tweetText];
BOOL ok = YES;
NSString *errorMessage;
if(urlAttach != nil){
ok = [tweetViewController addURL:[NSURL URLWithString:urlAttach]];
if(!ok){
errorMessage = @"URL too long";
}
}
if(imageAttach != nil){
// Note that the image is loaded syncronously
UIImage *img = [[UIImage alloc] initWithData:[NSData dataWithContentsOfURL:[NSURL URLWithString:imageAttach]]];
ok = [tweetViewController addImage:img];
if(!ok){
errorMessage = @"Image could not be added";
}
}
if(!ok){
[super writeJavascript:[[PluginResult resultWithStatus:PGCommandStatus_ERROR
messageAsString:errorMessage] toErrorCallbackString:callbackId]];
}
else{
[tweetViewController setCompletionHandler:^(TWTweetComposeViewControllerResult result) {
switch (result) {
case TWTweetComposeViewControllerResultDone:
[super writeJavascript:[[PluginResult resultWithStatus:PGCommandStatus_OK] toSuccessCallbackString:callbackId]];
break;
case TWTweetComposeViewControllerResultCancelled:
default:
[super writeJavascript:[[PluginResult resultWithStatus:PGCommandStatus_ERROR
messageAsString:@"Cancelled"] toErrorCallbackString:callbackId]];
break;
}
[super.appViewController dismissModalViewControllerAnimated:YES];
}];
[super.appViewController presentModalViewController:tweetViewController animated:YES];
}
[tweetViewController release];
}
- (void) getPublicTimeline:(NSMutableArray*)arguments withDict:(NSMutableDictionary*)options{
NSString *callbackId = [arguments objectAtIndex:0];
NSString *url = [NSString stringWithFormat:@"%@statuses/public_timeline.json", TWITTER_URL];
TWRequest *postRequest = [[TWRequest alloc] initWithURL:[NSURL URLWithString:url] parameters:nil requestMethod:TWRequestMethodGET];
[postRequest performRequestWithHandler:^(NSData *responseData, NSHTTPURLResponse *urlResponse, NSError *error) {
NSString *jsResponse;
if([urlResponse statusCode] == 200) {
NSString *dataString = [[NSString alloc] initWithData:responseData encoding:NSUTF8StringEncoding];
NSDictionary *dict = [dataString JSONValue];
jsResponse = [[PluginResult resultWithStatus:PGCommandStatus_OK messageAsDictionary:dict] toSuccessCallbackString:callbackId];
[dataString release];
}
else{
jsResponse = [[PluginResult resultWithStatus:PGCommandStatus_ERROR
messageAsString:[NSString stringWithFormat:@"HTTP Error: %i", [urlResponse statusCode]]]
toErrorCallbackString:callbackId];
}
[self performCallbackOnMainThreadforJS:jsResponse];
}];
[postRequest release];
}
- (void) getMentions:(NSMutableArray*)arguments withDict:(NSMutableDictionary*)options{
NSString *callbackId = [arguments objectAtIndex:0];
NSString *url = [NSString stringWithFormat:@"%@statuses/mentions.json", TWITTER_URL];
ACAccountStore *accountStore = [[ACAccountStore alloc] init];
ACAccountType *accountType = [accountStore accountTypeWithAccountTypeIdentifier:ACAccountTypeIdentifierTwitter];
[accountStore requestAccessToAccountsWithType:accountType withCompletionHandler:^(BOOL granted, NSError *error) {
if(granted) {
NSArray *accountsArray = [accountStore accountsWithAccountType:accountType];
// making assumption they only have one twitter account configured, should probably revist
if([accountsArray count] > 0) {
TWRequest *postRequest = [[TWRequest alloc] initWithURL:[NSURL URLWithString:url] parameters:nil requestMethod:TWRequestMethodGET];
[postRequest setAccount:[accountsArray objectAtIndex:0]];
[postRequest performRequestWithHandler:^(NSData *responseData, NSHTTPURLResponse *urlResponse, NSError *error) {
NSString *jsResponse;
if([urlResponse statusCode] == 200) {
NSString *dataString = [[NSString alloc] initWithData:responseData encoding:NSUTF8StringEncoding];
NSDictionary *dict = [dataString JSONValue];
jsResponse = [[PluginResult resultWithStatus:PGCommandStatus_OK messageAsDictionary:dict] toSuccessCallbackString:callbackId];
[dataString release];
}
else{
jsResponse = [[PluginResult resultWithStatus:PGCommandStatus_ERROR
messageAsString:[NSString stringWithFormat:@"HTTP Error: %i", [urlResponse statusCode]]]
toErrorCallbackString:callbackId];
}
[self performCallbackOnMainThreadforJS:jsResponse];
}];
[postRequest release];
}
else{
NSString *jsResponse = [[PluginResult resultWithStatus:PGCommandStatus_ERROR
messageAsString:@"No Twitter accounts available"]
toErrorCallbackString:callbackId];
[self performCallbackOnMainThreadforJS:jsResponse];
}
}
else{
NSString *jsResponse = [[PluginResult resultWithStatus:PGCommandStatus_ERROR
messageAsString:@"Access to Twitter accounts denied by user"]
toErrorCallbackString:callbackId];
[self performCallbackOnMainThreadforJS:jsResponse];
}
}];
[accountStore release];
}
// The JS must run on the main thread because you can't make a uikit call (uiwebview) from another thread (what twitter does for calls)
- (void) performCallbackOnMainThreadforJS:(NSString*)javascript{
[super performSelectorOnMainThread:@selector(writeJavascript:)
withObject:javascript
waitUntilDone:YES];
}
@end

View File

@@ -0,0 +1,15 @@
{
"author": "Brian Antonelli",
"name": "com.phonegap.twitter",
"description": "PhoneGap Twitter Plugin",
"version": "0.0.1",
"repository": {
"type": "git",
"url": "git://github.com/brianantonelli/phonegap-plugin-twitter.git"
},
"engines": {
"phonegap": "1.0.0"
},
"dependencies": {},
"development_dependencies": {}
}

View File

@@ -0,0 +1,29 @@
var Twitter = function(){};
Twitter.prototype.isTwitterAvailable = function(response){
PhoneGap.exec(response, null, "com.phonegap.twitter", "isTwitterAvailable", []);
};
Twitter.prototype.isTwitterSetup = function(response){
PhoneGap.exec(response, null, "com.phonegap.twitter", "isTwitterSetup", []);
};
Twitter.prototype.sendTweet = function(success, failure, tweetText, urlAttach, imageAttach){
if(typeof urlAttach === "undefined") urlAttach = "";
if(typeof imageAttach === "undefined") imageAttach = "";
PhoneGap.exec(success, failure, "com.phonegap.twitter", "sendTweet", [tweetText, urlAttach, imageAttach]);
};
Twitter.prototype.getPublicTimeline = function(success, failure){
PhoneGap.exec(success, failure, "com.phonegap.twitter", "getPublicTimeline", []);
};
Twitter.prototype.getMentions = function(success, failure){
PhoneGap.exec(success, failure, "com.phonegap.twitter", "getMentions", []);
};
PhoneGap.addConstructor(function() {
if(!window.plugins) window.plugins = {};
window.plugins.twitter = new Twitter();
});