Skip to content

Instantly share code, notes, and snippets.

@SongJiaqiang
Created October 19, 2016 08:31
Show Gist options
  • Save SongJiaqiang/3cf5b838abfcf772a8329d71c6aba62a to your computer and use it in GitHub Desktop.
Save SongJiaqiang/3cf5b838abfcf772a8329d71c6aba62a to your computer and use it in GitHub Desktop.
Share video to Twitter with System Social framework
#import <Foundation/Foundation.h>
#import <Social/Social.h>
#import <Accounts/Accounts.h>
typedef void(^VideoUploadCompletion)(BOOL success, NSString *errorMessage);
typedef void(^ResultAccount)(ACAccount *account);
@interface SocialVideoHelper : NSObject
+(void)getFirstTwitterAccount:(ResultAccount)complete;
+(void)uploadTwitterVideo:(NSData*)videoData comment:(NSString*)comment account:(ACAccount*)account withCompletion:(VideoUploadCompletion)completion;
+(BOOL)userHasAccessToTwitter;
@end
@implementation TwitterVideoUpload
#define DispatchMainThread(block, ...) if(block) dispatch_async(dispatch_get_main_queue(), ^{ block(__VA_ARGS__); })
#define Video_Chunk_Max_size 1000 * 1000 * 5
+(void)uploadError:(NSError*)error withCompletion:(VideoUploadCompletion)completion{
NSString *errorDes = [error localizedDescription];
NSLog(@"There was an error:%@", errorDes);
DispatchMainThread(^(){completion(NO, errorDes);});
}
+(void)uploadSuccessWithCompletion:(VideoUploadCompletion)completion{
DispatchMainThread(^(){completion(YES, nil);});
}
+(BOOL)userHasAccessToTwitter
{
return [SLComposeViewController isAvailableForServiceType:SLServiceTypeTwitter];
}
+ (void)getFirstTwitterAccount:(ResultAccount)complete {
if ([SocialVideoHelper userHasAccessToTwitter]) {
ACAccountStore *accountStore = [[ACAccountStore alloc] init];
ACAccountType *accountType = [accountStore accountTypeWithAccountTypeIdentifier:ACAccountTypeIdentifierTwitter];
[accountStore requestAccessToAccountsWithType:accountType
options:nil
completion:^(BOOL granted, NSError *error) {
if (granted) {
NSArray *arrayOfAccounts = [accountStore accountsWithAccountType:accountType];
if (arrayOfAccounts.count > 0) {
ACAccount *twitterAccount = arrayOfAccounts.firstObject;
if (twitterAccount) {
complete(twitterAccount);
} else {
complete(nil);
}
} else {
complete(nil);
}
} else {
NSLog(@"Failed to access Twitter account with error: %@", error.userInfo);
complete(nil);
}
}];
}else {
complete(nil);
}
}
+(void)uploadTwitterVideo:(NSData*)videoData comment:(NSString*)comment account:(ACAccount*)account withCompletion:(VideoUploadCompletion)completion{
NSURL *twitterPostURL = [[NSURL alloc] initWithString:@"https://upload.twitter.com/1.1/media/upload.json"];
NSDictionary *postParams = @{@"command": @"INIT",
@"total_bytes" : [NSNumber numberWithInteger: videoData.length].stringValue,
@"media_type" : @"video/mp4"
};
SLRequest *request = [SLRequest requestForServiceType:SLServiceTypeTwitter requestMethod:SLRequestMethodPOST URL:twitterPostURL parameters:postParams];
request.account = account;
[request performRequestWithHandler:^(NSData *responseData, NSHTTPURLResponse *urlResponse, NSError *error) {
NSLog(@"Twitter Stage1 HTTP Response: %li, responseData: %@", (long)[urlResponse statusCode], [[NSString alloc] initWithData:responseData encoding:NSUTF8StringEncoding]);
if (error) {
NSLog(@"Twitter Error stage 1 - %@", error);
[SocialVideoHelper uploadError:error withCompletion:completion];
} else {
NSMutableDictionary *returnedData = [NSJSONSerialization JSONObjectWithData:responseData options:NSJSONReadingMutableContainers error:&error];
NSString *mediaID = [NSString stringWithFormat:@"%@", [returnedData valueForKey:@"media_id_string"]];
[SocialVideoHelper tweetVideoStage2:videoData mediaID:mediaID comment:comment account:account withCompletion:completion];
NSLog(@"stage one success, mediaID -> %@", mediaID);
}
}];
}
+(void)tweetVideoStage2:(NSData*)videoData mediaID:(NSString *)mediaID comment:(NSString*)comment account:(ACAccount*)account withCompletion:(VideoUploadCompletion)completion{
NSURL *twitterPostURL = [[NSURL alloc] initWithString:@"https://upload.twitter.com/1.1/media/upload.json"];
NSArray *chunks = [SocialVideoHelper separateToMultipartData:videoData];
NSMutableArray *requests = [NSMutableArray array];
for (int i = 0; i < chunks.count; i++) {
NSString *seg_index = [NSString stringWithFormat:@"%d",i];
NSDictionary *postParams = @{@"command": @"APPEND",
@"media_id" : mediaID,
@"segment_index" : seg_index,
};
SLRequest *postRequest = [SLRequest requestForServiceType:SLServiceTypeTwitter requestMethod:SLRequestMethodPOST URL:twitterPostURL parameters:postParams];
postRequest.account = account;
[postRequest addMultipartData:chunks[i] withName:@"media" type:@"video/mp4" filename:@"video"];
[requests addObject:postRequest];
}
__block NSError *theError = nil;
dispatch_queue_t chunksRequestQueue = dispatch_queue_create("chunksRequestQueue", DISPATCH_QUEUE_SERIAL);
dispatch_async(chunksRequestQueue, ^{
dispatch_group_t requestGroup = dispatch_group_create();
for (int i = 0; i < (requests.count - 1); i++) {
dispatch_group_enter(requestGroup);
SLRequest *postRequest = requests[i];
[postRequest performRequestWithHandler:^(NSData *responseData, NSHTTPURLResponse *urlResponse, NSError *error) {
NSLog(@"Twitter Stage2 - %d HTTP Response: %li, %@", (i+1),(long)[urlResponse statusCode], [[NSString alloc] initWithData:responseData encoding:NSUTF8StringEncoding]);
if (error) {
NSLog(@"Twitter Error stage 2 - %d, error - %@", (i+1), error);
theError = error;
} else {
if (i == requests.count - 1) {
[SocialVideoHelper tweetVideoStage3:videoData mediaID:mediaID comment:comment account:account withCompletion:completion];
}
}
dispatch_group_leave(requestGroup);
}];
dispatch_group_wait(requestGroup, DISPATCH_TIME_FOREVER);
}
if (theError) {
[SocialVideoHelper uploadError:theError withCompletion:completion];
} else {
SLRequest *postRequest = requests.lastObject;
[postRequest performRequestWithHandler:^(NSData *responseData, NSHTTPURLResponse *urlResponse, NSError *error) {
NSLog(@"Twitter Stage2 - final, HTTP Response: %li, %@",(long)[urlResponse statusCode], [[NSString alloc] initWithData:responseData encoding:NSUTF8StringEncoding]);
if (error) {
NSLog(@"Twitter Error stage 2 - final, error - %@", error);
} else {
[SocialVideoHelper tweetVideoStage3:videoData mediaID:mediaID comment:comment account:account withCompletion:completion];
}
}];
}
});
}
+(void)tweetVideoStage3:(NSData*)videoData mediaID:(NSString *)mediaID comment:(NSString*)comment account:(ACAccount*)account withCompletion:(VideoUploadCompletion)completion{
NSURL *twitterPostURL = [[NSURL alloc] initWithString:@"https://upload.twitter.com/1.1/media/upload.json"];
NSDictionary *postParams = @{@"command": @"FINALIZE",
@"media_id" : mediaID };
SLRequest *postRequest = [SLRequest requestForServiceType:SLServiceTypeTwitter requestMethod:SLRequestMethodPOST URL:twitterPostURL parameters:postParams];
// Set the account and begin the request.
postRequest.account = account;
[postRequest performRequestWithHandler:^(NSData *responseData, NSHTTPURLResponse *urlResponse, NSError *error) {
NSLog(@"Twitter Stage3 HTTP Response: %li, %@", (long)[urlResponse statusCode], [[NSString alloc] initWithData:responseData encoding:NSUTF8StringEncoding]);
if (error) {
NSLog(@"Twitter Error stage 3 - %@", error);
[SocialVideoHelper uploadError:error withCompletion:completion];
} else {
[SocialVideoHelper tweetVideoStage4:videoData mediaID:mediaID comment:comment account:account withCompletion:completion];
}
}];
}
+(void)tweetVideoStage4:(NSData*)videoData mediaID:(NSString *)mediaID comment:(NSString*)comment account:(ACAccount*)account withCompletion:(VideoUploadCompletion)completion{
NSURL *twitterPostURL = [[NSURL alloc] initWithString:@"https://api.twitter.com/1.1/statuses/update.json"];
if (comment == nil) {
comment = [NSString stringWithFormat:@"#SocialVideoHelper# https://github.com/liu044100/SocialVideoHelper"];
}
// Set the parameters for the third twitter video request.
NSDictionary *postParams = @{@"status": comment,
@"media_ids" : @[mediaID]};
SLRequest *postRequest = [SLRequest requestForServiceType:SLServiceTypeTwitter requestMethod:SLRequestMethodPOST URL:twitterPostURL parameters:postParams];
postRequest.account = account;
[postRequest performRequestWithHandler:^(NSData *responseData, NSHTTPURLResponse *urlResponse, NSError *error) {
NSLog(@"Twitter Stage4 HTTP Response: %li", (long)[urlResponse statusCode]);
if (error) {
NSLog(@"Twitter Error stage 4 - %@", error);
[SocialVideoHelper uploadError:error withCompletion:completion];
} else {
if ([urlResponse statusCode] == 200){
NSLog(@"Twitter upload success !");
[SocialVideoHelper uploadSuccessWithCompletion:completion];
}
}
}];
}
+(NSArray*)separateToMultipartData:(NSData*)videoData{
NSMutableArray *multipartData = [NSMutableArray new];
CGFloat length = videoData.length;
CGFloat standard_length = Video_Chunk_Max_size;
if (length <= standard_length) {
[multipartData addObject:videoData];
NSLog(@"need not separate as chunk, data size -> %ld bytes", (long)videoData.length);
} else {
NSUInteger count = ceil(length/standard_length);
for (int i = 0; i < count; i++) {
NSRange range;
if (i == count - 1) {
range = NSMakeRange(i * standard_length, length - i * standard_length);
} else {
range = NSMakeRange(i * standard_length, standard_length);
}
NSData *part_data = [videoData subdataWithRange:range];
[multipartData addObject:part_data];
NSLog(@"chunk index -> %d, data size -> %ld bytes", (i+1), (long)part_data.length);
}
}
return multipartData.copy;
}
@end
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment