From 956f0eecbc483a7a2be6988b878de9b4ecdd23eb Mon Sep 17 00:00:00 2001 From: shoya <12c1055@gmail.com> Date: Tue, 25 Oct 2016 21:39:02 +0900 Subject: [PATCH 1/2] Fix build error 'No type or protocol named UIApplicationOpenURLOptionsKey' in iOS 9.3 before. --- FBSDKCoreKit/FBSDKCoreKit/FBSDKApplicationDelegate.h | 2 ++ FBSDKCoreKit/FBSDKCoreKit/FBSDKApplicationDelegate.m | 2 ++ 2 files changed, 4 insertions(+) diff --git a/FBSDKCoreKit/FBSDKCoreKit/FBSDKApplicationDelegate.h b/FBSDKCoreKit/FBSDKCoreKit/FBSDKApplicationDelegate.h index 3aef327ee6..005ab661ff 100644 --- a/FBSDKCoreKit/FBSDKCoreKit/FBSDKApplicationDelegate.h +++ b/FBSDKCoreKit/FBSDKCoreKit/FBSDKApplicationDelegate.h @@ -58,6 +58,7 @@ sourceApplication:(NSString *)sourceApplication annotation:(id)annotation; +#if __IPHONE_OS_VERSION_MAX_ALLOWED > __IPHONE_9_3 /*! @abstract Call this method from the [UIApplicationDelegate application:openURL:options:] method @@ -75,6 +76,7 @@ - (BOOL)application:(UIApplication *)application openURL:(NSURL *)url options:(NSDictionary *)options; +#endif /*! @abstract diff --git a/FBSDKCoreKit/FBSDKCoreKit/FBSDKApplicationDelegate.m b/FBSDKCoreKit/FBSDKCoreKit/FBSDKApplicationDelegate.m index d3ebb29d88..8688c02942 100644 --- a/FBSDKCoreKit/FBSDKCoreKit/FBSDKApplicationDelegate.m +++ b/FBSDKCoreKit/FBSDKCoreKit/FBSDKApplicationDelegate.m @@ -122,6 +122,7 @@ - (void)dealloc #pragma mark - UIApplicationDelegate +#if __IPHONE_OS_VERSION_MAX_ALLOWED > __IPHONE_9_3 - (BOOL)application:(UIApplication *)application openURL:(NSURL *)url options:(NSDictionary *)options @@ -131,6 +132,7 @@ - (BOOL)application:(UIApplication *)application sourceApplication:options[UIApplicationOpenURLOptionsSourceApplicationKey] annotation:options[UIApplicationOpenURLOptionsAnnotationKey]]; } +#endif - (BOOL)application:(UIApplication *)application openURL:(NSURL *)url From 514a8c86e233aeea302b03ab0a6b3bcbbefab907 Mon Sep 17 00:00:00 2001 From: David Emmel Date: Wed, 26 Oct 2016 14:29:38 -0700 Subject: [PATCH 2/2] Facebook iOS SDK 4.17 --- .buckversion | 1 + .gitignore | 4 + .gitmodules | 4 +- Configurations/Version.xcconfig | 2 +- FBSDKCoreKit.podspec | 4 +- .../FBSDKCoreKit.xcodeproj/project.pbxproj | 54 ++- FBSDKCoreKit/FBSDKCoreKit/FBSDKAppEvents.h | 22 ++ FBSDKCoreKit/FBSDKCoreKit/FBSDKAppEvents.m | 72 ++++ .../FBSDKCoreKit/FBSDKApplicationDelegate.m | 72 ++-- FBSDKCoreKit/FBSDKCoreKit/FBSDKCoreKit.h | 2 +- .../FBSDKDeviceViewControllerBase.m | 8 +- .../AppEvents/FBSDKAppEvents+Internal.h | 5 + .../Internal/BridgeAPI/FBSDKURLOpening.h | 9 + .../Internal/Device/FBSDKDeviceDialogView.m | 6 +- .../Device/FBSDKSmartDeviceDialogView.h | 25 ++ .../Device/FBSDKSmartDeviceDialogView.m | 317 ++++++++++++++++++ .../Internal/FBSDKCoreKit+Internal.h | 3 + .../Internal/FBSDKDeviceRequestsHelper.h | 58 ++++ .../Internal/FBSDKDeviceRequestsHelper.m | 125 +++++++ .../Internal/FBSDKImageDownloader.h | 40 +++ .../Internal/FBSDKImageDownloader.m | 93 +++++ .../FBSDKServerConfiguration.h | 16 + .../FBSDKServerConfiguration.m | 64 +++- .../FBSDKServerConfigurationManager.m | 45 ++- .../project.pbxproj | 79 +++++ .../FBSDKAppEventsIntegrationTests.m | 126 +++++++ .../FBSDKImageDownloaderTests.m | 134 ++++++++ ...rverConfigurationManagerIntegrationTests.m | 42 ++- .../FBSDKShareAPIIntegrationTests.m | 14 +- FBSDKLoginKit.podspec | 4 +- .../FBSDKLoginKit/FBSDKLoginManager.m | 29 +- .../Internal/FBSDKLoginManagerLogger.h | 4 +- .../Internal/FBSDKLoginManagerLogger.m | 18 +- .../FBSDKLoginManagerTests.m | 33 +- .../FBSDKSystemAccountAuthenticationTests.m | 6 +- FBSDKShareKit.podspec | 4 +- .../FBSDKDeviceShareViewController.m | 12 +- FBSDKTVOSKit.podspec | 4 +- .../FBSDKDeviceLoginViewController.m | 117 ++++++- .../Internal/FBSDKDeviceLoginManager.h | 6 +- .../Internal/FBSDKDeviceLoginManager.m | 26 +- .../Resources/en.lproj/FacebookSDK.strings | Bin 7456 -> 9186 bytes .../LoginSample.xcodeproj/project.pbxproj | 6 +- .../LoginSample/AKThemes/AdvancedUIManager.m | 4 + .../AKThemes/ReverbActionBarView.m | 1 + .../LoginSample/AKThemes/ReverbUIManager.m | 3 + .../LoginSample/LoginSample-Prefix.pch | 4 +- .../scrumptious/scrumptious-Info.plist | 2 + scripts/run_tests.sh | 4 +- 49 files changed, 1616 insertions(+), 117 deletions(-) create mode 100644 .buckversion create mode 100644 FBSDKCoreKit/FBSDKCoreKit/Internal/Device/FBSDKSmartDeviceDialogView.h create mode 100644 FBSDKCoreKit/FBSDKCoreKit/Internal/Device/FBSDKSmartDeviceDialogView.m create mode 100644 FBSDKCoreKit/FBSDKCoreKit/Internal/FBSDKDeviceRequestsHelper.h create mode 100644 FBSDKCoreKit/FBSDKCoreKit/Internal/FBSDKDeviceRequestsHelper.m create mode 100644 FBSDKCoreKit/FBSDKCoreKit/Internal/FBSDKImageDownloader.h create mode 100644 FBSDKCoreKit/FBSDKCoreKit/Internal/FBSDKImageDownloader.m create mode 100644 FBSDKIntegrationTests/FBSDKIntegrationTests/FBSDKImageDownloaderTests.m diff --git a/.buckversion b/.buckversion new file mode 100644 index 0000000000..756d09135e --- /dev/null +++ b/.buckversion @@ -0,0 +1 @@ +3a688647b6fca77bda05ab55f2f53860a470edfc diff --git a/.gitignore b/.gitignore index ad8adc7833..56e5a303ae 100644 --- a/.gitignore +++ b/.gitignore @@ -23,3 +23,7 @@ Carthage # vim temp files *.swp + +# buck +buck-out/ +.buckd/ \ No newline at end of file diff --git a/.gitmodules b/.gitmodules index 246a94609a..39340bb08a 100644 --- a/.gitmodules +++ b/.gitmodules @@ -16,11 +16,9 @@ [submodule "vendor/xctool"] path = vendor/xctool url = https://github.com/facebook/xctool.git - - [submodule "FBNotifications"] path = FBNotifications url = https://github.com/facebook/FBNotifications.git [submodule "vendor/FBTweak"] path = vendor/FBTweak - url = git://github.com/facebook/Tweaks.git + url = https://github.com/facebook/Tweaks.git diff --git a/Configurations/Version.xcconfig b/Configurations/Version.xcconfig index f4d746781b..bf49cc2794 100644 --- a/Configurations/Version.xcconfig +++ b/Configurations/Version.xcconfig @@ -17,6 +17,6 @@ // CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. // The versions for FBSDK and Messenger SDK. -FBSDK_PROJECT_VERSION=4.16.1 +FBSDK_PROJECT_VERSION=4.17.0 MNSDK_PROJECT_VERSION=TODO_SUPPORT_MNSDK diff --git a/FBSDKCoreKit.podspec b/FBSDKCoreKit.podspec index 3e99362696..dcb4026974 100644 --- a/FBSDKCoreKit.podspec +++ b/FBSDKCoreKit.podspec @@ -3,7 +3,7 @@ Pod::Spec.new do |s| s.name = "FBSDKCoreKit" - s.version = "4.16.1" + s.version = "4.17.0" s.summary = "Official Facebook SDK for iOS to access Facebook Platform's core features" s.description = <<-DESC @@ -22,7 +22,7 @@ Pod::Spec.new do |s| s.tvos.deployment_target = '9.0' s.source = { :git => "https://github.com/facebook/facebook-ios-sdk.git", - :tag => "sdk-version-4.16.1" + :tag => "sdk-version-4.17.0" } s.ios.weak_frameworks = 'Accounts', 'CoreLocation', 'Social', 'Security', 'QuartzCore', 'CoreGraphics', 'UIKit', 'Foundation', 'AudioToolbox' diff --git a/FBSDKCoreKit/FBSDKCoreKit.xcodeproj/project.pbxproj b/FBSDKCoreKit/FBSDKCoreKit.xcodeproj/project.pbxproj index 33d511348a..4323bb0389 100644 --- a/FBSDKCoreKit/FBSDKCoreKit.xcodeproj/project.pbxproj +++ b/FBSDKCoreKit/FBSDKCoreKit.xcodeproj/project.pbxproj @@ -36,6 +36,14 @@ /* Begin PBXBuildFile section */ 2A3DA4161CB4C0F600339BD4 /* FBSDKAppLinkUtilityTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 2A3DA4151CB4C0F600339BD4 /* FBSDKAppLinkUtilityTests.m */; }; + 520223F71D83C8D200CE0AB5 /* FBSDKDeviceRequestsHelper.h in Headers */ = {isa = PBXBuildFile; fileRef = 520223F51D83C8D200CE0AB5 /* FBSDKDeviceRequestsHelper.h */; }; + 520223F81D83C8D200CE0AB5 /* FBSDKDeviceRequestsHelper.m in Sources */ = {isa = PBXBuildFile; fileRef = 520223F61D83C8D200CE0AB5 /* FBSDKDeviceRequestsHelper.m */; }; + 52D4F0BC1D91A18D0030B7FC /* FBSDKDeviceRequestsHelper.m in Sources */ = {isa = PBXBuildFile; fileRef = 520223F61D83C8D200CE0AB5 /* FBSDKDeviceRequestsHelper.m */; }; + 52D4F0D11D91A18F0030B7FC /* FBSDKDeviceRequestsHelper.m in Sources */ = {isa = PBXBuildFile; fileRef = 520223F61D83C8D200CE0AB5 /* FBSDKDeviceRequestsHelper.m */; }; + 52D4F0D21D91A18F0030B7FC /* FBSDKDeviceRequestsHelper.m in Sources */ = {isa = PBXBuildFile; fileRef = 520223F61D83C8D200CE0AB5 /* FBSDKDeviceRequestsHelper.m */; }; + 52D4F0D31D91A1940030B7FC /* FBSDKDeviceRequestsHelper.h in Headers */ = {isa = PBXBuildFile; fileRef = 520223F51D83C8D200CE0AB5 /* FBSDKDeviceRequestsHelper.h */; }; + 52D4F0D41D91A1950030B7FC /* FBSDKDeviceRequestsHelper.h in Headers */ = {isa = PBXBuildFile; fileRef = 520223F51D83C8D200CE0AB5 /* FBSDKDeviceRequestsHelper.h */; }; + 52D4F0D51D91A1950030B7FC /* FBSDKDeviceRequestsHelper.h in Headers */ = {isa = PBXBuildFile; fileRef = 520223F51D83C8D200CE0AB5 /* FBSDKDeviceRequestsHelper.h */; }; 5F7063FB1AF733F300E42ED7 /* FBSDKAppEventsDeviceInfo.h in Headers */ = {isa = PBXBuildFile; fileRef = 5F7063FA1AF733F300E42ED7 /* FBSDKAppEventsDeviceInfo.h */; }; 5F7064091AF7342600E42ED7 /* FBSDKAppEventsDeviceInfo.m in Sources */ = {isa = PBXBuildFile; fileRef = 5F7064081AF7342600E42ED7 /* FBSDKAppEventsDeviceInfo.m */; }; 7E253D811A8EAEF500CCCFE7 /* FBSDKInternalUtilityTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 893F44A51A6445C1001DB0B6 /* FBSDKInternalUtilityTests.m */; }; @@ -420,6 +428,14 @@ 9D195CC91B9FE2E000BD6BEC /* FBSDKContainerViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 9D195CC71B9FE2E000BD6BEC /* FBSDKContainerViewController.m */; }; 9D26974D1A5DF40700143BFC /* FBSDKCoreKit.h in Headers */ = {isa = PBXBuildFile; fileRef = 9D26974C1A5DF40700143BFC /* FBSDKCoreKit.h */; settings = {ATTRIBUTES = (Public, ); }; }; 9D2697531A5DF40700143BFC /* FBSDKCoreKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 9D2697471A5DF40700143BFC /* FBSDKCoreKit.framework */; }; + 9D28F1931DB14DBB0057D709 /* FBSDKImageDownloader.h in Headers */ = {isa = PBXBuildFile; fileRef = 9D28F1911DB14DBB0057D709 /* FBSDKImageDownloader.h */; }; + 9D28F1941DB14DBB0057D709 /* FBSDKImageDownloader.h in Headers */ = {isa = PBXBuildFile; fileRef = 9D28F1911DB14DBB0057D709 /* FBSDKImageDownloader.h */; }; + 9D28F1951DB14DBB0057D709 /* FBSDKImageDownloader.h in Headers */ = {isa = PBXBuildFile; fileRef = 9D28F1911DB14DBB0057D709 /* FBSDKImageDownloader.h */; }; + 9D28F1961DB14DBB0057D709 /* FBSDKImageDownloader.h in Headers */ = {isa = PBXBuildFile; fileRef = 9D28F1911DB14DBB0057D709 /* FBSDKImageDownloader.h */; }; + 9D28F1971DB14DBB0057D709 /* FBSDKImageDownloader.m in Sources */ = {isa = PBXBuildFile; fileRef = 9D28F1921DB14DBB0057D709 /* FBSDKImageDownloader.m */; }; + 9D28F1981DB14DBB0057D709 /* FBSDKImageDownloader.m in Sources */ = {isa = PBXBuildFile; fileRef = 9D28F1921DB14DBB0057D709 /* FBSDKImageDownloader.m */; }; + 9D28F1991DB14DBB0057D709 /* FBSDKImageDownloader.m in Sources */ = {isa = PBXBuildFile; fileRef = 9D28F1921DB14DBB0057D709 /* FBSDKImageDownloader.m */; }; + 9D28F19A1DB14DBB0057D709 /* FBSDKImageDownloader.m in Sources */ = {isa = PBXBuildFile; fileRef = 9D28F1921DB14DBB0057D709 /* FBSDKImageDownloader.m */; }; 9D30290A1A65C4420086B9ED /* FBSDKAccessToken.h in Headers */ = {isa = PBXBuildFile; fileRef = 9D3029081A65C4420086B9ED /* FBSDKAccessToken.h */; settings = {ATTRIBUTES = (Public, ); }; }; 9D30290B1A65C4420086B9ED /* FBSDKAccessToken.m in Sources */ = {isa = PBXBuildFile; fileRef = 9D3029091A65C4420086B9ED /* FBSDKAccessToken.m */; }; 9D30292B1A65E6680086B9ED /* FBSDKMacros.h in Headers */ = {isa = PBXBuildFile; fileRef = 9D30292A1A65E6680086B9ED /* FBSDKMacros.h */; settings = {ATTRIBUTES = (Public, ); }; }; @@ -550,6 +566,10 @@ 9DBA6A371A80267400B4DE6A /* FBSDKColor.h in Headers */ = {isa = PBXBuildFile; fileRef = 9DBA6A361A80267400B4DE6A /* FBSDKColor.h */; }; 9DC1DD781BC4629F000D5AD5 /* FBSDKApplicationDelegate.m in Sources */ = {isa = PBXBuildFile; fileRef = 9D34A11E1A5F038300C37317 /* FBSDKApplicationDelegate.m */; }; 9DC1DD851BC462B0000D5AD5 /* FBSDKApplicationDelegate.h in Headers */ = {isa = PBXBuildFile; fileRef = 9D34A11D1A5F038300C37317 /* FBSDKApplicationDelegate.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 9DC277921DAF4D6B004F4AB5 /* FBSDKSmartDeviceDialogView.h in Headers */ = {isa = PBXBuildFile; fileRef = 9DC2777B1DAF4CCD004F4AB5 /* FBSDKSmartDeviceDialogView.h */; }; + 9DC277931DAF4D6B004F4AB5 /* FBSDKSmartDeviceDialogView.h in Headers */ = {isa = PBXBuildFile; fileRef = 9DC2777B1DAF4CCD004F4AB5 /* FBSDKSmartDeviceDialogView.h */; }; + 9DC277941DAF4D70004F4AB5 /* FBSDKSmartDeviceDialogView.m in Sources */ = {isa = PBXBuildFile; fileRef = 9DC2777A1DAF4CCD004F4AB5 /* FBSDKSmartDeviceDialogView.m */; }; + 9DC277951DAF4D71004F4AB5 /* FBSDKSmartDeviceDialogView.m in Sources */ = {isa = PBXBuildFile; fileRef = 9DC2777A1DAF4CCD004F4AB5 /* FBSDKSmartDeviceDialogView.m */; }; 9DC658951A6EE5C500B85AAF /* FBSDKGraphRequest.h in Headers */ = {isa = PBXBuildFile; fileRef = 9DC658931A6EE5C500B85AAF /* FBSDKGraphRequest.h */; settings = {ATTRIBUTES = (Public, ); }; }; 9DC658961A6EE5C500B85AAF /* FBSDKGraphRequest.m in Sources */ = {isa = PBXBuildFile; fileRef = 9DC658941A6EE5C500B85AAF /* FBSDKGraphRequest.m */; }; 9DC6589B1A6EE5CD00B85AAF /* FBSDKGraphRequest+Internal.h in Headers */ = {isa = PBXBuildFile; fileRef = 9DC6589A1A6EE5CD00B85AAF /* FBSDKGraphRequest+Internal.h */; }; @@ -810,6 +830,8 @@ /* Begin PBXFileReference section */ 2A3DA4151CB4C0F600339BD4 /* FBSDKAppLinkUtilityTests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = FBSDKAppLinkUtilityTests.m; sourceTree = ""; }; + 520223F51D83C8D200CE0AB5 /* FBSDKDeviceRequestsHelper.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = FBSDKDeviceRequestsHelper.h; sourceTree = ""; }; + 520223F61D83C8D200CE0AB5 /* FBSDKDeviceRequestsHelper.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = FBSDKDeviceRequestsHelper.m; sourceTree = ""; }; 5F7063FA1AF733F300E42ED7 /* FBSDKAppEventsDeviceInfo.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = FBSDKAppEventsDeviceInfo.h; sourceTree = ""; }; 5F7064081AF7342600E42ED7 /* FBSDKAppEventsDeviceInfo.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = FBSDKAppEventsDeviceInfo.m; sourceTree = ""; }; 6BE0966D1AAE687F00CCD61A /* FBSDKServerConfigurationManager+Internal.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "FBSDKServerConfigurationManager+Internal.h"; sourceTree = ""; }; @@ -958,6 +980,8 @@ 9D26974C1A5DF40700143BFC /* FBSDKCoreKit.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = FBSDKCoreKit.h; sourceTree = ""; }; 9D2697521A5DF40700143BFC /* FBSDKCoreKitTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = FBSDKCoreKitTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; 9D2697581A5DF40700143BFC /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; + 9D28F1911DB14DBB0057D709 /* FBSDKImageDownloader.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = FBSDKImageDownloader.h; sourceTree = ""; }; + 9D28F1921DB14DBB0057D709 /* FBSDKImageDownloader.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = FBSDKImageDownloader.m; sourceTree = ""; }; 9D3029081A65C4420086B9ED /* FBSDKAccessToken.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = FBSDKAccessToken.h; sourceTree = ""; }; 9D3029091A65C4420086B9ED /* FBSDKAccessToken.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = FBSDKAccessToken.m; sourceTree = ""; }; 9D30292A1A65E6680086B9ED /* FBSDKMacros.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = FBSDKMacros.h; sourceTree = ""; }; @@ -1000,6 +1024,8 @@ 9DB0FA731BC1CA71005EB8B1 /* FBSDKCoreKit.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = FBSDKCoreKit.framework; sourceTree = BUILT_PRODUCTS_DIR; }; 9DBA6A301A80265A00B4DE6A /* FBSDKColor.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = FBSDKColor.m; sourceTree = ""; }; 9DBA6A361A80267400B4DE6A /* FBSDKColor.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = FBSDKColor.h; sourceTree = ""; }; + 9DC2777A1DAF4CCD004F4AB5 /* FBSDKSmartDeviceDialogView.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = FBSDKSmartDeviceDialogView.m; sourceTree = ""; }; + 9DC2777B1DAF4CCD004F4AB5 /* FBSDKSmartDeviceDialogView.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = FBSDKSmartDeviceDialogView.h; sourceTree = ""; }; 9DC658931A6EE5C500B85AAF /* FBSDKGraphRequest.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = FBSDKGraphRequest.h; sourceTree = ""; }; 9DC658941A6EE5C500B85AAF /* FBSDKGraphRequest.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = FBSDKGraphRequest.m; sourceTree = ""; }; 9DC6589A1A6EE5CD00B85AAF /* FBSDKGraphRequest+Internal.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "FBSDKGraphRequest+Internal.h"; sourceTree = ""; }; @@ -1221,12 +1247,12 @@ 893F44921A64448E001DB0B6 /* Internal */ = { isa = PBXGroup; children = ( - 9D92FC4B1CB31F3A0091B6F7 /* Device */, 9D0BC1481A8D236200BE8BA4 /* AppEvents */, 7EB63D901A9BE720003A7AED /* AppLink */, 894C0B0E1A7021F8009137EF /* Base64 */, 894C0AE11A6F1D27009137EF /* BridgeAPI */, 894C0B061A702194009137EF /* Cryptography */, + 9D92FC4B1CB31F3A0091B6F7 /* Device */, 9D3AF4861A9EF9E200EEF724 /* ErrorRecovery */, 894C0ACD1A6F0D3F009137EF /* FBSDKApplicationDelegate+Internal.h */, 89D05AA71AA1134000609300 /* FBSDKAudioResourceLoader.h */, @@ -1234,9 +1260,13 @@ 9D195CC61B9FE2E000BD6BEC /* FBSDKContainerViewController.h */, 9D195CC71B9FE2E000BD6BEC /* FBSDKContainerViewController.m */, 890414731A647D2E00617215 /* FBSDKCoreKit+Internal.h */, + 520223F51D83C8D200CE0AB5 /* FBSDKDeviceRequestsHelper.h */, + 520223F61D83C8D200CE0AB5 /* FBSDKDeviceRequestsHelper.m */, 81C969301C114723002FC037 /* FBSDKDynamicFrameworkLoader.h */, 894C0B5F1A7150AC009137EF /* FBSDKError.h */, 894C0B601A7150AC009137EF /* FBSDKError.m */, + 9D28F1911DB14DBB0057D709 /* FBSDKImageDownloader.h */, + 9D28F1921DB14DBB0057D709 /* FBSDKImageDownloader.m */, 893F44891A642F11001DB0B6 /* FBSDKInternalUtility.h */, 893F448A1A642F11001DB0B6 /* FBSDKInternalUtility.m */, 9D6999FC1A76E166003AE384 /* FBSDKLogger.h */, @@ -1622,6 +1652,8 @@ 9D10A6881CB38CCB00F42AC1 /* FBSDKDeviceViewControllerBase+Internal.h */, 9D10A6691CB388E800F42AC1 /* FBSDKModalFormPresentationController.h */, 9D10A6801CB388ED00F42AC1 /* FBSDKModalFormPresentationController.m */, + 9DC2777B1DAF4CCD004F4AB5 /* FBSDKSmartDeviceDialogView.h */, + 9DC2777A1DAF4CCD004F4AB5 /* FBSDKSmartDeviceDialogView.m */, ); path = Device; sourceTree = ""; @@ -1653,6 +1685,7 @@ 814AC81D1D1B528900D61E6C /* FBSDKDialogConfiguration.h in Headers */, 814AC81E1D1B528900D61E6C /* FBSDKInternalUtility.h in Headers */, 814AC81F1D1B528900D61E6C /* FBSDKModalFormPresentationController.h in Headers */, + 9DC277931DAF4D6B004F4AB5 /* FBSDKSmartDeviceDialogView.h in Headers */, 814AC8201D1B528900D61E6C /* FBSDKAccessToken.h in Headers */, 814AC8211D1B528900D61E6C /* FBSDKBase64.h in Headers */, 814AC8221D1B528900D61E6C /* FBSDKDeviceDialogView.h in Headers */, @@ -1665,6 +1698,7 @@ 814AC8291D1B528900D61E6C /* FBSDKAccessTokenCache.h in Headers */, 814AC82A1D1B528900D61E6C /* FBSDKAppEventsState.h in Headers */, 814AC82B1D1B528900D61E6C /* FBSDKAppEvents+Internal.h in Headers */, + 52D4F0D51D91A1950030B7FC /* FBSDKDeviceRequestsHelper.h in Headers */, 814AC82C1D1B528900D61E6C /* FBSDKTestUsersManager.h in Headers */, 814AC82D1D1B528900D61E6C /* _FBSDKTemporaryErrorRecoveryAttempter.h in Headers */, 814AC82E1D1B528900D61E6C /* FBSDKGraphRequestBody.h in Headers */, @@ -1696,6 +1730,7 @@ 814AC8481D1B528900D61E6C /* FBSDKAccessTokenCacheV3_21.h in Headers */, 814AC8491D1B528900D61E6C /* FBSDKLogger.h in Headers */, 814AC84A1D1B528900D61E6C /* FBSDKGraphRequest.h in Headers */, + 9D28F1961DB14DBB0057D709 /* FBSDKImageDownloader.h in Headers */, 814AC84B1D1B528900D61E6C /* FBSDKDeviceButton+Internal.h in Headers */, 814AC84C1D1B528900D61E6C /* FBSDKGraphRequest+Internal.h in Headers */, 814AC84D1D1B528900D61E6C /* FBSDKApplicationDelegate+Internal.h in Headers */, @@ -1728,6 +1763,7 @@ 81B71D561D19C87400933E93 /* FBSDKSystemAccountStoreAdapter.h in Headers */, 81B71D571D19C87400933E93 /* FBSDKAppEventsUtility.h in Headers */, 81B71D581D19C87400933E93 /* FBSDKAppEvents+Internal.h in Headers */, + 9D28F1941DB14DBB0057D709 /* FBSDKImageDownloader.h in Headers */, 81B71D591D19C87400933E93 /* FBSDKErrorConfiguration.h in Headers */, 81B71D5A1D19C87400933E93 /* FBSDKTimeSpentData.h in Headers */, 81B71D5B1D19C87400933E93 /* FBSDKIcon.h in Headers */, @@ -1769,6 +1805,7 @@ 81B71D7E1D19C87400933E93 /* FBSDKAccessTokenCacheV3_17.h in Headers */, 81B71D7F1D19C87400933E93 /* FBSDKColor.h in Headers */, 81B71D801D19C87400933E93 /* FBSDKDialogConfiguration.h in Headers */, + 52D4F0D31D91A1940030B7FC /* FBSDKDeviceRequestsHelper.h in Headers */, 81B71D811D19C87400933E93 /* FBSDKCrypto.h in Headers */, 81B71D821D19C87400933E93 /* FBSDKUIUtility.h in Headers */, 81B71D831D19C87400933E93 /* FBSDKDynamicFrameworkLoader.h in Headers */, @@ -1822,6 +1859,7 @@ 9DD696EE1AAADE4000838AF2 /* FBSDKSystemAccountStoreAdapter.h in Headers */, 9D0BC15B1A8D427800BE8BA4 /* FBSDKAppEventsUtility.h in Headers */, 9D0BC1541A8D23DB00BE8BA4 /* FBSDKAppEvents+Internal.h in Headers */, + 9D28F1931DB14DBB0057D709 /* FBSDKImageDownloader.h in Headers */, 9D3AF4501A9EA4BE00EEF724 /* FBSDKErrorConfiguration.h in Headers */, 9D0BC1511A8D236200BE8BA4 /* FBSDKTimeSpentData.h in Headers */, 891687D21AB33CA200F55364 /* FBSDKIcon.h in Headers */, @@ -1863,6 +1901,7 @@ 9DE1F3C51A89C9BE00B54D98 /* FBSDKAccessTokenCacheV3_17.h in Headers */, 9DBA6A371A80267400B4DE6A /* FBSDKColor.h in Headers */, 89830F331A78060A00226ABB /* FBSDKDialogConfiguration.h in Headers */, + 520223F71D83C8D200CE0AB5 /* FBSDKDeviceRequestsHelper.h in Headers */, 894C0B091A702194009137EF /* FBSDKCrypto.h in Headers */, 89688B471AA62E3B00A98519 /* FBSDKUIUtility.h in Headers */, 81C969321C114723002FC037 /* FBSDKDynamicFrameworkLoader.h in Headers */, @@ -1908,6 +1947,7 @@ 9D6DEECC1BC23A46001A94ED /* FBSDKDialogConfiguration.h in Headers */, 9DB0FA921BC22B1A005EB8B1 /* FBSDKInternalUtility.h in Headers */, 9D10A6821CB3892E00F42AC1 /* FBSDKModalFormPresentationController.h in Headers */, + 9DC277921DAF4D6B004F4AB5 /* FBSDKSmartDeviceDialogView.h in Headers */, 9DB0FA811BC1CB64005EB8B1 /* FBSDKAccessToken.h in Headers */, 9DE155311C161A60005FCF5C /* FBSDKBase64.h in Headers */, 9D92FC631CB320430091B6F7 /* FBSDKDeviceDialogView.h in Headers */, @@ -1920,6 +1960,7 @@ 9D6DEEB41BC23855001A94ED /* FBSDKAccessTokenCache.h in Headers */, 9D6DEEB21BC23838001A94ED /* FBSDKAppEventsState.h in Headers */, 9DB0FA8A1BC1CED0005EB8B1 /* FBSDKAppEvents+Internal.h in Headers */, + 52D4F0D41D91A1950030B7FC /* FBSDKDeviceRequestsHelper.h in Headers */, 9D6DEEDB1BC24295001A94ED /* FBSDKTestUsersManager.h in Headers */, 9D6DEED11BC23A93001A94ED /* _FBSDKTemporaryErrorRecoveryAttempter.h in Headers */, 9DB0FAA01BC22CF5005EB8B1 /* FBSDKGraphRequestBody.h in Headers */, @@ -1951,6 +1992,7 @@ 9D6DEEC11BC239D8001A94ED /* FBSDKAccessTokenCacheV3_21.h in Headers */, 9DB0FAA41BC22D17005EB8B1 /* FBSDKLogger.h in Headers */, 9DB0FA9E1BC22CB0005EB8B1 /* FBSDKGraphRequest.h in Headers */, + 9D28F1951DB14DBB0057D709 /* FBSDKImageDownloader.h in Headers */, 9D9E16B11CB46D3C00C8B68F /* FBSDKDeviceButton+Internal.h in Headers */, 9DB0FA9D1BC22CA2005EB8B1 /* FBSDKGraphRequest+Internal.h in Headers */, 9D65383D1BF44F7D008A08E9 /* FBSDKApplicationDelegate+Internal.h in Headers */, @@ -2394,6 +2436,7 @@ 814AC7F91D1B528900D61E6C /* FBSDKIcon.m in Sources */, 814AC7FA1D1B528900D61E6C /* FBSDKMath.m in Sources */, 814AC7FB1D1B528900D61E6C /* FBSDKAccessTokenCacheV3.m in Sources */, + 9DC277951DAF4D71004F4AB5 /* FBSDKSmartDeviceDialogView.m in Sources */, 814AC7FC1D1B528900D61E6C /* FBSDKAccessToken.m in Sources */, 814AC7FD1D1B528900D61E6C /* FBSDKGraphRequestBody.m in Sources */, 814AC7FE1D1B528900D61E6C /* FBSDKErrorConfiguration.m in Sources */, @@ -2403,6 +2446,7 @@ 814AC8021D1B528900D61E6C /* FBSDKAppEvents.m in Sources */, 814AC8031D1B528900D61E6C /* FBSDKTestUsersManager.m in Sources */, 814AC8041D1B528900D61E6C /* FBSDKGraphRequest.m in Sources */, + 52D4F0D21D91A18F0030B7FC /* FBSDKDeviceRequestsHelper.m in Sources */, 814AC8051D1B528900D61E6C /* FBSDKLogo.m in Sources */, 814AC8061D1B528900D61E6C /* FBSDKDeviceButton.m in Sources */, 814AC8071D1B528900D61E6C /* FBSDKGraphRequestConnection.m in Sources */, @@ -2412,6 +2456,7 @@ 814AC80B1D1B528900D61E6C /* FBSDKDeviceDialogView.m in Sources */, 814AC80C1D1B528900D61E6C /* FBSDKURLConnection.m in Sources */, 814AC80D1D1B528900D61E6C /* FBSDKAppEventsStateManager.m in Sources */, + 9D28F19A1DB14DBB0057D709 /* FBSDKImageDownloader.m in Sources */, 814AC80E1D1B528900D61E6C /* FBSDKLogger.m in Sources */, 814AC80F1D1B528900D61E6C /* FBSDKTimeSpentData.m in Sources */, 814AC8101D1B528900D61E6C /* FBSDKAccessTokenCacheV3_21.m in Sources */, @@ -2454,6 +2499,7 @@ 81B71D191D19C87400933E93 /* FBSDKMonotonicTime.m in Sources */, 81B71D1A1D19C87400933E93 /* FBSDKAccessTokenCacheV4.m in Sources */, 81B71D1B1D19C87400933E93 /* FBSDKAppLinkUtility.m in Sources */, + 9D28F1981DB14DBB0057D709 /* FBSDKImageDownloader.m in Sources */, 81B71D1C1D19C87400933E93 /* FBSDKViewImpressionTracker.m in Sources */, 81B71D1D1D19C87400933E93 /* FBSDKBridgeAPIRequest.m in Sources */, 81B71D1E1D19C87400933E93 /* FBSDKAppEventsDeviceInfo.m in Sources */, @@ -2491,6 +2537,7 @@ 81B71D3F1D19C87400933E93 /* FBSDKBridgeAPIProtocolNativeV1.m in Sources */, 81B71D401D19C87400933E93 /* FBSDKWebDialogView.m in Sources */, 81B71D411D19C87400933E93 /* FBSDKErrorConfiguration.m in Sources */, + 52D4F0BC1D91A18D0030B7FC /* FBSDKDeviceRequestsHelper.m in Sources */, 81B71D421D19C87400933E93 /* FBSDKKeychainStoreViaBundleID.m in Sources */, 81B71D431D19C87400933E93 /* FBSDKServerConfiguration.m in Sources */, 81B71D441D19C87400933E93 /* FBSDKMaleSilhouetteIcon.m in Sources */, @@ -2530,6 +2577,7 @@ 9D32A83F1A69941A000A936D /* FBSDKAccessTokenCacheV4.m in Sources */, 7E30917A1AA907E0004E91D5 /* FBSDKAppLinkUtility.m in Sources */, 89688B631AA64C5E00A98519 /* FBSDKViewImpressionTracker.m in Sources */, + 9D28F1971DB14DBB0057D709 /* FBSDKImageDownloader.m in Sources */, 894C0AD21A6F15F7009137EF /* FBSDKBridgeAPIRequest.m in Sources */, EA4CF2401D344BFC007AA2EB /* FBSDKURLSessionTask.m in Sources */, 5F7064091AF7342600E42ED7 /* FBSDKAppEventsDeviceInfo.m in Sources */, @@ -2567,6 +2615,7 @@ 894C0AF71A6F2278009137EF /* FBSDKBridgeAPIProtocolNativeV1.m in Sources */, 89FB8C4B1A842A8A003CAE60 /* FBSDKWebDialogView.m in Sources */, 9D3AF4511A9EA4BE00EEF724 /* FBSDKErrorConfiguration.m in Sources */, + 520223F81D83C8D200CE0AB5 /* FBSDKDeviceRequestsHelper.m in Sources */, 9DE1F3CE1A89D9CD00B54D98 /* FBSDKKeychainStoreViaBundleID.m in Sources */, 89830F301A7805E100226ABB /* FBSDKServerConfiguration.m in Sources */, 899C3D031A8C1ED200EA8658 /* FBSDKMaleSilhouetteIcon.m in Sources */, @@ -2621,6 +2670,7 @@ 9DDC11281BEC412C00A88306 /* FBSDKIcon.m in Sources */, 9D6DEEBB1BC238EB001A94ED /* FBSDKMath.m in Sources */, 9D6DEEC61BC23A11001A94ED /* FBSDKAccessTokenCacheV3.m in Sources */, + 9DC277941DAF4D70004F4AB5 /* FBSDKSmartDeviceDialogView.m in Sources */, 9D6DEEAA1BC236B9001A94ED /* FBSDKAccessToken.m in Sources */, 9DB0FAA11BC22CF9005EB8B1 /* FBSDKGraphRequestBody.m in Sources */, 9DB0FA9B1BC22BED005EB8B1 /* FBSDKErrorConfiguration.m in Sources */, @@ -2630,6 +2680,7 @@ 9D6DEEA91BC2368D001A94ED /* FBSDKAppEvents.m in Sources */, 9D6DEEE81BC2429B001A94ED /* FBSDKTestUsersManager.m in Sources */, 9DB0FA9F1BC22CBB005EB8B1 /* FBSDKGraphRequest.m in Sources */, + 52D4F0D11D91A18F0030B7FC /* FBSDKDeviceRequestsHelper.m in Sources */, 9DDC112A1BEC413900A88306 /* FBSDKLogo.m in Sources */, 9D9E16AF1CB46C8E00C8B68F /* FBSDKDeviceButton.m in Sources */, 9DB0FA891BC1CDD0005EB8B1 /* FBSDKGraphRequestConnection.m in Sources */, @@ -2639,6 +2690,7 @@ 9D92FC641CB320430091B6F7 /* FBSDKDeviceDialogView.m in Sources */, 9DB0FAAB1BC22D93005EB8B1 /* FBSDKURLConnection.m in Sources */, 9D6DEEB51BC23862001A94ED /* FBSDKAppEventsStateManager.m in Sources */, + 9D28F1991DB14DBB0057D709 /* FBSDKImageDownloader.m in Sources */, 9DB0FAA51BC22D1C005EB8B1 /* FBSDKLogger.m in Sources */, 9D6DEEB01BC2379C001A94ED /* FBSDKTimeSpentData.m in Sources */, 9D6DEEC21BC239DA001A94ED /* FBSDKAccessTokenCacheV3_21.m in Sources */, diff --git a/FBSDKCoreKit/FBSDKCoreKit/FBSDKAppEvents.h b/FBSDKCoreKit/FBSDKCoreKit/FBSDKAppEvents.h index 9b5baab082..93a4441791 100644 --- a/FBSDKCoreKit/FBSDKCoreKit/FBSDKAppEvents.h +++ b/FBSDKCoreKit/FBSDKCoreKit/FBSDKAppEvents.h @@ -18,6 +18,8 @@ #import +#import + #import "FBSDKMacros.h" @class FBSDKAccessToken; @@ -496,4 +498,24 @@ FBSDK_EXTERN NSString *const FBSDKAppEventParameterValueNo; via the `[FBSDKSettings limitEventAndDataUsage]` flag, or a specific Facebook user cannot be identified. */ + (FBSDKGraphRequest *)requestForCustomAudienceThirdPartyIDWithAccessToken:(FBSDKAccessToken *)accessToken; + +/* + @abstract Sets a custom user ID to associate with all app events. + @discussion The userID is persisted until it is cleared by passing nil. + */ ++ (void)setUserID:(NSString *)userID; + +/* + @abstract Returns the set custom user ID. + */ ++ (NSString *)userID; + +/* + @abstract Sends a request to update the properties for the current user, set by `setUserID:` + @discussion You must call `FBSDKAppEvents setUserID:` before making this call. + @param properties the custom user properties + @param handler the optional completion handler + */ ++ (void)updateUserProperties:(NSDictionary *)properties handler:(FBSDKGraphRequestHandler)handler; + @end diff --git a/FBSDKCoreKit/FBSDKCoreKit/FBSDKAppEvents.m b/FBSDKCoreKit/FBSDKCoreKit/FBSDKAppEvents.m index fe8efc1571..a28114789a 100644 --- a/FBSDKCoreKit/FBSDKCoreKit/FBSDKAppEvents.m +++ b/FBSDKCoreKit/FBSDKCoreKit/FBSDKAppEvents.m @@ -120,6 +120,8 @@ NSString *const FBSDKAppEventNameFBSDKSendButtonImpression = @"fb_send_button_impression"; NSString *const FBSDKAppEventNameFBSDKShareButtonImpression = @"fb_share_button_impression"; +NSString *const FBSDKAppEventNameFBSDKSmartLoginService = @"fb_smart_login_service"; + NSString *const FBSDKAppEventNameFBSDKLikeButtonDidTap = @"fb_like_button_did_tap"; NSString *const FBSDKAppEventNameFBSDKLoginButtonDidTap = @"fb_login_button_did_tap"; NSString *const FBSDKAppEventNameFBSDKSendButtonDidTap = @"fb_send_button_did_tap"; @@ -142,6 +144,8 @@ NSString *const FBSDKAppEventNameFBSDKEventMessengerShareDialogShow = @"fb_messenger_dialog_share_show"; NSString *const FBSDKAppEventNameFBSDKEventAppInviteShareDialogShow = @"fb_app_invite_share_show"; +NSString *const FBSDKAppEventNameFBSessionNativeAppSwitchLoginDialogResult = @"fb_mobile_login_native_app_switch_dialog_result"; + // Event Parameters internal to this file NSString *const FBSDKAppEventParameterDialogOutcome = @"fb_dialog_outcome"; NSString *const FBSDKAppEventParameterDialogErrorMessage = @"fb_dialog_outcome_error_message"; @@ -191,6 +195,7 @@ #define NUM_LOG_EVENTS_TO_TRY_TO_FLUSH_AFTER 100 #define FLUSH_PERIOD_IN_SECONDS 15 #define APP_SUPPORTS_ATTRIBUTION_ID_RECHECK_PERIOD 60 * 60 * 24 +#define USER_ID_USER_DEFAULTS_KEY @"com.facebook.sdk.appevents.userid" static NSString *g_overrideAppID = nil; @@ -211,6 +216,7 @@ @implementation FBSDKAppEvents NSTimer *_attributionIDRecheckTimer; FBSDKServerConfiguration *_serverConfiguration; FBSDKAppEventsState *_appEventsState; + NSString *_userID; } #pragma mark - Object Lifecycle @@ -256,6 +262,9 @@ - (FBSDKAppEvents *)init selector:@selector(applicationDidBecomeActive) name:UIApplicationDidBecomeActiveNotification object:NULL]; + + NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults]; + _userID = [defaults stringForKey:USER_ID_USER_DEFAULTS_KEY]; } return self; @@ -443,6 +452,68 @@ + (void)flush [[FBSDKAppEvents singleton] flushForReason:FBSDKAppEventsFlushReasonExplicit]; } ++ (void)setUserID:(NSString *)userID +{ + if ([[[self class] singleton]->_userID isEqualToString:userID]) { + return; + } + [[self class] singleton]->_userID = userID; + NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults]; + [defaults setObject:userID forKey:USER_ID_USER_DEFAULTS_KEY]; + [defaults synchronize]; +} + ++ (NSString *)userID +{ + return [[self class] singleton]->_userID; +} + ++ (void)updateUserProperties:(NSDictionary *)properties handler:(FBSDKGraphRequestHandler)handler +{ + NSString *userID = [[self class] userID]; + + if (userID.length == 0) { + [FBSDKLogger singleShotLogEntry:FBSDKLoggingBehaviorDeveloperErrors logEntry:@"Missing [FBSDKAppEvents userID] for [FBSDKAppEvents updateUserProperties:]"]; + NSError *error = [FBSDKError requiredArgumentErrorWithName:@"userID" message:@"Missing [FBSDKAppEvents userID] for [FBSDKAppEvents updateUserProperties:]"]; + if (handler) { + handler(nil, nil, error); + } + return; + } + NSMutableDictionary *dataDictionary = [NSMutableDictionary dictionaryWithCapacity:3]; + dataDictionary[@"user_unique_id"] = [FBSDKAppEvents userID]; + [FBSDKInternalUtility dictionary:dataDictionary setObject:[FBSDKAppEventsUtility advertiserID] forKey:@"advertiser_id"]; + [FBSDKInternalUtility dictionary:dataDictionary setObject:properties forKey:@"custom_data"]; + + NSError *error; + __block NSError *invalidObjectError; + NSString *dataJSONString = [FBSDKInternalUtility JSONStringForObject:@[dataDictionary] error:&error invalidObjectHandler:^id(id object, BOOL *stop) { + *stop = YES; + invalidObjectError = [FBSDKError unknownErrorWithMessage:@"The values in the properties dictionary must be NSStrings or NSNumbers"]; + return nil; + }]; + if (!error) { + error = invalidObjectError; + } + if (error) { + [FBSDKLogger singleShotLogEntry:FBSDKLoggingBehaviorDeveloperErrors logEntry:@"Failed to serialize properties for [FBSDKAppEvents updateUserProperties:]"]; + if (handler) { + handler(nil, nil, error); + } + return; + } + NSDictionary *params = @{ @"data" : dataJSONString }; + FBSDKGraphRequest *request = [[FBSDKGraphRequest alloc] initWithGraphPath:[NSString stringWithFormat:@"%@/user_properties", [FBSDKSettings appID]] + parameters:params + tokenString:[FBSDKAccessToken currentAccessToken].tokenString + HTTPMethod:@"POST" + flags:FBSDKGraphRequestFlagDisableErrorRecovery | + FBSDKGraphRequestFlagDoNotInvalidateTokenOnError | + FBSDKGraphRequestFlagSkipClientToken + ]; + [request startWithCompletionHandler:handler]; +} + #pragma mark - Internal Methods + (void)logImplicitEvent:(NSString *)eventName @@ -593,6 +664,7 @@ - (void)instanceLogEvent:(NSString *)eventName if (isImplicitlyLogged) { eventDictionary[FBSDKAppEventParameterImplicitlyLogged] = @"1"; } + [FBSDKInternalUtility dictionary:eventDictionary setObject:_userID forKey:@"_app_user_id"]; NSString *currentViewControllerName; if ([NSThread isMainThread]) { diff --git a/FBSDKCoreKit/FBSDKCoreKit/FBSDKApplicationDelegate.m b/FBSDKCoreKit/FBSDKCoreKit/FBSDKApplicationDelegate.m index 8688c02942..8ff7f118ca 100644 --- a/FBSDKCoreKit/FBSDKCoreKit/FBSDKApplicationDelegate.m +++ b/FBSDKCoreKit/FBSDKCoreKit/FBSDKApplicationDelegate.m @@ -41,6 +41,13 @@ #import "FBSDKProfile+Internal.h" #endif +// TODO: t13635729 Remove when Sandcastle builds witn Xcode8 +@interface UIApplication (iOS10) + +- (void)openURL:(NSURL *)url options:(NSDictionary *)options completionHandler:(void (^)(BOOL))completion; + +@end + NSString *const FBSDKApplicationDidBecomeActiveNotification = @"com.facebook.sdk.FBSDKApplicationDidBecomeActiveNotification"; static NSString *const FBSDKAppLinkInboundEvent = @"fb_al_inbound"; @@ -54,6 +61,7 @@ @implementation FBSDKApplicationDelegate #endif BOOL _expectingBackground; UIViewController *_safariViewController; + BOOL _isDismissingSafariViewController; } #pragma mark - Class Methods @@ -147,21 +155,30 @@ - (BOOL)application:(UIApplication *)application [FBSDKTimeSpentData setSourceApplication:sourceApplication openURL:url]; #if !TARGET_OS_TV - // if they completed a SFVC flow, dismiss it. - [_safariViewController.presentingViewController dismissViewControllerAnimated:YES completion: nil]; - _safariViewController = nil; - - if (_pendingURLOpen) { - id pendingURLOpen = _pendingURLOpen; + id pendingURLOpen = _pendingURLOpen; + void (^completePendingOpenURLBlock)(void) = ^{ _pendingURLOpen = nil; - - if ([pendingURLOpen application:application - openURL:url - sourceApplication:sourceApplication - annotation:annotation]) { - return YES; - } + [pendingURLOpen application:application + openURL:url + sourceApplication:sourceApplication + annotation:annotation]; + _isDismissingSafariViewController = NO; + }; + // if they completed a SFVC flow, dismiss it. + if (_safariViewController) { + _isDismissingSafariViewController = YES; + [_safariViewController.presentingViewController dismissViewControllerAnimated:YES + completion:completePendingOpenURLBlock]; + _safariViewController = nil; + } else { + completePendingOpenURLBlock(); + } + if ([pendingURLOpen canOpenURL:url + forApplication:application + sourceApplication:sourceApplication + annotation:annotation]) { + return YES; } if ([self _handleBridgeAPIResponseURL:url sourceApplication:sourceApplication]) { return YES; @@ -211,7 +228,7 @@ - (void)applicationDidBecomeActive:(NSNotification *)notification // _expectingBackground can be YES if the caller started doing work (like login) // within the app delegate's lifecycle like openURL, in which case there // might have been a "didBecomeActive" event pending that we want to ignore. - if (!_expectingBackground && !_safariViewController) { + if (!_expectingBackground && !_safariViewController && !_isDismissingSafariViewController) { _active = YES; #if !TARGET_OS_TV [_pendingURLOpen applicationDidBecomeActive:[notification object]]; @@ -233,18 +250,23 @@ - (void)openURL:(NSURL *)url sender:(id)sender handler:(void(^) _pendingURLOpen = sender; dispatch_async(dispatch_get_main_queue(), ^{ // Dispatch openURL calls to prevent hangs if we're inside the current app delegate's openURL flow already - BOOL opened = [[UIApplication sharedApplication] openURL:url]; - - if ([url.scheme hasPrefix:@"http"] && !opened) { - NSOperatingSystemVersion iOS8Version = { .majorVersion = 8, .minorVersion = 0, .patchVersion = 0 }; - if (![FBSDKInternalUtility isOSRunTimeVersionAtLeast:iOS8Version]) { - // Safari openURL calls can wrongly return NO on iOS 7 so manually overwrite that case to YES. - // Otherwise we would rather trust in the actual result of openURL - opened = YES; + NSOperatingSystemVersion iOS10Version = { .majorVersion = 10, .minorVersion = 0, .patchVersion = 0 }; + if ([FBSDKInternalUtility isOSRunTimeVersionAtLeast:iOS10Version]) { + [[UIApplication sharedApplication] openURL:url options:@{} completionHandler:handler]; + } else { + BOOL opened = [[UIApplication sharedApplication] openURL:url]; + + if ([url.scheme hasPrefix:@"http"] && !opened) { + NSOperatingSystemVersion iOS8Version = { .majorVersion = 8, .minorVersion = 0, .patchVersion = 0 }; + if (![FBSDKInternalUtility isOSRunTimeVersionAtLeast:iOS8Version]) { + // Safari openURL calls can wrongly return NO on iOS 7 so manually overwrite that case to YES. + // Otherwise we would rather trust in the actual result of openURL + opened = YES; + } + } + if (handler) { + handler(opened); } - } - if (handler) { - handler(opened); } }); } diff --git a/FBSDKCoreKit/FBSDKCoreKit/FBSDKCoreKit.h b/FBSDKCoreKit/FBSDKCoreKit/FBSDKCoreKit.h index 802e1e293d..0400f0375e 100644 --- a/FBSDKCoreKit/FBSDKCoreKit/FBSDKCoreKit.h +++ b/FBSDKCoreKit/FBSDKCoreKit/FBSDKCoreKit.h @@ -44,5 +44,5 @@ #import #endif -#define FBSDK_VERSION_STRING @"4.16.1" +#define FBSDK_VERSION_STRING @"4.17.0" #define FBSDK_TARGET_PLATFORM_VERSION @"v2.8" diff --git a/FBSDKCoreKit/FBSDKCoreKit/FBSDKDeviceViewControllerBase.m b/FBSDKCoreKit/FBSDKCoreKit/FBSDKDeviceViewControllerBase.m index ee11d53f0c..20ca7ad9f7 100644 --- a/FBSDKCoreKit/FBSDKCoreKit/FBSDKDeviceViewControllerBase.m +++ b/FBSDKCoreKit/FBSDKCoreKit/FBSDKDeviceViewControllerBase.m @@ -40,7 +40,12 @@ - (instancetype)init - (void)loadView { - FBSDKDeviceDialogView *deviceView = [[FBSDKDeviceDialogView alloc] initWithFrame:[UIScreen mainScreen].bounds]; + CGRect frame = [UIScreen mainScreen].bounds; + BOOL smartLoginEnabled = ([FBSDKServerConfigurationManager cachedServerConfiguration].smartLoginOptions & FBSDKServerConfigurationSmartLoginOptionsEnabled); + FBSDKDeviceDialogView *deviceView = + (smartLoginEnabled ? + [[FBSDKSmartDeviceDialogView alloc] initWithFrame:frame] : + [[FBSDKDeviceDialogView alloc] initWithFrame:frame] ); deviceView.delegate = self; self.view = deviceView; } @@ -118,5 +123,4 @@ - (void)deviceDialogViewDidCancel:(FBSDKDeviceDialogView *)deviceDialogView { [self dismissViewControllerAnimated:YES completion:NULL]; } - @end diff --git a/FBSDKCoreKit/FBSDKCoreKit/Internal/AppEvents/FBSDKAppEvents+Internal.h b/FBSDKCoreKit/FBSDKCoreKit/Internal/AppEvents/FBSDKAppEvents+Internal.h index 320150ab74..6012fef02a 100644 --- a/FBSDKCoreKit/FBSDKCoreKit/Internal/AppEvents/FBSDKAppEvents+Internal.h +++ b/FBSDKCoreKit/FBSDKCoreKit/Internal/AppEvents/FBSDKAppEvents+Internal.h @@ -85,6 +85,9 @@ FBSDK_EXTERN NSString *const FBSDKAppEventNameFBDialogsNativeLoginDialogEnd; /*! Use to log the e2e timestamp metrics for web login */ FBSDK_EXTERN NSString *const FBSDKAppEventNameFBDialogsWebLoginCompleted; +/*! Use to log the result of the App Switch OS AlertView. Only available on OS >= iOS10 */ +FBSDK_EXTERN NSString *const FBSDKAppEventNameFBSessionNativeAppSwitchLoginDialogResult; + /*! Use to log the results of a share dialog */ FBSDK_EXTERN NSString *const FBSDLAppEventNameFBSDKEventShareDialogResult; FBSDK_EXTERN NSString *const FBSDKAppEventNameFBSDKEventMessengerShareDialogResult; @@ -129,6 +132,8 @@ FBSDK_EXTERN NSString *const FBSDKAppEventNameFBSDKLoginButtonImpression; FBSDK_EXTERN NSString *const FBSDKAppEventNameFBSDKSendButtonImpression; FBSDK_EXTERN NSString *const FBSDKAppEventNameFBSDKShareButtonImpression; +FBSDK_EXTERN NSString *const FBSDKAppEventNameFBSDKSmartLoginService; + FBSDK_EXTERN NSString *const FBSDKAppEventNameFBSDKLikeButtonDidTap; FBSDK_EXTERN NSString *const FBSDKAppEventNameFBSDKLoginButtonDidTap; FBSDK_EXTERN NSString *const FBSDKAppEventNameFBSDKSendButtonDidTap; diff --git a/FBSDKCoreKit/FBSDKCoreKit/Internal/BridgeAPI/FBSDKURLOpening.h b/FBSDKCoreKit/FBSDKCoreKit/Internal/BridgeAPI/FBSDKURLOpening.h index 83c355aacb..22250d4af4 100644 --- a/FBSDKCoreKit/FBSDKCoreKit/Internal/BridgeAPI/FBSDKURLOpening.h +++ b/FBSDKCoreKit/FBSDKCoreKit/Internal/BridgeAPI/FBSDKURLOpening.h @@ -20,11 +20,20 @@ // Implementations should make sure they can handle nil parameters // which is possible in SafariViewController. +// see canOpenURL below. - (BOOL)application:(UIApplication *)application openURL:(NSURL *)url sourceApplication:(NSString *)sourceApplication annotation:(id)annotation; +// create a different handler to return YES/NO if the receiver can process the above openURL:. +// This is separated so that we can process the openURL: in callbacks, while still returning +// the result of canOpenURL synchronously in FBSDKApplicationDelegate +- (BOOL)canOpenURL:(NSURL *)url + forApplication:(UIApplication *)application + sourceApplication:(NSString *)sourceApplication + annotation:(id)annotation; + - (void)applicationDidBecomeActive:(UIApplication *)application; @end diff --git a/FBSDKCoreKit/FBSDKCoreKit/Internal/Device/FBSDKDeviceDialogView.m b/FBSDKCoreKit/FBSDKCoreKit/Internal/Device/FBSDKDeviceDialogView.m index d8a011bf17..225d839c23 100644 --- a/FBSDKCoreKit/FBSDKCoreKit/Internal/Device/FBSDKDeviceDialogView.m +++ b/FBSDKCoreKit/FBSDKCoreKit/Internal/Device/FBSDKDeviceDialogView.m @@ -133,7 +133,7 @@ - (void)_buildView NSString *localizedFormatString = NSLocalizedStringWithDefaultValue(@"DeviceLogin.LogInPrompt", @"FacebookSDK", [FBSDKInternalUtility bundleForStrings], - @"Visit %@ on your smartphone or computer and enter the above code.", + @"Visit %@ and enter your code.", @"The format string for device login instructions"); NSString *const deviceLoginURLString = @"facebook.com/device"; NSString *instructionString = [NSString localizedStringWithFormat:localizedFormatString, deviceLoginURLString]; @@ -160,8 +160,8 @@ - (void)_buildView buttonContainerView.translatesAutoresizingMaskIntoConstraints = NO; [dialogView addSubview:buttonContainerView]; [NSLayoutConstraint constraintWithItem:buttonContainerView attribute:NSLayoutAttributeCenterX relatedBy:NSLayoutRelationEqual toItem:dialogView attribute:NSLayoutAttributeCenterX multiplier:1.0 constant:0].active = YES; - [buttonContainerView.topAnchor constraintEqualToAnchor:instructionLabel.bottomAnchor - constant:96].active = YES; + + [buttonContainerView.heightAnchor constraintEqualToConstant:100].active = YES; [buttonContainerView.leadingAnchor constraintEqualToAnchor:dialogView.leadingAnchor constant:400].active = YES; [dialogView.trailingAnchor constraintEqualToAnchor:buttonContainerView.trailingAnchor diff --git a/FBSDKCoreKit/FBSDKCoreKit/Internal/Device/FBSDKSmartDeviceDialogView.h b/FBSDKCoreKit/FBSDKCoreKit/Internal/Device/FBSDKSmartDeviceDialogView.h new file mode 100644 index 0000000000..5649440643 --- /dev/null +++ b/FBSDKCoreKit/FBSDKCoreKit/Internal/Device/FBSDKSmartDeviceDialogView.h @@ -0,0 +1,25 @@ +// Copyright (c) 2014-present, Facebook, Inc. All rights reserved. +// +// You are hereby granted a non-exclusive, worldwide, royalty-free license to use, +// copy, modify, and distribute this software in source code or binary form for use +// in connection with the web services and APIs provided by Facebook. +// +// As with any software that integrates with the Facebook platform, your use of +// this software is subject to the Facebook Developer Principles and Policies +// [http://developers.facebook.com/policy/]. This copyright notice shall be +// included in all copies or substantial portions of the software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS +// FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR +// COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER +// IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN +// CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + +#import + +#import "FBSDKDeviceDialogView.h" + +@interface FBSDKSmartDeviceDialogView : FBSDKDeviceDialogView + +@end diff --git a/FBSDKCoreKit/FBSDKCoreKit/Internal/Device/FBSDKSmartDeviceDialogView.m b/FBSDKCoreKit/FBSDKCoreKit/Internal/Device/FBSDKSmartDeviceDialogView.m new file mode 100644 index 0000000000..21763eeb93 --- /dev/null +++ b/FBSDKCoreKit/FBSDKCoreKit/Internal/Device/FBSDKSmartDeviceDialogView.m @@ -0,0 +1,317 @@ +// Copyright (c) 2014-present, Facebook, Inc. All rights reserved. +// +// You are hereby granted a non-exclusive, worldwide, royalty-free license to use, +// copy, modify, and distribute this software in source code or binary form for use +// in connection with the web services and APIs provided by Facebook. +// +// As with any software that integrates with the Facebook platform, your use of +// this software is subject to the Facebook Developer Principles and Policies +// [http://developers.facebook.com/policy/]. This copyright notice shall be +// included in all copies or substantial portions of the software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS +// FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR +// COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER +// IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN +// CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + +#import "FBSDKSmartDeviceDialogView.h" + +#import "FBSDKCoreKit+Internal.h" + +// don't download icons more than once a day. +static const NSTimeInterval kSmartLoginIconsTTL = 60 * 60 * 24; + +@implementation FBSDKSmartDeviceDialogView +{ + UIActivityIndicatorView *_spinner; + UILabel *_confirmationCodeLabel; +} + +- (instancetype)initWithFrame:(CGRect)frame +{ + if ((self = [super initWithFrame:frame])) { + [self _prepareImageCache]; + } + return self; +} + +#pragma mark - Properties + +- (void)setConfirmationCode:(NSString *)confirmationCode +{ + if (![self.confirmationCode isEqualToString:confirmationCode]) { + if (confirmationCode == nil) { + _confirmationCodeLabel.text = @""; + _confirmationCodeLabel.hidden = YES; + [_spinner startAnimating]; + } else { + [_spinner stopAnimating]; + _confirmationCodeLabel.text = confirmationCode; + _confirmationCodeLabel.hidden = NO; + } + } +} + +#pragma mark - Helpers + +- (void)_prepareImageCache +{ + [FBSDKServerConfigurationManager loadServerConfigurationWithCompletionBlock:^(FBSDKServerConfiguration *serverConfiguration, NSError *error) { + if ((serverConfiguration.smartLoginOptions & FBSDKServerConfigurationSmartLoginOptionsEnabled) && + serverConfiguration.smartLoginMenuIconURL && + serverConfiguration.smartLoginBookmarkIconURL) { + __block UIImage *bookmarkIconImage; + __block UIImage *menuIconImage; + __block NSUInteger count = 0; + void(^buildViewBlock)(void) = ^{ + count++; + if (count >= 2){ + dispatch_async(dispatch_get_main_queue(), ^{ + [self _buildViewWithBookmarkIcon:bookmarkIconImage + menuIcon:menuIconImage]; + [self setNeedsLayout]; + }); + } + }; + [[FBSDKImageDownloader sharedInstance] downloadImageWithURL:serverConfiguration.smartLoginBookmarkIconURL + ttl:kSmartLoginIconsTTL + completion:^(UIImage *image) { + bookmarkIconImage = image; + buildViewBlock(); + }]; + [[FBSDKImageDownloader sharedInstance] downloadImageWithURL:serverConfiguration.smartLoginMenuIconURL + ttl:kSmartLoginIconsTTL + completion:^(UIImage *image) { + menuIconImage = image; + buildViewBlock(); + }]; + } else { + [self _buildViewWithBookmarkIcon:nil menuIcon:nil]; + } + }]; +} + +- (void)_buildViewWithBookmarkIcon:(UIImage *)bookmarkIcon + menuIcon:(UIImage *)menuIcon +{ + // This is a "static" view with just a cancel button so add all the constraints here + // rather than properly override `updateConstraints`. + const CGFloat kWidth = 1080; + const CGFloat kHeight = 820; + const CGFloat kVerticalSpaceBetweenHeaderViewAndInstructionLabel = 50; + const CGFloat kDialogHeaderViewHeight = 250; + const CGFloat kLogoSize = 44; + const CGFloat kLogoMargin = 30; + const CGFloat kInstructionTextHorizontalMargin = 100; + const CGFloat kConfirmationCodeFontSize = 108; + const CGFloat kFontColorValue = 119.0/255.0; + const CGFloat kInstructionFontSize = 32; + const CGFloat kVerticalMarginOrLabel = 40; + + // build the container view. + UIView *dialogView = [[UIView alloc] init]; + dialogView.layer.cornerRadius = 3; + dialogView.translatesAutoresizingMaskIntoConstraints = NO; + dialogView.clipsToBounds = YES; + dialogView.backgroundColor = [UIColor whiteColor]; + [self addSubview:dialogView]; + [NSLayoutConstraint constraintWithItem:dialogView attribute:NSLayoutAttributeCenterX relatedBy:NSLayoutRelationEqual toItem:self attribute:NSLayoutAttributeCenterX multiplier:1.0 constant:0].active = YES;; + [NSLayoutConstraint constraintWithItem:dialogView attribute:NSLayoutAttributeCenterY relatedBy:NSLayoutRelationEqual toItem:self attribute:NSLayoutAttributeCenterY multiplier:1.0 constant:0].active = YES; + [dialogView.widthAnchor constraintEqualToConstant:kWidth].active = YES; + [dialogView.heightAnchor constraintEqualToConstant:kHeight].active = YES; + + // build the header container view (which will contain the logo and code). + UIView *dialogHeaderView = [[UIView alloc] init]; + dialogHeaderView.translatesAutoresizingMaskIntoConstraints = NO; + dialogHeaderView.backgroundColor = [UIColor colorWithRed:226.0/255.0 green:231.0/255.0 blue:235.0/255.0 alpha:0.85]; + [dialogView addSubview:dialogHeaderView]; + [dialogHeaderView.leadingAnchor constraintEqualToAnchor:dialogView.leadingAnchor].active = YES; + [dialogHeaderView.trailingAnchor constraintEqualToAnchor:dialogView.trailingAnchor].active = YES; + [dialogHeaderView.heightAnchor constraintEqualToConstant:kDialogHeaderViewHeight].active = YES; + [dialogHeaderView.topAnchor constraintEqualToAnchor:dialogView.topAnchor].active = YES; + + // build the logo. + CGSize imageSize = CGSizeMake(kLogoSize, kLogoSize); + FBSDKLogo *logoHelper =[[FBSDKLogo alloc] initWithColor:[UIColor colorWithRed:66.0/255.0 green:103.0/255.0 blue:178.0/255.0 alpha:1]]; + UIImage *image = [logoHelper imageWithSize:imageSize]; + image = [image resizableImageWithCapInsets:UIEdgeInsetsZero resizingMode:UIImageResizingModeStretch]; + UIImageView *imageView = [[UIImageView alloc] initWithImage:image]; + imageView.translatesAutoresizingMaskIntoConstraints = NO; + [dialogHeaderView addSubview:imageView]; + [imageView.widthAnchor constraintEqualToConstant:kLogoSize].active = YES; + [imageView.heightAnchor constraintEqualToConstant:kLogoSize].active = YES; + [imageView.topAnchor constraintEqualToAnchor:dialogHeaderView.topAnchor constant:kLogoMargin].active = YES; + [imageView.leadingAnchor constraintEqualToAnchor:dialogHeaderView.leadingAnchor constant:kLogoMargin].active = YES; + + // build the activity spinner + _spinner = [[UIActivityIndicatorView alloc] initWithActivityIndicatorStyle:UIActivityIndicatorViewStyleWhiteLarge]; + _spinner.translatesAutoresizingMaskIntoConstraints = NO; + [dialogHeaderView addSubview:_spinner]; + [NSLayoutConstraint constraintWithItem:_spinner attribute:NSLayoutAttributeCenterX relatedBy:NSLayoutRelationEqual toItem:dialogHeaderView attribute:NSLayoutAttributeCenterX multiplier:1.0 constant:0].active = YES; + [NSLayoutConstraint constraintWithItem:_spinner attribute:NSLayoutAttributeCenterY relatedBy:NSLayoutRelationEqual toItem:dialogHeaderView attribute:NSLayoutAttributeCenterY multiplier:1.0 constant:0].active = YES; + [_spinner.widthAnchor constraintEqualToConstant:kConfirmationCodeFontSize].active = YES; + [_spinner.heightAnchor constraintEqualToConstant:kConfirmationCodeFontSize].active = YES; + [_spinner startAnimating]; + + // build the confirmation code (which replaces the spinner when the code is available). + _confirmationCodeLabel = [[UILabel alloc] init]; + _confirmationCodeLabel.translatesAutoresizingMaskIntoConstraints = NO; + _confirmationCodeLabel.textColor = logoHelper.color; + _confirmationCodeLabel.font = [UIFont systemFontOfSize:kConfirmationCodeFontSize weight:UIFontWeightLight]; + _confirmationCodeLabel.textAlignment = NSTextAlignmentCenter; + [_confirmationCodeLabel sizeToFit]; + [dialogHeaderView addSubview:_confirmationCodeLabel]; + [NSLayoutConstraint constraintWithItem:_confirmationCodeLabel attribute:NSLayoutAttributeCenterX relatedBy:NSLayoutRelationEqual toItem:dialogHeaderView attribute:NSLayoutAttributeCenterX multiplier:1.0 constant:0].active = YES; + [NSLayoutConstraint constraintWithItem:_confirmationCodeLabel attribute:NSLayoutAttributeCenterY relatedBy:NSLayoutRelationEqual toItem:dialogHeaderView attribute:NSLayoutAttributeCenterY multiplier:1.0 constant:0].active = YES; + _confirmationCodeLabel.hidden = YES; + + // build the smartlogin instructions + UILabel *smartInstructionLabel = [[UILabel alloc] init]; + smartInstructionLabel.translatesAutoresizingMaskIntoConstraints = NO; + NSString *smartInstructionsStep0 = NSLocalizedStringWithDefaultValue(@"DeviceLogin.SmartLogInStep0", + @"FacebookSDK", + [FBSDKInternalUtility bundleForStrings], + @"Confirm your code on Facebook.", + @"The string for smart login instructions"); + NSString *smartInstructionsStep1 = NSLocalizedStringWithDefaultValue(@"DeviceLogin.SmartLogInStep1", + @"FacebookSDK", + [FBSDKInternalUtility bundleForStrings], + @"\n1. Go to the Menu ", + @"The string for smart login instructions"); + NSString *smartInstructionsStep2 = NSLocalizedStringWithDefaultValue(@"DeviceLogin.SmartLogInStep2", + @"FacebookSDK", + [FBSDKInternalUtility bundleForStrings], + @"\n2. Select Device Requests ", + @"The string for smart login instructions"); + NSString *smartInstructionsStep3 = NSLocalizedStringWithDefaultValue(@"DeviceLogin.SmartLogInStep3", + @"FacebookSDK", + [FBSDKInternalUtility bundleForStrings], + @"\n3. Confirm code.", + @"The string for smart login instructions"); + NSTextAttachment *bookmarkAttachment = [[NSTextAttachment alloc] init]; + bookmarkAttachment.image = bookmarkIcon; + NSAttributedString *attributedBookmarkString = [NSAttributedString attributedStringWithAttachment:bookmarkAttachment]; + + NSTextAttachment *menuAttachment = [[NSTextAttachment alloc] init]; + menuAttachment.image = menuIcon; + NSAttributedString *attributedMenuString = [NSAttributedString attributedStringWithAttachment:menuAttachment]; + + NSMutableParagraphStyle *instructionLabelParagraphStyle = [[NSMutableParagraphStyle alloc] init]; + instructionLabelParagraphStyle.lineHeightMultiple = 1.3; + NSMutableAttributedString *attributedSmartString = [[NSMutableAttributedString alloc] initWithString:smartInstructionsStep0 + attributes:@{ NSParagraphStyleAttributeName : instructionLabelParagraphStyle }]; + [attributedSmartString appendAttributedString:[[NSAttributedString alloc] initWithString:smartInstructionsStep1]]; + [attributedSmartString appendAttributedString:attributedMenuString]; + [attributedSmartString appendAttributedString:[[NSAttributedString alloc] initWithString:smartInstructionsStep2]]; + [attributedSmartString appendAttributedString:attributedBookmarkString]; + [attributedSmartString appendAttributedString:[[NSAttributedString alloc] initWithString:smartInstructionsStep3]]; + + UIFont *instructionFont = [UIFont systemFontOfSize:kInstructionFontSize weight:UIFontWeightLight]; + smartInstructionLabel.font = instructionFont; + smartInstructionLabel.attributedText = attributedSmartString; + smartInstructionLabel.numberOfLines = 0; + smartInstructionLabel.textAlignment = NSTextAlignmentCenter; + [smartInstructionLabel sizeToFit]; + + // resize the icons to fit with the font, and also vertically align them + // so that they start at the cap height. Annoyingly, the menu bookmark has some extra padding + // in the image so we offset that by an additional 2 points. + menuAttachment.bounds = CGRectMake(0, -(instructionFont.ascender - instructionFont.capHeight)+2, kInstructionFontSize, kInstructionFontSize); + bookmarkAttachment.bounds = CGRectMake(0, -(instructionFont.ascender - instructionFont.capHeight), kInstructionFontSize, kInstructionFontSize); + + smartInstructionLabel.textColor = [UIColor colorWithWhite:kFontColorValue alpha:1.0]; + [dialogView addSubview:smartInstructionLabel]; + [smartInstructionLabel.topAnchor constraintEqualToAnchor:dialogHeaderView.bottomAnchor + constant:kVerticalSpaceBetweenHeaderViewAndInstructionLabel].active = YES; + [smartInstructionLabel.leadingAnchor constraintEqualToAnchor:dialogView.leadingAnchor constant:kInstructionTextHorizontalMargin].active = YES; + [dialogView.trailingAnchor constraintEqualToAnchor:smartInstructionLabel.trailingAnchor constant:kInstructionTextHorizontalMargin].active = YES; + + // build 'or' label + UILabel *orInstructionLabel = [[UILabel alloc] init]; + orInstructionLabel.translatesAutoresizingMaskIntoConstraints = NO; + orInstructionLabel.font = [UIFont systemFontOfSize:kInstructionFontSize weight:UIFontWeightBold]; + orInstructionLabel.text = NSLocalizedStringWithDefaultValue(@"DeviceLogin.SmartLogInOrLabel", + @"FacebookSDK", + [FBSDKInternalUtility bundleForStrings], + @"-- OR --", + @"The 'or' string for smart login instructions");; + orInstructionLabel.numberOfLines = 0; + orInstructionLabel.textAlignment = NSTextAlignmentCenter; + [orInstructionLabel sizeToFit]; + orInstructionLabel.textColor = [UIColor colorWithWhite:kFontColorValue alpha:1.0]; + [dialogView addSubview:orInstructionLabel]; + [orInstructionLabel.topAnchor constraintGreaterThanOrEqualToAnchor:smartInstructionLabel.bottomAnchor + constant:kVerticalMarginOrLabel].active = YES; + + [orInstructionLabel.leadingAnchor constraintEqualToAnchor:dialogView.leadingAnchor constant:kInstructionTextHorizontalMargin].active = YES; + [dialogView.trailingAnchor constraintEqualToAnchor:orInstructionLabel.trailingAnchor constant:kInstructionTextHorizontalMargin].active = YES; + + // build the instructions UILabel + UILabel *instructionLabel = [[UILabel alloc] init]; + instructionLabel.translatesAutoresizingMaskIntoConstraints = NO; + NSString *localizedFormatString = NSLocalizedStringWithDefaultValue(@"DeviceLogin.LogInPrompt", + @"FacebookSDK", + [FBSDKInternalUtility bundleForStrings], + @"Visit %@ and enter your code.", + @"The format string for device login instructions"); + + NSString *const deviceLoginURLString = @"facebook.com/device"; + NSString *instructionString = [NSString localizedStringWithFormat:localizedFormatString, deviceLoginURLString]; + NSMutableAttributedString *attributedString = [[NSMutableAttributedString alloc] initWithString:instructionString + attributes:@{ NSParagraphStyleAttributeName : instructionLabelParagraphStyle }]; + NSRange range = [instructionString rangeOfString:deviceLoginURLString]; + [attributedString addAttribute:NSFontAttributeName value:[UIFont systemFontOfSize:kInstructionFontSize weight:UIFontWeightMedium] range:range]; + instructionLabel.font = instructionFont; + instructionLabel.attributedText = attributedString; + instructionLabel.numberOfLines = 0; + instructionLabel.textAlignment = NSTextAlignmentCenter; + [instructionLabel sizeToFit]; + instructionLabel.textColor = [UIColor colorWithWhite:kFontColorValue alpha:1.0]; + [dialogView addSubview:instructionLabel]; + [instructionLabel.topAnchor constraintEqualToAnchor:orInstructionLabel.bottomAnchor + constant:kVerticalMarginOrLabel].active = YES; + [instructionLabel.leadingAnchor constraintEqualToAnchor:dialogView.leadingAnchor constant:kInstructionTextHorizontalMargin].active = YES; + [dialogView.trailingAnchor constraintEqualToAnchor:instructionLabel.trailingAnchor constant:kInstructionTextHorizontalMargin].active = YES; + + // build the container view for the cancel button. + UIView *buttonContainerView = [[UIView alloc] init]; + buttonContainerView.translatesAutoresizingMaskIntoConstraints = NO; + [dialogView addSubview:buttonContainerView]; + [NSLayoutConstraint constraintWithItem:buttonContainerView attribute:NSLayoutAttributeCenterX relatedBy:NSLayoutRelationEqual toItem:dialogView attribute:NSLayoutAttributeCenterX multiplier:1.0 constant:0].active = YES; + [buttonContainerView.heightAnchor constraintEqualToConstant:60].active = YES; + [buttonContainerView.leadingAnchor constraintEqualToAnchor:dialogView.leadingAnchor + constant:400].active = YES; + [dialogView.trailingAnchor constraintEqualToAnchor:buttonContainerView.trailingAnchor + constant:400].active = YES; + [buttonContainerView.topAnchor constraintEqualToAnchor:instructionLabel.bottomAnchor + constant:kVerticalMarginOrLabel].active = YES; + + // build the cancel button. + UIButton *button = [UIButton buttonWithType:UIButtonTypeSystem]; + button.layer.cornerRadius = 4.0; + button.translatesAutoresizingMaskIntoConstraints = NO; + [button setTitle:NSLocalizedStringWithDefaultValue(@"LoginButton.CancelLogout", + @"FacebookSDK", + [FBSDKInternalUtility bundleForStrings], + @"Cancel", + @"The label for the FBSDKLoginButton action sheet to cancel logging out") + forState:UIControlStateNormal]; + button.titleLabel.font = instructionLabel.font; + [buttonContainerView addSubview:button]; + [button.leadingAnchor constraintEqualToAnchor:buttonContainerView.leadingAnchor].active = YES; + [button.trailingAnchor constraintEqualToAnchor:buttonContainerView.trailingAnchor].active = YES; + [button.topAnchor constraintEqualToAnchor:buttonContainerView.topAnchor].active = YES; + [button.bottomAnchor constraintEqualToAnchor:buttonContainerView.bottomAnchor].active = YES; + [button setTitleColor:[UIColor colorWithWhite:kFontColorValue alpha:1] forState:UIControlStateNormal]; + + [button addTarget:self action:@selector(_cancelButtonTap:) forControlEvents:UIControlEventPrimaryActionTriggered]; +} + +- (void)_cancelButtonTap:(id)sender +{ + [self.delegate deviceDialogViewDidCancel:self]; +} + +@end diff --git a/FBSDKCoreKit/FBSDKCoreKit/Internal/FBSDKCoreKit+Internal.h b/FBSDKCoreKit/FBSDKCoreKit/Internal/FBSDKCoreKit+Internal.h index 5990b8a2a7..e8911cd016 100644 --- a/FBSDKCoreKit/FBSDKCoreKit/Internal/FBSDKCoreKit+Internal.h +++ b/FBSDKCoreKit/FBSDKCoreKit/Internal/FBSDKCoreKit+Internal.h @@ -41,6 +41,7 @@ #else #import "Device/FBSDKDeviceButton+Internal.h" #import "Device/FBSDKDeviceDialogView.h" +#import "Device/FBSDKSmartDeviceDialogView.h" #import "Device/FBSDKDeviceViewControllerBase+Internal.h" #import "Device/FBSDKModalFormPresentationController.h" #endif @@ -54,7 +55,9 @@ #import "ErrorRecovery/FBSDKErrorRecoveryAttempter.h" #import "FBSDKDynamicFrameworkLoader.h" #import "FBSDKApplicationDelegate+Internal.h" +#import "FBSDKDeviceRequestsHelper.h" #import "FBSDKError.h" +#import "FBSDKImageDownloader.h" #import "FBSDKInternalUtility.h" #import "FBSDKLogger.h" #import "FBSDKMath.h" diff --git a/FBSDKCoreKit/FBSDKCoreKit/Internal/FBSDKDeviceRequestsHelper.h b/FBSDKCoreKit/FBSDKCoreKit/Internal/FBSDKDeviceRequestsHelper.h new file mode 100644 index 0000000000..dc1ed17d41 --- /dev/null +++ b/FBSDKCoreKit/FBSDKCoreKit/Internal/FBSDKDeviceRequestsHelper.h @@ -0,0 +1,58 @@ +// Copyright (c) 2014-present, Facebook, Inc. All rights reserved. +// +// You are hereby granted a non-exclusive, worldwide, royalty-free license to use, +// copy, modify, and distribute this software in source code or binary form for use +// in connection with the web services and APIs provided by Facebook. +// +// As with any software that integrates with the Facebook platform, your use of +// this software is subject to the Facebook Developer Principles and Policies +// [http://developers.facebook.com/policy/]. This copyright notice shall be +// included in all copies or substantial portions of the software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS +// FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR +// COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER +// IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN +// CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + +#import +#import + +#define FBSDK_DEVICE_INFO_PARAM @"device_info" + +/* + @class + + @abstract Helper class for device requests mDNS broadcasts. Note this is only intended for + internal consumption. + */ +@interface FBSDKDeviceRequestsHelper : NSObject + +/*! + @abstract Get device info to include with the GraphRequest + */ ++ (NSString *)getDeviceInfo; + +/*! + @abstract Start the mDNS advertisement service for a device request + @param loginCode The login code associated with the action for the device request. + @return True if the service broadcast was successfully started. + */ ++ (BOOL)startAdvertisementService:(NSString *)loginCode withDelegate:(id)delegate; + +/*! + @abstract Check if a service delegate is registered with particular advertisement service + @param delegate The delegate to check if registered. + @param service The advertisement service to check for. + @return True if the service is the one the delegate registered with. + */ ++ (BOOL)isDelegate:(id)delegate forAdvertisementService:(NSNetService *)service; + +/*! + @abstract Stop the mDNS advertisement service for a device request + @param delegate The delegate registered with the service. + */ ++ (void)cleanUpAdvertisementService:(id)delegate; + +@end diff --git a/FBSDKCoreKit/FBSDKCoreKit/Internal/FBSDKDeviceRequestsHelper.m b/FBSDKCoreKit/FBSDKCoreKit/Internal/FBSDKDeviceRequestsHelper.m new file mode 100644 index 0000000000..0f83e1de8c --- /dev/null +++ b/FBSDKCoreKit/FBSDKCoreKit/Internal/FBSDKDeviceRequestsHelper.m @@ -0,0 +1,125 @@ +// Copyright (c) 2014-present, Facebook, Inc. All rights reserved. +// +// You are hereby granted a non-exclusive, worldwide, royalty-free license to use, +// copy, modify, and distribute this software in source code or binary form for use +// in connection with the web services and APIs provided by Facebook. +// +// As with any software that integrates with the Facebook platform, your use of +// this software is subject to the Facebook Developer Principles and Policies +// [http://developers.facebook.com/policy/]. This copyright notice shall be +// included in all copies or substantial portions of the software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS +// FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR +// COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER +// IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN +// CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + +#import "FBSDKDeviceRequestsHelper.h" + +#import + +#import + +#import "FBSDKCoreKit+Internal.h" + +#define FBSDK_DEVICE_INFO_DEVICE @"device" +#define FBSDK_DEVICE_INFO_MODEL @"model" +#define FBSDK_HEADER @"fbsdk" +#if !TARGET_OS_TV +#define FBSDK_FLAVOR @"ios" +#else +#define FBSDK_FLAVOR @"tvos" +#endif +#define FBSDK_SERVICE_TYPE @"_fb._tcp." + +static NSMapTable *g_mdnsAdvertisementServices; + +@implementation FBSDKDeviceRequestsHelper + +#pragma mark - Class Methods + ++ (void)initialize { + // We use weak to strong in order to retain the advertisement services + // without having to pass them back to the delegate that started them + // Note that in case the delegate is destroyed before it had a chance to + // stop the service, the service will continue broadcasting until the map + // resizes itself and releases the service, causing it to stop + g_mdnsAdvertisementServices = [NSMapTable weakToStrongObjectsMapTable]; +} + ++ (NSString *)getDeviceInfo +{ + struct utsname systemInfo; + uname(&systemInfo); + NSDictionary *deviceInfo = @{ + FBSDK_DEVICE_INFO_DEVICE: [NSString stringWithCString:systemInfo.machine + encoding:NSUTF8StringEncoding], + FBSDK_DEVICE_INFO_MODEL: [[UIDevice currentDevice] model], + }; + NSError *err; + NSData *jsonDeviceInfo = [NSJSONSerialization dataWithJSONObject:deviceInfo + options:0 + error:&err]; + + return [[NSString alloc] initWithData:jsonDeviceInfo encoding:NSUTF8StringEncoding]; +} + ++ (BOOL)startAdvertisementService:(NSString *)loginCode withDelegate:(id)delegate; +{ + static NSString *sdkVersion = nil; + static dispatch_once_t onceToken; + dispatch_once(&onceToken, ^{ + // Dots in the version will mess up the bonjour DNS record parsing + sdkVersion = [[FBSDKSettings sdkVersion] stringByReplacingOccurrencesOfString:@"." withString:@"|"]; + if (sdkVersion.length > 10 || + ![[NSCharacterSet decimalDigitCharacterSet] characterIsMember:[sdkVersion characterAtIndex:0]]) { + sdkVersion = @"dev"; + } + }); + NSString *serviceName = [NSString stringWithFormat:@"%@_%@_%@", + FBSDK_HEADER, + [NSString stringWithFormat:@"%@-%@", + FBSDK_FLAVOR, + sdkVersion + ], + loginCode + ]; + if (serviceName.length > 60) { + [FBSDKLogger singleShotLogEntry:FBSDKLoggingBehaviorDeveloperErrors logEntry:@"serviceName exceeded 60 characters"]; + } + NSNetService *mdnsAdvertisementService = [[NSNetService alloc] + initWithDomain:@"local." + type:FBSDK_SERVICE_TYPE + name:serviceName + port:0]; + mdnsAdvertisementService.delegate = delegate; + [mdnsAdvertisementService publishWithOptions:NSNetServiceNoAutoRename | NSNetServiceListenForConnections]; + [FBSDKAppEvents logImplicitEvent:FBSDKAppEventNameFBSDKSmartLoginService + valueToSum:nil + parameters:nil + accessToken:nil]; + [g_mdnsAdvertisementServices setObject:mdnsAdvertisementService forKey:delegate]; + + return YES; +} + ++ (BOOL)isDelegate:(id)delegate forAdvertisementService:(NSNetService *)service +{ + NSNetService *mdnsAdvertisementService = [g_mdnsAdvertisementServices objectForKey:delegate]; + return (mdnsAdvertisementService == service); +} + ++ (void)cleanUpAdvertisementService:(id)delegate +{ + NSNetService *mdnsAdvertisementService = [g_mdnsAdvertisementServices objectForKey:delegate]; + if (mdnsAdvertisementService != nil) { + // We are not interested in the stop publish event + mdnsAdvertisementService.delegate = nil; + [mdnsAdvertisementService stop]; + [g_mdnsAdvertisementServices removeObjectForKey:delegate]; + } +} + +@end diff --git a/FBSDKCoreKit/FBSDKCoreKit/Internal/FBSDKImageDownloader.h b/FBSDKCoreKit/FBSDKCoreKit/Internal/FBSDKImageDownloader.h new file mode 100644 index 0000000000..0382360f23 --- /dev/null +++ b/FBSDKCoreKit/FBSDKCoreKit/Internal/FBSDKImageDownloader.h @@ -0,0 +1,40 @@ +// Copyright (c) 2014-present, Facebook, Inc. All rights reserved. +// +// You are hereby granted a non-exclusive, worldwide, royalty-free license to use, +// copy, modify, and distribute this software in source code or binary form for use +// in connection with the web services and APIs provided by Facebook. +// +// As with any software that integrates with the Facebook platform, your use of +// this software is subject to the Facebook Developer Principles and Policies +// [http://developers.facebook.com/policy/]. This copyright notice shall be +// included in all copies or substantial portions of the software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS +// FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR +// COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER +// IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN +// CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + +#import +#import + +/* + @abstract simple class to manage image downloads + @discussion this class is not smart enough to dedupe identical requests in flight. + */ +@interface FBSDKImageDownloader : NSObject + ++ (instancetype)sharedInstance; + +/* + @abstract download an image or retrieve it from cache + @param url the url to download + @param ttl the amount of time (in seconds) that using a cached version is acceptable. + @param completion the callback with the image - for simplicity nil is returned rather than surfacing an error. + */ +- (void)downloadImageWithURL:(NSURL *)url ttl:(NSTimeInterval)ttl completion:(void(^)(UIImage* image))completion; + +- (void)removeAll; + +@end diff --git a/FBSDKCoreKit/FBSDKCoreKit/Internal/FBSDKImageDownloader.m b/FBSDKCoreKit/FBSDKCoreKit/Internal/FBSDKImageDownloader.m new file mode 100644 index 0000000000..4c9f5c9074 --- /dev/null +++ b/FBSDKCoreKit/FBSDKCoreKit/Internal/FBSDKImageDownloader.m @@ -0,0 +1,93 @@ +// Copyright (c) 2014-present, Facebook, Inc. All rights reserved. +// +// You are hereby granted a non-exclusive, worldwide, royalty-free license to use, +// copy, modify, and distribute this software in source code or binary form for use +// in connection with the web services and APIs provided by Facebook. +// +// As with any software that integrates with the Facebook platform, your use of +// this software is subject to the Facebook Developer Principles and Policies +// [http://developers.facebook.com/policy/]. This copyright notice shall be +// included in all copies or substantial portions of the software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS +// FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR +// COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER +// IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN +// CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + +#import "FBSDKImageDownloader.h" + +static NSString *const kImageDirectory = @"fbsdkimages"; +static NSString *const kCachedResponseUserInfoKeyTimestamp = @"timestamp"; + +@implementation FBSDKImageDownloader { + NSURLCache *_urlCache; +} + ++ (instancetype)sharedInstance +{ + static FBSDKImageDownloader *instance; + static dispatch_once_t onceToken; + dispatch_once(&onceToken, ^{ + instance = [[FBSDKImageDownloader alloc] init]; + }); + return instance; +} + +- (instancetype)init +{ + if ((self = [super init])) { + _urlCache = [[NSURLCache alloc] initWithMemoryCapacity:1024*1024*8 + diskCapacity:1024*1024*100 + diskPath:kImageDirectory]; + } + return self; +} + +- (void)removeAll +{ + [_urlCache removeAllCachedResponses]; +} + +- (void)downloadImageWithURL:(NSURL *)url ttl:(NSTimeInterval)ttl completion:(void(^)(UIImage* image))completion +{ + NSURLRequest *request = [NSURLRequest requestWithURL:url]; + NSCachedURLResponse *cachedResponse = [_urlCache cachedResponseForRequest:request]; + NSDate *modificationDate = cachedResponse.userInfo[kCachedResponseUserInfoKeyTimestamp]; + BOOL isExpired = ([[modificationDate dateByAddingTimeInterval:ttl] compare:[NSDate date]] == NSOrderedAscending); + + void (^completionWrapper)(NSCachedURLResponse *) = ^(NSCachedURLResponse *responseData){ + if (completion != NULL) { + UIImage *image = [UIImage imageWithData:responseData.data]; + completion(image); + } + }; + + if (cachedResponse == nil || isExpired) { + NSURLSession *session = [NSURLSession sharedSession]; + NSURLSessionDataTask *task = [session dataTaskWithRequest:request + completionHandler: + ^(NSData *data, NSURLResponse *response, NSError *error) { + if ([response isKindOfClass:[NSHTTPURLResponse class]] && + ((NSHTTPURLResponse *)response).statusCode == 200 && + error == nil && + data != nil) { + NSCachedURLResponse *responseToCache = + [[NSCachedURLResponse alloc] initWithResponse:response + data:data + userInfo:@{ kCachedResponseUserInfoKeyTimestamp : [NSDate date] } + storagePolicy:NSURLCacheStorageAllowed]; + [_urlCache storeCachedResponse:responseToCache forRequest:request]; + completionWrapper(responseToCache); + } else if (completion != NULL) { + completion(nil); + } + }]; + [task resume]; + } else { + completionWrapper(cachedResponse); + } +} + +@end diff --git a/FBSDKCoreKit/FBSDKCoreKit/Internal/ServerConfiguration/FBSDKServerConfiguration.h b/FBSDKCoreKit/FBSDKCoreKit/Internal/ServerConfiguration/FBSDKServerConfiguration.h index 675a4cf477..cec49f35f7 100644 --- a/FBSDKCoreKit/FBSDKCoreKit/Internal/ServerConfiguration/FBSDKServerConfiguration.h +++ b/FBSDKCoreKit/FBSDKCoreKit/Internal/ServerConfiguration/FBSDKServerConfiguration.h @@ -34,6 +34,15 @@ extern NSString *const FBSDKDialogConfigurationNameLike; extern NSString *const FBSDKDialogConfigurationNameMessage; extern NSString *const FBSDKDialogConfigurationNameShare; +extern const NSInteger FBSDKServerConfigurationVersion; + +typedef NS_OPTIONS(NSUInteger, FBSDKServerConfigurationSmartLoginOptions) +{ + FBSDKServerConfigurationSmartLoginOptionsUnknown = 0, + FBSDKServerConfigurationSmartLoginOptionsEnabled = 1 << 0, + FBSDKServerConfigurationSmartLoginOptionsRequireConfirmation = 1 << 1, +}; + @interface FBSDKServerConfiguration : NSObject - (instancetype)initWithAppID:(NSString *)appID @@ -53,6 +62,9 @@ implicitPurchaseLoggingEnabled:(BOOL)implicitPurchaseLoggingEnabled sessionTimeoutInterval:(NSTimeInterval) sessionTimeoutInterval defaults:(BOOL)defaults loggingToken:(NSString *)loggingToken + smartLoginOptions:(FBSDKServerConfigurationSmartLoginOptions)smartLoginOptions + smartLoginBookmarkIconURL:(NSURL *)smartLoginBookmarkIconURL + smartLoginMenuIconURL:(NSURL *)smartLoginMenuIconURL NS_DESIGNATED_INITIALIZER; @property (nonatomic, assign, readonly, getter=isAdvertisingIDEnabled) BOOL advertisingIDEnabled; @@ -70,6 +82,10 @@ NS_DESIGNATED_INITIALIZER; @property (nonatomic, copy, readonly) NSDate *timestamp; @property (nonatomic, assign) NSTimeInterval sessionTimoutInterval; @property (nonatomic, copy, readonly) NSString *loggingToken; +@property (nonatomic, assign, readonly) FBSDKServerConfigurationSmartLoginOptions smartLoginOptions; +@property (nonatomic, copy, readonly) NSURL *smartLoginBookmarkIconURL; +@property (nonatomic, copy, readonly) NSURL *smartLoginMenuIconURL; +@property (nonatomic, readonly) NSInteger version; - (FBSDKDialogConfiguration *)dialogConfigurationForDialogName:(NSString *)dialogName; - (BOOL)useNativeDialogForDialogName:(NSString *)dialogName; diff --git a/FBSDKCoreKit/FBSDKCoreKit/Internal/ServerConfiguration/FBSDKServerConfiguration.m b/FBSDKCoreKit/FBSDKCoreKit/Internal/ServerConfiguration/FBSDKServerConfiguration.m index d033ab4283..54a85301af 100644 --- a/FBSDKCoreKit/FBSDKCoreKit/Internal/ServerConfiguration/FBSDKServerConfiguration.m +++ b/FBSDKCoreKit/FBSDKCoreKit/Internal/ServerConfiguration/FBSDKServerConfiguration.m @@ -38,6 +38,10 @@ #define FBSDK_SERVER_CONFIGURATION_TIMESTAMP_KEY @"timestamp" #define FBSDK_SERVER_CONFIGURATION_SESSION_TIMEOUT_INTERVAL @"sessionTimeoutInterval" #define FBSDK_SERVER_CONFIGURATION_LOGGING_TOKEN @"loggingToken" +#define FBSDK_SERVER_CONFIGURATION_SMART_LOGIN_OPTIONS_KEY @"smartLoginEnabled" +#define FBSDK_SERVER_CONFIGURATION_SMART_LOGIN_BOOKMARK_ICON_URL_KEY @"smarstLoginBookmarkIconURL" +#define FBSDK_SERVER_CONFIGURATION_SMART_LOGIN_MENU_ICON_URL_KEY @"smarstLoginBookmarkMenuURL" +#define FBSDK_SERVER_CONFIGURATION_VERSION_KEY @"version" #pragma mark - Dialog Names @@ -57,10 +61,15 @@ NSString *const FBSDKDialogConfigurationFeatureUseNativeFlow = @"use_native_flow"; NSString *const FBSDKDialogConfigurationFeatureUseSafariViewController = @"use_safari_vc"; +// Increase this value when adding new fields and previous cached configurations should be +// treated as stale. +const NSInteger FBSDKServerConfigurationVersion = 2; + @implementation FBSDKServerConfiguration { NSDictionary *_dialogConfigurations; NSDictionary *_dialogFlows; + NSInteger _version; } #pragma mark - Object Lifecycle @@ -87,6 +96,9 @@ - (instancetype)initWithAppID:(NSString *)appID sessionTimeoutInterval:(NSTimeInterval) sessionTimeoutInterval defaults:(BOOL)defaults loggingToken:(NSString *)loggingToken + smartLoginOptions:(FBSDKServerConfigurationSmartLoginOptions)smartLoginOptions + smartLoginBookmarkIconURL:(NSURL *)smartLoginBookmarkIconURL + smartLoginMenuIconURL:(NSURL *)smartLoginMenuIconURL { if ((self = [super init])) { _appID = [appID copy]; @@ -106,6 +118,10 @@ - (instancetype)initWithAppID:(NSString *)appID _sessionTimoutInterval = sessionTimeoutInterval; _defaults = defaults; _loggingToken = loggingToken; + _smartLoginOptions = smartLoginOptions; + _smartLoginMenuIconURL = [smartLoginMenuIconURL copy]; + _smartLoginBookmarkIconURL = [smartLoginBookmarkIconURL copy]; + _version = FBSDKServerConfigurationVersion; } return self; } @@ -163,6 +179,7 @@ - (id)initWithCoder:(NSCoder *)decoder [decoder decodeBoolForKey:FBSDK_SERVER_CONFIGURATION_IMPLICIT_PURCHASE_LOGGING_ENABLED_KEY]; BOOL systemAuthenticationEnabled = [decoder decodeBoolForKey:FBSDK_SERVER_CONFIGURATION_SYSTEM_AUTHENTICATION_ENABLED_KEY]; + FBSDKServerConfigurationSmartLoginOptions smartLoginOptions = [decoder decodeIntegerForKey:FBSDK_SERVER_CONFIGURATION_SMART_LOGIN_OPTIONS_KEY]; BOOL nativeAuthFlowEnabled = [decoder decodeBoolForKey:FBSDK_SERVER_CONFIGURATION_NATIVE_AUTH_FLOW_ENABLED_KEY]; NSDate *timestamp = [decoder decodeObjectOfClass:[NSDate class] forKey:FBSDK_SERVER_CONFIGURATION_TIMESTAMP_KEY]; NSSet *dialogConfigurationsClasses = [[NSSet alloc] initWithObjects: @@ -181,23 +198,32 @@ - (id)initWithCoder:(NSCoder *)decoder FBSDKErrorConfiguration *errorConfiguration = [decoder decodeObjectOfClass:[FBSDKErrorConfiguration class] forKey:FBSDK_SERVER_CONFIGURATION_ERROR_CONFIGS_KEY]; NSTimeInterval sessionTimeoutInterval = [decoder decodeDoubleForKey:FBSDK_SERVER_CONFIGURATION_SESSION_TIMEOUT_INTERVAL]; NSString *loggingToken = [decoder decodeObjectOfClass:[NSString class] forKey:FBSDK_SERVER_CONFIGURATION_LOGGING_TOKEN]; - return [self initWithAppID:appID - appName:appName - loginTooltipEnabled:loginTooltipEnabled - loginTooltipText:loginTooltipText - defaultShareMode:defaultShareMode - advertisingIDEnabled:advertisingIDEnabled - implicitLoggingEnabled:implicitLoggingEnabled -implicitPurchaseLoggingEnabled:implicitPurchaseLoggingEnabled - systemAuthenticationEnabled:systemAuthenticationEnabled - nativeAuthFlowEnabled:nativeAuthFlowEnabled - dialogConfigurations:dialogConfigurations - dialogFlows:dialogFlows - timestamp:timestamp - errorConfiguration:errorConfiguration - sessionTimeoutInterval:sessionTimeoutInterval - defaults:NO - loggingToken:loggingToken]; + NSURL *smartLoginBookmarkIconURL = [decoder decodeObjectOfClass:[NSURL class] forKey:FBSDK_SERVER_CONFIGURATION_SMART_LOGIN_BOOKMARK_ICON_URL_KEY]; + NSURL *smartLoginMenuIconURL = [decoder decodeObjectOfClass:[NSURL class] forKey:FBSDK_SERVER_CONFIGURATION_SMART_LOGIN_MENU_ICON_URL_KEY]; + NSInteger version = [decoder decodeIntegerForKey:FBSDK_SERVER_CONFIGURATION_VERSION_KEY]; + FBSDKServerConfiguration *configuration = [self initWithAppID:appID + appName:appName + loginTooltipEnabled:loginTooltipEnabled + loginTooltipText:loginTooltipText + defaultShareMode:defaultShareMode + advertisingIDEnabled:advertisingIDEnabled + implicitLoggingEnabled:implicitLoggingEnabled + implicitPurchaseLoggingEnabled:implicitPurchaseLoggingEnabled + systemAuthenticationEnabled:systemAuthenticationEnabled + nativeAuthFlowEnabled:nativeAuthFlowEnabled + dialogConfigurations:dialogConfigurations + dialogFlows:dialogFlows + timestamp:timestamp + errorConfiguration:errorConfiguration + sessionTimeoutInterval:sessionTimeoutInterval + defaults:NO + loggingToken:loggingToken + smartLoginOptions:smartLoginOptions + smartLoginBookmarkIconURL:smartLoginBookmarkIconURL + smartLoginMenuIconURL:smartLoginMenuIconURL + ]; + configuration->_version = version; + return configuration; } - (void)encodeWithCoder:(NSCoder *)encoder @@ -219,6 +245,10 @@ - (void)encodeWithCoder:(NSCoder *)encoder [encoder encodeObject:_timestamp forKey:FBSDK_SERVER_CONFIGURATION_TIMESTAMP_KEY]; [encoder encodeDouble:_sessionTimoutInterval forKey:FBSDK_SERVER_CONFIGURATION_SESSION_TIMEOUT_INTERVAL]; [encoder encodeObject:_loggingToken forKey:FBSDK_SERVER_CONFIGURATION_LOGGING_TOKEN]; + [encoder encodeInteger:_smartLoginOptions forKey:FBSDK_SERVER_CONFIGURATION_SMART_LOGIN_OPTIONS_KEY]; + [encoder encodeObject:_smartLoginBookmarkIconURL forKey:FBSDK_SERVER_CONFIGURATION_SMART_LOGIN_BOOKMARK_ICON_URL_KEY]; + [encoder encodeObject:_smartLoginMenuIconURL forKey:FBSDK_SERVER_CONFIGURATION_SMART_LOGIN_MENU_ICON_URL_KEY]; + [encoder encodeInteger:_version forKey:FBSDK_SERVER_CONFIGURATION_VERSION_KEY]; } #pragma mark - NSCopying diff --git a/FBSDKCoreKit/FBSDKCoreKit/Internal/ServerConfiguration/FBSDKServerConfigurationManager.m b/FBSDKCoreKit/FBSDKCoreKit/Internal/ServerConfiguration/FBSDKServerConfigurationManager.m index d7d49c9b27..3d1c048cde 100644 --- a/FBSDKCoreKit/FBSDKCoreKit/Internal/ServerConfiguration/FBSDKServerConfigurationManager.m +++ b/FBSDKCoreKit/FBSDKCoreKit/Internal/ServerConfiguration/FBSDKServerConfigurationManager.m @@ -20,6 +20,7 @@ #import "FBSDKGraphRequest+Internal.h" #import "FBSDKGraphRequest.h" +#import "FBSDKImageDownloader.h" #import "FBSDKInternalUtility.h" #import "FBSDKLogger.h" #import "FBSDKServerConfiguration+Internal.h" @@ -46,6 +47,9 @@ #define FBSDK_SERVER_CONFIGURATION_SYSTEM_AUTHENTICATION_ENABLED_FIELD @"ios_supports_system_auth" #define FBSDK_SERVER_CONFIGURATION_SESSION_TIMEOUT_FIELD @"app_events_session_timeout" #define FBSDK_SERVER_CONFIGURATION_LOGGIN_TOKEN_FIELD @"logging_token" +#define FBSDK_SERVER_CONFIGURATION_SMART_LOGIN_OPTIONS_FIELD @"seamless_login" +#define FBSDK_SERVER_CONFIGURATION_SMART_LOGIN_BOOKMARK_ICON_URL_FIELD @"smart_login_bookmark_icon_url" +#define FBSDK_SERVER_CONFIGURATION_SMART_LOGIN_MENU_ICON_URL_FIELD @"smart_login_menu_icon_url" @implementation FBSDKServerConfigurationManager @@ -125,7 +129,7 @@ + (void)loadServerConfigurationWithCompletionBlock:(FBSDKServerConfigurationMana } } - if ((_serverConfiguration && [self _serverConfigurationTimestampIsValid:_serverConfiguration.timestamp]) || + if ((_serverConfiguration && [self _serverConfigurationTimestampIsValid:_serverConfiguration.timestamp] && _serverConfiguration.version > FBSDKServerConfigurationVersion) || (_serverConfigurationErrorTimestamp && [self _serverConfigurationTimestampIsValid:_serverConfigurationErrorTimestamp])) { // we have a valid server configuration, use that loadBlock = [self _wrapperBlockForLoadBlock:completionBlock]; @@ -183,6 +187,9 @@ + (void)processLoadRequestResponse:(id)result error:(NSError *)error appID:(NSSt [errorConfiguration parseArray:resultDictionary[FBSDK_SERVER_CONFIGURATION_ERROR_CONFIGURATION_FIELD]]; NSTimeInterval sessionTimeoutInterval = [FBSDKTypeUtility timeIntervalValue:resultDictionary[FBSDK_SERVER_CONFIGURATION_SESSION_TIMEOUT_FIELD]] ?: DEFAULT_SESSION_TIMEOUT_INTERVAL; NSString *loggingToken = [FBSDKTypeUtility stringValue:resultDictionary[FBSDK_SERVER_CONFIGURATION_LOGGIN_TOKEN_FIELD]]; + FBSDKServerConfigurationSmartLoginOptions smartLoginOptions = [FBSDKTypeUtility integerValue:resultDictionary[FBSDK_SERVER_CONFIGURATION_SMART_LOGIN_OPTIONS_FIELD]]; + NSURL *smartLoginBookmarkIconURL = [FBSDKTypeUtility URLValue:resultDictionary[FBSDK_SERVER_CONFIGURATION_SMART_LOGIN_BOOKMARK_ICON_URL_FIELD]]; + NSURL *smartLoginMenuIconURL = [FBSDKTypeUtility URLValue:resultDictionary[FBSDK_SERVER_CONFIGURATION_SMART_LOGIN_MENU_ICON_URL_FIELD]]; FBSDKServerConfiguration *serverConfiguration = [[FBSDKServerConfiguration alloc] initWithAppID:appID appName:appName loginTooltipEnabled:loginTooltipEnabled @@ -199,7 +206,28 @@ + (void)processLoadRequestResponse:(id)result error:(NSError *)error appID:(NSSt errorConfiguration:errorConfiguration sessionTimeoutInterval:sessionTimeoutInterval defaults:NO - loggingToken:loggingToken]; + loggingToken:loggingToken + smartLoginOptions:smartLoginOptions + smartLoginBookmarkIconURL:smartLoginBookmarkIconURL + smartLoginMenuIconURL:smartLoginMenuIconURL + ]; +#if TARGET_OS_TV + // don't download icons more than once a day. + static const NSTimeInterval kSmartLoginIconsTTL = 60 * 60 * 24; + + BOOL smartLoginEnabled = (smartLoginOptions & FBSDKServerConfigurationSmartLoginOptionsEnabled); + // for TVs go ahead and prime the images + if (smartLoginEnabled && + smartLoginMenuIconURL && + smartLoginBookmarkIconURL) { + [[FBSDKImageDownloader sharedInstance] downloadImageWithURL:serverConfiguration.smartLoginBookmarkIconURL + ttl:kSmartLoginIconsTTL + completion:NULL]; + [[FBSDKImageDownloader sharedInstance] downloadImageWithURL:serverConfiguration.smartLoginMenuIconURL + ttl:kSmartLoginIconsTTL + completion:NULL]; + } +#endif [self _didProcessConfigurationFromNetwork:serverConfiguration appID:appID error:nil]; } @@ -223,7 +251,12 @@ + (FBSDKGraphRequest *)requestToLoadServerConfiguration:(NSString *)appID FBSDK_SERVER_CONFIGURATION_NATIVE_PROXY_AUTH_FLOW_ENABLED_FIELD, FBSDK_SERVER_CONFIGURATION_SYSTEM_AUTHENTICATION_ENABLED_FIELD, FBSDK_SERVER_CONFIGURATION_SESSION_TIMEOUT_FIELD, - FBSDK_SERVER_CONFIGURATION_LOGGIN_TOKEN_FIELD, + FBSDK_SERVER_CONFIGURATION_LOGGIN_TOKEN_FIELD +#if TARGET_OS_TV + ,FBSDK_SERVER_CONFIGURATION_SMART_LOGIN_OPTIONS_FIELD, + FBSDK_SERVER_CONFIGURATION_SMART_LOGIN_BOOKMARK_ICON_URL_FIELD, + FBSDK_SERVER_CONFIGURATION_SMART_LOGIN_MENU_ICON_URL_FIELD +#endif ]; NSDictionary *parameters = @{ @"fields": [fields componentsJoinedByString:@","] }; FBSDKGraphRequest *request = [[FBSDKGraphRequest alloc] initWithGraphPath:appID @@ -273,7 +306,11 @@ + (FBSDKServerConfiguration *)_defaultServerConfigurationForAppID:(NSString *)ap errorConfiguration:nil sessionTimeoutInterval:DEFAULT_SESSION_TIMEOUT_INTERVAL defaults:YES - loggingToken:nil]; + loggingToken:nil + smartLoginOptions:FBSDKServerConfigurationSmartLoginOptionsUnknown + smartLoginBookmarkIconURL:nil + smartLoginMenuIconURL:nil + ]; } return _defaultServerConfiguration; } diff --git a/FBSDKIntegrationTests/FBSDKIntegrationTests.xcodeproj/project.pbxproj b/FBSDKIntegrationTests/FBSDKIntegrationTests.xcodeproj/project.pbxproj index 30bb2fa02b..5e4d40c959 100644 --- a/FBSDKIntegrationTests/FBSDKIntegrationTests.xcodeproj/project.pbxproj +++ b/FBSDKIntegrationTests/FBSDKIntegrationTests.xcodeproj/project.pbxproj @@ -28,6 +28,7 @@ 9D1999E61A7AF7C900CAD112 /* Foundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 9D1999E51A7AF7C900CAD112 /* Foundation.framework */; }; 9D1999E81A7AF7E900CAD112 /* XCTest.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 9D1999E71A7AF7E900CAD112 /* XCTest.framework */; }; 9D1999EA1A7AF7F100CAD112 /* UIKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 9D1999E91A7AF7F100CAD112 /* UIKit.framework */; }; + 9D28F1B01DB162FC0057D709 /* FBSDKImageDownloaderTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 9D28F1AF1DB162FC0057D709 /* FBSDKImageDownloaderTests.m */; }; 9D2BA09C1AB9E110004C7C0C /* main.m in Sources */ = {isa = PBXBuildFile; fileRef = 9D2BA09B1AB9E110004C7C0C /* main.m */; }; 9D2BA09F1AB9E110004C7C0C /* AppDelegate.m in Sources */ = {isa = PBXBuildFile; fileRef = 9D2BA09E1AB9E110004C7C0C /* AppDelegate.m */; }; 9D2BA0A21AB9E110004C7C0C /* ViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 9D2BA0A11AB9E110004C7C0C /* ViewController.m */; }; @@ -119,6 +120,41 @@ remoteGlobalIDString = 9D9DB8D81A114E500086167B; remoteInfo = FBSDKLoginKit; }; + 9D28F1B71DB162FC0057D709 /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = 8118B69F1D0A6FD600962084 /* FBSDKCoreKit.xcodeproj */; + proxyType = 2; + remoteGlobalIDString = 81B71DA31D19C87400933E93; + remoteInfo = "FBSDKCoreKit-Dynamic"; + }; + 9D28F1B91DB162FC0057D709 /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = 8118B69F1D0A6FD600962084 /* FBSDKCoreKit.xcodeproj */; + proxyType = 2; + remoteGlobalIDString = 814AC8571D1B528900D61E6C; + remoteInfo = "FBSDKCoreKit_TV-Dynamic"; + }; + 9D28F1BE1DB162FC0057D709 /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = 8118B6BB1D0A6FE300962084 /* FBSDKLoginKit.xcodeproj */; + proxyType = 2; + remoteGlobalIDString = 818EB4411D1A283100252851; + remoteInfo = "FBSDKLoginKit-Dynamic"; + }; + 9D28F1C61DB162FC0057D709 /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = 8118B6AD1D0A6FDB00962084 /* FBSDKShareKit.xcodeproj */; + proxyType = 2; + remoteGlobalIDString = 81A07F441D1A2E6A0041A29C; + remoteInfo = "FBSDKShareKit-Dynamic"; + }; + 9D28F1C81DB162FC0057D709 /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = 8118B6AD1D0A6FDB00962084 /* FBSDKShareKit.xcodeproj */; + proxyType = 2; + remoteGlobalIDString = 8144D91B1D261D2900C8E4AC; + remoteInfo = "FBSDKShareKit_TV-Dynamic"; + }; 9D2BA0BD1AB9E11F004C7C0C /* PBXContainerItemProxy */ = { isa = PBXContainerItemProxy; containerPortal = 9D1999A91A7AF41B00CAD112 /* Project object */; @@ -163,6 +199,7 @@ 9D1999E51A7AF7C900CAD112 /* Foundation.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Foundation.framework; path = System/Library/Frameworks/Foundation.framework; sourceTree = SDKROOT; }; 9D1999E71A7AF7E900CAD112 /* XCTest.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = XCTest.framework; path = Platforms/iPhoneSimulator.platform/Developer/Library/Frameworks/XCTest.framework; sourceTree = DEVELOPER_DIR; }; 9D1999E91A7AF7F100CAD112 /* UIKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = UIKit.framework; path = System/Library/Frameworks/UIKit.framework; sourceTree = SDKROOT; }; + 9D28F1AF1DB162FC0057D709 /* FBSDKImageDownloaderTests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = FBSDKImageDownloaderTests.m; sourceTree = ""; }; 9D2BA0971AB9E110004C7C0C /* FBSDKTestHost.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = FBSDKTestHost.app; sourceTree = BUILT_PRODUCTS_DIR; }; 9D2BA09A1AB9E110004C7C0C /* FBSDKTestHost-Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = "FBSDKTestHost-Info.plist"; sourceTree = ""; }; 9D2BA09B1AB9E110004C7C0C /* main.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = main.m; sourceTree = ""; }; @@ -216,8 +253,10 @@ isa = PBXGroup; children = ( 8118B6A81D0A6FD600962084 /* FBSDKCoreKit.framework */, + 9D28F1B81DB162FC0057D709 /* FBSDKCoreKit.framework */, 8118B6AA1D0A6FD600962084 /* FBSDKCoreKitTests.xctest */, 8118B6AC1D0A6FD600962084 /* FBSDKCoreKit.framework */, + 9D28F1BA1DB162FC0057D709 /* FBSDKCoreKit.framework */, ); name = Products; sourceTree = ""; @@ -226,8 +265,10 @@ isa = PBXGroup; children = ( 8118B6B61D0A6FDB00962084 /* FBSDKShareKit.framework */, + 9D28F1C71DB162FC0057D709 /* FBSDKShareKit.framework */, 8118B6B81D0A6FDB00962084 /* FBSDKShareKitTests.xctest */, 8118B6BA1D0A6FDB00962084 /* FBSDKShareKit.framework */, + 9D28F1C91DB162FC0057D709 /* FBSDKShareKit.framework */, ); name = Products; sourceTree = ""; @@ -236,6 +277,7 @@ isa = PBXGroup; children = ( 8118B6C21D0A6FE400962084 /* FBSDKLoginKit.framework */, + 9D28F1BF1DB162FC0057D709 /* FBSDKLoginKit.framework */, 8118B6C41D0A6FE400962084 /* FBSDKLoginKitTests.xctest */, ); name = Products; @@ -271,6 +313,7 @@ 9D5F8ADB1A97C15400EC4FD3 /* FBSDKAppEventsStateManagerIntegrationTests.m */, 9D1999D61A7AF67B00CAD112 /* FBSDKCoreKit+Internal.h */, 9D1999DC1A7AF76200CAD112 /* FBSDKGraphRequestConnectionIntegrationTests.m */, + 9D28F1AF1DB162FC0057D709 /* FBSDKImageDownloaderTests.m */, 9D1999C51A7AF5C900CAD112 /* FBSDKIntegrationTestCase.h */, 9D1999C61A7AF5C900CAD112 /* FBSDKIntegrationTestCase.m */, 9D1999DB1A7AF76200CAD112 /* FBSDKKeychainIntegrationTests.m */, @@ -505,6 +548,41 @@ remoteRef = 8118B6C31D0A6FE400962084 /* PBXContainerItemProxy */; sourceTree = BUILT_PRODUCTS_DIR; }; + 9D28F1B81DB162FC0057D709 /* FBSDKCoreKit.framework */ = { + isa = PBXReferenceProxy; + fileType = wrapper.framework; + path = FBSDKCoreKit.framework; + remoteRef = 9D28F1B71DB162FC0057D709 /* PBXContainerItemProxy */; + sourceTree = BUILT_PRODUCTS_DIR; + }; + 9D28F1BA1DB162FC0057D709 /* FBSDKCoreKit.framework */ = { + isa = PBXReferenceProxy; + fileType = wrapper.framework; + path = FBSDKCoreKit.framework; + remoteRef = 9D28F1B91DB162FC0057D709 /* PBXContainerItemProxy */; + sourceTree = BUILT_PRODUCTS_DIR; + }; + 9D28F1BF1DB162FC0057D709 /* FBSDKLoginKit.framework */ = { + isa = PBXReferenceProxy; + fileType = wrapper.framework; + path = FBSDKLoginKit.framework; + remoteRef = 9D28F1BE1DB162FC0057D709 /* PBXContainerItemProxy */; + sourceTree = BUILT_PRODUCTS_DIR; + }; + 9D28F1C71DB162FC0057D709 /* FBSDKShareKit.framework */ = { + isa = PBXReferenceProxy; + fileType = wrapper.framework; + path = FBSDKShareKit.framework; + remoteRef = 9D28F1C61DB162FC0057D709 /* PBXContainerItemProxy */; + sourceTree = BUILT_PRODUCTS_DIR; + }; + 9D28F1C91DB162FC0057D709 /* FBSDKShareKit.framework */ = { + isa = PBXReferenceProxy; + fileType = wrapper.framework; + path = FBSDKShareKit.framework; + remoteRef = 9D28F1C81DB162FC0057D709 /* PBXContainerItemProxy */; + sourceTree = BUILT_PRODUCTS_DIR; + }; /* End PBXReferenceProxy section */ /* Begin PBXResourcesBuildPhase section */ @@ -544,6 +622,7 @@ 9D1999C71A7AF5C900CAD112 /* FBSDKIntegrationTestCase.m in Sources */, 9D9ACC3B1ABCAF950050BF2E /* FBSDKShareAPIIntegrationTests.m in Sources */, 9D5F8ADC1A97C15400EC4FD3 /* FBSDKAppEventsStateManagerIntegrationTests.m in Sources */, + 9D28F1B01DB162FC0057D709 /* FBSDKImageDownloaderTests.m in Sources */, 9D1999DE1A7AF76200CAD112 /* FBSDKKeychainIntegrationTests.m in Sources */, 9D1999E01A7AF76200CAD112 /* FBSDKServerConfigurationManagerIntegrationTests.m in Sources */, ); diff --git a/FBSDKIntegrationTests/FBSDKIntegrationTests/FBSDKAppEventsIntegrationTests.m b/FBSDKIntegrationTests/FBSDKIntegrationTests/FBSDKAppEventsIntegrationTests.m index 588d26888a..a40d6ef904 100644 --- a/FBSDKIntegrationTests/FBSDKIntegrationTests/FBSDKAppEventsIntegrationTests.m +++ b/FBSDKIntegrationTests/FBSDKIntegrationTests/FBSDKAppEventsIntegrationTests.m @@ -578,4 +578,130 @@ + (void)appEventSessionIDsForRequestBody:(NSString *)body activateSessions:(NSSe } } +- (void)testUserID { + // default to disabling timer based flushes so that long tests + // don't get more flushes than explicitly expecting. + [FBSDKAppEvents singleton].disableTimer = YES; + NSString *appID = self.testAppID; + FBSDKTestBlocker *blocker = [[FBSDKTestBlocker alloc] initWithExpectedSignalCount:1]; + __block int activiesEndpointCalledForUserCount = 0; + __block int activiesEndpointCalledWithoutUserCount = 0; + NSString *expectedUserID = @"bobbytables"; + NSString *expectedEventString = [NSString stringWithFormat:@"_app_user_id\":\"%@", expectedUserID]; + + [OHHTTPStubs stubRequestsPassingTest:^BOOL(NSURLRequest *request) { + NSString *const activitiesPath = [NSString stringWithFormat:@"%@/activities", appID]; + if ([request.URL.path hasSuffix:activitiesPath]) { + NSString *body = [[NSString alloc] initWithData:request.OHHTTPStubs_HTTPBody encoding:NSUTF8StringEncoding]; + if ([body rangeOfString:expectedEventString].location != NSNotFound) { + activiesEndpointCalledForUserCount++; + } else { + activiesEndpointCalledWithoutUserCount++; + } + + if (activiesEndpointCalledForUserCount + activiesEndpointCalledWithoutUserCount == 4){ + [blocker signal]; + } + } + // always return NO because we don't actually want to stub a http response, only + // to intercept and verify request to fufill the expectation. + return NO; + } withStubResponse:^OHHTTPStubsResponse *(NSURLRequest *request) { + return [OHHTTPStubsResponse responseWithData:[NSData data] + statusCode:200 + headers:nil]; + }]; + + [FBSDKAppEvents setFlushBehavior:FBSDKAppEventsFlushBehaviorExplicitOnly]; + + // perform 4 different flushes, making sure there's no userid + // then two flushes with user id, then verify again it is cleared. + [FBSDKAppEvents setUserID:nil]; + [FBSDKAppEvents logEvent:@"testUserID"]; + [FBSDKAppEvents flush]; + + [FBSDKAppEvents setUserID:expectedUserID]; + [FBSDKAppEvents logEvent:@"testUserID"]; + [FBSDKAppEvents flush]; + + XCTAssertEqualObjects([FBSDKAppEvents userID], expectedUserID); + [FBSDKAppEvents logEvent:@"testUserID"]; + [FBSDKAppEvents flush]; + + [FBSDKAppEvents setUserID:nil]; + [FBSDKAppEvents logEvent:@"testUserID"]; + [FBSDKAppEvents flush]; + + XCTAssertTrue([blocker waitWithTimeout:16], @"did not get 4 flushes"); + XCTAssertEqual(2,activiesEndpointCalledForUserCount, @"more than 2 log request made with userid"); + XCTAssertEqual(2,activiesEndpointCalledWithoutUserCount, @"more than 2 log request made without userid"); + + // reset flush behavior. + [FBSDKAppEvents setFlushBehavior:FBSDKAppEventsFlushBehaviorAuto]; +} + + +- (void)testUserProperties { + NSString *appID = self.testAppID; + FBSDKTestBlocker *blocker = [[FBSDKTestBlocker alloc] initWithExpectedSignalCount:1]; + __block int endpointCalledCount = 0; + + [OHHTTPStubs stubRequestsPassingTest:^BOOL(NSURLRequest *request) { + NSString *const activitiesPath = [NSString stringWithFormat:@"%@/user_properties", appID]; + if ([request.URL.path hasSuffix:activitiesPath]) { + NSString *body = [[NSString alloc] initWithData:request.OHHTTPStubs_HTTPBody encoding:NSUTF8StringEncoding]; + XCTAssertTrue([body rangeOfString:@"advertiser_id"].location != NSNotFound); + XCTAssertTrue([body rangeOfString:@"custom_data"].location != NSNotFound); + XCTAssertTrue([body rangeOfString:@"user_unique_id"].location != NSNotFound); + XCTAssertTrue([body rangeOfString:@"favorite_color"].location != NSNotFound); + endpointCalledCount++; + } + // always return NO because we don't actually want to stub a http response, only + // to intercept and verify request to fufill the expectation. + return NO; + } withStubResponse:^OHHTTPStubsResponse *(NSURLRequest *request) { + return [OHHTTPStubsResponse responseWithData:[NSData data] + statusCode:200 + headers:nil]; + }]; + + [FBSDKAppEvents setUserID:@"lilbobbytables"]; + [FBSDKAppEvents updateUserProperties:@{ + @"favorite_color" : @"blue", + @"created" : [NSDate date].description, + @"email" : @"someemail@email.com", + @"some_id" : @"Custom:1", + @"validated" : @YES, + } + handler:^(FBSDKGraphRequestConnection *connection, id result, NSError *error) { + XCTAssertNil(error); + [blocker signal]; + }]; + XCTAssertTrue([blocker waitWithTimeout:5], @"did not get callback"); + XCTAssertEqual(1, endpointCalledCount); + + //now make sure there is an error for invalid values like nsdate + blocker = [[FBSDKTestBlocker alloc] initWithExpectedSignalCount:1]; + [FBSDKAppEvents updateUserProperties:@{ + @"created" : [NSDate date] + } + handler:^(FBSDKGraphRequestConnection *connection, id result, NSError *error) { + XCTAssertNotNil(error); + [blocker signal]; + }]; + XCTAssertTrue([blocker waitWithTimeout:5], @"did not get callback for nsdate error"); + + //now make sure there is an error + blocker = [[FBSDKTestBlocker alloc] initWithExpectedSignalCount:1]; + [FBSDKAppEvents setUserID:nil]; + [FBSDKAppEvents updateUserProperties:@{ + @"favorite_color" : @"blue" + } + handler:^(FBSDKGraphRequestConnection *connection, id result, NSError *error) { + XCTAssertNotNil(error); + [blocker signal]; + }]; + XCTAssertTrue([blocker waitWithTimeout:5], @"did not get callback"); +} + @end diff --git a/FBSDKIntegrationTests/FBSDKIntegrationTests/FBSDKImageDownloaderTests.m b/FBSDKIntegrationTests/FBSDKIntegrationTests/FBSDKImageDownloaderTests.m new file mode 100644 index 0000000000..f266aa88f5 --- /dev/null +++ b/FBSDKIntegrationTests/FBSDKIntegrationTests/FBSDKImageDownloaderTests.m @@ -0,0 +1,134 @@ +// Copyright (c) 2014-present, Facebook, Inc. All rights reserved. +// +// You are hereby granted a non-exclusive, worldwide, royalty-free license to use, +// copy, modify, and distribute this software in source code or binary form for use +// in connection with the web services and APIs provided by Facebook. +// +// As with any software that integrates with the Facebook platform, your use of +// this software is subject to the Facebook Developer Principles and Policies +// [http://developers.facebook.com/policy/]. This copyright notice shall be +// included in all copies or substantial portions of the software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS +// FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR +// COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER +// IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN +// CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +#import +#import + +#import +#import + +#import "FBSDKCoreKit+Internal.h" +#import "FBSDKIntegrationTestCase.h" +#import "FBSDKTestBlocker.h" + +@interface FBSDKImageDownloaderTests : FBSDKIntegrationTestCase + +@end + +@implementation FBSDKImageDownloaderTests + +- (void)testImageCache +{ + FBSDKTestBlocker *blocker = [[FBSDKTestBlocker alloc] initWithExpectedSignalCount:1]; + + __block NSUInteger numRequests = 0; + + UIGraphicsBeginImageContextWithOptions(CGSizeMake(36, 36), NO, 0.0); + UIImage *blank = UIGraphicsGetImageFromCurrentImageContext(); + UIGraphicsEndImageContext(); + + [OHHTTPStubs stubRequestsPassingTest:^BOOL(NSURLRequest *request) { + if ([request.URL.path rangeOfString:@"favicon.ico"].location != NSNotFound) { + return YES; + } else { + return NO; + } + } withStubResponse:^OHHTTPStubsResponse *(NSURLRequest *request) { + // count num requests in the response - ohttpstubs can call the test block + // multiple times for the same request so we cannot count there accurately. + numRequests++; + return [OHHTTPStubsResponse responseWithData:UIImagePNGRepresentation(blank) + statusCode:200 + headers:nil]; + }]; + + [[FBSDKImageDownloader sharedInstance] removeAll]; + NSURL *url = [NSURL URLWithString:@"https://www.facebook.com/favicon.ico"]; + + // we'll make 3 calls for the same image and make sure there are only 2 actual network requests. + + // call #1, ttl = 0 so it should definitely make a request. + dispatch_queue_t aQueue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_BACKGROUND, 0); + dispatch_async(aQueue, ^{ + [[FBSDKImageDownloader sharedInstance] downloadImageWithURL:url + ttl:0 + completion:^(UIImage *image) { + [blocker signal]; + XCTAssertNotNil(image); + }]; + }); + XCTAssertTrue([blocker waitWithTimeout:5], @"did not get callback."); + blocker = [[FBSDKTestBlocker alloc] initWithExpectedSignalCount:1]; + // call #2, ttl = 1 hour so it should not make a request. + [[FBSDKImageDownloader sharedInstance] downloadImageWithURL:url + ttl:60*60 + completion:^(UIImage *image) { + [blocker signal]; + XCTAssertNotNil(image); + }]; + XCTAssertTrue([blocker waitWithTimeout:5], @"did not get callback."); + blocker = [[FBSDKTestBlocker alloc] initWithExpectedSignalCount:1]; + // call #3, ttl = 0 so it should definitely make a request again + [[FBSDKImageDownloader sharedInstance] downloadImageWithURL:url + ttl:0 + completion:^(UIImage *image) { + [blocker signal]; + XCTAssertNotNil(image); + }]; + + XCTAssertTrue([blocker waitWithTimeout:5], @"did not get callback."); + XCTAssertEqual(2, numRequests, @"unexpected number of requests to download"); + [OHHTTPStubs removeAllStubs]; +} + +- (void)testImageCacheBadURL +{ + FBSDKTestBlocker *blocker = [[FBSDKTestBlocker alloc] initWithExpectedSignalCount:2]; + __block NSUInteger numRequests = 0; + [OHHTTPStubs stubRequestsPassingTest:^BOOL(NSURLRequest *request) { + if ([request.URL.path rangeOfString:@"favicon.ico"].location != NSNotFound) { + return YES; + } + else { + return NO; + } + } withStubResponse:^OHHTTPStubsResponse *(NSURLRequest *request) { + numRequests++; + return [OHHTTPStubsResponse responseWithData:[NSData data] + statusCode:404 + headers:nil]; + }]; + NSURL *url = [NSURL URLWithString:@"https://www.facebook.com/favicon.ico"]; + [[FBSDKImageDownloader sharedInstance] downloadImageWithURL:url + ttl:0 + completion:^(UIImage *image) { + [blocker signal]; + XCTAssertNil(image); + }]; + // try twice. + [[FBSDKImageDownloader sharedInstance] downloadImageWithURL:url + ttl:0 + completion:^(UIImage *image) { + [blocker signal]; + XCTAssertNil(image); + }]; + XCTAssertTrue([blocker waitWithTimeout:10], @"did not get 2 callbacks."); + XCTAssertEqual(2, numRequests, @"unexpected number of requests to download"); + [OHHTTPStubs removeAllStubs]; +} + +@end diff --git a/FBSDKIntegrationTests/FBSDKIntegrationTests/FBSDKServerConfigurationManagerIntegrationTests.m b/FBSDKIntegrationTests/FBSDKIntegrationTests/FBSDKServerConfigurationManagerIntegrationTests.m index f21e275050..bfd8e9c29c 100644 --- a/FBSDKIntegrationTests/FBSDKIntegrationTests/FBSDKServerConfigurationManagerIntegrationTests.m +++ b/FBSDKIntegrationTests/FBSDKIntegrationTests/FBSDKServerConfigurationManagerIntegrationTests.m @@ -16,8 +16,9 @@ // IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN // CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. -#import +#import +#import #import #import "FBSDKCoreKit+Internal.h" @@ -52,4 +53,43 @@ - (void)testLoadServerConfiguration XCTAssertEqual(61.0, [FBSDKServerConfigurationManager cachedServerConfiguration].sessionTimoutInterval); } +- (void)testServerConfigurationVersion +{ + XCTestExpectation *expectation = [self expectationWithDescription:@"completed load"]; + + [FBSDKServerConfigurationManager clearCache]; + // assert default configuration version is equal + XCTAssertEqual(FBSDKServerConfigurationVersion, [FBSDKServerConfigurationManager cachedServerConfiguration].version); + + [FBSDKServerConfigurationManager loadServerConfigurationWithCompletionBlock: + ^(FBSDKServerConfiguration *serverConfiguration, NSError *error) { + XCTAssertNotNil(serverConfiguration); + XCTAssertNil(error, @"unexpected error: %@", error); + XCTAssertEqual(FBSDKServerConfigurationVersion, serverConfiguration.version); + + // manually reset the version. + Ivar ivar = class_getInstanceVariable([FBSDKServerConfiguration.class class], "_version"); + object_setIvar(serverConfiguration, ivar, 0); + + XCTAssertEqual(0, serverConfiguration.version); + [expectation fulfill]; + }]; + [self waitForExpectationsWithTimeout:5 handler:^(NSError *error) { + XCTAssertNil(error, @"expectations not fulfilled: %@", error); + }]; + + XCTestExpectation *expectation2 = [self expectationWithDescription:@"completed load2"]; + [FBSDKServerConfigurationManager loadServerConfigurationWithCompletionBlock: + ^(FBSDKServerConfiguration *serverConfiguration, NSError *error) { + XCTAssertNotNil(serverConfiguration); + XCTAssertNil(error, @"unexpected error: %@", error); + // assert it's got the correct version now, implying fresh request. + XCTAssertEqual(FBSDKServerConfigurationVersion, serverConfiguration.version); + [expectation2 fulfill]; + }]; + + [self waitForExpectationsWithTimeout:5 handler:^(NSError *error) { + XCTAssertNil(error, @"expectations not fulfilled: %@", error); + }]; +} @end diff --git a/FBSDKIntegrationTests/FBSDKIntegrationTests/FBSDKShareAPIIntegrationTests.m b/FBSDKIntegrationTests/FBSDKIntegrationTests/FBSDKShareAPIIntegrationTests.m index efd69e381b..51244b02d4 100644 --- a/FBSDKIntegrationTests/FBSDKIntegrationTests/FBSDKShareAPIIntegrationTests.m +++ b/FBSDKIntegrationTests/FBSDKIntegrationTests/FBSDKShareAPIIntegrationTests.m @@ -36,7 +36,7 @@ @interface FBSDKShareAPIIntegrationTests : FBSDKIntegrationTestCase "https://github.com/facebook/facebook-ios-sdk.git", - :tag => "sdk-version-4.16.1" + :tag => "sdk-version-4.17.0" } s.weak_frameworks = "Accounts", "CoreLocation", "Social", "Security", "QuartzCore", "CoreGraphics", "UIKit", "Foundation", "AudioToolbox" diff --git a/FBSDKLoginKit/FBSDKLoginKit/FBSDKLoginManager.m b/FBSDKLoginKit/FBSDKLoginKit/FBSDKLoginManager.m index 6a630870e5..6877a60e8f 100644 --- a/FBSDKLoginKit/FBSDKLoginKit/FBSDKLoginManager.m +++ b/FBSDKLoginKit/FBSDKLoginKit/FBSDKLoginManager.m @@ -326,9 +326,8 @@ - (void)logInWithPermissions:(NSSet *)permissions handler:(FBSDKLoginManagerRequ - (void)logInWithBehavior:(FBSDKLoginBehavior)loginBehavior { - __weak __typeof__(self) weakSelf = self; [FBSDKServerConfigurationManager loadServerConfigurationWithCompletionBlock:^(FBSDKServerConfiguration *serverConfiguration, NSError *loadError) { - [weakSelf logInWithBehavior:loginBehavior serverConfiguration:serverConfiguration serverConfigurationLoadError:loadError]; + [self logInWithBehavior:loginBehavior serverConfiguration:serverConfiguration serverConfigurationLoadError:loadError]; }]; } @@ -338,7 +337,7 @@ - (void)logInWithBehavior:(FBSDKLoginBehavior)loginBehavior serverConfiguration: void(^completion)(BOOL, NSString *, NSError *) = ^void(BOOL didPerformLogIn, NSString *authMethod, NSError *error) { if (didPerformLogIn) { - [_logger startAuthMethod:authMethod]; + [_logger startAuthMethod:authMethod loggingToken:serverConfiguration.loggingToken]; _performingLogIn = YES; } else { if (!error) { @@ -459,7 +458,9 @@ - (void)performNativeLogInWithParameters:(NSDictionary *)loginParams handler:(vo NSError *error; NSURL *authURL = [FBSDKInternalUtility URLWithScheme:scheme host:@"authorize" path:@"" queryParameters:mutableParams error:&error]; + NSDate *start = [NSDate date]; [[FBSDKApplicationDelegate sharedInstance] openURL:authURL sender:self handler:^(BOOL openedURL) { + [_logger logNativeAppDialogResult:openedURL dialogDuration:-[start timeIntervalSinceNow]]; if (handler) { handler(openedURL, error); } @@ -516,18 +517,14 @@ - (void)performBrowserLogInWithParameters:(NSDictionary *)loginParams - (BOOL)application:(UIApplication *)application openURL:(NSURL *)url sourceApplication:(NSString *)sourceApplication annotation:(id)annotation { - // verify the URL is intended as a callback for the SDK's log in - BOOL isFacebookURL = [[url scheme] hasPrefix:[NSString stringWithFormat:@"fb%@", [FBSDKSettings appID]]] && - [[url host] isEqualToString:@"authorize"]; - - BOOL isExpectedSourceApplication = [sourceApplication hasPrefix:@"com.facebook"] || [sourceApplication hasPrefix:@"com.apple"]; + BOOL isFacebookURL = [self canOpenURL:url forApplication:application sourceApplication:sourceApplication annotation:annotation]; if (!isFacebookURL && _performingLogIn) { [self handleImplicitCancelOfLogIn]; } _performingLogIn = NO; - if (isFacebookURL && isExpectedSourceApplication) { + if (isFacebookURL) { NSDictionary *urlParameters = [FBSDKLoginUtility queryParamsFromLoginURL:url]; id completer = [[FBSDKLoginURLCompleter alloc] initWithURLParameters:urlParameters appID:[FBSDKSettings appID]]; @@ -544,6 +541,20 @@ - (BOOL)application:(UIApplication *)application openURL:(NSURL *)url sourceAppl return isFacebookURL; } +- (BOOL)canOpenURL:(NSURL *)url + forApplication:(UIApplication *)application + sourceApplication:(NSString *)sourceApplication + annotation:(id)annotation +{ + // verify the URL is intended as a callback for the SDK's log in + BOOL isFacebookURL = [[url scheme] hasPrefix:[NSString stringWithFormat:@"fb%@", [FBSDKSettings appID]]] && + [[url host] isEqualToString:@"authorize"]; + + BOOL isExpectedSourceApplication = [sourceApplication hasPrefix:@"com.facebook"] || [sourceApplication hasPrefix:@"com.apple"]; + + return isFacebookURL && isExpectedSourceApplication; +} + - (void)applicationDidBecomeActive:(UIApplication *)application { if (_performingLogIn) { diff --git a/FBSDKLoginKit/FBSDKLoginKit/Internal/FBSDKLoginManagerLogger.h b/FBSDKLoginKit/FBSDKLoginKit/Internal/FBSDKLoginManagerLogger.h index 8d2459f4a3..b04d6fdf39 100644 --- a/FBSDKLoginKit/FBSDKLoginKit/Internal/FBSDKLoginManagerLogger.h +++ b/FBSDKLoginKit/FBSDKLoginKit/Internal/FBSDKLoginManagerLogger.h @@ -32,10 +32,12 @@ extern NSString *const FBSDKLoginManagerLoggerAuthMethod_SFVC; - (void)startSessionForLoginManager:(FBSDKLoginManager *)loginManager; - (void)endSession; -- (void)startAuthMethod:(NSString *)authMethod; +- (void)startAuthMethod:(NSString *)authMethod loggingToken:(NSString *)loggingToken; - (void)endLoginWithResult:(FBSDKLoginManagerLoginResult *)result error:(NSError *)error; - (NSDictionary *)parametersWithTimeStampAndClientState:(NSDictionary *)loginParams forAuthMethod:(NSString *)authMethod; - (void)willAttemptAppSwitchingBehavior; - (void)systemAuthDidShowDialog:(BOOL)didShowDialog isUnTOSedDevice:(BOOL)isUnTOSedDevice; + +- (void)logNativeAppDialogResult:(BOOL)result dialogDuration:(NSTimeInterval)dialogDuration; @end diff --git a/FBSDKLoginKit/FBSDKLoginKit/Internal/FBSDKLoginManagerLogger.m b/FBSDKLoginKit/FBSDKLoginKit/Internal/FBSDKLoginManagerLogger.m index 531fd218ad..e43a859bfe 100644 --- a/FBSDKLoginKit/FBSDKLoginKit/Internal/FBSDKLoginManagerLogger.m +++ b/FBSDKLoginKit/FBSDKLoginKit/Internal/FBSDKLoginManagerLogger.m @@ -39,6 +39,7 @@ static NSString *const FBSDKLoginManagerLoggerParamErrorCodeKey = @"4_error_code"; static NSString *const FBSDKLoginManagerLoggerParamErrorMessageKey = @"5_error_message"; static NSString *const FBSDKLoginManagerLoggerParamExtrasKey = @"6_extras"; +static NSString *const FBSDKLoginManagerLoggerParamLoggingTokenKey = @"7_logging_token"; static NSString *const FBSDKLoginManagerLoggerValueEmpty = @""; @@ -62,6 +63,7 @@ @implementation FBSDKLoginManagerLogger NSError *_lastError; NSString *_authMethod; + NSString *_loggingToken; } + (FBSDKLoginManagerLogger *)loggerFromParameters:(NSDictionary *)parameters @@ -139,9 +141,10 @@ - (void)endSession [self logEvent:FBSDKAppEventNameFBSessionAuthEnd result:_lastResult error:_lastError]; } -- (void)startAuthMethod:(NSString *)authMethod +- (void)startAuthMethod:(NSString *)authMethod loggingToken:(NSString *)loggingToken { - _authMethod = authMethod; + _authMethod = [authMethod copy]; + _loggingToken = [loggingToken copy]; [self logEvent:FBSDKAppEventNameFBSessionAuthMethodStart params:[self _parametersForNewEvent]]; } @@ -208,6 +211,16 @@ - (void)systemAuthDidShowDialog:(BOOL)didShowDialog isUnTOSedDevice:(BOOL)isUnTO }]; } +- (void)logNativeAppDialogResult:(BOOL)result dialogDuration:(NSTimeInterval)dialogDuration +{ + NSOperatingSystemVersion iOS10Version = { .majorVersion = 10, .minorVersion = 0, .patchVersion = 0 }; + if ([FBSDKInternalUtility isOSRunTimeVersionAtLeast:iOS10Version]) { + _extras[@"native_app_login_dialog_duration"] = @(dialogDuration); + _extras[@"native_app_login_dialog_result"] = @(result); + [self logEvent:FBSDKAppEventNameFBSessionNativeAppSwitchLoginDialogResult params:[self _parametersForNewEvent]]; + } +} + #pragma mark - Private - (NSString *)clientStateForAuthMethod:(NSString *)authMethod andExistingState:(NSDictionary *)existingState @@ -239,6 +252,7 @@ - (NSMutableDictionary *)_parametersForNewEvent eventParameters[FBSDKLoginManagerLoggerParamErrorCodeKey] = FBSDKLoginManagerLoggerValueEmpty; eventParameters[FBSDKLoginManagerLoggerParamErrorMessageKey] = FBSDKLoginManagerLoggerValueEmpty; eventParameters[FBSDKLoginManagerLoggerParamExtrasKey] = FBSDKLoginManagerLoggerValueEmpty; + eventParameters[FBSDKLoginManagerLoggerParamLoggingTokenKey] = _loggingToken ?: FBSDKLoginManagerLoggerValueEmpty; return eventParameters; } diff --git a/FBSDKLoginKit/FBSDKLoginKitTests/FBSDKLoginManagerTests.m b/FBSDKLoginKit/FBSDKLoginKitTests/FBSDKLoginManagerTests.m index 725f8b250e..c611793a28 100644 --- a/FBSDKLoginKit/FBSDKLoginKitTests/FBSDKLoginManagerTests.m +++ b/FBSDKLoginKit/FBSDKLoginKitTests/FBSDKLoginManagerTests.m @@ -108,7 +108,7 @@ - (void)testOpenURLAuth // now test a cancel and make sure the current token is not touched. url = [self authorizeURLWithParameters:@"error=access_denied&error_code=200&error_description=Permissions+error&error_reason=user_denied#_=_" joinedBy:@"?"]; - XCTAssertTrue([target application:nil openURL:url sourceApplication:nil annotation:nil]); + XCTAssertTrue([target application:nil openURL:url sourceApplication:@"com.apple.mobilesafari" annotation:nil]); FBSDKAccessToken *actualTokenAfterCancel = [FBSDKAccessToken currentAccessToken]; XCTAssertEqualObjects(tokenAfterAuth, actualTokenAfterCancel); } @@ -239,7 +239,7 @@ - (void)testOpenURLReauthSamePermissionsIsNotCancelled #pragma clang diagnostic push #pragma clang diagnostic ignored "-Wnonnull" - XCTAssertTrue([target application:nil openURL:url sourceApplication:nil annotation:nil]); + XCTAssertTrue([target application:nil openURL:url sourceApplication:@"com.apple.mobilesafari" annotation:nil]); #pragma clang diagnostic pop @@ -274,7 +274,7 @@ - (void)testOpenURLReauthNoPermissionsIsNotCancelled #pragma clang diagnostic push #pragma clang diagnostic ignored "-Wnonnull" - XCTAssertTrue([target application:nil openURL:url sourceApplication:nil annotation:nil]); + XCTAssertTrue([target application:nil openURL:url sourceApplication:@"com.apple.mobilesafari" annotation:nil]); #pragma clang diagnostic pop @@ -346,4 +346,31 @@ - (void)testOpenURLWithNoChallengeAndError }]; } + +- (void)testLoginManagerRetainsItselfForLoginMethod +{ + // Mock some methods to force an error callback. + id FBSDKInternalUtilityMock = [OCMockObject niceMockForClass:[FBSDKInternalUtility class]]; + [[[FBSDKInternalUtilityMock stub] andDo:^(NSInvocation *invocation) { + // Nothing + }] validateURLSchemes]; + [[[FBSDKInternalUtilityMock stub] andReturnValue:@NO] isFacebookAppInstalled]; + NSError *URLError = [NSError new]; + [[FBSDKInternalUtilityMock stub] appURLWithHost:OCMOCK_ANY + path:OCMOCK_ANY + queryParameters:OCMOCK_ANY + error:((NSError __autoreleasing **)[OCMArg setTo:URLError])]; + + XCTestExpectation *expectation = [self expectationWithDescription:@"completed auth"]; + FBSDKLoginManager *manager = [FBSDKLoginManager new]; + [manager logInWithReadPermissions:@[@"public_profile"] fromViewController:nil handler:^(FBSDKLoginManagerLoginResult *result, NSError *error) { + [expectation fulfill]; + }]; + // This makes sure that FBSDKLoginManager is retaining itself for the duration of the call + manager = nil; + [self waitForExpectationsWithTimeout:5 handler:^(NSError * _Nullable error) { + XCTAssertNil(error); + }]; +} + @end diff --git a/FBSDKLoginKit/FBSDKLoginKitTests/FBSDKSystemAccountAuthenticationTests.m b/FBSDKLoginKit/FBSDKLoginKitTests/FBSDKSystemAccountAuthenticationTests.m index 0de0bba67d..2f7aca560a 100644 --- a/FBSDKLoginKit/FBSDKLoginKitTests/FBSDKSystemAccountAuthenticationTests.m +++ b/FBSDKLoginKit/FBSDKLoginKitTests/FBSDKSystemAccountAuthenticationTests.m @@ -227,7 +227,11 @@ - (void)testSystemAccountNotAvailableTriesNextAuthMethodServer:(BOOL)serverSuppo errorConfiguration:nil sessionTimeoutInterval:60.0 defaults:NO - loggingToken:nil]; + loggingToken:nil + smartLoginOptions:0 + smartLoginBookmarkIconURL:nil + smartLoginMenuIconURL:nil + ]; id serverConfigurationManager = [OCMockObject mockForClass:[FBSDKServerConfigurationManager class]]; [[[serverConfigurationManager stub] andReturn:serverConfiguration] cachedServerConfiguration]; [[[serverConfigurationManager stub] andDo:^(NSInvocation *invocation) { diff --git a/FBSDKShareKit.podspec b/FBSDKShareKit.podspec index 7803f92b8c..4758be521a 100644 --- a/FBSDKShareKit.podspec +++ b/FBSDKShareKit.podspec @@ -3,7 +3,7 @@ Pod::Spec.new do |s| s.name = "FBSDKShareKit" - s.version = "4.16.1" + s.version = "4.17.0" s.summary = "Official Facebook SDK for iOS to access Facebook Platform's Sharing Features" s.description = <<-DESC @@ -22,7 +22,7 @@ Pod::Spec.new do |s| s.tvos.deployment_target = '9.0' s.source = { :git => "https://github.com/facebook/facebook-ios-sdk.git", - :tag => "sdk-version-4.16.1" + :tag => "sdk-version-4.17.0" } s.ios.weak_frameworks = 'Accounts', 'AudioToolbox', 'CoreGraphics', 'CoreLocation', 'Foundation', 'QuartzCore', 'Security', 'Social', 'UIKit' diff --git a/FBSDKShareKit/FBSDKShareKit/FBSDKDeviceShareViewController.m b/FBSDKShareKit/FBSDKShareKit/FBSDKDeviceShareViewController.m index 8272cebfec..872e99b734 100644 --- a/FBSDKShareKit/FBSDKShareKit/FBSDKDeviceShareViewController.m +++ b/FBSDKShareKit/FBSDKShareKit/FBSDKDeviceShareViewController.m @@ -34,6 +34,14 @@ - (instancetype)initWithShareContent:(id)shareContent return self; } +- (void)loadView +{ + CGRect frame = [UIScreen mainScreen].bounds; + FBSDKDeviceDialogView *deviceView = [[FBSDKDeviceDialogView alloc] initWithFrame:frame]; + deviceView.delegate = self; + self.view = deviceView; +} + - (void)viewDidDisappear:(BOOL)animated { [super viewDidDisappear:animated]; @@ -56,9 +64,11 @@ - (void)viewDidAppear:(BOOL)animated [self _dismissWithError:error]; return; } + NSMutableDictionary *mutableParameters = [params mutableCopy]; + [mutableParameters setValue:[FBSDKDeviceRequestsHelper getDeviceInfo] forKey:FBSDK_DEVICE_INFO_PARAM]; FBSDKGraphRequest *request = [[FBSDKGraphRequest alloc] initWithGraphPath:@"device/share" - parameters:params + parameters:[mutableParameters copy] tokenString:[FBSDKInternalUtility validateRequiredClientAccessToken] HTTPMethod:@"POST" flags:FBSDKGraphRequestFlagNone]; diff --git a/FBSDKTVOSKit.podspec b/FBSDKTVOSKit.podspec index 1d5db89d76..005bd7c244 100644 --- a/FBSDKTVOSKit.podspec +++ b/FBSDKTVOSKit.podspec @@ -3,7 +3,7 @@ Pod::Spec.new do |s| s.name = 'FBSDKTVOSKit' - s.version = '4.16.1' + s.version = '4.17.0' s.summary = 'Official Facebook SDK for tvOS to access Facebook Platform with features like Login and Graph API.' s.description = <<-DESC @@ -20,7 +20,7 @@ Pod::Spec.new do |s| s.tvos.deployment_target = '9.0' s.source = { :git => 'https://github.com/facebook/facebook-ios-sdk.git', - :tag => 'sdk-version-4.16.1' } + :tag => 'sdk-version-4.17.0' } s.source_files = 'FBSDKTVOSKit/FBSDKTVOSKit/**/*.{h,m}' s.public_header_files = 'FBSDKTVOSKit/FBSDKTVOSKit/*.h' diff --git a/FBSDKTVOSKit/FBSDKTVOSKit/FBSDKDeviceLoginViewController.m b/FBSDKTVOSKit/FBSDKTVOSKit/FBSDKDeviceLoginViewController.m index 4af9d51793..7b246c0695 100644 --- a/FBSDKTVOSKit/FBSDKTVOSKit/FBSDKDeviceLoginViewController.m +++ b/FBSDKTVOSKit/FBSDKTVOSKit/FBSDKDeviceLoginViewController.m @@ -28,6 +28,8 @@ @interface FBSDKDeviceLoginViewController() < @implementation FBSDKDeviceLoginViewController { FBSDKDeviceLoginManager *_loginManager; + BOOL _isRetry; + NSArray *_permissions; } - (void)viewDidDisappear:(BOOL)animated @@ -40,7 +42,6 @@ - (void)viewDidLoad { [super viewDidLoad]; - NSArray *permissions = nil; if ((self.readPermissions).count > 0) { NSSet *permissionSet = [NSSet setWithArray:self.readPermissions]; if ((self.publishPermissions).count > 0 || ![FBSDKInternalUtility areAllPermissionsReadPermissions:permissionSet]) { @@ -49,7 +50,7 @@ - (void)viewDidLoad userInfo:nil] raise]; } else { - permissions = self.readPermissions; + _permissions = self.readPermissions; } } else { NSSet *permissionSet = [NSSet setWithArray:self.publishPermissions]; @@ -59,13 +60,10 @@ - (void)viewDidLoad userInfo:nil] raise]; } else { - permissions = self.publishPermissions; + _permissions = self.publishPermissions; } } - _loginManager = [[FBSDKDeviceLoginManager alloc] initWithPermissions:permissions]; - _loginManager.delegate = self; - _loginManager.redirectURL = self.redirectURL; - [_loginManager start]; + [self _initializeLoginManager]; } - (void)dealloc @@ -87,20 +85,105 @@ - (void)deviceLoginManager:(FBSDKDeviceLoginManager *)loginManager completedWith // ourselves we don't want a didCancel (from viewDidDisappear) then didFinish. id delegate = self.delegate; self.delegate = nil; - [self dismissViewControllerAnimated:YES completion:^{ - if (result.isCancelled) { - [self _cancel]; - } else if (result.accessToken) { - [FBSDKAccessToken setCurrentAccessToken:result.accessToken]; - [delegate deviceLoginViewControllerDidFinish:self]; - } else { - [delegate deviceLoginViewControllerDidFail:self error:error]; - } - }]; + + FBSDKAccessToken *token = result.accessToken; + BOOL requireConfirm = (([FBSDKServerConfigurationManager cachedServerConfiguration].smartLoginOptions & FBSDKServerConfigurationSmartLoginOptionsRequireConfirmation) && + (token != nil) && + !_isRetry); + if (requireConfirm) { + FBSDKGraphRequest *graphRequest = [[FBSDKGraphRequest alloc] initWithGraphPath:@"me" + parameters:@{ @"fields" : @"name" } + tokenString:token.tokenString + version:nil + HTTPMethod:@"GET"]; + [graphRequest startWithCompletionHandler:^(FBSDKGraphRequestConnection *connection, id graphResult, NSError *graphError) { + dispatch_async(dispatch_get_main_queue(), ^{ + [self _presentConfirmationForDelegate:delegate + token:result.accessToken + name:graphResult[@"name"] ?: token.userID]; + }); + }]; + } else { + [self dismissViewControllerAnimated:YES completion:^{ + if (result.isCancelled) { + [self _cancel]; + } else if (token != nil) { + [self _notifySuccessForDelegate:delegate token:token]; + } else { + [delegate deviceLoginViewControllerDidFail:self error:error]; + } + }]; + } } #pragma mark - Private impl +- (void)_notifySuccessForDelegate:(id)delegate + token:(FBSDKAccessToken *)token +{ + [FBSDKAccessToken setCurrentAccessToken:token]; + [delegate deviceLoginViewControllerDidFinish:self]; +} + +- (void)_presentConfirmationForDelegate:(id)delegate + token:(FBSDKAccessToken *)token + name:(NSString *)name +{ + NSString *title = + NSLocalizedStringWithDefaultValue(@"SmartLogin.ConfirmationTitle", @"FacebookSDK", [FBSDKInternalUtility bundleForStrings], + @"Confirm Login", + @"The title for the alert when smart login requires confirmation"); + NSString *cancelTitle = + NSLocalizedStringWithDefaultValue(@"SmartLogin.NotYou", @"FacebookSDK", [FBSDKInternalUtility bundleForStrings], + @"Not you?", + @"The cancel label for the alert when smart login requires confirmation"); + NSString *continueTitleFormatString = + NSLocalizedStringWithDefaultValue(@"SmartLogin.Continue", @"FacebookSDK", [FBSDKInternalUtility bundleForStrings], + @"Continue as %@", + @"The format string to continue as for the alert when smart login requires confirmation"); + NSString *continueTitle = [NSString stringWithFormat:continueTitleFormatString, name]; + UIAlertController *alertController = [UIAlertController alertControllerWithTitle:nil + message:title preferredStyle:UIAlertControllerStyleActionSheet]; + [alertController addAction:[UIAlertAction actionWithTitle:continueTitle + style:UIAlertActionStyleDestructive + handler:^(UIAlertAction * _Nonnull action) { + [self dismissViewControllerAnimated:YES completion:^{ + [self _notifySuccessForDelegate:delegate token:token]; + }]; + }]]; + [alertController addAction:[UIAlertAction actionWithTitle:cancelTitle + style:UIAlertActionStyleCancel + handler:^(UIAlertAction * _Nonnull action) { + _isRetry = YES; + FBSDKDeviceDialogView *view = [[FBSDKDeviceDialogView alloc] initWithFrame:self.view.frame]; + view.delegate = self; + self.view = view; + [self.view setNeedsDisplay]; + [self _initializeLoginManager]; + // reconnect delegate before since now + // we are not dismissing. + self.delegate = delegate; + + }]]; + [self presentViewController:alertController animated:YES completion:NULL]; +} + +- (void)_initializeLoginManager +{ + //clear any existing login manager + _loginManager.delegate = nil; + [_loginManager cancel]; + _loginManager = nil; + + BOOL enableSmartLogin = (!_isRetry && + ([FBSDKServerConfigurationManager cachedServerConfiguration].smartLoginOptions & FBSDKServerConfigurationSmartLoginOptionsEnabled)); + _loginManager = [[FBSDKDeviceLoginManager alloc] initWithPermissions:_permissions + enableSmartLogin:enableSmartLogin]; + _loginManager.delegate = self; + _loginManager.redirectURL = self.redirectURL; + [_loginManager start]; +} + - (void)_cancel { [_loginManager cancel]; diff --git a/FBSDKTVOSKit/FBSDKTVOSKit/Internal/FBSDKDeviceLoginManager.h b/FBSDKTVOSKit/FBSDKTVOSKit/Internal/FBSDKDeviceLoginManager.h index 01a004909b..90d61a29ea 100644 --- a/FBSDKTVOSKit/FBSDKTVOSKit/Internal/FBSDKDeviceLoginManager.h +++ b/FBSDKTVOSKit/FBSDKTVOSKit/Internal/FBSDKDeviceLoginManager.h @@ -60,13 +60,15 @@ NS_ASSUME_NONNULL_BEGIN See [Facebook Device Login](https://developers.facebook.com/docs/facebook-login/for-devices). */ -@interface FBSDKDeviceLoginManager : NSObject +@interface FBSDKDeviceLoginManager : NSObject /*! @abstract Initializes a new instance. @param permissions permissions to request. */ -- (instancetype)initWithPermissions:(nullable NSArray *)permissions NS_DESIGNATED_INITIALIZER; +- (instancetype)initWithPermissions:(nullable NSArray *)permissions + enableSmartLogin:(BOOL)enableSmartLogin +NS_DESIGNATED_INITIALIZER; - (instancetype)init NS_UNAVAILABLE; + (instancetype)new NS_UNAVAILABLE; diff --git a/FBSDKTVOSKit/FBSDKTVOSKit/Internal/FBSDKDeviceLoginManager.m b/FBSDKTVOSKit/FBSDKTVOSKit/Internal/FBSDKDeviceLoginManager.m index 3e6a8a9f92..4318aa246f 100644 --- a/FBSDKTVOSKit/FBSDKTVOSKit/Internal/FBSDKDeviceLoginManager.m +++ b/FBSDKTVOSKit/FBSDKTVOSKit/Internal/FBSDKDeviceLoginManager.m @@ -29,6 +29,8 @@ @implementation FBSDKDeviceLoginManager { FBSDKDeviceLoginCodeInfo *_codeInfo; BOOL _isCancelled; + NSNetService * _loginAdvertisementService; + BOOL _isSmartLoginEnabled; } + (void)initialize @@ -38,10 +40,11 @@ + (void)initialize } } -- (instancetype)initWithPermissions:(NSArray *)permissions +- (instancetype)initWithPermissions:(NSArray *)permissions enableSmartLogin:(BOOL)enableSmartLogin { if ((self = [super init])) { _permissions = [permissions copy]; + _isSmartLoginEnabled = enableSmartLogin; } return self; } @@ -54,6 +57,7 @@ - (void)start NSDictionary *parameters = @{ @"scope": [self.permissions componentsJoinedByString:@","] ?: @"", @"redirect_uri": self.redirectURL.absoluteString ?: @"", + FBSDK_DEVICE_INFO_PARAM: [FBSDKDeviceRequestsHelper getDeviceInfo], }; FBSDKGraphRequest *request = [[FBSDKGraphRequest alloc] initWithGraphPath:@"device/login" parameters:parameters @@ -73,6 +77,13 @@ - (void)start verificationURL:[NSURL URLWithString:result[@"verification_uri"]] expirationDate:[[NSDate date] dateByAddingTimeInterval:[result[@"expires_in"] doubleValue]] pollingInterval:[result[@"interval"] integerValue]]; + + if (_isSmartLoginEnabled) { + [FBSDKDeviceRequestsHelper startAdvertisementService:_codeInfo.loginCode + withDelegate:self + ]; + } + [self.delegate deviceLoginManager:self startedWithCodeInfo:_codeInfo]; [self _schedulePoll:_codeInfo.pollingInterval]; }]; @@ -80,6 +91,7 @@ - (void)start - (void)cancel { + [FBSDKDeviceRequestsHelper cleanUpAdvertisementService:self]; _isCancelled = YES; [g_loginManagerInstances removeObject:self]; } @@ -88,6 +100,7 @@ - (void)cancel - (void)_notifyError:(NSError *)error { + [FBSDKDeviceRequestsHelper cleanUpAdvertisementService:self]; [self.delegate deviceLoginManager:self completedWithResult:nil error:error]; @@ -96,6 +109,7 @@ - (void)_notifyError:(NSError *)error - (void)_notifyToken:(NSString *)tokenString { + [FBSDKDeviceRequestsHelper cleanUpAdvertisementService:self]; void(^completeWithResult)(FBSDKDeviceLoginManagerResult *) = ^(FBSDKDeviceLoginManagerResult *result) { [self.delegate deviceLoginManager:self completedWithResult:result error:nil]; [g_loginManagerInstances removeObject:self]; @@ -197,4 +211,14 @@ - (void)_schedulePoll:(NSUInteger)interval }); } +- (void)netService:(NSNetService *)sender + didNotPublish:(NSDictionary *)errorDict +{ + // Only cleanup if the publish error is from our advertising service + if ([FBSDKDeviceRequestsHelper isDelegate:self forAdvertisementService:sender]) + { + [FBSDKDeviceRequestsHelper cleanUpAdvertisementService:self]; + } +} + @end diff --git a/FacebookSDKStrings.bundle/Resources/en.lproj/FacebookSDK.strings b/FacebookSDKStrings.bundle/Resources/en.lproj/FacebookSDK.strings index 9a247b3b72ece1632b8ed33b82bc0941ba3efacd..9ad80d12634703d7e8a74352eaa40219c76770a1 100644 GIT binary patch delta 1075 zcmcgrO-lk%6um<*F*-#d8KKfc7gCU=?y|Bk5W|wRO;C$Try9_yoH1zCrXP^JKhR%@ zcKw;4Z3O*<&YQ7jG;>!BbLYOfZ_YXQo;MH6uZ8>Ph(;#WiII+{jo3ndLl@Mh7LQ4Z z(IoN~CHcEDqgd1=4J*5oFXpOW3o2pD85IFgMzMq~Cje?t98jyc0Ly~_tB$5iTJnKyfzG5;GCAGqHm|MyB|t9R5MHKa42tBRT?2 zno@u(0FXh78_ykzT|^UOV#EgaqYb0U^wDG-nyNE49evn<-Nxz~UDh1B3gQ*$bh1#j zex=P1qs{iwW*yp|fj-5gktpAh$N9RX^13|1@1*dt^=&qTvBGWx%`-UQsuErZWv&OF zI~uea!VOgBX^{dD&@a&79VL|Yb{^mh8!Wm{Y>z}>IY*Aas1dFPqjRn?!hLaug)%WG zhJz>+tzfM|zfNmhlS87P+du6Gd?|7jduqI&0TtCRl)nWN!5So*j@1n=jm`d($UThA XrbEQGZ`kfzHrf5N@+jrtT!20SqQ1_g delta 111 zcmaFlzQAh2uH<}%JO%}ZN``!fQidWRtC%5|ArZ(bVJKk80E*@@qyk0r!E(t!Iu|Hj m3Y1L+izEW&Q-JcRKpG}i0@Mf6lgN++RA0uBy7?;$vkU;P=onZ4 diff --git a/samples/LoginSample/LoginSample.xcodeproj/project.pbxproj b/samples/LoginSample/LoginSample.xcodeproj/project.pbxproj index 9e28a423a9..8111b9dd30 100644 --- a/samples/LoginSample/LoginSample.xcodeproj/project.pbxproj +++ b/samples/LoginSample/LoginSample.xcodeproj/project.pbxproj @@ -352,7 +352,7 @@ TargetAttributes = { 030529EF1D6B7D2C0072BA47 = { CreatedOnToolsVersion = 7.3.1; - DevelopmentTeam = V9WTTPBFK9; + ProvisioningStyle = Manual; }; }; }; @@ -543,7 +543,7 @@ baseConfigurationReference = 03F4CF101D80BBE400C8135E /* Application.xcconfig */; buildSettings = { ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; - DEVELOPMENT_TEAM = V9WTTPBFK9; + DEVELOPMENT_TEAM = ""; FRAMEWORK_SEARCH_PATHS = "$(inherited)"; GCC_TREAT_WARNINGS_AS_ERRORS = NO; INFOPLIST_FILE = "$(SRCROOT)/LoginSample/Info.plist"; @@ -558,7 +558,7 @@ baseConfigurationReference = 03F4CF101D80BBE400C8135E /* Application.xcconfig */; buildSettings = { ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; - DEVELOPMENT_TEAM = V9WTTPBFK9; + DEVELOPMENT_TEAM = ""; FRAMEWORK_SEARCH_PATHS = "$(inherited)"; GCC_TREAT_WARNINGS_AS_ERRORS = NO; INFOPLIST_FILE = "$(SRCROOT)/LoginSample/Info.plist"; diff --git a/samples/LoginSample/LoginSample/AKThemes/AdvancedUIManager.m b/samples/LoginSample/LoginSample/AKThemes/AdvancedUIManager.m index f11343a6bd..5f69ae07c5 100644 --- a/samples/LoginSample/LoginSample/AKThemes/AdvancedUIManager.m +++ b/samples/LoginSample/LoginSample/AKThemes/AdvancedUIManager.m @@ -72,6 +72,7 @@ - (AKFButtonType)buttonTypeForState:(AKFLoginFlowState)state case AKFLoginFlowStateEmailVerify: case AKFLoginFlowStateSendingCode: case AKFLoginFlowStateVerifyingCode: + case AKFLoginFlowStateResendCode: return AKFButtonTypeDefault; } } @@ -159,6 +160,9 @@ - (PlaceholderView *)_viewForState:(AKFLoginFlowState)state case AKFLoginFlowStateError: prefix = @"Custom Error"; break; + case AKFLoginFlowStateResendCode: + prefix = @"Custom Resend Code"; + break; case AKFLoginFlowStateNone: return nil; } diff --git a/samples/LoginSample/LoginSample/AKThemes/ReverbActionBarView.m b/samples/LoginSample/LoginSample/AKThemes/ReverbActionBarView.m index f86521ab9c..f2908cebf2 100644 --- a/samples/LoginSample/LoginSample/AKThemes/ReverbActionBarView.m +++ b/samples/LoginSample/LoginSample/AKThemes/ReverbActionBarView.m @@ -106,6 +106,7 @@ - (NSString *)_titleForState:(AKFLoginFlowState)state theme:(ReverbTheme *)theme NSString *title; switch (state) { case AKFLoginFlowStateNone: + case AKFLoginFlowStateResendCode: return nil; case AKFLoginFlowStatePhoneNumberInput: title = @"Enter your phone number"; diff --git a/samples/LoginSample/LoginSample/AKThemes/ReverbUIManager.m b/samples/LoginSample/LoginSample/AKThemes/ReverbUIManager.m index 957463f747..311791aea2 100644 --- a/samples/LoginSample/LoginSample/AKThemes/ReverbUIManager.m +++ b/samples/LoginSample/LoginSample/AKThemes/ReverbUIManager.m @@ -90,6 +90,7 @@ - (nullable UIView *)bodyViewForState:(AKFLoginFlowState)state case AKFLoginFlowStateEmailInput: case AKFLoginFlowStateCodeInput: case AKFLoginFlowStateNone: + case AKFLoginFlowStateResendCode: return nil; } @@ -111,6 +112,7 @@ - (AKFButtonType)buttonTypeForState:(AKFLoginFlowState)state case AKFLoginFlowStateEmailVerify: case AKFLoginFlowStateSendingCode: case AKFLoginFlowStateVerifyingCode: + case AKFLoginFlowStateResendCode: return AKFButtonTypeDefault; } } @@ -140,6 +142,7 @@ - (nullable UIView *)footerViewForState:(AKFLoginFlowState)state progress = 5; break; case AKFLoginFlowStateError: + case AKFLoginFlowStateResendCode: case AKFLoginFlowStateNone: return nil; } diff --git a/samples/LoginSample/LoginSample/LoginSample-Prefix.pch b/samples/LoginSample/LoginSample/LoginSample-Prefix.pch index 65193bdf5a..81246822f2 100644 --- a/samples/LoginSample/LoginSample/LoginSample-Prefix.pch +++ b/samples/LoginSample/LoginSample/LoginSample-Prefix.pch @@ -15,10 +15,12 @@ */ // -// Prefix header for all source files of the 'Scrumptious' target in the 'Scrumptious' project +// Prefix header for all source files of the 'LoginSample' target in the 'LoginSample' project // #ifdef __OBJC__ #import #import #endif + +#define FB_TWEAK_ENABLED 1 diff --git a/samples/Scrumptious/scrumptious/scrumptious-Info.plist b/samples/Scrumptious/scrumptious/scrumptious-Info.plist index cff54dfe08..3c1281b0b4 100644 --- a/samples/Scrumptious/scrumptious/scrumptious-Info.plist +++ b/samples/Scrumptious/scrumptious/scrumptious-Info.plist @@ -60,5 +60,7 @@ fbauth2 fbshareextension + NSPhotoLibraryUsageDescription + Scrumptious lets you attach photos of delicious food. diff --git a/scripts/run_tests.sh b/scripts/run_tests.sh index e107d1006e..89a2e68e76 100755 --- a/scripts/run_tests.sh +++ b/scripts/run_tests.sh @@ -73,7 +73,7 @@ for SCHEME in $SCHEMES; do -scheme "BuildAllKits" \ -configuration $BUILDCONFIGURATION \ -sdk iphonesimulator \ - -destination 'platform=iOS Simulator,name=iPhone 5' \ + -destination 'platform=iOS Simulator,name=iPhone 6' \ test ) || exit $? @@ -111,7 +111,7 @@ for SCHEME in $SCHEMES; do -scheme $SCHEME \ -configuration $BUILDCONFIGURATION \ -sdk iphonesimulator \ - -destination 'platform=iOS Simulator,name=iPhone 5' \ + -destination 'platform=iOS Simulator,name=iPhone 6' \ test" eval $COMMAND || die "Error while running tests ($COMMAND)" fi