Created
October 19, 2016 08:31
-
-
Save SongJiaqiang/3cf5b838abfcf772a8329d71c6aba62a to your computer and use it in GitHub Desktop.
Share video to Twitter with System Social framework
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#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