From 41f9a1895e38f145d1868b23e35411e443c4a12e Mon Sep 17 00:00:00 2001 From: Jason Clark Date: Wed, 18 Sep 2013 20:53:15 -0700 Subject: [PATCH] Facebook iOS SDK 3.8 Summary: see https://developers.facebook.com/ios/change-log-3.x/ Upgrading from 3.7 https://developers.facebook.com/docs/ios/upgrading-from-3.7-to-3.8/ Reviewed By: chrisp Test Plan: Release Testing. --- .../project.pbxproj | 2 + .../FriendPickerSample/FPAppDelegate.m | 23 +- .../GraphApiSample.xcodeproj/project.pbxproj | 2 + .../project.pbxproj | 2 + .../HelloFacebookSample/HFViewController.m | 9 + .../en.lproj/HFViewController_iPad.xib | 120 ++- .../en.lproj/HFViewController_iPhone.xib | 214 ++--- .../project.pbxproj | 2 + .../PlacePickerSample/PPAppDelegate.m | 9 +- .../en.lproj/PPViewController_iPad.xib | 93 ++- .../en.lproj/PPViewController_iPhone.xib | 109 +-- .../project.pbxproj | 2 + .../ProfilePictureSample/PPAppDelegate.m | 4 +- .../ProfilePictureSample-Info.plist | 11 + .../en.lproj/PPViewController_iPad.xib | 314 +++++--- .../en.lproj/PPViewController_iPhone.xib | 212 +++-- .../RPSSample.xcodeproj/project.pbxproj | 2 + .../en.lproj/RPSGameViewController_iPad.xib | 739 ++++++++++------- .../en.lproj/RPSGameViewController_iPhone.xib | 756 ++++++++---------- .../Scrumptious.xcodeproj/project.pbxproj | 2 + .../Scrumptious/scrumptious/SCAppDelegate.m | 5 + .../scrumptious/SCViewController.m | 5 + .../project.pbxproj | 2 + .../project.pbxproj | 2 + .../SwitchUserSample/SUAppDelegate.m | 1 + .../SUSettingsViewController_iPad.xib | 72 +- .../SUSettingsViewController_iPhone.xib | 123 +-- scripts/build_distribution.sh | 2 +- scripts/build_documentation.sh | 2 +- scripts/build_framework.sh | 8 +- scripts/run_tests.sh | 3 +- src/Cryptography/FBCrypto.m | 32 +- src/FBAccessTokenData.h | 24 + src/FBAccessTokenData.m | 66 +- src/FBAppBridge.m | 3 +- src/FBAppBridgeTypeToJSONConverter.m | 2 +- src/FBAppCall.m | 5 +- src/FBAppEvents+Internal.h | 4 +- src/FBAppEvents.m | 46 +- src/FBCacheIndex.m | 35 +- src/FBDialog.m | 2 +- src/FBDialogs.m | 36 +- src/FBErrorUtility+Internal.h | 2 +- src/FBErrorUtility.m | 2 +- src/FBFrictionlessRecipientCache.m | 2 +- src/FBFrictionlessRequestSettings.m | 4 +- src/FBGraphObject.m | 6 +- src/FBGraphObjectTableDataSource.m | 2 +- src/FBLogger.h | 6 +- src/FBLoginView.m | 10 +- src/FBProfilePictureView.m | 3 + src/FBRequest+Internal.h | 3 + src/FBRequest.h | 5 +- src/FBRequest.m | 7 +- src/FBRequestBody.m | 4 +- src/FBRequestConnection.h | 35 +- src/FBRequestConnection.m | 325 ++++---- src/FBRequestConnectionRetryManager.h | 6 - src/FBRequestConnectionRetryManager.m | 32 +- src/FBRequestMetadata.h | 4 +- src/FBRequestMetadata.m | 45 +- src/FBSDKVersion.h | 2 +- src/FBSession+FBSessionLoginStrategy.h | 45 ++ src/FBSession+Internal.h | 8 +- src/FBSession+Protected.h | 12 +- src/FBSession.h | 22 +- src/FBSession.m | 648 ++++----------- src/FBSessionAppEventsState.h | 2 +- src/FBSessionAppEventsState.m | 2 +- src/FBSessionAppSwitchingLoginStategy.h | 19 + src/FBSessionAppSwitchingLoginStategy.m | 86 ++ src/FBSessionAuthLogger.m | 4 +- src/FBSessionFacebookAppNativeLoginStategy.h | 19 + src/FBSessionFacebookAppNativeLoginStategy.m | 88 ++ src/FBSessionFacebookAppWebLoginStategy.h | 19 + src/FBSessionFacebookAppWebLoginStategy.m | 40 + src/FBSessionInlineWebViewLoginStategy.h | 19 + src/FBSessionInlineWebViewLoginStategy.m | 41 + src/FBSessionLoginStrategy.h | 65 ++ src/FBSessionLoginStrategyParams.h | 31 + src/FBSessionLoginStrategyParams.m | 28 + src/FBSessionSafariLoginStategy.h | 19 + src/FBSessionSafariLoginStategy.m | 41 + src/FBSessionSystemLoginStategy.h | 20 + src/FBSessionSystemLoginStategy.m | 49 ++ src/FBSessionTokenCachingStrategy.h | 7 + src/FBSessionTokenCachingStrategy.m | 1 + src/FBSessionUtility.h | 37 + src/FBSessionUtility.m | 190 +++++ src/FBSettings.m | 6 +- src/FBSystemAccountStoreAdapter.h | 10 + src/FBSystemAccountStoreAdapter.m | 28 +- src/FBTask+Private.h | 29 + src/FBTask.h | 123 +++ src/FBTask.m | 353 ++++++++ src/FBTaskCompletionSource.h | 85 ++ src/FBTaskCompletionSource.m | 87 ++ src/FBTestSession.m | 31 +- src/FBURLConnection.m | 20 +- src/FBUserSettingsViewController.m | 18 +- src/FBUtility.h | 6 +- src/FBUtility.m | 68 +- src/FBViewController+Internal.h | 2 - src/FBViewController.m | 223 +++--- src/FBWebDialogs.m | 2 +- src/Facebook.m | 7 +- src/FacebookSDK.h | 2 +- .../FBAccessTokenDataTests.m | 4 +- .../FBAppEventsIntegrationTests.m | 12 +- .../FBBatchRequestIntegrationTests.m | 60 ++ .../FBCacheIntegrationTests.m | 6 +- .../FBIntegrationTests.h | 3 +- .../FBIntegrationTests.m | 26 +- .../FBOpenGraphActionTests.m | 6 +- .../FBRequestConnectionIntegrationTests.m | 67 +- .../FBRequestIntegrationTests.m | 15 +- .../project.pbxproj | 447 ++++++++++- src/tests/FBAppBridgeTests.h | 21 + src/tests/FBAppBridgeTests.m | 651 +++++++++++++++ src/tests/FBAppCallTests.m | 32 +- src/tests/FBAuthenticationTests.m | 7 +- src/tests/FBFacebookAppAuthenticationTests.m | 2 +- .../FBIsStringRepresentingJSONDictionary.h | 33 + .../FBIsStringRepresentingJSONDictionary.m | 85 ++ src/tests/FBIsURLHavingQueryParams.h | 33 + src/tests/FBIsURLHavingQueryParams.m | 85 ++ src/tests/FBRequestConnectionTests.m | 141 +++- src/tests/FBSessionTests.m | 43 +- src/tests/FBTests.h | 3 + src/tests/FBTests.m | 10 + src/tests/FacebookSDKTests-Prefix.pch | 3 + 131 files changed, 5546 insertions(+), 2523 deletions(-) create mode 100644 src/FBSession+FBSessionLoginStrategy.h create mode 100644 src/FBSessionAppSwitchingLoginStategy.h create mode 100644 src/FBSessionAppSwitchingLoginStategy.m create mode 100644 src/FBSessionFacebookAppNativeLoginStategy.h create mode 100644 src/FBSessionFacebookAppNativeLoginStategy.m create mode 100644 src/FBSessionFacebookAppWebLoginStategy.h create mode 100644 src/FBSessionFacebookAppWebLoginStategy.m create mode 100644 src/FBSessionInlineWebViewLoginStategy.h create mode 100644 src/FBSessionInlineWebViewLoginStategy.m create mode 100644 src/FBSessionLoginStrategy.h create mode 100644 src/FBSessionLoginStrategyParams.h create mode 100644 src/FBSessionLoginStrategyParams.m create mode 100644 src/FBSessionSafariLoginStategy.h create mode 100644 src/FBSessionSafariLoginStategy.m create mode 100644 src/FBSessionSystemLoginStategy.h create mode 100644 src/FBSessionSystemLoginStategy.m create mode 100644 src/FBSessionUtility.h create mode 100644 src/FBSessionUtility.m create mode 100644 src/FBTask+Private.h create mode 100644 src/FBTask.h create mode 100644 src/FBTask.m create mode 100644 src/FBTaskCompletionSource.h create mode 100644 src/FBTaskCompletionSource.m create mode 100644 src/tests/FBAppBridgeTests.h create mode 100644 src/tests/FBAppBridgeTests.m create mode 100644 src/tests/FBIsStringRepresentingJSONDictionary.h create mode 100644 src/tests/FBIsStringRepresentingJSONDictionary.m create mode 100644 src/tests/FBIsURLHavingQueryParams.h create mode 100644 src/tests/FBIsURLHavingQueryParams.m diff --git a/samples/FriendPickerSample/FriendPickerSample.xcodeproj/project.pbxproj b/samples/FriendPickerSample/FriendPickerSample.xcodeproj/project.pbxproj index b30cb3ebab..34ea81bbf5 100644 --- a/samples/FriendPickerSample/FriendPickerSample.xcodeproj/project.pbxproj +++ b/samples/FriendPickerSample/FriendPickerSample.xcodeproj/project.pbxproj @@ -73,7 +73,9 @@ E2D6B81115413A570050835F /* Frameworks */, E2D6B80F15413A570050835F /* Products */, ); + indentWidth = 4; sourceTree = ""; + tabWidth = 4; }; E2D6B80F15413A570050835F /* Products */ = { isa = PBXGroup; diff --git a/samples/FriendPickerSample/FriendPickerSample/FPAppDelegate.m b/samples/FriendPickerSample/FriendPickerSample/FPAppDelegate.m index 5034f0a09a..7602765803 100644 --- a/samples/FriendPickerSample/FriendPickerSample/FPAppDelegate.m +++ b/samples/FriendPickerSample/FriendPickerSample/FPAppDelegate.m @@ -6,7 +6,7 @@ * 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. @@ -50,17 +50,26 @@ - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:( } else { self.rootViewController = [[FPViewController alloc] initWithNibName:@"FPViewController_iPad" bundle:nil]; } +#ifdef __IPHONE_7_0 +#ifdef __IPHONE_OS_VERSION_MAX_ALLOWED +#if __IPHONE_OS_VERSION_MAX_ALLOWED >= __IPHONE_7_0 + if ([self.rootViewController respondsToSelector:@selector(setEdgesForExtendedLayout:)]) { + self.rootViewController.edgesForExtendedLayout &= ~UIRectEdgeTop; + } +#endif +#endif +#endif self.rootViewController.navigationItem.title = @"Friend Picker"; - + // Set up a UINavigationController as the basis of this app, with the nib generated viewController // as the initial view. - UINavigationController *navigationController = - [[UINavigationController alloc] initWithRootViewController:self.rootViewController]; - + UINavigationController *navigationController = + [[UINavigationController alloc] initWithRootViewController:self.rootViewController]; + self.window = [[UIWindow alloc] initWithFrame:[[UIScreen mainScreen] bounds]]; self.window.rootViewController = navigationController; [self.window makeKeyAndVisible]; - + return YES; } @@ -72,7 +81,7 @@ - (NSUInteger)supportedInterfaceOrientations{ // It is important to close any FBSession object that is no longer useful - (void)applicationWillTerminate:(UIApplication *)application { // Close the session before quitting - // this is a good idea because things may be hanging off the session, that need + // this is a good idea because things may be hanging off the session, that need // releasing (completion block, etc.) and other components in the app may be awaiting // close notification in order to do cleanup [FBSession.activeSession close]; diff --git a/samples/GraphApiSample/GraphApiSample.xcodeproj/project.pbxproj b/samples/GraphApiSample/GraphApiSample.xcodeproj/project.pbxproj index 00f93cd1f2..9db9a680ff 100644 --- a/samples/GraphApiSample/GraphApiSample.xcodeproj/project.pbxproj +++ b/samples/GraphApiSample/GraphApiSample.xcodeproj/project.pbxproj @@ -73,7 +73,9 @@ E2511740152BB6FE00CB342A /* Frameworks */, E251173E152BB6FE00CB342A /* Products */, ); + indentWidth = 4; sourceTree = ""; + tabWidth = 4; }; E251173E152BB6FE00CB342A /* Products */ = { isa = PBXGroup; diff --git a/samples/HelloFacebookSample/HelloFacebookSample.xcodeproj/project.pbxproj b/samples/HelloFacebookSample/HelloFacebookSample.xcodeproj/project.pbxproj index 4bcfe90c2c..2dbce853da 100644 --- a/samples/HelloFacebookSample/HelloFacebookSample.xcodeproj/project.pbxproj +++ b/samples/HelloFacebookSample/HelloFacebookSample.xcodeproj/project.pbxproj @@ -76,7 +76,9 @@ 5F6AF18C1532A37B00DDBD75 /* Frameworks */, 5F6AF18A1532A37B00DDBD75 /* Products */, ); + indentWidth = 4; sourceTree = ""; + tabWidth = 4; }; 5F6AF18A1532A37B00DDBD75 /* Products */ = { isa = PBXGroup; diff --git a/samples/HelloFacebookSample/HelloFacebookSample/HFViewController.m b/samples/HelloFacebookSample/HelloFacebookSample/HFViewController.m index a1e141505e..38bd885c48 100644 --- a/samples/HelloFacebookSample/HelloFacebookSample/HFViewController.m +++ b/samples/HelloFacebookSample/HelloFacebookSample/HFViewController.m @@ -61,6 +61,15 @@ - (void)viewDidLoad { FBLoginView *loginview = [[FBLoginView alloc] init]; loginview.frame = CGRectOffset(loginview.frame, 5, 5); +#ifdef __IPHONE_7_0 +#ifdef __IPHONE_OS_VERSION_MAX_ALLOWED +#if __IPHONE_OS_VERSION_MAX_ALLOWED >= __IPHONE_7_0 + if ([self respondsToSelector:@selector(setEdgesForExtendedLayout:)]) { + loginview.frame = CGRectOffset(loginview.frame, 5, 25); + } +#endif +#endif +#endif loginview.delegate = self; [self.view addSubview:loginview]; diff --git a/samples/HelloFacebookSample/HelloFacebookSample/en.lproj/HFViewController_iPad.xib b/samples/HelloFacebookSample/HelloFacebookSample/en.lproj/HFViewController_iPad.xib index b4a580acf4..5e0b125b46 100644 --- a/samples/HelloFacebookSample/HelloFacebookSample/en.lproj/HFViewController_iPad.xib +++ b/samples/HelloFacebookSample/HelloFacebookSample/en.lproj/HFViewController_iPad.xib @@ -1,14 +1,14 @@ - 1552 - 12C2034 - 3084 - 1187.34 - 625.00 + 1280 + 12E55 + 4504 + 1187.39 + 626.00 com.apple.InterfaceBuilder.IBCocoaTouchPlugin - 2083 + 3734.1 IBProxyObject @@ -39,7 +39,7 @@ 292 - {{284, 57}, {200, 200}} + {{284, 77}, {200, 200}} @@ -56,7 +56,7 @@ 292 - {{240, 265}, {288, 21}} + {{240, 285}, {288, 21}} @@ -81,7 +81,7 @@ 17 - Helvetica + HelveticaNeue 17 16 @@ -89,7 +89,7 @@ 292 - {{92, 320}, {288, 37}} + {{92, 340}, {288, 37}} @@ -99,19 +99,19 @@ 0 0 1 - Post Status Update + + 1 + MC4xOTYwNzg0MzE0IDAuMzA5ODAzOTIxNiAwLjUyMTU2ODYyNzUAA + 3 MQA + Post Status Update 3 MC42NjY2NjY2NjY3AA - - 1 - MC4xOTYwNzg0MzQ2IDAuMzA5ODAzOTMyOSAwLjUyMTU2ODY1NgA - 3 MC41AA @@ -120,8 +120,8 @@ 2 15 - - Helvetica-Bold + + HelveticaNeue-Bold 15 16 @@ -129,7 +129,7 @@ 292 - {{388, 320}, {288, 37}} + {{388, 340}, {288, 37}} @@ -139,21 +139,18 @@ 0 0 1 - Post Photo + + Post Photo - - 1 - MC4xOTYwNzg0MzQ2IDAuMzA5ODAzOTMyOSAwLjUyMTU2ODY1NgA - - + 292 - {{92, 365}, {288, 37}} + {{92, 385}, {288, 37}} @@ -163,21 +160,18 @@ 0 0 1 - Pick Some Friends + + Pick Some Friends - - 1 - MC4xOTYwNzg0MzQ2IDAuMzA5ODAzOTMyOSAwLjUyMTU2ODY1NgA - - + 292 - {{388, 365}, {288, 37}} + {{388, 385}, {288, 37}} _NS:9 @@ -186,16 +180,13 @@ 0 0 1 - Pick Place + + Pick Place - - 1 - MC4xOTYwNzg0MzQ2IDAuMzA5ODAzOTMyOSAwLjUyMTU2ODY1NgA - - + {{0, 20}, {768, 1004}} @@ -214,6 +205,7 @@ + NO @@ -221,7 +213,7 @@ - 3 + 3 @@ -229,7 +221,7 @@ - 15 + 15 @@ -237,7 +229,7 @@ - 19 + 19 @@ -245,7 +237,7 @@ - 26 + 26 @@ -253,7 +245,7 @@ - 27 + 27 @@ -261,7 +253,7 @@ - 28 + 28 @@ -269,7 +261,7 @@ - 35 + 35 @@ -278,7 +270,7 @@ 7 - 23 + 23 @@ -287,7 +279,7 @@ 7 - 24 + 24 @@ -296,7 +288,7 @@ 7 - 29 + 29 @@ -305,30 +297,30 @@ 7 - 34 + 34 - 0 + 0 - -1 + -1 File's Owner - -2 + -2 - 2 + 2 @@ -341,32 +333,32 @@ - 13 + 13 - 18 + 18 - 20 + 20 - 21 + 21 - 22 + 22 - 32 + 32 @@ -394,7 +386,6 @@ - 35 @@ -476,12 +467,17 @@ 0 IBIPadFramework + YES com.apple.InterfaceBuilder.CocoaTouchPlugin.iPhoneOS - + + + + com.apple.InterfaceBuilder.CocoaTouchPlugin.InterfaceBuilder3 + YES 3 - 2083 + 3734.1 diff --git a/samples/HelloFacebookSample/HelloFacebookSample/en.lproj/HFViewController_iPhone.xib b/samples/HelloFacebookSample/HelloFacebookSample/en.lproj/HFViewController_iPhone.xib index 2fe12106ee..c9f4ab21a9 100644 --- a/samples/HelloFacebookSample/HelloFacebookSample/en.lproj/HFViewController_iPhone.xib +++ b/samples/HelloFacebookSample/HelloFacebookSample/en.lproj/HFViewController_iPhone.xib @@ -1,20 +1,20 @@ - 1296 - 11E53 - 2182 - 1138.47 - 569.00 + 1280 + 12E55 + 4504 + 1187.39 + 626.00 com.apple.InterfaceBuilder.IBCocoaTouchPlugin - 1181 + 3734.1 + IBProxyObject IBUIButton - IBUIView IBUILabel - IBProxyObject + IBUIView com.apple.InterfaceBuilder.IBCocoaTouchPlugin @@ -33,15 +33,14 @@ IBCocoaTouchFramework - + 274 292 - {{90, 52}, {140, 39}} + {{90, 72}, {140, 39}} - _NS:328 NO @@ -53,6 +52,7 @@ 1 MCAwIDAAA + darkTextColor 1 @@ -62,7 +62,7 @@ 17 - Helvetica + HelveticaNeue 17 16 @@ -70,9 +70,8 @@ 292 - {{95, 102}, {130, 130}} + {{95, 122}, {130, 130}} - _NS:196 @@ -87,9 +86,8 @@ 292 - {{20, 254}, {280, 37}} + {{20, 274}, {280, 37}} - _NS:9 NO @@ -97,19 +95,19 @@ 0 0 1 - Post Status Update + + 1 + MC4xOTYwNzg0MzE0IDAuMzA5ODAzOTIxNiAwLjUyMTU2ODYyNzUAA + 3 MQA + Post Status Update 3 MC42NjY2NjY2NjY3AA - - 1 - MC4xOTYwNzg0MzQ2IDAuMzA5ODAzOTMyOSAwLjUyMTU2ODY1NgA - 3 MC41AA @@ -118,8 +116,8 @@ 2 15 - - Helvetica-Bold + + HelveticaNeue-Bold 15 16 @@ -127,9 +125,8 @@ 292 - {{20, 299}, {280, 37}} + {{20, 319}, {280, 37}} - _NS:9 NO @@ -137,23 +134,19 @@ 0 0 1 - Post Photo + + Post Photo - - 1 - MC4xOTYwNzg0MzQ2IDAuMzA5ODAzOTMyOSAwLjUyMTU2ODY1NgA - - + 292 - {{20, 344}, {280, 37}} + {{20, 364}, {280, 37}} - _NS:9 NO @@ -161,44 +154,35 @@ 0 0 1 - Pick Some Friends + + Pick Some Friends - - 1 - MC4xOTYwNzg0MzQ2IDAuMzA5ODAzOTMyOSAwLjUyMTU2ODY1NgA - - + 292 - {{20, 389}, {280, 37}} + {{20, 409}, {280, 37}} - _NS:9 NO IBCocoaTouchFramework 0 0 1 - Pick Place + + Pick Place - - 1 - MC4xOTYwNzg0MzQ2IDAuMzA5ODAzOTMyOSAwLjUyMTU2ODY1NgA - - + {{0, 20}, {320, 460}} - - NO @@ -207,6 +191,7 @@ + NO @@ -214,7 +199,7 @@ - 19 + 19 @@ -222,7 +207,7 @@ - 23 + 23 @@ -230,7 +215,7 @@ - 28 + 28 @@ -238,7 +223,7 @@ - 29 + 29 @@ -246,7 +231,7 @@ - 33 + 33 @@ -254,7 +239,7 @@ - 21 + 21 @@ -262,7 +247,7 @@ - 39 + 39 @@ -271,7 +256,7 @@ 7 - 26 + 26 @@ -280,7 +265,7 @@ 7 - 27 + 27 @@ -289,7 +274,7 @@ 7 - 34 + 34 @@ -298,30 +283,30 @@ 7 - 40 + 40 - 0 + 0 - -1 + -1 File's Owner - -2 + -2 - 6 + 6 @@ -334,33 +319,33 @@ - 18 + 18 - 24 + 24 - 25 + 25 - 30 + 30 - 17 + 17 - 37 + 37 @@ -389,94 +374,21 @@ - 40 - - - - - FBProfilePictureView - UIView - - IBProjectSource - ./Classes/FBProfilePictureView.h - - - - HFViewController - UIViewController - - UIButton - UIButton - UIButton - UIButton - - - - pickFriendsClick: - UIButton - - - pickPlaceClick: - UIButton - - - postPhotoClick: - UIButton - - - postStatusUpdateClick: - UIButton - - - - UIButton - UIButton - UIButton - UIButton - UILabel - FBProfilePictureView - - - - buttonPickFriends - UIButton - - - buttonPickPlace - UIButton - - - buttonPostPhoto - UIButton - - - buttonPostStatus - UIButton - - - labelFirstName - UILabel - - - profilePic - FBProfilePictureView - - - - IBProjectSource - ./Classes/HFViewController.h - - - + 0 IBCocoaTouchFramework + YES com.apple.InterfaceBuilder.CocoaTouchPlugin.iPhoneOS - + + + + com.apple.InterfaceBuilder.CocoaTouchPlugin.InterfaceBuilder3 + YES 3 - 1181 + 3734.1 diff --git a/samples/PlacePickerSample/PlacePickerSample.xcodeproj/project.pbxproj b/samples/PlacePickerSample/PlacePickerSample.xcodeproj/project.pbxproj index 476bf272b2..b7ea5a2029 100644 --- a/samples/PlacePickerSample/PlacePickerSample.xcodeproj/project.pbxproj +++ b/samples/PlacePickerSample/PlacePickerSample.xcodeproj/project.pbxproj @@ -76,7 +76,9 @@ B9AF4BA7152E43EE00E9BA6C /* Frameworks */, B9AF4BA5152E43EE00E9BA6C /* Products */, ); + indentWidth = 4; sourceTree = ""; + tabWidth = 4; }; B9AF4BA5152E43EE00E9BA6C /* Products */ = { isa = PBXGroup; diff --git a/samples/PlacePickerSample/PlacePickerSample/PPAppDelegate.m b/samples/PlacePickerSample/PlacePickerSample/PPAppDelegate.m index ee9361cf32..aec4fcf035 100644 --- a/samples/PlacePickerSample/PlacePickerSample/PPAppDelegate.m +++ b/samples/PlacePickerSample/PlacePickerSample/PPAppDelegate.m @@ -6,7 +6,7 @@ * 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. @@ -22,13 +22,14 @@ @implementation PPAppDelegate @synthesize window = _window; @synthesize viewController = _viewController; -- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions { +- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions { self.window = [[UIWindow alloc] initWithFrame:[[UIScreen mainScreen] bounds]]; if ([[UIDevice currentDevice] userInterfaceIdiom] == UIUserInterfaceIdiomPhone) { self.viewController = [[PPViewController alloc] initWithNibName:@"PPViewController_iPhone" bundle:nil]; } else { self.viewController = [[PPViewController alloc] initWithNibName:@"PPViewController_iPad" bundle:nil]; } + self.viewController.wantsFullScreenLayout = YES; self.window.rootViewController = self.viewController; [self.window makeKeyAndVisible]; return YES; @@ -38,9 +39,9 @@ - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:( // In the login workflow, the Facebook native application, or Safari will transition back to // this applicaiton via a url following the scheme fb[app id]://; the call to handleOpenURL // below captures the token, in the case of success, on behalf of the FBSession object -- (BOOL)application:(UIApplication *)application +- (BOOL)application:(UIApplication *)application openURL:(NSURL *)url - sourceApplication:(NSString *)sourceApplication + sourceApplication:(NSString *)sourceApplication annotation:(id)annotation { return [FBAppCall handleOpenURL:url sourceApplication:sourceApplication]; diff --git a/samples/PlacePickerSample/PlacePickerSample/en.lproj/PPViewController_iPad.xib b/samples/PlacePickerSample/PlacePickerSample/en.lproj/PPViewController_iPad.xib index 6e9aa63df4..db10d23f3c 100644 --- a/samples/PlacePickerSample/PlacePickerSample/en.lproj/PPViewController_iPad.xib +++ b/samples/PlacePickerSample/PlacePickerSample/en.lproj/PPViewController_iPad.xib @@ -1,21 +1,21 @@ - 1296 - 11D50d - 2182 - 1138.32 - 568.00 + 1280 + 12E55 + 4504 + 1187.39 + 626.00 com.apple.InterfaceBuilder.IBCocoaTouchPlugin - 1179 + 3734.1 + IBProxyObject + IBUISearchBar IBUISearchDisplayController IBUITableView IBUIView - IBUISearchBar - IBProxyObject com.apple.InterfaceBuilder.IBCocoaTouchPlugin @@ -40,10 +40,9 @@ 274 - {{0, 44}, {768, 960}} + {{0, 66}, {768, 960}} - _NS:9 3 @@ -62,7 +61,7 @@ 290 - {768, 88} + {{0, 22}, {768, 88}} @@ -98,6 +97,7 @@ + NO @@ -105,7 +105,7 @@ - 3 + 3 @@ -113,7 +113,7 @@ - 27 + 27 @@ -121,7 +121,7 @@ - 33 + 33 @@ -129,7 +129,7 @@ - 38 + 38 @@ -137,7 +137,7 @@ - 32 + 32 @@ -145,7 +145,7 @@ - 34 + 34 @@ -153,7 +153,7 @@ - 35 + 35 @@ -161,7 +161,7 @@ - 36 + 36 @@ -169,30 +169,30 @@ - 37 + 37 - 0 + 0 - -1 + -1 File's Owner - -2 + -2 - 2 + 2 @@ -201,17 +201,17 @@ - 26 + 26 - 30 + 30 - 31 + 31 @@ -231,13 +231,12 @@ - 38 FBPlacePickerViewController - UIViewController + FBViewController UIActivityIndicatorView UITableView @@ -257,16 +256,48 @@ ./Classes/FBPlacePickerViewController.h + + FBViewController + UIViewController + + UIBarButtonItem + id + UIBarButtonItem + + + + cancelButton + UIBarButtonItem + + + delegate + id + + + doneButton + UIBarButtonItem + + + + IBProjectSource + ./Classes/FBViewController.h + + 0 IBIPadFramework + YES com.apple.InterfaceBuilder.CocoaTouchPlugin.iPhoneOS - + + + + com.apple.InterfaceBuilder.CocoaTouchPlugin.InterfaceBuilder3 + YES 3 - 1179 + 3734.1 diff --git a/samples/PlacePickerSample/PlacePickerSample/en.lproj/PPViewController_iPhone.xib b/samples/PlacePickerSample/PlacePickerSample/en.lproj/PPViewController_iPhone.xib index c588e770ae..39603e19be 100644 --- a/samples/PlacePickerSample/PlacePickerSample/en.lproj/PPViewController_iPhone.xib +++ b/samples/PlacePickerSample/PlacePickerSample/en.lproj/PPViewController_iPhone.xib @@ -1,21 +1,21 @@ - 1296 - 11D50d - 2182 - 1138.32 - 568.00 + 1280 + 12E55 + 4504 + 1187.39 + 626.00 com.apple.InterfaceBuilder.IBCocoaTouchPlugin - 1179 + 3734.1 + IBProxyObject + IBUISearchBar IBUISearchDisplayController IBUITableView IBUIView - IBUISearchBar - IBProxyObject com.apple.InterfaceBuilder.IBCocoaTouchPlugin @@ -34,16 +34,15 @@ IBCocoaTouchFramework - + 274 274 - {{0, 45}, {320, 415}} + + {{0, 67}, {320, 415}} - - _NS:9 3 @@ -62,9 +61,8 @@ 290 - {320, 44} + {{0, 23}, {320, 44}} - _NS:9 3 @@ -80,8 +78,6 @@ {{0, 20}, {320, 460}} - - NO @@ -93,6 +89,7 @@ + NO @@ -100,7 +97,7 @@ - 28 + 28 @@ -108,7 +105,7 @@ - 29 + 29 @@ -116,7 +113,7 @@ - 35 + 35 @@ -124,7 +121,7 @@ - 40 + 40 @@ -132,7 +129,7 @@ - 34 + 34 @@ -140,7 +137,7 @@ - 36 + 36 @@ -148,7 +145,7 @@ - 37 + 37 @@ -156,7 +153,7 @@ - 38 + 38 @@ -164,53 +161,54 @@ - 39 + 39 - 0 + 0 - -1 + -1 File's Owner - -2 + -2 - 6 + 6 - + - 27 + 27 + table - 32 - - - - - 33 + 33 + + 32 + + + @@ -227,42 +225,21 @@ - 40 - - - - - FBPlacePickerViewController - UIViewController - - UIActivityIndicatorView - UITableView - - - - spinner - UIActivityIndicatorView - - - tableView - UITableView - - - - IBProjectSource - ./Classes/FBPlacePickerViewController.h - - - + 0 IBCocoaTouchFramework + YES com.apple.InterfaceBuilder.CocoaTouchPlugin.iPhoneOS - + + + + com.apple.InterfaceBuilder.CocoaTouchPlugin.InterfaceBuilder3 + YES 3 - 1179 + 3734.1 diff --git a/samples/ProfilePictureSample/ProfilePictureSample.xcodeproj/project.pbxproj b/samples/ProfilePictureSample/ProfilePictureSample.xcodeproj/project.pbxproj index 38e5f0d8cc..6143752b49 100644 --- a/samples/ProfilePictureSample/ProfilePictureSample.xcodeproj/project.pbxproj +++ b/samples/ProfilePictureSample/ProfilePictureSample.xcodeproj/project.pbxproj @@ -73,7 +73,9 @@ B98686BB151D0B3700616412 /* Frameworks */, B98686B9151D0B3700616412 /* Products */, ); + indentWidth = 4; sourceTree = ""; + tabWidth = 4; }; B98686B9151D0B3700616412 /* Products */ = { isa = PBXGroup; diff --git a/samples/ProfilePictureSample/ProfilePictureSample/PPAppDelegate.m b/samples/ProfilePictureSample/ProfilePictureSample/PPAppDelegate.m index a17fb71428..bee7d8e49d 100644 --- a/samples/ProfilePictureSample/ProfilePictureSample/PPAppDelegate.m +++ b/samples/ProfilePictureSample/ProfilePictureSample/PPAppDelegate.m @@ -37,12 +37,10 @@ - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:( } else { self.viewController = [[PPViewController alloc] initWithNibName:@"PPViewController_iPad" bundle:nil]; } + self.viewController.wantsFullScreenLayout = YES; self.window.rootViewController = self.viewController; [self.window makeKeyAndVisible]; return YES; } -- (void)applicationDidBecomeActive:(UIApplication *)application { - [FBAppEvents activateApp]; -} @end diff --git a/samples/ProfilePictureSample/ProfilePictureSample/ProfilePictureSample-Info.plist b/samples/ProfilePictureSample/ProfilePictureSample/ProfilePictureSample-Info.plist index 12963211ef..18d68f578a 100644 --- a/samples/ProfilePictureSample/ProfilePictureSample/ProfilePictureSample-Info.plist +++ b/samples/ProfilePictureSample/ProfilePictureSample/ProfilePictureSample-Info.plist @@ -61,5 +61,16 @@ UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight + FacebookAppID + 309692849172593 + FacebookDisplayName + ProfilePictureSample + CFBundleURLTypes + + + CFBundleURLName + fb309692849172593 + + diff --git a/samples/ProfilePictureSample/ProfilePictureSample/en.lproj/PPViewController_iPad.xib b/samples/ProfilePictureSample/ProfilePictureSample/en.lproj/PPViewController_iPad.xib index 1c7c7eb5eb..456fa7150b 100644 --- a/samples/ProfilePictureSample/ProfilePictureSample/en.lproj/PPViewController_iPad.xib +++ b/samples/ProfilePictureSample/ProfilePictureSample/en.lproj/PPViewController_iPad.xib @@ -1,14 +1,14 @@ - 1536 - 11G63 - 2843 - 1138.51 - 569.00 + 1280 + 12E55 + 4504 + 1187.39 + 626.00 com.apple.InterfaceBuilder.IBCocoaTouchPlugin - 1929 + 3734.1 IBProxyObject @@ -45,6 +45,7 @@ 274 {{6, 7}, {189, 186}} + _NS:9 @@ -55,8 +56,9 @@ IBIPadFramework - {{20, 55}, {200, 200}} + {{21, 55}, {200, 200}} + _NS:9 @@ -68,8 +70,9 @@ 292 - {{249, 55}, {112, 37}} + {{250, 55}, {112, 37}} + _NS:9 NO @@ -77,15 +80,15 @@ 0 0 1 - Jason + + 1 + MC4xOTYwNzg0MzE0IDAuMzA5ODAzOTIxNiAwLjUyMTU2ODYyNzUAA + 3 MQA - - 1 - MC4xOTYwNzg0MzQ2IDAuMzA5ODAzOTMyOSAwLjUyMTU2ODY1NgA - + Jason 3 MC41AA @@ -94,8 +97,8 @@ 2 15 - - Helvetica-Bold + + HelveticaNeue-Bold 15 16 @@ -103,8 +106,9 @@ 292 - {{249, 100}, {112, 37}} + {{250, 100}, {112, 37}} + _NS:9 NO @@ -112,21 +116,19 @@ 0 0 1 - Michael + - - 1 - MC4xOTYwNzg0MzQ2IDAuMzA5ODAzOTMyOSAwLjUyMTU2ODY1NgA - + Michael - + 292 - {{249, 145}, {112, 37}} + {{250, 145}, {112, 37}} + _NS:9 NO @@ -134,21 +136,19 @@ 0 0 1 - Vijaye + - - 1 - MC4xOTYwNzg0MzQ2IDAuMzA5ODAzOTMyOSAwLjUyMTU2ODY1NgA - + Vijaye - + 292 - {{249, 190}, {112, 37}} + {{250, 190}, {112, 37}} + _NS:9 NO @@ -156,21 +156,19 @@ 0 0 1 - Random + - - 1 - MC4xOTYwNzg0MzQ2IDAuMzA5ODAzOTMyOSAwLjUyMTU2ODY1NgA - + Random - + 292 - {{249, 235}, {112, 37}} + {{250, 235}, {112, 37}} + _NS:9 NO @@ -178,21 +176,19 @@ 0 0 1 - None + - - 1 - MC4xOTYwNzg0MzQ2IDAuMzA5ODAzOTMyOSAwLjUyMTU2ODY1NgA - + None - + 292 - {{129, 288}, {82, 37}} + {{130, 288}, {82, 37}} + _NS:9 NO @@ -200,21 +196,19 @@ 0 0 1 - Original + - - 1 - MC4xOTYwNzg0MzQ2IDAuMzA5ODAzOTMyOSAwLjUyMTU2ODY1NgA - + Original - + 292 - {{129, 333}, {122, 37}} + {{130, 333}, {122, 37}} + _NS:9 NO @@ -222,42 +216,38 @@ 0 0 1 - Small View + - - 1 - MC4xOTYwNzg0MzQ2IDAuMzA5ODAzOTMyOSAwLjUyMTU2ODY1NgA - + Small View - + 292 - {{271, 333}, {123, 37}} + {{272, 333}, {123, 37}} + _NS:9 NO IBIPadFramework 0 0 1 - Large View + - - 1 - MC4xOTYwNzg0MzQ2IDAuMzA5ODAzOTMyOSAwLjUyMTU2ODY1NgA - + Large View - + 292 - {{271, 288}, {82, 37}} + {{272, 288}, {82, 37}} + _NS:9 NO @@ -265,21 +255,19 @@ 0 0 1 - Square + - - 1 - MC4xOTYwNzg0MzQ2IDAuMzA5ODAzOTMyOSAwLjUyMTU2ODY1NgA - + Square - + 292 - {{20, 20}, {408, 27}} + {{21, 20}, {408, 27}} + _NS:9 NO @@ -302,7 +290,7 @@ 14 - Helvetica + HelveticaNeue 14 16 @@ -311,8 +299,9 @@ 292 - {{10, 295}, {71, 21}} + {{11, 295}, {71, 21}} + _NS:9 NO @@ -329,8 +318,8 @@ 1 17 - - Helvetica + + HelveticaNeue 17 16 @@ -338,8 +327,9 @@ 292 - {{10, 340}, {75, 21}} + {{11, 340}, {75, 21}} + _NS:9 NO @@ -353,11 +343,12 @@ 0 10 - + {{0, 20}, {768, 1004}} + 2 @@ -370,6 +361,7 @@ + NO @@ -377,7 +369,7 @@ - 3 + 3 @@ -385,7 +377,7 @@ - 34 + 34 @@ -393,7 +385,7 @@ - 62 + 62 @@ -402,7 +394,7 @@ 7 - 28 + 28 @@ -411,7 +403,7 @@ 7 - 29 + 29 @@ -420,7 +412,7 @@ 7 - 30 + 30 @@ -429,7 +421,7 @@ 7 - 37 + 37 @@ -438,7 +430,7 @@ 7 - 59 + 59 @@ -447,7 +439,7 @@ 7 - 60 + 60 @@ -456,7 +448,7 @@ 7 - 57 + 57 @@ -465,7 +457,7 @@ 7 - 58 + 58 @@ -474,75 +466,75 @@ 7 - 69 + 69 - 0 + 0 - -1 + -1 File's Owner - -2 + -2 - 2 + 2 - - - - - - + + + + + + - 21 + 21 - 22 + 22 - 23 + 23 - 35 + 35 - 38 + 38 - 42 + 42 @@ -550,42 +542,42 @@ - 27 + 27 - 43 + 43 - 45 + 45 - 47 + 47 - 49 + 49 - 64 + 64 - 65 + 65 - 67 + 67 @@ -617,17 +609,103 @@ - 69 - + + + + FBProfilePictureView + UIView + + IBProjectSource + ./Classes/FBProfilePictureView.h + + + + PPViewController + UIViewController + + id + id + id + id + id + id + id + id + id + + + + makePictureOriginal: + id + + + makePictureSquare: + id + + + makeViewLarge: + id + + + makeViewSmall: + id + + + showJasonProfile: + id + + + showMichaelProfile: + id + + + showNoProfile: + id + + + showRandomProfile: + id + + + showVijayeProfile: + id + + + + UIView + FBProfilePictureView + + + + profilePictureOuterView + UIView + + + profilePictureView + FBProfilePictureView + + + + IBProjectSource + ./Classes/PPViewController.h + + + + 0 IBIPadFramework + YES com.apple.InterfaceBuilder.CocoaTouchPlugin.iPhoneOS - + + + + com.apple.InterfaceBuilder.CocoaTouchPlugin.InterfaceBuilder3 + YES 3 - 1929 + 3734.1 diff --git a/samples/ProfilePictureSample/ProfilePictureSample/en.lproj/PPViewController_iPhone.xib b/samples/ProfilePictureSample/ProfilePictureSample/en.lproj/PPViewController_iPhone.xib index cd1f659172..e7435c5fb2 100644 --- a/samples/ProfilePictureSample/ProfilePictureSample/en.lproj/PPViewController_iPhone.xib +++ b/samples/ProfilePictureSample/ProfilePictureSample/en.lproj/PPViewController_iPhone.xib @@ -1,14 +1,14 @@ - 1536 - 11E53 - 2840 - 1138.47 - 569.00 + 1280 + 12E55 + 4504 + 1187.39 + 626.00 com.apple.InterfaceBuilder.IBCocoaTouchPlugin - 1926 + 3734.1 IBProxyObject @@ -56,7 +56,7 @@ IBCocoaTouchFramework - {{20, 54}, {218, 275}} + {{20, 71}, {218, 275}} @@ -70,7 +70,7 @@ 292 - {{246, 67}, {70, 37}} + {{246, 84}, {70, 37}} @@ -80,15 +80,15 @@ 0 0 1 - Jason + + 1 + MC4xOTYwNzg0MzE0IDAuMzA5ODAzOTIxNiAwLjUyMTU2ODYyNzUAA + 3 MQA - - 1 - MC4xOTYwNzg0MzQ2IDAuMzA5ODAzOTMyOSAwLjUyMTU2ODY1NgA - + Jason 3 MC41AA @@ -97,8 +97,8 @@ 2 15 - - Helvetica-Bold + + HelveticaNeue-Bold 15 16 @@ -106,7 +106,7 @@ 292 - {{246, 112}, {70, 37}} + {{246, 129}, {70, 37}} @@ -116,20 +116,17 @@ 0 0 1 - Michael + - - 1 - MC4xOTYwNzg0MzQ2IDAuMzA5ODAzOTMyOSAwLjUyMTU2ODY1NgA - + Michael - + 292 - {{246, 157}, {70, 37}} + {{246, 174}, {70, 37}} @@ -139,20 +136,17 @@ 0 0 1 - Vijaye + - - 1 - MC4xOTYwNzg0MzQ2IDAuMzA5ODAzOTMyOSAwLjUyMTU2ODY1NgA - + Vijaye - + 292 - {{246, 202}, {70, 37}} + {{246, 219}, {70, 37}} @@ -162,20 +156,17 @@ 0 0 1 - Random + - - 1 - MC4xOTYwNzg0MzQ2IDAuMzA5ODAzOTMyOSAwLjUyMTU2ODY1NgA - + Random - + 292 - {{246, 247}, {70, 37}} + {{246, 264}, {70, 37}} @@ -185,20 +176,17 @@ 0 0 1 - None + - - 1 - MC4xOTYwNzg0MzQ2IDAuMzA5ODAzOTMyOSAwLjUyMTU2ODY1NgA - + None - + 292 - {{84, 348}, {81, 35}} + {{84, 365}, {81, 35}} @@ -208,20 +196,17 @@ 0 0 1 - Original + - - 1 - MC4xOTYwNzg0MzQ2IDAuMzA5ODAzOTMyOSAwLjUyMTU2ODY1NgA - + Original - + 292 - {{170, 348}, {75, 35}} + {{170, 365}, {75, 35}} @@ -231,20 +216,17 @@ 0 0 1 - Square + - - 1 - MC4xOTYwNzg0MzQ2IDAuMzA5ODAzOTMyOSAwLjUyMTU2ODY1NgA - + Square - + 292 - {{20, 3}, {280, 43}} + {{20, 20}, {280, 43}} @@ -269,7 +251,7 @@ 14 - Helvetica + HelveticaNeue 14 16 @@ -278,7 +260,7 @@ 292 - {{5, 355}, {71, 21}} + {{5, 372}, {71, 21}} @@ -297,8 +279,8 @@ 1 17 - - Helvetica + + HelveticaNeue 17 16 @@ -306,7 +288,7 @@ 292 - {{88, 396}, {89, 37}} + {{88, 413}, {89, 37}} @@ -316,43 +298,36 @@ 0 0 1 - Small View + - - 1 - MC4xOTYwNzg0MzQ2IDAuMzA5ODAzOTMyOSAwLjUyMTU2ODY1NgA - + Small View - + 292 - {{189, 396}, {99, 37}} + {{189, 413}, {99, 37}} - _NS:9 NO IBCocoaTouchFramework 0 0 1 - Large View + - - 1 - MC4xOTYwNzg0MzQ2IDAuMzA5ODAzOTMyOSAwLjUyMTU2ODY1NgA - + Large View - + 292 - {{5, 404}, {75, 21}} + {{5, 421}, {75, 21}} @@ -368,7 +343,7 @@ 0 10 - + {{0, 20}, {320, 460}} @@ -385,6 +360,7 @@ + NO @@ -392,7 +368,7 @@ - 7 + 7 @@ -400,7 +376,7 @@ - 18 + 18 @@ -408,7 +384,7 @@ - 49 + 49 @@ -417,7 +393,7 @@ 7 - 19 + 19 @@ -426,7 +402,7 @@ 7 - 20 + 20 @@ -435,7 +411,7 @@ 7 - 21 + 21 @@ -444,7 +420,7 @@ 7 - 28 + 28 @@ -453,7 +429,7 @@ 7 - 36 + 36 @@ -462,7 +438,7 @@ 7 - 35 + 35 @@ -471,7 +447,7 @@ 7 - 47 + 47 @@ -480,7 +456,7 @@ 7 - 48 + 48 @@ -489,85 +465,85 @@ 7 - 52 + 52 - 0 + 0 - -1 + -1 File's Owner - -2 + -2 - 6 + 6 - + + - + - + - - 9 + 9 - 10 + 10 - 11 + 11 - 25 + 25 - 30 + 30 - 31 + 31 - 34 + 34 - 40 + 40 @@ -575,32 +551,32 @@ - 17 + 17 - 43 + 43 - 44 + 44 - 45 + 45 - 46 + 46 - 50 + 50 @@ -632,7 +608,6 @@ - 52 @@ -719,12 +694,17 @@ 0 IBCocoaTouchFramework + YES com.apple.InterfaceBuilder.CocoaTouchPlugin.iPhoneOS - + + + + com.apple.InterfaceBuilder.CocoaTouchPlugin.InterfaceBuilder3 + YES 3 - 1926 + 3734.1 diff --git a/samples/RPSSample/RPSSample.xcodeproj/project.pbxproj b/samples/RPSSample/RPSSample.xcodeproj/project.pbxproj index 1f1f2babe5..7ee7453dee 100644 --- a/samples/RPSSample/RPSSample.xcodeproj/project.pbxproj +++ b/samples/RPSSample/RPSSample.xcodeproj/project.pbxproj @@ -114,7 +114,9 @@ 84070D191540AA2B00E39703 /* Frameworks */, 84070D171540AA2B00E39703 /* Products */, ); + indentWidth = 4; sourceTree = ""; + tabWidth = 4; }; 84070D171540AA2B00E39703 /* Products */ = { isa = PBXGroup; diff --git a/samples/RPSSample/RPSSample/en.lproj/RPSGameViewController_iPad.xib b/samples/RPSSample/RPSSample/en.lproj/RPSGameViewController_iPad.xib index 5fcbcdaf0a..81c37aecb0 100644 --- a/samples/RPSSample/RPSSample/en.lproj/RPSGameViewController_iPad.xib +++ b/samples/RPSSample/RPSSample/en.lproj/RPSGameViewController_iPad.xib @@ -2,10 +2,10 @@ 1552 - 12C2034 + 12E55 3084 - 1187.34 - 625.00 + 1187.39 + 626.00 com.apple.InterfaceBuilder.IBCocoaTouchPlugin 2083 @@ -25,26 +25,27 @@ PluginDependencyRecalculationVersion - - + + IBFilesOwner IBIPadFramework - + IBFirstResponder IBIPadFramework - + - 274 + 1298 - - - 292 + + + 1316 + {{0, 721}, {768, 283}} - - - _NS:9 + + + 3 MC42NQA @@ -73,61 +74,64 @@ NO - - - 292 + + + 1316 + {{87, 631}, {593, 129}} - - - _NS:9 + + + + + 3 + MQA + NO IBIPadFramework 0 0 - 1 Again! - - 3 - MQA - 1 - MC4xOTYwNzg0MzQ2IDAuMzA5ODAzOTMyOSAwLjUyMTU2ODY1NgA + MC4xOTYwNzg0MzE0IDAuMzA5ODAzOTIxNiAwLjUyMTU2ODYyNzUAA - + 3 MC41AA - + Futura-Medium Futura 0 74 - + Futura-Medium 74 16 - - - 290 + + + 1314 + {768, 44} - - + + + NO NO IBIPadFramework - - - 292 + + + 1316 + {{20, 44}, {263, 116}} - - - _NS:9 + + + NO YES 7 @@ -141,17 +145,18 @@ 0 1 - - + + NO - - - 292 + + + 1316 + {{72, 416}, {263, 116}} - - - _NS:9 + + + NO YES 7 @@ -164,17 +169,18 @@ 0 - - + + NO - - - 292 + + + 1316 + {{229, 416}, {310, 116}} - - - _NS:9 + + + NO YES 7 @@ -188,17 +194,18 @@ 0 1 - - + + NO - - - 292 + + + 1316 + {{253, 168}, {263, 116}} - - - _NS:9 + + + NO YES 7 @@ -212,17 +219,18 @@ 0 1 - - + + NO - - - 292 + + + 1316 + {{485, 292}, {263, 116}} - - - _NS:9 + + + NO YES 7 @@ -236,34 +244,36 @@ 0 1 - - + + NO - - - 292 + + + 1316 + {{87, 416}, {128, 128}} - - - _NS:9 - + + + + 1 NO IBIPadFramework - + NSImage left-rock-128.png - - - 292 + + + 1316 + {{552, 416}, {128, 128}} - - - _NS:9 - + + + + 1 NO IBIPadFramework @@ -272,99 +282,97 @@ R.png - - - 292 - {{87, 631}, {128, 128}} - - - _NS:9 + + + 1316 + + {{87, 631}, {104, 112}} + + + NO IBIPadFramework 0 0 1 - - + + 1 - MC4xOTYwNzg0MzQ2IDAuMzA5ODAzOTMyOSAwLjUyMTU2ODY1NgA + MC4xOTYwNzg0MzE0IDAuMzA5ODAzOTIxNiAwLjUyMTU2ODYyNzUAA - - - + + + 2 15 - + Helvetica-Bold 15 16 - - - 292 - {{320, 631}, {128, 128}} - - - _NS:9 + + + 1316 + + {{320, 631}, {104, 112}} + + + NO IBIPadFramework 0 0 1 - - - 1 - MC4xOTYwNzg0MzQ2IDAuMzA5ODAzOTMyOSAwLjUyMTU2ODY1NgA - - + + + NSImage left-paper-128.png - - - - - - 292 - {{552, 631}, {128, 128}} - - - _NS:9 + + + + + + 1316 + + {{552, 631}, {104, 112}} + + + NO IBIPadFramework 0 0 1 - - - 1 - MC4xOTYwNzg0MzQ2IDAuMzA5ODAzOTMyOSAwLjUyMTU2ODY1NgA - - + + + NSImage left-scissors-128.png - - + + - - - 292 + + + 1316 + {{87, 895}, {401, 89}} - - - _NS:9 + + + NO YES 7 NO IBIPadFramework w: l: t: - + 0 @@ -380,34 +388,35 @@ NO - - - 292 - {{608, 904}, {72, 72}} - - _NS:9 + + + 1316 + + {{608, 904}, {48, 56}} + + + NO IBIPadFramework 0 0 1 - - - 1 - MC4xOTYwNzg0MzQ2IDAuMzA5ODAzOTMyOSAwLjUyMTU2ODY1NgA - - + + + NSImage - IconFacebook-72@2x.png + IconFacebook-72.png - - + + + {{0, 20}, {768, 1004}} - + + 3 MC44OAA @@ -419,320 +428,456 @@ + NO - view - - + againButton + + - 7 + 52 - rockLabel - - + computerHand + + - 36 + 44 - paperLabel - - + facebookButton + + - 37 + 60 - scissorsLabel - - + paperButton + + - 38 + 49 - shootLabel - - + paperLabel + + - 40 + 37 playerHand - - + + - 43 + 43 - computerHand - - + resultLabel + + - 44 + 63 rockButton - - + + - 48 + 48 - paperButton - - + rockLabel + + - 49 + 36 scissorsButton - - + + - 50 + 50 - againButton - - + scissorsLabel + + - 52 + 38 - facebookButton - - + scoreLabel + + - 60 + 61 - scoreLabel - - + shootLabel + + - 61 + 40 - resultLabel - - + view + + - 63 + 7 clickRPSButton: - - + + 7 - 54 + 54 clickRPSButton: - - + + 7 - 55 + 55 clickRPSButton: - - + + 7 - 56 + 56 clickAgainButton: - - + + 7 - 53 + 53 clickFacebookButton: - - + + 7 - 59 + 59 - 0 + 0 - + - -1 - + -1 + File's Owner - -2 - + -2 + - 2 - + 2 + - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + - 5 - - - + 30 + + - 30 - - + 51 + + - 31 - - + 5 + + - 34 - - + 31 + + - 35 - - + 39 + + - 39 - - + 62 + + - 41 - - + 34 + + - 42 - - + 35 + + - 45 - - + 41 + + - 46 - - + 42 + + - 47 - - + 45 + + - 51 - - + 46 + + - 57 - - + 47 + + - 58 - - + 57 + + - 62 - - + 58 + + RPSGameViewController com.apple.InterfaceBuilder.IBCocoaTouchPlugin + UIResponder com.apple.InterfaceBuilder.IBCocoaTouchPlugin + com.apple.InterfaceBuilder.IBCocoaTouchPlugin + + com.apple.InterfaceBuilder.IBCocoaTouchPlugin + + com.apple.InterfaceBuilder.IBCocoaTouchPlugin + + com.apple.InterfaceBuilder.IBCocoaTouchPlugin + + com.apple.InterfaceBuilder.IBCocoaTouchPlugin + + com.apple.InterfaceBuilder.IBCocoaTouchPlugin + + com.apple.InterfaceBuilder.IBCocoaTouchPlugin + + com.apple.InterfaceBuilder.IBCocoaTouchPlugin + + com.apple.InterfaceBuilder.IBCocoaTouchPlugin + + com.apple.InterfaceBuilder.IBCocoaTouchPlugin + + com.apple.InterfaceBuilder.IBCocoaTouchPlugin + + com.apple.InterfaceBuilder.IBCocoaTouchPlugin + + com.apple.InterfaceBuilder.IBCocoaTouchPlugin + + com.apple.InterfaceBuilder.IBCocoaTouchPlugin + + com.apple.InterfaceBuilder.IBCocoaTouchPlugin + + com.apple.InterfaceBuilder.IBCocoaTouchPlugin + + - 63 - + + + + RPSGameViewController + UIViewController + + id + id + id + + + + clickAgainButton: + id + + + clickFacebookButton: + id + + + clickRPSButton: + id + + + + UIButton + UIImageView + UIButton + UIButton + UILabel + UIImageView + UILabel + UIButton + UILabel + UIButton + UILabel + UILabel + UILabel + + + + againButton + UIButton + + + computerHand + UIImageView + + + facebookButton + UIButton + + + paperButton + UIButton + + + paperLabel + UILabel + + + playerHand + UIImageView + + + resultLabel + UILabel + + + rockButton + UIButton + + + rockLabel + UILabel + + + scissorsButton + UIButton + + + scissorsLabel + UILabel + + + scoreLabel + UILabel + + + shootLabel + UILabel + + + + IBProjectSource + ./Classes/RPSGameViewController.h + + + + 0 IBIPadFramework + + com.apple.InterfaceBuilder.CocoaTouchPlugin.InterfaceBuilder3 + + YES 3 - {144, 144} + {72, 72} {50, 45} {128, 128} {128, 128} diff --git a/samples/RPSSample/RPSSample/en.lproj/RPSGameViewController_iPhone.xib b/samples/RPSSample/RPSSample/en.lproj/RPSGameViewController_iPhone.xib index cb87f09295..202db5db3d 100644 --- a/samples/RPSSample/RPSSample/en.lproj/RPSGameViewController_iPhone.xib +++ b/samples/RPSSample/RPSSample/en.lproj/RPSGameViewController_iPhone.xib @@ -1,14 +1,14 @@ - 1552 - 12C2034 - 3084 - 1187.34 - 625.00 + 1280 + 12E55 + 4504 + 1187.39 + 626.00 com.apple.InterfaceBuilder.IBCocoaTouchPlugin - 2083 + 3734.1 IBProxyObject @@ -24,26 +24,25 @@ PluginDependencyRecalculationVersion - - + + IBFilesOwner IBCocoaTouchFramework - + IBFirstResponder IBCocoaTouchFramework - - - 274 + + + 1298 - - - 292 + + + 1316 + {{0, 353}, {320, 146}} - - - _NS:9 + 3 MC42NDg4MzY2Nzg4AA @@ -65,79 +64,74 @@ 17 - Helvetica + HelveticaNeue 17 16 NO - - - 292 + + + 1316 + {{20, 289}, {280, 88}} - - - _NS:9 - NO - IBCocoaTouchFramework - 0 - 0 - 1 - Again! - + + 3 MQA + IBCocoaTouchFramework + 0 + 0 1 MCAwLjI1MDk4MDQwNyAwLjUwMTk2MDgxNAA - + Again! + 3 MC41AA - + Futura-Medium Futura 0 54 - + Futura-Medium 54 16 - - - 292 + + + 1316 + {{20, 20}, {280, 68}} - - - _NS:9 + NO YES 7 NO IBCocoaTouchFramework Rock - + 1 MCAwLjI1MDk4MDQwNyAwLjUwMTk2MDgxNAA 0 - - + + NO - - - 292 + + + 1316 + {{20, 65}, {280, 68}} - - - _NS:9 + NO YES 7 @@ -151,24 +145,23 @@ 0 1 - - + + NO - - - 292 + + + 1316 + {{20, 387}, {280, 77}} - - - _NS:9 + NO YES 7 NO IBCocoaTouchFramework w: l: t: - + 0 3 @@ -186,161 +179,148 @@ NO 280 - - - 292 + + + 1316 + {{20, 120}, {280, 50}} - - - _NS:9 + NO YES 7 NO IBCocoaTouchFramework Scissors - + + 1 + MCAwLjI1MDk4MDQwNyAwLjUwMTk2MDgxNAA + 0 2 - + Futura-CondensedMedium Futura 0 54 - + + Futura-CondensedMedium + 54 + 16 + NO - - - 292 + + + 1316 + {{20, 289}, {88, 88}} - - - _NS:9 + NO IBCocoaTouchFramework 0 0 - 1 1 MCAwLjI1MDk4MDQwNyAwLjUwMTk2MDgxNAA - - 1 - MC4xOTYwNzg0MzQ2IDAuMzA5ODAzOTMyOSAwLjUyMTU2ODY1NgA - - - + + NSImage left-rock-88.png - - - - - 2 + + + + + 1 15 - - Helvetica-Bold + + HelveticaNeue 15 16 - - - 292 + + + 1316 + {{116, 289}, {88, 88}} - - - _NS:9 + NO IBCocoaTouchFramework 0 0 - 1 - 1 MCAwLjI1MDk4MDQwNyAwLjUwMTk2MDgxNAA - - 1 - MC4xOTYwNzg0MzQ2IDAuMzA5ODAzOTMyOSAwLjUyMTU2ODY1NgA - - - + + NSImage left-paper-88.png - - - - - - - - - 292 + + + + + + + + + 1316 + {{212, 289}, {88, 88}} - - - _NS:9 + NO IBCocoaTouchFramework 0 0 - 1 - 1 MCAwLjI1MDk4MDQwNyAwLjUwMTk2MDgxNAA - - 1 - MC4xOTYwNzg0MzQ2IDAuMzA5ODAzOTMyOSAwLjUyMTU2ODY1NgA - - - + + NSImage left-scissors-88.png - - - - - - - - - 292 + + + + + + + + + 1316 + {{20, 178}, {184, 95}} - - - _NS:9 + NO YES 7 NO IBCocoaTouchFramework Shoot! - + + 1 + MCAwLjI1MDk4MDQwNyAwLjUwMTk2MDgxNAA + 0 - - + + NO - - - 292 + + + 1316 + {{107, 182}, {106, 88}} - - - _NS:9 + NO YES 7 @@ -354,18 +334,17 @@ 0 1 - - + + NO - - - 292 + + + 1316 + {{212, 182}, {88, 88}} - - - _NS:9 - + + 4 NO IBCocoaTouchFramework @@ -374,47 +353,52 @@ right-rock-88.png - - - 292 + + + 1316 + {{20, 182}, {88, 88}} - - - _NS:9 - + + 4 NO IBCocoaTouchFramework - + - - - 292 + + + 1316 + {{256, 404}, {44, 44}} - - _NS:9 + NO IBCocoaTouchFramework 0 0 1 - 1 MCAwLjI1MDk4MDQwNyAwLjUwMTk2MDgxNAA - + + NSImage - IconFacebook-72@2x.png + IconFacebook-72.png + + + 2 + 15 + + + HelveticaNeue-Bold + 15 + 16 - - + {{0, 20}, {320, 499}} - - 3 MC44ODE5Mjg2MDQAA @@ -435,426 +419,364 @@ IBCocoaTouchFramework - Retina 4 Full Screen + Retina 4-inch Full Screen 2 IBCocoaTouchFramework + NO - view - - + againButton + + - 3 + 65 - rockLabel - - + computerHand + + - 39 + 60 - rockButton - - + facebookButton + + - 47 + 69 paperButton - - + + - 48 + 48 - scissorsButton - - + paperLabel + + - 49 + 50 - paperLabel - - + playerHand + + - 50 + 66 - scissorsLabel - - + resultLabel + + - 51 + 74 - shootLabel - - + rockButton + + - 52 + 47 - computerHand - - + rockLabel + + - 60 + 39 - againButton - - + scissorsButton + + - 65 + 49 - playerHand - - + scissorsLabel + + - 66 + 51 - facebookButton - - + scoreLabel + + - 69 + 72 - scoreLabel - - + shootLabel + + - 72 + 52 - resultLabel - - + view + + - 74 + 3 clickRPSButton: - - + + 7 - 75 + 75 clickRPSButton: - - + + 7 - 76 + 76 clickRPSButton: - - + + 7 - 77 + 77 clickAgainButton: - - + + 7 - 67 + 67 clickFacebookButton: - - + + 7 - 70 + 70 - 0 + 0 - + - 1 - - - - - - - - - - - - - - - - - + -1 + + File's Owner - -1 - + -2 + - File's Owner - -2 - + 1 + + + + + + + + + + + + + + + + + - 28 - - + 38 + + - 30 - - + 62 + + - 31 - - + 28 + + - 34 - - + 30 + + - 35 - - + 71 + + - 36 - - + 31 + + - 37 - - + 34 + + - 38 - - + 35 + + - 53 - - + 36 + + - 61 - - + 37 + + - 68 - - + 73 + + - 62 - - + 53 + + - 71 - - + 61 + + - 73 - - + 68 + + RPSGameViewController com.apple.InterfaceBuilder.IBCocoaTouchPlugin + UIResponder com.apple.InterfaceBuilder.IBCocoaTouchPlugin + com.apple.InterfaceBuilder.IBCocoaTouchPlugin + + com.apple.InterfaceBuilder.IBCocoaTouchPlugin + + com.apple.InterfaceBuilder.IBCocoaTouchPlugin + + com.apple.InterfaceBuilder.IBCocoaTouchPlugin + + com.apple.InterfaceBuilder.IBCocoaTouchPlugin - + + com.apple.InterfaceBuilder.IBCocoaTouchPlugin - + + com.apple.InterfaceBuilder.IBCocoaTouchPlugin - + + com.apple.InterfaceBuilder.IBCocoaTouchPlugin + + com.apple.InterfaceBuilder.IBCocoaTouchPlugin + + com.apple.InterfaceBuilder.IBCocoaTouchPlugin + + com.apple.InterfaceBuilder.IBCocoaTouchPlugin + + com.apple.InterfaceBuilder.IBCocoaTouchPlugin + + com.apple.InterfaceBuilder.IBCocoaTouchPlugin - + + com.apple.InterfaceBuilder.IBCocoaTouchPlugin + + com.apple.InterfaceBuilder.IBCocoaTouchPlugin + + - 77 - - - - - RPSGameViewController - UIViewController - - id - id - id - - - - clickAgainButton: - id - - - clickFacebookButton: - id - - - clickRPSButton: - id - - - - UIButton - UIImageView - UIButton - UIButton - UILabel - UIImageView - UILabel - UIButton - UILabel - UIButton - UILabel - UILabel - UILabel - - - - againButton - UIButton - - - computerHand - UIImageView - - - facebookButton - UIButton - - - paperButton - UIButton - - - paperLabel - UILabel - - - playerHand - UIImageView - - - resultLabel - UILabel - - - rockButton - UIButton - - - rockLabel - UILabel - - - scissorsButton - UIButton - - - scissorsLabel - UILabel - - - scoreLabel - UILabel - - - shootLabel - UILabel - - - - IBProjectSource - ./Classes/RPSGameViewController.h - - - + 0 IBCocoaTouchFramework + YES + + com.apple.InterfaceBuilder.CocoaTouchPlugin.iPhoneOS + + + + com.apple.InterfaceBuilder.CocoaTouchPlugin.InterfaceBuilder3 + + YES 3 - {144, 144} + {72, 72} {88, 88} {88, 88} {88, 88} {88, 88} - 2083 + 3734.1 diff --git a/samples/Scrumptious/Scrumptious.xcodeproj/project.pbxproj b/samples/Scrumptious/Scrumptious.xcodeproj/project.pbxproj index 101b401195..c2998c0121 100644 --- a/samples/Scrumptious/Scrumptious.xcodeproj/project.pbxproj +++ b/samples/Scrumptious/Scrumptious.xcodeproj/project.pbxproj @@ -106,7 +106,9 @@ B9CD0A72150EA4DD00560A93 /* Frameworks */, B9CD0A70150EA4DD00560A93 /* Products */, ); + indentWidth = 4; sourceTree = ""; + tabWidth = 4; }; B9CD0A70150EA4DD00560A93 /* Products */ = { isa = PBXGroup; diff --git a/samples/Scrumptious/scrumptious/SCAppDelegate.m b/samples/Scrumptious/scrumptious/SCAppDelegate.m index ed9d3fdb64..b2c6a7d544 100644 --- a/samples/Scrumptious/scrumptious/SCAppDelegate.m +++ b/samples/Scrumptious/scrumptious/SCAppDelegate.m @@ -125,6 +125,11 @@ - (void)navigationController:(UINavigationController *)navigationController will - (void)resetMainViewController { self.mainViewController = [[SCViewController alloc] initWithNibName:@"SCViewController" bundle:nil]; +#ifdef __IPHONE_7_0 + if ([self.mainViewController respondsToSelector:@selector(setEdgesForExtendedLayout:)]) { + self.mainViewController.edgesForExtendedLayout &= ~UIRectEdgeTop; + } +#endif } @end diff --git a/samples/Scrumptious/scrumptious/SCViewController.m b/samples/Scrumptious/scrumptious/SCViewController.m index a755c3915e..9a7a00902b 100644 --- a/samples/Scrumptious/scrumptious/SCViewController.m +++ b/samples/Scrumptious/scrumptious/SCViewController.m @@ -299,6 +299,11 @@ -(void)settingsButtonWasPressed:(id)sender { -(void)presentLoginSettings { if (self.settingsViewController == nil) { self.settingsViewController = [[FBUserSettingsViewController alloc] init]; +#ifdef __IPHONE_7_0 + if ([self.settingsViewController respondsToSelector:@selector(setEdgesForExtendedLayout:)]) { + self.settingsViewController.edgesForExtendedLayout &= ~UIRectEdgeTop; + } +#endif self.settingsViewController.delegate = self; } diff --git a/samples/SessionLoginSample/SessionLoginSample.xcodeproj/project.pbxproj b/samples/SessionLoginSample/SessionLoginSample.xcodeproj/project.pbxproj index 31d278bca8..649b59fad3 100644 --- a/samples/SessionLoginSample/SessionLoginSample.xcodeproj/project.pbxproj +++ b/samples/SessionLoginSample/SessionLoginSample.xcodeproj/project.pbxproj @@ -73,7 +73,9 @@ 84C757BB152A38770070DD9C /* Frameworks */, 84C757B9152A38770070DD9C /* Products */, ); + indentWidth = 4; sourceTree = ""; + tabWidth = 4; }; 84C757B9152A38770070DD9C /* Products */ = { isa = PBXGroup; diff --git a/samples/SwitchUserSample/SwitchUserSample.xcodeproj/project.pbxproj b/samples/SwitchUserSample/SwitchUserSample.xcodeproj/project.pbxproj index 153b317781..c80bb28c35 100644 --- a/samples/SwitchUserSample/SwitchUserSample.xcodeproj/project.pbxproj +++ b/samples/SwitchUserSample/SwitchUserSample.xcodeproj/project.pbxproj @@ -94,7 +94,9 @@ 856DB04C153393C3006F6A33 /* Frameworks */, 856DB04A153393C3006F6A33 /* Products */, ); + indentWidth = 4; sourceTree = ""; + tabWidth = 4; }; 856DB04A153393C3006F6A33 /* Products */ = { isa = PBXGroup; diff --git a/samples/SwitchUserSample/SwitchUserSample/SUAppDelegate.m b/samples/SwitchUserSample/SwitchUserSample/SUAppDelegate.m index 6a78dca28f..46910c35b1 100644 --- a/samples/SwitchUserSample/SwitchUserSample/SUAppDelegate.m +++ b/samples/SwitchUserSample/SwitchUserSample/SUAppDelegate.m @@ -44,6 +44,7 @@ - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:( viewController1 = [[SUUsingViewController alloc] initWithNibName:@"SUUsingViewController_iPad" bundle:nil]; viewController2 = [[SUSettingsViewController alloc] initWithNibName:@"SUSettingsViewController_iPad" bundle:nil]; } + viewController2.wantsFullScreenLayout = YES; self.tabBarController = [[UITabBarController alloc] init]; self.tabBarController.viewControllers = [NSArray arrayWithObjects:viewController1, viewController2, nil]; self.window.rootViewController = self.tabBarController; diff --git a/samples/SwitchUserSample/SwitchUserSample/SUSettingsViewController_iPad.xib b/samples/SwitchUserSample/SwitchUserSample/SUSettingsViewController_iPad.xib index 6fd1dc841f..e1a769b90c 100644 --- a/samples/SwitchUserSample/SwitchUserSample/SUSettingsViewController_iPad.xib +++ b/samples/SwitchUserSample/SwitchUserSample/SUSettingsViewController_iPad.xib @@ -1,19 +1,19 @@ - 1296 - 11D50d - 2182 - 1138.32 - 568.00 + 1280 + 12E55 + 4504 + 1187.39 + 626.00 com.apple.InterfaceBuilder.IBCocoaTouchPlugin - 1179 + 3734.1 IBProxyObject - IBUIView IBUITableView + IBUIView com.apple.InterfaceBuilder.IBCocoaTouchPlugin @@ -38,9 +38,9 @@ 274 - {768, 1004} + {{0, 20}, {768, 984}} - + _NS:9 3 @@ -59,6 +59,7 @@ {{0, 20}, {768, 1004}} + 3 @@ -74,6 +75,7 @@ + NO @@ -81,7 +83,7 @@ - 4 + 4 @@ -89,7 +91,7 @@ - 7 + 7 @@ -97,7 +99,7 @@ - 5 + 5 @@ -105,19 +107,19 @@ - 6 + 6 - 0 + 0 - 1 + 1 @@ -125,18 +127,18 @@ - -1 + -1 File's Owner - -2 + -2 - 3 + 3 @@ -154,17 +156,43 @@ - 7 - + + + + SUSettingsViewController + UIViewController + + usersTableView + UITableView + + + usersTableView + + usersTableView + UITableView + + + + IBProjectSource + ./Classes/SUSettingsViewController.h + + + + 0 IBIPadFramework + YES com.apple.InterfaceBuilder.CocoaTouchPlugin.iPhoneOS - + + + + com.apple.InterfaceBuilder.CocoaTouchPlugin.InterfaceBuilder3 + YES 3 - 1179 + 3734.1 diff --git a/samples/SwitchUserSample/SwitchUserSample/SUSettingsViewController_iPhone.xib b/samples/SwitchUserSample/SwitchUserSample/SUSettingsViewController_iPhone.xib index be9f4edcd3..16c1ee6149 100644 --- a/samples/SwitchUserSample/SwitchUserSample/SUSettingsViewController_iPhone.xib +++ b/samples/SwitchUserSample/SwitchUserSample/SUSettingsViewController_iPhone.xib @@ -1,19 +1,19 @@ - 1296 - 11D50d - 2182 - 1138.32 - 568.00 + 1280 + 12E55 + 4504 + 1187.39 + 626.00 com.apple.InterfaceBuilder.IBCocoaTouchPlugin - 1179 + 3734.1 IBProxyObject - IBUIView IBUITableView + IBUIView com.apple.InterfaceBuilder.IBCocoaTouchPlugin @@ -22,26 +22,26 @@ PluginDependencyRecalculationVersion - - + + IBFilesOwner IBCocoaTouchFramework - + IBFirstResponder IBCocoaTouchFramework - + - 274 + 1298 - - - 274 - {320, 460} - + + + 1298 + + {{0, 20}, {320, 440}} + - _NS:9 3 MQA @@ -57,6 +57,7 @@ 22 + {{0, 20}, {320, 460}} @@ -72,104 +73,99 @@ + NO - view - - + usersTableView + + - 3 + 11 - usersTableView - - + view + + - 11 + 3 dataSource - - + + - 7 + 7 delegate - - + + - 8 + 8 - 0 + 0 - + - 1 - - - - + -1 + + File's Owner - -1 - + -2 + - File's Owner - -2 - + 1 + + + + - 4 - - + 4 + + SUSettingsViewController com.apple.InterfaceBuilder.IBCocoaTouchPlugin + UIResponder com.apple.InterfaceBuilder.IBCocoaTouchPlugin + com.apple.InterfaceBuilder.IBCocoaTouchPlugin + + com.apple.InterfaceBuilder.IBCocoaTouchPlugin + + - 11 SUSettingsViewController UIViewController - - performLoginLogout: - id - - - performLoginLogout: - - performLoginLogout: - id - - usersTableView UITableView @@ -190,12 +186,17 @@ 0 IBCocoaTouchFramework + YES com.apple.InterfaceBuilder.CocoaTouchPlugin.iPhoneOS - + + + + com.apple.InterfaceBuilder.CocoaTouchPlugin.InterfaceBuilder3 + YES 3 - 1179 + 3734.1 diff --git a/scripts/build_distribution.sh b/scripts/build_distribution.sh index 214a3bed44..34ac7e8bbf 100755 --- a/scripts/build_distribution.sh +++ b/scripts/build_distribution.sh @@ -87,7 +87,7 @@ $PACKAGEMAKER \ --target 10.5 \ --version $FB_SDK_VERSION \ --out $FB_SDK_UNSIGNED_PKG \ - --title 'Facebook SDK 3.7.1 for iOS' \ + --title 'Facebook SDK 3.8 for iOS' \ || die "PackageMaker reported error" progress_message "Signing package." diff --git a/scripts/build_documentation.sh b/scripts/build_documentation.sh index e254b2176a..e8b08a3a4f 100755 --- a/scripts/build_documentation.sh +++ b/scripts/build_documentation.sh @@ -45,7 +45,7 @@ rm -rf $DOCSET hash $APPLEDOC &>/dev/null if [ "$?" -eq "0" ]; then - APPLEDOC_DOCSET_NAME="Facebook SDK 3.7.1 for iOS" + APPLEDOC_DOCSET_NAME="Facebook SDK 3.8 for iOS" $APPLEDOC --project-name "$APPLEDOC_DOCSET_NAME" \ --project-company "Facebook" \ --company-id "com.facebook" \ diff --git a/scripts/build_framework.sh b/scripts/build_framework.sh index 8206d97fdb..9d4bbc4b1d 100755 --- a/scripts/build_framework.sh +++ b/scripts/build_framework.sh @@ -84,8 +84,10 @@ function xcode_build_target() { || die "XCode build failed for platform: ${1}." } -xcode_build_target "iphonesimulator" "$BUILDCONFIGURATION" -xcode_build_target "iphoneos" "$BUILDCONFIGURATION" +xcode_build_target "iphonesimulator" "${BUILDCONFIGURATION}" +xcode_build_target "iphoneos" "${BUILDCONFIGURATION}" +xcode_build_target "iphonesimulator" "${BUILDCONFIGURATION}64" +xcode_build_target "iphoneos" "${BUILDCONFIGURATION}64" # ----------------------------------------------------------------------------- # Merge lib files for different platforms into universal binary @@ -98,6 +100,8 @@ $LIPO \ -create \ $FB_SDK_BUILD/${BUILDCONFIGURATION}-iphonesimulator/libfacebook_ios_sdk.a \ $FB_SDK_BUILD/${BUILDCONFIGURATION}-iphoneos/libfacebook_ios_sdk.a \ + $FB_SDK_BUILD/${BUILDCONFIGURATION}64-iphonesimulator/libfacebook_ios_sdk.a \ + $FB_SDK_BUILD/${BUILDCONFIGURATION}64-iphoneos/libfacebook_ios_sdk.a \ -output $FB_SDK_UNIVERSAL_BINARY \ || die "lipo failed - could not create universal static library" diff --git a/scripts/run_tests.sh b/scripts/run_tests.sh index 72485c622f..a5b4d26234 100755 --- a/scripts/run_tests.sh +++ b/scripts/run_tests.sh @@ -65,7 +65,6 @@ for SCHEME in $SCHEMES; do -sdk iphonesimulator \ -configuration $BUILDCONFIGURATION \ -scheme $SCHEME \ - TEST_AFTER_BUILD=YES \ - $CLEAN build \ + $CLEAN test \ || die "Error while running unit tests" done diff --git a/src/Cryptography/FBCrypto.m b/src/Cryptography/FBCrypto.m index d3c8af049c..3152bc5135 100644 --- a/src/Cryptography/FBCrypto.m +++ b/src/Cryptography/FBCrypto.m @@ -146,20 +146,26 @@ + (NSData *)randomBytes:(NSUInteger)numOfBytes */ - (NSData *)_macForIV:(NSData *)IV cipherData:(NSData *)cipherData additionalDataToSign:(NSData *)additionalDataToSign { + NSAssert(cipherData.length <= INT_MAX, @""); + int cipherDataLength = (int)cipherData.length; + + NSAssert(additionalDataToSign.length <= INT_MAX, @""); + int additionalDataToSignLength = (int)additionalDataToSign.length; size_t macBufferLength = kCCBlockSizeAES128 + 4 + cipherData.length + 4 + additionalDataToSign.length; uint8_t *macBuffer = malloc(macBufferLength); int offsetIV = 0; int offsetCipherTextLength = offsetIV + kCCBlockSizeAES128; int offsetCipherText = offsetCipherTextLength + 4; - int offsetAdditionalDataLength = offsetCipherText + cipherData.length; + + int offsetAdditionalDataLength = offsetCipherText + cipherDataLength; int offsetAdditionalData = offsetAdditionalDataLength + 4; memcpy(macBuffer + offsetIV, IV.bytes, kCCBlockSizeAES128); // [IV 16 bytes] - FBWriteIntBigEndian(macBuffer + offsetCipherTextLength, cipherData.length); // [length of ciphertext 4 bytes] - memcpy(macBuffer + offsetCipherText, cipherData.bytes, cipherData.length); // [ciphertext] - FBWriteIntBigEndian(macBuffer + offsetAdditionalDataLength, additionalDataToSign.length); // [length of additionalDataToSign, 4 bytes] - memcpy(macBuffer + offsetAdditionalData, additionalDataToSign.bytes, additionalDataToSign.length); + FBWriteIntBigEndian(macBuffer + offsetCipherTextLength, cipherDataLength); // [length of ciphertext 4 bytes] + memcpy(macBuffer + offsetCipherText, cipherData.bytes, cipherDataLength); // [ciphertext] + FBWriteIntBigEndian(macBuffer + offsetAdditionalDataLength, additionalDataToSignLength); // [length of additionalDataToSign, 4 bytes] + memcpy(macBuffer + offsetAdditionalData, additionalDataToSign.bytes, additionalDataToSignLength); uint8_t *result = malloc(CC_SHA256_DIGEST_LENGTH); @@ -174,8 +180,11 @@ - (NSData *)_macForIV:(NSData *)IV cipherData:(NSData *)cipherData additionalDat */ - (NSString *)encrypt:(NSData *)plainText additionalDataToSign:(NSData *)additionalDataToSign { + NSAssert(plainText.length <= INT_MAX, @""); + int plainTextLength = (int)plainText.length; + uint8_t numPaddingBytes = kCCBlockSizeAES128 - (plainText.length % kCCBlockSizeAES128); // Pad 1 .. 16 bytes - int cipherDataLength = plainText.length + numPaddingBytes; + int cipherDataLength = plainTextLength + numPaddingBytes; size_t bufferSize = 1 + CC_SHA256_DIGEST_LENGTH + kCCBlockSizeAES128 + cipherDataLength; int offsetMAC = 1; int offsetIV = offsetMAC + CC_SHA256_DIGEST_LENGTH; @@ -186,8 +195,8 @@ - (NSString *)encrypt:(NSData *)plainText additionalDataToSign:(NSData *)additio NSData *IV = [[self class] randomBytes:kCCBlockSizeAES128]; memcpy(buffer + offsetIV, IV.bytes, IV.length); - memcpy(buffer + offsetCipherData, plainText.bytes, plainText.length); // Copy input in - fbdfl_SecRandomCopyBytes([FBDynamicFrameworkLoader loadkSecRandomDefault], numPaddingBytes, buffer + offsetCipherData + plainText.length); // Random pad + memcpy(buffer + offsetCipherData, plainText.bytes, plainTextLength); // Copy input in + fbdfl_SecRandomCopyBytes([FBDynamicFrameworkLoader loadkSecRandomDefault], numPaddingBytes, buffer + offsetCipherData + plainTextLength); // Random pad buffer[offsetCipherData + cipherDataLength - 1] = numPaddingBytes; // Record the number of padded bytes at the end size_t numOutputBytes = 0; @@ -213,10 +222,13 @@ - (NSString *)encrypt:(NSData *)plainText additionalDataToSign:(NSData *)additio - (NSData *)decrypt:(NSString *)base64EncodedCipherText additionalSignedData:(NSData *)additionalSignedData { NSData *cipherText = FBDecodeBase64(base64EncodedCipherText); - if (!cipherText || cipherText.length < 1 + CC_SHA256_DIGEST_LENGTH + kCCBlockSizeAES128) { + NSAssert(cipherText.length <= INT_MAX, @""); + int cipherTextLength = (int)cipherText.length; + + if (!cipherText || cipherTextLength < 1 + CC_SHA256_DIGEST_LENGTH + kCCBlockSizeAES128) { return nil; } - int cipherDataLength = cipherText.length - (1 + CC_SHA256_DIGEST_LENGTH + kCCBlockSizeAES128); + int cipherDataLength = cipherTextLength - (1 + CC_SHA256_DIGEST_LENGTH + kCCBlockSizeAES128); if (cipherDataLength % kCCBlockSizeAES128 != 0) { return nil; } diff --git a/src/FBAccessTokenData.h b/src/FBAccessTokenData.h index ef6ce35d8d..1665d5abe7 100644 --- a/src/FBAccessTokenData.h +++ b/src/FBAccessTokenData.h @@ -68,6 +68,25 @@ loginType:(FBSessionLoginType)loginType refreshDate:(NSDate *)refreshDate; +/*! + @method + + @abstract Creates an FBAccessTokenData from existing information or returns nil if required data is missing. + + @param accessToken The token string. If nil or empty, this method will return nil. + @param permissions The permissions set. A value of nil indicates basic permissions. + @param expirationDate The expiration date. A value of nil defaults to `[NSDate distantFuture]`. + @param loginType The login source of the token. + @param refreshDate The date that token was last refreshed. A value of nil defaults to `[NSDate date]`. + @param permissionsRefreshDate The date the permissions were last refreshed. A value of nil defaults to `[NSDate distantPast]`. + */ ++ (FBAccessTokenData *) createTokenFromString:(NSString *)accessToken + permissions:(NSArray *)permissions + expirationDate:(NSDate *)expirationDate + loginType:(FBSessionLoginType)loginType + refreshDate:(NSDate *)refreshDate + permissionsRefreshDate:(NSDate *)permissionsRefreshDate; + /*! @method @@ -112,4 +131,9 @@ */ @property (readonly, nonatomic, copy) NSDate *refreshDate; +/*! + @abstract returns the date the permissions were last refreshed. +*/ +@property (readonly, nonatomic, copy) NSDate *permissionsRefreshDate; + @end diff --git a/src/FBAccessTokenData.m b/src/FBAccessTokenData.m index 67765131dd..c53a0ea3de 100644 --- a/src/FBAccessTokenData.m +++ b/src/FBAccessTokenData.m @@ -28,33 +28,25 @@ @interface FBAccessTokenData() @property (nonatomic, readwrite, copy) NSDate *refreshDate; @property (nonatomic, readwrite, copy) NSArray *permissions; -- (id) initWithToken:(NSString *)accessToken - permissions:(NSArray *)permissions - expirationDate:(NSDate *)expirationDate - loginType:(FBSessionLoginType)loginType - refreshDate:(NSDate *)refreshDate; +@property (nonatomic, readwrite, copy) NSDate *permissionsRefreshDate; @end @implementation FBAccessTokenData -@synthesize accessToken = _accessToken, - permissions = _permissions, - expirationDate = _expirationDate, - loginType = _loginType, - refreshDate = _refreshDate; - -- (id) initWithToken:(NSString *)accessToken - permissions:(NSArray *)permissions - expirationDate:(NSDate *)expirationDate - loginType:(FBSessionLoginType)loginType - refreshDate:(NSDate *)refreshDate { - if (self = [super init]){ +- (id) initWithToken:(NSString *)accessToken + permissions:(NSArray *)permissions + expirationDate:(NSDate *)expirationDate + loginType:(FBSessionLoginType)loginType + refreshDate:(NSDate *)refreshDate + permissionsRefreshDate:(NSDate *)permissionsRefreshDate { + if ((self = [super init])){ _accessToken = [accessToken copy]; _permissions = [permissions copy]; _expirationDate = [expirationDate copy]; _refreshDate = [refreshDate copy]; _loginType = loginType; + _permissionsRefreshDate = [permissionsRefreshDate copy]; } return self; } @@ -64,6 +56,7 @@ - (void) dealloc { [_permissions release]; [_expirationDate release]; [_refreshDate release]; + [_permissionsRefreshDate release]; [super dealloc]; } @@ -92,6 +85,7 @@ + (FBAccessTokenData *) createTokenFromDictionary:(NSDictionary *)dictionary { FBSessionLoginType dictionaryLoginType = [dictionary[FBTokenInformationLoginTypeLoginKey] intValue]; BOOL dictionaryIsFacebookLoginType = [dictionary[FBTokenInformationIsFacebookLoginKey] boolValue]; NSDate *dictionaryRefreshDate = dictionary[FBTokenInformationRefreshDateKey]; + NSDate *dictionaryPermissionsRefreshDate = dictionary[FBTokenInformationPermissionsRefreshDateKey]; if (dictionaryIsFacebookLoginType && dictionaryLoginType == FBSessionLoginTypeNone) { // The internal isFacebookLogin has been removed but to support backwards compatibility, @@ -103,15 +97,30 @@ + (FBAccessTokenData *) createTokenFromDictionary:(NSDictionary *)dictionary { permissions:dictionaryPermissions expirationDate:dictionaryExpirationDate loginType:dictionaryLoginType - refreshDate:dictionaryRefreshDate]; + refreshDate:dictionaryRefreshDate + permissionsRefreshDate:dictionaryPermissionsRefreshDate]; return tokenData; } ++ (FBAccessTokenData *) createTokenFromString:(NSString *)accessToken + permissions:(NSArray *)permissions + expirationDate:(NSDate *)expirationDate + loginType:(FBSessionLoginType)loginType + refreshDate:(NSDate *)refreshDate { + return [self createTokenFromString:accessToken + permissions:permissions + expirationDate:expirationDate + loginType:loginType + refreshDate:refreshDate + permissionsRefreshDate:nil]; +} + + (FBAccessTokenData *) createTokenFromString:(NSString *)accessToken permissions:(NSArray *)permissions expirationDate:(NSDate *)expirationDate loginType:(FBSessionLoginType)loginType refreshDate:(NSDate *)refreshDate + permissionsRefreshDate:(NSDate *)permissionsRefreshDate { if (accessToken == nil || [accessToken stringByTrimmingCharactersInSet: [NSCharacterSet whitespaceCharacterSet]].length == 0) { @@ -123,11 +132,16 @@ + (FBAccessTokenData *) createTokenFromString:(NSString *)accessToken if (refreshDate == nil) { refreshDate = [NSDate date]; } + if (permissionsRefreshDate == nil) { + permissionsRefreshDate = [NSDate distantPast]; + } + FBAccessTokenData* fbAccessToken = [[FBAccessTokenData alloc] initWithToken:accessToken permissions:permissions expirationDate:expirationDate loginType:loginType - refreshDate:refreshDate]; + refreshDate:refreshDate + permissionsRefreshDate:permissionsRefreshDate]; return [fbAccessToken autorelease]; } @@ -151,6 +165,7 @@ - (NSUInteger)hash { result = prime * result + [self.expirationDate hash]; result = prime * result + [self.refreshDate hash]; result = prime * result + self.loginType; + result = prime * result + [self.permissionsRefreshDate hash]; return result; } @@ -161,7 +176,8 @@ - (id)copyWithZone:(NSZone *)zone permissions:self.permissions expirationDate:self.expirationDate loginType:self.loginType - refreshDate:self.refreshDate]; + refreshDate:self.refreshDate + permissionsRefreshDate:self.permissionsRefreshDate]; return copy; } @@ -174,7 +190,8 @@ - (BOOL) isEqualToAccessTokenData:(FBAccessTokenData *)accessTokenData { && [[NSSet setWithArray:self.permissions] isEqualToSet:[NSSet setWithArray:accessTokenData.permissions]] && [self.expirationDate isEqualToDate:accessTokenData.expirationDate] && self.loginType == accessTokenData.loginType - && [self.refreshDate isEqualToDate:accessTokenData.refreshDate]) { + && [self.refreshDate isEqualToDate:accessTokenData.refreshDate] + && [self.permissionsRefreshDate isEqualToDate:accessTokenData.permissionsRefreshDate]) { return YES; } @@ -188,10 +205,13 @@ - (NSMutableDictionary *) dictionary { [NSNumber numberWithInt:self.loginType], FBTokenInformationLoginTypeLoginKey, nil]; if (self.refreshDate) { - [dict setObject:self.refreshDate forKey:FBTokenInformationRefreshDateKey]; + dict[FBTokenInformationRefreshDateKey] = self.refreshDate; } if (self.permissions) { - [dict setObject:self.permissions forKey:FBTokenInformationPermissionsKey]; + dict[FBTokenInformationPermissionsKey] = self.permissions; + } + if (self.permissionsRefreshDate) { + dict[FBTokenInformationPermissionsRefreshDateKey] = self.permissionsRefreshDate; } return [dict autorelease]; } diff --git a/src/FBAppBridge.m b/src/FBAppBridge.m index 097420ca1a..4508e9cefb 100644 --- a/src/FBAppBridge.m +++ b/src/FBAppBridge.m @@ -365,12 +365,13 @@ - (void)handleDidBecomeActive { @try { if (handler) { if (!error) { - call.error = [NSError errorWithDomain:FacebookSDKDomain + error = [NSError errorWithDomain:FacebookSDKDomain code:FBErrorAppActivatedWhilePendingAppCall userInfo:@{NSLocalizedDescriptionKey : @"The user navigated away from " @"the Facebook app prior to completing this AppCall. This AppCall is now cancelled " @"and needs to be retried to get a successful completion"}]; } + call.error = error; // Passing nil for results, since we are effectively cancelling this action handler(call); diff --git a/src/FBAppBridgeTypeToJSONConverter.m b/src/FBAppBridgeTypeToJSONConverter.m index 0961fdac61..8f31616ca1 100644 --- a/src/FBAppBridgeTypeToJSONConverter.m +++ b/src/FBAppBridgeTypeToJSONConverter.m @@ -122,7 +122,7 @@ - (NSDictionary *)convertedDictionaryFromDictionary:(NSDictionary *)dictionary } - (NSArray *)convertedArrayFromArray:(NSArray *)array convertingToJSON:(BOOL)convertingToJSON { - int length = array.count; + NSUInteger length = array.count; if (length == 0) { return array; } diff --git a/src/FBAppCall.m b/src/FBAppCall.m index 45f2ddf0d2..3b9860d194 100644 --- a/src/FBAppCall.m +++ b/src/FBAppCall.m @@ -19,6 +19,7 @@ #import "FBUtility.h" #import "FBError.h" #import "FBSession+Internal.h" +#import "FBSessionUtility.h" #import "FBAccessTokenData+Internal.h" #import "FBDialogsData+Internal.h" #import "FBAppLinkData+Internal.h" @@ -293,7 +294,7 @@ + (BOOL)handleOpenURL:(NSURL *)url appID:[FBSettings defaultAppID] urlSchemeSuffix:[FBSettings defaultUrlSchemeSuffix]]; - if ([FBSession isOpenSessionResponseURL:url]) { + if ([FBSessionUtility isOpenSessionResponseURL:url]) { // If we're here, it's because the session was not specified or was not expecting a token. In some cases, // it might be fine to go ahead and open the session anyways. if ([FBAppCall tryOpenSession:workingSession withAccessToken:accessToken]) { @@ -412,7 +413,7 @@ + (void)openDeferredAppLink:(FBAppLinkFallbackHandler)fallbackHandler { [deferredAppLinkParameters setObject:advertiserID forKey:@"advertiser_id"]; } - [FBUtility updateParametersWithEventUsageLimits:deferredAppLinkParameters]; + [FBUtility updateParametersWithEventUsageLimitsAndBundleInfo:deferredAppLinkParameters]; FBRequest *deferredAppLinkRequest = [[[FBRequest alloc] initForPostWithSession:nil graphPath:[NSString stringWithFormat:@"%@/activities", appID, nil] diff --git a/src/FBAppEvents+Internal.h b/src/FBAppEvents+Internal.h index 02dfcd3073..1df99b4cf9 100644 --- a/src/FBAppEvents+Internal.h +++ b/src/FBAppEvents+Internal.h @@ -50,10 +50,10 @@ extern NSString *const FBAppEventNameUserSettingsUsage; extern NSString *const FBAppEventParameterDialogOutcome; /*! Use to log the result of a call to FBDialogs canPresentShareDialogWithParams: */ -extern NSString *const FBAppEventNameFBDialogsCanPresentShareDialog; +extern NSString *const FBAppEventNameFBDialogsPresentShareDialog; /*! Use to log the result of a call to FBDialogs canPresentShareDialogWithOpenGraphActionParams: */ -extern NSString *const FBAppEventNameFBDialogsCanPresentShareDialogOG; +extern NSString *const FBAppEventNameFBDialogsPresentShareDialogOG; /*! Use to log the start of an auth request that cannot be fulfilled by the token cache */ extern NSString *const FBAppEventNameFBSessionAuthStart; diff --git a/src/FBAppEvents.m b/src/FBAppEvents.m index ec7e8e3dc5..f52a519070 100644 --- a/src/FBAppEvents.m +++ b/src/FBAppEvents.m @@ -86,8 +86,9 @@ NSString *const FBAppEventNameShareSheetDismiss = @"fb_share_sheet_dismiss"; NSString *const FBAppEventNamePermissionsUILaunch = @"fb_permissions_ui_launch"; NSString *const FBAppEventNamePermissionsUIDismiss = @"fb_permissions_ui_dismiss"; -NSString *const FBAppEventNameFBDialogsCanPresentShareDialog = @"fb_dialogs_can_present_share"; -NSString *const FBAppEventNameFBDialogsCanPresentShareDialogOG = @"fb_dialogs_can_present_share_og"; +NSString *const FBAppEventNameFBDialogsPresentShareDialog = @"fb_dialogs_present_share"; +NSString *const FBAppEventNameFBDialogsPresentShareDialogOG = @"fb_dialogs_present_share_og"; + NSString *const FBAppEventNameFBDialogsNativeLoginDialogStart = @"fb_dialogs_native_login_dialog_start"; NSString *const FBAppEventsNativeLoginDialogStartTime = @"fb_native_login_dialog_start_time"; @@ -534,6 +535,10 @@ - (void)instanceLogEvent:(NSString *)eventName [eventDictionary setObject:valueToSum forKey:@"_valueToSum"]; } + if (isImplicitlyLogged) { + [eventDictionary setObject:@"1" forKey:@"_implicitlyLogged"]; + } + @synchronized (self) { if ([FBSettings appVersion]) { [eventDictionary setObject:[FBSettings appVersion] forKey:@"_appVersion"]; @@ -556,10 +561,12 @@ - (void)instanceLogEvent:(NSString *)eventName [appEventsState addEvent:eventDictionary isImplicit:isImplicitlyLogged]; - [FBLogger singleShotLogEntry:FBLoggingBehaviorAppEvents - formatString:@"FBAppEvents: Recording event @ %ld: %@", - [FBAppEvents unixTimeNow], - eventDictionary]; + if (!isImplicitlyLogged) { + [FBLogger singleShotLogEntry:FBLoggingBehaviorAppEvents + formatString:@"FBAppEvents: Recording event @ %ld: %@", + [FBAppEvents unixTimeNow], + eventDictionary]; + } BOOL eventsRetrievedFromPersistedData = NO; if (self.haveOutstandingPersistedData) { @@ -662,7 +669,7 @@ - (void)flushOnMainQueue:(FBAppEventsFlushReason)flushReason } NSString *jsonEncodedEvents; - int eventCount, numSkipped; + NSUInteger eventCount, numSkipped; @synchronized (appEventsState) { [appEventsState.inFlightEvents addObjectsFromArray:appEventsState.accumulatedEvents]; @@ -695,7 +702,7 @@ - (void)flushOnMainQueue:(FBAppEventsFlushReason)flushReason ]; if (numSkipped > 0) { - postParameters[@"num_skipped_events"] = [NSString stringWithFormat:@"%d", numSkipped]; + postParameters[@"num_skipped_events"] = [NSString stringWithFormat:@"%lu", (unsigned long)numSkipped]; } [self appendAttributionAndAdvertiserIDs:postParameters @@ -713,9 +720,9 @@ - (void)flushOnMainQueue:(FBAppEventsFlushReason)flushReason NSMutableDictionary *paramsForPrinting = [NSMutableDictionary dictionaryWithDictionary:postParameters]; [paramsForPrinting removeObjectForKey:@"custom_events_file"]; - loggingEntry = [NSString stringWithFormat:@"FBAppEvents: Flushed @ %ld, %d events due to '%@' - %@\nEvents: %@", + loggingEntry = [NSString stringWithFormat:@"FBAppEvents: Flushed @ %ld, %lu events due to '%@' - %@\nEvents: %@", [FBAppEvents unixTimeNow], - eventCount, + (unsigned long)eventCount, [FBAppEvents flushReasonToString:flushReason], paramsForPrinting, prettyPrintedJsonEvents]; @@ -753,8 +760,8 @@ - (void)appendAttributionAndAdvertiserIDs:(NSMutableDictionary *)postParameters if (advertiserID) { [postParameters setObject:advertiserID forKey:@"advertiser_id"]; } - - [FBUtility updateParametersWithEventUsageLimits:postParameters]; + + [FBUtility updateParametersWithEventUsageLimitsAndBundleInfo:postParameters]; } - (BOOL)doesSessionHaveUserToken:(FBSession *)session { @@ -866,7 +873,7 @@ - (void)handleActivitiesPostCompletion:(NSError *)error FlushResult flushResult = FlushResultSuccess; if (error) { - int errorCode = [[[error userInfo] objectForKey:FBErrorHTTPStatusCodeKey] integerValue]; + NSInteger errorCode = [[[error userInfo] objectForKey:FBErrorHTTPStatusCodeKey] integerValue]; // We interpret a 400 coming back from FBRequestConnection as a server error due to improper data being // sent down. Otherwise we assume no connectivity, or another condition where we could treat it as no connectivity. @@ -1039,7 +1046,7 @@ + (void)persistAppEventsData:(FBSessionAppEventsState *)appEventsState { [appEventsState.accumulatedEvents removeAllObjects]; [FBLogger singleShotLogEntry:FBLoggingBehaviorAppEvents - formatString:@"FBAppEvents Persist: Writing %d events", appEventsState.inFlightEvents.count]; + formatString:@"FBAppEvents Persist: Writing %lu events", (unsigned long)appEventsState.inFlightEvents.count]; if (!appEventsState.inFlightEvents.count) { return; @@ -1072,7 +1079,8 @@ + (NSDictionary *)retrievePersistedAppEventData { [content release]; [FBLogger singleShotLogEntry:FBLoggingBehaviorAppEvents - formatString:@"FBAppEvents Persist: Read %d events", results ? [[results objectForKey:FBAppEventsPersistKeyEvents] count] : 0]; + formatString:@"FBAppEvents Persist: Read %lu events", + (unsigned long)(results ? [[results objectForKey:FBAppEventsPersistKeyEvents] count] : 0)]; return results; } @@ -1107,10 +1115,12 @@ - (FBRequest *)instanceCustomAudienceThirdPartyIDRequest:(FBSession *)session { // Rules for how we use the attribution ID / advertiser ID for an 'custom_audience_third_party_id' Graph API request // 1) if the OS tells us that the user has Limited Ad Tracking, then just don't send, and return a nil in the token. - // 2) if we have a user session token, then no need to send attribution ID / advertiser ID back as the udid parameter - // 3) otherwise, send back the udid parameter. + // 2) if the app has set 'limitEventUsage', this effectively implies that app-initiated ad targeting shouldn't happen, + // so use that data here to return nil as well. + // 3) if we have a user session token, then no need to send attribution ID / advertiser ID back as the udid parameter + // 4) otherwise, send back the udid parameter. - if ([FBUtility advertisingTrackingStatus] == AdvertisingTrackingDisallowed) { + if ([FBUtility advertisingTrackingStatus] == AdvertisingTrackingDisallowed || [FBAppEvents limitEventUsage]) { return nil; } diff --git a/src/FBCacheIndex.m b/src/FBCacheIndex.m index f1451cd146..a1362b207e 100644 --- a/src/FBCacheIndex.m +++ b/src/FBCacheIndex.m @@ -359,31 +359,31 @@ - (void)cache:(NSCache*)cache willEvictObject:(id)obj - (void)_updateEntryInDatabaseForKey:(NSString*)key entry:(FBCacheEntityInfo*)entry { - NSAssert(dispatch_get_current_queue() == _databaseQueue, @""); initializeStatement(_database, &_updateStatement, updateQuery); CHECK_SQLITE_SUCCESS(fbdfl_sqlite3_bind_text( _updateStatement, 1, entry.uuid.UTF8String, - entry.uuid.length, + (int)entry.uuid.length, nil), _database); CHECK_SQLITE_SUCCESS(fbdfl_sqlite3_bind_double( _updateStatement, 2, entry.accessTime), _database); - + + NSAssert(entry.fileSize <= INT_MAX, @""); CHECK_SQLITE_SUCCESS(fbdfl_sqlite3_bind_int( _updateStatement, 3, - entry.fileSize), _database); + (int)entry.fileSize), _database); CHECK_SQLITE_SUCCESS(fbdfl_sqlite3_bind_text( _updateStatement, 4, entry.key.UTF8String, - entry.key.length, + (int)entry.key.length, nil), _database); CHECK_SQLITE_DONE(fbdfl_sqlite3_step(_updateStatement), _database); @@ -393,8 +393,6 @@ - (void)_updateEntryInDatabaseForKey:(NSString*)key - (void)_writeEntryInDatabase:(FBCacheEntityInfo*)entry { - NSAssert(dispatch_get_current_queue() == _databaseQueue, @""); - FBCacheEntityInfo* existing = [self _readEntryFromDatabase:entry.key]; if (existing) { @@ -414,14 +412,14 @@ - (void)_writeEntryInDatabase:(FBCacheEntityInfo*)entry _insertStatement, 1, entry.uuid.UTF8String, - entry.uuid.length, + (int)entry.uuid.length, nil), _database); CHECK_SQLITE_SUCCESS(fbdfl_sqlite3_bind_text( _insertStatement, 2, entry.key.UTF8String, - entry.key.length, + (int)entry.key.length, nil), _database); CHECK_SQLITE_SUCCESS(fbdfl_sqlite3_bind_double( @@ -429,10 +427,11 @@ - (void)_writeEntryInDatabase:(FBCacheEntityInfo*)entry 3, entry.accessTime), _database); + NSAssert(entry.fileSize <= INT_MAX, @""); CHECK_SQLITE_SUCCESS(fbdfl_sqlite3_bind_int( _insertStatement, 4, - entry.fileSize), _database); + (int)entry.fileSize), _database); CHECK_SQLITE_DONE(fbdfl_sqlite3_step(_insertStatement), _database); @@ -441,14 +440,13 @@ - (void)_writeEntryInDatabase:(FBCacheEntityInfo*)entry - (FBCacheEntityInfo*)_readEntryFromDatabase:(NSString*)key { - NSAssert(dispatch_get_current_queue() == _databaseQueue, @""); initializeStatement(_database, &_selectByKeyStatement, selectByKeyQuery); CHECK_SQLITE_SUCCESS(fbdfl_sqlite3_bind_text( _selectByKeyStatement, 1, key.UTF8String, - key.length, + (int)key.length, nil), _database); return [self _createCacheEntityInfo:_selectByKeyStatement]; @@ -457,8 +455,6 @@ - (FBCacheEntityInfo*)_readEntryFromDatabase:(NSString*)key - (NSMutableArray*) _readEntriesFromDatabase:(NSString*)keyFragment excludingFragment:(BOOL)exclude { - NSAssert(dispatch_get_current_queue() == _databaseQueue, @""); - sqlite3_stmt* selectStatement; const char* query; if (exclude) { @@ -476,7 +472,7 @@ - (NSMutableArray*) _readEntriesFromDatabase:(NSString*)keyFragment selectStatement, 1, wildcardKeyFragment.UTF8String, - wildcardKeyFragment.length, + (int)wildcardKeyFragment.length, nil), _database); NSMutableArray *entries = [[[NSMutableArray alloc] init] autorelease]; @@ -518,8 +514,6 @@ -(FBCacheEntityInfo*)_createCacheEntityInfo:(sqlite3_stmt*)selectStatement - (void)_fetchCurrentDiskUsage { - NSAssert(dispatch_get_current_queue() == _databaseQueue, @""); - sqlite3_stmt* sizeStatement = nil; initializeStatement(_database, &sizeStatement, selectStorageSizeQuery); @@ -530,8 +524,6 @@ - (void)_fetchCurrentDiskUsage - (FBCacheEntityInfo*)_entryForKey:(NSString*)key { - NSAssert(dispatch_get_current_queue() != _databaseQueue, @""); - __block FBCacheEntityInfo *entryInfo = [_cachedEntries objectForKey:key]; if (entryInfo == nil) { // TODO: This is really bad if higher layers are going to be @@ -550,14 +542,12 @@ - (FBCacheEntityInfo*)_entryForKey:(NSString*)key - (void)_removeEntryFromDatabaseForKey:(NSString*)key { - NSAssert(dispatch_get_current_queue() == _databaseQueue, @""); - initializeStatement(_database, &_removeByKeyStatement, deleteEntryQuery); CHECK_SQLITE_SUCCESS(fbdfl_sqlite3_bind_text( _removeByKeyStatement, 1, key.UTF8String, - key.length, + (int)key.length, nil), _database); CHECK_SQLITE_DONE(fbdfl_sqlite3_step(_removeByKeyStatement), _database); @@ -589,7 +579,6 @@ - (void)_flushOrphanedFiles // - drop the temporary 'trimmed' table. - (void)_trimDatabase { - NSAssert(dispatch_get_current_queue() == _databaseQueue, @""); NSAssert(_currentDiskUsage > _diskCapacity, @""); if (_currentDiskUsage <= _diskCapacity) { return; diff --git a/src/FBDialog.m b/src/FBDialog.m index 84859ef9d0..f20e290be1 100644 --- a/src/FBDialog.m +++ b/src/FBDialog.m @@ -513,7 +513,7 @@ - (void)webView:(UIWebView *)webView didFailLoadWithError:(NSError *)error { - (void)deviceOrientationDidChange:(void*)object { UIInterfaceOrientation orientation = [UIApplication sharedApplication].statusBarOrientation; - if (!_showingKeyboard && [self shouldRotateToOrientation:orientation]) { + if ([self shouldRotateToOrientation:orientation]) { [self updateWebOrientation]; CGFloat duration = [UIApplication sharedApplication].statusBarOrientationAnimationDuration; diff --git a/src/FBDialogs.m b/src/FBDialogs.m index 81edb3dcb9..59e994fe3f 100644 --- a/src/FBDialogs.m +++ b/src/FBDialogs.m @@ -118,7 +118,7 @@ + (BOOL)presentOSIntegratedShareDialogModallyFrom:(UIViewController*)viewControl valueToSum:nil parameters:@{ @"render_type" : @"Native" } session:session]; - [viewController presentModalViewController:composeViewController animated:YES]; + [viewController presentViewController:composeViewController animated:YES completion:nil]; return YES; } @@ -171,15 +171,7 @@ + (FBAppCall *)presentLoginDialogWithParams:(FBLoginDialogParams *)params } + (BOOL)canPresentShareDialogWithParams:(FBShareDialogParams *)params { - BOOL canPresent = [params appBridgeVersion] != nil; - [FBAppEvents logImplicitEvent:FBAppEventNameFBDialogsCanPresentShareDialog - valueToSum:nil - parameters:@{ FBAppEventParameterDialogOutcome : - canPresent ? - FBAppEventsDialogOutcomeValue_Completed : - FBAppEventsDialogOutcomeValue_Failed } - session:nil]; - return canPresent; + return [params appBridgeVersion] != nil; } + (FBAppCall *)presentShareDialogWithParams:(FBShareDialogParams *)params @@ -205,6 +197,12 @@ + (FBAppCall *)presentShareDialogWithParams:(FBShareDialogParams *)params } }]; } + [FBAppEvents logImplicitEvent:FBAppEventNameFBDialogsPresentShareDialog + valueToSum:nil + parameters:@{ FBAppEventParameterDialogOutcome : call ? + FBAppEventsDialogOutcomeValue_Completed : + FBAppEventsDialogOutcomeValue_Failed } + session:nil]; return call; } @@ -253,15 +251,7 @@ + (FBAppCall *)presentShareDialogWithLink:(NSURL *)link } + (BOOL)canPresentShareDialogWithOpenGraphActionParams:(FBOpenGraphActionShareDialogParams *)params { - BOOL canPresent = [params appBridgeVersion] != nil; - [FBAppEvents logImplicitEvent:FBAppEventNameFBDialogsCanPresentShareDialogOG - valueToSum:nil - parameters:@{ FBAppEventParameterDialogOutcome : - canPresent ? - FBAppEventsDialogOutcomeValue_Completed : - FBAppEventsDialogOutcomeValue_Failed } - session:nil]; - return canPresent; + return [params appBridgeVersion] != nil; } + (FBAppCall *)presentShareDialogWithOpenGraphActionParams:(FBOpenGraphActionShareDialogParams *)params @@ -295,7 +285,13 @@ + (FBAppCall *)presentShareDialogWithOpenGraphActionParams:(FBOpenGraphActionSha }]; } } - + [FBAppEvents logImplicitEvent:FBAppEventNameFBDialogsPresentShareDialogOG + valueToSum:nil + parameters:@{ FBAppEventParameterDialogOutcome : call ? + FBAppEventsDialogOutcomeValue_Completed : + FBAppEventsDialogOutcomeValue_Failed } + session:nil]; + return call; } diff --git a/src/FBErrorUtility+Internal.h b/src/FBErrorUtility+Internal.h index 2e57732ba6..a985d67ba7 100644 --- a/src/FBErrorUtility+Internal.h +++ b/src/FBErrorUtility+Internal.h @@ -39,7 +39,7 @@ extern const int FBOAuthError; andShouldNotifyUser:(BOOL *)pshouldNotifyUser; + (void)fberrorGetCodeValueForError:(NSError *)error - index:(int)index + index:(NSUInteger)index code:(int *)pcode subcode:(int *)psubcode; diff --git a/src/FBErrorUtility.m b/src/FBErrorUtility.m index da93129ddf..b7119211ec 100644 --- a/src/FBErrorUtility.m +++ b/src/FBErrorUtility.m @@ -240,7 +240,7 @@ + (BOOL)fberrorIsErrorFromSystemSession:(NSError *)error { } + (void)fberrorGetCodeValueForError:(NSError *)error - index:(int)index + index:(NSUInteger)index code:(int *)pcode subcode:(int *)psubcode { diff --git a/src/FBFrictionlessRecipientCache.m b/src/FBFrictionlessRecipientCache.m index 25e2b328e6..fabd187132 100644 --- a/src/FBFrictionlessRecipientCache.m +++ b/src/FBFrictionlessRecipientCache.m @@ -116,7 +116,7 @@ - (void)webDialogsWillPresentDialog:(NSString *)dialog // if value parses as a json array expression get the list that way id fbids = [FBUtility simpleJSONDecode:fbid]; if (![fbids isKindOfClass:[NSArray class]]) { - // otherwise seperate by commas (handles the singleton case too) + // otherwise separate by commas (handles the singleton case too) fbids = [fbid componentsSeparatedByString:@","]; } self.frictionlessShouldMakeViewInvisible = [self.frictionlessSettings isFrictionlessEnabledForRecipients:fbids]; diff --git a/src/FBFrictionlessRequestSettings.m b/src/FBFrictionlessRequestSettings.m index f3864f88e7..4124e34cbf 100644 --- a/src/FBFrictionlessRequestSettings.m +++ b/src/FBFrictionlessRequestSettings.m @@ -122,10 +122,10 @@ - (void)updateRecipientCacheWithRequestResult:(id)result { // a little request bookkeeping self.activeRequest = nil; - int items = [[result objectForKey: @"data"] count]; + NSUInteger items = [[result objectForKey: @"data"] count]; NSMutableArray* recipients = [[[NSMutableArray alloc] initWithCapacity: items] autorelease]; - for (int i = 0; i < items; i++) { + for (NSUInteger i = 0; i < items; i++) { [recipients addObject: [[[result objectForKey: @"data"] objectAtIndex: i] objectForKey: @"recipient_id"]] ; diff --git a/src/FBGraphObject.m b/src/FBGraphObject.m index 1882681571..ac4670ab6f 100644 --- a/src/FBGraphObject.m +++ b/src/FBGraphObject.m @@ -328,7 +328,7 @@ + (SelectorInferredImplType)inferredImplTypeForSelector:(SEL)sel { // processing, indexed by selector NSString *selectorName = NSStringFromSelector(sel); - int parameterCount = [[selectorName componentsSeparatedByString:@":"] count]-1; + NSUInteger parameterCount = [[selectorName componentsSeparatedByString:@":"] count]-1; // we will process a selector as a getter if paramCount == 0 if (parameterCount == 0) { return SelectorInferredImplTypeGet; @@ -458,8 +458,8 @@ - (id)graphObjectifyAtIndex:(NSUInteger)index { } - (void)graphObjectifyAll { - int count = [_jsonArray count]; - for (int i = 0; i < count; ++i) { + NSUInteger count = [_jsonArray count]; + for (NSUInteger i = 0; i < count; ++i) { [self graphObjectifyAtIndex:i]; } } diff --git a/src/FBGraphObjectTableDataSource.m b/src/FBGraphObjectTableDataSource.m index fac0a952db..d188fcfbab 100644 --- a/src/FBGraphObjectTableDataSource.m +++ b/src/FBGraphObjectTableDataSource.m @@ -464,7 +464,7 @@ - (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger { NSArray *sectionItems = [self sectionItemsForSection:section]; - int count = [sectionItems count]; + NSUInteger count = [sectionItems count]; // If we are expecting more objects to be loaded via paging, add 1 to the // row count for the last section. if (self.expectingMoreGraphObjects && diff --git a/src/FBLogger.h b/src/FBLogger.h index 0f85ace99c..f031f90768 100644 --- a/src/FBLogger.h +++ b/src/FBLogger.h @@ -48,7 +48,7 @@ // Append string, or key/value pair - (void)appendString:(NSString *)string; -- (void)appendFormat:(NSString *)formatString, ...; +- (void)appendFormat:(NSString *)formatString, ... NS_FORMAT_FUNCTION(1,2); - (void)appendKey:(NSString *)key value:(NSString *)value; // Emit log, clearing out the logger contents. @@ -68,11 +68,11 @@ logEntry:(NSString *)logEntry; + (void)singleShotLogEntry:(NSString *)loggingBehavior - formatString:(NSString *)formatString, ...; + formatString:(NSString *)formatString, ... NS_FORMAT_FUNCTION(2,3); + (void)singleShotLogEntry:(NSString *)loggingBehavior timestampTag:(NSObject *)timestampTag - formatString:(NSString *)formatString, ...; + formatString:(NSString *)formatString, ... NS_FORMAT_FUNCTION(3,4); // Register a timestamp label with the "current" time, to then be retrieved by singleShotLogEntry // to include a duration. diff --git a/src/FBLoginView.m b/src/FBLoginView.m index 157b3b3c8a..296c1f18ab 100644 --- a/src/FBLoginView.m +++ b/src/FBLoginView.m @@ -272,7 +272,11 @@ - (void)initialize { // add a label that will appear over the button self.label = [[[FBShadowLabel alloc] init] autorelease]; self.label.autoresizingMask = UIViewAutoresizingFlexibleWidth; +#ifdef __IPHONE_6_0 + self.label.textAlignment = NSTextAlignmentCenter; +#else self.label.textAlignment = UITextAlignmentCenter; +#endif self.label.backgroundColor = [UIColor clearColor]; self.label.font = font; self.label.textColor = [UIColor whiteColor]; @@ -301,6 +305,10 @@ - (void)initialize { } } +- (CGSize)intrinsicContentSize { + return self.bounds.size; +} + - (CGSize)sizeThatFits:(CGSize)size { return CGSizeMake(g_buttonSize.width, g_buttonSize.height); } @@ -517,7 +525,7 @@ @implementation FBShadowLabel - (void) drawTextInRect:(CGRect)rect { CGSize myShadowOffset = CGSizeMake(0, -1); - float myColorValues[] = {0, 0, 0, .3}; + CGFloat myColorValues[] = {0, 0, 0, .3}; CGContextRef myContext = UIGraphicsGetCurrentContext(); CGContextSaveGState(myContext); diff --git a/src/FBProfilePictureView.m b/src/FBProfilePictureView.m index 1fb102dff7..1d2ccb69d3 100644 --- a/src/FBProfilePictureView.m +++ b/src/FBProfilePictureView.m @@ -232,5 +232,8 @@ - (void)layoutSubviews { [super layoutSubviews]; } +- (CGSize)intrinsicContentSize { + return self.bounds.size; +} @end diff --git a/src/FBRequest+Internal.h b/src/FBRequest+Internal.h index 39b217f87c..6ceab67c47 100644 --- a/src/FBRequest+Internal.h +++ b/src/FBRequest+Internal.h @@ -23,6 +23,9 @@ Gets or sets the flag indicating if this request can close the session in case of errors (like invalid sessions). For example, implicit requests like app events logging should not close the session. Defaults to YES. + + @discussion + For simplicity, setting this flag to NO also bypasses any errorBehavior retry logic. */ @property (assign, nonatomic) BOOL canCloseSessionOnError; diff --git a/src/FBRequest.h b/src/FBRequest.h index 2a03dcf650..b88790702d 100644 --- a/src/FBRequest.h +++ b/src/FBRequest.h @@ -278,6 +278,7 @@ typedef NSUInteger FBRequestState __attribute__((deprecated)); request completes with a success, error, or cancel. @param handler The handler block to call when the request completes with a success, error, or cancel action. + The handler will be invoked on the main thread. */ - (FBRequestConnection*)startWithCompletionHandler:(FBRequestHandler)handler; @@ -476,8 +477,8 @@ typedef NSUInteger FBRequestState __attribute__((deprecated)); native Facebook app on the device. If there is no native Facebook app, no one is logged into it, or the user has opted out at the iOS level from ad tracking, then a `nil` ID will be returned. - This method itself returning `nil` indicates that either the user has opted-out (via iOS) from Ad Tracking, or a specific Facebook user cannot - be identified. + This method returns `nil` if either the user has opted-out (via iOS) from Ad Tracking, the app itself has limited event usage + via the `[FBAppEvents setLimitEventUsage]` flag, or a specific Facebook user cannot be identified. */ + (FBRequest *)requestForCustomAudienceThirdPartyID:(FBSession *)session; diff --git a/src/FBRequest.m b/src/FBRequest.m index c6a96191ae..c5af5276dc 100644 --- a/src/FBRequest.m +++ b/src/FBRequest.m @@ -14,6 +14,7 @@ * limitations under the License. */ +#import #import "Facebook.h" #import "FBAppEvents+Internal.h" #import "FBLogger.h" @@ -269,10 +270,10 @@ + (FBRequest*)requestForPlacesSearchAtCoordinate:(CLLocationCoordinate2D)coordin { NSMutableDictionary *parameters = [[NSMutableDictionary alloc] init]; [parameters setObject:@"place" forKey:@"type"]; - [parameters setObject:[NSString stringWithFormat:@"%d", limit] forKey:@"limit"]; + [parameters setObject:[NSString stringWithFormat:@"%ld", (long)limit] forKey:@"limit"]; [parameters setObject:[NSString stringWithFormat:@"%lf,%lf", coordinate.latitude, coordinate.longitude] forKey:@"center"]; - [parameters setObject:[NSString stringWithFormat:@"%d", radius] forKey:@"distance"]; + [parameters setObject:[NSString stringWithFormat:@"%ld", (long)radius] forKey:@"distance"]; if ([searchText length]) { [parameters setObject:searchText forKey:@"q"]; } @@ -495,7 +496,7 @@ + (NSString*)serializeURL:(NSString *)baseUrl params:(NSDictionary *)params httpMethod:(NSString *)httpMethod { - NSURL* parsedURL = [NSURL URLWithString:baseUrl]; + NSURL* parsedURL = [NSURL URLWithString:[baseUrl stringByAddingPercentEscapesUsingEncoding:NSUTF8StringEncoding]]; NSString* queryPrefix = parsedURL.query ? @"&" : @"?"; NSMutableArray* pairs = [NSMutableArray array]; diff --git a/src/FBRequestBody.m b/src/FBRequestBody.m index d028346199..6b15174286 100644 --- a/src/FBRequestBody.m +++ b/src/FBRequestBody.m @@ -88,7 +88,7 @@ - (void)appendWithKey:(NSString *)key NSData *data = UIImageJPEGRepresentation(image, [FBSettings defaultJPEGCompressionQuality]); [self.mutableData appendData:data]; [self appendRecordBoundary]; - [logger appendFormat:@"\n %@:\t", key, [data length] / 1024]; + [logger appendFormat:@"\n %@:\t", key, (unsigned long)([data length] / 1024)]; } - (void)appendWithKey:(NSString *)key @@ -101,7 +101,7 @@ - (void)appendWithKey:(NSString *)key [self appendUTF8:@"Content-Type: content/unknown\r\n\r\n"]; [self.mutableData appendData:data]; [self appendRecordBoundary]; - [logger appendFormat:@"\n %@:\t", key, [data length] / 1024]; + [logger appendFormat:@"\n %@:\t", key, (unsigned long)([data length] / 1024)]; } - (NSData *)data diff --git a/src/FBRequestConnection.h b/src/FBRequestConnection.h index 0fe5a7ec67..229c8b4044 100644 --- a/src/FBRequestConnection.h +++ b/src/FBRequestConnection.h @@ -203,8 +203,7 @@ typedef void (^FBRequestHandler)(FBRequestConnection *connection, @method @abstract - This method adds an object to this connection and then calls - on the connection. + This method adds an object to this connection. @discussion The completion handler is retained until the block is called upon the @@ -212,6 +211,7 @@ typedef void (^FBRequestHandler)(FBRequestConnection *connection, @param request A request to be included in the round-trip when start is called. @param handler A handler to call back when the round-trip completes or times out. + The handler will be invoked on the main thread. */ - (void)addRequest:(FBRequest*)request completionHandler:(FBRequestHandler)handler; @@ -220,8 +220,7 @@ typedef void (^FBRequestHandler)(FBRequestConnection *connection, @method @abstract - This method adds an object to this connection and then calls - on the connection. + This method adds an object to this connection. @discussion The completion handler is retained until the block is called upon the @@ -231,6 +230,7 @@ typedef void (^FBRequestHandler)(FBRequestConnection *connection, @param request A request to be included in the round-trip when start is called. @param handler A handler to call back when the round-trip completes or times out. + The handler will be invoked on the main thread. @param name An optional name for this request. This can be used to feed the results of one request to the input of another in the same @@ -241,6 +241,29 @@ typedef void (^FBRequestHandler)(FBRequestConnection *connection, completionHandler:(FBRequestHandler)handler batchEntryName:(NSString*)name; +/*! + @method + + @abstract + This method adds an object to this connection. + + @discussion + The completion handler is retained until the block is called upon the + completion or cancellation of the connection. This request can be named + to allow for using the request's response in a subsequent request. + + @param request A request to be included in the round-trip when start is called. + + @param handler A handler to call back when the round-trip completes or times out. + + @param batchParameters The optional dictionary of parameters to include for this request + as described in [Graph API Batch Requests]( https://developers.facebook.com/docs/reference/api/batch/ ). + Examples include "depends_on", "name", or "omit_response_on_success". + */ +- (void)addRequest:(FBRequest*)request + completionHandler:(FBRequestHandler)handler + batchParameters:(NSDictionary*)batchParameters; + /*! @methodgroup Instance methods */ @@ -415,8 +438,8 @@ typedef void (^FBRequestHandler)(FBRequestConnection *connection, native Facebook app on the device. If there is no native Facebook app, no one is logged into it, or the user has opted out at the iOS level from ad tracking, then a `nil` ID will be returned. - This method itself returning `nil` indicates that either the user has opted-out (via iOS) from Ad Tracking, or a specific Facebook user cannot - be identified. + This method returns `nil` if either the user has opted-out (via iOS) from Ad Tracking, the app itself has limited event usage + via the `[FBAppEvents setLimitEventUsage]` flag, or a specific Facebook user cannot be identified. @param handler The handler block to call when the request completes with a success, error, or cancel action. */ diff --git a/src/FBRequestConnection.m b/src/FBRequestConnection.m index c52abce1ee..5f9f5477a3 100644 --- a/src/FBRequestConnection.m +++ b/src/FBRequestConnection.m @@ -45,6 +45,7 @@ NSString *const kBatchRelativeURLKey = @"relative_url"; NSString *const kBatchAttachmentKey = @"attached_files"; NSString *const kBatchFileNamePrefix = @"file"; +NSString *const kBatchEntryName = @"name"; NSString *const kAccessTokenKey = @"access_token"; NSString *const kSDK = @"ios"; @@ -94,80 +95,6 @@ @interface FBRequestConnection () { @property (nonatomic, readonly) BOOL isResultFromCache; @property (nonatomic, retain) FBRequestConnectionRetryManager *retryManager; -- (NSMutableURLRequest *)requestWithBatch:(NSArray *)requests - timeout:(NSTimeInterval)timeout; - -- (NSString *)urlStringForSingleRequest:(FBRequest *)request forBatch:(BOOL)forBatch; - -- (void)appendJSONRequests:(NSArray *)requests - toBody:(FBRequestBody *)body - andNameAttachments:(NSMutableDictionary *)attachments - logger:(FBLogger *)logger; - -- (void)addRequest:(FBRequestMetadata *)metadata - toBatch:(NSMutableArray *)batch - attachments:(NSDictionary *)attachments; - -- (BOOL)isAttachment:(id)item; - -- (void)appendAttachments:(NSDictionary *)attachments - toBody:(FBRequestBody *)body - addFormData:(BOOL)addFormData - logger:(FBLogger *)logger; - -+ (void)processGraphObject:(id)object - forPath:(NSString*)path - withAction:(KeyValueActionHandler)action; - -- (void)completeWithResponse:(NSURLResponse *)response - data:(NSData *)data - orError:(NSError *)error; - -- (NSArray *)parseJSONResponse:(NSData *)data - error:(NSError **)error - statusCode:(int)statusCode; - -- (id)parseJSONOrOtherwise:(NSString *)utf8 - error:(NSError **)error; - -- (void)completeDeprecatedWithData:(NSData *)data - results:(NSArray *)results - orError:(NSError *)error; - -- (void)completeWithResults:(NSArray *)results - orError:(NSError *)error; - -- (NSError *)errorFromResult:(id)idResult; - -- (NSError *)errorWithCode:(FBErrorCode)code - statusCode:(int)statusCode - parsedJSONResponse:(id)response - innerError:(NSError*)innerError - message:(NSString*)message; - -- (NSError *)checkConnectionError:(NSError *)innerError - statusCode:(int)statusCode - parsedJSONResponse:(id)response; - -- (BOOL)isInvalidSessionError:(NSError *)error - resultIndex:(int)index; - -- (void)registerTokenToOmitFromLog:(NSString *)token; - -- (void)addPiggybackRequests; - -- (void)logRequest:(NSMutableURLRequest *)request - bodyLength:(int)bodyLength - bodyLogger:(FBLogger *)bodyLogger - attachmentLogger:(FBLogger *)attachmentLogger; - -- (NSString *)getBatchAppID:(NSArray*)requests; - -+ (NSString *)userAgent; - -+ (void)addRequestToExtendTokenForSession:(FBSession*)session connection:(FBRequestConnection*)connection; - -- (NSError*) unpackIndividualJSONResponseError:(NSError *)itemError; @end // ---------------------------------------------------------------------------- @@ -272,12 +199,19 @@ - (void)addRequest:(FBRequest *)request completionHandler:(FBRequestHandler)handler batchEntryName:(NSString *)name { - [self addRequest:request completionHandler:handler batchEntryName:name behavior:self.errorBehavior]; + NSDictionary *batchParams = (name)? @{kBatchEntryName : name } : nil; + [self addRequest:request completionHandler:handler batchParameters:batchParams behavior:self.errorBehavior]; +} + +- (void)addRequest:(FBRequest*)request + completionHandler:(FBRequestHandler)handler + batchParameters:(NSDictionary*)batchParameters { + [self addRequest:request completionHandler:handler batchParameters:batchParameters behavior:self.errorBehavior]; } - (void)addRequest:(FBRequest*)request completionHandler:(FBRequestHandler)handler - batchEntryName:(NSString*)name + batchParameters:(NSDictionary*)batchParameters behavior:(FBRequestConnectionErrorBehavior)behavior { NSAssert(self.state == kStateCreated, @@ -285,7 +219,7 @@ - (void)addRequest:(FBRequest*)request FBRequestMetadata *metadata = [[FBRequestMetadata alloc] initWithRequest:request completionHandler:handler - batchEntryName:name + batchParameters:batchParameters behavior:behavior]; [self.requests addObject:metadata]; @@ -675,19 +609,19 @@ - (NSMutableURLRequest *)requestWithBatch:(NSArray *)requests } - (void)logRequest:(NSMutableURLRequest *)request - bodyLength:(int)bodyLength + bodyLength:(NSUInteger)bodyLength bodyLogger:(FBLogger *)bodyLogger attachmentLogger:(FBLogger *)attachmentLogger { if (_logger.isActive) { - [_logger appendFormat:@"Request <#%d>:\n", _logger.loggerSerialNumber]; + [_logger appendFormat:@"Request <#%lu>:\n", (unsigned long)_logger.loggerSerialNumber]; [_logger appendKey:@"URL" value:[[request URL] absoluteString]]; [_logger appendKey:@"Method" value:[request HTTPMethod]]; [_logger appendKey:@"UserAgent" value:[request valueForHTTPHeaderField:@"User-Agent"]]; [_logger appendKey:@"MIME" value:[request valueForHTTPHeaderField:@"Content-Type"]]; if (bodyLength != 0) { - [_logger appendKey:@"Body Size" value:[NSString stringWithFormat:@"%d kB", bodyLength / 1024]]; + [_logger appendKey:@"Body Size" value:[NSString stringWithFormat:@"%lu kB", (unsigned long)bodyLength / 1024]]; } if (bodyLogger != nil) { @@ -806,8 +740,8 @@ - (void)addRequest:(FBRequestMetadata *)metadata { NSMutableDictionary *requestElement = [[[NSMutableDictionary alloc] init] autorelease]; - if (metadata.batchEntryName) { - [requestElement setObject:metadata.batchEntryName forKey:@"name"]; + if (metadata.batchParameters) { + [requestElement addEntriesFromDictionary:metadata.batchParameters]; } NSString *token = metadata.request.session.accessTokenData.accessToken; @@ -825,9 +759,9 @@ - (void)addRequest:(FBRequestMetadata *)metadata for (id key in [metadata.request.parameters keyEnumerator]) { NSObject *value = [metadata.request.parameters objectForKey:key]; if ([self isAttachment:value]) { - NSString *name = [NSString stringWithFormat:@"%@%d", + NSString *name = [NSString stringWithFormat:@"%@%lu", kBatchFileNamePrefix, - [attachments count]]; + (unsigned long)[attachments count]]; if ([attachmentNames length]) { [attachmentNames appendString:@","]; } @@ -934,9 +868,9 @@ + (void)processGraphObjectPropertyKey:(NSString*)key // Arrays are serialized as multiple elements with keys of the // form key[0], key[1], etc. NSArray *array = (NSArray*)value; - int count = array.count; - for (int i = 0; i < count; ++i) { - NSString *subKey = [NSString stringWithFormat:@"%@[%d]", key, i]; + NSUInteger count = array.count; + for (NSUInteger i = 0; i < count; ++i) { + NSString *subKey = [NSString stringWithFormat:@"%@[%lu]", key, (unsigned long)i]; id subValue = [array objectAtIndex:i]; [self processGraphObjectPropertyKey:subKey value:subValue action:action passByValue:passByValue]; } @@ -979,7 +913,7 @@ - (void)completeWithResponse:(NSURLResponse *)response self.state = kStateCompleted; } - int statusCode; + NSInteger statusCode; if (response) { NSAssert([response isKindOfClass:[NSHTTPURLResponse class]], @"Expected NSHTTPURLResponse, got %@", @@ -1019,8 +953,8 @@ - (void)completeWithResponse:(NSURLResponse *)response if (!error) { if ([self.requests count] != [results count]) { - [FBLogger singleShotLogEntry:FBLoggingBehaviorFBRequests formatString:@"Expected %d results, got %d", - [self.requests count], [results count]]; + [FBLogger singleShotLogEntry:FBLoggingBehaviorFBRequests formatString:@"Expected %lu results, got %lu", + (unsigned long)[self.requests count], (unsigned long)[results count]]; error = [self errorWithCode:FBErrorProtocolMismatch statusCode:statusCode parsedJSONResponse:results @@ -1031,16 +965,16 @@ - (void)completeWithResponse:(NSURLResponse *)response if (!error) { - [_logger appendFormat:@"Response <#%d>\nDuration: %lu msec\nSize: %d kB\nResponse Body:\n%@\n\n", - [_logger loggerSerialNumber], + [_logger appendFormat:@"Response <#%lu>\nDuration: %lu msec\nSize: %lu kB\nResponse Body:\n%@\n\n", + (unsigned long)[_logger loggerSerialNumber], [FBUtility currentTimeInMilliseconds] - _requestStartTime, - [data length], + (unsigned long)[data length], results]; } else { - [_logger appendFormat:@"Response <#%d> :\n%@\n%@\n", - [_logger loggerSerialNumber], + [_logger appendFormat:@"Response <#%lu> :\n%@\n%@\n", + (unsigned long)[_logger loggerSerialNumber], [error localizedDescription], [error userInfo]]; @@ -1072,7 +1006,7 @@ - (void)completeWithResponse:(NSURLResponse *)response // - (NSArray *)parseJSONResponse:(NSData *)data error:(NSError **)error - statusCode:(int)statusCode; + statusCode:(NSInteger)statusCode; { // Graph API can return "true" or "false", which is not valid JSON. // Translate that before asking JSON parser to look at it. @@ -1086,7 +1020,7 @@ - (NSArray *)parseJSONResponse:(NSData *)data // response is the entry, so put it in a dictionary under "body" and add // that to array of responses. NSMutableDictionary *result = [[[NSMutableDictionary alloc] init] autorelease]; - [result setObject:[NSNumber numberWithInt:statusCode] forKey:@"code"]; + [result setObject:[NSNumber numberWithInteger:statusCode] forKey:@"code"]; [result setObject:response forKey:@"body"]; NSMutableArray *mutableResults = [[[NSMutableArray alloc] init] autorelease]; @@ -1219,7 +1153,7 @@ - (NSError*) unpackIndividualJSONResponseError:(NSError *)itemError { if ([parsedResponse count]) { newValue = [parsedResponse objectAtIndex:0]; } - itemError = [self errorWithCode:itemError.code + itemError = [self errorWithCode:(FBErrorCode)itemError.code statusCode:[[itemError.userInfo objectForKey:FBErrorHTTPStatusCodeKey] intValue] parsedJSONResponse:newValue innerError:[itemError.userInfo objectForKey:FBErrorInnerErrorKey] @@ -1244,8 +1178,9 @@ - (void)completeWithResults:(NSArray *)results // set up a new retry manager for this flow. self.retryManager = [[[FBRequestConnectionRetryManager alloc] initWithFBRequestConnection:self] autorelease]; - int count = [self.requests count]; - for (int i = 0; i < count; i++) { + NSUInteger count = [self.requests count]; + NSMutableArray *tasks = [[NSMutableArray alloc] init]; + for (NSUInteger i = 0; i < count; i++) { FBRequestMetadata *metadata = [self.requests objectAtIndex:i]; id result = error ? nil : [results objectAtIndex:i]; NSError *itemError = error ? error : [self errorFromResult:result]; @@ -1259,65 +1194,47 @@ - (void)completeWithResults:(NSArray *)results body = [FBGraphObject graphObjectWrappingDictionary:[resultDictionary objectForKey:@"body"]]; } - int resultIndex = error == itemError ? i : 0; + NSUInteger resultIndex = error == itemError ? i : 0; + FBTask *taskWork = [FBTask taskWithResult:nil]; + FBSystemAccountStoreAdapter *systemAccountStoreAdapter = [FBSystemAccountStoreAdapter sharedInstance]; - // For the renewSystemAuthorization calls below, we want the renew call - // to finish before executing any further logic. For now, the "further - // 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]) { // if we lack permissions, use this as a cue to refresh the // OS's understanding of current permissions - [self.retryManager incrementExpectedPerformRetryCount]; - [[FBSystemAccountStoreAdapter sharedInstance] - renewSystemAuthorization:^(ACAccountCredentialRenewResult result, NSError *error) { - [metadata invokeCompletionHandlerForConnection:self withResults:body error:unpackedError]; - [self.retryManager performRetries]; - }]; - + taskWork = [taskWork dependentTaskWithBlock:^id(FBTask *task) { + return [systemAccountStoreAdapter renewSystemAuthorizationAsTask]; + } queue:dispatch_get_main_queue()]; } else if ([self isInvalidSessionError:itemError resultIndex:resultIndex]) { - // For invalid sessions, we also need to close the session before - // invoking the "further logic". - if (metadata.request.session.accessTokenData.loginType == FBSessionLoginTypeSystemAccount){ + // For system auth, there are a number of edge cases we pre-process before + // closing the session. + if ([self isExpiredTokenError:itemError resultIndex:resultIndex] - && [FBSystemAccountStoreAdapter sharedInstance].canRequestAccessWithoutUI) { + && systemAccountStoreAdapter.canRequestAccessWithoutUI) { // If token is expired and iOS says user has granted permissions // we can simply renew the token and flip the error to a retry. - [self.retryManager incrementExpectedPerformRetryCount]; - [[FBSystemAccountStoreAdapter sharedInstance] - renewSystemAuthorization:^(ACAccountCredentialRenewResult result, NSError *error) { - if (result == ACAccountCredentialRenewResultRenewed) { - FBSession *session = metadata.request.session; - [[FBSystemAccountStoreAdapter sharedInstance] - requestAccessToFacebookAccountStore:session - handler:^(NSString *oauthToken, NSError *accountStoreError) { - if (oauthToken) { - [session refreshAccessToken:oauthToken expirationDate:[NSDate distantFuture]]; - [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. - if ([self shouldCloseRequestSession:metadata.request]) { - [metadata.request.session closeAndClearTokenInformation:unpackedError]; - } - [metadata invokeCompletionHandlerForConnection:self withResults:body error:unpackedError]; - } - [self.retryManager performRetries]; - } - ]; - } else { - if ([self shouldCloseRequestSession:metadata.request]) { - [metadata.request.session closeAndClearTokenInformation:unpackedError]; + + taskWork = [taskWork dependentTaskWithBlock:^id(FBTask *task) { + return [systemAccountStoreAdapter renewSystemAuthorizationAsTask]; + } queue:dispatch_get_main_queue()]; + + taskWork = [taskWork completionTaskWithQueue:dispatch_get_main_queue() block:^id(FBTask *task) { + if (task.result == ACAccountCredentialRenewResultRenewed) { + FBTask *requestAccessTask = [systemAccountStoreAdapter requestAccessToFacebookAccountStoreAsTask:metadata.request.session]; + return [requestAccessTask completionTaskWithQueue:dispatch_get_main_queue() block:^id(FBTask *task) { + if (task.result) { // aka success means task.result == (oauthToken) + [metadata.request.session refreshAccessToken:task.result expirationDate:[NSDate distantFuture]]; + [metadata invokeCompletionHandlerForConnection:self + withResults:body + error:[FBErrorUtility fberrorForRetry:unpackedError]]; + return [FBTask cancelledTask]; } - [metadata invokeCompletionHandlerForConnection:self withResults:body error:unpackedError]; - [self.retryManager performRetries]; - } - }]; + return [FBTask taskWithError:nil]; + }]; + } + return [FBTask taskWithError:nil]; + }]; } else if ([self isPasswordChangeError:itemError resultIndex:resultIndex]) { // For iOS6, when the password is changed on the server, the system account store // will continue to issue the old token until the user has changed the @@ -1325,49 +1242,56 @@ - (void)completeWithResults:(NSArray *)results // with an old token which would immediately be closed, we tell our adapter // that we want to force a blocking renew until success. [FBSystemAccountStoreAdapter sharedInstance].forceBlockingRenew = YES; - if ([self shouldCloseRequestSession:metadata.request]) { - [metadata.request.session closeAndClearTokenInformation: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. - [self.retryManager incrementExpectedPerformRetryCount]; - [[FBSystemAccountStoreAdapter sharedInstance] - renewSystemAuthorization:^(ACAccountCredentialRenewResult result, NSError *error) { - if ([self shouldCloseRequestSession:metadata.request]) { - [metadata.request.session closeAndClearTokenInformation:unpackedError]; - } - [metadata invokeCompletionHandlerForConnection:self withResults:body error:unpackedError]; - [self.retryManager performRetries]; - }]; + taskWork = [taskWork dependentTaskWithBlock:^id(FBTask *task) { + return [systemAccountStoreAdapter renewSystemAuthorizationAsTask]; + } queue:dispatch_get_main_queue()]; + } + } + // Invalid session case, should close the session at end of this if block + // unless we signified not to earlier via a task cancellation. + taskWork = [taskWork dependentTaskWithBlock:^id(FBTask *task) { + if (task.isCancelled) { + return task; } - } else { if ([self shouldCloseRequestSession:metadata.request]) { [metadata.request.session closeAndClearTokenInformation:unpackedError]; } - [metadata invokeCompletionHandlerForConnection:self withResults:body error:unpackedError]; - } + return [FBTask taskWithResult:nil]; + } queue:dispatch_get_main_queue()]; } else if ([metadata.request.session shouldExtendAccessToken]) { // If we have not had the opportunity to piggyback a token-extension request, // but we need to, do so now as a separate request. - FBRequestConnection *connection = [[FBRequestConnection alloc] init]; - [FBRequestConnection addRequestToExtendTokenForSession:metadata.request.session - connection:connection]; - [connection start]; - [connection release]; - [metadata invokeCompletionHandlerForConnection:self withResults:body error:unpackedError]; - } else { - [metadata invokeCompletionHandlerForConnection:self withResults:body error:unpackedError]; + taskWork = [taskWork dependentTaskWithBlock:^id(FBTask *task) { + FBRequestConnection *connection = [[FBRequestConnection alloc] init]; + [FBRequestConnection addRequestToExtendTokenForSession:metadata.request.session + connection:connection]; + [connection start]; + [connection release]; + return [FBTask taskWithResult:nil]; + } queue:dispatch_get_main_queue()]; } - } - - // NOTE: if you are attempting to add more logic that should be executed after processing - // the results, you will also need to the same logic to any area above that calls - // invokeCompletionHandlerForConnection inside a block. Until we refactor to a better async framework, - // this is necessary to chain work after the ios 6 calls. + + // Always invoke handler at the end. + taskWork = [taskWork dependentTaskWithBlock:^id(FBTask *task) { + if (task.isCancelled) { + return task; + } + [metadata invokeCompletionHandlerForConnection:self withResults:body error:unpackedError]; + return [FBTask taskWithResult:nil]; + } queue:dispatch_get_main_queue()]; + [tasks addObject:taskWork]; + } //end for loop - [self.retryManager performRetries]; + + FBTask *finalTask = [FBTask taskDependentOnTasks:tasks]; + [finalTask dependentTaskWithBlock:^id(FBTask *task) { + [self.retryManager performRetries]; + return [FBTask taskWithResult:nil]; + } queue:dispatch_get_main_queue()]; + [tasks release]; } - (NSError *)errorFromResult:(id)idResult @@ -1401,12 +1325,12 @@ - (NSError *)errorFromResult:(id)idResult } - (NSError *)errorWithCode:(FBErrorCode)code - statusCode:(int)statusCode + statusCode:(NSInteger)statusCode parsedJSONResponse:(id)response innerError:(NSError*)innerError message:(NSString*)message { NSMutableDictionary *userInfo = [[[NSMutableDictionary alloc] init] autorelease]; - [userInfo setObject:[NSNumber numberWithInt:statusCode] forKey:FBErrorHTTPStatusCodeKey]; + [userInfo setObject:[NSNumber numberWithInteger:statusCode] forKey:FBErrorHTTPStatusCodeKey]; if (response) { userInfo[FBErrorParsedJSONResponseKey] = response; @@ -1448,7 +1372,7 @@ - (NSError *)errorWithCode:(FBErrorCode)code } - (NSError *)checkConnectionError:(NSError *)innerError - statusCode:(int)statusCode + statusCode:(NSInteger)statusCode parsedJSONResponse:response { // We don't want to re-wrap our own errors. @@ -1458,7 +1382,7 @@ - (NSError *)checkConnectionError:(NSError *)innerError } NSError *result = nil; if (innerError || ((statusCode < 200) || (statusCode >= 300))) { - [FBLogger singleShotLogEntry:FBLoggingBehaviorFBRequests formatString:@"Error: HTTP status code: %d", statusCode]; + [FBLogger singleShotLogEntry:FBLoggingBehaviorFBRequests formatString:@"Error: HTTP status code: %lu", (unsigned long)statusCode]; result = [self errorWithCode:FBErrorHTTPError statusCode:statusCode parsedJSONResponse:response @@ -1469,7 +1393,7 @@ - (NSError *)checkConnectionError:(NSError *)innerError } - (BOOL)isInsufficientPermissionError:(NSError *)error - resultIndex:(int)index { + resultIndex:(NSUInteger)index { int code; [FBErrorUtility fberrorGetCodeValueForError:error index:index @@ -1479,7 +1403,7 @@ - (BOOL)isInsufficientPermissionError:(NSError *)error } - (BOOL)isInvalidSessionError:(NSError *)error - resultIndex:(int)index { + resultIndex:(NSUInteger)index { // Please note the retry behaviors in FBRequestHandlerFactory are coupled // to the FBRequestConnection invalid session behavior, so any changes // to conditions that trigger `closeAndClearTokenInformation` will probably @@ -1498,7 +1422,7 @@ - (BOOL)isInvalidSessionError:(NSError *)error } - (BOOL)isPasswordChangeError:(NSError *)error - resultIndex:(int)index { + resultIndex:(NSUInteger)index { int code = 0, subcode = 0; [FBErrorUtility fberrorGetCodeValueForError:error index:index @@ -1514,7 +1438,7 @@ - (BOOL)isPasswordChangeError:(NSError *)error } - (BOOL)isExpiredTokenError:(NSError *)error - resultIndex:(int)index { + resultIndex:(NSUInteger)index { int code = 0, subcode = 0; [FBErrorUtility fberrorGetCodeValueForError:error index:index @@ -1565,6 +1489,9 @@ - (void)addPiggybackRequests if ([session shouldExtendAccessToken]) { [FBRequestConnection addRequestToExtendTokenForSession:session connection:self]; } + if (self.requests.count < kMaximumBatchSize && [session shouldRefreshPermissions]) { + [FBRequestConnection addRequestToRefreshPermissionsSession:session connection:self]; + } } [sessions release]; @@ -1605,6 +1532,26 @@ + (void)addRequestToExtendTokenForSession:(FBSession*)session connection:(FBRequ [request release]; } ++ (void)addRequestToRefreshPermissionsSession:(FBSession*)session connection:(FBRequestConnection*)connection { + FBRequest *request = [[FBRequest alloc] initWithSession:session graphPath:@"me/permissions"]; + request.canCloseSessionOnError = NO; + + [connection addRequest:request + completionHandler:^(FBRequestConnection *connection, id result, NSError *error) { + if (!error && [result isKindOfClass:[NSDictionary class] ]) { + NSArray *resultData = result[@"data"]; + if (resultData.count > 0) { + NSDictionary *permissionsDictionary = resultData[0]; + id permissions = [permissionsDictionary allKeys]; + if (permissions && [permissions isKindOfClass:[NSArray class]]) { + [session refreshPermissions:permissions]; + } + } + } + }]; + [request release]; +} + // Helper method to map a request to its metadata instance. - (FBRequestMetadata *) getRequestMetadata:(FBRequest *)request { for (FBRequestMetadata *metadata in self.requests) { @@ -1618,10 +1565,10 @@ - (FBRequestMetadata *) getRequestMetadata:(FBRequest *)request { #pragma mark Debugging helpers - (NSString*)description { - NSMutableString *result = [NSMutableString stringWithFormat:@"<%@: %p, %d request(s): (\n", + NSMutableString *result = [NSMutableString stringWithFormat:@"<%@: %p, %lu request(s): (\n", NSStringFromClass([self class]), self, - self.requests.count]; + (unsigned long)self.requests.count]; BOOL comma = NO; for (FBRequestMetadata *metadata in self.requests) { FBRequest *request = metadata.request; diff --git a/src/FBRequestConnectionRetryManager.h b/src/FBRequestConnectionRetryManager.h index 2037161fe0..615721757d 100644 --- a/src/FBRequestConnectionRetryManager.h +++ b/src/FBRequestConnectionRetryManager.h @@ -71,10 +71,4 @@ typedef enum { // Add a request to the retry batch. -(void) addRequestMetadata:(FBRequestMetadata *)metadata; -// A hack to deal with the async callbacks in FBRequestConnection, -// specifically this acts as a semaphore-like counter to make sure -// performRetries is invoked logically once and at the right time. See -// that method's implementation for more details. --(void) incrementExpectedPerformRetryCount; - @end diff --git a/src/FBRequestConnectionRetryManager.m b/src/FBRequestConnectionRetryManager.m index f56c69908f..17d10da57d 100644 --- a/src/FBRequestConnectionRetryManager.m +++ b/src/FBRequestConnectionRetryManager.m @@ -67,7 +67,6 @@ @interface FBRequestConnectionRetryManager() @property (nonatomic, retain) NSMutableArray *requestMetadatas; @property (nonatomic, retain) FBRequestConnectionRetryManagerAlertViewHelper *alertViewHelper; -@property (atomic, assign) int expectedPerformRetryCount; @end @@ -88,23 +87,6 @@ -(void) addRequestMetadata:(FBRequestMetadata *)metadata { } -(void) performRetries { - if (self.expectedPerformRetryCount > 0) { - // As noted in `expectedPerformRetryCount` declaration, this condition - // is to help deal with the async callbacks in FBRequestConnection. Specifically, - // the async ios 6 calls need to be processed before any attempt at performRetries. - // So before issuing the async calls, we increment the counter so that at the end - // of the callback, we can call performRetries. The performRetries will no-op - // if the counter is still positive; otherwise it decrements the counter. This - // allows the "last" performRetries invocation to actually do its work (since - // there a performRetries call at the end of FBRequestConnection completeWithResults - // that is _not_ paired with a counter increment). - // Note this is still not 100% thread-safe but since all the counter increments - // happen beforehand and on the same thread (the completeWithResults loop), it - // should be fine albeit fragile until we refactor the async callbacks. - self.expectedPerformRetryCount--; - return; - } - if (self.alertMessage.length > 0) { [_requestConnection retain]; NSString *buttonText = [FBUtility localizedStringForKey:@"FBE:AlertMessageButton" withDefault:@"OK"]; @@ -163,7 +145,7 @@ -(void) repairSuccess { metadata.request.canCloseSessionOnError = YES; [connectionToRetry addRequest:metadata.request completionHandler:metadata.originalCompletionHandler - batchEntryName:metadata.batchEntryName]; + batchParameters:metadata.batchParameters]; } [connectionToRetry start]; } @@ -182,15 +164,11 @@ -(void) repairFailed { } } --(void) incrementExpectedPerformRetryCount { - self.expectedPerformRetryCount++; -} - -(void) dealloc { - self.sessionToReconnect = nil; - self.alertMessage = nil; - self.requestMetadatas = nil; - self.alertViewHelper = nil; + [_sessionToReconnect release]; + [_alertMessage release]; + [_requestMetadatas release]; + [_alertViewHelper release]; [super dealloc]; } diff --git a/src/FBRequestMetadata.h b/src/FBRequestMetadata.h index 7cf81692aa..309a8be4f8 100644 --- a/src/FBRequestMetadata.h +++ b/src/FBRequestMetadata.h @@ -26,7 +26,7 @@ extern const int FBREQUEST_DEFAULT_MAX_RETRY_LIMIT; @property (nonatomic, retain) FBRequest *request; @property (nonatomic, copy) FBRequestHandler completionHandler; -@property (nonatomic, copy) NSString *batchEntryName; +@property (nonatomic, copy) NSDictionary *batchParameters; @property (nonatomic, assign) FBRequestConnectionErrorBehavior behavior; @property (nonatomic, copy) FBRequestHandler originalCompletionHandler; @@ -36,7 +36,7 @@ extern const int FBREQUEST_DEFAULT_MAX_RETRY_LIMIT; - (id) initWithRequest:(FBRequest *)request completionHandler:(FBRequestHandler)handler - batchEntryName:(NSString *)name + batchParameters:(NSDictionary *)batchParameters behavior:(FBRequestConnectionErrorBehavior) behavior; - (void)invokeCompletionHandlerForConnection:(FBRequestConnection *)connection diff --git a/src/FBRequestMetadata.m b/src/FBRequestMetadata.m index 8d31710186..c8772a8fde 100644 --- a/src/FBRequestMetadata.m +++ b/src/FBRequestMetadata.m @@ -14,7 +14,7 @@ * limitations under the License. */ -#import "FBRequest.h" +#import "FBRequest+Internal.h" #import "FBRequestMetadata.h" #import "FBRequestHandlerFactory.h" @@ -24,25 +24,30 @@ @implementation FBRequestMetadata - (id) initWithRequest:(FBRequest *)request completionHandler:(FBRequestHandler)handler - batchEntryName:(NSString *)name + batchParameters:(NSDictionary *)batchParameters behavior:(FBRequestConnectionErrorBehavior) behavior { - if (self = [super init]) { - self.request = request; - self.originalCompletionHandler = handler; - self.batchEntryName = name; - self.behavior = behavior; + if ((self = [super init])) { + _request = [request retain]; + _originalCompletionHandler = [handler copy]; + _batchParameters = [batchParameters copy]; + _behavior = behavior; - // Note the order of composing these retry handlers is significant and - // is like a stack (last wrapping handler is invoked first). - if (behavior & FBRequestConnectionErrorBehaviorReconnectSession) { - handler = [FBRequestHandlerFactory handlerThatReconnects:handler forRequest:request]; - } - if (behavior & FBRequestConnectionErrorBehaviorAlertUser) { - handler = [FBRequestHandlerFactory handlerThatAlertsUser:handler forRequest:request]; - } - if (behavior & FBRequestConnectionErrorBehaviorRetry) { - handler = [FBRequestHandlerFactory handlerThatRetries:handler forRequest:request]; + // Only consider retry handlers if the request has enabled canCloseSessionOnError. + // We are essentially reusing that flag to identify implicit requests, and we + // don't want implicit requests to trigger retries. + if (request.canCloseSessionOnError) { + // Note the order of composing these retry handlers is significant and + // is like a stack (last wrapping handler is invoked first). + if (behavior & FBRequestConnectionErrorBehaviorReconnectSession) { + handler = [FBRequestHandlerFactory handlerThatReconnects:handler forRequest:request]; + } + if (behavior & FBRequestConnectionErrorBehaviorAlertUser) { + handler = [FBRequestHandlerFactory handlerThatAlertsUser:handler forRequest:request]; + } + if (behavior & FBRequestConnectionErrorBehaviorRetry) { + handler = [FBRequestHandlerFactory handlerThatRetries:handler forRequest:request]; + } } @@ -54,7 +59,7 @@ - (id) initWithRequest:(FBRequest *)request - (void) dealloc { [_request release]; [_completionHandler release]; - [_batchEntryName release]; + [_batchParameters release]; [_originalCompletionHandler release]; [_originalResult release]; [_originalError release]; @@ -71,10 +76,10 @@ - (void)invokeCompletionHandlerForConnection:(FBRequestConnection *)connection } - (NSString*)description { - return [NSString stringWithFormat:@"<%@: %p, batchEntryName: %@, completionHandler: %p, request: %@>", + return [NSString stringWithFormat:@"<%@: %p, batchParameters: %@, completionHandler: %p, request: %@>", NSStringFromClass([self class]), self, - self.batchEntryName, + self.batchParameters, self.completionHandler, self.request.description]; } diff --git a/src/FBSDKVersion.h b/src/FBSDKVersion.h index 9a8db935c8..b6f90fdafa 100644 --- a/src/FBSDKVersion.h +++ b/src/FBSDKVersion.h @@ -1,3 +1,3 @@ // Note the versioning string has moved to the public FacebookSDK.h -#define FB_IOS_SDK_MIGRATION_BUNDLE @"fbsdk:20130808" +#define FB_IOS_SDK_MIGRATION_BUNDLE @"fbsdk:20130912" diff --git a/src/FBSession+FBSessionLoginStrategy.h b/src/FBSession+FBSessionLoginStrategy.h new file mode 100644 index 0000000000..f3ad8bbfbc --- /dev/null +++ b/src/FBSession+FBSessionLoginStrategy.h @@ -0,0 +1,45 @@ +/* + * Copyright 2010-present 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 "FacebookSDK.h" + +// A category on FBSession to declare members that FBSessionLoginStrategy +// implementations needs access to (aka "friend" access). +@interface FBSession (FBSessionLoginStrategy) + +- (void)authorizeUsingSystemAccountStore:(NSArray*)permissions + defaultAudience:(FBSessionDefaultAudience)defaultAudience + isReauthorize:(BOOL)isReauthorize; +- (FBAppCall *)authorizeUsingFacebookNativeLoginWithPermissions:(NSArray*)permissions + defaultAudience:(FBSessionDefaultAudience)defaultAudience + clientState:(NSDictionary *)clientState; +- (BOOL)isURLSchemeRegistered; +- (NSString *)jsonClientStateWithDictionary:(NSDictionary *)dictionary; +- (void)retryableAuthorizeWithPermissions:(NSArray*)permissions + defaultAudience:(FBSessionDefaultAudience)defaultAudience + integratedAuth:(BOOL)tryIntegratedAuth + FBAppAuth:(BOOL)tryFBAppAuth + safariAuth:(BOOL)trySafariAuth + fallback:(BOOL)tryFallback + isReauthorize:(BOOL)isReauthorize + canFetchAppSettings:(BOOL)canFetchAppSettings; +- (BOOL)authorizeUsingFacebookApplication:(NSMutableDictionary *)params; +- (BOOL)authorizeUsingSafari:(NSMutableDictionary *)params; +- (void)setLoginTypeOfPendingOpenUrlCallback:(FBSessionLoginType) loginType; +- (void)authorizeUsingLoginDialog:(NSMutableDictionary *)params; + +@end + diff --git a/src/FBSession+Internal.h b/src/FBSession+Internal.h index 75cd1a4048..2711e97f14 100644 --- a/src/FBSession+Internal.h +++ b/src/FBSession+Internal.h @@ -18,8 +18,6 @@ #import "FBSystemAccountStoreAdapter.h" #import "FBSessionAppEventsState.h" -@class FBSystemAccountStoreAdapter; - extern NSString *const FBLoginUXClientState; extern NSString *const FBLoginUXClientStateIsClientState; extern NSString *const FBLoginUXClientStateIsOpenSession; @@ -38,6 +36,8 @@ extern NSString *const FacebookNativeApplicationLoginDomain; - (void)refreshAccessToken:(NSString*)token expirationDate:(NSDate*)expireDate; - (BOOL)shouldExtendAccessToken; +- (BOOL)shouldRefreshPermissions; +- (void)refreshPermissions:(NSArray *)permissions; - (void)closeAndClearTokenInformation:(NSError*) error; - (void)clearAffinitizedThread; @@ -45,8 +45,6 @@ extern NSString *const FacebookNativeApplicationLoginDomain; + (FBSession*)activeSessionIfOpen; -+ (void)deleteFacebookCookies; - - (NSError*)errorLoginFailedWithReason:(NSString*)errorReason errorCode:(NSString*)errorCode innerError:(NSError*)innerError; @@ -55,8 +53,6 @@ extern NSString *const FacebookNativeApplicationLoginDomain; completionHandler:(FBSessionStateHandler) handler raiseExceptionIfInvalidState:(BOOL)raiseException; -+ (BOOL)isOpenSessionResponseURL:(NSURL *)url; - + (NSError *)sdkSurfacedErrorForNativeLoginError:(NSError *)nativeLoginError; - (void)repairWithHandler:(FBSessionRequestPermissionResultHandler) handler; diff --git a/src/FBSession+Protected.h b/src/FBSession+Protected.h index 9d2dacf9fa..0572d27baa 100644 --- a/src/FBSession+Protected.h +++ b/src/FBSession+Protected.h @@ -27,16 +27,12 @@ @property(readonly, copy) NSArray *initializedPermissions; - (BOOL)transitionToState:(FBSessionState)state - andUpdateToken:(NSString*)token - andExpirationDate:(NSDate*)date - shouldCache:(BOOL)shouldCache - loginType:(FBSessionLoginType)loginType; + withAccessTokenData:(FBAccessTokenData *)tokenData + shouldCache:(BOOL)shouldCache; - (void)transitionAndCallHandlerWithState:(FBSessionState)status error:(NSError*)error - token:(NSString*)token - expirationDate:(NSDate*)date - shouldCache:(BOOL)shouldCache - loginType:(FBSessionLoginType)loginType; + tokenData:(FBAccessTokenData *)tokenData + shouldCache:(BOOL)shouldCache; - (void)authorizeWithPermissions:(NSArray*)permissions behavior:(FBSessionLoginBehavior)behavior defaultAudience:(FBSessionDefaultAudience)audience diff --git a/src/FBSession.h b/src/FBSession.h index d703258e61..531fb07a3f 100644 --- a/src/FBSession.h +++ b/src/FBSession.h @@ -167,6 +167,12 @@ typedef enum { @abstract Block type used to define blocks called by for state updates @discussion See https://developers.facebook.com/docs/technical-guides/iossdk/errors/ for error handling best practices. + + Requesting additional permissions inside this handler (such as by calling + `requestNewPublishPermissions`) should be avoided because it is a poor user + experience and its behavior may vary depending on the login type. You should + request the permissions closer to the operation that requires it (e.g., when + the user performs some action). */ typedef void (^FBSessionStateHandler)(FBSession *session, FBSessionState status, @@ -180,6 +186,12 @@ typedef void (^FBSessionStateHandler)(FBSession *session, @discussion See https://developers.facebook.com/docs/technical-guides/iossdk/errors/ for error handling best practices. + + Requesting additional permissions inside this handler (such as by calling + `requestNewPublishPermissions`) should be avoided because it is a poor user + experience and its behavior may vary depending on the login type. You should + request the permissions closer to the operation that requires it (e.g., when + the user performs some action). */ typedef void (^FBSessionRequestPermissionResultHandler)(FBSession *session, NSError *error); @@ -554,7 +566,7 @@ __attribute__((deprecated)); when there is a cached token available from a previous run of the application. If NO is returned, this indicates that the session was not immediately opened, via cache. However, if YES was passed as allowLoginUI, then it is possible that the user will login, and the session will become open asynchronously. The primary use for - this return value is to switch-on facebook capabilities in your UX upon startup, in the case were the session + this return value is to switch-on facebook capabilities in your UX upon startup, in the case where the session is opened via cache. */ + (BOOL)openActiveSessionWithAllowLoginUI:(BOOL)allowLoginUI; @@ -584,13 +596,13 @@ __attribute__((deprecated)); when there is a cached token available from a previous run of the application. If NO is returned, this indicates that the session was not immediately opened, via cache. However, if YES was passed as allowLoginUI, then it is possible that the user will login, and the session will become open asynchronously. The primary use for - this return value is to switch-on facebook capabilities in your UX upon startup, in the case were the session + this return value is to switch-on facebook capabilities in your UX upon startup, in the case where the session is opened via cache. It is required that initial permissions requests represent read-only permissions only. If publish permissions are needed, you may use reauthorizeWithPermissions to specify additional permissions as well as an audience. Use of this method will result in a legacy fast-app-switch Facebook Login due to - the requirement to seperate read and publish permissions for newer applications. Methods and properties + the requirement to separate read and publish permissions for newer applications. Methods and properties that specify permissions without a read or publish qualification are deprecated; use of a read-qualified or publish-qualified alternative is preferred. */ @@ -624,7 +636,7 @@ __attribute__((deprecated)); when there is a cached token available from a previous run of the application. If NO is returned, this indicates that the session was not immediately opened, via cache. However, if YES was passed as allowLoginUI, then it is possible that the user will login, and the session will become open asynchronously. The primary use for - this return value is to switch-on facebook capabilities in your UX upon startup, in the case were the session + this return value is to switch-on facebook capabilities in your UX upon startup, in the case where the session is opened via cache. */ @@ -659,7 +671,7 @@ __attribute__((deprecated)); when there is a cached token available from a previous run of the application. If NO is returned, this indicates that the session was not immediately opened, via cache. However, if YES was passed as allowLoginUI, then it is possible that the user will login, and the session will become open asynchronously. The primary use for - this return value is to switch-on facebook capabilities in your UX upon startup, in the case were the session + this return value is to switch-on facebook capabilities in your UX upon startup, in the case where the session is opened via cache. */ diff --git a/src/FBSession.m b/src/FBSession.m index 09ea249431..6f19a35727 100644 --- a/src/FBSession.m +++ b/src/FBSession.m @@ -20,7 +20,11 @@ #import "FBSession.h" #import "FBSession+Internal.h" #import "FBSession+Protected.h" +#import "FBSessionAppSwitchingLoginStategy.h" +#import "FBSessionInlineWebViewLoginStategy.h" +#import "FBSessionSystemLoginStategy.h" #import "FBSessionTokenCachingStrategy.h" +#import "FBSessionUtility.h" #import "FBSettings.h" #import "FBSettings+Internal.h" #import "FBError.h" @@ -41,11 +45,6 @@ #import "Facebook.h" #import "FBLoginDialog.h" -// these are helpful macros for testing various login methods, should always checkin as NO/NO -#define TEST_DISABLE_MULTITASKING_LOGIN NO -#define TEST_DISABLE_FACEBOOKLOGIN NO -#define TEST_DISABLE_FACEBOOKNATIVELOGIN NO - // for unit testing mode only (DO NOT store application secrets in a published application plist) static NSString *const FBAuthURLScheme = @"fbauth"; static NSString *const FBAuthURLPath = @"authorize"; @@ -121,9 +120,9 @@ @interface FBSession () { // private properties @property(readwrite, retain) FBSessionTokenCachingStrategy *tokenCachingStrategy; @property(readwrite, copy) NSDate *attemptedRefreshDate; +@property(readwrite, copy) NSDate *attemptedPermissionsRefreshDate; @property(readwrite, copy) FBSessionStateHandler loginHandler; @property(readwrite, copy) FBSessionRequestPermissionResultHandler reauthorizeHandler; -@property(readwrite, copy) NSArray *reauthorizePermissions; @property(readonly) NSString *appBaseUrl; @property(readwrite, retain) FBLoginDialog *loginDialog; @property(readwrite, retain) NSThread *affinitizedThread; @@ -134,27 +133,6 @@ @interface FBSession () { @implementation FBSession : NSObject -@synthesize - // public properties - appID = _appID, - - // following properties use manual KVO -- changes to names require - // changes to static property name variables (e.g. FBisOpenPropertyName) - state = _state, - accessTokenData = _accessTokenData, - - // private properties - initializedPermissions = _initializedPermissions, - tokenCachingStrategy = _tokenCachingStrategy, - attemptedRefreshDate = _attemptedRefreshDate, - loginDialog = _loginDialog, - affinitizedThread = _affinitizedThread, - loginHandler = _loginHandler, - reauthorizeHandler = _reauthorizeHandler, - reauthorizePermissions = _reauthorizePermissions, - lastRequestedSystemAudience = _lastRequestedSystemAudience, - appEventsState = _appEventsState; - #pragma mark Lifecycle - (id)init { @@ -215,10 +193,10 @@ - (id)initWithAppID:(NSString*)appID } // assign arguments; - self.appID = appID; - self.initializedPermissions = permissions; - self.urlSchemeSuffix = urlSchemeSuffix; - self.tokenCachingStrategy = tokenCachingStrategy; + _appID = [appID copy]; + _initializedPermissions = [permissions copy]; + _urlSchemeSuffix = [urlSchemeSuffix copy]; + _tokenCachingStrategy = [tokenCachingStrategy retain]; // additional setup _isInStateTransition = NO; @@ -226,9 +204,11 @@ - (id)initWithAppID:(NSString*)appID _defaultDefaultAudience = defaultAudience; _appEventsState = [[FBSessionAppEventsState alloc] init]; - self.attemptedRefreshDate = [NSDate distantPast]; - self.state = FBSessionStateCreated; - self.affinitizedThread = [NSThread currentThread]; + _attemptedRefreshDate = [[NSDate distantPast] copy]; + _attemptedPermissionsRefreshDate = [[NSDate distantPast] copy]; + _state = FBSessionStateCreated; + _affinitizedThread = [[NSThread currentThread] retain]; + [FBLogger registerCurrentTime:FBLoggingBehaviorPerformanceCharacteristics withTag:self]; FBAccessTokenData *cachedTokenData = [self.tokenCachingStrategy fetchFBAccessTokenData]; @@ -247,21 +227,13 @@ - (id)initWithAppID:(NSString*)appID - (BOOL)initializeFromCachedToken:(FBAccessTokenData *) cachedToken withPermissions:(NSArray *)permissions { if (cachedToken && self.state == FBSessionStateCreated) { - BOOL isSubset = [FBSession areRequiredPermissions:permissions - aSubsetOfPermissions:cachedToken.permissions]; + BOOL isSubset = [FBSessionUtility areRequiredPermissions:permissions + aSubsetOfPermissions:cachedToken.permissions]; if (isSubset && (NSOrderedDescending == [cachedToken.expirationDate compare:[NSDate date]])) { - self.initializedPermissions = cachedToken.permissions; - [self transitionToState:FBSessionStateCreatedTokenLoaded - andUpdateToken:cachedToken.accessToken - andExpirationDate:cachedToken.expirationDate - shouldCache:NO - loginType:cachedToken.loginType]; - // Task #2015922 - refactor transitionToState methods to use FBAccessTokenData - // so that we do not need to manually set these additional fields that - // are lost inside transitionToState. - self.accessTokenData.refreshDate = cachedToken.refreshDate; + withAccessTokenData:cachedToken + shouldCache:NO]; return YES; } } @@ -271,10 +243,10 @@ - (BOOL)initializeFromCachedToken:(FBAccessTokenData *) cachedToken withPermissi - (void)dealloc { [_loginDialog release]; [_attemptedRefreshDate release]; + [_attemptedPermissionsRefreshDate release]; [_accessTokenData release]; [_reauthorizeHandler release]; [_loginHandler release]; - [_reauthorizePermissions release]; [_appID release]; [_urlSchemeSuffix release]; [_initializedPermissions release]; @@ -360,10 +332,8 @@ - (void)openWithBehavior:(FBSessionLoginBehavior)behavior // set the state and token info [self transitionToState:FBSessionStateCreatedOpening - andUpdateToken:nil - andExpirationDate:nil - shouldCache:NO - loginType:FBSessionLoginTypeNone]; + withAccessTokenData:nil + shouldCache:NO]; [self authorizeWithPermissions:self.initializedPermissions behavior:behavior @@ -376,10 +346,8 @@ - (void)openWithBehavior:(FBSessionLoginBehavior)behavior // "1-session-1-identity" rule, by transitioning to logged in, without a transition to login UX [self transitionAndCallHandlerWithState:FBSessionStateOpen error:nil - token:nil - expirationDate:nil - shouldCache:NO - loginType:FBSessionLoginTypeNone]; + tokenData:nil + shouldCache:NO]; } } @@ -438,10 +406,8 @@ - (void)close { [self transitionAndCallHandlerWithState:state error:nil - token:nil - expirationDate:nil - shouldCache:NO - loginType:FBSessionLoginTypeNone]; + tokenData:nil + shouldCache:NO]; } - (void)closeAndClearTokenInformation { @@ -454,7 +420,7 @@ - (void)closeAndClearTokenInformation { - (BOOL)handleAuthorizationCallbacks:(NSString *)accessToken params:(NSDictionary *)params loginType:(FBSessionLoginType)loginType { // Make sure our logger is setup to finish up the authorization roundtrip if (!self.authLogger) { - NSDictionary *clientState = [FBSession clientStateFromQueryParams:params]; + NSDictionary *clientState = [FBSessionUtility clientStateFromQueryParams:params]; NSString *ID = clientState[FBSessionAuthLoggerParamIDKey]; NSString *authMethod = clientState[FBSessionAuthLoggerParamAuthMethodKey]; if (ID || authMethod) { @@ -487,7 +453,7 @@ - (BOOL)handleAuthorizationCallbacks:(NSString *)accessToken params:(NSDictionar - (BOOL)handleOpenURL:(NSURL *)url { [self checkThreadAffinity]; - NSDictionary *params = [FBSession queryParamsFromLoginURL:url + NSDictionary *params = [FBSessionUtility queryParamsFromLoginURL:url appID:self.appID urlSchemeSuffix:self.urlSchemeSuffix]; @@ -510,8 +476,6 @@ - (BOOL)handleOpenURL:(NSURL *)url { NSString *accessToken = [params objectForKey:@"access_token"]; - // #2015922 should refactor these methods to take the FBAccessTokenData instance at which time - // we should also use +FBAccessTokenData createTokenFromFacebookURL. return [self handleAuthorizationCallbacks:accessToken params:params loginType:loginType]; } @@ -678,12 +642,13 @@ + (void)renewSystemCredentials:(FBSessionRenewSystemCredentialsHandler) handler // private methods are broken into two categories: core session and helpers // core member that owns all state transitions as well as property setting for status and isOpen +// `tokenData` will NOT be retained, it will be used to construct a +// new instance - the difference is for things that should not change +// if the session already had a token (e.g., loginType). - (BOOL)transitionToState:(FBSessionState)state - andUpdateToken:(NSString*)token - andExpirationDate:(NSDate*)date - shouldCache:(BOOL)shouldCache - loginType:(FBSessionLoginType)loginType { - + withAccessTokenData:(FBAccessTokenData *)tokenData + shouldCache:(BOOL)shouldCache { + // is this a valid transition? BOOL isValidTransition; FBSessionState statePrior; @@ -726,8 +691,8 @@ - (BOOL)transitionToState:(FBSessionState)state if (!isValidTransition) { [FBLogger singleShotLogEntry:FBLoggingBehaviorSessionStateTransitions logEntry:[NSString stringWithFormat:@"FBSession **INVALID** transition from %@ to %@", - [FBSession sessionStateDescription:statePrior], - [FBSession sessionStateDescription:state]]]; + [FBSessionUtility sessionStateDescription:statePrior], + [FBSessionUtility sessionStateDescription:state]]]; return NO; } @@ -744,8 +709,8 @@ - (BOOL)transitionToState:(FBSessionState)state // valid transitions notify NSString *logString = [NSString stringWithFormat:@"FBSession transition from %@ to %@ ", - [FBSession sessionStateDescription:statePrior], - [FBSession sessionStateDescription:state]]; + [FBSessionUtility sessionStateDescription:statePrior], + [FBSessionUtility sessionStateDescription:state]]; [FBLogger singleShotLogEntry:FBLoggingBehaviorSessionStateTransitions logEntry:logString]; [FBLogger singleShotLogEntry:FBLoggingBehaviorPerformanceCharacteristics @@ -758,13 +723,12 @@ - (BOOL)transitionToState:(FBSessionState)state // identify whether we will update token and date, and what the values will be BOOL changingTokenAndDate = NO; - if (token && date) { + if (tokenData.accessToken && tokenData.expirationDate) { changingTokenAndDate = YES; } else if (!FB_ISSESSIONOPENWITHSTATE(state) && FB_ISSESSIONOPENWITHSTATE(statePrior)) { changingTokenAndDate = YES; - token = nil; - date = nil; + tokenData = nil; } BOOL changingIsOpen = FB_ISSESSIONOPENWITHSTATE(state) != FB_ISSESSIONOPENWITHSTATE(statePrior); @@ -779,13 +743,14 @@ - (BOOL)transitionToState:(FBSessionState)state } if (changingTokenAndDate) { + FBSessionLoginType newLoginType = tokenData.loginType; // if we are just about to transition to open or token loaded, and the caller // wants to specify a login type other than none, then we set the login type FBSessionLoginType loginTypeUpdated = self.accessTokenData.loginType; if (isValidTransition && (state == FBSessionStateOpen || state == FBSessionStateCreatedTokenLoaded) && - loginType != FBSessionLoginTypeNone) { - loginTypeUpdated = loginType; + newLoginType != FBSessionLoginTypeNone) { + loginTypeUpdated = newLoginType; } // KVO property will-change notifications for token and date @@ -795,12 +760,13 @@ - (BOOL)transitionToState:(FBSessionState)state // set the new access token as a copy of any existing token with the updated // token string and expiration date. - if (token) { - FBAccessTokenData *fbAccessToken = [FBAccessTokenData createTokenFromString:token - permissions:(self.accessTokenData.permissions ?: self.initializedPermissions) - expirationDate:date + if (tokenData.accessToken) { + FBAccessTokenData *fbAccessToken = [FBAccessTokenData createTokenFromString:tokenData.accessToken + permissions:tokenData.permissions + expirationDate:tokenData.expirationDate loginType:loginTypeUpdated - refreshDate:[NSDate date]]; + refreshDate:tokenData.refreshDate + permissionsRefreshDate:tokenData.permissionsRefreshDate]; self.accessTokenData = fbAccessToken; } else { self.accessTokenData = nil; @@ -920,162 +886,44 @@ - (void)retryableAuthorizeWithPermissions:(NSArray*)permissions FBLoginUXTouch, FBLoginUXDisplay, FBLoginUXIOS, FBLoginUXSDK, nil]; - - NSMutableDictionary *clientState = [NSMutableDictionary dictionary]; - // Note, in url-roundtrips, unlogged metadata in authLogger is lost. Only the ID makes it back - // for completion-event logging - clientState[FBSessionAuthLoggerParamIDKey] = self.authLogger.ID; - if (permissions != nil) { - NSString* scope = [permissions componentsJoinedByString:@","]; - [params setValue:scope forKey:@"scope"]; + params[@"scope"] = [permissions componentsJoinedByString:@","]; } - if (_urlSchemeSuffix) { - [params setValue:_urlSchemeSuffix forKey:@"local_client_id"]; + params[@"local_client_id"] = _urlSchemeSuffix; } - + // To avoid surprises, delete any cookies we currently have. - [FBSession deleteFacebookCookies]; + [FBUtility deleteFacebookCookies]; - // we prefer OS-integrated Facebook login if supported by the device - // attempt to open an account store with the type Facebook; and if successful authorize - // using the OS BOOL didRequestAuthorize = NO; NSString *authMethod = nil; - // do we have the ability to attempt integrated authn - BOOL systemAccountStoreAvailable = [self isSystemAccountStoreAvailable]; - [self.authLogger addExtrasForNextEvent:@{ - @"systemAccountStoreAvailable":[NSNumber numberWithBool:systemAccountStoreAvailable] - }]; - // do we want to attempt integrated authn - if (!didRequestAuthorize && - tryIntegratedAuth && - (!isReauthorize || self.accessTokenData.loginType == FBSessionLoginTypeSystemAccount) && - systemAccountStoreAvailable) { - - // looks like we will get to attempt a login with integrated authn - didRequestAuthorize = YES; - - authMethod = FBSessionAuthLoggerAuthMethodIntegrated; - - [self authorizeUsingSystemAccountStore:permissions - defaultAudience:defaultAudience - isReauthorize:isReauthorize]; - } - - // if the device is running a version of iOS that supports multitasking, - // try to obtain the access token from the Facebook app installed - // on the device. - // If the Facebook app isn't installed or it doesn't support - // the fbauth:// URL scheme, fall back on Safari for obtaining the access token. - // This minimizes the chance that the user will have to enter his or - // her credentials in order to authorize the application. - BOOL isMultitaskingSupported = YES; - BOOL isURLSchemeRegistered = YES; - if (!didRequestAuthorize) { - isMultitaskingSupported = [self isMultitaskingSupported]; - isURLSchemeRegistered = [self isURLSchemeRegistered]; - - [self.authLogger addExtrasForNextEvent:@{ - @"isMultitaskingSupported":[NSNumber numberWithBool:isMultitaskingSupported], - @"isURLSchemeRegistered":[NSNumber numberWithBool:isURLSchemeRegistered] - }]; - } - - if (!didRequestAuthorize && - isMultitaskingSupported && - isURLSchemeRegistered && - !TEST_DISABLE_MULTITASKING_LOGIN) { - - if (tryFBAppAuth) { - FBFetchedAppSettings *fetchedSettings = [FBUtility fetchedAppSettings]; - [self.authLogger addExtrasForNextEvent:@{ - @"hasFetchedAppSettings": [NSNumber numberWithBool:(fetchedSettings != nil)], - @"pListFacebookDisplayName": [FBSettings defaultDisplayName] ?: @"" - }]; - if ([FBSettings defaultDisplayName] && // don't autoselect Native Login unless the app has been setup for it, - [self.appID isEqualToString:[FBSettings defaultAppID]] && // If the appId has been overridden, then the bridge cannot be used and native login is denied - (fetchedSettings || canFetchAppSettings) && // and we have app-settings available to us, or could fetch if needed - !TEST_DISABLE_FACEBOOKNATIVELOGIN) { - if (!fetchedSettings) { - // fetch the settings and call this method again - didRequestAuthorize = YES; - [FBUtility fetchAppSettings:[FBSettings defaultAppID] callback:^(FBFetchedAppSettings * settings, NSError * error) { - [self retryableAuthorizeWithPermissions:permissions - defaultAudience:defaultAudience - integratedAuth:tryIntegratedAuth - FBAppAuth:tryFBAppAuth - safariAuth:trySafariAuth - fallback:tryFallback - isReauthorize:isReauthorize - canFetchAppSettings:NO]; - }]; - } else { - [self.authLogger addExtrasForNextEvent:@{ - @"suppressNativeGdp": [NSNumber numberWithBool:fetchedSettings.suppressNativeGdp], - @"serverAppName": fetchedSettings.serverAppName ?: @"" - }]; - if (!fetchedSettings.suppressNativeGdp) { - if (![[FBSettings defaultDisplayName] isEqualToString:fetchedSettings.serverAppName]) { - [FBLogger singleShotLogEntry:FBLoggingBehaviorDeveloperErrors - logEntry:@"PLIST entry for FacebookDisplayName does not match Facebook app name."]; - [self.authLogger addExtrasForNextEvent:@{ - @"nameMismatch": [NSNumber numberWithBool:YES] - }]; - } - authMethod = FBSessionAuthLoggerAuthMethodFBApplicationNative; - clientState[FBSessionAuthLoggerParamAuthMethodKey] = authMethod; - FBAppCall *call = [self authorizeUsingFacebookNativeLoginWithPermissions:permissions - defaultAudience:defaultAudience - clientState:clientState]; - if (call) { - [self.authLogger addExtrasForNextEvent:@{ - @"native_auth_appcall_id":call.ID - }]; - - didRequestAuthorize = YES; - } - } - } - } - - if (!TEST_DISABLE_FACEBOOKLOGIN && !didRequestAuthorize) { - authMethod = FBSessionAuthLoggerAuthMethodFBApplicationWeb; - clientState[FBSessionAuthLoggerParamAuthMethodKey] = authMethod; - params[FBLoginUXClientState] = [self jsonClientStateWithDictionary:clientState]; - - didRequestAuthorize = [self authorizeUsingFacebookApplication:params]; - } - } - - if (trySafariAuth && !didRequestAuthorize) { - authMethod = FBSessionAuthLoggerAuthMethodBrowser; - clientState[FBSessionAuthLoggerParamAuthMethodKey] = authMethod; - params[FBLoginUXClientState] = [self jsonClientStateWithDictionary:clientState]; - - didRequestAuthorize = [self authorizeUsingSafari:params]; - } - - //In case openURL failed, make sure we don't still expect a openURL callback. - if (!didRequestAuthorize){ - _loginTypeOfPendingOpenUrlCallback = FBSessionLoginTypeNone; - authMethod = nil; + FBSessionLoginStrategyParams *authorizeParams = [[[FBSessionLoginStrategyParams alloc] init] autorelease]; + authorizeParams.tryIntegratedAuth = tryIntegratedAuth; + authorizeParams.tryFBAppAuth = tryFBAppAuth; + authorizeParams.trySafariAuth = trySafariAuth; + authorizeParams.tryFallback = tryFallback; + authorizeParams.isReauthorize = isReauthorize; + authorizeParams.defaultAudience = defaultAudience; + authorizeParams.permissions = permissions; + authorizeParams.canFetchAppSettings = canFetchAppSettings; + authorizeParams.webParams = params; + + // Note ordering is significant here. + NSArray *loginStrategies = @[ [[[FBSessionSystemLoginStategy alloc] init] autorelease], + [[[FBSessionAppSwitchingLoginStategy alloc] init] autorelease], + [[[FBSessionInlineWebViewLoginStategy alloc] init] autorelease] + ]; + + for (id loginStrategy in loginStrategies) { + if ([loginStrategy tryPerformAuthorizeWithParams:authorizeParams session:self logger:self.authLogger]) { + didRequestAuthorize = YES; + authMethod = loginStrategy.methodName; + break; } } - - // If single sign-on failed, see if we should attempt to fallback - if (!didRequestAuthorize && tryFallback) { - authMethod = FBSessionAuthLoggerAuthMethodFallback; - clientState[FBSessionAuthLoggerParamAuthMethodKey] = authMethod; - params[FBLoginUXClientState] = [self jsonClientStateWithDictionary:clientState]; - - didRequestAuthorize = YES; - - [self authorizeUsingLoginDialog:params]; - } - + if (didRequestAuthorize) { if (authMethod) { // This is a nested-if, because we might not have an authmethod yet if waiting on fetchedAppSettings // Some method of authentication was kicked off @@ -1090,13 +938,15 @@ - (void)retryableAuthorizeWithPermissions:(NSArray*)permissions // state transition, and call the handler if there is one [self transitionAndCallHandlerWithState:FBSessionStateClosedLoginFailed error:error - token:nil - expirationDate:nil - shouldCache:NO - loginType:FBSessionLoginTypeNone]; + tokenData:nil + shouldCache:NO]; } } +- (void)setLoginTypeOfPendingOpenUrlCallback:(FBSessionLoginType)loginType { + _loginTypeOfPendingOpenUrlCallback = loginType; +} + - (void)logIntegratedAuthAppEvent:(NSString *)dialogOutcome permissions:(NSArray *)permissions { @@ -1127,14 +977,6 @@ - (void)logIntegratedAuthAppEvent:(NSString *)dialogOutcome session:self]; } -- (BOOL)isSystemAccountStoreAvailable { - id accountStore = nil; - id accountTypeFB = nil; - - return (accountStore = [[[NSClassFromString(@"ACAccountStore") alloc] init] autorelease]) && - (accountTypeFB = [accountStore accountTypeWithAccountTypeIdentifier:@"com.apple.facebook"]); -} - - (void)authorizeUsingSystemAccountStore:(NSArray*)permissions defaultAudience:(FBSessionDefaultAudience)defaultAudience isReauthorize:(BOOL)isReauthorize { @@ -1153,7 +995,7 @@ - (void)authorizeUsingSystemAccountStore:(NSArray*)permissions handler:^(NSString *oauthToken, NSError *accountStoreError) { BOOL isUntosedDevice = (!oauthToken && accountStoreError.code == ACErrorAccountNotFound); - int millisecondsSinceUIWasPotentiallyShown = [FBUtility currentTimeInMilliseconds] - timePriorToShowingUI; + unsigned long millisecondsSinceUIWasPotentiallyShown = [FBUtility currentTimeInMilliseconds] - timePriorToShowingUI; // There doesn't appear to be a reliable way to determine whether or not a UI was invoked // to get us here, or whether the cached token was sufficient. So we use a timer heuristic @@ -1178,13 +1020,16 @@ - (void)authorizeUsingSystemAccountStore:(NSArray*)permissions [self.authLogger logEndAuthMethodWithResult:FBSessionAuthLoggerResultSuccess error:nil]; - [self transitionAndCallHandlerWithState:FBSessionStateOpen - error:nil - token:oauthToken // BUG: we need a means for fetching the expiration date of the token - expirationDate:[NSDate distantFuture] - shouldCache:YES - loginType:FBSessionLoginTypeSystemAccount]; + FBAccessTokenData *tokenData = [FBAccessTokenData createTokenFromString:oauthToken + permissions:permissions + expirationDate:[NSDate distantFuture] + loginType:FBSessionLoginTypeSystemAccount + refreshDate:[NSDate date]]; + [self transitionAndCallHandlerWithState:FBSessionStateOpen + error:nil + tokenData:tokenData + shouldCache:YES]; } else if (isUntosedDevice) { @@ -1239,10 +1084,8 @@ - (void)authorizeUsingSystemAccountStore:(NSArray*)permissions // state transition, and call the handler if there is one [self transitionAndCallHandlerWithState:FBSessionStateClosedLoginFailed error:err - token:nil - expirationDate:nil - shouldCache:NO - loginType:FBSessionLoginTypeNone]; + tokenData:nil + shouldCache:NO]; } } else { // reauth case if (oauthToken) { @@ -1310,11 +1153,6 @@ - (FBSystemAccountStoreAdapter *)getSystemAccountStoreAdapter { return [FBSystemAccountStoreAdapter sharedInstance]; } -- (BOOL)isMultitaskingSupported { - return [[UIDevice currentDevice] respondsToSelector:@selector(isMultitaskingSupported)] && - [[UIDevice currentDevice] isMultitaskingSupported]; -} - - (FBAppCall *)authorizeUsingFacebookNativeLoginWithPermissions:(NSArray*)permissions defaultAudience:(FBSessionDefaultAudience)defaultAudience clientState:(NSDictionary *)clientState { @@ -1372,13 +1210,6 @@ - (void)handleDidCompleteNativeLoginForAppCall:(FBAppCall *)call { loginType:loginType]; } -- (void)addWebLoginStartTimeToParams:(NSMutableDictionary *)params -{ - NSNumber *timeValue = [NSNumber numberWithDouble:round(1000 * [[NSDate date] timeIntervalSince1970])]; - NSString *e2eTimestampString = [FBUtility simpleJSONEncode:@{@"init":timeValue}]; - [params setObject:e2eTimestampString forKey:@"e2e"]; -} - - (BOOL)isURLSchemeRegistered { // If the url scheme is not registered, then the app we delegate to cannot call // back, and hence this is an invalid call. @@ -1397,7 +1228,7 @@ - (BOOL)authorizeUsingFacebookApplication:(NSMutableDictionary *)params { scheme = [scheme stringByAppendingString:@"2"]; } // add a timestamp for tracking GDP e2e time - [self addWebLoginStartTimeToParams:params]; + [FBSessionUtility addWebLoginStartTimeToParams:params]; NSString *urlPrefix = [NSString stringWithFormat:@"%@://%@", scheme, FBAuthURLPath]; NSString *fbAppUrl = [FBRequest serializeURL:urlPrefix params:params]; @@ -1408,9 +1239,9 @@ - (BOOL)authorizeUsingFacebookApplication:(NSMutableDictionary *)params { - (BOOL)authorizeUsingSafari:(NSMutableDictionary *)params { // add a timestamp for tracking GDP e2e time - [self addWebLoginStartTimeToParams:params]; + [FBSessionUtility addWebLoginStartTimeToParams:params]; - NSString *loginDialogURL = [[FBSession dialogBaseURL] stringByAppendingString:FBLoginDialogMethod]; + NSString *loginDialogURL = [[FBUtility dialogBaseURL] stringByAppendingString:FBLoginDialogMethod]; NSString *nextUrl = self.appBaseUrl; [params setValue:nextUrl forKey:@"redirect_uri"]; @@ -1427,9 +1258,9 @@ - (BOOL)tryOpenURL:(NSURL *)url { - (void)authorizeUsingLoginDialog:(NSMutableDictionary *)params { // add a timestamp for tracking GDP e2e time - [self addWebLoginStartTimeToParams:params]; + [FBSessionUtility addWebLoginStartTimeToParams:params]; - NSString *loginDialogURL = [[FBSession dialogBaseURL] stringByAppendingString:FBLoginDialogMethod]; + NSString *loginDialogURL = [[FBUtility dialogBaseURL] stringByAppendingString:FBLoginDialogMethod]; // open an inline login dialog. This will require the user to enter his or her credentials. self.loginDialog = [[[FBLoginDialog alloc] initWithURL:loginDialogURL @@ -1521,23 +1352,24 @@ - (BOOL)handleAuthorizationOpen:(NSDictionary*)parameters // state transition, and call the handler if there is one [self transitionAndCallHandlerWithState:FBSessionStateClosedLoginFailed error:errorToSurface - token:nil - expirationDate:nil - shouldCache:NO - loginType:FBSessionLoginTypeNone]; + tokenData:nil + shouldCache:NO]; } else { [self.authLogger logEndAuthMethodWithResult:FBSessionAuthLoggerResultSuccess error:nil]; // we have an access token, so parse the expiration date. - NSDate *expirationDate = [FBSession expirationDateFromResponseParams:parameters]; + NSDate *expirationDate = [FBSessionUtility expirationDateFromResponseParams:parameters]; // set token and date, state transition, and call the handler if there is one + FBAccessTokenData *tokenData = [FBAccessTokenData createTokenFromString:accessToken + permissions:self.initializedPermissions + expirationDate:expirationDate + loginType:loginType + refreshDate:[NSDate date]]; [self transitionAndCallHandlerWithState:FBSessionStateOpen error:nil - token:accessToken - expirationDate:expirationDate - shouldCache:YES - loginType:loginType]; + tokenData:tokenData + shouldCache:YES]; } return YES; } @@ -1585,7 +1417,7 @@ - (BOOL)handleReauthorize:(NSDictionary*)parameters } else { // we have an access token, so parse the expiration date. - NSDate *expirationDate = [FBSession expirationDateFromResponseParams:parameters]; + NSDate *expirationDate = [FBSessionUtility expirationDateFromResponseParams:parameters]; [self validateReauthorizedAccessToken:accessToken expirationDate:expirationDate]; } @@ -1713,13 +1545,12 @@ - (void)reauthorizeWithPermissions:(NSArray*)permissions } // is everything in good order argument-wise? - [FBSession validateRequestForPermissions:permissions + [FBSessionUtility validateRequestForPermissions:permissions defaultAudience:audience allowSystemAccount:behavior == FBSessionLoginBehaviorUseSystemAccountIfPresent isRead:isRead]; // setup handler and permissions and perform the actual reauthorize - self.reauthorizePermissions = permissions; self.reauthorizeHandler = handler; [self authorizeWithPermissions:permissions behavior:behavior @@ -1769,19 +1600,20 @@ - (void)repairWithHandler:(FBSessionRequestPermissionResultHandler) handler { - (void)completeReauthorizeWithAccessToken:(NSString*)accessToken expirationDate:(NSDate*)expirationDate permissions:(NSArray*)permissions { - if (permissions) { - self.accessTokenData.permissions = permissions; - } - [self.authLogger logEndAuthMethodWithResult:FBSessionAuthLoggerResultSuccess error:nil]; // set token and date, state transition, and call the handler if there is one + NSDate *now = [NSDate date]; + FBAccessTokenData *tokenData = [FBAccessTokenData createTokenFromString:accessToken + permissions:permissions + expirationDate:expirationDate + loginType:FBSessionLoginTypeNone + refreshDate:now + permissionsRefreshDate:now]; [self transitionAndCallHandlerWithState:FBSessionStateOpenTokenExtended error:nil - token:accessToken - expirationDate:expirationDate - shouldCache:YES - loginType:FBSessionLoginTypeNone]; + tokenData:tokenData + shouldCache:YES]; // no error, ack a completed permission upgrade [self callReauthorizeHandlerAndClearState:nil]; @@ -1821,12 +1653,16 @@ -(void)authorizeRequestWasImplicitlyCancelled { - (void)refreshAccessToken:(NSString*)token expirationDate:(NSDate*)expireDate { // refresh token and date, state transition, and call the handler if there is one + FBAccessTokenData *tokenData = [FBAccessTokenData createTokenFromString:token ?: self.accessTokenData.accessToken + permissions:self.accessTokenData.permissions + expirationDate:expireDate + loginType:FBSessionLoginTypeNone + refreshDate:[NSDate date] + permissionsRefreshDate:self.accessTokenData.permissionsRefreshDate]; [self transitionAndCallHandlerWithState:FBSessionStateOpenTokenExtended error:nil - token:token ? token : self.accessTokenData.accessToken - expirationDate:expireDate - shouldCache:YES - loginType:FBSessionLoginTypeNone]; + tokenData:tokenData + shouldCache:YES]; } - (BOOL)shouldExtendAccessToken { @@ -1846,6 +1682,40 @@ - (BOOL)shouldExtendAccessToken { return result; } +// For simplicity, checking `shouldRefreshPermission` will toggle the flag +// such that future calls within the next hour (as defined by the threshold constant) +// will return NO. Therefore, you should only call this method if you are also +// prepared to actually `refreshPermissions`. +- (BOOL)shouldRefreshPermissions { + @synchronized(self.attemptedPermissionsRefreshDate) { + NSDate *now = [NSDate date]; + + if (self.isOpen && + // Share the same thresholds as the access token string for convenience, we may change in the future. + [now timeIntervalSinceDate:self.attemptedPermissionsRefreshDate] > FBTokenRetryExtendSeconds && + [now timeIntervalSinceDate:self.accessTokenData.permissionsRefreshDate] > FBTokenExtendThresholdSeconds) { + self.attemptedPermissionsRefreshDate = now; + return YES; + } + } + return NO; +} + +- (void)refreshPermissions:(NSArray *)permissions { + NSDate *now = [NSDate date]; + FBAccessTokenData *tokenData = [FBAccessTokenData createTokenFromString:self.accessTokenData.accessToken + permissions:permissions + expirationDate:self.accessTokenData.expirationDate + loginType:self.accessTokenData.loginType + refreshDate:self.accessTokenData.refreshDate + permissionsRefreshDate:now]; + self.attemptedPermissionsRefreshDate = now; + // Note we intentionally do not notify KVO that `accessTokenData `is changing since + // the implied contract is for that to only occur during state transitions. + self.accessTokenData = tokenData; + [self.tokenCachingStrategy cacheFBAccessTokenData:self.accessTokenData]; +} + // Internally accessed, so we can bind the affinitized thread later. - (void)clearAffinitizedThread { self.affinitizedThread = nil; @@ -1895,18 +1765,14 @@ - (void)fbDialogNotLogin:(BOOL)cancelled { // helper to wrap-up handler callback and state-change - (void)transitionAndCallHandlerWithState:(FBSessionState)status error:(NSError*)error - token:(NSString*)token - expirationDate:(NSDate*)date - shouldCache:(BOOL)shouldCache - loginType:(FBSessionLoginType)loginType { + tokenData:(FBAccessTokenData *)tokenData + shouldCache:(BOOL)shouldCache { // lets get the state transition out of the way BOOL didTransition = [self transitionToState:status - andUpdateToken:token - andExpirationDate:date - shouldCache:shouldCache - loginType:loginType]; + withAccessTokenData:tokenData + shouldCache:shouldCache]; NSString *authLoggerResult = FBSessionAuthLoggerResultError; if (!error) { @@ -1969,7 +1835,6 @@ - (void)callReauthorizeHandlerAndClearState:(NSError*)error { FBSessionRequestPermissionResultHandler reauthorizeHandler = [self.reauthorizeHandler retain]; @try { self.reauthorizeHandler = nil; - self.reauthorizePermissions = nil; if (reauthorizeHandler) { reauthorizeHandler(self, error); @@ -2032,31 +1897,6 @@ - (NSString *)jsonClientStateWithDictionary:(NSDictionary *)dictionary{ return clientStateString ?: @"{}"; } -+ (NSDictionary *)queryParamsFromLoginURL:(NSURL *)url appID:(NSString *)appID urlSchemeSuffix:(NSString *)urlSchemeSuffix { - // if the URL's structure doesn't match the structure used for Facebook authorization, abort. - if (appID) { - NSString* expectedUrlPrefix = [FBUtility stringAppBaseUrlFromAppId:appID urlSchemeSuffix:urlSchemeSuffix]; - if (![[url absoluteString] hasPrefix:expectedUrlPrefix]) { - return nil; - } - } else { - // Don't have an App ID, just verify path. - NSString *host = url.host; - if (![host isEqualToString:@"authorize"]) { - return nil; - } - } - - return [FBUtility queryParamsDictionaryFromFBURL:url]; -} - -+ (NSDictionary *)clientStateFromQueryParams:(NSDictionary *)params { - NSDictionary *clientState = [FBUtility simpleJSONDecode:params[FBLoginUXClientState]]; - if (!clientState[FBLoginUXClientStateIsClientState]) { - return nil; - } - return clientState; -} + (BOOL)automaticallyNotifiesObserversForKey:(NSString *)key { // these properties must manually notify for KVO @@ -2071,49 +1911,9 @@ + (BOOL)automaticallyNotifiesObserversForKey:(NSString *)key { } } -+ (BOOL)areRequiredPermissions:(NSArray*)requiredPermissions - aSubsetOfPermissions:(NSArray*)cachedPermissions { - NSSet *required = [NSSet setWithArray:requiredPermissions]; - NSSet *cached = [NSSet setWithArray:cachedPermissions]; - return [required isSubsetOfSet:cached]; -} - -+ (NSDate *)expirationDateFromResponseParams:(NSDictionary *)parameters { - NSString *expTime = [parameters objectForKey:@"expires_in"]; - NSDate *expirationDate = nil; - if (expTime) { - // If we have an interval, it is assumed to be since now. (e.g. 60 days) - expirationDate = [FBUtility expirationDateFromExpirationTimeIntervalString:expTime]; - } else { - // If we have an actual timestamp, create the date from that instead. - expirationDate = [FBUtility expirationDateFromExpirationUnixTimeString:parameters[@"expires"]]; - } - - if (!expirationDate) { - expirationDate = [NSDate distantFuture]; - } - - return expirationDate; -} - -+ (NSString *)dialogBaseURL { - return [FBUtility buildFacebookUrlWithPre:@"https://m." withPost:@"/dialog/"]; -} - #pragma mark - #pragma mark Internal members -+ (BOOL)isOpenSessionResponseURL:(NSURL *)url { - NSDictionary *params = [FBSession queryParamsFromLoginURL:url appID:nil urlSchemeSuffix:nil]; - NSDictionary *clientState = [FBSession clientStateFromQueryParams:params]; - if (!clientState) { - return NO; - } - - NSNumber *isOpenSessionBit = clientState[FBLoginUXClientStateIsOpenSession]; - return [isOpenSessionBit boolValue]; -} - - (BOOL)openFromAccessTokenData:(FBAccessTokenData *)accessTokenData completionHandler:(FBSessionStateHandler) handler raiseExceptionIfInvalidState:(BOOL)raiseException { @@ -2151,7 +1951,7 @@ + (BOOL)openActiveSessionWithPermissions:(NSArray*)permissions defaultAudience:(FBSessionDefaultAudience)defaultAudience completionHandler:(FBSessionStateHandler)handler { // is everything in good order? - [FBSession validateRequestForPermissions:permissions + [FBSessionUtility validateRequestForPermissions:permissions defaultAudience:defaultAudience allowSystemAccount:allowSystemAccount isRead:isRead]; @@ -2187,41 +1987,6 @@ + (FBSession*)activeSessionIfOpen { return nil; } -+ (void)validateRequestForPermissions:(NSArray*)permissions - defaultAudience:(FBSessionDefaultAudience)defaultAudience - allowSystemAccount:(BOOL)allowSystemAccount - isRead:(BOOL)isRead { - // validate audience argument - if ([permissions count]) { - if (allowSystemAccount && !isRead) { - switch (defaultAudience) { - case FBSessionDefaultAudienceEveryone: - case FBSessionDefaultAudienceFriends: - case FBSessionDefaultAudienceOnlyMe: - break; - default: - [[NSException exceptionWithName:FBInvalidOperationException - reason:@"FBSession: Publish permissions were requested " - @"without specifying an audience; use FBSessionDefaultAudienceJustMe, " - @"FBSessionDefaultAudienceFriends, or FBSessionDefaultAudienceEveryone" - userInfo:nil] - raise]; - break; - } - } - // log unexpected permissions, and throw on read w/publish permissions - if (allowSystemAccount && - [FBSession logIfFoundUnexpectedPermissions:permissions isRead:isRead] && - isRead) { - [[NSException exceptionWithName:FBInvalidOperationException - reason:@"FBSession: Publish or manage permissions are not permitted to " - @"to be requested with read permissions." - userInfo:nil] - raise]; - } - } -} - // This method is used to support early versions of native login that were using the // platform module's error domain to pass through server errors. The goal is to put those // errors in a separate domain to avoid collisions. @@ -2236,46 +2001,6 @@ + (NSError *)sdkSurfacedErrorForNativeLoginError:(NSError *)nativeLoginError { return error; } -+ (BOOL)logIfFoundUnexpectedPermissions:(NSArray*)permissions - isRead:(BOOL)isRead { - BOOL publishPermissionFound = NO; - BOOL readPermissionFound = NO; - BOOL result = NO; - for (NSString *p in permissions) { - if ([FBUtility isPublishPermission:p]) { - publishPermissionFound = YES; - } else { - readPermissionFound = YES; - } - - // If we've found one of each we can stop looking. - if (publishPermissionFound && readPermissionFound) { - break; - } - } - - if (!isRead && readPermissionFound) { - [FBLogger singleShotLogEntry:FBLoggingBehaviorDeveloperErrors logEntry:@"FBSession: a permission request for publish or manage permissions contains unexpected read permissions"]; - result = YES; - } - if (isRead && publishPermissionFound) { - [FBLogger singleShotLogEntry:FBLoggingBehaviorDeveloperErrors logEntry:@"FBSession: a permission request for read permissions contains unexpected publish or manage permissions"]; - result = YES; - } - - return result; -} - -+ (void)deleteFacebookCookies { - NSHTTPCookieStorage* cookies = [NSHTTPCookieStorage sharedHTTPCookieStorage]; - NSArray* facebookCookies = [cookies cookiesForURL: - [NSURL URLWithString:[FBSession dialogBaseURL]]]; - - for (NSHTTPCookie* cookie in facebookCookies) { - [cookies deleteCookie:cookie]; - } -} - - (void)closeAndClearTokenInformation:(NSError*) error { [self checkThreadAffinity]; @@ -2286,51 +2011,16 @@ - (void)closeAndClearTokenInformation:(NSError*) error { if (!FB_ISSESSIONSTATETERMINAL(self.state)) { [self transitionAndCallHandlerWithState:FBSessionStateClosed error:error - token:nil - expirationDate:nil - shouldCache:NO - loginType:FBSessionLoginTypeNone]; + tokenData:nil + shouldCache:NO]; } } #pragma mark - #pragma mark Debugging helpers -+ (NSString *)sessionStateDescription:(FBSessionState)sessionState { - NSString *stateDescription = nil; - switch (sessionState) { - case FBSessionStateCreated: - stateDescription = @"FBSessionStateCreated"; - break; - case FBSessionStateCreatedTokenLoaded: - stateDescription = @"FBSessionStateCreatedTokenLoaded"; - break; - case FBSessionStateCreatedOpening: - stateDescription = @"FBSessionStateCreatedOpening"; - break; - case FBSessionStateOpen: - stateDescription = @"FBSessionStateOpen"; - break; - case FBSessionStateOpenTokenExtended: - stateDescription = @"FBSessionStateOpenTokenExtended"; - break; - case FBSessionStateClosedLoginFailed: - stateDescription = @"FBSessionStateClosedLoginFailed"; - break; - case FBSessionStateClosed: - stateDescription = @"FBSessionStateClosed"; - break; - default: - stateDescription = @"[Unknown]"; - break; - } - - return stateDescription; -} - - - (NSString*)description { - NSString *stateDescription = [FBSession sessionStateDescription:self.state]; + NSString *stateDescription = [FBSessionUtility sessionStateDescription:self.state]; return [NSString stringWithFormat:@"<%@: %p, state: %@, loginHandler: %p, appID: %@, urlSchemeSuffix: %@, tokenCachingStrategy:%@, expirationDate: %@, refreshDate: %@, attemptedRefreshDate: %@, permissions:%@>", NSStringFromClass([self class]), self, diff --git a/src/FBSessionAppEventsState.h b/src/FBSessionAppEventsState.h index 0a3ed4228c..0c8f553f5d 100644 --- a/src/FBSessionAppEventsState.h +++ b/src/FBSessionAppEventsState.h @@ -30,7 +30,7 @@ - (void)addEvent:(NSDictionary *)eventDictionary isImplicit:(BOOL)isImplicit; - (NSString *)jsonEncodeInFlightEvents:(BOOL)includeImplicitEvents; -- (int)getAccumulatedEventCount; +- (NSUInteger)getAccumulatedEventCount; - (void)clearInFlightAndStats; @end diff --git a/src/FBSessionAppEventsState.m b/src/FBSessionAppEventsState.m index 2a443f9377..e2e7907eac 100644 --- a/src/FBSessionAppEventsState.m +++ b/src/FBSessionAppEventsState.m @@ -63,7 +63,7 @@ - (void)addEvent:(NSDictionary *)eventDictionary } } -- (int)getAccumulatedEventCount { +- (NSUInteger)getAccumulatedEventCount { @synchronized (self) { return self.accumulatedEvents.count; } diff --git a/src/FBSessionAppSwitchingLoginStategy.h b/src/FBSessionAppSwitchingLoginStategy.h new file mode 100644 index 0000000000..f610adceba --- /dev/null +++ b/src/FBSessionAppSwitchingLoginStategy.h @@ -0,0 +1,19 @@ +/* + * Copyright 2010-present 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 "FBSessionLoginStrategy.h" + +@interface FBSessionAppSwitchingLoginStategy : NSObject +@end diff --git a/src/FBSessionAppSwitchingLoginStategy.m b/src/FBSessionAppSwitchingLoginStategy.m new file mode 100644 index 0000000000..123684b698 --- /dev/null +++ b/src/FBSessionAppSwitchingLoginStategy.m @@ -0,0 +1,86 @@ +/* + * Copyright 2010-present 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 "FBLogger.h" +#import "FBSession+Internal.h" +#import "FBSessionAppSwitchingLoginStategy.h" +#import "FBSessionAuthLogger.h" +#import "FBSessionFacebookAppNativeLoginStategy.h" +#import "FBSessionFacebookAppWebLoginStategy.h" +#import "FBSessionLoginStrategy.h" +#import "FBSessionSafariLoginStategy.h" +#import "FBUtility.h" + +// A composite login strategy that tries strategies that require app switching +// (e.g., native gdp, native web gdp, safari) +@interface FBSessionAppSwitchingLoginStategy() + +@property (copy, nonatomic, readwrite) NSString *methodName; + +@end + +@implementation FBSessionAppSwitchingLoginStategy + +- (id)init { + if ((self = [super init])){ + self.methodName = FBSessionAuthLoggerAuthMethodFBApplicationNative; + } + return self; +} + +- (void)dealloc { + [_methodName release]; + [super dealloc]; +} + +- (BOOL)tryPerformAuthorizeWithParams:(FBSessionLoginStrategyParams *)params session:(FBSession *)session logger:(FBSessionAuthLogger *)logger { + // if the device is running a version of iOS that supports multitasking, + // try to obtain the access token from the Facebook app installed + // on the device. + // If the Facebook app isn't installed or it doesn't support + // the fbauth:// URL scheme, fall back on Safari for obtaining the access token. + // This minimizes the chance that the user will have to enter his or + // her credentials in order to authorize the application. + BOOL isMultitaskingSupported = [FBUtility isMultitaskingSupported]; + BOOL isURLSchemeRegistered = [session isURLSchemeRegistered];; + + [logger addExtrasForNextEvent:@{ + @"isMultitaskingSupported":@(isMultitaskingSupported), + @"isURLSchemeRegistered":@(isURLSchemeRegistered) + }]; + + if (isMultitaskingSupported && + isURLSchemeRegistered && + !TEST_DISABLE_MULTITASKING_LOGIN) { + + NSArray *loginStrategies = @[ [[[FBSessionFacebookAppNativeLoginStategy alloc] init] autorelease], + [[[FBSessionFacebookAppWebLoginStategy alloc] init] autorelease], + [[[FBSessionSafariLoginStategy alloc] init] autorelease] ]; + + for (id loginStrategy in loginStrategies) { + + if ([loginStrategy tryPerformAuthorizeWithParams:params session:session logger:logger]) { + self.methodName = loginStrategy.methodName; + return YES; + } + } + + [session setLoginTypeOfPendingOpenUrlCallback:FBSessionLoginTypeNone]; + } + return NO; +} + +@end diff --git a/src/FBSessionAuthLogger.m b/src/FBSessionAuthLogger.m index fb36c8ad40..90922e6ce0 100644 --- a/src/FBSessionAuthLogger.m +++ b/src/FBSessionAuthLogger.m @@ -114,7 +114,7 @@ - (void)logEvent:(NSString *)eventName result:(NSString *)result error:(NSError params[FBSessionAuthLoggerParamErrorMessageKey] = value; } - value = error.userInfo[FBErrorLoginFailedOriginalErrorCode] ?: [NSString stringWithFormat:@"%d", error.code]; + value = error.userInfo[FBErrorLoginFailedOriginalErrorCode] ?: [NSString stringWithFormat:@"%ld", (long)error.code]; if (value) { params[FBSessionAuthLoggerParamErrorCodeKey] = value; } @@ -125,7 +125,7 @@ - (void)logEvent:(NSString *)eventName result:(NSString *)result error:(NSError [self addExtrasForNextEvent:@{@"inner_error_message": value}]; } - value = innerError.userInfo[FBErrorLoginFailedOriginalErrorCode] ?: [NSString stringWithFormat:@"%d", innerError.code]; + value = innerError.userInfo[FBErrorLoginFailedOriginalErrorCode] ?: [NSString stringWithFormat:@"%ld", (long)innerError.code]; if (value) { [self addExtrasForNextEvent:@{@"inner_error_code": value}]; } diff --git a/src/FBSessionFacebookAppNativeLoginStategy.h b/src/FBSessionFacebookAppNativeLoginStategy.h new file mode 100644 index 0000000000..92d631dc70 --- /dev/null +++ b/src/FBSessionFacebookAppNativeLoginStategy.h @@ -0,0 +1,19 @@ +/* + * Copyright 2010-present 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 "FBSessionLoginStrategy.h" + +@interface FBSessionFacebookAppNativeLoginStategy : NSObject +@end \ No newline at end of file diff --git a/src/FBSessionFacebookAppNativeLoginStategy.m b/src/FBSessionFacebookAppNativeLoginStategy.m new file mode 100644 index 0000000000..3a4c74fd7f --- /dev/null +++ b/src/FBSessionFacebookAppNativeLoginStategy.m @@ -0,0 +1,88 @@ +/* + * Copyright 2010-present 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 "FBLogger.h" +#import "FBSession+Internal.h" +#import "FBSessionAuthLogger.h" +#import "FBSessionLoginStrategy.h" +#import "FBSessionFacebookAppNativeLoginStategy.h" +#import "FBUtility.h" + +@implementation FBSessionFacebookAppNativeLoginStategy + +- (BOOL)tryPerformAuthorizeWithParams:(FBSessionLoginStrategyParams *)params session:(FBSession *)session logger:(FBSessionAuthLogger *)logger { + if (params.tryFBAppAuth) { + FBFetchedAppSettings *fetchedSettings = [FBUtility fetchedAppSettings]; + [logger addExtrasForNextEvent:@{ + @"hasFetchedAppSettings": @(fetchedSettings != nil), + @"pListFacebookDisplayName": [FBSettings defaultDisplayName] ?: @"" + }]; + if ([FBSettings defaultDisplayName] && // don't autoselect Native Login unless the app has been setup for it, + [session.appID isEqualToString:[FBSettings defaultAppID]] && // If the appId has been overridden, then the bridge cannot be used and native login is denied + (fetchedSettings || params.canFetchAppSettings) && // and we have app-settings available to us, or could fetch if needed + !TEST_DISABLE_FACEBOOKNATIVELOGIN) { + if (!fetchedSettings) { + // fetch the settings and call the session auth method again. + [FBUtility fetchAppSettings:[FBSettings defaultAppID] callback:^(FBFetchedAppSettings * settings, NSError * error) { + [session retryableAuthorizeWithPermissions:params.permissions + defaultAudience:params.defaultAudience + integratedAuth:params.tryIntegratedAuth + FBAppAuth:params.tryFBAppAuth + safariAuth:params.trySafariAuth + fallback:params.tryFallback + isReauthorize:params.isReauthorize + canFetchAppSettings:NO]; + }]; + return YES; + } else { + [logger addExtrasForNextEvent:@{ + @"suppressNativeGdp": @(fetchedSettings.suppressNativeGdp), + @"serverAppName": fetchedSettings.serverAppName ?: @"" + }]; + if (!fetchedSettings.suppressNativeGdp) { + if (![[FBSettings defaultDisplayName] isEqualToString:fetchedSettings.serverAppName]) { + [FBLogger singleShotLogEntry:FBLoggingBehaviorDeveloperErrors + logEntry:@"PLIST entry for FacebookDisplayName does not match Facebook app name."]; + [logger addExtrasForNextEvent:@{ + @"nameMismatch": @(YES) + }]; + } + + NSDictionary *clientState = @{FBSessionAuthLoggerParamAuthMethodKey: self.methodName, + FBSessionAuthLoggerParamIDKey : logger.ID ?: @""}; + + FBAppCall *call = [session authorizeUsingFacebookNativeLoginWithPermissions:params.permissions + defaultAudience:params.defaultAudience + clientState:clientState]; + if (call) { + [logger addExtrasForNextEvent:@{ + @"native_auth_appcall_id":call.ID + }]; + + return YES; + } + } + } + } + } + return NO; +} + +- (NSString *)methodName { + return FBSessionAuthLoggerAuthMethodFBApplicationNative; +} + +@end \ No newline at end of file diff --git a/src/FBSessionFacebookAppWebLoginStategy.h b/src/FBSessionFacebookAppWebLoginStategy.h new file mode 100644 index 0000000000..48c12bca38 --- /dev/null +++ b/src/FBSessionFacebookAppWebLoginStategy.h @@ -0,0 +1,19 @@ +/* + * Copyright 2010-present 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 "FBSessionLoginStrategy.h" + +@interface FBSessionFacebookAppWebLoginStategy : NSObject +@end diff --git a/src/FBSessionFacebookAppWebLoginStategy.m b/src/FBSessionFacebookAppWebLoginStategy.m new file mode 100644 index 0000000000..540ea8da1c --- /dev/null +++ b/src/FBSessionFacebookAppWebLoginStategy.m @@ -0,0 +1,40 @@ +/* + * Copyright 2010-present 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 "FBLogger.h" +#import "FBSession+Internal.h" +#import "FBSessionAuthLogger.h" +#import "FBSessionFacebookAppWebLoginStategy.h" +#import "FBSessionLoginStrategy.h" +#import "FBUtility.h" + +@implementation FBSessionFacebookAppWebLoginStategy + +- (BOOL)tryPerformAuthorizeWithParams:(FBSessionLoginStrategyParams *)params session:(FBSession *)session logger:(FBSessionAuthLogger *)logger { + if (params.tryFBAppAuth && !TEST_DISABLE_FACEBOOKLOGIN) { + NSDictionary *clientState = @{FBSessionAuthLoggerParamAuthMethodKey: self.methodName, + FBSessionAuthLoggerParamIDKey : logger.ID ?: @""}; + params.webParams[FBLoginUXClientState] = [session jsonClientStateWithDictionary:clientState]; + return [session authorizeUsingFacebookApplication:params.webParams]; + } + return NO; +} + +- (NSString *)methodName { + return FBSessionAuthLoggerAuthMethodFBApplicationWeb; +} + +@end diff --git a/src/FBSessionInlineWebViewLoginStategy.h b/src/FBSessionInlineWebViewLoginStategy.h new file mode 100644 index 0000000000..0156a381fe --- /dev/null +++ b/src/FBSessionInlineWebViewLoginStategy.h @@ -0,0 +1,19 @@ +/* + * Copyright 2010-present 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 "FBSessionLoginStrategy.h" + +@interface FBSessionInlineWebViewLoginStategy : NSObject +@end diff --git a/src/FBSessionInlineWebViewLoginStategy.m b/src/FBSessionInlineWebViewLoginStategy.m new file mode 100644 index 0000000000..4de4ef67c3 --- /dev/null +++ b/src/FBSessionInlineWebViewLoginStategy.m @@ -0,0 +1,41 @@ +/* + * Copyright 2010-present 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 "FBLogger.h" +#import "FBSession+Internal.h" +#import "FBSessionAuthLogger.h" +#import "FBSessionInlineWebViewLoginStategy.h" +#import "FBSessionLoginStrategy.h" +#import "FBUtility.h" + +@implementation FBSessionInlineWebViewLoginStategy + +- (BOOL)tryPerformAuthorizeWithParams:(FBSessionLoginStrategyParams *)params session:(FBSession *)session logger:(FBSessionAuthLogger *)logger { + if (params.tryFallback) { + NSDictionary *clientState = @{FBSessionAuthLoggerParamAuthMethodKey: self.methodName, + FBSessionAuthLoggerParamIDKey : logger.ID ?: @""}; + params.webParams[FBLoginUXClientState] = [session jsonClientStateWithDictionary:clientState]; + [session authorizeUsingLoginDialog:params.webParams]; + return YES; + } + return NO; +} + +- (NSString *)methodName { + return FBSessionAuthLoggerAuthMethodFallback; +} + +@end \ No newline at end of file diff --git a/src/FBSessionLoginStrategy.h b/src/FBSessionLoginStrategy.h new file mode 100644 index 0000000000..1f8ac066c4 --- /dev/null +++ b/src/FBSessionLoginStrategy.h @@ -0,0 +1,65 @@ +/* + * Copyright 2010-present 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 "FacebookSDK.h" +#import "FBSessionAuthLogger.h" +#import "FBSessionLoginStrategyParams.h" +#import "FBSession+FBSessionLoginStrategy.h" + +// these are helpful macros for testing various login methods, should always checkin as NO/NO +#define TEST_DISABLE_MULTITASKING_LOGIN NO +#define TEST_DISABLE_FACEBOOKLOGIN NO +#define TEST_DISABLE_FACEBOOKNATIVELOGIN NO + +// Internal protocol that describes a type that can perform authorization. +// It's possible for a strategy to compose over other strategies. See `FBSessionAppSwitchingLoginStategy`. +@protocol FBSessionLoginStrategy + +@required + +/*! + @method + + @abstract Instructs the instance to attempt to perform the login. + + @param params A collection of parameters typically used to evalute if a given strategy should be invoked. + @param session The session instance. + @param logger The logger instance. + + @discussion + A return value of 'NO' indicates + another login strategy should be tried. A value of 'YES' means the strategy has handled the login flow + and no other strategies should be tried (note this does not necessarily mean the login was successful). +*/ +- (BOOL)tryPerformAuthorizeWithParams:(FBSessionLoginStrategyParams *)params session:(FBSession *)session logger:(FBSessionAuthLogger *)logger; + +/*! @ + abstract Gets the methodName describing this login strategy, typically for external logging. + @discussion This should only be invoked if a `tryPerformAuthorizeWithParams:...` call returned YES. +*/ +@property (readonly) NSString *methodName; + +@end + + + + + + + + + + diff --git a/src/FBSessionLoginStrategyParams.h b/src/FBSessionLoginStrategyParams.h new file mode 100644 index 0000000000..e4ad0a6123 --- /dev/null +++ b/src/FBSessionLoginStrategyParams.h @@ -0,0 +1,31 @@ +/* + * Copyright 2010-present 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 "FacebookSDK.h" + +@interface FBSessionLoginStrategyParams : NSObject + +@property BOOL tryIntegratedAuth; +@property BOOL tryFBAppAuth; +@property BOOL trySafariAuth; +@property BOOL tryFallback; +@property BOOL isReauthorize; +@property FBSessionDefaultAudience defaultAudience; +@property (retain, nonatomic) NSArray *permissions; +@property BOOL canFetchAppSettings; +@property (retain, nonatomic) NSMutableDictionary *webParams; + +@end diff --git a/src/FBSessionLoginStrategyParams.m b/src/FBSessionLoginStrategyParams.m new file mode 100644 index 0000000000..c61c7c6f2d --- /dev/null +++ b/src/FBSessionLoginStrategyParams.m @@ -0,0 +1,28 @@ +/* + * Copyright 2010-present 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 "FBSessionLoginStrategyParams.h" + +@implementation FBSessionLoginStrategyParams + +-(void) dealloc { + [_permissions release]; + [_webParams release]; + [super dealloc]; +} + +@end diff --git a/src/FBSessionSafariLoginStategy.h b/src/FBSessionSafariLoginStategy.h new file mode 100644 index 0000000000..e628bc249c --- /dev/null +++ b/src/FBSessionSafariLoginStategy.h @@ -0,0 +1,19 @@ +/* + * Copyright 2010-present 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 "FBSessionLoginStrategy.h" + +@interface FBSessionSafariLoginStategy : NSObject +@end \ No newline at end of file diff --git a/src/FBSessionSafariLoginStategy.m b/src/FBSessionSafariLoginStategy.m new file mode 100644 index 0000000000..b228126bdb --- /dev/null +++ b/src/FBSessionSafariLoginStategy.m @@ -0,0 +1,41 @@ +/* + * Copyright 2010-present 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 "FBLogger.h" +#import "FBSession+Internal.h" +#import "FBSessionAuthLogger.h" +#import "FBSessionLoginStrategy.h" +#import "FBSessionSafariLoginStategy.h" +#import "FBUtility.h" + +@implementation FBSessionSafariLoginStategy + +- (BOOL)tryPerformAuthorizeWithParams:(FBSessionLoginStrategyParams *)params session:(FBSession *)session logger:(FBSessionAuthLogger *)logger { + if (params.trySafariAuth) { + NSDictionary *clientState = @{FBSessionAuthLoggerParamAuthMethodKey: self.methodName, + FBSessionAuthLoggerParamIDKey : logger.ID ?: @""}; + params.webParams[FBLoginUXClientState] = [session jsonClientStateWithDictionary:clientState]; + return [session authorizeUsingSafari:params.webParams]; + } + return NO; +} + +- (NSString *)methodName { + return FBSessionAuthLoggerAuthMethodBrowser; +} + +@end + diff --git a/src/FBSessionSystemLoginStategy.h b/src/FBSessionSystemLoginStategy.h new file mode 100644 index 0000000000..5cf36be7d6 --- /dev/null +++ b/src/FBSessionSystemLoginStategy.h @@ -0,0 +1,20 @@ +/* + * Copyright 2010-present 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 "FBSessionLoginStrategy.h" + +@interface FBSessionSystemLoginStategy : NSObject +@end + diff --git a/src/FBSessionSystemLoginStategy.m b/src/FBSessionSystemLoginStategy.m new file mode 100644 index 0000000000..77abefbe3f --- /dev/null +++ b/src/FBSessionSystemLoginStategy.m @@ -0,0 +1,49 @@ +/* + * Copyright 2010-present 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 "FBLogger.h" +#import "FBSession+Internal.h" +#import "FBSessionAuthLogger.h" +#import "FBSessionLoginStrategy.h" +#import "FBSessionSystemLoginStategy.h" +#import "FBUtility.h" + +@implementation FBSessionSystemLoginStategy + +- (BOOL)tryPerformAuthorizeWithParams:(FBSessionLoginStrategyParams *)params session:(FBSession *)session logger:(FBSessionAuthLogger *)logger { + + BOOL systemAccountStoreAvailable = [FBUtility isSystemAccountStoreAvailable]; + [logger addExtrasForNextEvent:@{ + @"systemAccountStoreAvailable":@(systemAccountStoreAvailable) + }]; + + if (params.tryIntegratedAuth && + (!params.isReauthorize || session.accessTokenData.loginType == FBSessionLoginTypeSystemAccount) && + systemAccountStoreAvailable) { + + [session authorizeUsingSystemAccountStore:params.permissions + defaultAudience:params.defaultAudience + isReauthorize:params.isReauthorize]; + return YES; + } + return NO; +} + +- (NSString *)methodName { + return FBSessionAuthLoggerAuthMethodIntegrated; +} + +@end \ No newline at end of file diff --git a/src/FBSessionTokenCachingStrategy.h b/src/FBSessionTokenCachingStrategy.h index 128991f31a..8722bbccc1 100644 --- a/src/FBSessionTokenCachingStrategy.h +++ b/src/FBSessionTokenCachingStrategy.h @@ -98,6 +98,10 @@ @discussion This essentially wraps a call to `fetchTokenInformation` so you should override this when providing a custom token caching strategy. + + In order for an `FBSession` instance to be able to use a cached token, + the token must be not be expired (see `+isValidTokenInformation:`) and + must also contain all permissions in the initialized session instance. */ - (FBAccessTokenData *)fetchFBAccessTokenData; @@ -150,3 +154,6 @@ extern NSString *const FBTokenInformationLoginTypeLoginKey; // The key to use with token information dictionaries to get the latest known permissions extern NSString *const FBTokenInformationPermissionsKey; + +// The key to use with token information dictionaries to get the date the permissions were last refreshed. +extern NSString *const FBTokenInformationPermissionsRefreshDateKey; diff --git a/src/FBSessionTokenCachingStrategy.m b/src/FBSessionTokenCachingStrategy.m index fbdfc9b5c7..58a0d75a0a 100644 --- a/src/FBSessionTokenCachingStrategy.m +++ b/src/FBSessionTokenCachingStrategy.m @@ -27,6 +27,7 @@ NSString *const FBTokenInformationIsFacebookLoginKey = @"com.facebook.sdk:TokenInformationIsFacebookLoginKey"; NSString *const FBTokenInformationLoginTypeLoginKey = @"com.facebook.sdk:TokenInformationLoginTypeLoginKey"; NSString *const FBTokenInformationPermissionsKey = @"com.facebook.sdk:TokenInformationPermissionsKey"; +NSString *const FBTokenInformationPermissionsRefreshDateKey = @"com.facebook.sdk:TokenInformationPermissionsRefreshDateKey"; #pragma mark - private FBSessionTokenCachingStrategyNoOpInstance class diff --git a/src/FBSessionUtility.h b/src/FBSessionUtility.h new file mode 100644 index 0000000000..001c00aa3f --- /dev/null +++ b/src/FBSessionUtility.h @@ -0,0 +1,37 @@ +/* + * Copyright 2010-present 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 "FacebookSDK.h" + +// An internal only utility class for logic related +// to FBSession but are not directly related to a session instance. +@interface FBSessionUtility : NSObject + ++ (BOOL)isOpenSessionResponseURL:(NSURL *)url; ++ (NSDictionary *)clientStateFromQueryParams:(NSDictionary *)params; ++ (NSDictionary *)queryParamsFromLoginURL:(NSURL *)url + appID:(NSString *)appID + urlSchemeSuffix:(NSString *)urlSchemeSuffix; ++ (NSString *)sessionStateDescription:(FBSessionState)sessionState; ++ (void)addWebLoginStartTimeToParams:(NSMutableDictionary *)params; ++ (NSDate *)expirationDateFromResponseParams:(NSDictionary *)parameters; ++ (BOOL)areRequiredPermissions:(NSArray*)requiredPermissions + aSubsetOfPermissions:(NSArray*)cachedPermissions; ++ (void)validateRequestForPermissions:(NSArray*)permissions + defaultAudience:(FBSessionDefaultAudience)defaultAudience + allowSystemAccount:(BOOL)allowSystemAccount + isRead:(BOOL)isRead; +@end diff --git a/src/FBSessionUtility.m b/src/FBSessionUtility.m new file mode 100644 index 0000000000..60c38253ac --- /dev/null +++ b/src/FBSessionUtility.m @@ -0,0 +1,190 @@ +/* + * Copyright 2010-present 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 "FBLogger.h" +#import "FBSessionUtility.h" +#import "FBSession+Internal.h" +#import "FBUtility.h" + +@implementation FBSessionUtility + ++ (BOOL)isOpenSessionResponseURL:(NSURL *)url { + NSDictionary *params = [FBSessionUtility queryParamsFromLoginURL:url appID:nil urlSchemeSuffix:nil]; + NSDictionary *clientState = [FBSessionUtility clientStateFromQueryParams:params]; + if (!clientState) { + return NO; + } + + NSNumber *isOpenSessionBit = clientState[FBLoginUXClientStateIsOpenSession]; + return [isOpenSessionBit boolValue]; +} + ++ (NSDictionary *)queryParamsFromLoginURL:(NSURL *)url appID:(NSString *)appID urlSchemeSuffix:(NSString *)urlSchemeSuffix { + // if the URL's structure doesn't match the structure used for Facebook authorization, abort. + if (appID) { + NSString* expectedUrlPrefix = [FBUtility stringAppBaseUrlFromAppId:appID urlSchemeSuffix:urlSchemeSuffix]; + if (![[url absoluteString] hasPrefix:expectedUrlPrefix]) { + return nil; + } + } else { + // Don't have an App ID, just verify path. + NSString *host = url.host; + if (![host isEqualToString:@"authorize"]) { + return nil; + } + } + + return [FBUtility queryParamsDictionaryFromFBURL:url]; +} + ++ (NSDictionary *)clientStateFromQueryParams:(NSDictionary *)params { + NSDictionary *clientState = [FBUtility simpleJSONDecode:params[FBLoginUXClientState]]; + if (!clientState[FBLoginUXClientStateIsClientState]) { + return nil; + } + return clientState; +} + ++ (NSString *)sessionStateDescription:(FBSessionState)sessionState { + NSString *stateDescription = nil; + switch (sessionState) { + case FBSessionStateCreated: + stateDescription = @"FBSessionStateCreated"; + break; + case FBSessionStateCreatedTokenLoaded: + stateDescription = @"FBSessionStateCreatedTokenLoaded"; + break; + case FBSessionStateCreatedOpening: + stateDescription = @"FBSessionStateCreatedOpening"; + break; + case FBSessionStateOpen: + stateDescription = @"FBSessionStateOpen"; + break; + case FBSessionStateOpenTokenExtended: + stateDescription = @"FBSessionStateOpenTokenExtended"; + break; + case FBSessionStateClosedLoginFailed: + stateDescription = @"FBSessionStateClosedLoginFailed"; + break; + case FBSessionStateClosed: + stateDescription = @"FBSessionStateClosed"; + break; + default: + stateDescription = @"[Unknown]"; + break; + } + + return stateDescription; +} + ++ (void)addWebLoginStartTimeToParams:(NSMutableDictionary *)params { + NSNumber *timeValue = [NSNumber numberWithDouble:round(1000 * [[NSDate date] timeIntervalSince1970])]; + NSString *e2eTimestampString = [FBUtility simpleJSONEncode:@{@"init":timeValue}]; + [params setObject:e2eTimestampString forKey:@"e2e"]; +} + ++ (NSDate *)expirationDateFromResponseParams:(NSDictionary *)parameters { + NSString *expTime = [parameters objectForKey:@"expires_in"]; + NSDate *expirationDate = nil; + if (expTime) { + // If we have an interval, it is assumed to be since now. (e.g. 60 days) + expirationDate = [FBUtility expirationDateFromExpirationTimeIntervalString:expTime]; + } else { + // If we have an actual timestamp, create the date from that instead. + expirationDate = [FBUtility expirationDateFromExpirationUnixTimeString:parameters[@"expires"]]; + } + + if (!expirationDate) { + expirationDate = [NSDate distantFuture]; + } + + return expirationDate; +} + ++ (BOOL)areRequiredPermissions:(NSArray*)requiredPermissions + aSubsetOfPermissions:(NSArray*)cachedPermissions { + NSSet *required = [NSSet setWithArray:requiredPermissions]; + NSSet *cached = [NSSet setWithArray:cachedPermissions]; + return [required isSubsetOfSet:cached]; +} + + ++ (void)validateRequestForPermissions:(NSArray*)permissions + defaultAudience:(FBSessionDefaultAudience)defaultAudience + allowSystemAccount:(BOOL)allowSystemAccount + isRead:(BOOL)isRead { + // validate audience argument + if ([permissions count]) { + if (allowSystemAccount && !isRead) { + switch (defaultAudience) { + case FBSessionDefaultAudienceEveryone: + case FBSessionDefaultAudienceFriends: + case FBSessionDefaultAudienceOnlyMe: + break; + default: + [[NSException exceptionWithName:FBInvalidOperationException + reason:@"FBSession: Publish permissions were requested " + @"without specifying an audience; use FBSessionDefaultAudienceJustMe, " + @"FBSessionDefaultAudienceFriends, or FBSessionDefaultAudienceEveryone" + userInfo:nil] + raise]; + break; + } + } + // log unexpected permissions, and throw on read w/publish permissions + if (allowSystemAccount && + [FBSessionUtility logIfFoundUnexpectedPermissions:permissions isRead:isRead] && + isRead) { + [[NSException exceptionWithName:FBInvalidOperationException + reason:@"FBSession: Publish or manage permissions are not permitted to " + @"to be requested with read permissions." + userInfo:nil] + raise]; + } + } +} + ++ (BOOL)logIfFoundUnexpectedPermissions:(NSArray*)permissions + isRead:(BOOL)isRead { + BOOL publishPermissionFound = NO; + BOOL readPermissionFound = NO; + BOOL result = NO; + for (NSString *p in permissions) { + if ([FBUtility isPublishPermission:p]) { + publishPermissionFound = YES; + } else { + readPermissionFound = YES; + } + + // If we've found one of each we can stop looking. + if (publishPermissionFound && readPermissionFound) { + break; + } + } + + if (!isRead && readPermissionFound) { + [FBLogger singleShotLogEntry:FBLoggingBehaviorDeveloperErrors logEntry:@"FBSession: a permission request for publish or manage permissions contains unexpected read permissions"]; + result = YES; + } + if (isRead && publishPermissionFound) { + [FBLogger singleShotLogEntry:FBLoggingBehaviorDeveloperErrors logEntry:@"FBSession: a permission request for read permissions contains unexpected publish or manage permissions"]; + result = YES; + } + + return result; +} + +@end diff --git a/src/FBSettings.m b/src/FBSettings.m index 2c189cd70e..6ec5afe2b3 100644 --- a/src/FBSettings.m +++ b/src/FBSettings.m @@ -347,8 +347,8 @@ + (void)publishInstall:(NSString *)appID if (advertiserID) { [installActivity setObject:advertiserID forKey:@"advertiser_id"]; } - [FBUtility updateParametersWithEventUsageLimits:installActivity]; - + [FBUtility updateParametersWithEventUsageLimitsAndBundleInfo:installActivity]; + [installActivity setObject:[NSNumber numberWithBool:isAutoPublish].stringValue forKey:@"auto_publish"]; FBRequest *publishRequest = [[[FBRequest alloc] initForPostWithSession:nil graphPath:publishPath graphObject:installActivity] autorelease]; @@ -398,4 +398,4 @@ + (void)publishInstall:(NSString *)appID } #pragma GCC diagnostic pop -@end +@end \ No newline at end of file diff --git a/src/FBSystemAccountStoreAdapter.h b/src/FBSystemAccountStoreAdapter.h index 4f8eeebe84..100017b5cd 100644 --- a/src/FBSystemAccountStoreAdapter.h +++ b/src/FBSystemAccountStoreAdapter.h @@ -17,6 +17,8 @@ #import #import #import "FBSession+Internal.h" +#import "FBTask.h" +#import "FBTaskCompletionSource.h" typedef void (^FBRequestAccessToAccountsHandler)(NSString* oauthToken, NSError *accountStoreError); @@ -51,7 +53,15 @@ typedef void (^FBRequestAccessToAccountsHandler)(NSString* oauthToken, NSError * appID:(NSString *)appID session:(FBSession *)session handler:(FBRequestAccessToAccountsHandler)handler; +/*! + @abstract Same as `renewSystemAuthorization:` but represented as `FBTask`. +*/ +- (FBTask *)renewSystemAuthorizationAsTask; +/*! + @abstract Same as `requestAccessToFacebookAccountStore:handler:` but represented as `FBTask` +*/ +- (FBTask *)requestAccessToFacebookAccountStoreAsTask:(FBSession *)session; /* @abstract Sends a message to the device account store to renew the Facebook account credentials diff --git a/src/FBSystemAccountStoreAdapter.m b/src/FBSystemAccountStoreAdapter.m index 00fc53e932..c68f03e5f2 100644 --- a/src/FBSystemAccountStoreAdapter.m +++ b/src/FBSystemAccountStoreAdapter.m @@ -104,6 +104,19 @@ - (BOOL) canRequestAccessWithoutUI { #pragma mark - Public properties and methods +- (FBTask *)requestAccessToFacebookAccountStoreAsTask:(FBSession *)session { + FBTaskCompletionSource* tcs = [FBTaskCompletionSource taskCompletionSource]; + [self requestAccessToFacebookAccountStore:session handler:^(NSString *oauthToken, NSError *accountStoreError) { + if (accountStoreError) { + [tcs setError:accountStoreError]; + } else { + [tcs setResult:oauthToken]; + } + }]; + return tcs.task; +} + + - (void)requestAccessToFacebookAccountStore:(FBSession *)session handler:(FBRequestAccessToAccountsHandler)handler { return [self requestAccessToFacebookAccountStore:session.accessTokenData.permissions @@ -255,8 +268,8 @@ - (void)renewSystemAuthorization:(void( ^ )(ACAccountCredentialRenewResult, NSEr [self.accountStore renewCredentialsForAccount:account completion:^(ACAccountCredentialRenewResult renewResult, NSError *error) { if (error){ [FBLogger singleShotLogEntry:FBLoggingBehaviorAccessTokens - logEntry:[NSString stringWithFormat:@"renewCredentialsForAccount result:%d, error: %@", - renewResult, + logEntry:[NSString stringWithFormat:@"renewCredentialsForAccount result:%ld, error: %@", + (long)renewResult, error]]; } if (handler) { @@ -292,4 +305,15 @@ - (void)renewSystemAuthorization:(void( ^ )(ACAccountCredentialRenewResult, NSEr } } +- (FBTask *)renewSystemAuthorizationAsTask { + FBTaskCompletionSource* tcs = [FBTaskCompletionSource taskCompletionSource]; + [self renewSystemAuthorization:^(ACAccountCredentialRenewResult result, NSError *error) { + if (error) { + [tcs setError:error]; + } else { + [tcs setResult:result]; + } + }]; + return tcs.task; +} @end diff --git a/src/FBTask+Private.h b/src/FBTask+Private.h new file mode 100644 index 0000000000..b6bef7eed9 --- /dev/null +++ b/src/FBTask+Private.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 "FBTask.h" + +@interface FBTask (Private) + +/*! + Waits until this operation is completed, then returns its value. + This method is inefficient and consumes a thread resource while + its running. It should be avoided. This method logs an warning + message if it is used on the main thread. If this task is cancelled, + nil is returned. + */ +- (id)waitForResult:(NSError **)error; + +@end diff --git a/src/FBTask.h b/src/FBTask.h new file mode 100644 index 0000000000..112816a2a2 --- /dev/null +++ b/src/FBTask.h @@ -0,0 +1,123 @@ +/* + * 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 + +/*! + The consumer view of a Task. A FBTask has methods to + inspect the state of the task, and to add continuations to + be run once the task is complete. + */ +@interface FBTask : NSObject + +/*! + Creates a task that is already completed with the given result. + */ ++ (FBTask *)taskWithResult:(id)result; + +/*! + Creates a task that is already completed with the given error. + */ ++ (FBTask *)taskWithError:(NSError *)error; + +/*! + Creates a task that is already completed with the given exception. + */ ++ (FBTask *)taskWithException:(NSException *)exception; + +/*! + Creates a task that is already cancelled. + */ ++ (FBTask *)cancelledTask; + +/*! + Returns a task that will be completed (with result == nil) once + all of the input tasks have completed. + */ ++ (FBTask *)taskDependentOnTasks:(NSArray *)tasks; + +/*! + Returns a task that will be completed a certain amount of time in the future. + @param delay The amount of time to wait before the + task will be finished (with result == nil). + */ ++ (FBTask *)taskWithDelay:(dispatch_time_t)delay; + +// Properties that will be set on the task once it is completed. + +/*! + The result of a successful task. + */ +- (id)result; + +/*! + The error of a failed task. + */ +- (NSError *)error; + +/*! + The exception of a failed task. + */ +- (NSException *)exception; + +/*! + Whether this task has been cancelled. + */ +- (BOOL)isCancelled; + +/*! + Whether this task has completed. + */ +- (BOOL)isCompleted; + +/*! + Enqueues the given block to be run once this task is complete. + @param block The block to be run once this task is complete. + @returns A task that will be completed after block has run. + If block returns a FBTask, then the task returned from + this method will not be completed until that task is completed. + */ +- (FBTask *)dependentTaskWithBlock:(id(^)(FBTask *task))block; + +/*! + Identical to `dependentTaskWithBlock:`, except the block + is dispatched to the specified queue. + */ +- (FBTask *)dependentTaskWithBlock:(id(^)(FBTask *task))block queue:(dispatch_queue_t)queue; + +/*! + Identical to `dependentTaskWithBlock:`, except that the block is only run + if this task did not produce a cancellation, error, or exception. + If it did, then the failure will be propagated to the returned + task. + */ +- (FBTask *)completionTaskWithBlock:(id(^)(FBTask *task))block; + +/*! + Identical to `completionTaskWithBlock:`, except the block + is dispatched to the specified queue. +*/ +- (FBTask *)completionTaskWithQueue:(dispatch_queue_t)queue block:(id(^)(FBTask *task))block; + +/*! + Waits until this operation is completed. + This method is inefficient and consumes a thread resource while + it's running. It should be avoided. This method logs a warning + message if it is used on the main thread. + */ +- (void)waitUntilFinished; + +@end diff --git a/src/FBTask.m b/src/FBTask.m new file mode 100644 index 0000000000..7f7a2e55a9 --- /dev/null +++ b/src/FBTask.m @@ -0,0 +1,353 @@ +/* + * 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 "FBTask.h" + +#import + +#import "FBTaskCompletionSource.h" + +__attribute__ ((noinline)) void logOperationOnMainThread() { + NSLog(@"Warning: A long-running FBTask operation is being executed on the main thread. \n" + " Break on logOperationOnMainThread() to debug."); +} + +@interface FBTask () { + id _result; + NSError *_error; + NSException *_exception; + BOOL _cancelled; +} + +@property (nonatomic, retain, readwrite) NSObject *lock; +@property (nonatomic, retain, readwrite) NSCondition *condition; +@property (nonatomic, assign, readwrite) BOOL completed; +@property (nonatomic, retain, readwrite) NSMutableArray *callbacks; +@end + +@implementation FBTask + +- (id)init { + if ((self = [super init])) { + _lock = [[NSObject alloc] init]; + _condition = [[NSCondition alloc] init]; + _callbacks = [[NSMutableArray array] retain]; + } + return self; +} + +- (void)dealloc { + [_lock release]; + [_condition release]; + [_callbacks release]; + + [super dealloc]; +} + ++ (FBTask *)taskWithResult:(id)result { + FBTaskCompletionSource *tcs = [FBTaskCompletionSource taskCompletionSource]; + tcs.result = result; + return tcs.task; +} + ++ (FBTask *)taskWithError:(NSError *)error { + FBTaskCompletionSource *tcs = [FBTaskCompletionSource taskCompletionSource]; + tcs.error = error; + return tcs.task; +} + ++ (FBTask *)taskWithException:(NSException *)exception { + FBTaskCompletionSource *tcs = [FBTaskCompletionSource taskCompletionSource]; + tcs.exception = exception; + return tcs.task; +} + ++ (FBTask *)cancelledTask { + FBTaskCompletionSource *tcs = [FBTaskCompletionSource taskCompletionSource]; + [tcs cancel]; + return tcs.task; +} + ++ (FBTask *)taskDependentOnTasks:(NSArray *)tasks { + __block int32_t total = (int32_t)tasks.count; + if (total == 0) { + return [FBTask taskWithResult:nil]; + } + + FBTaskCompletionSource *tcs = [FBTaskCompletionSource taskCompletionSource]; + for (FBTask *task in tasks) { + [task dependentTaskWithBlock:^id(FBTask *task) { + if (OSAtomicDecrement32(&total) == 0) { + tcs.result = nil; + } + return nil; + }]; + } + return tcs.task; +} + ++ (FBTask *)taskWithDelay:(dispatch_time_t)delay { + FBTaskCompletionSource *tcs = [FBTaskCompletionSource taskCompletionSource]; + dispatch_after(delay, dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^(void){ + tcs.result = nil; + }); + return tcs.task; +} + +- (id)result { + @synchronized (self.lock) { + return _result; + } +} + +- (void)setResult:(id)result { + if (![self trySetResult:result]) { + [NSException raise:NSInternalInconsistencyException + format:@"Cannot set the result on a completed task."]; + } +} + +- (BOOL)trySetResult:(id)result { + @synchronized (self.lock) { + if (self.completed) { + return NO; + } + self.completed = YES; + _result = result; + [self runContinuations]; + return YES; + } +} + +- (NSError *)error { + @synchronized (self.lock) { + return _error; + } +} + +- (void)setError:(NSError *)error { + if (![self trySetError:error]) { + [NSException raise:NSInternalInconsistencyException + format:@"Cannot set the error on a completed task."]; + } +} + +- (BOOL)trySetError:(NSError *)error { + @synchronized (self.lock) { + if (self.completed) { + return NO; + } + self.completed = YES; + _error = error; + [self runContinuations]; + return YES; + } +} + +- (NSException *)exception { + @synchronized (self.lock) { + return _exception; + } +} + +- (void)setException:(NSException *)exception { + if (![self trySetException:exception]) { + [NSException raise:NSInternalInconsistencyException + format:@"Cannot set the exception on a completed task."]; + } +} + +- (BOOL)trySetException:(NSException *)exception { + @synchronized (self.lock) { + if (self.completed) { + return NO; + } + self.completed = YES; + _exception = exception; + [self runContinuations]; + return YES; + } +} + +- (BOOL)isCancelled { + @synchronized (self.lock) { + return _cancelled; + } +} + +- (void)cancel { + @synchronized (self.lock) { + if (![self trySetCancelled]) { + [NSException raise:NSInternalInconsistencyException + format:@"Cannot cancel a completed task."]; + } + } +} + +- (BOOL)trySetCancelled { + @synchronized (self.lock) { + if (self.completed) { + return NO; + } + self.completed = YES; + _cancelled = YES; + [self runContinuations]; + return YES; + } +} + +- (BOOL)isCompleted { + @synchronized (self.lock) { + return _completed; + } +} + +- (void)setCompleted { + @synchronized (self.lock) { + _completed = YES; + } +} + +- (void)runContinuations { + @synchronized (self.lock) { + [self.condition lock]; + [self.condition broadcast]; + [self.condition unlock]; + for (void (^callback)() in self.callbacks) { + callback(); + } + [self.callbacks removeAllObjects]; + } +} + +- (FBTask *)dependentTaskWithBlock:(id(^)(FBTask *task))block { + return [self dependentTaskWithBlock:block queue:nil]; +} + +- (FBTask *)dependentTaskWithBlock:(id(^)(FBTask *task))block queue:(dispatch_queue_t)queue { + block = [[block copy] autorelease]; + + FBTaskCompletionSource *tcs = [FBTaskCompletionSource taskCompletionSource]; + queue = queue ?: dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0); + + // Capture all of the state that needs to used when the continuation is complete. + void (^wrappedBlock)() = ^() { + // Always dispatching callbacks async consumes less stack space, and seems + // to be a little faster, but loses stacktrace information. If you're debugging, + // consider commenting this line out and running the block synchronously. + dispatch_async(queue, ^{ + id result = nil; + @try { + result = block(self); + } @catch (NSException *exception) { + tcs.exception = exception; + return; + } + + if ([result isKindOfClass:[FBTask class]]) { + [(FBTask *)result dependentTaskWithBlock:^id(FBTask *task) { + if (task.isCancelled) { + [tcs cancel]; + } else if (task.exception) { + tcs.exception = task.exception; + } else if (task.error) { + tcs.error = task.error; + } else { + tcs.result = task.result; + } + return nil; + } queue:queue]; + } else { + tcs.result = result; + } + }); + }; + + BOOL completed; + @synchronized (self.lock) { + completed = self.completed; + if (!completed) { + [self.callbacks addObject:[[wrappedBlock copy] autorelease]]; + } + } + if (completed) { + wrappedBlock(); + } + + return tcs.task; +} + +- (FBTask *)completionTaskWithBlock:(id(^)(FBTask *task))block { + block = [block copy]; + return [self dependentTaskWithBlock:^id(FBTask *task) { + if (task.error || task.exception || task.isCancelled) { + return task; + } else { + return [task dependentTaskWithBlock:block]; + } + }]; +} + +- (FBTask *)completionTaskWithQueue:(dispatch_queue_t)queue block:(id(^)(FBTask *task))block { + block = [block copy]; + return [self dependentTaskWithBlock:^id(FBTask *task) { + if (task.error || task.exception || task.isCancelled) { + return task; + } else { + return [task dependentTaskWithBlock:block queue:queue]; + } + }]; +} + +- (void)warnOperationOnMainThread { + logOperationOnMainThread(); +} + +// A no-op version to be swizzled in for tests. +- (void)warnOperationOnMainThreadNoOp { +} + +// Private methods. + +- (void)waitUntilFinished { + if ([NSThread isMainThread]) { + [self warnOperationOnMainThread]; + } + + @synchronized (self.lock) { + if (self.isCompleted) { + return; + } + [self.condition lock]; + } + [self.condition wait]; + [self.condition unlock]; +} + +- (id)waitForResult:(NSError **)error { + [self waitUntilFinished]; + if (self.isCancelled) { + return nil; + } else if (self.exception) { + @throw self.exception; + } + if (self.error && error) { + *error = self.error; + } + return self.result; +} + +@end diff --git a/src/FBTaskCompletionSource.h b/src/FBTaskCompletionSource.h new file mode 100644 index 0000000000..55bc709c64 --- /dev/null +++ b/src/FBTaskCompletionSource.h @@ -0,0 +1,85 @@ +/* + * 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 + +@class FBTask; + +/*! + A FBTaskCompletionSource represents the producer side of tasks. + It is a task that also has methods for changing the state of the + task by settings its completion values. + */ +@interface FBTaskCompletionSource : NSObject + +/*! + Creates a new unfinished task. + */ ++ (FBTaskCompletionSource *)taskCompletionSource; + +/*! + The task associated with this TaskCompletionSource. + */ +@property (nonatomic, retain, readonly) FBTask *task; + +/*! + Completes the task by setting the result. + Attempting to set this for a completed task will raise an exception. + */ +- (void)setResult:(id)result; + +/*! + Completes the task by setting the error. + Attempting to set this for a completed task will raise an exception. + */ +- (void)setError:(NSError *)error; + +/*! + Completes the task by setting an exception. + Attempting to set this for a completed task will raise an exception. + */ +- (void)setException:(NSException *)exception; + +/*! + Completes the task by marking it as cancelled. + Attempting to set this for a completed task will raise an exception. + */ +- (void)cancel; + +/*! + Sets the result of the task if it wasn't already completed. + @returns whether the new value was set. + */ +- (BOOL)trySetResult:(id)result; + +/*! + Sets the error of the task if it wasn't already completed. + @returns whether the new value was set. + */ +- (BOOL)trySetError:(NSError *)error; + +/*! + Sets the exception of the task if it wasn't already completed. + @returns whether the new value was set. + */ +- (BOOL)trySetException:(NSException *)exception; + +/*! + Sets the cancellation state of the task if it wasn't already completed. + @returns whether the new value was set. + */ +- (BOOL)trySetCancelled; + +@end diff --git a/src/FBTaskCompletionSource.m b/src/FBTaskCompletionSource.m new file mode 100644 index 0000000000..509fdd3d0f --- /dev/null +++ b/src/FBTaskCompletionSource.m @@ -0,0 +1,87 @@ +/* + * 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 "FBTaskCompletionSource.h" + +#import "FBTask.h" + +@interface FBTaskCompletionSource () +@property (nonatomic, retain, readwrite) FBTask *task; +@end + +@interface FBTask (FBTaskCompletionSource) +- (void)setResult:(id)result; +- (void)setError:(NSError *)error; +- (void)setException:(NSException *)exception; +- (void)cancel; +- (BOOL)trySetResult:(id)result; +- (BOOL)trySetError:(NSError *)error; +- (BOOL)trySetException:(NSException *)exception; +- (BOOL)trySetCancelled; +@end + +@implementation FBTaskCompletionSource + ++ (FBTaskCompletionSource *)taskCompletionSource { + return [[[FBTaskCompletionSource alloc] init] autorelease]; +} + +- (id)init { + if ((self = [super init])) { + _task = [[FBTask alloc] init]; + } + return self; +} + +- (void)dealloc { + [_task release]; + + [super dealloc]; +} + +- (void)setResult:(id)result { + [self.task setResult:result]; +} + +- (void)setError:(NSError *)error { + [self.task setError:error]; +} + +- (void)setException:(NSException *)exception { + [self.task setException:exception]; +} + +- (void)cancel { + [self.task cancel]; +} + +- (BOOL)trySetResult:(id)result { + return [self.task trySetResult:result]; +} + +- (BOOL)trySetError:(NSError *)error { + return [self.task trySetError:error]; +} + +- (BOOL)trySetException:(NSException *)exception { + return [self.task trySetException:exception]; +} + +- (BOOL)trySetCancelled { + return [self.task trySetCancelled]; +} + +@end diff --git a/src/FBTestSession.m b/src/FBTestSession.m index 67a59527e2..090a4bf830 100644 --- a/src/FBTestSession.m +++ b/src/FBTestSession.m @@ -226,22 +226,23 @@ - (void)createNewTestUser // state transition, and call the handler if there is one [self transitionAndCallHandlerWithState:FBSessionStateClosedLoginFailed error:error - token:nil - expirationDate:nil - shouldCache:NO - loginType:FBSessionLoginTypeNone]; + tokenData:nil + shouldCache:NO]; } }]; } - (void)transitionToOpenWithToken:(NSString*)token { + FBAccessTokenData *tokenData = [FBAccessTokenData createTokenFromString:token + permissions:nil + expirationDate:[NSDate distantFuture] + loginType:FBSessionLoginTypeTestUser + refreshDate:[NSDate date]]; [self transitionAndCallHandlerWithState:FBSessionStateOpen error:nil - token:token - expirationDate:[NSDate distantFuture] - shouldCache:NO - loginType:FBSessionLoginTypeTestUser]; + tokenData:tokenData + shouldCache:NO]; } // We raise exceptions when things go wrong here, because this is intended for use only @@ -344,7 +345,7 @@ - (void)retrieveTestUsersForApp // we can use as part of a Facebook test user name (i.e., no digits). - (NSString*)validNameStringFromInteger:(NSUInteger)input { - NSString *hashAsString = [NSString stringWithFormat:@"%u", input]; + NSString *hashAsString = [NSString stringWithFormat:@"%lu", (unsigned long)input]; NSMutableString *result = [NSMutableString stringWithString:@"Perm"]; // We know each character is a digit. Convert it into a letter starting with 'a'. @@ -408,18 +409,14 @@ - (BOOL)forceAccessTokenRefresh { #pragma mark Overrides - (BOOL)transitionToState:(FBSessionState)state - andUpdateToken:(NSString*)token - andExpirationDate:(NSDate*)date - shouldCache:(BOOL)shouldCache - loginType:(FBSessionLoginType)loginType { + withAccessTokenData:(FBAccessTokenData *)tokenData + shouldCache:(BOOL)shouldCache { // in case we need these after the transition NSString *userID = self.testUserID; BOOL didTransition = [super transitionToState:state - andUpdateToken:token - andExpirationDate:date - shouldCache:shouldCache - loginType:loginType]; + withAccessTokenData:tokenData + shouldCache:shouldCache]; if (didTransition && FB_ISSESSIONSTATETERMINAL(self.state)) { if (self.mode == FBTestSessionModePrivate) { diff --git a/src/FBURLConnection.m b/src/FBURLConnection.m index 944c3b20f6..b628c145a2 100644 --- a/src/FBURLConnection.m +++ b/src/FBURLConnection.m @@ -98,8 +98,8 @@ - (FBURLConnection *)initWithRequest:(NSURLRequest *)request delegate:self]; _data = [[NSMutableData alloc] init]; - [self logMessage:[NSString stringWithFormat:@"FBURLConnection <#%d>:\n URL: '%@'\n\n", - self.loggerSerialNumber, + [self logMessage:[NSString stringWithFormat:@"FBURLConnection <#%lu>:\n URL: '%@'\n\n", + (unsigned long)self.loggerSerialNumber, url.absoluteString]]; self.handler = handler; @@ -116,8 +116,8 @@ - (void)logAndInvokeHandler:(FBURLConnectionHandler)handler error:(NSError *)error { if (error) { NSString *logEntry = [NSString - stringWithFormat:@"FBURLConnection <#%d>:\n Error: '%@'\n%@\n", - self.loggerSerialNumber, + stringWithFormat:@"FBURLConnection <#%lu>:\n Error: '%@'\n%@\n", + (unsigned long)self.loggerSerialNumber, [error localizedDescription], [error userInfo]]; @@ -132,10 +132,10 @@ - (void)logAndInvokeHandler:(FBURLConnectionHandler)handler responseData:(NSData *)responseData { // Basic FBURLConnection logging just prints out the URL. FBRequest logging provides more details. NSString *mimeType = [response MIMEType]; - NSMutableString *mutableLogEntry = [NSMutableString stringWithFormat:@"FBURLConnection <#%d>:\n Duration: %lu msec\nResponse Size: %d kB\n MIME type: %@\n", - self.loggerSerialNumber, + NSMutableString *mutableLogEntry = [NSMutableString stringWithFormat:@"FBURLConnection <#%lu>:\n Duration: %lu msec\nResponse Size: %lu kB\n MIME type: %@\n", + (unsigned long)self.loggerSerialNumber, [FBUtility currentTimeInMilliseconds] - self.requestStartTime, - [responseData length] / 1024, + (unsigned long)[responseData length] / 1024, mimeType]; if ([mimeType isEqualToString:@"text/javascript"]) { @@ -152,9 +152,9 @@ - (void)logAndInvokeHandler:(FBURLConnectionHandler)handler - (void)logAndInvokeHandler:(FBURLConnectionHandler)handler cachedData:(NSData *)cachedData forURL:(NSURL *)url { - [self logMessage:[NSString stringWithFormat:@"FBUrlConnection: <#%d>. Cached response %d kB\n", - self.loggerSerialNumber, - cachedData.length / 1024]]; + [self logMessage:[NSString stringWithFormat:@"FBUrlConnection: <#%lu>. Cached response %lu kB\n", + (unsigned long)self.loggerSerialNumber, + (unsigned long)cachedData.length / 1024]]; [self invokeHandler:handler error:nil response:nil responseData:cachedData]; } diff --git a/src/FBUserSettingsViewController.m b/src/FBUserSettingsViewController.m index e43efdc096..6f897fb098 100644 --- a/src/FBUserSettingsViewController.m +++ b/src/FBUserSettingsViewController.m @@ -140,7 +140,7 @@ - (void)viewDidLoad { [super viewDidLoad]; // If we are not being presented modally, we don't need a Done button. - if (self.compatiblePresentingViewController == nil) { + if (self.presentingViewController == nil) { self.doneButton = nil; } @@ -191,7 +191,11 @@ - (void)viewDidLoad { containerView.frame.size.width, 20); self.connectedStateLabel.backgroundColor = [UIColor clearColor]; +#ifdef __IPHONE_6_0 + self.connectedStateLabel.textAlignment = NSTextAlignmentCenter; +#else self.connectedStateLabel.textAlignment = UITextAlignmentCenter; +#endif self.connectedStateLabel.numberOfLines = 0; self.connectedStateLabel.font = [UIFont boldSystemFontOfSize:16.0]; self.connectedStateLabel.shadowColor = [UIColor blackColor]; @@ -259,14 +263,16 @@ - (void)willRotateToInterfaceOrientation:(UIInterfaceOrientation)toInterfaceOrie [self updateBackgroundImage]; } -- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation { - return (UI_USER_INTERFACE_IDIOM() == UIUserInterfaceIdiomPad) || UIInterfaceOrientationIsPortrait(interfaceOrientation); +- (NSUInteger)supportedInterfaceOrientations { + if (UI_USER_INTERFACE_IDIOM() == UIUserInterfaceIdiomPad) { + return UIInterfaceOrientationMaskAll; + } else { + return UIInterfaceOrientationMaskPortrait; + } } - (BOOL)shouldAutorotate { - UIInterfaceOrientation orientation = [[UIDevice currentDevice] orientation]; - - return [self shouldAutorotateToInterfaceOrientation:orientation]; + return YES; } - (void)viewWillAppear:(BOOL)animated { diff --git a/src/FBUtility.h b/src/FBUtility.h index dc9ad8df87..5f86497448 100644 --- a/src/FBUtility.h +++ b/src/FBUtility.h @@ -65,7 +65,7 @@ typedef enum FBAdvertisingTrackingStatus { + (NSString *)attributionID; + (NSString *)advertiserID; + (FBAdvertisingTrackingStatus)advertisingTrackingStatus; -+ (void)updateParametersWithEventUsageLimits:(NSMutableDictionary *)parameters; ++ (void)updateParametersWithEventUsageLimitsAndBundleInfo:(NSMutableDictionary *)parameters; // Encode a data structure in JSON, any errors will just be logged. + (NSString *)simpleJSONEncode:(id)data; @@ -82,6 +82,10 @@ typedef enum FBAdvertisingTrackingStatus { + (NSString *) buildFacebookUrlWithPre:(NSString*)pre; + (NSString *) buildFacebookUrlWithPre:(NSString*)pre withPost:(NSString *)post; ++ (BOOL)isMultitaskingSupported; ++ (BOOL)isSystemAccountStoreAvailable; ++ (void)deleteFacebookCookies; ++ (NSString *)dialogBaseURL; @end diff --git a/src/FBUtility.m b/src/FBUtility.m index 0e5780282c..be09d43816 100644 --- a/src/FBUtility.m +++ b/src/FBUtility.m @@ -352,17 +352,51 @@ + (FBAdvertisingTrackingStatus)advertisingTrackingStatus { return status; } -+ (void)updateParametersWithEventUsageLimits:(NSMutableDictionary *)parameters { ++ (void)updateParametersWithEventUsageLimitsAndBundleInfo:(NSMutableDictionary *)parameters { // Only add the iOS global value if we have a definitive allowed/disallowed on advertising tracking. Otherwise, // absence of this parameter is to be interpreted as 'unspecified'. FBAdvertisingTrackingStatus advertisingTrackingStatus = [FBUtility advertisingTrackingStatus]; if (advertisingTrackingStatus != AdvertisingTrackingUnspecified) { - BOOL allowed = (advertisingTrackingStatus == AdvertisingTrackingAllowed); - [parameters setObject:[[NSNumber numberWithBool:allowed] stringValue] - forKey:@"advertiser_tracking_enabled"]; + BOOL allowed = (advertisingTrackingStatus == AdvertisingTrackingAllowed); + [parameters setObject:[[NSNumber numberWithBool:allowed] stringValue] + forKey:@"advertiser_tracking_enabled"]; } [parameters setObject:[[NSNumber numberWithBool:!FBAppEvents.limitEventUsage] stringValue] forKey:@"application_tracking_enabled"]; + + static dispatch_once_t fetchBundleOnce; + static NSString *bundleIdentifier; + static NSMutableArray *urlSchemes; + static NSString *longVersion; + static NSString *shortVersion; + + dispatch_once(&fetchBundleOnce, ^{ + NSBundle *mainBundle = [NSBundle mainBundle]; + urlSchemes = [[NSMutableArray alloc] init]; + for (NSDictionary *fields in [mainBundle objectForInfoDictionaryKey:@"CFBundleURLTypes"]) { + NSArray *schemesForType = [fields objectForKey:@"CFBundleURLSchemes"]; + if (schemesForType) { + [urlSchemes addObjectsFromArray:schemesForType]; + } + } + bundleIdentifier = mainBundle.bundleIdentifier; + longVersion = [mainBundle objectForInfoDictionaryKey:@"CFBundleVersion"]; + shortVersion = [mainBundle objectForInfoDictionaryKey:@"CFBundleShortVersionString"]; + }); + + if (bundleIdentifier.length > 0) { + [parameters setObject:bundleIdentifier forKey:@"bundle_id"]; + } + if (urlSchemes.count > 0) { + [parameters setObject:[FBUtility simpleJSONEncode:urlSchemes] forKey:@"url_schemes"]; + } + if (longVersion.length > 0) { + [parameters setObject:longVersion forKey:@"bundle_version"]; + } + if (shortVersion.length > 0) { + [parameters setObject:shortVersion forKey:@"bundle_short_version"]; + } + } + (NSString *)simpleJSONEncode:(id)data { @@ -458,4 +492,30 @@ + (NSString *)buildFacebookUrlWithPre:(NSString *)pre return [NSString stringWithFormat:@"%@%@%@", pre, domain, post ?: @""]; } ++ (BOOL)isMultitaskingSupported { + return [[UIDevice currentDevice] respondsToSelector:@selector(isMultitaskingSupported)] && + [[UIDevice currentDevice] isMultitaskingSupported]; +} + ++ (BOOL)isSystemAccountStoreAvailable { + id accountStore = nil; + id accountTypeFB = nil; + + return (accountStore = [[[NSClassFromString(@"ACAccountStore") alloc] init] autorelease]) && + (accountTypeFB = [accountStore accountTypeWithAccountTypeIdentifier:@"com.apple.facebook"]); +} + ++ (void)deleteFacebookCookies { + NSHTTPCookieStorage* cookies = [NSHTTPCookieStorage sharedHTTPCookieStorage]; + NSArray* facebookCookies = [cookies cookiesForURL: + [NSURL URLWithString:[FBUtility dialogBaseURL]]]; + + for (NSHTTPCookie* cookie in facebookCookies) { + [cookies deleteCookie:cookie]; + } +} + ++ (NSString *)dialogBaseURL { + return [FBUtility buildFacebookUrlWithPre:@"https://m." withPost:@"/dialog/"]; +} @end diff --git a/src/FBViewController+Internal.h b/src/FBViewController+Internal.h index 4c2b4d7bb7..15845e7d91 100644 --- a/src/FBViewController+Internal.h +++ b/src/FBViewController+Internal.h @@ -18,7 +18,5 @@ @interface FBViewController (Internal) -@property (nonatomic, readonly) UIViewController *compatiblePresentingViewController; - @end diff --git a/src/FBViewController.m b/src/FBViewController.m index 5827e485e9..845453f9d1 100644 --- a/src/FBViewController.m +++ b/src/FBViewController.m @@ -4,9 +4,9 @@ * 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. @@ -21,9 +21,8 @@ @interface FBViewController () -@property (nonatomic, retain) UIToolbar *toolbar; +@property (nonatomic, retain) UINavigationBar *navigationBar; @property (nonatomic, retain) UIView *canvasView; -@property (nonatomic, retain) UIBarButtonItem *titleLabel; @property (nonatomic, copy) FBModalCompletionHandler handler; @property (nonatomic) BOOL autoDismiss; @property (nonatomic) BOOL dismissAnimated; @@ -41,16 +40,15 @@ @implementation FBViewController @synthesize cancelButton = _cancelButton; @synthesize doneButton = _doneButton; @synthesize delegate = _delegate; -@synthesize toolbar = _toolbar; +@synthesize navigationBar = _navigationBar; @synthesize canvasView = _canvasView; -@synthesize titleLabel = _titleLabel; @synthesize handler = _handler; @synthesize autoDismiss = _autoDismiss; @synthesize dismissAnimated = _dismissAnimated; #pragma mark View controller lifecycle -- (void) commonInit { +- (void)commonInit { // We do this at init-time rather than in viewDidLoad so the caller can change the buttons if // they want prior to the view loading. self.cancelButton = [[[UIBarButtonItem alloc] initWithBarButtonSystemItem:UIBarButtonSystemItemCancel @@ -61,7 +59,20 @@ - (void) commonInit { target:self action:@selector(doneButtonPressed:)] autorelease]; - +#ifdef __IPHONE_7_0 +#ifdef __IPHONE_OS_VERSION_MAX_ALLOWED +#if __IPHONE_OS_VERSION_MAX_ALLOWED >= __IPHONE_7_0 + if (self.parentViewController == nil) { + UIApplication *application = [UIApplication sharedApplication]; + if (!application.statusBarHidden) { + if ([self respondsToSelector:@selector(setEdgesForExtendedLayout:)]) { + self.edgesForExtendedLayout = UIRectEdgeNone; + } + } + } +#endif +#endif +#endif } - (id)init { @@ -88,12 +99,11 @@ - (id)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil - (void)dealloc { [super dealloc]; - + [_cancelButton release]; [_doneButton release]; - [_toolbar release]; + [_navigationBar release]; [_canvasView release]; - [_titleLabel release]; [_handler release]; } @@ -101,23 +111,23 @@ - (void)dealloc { - (void)viewDidLoad { [super viewDidLoad]; - + self.view.autoresizesSubviews = YES; - + self.canvasView = [[[UIView alloc] init] autorelease]; [self.canvasView setAutoresizingMask:UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight]; - self.canvasView.frame = self.view.bounds; + self.canvasView.frame = [self contentBounds]; [self.view addSubview:self.canvasView]; [self.view sendSubviewToBack:self.canvasView]; - + self.autoDismiss = NO; self.doneButton.target = self; self.doneButton.action = @selector(doneButtonPressed:); self.cancelButton.target = self; self.cancelButton.action = @selector(cancelButtonPressed:); - + [self updateBar]; } @@ -141,21 +151,41 @@ - (void)presentModallyFromViewController:(UIViewController*)viewController self.handler = handler; // Assumption: we want to dismiss with the same animated-ness as we present. self.dismissAnimated = animated; - - if ([viewController respondsToSelector:@selector(presentViewController:animated:completion:)]) { - [viewController presentViewController:self animated:animated completion:nil]; - } else { - [viewController presentModalViewController:self animated:animated]; - } - + + [viewController presentViewController:self animated:animated completion:nil]; + // Set this here because we always revert to NO in viewDidLoad. self.autoDismiss = YES; } #pragma mark Implementation +- (CGRect)contentBounds +{ + CGRect bounds = self.view.bounds; +#ifdef __IPHONE_7_0 +#ifdef __IPHONE_OS_VERSION_MAX_ALLOWED +#if __IPHONE_OS_VERSION_MAX_ALLOWED >= __IPHONE_7_0 + if (self.parentViewController == nil) { + UIApplication *application = [UIApplication sharedApplication]; + if (!application.statusBarHidden) { + if ([self respondsToSelector:@selector(edgesForExtendedLayout)]) { + if ((self.edgesForExtendedLayout & UIRectEdgeTop) == 0) { + CGFloat offset = CGRectGetMaxY(application.statusBarFrame); + bounds.origin.y += offset; + bounds.size.height -= offset; + } + } + } + } +#endif +#endif +#endif + return bounds; +} + - (void)updateBar { - if (self.compatiblePresentingViewController != nil) { + if (self.presentingViewController != nil) { [self updateBarForPresentedMode]; } else if (self.navigationController != nil) { [self updateBarForNavigationMode]; @@ -166,102 +196,51 @@ - (void)updateBarForPresentedMode { BOOL needBar = (self.doneButton != nil) || (self.cancelButton != nil); if (needBar) { // If we need a bar but don't have one, create it. - if (self.toolbar == nil) { - self.toolbar = [[[UIToolbar alloc] init] autorelease]; - self.toolbar.barStyle = UIBarStyleDefault; - - [self.toolbar setAutoresizingMask:UIViewAutoresizingFlexibleWidth]; - - [self.view addSubview:self.toolbar]; + if (self.navigationBar == nil) { + self.navigationBar = [[[UINavigationBar alloc] init] autorelease]; + self.navigationBar.barStyle = UIBarStyleDefault; + + [self.navigationBar setAutoresizingMask:UIViewAutoresizingFlexibleWidth]; + + [self.view addSubview:self.navigationBar]; } } else { // If we have a bar but don't need one, get rid of it. - if (self.toolbar != nil) { - [self.toolbar removeFromSuperview]; - self.toolbar = nil; - - self.canvasView.frame = self.view.bounds; + if (self.navigationBar != nil) { + [self.navigationBar removeFromSuperview]; + self.navigationBar = nil; + + self.canvasView.frame = [self contentBounds]; } return; } - - NSMutableArray *buttons = [NSMutableArray array]; + + UINavigationItem *navigationItem = [[[UINavigationItem alloc] initWithTitle:nil] autorelease]; + if (self.cancelButton != nil) { - [buttons addObject:self.cancelButton]; - } else { - // No cancel button, but if we have a done and a title, add some space at the beginning to help center the title. - if (self.doneButton != nil && self.title.length > 0) { - UIBarButtonItem *space = [[[UIBarButtonItem alloc] initWithBarButtonSystemItem:UIBarButtonSystemItemFlexibleSpace - target:nil - action:nil] - autorelease]; - [buttons addObject:space]; - } + navigationItem.leftBarButtonItem = self.cancelButton; } if (self.title.length > 0) { - UIBarButtonItem *space = [[[UIBarButtonItem alloc] initWithBarButtonSystemItem:UIBarButtonSystemItemFlexibleSpace - target:nil - action:nil] - autorelease]; - [buttons addObject:space]; - - if (self.titleLabel == nil) { - UILabel *label = [[[UILabel alloc] init] autorelease]; - label.font = [UIFont fontWithName:@"Helvetica-Bold" size:20]; - label.backgroundColor = [UIColor clearColor]; - label.textColor = [UIColor colorWithRed:255.0/255.0 green:255.0/255.0 blue:255.0/255.0 alpha:1.0]; - label.textAlignment = UITextAlignmentCenter; - label.shadowColor = [UIColor colorWithWhite:0.0 alpha:0.5]; - - self.titleLabel = [[[UIBarButtonItem alloc] initWithCustomView:label] autorelease]; - } - [(UILabel*)self.titleLabel.customView setText:self.title]; - [self.titleLabel.customView sizeToFit]; - - [buttons addObject:self.titleLabel]; - - space = [[[UIBarButtonItem alloc] initWithBarButtonSystemItem:UIBarButtonSystemItemFlexibleSpace - target:nil - action:nil] - autorelease]; - [buttons addObject:space]; - + navigationItem.title = self.title; } if (self.doneButton != nil) { - // If no title, we need a space to right-align - if (self.title.length == 0) { - UIBarButtonItem *space = [[[UIBarButtonItem alloc] initWithBarButtonSystemItem:UIBarButtonSystemItemFlexibleSpace - target:nil - action:nil] - autorelease]; - [buttons addObject:space]; - } - [buttons addObject:self.doneButton]; - } else { - // No done button, but if we have a cancel and a title, add some space at the end to help center the title. - if (self.cancelButton != nil && self.title.length > 0) { - UIBarButtonItem *space = [[[UIBarButtonItem alloc] initWithBarButtonSystemItem:UIBarButtonSystemItemFlexibleSpace - target:nil - action:nil] - autorelease]; - [buttons addObject:space]; - } + navigationItem.rightBarButtonItem = self.doneButton; } - [self.toolbar sizeToFit]; - CGRect bounds = self.toolbar.bounds; - bounds = CGRectMake(0, 0, self.view.bounds.size.width, bounds.size.height); - self.toolbar.bounds = bounds; + [self.navigationBar sizeToFit]; + CGRect contentBounds = [self contentBounds]; + CGRect frame = CGRectMake(0, 0, CGRectGetWidth(contentBounds), CGRectGetMinY(contentBounds) + CGRectGetHeight(self.navigationBar.bounds)); + self.navigationBar.frame = frame; - // Make the canvas shorter to account for the toolbar. - bounds = self.view.bounds; - CGFloat toolbarHeight = self.toolbar.bounds.size.height; - bounds.origin.y += toolbarHeight; - bounds.size.height -= toolbarHeight; - self.canvasView.frame = bounds; + // Make the canvas shorter to account for the navigationBar. + frame = contentBounds; + CGFloat navigationBarHeight = self.navigationBar.bounds.size.height; + frame.origin.y = navigationBarHeight; + frame.size.height -= navigationBarHeight; + self.canvasView.frame = frame; - self.toolbar.items = buttons; + self.navigationBar.items = @[navigationItem]; } - (void)updateBarForNavigationMode { @@ -289,33 +268,17 @@ - (void)setTitle:(NSString *)title { [self updateBar]; } -- (UIViewController *)compatiblePresentingViewController { - if ([self respondsToSelector:@selector(presentingViewController)]) { - return [self presentingViewController]; - } else { - UIViewController *parentViewController = [self parentViewController]; - if (self == [parentViewController modalViewController]) { - return parentViewController; - } - } - return nil; -} - #pragma mark Handlers - (void)cancelButtonPressed:(id)sender { if ([self.delegate respondsToSelector:@selector(facebookViewControllerCancelWasPressed:)]) { [self.delegate facebookViewControllerCancelWasPressed:self]; } - - UIViewController *presentingViewController = [self compatiblePresentingViewController]; + + UIViewController *presentingViewController = [self presentingViewController]; if (self.autoDismiss && presentingViewController) { - if ([presentingViewController respondsToSelector:@selector(dismissViewControllerAnimated:completion:)]) { - [presentingViewController dismissViewControllerAnimated:self.dismissAnimated completion:nil]; - } else { - [presentingViewController dismissModalViewControllerAnimated:self.dismissAnimated]; - } - + [presentingViewController dismissViewControllerAnimated:self.dismissAnimated completion:nil]; + [self logAppEvents:YES]; if (self.handler) { self.handler(self, NO); @@ -327,15 +290,11 @@ - (void)doneButtonPressed:(id)sender { if ([self.delegate respondsToSelector:@selector(facebookViewControllerDoneWasPressed:)]) { [self.delegate facebookViewControllerDoneWasPressed:self]; } - - UIViewController *presentingViewController = [self compatiblePresentingViewController]; + + UIViewController *presentingViewController = [self presentingViewController]; if (self.autoDismiss && presentingViewController) { - if ([presentingViewController respondsToSelector:@selector(dismissViewControllerAnimated:completion:)]) { - [presentingViewController dismissViewControllerAnimated:self.dismissAnimated completion:nil]; - } else { - [presentingViewController dismissModalViewControllerAnimated:self.dismissAnimated]; - } - + [presentingViewController dismissViewControllerAnimated:self.dismissAnimated completion:nil]; + [self logAppEvents:NO]; if (self.handler) { self.handler(self, YES); diff --git a/src/FBWebDialogs.m b/src/FBWebDialogs.m index ac1cbca317..709f39ab40 100644 --- a/src/FBWebDialogs.m +++ b/src/FBWebDialogs.m @@ -195,7 +195,7 @@ + (void)presentDialogModallyWithSession:(FBSession *)session handler:(FBWebDialogHandler)handler delegate:(id)delegate { - NSString *dialogURL = [[FBUtility buildFacebookUrlWithPre:@"https://m." withPost:@"/dialog/"] stringByAppendingString:dialog]; + NSString *dialogURL = [[FBUtility dialogBaseURL] stringByAppendingString:dialog]; NSMutableDictionary *parametersImpl = [NSMutableDictionary dictionary]; diff --git a/src/Facebook.m b/src/Facebook.m index 0d79a99f37..91e6449f41 100644 --- a/src/Facebook.m +++ b/src/Facebook.m @@ -21,6 +21,7 @@ #import "FBError.h" #import "FBSessionManualTokenCachingStrategy.h" #import "FBSession+Internal.h" +#import "FBSessionUtility.h" #import "FBUtility.h" static NSString* kRedirectURL = @"fbconnect://success"; @@ -159,7 +160,7 @@ - (void)invalidateSession { [self.session close]; [self.tokenCaching clearToken]; - [FBSession deleteFacebookCookies]; + [FBUtility deleteFacebookCookies]; // setting to nil also terminates any active request for whitelist [_frictionlessRequestSettings updateRecipientCacheWithRecipients:nil]; @@ -657,7 +658,7 @@ - (void)dialog:(NSString *)action [_fbDialog release]; - NSString *dialogURL = [[FBUtility buildFacebookUrlWithPre:@"https://m." withPost:@"/dialog/"] stringByAppendingString:action]; + NSString *dialogURL = [[FBUtility dialogBaseURL] stringByAppendingString:action]; [params setObject:@"touch" forKey:@"display"]; [params setObject:kSDKVersion forKey:@"sdk"]; [params setObject:kRedirectURL forKey:@"redirect_uri"]; @@ -693,7 +694,7 @@ - (void)dialog:(NSString *)action // if value parses as a json array expression get the list that way id fbids = [FBUtility simpleJSONDecode:fbid]; if (![fbids isKindOfClass:[NSArray class]]) { - // otherwise seperate by commas (handles the singleton case too) + // otherwise separate by commas (handles the singleton case too) fbids = [fbid componentsSeparatedByString:@","]; } invisible = [self isFrictionlessEnabledForRecipients:fbids]; diff --git a/src/FacebookSDK.h b/src/FacebookSDK.h index e1ab0889ea..af13eae61a 100644 --- a/src/FacebookSDK.h +++ b/src/FacebookSDK.h @@ -141,5 +141,5 @@ */ -#define FB_IOS_SDK_VERSION_STRING @"3.7.1" +#define FB_IOS_SDK_VERSION_STRING @"3.8.0" diff --git a/src/FacebookSDKIntegrationTests/FBAccessTokenDataTests.m b/src/FacebookSDKIntegrationTests/FBAccessTokenDataTests.m index 84c819f7db..24628e2104 100644 --- a/src/FacebookSDKIntegrationTests/FBAccessTokenDataTests.m +++ b/src/FacebookSDKIntegrationTests/FBAccessTokenDataTests.m @@ -97,7 +97,8 @@ - (void) testCachingStrategyAndSessionInit { FBTokenInformationExpirationDateKey : [NSDate dateWithTimeIntervalSince1970:1893456000], FBTokenInformationPermissionsKey : expectedPermissions, FBTokenInformationLoginTypeLoginKey : [NSNumber numberWithInt:FBSessionLoginTypeFacebookViaSafari], - FBTokenInformationRefreshDateKey : [NSDate dateWithTimeIntervalSince1970:1356998400] + FBTokenInformationRefreshDateKey : [NSDate dateWithTimeIntervalSince1970:1356998400], + FBTokenInformationPermissionsRefreshDateKey : [NSDate dateWithTimeIntervalSince1970:1356998401], }; FBAccessTokenData *randomToken = [FBAccessTokenData createTokenFromDictionary:dictionary]; @@ -119,6 +120,7 @@ - (void) testCachingStrategyAndSessionInit { STAssertEqualObjects(expectedPermissions, fetchedToken.permissions, @"permissions does not match"); STAssertEquals(FBSessionLoginTypeFacebookViaSafari, fetchedToken.loginType, @"loginType does not match"); STAssertEqualObjects([NSDate dateWithTimeIntervalSince1970:1356998400], fetchedToken.refreshDate, @"refreshDate does not match"); + STAssertEqualObjects([NSDate dateWithTimeIntervalSince1970:1356998401], fetchedToken.permissionsRefreshDate, @"permissionsRefreshDate does not match"); } // Verifies that session init can read everything as expected from the cache for old cache entries that use isFacebookLogin - (void) testCachingStrategyAndSessionInit_IsFacebookLogin { diff --git a/src/FacebookSDKIntegrationTests/FBAppEventsIntegrationTests.m b/src/FacebookSDKIntegrationTests/FBAppEventsIntegrationTests.m index 3e8e828ed6..a515606add 100644 --- a/src/FacebookSDKIntegrationTests/FBAppEventsIntegrationTests.m +++ b/src/FacebookSDKIntegrationTests/FBAppEventsIntegrationTests.m @@ -46,7 +46,9 @@ - (void)setUp { } - (void)tearDown { - [super tearDown]; + [super tearDown]; + [_mockFBUtility release]; + _mockFBUtility = nil; } + (void)publishInstallCounter:(NSString *)appID { @@ -98,25 +100,25 @@ -(void) testSessionNotClosed { [appEventsSingletonMock stopMocking]; } --(void) testUpdateParametersWithEventUsageLimits { +-(void) testUpdateParametersWithEventUsageLimitsAndBundleInfo { NSMutableDictionary *parameters = [NSMutableDictionary dictionary]; // default should set 1 for the app setting. NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults]; [defaults removeObjectForKey:@"com.facebook.sdk:FBAppEventsLimitEventUsage"]; - [FBUtility updateParametersWithEventUsageLimits:parameters]; + [FBUtility updateParametersWithEventUsageLimitsAndBundleInfo:parameters]; STAssertTrue([parameters[@"application_tracking_enabled"] isEqualToString:@"1"], @"app tracking should default to 1"); // when limited, app tracking is 0. [parameters removeAllObjects]; FBAppEvents.limitEventUsage = YES; - [FBUtility updateParametersWithEventUsageLimits:parameters]; + [FBUtility updateParametersWithEventUsageLimitsAndBundleInfo:parameters]; STAssertTrue([parameters[@"application_tracking_enabled"] isEqualToString:@"0"], @"app tracking should be 0 when event usage is limited"); // when explicitly unlimited, app tracking is 1. [parameters removeAllObjects]; FBAppEvents.limitEventUsage = NO; - [FBUtility updateParametersWithEventUsageLimits:parameters]; + [FBUtility updateParametersWithEventUsageLimitsAndBundleInfo:parameters]; STAssertTrue([parameters[@"application_tracking_enabled"] isEqualToString:@"1"], @"app tracking should be 1 when event usage is explicitly unlimited"); } diff --git a/src/FacebookSDKIntegrationTests/FBBatchRequestIntegrationTests.m b/src/FacebookSDKIntegrationTests/FBBatchRequestIntegrationTests.m index c8b6198bbe..4c44b20ea5 100644 --- a/src/FacebookSDKIntegrationTests/FBBatchRequestIntegrationTests.m +++ b/src/FacebookSDKIntegrationTests/FBBatchRequestIntegrationTests.m @@ -14,12 +14,16 @@ * limitations under the License. */ +#import "FBAccessTokenData.h" #import "FBBatchRequestIntegrationTests.h" #import "FBTestSession.h" #import "FBRequestConnection.h" #import "FBRequest.h" #import "FBTestBlocker.h" #import "FBGraphUser.h" +#import "FBSessionTokenCachingStrategy.h" + +#pragma GCC diagnostic ignored "-Wdeprecated-declarations" #if defined(FACEBOOKSDK_SKIP_BATCH_REQUEST_TESTS) @@ -286,6 +290,62 @@ - (void)testBatchUploadPhoto [blocker release]; } +- (void)testBatchParametersOmitResponseOnSuccess { + FBTestSession *session = [self defaultTestSession]; + FBRequestConnection *connection = [[[FBRequestConnection alloc] init] autorelease]; + FBTestBlocker *blocker = [[FBTestBlocker alloc] initWithExpectedSignalCount:2]; + // Note these ids are significant in that they are ids of other test users. Since we use FBTestSession + // above (which will have a platform test user access token), the ids need to be objects that are visible + // to the platform test user (such as other test users). + FBRequest *parent = [[[FBRequest alloc] initWithSession:session graphPath:@"?ids=100006424828400,100006675870174"] autorelease]; + [connection addRequest:parent + completionHandler:^(FBRequestConnection *connection, id result, NSError *error) { + STAssertNil(error, @"unexpected error in parent request :%@", error); + STAssertNotNil(result, @"expected parent results since we said to include response"); + [blocker signal]; + } batchParameters:@{@"name":@"getactions", @"omit_response_on_success":@(NO)}]; + + FBRequest *child = [[[FBRequest alloc] initWithSession:session graphPath:@"?ids={result=getactions:$.*.id}"] autorelease]; + [connection addRequest:child + completionHandler:^(FBRequestConnection *connection, id result, NSError *error) { + STAssertNil(error, @"unexpected error in child request :%@", error); + STAssertNotNil(result, @"expected results"); + [blocker signal]; + } batchEntryName:nil]; + [connection start]; + + STAssertTrue([blocker waitWithTimeout:60], @"blocker timed out"); +} + +- (void)testBatchParametersDependsOn { + FBTestSession *session = [self defaultTestSession]; + FBRequestConnection *connection = [[[FBRequestConnection alloc] init] autorelease]; + FBTestBlocker *blocker = [[FBTestBlocker alloc] initWithExpectedSignalCount:2]; + + // Set up a parent request to an invalid graph path that will result in an error. + FBRequest *parent = [[[FBRequest alloc] initWithSession:session graphPath:@"invalidpath"] autorelease]; + [connection addRequest:parent + completionHandler:^(FBRequestConnection *connection, id result, NSError *error) { + STAssertNotNil(error, @"expected error in parent request but did not get one."); + [blocker signal]; + } batchParameters:@{@"name":@"parent"}]; + + // The child request does not have an implicit dependency on the parent, but we will test it + // by adding the depends_on explicitly and verifying that the child response has no data (nor error). + // "If the parent operation execution results in an error, then the subsequent operation is not executed" + // (see https://developers.facebook.com/docs/reference/api/batch/) + FBRequest *child = [[[FBRequest alloc] initWithSession:session graphPath:@"?ids=4,6"] autorelease]; + [connection addRequest:child + completionHandler:^(FBRequestConnection *connection, id result, NSError *error) { + STAssertNil(error, @"unexpected error in child request :%@", error); + STAssertNil(result, @"unexpected results in child request %@", result); + [blocker signal]; + } batchParameters:@{@"depends_on":@"parent"}]; + [connection start]; + + STAssertTrue([blocker waitWithTimeout:60], @"blocker timed out"); +} + @end #endif diff --git a/src/FacebookSDKIntegrationTests/FBCacheIntegrationTests.m b/src/FacebookSDKIntegrationTests/FBCacheIntegrationTests.m index 97e24c91d7..b4bed108e9 100644 --- a/src/FacebookSDKIntegrationTests/FBCacheIntegrationTests.m +++ b/src/FacebookSDKIntegrationTests/FBCacheIntegrationTests.m @@ -222,7 +222,7 @@ - (void)testDataPersistence dataUsingEncoding:NSUTF8StringEncoding]; for (NSUInteger counter = 0; counter < numberOfFiles; counter++) { NSString *fileName = [cacheIndex - storeFileForKey:[NSString stringWithFormat:@"test%d", counter] + storeFileForKey:[NSString stringWithFormat:@"test%lu", (unsigned long)counter] withData:dummyData]; STAssertNotNil(fileName, @""); } @@ -249,7 +249,7 @@ - (void)testDataPersistence // test that the data is still there for (int counter = 0; counter < numberOfFiles; counter++) { - NSString* key = [NSString stringWithFormat:@"test%d", counter]; + NSString* key = [NSString stringWithFormat:@"test%lu", (unsigned long)counter]; NSString* fileName = [cacheIndex fileNameForKey:key]; STAssertNotNil(fileName, @"Entity missing from the cache"); @@ -283,7 +283,7 @@ - (void)testTrimming NSData *dummyData = [dummy dataUsingEncoding:NSUTF8StringEncoding]; for (NSUInteger counter = 0; counter < numberOfFiles; counter++) { NSString *fileName = [cacheIndex - storeFileForKey:[NSString stringWithFormat:@"test%d", counter] + storeFileForKey:[NSString stringWithFormat:@"test%lu", (unsigned long)counter] withData:dummyData]; STAssertNotNil(fileName, @""); } diff --git a/src/FacebookSDKIntegrationTests/FBIntegrationTests.h b/src/FacebookSDKIntegrationTests/FBIntegrationTests.h index 964964c4d2..84509a0fca 100644 --- a/src/FacebookSDKIntegrationTests/FBIntegrationTests.h +++ b/src/FacebookSDKIntegrationTests/FBIntegrationTests.h @@ -60,7 +60,8 @@ hasProperties:(NSArray*)propertyNames; - (void)validateGraphObjectWithId:(NSString*)idString hasProperties:(NSArray*)propertyNames - withSession:(FBSession*)session; + withSession:(FBSession*)session + blocker:(FBTestBlocker*)blocker; - (void)postAndValidateWithSession:(FBSession*)session graphPath:(NSString*)graphPath graphObject:(id)graphObject diff --git a/src/FacebookSDKIntegrationTests/FBIntegrationTests.m b/src/FacebookSDKIntegrationTests/FBIntegrationTests.m index c63d213827..52c3b2f9c2 100644 --- a/src/FacebookSDKIntegrationTests/FBIntegrationTests.m +++ b/src/FacebookSDKIntegrationTests/FBIntegrationTests.m @@ -20,6 +20,8 @@ #import "FBRequest.h" #import "FBSettings.h" #import "FBError.h" +#import "FBUtility.h" +#import #include static NSMutableDictionary *mapTestCasesToSessions; @@ -28,7 +30,9 @@ #pragma mark Private interface -@interface FBIntegrationTests () +@interface FBIntegrationTests () { + id _mockFBUtility; +} - (void)issueFriendRequestInSession:(FBTestSession*)session toFriend:(NSString*)userID; @@ -61,6 +65,9 @@ - (void)setUp forKey:[NSValue valueWithNonretainedObject:self]]; pthread_mutex_unlock(&mutex); + + _mockFBUtility = [[OCMockObject mockForClass:[FBUtility class]] retain]; + [[[_mockFBUtility stub] andReturn:nil] advertiserID]; //stub advertiserID since that often hangs. } - (void)tearDown @@ -77,6 +84,9 @@ - (void)tearDown } [sessions release]; + [_mockFBUtility release]; + _mockFBUtility = nil; + [super tearDown]; } @@ -182,10 +192,8 @@ - (void)validateGraphObject:(id)graphObject hasProperties:(NSArra } } -- (void)validateGraphObjectWithId:(NSString*)idString hasProperties:(NSArray*)propertyNames withSession:(FBSession*)session { - __block FBTestBlocker *blocker = [[FBTestBlocker alloc] init]; - - FBRequest *request = [[[FBRequest alloc] initWithSession:session +- (void)validateGraphObjectWithId:(NSString*)idString hasProperties:(NSArray*)propertyNames withSession:(FBSession*)session blocker:(FBTestBlocker *)blocker { + FBRequest *request = [[[FBRequest alloc] initWithSession:session graphPath:idString] autorelease]; @@ -198,11 +206,10 @@ - (void)validateGraphObjectWithId:(NSString*)idString hasProperties:(NSArray*)pr [blocker signal]; }]; - [blocker wait]; } - (void)postAndValidateWithSession:(FBSession*)session graphPath:(NSString*)graphPath graphObject:(id)graphObject hasProperties:(NSArray*)propertyNames { - __block FBTestBlocker *blocker = [[FBTestBlocker alloc] init]; + __block FBTestBlocker *blocker = [[FBTestBlocker alloc] initWithExpectedSignalCount:2]; FBRequest *request = [[[FBRequest alloc] initForPostWithSession:session graphPath:graphPath @@ -216,12 +223,13 @@ - (void)postAndValidateWithSession:(FBSession*)session graphPath:(NSString*)grap NSString *newObjectId = [result objectForKey:@"id"]; [self validateGraphObjectWithId:newObjectId hasProperties:propertyNames - withSession:session]; + withSession:session + blocker:blocker]; } [blocker signal]; }]; - [blocker wait]; + STAssertTrue([blocker waitWithTimeout:15], @"blocker timed out"); } // Unit tests failing? Turn on some logging with this helper. diff --git a/src/FacebookSDKIntegrationTests/FBOpenGraphActionTests.m b/src/FacebookSDKIntegrationTests/FBOpenGraphActionTests.m index 1c932e169a..7a9dc3caf9 100644 --- a/src/FacebookSDKIntegrationTests/FBOpenGraphActionTests.m +++ b/src/FacebookSDKIntegrationTests/FBOpenGraphActionTests.m @@ -72,10 +72,10 @@ - (void)testPostingSimpleOpenGraphAction { action.test = testObject; action.tags = [NSArray arrayWithObject:userObject]; - NSMutableDictionary *image = [NSDictionary dictionaryWithObjectsAndKeys: + NSDictionary *image = [NSDictionary dictionaryWithObjectsAndKeys: @"https://sphotos-b.xx.fbcdn.net/hphotos-ash4/387972_10152013102225492_1756755651_n.jpg", @"url", nil]; - NSMutableArray *images = [NSArray arrayWithObject:image]; + NSArray *images = [NSArray arrayWithObject:image]; action.image = images; return action; @@ -130,7 +130,7 @@ - (void)testPostingUserGeneratedImageInAction @"false", @"user_generated", @"https://sphotos-b.xx.fbcdn.net/hphotos-ash4/387972_10152013102225492_1756755651_n.jpg", @"url", nil]; - NSMutableArray *images = [NSArray arrayWithObject:image]; + NSArray *images = [NSArray arrayWithObject:image]; action.image = images; id postedAction = [self batchedPostAndGetWithSession:self.defaultTestSession diff --git a/src/FacebookSDKIntegrationTests/FBRequestConnectionIntegrationTests.m b/src/FacebookSDKIntegrationTests/FBRequestConnectionIntegrationTests.m index 3fded629bd..ba3334fcf5 100644 --- a/src/FacebookSDKIntegrationTests/FBRequestConnectionIntegrationTests.m +++ b/src/FacebookSDKIntegrationTests/FBRequestConnectionIntegrationTests.m @@ -14,7 +14,9 @@ * limitations under the License. */ +#import "FBAccessTokenData.h" #import "FBRequestConnectionIntegrationTests.h" +#import "FBSession+Internal.h" #import "FBTestSession.h" #import "FBTestSession+Internal.h" #import "FBRequestConnection.h" @@ -72,6 +74,8 @@ - (void)testWillPiggybackTokenExtensionIfNeeded { FBTestSession *session = [self getSessionWithSharedUserWithPermissions:nil]; session.forceAccessTokenRefresh = YES; + // Invoke shouldRefreshPermissions which has the side affect of disabling permissions refresh piggybacking for an hour. + [session shouldRefreshPermissions]; FBRequest *request = [[[FBRequest alloc] initWithSession:session graphPath:@"me"] autorelease]; @@ -84,9 +88,40 @@ - (void)testWillPiggybackTokenExtensionIfNeeded [blocker release]; NSArray *requests = [connection performSelector:@selector(requests)]; - STAssertTrue(requests.count == 2, @"didn't piggyback"); + + // Therefore, only expect the the token refresh piggyback in addition to the original request for /me + int count = requests.count; + STAssertEquals(2,count, @"unexpected number of piggybacks"); + + [connection release]; +} + +- (void)testWillPiggybackPermissionsRefresh +{ + FBTestSession *session = [self getSessionWithSharedUserWithPermissions:nil]; + session.forceAccessTokenRefresh = YES; + // verify session's permissions refresh date is initially in the past. + STAssertEquals([NSDate distantPast], session.accessTokenData.permissionsRefreshDate, @"session permission refresh date does not match"); + + FBRequest *request = [[[FBRequest alloc] initWithSession:session graphPath:@"me"] autorelease]; + + FBTestBlocker *blocker = [[FBTestBlocker alloc] init]; + FBRequestConnection *connection = [[FBRequestConnection alloc] init]; + [connection addRequest:request completionHandler:[self handlerExpectingSuccessSignaling:blocker]]; + [connection start]; + + [blocker wait]; + [blocker release]; + + NSArray *requests = [connection performSelector:@selector(requests)]; + + // Expect the token refresh and permission refresh to be piggybacked. + int count = requests.count; + STAssertEquals(3,count, @"unexpected number of piggybacks"); [connection release]; + + STAssertTrue([session.accessTokenData.permissionsRefreshDate timeIntervalSinceNow]> -3, @"session permission refresh date should be within a few seconds of now"); } - (void)testCachedRequests @@ -119,10 +154,14 @@ - (void)testCachedRequests [connection addRequest:request completionHandler:^(FBRequestConnection *connection, id result, NSError *error) { STAssertNotNil(result, @"Expected a successful result"); completedWithoutBlocking = YES; + [blocker signal]; }]; [connection startWithCacheIdentity:@"FBUnitTests" skipRoundtripIfCached:YES]; + // Note despite the skipping of round trip, the completion handler is still dispatched async since we + // started using the Task framework in FBRequestConnection. + STAssertTrue([blocker waitWithTimeout:3], @"blocker timed out"); // should have completed successfully by here STAssertTrue(completedWithoutBlocking, @"Should have called the handler, due to cache hit"); STAssertTrue(connection.isResultFromCache, @"Should not have fetched from server"); @@ -335,6 +374,32 @@ - (void)testNilCompletionHandler { [blocker release]; } +- (void)testMultipleSelectionWithDependenciesBatch { + FBTestSession *session = [self getSessionWithSharedUserWithPermissions:nil]; + FBRequestConnection *connection = [[FBRequestConnection alloc] init]; + FBTestBlocker *blocker = [[FBTestBlocker alloc] initWithExpectedSignalCount:2]; + // Note these ids are significant in that they are ids of other test users. Since we use FBTestSession + // above (which will have a platform test user access token), the ids need to be objects that are visible + // to the platform test user (such as other test users). + FBRequest *parent = [[[FBRequest alloc] initWithSession:session graphPath:@"?ids=100006424828400,100006675870174"] autorelease]; + [connection addRequest:parent + completionHandler:^(FBRequestConnection *connection, id result, NSError *error) { + STAssertNil(error, @"unexpected error in parent request :%@", error); + [blocker signal]; + } batchEntryName:@"getactions"]; + + FBRequest *child = [[[FBRequest alloc] initWithSession:session graphPath:@"?ids={result=getactions:$.*.id}"] autorelease]; + [connection addRequest:child + completionHandler:^(FBRequestConnection *connection, id result, NSError *error) { + STAssertNil(error, @"unexpected error in child request :%@", error); + STAssertNotNil(result, @"expected results"); + [blocker signal]; + } batchEntryName:nil]; + [connection start]; + [connection release]; + + STAssertTrue([blocker waitWithTimeout:60], @"blocker timed out"); +} @end #endif diff --git a/src/FacebookSDKIntegrationTests/FBRequestIntegrationTests.m b/src/FacebookSDKIntegrationTests/FBRequestIntegrationTests.m index a40adb1dfc..56927bc216 100644 --- a/src/FacebookSDKIntegrationTests/FBRequestIntegrationTests.m +++ b/src/FacebookSDKIntegrationTests/FBRequestIntegrationTests.m @@ -114,7 +114,7 @@ - (void)testRequestPlaceSearchWithSearchText CLLocationCoordinate2D coordinate = CLLocationCoordinate2DMake(38.889227, -77.049078); FBRequest *searchRequest = [FBRequest requestForPlacesSearchAtCoordinate:coordinate radiusInMeters:200 - resultsLimit:5 + resultsLimit:5 searchText:@"Lincoln Memorial"]; [searchRequest setSession:self.defaultTestSession]; NSArray *response = [self sendRequests:searchRequest, nil]; @@ -131,12 +131,15 @@ - (void)testRequestPlaceSearchWithSearchText NSDictionary *firstResponse = (NSDictionary *)[response objectAtIndex:0]; NSArray *data = (NSArray*)[firstResponse objectForKey:@"data"]; - id targetPlace = (id)[FBGraphObject graphObject]; - targetPlace.id = @"116411068417081"; - targetPlace.name = @"Lincoln Memorial"; + BOOL found = NO; + for (FBGraphObject *object in data) { + if ([object[@"name"] rangeOfString:@"Lincoln Memorial"].location != NSNotFound) { + found = YES; + break; + } + } - id foundPlace = [FBUtility graphObjectInArray:data withSameIDAs:targetPlace]; - STAssertNotNil(foundPlace, @"didn't find Lincoln Memorial"); + STAssertTrue(found, @"didn't find Lincoln Memorial"); } - (void)testRestRequestGetUser { diff --git a/src/facebook-ios-sdk.xcodeproj/project.pbxproj b/src/facebook-ios-sdk.xcodeproj/project.pbxproj index d87e358ea6..05ffea5e81 100644 --- a/src/facebook-ios-sdk.xcodeproj/project.pbxproj +++ b/src/facebook-ios-sdk.xcodeproj/project.pbxproj @@ -114,6 +114,8 @@ 8525A5BC156F2049009F6F3F /* FBTestSession.m in Sources */ = {isa = PBXBuildFile; fileRef = 8525A5B9156F2049009F6F3F /* FBTestSession.m */; }; 85468F0617189864004A747A /* FBDialogs.m in Sources */ = {isa = PBXBuildFile; fileRef = 84C1E2061717DD0F0037E406 /* FBDialogs.m */; }; 85468F0C17189865004A747A /* FBDialogs.m in Sources */ = {isa = PBXBuildFile; fileRef = 84C1E2061717DD0F0037E406 /* FBDialogs.m */; }; + 857E927717CE959200F5F2BC /* FBIsURLHavingQueryParams.m in Sources */ = {isa = PBXBuildFile; fileRef = 85BDF76617CE7FDF002E7225 /* FBIsURLHavingQueryParams.m */; }; + 857E927A17CE9C9800F5F2BC /* FBIsStringRepresentingJSONDictionary.m in Sources */ = {isa = PBXBuildFile; fileRef = 857E927917CE9C9800F5F2BC /* FBIsStringRepresentingJSONDictionary.m */; }; 8582701016E02E5600795734 /* FBOpenGraphActionShareDialogParams.m in Sources */ = {isa = PBXBuildFile; fileRef = 8582700F16E02E5600795734 /* FBOpenGraphActionShareDialogParams.m */; }; 8582701716E02E6000795734 /* FBOpenGraphActionShareDialogParams.h in Headers */ = {isa = PBXBuildFile; fileRef = 8582701616E02E6000795734 /* FBOpenGraphActionShareDialogParams.h */; settings = {ATTRIBUTES = (Public, ); }; }; 8582701A16E030EE00795734 /* FBOpenGraphActionShareDialogParams.m in Sources */ = {isa = PBXBuildFile; fileRef = 8582700F16E02E5600795734 /* FBOpenGraphActionShareDialogParams.m */; }; @@ -190,6 +192,8 @@ 85ADAACE16A0DA6D00145328 /* FBLoginDialogAuthenticationTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 85ADAAC616A0DA6D00145328 /* FBLoginDialogAuthenticationTests.m */; }; 85ADAACF16A0DA6D00145328 /* FBSafariAuthenticationTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 85ADAAC816A0DA6D00145328 /* FBSafariAuthenticationTests.m */; }; 85ADAAD116A0DA6D00145328 /* FBSystemAccountAuthenticationTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 85ADAACB16A0DA6D00145328 /* FBSystemAccountAuthenticationTests.m */; }; + 85BDF76317CD57C3002E7225 /* FBAppBridgeTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 85BDF76217CD57C3002E7225 /* FBAppBridgeTests.m */; }; + 85BDF76717CE7FDF002E7225 /* FBIsURLHavingQueryParams.h in Headers */ = {isa = PBXBuildFile; fileRef = 85BDF76517CE7FDF002E7225 /* FBIsURLHavingQueryParams.h */; }; 85C60EE41698CFC000E7BB7D /* FBURLConnectionTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 85C60EE31698CFC000E7BB7D /* FBURLConnectionTests.m */; }; 85C60EF21698DA8400E7BB7D /* libOHHTTPStubs.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 85C60EEF1698DA5300E7BB7D /* libOHHTTPStubs.a */; }; 85C610961699109C00E7BB7D /* libOCMock.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 85C610951699109000E7BB7D /* libOCMock.a */; }; @@ -244,6 +248,49 @@ 9D366B29178DC002007B4CEC /* FBRequestConnectionRetryManager.m in Sources */ = {isa = PBXBuildFile; fileRef = 9D366B25178DC001007B4CEC /* FBRequestConnectionRetryManager.m */; }; 9D366B2B178F230D007B4CEC /* FacebookSDKResources.bundle.README in Resources */ = {isa = PBXBuildFile; fileRef = 9D366B2A178F230A007B4CEC /* FacebookSDKResources.bundle.README */; }; 9D366B2C178F230D007B4CEC /* FacebookSDKResources.bundle.README in Resources */ = {isa = PBXBuildFile; fileRef = 9D366B2A178F230A007B4CEC /* FacebookSDKResources.bundle.README */; }; + 9D393AE717BAEE5B00658BC5 /* FBSessionLoginStrategy.h in Headers */ = {isa = PBXBuildFile; fileRef = 9D393AE517BAEE5B00658BC5 /* FBSessionLoginStrategy.h */; }; + 9D3B0D8217BC230B00CA3C04 /* FBSessionLoginStrategyParams.h in Headers */ = {isa = PBXBuildFile; fileRef = 9D3B0D8017BC230B00CA3C04 /* FBSessionLoginStrategyParams.h */; }; + 9D3B0D8317BC230B00CA3C04 /* FBSessionLoginStrategyParams.m in Sources */ = {isa = PBXBuildFile; fileRef = 9D3B0D8117BC230B00CA3C04 /* FBSessionLoginStrategyParams.m */; }; + 9D3B0D8417BC230B00CA3C04 /* FBSessionLoginStrategyParams.m in Sources */ = {isa = PBXBuildFile; fileRef = 9D3B0D8117BC230B00CA3C04 /* FBSessionLoginStrategyParams.m */; }; + 9D3B0D8517BC230B00CA3C04 /* FBSessionLoginStrategyParams.m in Sources */ = {isa = PBXBuildFile; fileRef = 9D3B0D8117BC230B00CA3C04 /* FBSessionLoginStrategyParams.m */; }; + 9D3D36AB17CBE6C500B9B049 /* FBTaskCompletionSource.m in Sources */ = {isa = PBXBuildFile; fileRef = 9D3D36A217CBE6C500B9B049 /* FBTaskCompletionSource.m */; }; + 9D3D36AC17CBE6C500B9B049 /* FBTaskCompletionSource.m in Sources */ = {isa = PBXBuildFile; fileRef = 9D3D36A217CBE6C500B9B049 /* FBTaskCompletionSource.m */; }; + 9D3D36AD17CBE6C500B9B049 /* FBTaskCompletionSource.m in Sources */ = {isa = PBXBuildFile; fileRef = 9D3D36A217CBE6C500B9B049 /* FBTaskCompletionSource.m */; }; + 9D3D36AE17CBE6C500B9B049 /* FBTaskCompletionSource.h in Headers */ = {isa = PBXBuildFile; fileRef = 9D3D36A317CBE6C500B9B049 /* FBTaskCompletionSource.h */; }; + 9D3D36AF17CBE6C500B9B049 /* FBTask.m in Sources */ = {isa = PBXBuildFile; fileRef = 9D3D36A417CBE6C500B9B049 /* FBTask.m */; }; + 9D3D36B017CBE6C500B9B049 /* FBTask.m in Sources */ = {isa = PBXBuildFile; fileRef = 9D3D36A417CBE6C500B9B049 /* FBTask.m */; }; + 9D3D36B117CBE6C500B9B049 /* FBTask.m in Sources */ = {isa = PBXBuildFile; fileRef = 9D3D36A417CBE6C500B9B049 /* FBTask.m */; }; + 9D3D36B217CBE6C500B9B049 /* FBTask.h in Headers */ = {isa = PBXBuildFile; fileRef = 9D3D36A517CBE6C500B9B049 /* FBTask.h */; }; + 9D3D36B317CBE6C500B9B049 /* FBTask+Private.h in Headers */ = {isa = PBXBuildFile; fileRef = 9D3D36A617CBE6C500B9B049 /* FBTask+Private.h */; }; + 9D3FC9AB17BA971C0072D6BC /* FBSessionUtility.h in Headers */ = {isa = PBXBuildFile; fileRef = 9D3FC9A917BA971C0072D6BC /* FBSessionUtility.h */; }; + 9D3FC9AC17BA971C0072D6BC /* FBSessionUtility.m in Sources */ = {isa = PBXBuildFile; fileRef = 9D3FC9AA17BA971C0072D6BC /* FBSessionUtility.m */; }; + 9D3FC9AD17BA971C0072D6BC /* FBSessionUtility.m in Sources */ = {isa = PBXBuildFile; fileRef = 9D3FC9AA17BA971C0072D6BC /* FBSessionUtility.m */; }; + 9D3FC9AE17BA971C0072D6BC /* FBSessionUtility.m in Sources */ = {isa = PBXBuildFile; fileRef = 9D3FC9AA17BA971C0072D6BC /* FBSessionUtility.m */; }; + 9D5B914417BD3570009DBABB /* FBSession+FBSessionLoginStrategy.h in Headers */ = {isa = PBXBuildFile; fileRef = 9D5B914317BD3570009DBABB /* FBSession+FBSessionLoginStrategy.h */; }; + 9D5B914C17BD3761009DBABB /* FBSessionSystemLoginStategy.h in Headers */ = {isa = PBXBuildFile; fileRef = 9D5B914A17BD3761009DBABB /* FBSessionSystemLoginStategy.h */; }; + 9D5B914D17BD3761009DBABB /* FBSessionSystemLoginStategy.m in Sources */ = {isa = PBXBuildFile; fileRef = 9D5B914B17BD3761009DBABB /* FBSessionSystemLoginStategy.m */; }; + 9D5B914E17BD3761009DBABB /* FBSessionSystemLoginStategy.m in Sources */ = {isa = PBXBuildFile; fileRef = 9D5B914B17BD3761009DBABB /* FBSessionSystemLoginStategy.m */; }; + 9D5B914F17BD3761009DBABB /* FBSessionSystemLoginStategy.m in Sources */ = {isa = PBXBuildFile; fileRef = 9D5B914B17BD3761009DBABB /* FBSessionSystemLoginStategy.m */; }; + 9D5B915217BD3773009DBABB /* FBSessionAppSwitchingLoginStategy.h in Headers */ = {isa = PBXBuildFile; fileRef = 9D5B915017BD3773009DBABB /* FBSessionAppSwitchingLoginStategy.h */; }; + 9D5B915317BD3773009DBABB /* FBSessionAppSwitchingLoginStategy.m in Sources */ = {isa = PBXBuildFile; fileRef = 9D5B915117BD3773009DBABB /* FBSessionAppSwitchingLoginStategy.m */; }; + 9D5B915417BD3773009DBABB /* FBSessionAppSwitchingLoginStategy.m in Sources */ = {isa = PBXBuildFile; fileRef = 9D5B915117BD3773009DBABB /* FBSessionAppSwitchingLoginStategy.m */; }; + 9D5B915517BD3773009DBABB /* FBSessionAppSwitchingLoginStategy.m in Sources */ = {isa = PBXBuildFile; fileRef = 9D5B915117BD3773009DBABB /* FBSessionAppSwitchingLoginStategy.m */; }; + 9D5B915817BD3784009DBABB /* FBSessionFacebookAppNativeLoginStategy.h in Headers */ = {isa = PBXBuildFile; fileRef = 9D5B915617BD3784009DBABB /* FBSessionFacebookAppNativeLoginStategy.h */; }; + 9D5B915917BD3784009DBABB /* FBSessionFacebookAppNativeLoginStategy.m in Sources */ = {isa = PBXBuildFile; fileRef = 9D5B915717BD3784009DBABB /* FBSessionFacebookAppNativeLoginStategy.m */; }; + 9D5B915A17BD3784009DBABB /* FBSessionFacebookAppNativeLoginStategy.m in Sources */ = {isa = PBXBuildFile; fileRef = 9D5B915717BD3784009DBABB /* FBSessionFacebookAppNativeLoginStategy.m */; }; + 9D5B915B17BD3784009DBABB /* FBSessionFacebookAppNativeLoginStategy.m in Sources */ = {isa = PBXBuildFile; fileRef = 9D5B915717BD3784009DBABB /* FBSessionFacebookAppNativeLoginStategy.m */; }; + 9D5B915E17BD3792009DBABB /* FBSessionFacebookAppWebLoginStategy.h in Headers */ = {isa = PBXBuildFile; fileRef = 9D5B915C17BD3792009DBABB /* FBSessionFacebookAppWebLoginStategy.h */; }; + 9D5B915F17BD3792009DBABB /* FBSessionFacebookAppWebLoginStategy.m in Sources */ = {isa = PBXBuildFile; fileRef = 9D5B915D17BD3792009DBABB /* FBSessionFacebookAppWebLoginStategy.m */; }; + 9D5B916017BD3792009DBABB /* FBSessionFacebookAppWebLoginStategy.m in Sources */ = {isa = PBXBuildFile; fileRef = 9D5B915D17BD3792009DBABB /* FBSessionFacebookAppWebLoginStategy.m */; }; + 9D5B916117BD3792009DBABB /* FBSessionFacebookAppWebLoginStategy.m in Sources */ = {isa = PBXBuildFile; fileRef = 9D5B915D17BD3792009DBABB /* FBSessionFacebookAppWebLoginStategy.m */; }; + 9D5B916417BD379C009DBABB /* FBSessionSafariLoginStategy.h in Headers */ = {isa = PBXBuildFile; fileRef = 9D5B916217BD379C009DBABB /* FBSessionSafariLoginStategy.h */; }; + 9D5B916517BD379C009DBABB /* FBSessionSafariLoginStategy.m in Sources */ = {isa = PBXBuildFile; fileRef = 9D5B916317BD379C009DBABB /* FBSessionSafariLoginStategy.m */; }; + 9D5B916617BD379C009DBABB /* FBSessionSafariLoginStategy.m in Sources */ = {isa = PBXBuildFile; fileRef = 9D5B916317BD379C009DBABB /* FBSessionSafariLoginStategy.m */; }; + 9D5B916717BD379C009DBABB /* FBSessionSafariLoginStategy.m in Sources */ = {isa = PBXBuildFile; fileRef = 9D5B916317BD379C009DBABB /* FBSessionSafariLoginStategy.m */; }; + 9D5B916A17BD37A8009DBABB /* FBSessionInlineWebViewLoginStategy.h in Headers */ = {isa = PBXBuildFile; fileRef = 9D5B916817BD37A8009DBABB /* FBSessionInlineWebViewLoginStategy.h */; }; + 9D5B916B17BD37A8009DBABB /* FBSessionInlineWebViewLoginStategy.m in Sources */ = {isa = PBXBuildFile; fileRef = 9D5B916917BD37A8009DBABB /* FBSessionInlineWebViewLoginStategy.m */; }; + 9D5B916C17BD37A8009DBABB /* FBSessionInlineWebViewLoginStategy.m in Sources */ = {isa = PBXBuildFile; fileRef = 9D5B916917BD37A8009DBABB /* FBSessionInlineWebViewLoginStategy.m */; }; + 9D5B916D17BD37A8009DBABB /* FBSessionInlineWebViewLoginStategy.m in Sources */ = {isa = PBXBuildFile; fileRef = 9D5B916917BD37A8009DBABB /* FBSessionInlineWebViewLoginStategy.m */; }; 9D5D2996170F788A00FA5899 /* FBErrorUtility.h in Headers */ = {isa = PBXBuildFile; fileRef = 9D5D2995170F788A00FA5899 /* FBErrorUtility.h */; settings = {ATTRIBUTES = (Public, ); }; }; 9D6E5934176649B400C8BD64 /* FBDynamicFrameworkLoader.m in Sources */ = {isa = PBXBuildFile; fileRef = 9942280C174C1EBF0096A70C /* FBDynamicFrameworkLoader.m */; }; 9D6E593E17664D9500C8BD64 /* FBDynamicFrameworkLoader.m in Sources */ = {isa = PBXBuildFile; fileRef = 9942280C174C1EBF0096A70C /* FBDynamicFrameworkLoader.m */; }; @@ -482,6 +529,8 @@ 8525A5B8156F2049009F6F3F /* FBTestSession.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; lineEnding = 0; path = FBTestSession.h; sourceTree = ""; xcLanguageSpecificationIdentifier = xcode.lang.objcpp; }; 8525A5B9156F2049009F6F3F /* FBTestSession.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; lineEnding = 0; path = FBTestSession.m; sourceTree = ""; xcLanguageSpecificationIdentifier = xcode.lang.objc; }; 8527EC5615C9D3CF00660673 /* FBUserSettingsViewResources.bundle */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.plug-in"; path = FBUserSettingsViewResources.bundle; sourceTree = ""; }; + 857E927817CE9C9800F5F2BC /* FBIsStringRepresentingJSONDictionary.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = FBIsStringRepresentingJSONDictionary.h; path = tests/FBIsStringRepresentingJSONDictionary.h; sourceTree = ""; }; + 857E927917CE9C9800F5F2BC /* FBIsStringRepresentingJSONDictionary.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = FBIsStringRepresentingJSONDictionary.m; path = tests/FBIsStringRepresentingJSONDictionary.m; sourceTree = ""; }; 8582700F16E02E5600795734 /* FBOpenGraphActionShareDialogParams.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = FBOpenGraphActionShareDialogParams.m; sourceTree = ""; }; 8582701616E02E6000795734 /* FBOpenGraphActionShareDialogParams.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = FBOpenGraphActionShareDialogParams.h; sourceTree = ""; }; 85877C01169A3FBC00A6D70A /* FBRequestTests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = FBRequestTests.m; path = tests/FBRequestTests.m; sourceTree = ""; }; @@ -523,6 +572,10 @@ 85ADAACA16A0DA6D00145328 /* FBSystemAccountAuthenticationTests.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = FBSystemAccountAuthenticationTests.h; path = tests/FBSystemAccountAuthenticationTests.h; sourceTree = ""; }; 85ADAACB16A0DA6D00145328 /* FBSystemAccountAuthenticationTests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = FBSystemAccountAuthenticationTests.m; path = tests/FBSystemAccountAuthenticationTests.m; sourceTree = ""; }; 85ADAB0116A6082D00145328 /* FacebookSDKTests.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; name = FacebookSDKTests.xcconfig; path = tests/FacebookSDKTests.xcconfig; sourceTree = ""; }; + 85BDF76117CD57C3002E7225 /* FBAppBridgeTests.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = FBAppBridgeTests.h; path = tests/FBAppBridgeTests.h; sourceTree = ""; }; + 85BDF76217CD57C3002E7225 /* FBAppBridgeTests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = FBAppBridgeTests.m; path = tests/FBAppBridgeTests.m; sourceTree = ""; }; + 85BDF76517CE7FDF002E7225 /* FBIsURLHavingQueryParams.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = FBIsURLHavingQueryParams.h; path = tests/FBIsURLHavingQueryParams.h; sourceTree = ""; }; + 85BDF76617CE7FDF002E7225 /* FBIsURLHavingQueryParams.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = FBIsURLHavingQueryParams.m; path = tests/FBIsURLHavingQueryParams.m; sourceTree = ""; }; 85C60EE31698CFC000E7BB7D /* FBURLConnectionTests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = FBURLConnectionTests.m; path = tests/FBURLConnectionTests.m; sourceTree = ""; }; 85C60EE61698DA5300E7BB7D /* OHHTTPStubs.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = OHHTTPStubs.xcodeproj; path = ../vendor/OHHTTPStubs/OHHTTPStubs/OHHTTPStubs.xcodeproj; sourceTree = ""; }; 85C610871699109000E7BB7D /* OCMock.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = OCMock.xcodeproj; path = ../vendor/OCMock/Source/OCMock.xcodeproj; sourceTree = ""; }; @@ -549,7 +602,6 @@ 99F1D80D17753C3100BEBF6F /* FBPlacePickerViewGenericPlace.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; name = FBPlacePickerViewGenericPlace.png; path = ImageResources/FBPlacePickerView/FBPlacePickerViewGenericPlace.png; sourceTree = ""; }; 99F1D81017753CE200BEBF6F /* FBProfilePictureViewBlankProfilePortrait.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; name = FBProfilePictureViewBlankProfilePortrait.png; path = ImageResources/FBProfilePictureView/FBProfilePictureViewBlankProfilePortrait.png; sourceTree = ""; }; 99F1D81117753CE200BEBF6F /* FBProfilePictureViewBlankProfileSquare.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; name = FBProfilePictureViewBlankProfileSquare.png; path = ImageResources/FBProfilePictureView/FBProfilePictureViewBlankProfileSquare.png; sourceTree = ""; }; - 9D070C3F1794A23F00982386 /* FacebookSDKResources.bundle.README */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = FacebookSDKResources.bundle.README; sourceTree = ""; }; 9D17A8DB1671774B00AB1148 /* FBSystemAccountStoreAdapter.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = FBSystemAccountStoreAdapter.h; sourceTree = ""; }; 9D17A8DC1671774B00AB1148 /* FBSystemAccountStoreAdapter.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = FBSystemAccountStoreAdapter.m; sourceTree = ""; }; 9D332F401782343A001715AE /* FBAppCallTests.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = FBAppCallTests.h; path = tests/FBAppCallTests.h; sourceTree = ""; }; @@ -561,6 +613,29 @@ 9D366B24178DC000007B4CEC /* FBRequestConnectionRetryManager.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = FBRequestConnectionRetryManager.h; sourceTree = ""; }; 9D366B25178DC001007B4CEC /* FBRequestConnectionRetryManager.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = FBRequestConnectionRetryManager.m; sourceTree = ""; }; 9D366B2A178F230A007B4CEC /* FacebookSDKResources.bundle.README */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = FacebookSDKResources.bundle.README; sourceTree = ""; }; + 9D393AE517BAEE5B00658BC5 /* FBSessionLoginStrategy.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = FBSessionLoginStrategy.h; sourceTree = ""; }; + 9D3B0D8017BC230B00CA3C04 /* FBSessionLoginStrategyParams.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = FBSessionLoginStrategyParams.h; sourceTree = ""; }; + 9D3B0D8117BC230B00CA3C04 /* FBSessionLoginStrategyParams.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = FBSessionLoginStrategyParams.m; sourceTree = ""; }; + 9D3D36A217CBE6C500B9B049 /* FBTaskCompletionSource.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = FBTaskCompletionSource.m; sourceTree = ""; }; + 9D3D36A317CBE6C500B9B049 /* FBTaskCompletionSource.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = FBTaskCompletionSource.h; sourceTree = ""; }; + 9D3D36A417CBE6C500B9B049 /* FBTask.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = FBTask.m; sourceTree = ""; }; + 9D3D36A517CBE6C500B9B049 /* FBTask.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = FBTask.h; sourceTree = ""; }; + 9D3D36A617CBE6C500B9B049 /* FBTask+Private.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "FBTask+Private.h"; sourceTree = ""; }; + 9D3FC9A917BA971C0072D6BC /* FBSessionUtility.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = FBSessionUtility.h; sourceTree = ""; }; + 9D3FC9AA17BA971C0072D6BC /* FBSessionUtility.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = FBSessionUtility.m; sourceTree = ""; }; + 9D5B914317BD3570009DBABB /* FBSession+FBSessionLoginStrategy.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "FBSession+FBSessionLoginStrategy.h"; sourceTree = ""; }; + 9D5B914A17BD3761009DBABB /* FBSessionSystemLoginStategy.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = FBSessionSystemLoginStategy.h; sourceTree = ""; }; + 9D5B914B17BD3761009DBABB /* FBSessionSystemLoginStategy.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = FBSessionSystemLoginStategy.m; sourceTree = ""; }; + 9D5B915017BD3773009DBABB /* FBSessionAppSwitchingLoginStategy.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = FBSessionAppSwitchingLoginStategy.h; sourceTree = ""; }; + 9D5B915117BD3773009DBABB /* FBSessionAppSwitchingLoginStategy.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = FBSessionAppSwitchingLoginStategy.m; sourceTree = ""; }; + 9D5B915617BD3784009DBABB /* FBSessionFacebookAppNativeLoginStategy.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = FBSessionFacebookAppNativeLoginStategy.h; sourceTree = ""; }; + 9D5B915717BD3784009DBABB /* FBSessionFacebookAppNativeLoginStategy.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = FBSessionFacebookAppNativeLoginStategy.m; sourceTree = ""; }; + 9D5B915C17BD3792009DBABB /* FBSessionFacebookAppWebLoginStategy.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = FBSessionFacebookAppWebLoginStategy.h; sourceTree = ""; }; + 9D5B915D17BD3792009DBABB /* FBSessionFacebookAppWebLoginStategy.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = FBSessionFacebookAppWebLoginStategy.m; sourceTree = ""; }; + 9D5B916217BD379C009DBABB /* FBSessionSafariLoginStategy.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = FBSessionSafariLoginStategy.h; sourceTree = ""; }; + 9D5B916317BD379C009DBABB /* FBSessionSafariLoginStategy.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = FBSessionSafariLoginStategy.m; sourceTree = ""; }; + 9D5B916817BD37A8009DBABB /* FBSessionInlineWebViewLoginStategy.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = FBSessionInlineWebViewLoginStategy.h; sourceTree = ""; }; + 9D5B916917BD37A8009DBABB /* FBSessionInlineWebViewLoginStategy.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = FBSessionInlineWebViewLoginStategy.m; sourceTree = ""; }; 9D5D2995170F788A00FA5899 /* FBErrorUtility.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = FBErrorUtility.h; sourceTree = ""; }; 9DCFB2241779FFAE0079E85B /* FBRequest+Internal.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "FBRequest+Internal.h"; sourceTree = ""; }; 9DCFB22B177A04E50079E85B /* FBAppEventsIntegrationTests.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = FBAppEventsIntegrationTests.h; sourceTree = ""; }; @@ -696,7 +771,6 @@ 0867D691FE84028FC02AAC07 /* facebook-ios-sdk */ = { isa = PBXGroup; children = ( - 9D070C3F1794A23F00982386 /* FacebookSDKResources.bundle.README */, 08FB77AEFE84172EC02AAC07 /* FacebookSDK */, 32C88DFF0371C24200C91783 /* Other Sources */, B9CBC54015254CAE0036AA71 /* FacebookSDKTests */, @@ -705,8 +779,10 @@ 0867D69AFE84028FC02AAC07 /* Frameworks */, 034768DFFF38A50411DB9C8B /* Products */, ); + indentWidth = 4; name = "facebook-ios-sdk"; sourceTree = ""; + tabWidth = 4; }; 0867D69AFE84028FC02AAC07 /* Frameworks */ = { isa = PBXGroup; @@ -814,6 +890,7 @@ 84C1E2121718830F0037E406 /* FBOpenGraphObject.h */, 8582701616E02E6000795734 /* FBOpenGraphActionShareDialogParams.h */, 8582700F16E02E5600795734 /* FBOpenGraphActionShareDialogParams.m */, + 9D3D369F17CBE5E900B9B049 /* FBTask */, 84D0A6571581A1C000A2FA5E /* FBPlacePickerCacheDescriptor.h */, 84D0A6551581A1A600A2FA5E /* FBPlacePickerCacheDescriptor.m */, 84E3132B1589948C004C4169 /* FBPlacePickerViewController+Internal.h */, @@ -840,6 +917,9 @@ 85052AF5156F50F300F8F9A5 /* FBSession+Protected.h */, 84BEDF4C151BC24F00F89C3B /* FBSession.h */, 8446FDA5151BC5C2000BE007 /* FBSession.m */, + 9D5B916E17BD395D009DBABB /* FBSessionLoginStrategy */, + 9D3FC9A917BA971C0072D6BC /* FBSessionUtility.h */, + 9D3FC9AA17BA971C0072D6BC /* FBSessionUtility.m */, 5F8BE21B164A30FD006329D6 /* FBSessionAppEventsState.h */, 5F8BE21C164A30FD006329D6 /* FBSessionAppEventsState.m */, B549647317A8703E002C9284 /* FBSessionAuthLogger.h */, @@ -951,6 +1031,17 @@ name = Authentication; sourceTree = ""; }; + 85BDF76417CE7B76002E7225 /* Matchers */ = { + isa = PBXGroup; + children = ( + 85BDF76517CE7FDF002E7225 /* FBIsURLHavingQueryParams.h */, + 85BDF76617CE7FDF002E7225 /* FBIsURLHavingQueryParams.m */, + 857E927817CE9C9800F5F2BC /* FBIsStringRepresentingJSONDictionary.h */, + 857E927917CE9C9800F5F2BC /* FBIsStringRepresentingJSONDictionary.m */, + ); + name = Matchers; + sourceTree = ""; + }; 85C60EE71698DA5300E7BB7D /* Products */ = { isa = PBXGroup; children = ( @@ -1027,6 +1118,41 @@ name = FBProfilePictureView; sourceTree = ""; }; + 9D3D369F17CBE5E900B9B049 /* FBTask */ = { + isa = PBXGroup; + children = ( + 9D3D36A217CBE6C500B9B049 /* FBTaskCompletionSource.m */, + 9D3D36A317CBE6C500B9B049 /* FBTaskCompletionSource.h */, + 9D3D36A417CBE6C500B9B049 /* FBTask.m */, + 9D3D36A517CBE6C500B9B049 /* FBTask.h */, + 9D3D36A617CBE6C500B9B049 /* FBTask+Private.h */, + ); + name = FBTask; + sourceTree = ""; + }; + 9D5B916E17BD395D009DBABB /* FBSessionLoginStrategy */ = { + isa = PBXGroup; + children = ( + 9D393AE517BAEE5B00658BC5 /* FBSessionLoginStrategy.h */, + 9D5B914317BD3570009DBABB /* FBSession+FBSessionLoginStrategy.h */, + 9D3B0D8017BC230B00CA3C04 /* FBSessionLoginStrategyParams.h */, + 9D3B0D8117BC230B00CA3C04 /* FBSessionLoginStrategyParams.m */, + 9D5B915017BD3773009DBABB /* FBSessionAppSwitchingLoginStategy.h */, + 9D5B915117BD3773009DBABB /* FBSessionAppSwitchingLoginStategy.m */, + 9D5B915617BD3784009DBABB /* FBSessionFacebookAppNativeLoginStategy.h */, + 9D5B915717BD3784009DBABB /* FBSessionFacebookAppNativeLoginStategy.m */, + 9D5B915C17BD3792009DBABB /* FBSessionFacebookAppWebLoginStategy.h */, + 9D5B915D17BD3792009DBABB /* FBSessionFacebookAppWebLoginStategy.m */, + 9D5B916817BD37A8009DBABB /* FBSessionInlineWebViewLoginStategy.h */, + 9D5B916917BD37A8009DBABB /* FBSessionInlineWebViewLoginStategy.m */, + 9D5B916217BD379C009DBABB /* FBSessionSafariLoginStategy.h */, + 9D5B916317BD379C009DBABB /* FBSessionSafariLoginStategy.m */, + 9D5B914A17BD3761009DBABB /* FBSessionSystemLoginStategy.h */, + 9D5B914B17BD3761009DBABB /* FBSessionSystemLoginStategy.m */, + ); + name = FBSessionLoginStrategy; + sourceTree = ""; + }; B51484E516C6223D0041257D /* Cryptography */ = { isa = PBXGroup; children = ( @@ -1048,9 +1174,12 @@ B9CBC54015254CAE0036AA71 /* FacebookSDKTests */ = { isa = PBXGroup; children = ( - 85ADAB0116A6082D00145328 /* FacebookSDKTests.xcconfig */, 85ADAAC016A0DA5000145328 /* Authentication */, - B9CBC54415254CC00036AA71 /* Supporting Files */, + 85ADAB0116A6082D00145328 /* FacebookSDKTests.xcconfig */, + 85BDF76117CD57C3002E7225 /* FBAppBridgeTests.h */, + 85BDF76217CD57C3002E7225 /* FBAppBridgeTests.m */, + 9D332F401782343A001715AE /* FBAppCallTests.h */, + 9D332F411782343A001715AE /* FBAppCallTests.m */, B59DA058170CE09000955BCD /* FBAppLinkDataTests.h */, B59DA059170CE09000955BCD /* FBAppLinkDataTests.m */, 85DF1125156C64140082AA04 /* FBBatchRequestTests.h */, @@ -1059,22 +1188,22 @@ B9CBC54215254CBD0036AA71 /* FBCacheTests.m */, 84E374BD153CC1140043B59C /* FBGraphObjectTests.h */, 84E374BE153CC1140043B59C /* FBGraphObjectTests.m */, - 85877C03169A3FC500A6D70A /* FBRequestTests.h */, - 85877C01169A3FBC00A6D70A /* FBRequestTests.m */, 8525A5AE156EFCA1009F6F3F /* FBRequestConnectionTests.h */, 8525A5AF156EFCA1009F6F3F /* FBRequestConnectionTests.m */, + 85877C03169A3FC500A6D70A /* FBRequestTests.h */, + 85877C01169A3FBC00A6D70A /* FBRequestTests.m */, 84B5F1131552E4AF00A55DDC /* FBSessionTests.h */, 84B5F1141552E4AF00A55DDC /* FBSessionTests.m */, + 7EBC39BB171CF1E400574F4E /* FBSettingsTests.h */, + 7EBC39BC171CF1E400574F4E /* FBSettingsTests.m */, 84FA4278153E1968009CEEF8 /* FBTestBlocker.h */, 84FA4279153E1968009CEEF8 /* FBTestBlocker.m */, 858E42481565EFC400246151 /* FBTests.h */, 858E424D1565FA2E00246151 /* FBTests.m */, 85ADA90F16A0B8B000145328 /* FBURLConnectionTests.h */, 85C60EE31698CFC000E7BB7D /* FBURLConnectionTests.m */, - 7EBC39BB171CF1E400574F4E /* FBSettingsTests.h */, - 7EBC39BC171CF1E400574F4E /* FBSettingsTests.m */, - 9D332F401782343A001715AE /* FBAppCallTests.h */, - 9D332F411782343A001715AE /* FBAppCallTests.m */, + 85BDF76417CE7B76002E7225 /* Matchers */, + B9CBC54415254CC00036AA71 /* Supporting Files */, ); name = FacebookSDKTests; sourceTree = ""; @@ -1099,6 +1228,7 @@ E23E5A0B1521161900A011A8 /* FBError.h in Headers */, B9DC7F40151AB56100DF1158 /* FBProfilePictureView.h in Headers */, 9DF9B2FF16851828008B6CC0 /* FBAccessTokenData.h in Headers */, + 9D3D36B217CBE6C500B9B049 /* FBTask.h in Headers */, AEA93B0B11D5293B000A4545 /* FBRequest.h in Headers */, 849CB18C1523416B00112220 /* FBLoginDialog.h in Headers */, 849CB18D1523416B00112220 /* FBDialog.h in Headers */, @@ -1119,6 +1249,7 @@ 84E0CA04153618E500778DA4 /* FacebookSDK.h in Headers */, 7EE2A6E116DE7D15009C2BA4 /* FBShareDialogParams.h in Headers */, 84E0CA061536198400778DA4 /* FBConnect.h in Headers */, + 9D3D36AE17CBE6C500B9B049 /* FBTaskCompletionSource.h in Headers */, 84E0CA0A153619D500778DA4 /* Facebook.h in Headers */, 84E0CA0C153619D500778DA4 /* FBDataDiskCache.h in Headers */, 85AA4B8F1545C54800E5352E /* FBSession+Internal.h in Headers */, @@ -1140,6 +1271,7 @@ 840F6595159B441D005D41AA /* FBLoginView.h in Headers */, DDB7C34C15A6181100C8DCE6 /* FBSettings.h in Headers */, 85E4AC7715B63CB600F17346 /* FBUserSettingsViewController.h in Headers */, + 9D3D36B317CBE6C500B9B049 /* FBTask+Private.h in Headers */, 85E4AC7D15B63CC500F17346 /* FBViewController.h in Headers */, 2A68590615C1E37E001D4EDD /* FBSettings+Internal.h in Headers */, 85F99E3F15C3751100D807A5 /* FBViewController+Internal.h in Headers */, @@ -1181,6 +1313,17 @@ 9D366B20178C7798007B4CEC /* FBRequestHandlerFactory.h in Headers */, 9D366B26178DC002007B4CEC /* FBRequestConnectionRetryManager.h in Headers */, B549647517A8703E002C9284 /* FBSessionAuthLogger.h in Headers */, + 9D3FC9AB17BA971C0072D6BC /* FBSessionUtility.h in Headers */, + 9D393AE717BAEE5B00658BC5 /* FBSessionLoginStrategy.h in Headers */, + 9D3B0D8217BC230B00CA3C04 /* FBSessionLoginStrategyParams.h in Headers */, + 9D5B914417BD3570009DBABB /* FBSession+FBSessionLoginStrategy.h in Headers */, + 9D5B914C17BD3761009DBABB /* FBSessionSystemLoginStategy.h in Headers */, + 9D5B915217BD3773009DBABB /* FBSessionAppSwitchingLoginStategy.h in Headers */, + 9D5B915817BD3784009DBABB /* FBSessionFacebookAppNativeLoginStategy.h in Headers */, + 9D5B915E17BD3792009DBABB /* FBSessionFacebookAppWebLoginStategy.h in Headers */, + 9D5B916417BD379C009DBABB /* FBSessionSafariLoginStategy.h in Headers */, + 9D5B916A17BD37A8009DBABB /* FBSessionInlineWebViewLoginStategy.h in Headers */, + 85BDF76717CE7FDF002E7225 /* FBIsURLHavingQueryParams.h in Headers */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -1194,7 +1337,6 @@ 85A44B9A16A8CE05007BE80E /* Sources */, 85A44B9B16A8CE05007BE80E /* Frameworks */, 85A44B9C16A8CE05007BE80E /* Resources */, - 85A44B9D16A8CE05007BE80E /* ShellScript */, ); buildRules = ( 99A90A53177104670025A7F7 /* PBXBuildRule */, @@ -1215,7 +1357,6 @@ B9CBC514152537270036AA71 /* Sources */, B9CBC515152537270036AA71 /* Frameworks */, B9CBC516152537270036AA71 /* Resources */, - B9CBC517152537270036AA71 /* ShellScript */, ); buildRules = ( 99A90A53177104670025A7F7 /* PBXBuildRule */, @@ -1348,19 +1489,6 @@ /* End PBXResourcesBuildPhase section */ /* Begin PBXShellScriptBuildPhase section */ - 85A44B9D16A8CE05007BE80E /* ShellScript */ = { - isa = PBXShellScriptBuildPhase; - buildActionMask = 2147483647; - files = ( - ); - inputPaths = ( - ); - outputPaths = ( - ); - runOnlyForDeploymentPostprocessing = 0; - shellPath = /bin/sh; - shellScript = "# Run the unit tests in this test bundle.\n\"${SYSTEM_DEVELOPER_DIR}/Tools/RunUnitTests\"\n"; - }; 85C9D0E516A7957A00D0ED57 /* ShellScript */ = { isa = PBXShellScriptBuildPhase; buildActionMask = 2147483647; @@ -1377,19 +1505,6 @@ shellScript = "HCSOURCE=\"${SRCROOT}/../vendor/OCHamcrest/Source\"\nHCFRAMEWORKDIR=\"${HCSOURCE}/build/Release/OCHamcrest.framework\"\ncd \"${HCSOURCE}\"\n\ntest -e \"${HCSOURCE}\" || { echo FATAL: OCHamcrest submodule does not appear to be updated; exit 1; }\n\n# xcodebuild should take care of this, but does not work consistently when MakeIOSFramework.sh is\n# launched as part of a build step.\nmkdir -p \"${HCFRAMEWORKDIR}/Versions\"\nln -sf A \"${HCFRAMEWORKDIR}/Versions/Current\"\nln -sf Versions/Current/Resources \"${HCFRAMEWORKDIR}/Resources\"\nln -sf Versions/Current/OCHamcrest \"${HCFRAMEWORKDIR}/OCHamcrest\"\nln -sf Versions/Current/Headers \"${HCFRAMEWORKDIR}/Headers\"\n\n./MakeIOSFramework.sh\n"; showEnvVarsInLog = 0; }; - B9CBC517152537270036AA71 /* ShellScript */ = { - isa = PBXShellScriptBuildPhase; - buildActionMask = 2147483647; - files = ( - ); - inputPaths = ( - ); - outputPaths = ( - ); - runOnlyForDeploymentPostprocessing = 0; - shellPath = /bin/sh; - shellScript = "# Run the unit tests in this test bundle.\n\"${SYSTEM_DEVELOPER_DIR}/Tools/RunUnitTests\"\n"; - }; /* End PBXShellScriptBuildPhase section */ /* Begin PBXSourcesBuildPhase section */ @@ -1420,9 +1535,11 @@ 85A44BF616A8D507007BE80E /* FBGraphObject.m in Sources */, 85A44BF716A8D507007BE80E /* FBGraphObjectTableCell.m in Sources */, 85A44BF816A8D507007BE80E /* FBGraphObjectPagingLoader.m in Sources */, + 9D3D36AD17CBE6C500B9B049 /* FBTaskCompletionSource.m in Sources */, 85A44BF916A8D507007BE80E /* FBGraphObjectTableDataSource.m in Sources */, 85A44BFA16A8D507007BE80E /* FBGraphObjectTableSelection.m in Sources */, 85A44BFB16A8D507007BE80E /* FBAppEvents.m in Sources */, + 9D3D36B117CBE6C500B9B049 /* FBTask.m in Sources */, 85A44BFC16A8D507007BE80E /* FBLogger.m in Sources */, 85A44BFD16A8D507007BE80E /* FBLoginDialog.m in Sources */, 85A44BFF16A8D507007BE80E /* FBNativeDialogs.m in Sources */, @@ -1470,6 +1587,14 @@ 9D366B23178C7798007B4CEC /* FBRequestHandlerFactory.m in Sources */, 9D366B29178DC002007B4CEC /* FBRequestConnectionRetryManager.m in Sources */, B549647817A8703E002C9284 /* FBSessionAuthLogger.m in Sources */, + 9D3FC9AE17BA971C0072D6BC /* FBSessionUtility.m in Sources */, + 9D3B0D8517BC230B00CA3C04 /* FBSessionLoginStrategyParams.m in Sources */, + 9D5B914F17BD3761009DBABB /* FBSessionSystemLoginStategy.m in Sources */, + 9D5B915517BD3773009DBABB /* FBSessionAppSwitchingLoginStategy.m in Sources */, + 9D5B915B17BD3784009DBABB /* FBSessionFacebookAppNativeLoginStategy.m in Sources */, + 9D5B916117BD3792009DBABB /* FBSessionFacebookAppWebLoginStategy.m in Sources */, + 9D5B916717BD379C009DBABB /* FBSessionSafariLoginStategy.m in Sources */, + 9D5B916D17BD37A8009DBABB /* FBSessionInlineWebViewLoginStategy.m in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -1492,6 +1617,7 @@ 84E374BF153CC1140043B59C /* FBGraphObjectTests.m in Sources */, 84D2FC13153CC34300D7F814 /* FBGraphObject.m in Sources */, 84FA4261153E155E009CEEF8 /* FBRequest.m in Sources */, + 9D3D36AC17CBE6C500B9B049 /* FBTaskCompletionSource.m in Sources */, 84FA4266153E15A3009CEEF8 /* FBFrictionlessRequestSettings.m in Sources */, 84FA426A153E15A3009CEEF8 /* FBRequestConnection.m in Sources */, 84FA426B153E15A3009CEEF8 /* FBSessionTokenCachingStrategy.m in Sources */, @@ -1552,6 +1678,18 @@ 9D366B22178C7798007B4CEC /* FBRequestHandlerFactory.m in Sources */, 9D366B28178DC002007B4CEC /* FBRequestConnectionRetryManager.m in Sources */, B549647717A8703E002C9284 /* FBSessionAuthLogger.m in Sources */, + 9D3FC9AD17BA971C0072D6BC /* FBSessionUtility.m in Sources */, + 9D3B0D8417BC230B00CA3C04 /* FBSessionLoginStrategyParams.m in Sources */, + 9D5B914E17BD3761009DBABB /* FBSessionSystemLoginStategy.m in Sources */, + 9D5B915417BD3773009DBABB /* FBSessionAppSwitchingLoginStategy.m in Sources */, + 9D5B915A17BD3784009DBABB /* FBSessionFacebookAppNativeLoginStategy.m in Sources */, + 9D5B916017BD3792009DBABB /* FBSessionFacebookAppWebLoginStategy.m in Sources */, + 9D5B916617BD379C009DBABB /* FBSessionSafariLoginStategy.m in Sources */, + 9D3D36B017CBE6C500B9B049 /* FBTask.m in Sources */, + 9D5B916C17BD37A8009DBABB /* FBSessionInlineWebViewLoginStategy.m in Sources */, + 85BDF76317CD57C3002E7225 /* FBAppBridgeTests.m in Sources */, + 857E927717CE959200F5F2BC /* FBIsURLHavingQueryParams.m in Sources */, + 857E927A17CE9C9800F5F2BC /* FBIsStringRepresentingJSONDictionary.m in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -1604,6 +1742,7 @@ 9D17A8DE1671774B00AB1148 /* FBSystemAccountStoreAdapter.m in Sources */, 5F0572BA16156625008B54E6 /* FBAppEvents.m in Sources */, 5F8BE21E164A30FD006329D6 /* FBSessionAppEventsState.m in Sources */, + 9D3D36AF17CBE6C500B9B049 /* FBTask.m in Sources */, 9DF9B30016851828008B6CC0 /* FBAccessTokenData.m in Sources */, 5F5F7E6716B21A500031AA95 /* FBFetchedAppSettings.m in Sources */, 84AD5AAE169602670026E6C3 /* FBWebDialogs.m in Sources */, @@ -1627,6 +1766,15 @@ 9D366B21178C7798007B4CEC /* FBRequestHandlerFactory.m in Sources */, 9D366B27178DC002007B4CEC /* FBRequestConnectionRetryManager.m in Sources */, B549647617A8703E002C9284 /* FBSessionAuthLogger.m in Sources */, + 9D3FC9AC17BA971C0072D6BC /* FBSessionUtility.m in Sources */, + 9D3B0D8317BC230B00CA3C04 /* FBSessionLoginStrategyParams.m in Sources */, + 9D5B914D17BD3761009DBABB /* FBSessionSystemLoginStategy.m in Sources */, + 9D5B915317BD3773009DBABB /* FBSessionAppSwitchingLoginStategy.m in Sources */, + 9D3D36AB17CBE6C500B9B049 /* FBTaskCompletionSource.m in Sources */, + 9D5B915917BD3784009DBABB /* FBSessionFacebookAppNativeLoginStategy.m in Sources */, + 9D5B915F17BD3792009DBABB /* FBSessionFacebookAppWebLoginStategy.m in Sources */, + 9D5B916517BD379C009DBABB /* FBSessionSafariLoginStategy.m in Sources */, + 9D5B916B17BD37A8009DBABB /* FBSessionInlineWebViewLoginStategy.m in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -1669,7 +1817,6 @@ isa = XCBuildConfiguration; buildSettings = { ALWAYS_SEARCH_USER_PATHS = NO; - ARCHS = "$(ARCHS_STANDARD_32_BIT)"; COPY_PHASE_STRIP = NO; DSTROOT = /tmp/facebook_ios_sdk.dst; FRAMEWORK_SEARCH_PATHS = "$(inherited)"; @@ -1691,9 +1838,10 @@ isa = XCBuildConfiguration; buildSettings = { ALWAYS_SEARCH_USER_PATHS = NO; - ARCHS = "$(ARCHS_STANDARD_32_BIT)"; + DEBUG_INFORMATION_FORMAT = dwarf; DSTROOT = /tmp/facebook_ios_sdk.dst; FRAMEWORK_SEARCH_PATHS = "$(inherited)"; + GCC_GENERATE_DEBUGGING_SYMBOLS = NO; GCC_MODEL_TUNING = G5; GCC_PRECOMPILE_PREFIX_HEADER = YES; GCC_PREFIX_HEADER = facebook_ios_sdk_Prefix.pch; @@ -1806,6 +1954,217 @@ }; name = Release; }; + 85AEC52D17E3930F0073BFEA /* Release64 */ = { + isa = XCBuildConfiguration; + buildSettings = { + ARCHS = "$(ARCHS_STANDARD_64_BIT)"; + GCC_C_LANGUAGE_STANDARD = c99; + GCC_THUMB_SUPPORT = NO; + GCC_WARN_ABOUT_RETURN_TYPE = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + IPHONEOS_DEPLOYMENT_TARGET = 6.0; + OTHER_LDFLAGS = "-ObjC"; + SDKROOT = iphoneos; + }; + name = Release64; + }; + 85AEC52E17E3930F0073BFEA /* Release64 */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + DEBUG_INFORMATION_FORMAT = dwarf; + DSTROOT = /tmp/facebook_ios_sdk.dst; + FRAMEWORK_SEARCH_PATHS = "$(inherited)"; + GCC_GENERATE_DEBUGGING_SYMBOLS = NO; + GCC_MODEL_TUNING = G5; + GCC_PRECOMPILE_PREFIX_HEADER = YES; + GCC_PREFIX_HEADER = facebook_ios_sdk_Prefix.pch; + INSTALL_PATH = "$(BUILT_PRODUCTS_DIR)"; + PRODUCT_NAME = facebook_ios_sdk; + PUBLIC_HEADERS_FOLDER_PATH = "$(TARGET_NAME)"; + RUN_CLANG_STATIC_ANALYZER = YES; + SKIP_INSTALL = YES; + }; + name = Release64; + }; + 85AEC52F17E3930F0073BFEA /* Release64 */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = 85ADAB0116A6082D00145328 /* FacebookSDKTests.xcconfig */; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + ARCHS = "$(ARCHS_STANDARD_32_BIT)"; + COPY_PHASE_STRIP = YES; + FRAMEWORK_SEARCH_PATHS = ( + "\"$(SRCROOT)/../vendor/OCHamcrest/Source/build/Release\"/**", + "\"$(SDKROOT)/Developer/Library/Frameworks\"", + "\"$(DEVELOPER_LIBRARY_DIR)/Frameworks\"", + ); + GCC_C_LANGUAGE_STANDARD = gnu99; + GCC_GENERATE_TEST_COVERAGE_FILES = YES; + GCC_INSTRUMENT_PROGRAM_FLOW_ARCS = YES; + GCC_PRECOMPILE_PREFIX_HEADER = YES; + GCC_PREFIX_HEADER = "tests/FacebookSDKTests-Prefix.pch"; + GCC_VERSION = ""; + GCC_WARN_UNINITIALIZED_AUTOS = YES; + HEADER_SEARCH_PATHS = "$(CONFIGURATION_BUILD_DIR)/**"; + INFOPLIST_FILE = "tests/FacebookSDKTests-Info.plist"; + IPHONEOS_DEPLOYMENT_TARGET = 5.0; + OTHER_LDFLAGS = ( + "-ObjC", + "-framework", + SenTestingKit, + "-all_load", + ); + PRODUCT_NAME = "$(TARGET_NAME)"; + VALIDATE_PRODUCT = YES; + WRAPPER_EXTENSION = octest; + }; + name = Release64; + }; + 85AEC53017E3930F0073BFEA /* Release64 */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = 85ADAB0116A6082D00145328 /* FacebookSDKTests.xcconfig */; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; + CLANG_CXX_LIBRARY = "libc++"; + CLANG_ENABLE_OBJC_ARC = NO; + CLANG_WARN_EMPTY_BODY = YES; + CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; + COPY_PHASE_STRIP = YES; + FRAMEWORK_SEARCH_PATHS = ( + "\"$(SDKROOT)/Developer/Library/Frameworks\"", + "\"$(DEVELOPER_LIBRARY_DIR)/Frameworks\"", + ); + GCC_C_LANGUAGE_STANDARD = gnu99; + GCC_GENERATE_TEST_COVERAGE_FILES = YES; + GCC_INSTRUMENT_PROGRAM_FLOW_ARCS = YES; + GCC_PRECOMPILE_PREFIX_HEADER = YES; + GCC_PREFIX_HEADER = "FacebookSDKIntegrationTests/FacebookSDKIntegrationTests-Prefix.pch"; + GCC_WARN_UNINITIALIZED_AUTOS = YES; + "HEADER_SEARCH_PATHS[arch=*]" = "$(CONFIGURATION_BUILD_DIR)"; + INFOPLIST_FILE = "FacebookSDKIntegrationTests/FacebookSDKIntegrationTests-Info.plist"; + IPHONEOS_DEPLOYMENT_TARGET = 6.0; + PRODUCT_NAME = "$(TARGET_NAME)"; + VALIDATE_PRODUCT = YES; + WRAPPER_EXTENSION = octest; + }; + name = Release64; + }; + 85AEC53617E3931A0073BFEA /* Debug64 */ = { + isa = XCBuildConfiguration; + buildSettings = { + ARCHS = "$(ARCHS_STANDARD_64_BIT)"; + GCC_C_LANGUAGE_STANDARD = c99; + GCC_OPTIMIZATION_LEVEL = 0; + GCC_THUMB_SUPPORT = NO; + GCC_WARN_ABOUT_RETURN_TYPE = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + IPHONEOS_DEPLOYMENT_TARGET = 6.0; + OTHER_LDFLAGS = "-ObjC"; + SDKROOT = iphoneos; + }; + name = Debug64; + }; + 85AEC53717E3931A0073BFEA /* Debug64 */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + COPY_PHASE_STRIP = NO; + DSTROOT = /tmp/facebook_ios_sdk.dst; + FRAMEWORK_SEARCH_PATHS = "$(inherited)"; + GCC_DYNAMIC_NO_PIC = NO; + GCC_MODEL_TUNING = G5; + GCC_OPTIMIZATION_LEVEL = 0; + GCC_PRECOMPILE_PREFIX_HEADER = YES; + GCC_PREFIX_HEADER = facebook_ios_sdk_Prefix.pch; + INSTALL_PATH = "$(BUILT_PRODUCTS_DIR)"; + PRODUCT_NAME = facebook_ios_sdk; + PUBLIC_HEADERS_FOLDER_PATH = "$(TARGET_NAME)"; + RUN_CLANG_STATIC_ANALYZER = YES; + SKIP_INSTALL = YES; + }; + name = Debug64; + }; + 85AEC53817E3931A0073BFEA /* Debug64 */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = 85ADAB0116A6082D00145328 /* FacebookSDKTests.xcconfig */; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + ARCHS = "$(ARCHS_STANDARD_32_BIT)"; + CODE_SIGN_IDENTITY = ""; + COPY_PHASE_STRIP = NO; + FRAMEWORK_SEARCH_PATHS = ( + "\"$(SDKROOT)/Developer/Library/Frameworks\"", + "\"$(DEVELOPER_LIBRARY_DIR)/Frameworks\"", + "\"$(SRCROOT)/../vendor/OCHamcrest/Source/build/Release\"/**", + ); + GCC_C_LANGUAGE_STANDARD = gnu99; + GCC_DYNAMIC_NO_PIC = NO; + GCC_GENERATE_TEST_COVERAGE_FILES = YES; + GCC_INSTRUMENT_PROGRAM_FLOW_ARCS = YES; + GCC_PRECOMPILE_PREFIX_HEADER = YES; + GCC_PREFIX_HEADER = "tests/FacebookSDKTests-Prefix.pch"; + GCC_PREPROCESSOR_DEFINITIONS = ( + "DEBUG=1", + "$(inherited)", + ); + "GCC_PREPROCESSOR_DEFINITIONS[arch=*]" = ( + "DEBUG=1", + "$(inherited)", + ); + GCC_SYMBOLS_PRIVATE_EXTERN = NO; + GCC_VERSION = ""; + GCC_WARN_UNINITIALIZED_AUTOS = YES; + HEADER_SEARCH_PATHS = "$(CONFIGURATION_BUILD_DIR)/**"; + INFOPLIST_FILE = "tests/FacebookSDKTests-Info.plist"; + IPHONEOS_DEPLOYMENT_TARGET = 5.0; + OTHER_LDFLAGS = ( + "-ObjC", + "-framework", + SenTestingKit, + "-all_load", + ); + PRODUCT_NAME = "$(TARGET_NAME)"; + WRAPPER_EXTENSION = octest; + }; + name = Debug64; + }; + 85AEC53917E3931A0073BFEA /* Debug64 */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = 85ADAB0116A6082D00145328 /* FacebookSDKTests.xcconfig */; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; + CLANG_CXX_LIBRARY = "libc++"; + CLANG_ENABLE_OBJC_ARC = NO; + CLANG_WARN_EMPTY_BODY = YES; + CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; + COPY_PHASE_STRIP = NO; + FRAMEWORK_SEARCH_PATHS = ( + "\"$(SDKROOT)/Developer/Library/Frameworks\"", + "\"$(DEVELOPER_LIBRARY_DIR)/Frameworks\"", + ); + GCC_C_LANGUAGE_STANDARD = gnu99; + GCC_DYNAMIC_NO_PIC = NO; + GCC_GENERATE_TEST_COVERAGE_FILES = YES; + GCC_INSTRUMENT_PROGRAM_FLOW_ARCS = YES; + GCC_PRECOMPILE_PREFIX_HEADER = YES; + GCC_PREFIX_HEADER = "FacebookSDKIntegrationTests/FacebookSDKIntegrationTests-Prefix.pch"; + GCC_PREPROCESSOR_DEFINITIONS = ( + "DEBUG=1", + "$(inherited)", + ); + GCC_SYMBOLS_PRIVATE_EXTERN = NO; + GCC_WARN_UNINITIALIZED_AUTOS = YES; + "HEADER_SEARCH_PATHS[arch=*]" = "$(CONFIGURATION_BUILD_DIR)"; + INFOPLIST_FILE = "FacebookSDKIntegrationTests/FacebookSDKIntegrationTests-Info.plist"; + IPHONEOS_DEPLOYMENT_TARGET = 6.0; + PRODUCT_NAME = "$(TARGET_NAME)"; + WRAPPER_EXTENSION = octest; + }; + name = Debug64; + }; B9CBC529152537270036AA71 /* Debug */ = { isa = XCBuildConfiguration; baseConfigurationReference = 85ADAB0116A6082D00145328 /* FacebookSDKTests.xcconfig */; @@ -1891,7 +2250,9 @@ isa = XCConfigurationList; buildConfigurations = ( 1DEB921F08733DC00010E9CD /* Debug */, + 85AEC53717E3931A0073BFEA /* Debug64 */, 1DEB922008733DC00010E9CD /* Release */, + 85AEC52E17E3930F0073BFEA /* Release64 */, ); defaultConfigurationIsVisible = 0; defaultConfigurationName = Release; @@ -1900,7 +2261,9 @@ isa = XCConfigurationList; buildConfigurations = ( 1DEB922308733DC00010E9CD /* Debug */, + 85AEC53617E3931A0073BFEA /* Debug64 */, 1DEB922408733DC00010E9CD /* Release */, + 85AEC52D17E3930F0073BFEA /* Release64 */, ); defaultConfigurationIsVisible = 0; defaultConfigurationName = Release; @@ -1909,7 +2272,9 @@ isa = XCConfigurationList; buildConfigurations = ( 85A44BAE16A8CE05007BE80E /* Debug */, + 85AEC53917E3931A0073BFEA /* Debug64 */, 85A44BAF16A8CE05007BE80E /* Release */, + 85AEC53017E3930F0073BFEA /* Release64 */, ); defaultConfigurationIsVisible = 0; defaultConfigurationName = Release; @@ -1918,7 +2283,9 @@ isa = XCConfigurationList; buildConfigurations = ( B9CBC529152537270036AA71 /* Debug */, + 85AEC53817E3931A0073BFEA /* Debug64 */, B9CBC52A152537270036AA71 /* Release */, + 85AEC52F17E3930F0073BFEA /* Release64 */, ); defaultConfigurationIsVisible = 0; defaultConfigurationName = Release; diff --git a/src/tests/FBAppBridgeTests.h b/src/tests/FBAppBridgeTests.h new file mode 100644 index 0000000000..b392c0b644 --- /dev/null +++ b/src/tests/FBAppBridgeTests.h @@ -0,0 +1,21 @@ +/* + * Copyright 2010-present 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 "FBTests.h" + +@interface FBAppBridgeTests : FBTests + +@end diff --git a/src/tests/FBAppBridgeTests.m b/src/tests/FBAppBridgeTests.m new file mode 100644 index 0000000000..d969de3c48 --- /dev/null +++ b/src/tests/FBAppBridgeTests.m @@ -0,0 +1,651 @@ +/* + * Copyright 2010-present 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 + +#import "FBAppBridgeTests.h" +#import "FBAppBridge.h" +#import "FBSettings.h" +#import "FBUtility.h" +#import "FBTestBlocker.h" +#import "FBAppCall+Internal.h" +#import "FBDialogsData+Internal.h" +#import "FBIsURLHavingQueryParams.h" +#import "FBIsStringRepresentingJSONDictionary.h" +#import "FBError.h" + +NSString * const kTestAppID = @"123456789"; +NSString * const kTestURLScheme = @"fb123456789"; +NSString * const kTestVersion = @"1"; +NSString * const kTestAppName = @"Awesome App"; +NSString * const kTestURLSchemeSuffix = @"mysuffix"; +NSString * const kTestNonFacebookBundleIdentifier = @"com.notfacebook.hello"; +NSString * const kTestFacebookBundleIdentifier = @"com.facebook.hello"; + +// If we create an FBDialogsData, we'll use this data. +NSString * const kTestDialogMethod = @"some_dialog"; + +NSString * const kNonAppBridgeAppCallURL = @"fb123456789://link?meal=Chicken&fb_applink_args=%7B%22version%22%3A2%2C%22bridge_args%22%3A%7B%22method%22%3A%22applink%22%7D%2C%22method_args%22%3A%7B%22ref%22%3A%22Tiramisu%22%7D%7D&fb_click_time_utc=123"; + +@interface FBAppBridge (Testing) + +@property (nonatomic, retain) NSMutableDictionary *pendingAppCalls; +@property (nonatomic, retain) NSMutableDictionary *callbacks; + ++ (NSString *)symmetricKeyAndForceRefresh:(BOOL)forceRefresh; + +- (void)performDialogAppCall:(FBAppCall *)appCall + version:(NSString *)version + session:(FBSession *)session + completionHandler:(FBAppCallHandler)handler; + +@end + +@implementation FBAppBridgeTests +{ + id _mockApplication; + // This one is just here to keep UIApplication class methods mocked without a circular reference. + id _anotherMockApplication; + id _mockFBSettings; + id _mockFBUtility; +} + +#pragma mark Helpers + +- (void)setUp { + // FBAppBridge relies on UIApplication for handling URLs; mock it and return + // the mock from [UIApplication sharedApplication]. This little dance is necessary to avoid + // a circular reference that keeps the class method from being unmocked. + _mockApplication = [OCMockObject mockForClass:[UIApplication class]]; + _anotherMockApplication = [OCMockObject mockForClass:[UIApplication class]]; + [[[_anotherMockApplication stub] andReturn:_mockApplication] sharedApplication]; + + // FBAppBridge assumes it can get an app ID. + _mockFBSettings = [OCMockObject mockForClass:[FBSettings class]]; + [[[_mockFBSettings stub] andReturn:kTestAppID] defaultAppID]; + [[[_mockFBSettings stub] andReturn:kTestAppName] defaultDisplayName]; + + // Pretend that all URL schemes are registered or [FBAppCall init] will fail. + _mockFBUtility = [OCMockObject mockForClass:[FBUtility class]]; + BOOL yes = YES; + [[[_mockFBUtility stub] andReturnValue:OCMOCK_VALUE(yes)] + isRegisteredURLScheme:kTestURLScheme]; +} + +- (void)tearDown { + // Deallocing the mock will also revert the class method mocking. + _mockApplication = nil; + _anotherMockApplication = nil; + _mockFBSettings = nil; + _mockFBUtility = nil; +} + +- (FBAppCall *)newAppCall:(BOOL)withDialogsData { + return [self newAppCall:withDialogsData arguments:nil]; +} + +- (FBAppCall *)newAppCall:(BOOL)withDialogsData arguments:(NSDictionary *)arguments { + FBAppCall *appCall = [[[FBAppCall alloc] init] autorelease]; + if (withDialogsData) { + appCall.dialogData = [[[FBDialogsData alloc] initWithMethod:kTestDialogMethod + arguments:arguments] autorelease]; + } + return appCall; +} + +// Helpers to construct incoming URLs for testing handleOpenURL, etc. +- (NSURL *)newAppBridgeURL { + return [self newAppBridgeURL:nil version:nil]; +} + +- (NSURL *)newAppBridgeURL:(NSString *)callId version:(NSString *)version { + return [self newAppBridgeURL:callId version:version methodArgs:nil clientState:nil]; +} + +- (NSURL *)newAppBridgeURL:(NSString *)callId + version:(NSString *)version + methodArgs:(NSDictionary *)methodArgs + clientState:(NSDictionary *)clientState { + NSMutableDictionary *params = [[[NSMutableDictionary alloc] init] autorelease]; + NSMutableDictionary *bridgeArgs = [[[NSMutableDictionary alloc] init] autorelease]; + + if (callId != nil) { + bridgeArgs[@"action_id"] = callId; + } + if (clientState.count > 0) { + bridgeArgs[@"client_state"] = [FBUtility simpleJSONEncode:clientState]; + } + if (bridgeArgs.count > 0) { + params[@"bridge_args"] = [FBUtility simpleJSONEncode:bridgeArgs]; + } + if (methodArgs.count > 0) { + params[@"method_args"] = [FBUtility simpleJSONEncode:methodArgs]; + } + if (version != nil) { + params[@"version"] = version; + } + + NSString *query = @""; + if (params.count > 0) { + query = [NSString stringWithFormat:@"?%@", [FBUtility stringBySerializingQueryParameters:params]]; + } + + NSString *urlString = [NSString stringWithFormat:@"%@://bridge/%@%@", + kTestURLScheme, kTestDialogMethod, query]; + return [NSURL URLWithString:urlString]; +} + + +#pragma mark Class method tests + +- (void)testSharedInstanceIsSingleton { + assertThat([FBAppBridge sharedInstance], equalTo([FBAppBridge sharedInstance])); +} + +#pragma mark dispatchDialogAppCall/performDialogAppCall tests + +- (void)testDispatchCallsPerformWithSameArgs { + // Most of the rest of our tests just call performDialogAppCall:... on the test + // thread to avoid threading complexity. But that shortcut assumes that dispatchDialogAppCall:... + // just dispatches performDialogAppCall: on the main thread. So this test asserts that + // the rest of the tests can make that simplifying assumption. + FBAppBridge *appBridge = [[[FBAppBridge alloc] init] autorelease]; + id mockAppBridge = [OCMockObject partialMockForObject:appBridge]; + + FBAppCall *appCall = [self newAppCall:NO]; + FBSession *session = [[[FBSession alloc] init] autorelease]; + id handler = ^(FBAppCall *call) { + }; + + [[mockAppBridge expect] performDialogAppCall:appCall + version:kTestVersion + session:session + completionHandler:handler]; + + [mockAppBridge dispatchDialogAppCall:appCall + version:kTestVersion + session:session + completionHandler:handler]; + + [self waitForMainQueueToFinish]; + [mockAppBridge verify]; + +} + +- (void)testNoDialogDataResultsInNoCall { + [[_mockApplication reject] openURL:OCMOCK_ANY]; + + FBAppBridge *appBridge = [[[FBAppBridge alloc] init] autorelease]; + + FBAppCall *appCall = [self newAppCall:NO]; + [appBridge performDialogAppCall:appCall + version:kTestVersion + session:nil + completionHandler:nil]; + + [_mockApplication verify]; +} + +- (void)testAppIDIncludedInGeneratedURL { + FBAppCall *appCall = [self newAppCall:YES]; + + id urlMatcher = hasQueryParams(hasEntry(@"app_id", kTestAppID)); + [[_mockApplication expect] openURL:urlMatcher]; + + FBAppBridge *appBridge = [[[FBAppBridge alloc] init] autorelease]; + [appBridge performDialogAppCall:appCall + version:kTestVersion + session:nil + completionHandler:nil]; + + [_mockApplication verify]; +} + +- (void)testInitFailsIfMissingAppID { + _mockFBSettings = [OCMockObject mockForClass:[FBSettings class]]; + [[[_mockFBSettings stub] andReturn:nil] defaultAppID]; + + STAssertThrowsSpecificNamed( + [[[FBAppBridge alloc] init] autorelease], + NSException, + FBInvalidOperationException, + @"expected exception"); + + [_mockApplication verify]; +} + +- (void)testAppMetadataIncludedInGeneratedURL { + FBAppCall *appCall = [self newAppCall:YES]; + + id urlMatcher = hasQueryParams(hasEntry(@"bridge_args", + representsJSONDictionary(hasEntries( + @"app_name", kTestAppName, + @"action_id", appCall.ID, + nil + )))); + [[_mockApplication expect] openURL:urlMatcher]; + + FBAppBridge *appBridge = [[[FBAppBridge alloc] init] autorelease]; + [appBridge performDialogAppCall:appCall + version:kTestVersion + session:nil + completionHandler:nil]; + + [_mockApplication verify]; +} + +// TODO add a test for app icon being serialized correctly + +- (void)testClientStateIncludedInGeneratedURL { + FBAppCall *appCall = [self newAppCall:YES]; + + NSDictionary *clientState = @{@"foo": @"bar", @"hello": @"world"}; + appCall.dialogData.clientState = clientState; + + id urlMatcher = hasQueryParams(hasEntry(@"bridge_args", + representsJSONDictionary(hasEntry(@"client_state", representsJSONDictionary(clientState))))); + [[_mockApplication expect] openURL:urlMatcher]; + + FBAppBridge *appBridge = [[[FBAppBridge alloc] init] autorelease]; + [appBridge performDialogAppCall:appCall + version:kTestVersion + session:nil + completionHandler:nil]; + + [_mockApplication verify]; +} + +- (void)testURLSchemeSuffixIncludedInGeneratedURL { + FBAppCall *appCall = [self newAppCall:YES]; + + FBSession *session = [[[FBSession alloc] initWithAppID:kTestAppID + permissions:nil + urlSchemeSuffix:kTestURLSchemeSuffix + tokenCacheStrategy:nil] autorelease]; + + id urlMatcher = hasQueryParams(hasEntry(@"scheme_suffix", kTestURLSchemeSuffix)); + [[_mockApplication expect] openURL:urlMatcher]; + + FBAppBridge *appBridge = [[[FBAppBridge alloc] init] autorelease]; + [appBridge performDialogAppCall:appCall + version:kTestVersion + session:session + completionHandler:nil]; + + [_mockApplication verify]; +} + +- (void)testMethodArgumentsIncludedInGeneratedURL { + NSDictionary *arguments = @{@"foo": @"bar", @"hello": @"world"}; + FBAppCall *appCall = [self newAppCall:YES arguments:arguments]; + + id urlMatcher = hasQueryParams(hasEntry(@"method_args", representsJSONDictionary(arguments))); + [[_mockApplication expect] openURL:urlMatcher]; + + FBAppBridge *appBridge = [[[FBAppBridge alloc] init] autorelease]; + [appBridge performDialogAppCall:appCall + version:kTestVersion + session:nil + completionHandler:nil]; + + [_mockApplication verify]; +} + +- (void)testAppCallIsTrackedOnSuccessfulOpen { + BOOL yes = YES; + [[[_mockApplication expect] andReturnValue:OCMOCK_VALUE(yes)] openURL:OCMOCK_ANY]; + + FBAppCall *appCall = [self newAppCall:YES]; + id handler = ^(FBAppCall *call) { + }; + + FBAppBridge *appBridge = [[[FBAppBridge alloc] init] autorelease]; + [appBridge performDialogAppCall:appCall + version:kTestVersion + session:nil + completionHandler:handler]; + + [_mockApplication verify]; + + assertThat(appBridge.pendingAppCalls, hasEntry(appCall.ID, appCall)); + assertThat(appBridge.callbacks, hasKey(appCall.ID)); +} + +- (void)testAppCallIsNotTrackedOnFailedOpen { + BOOL no = NO; + [[[_mockApplication expect] andReturnValue:OCMOCK_VALUE(no)] openURL:OCMOCK_ANY]; + + FBAppCall *appCall = [self newAppCall:YES]; + id handler = ^(FBAppCall *call) { + }; + + FBAppBridge *appBridge = [[[FBAppBridge alloc] init] autorelease]; + [appBridge performDialogAppCall:appCall + version:kTestVersion + session:nil + completionHandler:handler]; + + [_mockApplication verify]; + + assertThat(appBridge.pendingAppCalls, isNot(hasKey(appCall.ID))); + assertThat(appBridge.callbacks, isNot(hasKey(appCall.ID))); +} + +- (void)testHandlerCalledWithErrorOnFailedOpen { + BOOL no = NO; + [[[_mockApplication expect] andReturnValue:OCMOCK_VALUE(no)] openURL:OCMOCK_ANY]; + + FBAppCall *appCall = [self newAppCall:YES]; + BOOL __block handlerCalled = NO; + id handler = ^(FBAppCall *call) { + assertThat(call, notNilValue()); + assertThat(call.error, notNilValue()); + assertThat(call.error.domain, equalTo(FacebookSDKDomain)); + assertThatInteger(call.error.code, equalToInteger(FBErrorDialog)); + handlerCalled = YES; + }; + + FBAppBridge *appBridge = [[[FBAppBridge alloc] init] autorelease]; + [appBridge performDialogAppCall:appCall + version:kTestVersion + session:nil + completionHandler:handler]; + + [_mockApplication verify]; + + assertThatBool(handlerCalled, equalToBool(YES)); +} + +#pragma mark handleDidBecomeActive tests + +- (void)testPendingCallIsCanceledOnDidBecomeActive { + BOOL yes = YES; + [[[_mockApplication expect] andReturnValue:OCMOCK_VALUE(yes)] openURL:OCMOCK_ANY]; + + FBAppCall *appCall = [self newAppCall:YES]; + BOOL __block handlerCalled = NO; + id handler = ^(FBAppCall *call) { + assertThat(call, notNilValue()); + assertThat(call.error, notNilValue()); + assertThat(call.error.domain, equalTo(FacebookSDKDomain)); + assertThatInteger(call.error.code, equalToInteger(FBErrorAppActivatedWhilePendingAppCall)); + handlerCalled = YES; + }; + + FBAppBridge *appBridge = [[[FBAppBridge alloc] init] autorelease]; + [appBridge performDialogAppCall:appCall + version:kTestVersion + session:nil + completionHandler:handler]; + + [appBridge handleDidBecomeActive]; + + [_mockApplication verify]; + + assertThatBool(handlerCalled, equalToBool(YES)); + +} + +#pragma mark handleOpenURL tests + +- (void)testWrongURLSchemeReturnsNoWithoutCallingFallbackHandler { + NSURL *url = [NSURL URLWithString:@"nope://bridge/"]; + + BOOL __block handlerCalled = NO; + id handler = ^(FBAppCall *call) { + handlerCalled = YES; + }; + + FBAppBridge *appBridge = [[[FBAppBridge alloc] init] autorelease]; + BOOL result = [appBridge handleOpenURL:url + sourceApplication:nil + session:nil + fallbackHandler:handler]; + + assertThatBool(result, equalToBool(NO)); + assertThatBool(handlerCalled, equalToBool(NO)); +} + +- (void)testWrongURLHostReturnsNoWithoutCallingFallbackHandler { + NSURL *url = [NSURL URLWithString:[NSString stringWithFormat:@"%@/nope/", kTestURLScheme]]; + + BOOL __block handlerCalled = NO; + id handler = ^(FBAppCall *call) { + handlerCalled = YES; + }; + + FBAppBridge *appBridge = [[[FBAppBridge alloc] init] autorelease]; + BOOL result = [appBridge handleOpenURL:url + sourceApplication:nil + session:nil + fallbackHandler:handler]; + + assertThatBool(result, equalToBool(NO)); + assertThatBool(handlerCalled, equalToBool(NO)); +} + +- (void)testNonAppBridgeURLWithoutFallbackHandlerReturnsNo { + NSURL *url = [NSURL URLWithString:kNonAppBridgeAppCallURL]; + + FBAppBridge *appBridge = [[[FBAppBridge alloc] init] autorelease]; + BOOL result = [appBridge handleOpenURL:url + sourceApplication:nil + session:nil + fallbackHandler:nil]; + + assertThatBool(result, equalToBool(NO)); +} + +- (void)testNonAppBridgeURLCallsFallbackHandlerAndReturnsYes { + NSURL *url = [NSURL URLWithString:kNonAppBridgeAppCallURL]; + + BOOL __block handlerCalled = NO; + id handler = ^(FBAppCall *call) { + assertThat(call, notNilValue()); + handlerCalled = YES; + }; + + FBAppBridge *appBridge = [[[FBAppBridge alloc] init] autorelease]; + BOOL result = [appBridge handleOpenURL:url + sourceApplication:nil + session:nil + fallbackHandler:handler]; + + assertThatBool(result, equalToBool(YES)); + assertThatBool(handlerCalled, equalToBool(YES)); +} + +- (void)testNonFacebookSourceApplicationFails { + NSURL *url = [self newAppBridgeURL]; + + BOOL __block handlerCalled = NO; + id handler = ^(FBAppCall *call) { + assertThat(call, notNilValue()); + assertThat(call.error, notNilValue()); + assertThat(call.error.domain, equalTo(FacebookSDKDomain)); + assertThatInteger(call.error.code, equalToInteger(FBErrorUntrustedURL)); + + handlerCalled = YES; + }; + + FBAppBridge *appBridge = [[[FBAppBridge alloc] init] autorelease]; + BOOL result = [appBridge handleOpenURL:url + sourceApplication:kTestNonFacebookBundleIdentifier + session:nil + fallbackHandler:handler]; + + assertThatBool(result, equalToBool(YES)); + assertThatBool(handlerCalled, equalToBool(YES)); +} + +- (void)testNonFacebookSourceApplicationChangesSymmetricKey { + NSURL *url = [self newAppBridgeURL]; + + NSString *oldKey = [FBAppBridge symmetricKeyAndForceRefresh:NO]; + + FBAppBridge *appBridge = [[[FBAppBridge alloc] init] autorelease]; + BOOL result = [appBridge handleOpenURL:url + sourceApplication:kTestNonFacebookBundleIdentifier + session:nil + fallbackHandler:nil]; + + assertThatBool(result, equalToBool(YES)); + + NSString *newKey = [FBAppBridge symmetricKeyAndForceRefresh:NO]; + assertThat(oldKey, isNot(equalTo(newKey))); +} + +- (void)testURLWithoutCallIDFails { + NSURL *url = [self newAppBridgeURL:nil version:@"1"]; + + BOOL __block handlerCalled = NO; + id handler = ^(FBAppCall *call) { + assertThat(call, notNilValue()); + assertThat(call.error, notNilValue()); + assertThat(call.error.domain, equalTo(FacebookSDKDomain)); + assertThatInteger(call.error.code, equalToInteger(FBErrorMalformedURL)); + + handlerCalled = YES; + }; + + FBAppBridge *appBridge = [[[FBAppBridge alloc] init] autorelease]; + BOOL result = [appBridge handleOpenURL:url + sourceApplication:kTestFacebookBundleIdentifier + session:nil + fallbackHandler:handler]; + + assertThatBool(result, equalToBool(YES)); + assertThatBool(handlerCalled, equalToBool(YES)); +} + +- (void)testURLWithoutVersionFails { + NSURL *url = [self newAppBridgeURL:[FBUtility newUUIDString] version:nil]; + + BOOL __block handlerCalled = NO; + id handler = ^(FBAppCall *call) { + assertThat(call, notNilValue()); + assertThat(call.error, notNilValue()); + assertThat(call.error.domain, equalTo(FacebookSDKDomain)); + assertThatInteger(call.error.code, equalToInteger(FBErrorMalformedURL)); + + handlerCalled = YES; + }; + + FBAppBridge *appBridge = [[[FBAppBridge alloc] init] autorelease]; + BOOL result = [appBridge handleOpenURL:url + sourceApplication:kTestFacebookBundleIdentifier + session:nil + fallbackHandler:handler]; + + assertThatBool(result, equalToBool(YES)); + assertThatBool(handlerCalled, equalToBool(YES)); +} + +- (void)testURLWithUntrackedCallIDCallsFallbackHandler { + NSString *callID = [FBUtility newUUIDString]; + NSDictionary *methodArgs = @{@"foo": @"bar"}; + NSDictionary *clientState = @{@"hello": @"world"}; + NSURL *url = [self newAppBridgeURL:callID version:@"1" methodArgs:methodArgs clientState:clientState]; + + BOOL __block handlerCalled = NO; + id handler = ^(FBAppCall *call) { + assertThat(call, notNilValue()); + assertThat(call.error, nilValue()); + assertThat(call.ID, equalTo(callID)); + + assertThat(call.dialogData, notNilValue()); + assertThat(call.dialogData.method, equalTo(kTestDialogMethod)); + assertThat(call.dialogData.arguments, equalTo(methodArgs)); + assertThat(call.dialogData.clientState, equalTo(clientState)); + + handlerCalled = YES; + }; + + FBAppBridge *appBridge = [[[FBAppBridge alloc] init] autorelease]; + BOOL result = [appBridge handleOpenURL:url + sourceApplication:kTestFacebookBundleIdentifier + session:nil + fallbackHandler:handler]; + + assertThatBool(result, equalToBool(YES)); + assertThatBool(handlerCalled, equalToBool(YES)); +} + +- (void)testURLWithTrackedCallIDCallsHandler { + NSDictionary *methodArgs = @{@"foo": @"bar"}; + NSDictionary *clientState = @{@"hello": @"world"}; + + BOOL yes = YES; + [[[_mockApplication expect] andReturnValue:OCMOCK_VALUE(yes)] openURL:OCMOCK_ANY]; + + FBAppCall *appCall = [self newAppCall:YES arguments:methodArgs]; + appCall.dialogData.clientState = clientState; + + BOOL __block completionHandlerCalled = NO; + id completionHandler = ^(FBAppCall *call) { + assertThat(call, notNilValue()); + assertThat(call.error, nilValue()); + assertThat(call.ID, equalTo(appCall.ID)); + + assertThat(call.dialogData, notNilValue()); + assertThat(call.dialogData.method, equalTo(kTestDialogMethod)); + assertThat(call.dialogData.arguments, equalTo(methodArgs)); + assertThat(call.dialogData.clientState, equalTo(clientState)); + + completionHandlerCalled = YES; + }; + + FBAppBridge *appBridge = [[[FBAppBridge alloc] init] autorelease]; + [appBridge performDialogAppCall:appCall + version:kTestVersion + session:nil + completionHandler:completionHandler]; + + [_mockApplication verify]; + + assertThat(appBridge.pendingAppCalls, hasEntry(appCall.ID, appCall)); + assertThat(appBridge.callbacks, hasKey(appCall.ID)); + + // We should get original method args, client state, etc., even if they aren't in the URL. + NSURL *url = [self newAppBridgeURL:appCall.ID version:@"1" methodArgs:nil clientState:nil]; + + BOOL __block fallbackHandlerCalled = NO; + id fallbackHandler = ^(FBAppCall *call) { + fallbackHandlerCalled = YES; + }; + + BOOL result = [appBridge handleOpenURL:url + sourceApplication:kTestFacebookBundleIdentifier + session:nil + fallbackHandler:fallbackHandler]; + + assertThatBool(result, equalToBool(YES)); + assertThatBool(fallbackHandlerCalled, equalToBool(NO)); + assertThatBool(completionHandlerCalled, equalToBool(YES)); + + assertThat(appBridge.pendingAppCalls, isNot(hasKey(appCall.ID))); + assertThat(appBridge.callbacks, isNot(hasKey(appCall.ID))); +} + +#pragma mark Encryption/decryption tests + +// TODO + +#pragma mark Pasteboard tests + +// TODO + +@end diff --git a/src/tests/FBAppCallTests.m b/src/tests/FBAppCallTests.m index ebbcef04c1..41798df0df 100644 --- a/src/tests/FBAppCallTests.m +++ b/src/tests/FBAppCallTests.m @@ -15,7 +15,7 @@ */ #import "FBAppCallTests.h" -#import "FBAppCall.h" +#import "FBAppCall+Internal.h" @implementation FBAppCallTests @@ -63,4 +63,34 @@ -(void) testAppCallFromURLWithTapTime { STAssertEqualObjects(@"Tiramisu", target.appLinkData.arguments[@"ref"], @"Failed to parse applinkdata arguments"); STAssertEqualObjects(@"123", target.appLinkData.arguments[@"tap_time_utc"], @"Failed to parse applinkdata arguments (tap_time)"); } + +- (void)testAppCallsWithSameIDAreEqual { + FBAppCall *appCall1 = [[FBAppCall alloc] initWithID:nil + enforceScheme:NO + appID:nil + urlSchemeSuffix:nil]; + FBAppCall *appCall2 = [[FBAppCall alloc] initWithID:appCall1.ID + enforceScheme:NO + appID:nil + urlSchemeSuffix:nil]; + + assertThat(appCall1, equalTo(appCall2)); + assertThatInteger([appCall1 hash], equalToInteger([appCall2 hash])); +} + +- (void)testAppCallsAreNotEqual { + FBAppCall *appCall1 = [[FBAppCall alloc] initWithID:nil + enforceScheme:NO + appID:nil + urlSchemeSuffix:nil]; + FBAppCall *appCall2 = [[FBAppCall alloc] initWithID:nil + enforceScheme:NO + appID:nil + urlSchemeSuffix:nil]; + + assertThat(appCall1, isNot(equalTo(appCall2))); + assertThat(appCall1, isNot(equalTo(nil))); + assertThat(appCall1, isNot(equalTo(@"string"))); +} + @end diff --git a/src/tests/FBAuthenticationTests.m b/src/tests/FBAuthenticationTests.m index e2149ac75a..6b4a35df88 100644 --- a/src/tests/FBAuthenticationTests.m +++ b/src/tests/FBAuthenticationTests.m @@ -29,11 +29,9 @@ @interface FBSession (AuthenticationTesting) -- (BOOL)isSystemAccountStoreAvailable; - (void)authorizeUsingSystemAccountStore:(NSArray*)permissions defaultAudience:(FBSessionDefaultAudience)defaultAudience isReauthorize:(BOOL)isReauthorize; -- (BOOL)isMultitaskingSupported; - (BOOL)authorizeUsingFacebookApplication:(NSMutableDictionary *)params; - (void)authorizeUsingLoginDialog:(NSMutableDictionary *)params; - (BOOL)authorizeUsingSafari:(NSMutableDictionary *)params; @@ -74,6 +72,7 @@ - (void)setUp { _mockFBUtility = [[OCMockObject mockForClass:[FBUtility class]] retain]; FBFetchedAppSettings *dummyFBFetchedAppSettings = [[[FBFetchedAppSettings alloc] init] autorelease]; [[[_mockFBUtility stub] andReturn:dummyFBFetchedAppSettings] fetchedAppSettings]; + [[[_mockFBUtility stub] andReturn:nil] advertiserID]; //also stub advertiserID since that often hangs. } - (void)tearDown { @@ -129,7 +128,7 @@ - (id)createMockSystemAccountStoreAdapter:(BOOL)succeed defaultAudience:(FBSessi } - (void)mockSession:(id)mockSession supportSystemAccount:(BOOL)supportSystemAccount { - [[[mockSession stub] andReturnValue:OCMOCK_VALUE(supportSystemAccount)] isSystemAccountStoreAvailable]; + [[[[_mockFBUtility stub] classMethod] andReturnValue:OCMOCK_VALUE(supportSystemAccount)] isSystemAccountStoreAvailable]; } @@ -155,7 +154,7 @@ - (void)mockSession:(id)mockSession } - (void)mockSession:(id)mockSession supportMultitasking:(BOOL)supportMultitasking { - [[[mockSession stub] andReturnValue:OCMOCK_VALUE(supportMultitasking)] isMultitaskingSupported]; + [[[[_mockFBUtility stub] classMethod] andReturnValue:OCMOCK_VALUE(supportMultitasking)] isMultitaskingSupported]; } - (void)mockSession:(id)mockSession diff --git a/src/tests/FBFacebookAppAuthenticationTests.m b/src/tests/FBFacebookAppAuthenticationTests.m index 974b1b8b8c..f8496567f7 100644 --- a/src/tests/FBFacebookAppAuthenticationTests.m +++ b/src/tests/FBFacebookAppAuthenticationTests.m @@ -158,7 +158,7 @@ - (void)testImplFacebookAppAuthSuccessWithBehavior:(FBSessionLoginBehavior)behav [_blocker signal]; }]; - [_blocker waitWithTimeout:.01]; + STAssertTrue([_blocker waitWithTimeout:1], @"blocker timed out"); [(id)mockSession verify]; diff --git a/src/tests/FBIsStringRepresentingJSONDictionary.h b/src/tests/FBIsStringRepresentingJSONDictionary.h new file mode 100644 index 0000000000..0d0506faa9 --- /dev/null +++ b/src/tests/FBIsStringRepresentingJSONDictionary.h @@ -0,0 +1,33 @@ +/* + * Copyright 2010-present 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 + +@interface FBIsStringRepresentingJSONDictionary : HCBaseMatcher +{ + id valueMatcher; +} + ++ (id)isStringRepresentingJSONDictionary:(id)aValueMatcher; +- (id)initWithValue:(id)aValueMatcher; + +@end + +OBJC_EXPORT id FB_representsJSONDictionary(id valueMatcher); + +#ifdef HC_SHORTHAND + #define representsJSONDictionary FB_representsJSONDictionary +#endif diff --git a/src/tests/FBIsStringRepresentingJSONDictionary.m b/src/tests/FBIsStringRepresentingJSONDictionary.m new file mode 100644 index 0000000000..009a8248a9 --- /dev/null +++ b/src/tests/FBIsStringRepresentingJSONDictionary.m @@ -0,0 +1,85 @@ +/* + * Copyright 2010-present 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 "FBIsStringRepresentingJSONDictionary.h" +#import "FBUtility.h" +#import +#import + +@implementation FBIsStringRepresentingJSONDictionary + ++ (id)isStringRepresentingJSONDictionary:(id)aValueMatcher +{ + return [[self alloc] initWithValue:aValueMatcher]; +} + +- (id)initWithValue:(id)aValueMatcher +{ + self = [super init]; + if (self != nil) + { + valueMatcher = aValueMatcher; + } + return self; +} + +- (BOOL)matches:(id)item +{ + return [self matches:item describingMismatchTo:nil]; +} + +- (BOOL)matches:(id)string describingMismatchTo:(id)mismatchDescription +{ + if (![string isKindOfClass:[NSString class]]) { + [super describeMismatchOf:string to:mismatchDescription]; + return NO; + } + + NSDictionary *dict = [FBUtility simpleJSONDecode:string]; + if (![dict isKindOfClass:[NSDictionary class]]) { + [[mismatchDescription appendText:@"no JSON dictionary in "] + appendDescriptionOf:string]; + return NO; + } + + if (![valueMatcher matches:dict]) { + return NO; + } + + return YES; +} + +- (void)describeMismatchOf:(id)item to:(id)mismatchDescription +{ + [self matches:item describingMismatchTo:mismatchDescription]; +} + +- (void)describeTo:(id)description +{ + [description appendText:@"a string representing a JSON dictionary { "]; + [description appendDescriptionOf:valueMatcher]; + [description appendText:@"}"]; +} + +@end + +#pragma mark - + +id FB_representsJSONDictionary(id valueMatcher) +{ + return [FBIsStringRepresentingJSONDictionary isStringRepresentingJSONDictionary: + HCWrapInMatcher(valueMatcher)]; +} diff --git a/src/tests/FBIsURLHavingQueryParams.h b/src/tests/FBIsURLHavingQueryParams.h new file mode 100644 index 0000000000..536262c315 --- /dev/null +++ b/src/tests/FBIsURLHavingQueryParams.h @@ -0,0 +1,33 @@ +/* + * Copyright 2010-present 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 + +@interface FBIsURLHavingQueryParams : HCBaseMatcher +{ + id valueMatcher; +} + ++ (id)isURLHavingQueryParams:(id)aValueMatcher; +- (id)initWithValue:(id)aValueMatcher; + +@end + +OBJC_EXPORT id FB_hasQueryParams(id valueMatcher); + +#ifdef HC_SHORTHAND + #define hasQueryParams FB_hasQueryParams +#endif diff --git a/src/tests/FBIsURLHavingQueryParams.m b/src/tests/FBIsURLHavingQueryParams.m new file mode 100644 index 0000000000..c788b8ea6f --- /dev/null +++ b/src/tests/FBIsURLHavingQueryParams.m @@ -0,0 +1,85 @@ +/* + * Copyright 2010-present 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 "FBIsURLHavingQueryParams.h" +#import "FBUtility.h" +#import +#import + +@implementation FBIsURLHavingQueryParams + ++ (id)isURLHavingQueryParams:(id)aValueMatcher +{ + return [[self alloc] initWithValue:aValueMatcher]; +} + +- (id)initWithValue:(id)aValueMatcher +{ + self = [super init]; + if (self != nil) + { + valueMatcher = aValueMatcher; + } + return self; +} + +- (BOOL)matches:(id)item +{ + return [self matches:item describingMismatchTo:nil]; +} + +- (BOOL)matches:(id)url describingMismatchTo:(id)mismatchDescription +{ + if (![url isKindOfClass:[NSURL class]]) { + [super describeMismatchOf:url to:mismatchDescription]; + return NO; + } + + id query = [url query]; + if (![query isKindOfClass:[NSString class]]) { + [[mismatchDescription appendText:@"no query in "] + appendDescriptionOf:url]; + return NO; + } + + NSDictionary *queryParams = [FBUtility dictionaryByParsingURLQueryPart:query]; + if (![valueMatcher matches:queryParams]) { + return NO; + } + + return YES; +} + +- (void)describeMismatchOf:(id)item to:(id)mismatchDescription +{ + [self matches:item describingMismatchTo:mismatchDescription]; +} + +- (void)describeTo:(id)description +{ + [description appendText:@"an NSURL containing query parameters { "]; + [description appendDescriptionOf:valueMatcher]; + [description appendText:@"}"]; +} + +@end + +#pragma mark - + +id FB_hasQueryParams(id valueMatcher) +{ + return [FBIsURLHavingQueryParams isURLHavingQueryParams:HCWrapInMatcher(valueMatcher)]; +} diff --git a/src/tests/FBRequestConnectionTests.m b/src/tests/FBRequestConnectionTests.m index 1f04911466..92528de958 100644 --- a/src/tests/FBRequestConnectionTests.m +++ b/src/tests/FBRequestConnectionTests.m @@ -28,6 +28,7 @@ #import "FBTestBlocker.h" #import "FBURLConnection.h" #import "FBSessionTokenCachingStrategy.h" +#import "FBUtility.h" // This is just to silence compiler warnings since we access internal methods in some tests. @interface FBSession (Testing) @@ -42,8 +43,26 @@ - (FBURLConnection *)newFBURLConnection; @end +@interface FBRequestConnectionTests() { + id _mockFBUtility; +} +@end + @implementation FBRequestConnectionTests +- (void)setUp { + [super setUp]; + _mockFBUtility = [[OCMockObject mockForClass:[FBUtility class]] retain]; + [[[_mockFBUtility stub] andReturn:nil] advertiserID]; //also stub advertiserID since that often hangs. +} + +- (void)tearDown { + [_mockFBUtility release]; + _mockFBUtility = nil; + + [super tearDown]; +} + - (void)testWillNotPiggybackIfWouldExceedBatchSize { // Get a swizzled session that will always want to extend its access token. @@ -124,14 +143,18 @@ - (void)testReconnectBehavior // Create a fake session that is already open // Note we rely on FBTestSession automatically succeeding reauthorize. FBTestSession *session = [[[FBTestSession alloc] initWithAppID:@"appid" permissions:nil defaultAudience:FBSessionDefaultAudienceOnlyMe urlSchemeSuffix:nil tokenCacheStrategy:[FBSessionTokenCachingStrategy nullCacheInstance]] autorelease]; - FBAccessTokenData *tokenData = [FBAccessTokenData createTokenFromString:@"token" permissions:nil expirationDate:nil loginType:FBSessionLoginTypeFacebookViaSafari refreshDate:nil]; + FBAccessTokenData *tokenData = [FBAccessTokenData createTokenFromString:@"token" permissions:nil expirationDate:nil loginType:FBSessionLoginTypeFacebookViaSafari refreshDate:nil permissionsRefreshDate:[NSDate date]]; [session openFromAccessTokenData:tokenData completionHandler:nil]; __block int requestCount = 0; [OHHTTPStubs shouldStubRequestsPassingTest:^BOOL(NSURLRequest *request) { return YES; } withStubResponse:^OHHTTPStubsResponse *(NSURLRequest *request) { - // Construct a fake error object that will be categorized for reconnecting the session + // Construct a fake error object that will be categorized for reconnecting the session. Note this error is only for non-batched requests. + // If there is a test failure, you should verify the requests.count == 1 and debug that if it's not; otherwise this error response will likely cause + // an unrelated error to be surfaced. For example, the session init above set the permissionsRefreshDate to [NSDate date]. If it had not, + // there could be piggy-backed permissions request which would then expect a batch response. So, this test doubles to verify there was no + // piggybacked request when the permissionRefreshDate is set. NSData *data = [@"{\"error\": {\"message\": \"Reconnect this\",\"code\": 190,\"error_subcode\": 463}}" dataUsingEncoding:NSUTF8StringEncoding]; requestCount++; @@ -165,7 +188,7 @@ - (void)testReconnectBehaviorBatch // Create a fake session that is already open // Note we rely on FBTestSession automatically succeeding reauthorize. FBTestSession *session = [[FBTestSession alloc] initWithAppID:@"appid" permissions:nil defaultAudience:FBSessionDefaultAudienceOnlyMe urlSchemeSuffix:nil tokenCacheStrategy:[FBSessionTokenCachingStrategy nullCacheInstance]]; - FBAccessTokenData *tokenData = [FBAccessTokenData createTokenFromString:@"token" permissions:nil expirationDate:nil loginType:FBSessionLoginTypeFacebookViaSafari refreshDate:nil]; + FBAccessTokenData *tokenData = [FBAccessTokenData createTokenFromString:@"token" permissions:nil expirationDate:nil loginType:FBSessionLoginTypeFacebookViaSafari refreshDate:nil permissionsRefreshDate:[NSDate date]]; [session openFromAccessTokenData:tokenData completionHandler:nil]; __block int requestCount = 0; @@ -223,7 +246,7 @@ - (void)testReconnectBehaviorBatchPartialSuccess // Create a fake session that is already open // Note we rely on FBTestSession automatically succeeding reauthorize. FBTestSession *session = [[FBTestSession alloc] initWithAppID:@"appid" permissions:nil defaultAudience:FBSessionDefaultAudienceOnlyMe urlSchemeSuffix:nil tokenCacheStrategy:[FBSessionTokenCachingStrategy nullCacheInstance]]; - FBAccessTokenData *tokenData = [FBAccessTokenData createTokenFromString:@"token" permissions:nil expirationDate:nil loginType:FBSessionLoginTypeFacebookViaSafari refreshDate:nil]; + FBAccessTokenData *tokenData = [FBAccessTokenData createTokenFromString:@"token" permissions:nil expirationDate:nil loginType:FBSessionLoginTypeFacebookViaSafari refreshDate:nil permissionsRefreshDate:[NSDate date]]; [session openFromAccessTokenData:tokenData completionHandler:nil]; __block int requestCount = 0; @@ -324,4 +347,114 @@ - (void)testReconnectBehaviorDeclineLogin [OHHTTPStubs removeAllRequestHandlers]; } +// test to exercise FBRequestConnectionErrorBehaviorReconnectSession +// with a permissions refresh piggybacked and the original request fails (and should be retried). +- (void)testReconnectBehaviorBatchWithPermissionsRefresh +{ + // Create a fake session that is already open + // Note we rely on FBTestSession automatically succeeding reauthorize. + FBTestSession *session = [[FBTestSession alloc] initWithAppID:@"appid" permissions:nil defaultAudience:FBSessionDefaultAudienceOnlyMe urlSchemeSuffix:nil tokenCacheStrategy:[FBSessionTokenCachingStrategy nullCacheInstance]]; + FBAccessTokenData *tokenData = [FBAccessTokenData createTokenFromString:@"token" permissions:nil expirationDate:nil loginType:FBSessionLoginTypeFacebookViaSafari refreshDate:nil]; + [session openFromAccessTokenData:tokenData completionHandler:nil]; + + __block int requestCount = 0; + + [OHHTTPStubs shouldStubRequestsPassingTest:^BOOL(NSURLRequest *request) { + return YES; + } withStubResponse:^OHHTTPStubsResponse *(NSURLRequest *request) { + // Construct a fake batch response with 1 failure and 1 success (permissions piggyback). + // Note this technically means the retried /friends request gets this stubbed + // batch response as well which doesn't matter for this test in particular. + NSString *errorBodyString = [@"{\"error\": {\"message\": \"Reconnect this\",\"code\": 190,\"error_subcode\": 463}}" stringByReplacingOccurrencesOfString:@"\"" withString:@"\\\""]; + NSData *data = [[NSString stringWithFormat:@"[" + "{\"code\":400,\"body\":\"%@\"}," + "{\"code\":200,\"body\":\"%@\"}" + "]", + errorBodyString, + [@"{\"data\":[ { \"installed\":1, \"basic_info\":1} ] }" stringByReplacingOccurrencesOfString:@"\"" withString:@"\\\""]] + dataUsingEncoding:NSUTF8StringEncoding]; + requestCount++; + + return [OHHTTPStubsResponse responseWithData:data + statusCode:200 + responseTime:0 + headers:nil]; + }]; + + FBTestBlocker *blocker = [[FBTestBlocker alloc] initWithExpectedSignalCount:1]; + FBRequestConnection *connection = [[[FBRequestConnection alloc] init] autorelease]; + FBRequest *requestFriends =[[[FBRequest alloc] initWithSession:session graphPath:@"me/friends"] autorelease]; + connection.errorBehavior = FBRequestConnectionErrorBehaviorReconnectSession; + + __block int userHandlerFriendsCount = 0; + [connection addRequest:requestFriends completionHandler:^(FBRequestConnection *connection, id result, NSError *error) { + userHandlerFriendsCount++; + [blocker signal]; + } ]; + + [connection start]; + + STAssertTrue([blocker waitWithTimeout:2], @"timed out waiting for request to return"); + STAssertEquals(2, requestCount, @"expected number of retries not met"); + STAssertEquals(1, userHandlerFriendsCount, @"user handler was not invoked once."); + [OHHTTPStubs removeAllRequestHandlers]; + NSTimeInterval timeSincePermissionsRefresh = [session.accessTokenData.permissionsRefreshDate timeIntervalSinceNow]; + STAssertTrue(timeSincePermissionsRefresh > -3, @"permissions refresh date should be within a few seconds of now"); +} + +// test to exercise FBRequestConnectionErrorBehaviorReconnectSession +// with a permissions refresh piggybacked (which fails) and the original request succeeds. +// there should be no retry since the session should not be closed. +- (void)testReconnectBehaviorBatchWithPermissionsRefreshFailure +{ + // Create a fake session that is already open + // Note we rely on FBTestSession automatically succeeding reauthorize. + FBTestSession *session = [[FBTestSession alloc] initWithAppID:@"appid" permissions:nil defaultAudience:FBSessionDefaultAudienceOnlyMe urlSchemeSuffix:nil tokenCacheStrategy:[FBSessionTokenCachingStrategy nullCacheInstance]]; + FBAccessTokenData *tokenData = [FBAccessTokenData createTokenFromString:@"token" permissions:nil expirationDate:nil loginType:FBSessionLoginTypeFacebookViaSafari refreshDate:nil]; + [session openFromAccessTokenData:tokenData completionHandler:nil]; + STAssertEquals([NSDate distantPast], session.accessTokenData.permissionsRefreshDate, @"permissions refresh date was not initialized properly to distantPast"); + __block int requestCount = 0; + + [OHHTTPStubs shouldStubRequestsPassingTest:^BOOL(NSURLRequest *request) { + return YES; + } withStubResponse:^OHHTTPStubsResponse *(NSURLRequest *request) { + NSString *errorBodyString = [@"{\"error\": {\"message\": \"Reconnect this\",\"code\": 190,\"error_subcode\": 463}}" stringByReplacingOccurrencesOfString:@"\"" withString:@"\\\""]; + NSData *data = [[NSString stringWithFormat:@"[" + "{\"code\":200,\"body\":\"%@\"}," + "{\"code\":400,\"body\":\"%@\"}" + "]", + [@"{\"data\":[ { \"name\":\"zuck\", \"id\":\"4\"} ] }" stringByReplacingOccurrencesOfString:@"\"" withString:@"\\\""], + errorBodyString] + dataUsingEncoding:NSUTF8StringEncoding]; + requestCount++; + + return [OHHTTPStubsResponse responseWithData:data + statusCode:200 + responseTime:0 + headers:nil]; + }]; + + FBTestBlocker *blocker = [[FBTestBlocker alloc] initWithExpectedSignalCount:1]; + FBRequestConnection *connection = [[[FBRequestConnection alloc] init] autorelease]; + FBRequest *requestFriends =[[[FBRequest alloc] initWithSession:session graphPath:@"me/friends"] autorelease]; + connection.errorBehavior = FBRequestConnectionErrorBehaviorReconnectSession; + + __block int userHandlerFriendsCount = 0; + [connection addRequest:requestFriends completionHandler:^(FBRequestConnection *connection, id result, NSError *error) { + userHandlerFriendsCount++; + STAssertTrue([@"zuck" isEqualToString:(result[@"data"][0][@"name"])], @"couldn't find friend"); + [blocker signal]; + } ]; + + [connection start]; + + STAssertTrue([blocker waitWithTimeout:2], @"timed out waiting for request to return"); + STAssertEquals(1, requestCount, @"there should have been no retry"); + STAssertEquals(1, userHandlerFriendsCount, @"user handler was not invoked once."); + [OHHTTPStubs removeAllRequestHandlers]; + + STAssertEquals([NSDate distantPast], session.accessTokenData.permissionsRefreshDate, @"permissions refresh date was unexpectedly updated"); +} + + @end diff --git a/src/tests/FBSessionTests.m b/src/tests/FBSessionTests.m index 6843781bbe..64bd273a46 100644 --- a/src/tests/FBSessionTests.m +++ b/src/tests/FBSessionTests.m @@ -22,6 +22,7 @@ #import "FBTests.h" #import "FBUtility.h" #import "FBSessionTokenCachingStrategy.h" +#import "FBSessionUtility.h" #import "FBSystemAccountStoreAdapter.h" #import "FBAccessTokenData+Internal.h" #import "FBError.h" @@ -40,8 +41,6 @@ @interface FBSession (Testing) @property(readwrite, copy) NSDate *refreshDate; @property(readwrite) FBSessionLoginType loginType; -+ (NSString *)sessionStateDescription:(FBSessionState)sessionState; - - (void)authorizeWithPermissions:(NSArray*)permissions behavior:(FBSessionLoginBehavior)behavior defaultAudience:(FBSessionDefaultAudience)audience @@ -56,8 +55,6 @@ - (void)authorizeWithPermissions:(NSArray*)permissions canFetchAppSettings:(BOOL)canFetchAppSettings; - (FBSystemAccountStoreAdapter *)getSystemAccountStoreAdapter; - (void)callReauthorizeHandlerAndClearState:(NSError*)error; -- (BOOL)isSystemAccountStoreAvailable; -- (BOOL)isMultitaskingSupported; @end @@ -759,15 +756,13 @@ - (void)testReauthorizeWithPermissionCallsAuthorizeAgain { [(id)mockSession verify]; } -/* TODO this fails if handler is nil; consider making reauthorizeWithPermission* more robust. - - (void)testReauthorizeWhileReauthorizeInProgressFailsWithNilHandler { FBSession *mockSession = [self allocMockSessionWithNoOpAuth]; FBAccessTokenData *mockToken = [self createValidMockToken]; FBSessionTokenCachingStrategy *mockStrategy = [self createMockTokenCachingStrategyWithToken:mockToken]; - FBSession *session = [mockSession initWithAppID:kAppId + FBSession *session = [mockSession initWithAppID:kTestAppId permissions:nil defaultAudience:FBSessionDefaultAudienceNone urlSchemeSuffix:nil @@ -781,15 +776,16 @@ - (void)testReauthorizeWhileReauthorizeInProgressFailsWithNilHandler { [session requestNewReadPermissions:nil completionHandler:^(FBSession *session, NSError *error) { }]; + BOOL caughtException = NO; @try { [session requestNewReadPermissions:nil completionHandler:nil]; STFail(@"expected exception"); } @catch (NSException *exception) { + caughtException = YES; } + STAssertTrue(caughtException, @"expected exception when requesting more permissions."); } -*/ - - (void)testRequestNewReadPermissionsFailsIfPassedPublishPermissions { FBSession *session = [[FBSession alloc] initWithAppID:kTestAppId permissions:nil @@ -912,26 +908,19 @@ - (void)testIsMultitaskingSupported { BOOL shouldBeSupported = [device respondsToSelector:@selector(isMultitaskingSupported)] && [device isMultitaskingSupported]; - [FBSession setDefaultAppID:kTestAppId]; - FBSession *session = [[FBSession alloc] init]; - - assertThatBool([session isMultitaskingSupported], equalToBool(shouldBeSupported)); + assertThatBool([FBUtility isMultitaskingSupported], equalToBool(shouldBeSupported)); } -/* TODO transitionToState: appears to be losing the isFacebookLogin-ness of cached tokens. - - (void)testWillAttemptToExtendToken { - FBAccessTokenData *token = [self createValidMockToken]; - // TODO add refresh date - [[[(id)token stub] andReturn:[NSDate dateWithTimeIntervalSince1970:0]] refreshDate]; - FBSessionLoginType loginType = FBSessionLoginTypeFacebookApplication; - [[[(id)token stub] andReturnValue:OCMOCK_VALUE(loginType)] loginType]; - BOOL yes = YES; - [[[(id)token stub] andReturnValue:OCMOCK_VALUE(yes)] isFacebookLogin]; + FBAccessTokenData *token = [FBAccessTokenData createTokenFromString:@"token" + permissions:nil + expirationDate:[NSDate distantFuture] + loginType:FBSessionLoginTypeFacebookApplication + refreshDate:[NSDate dateWithTimeIntervalSince1970:0]]; FBSessionTokenCachingStrategy *mockStrategy = [self createMockTokenCachingStrategyWithToken:token]; - FBSession *session = [[FBSession alloc] initWithAppID:kAppId + FBSession *session = [[FBSession alloc] initWithAppID:kTestAppId permissions:nil defaultAudience:FBSessionDefaultAudienceNone urlSchemeSuffix:nil @@ -942,7 +931,7 @@ - (void)testWillAttemptToExtendToken { assertThatBool(shouldExtend, equalToBool(YES)); } -*/ + #pragma mark Active session tests @@ -1315,7 +1304,7 @@ - (void)testSessionStateDescription { const int numTests = sizeof(states) / sizeof(FBSessionState); for (int i = 0; i < numTests; ++i) { - NSString *description = [FBSession sessionStateDescription:states[i]]; + NSString *description = [FBSessionUtility sessionStateDescription:states[i]]; assertThat(description, equalTo([expectedStrings objectAtIndex:i])); } } @@ -1336,12 +1325,12 @@ - (void)testDeleteFacebookCookies { [self addFacebookCookieToSharedStorage]; NSHTTPCookieStorage *storage = [NSHTTPCookieStorage sharedHTTPCookieStorage]; - NSURL *url = [NSURL URLWithString:[FBUtility buildFacebookUrlWithPre:@"https://m." withPost:@"/dialog/"]]; + NSURL *url = [NSURL URLWithString:[FBUtility dialogBaseURL]]; NSArray *cookiesForFacebook = [storage cookiesForURL:url]; assertThatInteger(cookiesForFacebook.count, greaterThan(@0)); - [FBSession deleteFacebookCookies]; + [FBUtility deleteFacebookCookies]; cookiesForFacebook = [storage cookiesForURL:url]; diff --git a/src/tests/FBTests.h b/src/tests/FBTests.h index d1c7ce535e..2fb9de3028 100644 --- a/src/tests/FBTests.h +++ b/src/tests/FBTests.h @@ -29,6 +29,9 @@ extern NSString *kTestAppId; - (FBRequestHandler)handlerExpectingSuccessSignaling:(FBTestBlocker*)blocker; - (FBRequestHandler)handlerExpectingFailureSignaling:(FBTestBlocker*)blocker; +// Used to test methods that dispatch blocks to the GCD main queue +- (void)waitForMainQueueToFinish; + // Methods related to session mocking. - (FBSession *)createAndOpenSessionWithMockToken; - (FBAccessTokenData *)createValidMockToken; diff --git a/src/tests/FBTests.m b/src/tests/FBTests.m index 06b495071d..b1962f9d9d 100644 --- a/src/tests/FBTests.m +++ b/src/tests/FBTests.m @@ -101,6 +101,16 @@ - (FBSessionTokenCachingStrategy *)createMockTokenCachingStrategyWithToken:(FBAc return strategy; } +- (void)waitForMainQueueToFinish { + FBTestBlocker *blocker = [[FBTestBlocker alloc] init]; + dispatch_async(dispatch_get_main_queue(), ^() { + [blocker signal]; + }); + + [blocker wait]; + + [blocker release]; +} #pragma mark - diff --git a/src/tests/FacebookSDKTests-Prefix.pch b/src/tests/FacebookSDKTests-Prefix.pch index ed843205a0..a20591564e 100644 --- a/src/tests/FacebookSDKTests-Prefix.pch +++ b/src/tests/FacebookSDKTests-Prefix.pch @@ -6,7 +6,10 @@ #import #import + #import + #import #define HC_SHORTHAND #import + #endif