Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Browse files

Facebook iOS SDK 3.2.1 (see https://developers.facebook.com/ios/chang…

…e-log-3.x/)

Summary:

Added
    Support for frictionless application requests in FBWebDialogs

Fixed
    Exception in certain request methods when a handler is nil

Reviewers: gregschechte

Reviewed By: gregschechte

Test Plan: Release Testing.
  • Loading branch information...
commit 836cec3b1dbf74148847dcac5352c7975c6e7853 1 parent 76b75a4
@onebit onebit authored
View
34 samples/BooleanOGSample/BooleanOGSample/BOGSecondViewController.m
@@ -20,6 +20,9 @@
@interface BOGSecondViewController () <FBFriendPickerDelegate>
+@property (readwrite, nonatomic, copy) NSString *fbidSelection;
+@property (readwrite, nonatomic, retain) FBFrictionlessRecipientCache *friendCache;
+
- (void)updateActivityForID:(NSString *)fbid;
@end
@@ -27,6 +30,7 @@ - (void)updateActivityForID:(NSString *)fbid;
@implementation BOGSecondViewController
@synthesize activityTextView = _activityTextView;
+@synthesize friendCache = _friendCache;
- (id)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil {
self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil];
@@ -47,9 +51,23 @@ - (void)viewDidAppear:(BOOL)animated {
if (FBSession.activeSession.isOpen) {
[self loadData];
- self.inviteButton.enabled = YES;
+
+ // we use frictionless requests, so let's get a cache and request the
+ // current list of frictionless friends before enabling the invite button
+ if (!self.friendCache) {
+ self.friendCache = [[FBFrictionlessRecipientCache alloc] init];
+ [self.friendCache prefetchAndCacheForSession:nil
+ completionHandler:^(FBRequestConnection *connection, id result, NSError *error) {
+
+ self.inviteButton.enabled = YES;
+ }];
+ } else {
+ // if we already have a primed cache, let's just run with it
+ self.inviteButton.enabled = YES;
+ }
} else {
self.inviteButton.enabled = NO;
+ self.friendCache = nil;
// display the message that we have
UIAlertView *alert = [[UIAlertView alloc] initWithTitle:@"Social Features Disabled"
@@ -88,9 +106,11 @@ - (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interface
// application.
- (void)friendPickerViewControllerSelectionDidChange:(FBFriendPickerViewController *)friendPicker {
+ self.activityTextView.text = @"";
if (friendPicker.selection.count) {
- self.activityTextView.text = @"";
[self updateActivityForID:[[friendPicker.selection objectAtIndex:0] id]];
+ } else {
+ self.fbidSelection = nil;
}
}
@@ -106,6 +126,9 @@ - (BOOL)friendPickerViewController:(FBFriendPickerViewController *)friendPicker
// accomplishes this by fetching the "or" and "and" actions for the selected user.
- (void)updateActivityForID:(NSString *)fbid {
+ // keep track of the selction
+ self.fbidSelection = fbid;
+
// create a request for the "or" activity
FBRequest *orActivity = [FBRequest requestForGraphPath:[NSString stringWithFormat:@"%@/fb_sample_boolean_og:or", fbid]];
[orActivity.parameters setObject:@"U" forKey:@"date_format"];
@@ -183,11 +206,14 @@ - (void)updateActivityForID:(NSString *)fbid {
}
- (IBAction)clickInviteFriends:(id)sender {
+ // if there is a selected user, seed the dialog with that user
+ NSDictionary *parameters = self.fbidSelection ? @{@"to":self.fbidSelection} : nil;
[FBWebDialogs presentRequestsDialogModallyWithSession:nil
message:@"Please come rock the logic with me!"
title:@"Invite a Friend"
- parameters:nil
- handler:nil];
+ parameters:parameters
+ handler:nil
+ friendCache:self.friendCache];
}
@end
View
2  scripts/build_distribution.sh
@@ -38,7 +38,7 @@ FB_SDK_BUILD_PACKAGE_DOCS=$FB_SDK_BUILD_PACKAGE/Library/Developer/Shared/Documen
# Call out to build prerequisites.
#
if is_outermost_build; then
- . $FB_SDK_SCRIPT/build_framework.sh
+ . $FB_SDK_SCRIPT/build_framework.sh -t -c Release
. $FB_SDK_SCRIPT/build_documentation.sh
fi
echo Building Distribution.
View
13 scripts/build_framework.sh
@@ -21,8 +21,9 @@
. ${FB_SDK_SCRIPT:-$(dirname $0)}/common.sh
# process options, valid arguments -c [Debug|Release] -n
-BUILDCONFIGURATION=Release
-while getopts ":nc:" OPTNAME
+BUILDCONFIGURATION=Debug
+NOEXTRAS=1
+while getopts ":ntc:" OPTNAME
do
case "$OPTNAME" in
"c")
@@ -31,10 +32,14 @@ do
"n")
NOEXTRAS=1
;;
+ "t")
+ NOEXTRAS=0
+ ;;
"?")
echo "$0 -c [Debug|Release] -n"
- echo " -c sets configuration"
- echo " -n no test run"
+ echo " -c sets configuration (default=Debug)"
+ echo " -n no test run (default)"
+ echo " -t test run"
die
;;
":")
View
2  scripts/build_samples.sh
@@ -31,7 +31,7 @@ progress_message Building samples.
# Call out to build .framework
#
if is_outermost_build; then
- . $FB_SDK_SCRIPT/build_framework.sh -n
+ . $FB_SDK_SCRIPT/build_framework.sh -n -c Release
fi
# -----------------------------------------------------------------------------
View
39 src/FBDialog.m
@@ -45,7 +45,9 @@ static BOOL FBIsDeviceIPad() {
///////////////////////////////////////////////////////////////////////////////////////////////////
-@implementation FBDialog
+@implementation FBDialog {
+ BOOL _everShown;
+}
@synthesize delegate = _delegate,
params = _params;
@@ -255,21 +257,22 @@ - (void)postDismissCleanup {
[self removeObservers];
[self removeFromSuperview];
[_modalBackgroundView removeFromSuperview];
+
+ // this method call could cause a self-cleanup, and needs to really happen "last"
+ // If the dialog has been closed, then we need to cancel the order to open it.
+ // This happens in the case of a frictionless request, see webViewDidFinishLoad for details
+ [NSObject cancelPreviousPerformRequestsWithTarget:self
+ selector:@selector(showWebView)
+ object:nil];
}
- (void)dismiss:(BOOL)animated {
[self dialogWillDisappear];
- // If the dialog has been closed, then we need to cancel the order to open it.
- // This happens in the case of a frictionless request, see webViewDidFinishLoad for details
- [NSObject cancelPreviousPerformRequestsWithTarget:self
- selector:@selector(showWebView)
- object:nil];
-
[_loadingURL release];
_loadingURL = nil;
- if (animated) {
+ if (animated && _everShown) {
[UIView beginAnimations:nil context:nil];
[UIView setAnimationDuration:kTransitionDuration];
[UIView setAnimationDelegate:self];
@@ -326,6 +329,7 @@ - (id)init {
_delegate = nil;
_loadingURL = nil;
_showingKeyboard = NO;
+ _everShown = NO;
self.backgroundColor = [UIColor clearColor];
self.autoresizesSubviews = YES;
@@ -418,8 +422,9 @@ - (void)showWebView {
[UIView setAnimationDidStopSelector:@selector(bounce1AnimationStopped)];
self.transform = CGAffineTransformScale([self transformForOrientation], 1.1, 1.1);
[UIView commitAnimations];
-
- [self dialogWillAppear];
+
+ _everShown = YES;
+ [self dialogWillAppear];
[self addObservers];
}
@@ -665,11 +670,19 @@ - (void)dialogWillDisappear {
}
- (void)dialogDidSucceed:(NSURL *)url {
+ // retain self for the life of this method, in case we are released by a client
+ id me = [self retain];
- if ([_delegate respondsToSelector:@selector(dialogCompleteWithUrl:)]) {
- [_delegate dialogCompleteWithUrl:url];
+ @try {
+ // call into client code
+ if ([_delegate respondsToSelector:@selector(dialogCompleteWithUrl:)]) {
+ [_delegate dialogCompleteWithUrl:url];
+ }
+
+ [self dismissWithSuccess:YES animated:YES];
+ } @finally {
+ [me release];
}
- [self dismissWithSuccess:YES animated:YES];
}
- (void)dialogDidCancel:(NSURL *)url {
View
29 src/FBFrictionlessDialogSupportDelegate.h
@@ -0,0 +1,29 @@
+/*
+ * Copyright 2013 Facebook
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#import "FBWebDialogs.h"
+
+@class FBFrictionlessRequestSettings;
+
+// this protocol is internal wiring which
+// enables frictionless uses of web dialogs
+// methods in this protocol are called after the will-present method
+// in the base protocol
+@protocol FBFrictionlessDialogSupportDelegate<FBWebDialogsDelegate>
+@required
+- (BOOL)frictionlessShouldMakeViewInvisible;
+- (FBFrictionlessRequestSettings *)frictionlessSettings;
+@end
View
86 src/FBFrictionlessRecipientCache.h
@@ -0,0 +1,86 @@
+/*
+ * Copyright 2013 Facebook
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+#import <Foundation/Foundation.h>
+#import "FBWebDialogs.h"
+#import "FBRequest.h"
+#import "FBCacheDescriptor.h"
+
+/*!
+ @class FBFrictionlessRecipientCache
+
+ @abstract
+ Maintains a cache of friends that can recieve application requests from the user in
+ using the frictionless feature of the requests web dialog.
+
+ This class follows the `FBCacheDescriptor` pattern used elsewhere in the SDK, and applications may call
+ one of the prefetchAndCacheForSession methods to fetch a friend list prior to the
+ point where a dialog is presented. The cache is also updated with each presentation of the request
+ dialog using the cache instance.
+ */
+@interface FBFrictionlessRecipientCache : FBCacheDescriptor<FBWebDialogsDelegate>
+
+/*!
+ @abstract
+ Initializes an empty cache instance
+ */
+- (id)init;
+
+/*! @abstract An array containing the list of known FBIDs for recipients enabled for frictionless requests */
+@property (nonatomic, readwrite, copy) NSArray *recipientIDs;
+
+/*!
+ @abstract
+ Checks to see if a given user or FBID for a user is known to be enabled for
+ frictionless requestests
+
+ @param user An NSString, NSNumber of `FBGraphUser` representing a user to check
+ */
+- (BOOL)isFrictionlessRecipient:(id)user;
+
+/*!
+ @abstract
+ Checks to see if a collection of users or FBIDs for users are known to be enabled for
+ frictionless requestests
+
+ @param users An NSArray of NSString, NSNumber of `FBGraphUser` objects
+ representing users to check
+ */
+- (BOOL)areFrictionlessRecipients:(NSArray*)users;
+
+/*!
+ @abstract
+ Issues a request and fills the cache with a list of users to use for frictionless requests
+
+ @param session The session to use for the request; nil indicates that the Active Session should
+ be used
+ */
+- (void)prefetchAndCacheForSession:(FBSession *)session;
+
+/*!
+ @abstract
+ Issues a request and fills the cache with a list of users to use for frictionless requests
+
+ @param session The session to use for the request; nil indicates that the Active Session should
+ be used
+
+ @param handler An optional completion handler, called when the request for cached users has
+ completed. It can be useful to use the handler to enable UI or perform other request-related
+ operations, after the cache is populated.
+ */
+- (void)prefetchAndCacheForSession:(FBSession *)session
+ completionHandler:(FBRequestHandler)handler;
+
+@end
View
129 src/FBFrictionlessRecipientCache.m
@@ -0,0 +1,129 @@
+/*
+ * Copyright 2013 Facebook
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#import "FBFrictionlessDialogSupportDelegate.h"
+#import "FBFrictionlessRequestSettings.h"
+#import "FBUtility.h"
+#import "FBSession+Internal.h"
+#import "FBSBJSON.h"
+#import "FBFrictionlessRecipientCache.h"
+
+@interface FBFrictionlessRecipientCache () <FBFrictionlessDialogSupportDelegate>
+@property (nonatomic, readwrite) BOOL frictionlessShouldMakeViewInvisible;
+@property (nonatomic, readwrite, retain) FBFrictionlessRequestSettings *frictionlessSettings;
+@end
+
+@implementation FBFrictionlessRecipientCache
+
+@synthesize frictionlessSettings = _frictionlessSettings;
+@synthesize frictionlessShouldMakeViewInvisible = _frictionlessShouldMakeViewInvisible;
+
+- (id)init {
+ self = [super init];
+ if (self) {
+ self.frictionlessSettings = [[[FBFrictionlessRequestSettings alloc] init] autorelease];
+ [self.frictionlessSettings enableWithFacebook:nil]; // sets the flag on
+ }
+ return self;
+}
+
+- (void)dealloc {
+ self.frictionlessSettings = nil;
+ [super dealloc];
+}
+
+- (NSArray *)recipientIDs {
+ return self.frictionlessSettings.recipientIDs;
+}
+
+- (void)setRecipientIDs:(NSArray *)recipientIDs {
+ [self.frictionlessSettings updateRecipientCacheWithRecipients:recipientIDs];
+}
+
+- (BOOL)isFrictionlessRecipient:(id)fbid {
+ // we support NSString, NSNumber and dictionary with id-key of string or number
+ return [self.frictionlessSettings isFrictionlessEnabledForRecipient:[FBUtility stringFBIDFromObject:fbid]];
+}
+
+- (BOOL)areFrictionlessRecipients:(NSArray*)fbids {
+ // we handle arrays of NSString, NSNumber, or dictionary with id-key of string or number
+ // and return NO on anything else
+ for (id fbid in fbids) {
+ // if we miss our cache once, we fail the set
+ if (![self isFrictionlessRecipient:fbid]) {
+ return NO;
+ }
+ }
+ return YES;
+}
+
+- (void)prefetchAndCacheForSession:(FBSession *)session {
+ [self prefetchAndCacheForSession:session
+ completionHandler:nil];
+}
+
+- (void)prefetchAndCacheForSession:(FBSession *)session
+ completionHandler:(FBRequestHandler)handler {
+
+ // if a session isn't specified, fall back to active session when available
+ if (!session) {
+ session = [FBSession activeSessionIfOpen];
+ }
+
+ [[[[FBRequest alloc] initWithSession:session
+ graphPath:@"me/apprequestformerrecipients"]
+ autorelease]
+ startWithCompletionHandler:^(FBRequestConnection *connection, id result, NSError *error) {
+ [self.frictionlessSettings updateRecipientCacheWithRequestResult:result];
+ if (handler) {
+ handler(connection, result, error);
+ }
+ }];
+}
+
+- (void)webDialogsWillPresentDialog:(NSString *)dialog
+ parameters:(NSMutableDictionary *)parameters
+ session:(FBSession *)session {
+
+ // this method adds fricitonless behaviors to a dialog presentation, if the
+ // dialog is an apprequests dialog; noop if a non-apprequests dialog
+ if ([dialog isEqualToString:@"apprequests"]) {
+
+ // frictionless parameter means:
+ // a. show the "Don't show this again for these friends" checkbox
+ // b. if the developer is sending a targeted request, then skip the loading screen
+ [parameters setValue:@"1" forKey:@"frictionless"];
+ // get_frictionless_recipients parameter means: request the frictionless recipient list encoded in the success url
+ [parameters setValue:@"1" forKey:@"get_frictionless_recipients"];
+
+ self.frictionlessShouldMakeViewInvisible = NO;
+
+ // set invisible if all recipients are enabled for frictionless requests
+ id fbid = [parameters objectForKey:@"to"];
+ if (fbid != nil) {
+ // if value parses as a json array expression get the list that way
+ FBSBJsonParser *parser = [[[FBSBJsonParser alloc] init] autorelease];
+ id fbids = [parser objectWithString:fbid];
+ if (![fbids isKindOfClass:[NSArray class]]) {
+ // otherwise seperate by commas (handles the singleton case too)
+ fbids = [fbid componentsSeparatedByString:@","];
+ }
+ self.frictionlessShouldMakeViewInvisible = [self.frictionlessSettings isFrictionlessEnabledForRecipients:fbids];
+ }
+ }
+}
+
+@end
View
11 src/FBFrictionlessRequestSettings.h
@@ -17,6 +17,7 @@
#import <Foundation/Foundation.h>
@class Facebook;
+@class FBRequest;
/**
* Do not use this interface directly, instead, use methods in Facebook.h
@@ -37,6 +38,11 @@
@property(nonatomic, readonly) BOOL enabled;
/**
+ * NSArray of recipients
+ */
+@property(nonatomic, readonly) NSArray *recipientIDs;
+
+/**
* Enable frictionless request sending by the sdk; this means:
* 1. query and cache the current set of frictionless recipients
* 2. flag other facets of the sdk to behave in a frictionless way
@@ -55,6 +61,11 @@
- (void)updateRecipientCacheWithRecipients:(NSArray*)ids;
/**
+ * Update the recipient cache, using a request result
+ */
+- (void)updateRecipientCacheWithRequestResult:(id)result;
+
+/**
* Given an fbID for a user, indicates whether user is enabled for
* frictionless calls
*/
View
26 src/FBFrictionlessRequestSettings.m
@@ -22,8 +22,8 @@
//
@interface FBFrictionlessRequestSettings () <FBRequestDelegate>
-@property (readwrite, retain) NSArray * allowedRecipients;
-@property (readwrite, retain) FBRequest* activeRequest;
+@property (readwrite, retain) NSArray *allowedRecipients;
+@property (readwrite, retain) FBRequest *activeRequest;
@end
@@ -47,7 +47,9 @@ - (id)init {
- (void)enableWithFacebook:(Facebook*)facebook {
if (!_enabled) {
_enabled = YES;
- [self reloadRecipientCacheWithFacebook:facebook];
+ if (facebook) {
+ [self reloadRecipientCacheWithFacebook:facebook];
+ }
}
}
@@ -60,6 +62,10 @@ - (void)reloadRecipientCacheWithFacebook:(Facebook *)facebook {
}
}
+- (NSArray *)recipientIDs {
+ return self.allowedRecipients;
+}
+
- (void)updateRecipientCacheWithRecipients:(NSArray*)ids {
// if setting recipients directly, no need to complete pending request
self.activeRequest = nil;
@@ -112,12 +118,7 @@ - (BOOL)isFrictionlessEnabledForRecipients:(NSArray*)fbids {
return YES;
}
-///////////////////////////////////////////////////////////////////////////////////////////////////
-// FBRequestDelegate
-
-- (void)request:(FBRequest *)request
- didLoad:(id)result {
-
+- (void)updateRecipientCacheWithRequestResult:(id)result {
// a little request bookkeeping
self.activeRequest = nil;
@@ -133,6 +134,13 @@ - (void)request:(FBRequest *)request
self.allowedRecipients = recipients;
}
+///////////////////////////////////////////////////////////////////////////////////////////////////
+// FBRequestDelegate
+- (void)request:(FBRequest *)request
+ didLoad:(id)result {
+ [self updateRecipientCacheWithRequestResult:result];
+}
+
- (void)request:(FBRequest *)request didFailWithError:(NSError *)error {
// if the request to load the frictionless recipients fails, proceed without updating
// the recipients cache; the cache may become invalid due to a failed update or other reasons
View
33 src/FBRequestConnection.m
@@ -76,6 +76,9 @@ - (id) initWithRequest:(FBRequest *)request
completionHandler:(FBRequestHandler)handler
batchEntryName:(NSString *)name;
+- (void)invokeCompletionHandlerForConnection:(FBRequestConnection *)connection
+ withResults:(id)results
+ error:(NSError *)error;
@end
@implementation FBRequestMetadata
@@ -103,6 +106,14 @@ - (void) dealloc {
[super dealloc];
}
+- (void)invokeCompletionHandlerForConnection:(FBRequestConnection *)connection
+ withResults:(id)results
+ error:(NSError *)error {
+ if (self.completionHandler) {
+ self.completionHandler(connection, results, error);
+ }
+}
+
- (NSString*)description {
return [NSString stringWithFormat:@"<%@: %p, batchEntryName: %@, completionHandler: %p, request: %@>",
NSStringFromClass([self class]),
@@ -1187,7 +1198,7 @@ - (void)completeWithResults:(NSArray *)results
// For the renewSystemAuthorization calls below, we want the renew call
// to finish before executing any further logic. For now, the "further
- // logic" is `metadata.completionHandler()` so every code path
+ // logic" is `[metadata invokeCompletionHandlerForConnection:withResults:error:]` so every code path
// below should result in its invocation.
if ((metadata.request.session.accessTokenData.loginType == FBSessionLoginTypeSystemAccount) &&
[self isInsufficientPermissionError:error resultIndex:resultIndex]) {
@@ -1195,7 +1206,7 @@ - (void)completeWithResults:(NSArray *)results
// OS's understanding of current permissions
[[FBSystemAccountStoreAdapter sharedInstance]
renewSystemAuthorization:^(ACAccountCredentialRenewResult result, NSError *error) {
- metadata.completionHandler(self, body, unpackedError);
+ [metadata invokeCompletionHandlerForConnection:self withResults:body error:unpackedError];
}];
} else if ([self isInvalidSessionError:itemError resultIndex:resultIndex]) {
// For invalid sessions, we also need to close the session before
@@ -1215,19 +1226,21 @@ - (void)completeWithResults:(NSArray *)results
handler:^(NSString *oauthToken, NSError *accountStoreError) {
if (oauthToken) {
[session refreshAccessToken:oauthToken expirationDate:[NSDate distantFuture]];
- metadata.completionHandler(self, body, [FBErrorUtility fberrorForRetry:unpackedError]);
+ [metadata invokeCompletionHandlerForConnection:self
+ withResults:body
+ error:[FBErrorUtility fberrorForRetry:unpackedError]];
} else {
// This shouldn't happen but if the request fails,
// revert to the original flow of closing session
// and surfacing the original error.
[metadata.request.session closeAndClearTokenInformation:unpackedError];
- metadata.completionHandler(self, body, unpackedError);
+ [metadata invokeCompletionHandlerForConnection:self withResults:body error:unpackedError];
}
}
];
} else {
[metadata.request.session closeAndClearTokenInformation:unpackedError];
- metadata.completionHandler(self, body, unpackedError);
+ [metadata invokeCompletionHandlerForConnection:self withResults:body error:unpackedError];
}
}];
} else if ([self isPasswordChangeError:itemError resultIndex:resultIndex]) {
@@ -1238,19 +1251,19 @@ - (void)completeWithResults:(NSArray *)results
// that we want to force a blocking renew until success.
[FBSystemAccountStoreAdapter sharedInstance].forceBlockingRenew = YES;
[metadata.request.session closeAndClearTokenInformation:unpackedError];
- metadata.completionHandler(self, body, unpackedError);
+ [metadata invokeCompletionHandlerForConnection:self withResults:body error:unpackedError];
} else {
// For other invalid session cases, we can simply issue the renew now
// to update the system account's world view.
[[FBSystemAccountStoreAdapter sharedInstance]
renewSystemAuthorization:^(ACAccountCredentialRenewResult result, NSError *error) {
[metadata.request.session closeAndClearTokenInformation:unpackedError];
- metadata.completionHandler(self, body, unpackedError);
+ [metadata invokeCompletionHandlerForConnection:self withResults:body error:unpackedError];
}];
}
} else {
[metadata.request.session closeAndClearTokenInformation:unpackedError];
- metadata.completionHandler(self, body, unpackedError);
+ [metadata invokeCompletionHandlerForConnection:self withResults:body error:unpackedError];
}
} else if ([metadata.request.session shouldExtendAccessToken]) {
// If we have not had the opportunity to piggyback a token-extension request,
@@ -1260,9 +1273,9 @@ - (void)completeWithResults:(NSArray *)results
connection:connection];
[connection start];
[connection release];
- metadata.completionHandler(self, body, unpackedError);
+ [metadata invokeCompletionHandlerForConnection:self withResults:body error:unpackedError];
} else {
- metadata.completionHandler(self, body, unpackedError);
+ [metadata invokeCompletionHandlerForConnection:self withResults:body error:unpackedError];
}
}
}
View
2  src/FBSDKVersion.h
@@ -1,2 +1,2 @@
-#define FB_IOS_SDK_VERSION_STRING @"3.2.0.0"
+#define FB_IOS_SDK_VERSION_STRING @"3.2.1"
#define FB_IOS_SDK_MIGRATION_BUNDLE @"fbsdk:20130120"
View
124 src/FBWebDialogs.h
@@ -16,7 +16,9 @@
#import <UIKit/UIKit.h>
+@class FBFrictionlessRecipientCache;
@class FBSession;
+@protocol FBWebDialogsDelegate;
/*!
@typedef FBWebDialogResult enum
@@ -73,6 +75,30 @@ typedef void (^FBWebDialogHandler)(
/*!
@abstract
+ Presents a Facebook web dialog (https://developers.facebook.com/docs/reference/dialogs/)
+ such as feed or apprequest.
+
+ @param session Represents the session to use for the dialog. May be nil, which uses
+ the active session if present, or returns NO, if not.
+
+ @param dialog Represents the dialog or method name, such as @"feed"
+
+ @param parameters A dictionary of parameters to be passed to the dialog
+
+ @param handler An optional handler that will be called when the dialog is dismissed. Note,
+ that if the method returns NO, the handler is not called. May be nil.
+
+ @param delegate An optional delegate to allow for advanced processing of web based
+ dialogs. See 'FBWebDialogsDelegate' for more details.
+ */
++ (void)presentDialogModallyWithSession:(FBSession *)session
+ dialog:(NSString *)dialog
+ parameters:(NSDictionary *)parameters
+ handler:(FBWebDialogHandler)handler
+ delegate:(id<FBWebDialogsDelegate>)delegate;
+
+/*!
+ @abstract
Presents a Facebook apprequest dialog.
@param session Represents the session to use for the dialog. May be nil, which uses
@@ -94,6 +120,32 @@ typedef void (^FBWebDialogHandler)(
/*!
@abstract
+ Presents a Facebook apprequest dialog.
+
+ @param session Represents the session to use for the dialog. May be nil, which uses
+ the active session if present.
+
+ @param message The required message for the dialog.
+
+ @param title An optional title for the dialog.
+
+ @param parameters A dictionary of additional parameters to be passed to the dialog. May be nil
+
+ @param handler An optional handler that will be called when the dialog is dismissed. May be nil.
+
+ @param friend cache An optional cache object used to enable frictionless sharing for a known set of friends. The
+ cache instance should be preserved for the life of the session and reused for multiple calls to the present method.
+ As the users set of friends enabled for frictionless sharing changes, this method auto-updates the cache.
+ */
++ (void)presentRequestsDialogModallyWithSession:(FBSession *)session
+ message:(NSString *)message
+ title:(NSString *)title
+ parameters:(NSDictionary *)parameters
+ handler:(FBWebDialogHandler)handler
+ friendCache:(FBFrictionlessRecipientCache *)friendCache;
+
+/*!
+ @abstract
Presents a Facebook feed dialog.
@param session Represents the session to use for the dialog. May be nil, which uses
@@ -108,3 +160,75 @@ typedef void (^FBWebDialogHandler)(
handler:(FBWebDialogHandler)handler;
@end
+
+/*!
+ @protocol
+
+ @abstract
+ The `FBWebDialogsDelegate` protocol enables the plugging of advanced behaviors into
+ the presentation flow of a Facebook web dialog. Advanced uses include modification
+ of parameters and application-level handling of links on the dialog. The
+ `FBFrictionlessRequestFriendCache` class implements this protocol to add frictionless
+ behaviors to a presentation of the request dialog.
+ */
+@protocol FBWebDialogsDelegate<NSObject>
+
+@optional
+
+/*!
+ @abstract
+ Called prior to the presentation of a web dialog
+
+ @param dialog A string representing the method or dialog name of the dialog being presented.
+
+ @param parameters A mutable dictionary of parameters which will be sent to the dialog.
+
+ @param session The session object to use with the dialog.
+ */
+- (void)webDialogsWillPresentDialog:(NSString *)dialog
+ parameters:(NSMutableDictionary *)parameters
+ session:(FBSession *)session;
+
+/*!
+ @abstract
+ Called when the user of a dialog clicks a link that would cause a transition away from the application.
+ Your application may handle this method, and return NO if the URL handling will be performed by the application.
+
+ @param dialog A string representing the method or dialog name of the dialog being presented.
+
+ @param parameters A dictionary of parameters which were sent to the dialog.
+
+ @param session The session object to use with the dialog.
+
+ @param url The url in question, which will not be handled by the SDK if this method NO
+ */
+- (BOOL)webDialogsDialog:(NSString *)dialog
+ parameters:(NSDictionary *)parameters
+ session:(FBSession *)session
+ shouldAutoHandleURL:(NSURL *)url;
+
+/*!
+ @abstract
+ Called when the dialog is about to be dismissed
+
+ @param dialog A string representing the method or dialog name of the dialog being presented.
+
+ @param parameters A dictionary of parameters which were sent to the dialog.
+
+ @param session The session object to use with the dialog.
+
+ @param result A pointer to a result, which may be read or changed by the handling method as needed
+
+ @param url A pointer to a pointer to a URL representing the URL returned by the dialog, which may be read or changed by this mehthod
+
+ @param error A pointer to a pointer to an error object which may be read or changed by this method as needed
+ */
+- (void)webDialogsWillDismissDialog:(NSString *)dialog
+ parameters:(NSDictionary *)parameters
+ session:(FBSession *)session
+ result:(FBWebDialogResult *)result
+ url:(NSURL **)url
+ error:(NSError **)error;
+
+@end
+
View
116 src/FBWebDialogs.m
@@ -23,25 +23,37 @@
#import "FBDialog.h"
#import "FBSDKVersion.h"
#import "FBViewController+Internal.h"
+#import "FBFrictionlessDialogSupportDelegate.h"
+#import "FBFrictionlessRequestSettings.h"
+#import "FBFrictionlessRecipientCache.h"
static NSString* dialogBaseURL = @"https://m." FB_BASE_URL "/dialog/";
// this is an implementation detail class which acts
// as the delegate in or to map to a block
-@interface FBWebDialogDelegate : NSObject <FBDialogDelegate>
+@interface FBWebDialogInternalDelegate : NSObject <FBDialogDelegate>
+
@property (nonatomic, copy) FBWebDialogHandler handler;
@property (nonatomic, retain) FBDialog *dialog;
+@property (nonatomic, copy) NSString *dialogMethod;
+@property (nonatomic, copy) NSDictionary *parameters;
+@property (nonatomic, retain) FBSession *session;
+@property (nonatomic, assign) id<FBWebDialogsDelegate> delegate;
- (void)goRetainYourself;
@end
-@implementation FBWebDialogDelegate {
+@implementation FBWebDialogInternalDelegate {
BOOL _isSelfRetained;
}
@synthesize handler = _handler;
@synthesize dialog = _dialog;
+@synthesize dialogMethod = _dialogMethod;
+@synthesize parameters = _parameters;
+@synthesize session = _session;
+@synthesize delegate = _delegate;
- (id)init {
self = [super init];
@@ -54,6 +66,10 @@ - (id)init {
- (void)dealloc {
self.handler = nil;
self.dialog = nil;
+ self.dialogMethod = nil;
+ self.parameters = nil;
+ self.session = nil;
+ // self.delegate is assign per the pattern
[super dealloc];
}
@@ -65,6 +81,8 @@ - (void)goRetainYourself {
}
- (void)releaseSelfIfNeeded {
+ self.handler = nil; // insurance
+ self.delegate = nil; // insurance
self.dialog = nil;
if (_isSelfRetained) {
[self autorelease];
@@ -75,6 +93,21 @@ - (void)releaseSelfIfNeeded {
- (void)completeWithResult:(FBWebDialogResult)result
url:(NSURL *)url
error:(NSError *)error {
+
+ // optional delegate invocation
+ if ([self.delegate respondsToSelector:@selector(webDialogsWillDismissDialog:parameters:session:result:url:error:)]) {
+ [self.delegate webDialogsWillDismissDialog:self.dialogMethod
+ parameters:self.parameters
+ session:self.session
+ result:&result // may mutate
+ url:&url // may mutate
+ error:&error]; // may mutate
+
+ // important! we must nil the delegate before nil'ing the handler, to preserve
+ // the case where the calling app is using a block to retain the delegate
+ self.delegate = nil;
+ }
+
if (self.handler) {
self.handler(result, url, error);
self.handler = nil;
@@ -83,6 +116,18 @@ - (void)completeWithResult:(FBWebDialogResult)result
// non-terminal delegate methods
+- (BOOL)dialog:(FBDialog*)dialog shouldOpenURLInExternalBrowser:(NSURL *)url {
+ BOOL result = YES;
+ // optional delegate invocation
+ if ([self.delegate respondsToSelector:@selector(webDialogsDialog:parameters:session:shouldAutoHandleURL:)]) {
+ result = [self.delegate webDialogsDialog:self.dialogMethod
+ parameters:self.parameters
+ session:self.session
+ shouldAutoHandleURL:url];
+ }
+ return result;
+}
+
- (void)dialogCompleteWithUrl:(NSURL *)url {
[self completeWithResult:FBWebDialogResultDialogCompleted
url:url
@@ -126,6 +171,18 @@ + (void)presentDialogModallyWithSession:(FBSession *)session
dialog:(NSString *)dialog
parameters:(NSDictionary *)parameters
handler:(FBWebDialogHandler)handler {
+ [FBWebDialogs presentDialogModallyWithSession:session
+ dialog:dialog
+ parameters:parameters
+ handler:handler
+ delegate:nil];
+}
+
++ (void)presentDialogModallyWithSession:(FBSession *)session
+ dialog:(NSString *)dialog
+ parameters:(NSDictionary *)parameters
+ handler:(FBWebDialogHandler)handler
+ delegate:(id<FBWebDialogsDelegate>)delegate {
NSString *dialogURL = [dialogBaseURL stringByAppendingString:dialog];
@@ -156,18 +213,40 @@ + (void)presentDialogModallyWithSession:(FBSession *)session
forKey:@"app_id"];
}
- FBWebDialogDelegate *delegate = [[[FBWebDialogDelegate alloc] init] autorelease];
- delegate.handler = handler;
- [delegate goRetainYourself];
+ BOOL isViewInvisible = NO;
+ FBFrictionlessRequestSettings *frictionlessSettings = nil;
+
+ // optional delegate invocation
+ if ([delegate respondsToSelector:@selector(webDialogsWillPresentDialog:parameters:session:)]) {
+ [delegate webDialogsWillPresentDialog:dialog
+ parameters:parametersImpl
+ session:session];
+
+ // Important! Per the spec of the internal protocol, calls to FBFrictionlessDialogSupportDelegate
+ // methods must be made after the base delegate call to webDialogsWillPresentDialog
+ if ([delegate conformsToProtocol:@protocol(FBFrictionlessDialogSupportDelegate)]) {
+ id<FBFrictionlessDialogSupportDelegate> supportDelegate = (id<FBFrictionlessDialogSupportDelegate>)delegate;
+ isViewInvisible = supportDelegate.frictionlessShouldMakeViewInvisible;
+ frictionlessSettings = supportDelegate.frictionlessSettings;
+ }
+ }
+
+ FBWebDialogInternalDelegate *innerDelegate = [[[FBWebDialogInternalDelegate alloc] init] autorelease];
+ innerDelegate.dialogMethod = dialog;
+ innerDelegate.parameters = parametersImpl;
+ innerDelegate.session = session;
+ innerDelegate.handler = handler;
+ innerDelegate.delegate = delegate;
+ [innerDelegate goRetainYourself];
FBDialog *d = [[FBDialog alloc] initWithURL:dialogURL
params:parametersImpl
- isViewInvisible:NO
- frictionlessSettings:nil
- delegate:delegate];
+ isViewInvisible:isViewInvisible
+ frictionlessSettings:frictionlessSettings
+ delegate:innerDelegate];
// this reference keeps the dialog alive as needed
- delegate.dialog = d;
+ innerDelegate.dialog = d;
[d show];
[d release];
}
@@ -177,6 +256,20 @@ + (void)presentRequestsDialogModallyWithSession:(FBSession *)session
title:(NSString *)title
parameters:(NSDictionary *)parameters
handler:(FBWebDialogHandler)handler {
+ [FBWebDialogs presentRequestsDialogModallyWithSession:session
+ message:message
+ title:title
+ parameters:parameters
+ handler:handler
+ friendCache:nil];
+}
+
++ (void)presentRequestsDialogModallyWithSession:(FBSession *)session
+ message:(NSString *)message
+ title:(NSString *)title
+ parameters:(NSDictionary *)parameters
+ handler:(FBWebDialogHandler)handler
+ friendCache:(FBFrictionlessRecipientCache *)friendCache {
NSMutableDictionary *parametersImpl = [NSMutableDictionary dictionary];
@@ -193,11 +286,12 @@ + (void)presentRequestsDialogModallyWithSession:(FBSession *)session
if (title) {
[parametersImpl setObject:title forKey:@"title"];
}
-
+
[FBWebDialogs presentDialogModallyWithSession:session
dialog:@"apprequests"
parameters:parametersImpl
- handler:handler];
+ handler:handler
+ delegate:friendCache];
}
+ (void)presentFeedDialogModallyWithSession:(FBSession *)session
View
1  src/FacebookSDK.h
@@ -39,6 +39,7 @@
#import "FBCacheDescriptor.h"
#import "FBNativeDialogs.h"
#import "FBWebDialogs.h"
+#import "FBFrictionlessRecipientCache.h"
/*!
@header
View
12 src/facebook-ios-sdk.xcodeproj/project.pbxproj
@@ -29,6 +29,9 @@
841062471582502B00FC561C /* FBPlacePickerCacheDescriptor.m in Sources */ = {isa = PBXBuildFile; fileRef = 84D0A6551581A1A600A2FA5E /* FBPlacePickerCacheDescriptor.m */; };
84137158152B94B000B2C0E1 /* FBSessionManualTokenCachingStrategy.h in Headers */ = {isa = PBXBuildFile; fileRef = 84137156152B94B000B2C0E1 /* FBSessionManualTokenCachingStrategy.h */; };
84137159152B94B000B2C0E1 /* FBSessionManualTokenCachingStrategy.m in Sources */ = {isa = PBXBuildFile; fileRef = 84137157152B94B000B2C0E1 /* FBSessionManualTokenCachingStrategy.m */; };
+ 841FA0C416DFF934002A51EA /* FBFrictionlessRecipientCache.h in Headers */ = {isa = PBXBuildFile; fileRef = 841FA0C316DFF934002A51EA /* FBFrictionlessRecipientCache.h */; settings = {ATTRIBUTES = (Public, ); }; };
+ 841FA0C616DFF95F002A51EA /* FBFrictionlessRecipientCache.m in Sources */ = {isa = PBXBuildFile; fileRef = 841FA0C516DFF95F002A51EA /* FBFrictionlessRecipientCache.m */; };
+ 841FA0C816E1236F002A51EA /* FBFrictionlessDialogSupportDelegate.h in Headers */ = {isa = PBXBuildFile; fileRef = 841FA0C716E1236F002A51EA /* FBFrictionlessDialogSupportDelegate.h */; };
8425CFA81639914800472F98 /* NSError+FBError.h in Resources */ = {isa = PBXBuildFile; fileRef = 8425CFA71639914800472F98 /* NSError+FBError.h */; };
8425CFAB1639AF5000472F98 /* NSError+FBError.m in Sources */ = {isa = PBXBuildFile; fileRef = 8425CFAA1639AF5000472F98 /* NSError+FBError.m */; };
8425CFAC1639AF5000472F98 /* NSError+FBError.m in Sources */ = {isa = PBXBuildFile; fileRef = 8425CFAA1639AF5000472F98 /* NSError+FBError.m */; };
@@ -216,6 +219,9 @@
840FDCBC152B5D9200F9C927 /* FBFrictionlessRequestSettings.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = FBFrictionlessRequestSettings.m; sourceTree = "<group>"; };
84137156152B94B000B2C0E1 /* FBSessionManualTokenCachingStrategy.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = FBSessionManualTokenCachingStrategy.h; sourceTree = "<group>"; };
84137157152B94B000B2C0E1 /* FBSessionManualTokenCachingStrategy.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = FBSessionManualTokenCachingStrategy.m; sourceTree = "<group>"; };
+ 841FA0C316DFF934002A51EA /* FBFrictionlessRecipientCache.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = FBFrictionlessRecipientCache.h; sourceTree = "<group>"; };
+ 841FA0C516DFF95F002A51EA /* FBFrictionlessRecipientCache.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = FBFrictionlessRecipientCache.m; sourceTree = "<group>"; };
+ 841FA0C716E1236F002A51EA /* FBFrictionlessDialogSupportDelegate.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = FBFrictionlessDialogSupportDelegate.h; sourceTree = "<group>"; };
8425CFA71639914800472F98 /* NSError+FBError.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "NSError+FBError.h"; sourceTree = "<group>"; };
8425CFAA1639AF5000472F98 /* NSError+FBError.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "NSError+FBError.m"; sourceTree = "<group>"; };
843D2DD61547C33000873F7C /* FBUtility.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = FBUtility.h; sourceTree = "<group>"; };
@@ -452,6 +458,9 @@
84D0F15316653F1000466CD1 /* FBErrorUtility.m */,
5F5F7E6416B21A4F0031AA95 /* FBFetchedAppSettings.h */,
5F5F7E6516B21A500031AA95 /* FBFetchedAppSettings.m */,
+ 841FA0C716E1236F002A51EA /* FBFrictionlessDialogSupportDelegate.h */,
+ 841FA0C316DFF934002A51EA /* FBFrictionlessRecipientCache.h */,
+ 841FA0C516DFF95F002A51EA /* FBFrictionlessRecipientCache.m */,
840FDCBB152B5D9200F9C927 /* FBFrictionlessRequestSettings.h */,
840FDCBC152B5D9200F9C927 /* FBFrictionlessRequestSettings.m */,
84D0A6511581A12800A2FA5E /* FBFriendPickerCacheDescriptor.h */,
@@ -670,6 +679,8 @@
9DF9B317168AD98A008B6CC0 /* FBAccessTokenData+Internal.h in Headers */,
5F5F7E6616B21A500031AA95 /* FBFetchedAppSettings.h in Headers */,
84AD5AAB169602490026E6C3 /* FBWebDialogs.h in Headers */,
+ 841FA0C416DFF934002A51EA /* FBFrictionlessRecipientCache.h in Headers */,
+ 841FA0C816E1236F002A51EA /* FBFrictionlessDialogSupportDelegate.h in Headers */,
);
runOnlyForDeploymentPostprocessing = 0;
};
@@ -880,6 +891,7 @@
9DF9B30016851828008B6CC0 /* FBAccessTokenData.m in Sources */,
5F5F7E6716B21A500031AA95 /* FBFetchedAppSettings.m in Sources */,
84AD5AAE169602670026E6C3 /* FBWebDialogs.m in Sources */,
+ 841FA0C616DFF95F002A51EA /* FBFrictionlessRecipientCache.m in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
View
83 src/tests/FBRequestConnectionTests.m
@@ -127,7 +127,7 @@ - (void)testCachedRequests
FBTestSession *session = [self getSessionWithSharedUserWithPermissions:nil];
// here we just want to seed the cache, by identifying the cache, and by choosing not to consult the cache
- FBRequestConnection *connection = [[FBRequestConnection alloc] init];
+ FBRequestConnection *connection = [[FBRequestConnection alloc] init];
FBRequest *request = [[[FBRequest alloc] initWithSession:session graphPath:@"me"] autorelease];
[request.parameters setObject:@"id,first_name" forKey:@"fields"];
[connection addRequest:request completionHandler:[self handlerExpectingSuccessSignaling:blocker]];
@@ -144,7 +144,7 @@ - (void)testCachedRequests
__block BOOL completedWithoutBlocking = NO;
// here we expect to complete on the cache, so we will confirm that
- connection = [[FBRequestConnection alloc] init];
+ connection = [[FBRequestConnection alloc] init];
request = [[[FBRequest alloc] initWithSession:session graphPath:@"me"] autorelease];
[request.parameters setObject:@"id,first_name" forKey:@"fields"];
[connection addRequest:request completionHandler:^(FBRequestConnection *connection, id result, NSError *error) {
@@ -223,7 +223,7 @@ - (void)testDelete
STAssertNil(error, @"should not have an error here: Handler 2");
STAssertTrue(0 != fbids.count, @"not enough fbids: Handler 2");
[fbids removeObjectAtIndex:fbids.count-1];
- [blocker signal];
+ [blocker signal];
}];
deleteRequest = [[FBRequest alloc] initWithSession:session
@@ -238,7 +238,7 @@ - (void)testDelete
STAssertNil(error, @"should not have an error here: Handler 3");
STAssertTrue(0 != fbids.count, @"not enough fbids: Handler 3");
[fbids removeObjectAtIndex:fbids.count-1];
- [blocker signal];
+ [blocker signal];
}];
[connection addRequest:request completionHandler:^(FBRequestConnection *connection, id result, NSError *error) {
@@ -279,8 +279,8 @@ - (void)testDelete
}];
// delete
request = [[[FBRequest alloc] initWithSession:session
- graphPath:[fbids objectAtIndex:fbids.count-1]
- parameters:nil
+ graphPath:[fbids objectAtIndex:fbids.count-1]
+ parameters:nil
HTTPMethod:@"delete"] autorelease];
[request startWithCompletionHandler:^(FBRequestConnection *connection, id result, NSError *error) {
STAssertNotNil(result, @"should have a result here: Handler 6");
@@ -295,6 +295,77 @@ - (void)testDelete
STAssertTrue(fbids.count == 0, @"Our fbid collection should be empty here");
}
+- (void)testNilCompletionHandler {
+ /*
+ Need to test that nil completion handlers don't cause crashes, and also don't prevent the request from completing.
+ We'll do this via the following steps:
+ 1. Create a post on me/feed with a valid handler and get the id.
+ 2. Delete the post without a handler
+ 3. Try delete the post again with a valid handler and make sure we get an error since Step #2 should have deleted
+ */
+
+ // Step 1
+
+ FBTestBlocker *blocker = [[FBTestBlocker alloc] init];
+
+ FBTestSession *session = [self getSessionWithSharedUserWithPermissions:nil];
+
+ FBRequest *postRequest = [[[FBRequest alloc] initWithSession:session
+ graphPath:@"me/feed"]
+ autorelease];
+
+ [postRequest.parameters setObject:@"dummy status"
+ forKey:@"name"];
+ [postRequest.parameters setObject:@"http://www.facebook.com"
+ forKey:@"link"];
+ [postRequest.parameters setObject:@"dummy description"
+ forKey:@"description"];
+ [postRequest.parameters setObject:@"post"
+ forKey:@"method"];
+
+ NSMutableArray *fbids = [NSMutableArray array];
+
+ [postRequest startWithCompletionHandler:^(FBRequestConnection *connection, id result, NSError *error) {
+ STAssertNotNil(result, @"should have a result here: Post Request handler");
+ STAssertNil(error, @"should not have an error here: Post Request handler");
+ [fbids addObject: [[result objectForKey:@"id"] description]];
+ [blocker signal];
+ }];
+
+ [blocker wait];
+ [blocker release];
+
+
+ // Step 2
+
+ blocker = [[FBTestBlocker alloc] init];
+ FBRequest *deleteRequest = [[[FBRequest alloc] initWithSession:session
+ graphPath:[fbids objectAtIndex:0]
+ parameters:nil
+ HTTPMethod:@"delete"] autorelease];
+ [deleteRequest startWithCompletionHandler:nil];
+ // Can't signal without a handler, so just wait 2 seconds.
+ [blocker waitWithTimeout:2];
+ [blocker release];
+
+
+ // Step 3
+
+ blocker = [[FBTestBlocker alloc] init];
+ deleteRequest = [[[FBRequest alloc] initWithSession:session
+ graphPath:[fbids objectAtIndex:0]
+ parameters:nil
+ HTTPMethod:@"delete"] autorelease];
+ [deleteRequest startWithCompletionHandler:^(FBRequestConnection *connection, id result, NSError *error) {
+ STAssertNil(result, @"should not have a result here: Dupe-Delete Handler");
+ STAssertNotNil(error, @"should have an error here: Dupe-Delete Handler");
+ [blocker signal];
+ }];
+
+ [blocker wait];
+ [blocker release];
+}
+
@end
#endif
Please sign in to comment.
Something went wrong with that request. Please try again.