Okechi Onyeje 544a7baa87 Working on posting a listing to ebay
Paypal sandbox account not linking, so cannot proceed until able to get this working
2016-08-17 10:22:15 -04:00

819 lines
34 KiB
Objective-C

// Copyright (c) 2014-present, Facebook, Inc. All rights reserved.
//
// You are hereby granted a non-exclusive, worldwide, royalty-free license to use,
// copy, modify, and distribute this software in source code or binary form for use
// in connection with the web services and APIs provided by Facebook.
//
// As with any software that integrates with the Facebook platform, your use of
// this software is subject to the Facebook Developer Principles and Policies
// [http://developers.facebook.com/policy/]. This copyright 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.
#import "FBSDKShareAPI.h"
#if !TARGET_OS_TV
#import <AssetsLibrary/AssetsLibrary.h>
#endif
#import <FBSDKCoreKit/FBSDKGraphRequest.h>
#import <FBSDKCoreKit/FBSDKGraphRequestDataAttachment.h>
#import "FBSDKCoreKit+Internal.h"
#import "FBSDKShareConstants.h"
#import "FBSDKShareDefines.h"
#import "FBSDKShareError.h"
#import "FBSDKShareLinkContent.h"
#import "FBSDKShareOpenGraphAction.h"
#import "FBSDKShareOpenGraphContent.h"
#import "FBSDKShareOpenGraphObject.h"
#import "FBSDKSharePhoto.h"
#import "FBSDKSharePhotoContent.h"
#import "FBSDKShareUtility.h"
#import "FBSDKShareVideo.h"
#import "FBSDKShareVideoContent.h"
#import "FBSDKVideoUploader.h"
static NSString *const FBSDKShareAPIDefaultGraphNode = @"me";
static NSString *const FBSDKShareAPIPhotosEdge = @"photos";
static NSString *const FBSDKShareAPIVideosEdge = @"videos";
static NSMutableArray *g_pendingFBSDKShareAPI;
@interface FBSDKShareAPI () <FBSDKVideoUploaderDelegate>
@end
@implementation FBSDKShareAPI {
NSFileHandle *_fileHandle;
#if !TARGET_OS_TV
ALAssetRepresentation *_assetRepresentation;
#endif
}
#pragma mark - Class Methods
+ (instancetype)shareWithContent:(id<FBSDKSharingContent>)content delegate:(id<FBSDKSharingDelegate>)delegate
{
FBSDKShareAPI *API = [[self alloc] init];
API.shareContent = content;
API.delegate = delegate;
[API share];
return API;
}
#pragma mark - Properties
@synthesize delegate = _delegate;
@synthesize shareContent = _shareContent;
@synthesize shouldFailOnDataError = _shouldFailOnDataError;
@synthesize accessToken = _accessToken;
#pragma mark - Object Lifecycle
#if !TARGET_OS_TV
+ (ALAssetsLibrary *)defaultAssetsLibrary {
static dispatch_once_t pred = 0;
static ALAssetsLibrary *library = nil;
dispatch_once(&pred, ^{
library = [[fbsdkdfl_ALAssetsLibraryClass() alloc] init];
});
return library;
}
#endif
+ (void)initialize
{
if (self == [FBSDKShareAPI class]) {
g_pendingFBSDKShareAPI = [[NSMutableArray alloc] init];
}
}
- (instancetype)init
{
if ((self = [super init])) {
_graphNode = FBSDKShareAPIDefaultGraphNode;
}
return self;
}
#pragma mark - Public Methods
- (BOOL)canShare
{
return YES;
}
- (BOOL)createOpenGraphObject:(FBSDKShareOpenGraphObject *)openGraphObject
{
NSError *error;
if (![self canShare]) {
NSString *message = @"Share API is not available; verify 'canShare' returns YES";
error = [FBSDKShareError errorWithCode:FBSDKShareDialogNotAvailableErrorCode message:message];
[_delegate sharer:self didFailWithError:error];
return NO;
}
if (![self _hasPublishActions]) {
NSString *message = @"Warning: Access token is missing publish_actions permissions";
[FBSDKLogger singleShotLogEntry:FBSDKLoggingBehaviorDeveloperErrors logEntry:message];
}
if (!openGraphObject) {
error = [FBSDKShareError requiredArgumentErrorWithName:@"openGraphObject" message:nil];
[_delegate sharer:self didFailWithError:error];
return NO;
}
FBSDKGraphRequestConnection *connection = [[FBSDKGraphRequestConnection alloc] init];
void(^completionHandler)(id) = ^(NSDictionary *result) {
[_delegate sharer:self didCompleteWithResults:result];
};
if (![self _stageOpenGraphObject:openGraphObject
connection:connection
stagingHandler:NULL
completionHandler:completionHandler]) {
return NO;
}
[connection start];
return YES;
}
- (BOOL)share
{
NSError *error;
if (![self canShare]) {
NSString *message = @"Share API is not available; verify 'canShare' returns YES";
error = [FBSDKShareError errorWithCode:FBSDKShareDialogNotAvailableErrorCode message:message];
[_delegate sharer:self didFailWithError:error];
return NO;
}
if (![self _hasPublishActions]) {
NSString *message = @"Warning: Access token is missing publish_actions permissions";
[FBSDKLogger singleShotLogEntry:FBSDKLoggingBehaviorDeveloperErrors logEntry:message];
}
if (![self validateWithError:&error]) {
[_delegate sharer:self didFailWithError:error];
return NO;
}
id<FBSDKSharingContent> shareContent = self.shareContent;
if ([shareContent isKindOfClass:[FBSDKShareLinkContent class]]) {
return [self _shareLinkContent:(FBSDKShareLinkContent *)shareContent];
} else if ([shareContent isKindOfClass:[FBSDKSharePhotoContent class]]) {
return [self _sharePhotoContent:(FBSDKSharePhotoContent *)shareContent];
} else if ([shareContent isKindOfClass:[FBSDKShareVideoContent class]]) {
return [self _shareVideoContent:(FBSDKShareVideoContent *)shareContent];
} else if ([shareContent isKindOfClass:[FBSDKShareOpenGraphContent class]]) {
return [self _shareOpenGraphContent:(FBSDKShareOpenGraphContent *)shareContent];
} else {
return NO;
}
}
- (BOOL)validateWithError:(NSError *__autoreleasing *)errorRef
{
id<FBSDKSharingContent> shareContent = self.shareContent;
if (!shareContent) {
if (errorRef != NULL) {
*errorRef = [FBSDKShareError requiredArgumentErrorWithName:@"shareContent" message:@"Share content cannot be null."];
}
return NO;
}
if ([shareContent isKindOfClass:[FBSDKShareVideoContent class]]) {
if (shareContent.peopleIDs.count > 0) {
if (errorRef != NULL) {
*errorRef = [FBSDKShareError invalidArgumentErrorWithName:@"peopleIDs" value:shareContent.peopleIDs message:@"Cannot specify peopleIDs with FBSDKShareVideoContent."];
}
return NO;
}
if (shareContent.placeID) {
if (errorRef != NULL) {
*errorRef = [FBSDKShareError invalidArgumentErrorWithName:@"placeID" value:shareContent.placeID message:@"Cannot specify place ID with FBSDKShareVideoContent."];
}
return NO;
}
}
if (errorRef != NULL){
*errorRef = nil;
}
return [FBSDKShareUtility validateShareContent:shareContent error:errorRef];
}
- (FBSDKAccessToken *)accessToken
{
return _accessToken ?: [FBSDKAccessToken currentAccessToken];
}
#pragma mark - Helper Methods
- (NSString *)_graphPathWithSuffix:(NSString *)suffix, ... NS_REQUIRES_NIL_TERMINATION
{
NSMutableString *graphPath = [[NSMutableString alloc] initWithString:self.graphNode];
va_list args;
va_start(args, suffix);
for (NSString *arg = suffix; arg != nil; arg = va_arg(args, NSString *)) {
[graphPath appendFormat:@"/%@", arg];
}
va_end(args);
return graphPath;
}
- (void)_addCommonParameters:(NSMutableDictionary *)parameters content:(id<FBSDKSharingContent>)content
{
if (content.peopleIDs.count > 0) {
NSString *tags;
if ([content isKindOfClass:[FBSDKSharePhotoContent class]]) {
NSMutableArray *tagsArray = [[NSMutableArray alloc] init];
for (NSString *peopleID in content.peopleIDs) {
[tagsArray addObject:@{ @"tag_uid" : peopleID }];
}
NSData *tagsJSON = [NSJSONSerialization dataWithJSONObject:tagsArray options:0 error:nil];
tags = [[NSString alloc] initWithData:tagsJSON encoding:NSUTF8StringEncoding];
} else {
tags = [content.peopleIDs componentsJoinedByString:@","];
}
[FBSDKInternalUtility dictionary:parameters setObject:tags forKey:@"tags"];
}
[FBSDKInternalUtility dictionary:parameters setObject:content.placeID forKey:@"place"];
[FBSDKInternalUtility dictionary:parameters setObject:content.ref forKey:@"ref"];
}
- (BOOL)_hasPublishActions
{
return [self.accessToken.permissions containsObject:@"publish_actions"];
}
- (BOOL)_shareLinkContent:(FBSDKShareLinkContent *)linkContent
{
FBSDKGraphRequestHandler completionHandler = ^(FBSDKGraphRequestConnection *connection, id result, NSError *error) {
if (!_delegate) {
return;
}
if (error) {
[_delegate sharer:self didFailWithError:error];
} else {
result = [FBSDKTypeUtility dictionaryValue:result];
NSMutableDictionary *shareResults = [[NSMutableDictionary alloc] init];
[FBSDKInternalUtility dictionary:shareResults setObject:FBSDK_SHARE_RESULT_COMPLETION_GESTURE_VALUE_POST
forKey:FBSDK_SHARE_RESULT_COMPLETION_GESTURE_KEY];
[FBSDKInternalUtility dictionary:shareResults setObject:[FBSDKTypeUtility stringValue:result[@"id"]]
forKey:FBSDK_SHARE_RESULT_POST_ID_KEY];
[_delegate sharer:self didCompleteWithResults:shareResults];
}
};
NSMutableDictionary *parameters = [[NSMutableDictionary alloc] init];
[self _addCommonParameters:parameters content:linkContent];
[FBSDKInternalUtility dictionary:parameters setObject:self.message forKey:@"message"];
[FBSDKInternalUtility dictionary:parameters setObject:linkContent.contentURL forKey:@"link"];
[FBSDKInternalUtility dictionary:parameters setObject:linkContent.imageURL forKey:@"picture"];
[FBSDKInternalUtility dictionary:parameters setObject:linkContent.contentTitle forKey:@"name"];
[FBSDKInternalUtility dictionary:parameters setObject:linkContent.contentDescription forKey:@"description"];
[[[FBSDKGraphRequest alloc] initWithGraphPath:[self _graphPathWithSuffix:@"feed", nil]
parameters:parameters
tokenString:self.accessToken.tokenString
version:nil
HTTPMethod:@"POST"] startWithCompletionHandler:completionHandler];
return YES;
}
- (BOOL)_shareOpenGraphContent:(FBSDKShareOpenGraphContent *)openGraphContent
{
// In order to create a new Open Graph action using a custom object that does not already exist (objectID or URL), you
// must first send a request to post the object and then another to post the action. If a local image is supplied
// with the object or action, that must be staged first and then referenced by the staging URL that is returned by
// that request.
FBSDKShareOpenGraphAction *action = openGraphContent.action;
FBSDKGraphRequestConnection *connection = [[FBSDKGraphRequestConnection alloc] init];
void(^stagingHandler)(NSDictionary *) = ^(NSDictionary *stagedContainer) {
NSMutableDictionary *parameters = [NSMutableDictionary dictionaryWithDictionary:stagedContainer];
[self _addCommonParameters:parameters content:openGraphContent];
[FBSDKInternalUtility dictionary:parameters setObject:self.message forKey:@"message"];
FBSDKGraphRequestHandler requestHandler = ^(FBSDKGraphRequestConnection *requestConnection,
id result,
NSError *requestError) {
if (!_delegate) {
return;
}
if (requestError) {
NSError *error = [FBSDKShareError errorWithCode:FBSDKShareOpenGraphErrorCode
message:@"Error sharing Open Graph content"
underlyingError:requestError];
[_delegate sharer:self didFailWithError:error];
} else if (result) {
NSMutableDictionary *shareResults = [[NSMutableDictionary alloc] init];
[FBSDKInternalUtility dictionary:shareResults setObject:FBSDK_SHARE_RESULT_COMPLETION_GESTURE_VALUE_POST
forKey:FBSDK_SHARE_RESULT_COMPLETION_GESTURE_KEY];
[FBSDKInternalUtility dictionary:shareResults setObject:[FBSDKTypeUtility stringValue:result[@"id"]]
forKey:FBSDK_SHARE_RESULT_POST_ID_KEY];
[_delegate sharer:self didCompleteWithResults:shareResults];
}
};
NSString *graphPath = [self _graphPathWithSuffix:[FBSDKUtility URLEncode:action.actionType], nil];
FBSDKGraphRequest *request = [[FBSDKGraphRequest alloc] initWithGraphPath:graphPath
parameters:parameters
tokenString:self.accessToken.tokenString
version:nil
HTTPMethod:@"POST"];
[self _connection:connection addRequest:request completionHandler:requestHandler];
[connection start];
};
if (![self _stageOpenGraphValueContainer:action connection:connection stagingHandler:stagingHandler]) {
return NO;
}
return YES;
}
- (BOOL)_sharePhotoContent:(FBSDKSharePhotoContent *)photoContent
{
NSArray *photos = photoContent.photos;
NSMutableArray *requests = [[NSMutableArray alloc] init];
for (FBSDKSharePhoto *photo in photos) {
UIImage *image = photo.image;
if (!image && [photo.imageURL isFileURL]) {
image = [UIImage imageWithContentsOfFile:[photo.imageURL path]];
}
if (image) {
NSString *graphPath = [self _graphPathWithSuffix:FBSDKShareAPIPhotosEdge, nil];
NSMutableDictionary *parameters = [[NSMutableDictionary alloc] init];
[self _addCommonParameters:parameters content:photoContent];
NSString *caption = photo.caption ?: self.message;
[FBSDKInternalUtility dictionary:parameters setObject:caption forKey:@"caption"];
parameters[@"picture"] = image;
[requests addObject:[[FBSDKGraphRequest alloc] initWithGraphPath:graphPath
parameters:parameters
tokenString:self.accessToken.tokenString
version:nil
HTTPMethod:@"POST"]];
}
}
NSUInteger requestCount = [requests count];
NSMutableArray *results = [[NSMutableArray alloc] init];
NSMutableArray *errors = [[NSMutableArray alloc] init];
__block NSUInteger completedCount = 0;
FBSDKGraphRequestHandler completionHandler = ^(FBSDKGraphRequestConnection *connection, id result, NSError *error) {
result = [FBSDKTypeUtility dictionaryValue:result];
[FBSDKInternalUtility array:results addObject:result];
[FBSDKInternalUtility array:errors addObject:error];
if (++completedCount != requestCount) {
return;
}
if (!_delegate) {
return;
}
if ([errors count]) {
[_delegate sharer:self didFailWithError:errors[0]];
} else if ([results count]) {
// each photo upload will be merged into the same post, so grab the post_id from the first and use that
NSMutableDictionary *shareResults = [[NSMutableDictionary alloc] init];
[FBSDKInternalUtility dictionary:shareResults setObject:FBSDK_SHARE_RESULT_COMPLETION_GESTURE_VALUE_POST
forKey:FBSDK_SHARE_RESULT_COMPLETION_GESTURE_KEY];
NSDictionary *firstResult = [FBSDKTypeUtility dictionaryValue:results[0]];
[FBSDKInternalUtility dictionary:shareResults setObject:[FBSDKTypeUtility stringValue:firstResult[@"post_id"]]
forKey:FBSDK_SHARE_RESULT_POST_ID_KEY];
[_delegate sharer:self didCompleteWithResults:shareResults];
}
};
for (FBSDKGraphRequest *request in requests) {
[request startWithCompletionHandler:completionHandler];
}
return YES;
}
- (BOOL)_shareVideoContent:(FBSDKShareVideoContent *)videoContent
{
NSMutableDictionary *parameters = [[NSMutableDictionary alloc] init];
[self _addCommonParameters:parameters content:videoContent];
[FBSDKInternalUtility dictionary:parameters setObject:self.message forKey:@"description"];
if ([self.accessToken.permissions containsObject:@"ads_management"]) {
FBSDKSharePhoto *photo = videoContent.previewPhoto;
UIImage *image = photo.image;
if (!image && [photo.imageURL isFileURL]) {
image = [UIImage imageWithContentsOfFile:[photo.imageURL path]];
}
[FBSDKInternalUtility dictionary:parameters setObject:image forKey:@"thumb"];
}
FBSDKShareVideo *video = videoContent.video;
NSURL *videoURL = video.videoURL;
if ([videoURL isFileURL]) {
NSError *fileError;
_fileHandle = [NSFileHandle fileHandleForReadingFromURL:videoURL error:&fileError];
if (!_fileHandle) {
[_delegate sharer:self didFailWithError:fileError];
return NO;
}
if (![self _addToPendingShareAPI]) {
return NO;
}
FBSDKVideoUploader *videoUploader = [[FBSDKVideoUploader alloc] initWithVideoName:[videoURL lastPathComponent]
videoSize:(unsigned long)[_fileHandle seekToEndOfFile]
parameters:parameters
delegate:self];
videoUploader.graphNode = self.graphNode;
[videoUploader start];
return YES;
} else if (videoURL) {
#if TARGET_OS_TV
return NO;
#else
if (![self _addToPendingShareAPI]) {
return NO;
}
[[FBSDKShareAPI defaultAssetsLibrary] assetForURL:videoURL resultBlock:^(ALAsset *asset) {
_assetRepresentation = [asset defaultRepresentation];
NSUInteger size = (NSUInteger)_assetRepresentation.size;
FBSDKVideoUploader *videoUploader = [[FBSDKVideoUploader alloc] initWithVideoName:[videoURL lastPathComponent]
videoSize:size
parameters:parameters
delegate:self];
videoUploader.graphNode = self.graphNode;
[videoUploader start];
} failureBlock:^(NSError *error) {
[_delegate sharer:self didFailWithError:error];
}];
return YES;
#endif
} else {
return NO;
}
}
- (BOOL)_addEncodedParametersToDictionary:(NSMutableDictionary *)parameters
key:(NSString *)key
value:(id)value
error:(NSError **)errorRef
{
if ([value isKindOfClass:[NSString class]] ||
[value isKindOfClass:[NSNumber class]] ||
[value isKindOfClass:[NSNull class]]) {
parameters[key] = value;
} else if ([value isKindOfClass:[NSArray class]]) {
__block BOOL didEncode = YES;
[(NSArray *)value enumerateObjectsUsingBlock:^(id subvalue, NSUInteger idx, BOOL *stop) {
NSString *subkey = [[NSString alloc] initWithFormat:@"%@[%lu]", key, (unsigned long)idx];
if (![self _addEncodedParametersToDictionary:parameters key:subkey value:subvalue error:errorRef]) {
*stop = YES;
didEncode = NO;
}
}];
if (!didEncode) {
return NO;
}
} else if ([value isKindOfClass:[NSDictionary class]]) {
__block BOOL didEncode = YES;
[(NSDictionary *)value enumerateKeysAndObjectsUsingBlock:^(id subkey, id subvalue, BOOL *stop) {
subkey = [[NSString alloc] initWithFormat:@"%@[%@]", key, subkey];
if (![self _addEncodedParametersToDictionary:parameters key:subkey value:subvalue error:errorRef]) {
*stop = YES;
didEncode = NO;
}
}];
if (!didEncode) {
return NO;
}
} else {
if (errorRef != NULL) {
*errorRef = [FBSDKShareError invalidArgumentErrorWithName:key value:value message:nil];
}
return NO;
}
if (errorRef != NULL) {
*errorRef = nil;
}
return YES;
}
- (BOOL)_stageArray:(NSArray *)array
connection:(FBSDKGraphRequestConnection *)connection
stagingHandler:(void(^)(NSArray *stagedArray))stagingHandler
{
__block BOOL result = YES;
__block NSUInteger pendingCount = 1;
NSMutableArray *stagedArray = [[NSMutableArray alloc] initWithArray:array];
void(^itemDidFail)(void) = ^{
if (!result) {
return;
}
result = NO;
};
void(^itemDidSucceed)(void) = ^{
if (!result) {
return;
}
if ((--pendingCount == 0) && (stagingHandler != NULL)) {
stagingHandler(stagedArray);
}
};
[array enumerateObjectsUsingBlock:^(id item, NSUInteger idx, BOOL *stop) {
pendingCount++;
void(^itemHandler)(id) = ^(id stagedValue) {
if (stagedValue) {
stagedArray[idx] = stagedValue;
itemDidSucceed();
} else {
NSError *error = [FBSDKShareError invalidArgumentErrorWithName:@"value"
value:item
message:@"Error staging object."];
[_delegate sharer:self didFailWithError:error];
itemDidFail();
*stop = YES;
result = NO;
}
};
if (![self _stageValue:item connection:connection stagingHandler:itemHandler]) {
itemDidFail();
*stop = YES;
result = NO;
}
}];
if (result) {
itemDidSucceed();
}
return result;
}
- (BOOL)_stageOpenGraphObject:(FBSDKShareOpenGraphObject *)openGraphObject
connection:(FBSDKGraphRequestConnection *)connection
stagingHandler:(void(^)(id stagedObject))stagingHandler
completionHandler:(void(^)(NSDictionary *result))completionHandler
{
NSString *type = [FBSDKTypeUtility stringValue:openGraphObject[@"og:type"]];
if (!type) {
NSString *message = @"Open Graph objects must contain a og:type value.";
NSError *error = [FBSDKShareError requiredArgumentErrorWithName:@"og:type" message:message];
[_delegate sharer:self didFailWithError:error];
return NO;
}
void(^containerHandler)(NSDictionary *) = ^(NSDictionary *stagedContainer) {
NSError *JSONError;
NSString *objectString = [FBSDKInternalUtility JSONStringForObject:stagedContainer
error:&JSONError
invalidObjectHandler:NULL];
if (!objectString) {
[_delegate sharer:self didFailWithError:JSONError];
return;
}
NSString *graphPath = [self _graphPathWithSuffix:@"objects", type, nil];
NSDictionary *parameters = @{ @"object": objectString };
FBSDKGraphRequest *request = [[FBSDKGraphRequest alloc] initWithGraphPath:graphPath
parameters:parameters
tokenString:self.accessToken.tokenString
version:nil
HTTPMethod:@"POST"];
FBSDKGraphRequestHandler requestCompletionHandler = ^(FBSDKGraphRequestConnection *requestConnection,
id result,
NSError *requestError) {
if (!_delegate) {
return;
}
if (requestError) {
NSString *message = [[NSString alloc] initWithFormat:@"Error creating Open Graph object: %@",
requestError.description];
NSError *error = [FBSDKShareError errorWithCode:FBSDKShareOpenGraphErrorCode
message:message
underlyingError:requestError];
[_delegate sharer:self didFailWithError:error];
} else if (completionHandler != NULL) {
completionHandler([FBSDKTypeUtility dictionaryValue:result]);
}
};
NSString *batchEntryName = [self _connection:connection
addRequest:request
completionHandler:requestCompletionHandler];
if (stagingHandler != NULL) {
stagingHandler([[NSString alloc] initWithFormat:@"{result=%@:$.id}", batchEntryName]);
}
};
return [self _stageOpenGraphValueContainer:openGraphObject connection:connection stagingHandler:containerHandler];
return YES;
}
- (BOOL)_stageOpenGraphValueContainer:(id<FBSDKShareOpenGraphValueContaining>)container
connection:(FBSDKGraphRequestConnection *)connection
stagingHandler:(void(^)(NSDictionary *stagedContainer))stagingHandler
{
__block BOOL result = YES;
__block NSUInteger pendingCount = 1;
NSMutableDictionary *stagedContainer = [[NSMutableDictionary alloc] init];
void(^itemDidFail)(void) = ^{
if (!result) {
return;
}
result = NO;
};
void(^itemDidSucceed)(void) = ^{
if (!result) {
return;
}
if ((--pendingCount == 0) && (stagingHandler != NULL)) {
stagingHandler(stagedContainer);
}
};
BOOL isAction = [container isKindOfClass:[FBSDKShareOpenGraphAction class]];
[container enumerateKeysAndObjectsUsingBlock:^(NSString *key, id object, BOOL *stop) {
pendingCount++;
// The server does not understand custom namespaces remove them until the server is fixed
NSString *namespace;
key = [FBSDKShareUtility getOpenGraphNameAndNamespaceFromFullName:key namespace:&namespace];
if (namespace && !isAction) {
if (!stagedContainer[namespace]) {
stagedContainer[namespace] = [[NSMutableDictionary alloc] init];
}
}
void(^itemHandler)(id) = ^(id stagedValue) {
if (stagedValue) {
if (isAction) {
NSError *error;
if (![self _addEncodedParametersToDictionary:stagedContainer key:key value:stagedValue error:&error]) {
[_delegate sharer:self didFailWithError:error];
itemDidFail();
return;
}
} else {
NSMutableDictionary *valueContainer = (namespace ? stagedContainer[namespace] : stagedContainer);
valueContainer[key] = stagedValue;
}
}
itemDidSucceed();
};
if (![self _stageValue:object connection:connection stagingHandler:itemHandler]) {
*stop = YES;
result = NO;
}
}];
if (result) {
itemDidSucceed();
}
return result;
}
- (BOOL)_stagePhoto:(FBSDKSharePhoto *)photo
connection:(FBSDKGraphRequestConnection *)connection
stagingHandler:(void(^)(id stagedPhoto))stagingHandler
{
if (photo.imageURL) {
NSMutableDictionary *stagedPhoto = [[NSMutableDictionary alloc]initWithDictionary: @{
@"url": photo.imageURL.absoluteString,
@"user_generated": @(photo.userGenerated),
}];
[FBSDKInternalUtility dictionary:stagedPhoto setObject:photo.caption forKey:@"caption"];
if (stagingHandler) {
stagingHandler(stagedPhoto);
}
return YES;
} else if (photo.image) {
NSString *graphPath = @"/me/staging_resources";
NSDictionary *parameters = @{ @"file": photo.image };
FBSDKGraphRequest *request = [[FBSDKGraphRequest alloc] initWithGraphPath:graphPath
parameters:parameters
tokenString:self.accessToken.tokenString
version:nil
HTTPMethod:@"POST"];
FBSDKGraphRequestHandler completionHandler = ^(FBSDKGraphRequestConnection *requestConnection,
id result,
NSError *requestError) {
NSString *stagedPhotoURLString = [FBSDKTypeUtility stringValue:result[@"uri"]];
if (requestError || !stagedPhotoURLString) {
NSError *error = [FBSDKShareError errorWithCode:FBSDKShareOpenGraphErrorCode
message:@"Error staging photo"
underlyingError:requestError];
[_delegate sharer:self didFailWithError:error];
} else if (stagingHandler) {
NSMutableDictionary *stagedPhoto = [[NSMutableDictionary alloc] initWithDictionary: @{
@"url": stagedPhotoURLString,
@"user_generated": @(photo.userGenerated),
}];
[FBSDKInternalUtility dictionary:stagedPhoto setObject:photo.caption forKey:@"caption"];
stagingHandler(stagedPhoto);
}
};
[request startWithCompletionHandler:completionHandler];
return YES;
} else {
NSError *error = [FBSDKShareError invalidArgumentErrorWithName:@"photo"
value:photo
message:@"Photos must have an imageURL or image."];
[_delegate sharer:self didFailWithError:error];
return NO;
}
}
- (BOOL)_stageValue:(id)value
connection:(FBSDKGraphRequestConnection *)connection
stagingHandler:(void(^)(id stagedValue))stagingHandler
{
if ([value isKindOfClass:[NSString class]] ||
[value isKindOfClass:[NSNumber class]]) {
if (stagingHandler != NULL) {
stagingHandler(value);
}
return YES;
} else if ([value isKindOfClass:[NSURL class]]) {
if (stagingHandler != NULL) {
stagingHandler([(NSURL *)value absoluteString]);
}
return YES;
} else if ([value isKindOfClass:[FBSDKSharePhoto class]]) {
return [self _stagePhoto:(FBSDKSharePhoto *)value connection:connection stagingHandler:stagingHandler];
} else if ([value isKindOfClass:[FBSDKShareOpenGraphObject class]]) {
return [self _stageOpenGraphObject:(FBSDKShareOpenGraphObject *)value
connection:connection
stagingHandler:stagingHandler
completionHandler:NULL];
} else if ([value isKindOfClass:[NSArray class]]) {
return [self _stageArray:(NSArray *)value connection:connection stagingHandler:stagingHandler];
} else {
NSError *error = [FBSDKShareError invalidArgumentErrorWithName:@"value"
value:value
message:@"Invalid value type found in Open Graph object."];
[_delegate sharer:self didFailWithError:error];
return NO;
}
}
- (NSString *)_connection:(FBSDKGraphRequestConnection *)connection
addRequest:(FBSDKGraphRequest *)request
completionHandler:(FBSDKGraphRequestHandler)completionHandler
{
NSUInteger requestCount = [connection.requests count];
NSString *batchEntryName = [[NSString alloc] initWithFormat:@"request_%lu", (unsigned long)requestCount];
[connection addRequest:request completionHandler:completionHandler batchEntryName:batchEntryName];
return batchEntryName;
}
- (BOOL)_addToPendingShareAPI
{
@synchronized(g_pendingFBSDKShareAPI) {
if ([g_pendingFBSDKShareAPI containsObject:self]) {
[FBSDKLogger singleShotLogEntry:FBSDKLoggingBehaviorDeveloperErrors logEntry:@"FBSDKShareAPI did not share video content. Video upload already in progress."];
return NO;
}
[g_pendingFBSDKShareAPI addObject:self];
return YES;
}
}
- (void)_removeFromPendingShareAPI
{
@synchronized(g_pendingFBSDKShareAPI) {
[g_pendingFBSDKShareAPI removeObject:self];
_fileHandle = nil;
#if !TARGET_OS_TV
_assetRepresentation = nil;
#endif
}
}
#pragma mark - FBSDKVideoUploaderDelegate
- (NSData *)videoChunkDataForVideoUploader:(FBSDKVideoUploader *)videoUploader startOffset:(NSUInteger)startOffset endOffset:(NSUInteger)endOffset
{
NSUInteger chunkSize = endOffset - startOffset;
if (_fileHandle) {
[_fileHandle seekToFileOffset:startOffset];
NSData *videoChunkData = [_fileHandle readDataOfLength:chunkSize];
if (videoChunkData == nil || videoChunkData.length != chunkSize) {
return nil;
}
return videoChunkData;
}
#if !TARGET_OS_TV
else if (_assetRepresentation) {
NSMutableData *data = [NSMutableData dataWithLength:chunkSize];
NSError *error;
NSUInteger bufferedLength = [_assetRepresentation getBytes:[data mutableBytes] fromOffset:startOffset length:chunkSize error:&error];
if (bufferedLength != chunkSize || data == nil || error) {
return nil;
}
return data;
}
#endif
return nil;
}
- (void)videoUploader:(FBSDKVideoUploader *)videoUploader didCompleteWithResults:(NSDictionary *)results
{
results = [FBSDKTypeUtility dictionaryValue:results];
NSMutableDictionary *shareResults = [[NSMutableDictionary alloc] init];
[FBSDKInternalUtility dictionary:shareResults setObject:FBSDK_SHARE_RESULT_COMPLETION_GESTURE_VALUE_POST forKey:FBSDK_SHARE_RESULT_COMPLETION_GESTURE_KEY];
[FBSDKInternalUtility dictionary:shareResults setObject:[FBSDKTypeUtility stringValue:results[FBSDK_SHARE_VIDEO_ID]] forKey:FBSDK_SHARE_VIDEO_ID];
[_delegate sharer:self didCompleteWithResults:shareResults];
[self _removeFromPendingShareAPI];
}
- (void)videoUploader:(FBSDKVideoUploader *)videoUploader didFailWithError:(NSError *)error
{
[_delegate sharer:self didFailWithError:error];
[self _removeFromPendingShareAPI];
}
@end