Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with
or
.
Download ZIP
A simple, ARC and blocks-based Twitter engine for Cocoa and Cocoa Touch. Supports Twitter's streaming (via user streams) & image service as well as Tweet Marker.
Objective-C C
branch: master

Fetching latest commit…

Cannot retrieve the latest commit at this time

Failed to load latest commit information.
CLDeletedTweet.h
CLDeletedTweet.m
CLDirectMessage.h
CLDirectMessage.m
CLFriendList.h
CLFriendList.m
CLNetworkUsageController.h
CLNetworkUsageController.m
CLReflection.h
CLReflection.m
CLTweet.h
CLTweet.m
CLTweetJSONStrings.h
CLTweetMarker.h
CLTweetMarker.m
CLTweetMedia.h
CLTweetMedia.m
CLTwitterConfiguration.h
CLTwitterConfiguration.m
CLTwitterEndpoints.h
CLTwitterEngine.h
CLTwitterEngine.m
CLTwitterEntity.h
CLTwitterEvent.h
CLTwitterEvent.m
CLTwitterList.h
CLTwitterList.m
CLTwitterPhotoLimit.h
CLTwitterPhotoLimit.m
CLTwitterPhotoSize.h
CLTwitterPhotoSize.m
CLTwitterRateLimitStatus.h
CLTwitterRateLimitStatus.m
CLTwitterSavedSearch.h
CLTwitterSavedSearch.m
CLTwitterSearch.h
CLTwitterSearch.m
CLTwitterUser.h
CLTwitterUser.m
CLTwitterUserTotals.h
CLTwitterUserTotals.m
NSDictionary+UrlEncoding.h
NSDictionary+UrlEncoding.m
NSURLConnection+Blocks.h
NSURLConnection+Blocks.m
README.md

README.md

CLTwitterEngine

A simple, ARC and blocks-based Twitter engine for Cocoa and CocoaTouch. CLTwitterEngine currently supports both TweetMarker and Twitter's image upload service. There is also very basic support for Twitter's user stream; this portion is under development.

Basics

CLTwitterEngine is designed with the following tenets:

  • Calls to and from Twitter should be asynchronous.
  • Callbacks will favor blocks, rather than protocols and delegates.
  • Clients of the library should be able to provide their own means of signing requests with OAuth information.
  • Clients of the library should be able to provide a JSON parser of their choice. This is to allow use on older versions of Mac OS X and iOS, which do not have NSJSONSerialization.
  • Objects should know how to get or post themselves, whenever possible. For example, the CLTweet class is capable of getting a tweet or posting one.
  • The API should be as simple as possible.
  • Yes, there are other frameworks, but sometimes you just want to roll your own. For the hell of it.

API Coverage

At some point, the entire Twitter API may be covered. However, the below are the API calls that are intended to be implemented.

Timeline

☑ Get home timeline
☑ Get mentions
☑ Get retweets of me
☑ Get user's timeline

Tweets

☑ Get users whom retweeted a tweet
☑ Get retweets of a tweet
☑ Get single tweet
☑ Delete tweet
☑ Post retweet of a tweet
☑ Post tweet
☑ Post tweet with an image
☑ Post tweet in reply to another tweet
☑ Post tweet with image in reply to another tweet

Search

☑ Get search results

Saved searches

☑ Get saved searches
☑ Get single saved search
☑ Create a saved search
☑ Delete a saved search

Direct Messages

☑ Get messages to me
☑ Get messages by me
☑ Delete a message
☑ Post a message
☑ Get single message

Followers

☑ Get my followers
☑ Get my followees
☑ Get incoming follow requests
☑ Get outgoing follow requests
☑ Follow a user
☑ Unfollow a user

Users

☑ Get users in bulk
☑ Get a user's profile image
☑ Get search results for a user search
☑ Get a user

Favorites

☑ Get my favorites
☑ Get user's favorites
☑ Create a favorite
☑ Remove a favorite

Account

☑ Get rate limit status
☑ Post update to account
☑ Post update of profile image
☑ Get account totals

Block

☑ Get all my blocked users
☑ Am I blocking a user?
☑ Create a new block
☑ Unblock a user

Spam

☑ Report spam

Help

☑ Get configuration

Lists

☑ Get all your lists
☑ Get a user's lists
☑ Get a list by ID
☑ Subscribe to a list
☑ Unsubscribe from a list
☑ Get list members
☑ Get list subscribers
☑ Add a user to a list
☑ Remove a user from a list
☑ Create a list
☑ Delete a list
☑ Update a list
☑ Get a user's list subscriptions

Requirements

Requires GTMHttpFetcher.

Because you are providing CLTwitterEngine with a block of code that will sign OAuth requests (see below), to sign into Twitter using OAuth is entirely your responsibility. There are frameworks which will handle this exchange for you; CLTwitterEngine used GTMOauthAuthentication during development.

Initialization

To initialize:

  • You must specify a way to convert JSON data to Foundation objects; the assumed way of doing so is using NSJSONSerialization, though presumably any framework will do, as long as NSDictionarys and NSArrays are returned where appropriate.
  • You must specify a way to add OAuth authorization to a NSMutableURLRequest. Again, any framework should work, though GTMOauthAuthentication was used during development.

Example:

[[CLTwitterEngine sharedEngine] setAuthorizer:^(NSMutableURLRequest *request)
{
    // Your authorizing code here
}];
[[CLTwitterEngine sharedEngine] setConverter:^(NSData *data)
{
    // Your code here
}];

Sample Initialization

For illustrative purposes, here are the authorizer and converter that are being used to test CLTwitterEngine:

[[CLTwitterEngine sharedEngine] setAuthorizer:^(NSMutableURLRequest *request)
 {
     // ivar: GTMOAuthAuthentication *_auth;
     [_auth authorizeRequest:request];
 }];
[[CLTwitterEngine sharedEngine] setConverter:^(NSData *data)
{
    NSError *error;
    return [NSJSONSerialization JSONObjectWithData:data 
                                           options:kNilOptions
                                             error:&error];
}];

Optional Step: Set up for Network Access Callbacks

Optionally, you can set up a handler for when network access begins or ceases. This is using the class CLNetworkUsageController. For example:

[[CLNetworkUsageController sharedController] setCallback:^(BOOL isAccessing)
 {
     NSLog(@"%@ using network.", isAccessing ? @"BEGIN" : @"END"); 
 }];

Use

Subsequent to initialization above, the engine is ready to use. You can get the timeline by:

[[CLTwitterEngine sharedEngine] getTimeLineWithCompletionHandler:^(NSArray *timeline, NSError *error) {
    if (error)
    {
        // Handle the error.
    }
    else
    {
        // The timeline is ready for use.  It is a NSArray of CLTweet objects.
    }
}];

Note that the general approach is the same for getMentionsWithCompletionHandler.

Using the streaming API

Also note that user streams are supported in a limited fashion. Currently working:

  • New tweets
  • Favorites
  • Deleted tweets
  • Received direct messages

Twitter sends various entities across the pipe when streaming is enabled. For example, when a new tweet is received, it will send that tweet via the streaming connection. However, in some instances, such as a new favorite, it will send an event. In the case of things such as tweets, a CLTweet object will be passed along. In the case of an event, a CLTwitterEvent object will be passed.

To engage streaming:

[[CLTwitterEngine sharedEngine] startStreamingWithTweetHandler:^(id entity) {
            NSLog(@"Got an entity: %@", entity);
        }];

As discussed above, an entity will be passed on, that conforms to the protocol CLTwitterEntity.

Using the non-streaming API

Posting Tweets

You can post a text-only tweet:

[CLTweet postTweet:@"Look at me; I'm tweeting!" completionHandler:^(CLTweet *tweet, NSError *error) {
    if (error)
    {
        // Handle the error.
    }
    else
    {
        // Your tweet has been posted; the result is provided for convenience.
    }
}];

You can post a tweet with an image:

[CLTweet postTweet:@"Some clever message." withImage:/*Some_nifty_NSImage*/ completionHandler:^(CLTweet *tweet, NSError *error) {
    if (error)
    {
        // Handle the error.
    }
    else
    {
        // The tweet and image have been posted; the result is provided for convenience.
    }
}];

To reply to a tweet, use the original tweet:

// Local variable: CLTweet *tweet
[tweet postReply:@"@sedgeapp this is an awesome tweet!" withCompletionHandler:^(CLTweet *tweet, NSError *innerError) {
    if (error)
    {
        // Handle the error.
    }
    else
    {
        // The tweet has been posted; the result is provided for convenience.
    }
}];

To reply to a tweet, including an image:

[CLTweet postTweet:@"This is a test image upload."
         withImage:[[NSImage alloc] initWithContentsOfFile:@"/Users/casey/Desktop/v-tech.png"]
 completionHandler:^(CLTweet *tweet, NSError *error) {
     if (error)
     {
        // Handle the error.
     }
     else
     {
        // The tweet has been posted; the result is provided for convenience.
     }
}];

Direct Messages

You can get recent direct messages (both sent and received are in this same array):

[[CLTwitterEngine sharedEngine] getRecentDirectMessagesWithCompletionHandler:^(NSArray *messages, NSError *error) {
    if (error)
    {
        // Hanlde the error.
    }
    else
    {
        // The direct messages (sent and received) are ready for use.
        // It is a NSArray of CLDirectMessage objects.
    }
}];

Or perhaps just an individual direct message:

[CLDirectMessage getDirectMessageWithId:[NSNumber numberWithLongLong:123456789012341234] 
                      completionHandler:^(CLDirectMessage *message, NSError *error) {
    if (error)
    {
        // Handle the error.
    }
    else
    {
        // The direct messages is provided.
    }
}];

To send a direct message:

[CLDirectMessage postDirectMessageToScreenName:@"SedgeApp" 
                                      withBody:@"This is a test DM." 
                             completionHandler:^(CLDirectMessage *message, NSError *error) {
                                 if (error)
                                 {
                                     // Handle error.
                                 }
                                 else 
                                 {
                                     // New direct message is provided.
                                 }
                             }];

Users

User objects are arguably the primary entity in the Twitter world. They are used in many ways.

Getting Users

You can get a user by handle/screen name:

[CLTwitterUser getUserWithScreenName:@"SedgeApp" completionHandler:^(CLTwitterUser *user, NSError *error) {
    if (error)
    {
        // Handle error.
    }
    else
    {
        // The user is provided.
    }
}

...or by ID:

[CLTwitterUser getUserWithId:[NSNumber numberWithLongLong:123456789012341234] completionHandler:^(CLTwitterUser *user, NSError *error) {
            if (error)
            {
                // Handle error.
            }
            else
            {
                // The user is provided.
            }
}];

Following/Unfollowing

To follow a user:

[[CLTwitterEngine sharedEngine] followUserWithScreenName:@"ironworkscandy" errorHandler:^(CLTwitterUser *user, NSError *error) {
    if (error)
    {
        // Handle error.
    }
    else
    {
        // The user is provided.
    }
}];

To stop following a user:

[[CLTwitterEngine sharedEngine] stopFollowingUserWithScreenName:@"ironworkscandy" errorHandler:^(NSError *error) {
    if (error)
    {
        // Handle error.
    }
}]; 

Blocking

To block a user:

// Local variable: CLTwitterUser *user;
[user blockUserWithErrorHandler:^(NSError *innerError) {
    if (error)
    {
        // Handle error.
    }
}];

To unblock a user:

// Local variable: CLTwitterUser *user;
[user unblockUserWithErrorHandler:^(NSError *innerError) {
    if (error)
    {
        // Handle error.
    }
}];

To get a list of all the users that you've blocked:

[[CLTwitterEngine sharedEngine] getBlockedUsersWithCompletionHandler:^(NSArray *array, NSError *error) {
    if (error)
    {
        // Handle error.
    }
    else 
    {
        // The array contains CLTwitterUser objects.
    }
}];

To determine if a user is blocked or not:

[CLTwitterUser isUserBlocked:@"souzaliys1" completionHandler:^(BOOL isBlocked) {
    // Take action
}];

Your Own Statistics

To get your own statistics:

[CLTwitterUserTotals getUserTotalsWithCompletionHandler:^(CLTwitterUserTotals *totals, NSError *error) {
    if (error)
    {
        // Handle error.
    }
    else
    {
        // Totals are provided.
    }
}];

Search

Regular searches, user searches, and saved searches are supported.

Regular Searches

To perform a search:

[CLTwitterSearch beginSearchWithQuery:@"some_search_query" completionHandler:^(CLTwitterSearch *searchResult, NSError *error) {
    if (error)
    {
        // Handle error.
    }
    else
    {
        // Search result object is provided.  The actual results are in [searchResult results].
    }

Once you have a CLTwitterSearch object, you can use it to get the next/older batch of results:

[searchResult getOlderSearchResultsWithCompletionHandler:^(CLTwitterSearch *searchResult, NSError *innerError) {
    if (error)
    {
        // Handle error.
    }
    else
    {
        // Now you have a new search result, which ostensibly could replace the one you have.
    }

Similarly, you can go the opposite direction and ask for new results since your last search:

[searchResult getNewerSearchResultsWithCompletionHandler:^(CLTwitterSearch *searchResult, NSError *error) {
    if (error)
    {
        // Handle error.
    }
    else
    {
        // Now you have a new search result, which ostensibly could replace the one you have.
    }

Saved Searches

Saved searches are also handled. To get all of a user's saved searches:

[CLTwitterSavedSearch getSavedSearchTermsWithHandler:^(NSArray *searches, NSError *error) {
    if (error)
    {
        // Handle error.
    }
    else
    {
        // searches is an array of CLTwitterSavedSearch objects.
    }
}];

To get an individual saved search:

[CLTwitterSavedSearch getSavedSearchWithId:[NSNumber numberWithLong:12345678]
                         completionHandler:^(CLTwitterSavedSearch *search, NSError *error) {
                             if (error)
                             {
                                 // Handle error.
                             }
                             else
                             {
                                 // Saved search retrieved.
                             }
                         }];

To create a new saved search:

[CLTwitterSavedSearch createSavedSearchWithQuery:@"Testing" completionHandler:^(CLTwitterSavedSearch *search, NSError *error) {
    if (error)
    {
        // Handle error.
    }
    else
    {
        // Newly created saved search provided for convenience.
    }
}];

To delete a saved search:

// Local variable: CLTwitterSavedSearch *search
[search deleteWithErrorHandler:^(NSError *innerError) {
    if (error)
    {
        // Handle error.
    }
}];

User Searches

To search for a user:

[CLTwitterUser searchForUserWithQuery:@"casey" page:0 resultsHandler:^(NSArray *array, NSError *error) {
    if (error)
    {
        // Handle error.
    }
    else
    {
        // array contains an array of CLTwitterUsers.
    }
}];

Favorites

To get your own favorites:

[[CLTwitterEngine sharedEngine] getMyFavoritesPage:0 withCompletionHandler:^(NSArray *array, NSError *error) {
    if (error)
    {
        // Handle error.
    }
    else
    {
        // array contains an array of CLTweets.
    }
}];

To get someone else's favorites:

CLTwitterUser *user; // Initialized elsewhere.
[user getFavoritesPage:0 withCompletionHandler:^(NSArray *array, NSError *innerError) {
    if (error)
    {
        // Handle error.
    }
    else
    {
        // Array contains an array of CLTweets.
    }
}];

To mark a tweet as a favorite:

CLTweet *tweet; // Initialized elsewhere.
[tweet markAsFavoriteWithErrorHandler:^(NSError *innerError) {
    if (error)
    {
        // Handle error.
    }
    else
    {
        // Tweet was favorited.
    }
}];

To remove a favorite:

CLTweet *tweet; // Initialized elsewhere.
[tweet removeAsFavoriteWithErrorHandler:^(NSError *innerError) {
    if (error)
    {
        // Handle error.
    }
    else
    {
        // Tweet was un-favorited.
    }
}];

Lists

To get all of your lists:

[CLTwitterList getAllListsWithCompletionHandler:^(NSArray *array, NSError *error) {
    if (error)
    {
        // Handle error.
    }
    else
    {
        // Array contains CLTwitterList objects.
    }
}];

To get all of a user's lists:

[CLTwitterList getListsForUser:@"sedgeapp" withCompletionHandler:^(NSArray *array, NSError *error) {
    if (error)
    {
        // Handle error.
    }
    else
    {
        // Array contains an array of CLTwitterList objects.
    }
}];

To get a specific list:

[CLTwitterList getListWithId:[NSNumber numberWithInt:46780714] completionHandler:^(CLTwitterList *list, NSError *error) {
    if (error)
    {
        // Handle error.
    }
    else 
    {
        // List is provided.
    }
}];     

To get the timeline (the tweets) for a list:

[list getTimelineOnPage:1 tweetsPerPage:25 completionHandler:^(NSArray *array, NSError *innerError) {
    if (innerError)
    {
        // Handle error.
    }
    else 
    {
        // Array contains CLTweet objects.
    }
}];

To get all the members of a list:

CLTwitterList *list; // Initialized somewhere
[list getListMembersWithCompletionHandler:^(NSArray *array, NSError *innerError) {
    if (innerError)
    {
        // Handle error.
    }
    else 
    {
        // Array contains CLTwitterUser objects.
    }
}];

To get the users whom subscribe to a list:

CLTwitterList *list; // Inititalized somewhere
[list getSubscribersWithCompletionHandler:^(NSArray *array, NSError *innerError) {
    if (innerError)
    {
        // Handle error.
    }
    else 
    {
        // Array contains CLTwitterUser objects.
    }
}];

To subscribe to a list:

CLTwitterList *list; // Initialized somewhere
[list subscribeWithErrorHandler:^(NSError *innerError) {
    // Handle error.
}];

To unsubscribe from a list:

CLTwitterList *list; // Initialized somewhere
[list unsubscribeWithErrorHandler:^(NSError *innerError) {
    // Handle error.
}];

To create a list:

[CLTwitterList createListWithName:@"Test List"
                      description:@"This is a test list."
                        isPrivate:NO
                completionHandler:^(CLTwitterList *list, NSError *error) {
                    if (error)
                    {
                        // Handle error.
                    }
                    else 
                    {
                        // Newly created list provided for convenience
                    }
}];

To update a list, include the one or more parameters you wish to update:

CLTwitterList *list; // Initialized somewhere
[list updateListWithName:nil 
             description:@"This is a sweet test list." 
               isPrivate:NO 
            errorHandler:^(NSError *innerError) {
            if (error)
            {
                // Handle error
            }
}];

To delete a list:

CLTwitterList *list; // Initialized somewhere
[list deleteListWithErrorHandler:^(NSError *error) {
    if (error)
    {
        // Handle error
    }
    else 
    {
        // List is deleted.
    }
}];

To add a member to one of your lists:

CLTwitterList *list; // Initialized somewhere
[list addMember:@"caseyliss" errorHandler:^(NSError *innerError) {
    if (error)
    {
        // Handle error
    }
}];

To remove a member from one of your lists:

CLTwitterList *list; // Initialized somewhere
[list removeMember:@"caseyliss" errorHandler:^(NSError *innerError) {
    if (error)
    {
        // Handle error
    }
}];

To get all the lists a user subscribes to:

[CLTwitterList getUsersListSubscriptionsForUser:@"caseyliss" 
                                         cursor:nil 
                                   countPerPage:[NSNumber numberWithInt:100]
                              completionHandler:^(NSNumber *previousCursor, NSNumber *nextCursor, NSArray *array, NSError *error) {
                                  if (error)
                                  {
                                      // Handle error.
                                  }
                                  else 
                                  {
                                      // Previous & next cursors are provided.
                                      // Array contains CLTwitterList objects.
                                  }
                              }];

Configuration

To get the current Twitter configuration:

[CLTwitterConfiguration getTwitterConfigurationWithHandler:^(CLTwitterConfiguration *configuration, NSError *error) {
    if (error)
    {
        // Handle error.
    }
    else 
    {
        // Configuration is provided.  Note:
        // [configuration photoSizes] is a dictionary of CLTwitterPhotoSizes
        // [configuration nonUsernamePaths] is an array of NSStrings.
    }
}];

To update your profile information:

[[CLTwitterEngine sharedEngine] updateProfileWithName:@"Sedge for OS X" 
                                                 url:nil 
                                            location:nil
                                         description:nil
                                   completionHandler:^(CLTwitterUser *user, NSError *error) {
                                       if (error)
                                       {
                                           // Handle error.
                                       }
                                       else 
                                       {
                                           // Your own user object is provided for convenience.
                                       }
                                   }];

To update your profile picture:

[[CLTwitterEngine sharedEngine] updateProfileImage:[[NSImage alloc] initWithContentsOfURL:[NSURL URLWithString:@"http://a1.twimg.com/sticky/default_profile_images/default_profile_6_normal.png"]]
                                  withErrorHandler:^(NSError *error) {
                                      if (error)
                                      {
                                          // Handle error.
                                      }
                                  }];

TweetMarker

Tweet Marker is, in short, a way for one or more Twitter clients to all cooperate and note a user's last read tweet. It is opt-in, and requires its own API key–see the website for details.

Tweet Marker allows several different "collections" to have marks. For example, "timeline", or "mentions". With that in mind...

To retrieve the current marker in the timeline for the user SedgeApp:

[CLTweetMarker getLastReadForUsername:@"sedgeapp" 
                         inCollection:@"timeline" 
                           withApiKey:@">>>tweetmarker api key<<<" 
                    completionHandler:^(NSNumber *tweetId, NSError *error) {
                        if (error == nil)
                        {
                            // Handle error.
                        }
                        else
                        {
                            // Scroll your view to the tweet with ID == tweetId.
                        }
}];

To set the current marker in the timeline:

[CLTweetMarker markLastReadAsTweet:[NSNumber numberWithLongLong:168739424295854082]
                       forUsername:@"sedgeapp"
                      inCollection:@"timeline"
                        withApiKey:@">>>tweetmarker api key<<<"
                 completionHandler:^(BOOL success, NSError *error) {
                     if (!success)
                     {
                         // Handle error.
                     }
                     else
                     {
                         // The marker has been set.  Optionally, update your UI to show that.
                     }
                 }];

Thanks

CLTwitterEngine leverages:

Something went wrong with that request. Please try again.