I posted this question a while ago, but finally found a way to implement this feature.
TWTRComposer still does not support adding anything other than images, so I used the media / loading of the REST API as suggested. I can tweet both GIF and video. Before tweeting on any medium, I create a custom UIAlertView that allows someone to compose a tweet:

Then, when they press the twitter button, the GIF gets twitter if it is small enough; otherwise the video will be on Twitter.
GIF Tweet Result: https://twitter.com/spinturntable/status/730609962817294336
Final video: https://twitter.com/spinturntable/status/730609829128081408
Here is how I implemented this function (a lot of help from this post https://stackoverflow.com/a/166605/ ).
Create the source UIAlertView file to compose a tweet message:
-(IBAction)tweet:(id)sender{ // check if the person has twitter if([[Twitter sharedInstance] session]){ // first, show a pop up window for someone to customize their tweet message, limited to 140 characters UIAlertView *tweetAlert = [[UIAlertView alloc] initWithTitle:@"Compose Tweet" message:nil delegate:self cancelButtonTitle:@"Cancel" otherButtonTitles:nil]; tweetAlert.tag = TAG_TWEET; tweetTextView = [UITextView new]; [tweetTextView setBackgroundColor:[UIColor clearColor]]; CGRect frame = tweetTextView.frame; frame.size.height = 500; tweetTextView.frame = frame; [tweetTextView setFont:[UIFont systemFontOfSize:15]]; tweetTextView.textContainerInset = UIEdgeInsetsMake(0, 10, 10, 10); [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(limitTextView:) name:@"UITextViewTextDidChangeNotification" object:tweetTextView]; [tweetTextView setText:[NSString stringWithFormat:@"%@ %@", [[NSUserDefaults standardUserDefaults] valueForKey:@"tweetText"], self.setShareURL]]; [tweetAlert setValue:tweetTextView forKey:@"accessoryView"]; [tweetAlert addButtonWithTitle:@"Tweet"]; [tweetAlert show]; }else{ UIAlertView *alert = [[UIAlertView alloc] initWithTitle:nil message:@"Please log in with your Twitter account to tweet!" delegate:nil cancelButtonTitle:@"OK" otherButtonTitles:nil]; [alert show]; } }
Then define the UIAlertView Tweet (I add the UIAlertViewDelegate):
- (void)alertView:(UIAlertView *)alertView didDismissWithButtonIndex:(NSInteger)buttonIndex { if(alertView.tag == TAG_TWEET){ if (buttonIndex == 1) { UIAlertView *tweetStartAlert = [[UIAlertView alloc] initWithTitle:nil message:@"Tweeting..." delegate:self cancelButtonTitle:nil otherButtonTitles:nil]; [tweetStartAlert show]; // get client __block TWTRAPIClient *client = [[Twitter sharedInstance] APIClient]; __block NSString *mediaID; NSString *text = [tweetTextView text]; // get tweet text NSLog(@"text: %@", text); NSData *mediaData; NSString *mediaLength; NSString *mediaType; NSString* url = @"https://upload.twitter.com/1.1/media/upload.json"; // if this is a single spin set, tweet the gif if([self.setSpins count] ==1){ NSLog(@"tweeting GIF with url %@", self.gifURL); mediaData = [NSData dataWithContentsOfURL:[NSURL URLWithString:self.gifURL]]; mediaLength = [NSString stringWithFormat:@"%lu", mediaData.length]; mediaType = @"image/gif"; }else if([self.setSpins count] > 1){ // multi-spin set - tweet the video NSLog(@"tweeting video with url %@", self.videoURL); mediaData = [NSData dataWithContentsOfURL:[NSURL URLWithString:self.videoURL]]; mediaLength = [NSString stringWithFormat:@"%lu", mediaData.length]; mediaType = @"video/mp4"; } NSError *error; // First call with command INIT __block NSDictionary *message = @{ @"status":text, @"command":@"INIT", @"media_type":mediaType, @"total_bytes":mediaLength}; NSURLRequest *preparedRequest = [client URLRequestWithMethod:@"POST" URL:url parameters:message error:&error]; [client sendTwitterRequest:preparedRequest completion:^(NSURLResponse *urlResponse, NSData *responseData, NSError *error){ if(!error){ NSError *jsonError; NSDictionary *json = [NSJSONSerialization JSONObjectWithData:responseData options:0 error:&jsonError]; mediaID = [json objectForKey:@"media_id_string"]; NSError *error; NSString *mediaString = [mediaData base64EncodedStringWithOptions:0]; // Second call with command APPEND message = @{@"command" : @"APPEND", @"media_id" : mediaID, @"segment_index" : @"0", @"media" : mediaString}; NSURLRequest *preparedRequest = [client URLRequestWithMethod:@"POST" URL:url parameters:message error:&error]; [client sendTwitterRequest:preparedRequest completion:^(NSURLResponse *urlResponse, NSData *responseData, NSError *error){ if(!error){ client = [[Twitter sharedInstance] APIClient]; NSError *error; // Third call with command FINALIZE message = @{@"command" : @"FINALIZE", @"media_id" : mediaID}; NSURLRequest *preparedRequest = [client URLRequestWithMethod:@"POST" URL:url parameters:message error:&error]; [client sendTwitterRequest:preparedRequest completion:^(NSURLResponse *urlResponse, NSData *responseData, NSError *error){ if(!error){ client = [[Twitter sharedInstance] APIClient]; NSError *error; // publish video with status NSLog(@"publish video!"); NSString *url = @"https://api.twitter.com/1.1/statuses/update.json"; NSMutableDictionary *message = [[NSMutableDictionary alloc] initWithObjectsAndKeys:text,@"status",@"true",@"wrap_links",mediaID, @"media_ids", nil]; NSURLRequest *preparedRequest = [client URLRequestWithMethod:@"POST" URL:url parameters:message error:&error]; [client sendTwitterRequest:preparedRequest completion:^(NSURLResponse *urlResponse, NSData *responseData, NSError *error){ if(!error){ NSError *jsonError; NSDictionary *json = [NSJSONSerialization JSONObjectWithData:responseData options:0 error:&jsonError]; NSLog(@"%@", json); [tweetStartAlert dismissWithClickedButtonIndex:0 animated:YES]; UIAlertView *tweetFinishedAlert = [[UIAlertView alloc] initWithTitle:nil message:@"Tweeted!" delegate:self cancelButtonTitle:nil otherButtonTitles:nil]; [tweetFinishedAlert show]; double delayInSeconds = 1.5; dispatch_time_t popTime = dispatch_time(DISPATCH_TIME_NOW, (int64_t)(delayInSeconds * NSEC_PER_SEC)); dispatch_after(popTime, dispatch_get_main_queue(), ^(void){ [tweetFinishedAlert dismissWithClickedButtonIndex:0 animated:YES]; }); [self logShare:@"twitter"]; }else{ NSLog(@"Error: %@", error); } }]; }else{ NSLog(@"Error command FINALIZE: %@", error); } }]; }else{ NSLog(@"Error command APPEND: %@", error); } }]; }else{ NSLog(@"Error command INIT: %@", error); } }]; } } }
Some additional things: I focus on a UITextView when a message about creating a Tweet message appears:
- (void)didPresentAlertView:(UIAlertView *)alertView { if(alertView.tag == TAG_TWEET){ NSLog(@"tweetAlertView appeared"); [tweetTextView becomeFirstResponder]; } }
Here, as I check if the UITextView has less than 140 characters:
- (void)limitTextView:(NSNotification *)note { int limit = 140; if ([[tweetTextView text] length] > limit) { [tweetTextView setText:[[tweetTextView text] substringToIndex:limit]]; } }
Hope this is helpful to others as it took me a long time to put everything together.