diff --git a/.circleci/config.yml b/.circleci/config.yml index 80701c2298..b5ade62254 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -50,7 +50,7 @@ commands: steps: - attach_workspace: at: . - - run: carthage build --no-skip-current + - run: CARTHAGE_BIN_PATH=$( which carthage ) sh scripts/carthage.sh build --no-skip-current build_for_release: description: Builds libraries for release @@ -82,7 +82,7 @@ commands: - attach_workspace: at: . - run: | - xcodebuild build \ + xcodebuild clean build \ -workspace FacebookSDK.xcworkspace \ -scheme << parameters.scheme >> \ -configuration << parameters.configuration >> diff --git a/.fast_checks b/.fast_checks new file mode 100644 index 0000000000..67e317cec9 --- /dev/null +++ b/.fast_checks @@ -0,0 +1,4 @@ +InheritParentConfig: true +Checks: ' +-idfa-usage +' diff --git a/.gitattributes b/.gitattributes index 983658214c..4712d86ebc 100644 --- a/.gitattributes +++ b/.gitattributes @@ -1,2 +1 @@ *.strings diff=localizablestrings -*.pbxproj binary merge=union diff --git a/.github/ISSUE_TEMPLATE/bug_report.md b/.github/ISSUE_TEMPLATE/bug_report.md index a0e783d3cd..a62729b3e9 100644 --- a/.github/ISSUE_TEMPLATE/bug_report.md +++ b/.github/ISSUE_TEMPLATE/bug_report.md @@ -9,7 +9,7 @@ labels: bug - [ ] I've updated to the latest released version of the SDK - [ ] I've searched for existing [GitHub issues](https://github.com/facebook/facebook-ios-sdk/issues) - [ ] I've looked for existing answers on [Stack Overflow](https://facebook.stackoverflow.com), the [Facebook Developer Community Forum](https://developers.facebook.com/community/) and the [Facebook Developers Group](https://www.facebook.com/groups/fbdevelopers) -- [ ] I've read the [Code of Conduct](CODE_OF_CONDUCT.md) +- [ ] I've read the [Code of Conduct](https://github.com/facebook/facebook-ios-sdk/blob/master/CODE_OF_CONDUCT.md) - [ ] This issue is not security related and can safely be disclosed publicly on GitHub ## Environment diff --git a/.swiftlint.yml b/.swiftlint.yml index 079b218014..e38e9b01e5 100644 --- a/.swiftlint.yml +++ b/.swiftlint.yml @@ -34,6 +34,12 @@ nesting: type_level: 2 private_outlet: allow_private_set: true +type_name: + excluded: [RawAppEventsConfigurationResponseFixtures] + +# Xcode fixits insert these as "public override" vs "override public" so overriding Swiftlint's defaullt of "override, acl, ..." to "acl, override, ..." +modifier_order: + preferred_modifier_order: [acl, override, setterACL, dynamic, mutators, lazy, final, required, convenience, typeMethods, owned] disabled_rules: - attributes @@ -55,6 +61,7 @@ disabled_rules: - discouraged_optional_collection - function_default_parameter_at_end - todo + - trailing_comma opt_in_rules: - array_init @@ -153,7 +160,6 @@ opt_in_rules: - switch_case_alignment - syntactic_sugar - trailing_closure - - trailing_comma - trailing_newline - trailing_semicolon - trailing_whitespace diff --git a/CHANGELOG.md b/CHANGELOG.md index 8c465281d4..7cd45dd56a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -9,7 +9,67 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), ### Important -[Full Changelog](https://github.com/facebook/facebook-ios-sdk/compare/v7.1.1...HEAD) +[Full Changelog](https://github.com/facebook/facebook-ios-sdk/compare/v8.2.0...HEAD) + +## 8.2.0 + +### Changed +- Remove SignalHandler to avoid hiding root cause of crashes caused by fatal signals. +- Expose functions in `FBSDKUserDataStore` as public for apps using [Audience Network SDK](https://developers.facebook.com/docs/audience-network) only to use advanced matching. + +[2020-11-10](https://github.com/facebook/facebook-ios-sdk/releases/tag/v8.2.0) | +[Full Changelog](https://github.com/facebook/facebook-ios-sdk/compare/v8.1.0...v8.2.0) + +## 8.1.0 + +### Added +- Introduced `AppLinkResolverRequestBuilder` for use in cleaning up and adding tests around `AppLinkResolver` + +### Changed +- Removed version checks for iOS 9 since it’s the default version now. +- Refactored `AppLinkResolver` to use a request builder +- Refactored and added tests around `FBSDKProfile` and `FBSDKProfilePictureView` +- Updated `FBSDKSettings` to use `ADIdentifierManager` for tracking status +- Removes usages of deprecated `UI_USER_INTERFACE_IDIOM()` + +### Fixed +- Issues with Swift names causing warnings - #1522 +- Fixes bugs related to crash handling - #1444 +- Fixes Carthage distribution to include the correct binary slices when building on Xcode12 - #1484 +- Fixes duplicate symbol for `FBSDKVideoUploader` bug #1512 +- GET requests now default to having a 'fields' parameter to avoid warnings about missing fields #1403 +- Fixes Multithreading issue related to crash reporting - #1550 + +[2020-10-23](https://github.com/facebook/facebook-ios-sdk/releases/tag/v8.1.0) | +[Full Changelog](https://github.com/facebook/facebook-ios-sdk/compare/v8.0.0...v8.1.0) + +## 8.0.0 + +## Added +- Added timestamp for install event in iOS 14 +- Added method `setAdvertiserTrackingEnabled` to overwrite the `advertiser_tracking_enabled` flag +- Added `SKAdNetwork` support for installs +- Added `SKAdNetwork` support for conversion value in iOS 14 +- Added `FBSDKReferralManager` for integrating with the web referral dialog +- Added method `loginWithURL` to `FBSDKLoginManager` for supporting deep link authentication +- Added E2E tests for all in-market versions of the SDK that run on server changes to avoid regressions + +## Changed +- Event handling in iOS 14: will drop events if `setAdvertiserTrackingEnabled` is called with `false` in iOS 14 +- `FBSDKProfile - imageURLForPictureMode:size:` - User profile images will only be available when an access or client token is available + +## Deprecated +- `FBSDKSettings - isAutoInitEnabled` - Auto-initialization flag. Will be removed in the next major release. Future versions of the SDK will not utilize the `+ load` method to automatically initialize the SDK. + +## Fixed / Patched +- #1444 - Update crash handling to use sigaction in signal handler and respect SIG_IGN +- #1447 - Login form automatically closing when SDK is not initialized on startup +- #1478 - Minimum iOS deployment target is now 9.0 +- #1485 - StoreKit is now added as a weak framework for CocoaPods +- Bug fix for Advanced Matching, which was not working on iOS 14 + +[2020-09-22](https://github.com/facebook/facebook-ios-sdk/releases/tag/v8.0.0) | +[Full Changelog](https://github.com/facebook/facebook-ios-sdk/compare/v7.1.1...v8.0.0) ## 7.1.1 diff --git a/Cartfile.private b/Cartfile.private index 7fb0ee31fa..ca5073ace6 100644 --- a/Cartfile.private +++ b/Cartfile.private @@ -1,2 +1,2 @@ github "AliSoftware/OHHTTPStubs" "543182991a0d59db81f5b835a40901a9a386847d" -github "erikdoe/ocmock" "630598a81c8d51ae98434a388d17fe67de37b21a" +github "erikdoe/ocmock" "58bb623eb3852ab72b4c993bfd53040f051a405c" diff --git a/Cartfile.resolved b/Cartfile.resolved index 7fb0ee31fa..ca5073ace6 100644 --- a/Cartfile.resolved +++ b/Cartfile.resolved @@ -1,2 +1,2 @@ github "AliSoftware/OHHTTPStubs" "543182991a0d59db81f5b835a40901a9a386847d" -github "erikdoe/ocmock" "630598a81c8d51ae98434a388d17fe67de37b21a" +github "erikdoe/ocmock" "58bb623eb3852ab72b4c993bfd53040f051a405c" diff --git a/Configurations/FacebookSDK-DynamicFramework.xcconfig b/Configurations/FacebookSDK-DynamicFramework.xcconfig index 8da434c645..245074f43a 100644 --- a/Configurations/FacebookSDK-DynamicFramework.xcconfig +++ b/Configurations/FacebookSDK-DynamicFramework.xcconfig @@ -17,7 +17,7 @@ // CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. // Deployment -IPHONEOS_DEPLOYMENT_TARGET = 8.0 +IPHONEOS_DEPLOYMENT_TARGET = 9.0 // Cocoa Touch Framework MACH_O_TYPE = mh_dylib diff --git a/Configurations/FacebookSDK-LogicTests.xcconfig b/Configurations/FacebookSDK-LogicTests.xcconfig index 1a381c8f49..44539f729b 100644 --- a/Configurations/FacebookSDK-LogicTests.xcconfig +++ b/Configurations/FacebookSDK-LogicTests.xcconfig @@ -28,4 +28,4 @@ WRAPPER_EXTENSION = xctest OTHER_LDFLAGS = -all_load -lc++ ENABLE_BITCODE = YES -IPHONEOS_DEPLOYMENT_TARGET = 8.0 +IPHONEOS_DEPLOYMENT_TARGET = 9.0 diff --git a/Configurations/FacebookSDK-Project.xcconfig b/Configurations/FacebookSDK-Project.xcconfig index 7b35492bdd..52f3d102ee 100644 --- a/Configurations/FacebookSDK-Project.xcconfig +++ b/Configurations/FacebookSDK-Project.xcconfig @@ -18,7 +18,7 @@ // Architectures ARCHS = i386 armv7 x86_64 arm64 -IPHONEOS_DEPLOYMENT_TARGET = 8.0 +IPHONEOS_DEPLOYMENT_TARGET = 9.0 SDKROOT = iphoneos // Build Options diff --git a/Configurations/Platform/iOS.xcconfig b/Configurations/Platform/iOS.xcconfig index 8b4c14958e..cc1c5b2705 100644 --- a/Configurations/Platform/iOS.xcconfig +++ b/Configurations/Platform/iOS.xcconfig @@ -17,7 +17,7 @@ // CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. SDKROOT = iphoneos -IPHONEOS_DEPLOYMENT_TARGET = 8.0 +IPHONEOS_DEPLOYMENT_TARGET = 9.0 // Supported device families (1 is iPhone, 2 is iPad, 3 is Apple TV) TARGETED_DEVICE_FAMILY = 1,2 diff --git a/Configurations/Version.xcconfig b/Configurations/Version.xcconfig index a54d2076b2..d1d10addd8 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=7.1.1 +FBSDK_PROJECT_VERSION=8.2.0 MNSDK_PROJECT_VERSION=TODO_SUPPORT_MNSDK diff --git a/FBSDKCoreKit.podspec b/FBSDKCoreKit.podspec index 107bdcf089..4460533659 100644 --- a/FBSDKCoreKit.podspec +++ b/FBSDKCoreKit.podspec @@ -3,7 +3,7 @@ Pod::Spec.new do |s| s.name = 'FBSDKCoreKit' - s.version = '7.1.1' + s.version = '8.2.0' s.summary = 'Official Facebook SDK for iOS to access Facebook Platform core features' s.description = <<-DESC @@ -18,7 +18,7 @@ Pod::Spec.new do |s| s.author = 'Facebook' s.platform = :ios, :tvos - s.ios.deployment_target = '8.0' + s.ios.deployment_target = '9.0' s.tvos.deployment_target = '10.0' s.source = { @@ -26,14 +26,14 @@ Pod::Spec.new do |s| tag: "v#{s.version}" } - s.ios.weak_frameworks = 'Accelerate', 'Accounts', 'Social', 'Security', 'QuartzCore', 'CoreGraphics', 'UIKit', 'Foundation', 'AudioToolbox' + s.ios.weak_frameworks = 'Accelerate', 'Accounts', 'AdSupport', 'Social', 'Security', 'StoreKit', 'QuartzCore', 'CoreGraphics', 'UIKit', 'Foundation', 'AudioToolbox' s.tvos.weak_frameworks = 'CoreLocation', 'Security', 'QuartzCore', 'CoreGraphics', 'UIKit', 'Foundation', 'AudioToolbox' # This excludes `FBSDKCoreKit/FBSDKCoreKit/Internal_NoARC/` folder, as that folder includes only `no-arc` files. s.requires_arc = ['FBSDKCoreKit/FBSDKCoreKit/*', 'FBSDKCoreKit/FBSDKCoreKit/AppEvents/**/*', 'FBSDKCoreKit/FBSDKCoreKit/AppLink/**/*', - 'FBSDKCoreKit/FBSDKCoreKit/Basics/**/*', + 'Sources/FBSDKCoreKit_Basics/**/*', 'FBSDKCoreKit/FBSDKCoreKit/GraphAPI/*', 'FBSDKCoreKit/FBSDKCoreKit/Internal/**/*'] @@ -47,20 +47,14 @@ Pod::Spec.new do |s| s.library = 'c++', 'stdc++' s.subspec 'Basics' do |ss| - ss.source_files = 'FBSDKCoreKit/FBSDKCoreKit/Basics/*.{h,m}', - 'FBSDKCoreKit/FBSDKCoreKit/Basics/**/*.{h,m}' - ss.public_header_files = 'FBSDKCoreKit/FBSDKCoreKit/Basics/Internal/**/*.h', - 'FBSDKCoreKit/FBSDKCoreKit/Basics/Instrument/**/*.h', - 'FBSDKCoreKit/FBSDKCoreKit/Basics/*.h' - ss.private_header_files = 'FBSDKCoreKit/FBSDKCoreKit/Basics/Internal/**/*.h', - 'FBSDKCoreKit/FBSDKCoreKit/Basics/Instrument/**/*.h' + ss.source_files = 'Sources/FBSDKCoreKit_Basics/**/*.{h,m}' + ss.private_header_files = 'Sources/FBSDKCoreKit_Basics/**/*+Internal.h' ss.library = 'z' end s.subspec 'Core' do |ss| ss.dependency 'FBSDKCoreKit/Basics' - ss.exclude_files = 'FBSDKCoreKit/FBSDKCoreKit/Basics/*', - 'FBSDKCoreKit/FBSDKCoreKit/Basics/**/*.{h,m}', + ss.exclude_files = 'Sources/FBSDKCoreKit_Basics/**/*', 'FBSDKCoreKit/FBSDKCoreKit/include/**/*', 'FBSDKCoreKit/FBSDKCoreKit/Swift/Exports.swift' ss.source_files = 'FBSDKCoreKit/FBSDKCoreKit/**/*.{h,hpp,m,mm,swift}' @@ -69,6 +63,7 @@ Pod::Spec.new do |s| 'FBSDKCoreKit/FBSDKCoreKit/*.h', 'FBSDKCoreKit/FBSDKCoreKit/AppEvents/*.h', 'FBSDKCoreKit/FBSDKCoreKit/AppLink/*.h', + 'FBSDKCoreKit/FBSDKCoreKit/AppLink/Resolver/*.h', 'FBSDKCoreKit/FBSDKCoreKit/GraphAPI/*.h' ss.private_header_files = 'FBSDKCoreKit/FBSDKCoreKit/Internal/**/*.h', 'FBSDKCoreKit/FBSDKCoreKit/AppEvents/Internal/**/*.h' diff --git a/FBSDKCoreKit/Configurations/FBSDKCoreKit-Dynamic.xcconfig b/FBSDKCoreKit/Configurations/FBSDKCoreKit-Dynamic.xcconfig index 339bfaf634..0e521e6cca 100644 --- a/FBSDKCoreKit/Configurations/FBSDKCoreKit-Dynamic.xcconfig +++ b/FBSDKCoreKit/Configurations/FBSDKCoreKit-Dynamic.xcconfig @@ -28,4 +28,4 @@ CURRENT_PROJECT_VERSION = $(FBSDK_PROJECT_VERSION) INFOPLIST_FILE = $(SRCROOT)/FBSDKCoreKit/Info.plist MODULEMAP_FILE = $(SRCROOT)/FBSDKCoreKit/FBSDKCoreKit.modulemap -IPHONEOS_DEPLOYMENT_TARGET = 8.0 +IPHONEOS_DEPLOYMENT_TARGET = 9.0 diff --git a/FBSDKCoreKit/Configurations/FBSDKCoreKitTests.xcconfig b/FBSDKCoreKit/Configurations/FBSDKCoreKitTests.xcconfig index 87fe289f18..596e98b4fb 100644 --- a/FBSDKCoreKit/Configurations/FBSDKCoreKitTests.xcconfig +++ b/FBSDKCoreKit/Configurations/FBSDKCoreKitTests.xcconfig @@ -24,7 +24,9 @@ PRODUCT_BUNDLE_IDENTIFIER = com.facebook.sdk.FBSDKCoreKitTests INFOPLIST_FILE = $(SRCROOT)/FBSDKCoreKitTests/Info.plist -IPHONEOS_DEPLOYMENT_TARGET = 8.0 +IPHONEOS_DEPLOYMENT_TARGET = 9.0 HEADER_SEARCH_PATHS = $(inherited) $(BUILT_PRODUCTS_DIR) LIBRARY_SEARCH_PATHS = $(inherited) $(BUILT_PRODUCTS_DIR) + +SWIFT_OBJC_BRIDGING_HEADER = FBSDKCoreKitTests/FBSDKCoreKitTests-Bridging-Header.h diff --git a/FBSDKCoreKit/FBSDKCoreKit.xcodeproj/project.pbxproj b/FBSDKCoreKit/FBSDKCoreKit.xcodeproj/project.pbxproj index 914b0ca430..ee0a1f263d 100644 --- a/FBSDKCoreKit/FBSDKCoreKit.xcodeproj/project.pbxproj +++ b/FBSDKCoreKit/FBSDKCoreKit.xcodeproj/project.pbxproj @@ -66,7 +66,13 @@ 0384CEBB208E60660013D404 /* FBSDKAccessTokenExpirer.m in Sources */ = {isa = PBXBuildFile; fileRef = 033429B120894D4700C94913 /* FBSDKAccessTokenExpirer.m */; }; 0384CED0208E606A0013D404 /* FBSDKAccessTokenExpirer.m in Sources */ = {isa = PBXBuildFile; fileRef = 033429B120894D4700C94913 /* FBSDKAccessTokenExpirer.m */; }; 0384CED1208E606B0013D404 /* FBSDKAccessTokenExpirer.m in Sources */ = {isa = PBXBuildFile; fileRef = 033429B120894D4700C94913 /* FBSDKAccessTokenExpirer.m */; }; - 2A3DA4161CB4C0F600339BD4 /* FBSDKAppLinkUtilityTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 2A3DA4151CB4C0F600339BD4 /* FBSDKAppLinkUtilityTests.m */; }; + 2A3DA4161CB4C0F600339BD4 /* FBSDKAppLinkUtilityTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2A3DA4151CB4C0F600339BD4 /* FBSDKAppLinkUtilityTests.swift */; }; + 40853B9424C8C43300A7CB16 /* FBSDKJSONValueTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 40853B9324C8C43300A7CB16 /* FBSDKJSONValueTests.m */; }; + 45540D9125271A4B008E853E /* FBSDKAppLinkResolverRequestBuilder.h in Headers */ = {isa = PBXBuildFile; fileRef = 45540D8F25271A4B008E853E /* FBSDKAppLinkResolverRequestBuilder.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 45540D9225271A4B008E853E /* FBSDKAppLinkResolverRequestBuilder.h in Headers */ = {isa = PBXBuildFile; fileRef = 45540D8F25271A4B008E853E /* FBSDKAppLinkResolverRequestBuilder.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 45540D9325271A4B008E853E /* FBSDKAppLinkResolverRequestBuilder.m in Sources */ = {isa = PBXBuildFile; fileRef = 45540D9025271A4B008E853E /* FBSDKAppLinkResolverRequestBuilder.m */; }; + 45540D9425271A4B008E853E /* FBSDKAppLinkResolverRequestBuilder.m in Sources */ = {isa = PBXBuildFile; fileRef = 45540D9025271A4B008E853E /* FBSDKAppLinkResolverRequestBuilder.m */; }; + 45540DB125271A88008E853E /* FBSDKAppLinkResolverRequestBuilderTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 45540DB025271A88008E853E /* FBSDKAppLinkResolverRequestBuilderTests.m */; }; 4AF47CF31F42468E00A57A67 /* FBSDKDeviceUtilities.m in Sources */ = {isa = PBXBuildFile; fileRef = 4AF47CF11F42468D00A57A67 /* FBSDKDeviceUtilities.m */; }; 4AF47CF41F42468E00A57A67 /* FBSDKDeviceUtilities.m in Sources */ = {isa = PBXBuildFile; fileRef = 4AF47CF11F42468D00A57A67 /* FBSDKDeviceUtilities.m */; }; 4AF47CF51F42468E00A57A67 /* FBSDKDeviceUtilities.h in Headers */ = {isa = PBXBuildFile; fileRef = 4AF47CF21F42468D00A57A67 /* FBSDKDeviceUtilities.h */; }; @@ -74,13 +80,6 @@ 4AF47D131F424A8700A57A67 /* CoreImage.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 4AF47CFE1F424A8700A57A67 /* CoreImage.framework */; }; 520223F71D83C8D200CE0AB5 /* FBSDKDeviceRequestsHelper.h in Headers */ = {isa = PBXBuildFile; fileRef = 520223F51D83C8D200CE0AB5 /* FBSDKDeviceRequestsHelper.h */; }; 520223F81D83C8D200CE0AB5 /* FBSDKDeviceRequestsHelper.m in Sources */ = {isa = PBXBuildFile; fileRef = 520223F61D83C8D200CE0AB5 /* FBSDKDeviceRequestsHelper.m */; }; - 5263EDBE215D97ED00FAAB0C /* FBSDKUserDataStore.h in Headers */ = {isa = PBXBuildFile; fileRef = F9169B822155A02000FA1789 /* FBSDKUserDataStore.h */; }; - 5263EDD7215D98A400FAAB0C /* FBSDKUserDataStore.h in Headers */ = {isa = PBXBuildFile; fileRef = F9169B822155A02000FA1789 /* FBSDKUserDataStore.h */; }; - 5263EDD8215D98B000FAAB0C /* FBSDKUserDataStore.m in Sources */ = {isa = PBXBuildFile; fileRef = F9169B832155A03C00FA1789 /* FBSDKUserDataStore.m */; }; - 5263EDD9215D98BC00FAAB0C /* FBSDKUserDataStore.h in Headers */ = {isa = PBXBuildFile; fileRef = F9169B822155A02000FA1789 /* FBSDKUserDataStore.h */; }; - 5263EDDA215D98BD00FAAB0C /* FBSDKUserDataStore.h in Headers */ = {isa = PBXBuildFile; fileRef = F9169B822155A02000FA1789 /* FBSDKUserDataStore.h */; }; - 5263EDDB215D98C100FAAB0C /* FBSDKUserDataStore.m in Sources */ = {isa = PBXBuildFile; fileRef = F9169B832155A03C00FA1789 /* FBSDKUserDataStore.m */; }; - 5263EDDC215D98C200FAAB0C /* FBSDKUserDataStore.m in Sources */ = {isa = PBXBuildFile; fileRef = F9169B832155A03C00FA1789 /* FBSDKUserDataStore.m */; }; 52963A76215992F400C7B252 /* FBSDKAppLinkReturnToRefererView.m in Sources */ = {isa = PBXBuildFile; fileRef = 52963A65215992F000C7B252 /* FBSDKAppLinkReturnToRefererView.m */; }; 52963A77215992F400C7B252 /* FBSDKAppLinkReturnToRefererView.m in Sources */ = {isa = PBXBuildFile; fileRef = 52963A65215992F000C7B252 /* FBSDKAppLinkReturnToRefererView.m */; }; 52963A7A215992F400C7B252 /* FBSDKAppLinkTarget.m in Sources */ = {isa = PBXBuildFile; fileRef = 52963A67215992F000C7B252 /* FBSDKAppLinkTarget.m */; }; @@ -129,6 +128,8 @@ 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 */; }; + 5C49DF9E2554C9ED00E0FD91 /* SampleUserProfile.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5C49DF8F2554C9ED00E0FD91 /* SampleUserProfile.swift */; }; + 5CD6DB3C255B1D07002C296F /* FBSDKAppEventsUtilityTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5CD6DB3B255B1D07002C296F /* FBSDKAppEventsUtilityTests.swift */; }; 5D2165E122264453004952D8 /* FBSDKAppEventsTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 5D2165E022264453004952D8 /* FBSDKAppEventsTests.m */; }; 5D2165F22229C6A3004952D8 /* FBSDKGraphRequestTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 5D2165F12229C6A3004952D8 /* FBSDKGraphRequestTests.m */; }; 5D41131A229F27DD002FF65A /* FBSDKRestrictiveDataFilterManager.m in Sources */ = {isa = PBXBuildFile; fileRef = 5D411318229F27DD002FF65A /* FBSDKRestrictiveDataFilterManager.m */; }; @@ -143,10 +144,6 @@ 5D4360E123219F8E00254DF7 /* FBSDKCrashObserver.m in Sources */ = {isa = PBXBuildFile; fileRef = 5D4360DF23219F8E00254DF7 /* FBSDKCrashObserver.m */; }; 5D4360E223219F8E00254DF7 /* FBSDKCrashObserver.m in Sources */ = {isa = PBXBuildFile; fileRef = 5D4360DF23219F8E00254DF7 /* FBSDKCrashObserver.m */; }; 5D4360E323219F8E00254DF7 /* FBSDKCrashObserver.m in Sources */ = {isa = PBXBuildFile; fileRef = 5D4360DF23219F8E00254DF7 /* FBSDKCrashObserver.m */; }; - 5D4361152321C30400254DF7 /* FBSDKCrashObserving.h in Headers */ = {isa = PBXBuildFile; fileRef = 5D4361142321C30400254DF7 /* FBSDKCrashObserving.h */; }; - 5D4361162321C30400254DF7 /* FBSDKCrashObserving.h in Headers */ = {isa = PBXBuildFile; fileRef = 5D4361142321C30400254DF7 /* FBSDKCrashObserving.h */; }; - 5D4361172321C30400254DF7 /* FBSDKCrashObserving.h in Headers */ = {isa = PBXBuildFile; fileRef = 5D4361142321C30400254DF7 /* FBSDKCrashObserving.h */; }; - 5D4361182321C30400254DF7 /* FBSDKCrashObserving.h in Headers */ = {isa = PBXBuildFile; fileRef = 5D4361142321C30400254DF7 /* FBSDKCrashObserving.h */; }; 5D497702244A3E6A00BD45C6 /* FBSDKIntegrityTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 5D497701244A3E6A00BD45C6 /* FBSDKIntegrityTests.m */; }; 5D59BFFD23A705010008CA5A /* FBSDKCrashHandlerTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 5D59BFFC23A705010008CA5A /* FBSDKCrashHandlerTests.m */; }; 5D59BFFF23A7505D0008CA5A /* FBSDKLibAnalyzerTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 5D59BFFE23A7505D0008CA5A /* FBSDKLibAnalyzerTests.m */; }; @@ -171,44 +168,8 @@ 5D90CDE72343D4D200AF326A /* FBSDKCrashShield.h in Headers */ = {isa = PBXBuildFile; fileRef = 5D90CDE52343D4D200AF326A /* FBSDKCrashShield.h */; }; 5D90CDE82343D4D200AF326A /* FBSDKCrashShield.h in Headers */ = {isa = PBXBuildFile; fileRef = 5D90CDE52343D4D200AF326A /* FBSDKCrashShield.h */; }; 5D90CDE92343D4D200AF326A /* FBSDKCrashShield.h in Headers */ = {isa = PBXBuildFile; fileRef = 5D90CDE52343D4D200AF326A /* FBSDKCrashShield.h */; }; - 5D9A703A23261D4A00BF9783 /* FBSDKCrashObserving.h in Headers */ = {isa = PBXBuildFile; fileRef = 5D4361142321C30400254DF7 /* FBSDKCrashObserving.h */; }; - 5D9A703B23261D4B00BF9783 /* FBSDKCrashObserving.h in Headers */ = {isa = PBXBuildFile; fileRef = 5D4361142321C30400254DF7 /* FBSDKCrashObserving.h */; }; - 5D9A703C23261D4C00BF9783 /* FBSDKCrashObserving.h in Headers */ = {isa = PBXBuildFile; fileRef = 5D4361142321C30400254DF7 /* FBSDKCrashObserving.h */; }; - 5D9A703D23261D4D00BF9783 /* FBSDKCrashObserving.h in Headers */ = {isa = PBXBuildFile; fileRef = 5D4361142321C30400254DF7 /* FBSDKCrashObserving.h */; }; - 5D9A704323261D6900BF9783 /* FBSDKCrashHandler.m in Sources */ = {isa = PBXBuildFile; fileRef = 5D9A703F23261D6900BF9783 /* FBSDKCrashHandler.m */; }; - 5D9A704423261D6900BF9783 /* FBSDKCrashHandler.m in Sources */ = {isa = PBXBuildFile; fileRef = 5D9A703F23261D6900BF9783 /* FBSDKCrashHandler.m */; }; - 5D9A704523261D6900BF9783 /* FBSDKCrashHandler.m in Sources */ = {isa = PBXBuildFile; fileRef = 5D9A703F23261D6900BF9783 /* FBSDKCrashHandler.m */; }; - 5D9A704623261D6900BF9783 /* FBSDKCrashHandler.m in Sources */ = {isa = PBXBuildFile; fileRef = 5D9A703F23261D6900BF9783 /* FBSDKCrashHandler.m */; }; - 5D9A704723261D6900BF9783 /* FBSDKCrashHandler.m in Sources */ = {isa = PBXBuildFile; fileRef = 5D9A703F23261D6900BF9783 /* FBSDKCrashHandler.m */; }; - 5D9A704823261D6900BF9783 /* FBSDKCrashHandler.m in Sources */ = {isa = PBXBuildFile; fileRef = 5D9A703F23261D6900BF9783 /* FBSDKCrashHandler.m */; }; - 5D9A704923261D6900BF9783 /* FBSDKCrashHandler.m in Sources */ = {isa = PBXBuildFile; fileRef = 5D9A703F23261D6900BF9783 /* FBSDKCrashHandler.m */; }; - 5D9A704A23261D6900BF9783 /* FBSDKCrashHandler.m in Sources */ = {isa = PBXBuildFile; fileRef = 5D9A703F23261D6900BF9783 /* FBSDKCrashHandler.m */; }; - 5D9A704C23261D6900BF9783 /* FBSDKCrashHandler.h in Headers */ = {isa = PBXBuildFile; fileRef = 5D9A704023261D6900BF9783 /* FBSDKCrashHandler.h */; }; - 5D9A704D23261D6900BF9783 /* FBSDKCrashHandler.h in Headers */ = {isa = PBXBuildFile; fileRef = 5D9A704023261D6900BF9783 /* FBSDKCrashHandler.h */; }; - 5D9A704E23261D6900BF9783 /* FBSDKCrashHandler.h in Headers */ = {isa = PBXBuildFile; fileRef = 5D9A704023261D6900BF9783 /* FBSDKCrashHandler.h */; }; - 5D9A704F23261D6900BF9783 /* FBSDKCrashHandler.h in Headers */ = {isa = PBXBuildFile; fileRef = 5D9A704023261D6900BF9783 /* FBSDKCrashHandler.h */; }; - 5D9A705023261D6900BF9783 /* FBSDKCrashHandler.h in Headers */ = {isa = PBXBuildFile; fileRef = 5D9A704023261D6900BF9783 /* FBSDKCrashHandler.h */; }; - 5D9A705123261D6900BF9783 /* FBSDKCrashHandler.h in Headers */ = {isa = PBXBuildFile; fileRef = 5D9A704023261D6900BF9783 /* FBSDKCrashHandler.h */; }; - 5D9A705223261D6900BF9783 /* FBSDKCrashHandler.h in Headers */ = {isa = PBXBuildFile; fileRef = 5D9A704023261D6900BF9783 /* FBSDKCrashHandler.h */; }; - 5D9A705323261D6900BF9783 /* FBSDKCrashHandler.h in Headers */ = {isa = PBXBuildFile; fileRef = 5D9A704023261D6900BF9783 /* FBSDKCrashHandler.h */; }; - 5D9A705523261D6900BF9783 /* FBSDKLibAnalyzer.h in Headers */ = {isa = PBXBuildFile; fileRef = 5D9A704123261D6900BF9783 /* FBSDKLibAnalyzer.h */; }; - 5D9A705623261D6900BF9783 /* FBSDKLibAnalyzer.h in Headers */ = {isa = PBXBuildFile; fileRef = 5D9A704123261D6900BF9783 /* FBSDKLibAnalyzer.h */; }; - 5D9A705723261D6900BF9783 /* FBSDKLibAnalyzer.h in Headers */ = {isa = PBXBuildFile; fileRef = 5D9A704123261D6900BF9783 /* FBSDKLibAnalyzer.h */; }; - 5D9A705823261D6900BF9783 /* FBSDKLibAnalyzer.h in Headers */ = {isa = PBXBuildFile; fileRef = 5D9A704123261D6900BF9783 /* FBSDKLibAnalyzer.h */; }; - 5D9A705923261D6900BF9783 /* FBSDKLibAnalyzer.h in Headers */ = {isa = PBXBuildFile; fileRef = 5D9A704123261D6900BF9783 /* FBSDKLibAnalyzer.h */; }; - 5D9A705A23261D6900BF9783 /* FBSDKLibAnalyzer.h in Headers */ = {isa = PBXBuildFile; fileRef = 5D9A704123261D6900BF9783 /* FBSDKLibAnalyzer.h */; }; - 5D9A705B23261D6900BF9783 /* FBSDKLibAnalyzer.h in Headers */ = {isa = PBXBuildFile; fileRef = 5D9A704123261D6900BF9783 /* FBSDKLibAnalyzer.h */; }; - 5D9A705C23261D6900BF9783 /* FBSDKLibAnalyzer.h in Headers */ = {isa = PBXBuildFile; fileRef = 5D9A704123261D6900BF9783 /* FBSDKLibAnalyzer.h */; }; - 5D9A705E23261D6900BF9783 /* FBSDKLibAnalyzer.m in Sources */ = {isa = PBXBuildFile; fileRef = 5D9A704223261D6900BF9783 /* FBSDKLibAnalyzer.m */; }; - 5D9A705F23261D6900BF9783 /* FBSDKLibAnalyzer.m in Sources */ = {isa = PBXBuildFile; fileRef = 5D9A704223261D6900BF9783 /* FBSDKLibAnalyzer.m */; }; - 5D9A706023261D6900BF9783 /* FBSDKLibAnalyzer.m in Sources */ = {isa = PBXBuildFile; fileRef = 5D9A704223261D6900BF9783 /* FBSDKLibAnalyzer.m */; }; - 5D9A706123261D6900BF9783 /* FBSDKLibAnalyzer.m in Sources */ = {isa = PBXBuildFile; fileRef = 5D9A704223261D6900BF9783 /* FBSDKLibAnalyzer.m */; }; - 5D9A706223261D6900BF9783 /* FBSDKLibAnalyzer.m in Sources */ = {isa = PBXBuildFile; fileRef = 5D9A704223261D6900BF9783 /* FBSDKLibAnalyzer.m */; }; - 5D9A706323261D6900BF9783 /* FBSDKLibAnalyzer.m in Sources */ = {isa = PBXBuildFile; fileRef = 5D9A704223261D6900BF9783 /* FBSDKLibAnalyzer.m */; }; - 5D9A706423261D6900BF9783 /* FBSDKLibAnalyzer.m in Sources */ = {isa = PBXBuildFile; fileRef = 5D9A704223261D6900BF9783 /* FBSDKLibAnalyzer.m */; }; - 5D9A706523261D6900BF9783 /* FBSDKLibAnalyzer.m in Sources */ = {isa = PBXBuildFile; fileRef = 5D9A704223261D6900BF9783 /* FBSDKLibAnalyzer.m */; }; 5DAB01F123A1831A005495FB /* FBSDKCrashObserverTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 5DAB01F023A1831A005495FB /* FBSDKCrashObserverTests.m */; }; - 5DAB023C23A1BA17005495FB /* FBSDKEventDeactivationTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 5DAB023B23A1BA17005495FB /* FBSDKEventDeactivationTests.m */; }; + 5DAB023C23A1BA17005495FB /* FBSDKEventDeactivationTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5DAB023B23A1BA17005495FB /* FBSDKEventDeactivationTests.swift */; }; 5DB612A823593AB600150851 /* FBSDKCrashShieldTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 5DB612A723593AB600150851 /* FBSDKCrashShieldTests.m */; }; 5DB7B07C230363190012E8CB /* FBSDKInstrumentManager.h in Headers */ = {isa = PBXBuildFile; fileRef = 5DB7B07B230363190012E8CB /* FBSDKInstrumentManager.h */; }; 5DB7B07D230363190012E8CB /* FBSDKInstrumentManager.h in Headers */ = {isa = PBXBuildFile; fileRef = 5DB7B07B230363190012E8CB /* FBSDKInstrumentManager.h */; }; @@ -223,10 +184,14 @@ 5DBC72D22373793400A9D859 /* FBSDKModelManager.h in Headers */ = {isa = PBXBuildFile; fileRef = 5DBC72D02373792A00A9D859 /* FBSDKModelManager.h */; }; 5DBC72D82373795600A9D859 /* FBSDKModelManager.mm in Sources */ = {isa = PBXBuildFile; fileRef = 5DBC72D72373795600A9D859 /* FBSDKModelManager.mm */; }; 5DBC72D92373795600A9D859 /* FBSDKModelManager.mm in Sources */ = {isa = PBXBuildFile; fileRef = 5DBC72D72373795600A9D859 /* FBSDKModelManager.mm */; }; - 5DEF22CF226A3E06004056C1 /* FBSDKBasicUtility.m in Sources */ = {isa = PBXBuildFile; fileRef = 5DEF22CE226A3E06004056C1 /* FBSDKBasicUtility.m */; }; - 5DEF22D0226A3E06004056C1 /* FBSDKBasicUtility.m in Sources */ = {isa = PBXBuildFile; fileRef = 5DEF22CE226A3E06004056C1 /* FBSDKBasicUtility.m */; }; - 5DEF22D1226A3E06004056C1 /* FBSDKBasicUtility.m in Sources */ = {isa = PBXBuildFile; fileRef = 5DEF22CE226A3E06004056C1 /* FBSDKBasicUtility.m */; }; - 5DEF22D2226A3E06004056C1 /* FBSDKBasicUtility.m in Sources */ = {isa = PBXBuildFile; fileRef = 5DEF22CE226A3E06004056C1 /* FBSDKBasicUtility.m */; }; + 5E3AFFE7258C191200BE46AB /* FBSDKAuthenticationTokenHeader.h in Headers */ = {isa = PBXBuildFile; fileRef = 5E3AFFE5258C191200BE46AB /* FBSDKAuthenticationTokenHeader.h */; }; + 5E3AFFE8258C191200BE46AB /* FBSDKAuthenticationTokenHeader.h in Headers */ = {isa = PBXBuildFile; fileRef = 5E3AFFE5258C191200BE46AB /* FBSDKAuthenticationTokenHeader.h */; }; + 5E3AFFE9258C191200BE46AB /* FBSDKAuthenticationTokenHeader.m in Sources */ = {isa = PBXBuildFile; fileRef = 5E3AFFE6258C191200BE46AB /* FBSDKAuthenticationTokenHeader.m */; }; + 5E3AFFEA258C191200BE46AB /* FBSDKAuthenticationTokenHeader.m in Sources */ = {isa = PBXBuildFile; fileRef = 5E3AFFE6258C191200BE46AB /* FBSDKAuthenticationTokenHeader.m */; }; + 5E47555D2580577C00C98E30 /* FBSDKAuthenticationTokenClaims.h in Headers */ = {isa = PBXBuildFile; fileRef = 5E47555B2580577B00C98E30 /* FBSDKAuthenticationTokenClaims.h */; }; + 5E47555E2580577C00C98E30 /* FBSDKAuthenticationTokenClaims.m in Sources */ = {isa = PBXBuildFile; fileRef = 5E47555C2580577C00C98E30 /* FBSDKAuthenticationTokenClaims.m */; }; + 5EF17EE52582DE3300FAA5D2 /* FBSDKAuthenticationTokenClaims.h in Headers */ = {isa = PBXBuildFile; fileRef = 5E47555B2580577B00C98E30 /* FBSDKAuthenticationTokenClaims.h */; }; + 5EF17F172582DE3D00FAA5D2 /* FBSDKAuthenticationTokenClaims.m in Sources */ = {isa = PBXBuildFile; fileRef = 5E47555C2580577C00C98E30 /* FBSDKAuthenticationTokenClaims.m */; }; 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 */; }; @@ -244,7 +209,7 @@ 814AC7E31D1B528900D61E6C /* FBSDKAppEventsDeviceInfo.m in Sources */ = {isa = PBXBuildFile; fileRef = 5F7064081AF7342600E42ED7 /* FBSDKAppEventsDeviceInfo.m */; }; 814AC7E41D1B528900D61E6C /* FBSDKAppEventsUtility.m in Sources */ = {isa = PBXBuildFile; fileRef = 9D0BC15A1A8D427800BE8BA4 /* FBSDKAppEventsUtility.m */; }; 814AC7E51D1B528900D61E6C /* FBSDKServerConfigurationManager.m in Sources */ = {isa = PBXBuildFile; fileRef = 89830F2A1A7805D100226ABB /* FBSDKServerConfigurationManager.m */; }; - 814AC7E61D1B528900D61E6C /* FBSDKAccessTokenCache.m in Sources */ = {isa = PBXBuildFile; fileRef = 9D32A8441A699459000A936D /* FBSDKAccessTokenCache.m */; }; + 814AC7E61D1B528900D61E6C /* FBSDKTokenCache.m in Sources */ = {isa = PBXBuildFile; fileRef = 9D32A8441A699459000A936D /* FBSDKTokenCache.m */; }; 814AC7E71D1B528900D61E6C /* FBSDKUtility.m in Sources */ = {isa = PBXBuildFile; fileRef = 89C8B1981A8D7A15009B07F5 /* FBSDKUtility.m */; }; 814AC7E81D1B528900D61E6C /* FBSDKBase64.m in Sources */ = {isa = PBXBuildFile; fileRef = 894C0B101A7021F8009137EF /* FBSDKBase64.m */; }; 814AC7E91D1B528900D61E6C /* _FBSDKTemporaryErrorRecoveryAttempter.m in Sources */ = {isa = PBXBuildFile; fileRef = 9DF18BB81A9F1A05000F6748 /* _FBSDKTemporaryErrorRecoveryAttempter.m */; }; @@ -301,7 +266,7 @@ 814AC8251D1B528900D61E6C /* FBSDKCoreKit+Internal.h in Headers */ = {isa = PBXBuildFile; fileRef = 890414731A647D2E00617215 /* FBSDKCoreKit+Internal.h */; }; 814AC8261D1B528900D61E6C /* FBSDKCoreKit.h in Headers */ = {isa = PBXBuildFile; fileRef = 9D26974C1A5DF40700143BFC /* FBSDKCoreKit.h */; settings = {ATTRIBUTES = (Public, ); }; }; 814AC8271D1B528900D61E6C /* FBSDKDeviceButton.h in Headers */ = {isa = PBXBuildFile; fileRef = 9D9E16AA1CB46C7D00C8B68F /* FBSDKDeviceButton.h */; settings = {ATTRIBUTES = (Public, ); }; }; - 814AC8291D1B528900D61E6C /* FBSDKAccessTokenCache.h in Headers */ = {isa = PBXBuildFile; fileRef = 9D32A8431A699459000A936D /* FBSDKAccessTokenCache.h */; }; + 814AC8291D1B528900D61E6C /* FBSDKTokenCache.h in Headers */ = {isa = PBXBuildFile; fileRef = 9D32A8431A699459000A936D /* FBSDKTokenCache.h */; }; 814AC82A1D1B528900D61E6C /* FBSDKAppEventsState.h in Headers */ = {isa = PBXBuildFile; fileRef = 9D0BC1641A8E892C00BE8BA4 /* FBSDKAppEventsState.h */; }; 814AC82B1D1B528900D61E6C /* FBSDKAppEvents+Internal.h in Headers */ = {isa = PBXBuildFile; fileRef = 9D0BC1531A8D23DB00BE8BA4 /* FBSDKAppEvents+Internal.h */; }; 814AC82C1D1B528900D61E6C /* FBSDKTestUsersManager.h in Headers */ = {isa = PBXBuildFile; fileRef = 9D7E7E5F1ADF038800F53E38 /* FBSDKTestUsersManager.h */; settings = {ATTRIBUTES = (Public, ); }; }; @@ -342,7 +307,7 @@ 81B71D031D19C87400933E93 /* FBSDKServerConfigurationManager.m in Sources */ = {isa = PBXBuildFile; fileRef = 89830F2A1A7805D100226ABB /* FBSDKServerConfigurationManager.m */; }; 81B71D041D19C87400933E93 /* FBSDKGraphRequest.m in Sources */ = {isa = PBXBuildFile; fileRef = 9DC658941A6EE5C500B85AAF /* FBSDKGraphRequest.m */; }; 81B71D051D19C87400933E93 /* FBSDKContainerViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 9D195CC71B9FE2E000BD6BEC /* FBSDKContainerViewController.m */; }; - 81B71D061D19C87400933E93 /* FBSDKAccessTokenCache.m in Sources */ = {isa = PBXBuildFile; fileRef = 9D32A8441A699459000A936D /* FBSDKAccessTokenCache.m */; }; + 81B71D061D19C87400933E93 /* FBSDKTokenCache.m in Sources */ = {isa = PBXBuildFile; fileRef = 9D32A8441A699459000A936D /* FBSDKTokenCache.m */; }; 81B71D071D19C87400933E93 /* FBSDKCrypto.m in Sources */ = {isa = PBXBuildFile; fileRef = 894C0B081A702194009137EF /* FBSDKCrypto.m */; }; 81B71D081D19C87400933E93 /* FBSDKAppEventsState.m in Sources */ = {isa = PBXBuildFile; fileRef = 9D0BC1651A8E892C00BE8BA4 /* FBSDKAppEventsState.m */; }; 81B71D091D19C87400933E93 /* FBSDKCloseIcon.m in Sources */ = {isa = PBXBuildFile; fileRef = 89D652831A855A6000BB651C /* FBSDKCloseIcon.m */; }; @@ -353,7 +318,6 @@ 81B71D0E1D19C87400933E93 /* FBSDKTestUsersManager.m in Sources */ = {isa = PBXBuildFile; fileRef = 9D7E7E601ADF038800F53E38 /* FBSDKTestUsersManager.m */; }; 81B71D0F1D19C87400933E93 /* FBSDKMeasurementEventListener.m in Sources */ = {isa = PBXBuildFile; fileRef = 7EB63D9D1A9BE730003A7AED /* FBSDKMeasurementEventListener.m */; }; 81B71D101D19C87400933E93 /* FBSDKProfilePictureView.m in Sources */ = {isa = PBXBuildFile; fileRef = 899C3CF71A8BF73A00EA8658 /* FBSDKProfilePictureView.m */; }; - 81B71D111D19C87400933E93 /* FBSDKTriStateBOOL.m in Sources */ = {isa = PBXBuildFile; fileRef = 89D05A941AA0E89B00609300 /* FBSDKTriStateBOOL.m */; }; 81B71D121D19C87400933E93 /* FBSDKLogger.m in Sources */ = {isa = PBXBuildFile; fileRef = 9D6999FD1A76E166003AE384 /* FBSDKLogger.m */; }; 81B71D131D19C87400933E93 /* FBSDKApplicationDelegate.m in Sources */ = {isa = PBXBuildFile; fileRef = 9D34A11E1A5F038300C37317 /* FBSDKApplicationDelegate.m */; }; 81B71D141D19C87400933E93 /* FBSDKDynamicFrameworkLoader.m in Sources */ = {isa = PBXBuildFile; fileRef = 81C969311C114723002FC037 /* FBSDKDynamicFrameworkLoader.m */; }; @@ -396,11 +360,11 @@ 81B71D411D19C87400933E93 /* FBSDKErrorConfiguration.m in Sources */ = {isa = PBXBuildFile; fileRef = 9D3AF44F1A9EA4BE00EEF724 /* FBSDKErrorConfiguration.m */; }; 81B71D421D19C87400933E93 /* FBSDKKeychainStoreViaBundleID.m in Sources */ = {isa = PBXBuildFile; fileRef = 9DE1F3CC1A89D9CD00B54D98 /* FBSDKKeychainStoreViaBundleID.m */; }; 81B71D431D19C87400933E93 /* FBSDKServerConfiguration.m in Sources */ = {isa = PBXBuildFile; fileRef = 89830F2E1A7805E100226ABB /* FBSDKServerConfiguration.m */; }; - 81B71D441D19C87400933E93 /* FBSDKMaleSilhouetteIcon.m in Sources */ = {isa = PBXBuildFile; fileRef = 899C3D011A8C1ED200EA8658 /* FBSDKMaleSilhouetteIcon.m */; }; + 81B71D441D19C87400933E93 /* FBSDKHumanSilhouetteIcon.m in Sources */ = {isa = PBXBuildFile; fileRef = 899C3D011A8C1ED200EA8658 /* FBSDKHumanSilhouetteIcon.m */; }; 81B71D461D19C87400933E93 /* SafariServices.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 9894BFFA1B73D8B300FBA6DB /* SafariServices.framework */; settings = {ATTRIBUTES = (Required, ); }; }; 81B71D481D19C87400933E93 /* FBSDKAppLinkUtility.h in Headers */ = {isa = PBXBuildFile; fileRef = 7E30916C1AA907BD004E91D5 /* FBSDKAppLinkUtility.h */; settings = {ATTRIBUTES = (Public, ); }; }; 81B71D491D19C87400933E93 /* FBSDKAppLinkResolver.h in Headers */ = {isa = PBXBuildFile; fileRef = 7E5557361A8D833100344F86 /* FBSDKAppLinkResolver.h */; settings = {ATTRIBUTES = (Public, ); }; }; - 81B71D4A1D19C87400933E93 /* FBSDKAccessTokenCaching.h in Headers */ = {isa = PBXBuildFile; fileRef = 9D32A83B1A69941A000A936D /* FBSDKAccessTokenCaching.h */; }; + 81B71D4A1D19C87400933E93 /* FBSDKTokenCaching.h in Headers */ = {isa = PBXBuildFile; fileRef = 9D32A83B1A69941A000A936D /* FBSDKTokenCaching.h */; }; 81B71D4B1D19C87400933E93 /* FBSDKBridgeAPIResponse.h in Headers */ = {isa = PBXBuildFile; fileRef = 894C0ADD1A6F1D1B009137EF /* FBSDKBridgeAPIResponse.h */; }; 81B71D4C1D19C87400933E93 /* FBSDKCoreKit+Internal.h in Headers */ = {isa = PBXBuildFile; fileRef = 890414731A647D2E00617215 /* FBSDKCoreKit+Internal.h */; }; 81B71D4D1D19C87400933E93 /* FBSDKMeasurementEventListener.h in Headers */ = {isa = PBXBuildFile; fileRef = 7EB63D9E1A9BE730003A7AED /* FBSDKMeasurementEventListener.h */; }; @@ -419,7 +383,7 @@ 81B71D5C1D19C87400933E93 /* FBSDKContainerViewController.h in Headers */ = {isa = PBXBuildFile; fileRef = 9D195CC61B9FE2E000BD6BEC /* FBSDKContainerViewController.h */; }; 81B71D5D1D19C87400933E93 /* FBSDKAppEventsState.h in Headers */ = {isa = PBXBuildFile; fileRef = 9D0BC1641A8E892C00BE8BA4 /* FBSDKAppEventsState.h */; }; 81B71D5E1D19C87400933E93 /* FBSDKBridgeAPIProtocolType.h in Headers */ = {isa = PBXBuildFile; fileRef = 894C0AEC1A6F1DAB009137EF /* FBSDKBridgeAPIProtocolType.h */; }; - 81B71D5F1D19C87400933E93 /* FBSDKMaleSilhouetteIcon.h in Headers */ = {isa = PBXBuildFile; fileRef = 899C3D001A8C1ED200EA8658 /* FBSDKMaleSilhouetteIcon.h */; }; + 81B71D5F1D19C87400933E93 /* FBSDKHumanSilhouetteIcon.h in Headers */ = {isa = PBXBuildFile; fileRef = 899C3D001A8C1ED200EA8658 /* FBSDKHumanSilhouetteIcon.h */; }; 81B71D601D19C87400933E93 /* FBSDKMutableCopying.h in Headers */ = {isa = PBXBuildFile; fileRef = 890414871A64893100617215 /* FBSDKMutableCopying.h */; settings = {ATTRIBUTES = (Public, ); }; }; 81B71D611D19C87400933E93 /* FBSDKErrorRecoveryAttempter.h in Headers */ = {isa = PBXBuildFile; fileRef = 9D3AF4871A9EFA0300EEF724 /* FBSDKErrorRecoveryAttempter.h */; }; 81B71D621D19C87400933E93 /* FBSDKPaymentObserver.h in Headers */ = {isa = PBXBuildFile; fileRef = 9D0BC14A1A8D236200BE8BA4 /* FBSDKPaymentObserver.h */; }; @@ -428,7 +392,6 @@ 81B71D651D19C87400933E93 /* FBSDKBridgeAPIRequest.h in Headers */ = {isa = PBXBuildFile; fileRef = 894C0ACF1A6F15F7009137EF /* FBSDKBridgeAPIRequest.h */; }; 81B71D671D19C87400933E93 /* FBSDKWebDialogView.h in Headers */ = {isa = PBXBuildFile; fileRef = 89FB8C481A842A8A003CAE60 /* FBSDKWebDialogView.h */; }; 81B71D681D19C87400933E93 /* FBSDKServerConfiguration+Internal.h in Headers */ = {isa = PBXBuildFile; fileRef = 8917C4AB1B7A46C800B0B96B /* FBSDKServerConfiguration+Internal.h */; }; - 81B71D691D19C87400933E93 /* FBSDKTriStateBOOL.h in Headers */ = {isa = PBXBuildFile; fileRef = 89D05A931AA0E89B00609300 /* FBSDKTriStateBOOL.h */; }; 81B71D6A1D19C87400933E93 /* FBSDKCloseIcon.h in Headers */ = {isa = PBXBuildFile; fileRef = 89D652821A855A6000BB651C /* FBSDKCloseIcon.h */; }; 81B71D6B1D19C87400933E93 /* _FBSDKTemporaryErrorRecoveryAttempter.h in Headers */ = {isa = PBXBuildFile; fileRef = 9DF18BB71A9F1A05000F6748 /* _FBSDKTemporaryErrorRecoveryAttempter.h */; }; 81B71D6D1D19C87400933E93 /* FBSDKProfile.h in Headers */ = {isa = PBXBuildFile; fileRef = 9DA81AF41AA4EF3300B9FE0B /* FBSDKProfile.h */; settings = {ATTRIBUTES = (Public, ); }; }; @@ -474,7 +437,7 @@ 81B71D9A1D19C87400933E93 /* FBSDKKeychainStore.h in Headers */ = {isa = PBXBuildFile; fileRef = 9D32A83C1A69941A000A936D /* FBSDKKeychainStore.h */; }; 81B71D9B1D19C87400933E93 /* FBSDKTestUsersManager.h in Headers */ = {isa = PBXBuildFile; fileRef = 9D7E7E5F1ADF038800F53E38 /* FBSDKTestUsersManager.h */; settings = {ATTRIBUTES = (Public, ); }; }; 81B71D9C1D19C87400933E93 /* FBSDKAppEventsDeviceInfo.h in Headers */ = {isa = PBXBuildFile; fileRef = 5F7063FA1AF733F300E42ED7 /* FBSDKAppEventsDeviceInfo.h */; }; - 81B71D9D1D19C87400933E93 /* FBSDKAccessTokenCache.h in Headers */ = {isa = PBXBuildFile; fileRef = 9D32A8431A699459000A936D /* FBSDKAccessTokenCache.h */; }; + 81B71D9D1D19C87400933E93 /* FBSDKTokenCache.h in Headers */ = {isa = PBXBuildFile; fileRef = 9D32A8431A699459000A936D /* FBSDKTokenCache.h */; }; 81B71DCE1D19C8BF00933E93 /* UIKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 893F449F1A64450C001DB0B6 /* UIKit.framework */; }; 81B71DD21D19C8CF00933E93 /* CoreGraphics.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 893F44A11A644512001DB0B6 /* CoreGraphics.framework */; }; 81C969321C114723002FC037 /* FBSDKDynamicFrameworkLoader.h in Headers */ = {isa = PBXBuildFile; fileRef = 81C969301C114723002FC037 /* FBSDKDynamicFrameworkLoader.h */; }; @@ -512,7 +475,7 @@ 894C0B111A7021F8009137EF /* FBSDKBase64.h in Headers */ = {isa = PBXBuildFile; fileRef = 894C0B0F1A7021F8009137EF /* FBSDKBase64.h */; }; 894C0B121A7021F8009137EF /* FBSDKBase64.m in Sources */ = {isa = PBXBuildFile; fileRef = 894C0B101A7021F8009137EF /* FBSDKBase64.m */; }; 894C0B3A1A702EBE009137EF /* FBSDKBridgeAPIProtocolNativeV1Tests.m in Sources */ = {isa = PBXBuildFile; fileRef = 894C0B391A702EBE009137EF /* FBSDKBridgeAPIProtocolNativeV1Tests.m */; }; - 894C0B4A1A70524F009137EF /* FBSDKBase64Tests.m in Sources */ = {isa = PBXBuildFile; fileRef = 894C0B491A70524F009137EF /* FBSDKBase64Tests.m */; }; + 894C0B4A1A70524F009137EF /* FBSDKBase64Tests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 894C0B491A70524F009137EF /* FBSDKBase64Tests.swift */; }; 894C0B611A7150AC009137EF /* FBSDKError.h in Headers */ = {isa = PBXBuildFile; fileRef = 894C0B5F1A7150AC009137EF /* FBSDKError.h */; }; 894C0B621A7150AC009137EF /* FBSDKError.m in Sources */ = {isa = PBXBuildFile; fileRef = 894C0B601A7150AC009137EF /* FBSDKError.m */; }; 894C0B681A7150CC009137EF /* FBSDKConstants.h in Headers */ = {isa = PBXBuildFile; fileRef = 894C0B661A7150CC009137EF /* FBSDKConstants.h */; settings = {ATTRIBUTES = (Public, ); }; }; @@ -530,13 +493,11 @@ 89830F341A78060A00226ABB /* FBSDKDialogConfiguration.m in Sources */ = {isa = PBXBuildFile; fileRef = 89830F321A78060A00226ABB /* FBSDKDialogConfiguration.m */; }; 899C3CF81A8BF73A00EA8658 /* FBSDKProfilePictureView.h in Headers */ = {isa = PBXBuildFile; fileRef = 899C3CF61A8BF73A00EA8658 /* FBSDKProfilePictureView.h */; settings = {ATTRIBUTES = (Public, ); }; }; 899C3CF91A8BF73A00EA8658 /* FBSDKProfilePictureView.m in Sources */ = {isa = PBXBuildFile; fileRef = 899C3CF71A8BF73A00EA8658 /* FBSDKProfilePictureView.m */; }; - 899C3D021A8C1ED200EA8658 /* FBSDKMaleSilhouetteIcon.h in Headers */ = {isa = PBXBuildFile; fileRef = 899C3D001A8C1ED200EA8658 /* FBSDKMaleSilhouetteIcon.h */; }; - 899C3D031A8C1ED200EA8658 /* FBSDKMaleSilhouetteIcon.m in Sources */ = {isa = PBXBuildFile; fileRef = 899C3D011A8C1ED200EA8658 /* FBSDKMaleSilhouetteIcon.m */; }; + 899C3D021A8C1ED200EA8658 /* FBSDKHumanSilhouetteIcon.h in Headers */ = {isa = PBXBuildFile; fileRef = 899C3D001A8C1ED200EA8658 /* FBSDKHumanSilhouetteIcon.h */; }; + 899C3D031A8C1ED200EA8658 /* FBSDKHumanSilhouetteIcon.m in Sources */ = {isa = PBXBuildFile; fileRef = 899C3D011A8C1ED200EA8658 /* FBSDKHumanSilhouetteIcon.m */; }; 89C8B1991A8D7A15009B07F5 /* FBSDKUtility.h in Headers */ = {isa = PBXBuildFile; fileRef = 89C8B1971A8D7A15009B07F5 /* FBSDKUtility.h */; settings = {ATTRIBUTES = (Public, ); }; }; 89C8B19A1A8D7A15009B07F5 /* FBSDKUtility.m in Sources */ = {isa = PBXBuildFile; fileRef = 89C8B1981A8D7A15009B07F5 /* FBSDKUtility.m */; }; - 89C8B19C1A8D7A27009B07F5 /* FBSDKUtilityTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 89C8B19B1A8D7A27009B07F5 /* FBSDKUtilityTests.m */; }; - 89D05A951AA0E89B00609300 /* FBSDKTriStateBOOL.h in Headers */ = {isa = PBXBuildFile; fileRef = 89D05A931AA0E89B00609300 /* FBSDKTriStateBOOL.h */; }; - 89D05A961AA0E89B00609300 /* FBSDKTriStateBOOL.m in Sources */ = {isa = PBXBuildFile; fileRef = 89D05A941AA0E89B00609300 /* FBSDKTriStateBOOL.m */; }; + 89C8B19C1A8D7A27009B07F5 /* FBSDKUtilityTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 89C8B19B1A8D7A27009B07F5 /* FBSDKUtilityTests.swift */; }; 89D05AA91AA1134000609300 /* FBSDKAudioResourceLoader.h in Headers */ = {isa = PBXBuildFile; fileRef = 89D05AA71AA1134000609300 /* FBSDKAudioResourceLoader.h */; }; 89D05AAA1AA1134000609300 /* FBSDKAudioResourceLoader.m in Sources */ = {isa = PBXBuildFile; fileRef = 89D05AA81AA1134000609300 /* FBSDKAudioResourceLoader.m */; }; 89D4AE861A7FFEFC00DB8C72 /* FBSDKBridgeAPIRequest+Private.h in Headers */ = {isa = PBXBuildFile; fileRef = 89D4AE851A7FFEFC00DB8C72 /* FBSDKBridgeAPIRequest+Private.h */; }; @@ -584,11 +545,11 @@ 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 */; }; - 9D32A8401A69941A000A936D /* FBSDKAccessTokenCaching.h in Headers */ = {isa = PBXBuildFile; fileRef = 9D32A83B1A69941A000A936D /* FBSDKAccessTokenCaching.h */; }; + 9D32A8401A69941A000A936D /* FBSDKTokenCaching.h in Headers */ = {isa = PBXBuildFile; fileRef = 9D32A83B1A69941A000A936D /* FBSDKTokenCaching.h */; }; 9D32A8411A69941A000A936D /* FBSDKKeychainStore.h in Headers */ = {isa = PBXBuildFile; fileRef = 9D32A83C1A69941A000A936D /* FBSDKKeychainStore.h */; }; 9D32A8421A69941A000A936D /* FBSDKKeychainStore.m in Sources */ = {isa = PBXBuildFile; fileRef = 9D32A83D1A69941A000A936D /* FBSDKKeychainStore.m */; }; - 9D32A8451A699459000A936D /* FBSDKAccessTokenCache.h in Headers */ = {isa = PBXBuildFile; fileRef = 9D32A8431A699459000A936D /* FBSDKAccessTokenCache.h */; }; - 9D32A8461A699459000A936D /* FBSDKAccessTokenCache.m in Sources */ = {isa = PBXBuildFile; fileRef = 9D32A8441A699459000A936D /* FBSDKAccessTokenCache.m */; }; + 9D32A8451A699459000A936D /* FBSDKTokenCache.h in Headers */ = {isa = PBXBuildFile; fileRef = 9D32A8431A699459000A936D /* FBSDKTokenCache.h */; }; + 9D32A8461A699459000A936D /* FBSDKTokenCache.m in Sources */ = {isa = PBXBuildFile; fileRef = 9D32A8441A699459000A936D /* FBSDKTokenCache.m */; }; 9D34A11F1A5F038300C37317 /* FBSDKApplicationDelegate.h in Headers */ = {isa = PBXBuildFile; fileRef = 9D34A11D1A5F038300C37317 /* FBSDKApplicationDelegate.h */; settings = {ATTRIBUTES = (Public, ); }; }; 9D34A1201A5F038300C37317 /* FBSDKApplicationDelegate.m in Sources */ = {isa = PBXBuildFile; fileRef = 9D34A11E1A5F038300C37317 /* FBSDKApplicationDelegate.m */; }; 9D3AF4501A9EA4BE00EEF724 /* FBSDKErrorConfiguration.h in Headers */ = {isa = PBXBuildFile; fileRef = 9D3AF44E1A9EA4BE00EEF724 /* FBSDKErrorConfiguration.h */; }; @@ -617,8 +578,8 @@ 9D6DEEB01BC2379C001A94ED /* FBSDKTimeSpentData.m in Sources */ = {isa = PBXBuildFile; fileRef = 9D0BC14D1A8D236200BE8BA4 /* FBSDKTimeSpentData.m */; }; 9D6DEEB11BC23834001A94ED /* FBSDKAppEventsState.m in Sources */ = {isa = PBXBuildFile; fileRef = 9D0BC1651A8E892C00BE8BA4 /* FBSDKAppEventsState.m */; }; 9D6DEEB21BC23838001A94ED /* FBSDKAppEventsState.h in Headers */ = {isa = PBXBuildFile; fileRef = 9D0BC1641A8E892C00BE8BA4 /* FBSDKAppEventsState.h */; }; - 9D6DEEB31BC23850001A94ED /* FBSDKAccessTokenCache.m in Sources */ = {isa = PBXBuildFile; fileRef = 9D32A8441A699459000A936D /* FBSDKAccessTokenCache.m */; }; - 9D6DEEB41BC23855001A94ED /* FBSDKAccessTokenCache.h in Headers */ = {isa = PBXBuildFile; fileRef = 9D32A8431A699459000A936D /* FBSDKAccessTokenCache.h */; }; + 9D6DEEB31BC23850001A94ED /* FBSDKTokenCache.m in Sources */ = {isa = PBXBuildFile; fileRef = 9D32A8441A699459000A936D /* FBSDKTokenCache.m */; }; + 9D6DEEB41BC23855001A94ED /* FBSDKTokenCache.h in Headers */ = {isa = PBXBuildFile; fileRef = 9D32A8431A699459000A936D /* FBSDKTokenCache.h */; }; 9D6DEEB51BC23862001A94ED /* FBSDKAppEventsStateManager.m in Sources */ = {isa = PBXBuildFile; fileRef = 9D0BC15E1A8D428700BE8BA4 /* FBSDKAppEventsStateManager.m */; }; 9D6DEEB61BC2386C001A94ED /* FBSDKAppEventsStateManager.h in Headers */ = {isa = PBXBuildFile; fileRef = 9D0BC15D1A8D428700BE8BA4 /* FBSDKAppEventsStateManager.h */; }; 9D6DEEB71BC23890001A94ED /* FBSDKErrorRecoveryAttempter.m in Sources */ = {isa = PBXBuildFile; fileRef = 9D3AF4881A9EFA0300EEF724 /* FBSDKErrorRecoveryAttempter.m */; }; @@ -727,6 +688,7 @@ 9DF2A4001A70572B00DFB2FD /* FBSDKGraphRequestMetadata.m in Sources */ = {isa = PBXBuildFile; fileRef = 9DF2A3FE1A70572B00DFB2FD /* FBSDKGraphRequestMetadata.m */; }; ADEA17731B7ECA1A0070EDC0 /* FBSDKMonotonicTime.h in Headers */ = {isa = PBXBuildFile; fileRef = ADEA17711B7ECA1A0070EDC0 /* FBSDKMonotonicTime.h */; }; ADEA17741B7ECA1A0070EDC0 /* FBSDKMonotonicTime.m in Sources */ = {isa = PBXBuildFile; fileRef = ADEA17721B7ECA1A0070EDC0 /* FBSDKMonotonicTime.m */; }; + B3C20ACD250B100F00DEC008 /* FBSDKAccessToken+Internal.h in Headers */ = {isa = PBXBuildFile; fileRef = B3C20ACC250B100F00DEC008 /* FBSDKAccessToken+Internal.h */; }; BF1C64E624039AD50052C580 /* FBSDKViewHierarchyTests.m in Sources */ = {isa = PBXBuildFile; fileRef = BF1C64E524039AD50052C580 /* FBSDKViewHierarchyTests.m */; }; BF247C822374E1B200A522C0 /* FBSDKSuggestedEventsIndexer.h in Headers */ = {isa = PBXBuildFile; fileRef = BF247C802374E1B100A522C0 /* FBSDKSuggestedEventsIndexer.h */; }; BF247C832374E1B200A522C0 /* FBSDKSuggestedEventsIndexer.m in Sources */ = {isa = PBXBuildFile; fileRef = BF247C812374E1B200A522C0 /* FBSDKSuggestedEventsIndexer.m */; }; @@ -745,7 +707,7 @@ C4FC99F1202CD56D0038C5ED /* FBSDKHybridAppEventsScriptMessageHandler.h in Headers */ = {isa = PBXBuildFile; fileRef = C4FC99D7202CD5590038C5ED /* FBSDKHybridAppEventsScriptMessageHandler.h */; }; C4FC99F2202CD56D0038C5ED /* FBSDKHybridAppEventsScriptMessageHandler.m in Sources */ = {isa = PBXBuildFile; fileRef = C4FC99D8202CD5590038C5ED /* FBSDKHybridAppEventsScriptMessageHandler.m */; }; C51121CB20A27EF50041DC94 /* FBSDKEventBindingTests.m in Sources */ = {isa = PBXBuildFile; fileRef = C51121C920A27EF50041DC94 /* FBSDKEventBindingTests.m */; }; - C51121CC20A27EF50041DC94 /* FBSDKSampleEventBinding.m in Sources */ = {isa = PBXBuildFile; fileRef = C51121CA20A27EF50041DC94 /* FBSDKSampleEventBinding.m */; }; + C51121CC20A27EF50041DC94 /* FBSDKSampleEventBinding.swift in Sources */ = {isa = PBXBuildFile; fileRef = C51121CA20A27EF50041DC94 /* FBSDKSampleEventBinding.swift */; }; C51122A020A4BCEB0041DC94 /* FBSDKApplicationDelegateTests.m in Sources */ = {isa = PBXBuildFile; fileRef = C511229F20A4BCEB0041DC94 /* FBSDKApplicationDelegateTests.m */; }; C5188DF9222F388400F4D8BC /* FBSDKApplicationObserving.h in Headers */ = {isa = PBXBuildFile; fileRef = C5188DF8222F388400F4D8BC /* FBSDKApplicationObserving.h */; }; C5188DFA222F388400F4D8BC /* FBSDKApplicationObserving.h in Headers */ = {isa = PBXBuildFile; fileRef = C5188DF8222F388400F4D8BC /* FBSDKApplicationObserving.h */; }; @@ -791,33 +753,22 @@ C5696FA8209CCEB4009C931F /* FBSDKSwizzler.m in Sources */ = {isa = PBXBuildFile; fileRef = C5696FA2209CCEB4009C931F /* FBSDKSwizzler.m */; }; C5696FA9209CCEB4009C931F /* FBSDKSwizzler.m in Sources */ = {isa = PBXBuildFile; fileRef = C5696FA2209CCEB4009C931F /* FBSDKSwizzler.m */; }; C5696FAA209CCEB4009C931F /* FBSDKSwizzler.m in Sources */ = {isa = PBXBuildFile; fileRef = C5696FA2209CCEB4009C931F /* FBSDKSwizzler.m */; }; - C5C4B3BA2276B54B00CA3706 /* FBSDKBasicUtility.m in Sources */ = {isa = PBXBuildFile; fileRef = 5DEF22CE226A3E06004056C1 /* FBSDKBasicUtility.m */; }; - C5C4B3C22276B67200CA3706 /* FBSDKBasicUtility.m in Sources */ = {isa = PBXBuildFile; fileRef = 5DEF22CE226A3E06004056C1 /* FBSDKBasicUtility.m */; }; - C5C4B3E82276B88600CA3706 /* FBSDKTypeUtility.h in Headers */ = {isa = PBXBuildFile; fileRef = C5C4B3E32276B88600CA3706 /* FBSDKTypeUtility.h */; }; - C5C4B3E92276B88600CA3706 /* FBSDKTypeUtility.h in Headers */ = {isa = PBXBuildFile; fileRef = C5C4B3E32276B88600CA3706 /* FBSDKTypeUtility.h */; }; - C5C4B3EA2276B88600CA3706 /* FBSDKTypeUtility.h in Headers */ = {isa = PBXBuildFile; fileRef = C5C4B3E32276B88600CA3706 /* FBSDKTypeUtility.h */; }; - C5C4B3EB2276B88600CA3706 /* FBSDKTypeUtility.h in Headers */ = {isa = PBXBuildFile; fileRef = C5C4B3E32276B88600CA3706 /* FBSDKTypeUtility.h */; }; - C5C4B3EC2276B88600CA3706 /* FBSDKTypeUtility.h in Headers */ = {isa = PBXBuildFile; fileRef = C5C4B3E32276B88600CA3706 /* FBSDKTypeUtility.h */; }; - C5C4B3ED2276B88600CA3706 /* FBSDKBasicUtility.h in Headers */ = {isa = PBXBuildFile; fileRef = C5C4B3E42276B88600CA3706 /* FBSDKBasicUtility.h */; }; - C5C4B3EE2276B88600CA3706 /* FBSDKBasicUtility.h in Headers */ = {isa = PBXBuildFile; fileRef = C5C4B3E42276B88600CA3706 /* FBSDKBasicUtility.h */; }; - C5C4B3EF2276B88600CA3706 /* FBSDKBasicUtility.h in Headers */ = {isa = PBXBuildFile; fileRef = C5C4B3E42276B88600CA3706 /* FBSDKBasicUtility.h */; }; - C5C4B3F02276B88600CA3706 /* FBSDKBasicUtility.h in Headers */ = {isa = PBXBuildFile; fileRef = C5C4B3E42276B88600CA3706 /* FBSDKBasicUtility.h */; }; - C5C4B3F12276B88600CA3706 /* FBSDKBasicUtility.h in Headers */ = {isa = PBXBuildFile; fileRef = C5C4B3E42276B88600CA3706 /* FBSDKBasicUtility.h */; }; - C5C4B3F22276B88600CA3706 /* FBSDKBasicUtility.h in Headers */ = {isa = PBXBuildFile; fileRef = C5C4B3E42276B88600CA3706 /* FBSDKBasicUtility.h */; }; - C5C4B3F32276B88600CA3706 /* FBSDKTypeUtility.m in Sources */ = {isa = PBXBuildFile; fileRef = C5C4B3E52276B88600CA3706 /* FBSDKTypeUtility.m */; }; - C5C4B3F42276B88600CA3706 /* FBSDKTypeUtility.m in Sources */ = {isa = PBXBuildFile; fileRef = C5C4B3E52276B88600CA3706 /* FBSDKTypeUtility.m */; }; - C5C4B3F62276B88600CA3706 /* FBSDKTypeUtility.m in Sources */ = {isa = PBXBuildFile; fileRef = C5C4B3E52276B88600CA3706 /* FBSDKTypeUtility.m */; }; - C5C4B3F72276B88600CA3706 /* FBSDKTypeUtility.m in Sources */ = {isa = PBXBuildFile; fileRef = C5C4B3E52276B88600CA3706 /* FBSDKTypeUtility.m */; }; - C5C4B3F82276B88600CA3706 /* FBSDKTypeUtility.m in Sources */ = {isa = PBXBuildFile; fileRef = C5C4B3E52276B88600CA3706 /* FBSDKTypeUtility.m */; }; - C5C4B3F92276B88600CA3706 /* FBSDKTypeUtility.m in Sources */ = {isa = PBXBuildFile; fileRef = C5C4B3E52276B88600CA3706 /* FBSDKTypeUtility.m */; }; - C5C4B4D72276BA5100CA3706 /* FBSDKTypeUtility.m in Sources */ = {isa = PBXBuildFile; fileRef = C5C4B3E52276B88600CA3706 /* FBSDKTypeUtility.m */; }; - C5C4B4D82276BA5100CA3706 /* FBSDKBasicUtility.m in Sources */ = {isa = PBXBuildFile; fileRef = 5DEF22CE226A3E06004056C1 /* FBSDKBasicUtility.m */; }; - C5C4B4D92276BA7000CA3706 /* FBSDKBasicUtility.h in Headers */ = {isa = PBXBuildFile; fileRef = C5C4B3E42276B88600CA3706 /* FBSDKBasicUtility.h */; }; - C5C4B4DA2276BA7000CA3706 /* FBSDKTypeUtility.h in Headers */ = {isa = PBXBuildFile; fileRef = C5C4B3E32276B88600CA3706 /* FBSDKTypeUtility.h */; }; - C5C4B4DF2276BA8D00CA3706 /* FBSDKTypeUtility.m in Sources */ = {isa = PBXBuildFile; fileRef = C5C4B3E52276B88600CA3706 /* FBSDKTypeUtility.m */; }; - C5C4B4E02276BA8D00CA3706 /* FBSDKBasicUtility.m in Sources */ = {isa = PBXBuildFile; fileRef = 5DEF22CE226A3E06004056C1 /* FBSDKBasicUtility.m */; }; - C5C4B4E42276BA8D00CA3706 /* FBSDKBasicUtility.h in Headers */ = {isa = PBXBuildFile; fileRef = C5C4B3E42276B88600CA3706 /* FBSDKBasicUtility.h */; }; - C5C4B4E52276BA8D00CA3706 /* FBSDKTypeUtility.h in Headers */ = {isa = PBXBuildFile; fileRef = C5C4B3E32276B88600CA3706 /* FBSDKTypeUtility.h */; }; + C57044CF24E26678009637AD /* FBSDKUserDataStore.h in Headers */ = {isa = PBXBuildFile; fileRef = C57044CD24E26678009637AD /* FBSDKUserDataStore.h */; }; + C57044D024E26678009637AD /* FBSDKUserDataStore.h in Headers */ = {isa = PBXBuildFile; fileRef = C57044CD24E26678009637AD /* FBSDKUserDataStore.h */; }; + C57044D124E26678009637AD /* FBSDKUserDataStore.h in Headers */ = {isa = PBXBuildFile; fileRef = C57044CD24E26678009637AD /* FBSDKUserDataStore.h */; }; + C57044D224E26678009637AD /* FBSDKUserDataStore.h in Headers */ = {isa = PBXBuildFile; fileRef = C57044CD24E26678009637AD /* FBSDKUserDataStore.h */; }; + C57044D324E26678009637AD /* FBSDKUserDataStore.h in Headers */ = {isa = PBXBuildFile; fileRef = C57044CD24E26678009637AD /* FBSDKUserDataStore.h */; settings = {ATTRIBUTES = (Public, ); }; }; + C57044D424E26678009637AD /* FBSDKUserDataStore.h in Headers */ = {isa = PBXBuildFile; fileRef = C57044CD24E26678009637AD /* FBSDKUserDataStore.h */; }; + C57044D524E26678009637AD /* FBSDKUserDataStore.h in Headers */ = {isa = PBXBuildFile; fileRef = C57044CD24E26678009637AD /* FBSDKUserDataStore.h */; }; + C57044D624E26678009637AD /* FBSDKUserDataStore.h in Headers */ = {isa = PBXBuildFile; fileRef = C57044CD24E26678009637AD /* FBSDKUserDataStore.h */; }; + C57044D724E26678009637AD /* FBSDKUserDataStore.m in Sources */ = {isa = PBXBuildFile; fileRef = C57044CE24E26678009637AD /* FBSDKUserDataStore.m */; }; + C57044D824E26678009637AD /* FBSDKUserDataStore.m in Sources */ = {isa = PBXBuildFile; fileRef = C57044CE24E26678009637AD /* FBSDKUserDataStore.m */; }; + C57044D924E26678009637AD /* FBSDKUserDataStore.m in Sources */ = {isa = PBXBuildFile; fileRef = C57044CE24E26678009637AD /* FBSDKUserDataStore.m */; }; + C57044DA24E26678009637AD /* FBSDKUserDataStore.m in Sources */ = {isa = PBXBuildFile; fileRef = C57044CE24E26678009637AD /* FBSDKUserDataStore.m */; }; + C57044DB24E26678009637AD /* FBSDKUserDataStore.m in Sources */ = {isa = PBXBuildFile; fileRef = C57044CE24E26678009637AD /* FBSDKUserDataStore.m */; }; + C57044DC24E26678009637AD /* FBSDKUserDataStore.m in Sources */ = {isa = PBXBuildFile; fileRef = C57044CE24E26678009637AD /* FBSDKUserDataStore.m */; }; + C57044DD24E26678009637AD /* FBSDKUserDataStore.m in Sources */ = {isa = PBXBuildFile; fileRef = C57044CE24E26678009637AD /* FBSDKUserDataStore.m */; }; + C57044DE24E26678009637AD /* FBSDKUserDataStore.m in Sources */ = {isa = PBXBuildFile; fileRef = C57044CE24E26678009637AD /* FBSDKUserDataStore.m */; }; C5C7B74A22D84F64004A5A0C /* FBSDKFeatureManager.h in Headers */ = {isa = PBXBuildFile; fileRef = C5C7B74822D84F64004A5A0C /* FBSDKFeatureManager.h */; }; C5C7B74B22D84F64004A5A0C /* FBSDKFeatureManager.h in Headers */ = {isa = PBXBuildFile; fileRef = C5C7B74822D84F64004A5A0C /* FBSDKFeatureManager.h */; }; C5C7B74C22D84F64004A5A0C /* FBSDKFeatureManager.h in Headers */ = {isa = PBXBuildFile; fileRef = C5C7B74822D84F64004A5A0C /* FBSDKFeatureManager.h */; }; @@ -830,8 +781,26 @@ C5D25D3721795B790037B13D /* FBSDKCodelessIndexer.h in Headers */ = {isa = PBXBuildFile; fileRef = C5D25D3421795B790037B13D /* FBSDKCodelessIndexer.h */; }; C5D25D3821795B790037B13D /* FBSDKCodelessIndexer.m in Sources */ = {isa = PBXBuildFile; fileRef = C5D25D3521795B790037B13D /* FBSDKCodelessIndexer.m */; }; C5D25D3921795B790037B13D /* FBSDKCodelessIndexer.m in Sources */ = {isa = PBXBuildFile; fileRef = C5D25D3521795B790037B13D /* FBSDKCodelessIndexer.m */; }; - C5DFB84322CBC1EB0086E16C /* FBSDKTypeUtility.h in Headers */ = {isa = PBXBuildFile; fileRef = C5C4B3E32276B88600CA3706 /* FBSDKTypeUtility.h */; }; + C5EAFA5E255283C100458DF5 /* FBSDKUserDataStore+Internal.h in Headers */ = {isa = PBXBuildFile; fileRef = C5EAFA5D255283C100458DF5 /* FBSDKUserDataStore+Internal.h */; }; + C5EAFA9B25528EA300458DF5 /* FBSDKUserDataStore+Internal.h in Headers */ = {isa = PBXBuildFile; fileRef = C5EAFA5D255283C100458DF5 /* FBSDKUserDataStore+Internal.h */; }; + C5EAFAA925528EAE00458DF5 /* FBSDKUserDataStore+Internal.h in Headers */ = {isa = PBXBuildFile; fileRef = C5EAFA5D255283C100458DF5 /* FBSDKUserDataStore+Internal.h */; }; + C5EAFAB725528EAF00458DF5 /* FBSDKUserDataStore+Internal.h in Headers */ = {isa = PBXBuildFile; fileRef = C5EAFA5D255283C100458DF5 /* FBSDKUserDataStore+Internal.h */; }; + C5EAFAB825528EB000458DF5 /* FBSDKUserDataStore+Internal.h in Headers */ = {isa = PBXBuildFile; fileRef = C5EAFA5D255283C100458DF5 /* FBSDKUserDataStore+Internal.h */; }; + C5EAFAC625528EB100458DF5 /* FBSDKUserDataStore+Internal.h in Headers */ = {isa = PBXBuildFile; fileRef = C5EAFA5D255283C100458DF5 /* FBSDKUserDataStore+Internal.h */; }; + C5EAFAC725528EB200458DF5 /* FBSDKUserDataStore+Internal.h in Headers */ = {isa = PBXBuildFile; fileRef = C5EAFA5D255283C100458DF5 /* FBSDKUserDataStore+Internal.h */; }; + C5EAFAD525528EB200458DF5 /* FBSDKUserDataStore+Internal.h in Headers */ = {isa = PBXBuildFile; fileRef = C5EAFA5D255283C100458DF5 /* FBSDKUserDataStore+Internal.h */; }; C5F6EC861FA24FAF009EB258 /* FBSDKPaymentObserverTests.m in Sources */ = {isa = PBXBuildFile; fileRef = C5F6EC851FA24FAF009EB258 /* FBSDKPaymentObserverTests.m */; }; + C638153D257EA7CC00B7690F /* FBSDKAuthenticationTokenFactory.h in Headers */ = {isa = PBXBuildFile; fileRef = C6A308BA257AD1FF003C52FD /* FBSDKAuthenticationTokenFactory.h */; }; + C638158A257EA8F000B7690F /* FBSDKAuthenticationTokenFactory.m in Sources */ = {isa = PBXBuildFile; fileRef = C6A308BB257AD1FF003C52FD /* FBSDKAuthenticationTokenFactory.m */; }; + C6381628257EDF2100B7690F /* FBSDKAuthenticationTokenFactoryTests.m in Sources */ = {isa = PBXBuildFile; fileRef = C6381619257EDF2100B7690F /* FBSDKAuthenticationTokenFactoryTests.m */; }; + C67BED2A257F1A6300D356C9 /* ProfilePictureViewTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = C67BED29257F1A6300D356C9 /* ProfilePictureViewTests.swift */; }; + C6A308BD257AD1FF003C52FD /* FBSDKAuthenticationTokenFactory.m in Sources */ = {isa = PBXBuildFile; fileRef = C6A308BB257AD1FF003C52FD /* FBSDKAuthenticationTokenFactory.m */; }; + C6A308D8257AD227003C52FD /* FBSDKAuthenticationTokenFactory.h in Headers */ = {isa = PBXBuildFile; fileRef = C6A308BA257AD1FF003C52FD /* FBSDKAuthenticationTokenFactory.h */; }; + C6C5CBCC2581871B00AA3BB3 /* FBSDKAuthenticationStatusUtility.m in Sources */ = {isa = PBXBuildFile; fileRef = C6C5CBCA2581871B00AA3BB3 /* FBSDKAuthenticationStatusUtility.m */; }; + C6C5CBCD2581871B00AA3BB3 /* FBSDKAuthenticationStatusUtility.h in Headers */ = {isa = PBXBuildFile; fileRef = C6C5CBCB2581871B00AA3BB3 /* FBSDKAuthenticationStatusUtility.h */; }; + C6C5CBDC2581875600AA3BB3 /* FBSDKAuthenticationStatusUtilityTests.m in Sources */ = {isa = PBXBuildFile; fileRef = C6C5CBDB2581875600AA3BB3 /* FBSDKAuthenticationStatusUtilityTests.m */; }; + C6C5CBF7258192E800AA3BB3 /* FBSDKAuthenticationStatusUtility.h in Headers */ = {isa = PBXBuildFile; fileRef = C6C5CBCB2581871B00AA3BB3 /* FBSDKAuthenticationStatusUtility.h */; }; + C6C5CC05258192ED00AA3BB3 /* FBSDKAuthenticationStatusUtility.m in Sources */ = {isa = PBXBuildFile; fileRef = C6C5CBCA2581871B00AA3BB3 /* FBSDKAuthenticationStatusUtility.m */; }; C77C07A62486BAC600345460 /* FBSDKRestrictiveDataFilterTests.m in Sources */ = {isa = PBXBuildFile; fileRef = C77C07A52486BAC600345460 /* FBSDKRestrictiveDataFilterTests.m */; }; E4416C0123F61902009CCBFA /* FBSDKModelParser.h in Headers */ = {isa = PBXBuildFile; fileRef = E4416BFF23F61902009CCBFA /* FBSDKModelParser.h */; }; E4416C0223F61902009CCBFA /* FBSDKModelParser.mm in Sources */ = {isa = PBXBuildFile; fileRef = E4416C0023F61902009CCBFA /* FBSDKModelParser.mm */; }; @@ -839,6 +808,13 @@ E4416C1623F61917009CCBFA /* FBSDKModelParser.mm in Sources */ = {isa = PBXBuildFile; fileRef = E4416C0023F61902009CCBFA /* FBSDKModelParser.mm */; }; E493252D23F7C60E0000B63A /* FBSDKModelParserTests.mm in Sources */ = {isa = PBXBuildFile; fileRef = E493252C23F7C60E0000B63A /* FBSDKModelParserTests.mm */; }; E4C2B97A23867327002335A4 /* FBSDKModelRuntimeTests.mm in Sources */ = {isa = PBXBuildFile; fileRef = E4C2B97923867327002335A4 /* FBSDKModelRuntimeTests.mm */; }; + F402AFC8255B49A500473083 /* FBSDKBridgeAPITests.m in Sources */ = {isa = PBXBuildFile; fileRef = F402AFC7255B49A500473083 /* FBSDKBridgeAPITests.m */; }; + F402AFF3255B4EE400473083 /* AuthenticationSessionSpy.swift in Sources */ = {isa = PBXBuildFile; fileRef = F402AFF2255B4EE400473083 /* AuthenticationSessionSpy.swift */; }; + F402B062255C645600473083 /* FakeLoginManager.m in Sources */ = {isa = PBXBuildFile; fileRef = F402B061255C645600473083 /* FakeLoginManager.m */; }; + F402B071255C740C00473083 /* ViewControllerSpy.swift in Sources */ = {isa = PBXBuildFile; fileRef = F402B070255C740C00473083 /* ViewControllerSpy.swift */; }; + F408C6C02563091900372D61 /* FBSDKBridgeAPIOpenBridgeRequestTests.m in Sources */ = {isa = PBXBuildFile; fileRef = F408C6BF2563091900372D61 /* FBSDKBridgeAPIOpenBridgeRequestTests.m */; }; + F408C6E9256309A500372D61 /* FakeBridgeApiRequest.swift in Sources */ = {isa = PBXBuildFile; fileRef = F408C6E8256309A500372D61 /* FakeBridgeApiRequest.swift */; }; + F408C712256320F300372D61 /* FBSDKBridgeAPIRequestTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = F408C711256320F300372D61 /* FBSDKBridgeAPIRequestTests.swift */; }; F40B24B2247324BB0059351C /* RawServerConfigurationResponseFixtures.swift in Sources */ = {isa = PBXBuildFile; fileRef = F40B24B1247324BB0059351C /* RawServerConfigurationResponseFixtures.swift */; }; F40B24C224732DD90059351C /* Fuzzer.swift in Sources */ = {isa = PBXBuildFile; fileRef = F40B24C124732DD90059351C /* Fuzzer.swift */; }; F40F6551241A974400B10EAA /* FBSDKMethodUsageMonitorEntryTests.m in Sources */ = {isa = PBXBuildFile; fileRef = F40F6550241A974400B10EAA /* FBSDKMethodUsageMonitorEntryTests.m */; }; @@ -850,22 +826,7 @@ F40F6568241A99E000B10EAA /* FBSDKMethodUsageMonitorEntry.m in Sources */ = {isa = PBXBuildFile; fileRef = F40F6560241A99E000B10EAA /* FBSDKMethodUsageMonitorEntry.m */; }; F40F6569241A99E000B10EAA /* FBSDKMethodUsageMonitorEntry.m in Sources */ = {isa = PBXBuildFile; fileRef = F40F6560241A99E000B10EAA /* FBSDKMethodUsageMonitorEntry.m */; }; F40F656A241A99E000B10EAA /* FBSDKMethodUsageMonitorEntry.m in Sources */ = {isa = PBXBuildFile; fileRef = F40F6560241A99E000B10EAA /* FBSDKMethodUsageMonitorEntry.m */; }; - F413881E24C76E0A001BC075 /* FBSDKSafeCast.h in Headers */ = {isa = PBXBuildFile; fileRef = F413881D24C76E0A001BC075 /* FBSDKSafeCast.h */; }; - F413881F24C76E0A001BC075 /* FBSDKSafeCast.h in Headers */ = {isa = PBXBuildFile; fileRef = F413881D24C76E0A001BC075 /* FBSDKSafeCast.h */; }; - F413882024C76E0A001BC075 /* FBSDKSafeCast.h in Headers */ = {isa = PBXBuildFile; fileRef = F413881D24C76E0A001BC075 /* FBSDKSafeCast.h */; }; - F413882124C76E0A001BC075 /* FBSDKSafeCast.h in Headers */ = {isa = PBXBuildFile; fileRef = F413881D24C76E0A001BC075 /* FBSDKSafeCast.h */; }; - F413882224C76E0A001BC075 /* FBSDKSafeCast.h in Headers */ = {isa = PBXBuildFile; fileRef = F413881D24C76E0A001BC075 /* FBSDKSafeCast.h */; }; - F413882324C76E0A001BC075 /* FBSDKSafeCast.h in Headers */ = {isa = PBXBuildFile; fileRef = F413881D24C76E0A001BC075 /* FBSDKSafeCast.h */; }; - F413882424C76E0A001BC075 /* FBSDKSafeCast.h in Headers */ = {isa = PBXBuildFile; fileRef = F413881D24C76E0A001BC075 /* FBSDKSafeCast.h */; }; - F413882524C76E0A001BC075 /* FBSDKSafeCast.h in Headers */ = {isa = PBXBuildFile; fileRef = F413881D24C76E0A001BC075 /* FBSDKSafeCast.h */; }; - F413883524C76E78001BC075 /* FBSDKSafeCast.m in Sources */ = {isa = PBXBuildFile; fileRef = F413883424C76E78001BC075 /* FBSDKSafeCast.m */; }; - F413883624C76E78001BC075 /* FBSDKSafeCast.m in Sources */ = {isa = PBXBuildFile; fileRef = F413883424C76E78001BC075 /* FBSDKSafeCast.m */; }; - F413883724C76E78001BC075 /* FBSDKSafeCast.m in Sources */ = {isa = PBXBuildFile; fileRef = F413883424C76E78001BC075 /* FBSDKSafeCast.m */; }; - F413883824C76E78001BC075 /* FBSDKSafeCast.m in Sources */ = {isa = PBXBuildFile; fileRef = F413883424C76E78001BC075 /* FBSDKSafeCast.m */; }; - F413883924C76E78001BC075 /* FBSDKSafeCast.m in Sources */ = {isa = PBXBuildFile; fileRef = F413883424C76E78001BC075 /* FBSDKSafeCast.m */; }; - F413883A24C76E78001BC075 /* FBSDKSafeCast.m in Sources */ = {isa = PBXBuildFile; fileRef = F413883424C76E78001BC075 /* FBSDKSafeCast.m */; }; - F413883B24C76E78001BC075 /* FBSDKSafeCast.m in Sources */ = {isa = PBXBuildFile; fileRef = F413883424C76E78001BC075 /* FBSDKSafeCast.m */; }; - F413883C24C76E78001BC075 /* FBSDKSafeCast.m in Sources */ = {isa = PBXBuildFile; fileRef = F413883424C76E78001BC075 /* FBSDKSafeCast.m */; }; + F41200D0255F45FE009E323F /* FBSDKBridgeAPI+ApplicationOpenUrlTests.m in Sources */ = {isa = PBXBuildFile; fileRef = F41200CF255F45FE009E323F /* FBSDKBridgeAPI+ApplicationOpenUrlTests.m */; }; F413883E24C76F3B001BC075 /* FBSDKSafeCastTests.m in Sources */ = {isa = PBXBuildFile; fileRef = F413883D24C76F3B001BC075 /* FBSDKSafeCastTests.m */; }; F4181B342416EC06006DB452 /* FBSDKMonitorTests.m in Sources */ = {isa = PBXBuildFile; fileRef = F4181B332416EC06006DB452 /* FBSDKMonitorTests.m */; }; F41979282475A20E003007CC /* FBSDKTypeUtilityTests.m in Sources */ = {isa = PBXBuildFile; fileRef = F41979272475A20E003007CC /* FBSDKTypeUtilityTests.m */; }; @@ -899,17 +860,76 @@ F4210E35241B00790061F56D /* FBSDKMethodUsageMonitor.m in Sources */ = {isa = PBXBuildFile; fileRef = F4210E2D241B00790061F56D /* FBSDKMethodUsageMonitor.m */; }; F4210E36241B00790061F56D /* FBSDKMethodUsageMonitor.m in Sources */ = {isa = PBXBuildFile; fileRef = F4210E2D241B00790061F56D /* FBSDKMethodUsageMonitor.m */; }; F4210E37241B00790061F56D /* FBSDKMethodUsageMonitor.m in Sources */ = {isa = PBXBuildFile; fileRef = F4210E2D241B00790061F56D /* FBSDKMethodUsageMonitor.m */; }; - F428430B246B427700CD4393 /* FBSDKServerConfigurationManagerTests.m in Sources */ = {isa = PBXBuildFile; fileRef = F428430A246B427700CD4393 /* FBSDKServerConfigurationManagerTests.m */; }; - F43960D02425513100C1868F /* FakeMonitorStore.m in Sources */ = {isa = PBXBuildFile; fileRef = F43960CF2425513100C1868F /* FakeMonitorStore.m */; }; + F428430B246B427700CD4393 /* FBSDKServerConfigurationManagerTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = F428430A246B427700CD4393 /* FBSDKServerConfigurationManagerTests.swift */; }; + F43960D02425513100C1868F /* FakeMonitorStore.swift in Sources */ = {isa = PBXBuildFile; fileRef = F43960CF2425513100C1868F /* FakeMonitorStore.swift */; }; + F44B04B7256442050059A3A6 /* FBSDKBridgeAPIOpenUrlWithSafariTests.m in Sources */ = {isa = PBXBuildFile; fileRef = F44B04A8256442050059A3A6 /* FBSDKBridgeAPIOpenUrlWithSafariTests.m */; }; + F44B04EE256456140059A3A6 /* FBSDKDynamicFrameworkResolving.h in Headers */ = {isa = PBXBuildFile; fileRef = F44B04EC256456140059A3A6 /* FBSDKDynamicFrameworkResolving.h */; }; + F44B04EF256456140059A3A6 /* FBSDKDynamicFrameworkResolving.h in Headers */ = {isa = PBXBuildFile; fileRef = F44B04EC256456140059A3A6 /* FBSDKDynamicFrameworkResolving.h */; }; + F44B04F0256456140059A3A6 /* FBSDKDynamicFrameworkResolving.h in Headers */ = {isa = PBXBuildFile; fileRef = F44B04EC256456140059A3A6 /* FBSDKDynamicFrameworkResolving.h */; }; + F44B04F1256456140059A3A6 /* FBSDKDynamicFrameworkResolving.h in Headers */ = {isa = PBXBuildFile; fileRef = F44B04EC256456140059A3A6 /* FBSDKDynamicFrameworkResolving.h */; }; + F44B051125645DAC0059A3A6 /* FakeDylibResolver.swift in Sources */ = {isa = PBXBuildFile; fileRef = F44B051025645DAC0059A3A6 /* FakeDylibResolver.swift */; }; + F44B056F256491590059A3A6 /* FBSDKBridgeAPI+SessionCompletionHandlerTests.m in Sources */ = {isa = PBXBuildFile; fileRef = F44B056E256491590059A3A6 /* FBSDKBridgeAPI+SessionCompletionHandlerTests.m */; }; F462DBF023B94C0F00FFCECA /* FBSDKCrypto.h in Headers */ = {isa = PBXBuildFile; fileRef = 894C0B071A702194009137EF /* FBSDKCrypto.h */; }; F462DBF123B94C1000FFCECA /* FBSDKCrypto.h in Headers */ = {isa = PBXBuildFile; fileRef = 894C0B071A702194009137EF /* FBSDKCrypto.h */; }; - F462DBFD23B9569000FFCECA /* FBSDKAccessTokenCaching.h in Headers */ = {isa = PBXBuildFile; fileRef = 9D32A83B1A69941A000A936D /* FBSDKAccessTokenCaching.h */; }; - F462DBFE23B9569000FFCECA /* FBSDKAccessTokenCaching.h in Headers */ = {isa = PBXBuildFile; fileRef = 9D32A83B1A69941A000A936D /* FBSDKAccessTokenCaching.h */; }; + F462DBFD23B9569000FFCECA /* FBSDKTokenCaching.h in Headers */ = {isa = PBXBuildFile; fileRef = 9D32A83B1A69941A000A936D /* FBSDKTokenCaching.h */; }; + F462DBFE23B9569000FFCECA /* FBSDKTokenCaching.h in Headers */ = {isa = PBXBuildFile; fileRef = 9D32A83B1A69941A000A936D /* FBSDKTokenCaching.h */; }; F462DBFF23B9575000FFCECA /* FBSDKServerConfiguration+Internal.h in Headers */ = {isa = PBXBuildFile; fileRef = 8917C4AB1B7A46C800B0B96B /* FBSDKServerConfiguration+Internal.h */; }; F462DC0023B9575100FFCECA /* FBSDKServerConfiguration+Internal.h in Headers */ = {isa = PBXBuildFile; fileRef = 8917C4AB1B7A46C800B0B96B /* FBSDKServerConfiguration+Internal.h */; }; F462DC0123B9578E00FFCECA /* FBSDKGraphRequestConnection+Internal.h in Headers */ = {isa = PBXBuildFile; fileRef = 9D69FC421AA66BBC0068EC76 /* FBSDKGraphRequestConnection+Internal.h */; }; F462DC0223B9578F00FFCECA /* FBSDKGraphRequestConnection+Internal.h in Headers */ = {isa = PBXBuildFile; fileRef = 9D69FC421AA66BBC0068EC76 /* FBSDKGraphRequestConnection+Internal.h */; }; - F46C4857243E34C600D03401 /* FBSDKMonitoringConfigurationTestHelper.m in Sources */ = {isa = PBXBuildFile; fileRef = F46C4856243E34C600D03401 /* FBSDKMonitoringConfigurationTestHelper.m */; }; + F468B2A024C2457000979F8D /* FBSDKRestrictiveData.h in Headers */ = {isa = PBXBuildFile; fileRef = F468B29C24C2456F00979F8D /* FBSDKRestrictiveData.h */; }; + F468B2A124C2457000979F8D /* FBSDKRestrictiveData.h in Headers */ = {isa = PBXBuildFile; fileRef = F468B29C24C2456F00979F8D /* FBSDKRestrictiveData.h */; }; + F468B2A624C2457000979F8D /* FBSDKRestrictiveData.m in Sources */ = {isa = PBXBuildFile; fileRef = F468B29D24C2456F00979F8D /* FBSDKRestrictiveData.m */; }; + F468B2A724C2457000979F8D /* FBSDKRestrictiveData.m in Sources */ = {isa = PBXBuildFile; fileRef = F468B29D24C2456F00979F8D /* FBSDKRestrictiveData.m */; }; + F468B30A24C25AB600979F8D /* FBSDKTypeUtility.m in Sources */ = {isa = PBXBuildFile; fileRef = F468B2E024C25AB500979F8D /* FBSDKTypeUtility.m */; }; + F468B30B24C25AB600979F8D /* FBSDKTypeUtility.m in Sources */ = {isa = PBXBuildFile; fileRef = F468B2E024C25AB500979F8D /* FBSDKTypeUtility.m */; }; + F468B30C24C25AB600979F8D /* FBSDKTypeUtility.m in Sources */ = {isa = PBXBuildFile; fileRef = F468B2E024C25AB500979F8D /* FBSDKTypeUtility.m */; }; + F468B30D24C25AB600979F8D /* FBSDKTypeUtility.m in Sources */ = {isa = PBXBuildFile; fileRef = F468B2E024C25AB500979F8D /* FBSDKTypeUtility.m */; }; + F468B30E24C25AB600979F8D /* FBSDKTypeUtility.m in Sources */ = {isa = PBXBuildFile; fileRef = F468B2E024C25AB500979F8D /* FBSDKTypeUtility.m */; }; + F468B30F24C25AB600979F8D /* FBSDKTypeUtility.m in Sources */ = {isa = PBXBuildFile; fileRef = F468B2E024C25AB500979F8D /* FBSDKTypeUtility.m */; }; + F468B31024C25AB600979F8D /* FBSDKTypeUtility.m in Sources */ = {isa = PBXBuildFile; fileRef = F468B2E024C25AB500979F8D /* FBSDKTypeUtility.m */; }; + F468B31124C25AB600979F8D /* FBSDKTypeUtility.m in Sources */ = {isa = PBXBuildFile; fileRef = F468B2E024C25AB500979F8D /* FBSDKTypeUtility.m */; }; + F468B31A24C25AB600979F8D /* FBSDKURLSessionTask.m in Sources */ = {isa = PBXBuildFile; fileRef = F468B2E224C25AB500979F8D /* FBSDKURLSessionTask.m */; }; + F468B31B24C25AB600979F8D /* FBSDKURLSessionTask.m in Sources */ = {isa = PBXBuildFile; fileRef = F468B2E224C25AB500979F8D /* FBSDKURLSessionTask.m */; }; + F468B31C24C25AB600979F8D /* FBSDKURLSessionTask.m in Sources */ = {isa = PBXBuildFile; fileRef = F468B2E224C25AB500979F8D /* FBSDKURLSessionTask.m */; }; + F468B31D24C25AB600979F8D /* FBSDKURLSessionTask.m in Sources */ = {isa = PBXBuildFile; fileRef = F468B2E224C25AB500979F8D /* FBSDKURLSessionTask.m */; }; + F468B31E24C25AB600979F8D /* FBSDKURLSessionTask.m in Sources */ = {isa = PBXBuildFile; fileRef = F468B2E224C25AB500979F8D /* FBSDKURLSessionTask.m */; }; + F468B31F24C25AB600979F8D /* FBSDKURLSessionTask.m in Sources */ = {isa = PBXBuildFile; fileRef = F468B2E224C25AB500979F8D /* FBSDKURLSessionTask.m */; }; + F468B32024C25AB600979F8D /* FBSDKURLSessionTask.m in Sources */ = {isa = PBXBuildFile; fileRef = F468B2E224C25AB500979F8D /* FBSDKURLSessionTask.m */; }; + F468B32124C25AB600979F8D /* FBSDKURLSessionTask.m in Sources */ = {isa = PBXBuildFile; fileRef = F468B2E224C25AB500979F8D /* FBSDKURLSessionTask.m */; }; + F468B32224C25AB600979F8D /* FBSDKCrashHandler.m in Sources */ = {isa = PBXBuildFile; fileRef = F468B2E324C25AB500979F8D /* FBSDKCrashHandler.m */; }; + F468B32324C25AB600979F8D /* FBSDKCrashHandler.m in Sources */ = {isa = PBXBuildFile; fileRef = F468B2E324C25AB500979F8D /* FBSDKCrashHandler.m */; }; + F468B32424C25AB600979F8D /* FBSDKCrashHandler.m in Sources */ = {isa = PBXBuildFile; fileRef = F468B2E324C25AB500979F8D /* FBSDKCrashHandler.m */; }; + F468B32524C25AB600979F8D /* FBSDKCrashHandler.m in Sources */ = {isa = PBXBuildFile; fileRef = F468B2E324C25AB500979F8D /* FBSDKCrashHandler.m */; }; + F468B32624C25AB600979F8D /* FBSDKCrashHandler.m in Sources */ = {isa = PBXBuildFile; fileRef = F468B2E324C25AB500979F8D /* FBSDKCrashHandler.m */; }; + F468B32724C25AB600979F8D /* FBSDKCrashHandler.m in Sources */ = {isa = PBXBuildFile; fileRef = F468B2E324C25AB500979F8D /* FBSDKCrashHandler.m */; }; + F468B32824C25AB600979F8D /* FBSDKCrashHandler.m in Sources */ = {isa = PBXBuildFile; fileRef = F468B2E324C25AB500979F8D /* FBSDKCrashHandler.m */; }; + F468B32924C25AB600979F8D /* FBSDKCrashHandler.m in Sources */ = {isa = PBXBuildFile; fileRef = F468B2E324C25AB500979F8D /* FBSDKCrashHandler.m */; }; + F468B34224C25AB600979F8D /* FBSDKBasicUtility.m in Sources */ = {isa = PBXBuildFile; fileRef = F468B2E724C25AB500979F8D /* FBSDKBasicUtility.m */; }; + F468B34324C25AB600979F8D /* FBSDKBasicUtility.m in Sources */ = {isa = PBXBuildFile; fileRef = F468B2E724C25AB500979F8D /* FBSDKBasicUtility.m */; }; + F468B34424C25AB600979F8D /* FBSDKBasicUtility.m in Sources */ = {isa = PBXBuildFile; fileRef = F468B2E724C25AB500979F8D /* FBSDKBasicUtility.m */; }; + F468B34524C25AB600979F8D /* FBSDKBasicUtility.m in Sources */ = {isa = PBXBuildFile; fileRef = F468B2E724C25AB500979F8D /* FBSDKBasicUtility.m */; }; + F468B34624C25AB600979F8D /* FBSDKBasicUtility.m in Sources */ = {isa = PBXBuildFile; fileRef = F468B2E724C25AB500979F8D /* FBSDKBasicUtility.m */; }; + F468B34724C25AB600979F8D /* FBSDKBasicUtility.m in Sources */ = {isa = PBXBuildFile; fileRef = F468B2E724C25AB500979F8D /* FBSDKBasicUtility.m */; }; + F468B34824C25AB600979F8D /* FBSDKBasicUtility.m in Sources */ = {isa = PBXBuildFile; fileRef = F468B2E724C25AB500979F8D /* FBSDKBasicUtility.m */; }; + F468B34924C25AB600979F8D /* FBSDKBasicUtility.m in Sources */ = {isa = PBXBuildFile; fileRef = F468B2E724C25AB500979F8D /* FBSDKBasicUtility.m */; }; + F468B34A24C25AB600979F8D /* FBSDKURLSession.m in Sources */ = {isa = PBXBuildFile; fileRef = F468B2E824C25AB600979F8D /* FBSDKURLSession.m */; }; + F468B34B24C25AB600979F8D /* FBSDKURLSession.m in Sources */ = {isa = PBXBuildFile; fileRef = F468B2E824C25AB600979F8D /* FBSDKURLSession.m */; }; + F468B34C24C25AB600979F8D /* FBSDKURLSession.m in Sources */ = {isa = PBXBuildFile; fileRef = F468B2E824C25AB600979F8D /* FBSDKURLSession.m */; }; + F468B34D24C25AB600979F8D /* FBSDKURLSession.m in Sources */ = {isa = PBXBuildFile; fileRef = F468B2E824C25AB600979F8D /* FBSDKURLSession.m */; }; + F468B34E24C25AB600979F8D /* FBSDKURLSession.m in Sources */ = {isa = PBXBuildFile; fileRef = F468B2E824C25AB600979F8D /* FBSDKURLSession.m */; }; + F468B34F24C25AB600979F8D /* FBSDKURLSession.m in Sources */ = {isa = PBXBuildFile; fileRef = F468B2E824C25AB600979F8D /* FBSDKURLSession.m */; }; + F468B35024C25AB600979F8D /* FBSDKURLSession.m in Sources */ = {isa = PBXBuildFile; fileRef = F468B2E824C25AB600979F8D /* FBSDKURLSession.m */; }; + F468B35124C25AB600979F8D /* FBSDKURLSession.m in Sources */ = {isa = PBXBuildFile; fileRef = F468B2E824C25AB600979F8D /* FBSDKURLSession.m */; }; + F468B35224C25AB600979F8D /* FBSDKLibAnalyzer.m in Sources */ = {isa = PBXBuildFile; fileRef = F468B2E924C25AB600979F8D /* FBSDKLibAnalyzer.m */; }; + F468B35324C25AB600979F8D /* FBSDKLibAnalyzer.m in Sources */ = {isa = PBXBuildFile; fileRef = F468B2E924C25AB600979F8D /* FBSDKLibAnalyzer.m */; }; + F468B35424C25AB600979F8D /* FBSDKLibAnalyzer.m in Sources */ = {isa = PBXBuildFile; fileRef = F468B2E924C25AB600979F8D /* FBSDKLibAnalyzer.m */; }; + F468B35524C25AB600979F8D /* FBSDKLibAnalyzer.m in Sources */ = {isa = PBXBuildFile; fileRef = F468B2E924C25AB600979F8D /* FBSDKLibAnalyzer.m */; }; + F468B35624C25AB600979F8D /* FBSDKLibAnalyzer.m in Sources */ = {isa = PBXBuildFile; fileRef = F468B2E924C25AB600979F8D /* FBSDKLibAnalyzer.m */; }; + F468B35724C25AB600979F8D /* FBSDKLibAnalyzer.m in Sources */ = {isa = PBXBuildFile; fileRef = F468B2E924C25AB600979F8D /* FBSDKLibAnalyzer.m */; }; + F468B35824C25AB600979F8D /* FBSDKLibAnalyzer.m in Sources */ = {isa = PBXBuildFile; fileRef = F468B2E924C25AB600979F8D /* FBSDKLibAnalyzer.m */; }; + F468B35924C25AB600979F8D /* FBSDKLibAnalyzer.m in Sources */ = {isa = PBXBuildFile; fileRef = F468B2E924C25AB600979F8D /* FBSDKLibAnalyzer.m */; }; + F46C4857243E34C600D03401 /* FBSDKMonitoringConfigurationTestHelper.swift in Sources */ = {isa = PBXBuildFile; fileRef = F46C4856243E34C600D03401 /* FBSDKMonitoringConfigurationTestHelper.swift */; }; F46FA62D24533F450060C902 /* AccessToken.swift in Sources */ = {isa = PBXBuildFile; fileRef = F487DBD9231EC293008416A9 /* AccessToken.swift */; }; F46FA63C24533F450060C902 /* AccessToken.swift in Sources */ = {isa = PBXBuildFile; fileRef = F487DBD9231EC293008416A9 /* AccessToken.swift */; }; F46FA63D24533F610060C902 /* Permission.swift in Sources */ = {isa = PBXBuildFile; fileRef = F487DBCB231EC1A0008416A9 /* Permission.swift */; }; @@ -917,7 +937,25 @@ F46FA63F24533F690060C902 /* Settings.swift in Sources */ = {isa = PBXBuildFile; fileRef = F487DBCD231EC20E008416A9 /* Settings.swift */; }; F46FA64024533F690060C902 /* Settings.swift in Sources */ = {isa = PBXBuildFile; fileRef = F487DBCD231EC20E008416A9 /* Settings.swift */; }; F4713D7D2375DA8200748692 /* FBSDKSuggestedEventsIndexer.m in Sources */ = {isa = PBXBuildFile; fileRef = BF247C812374E1B200A522C0 /* FBSDKSuggestedEventsIndexer.m */; }; + F47E560D24E1EA8D001497C9 /* FakeBundle.m in Sources */ = {isa = PBXBuildFile; fileRef = F47E55FB24E1EA8D001497C9 /* FakeBundle.m */; }; F48A6AED24170D29002C6CA1 /* TestMonitorEntry.m in Sources */ = {isa = PBXBuildFile; fileRef = F48A6AEC24170D29002C6CA1 /* TestMonitorEntry.m */; }; + F48AD2E52576FE050052C056 /* FBSDKAuthenticationToken.m in Sources */ = {isa = PBXBuildFile; fileRef = F48AD2E32576FE050052C056 /* FBSDKAuthenticationToken.m */; }; + F48AD2E62576FE050052C056 /* FBSDKAuthenticationToken.m in Sources */ = {isa = PBXBuildFile; fileRef = F48AD2E32576FE050052C056 /* FBSDKAuthenticationToken.m */; }; + F48AD2E92576FE050052C056 /* FBSDKAuthenticationToken.h in Headers */ = {isa = PBXBuildFile; fileRef = F48AD2E42576FE050052C056 /* FBSDKAuthenticationToken.h */; settings = {ATTRIBUTES = (Public, ); }; }; + F48AD2EA2576FE050052C056 /* FBSDKAuthenticationToken.h in Headers */ = {isa = PBXBuildFile; fileRef = F48AD2E42576FE050052C056 /* FBSDKAuthenticationToken.h */; settings = {ATTRIBUTES = (Public, ); }; }; + F48AD2EB2576FE050052C056 /* FBSDKAuthenticationToken.h in Headers */ = {isa = PBXBuildFile; fileRef = F48AD2E42576FE050052C056 /* FBSDKAuthenticationToken.h */; settings = {ATTRIBUTES = (Public, ); }; }; + F48AD2EC2576FE050052C056 /* FBSDKAuthenticationToken.h in Headers */ = {isa = PBXBuildFile; fileRef = F48AD2E42576FE050052C056 /* FBSDKAuthenticationToken.h */; settings = {ATTRIBUTES = (Public, ); }; }; + F48AD3252576FEB20052C056 /* FBSDKAuthenticationTokenTests.m in Sources */ = {isa = PBXBuildFile; fileRef = F48AD3242576FEB20052C056 /* FBSDKAuthenticationTokenTests.m */; }; + F48AD348257700B80052C056 /* SampleAuthenticationToken.swift in Sources */ = {isa = PBXBuildFile; fileRef = F48AD347257700B80052C056 /* SampleAuthenticationToken.swift */; }; + F492C0A224E1F5F600BA21F7 /* KeychainStoreSpy.swift in Sources */ = {isa = PBXBuildFile; fileRef = F492C0A024E1F5F600BA21F7 /* KeychainStoreSpy.swift */; }; + F492C0A324E1F5F600BA21F7 /* UserDefaultsSpy.m in Sources */ = {isa = PBXBuildFile; fileRef = F492C0A124E1F5F600BA21F7 /* UserDefaultsSpy.m */; }; + F494285D25829C72008FE009 /* FakeSessionProvider.swift in Sources */ = {isa = PBXBuildFile; fileRef = F494285C25829C72008FE009 /* FakeSessionProvider.swift */; }; + F494286B25829CE8008FE009 /* FBSDKSessionProviding.h in Headers */ = {isa = PBXBuildFile; fileRef = F494284025829B98008FE009 /* FBSDKSessionProviding.h */; }; + F494287925829CE8008FE009 /* FBSDKSessionProviding.h in Headers */ = {isa = PBXBuildFile; fileRef = F494284025829B98008FE009 /* FBSDKSessionProviding.h */; }; + F496E58F24E49DBC006231A2 /* SampleAppEvents.swift in Sources */ = {isa = PBXBuildFile; fileRef = F496E58E24E49DBC006231A2 /* SampleAppEvents.swift */; }; + F496E5FE24E5D0D5006231A2 /* FakeTokenCache.swift in Sources */ = {isa = PBXBuildFile; fileRef = F496E5FD24E5D0D5006231A2 /* FakeTokenCache.swift */; }; + F496E60F24E5D195006231A2 /* SampleAccessToken.swift in Sources */ = {isa = PBXBuildFile; fileRef = F496E60E24E5D195006231A2 /* SampleAccessToken.swift */; }; + F496E61224E6049A006231A2 /* AppDelegateObserverFake.swift in Sources */ = {isa = PBXBuildFile; fileRef = F496E61124E6049A006231A2 /* AppDelegateObserverFake.swift */; }; F4A52AED242A7D12005F65CE /* FBSDKPerformanceMonitorEntryTests.m in Sources */ = {isa = PBXBuildFile; fileRef = F4A52AEC242A7D12005F65CE /* FBSDKPerformanceMonitorEntryTests.m */; }; F4A52AFE242A80F4005F65CE /* FBSDKPerformanceMonitorEntry.h in Headers */ = {isa = PBXBuildFile; fileRef = F4A52AFC242A80F4005F65CE /* FBSDKPerformanceMonitorEntry.h */; }; F4A52AFF242A80F4005F65CE /* FBSDKPerformanceMonitorEntry.h in Headers */ = {isa = PBXBuildFile; fileRef = F4A52AFC242A80F4005F65CE /* FBSDKPerformanceMonitorEntry.h */; }; @@ -936,7 +974,8 @@ F4A52B15242AA532005F65CE /* FBSDKPerformanceMonitor.m in Sources */ = {isa = PBXBuildFile; fileRef = F4A52B0D242AA532005F65CE /* FBSDKPerformanceMonitor.m */; }; F4A52B16242AA532005F65CE /* FBSDKPerformanceMonitor.m in Sources */ = {isa = PBXBuildFile; fileRef = F4A52B0D242AA532005F65CE /* FBSDKPerformanceMonitor.m */; }; F4A52B17242AA532005F65CE /* FBSDKPerformanceMonitor.m in Sources */ = {isa = PBXBuildFile; fileRef = F4A52B0D242AA532005F65CE /* FBSDKPerformanceMonitor.m */; }; - F4B3CA3222B3FED80098ADF5 /* ExampleSwiftTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = F4B3CA3122B3FED80098ADF5 /* ExampleSwiftTests.swift */; }; + F4A826B724EC6FAD00EB2CD4 /* FBSDKProfileTests.m in Sources */ = {isa = PBXBuildFile; fileRef = F4A826B624EC6FAC00EB2CD4 /* FBSDKProfileTests.m */; }; + F4AE435225508FEA00FA9DE6 /* FBSDKProfilePictureView+Internal.h in Headers */ = {isa = PBXBuildFile; fileRef = B301910D253622A2001A8DB0 /* FBSDKProfilePictureView+Internal.h */; }; F4BD4024241EEDE500B45D39 /* FBSDKTestCoder.m in Sources */ = {isa = PBXBuildFile; fileRef = F4BD4023241EEDE500B45D39 /* FBSDKTestCoder.m */; }; F4BF22A0241954B400BFB494 /* FBSDKMonitorStoreTests.m in Sources */ = {isa = PBXBuildFile; fileRef = F4BF2292241954B400BFB494 /* FBSDKMonitorStoreTests.m */; }; F4BF22A3241954D800BFB494 /* FBSDKMonitorStore.h in Headers */ = {isa = PBXBuildFile; fileRef = F4BF22A1241954D800BFB494 /* FBSDKMonitorStore.h */; }; @@ -947,6 +986,10 @@ F4BF22AA241954D800BFB494 /* FBSDKMonitorStore.m in Sources */ = {isa = PBXBuildFile; fileRef = F4BF22A2241954D800BFB494 /* FBSDKMonitorStore.m */; }; F4BF22AB241954D800BFB494 /* FBSDKMonitorStore.m in Sources */ = {isa = PBXBuildFile; fileRef = F4BF22A2241954D800BFB494 /* FBSDKMonitorStore.m */; }; F4BF22AC241954D800BFB494 /* FBSDKMonitorStore.m in Sources */ = {isa = PBXBuildFile; fileRef = F4BF22A2241954D800BFB494 /* FBSDKMonitorStore.m */; }; + F4D80B582565D6B3008CCAF0 /* FBSDKRestrictiveDataTests.m in Sources */ = {isa = PBXBuildFile; fileRef = F4D80B572565D6B2008CCAF0 /* FBSDKRestrictiveDataTests.m */; }; + F4D80B812565E1C2008CCAF0 /* FBSDKInstrumentManagerTests.m in Sources */ = {isa = PBXBuildFile; fileRef = F4D80B802565E1C2008CCAF0 /* FBSDKInstrumentManagerTests.m */; }; + F4D80C082566D469008CCAF0 /* FBSDKDrawableTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = F4D80C072566D469008CCAF0 /* FBSDKDrawableTests.swift */; }; + F4D80C402566D856008CCAF0 /* TestAssets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = F4D80C3F2566D856008CCAF0 /* TestAssets.xcassets */; }; F4D8D8DC2422887600C28384 /* FBSDKMonitorNetworker.h in Headers */ = {isa = PBXBuildFile; fileRef = F4D8D8DA2422887600C28384 /* FBSDKMonitorNetworker.h */; }; F4D8D8DD2422887600C28384 /* FBSDKMonitorNetworker.h in Headers */ = {isa = PBXBuildFile; fileRef = F4D8D8DA2422887600C28384 /* FBSDKMonitorNetworker.h */; }; F4D8D8DE2422887600C28384 /* FBSDKMonitorNetworker.h in Headers */ = {isa = PBXBuildFile; fileRef = F4D8D8DA2422887600C28384 /* FBSDKMonitorNetworker.h */; }; @@ -956,51 +999,82 @@ F4D8D8E42422887600C28384 /* FBSDKMonitorNetworker.m in Sources */ = {isa = PBXBuildFile; fileRef = F4D8D8DB2422887600C28384 /* FBSDKMonitorNetworker.m */; }; F4D8D8E52422887600C28384 /* FBSDKMonitorNetworker.m in Sources */ = {isa = PBXBuildFile; fileRef = F4D8D8DB2422887600C28384 /* FBSDKMonitorNetworker.m */; }; F4D8D8FA242293ED00C28384 /* FBSDKMonitorNetworkerTests.m in Sources */ = {isa = PBXBuildFile; fileRef = F4D8D8F9242293ED00C28384 /* FBSDKMonitorNetworkerTests.m */; }; + F4DE31F624DB695100297C18 /* FBSDKTestCase.m in Sources */ = {isa = PBXBuildFile; fileRef = F4DE31F524DB695100297C18 /* FBSDKTestCase.m */; }; F4E50155243648A100C99262 /* FBSDKServerConfigurationFixtures.m in Sources */ = {isa = PBXBuildFile; fileRef = F4E50153243648A100C99262 /* FBSDKServerConfigurationFixtures.m */; }; F4E50156243648A100C99262 /* FBSDKServerConfigurationTests.m in Sources */ = {isa = PBXBuildFile; fileRef = F4E50154243648A100C99262 /* FBSDKServerConfigurationTests.m */; }; - F9098FC322BC332C00857C2D /* FBSDKURLSession.h in Headers */ = {isa = PBXBuildFile; fileRef = F9098FC122BC332C00857C2D /* FBSDKURLSession.h */; }; - F9098FC422BC332C00857C2D /* FBSDKURLSession.h in Headers */ = {isa = PBXBuildFile; fileRef = F9098FC122BC332C00857C2D /* FBSDKURLSession.h */; }; - F9098FC522BC332C00857C2D /* FBSDKURLSession.h in Headers */ = {isa = PBXBuildFile; fileRef = F9098FC122BC332C00857C2D /* FBSDKURLSession.h */; }; - F9098FC622BC332C00857C2D /* FBSDKURLSession.h in Headers */ = {isa = PBXBuildFile; fileRef = F9098FC122BC332C00857C2D /* FBSDKURLSession.h */; }; - F9098FC722BC332C00857C2D /* FBSDKURLSession.h in Headers */ = {isa = PBXBuildFile; fileRef = F9098FC122BC332C00857C2D /* FBSDKURLSession.h */; }; - F9098FC822BC332C00857C2D /* FBSDKURLSession.h in Headers */ = {isa = PBXBuildFile; fileRef = F9098FC122BC332C00857C2D /* FBSDKURLSession.h */; }; - F9098FC922BC332C00857C2D /* FBSDKURLSession.h in Headers */ = {isa = PBXBuildFile; fileRef = F9098FC122BC332C00857C2D /* FBSDKURLSession.h */; }; - F9098FCA22BC332C00857C2D /* FBSDKURLSession.h in Headers */ = {isa = PBXBuildFile; fileRef = F9098FC122BC332C00857C2D /* FBSDKURLSession.h */; }; - F9098FCB22BC332C00857C2D /* FBSDKURLSession.m in Sources */ = {isa = PBXBuildFile; fileRef = F9098FC222BC332C00857C2D /* FBSDKURLSession.m */; }; - F9098FCC22BC332C00857C2D /* FBSDKURLSession.m in Sources */ = {isa = PBXBuildFile; fileRef = F9098FC222BC332C00857C2D /* FBSDKURLSession.m */; }; - F9098FCD22BC332C00857C2D /* FBSDKURLSession.m in Sources */ = {isa = PBXBuildFile; fileRef = F9098FC222BC332C00857C2D /* FBSDKURLSession.m */; }; - F9098FCE22BC332C00857C2D /* FBSDKURLSession.m in Sources */ = {isa = PBXBuildFile; fileRef = F9098FC222BC332C00857C2D /* FBSDKURLSession.m */; }; - F9098FCF22BC332C00857C2D /* FBSDKURLSession.m in Sources */ = {isa = PBXBuildFile; fileRef = F9098FC222BC332C00857C2D /* FBSDKURLSession.m */; }; - F9098FD022BC332C00857C2D /* FBSDKURLSession.m in Sources */ = {isa = PBXBuildFile; fileRef = F9098FC222BC332C00857C2D /* FBSDKURLSession.m */; }; - F9098FD122BC332C00857C2D /* FBSDKURLSession.m in Sources */ = {isa = PBXBuildFile; fileRef = F9098FC222BC332C00857C2D /* FBSDKURLSession.m */; }; - F9098FD222BC332C00857C2D /* FBSDKURLSession.m in Sources */ = {isa = PBXBuildFile; fileRef = F9098FC222BC332C00857C2D /* FBSDKURLSession.m */; }; - F9169B842155A03C00FA1789 /* FBSDKUserDataStore.m in Sources */ = {isa = PBXBuildFile; fileRef = F9169B832155A03C00FA1789 /* FBSDKUserDataStore.m */; }; + F4EB317F2540955500736B67 /* FBSDKGraphRequestPiggybackManagerTests.m in Sources */ = {isa = PBXBuildFile; fileRef = F4EB317E2540955500736B67 /* FBSDKGraphRequestPiggybackManagerTests.m */; }; + F4EB31BB2540A1B800736B67 /* SampleGraphRequestConnection.swift in Sources */ = {isa = PBXBuildFile; fileRef = F4EB31BA2540A1B800736B67 /* SampleGraphRequestConnection.swift */; }; + F4EB31D32540A35800736B67 /* SampleGraphRequest.swift in Sources */ = {isa = PBXBuildFile; fileRef = F4EB31D22540A35800736B67 /* SampleGraphRequest.swift */; }; + F4EB33CE2542323500736B67 /* SampleRawRemotePermissions.swift in Sources */ = {isa = PBXBuildFile; fileRef = F4EB33CD2542323500736B67 /* SampleRawRemotePermissions.swift */; }; + F4ED90DB24EDDC610048D283 /* NotificationCenterSpy.swift in Sources */ = {isa = PBXBuildFile; fileRef = F4ED90DA24EDDC610048D283 /* NotificationCenterSpy.swift */; }; + F4F98C5324CB820A00F0D6EC /* FBSDKSafeCast.m in Sources */ = {isa = PBXBuildFile; fileRef = F4F98C4A24CB820A00F0D6EC /* FBSDKSafeCast.m */; }; + F4F98C5424CB820A00F0D6EC /* FBSDKSafeCast.m in Sources */ = {isa = PBXBuildFile; fileRef = F4F98C4A24CB820A00F0D6EC /* FBSDKSafeCast.m */; }; + F4F98C5524CB820A00F0D6EC /* FBSDKSafeCast.m in Sources */ = {isa = PBXBuildFile; fileRef = F4F98C4A24CB820A00F0D6EC /* FBSDKSafeCast.m */; }; + F4F98C5624CB820A00F0D6EC /* FBSDKSafeCast.m in Sources */ = {isa = PBXBuildFile; fileRef = F4F98C4A24CB820A00F0D6EC /* FBSDKSafeCast.m */; }; + F4F98C5724CB820A00F0D6EC /* FBSDKSafeCast.m in Sources */ = {isa = PBXBuildFile; fileRef = F4F98C4A24CB820A00F0D6EC /* FBSDKSafeCast.m */; }; + F4F98C5824CB820A00F0D6EC /* FBSDKSafeCast.m in Sources */ = {isa = PBXBuildFile; fileRef = F4F98C4A24CB820A00F0D6EC /* FBSDKSafeCast.m */; }; + F4F98C5924CB820A00F0D6EC /* FBSDKSafeCast.m in Sources */ = {isa = PBXBuildFile; fileRef = F4F98C4A24CB820A00F0D6EC /* FBSDKSafeCast.m */; }; + F4F98C5A24CB820A00F0D6EC /* FBSDKSafeCast.m in Sources */ = {isa = PBXBuildFile; fileRef = F4F98C4A24CB820A00F0D6EC /* FBSDKSafeCast.m */; }; + F4FE997F24D906BA008879A4 /* FBSDKJSONValue.m in Sources */ = {isa = PBXBuildFile; fileRef = F4FE997D24D906B9008879A4 /* FBSDKJSONValue.m */; }; + F4FE998024D906BA008879A4 /* FBSDKJSONValue.m in Sources */ = {isa = PBXBuildFile; fileRef = F4FE997D24D906B9008879A4 /* FBSDKJSONValue.m */; }; + F4FE998124D906BA008879A4 /* FBSDKJSONValue.m in Sources */ = {isa = PBXBuildFile; fileRef = F4FE997D24D906B9008879A4 /* FBSDKJSONValue.m */; }; + F4FE998224D906BA008879A4 /* FBSDKJSONValue.m in Sources */ = {isa = PBXBuildFile; fileRef = F4FE997D24D906B9008879A4 /* FBSDKJSONValue.m */; }; + F4FE998324D906BA008879A4 /* FBSDKJSONValue.m in Sources */ = {isa = PBXBuildFile; fileRef = F4FE997D24D906B9008879A4 /* FBSDKJSONValue.m */; }; + F4FE998424D906BA008879A4 /* FBSDKJSONValue.m in Sources */ = {isa = PBXBuildFile; fileRef = F4FE997D24D906B9008879A4 /* FBSDKJSONValue.m */; }; + F4FE998524D906BA008879A4 /* FBSDKJSONValue.m in Sources */ = {isa = PBXBuildFile; fileRef = F4FE997D24D906B9008879A4 /* FBSDKJSONValue.m */; }; + F4FE998624D906BA008879A4 /* FBSDKJSONValue.m in Sources */ = {isa = PBXBuildFile; fileRef = F4FE997D24D906B9008879A4 /* FBSDKJSONValue.m */; }; + F916581524F6BAB200BB759A /* FBSDKSKAdNetworkConversionConfigurationTests.m in Sources */ = {isa = PBXBuildFile; fileRef = F916581424F6BAB200BB759A /* FBSDKSKAdNetworkConversionConfigurationTests.m */; }; + F916581624F6BB4D00BB759A /* FBSDKSKAdNetworkConversionConfiguration.h in Headers */ = {isa = PBXBuildFile; fileRef = F9DE56B824F61B090009CD69 /* FBSDKSKAdNetworkConversionConfiguration.h */; }; + F916581724F6BB5100BB759A /* FBSDKSKAdNetworkConversionConfiguration.m in Sources */ = {isa = PBXBuildFile; fileRef = F9DE56B924F61B090009CD69 /* FBSDKSKAdNetworkConversionConfiguration.m */; }; F91BA4DD216D6CF200CDDFE0 /* FBSDKCrypto.m in Sources */ = {isa = PBXBuildFile; fileRef = 894C0B081A702194009137EF /* FBSDKCrypto.m */; }; F91BA4F4216D6CF300CDDFE0 /* FBSDKCrypto.m in Sources */ = {isa = PBXBuildFile; fileRef = 894C0B081A702194009137EF /* FBSDKCrypto.m */; }; - F92F8F7E22BA274300494727 /* FBSDKURLSessionTask.h in Headers */ = {isa = PBXBuildFile; fileRef = F92F8F7C22BA274300494727 /* FBSDKURLSessionTask.h */; }; - F92F8F7F22BA274300494727 /* FBSDKURLSessionTask.h in Headers */ = {isa = PBXBuildFile; fileRef = F92F8F7C22BA274300494727 /* FBSDKURLSessionTask.h */; }; - F92F8F8022BA274300494727 /* FBSDKURLSessionTask.h in Headers */ = {isa = PBXBuildFile; fileRef = F92F8F7C22BA274300494727 /* FBSDKURLSessionTask.h */; }; - F92F8F8122BA274300494727 /* FBSDKURLSessionTask.m in Sources */ = {isa = PBXBuildFile; fileRef = F92F8F7D22BA274300494727 /* FBSDKURLSessionTask.m */; }; - F92F8F8222BA274300494727 /* FBSDKURLSessionTask.m in Sources */ = {isa = PBXBuildFile; fileRef = F92F8F7D22BA274300494727 /* FBSDKURLSessionTask.m */; }; - F92F8F8322BA274300494727 /* FBSDKURLSessionTask.m in Sources */ = {isa = PBXBuildFile; fileRef = F92F8F7D22BA274300494727 /* FBSDKURLSessionTask.m */; }; - F92F8F8422BA275200494727 /* FBSDKURLSessionTask.m in Sources */ = {isa = PBXBuildFile; fileRef = F92F8F7D22BA274300494727 /* FBSDKURLSessionTask.m */; }; - F92F8F8522BA275300494727 /* FBSDKURLSessionTask.m in Sources */ = {isa = PBXBuildFile; fileRef = F92F8F7D22BA274300494727 /* FBSDKURLSessionTask.m */; }; - F92F8F8622BA275400494727 /* FBSDKURLSessionTask.m in Sources */ = {isa = PBXBuildFile; fileRef = F92F8F7D22BA274300494727 /* FBSDKURLSessionTask.m */; }; - F92F8F8722BA275500494727 /* FBSDKURLSessionTask.m in Sources */ = {isa = PBXBuildFile; fileRef = F92F8F7D22BA274300494727 /* FBSDKURLSessionTask.m */; }; - F92F8F8822BA275600494727 /* FBSDKURLSessionTask.m in Sources */ = {isa = PBXBuildFile; fileRef = F92F8F7D22BA274300494727 /* FBSDKURLSessionTask.m */; }; - F92F8F8922BA275900494727 /* FBSDKURLSessionTask.h in Headers */ = {isa = PBXBuildFile; fileRef = F92F8F7C22BA274300494727 /* FBSDKURLSessionTask.h */; }; - F92F8F8A22BA275A00494727 /* FBSDKURLSessionTask.h in Headers */ = {isa = PBXBuildFile; fileRef = F92F8F7C22BA274300494727 /* FBSDKURLSessionTask.h */; }; - F92F8F8B22BA275B00494727 /* FBSDKURLSessionTask.h in Headers */ = {isa = PBXBuildFile; fileRef = F92F8F7C22BA274300494727 /* FBSDKURLSessionTask.h */; }; - F92F8F8C22BA275C00494727 /* FBSDKURLSessionTask.h in Headers */ = {isa = PBXBuildFile; fileRef = F92F8F7C22BA274300494727 /* FBSDKURLSessionTask.h */; }; - F92F8F8D22BA275D00494727 /* FBSDKURLSessionTask.h in Headers */ = {isa = PBXBuildFile; fileRef = F92F8F7C22BA274300494727 /* FBSDKURLSessionTask.h */; }; + F931C07024F960D00088AD5F /* FBSDKSKAdNetworkRuleTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = F931C06F24F960D00088AD5F /* FBSDKSKAdNetworkRuleTests.swift */; }; F93680E3249D490600446E35 /* FBSDKSettingsTests.m in Sources */ = {isa = PBXBuildFile; fileRef = F93680D4249D490600446E35 /* FBSDKSettingsTests.m */; }; + F94310F424F8D608002441F1 /* FBSDKSKAdNetworkRule.h in Headers */ = {isa = PBXBuildFile; fileRef = F94310F224F8D608002441F1 /* FBSDKSKAdNetworkRule.h */; }; + F94310F524F8D608002441F1 /* FBSDKSKAdNetworkRule.m in Sources */ = {isa = PBXBuildFile; fileRef = F94310F324F8D608002441F1 /* FBSDKSKAdNetworkRule.m */; }; + F943110424F8D613002441F1 /* FBSDKSKAdNetworkRule.h in Headers */ = {isa = PBXBuildFile; fileRef = F94310F224F8D608002441F1 /* FBSDKSKAdNetworkRule.h */; }; + F943110524F8D615002441F1 /* FBSDKSKAdNetworkRule.m in Sources */ = {isa = PBXBuildFile; fileRef = F94310F324F8D608002441F1 /* FBSDKSKAdNetworkRule.m */; }; + F943112124FB1A54002441F1 /* FBSDKSKAdNetworkEvent.h in Headers */ = {isa = PBXBuildFile; fileRef = F943111F24FB1A54002441F1 /* FBSDKSKAdNetworkEvent.h */; }; + F943112224FB1A54002441F1 /* FBSDKSKAdNetworkEvent.m in Sources */ = {isa = PBXBuildFile; fileRef = F943112024FB1A54002441F1 /* FBSDKSKAdNetworkEvent.m */; }; + F943113124FB25A3002441F1 /* FBSDKSKAdNetworkEvent.h in Headers */ = {isa = PBXBuildFile; fileRef = F943111F24FB1A54002441F1 /* FBSDKSKAdNetworkEvent.h */; }; + F943113224FB25A7002441F1 /* FBSDKSKAdNetworkEvent.m in Sources */ = {isa = PBXBuildFile; fileRef = F943112024FB1A54002441F1 /* FBSDKSKAdNetworkEvent.m */; }; + F943113424FB2E67002441F1 /* FBSDKSKAdNetworkEventTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = F943113324FB2E67002441F1 /* FBSDKSKAdNetworkEventTests.swift */; }; F952EA472339403900B20652 /* FBSDKMetadataIndexer.m in Sources */ = {isa = PBXBuildFile; fileRef = F952EA452339403900B20652 /* FBSDKMetadataIndexer.m */; }; F952EA482339403900B20652 /* FBSDKMetadataIndexer.h in Headers */ = {isa = PBXBuildFile; fileRef = F952EA462339403900B20652 /* FBSDKMetadataIndexer.h */; }; F952EA492339405D00B20652 /* FBSDKMetadataIndexer.m in Sources */ = {isa = PBXBuildFile; fileRef = F952EA452339403900B20652 /* FBSDKMetadataIndexer.m */; }; F952EA4B2339406400B20652 /* FBSDKMetadataIndexer.h in Headers */ = {isa = PBXBuildFile; fileRef = F952EA462339403900B20652 /* FBSDKMetadataIndexer.h */; }; F952EA4F2339432100B20652 /* FBSDKMetadataIndexerTests.m in Sources */ = {isa = PBXBuildFile; fileRef = F952EA4E2339432000B20652 /* FBSDKMetadataIndexerTests.m */; }; F96ADE7A21E6ABB400F6043F /* StoreKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = F96ADE6321E6ABB400F6043F /* StoreKit.framework */; settings = {ATTRIBUTES = (Weak, ); }; }; + F98D1D3825124FAE00276B68 /* FBSDKAppEventsConfigurationFixtures.m in Sources */ = {isa = PBXBuildFile; fileRef = F98D1D3725124FAE00276B68 /* FBSDKAppEventsConfigurationFixtures.m */; }; + F98D1D5525124FCB00276B68 /* FBSDKAppEventsConfigurationTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = F98D1D5425124FCB00276B68 /* FBSDKAppEventsConfigurationTests.swift */; }; + F98D1D64251295E900276B68 /* RawAppEventsConfigurationResponseFixtures.swift in Sources */ = {isa = PBXBuildFile; fileRef = F98D1D63251295E900276B68 /* RawAppEventsConfigurationResponseFixtures.swift */; }; + F98D1D73251297A900276B68 /* FBSDKAppEventsConfigurationManagerTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = F98D1D72251297A900276B68 /* FBSDKAppEventsConfigurationManagerTests.swift */; }; + F9A06DB72510FAD3007E6386 /* FBSDKAppEventsConfiguration.h in Headers */ = {isa = PBXBuildFile; fileRef = F9A06DB52510FAD3007E6386 /* FBSDKAppEventsConfiguration.h */; }; + F9A06DB82510FAD3007E6386 /* FBSDKAppEventsConfiguration.h in Headers */ = {isa = PBXBuildFile; fileRef = F9A06DB52510FAD3007E6386 /* FBSDKAppEventsConfiguration.h */; }; + F9A06DB92510FAD3007E6386 /* FBSDKAppEventsConfiguration.m in Sources */ = {isa = PBXBuildFile; fileRef = F9A06DB62510FAD3007E6386 /* FBSDKAppEventsConfiguration.m */; }; + F9A06DBA2510FAD3007E6386 /* FBSDKAppEventsConfiguration.m in Sources */ = {isa = PBXBuildFile; fileRef = F9A06DB62510FAD3007E6386 /* FBSDKAppEventsConfiguration.m */; }; + F9A06DC92510FAF0007E6386 /* FBSDKAppEventsConfiguration.m in Sources */ = {isa = PBXBuildFile; fileRef = F9A06DB62510FAD3007E6386 /* FBSDKAppEventsConfiguration.m */; }; + F9A06DCA2510FAF0007E6386 /* FBSDKAppEventsConfiguration.m in Sources */ = {isa = PBXBuildFile; fileRef = F9A06DB62510FAD3007E6386 /* FBSDKAppEventsConfiguration.m */; }; + F9A06DCB2510FAF6007E6386 /* FBSDKAppEventsConfiguration.h in Headers */ = {isa = PBXBuildFile; fileRef = F9A06DB52510FAD3007E6386 /* FBSDKAppEventsConfiguration.h */; }; + F9A06DCC2510FAF7007E6386 /* FBSDKAppEventsConfiguration.h in Headers */ = {isa = PBXBuildFile; fileRef = F9A06DB52510FAD3007E6386 /* FBSDKAppEventsConfiguration.h */; }; + F9A06DCF2510FB0F007E6386 /* FBSDKAppEventsConfigurationManager.h in Headers */ = {isa = PBXBuildFile; fileRef = F9A06DCD2510FB0F007E6386 /* FBSDKAppEventsConfigurationManager.h */; }; + F9A06DD02510FB0F007E6386 /* FBSDKAppEventsConfigurationManager.h in Headers */ = {isa = PBXBuildFile; fileRef = F9A06DCD2510FB0F007E6386 /* FBSDKAppEventsConfigurationManager.h */; }; + F9A06DD12510FB0F007E6386 /* FBSDKAppEventsConfigurationManager.h in Headers */ = {isa = PBXBuildFile; fileRef = F9A06DCD2510FB0F007E6386 /* FBSDKAppEventsConfigurationManager.h */; }; + F9A06DD22510FB0F007E6386 /* FBSDKAppEventsConfigurationManager.h in Headers */ = {isa = PBXBuildFile; fileRef = F9A06DCD2510FB0F007E6386 /* FBSDKAppEventsConfigurationManager.h */; }; + F9A06DD32510FB0F007E6386 /* FBSDKAppEventsConfigurationManager.m in Sources */ = {isa = PBXBuildFile; fileRef = F9A06DCE2510FB0F007E6386 /* FBSDKAppEventsConfigurationManager.m */; }; + F9A06DD42510FB0F007E6386 /* FBSDKAppEventsConfigurationManager.m in Sources */ = {isa = PBXBuildFile; fileRef = F9A06DCE2510FB0F007E6386 /* FBSDKAppEventsConfigurationManager.m */; }; + F9A06DD52510FB0F007E6386 /* FBSDKAppEventsConfigurationManager.m in Sources */ = {isa = PBXBuildFile; fileRef = F9A06DCE2510FB0F007E6386 /* FBSDKAppEventsConfigurationManager.m */; }; + F9A06DD62510FB0F007E6386 /* FBSDKAppEventsConfigurationManager.m in Sources */ = {isa = PBXBuildFile; fileRef = F9A06DCE2510FB0F007E6386 /* FBSDKAppEventsConfigurationManager.m */; }; + F9A18071252150610062E634 /* AdSupport.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = F9A18062252150610062E634 /* AdSupport.framework */; }; + F9A180732521506C0062E634 /* AdSupport.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = F9A180722521506C0062E634 /* AdSupport.framework */; }; F9A80C9E237D02860019D7E0 /* Accelerate.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = F9A80C9D237D02860019D7E0 /* Accelerate.framework */; settings = {ATTRIBUTES = (Weak, ); }; }; + F9CBE51124E085CD0097B442 /* FBSDKSKAdNetworkReporter.h in Headers */ = {isa = PBXBuildFile; fileRef = F9CBE50F24E085CD0097B442 /* FBSDKSKAdNetworkReporter.h */; }; + F9CBE51224E085CD0097B442 /* FBSDKSKAdNetworkReporter.m in Sources */ = {isa = PBXBuildFile; fileRef = F9CBE51024E085CD0097B442 /* FBSDKSKAdNetworkReporter.m */; }; + F9CBE51324E085E70097B442 /* FBSDKSKAdNetworkReporter.h in Headers */ = {isa = PBXBuildFile; fileRef = F9CBE50F24E085CD0097B442 /* FBSDKSKAdNetworkReporter.h */; }; + F9CBE51424E090590097B442 /* FBSDKSKAdNetworkReporter.m in Sources */ = {isa = PBXBuildFile; fileRef = F9CBE51024E085CD0097B442 /* FBSDKSKAdNetworkReporter.m */; }; + F9CEF1EB24F769F900EB0C3D /* FBSDKSKAdNetworkReporterTests.m in Sources */ = {isa = PBXBuildFile; fileRef = F9CEF1EA24F769F900EB0C3D /* FBSDKSKAdNetworkReporterTests.m */; }; + F9DE56BA24F61B090009CD69 /* FBSDKSKAdNetworkConversionConfiguration.h in Headers */ = {isa = PBXBuildFile; fileRef = F9DE56B824F61B090009CD69 /* FBSDKSKAdNetworkConversionConfiguration.h */; }; + F9DE56BB24F61B090009CD69 /* FBSDKSKAdNetworkConversionConfiguration.m in Sources */ = {isa = PBXBuildFile; fileRef = F9DE56B924F61B090009CD69 /* FBSDKSKAdNetworkConversionConfiguration.m */; }; F9FD9A6221659F120068DEAF /* FBSDKGateKeeperManager.h in Headers */ = {isa = PBXBuildFile; fileRef = F9FD9A6121659F120068DEAF /* FBSDKGateKeeperManager.h */; }; F9FD9A6321659F120068DEAF /* FBSDKGateKeeperManager.h in Headers */ = {isa = PBXBuildFile; fileRef = F9FD9A6121659F120068DEAF /* FBSDKGateKeeperManager.h */; }; F9FD9A6421659F120068DEAF /* FBSDKGateKeeperManager.h in Headers */ = {isa = PBXBuildFile; fileRef = F9FD9A6121659F120068DEAF /* FBSDKGateKeeperManager.h */; }; @@ -1178,7 +1252,11 @@ /* Begin PBXFileReference section */ 033429B020894D4700C94913 /* FBSDKAccessTokenExpirer.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = FBSDKAccessTokenExpirer.h; sourceTree = ""; }; 033429B120894D4700C94913 /* FBSDKAccessTokenExpirer.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = FBSDKAccessTokenExpirer.m; sourceTree = ""; }; - 2A3DA4151CB4C0F600339BD4 /* FBSDKAppLinkUtilityTests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = FBSDKAppLinkUtilityTests.m; sourceTree = ""; }; + 2A3DA4151CB4C0F600339BD4 /* FBSDKAppLinkUtilityTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = FBSDKAppLinkUtilityTests.swift; sourceTree = ""; }; + 40853B9324C8C43300A7CB16 /* FBSDKJSONValueTests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = FBSDKJSONValueTests.m; sourceTree = ""; }; + 45540D8F25271A4B008E853E /* FBSDKAppLinkResolverRequestBuilder.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = FBSDKAppLinkResolverRequestBuilder.h; sourceTree = ""; }; + 45540D9025271A4B008E853E /* FBSDKAppLinkResolverRequestBuilder.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = FBSDKAppLinkResolverRequestBuilder.m; sourceTree = ""; }; + 45540DB025271A88008E853E /* FBSDKAppLinkResolverRequestBuilderTests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = FBSDKAppLinkResolverRequestBuilderTests.m; sourceTree = ""; }; 4AF47CF11F42468D00A57A67 /* FBSDKDeviceUtilities.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = FBSDKDeviceUtilities.m; sourceTree = ""; }; 4AF47CF21F42468D00A57A67 /* FBSDKDeviceUtilities.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = FBSDKDeviceUtilities.h; sourceTree = ""; }; 4AF47CFE1F424A8700A57A67 /* CoreImage.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = CoreImage.framework; path = Platforms/AppleTVOS.platform/Developer/SDKs/AppleTVOS11.0.sdk/System/Library/Frameworks/CoreImage.framework; sourceTree = DEVELOPER_DIR; }; @@ -1206,13 +1284,15 @@ 52963AA72159A13300C7B252 /* FBSDKMeasurementEvent_Internal.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = FBSDKMeasurementEvent_Internal.h; sourceTree = ""; }; 52963AAA2159A16E00C7B252 /* FBSDKMeasurementEvent.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = FBSDKMeasurementEvent.h; sourceTree = ""; }; 52963AAB2159A16E00C7B252 /* FBSDKMeasurementEvent.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = FBSDKMeasurementEvent.m; sourceTree = ""; }; + 5C49DF8F2554C9ED00E0FD91 /* SampleUserProfile.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SampleUserProfile.swift; sourceTree = ""; }; + 5C59531F25548690000D0A3C /* FBSDKCoreKitTests-Bridging-Header.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "FBSDKCoreKitTests-Bridging-Header.h"; sourceTree = ""; }; + 5CD6DB3B255B1D07002C296F /* FBSDKAppEventsUtilityTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FBSDKAppEventsUtilityTests.swift; sourceTree = ""; }; 5D2165E022264453004952D8 /* FBSDKAppEventsTests.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = FBSDKAppEventsTests.m; sourceTree = ""; }; 5D2165F12229C6A3004952D8 /* FBSDKGraphRequestTests.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = FBSDKGraphRequestTests.m; sourceTree = ""; }; 5D411318229F27DD002FF65A /* FBSDKRestrictiveDataFilterManager.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = FBSDKRestrictiveDataFilterManager.m; sourceTree = ""; }; 5D411319229F27DD002FF65A /* FBSDKRestrictiveDataFilterManager.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = FBSDKRestrictiveDataFilterManager.h; sourceTree = ""; }; 5D4360DA23219F7900254DF7 /* FBSDKCrashObserver.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = FBSDKCrashObserver.h; sourceTree = ""; }; 5D4360DF23219F8E00254DF7 /* FBSDKCrashObserver.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = FBSDKCrashObserver.m; sourceTree = ""; }; - 5D4361142321C30400254DF7 /* FBSDKCrashObserving.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = FBSDKCrashObserving.h; sourceTree = ""; }; 5D497701244A3E6A00BD45C6 /* FBSDKIntegrityTests.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = FBSDKIntegrityTests.m; sourceTree = ""; }; 5D59BFFC23A705010008CA5A /* FBSDKCrashHandlerTests.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = FBSDKCrashHandlerTests.m; sourceTree = ""; }; 5D59BFFE23A7505D0008CA5A /* FBSDKLibAnalyzerTests.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = FBSDKLibAnalyzerTests.m; sourceTree = ""; }; @@ -1223,19 +1303,18 @@ 5D81B422238739E600B02B2E /* FBSDKIntegrityManager.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = FBSDKIntegrityManager.m; sourceTree = ""; }; 5D90CDDE2343D4BD00AF326A /* FBSDKCrashShield.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = FBSDKCrashShield.m; sourceTree = ""; }; 5D90CDE52343D4D200AF326A /* FBSDKCrashShield.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = FBSDKCrashShield.h; sourceTree = ""; }; - 5D9A703F23261D6900BF9783 /* FBSDKCrashHandler.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = FBSDKCrashHandler.m; sourceTree = ""; }; - 5D9A704023261D6900BF9783 /* FBSDKCrashHandler.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = FBSDKCrashHandler.h; sourceTree = ""; }; - 5D9A704123261D6900BF9783 /* FBSDKLibAnalyzer.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = FBSDKLibAnalyzer.h; sourceTree = ""; }; - 5D9A704223261D6900BF9783 /* FBSDKLibAnalyzer.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = FBSDKLibAnalyzer.m; sourceTree = ""; }; 5DAB01F023A1831A005495FB /* FBSDKCrashObserverTests.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = FBSDKCrashObserverTests.m; sourceTree = ""; }; - 5DAB023B23A1BA17005495FB /* FBSDKEventDeactivationTests.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = FBSDKEventDeactivationTests.m; sourceTree = ""; }; + 5DAB023B23A1BA17005495FB /* FBSDKEventDeactivationTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FBSDKEventDeactivationTests.swift; sourceTree = ""; }; 5DB612A723593AB600150851 /* FBSDKCrashShieldTests.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = FBSDKCrashShieldTests.m; sourceTree = ""; }; 5DB7B07B230363190012E8CB /* FBSDKInstrumentManager.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = FBSDKInstrumentManager.h; sourceTree = ""; }; 5DB7B08D2303632E0012E8CB /* FBSDKInstrumentManager.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = FBSDKInstrumentManager.m; sourceTree = ""; }; 5DBB0446227FEF700009E0A6 /* FBSDKBasicUtilityTests.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = FBSDKBasicUtilityTests.m; sourceTree = ""; }; 5DBC72D02373792A00A9D859 /* FBSDKModelManager.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = FBSDKModelManager.h; sourceTree = ""; }; 5DBC72D72373795600A9D859 /* FBSDKModelManager.mm */ = {isa = PBXFileReference; explicitFileType = sourcecode.cpp.objcpp; path = FBSDKModelManager.mm; sourceTree = ""; }; - 5DEF22CE226A3E06004056C1 /* FBSDKBasicUtility.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = FBSDKBasicUtility.m; sourceTree = ""; }; + 5E3AFFE5258C191200BE46AB /* FBSDKAuthenticationTokenHeader.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = FBSDKAuthenticationTokenHeader.h; sourceTree = ""; }; + 5E3AFFE6258C191200BE46AB /* FBSDKAuthenticationTokenHeader.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = FBSDKAuthenticationTokenHeader.m; sourceTree = ""; }; + 5E47555B2580577B00C98E30 /* FBSDKAuthenticationTokenClaims.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = FBSDKAuthenticationTokenClaims.h; sourceTree = ""; }; + 5E47555C2580577C00C98E30 /* FBSDKAuthenticationTokenClaims.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = FBSDKAuthenticationTokenClaims.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 = ""; }; @@ -1245,7 +1324,7 @@ 7E3091791AA907E0004E91D5 /* FBSDKAppLinkUtility.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = FBSDKAppLinkUtility.m; sourceTree = ""; }; 7E5557351A8D833100344F86 /* FBSDKAppLinkResolver.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = FBSDKAppLinkResolver.m; sourceTree = ""; }; 7E5557361A8D833100344F86 /* FBSDKAppLinkResolver.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = FBSDKAppLinkResolver.h; sourceTree = ""; }; - 7E55573A1A8D834B00344F86 /* FBSDKAppLinkResolverTests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = FBSDKAppLinkResolverTests.m; path = AppLinks/FBSDKAppLinkResolverTests.m; sourceTree = ""; }; + 7E55573A1A8D834B00344F86 /* FBSDKAppLinkResolverTests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = FBSDKAppLinkResolverTests.m; sourceTree = ""; }; 7E55573F1A8E89A800344F86 /* OHHTTPStubs.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = OHHTTPStubs.xcodeproj; path = ../Carthage/Checkouts/OHHTTPStubs/OHHTTPStubs/OHHTTPStubs.xcodeproj; sourceTree = ""; }; 7EB63D9D1A9BE730003A7AED /* FBSDKMeasurementEventListener.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = FBSDKMeasurementEventListener.m; sourceTree = ""; }; 7EB63D9E1A9BE730003A7AED /* FBSDKMeasurementEventListener.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = FBSDKMeasurementEventListener.h; sourceTree = ""; }; @@ -1311,7 +1390,7 @@ 894C0B0F1A7021F8009137EF /* FBSDKBase64.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = FBSDKBase64.h; sourceTree = ""; }; 894C0B101A7021F8009137EF /* FBSDKBase64.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = FBSDKBase64.m; sourceTree = ""; }; 894C0B391A702EBE009137EF /* FBSDKBridgeAPIProtocolNativeV1Tests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = FBSDKBridgeAPIProtocolNativeV1Tests.m; sourceTree = ""; }; - 894C0B491A70524F009137EF /* FBSDKBase64Tests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = FBSDKBase64Tests.m; sourceTree = ""; }; + 894C0B491A70524F009137EF /* FBSDKBase64Tests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = FBSDKBase64Tests.swift; sourceTree = ""; }; 894C0B5F1A7150AC009137EF /* FBSDKError.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = FBSDKError.h; sourceTree = ""; }; 894C0B601A7150AC009137EF /* FBSDKError.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = FBSDKError.m; sourceTree = ""; }; 894C0B661A7150CC009137EF /* FBSDKConstants.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = FBSDKConstants.h; sourceTree = ""; }; @@ -1329,13 +1408,11 @@ 89830F321A78060A00226ABB /* FBSDKDialogConfiguration.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = FBSDKDialogConfiguration.m; sourceTree = ""; }; 899C3CF61A8BF73A00EA8658 /* FBSDKProfilePictureView.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = FBSDKProfilePictureView.h; sourceTree = ""; }; 899C3CF71A8BF73A00EA8658 /* FBSDKProfilePictureView.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = FBSDKProfilePictureView.m; sourceTree = ""; }; - 899C3D001A8C1ED200EA8658 /* FBSDKMaleSilhouetteIcon.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = FBSDKMaleSilhouetteIcon.h; sourceTree = ""; }; - 899C3D011A8C1ED200EA8658 /* FBSDKMaleSilhouetteIcon.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = FBSDKMaleSilhouetteIcon.m; sourceTree = ""; }; + 899C3D001A8C1ED200EA8658 /* FBSDKHumanSilhouetteIcon.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = FBSDKHumanSilhouetteIcon.h; sourceTree = ""; }; + 899C3D011A8C1ED200EA8658 /* FBSDKHumanSilhouetteIcon.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = FBSDKHumanSilhouetteIcon.m; sourceTree = ""; }; 89C8B1971A8D7A15009B07F5 /* FBSDKUtility.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = FBSDKUtility.h; sourceTree = ""; }; 89C8B1981A8D7A15009B07F5 /* FBSDKUtility.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = FBSDKUtility.m; sourceTree = ""; }; - 89C8B19B1A8D7A27009B07F5 /* FBSDKUtilityTests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = FBSDKUtilityTests.m; sourceTree = ""; }; - 89D05A931AA0E89B00609300 /* FBSDKTriStateBOOL.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = FBSDKTriStateBOOL.h; sourceTree = ""; }; - 89D05A941AA0E89B00609300 /* FBSDKTriStateBOOL.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = FBSDKTriStateBOOL.m; sourceTree = ""; }; + 89C8B19B1A8D7A27009B07F5 /* FBSDKUtilityTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = FBSDKUtilityTests.swift; sourceTree = ""; }; 89D05AA71AA1134000609300 /* FBSDKAudioResourceLoader.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = FBSDKAudioResourceLoader.h; sourceTree = ""; }; 89D05AA81AA1134000609300 /* FBSDKAudioResourceLoader.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = FBSDKAudioResourceLoader.m; sourceTree = ""; }; 89D4AE851A7FFEFC00DB8C72 /* FBSDKBridgeAPIRequest+Private.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "FBSDKBridgeAPIRequest+Private.h"; sourceTree = ""; }; @@ -1381,11 +1458,11 @@ 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 = ""; }; - 9D32A83B1A69941A000A936D /* FBSDKAccessTokenCaching.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = FBSDKAccessTokenCaching.h; sourceTree = ""; }; + 9D32A83B1A69941A000A936D /* FBSDKTokenCaching.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = FBSDKTokenCaching.h; sourceTree = ""; }; 9D32A83C1A69941A000A936D /* FBSDKKeychainStore.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = FBSDKKeychainStore.h; sourceTree = ""; }; 9D32A83D1A69941A000A936D /* FBSDKKeychainStore.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = FBSDKKeychainStore.m; sourceTree = ""; }; - 9D32A8431A699459000A936D /* FBSDKAccessTokenCache.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = FBSDKAccessTokenCache.h; sourceTree = ""; }; - 9D32A8441A699459000A936D /* FBSDKAccessTokenCache.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = FBSDKAccessTokenCache.m; sourceTree = ""; }; + 9D32A8431A699459000A936D /* FBSDKTokenCache.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = FBSDKTokenCache.h; sourceTree = ""; }; + 9D32A8441A699459000A936D /* FBSDKTokenCache.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = FBSDKTokenCache.m; sourceTree = ""; }; 9D34A11D1A5F038300C37317 /* FBSDKApplicationDelegate.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = FBSDKApplicationDelegate.h; sourceTree = ""; }; 9D34A11E1A5F038300C37317 /* FBSDKApplicationDelegate.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = FBSDKApplicationDelegate.m; sourceTree = ""; }; 9D3AF44E1A9EA4BE00EEF724 /* FBSDKErrorConfiguration.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = FBSDKErrorConfiguration.h; sourceTree = ""; }; @@ -1443,6 +1520,8 @@ 9DF2A3FE1A70572B00DFB2FD /* FBSDKGraphRequestMetadata.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = FBSDKGraphRequestMetadata.m; sourceTree = ""; }; ADEA17711B7ECA1A0070EDC0 /* FBSDKMonotonicTime.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = FBSDKMonotonicTime.h; sourceTree = ""; }; ADEA17721B7ECA1A0070EDC0 /* FBSDKMonotonicTime.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = FBSDKMonotonicTime.m; sourceTree = ""; }; + B301910D253622A2001A8DB0 /* FBSDKProfilePictureView+Internal.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "FBSDKProfilePictureView+Internal.h"; sourceTree = ""; }; + B3C20ACC250B100F00DEC008 /* FBSDKAccessToken+Internal.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "FBSDKAccessToken+Internal.h"; sourceTree = ""; }; BF1C64E524039AD50052C580 /* FBSDKViewHierarchyTests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = FBSDKViewHierarchyTests.m; sourceTree = ""; }; BF247C802374E1B100A522C0 /* FBSDKSuggestedEventsIndexer.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = FBSDKSuggestedEventsIndexer.h; sourceTree = ""; }; BF247C812374E1B200A522C0 /* FBSDKSuggestedEventsIndexer.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = FBSDKSuggestedEventsIndexer.m; sourceTree = ""; }; @@ -1454,9 +1533,8 @@ C4513900202CBF1F0063275D /* WebKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = WebKit.framework; path = Platforms/iPhoneOS.platform/Developer/SDKs/iPhoneOS11.2.sdk/System/Library/Frameworks/WebKit.framework; sourceTree = DEVELOPER_DIR; }; C4FC99D7202CD5590038C5ED /* FBSDKHybridAppEventsScriptMessageHandler.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = FBSDKHybridAppEventsScriptMessageHandler.h; sourceTree = ""; }; C4FC99D8202CD5590038C5ED /* FBSDKHybridAppEventsScriptMessageHandler.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = FBSDKHybridAppEventsScriptMessageHandler.m; sourceTree = ""; }; - C51121C820A27EF50041DC94 /* FBSDKSampleEventBinding.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = FBSDKSampleEventBinding.h; sourceTree = ""; }; C51121C920A27EF50041DC94 /* FBSDKEventBindingTests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = FBSDKEventBindingTests.m; sourceTree = ""; }; - C51121CA20A27EF50041DC94 /* FBSDKSampleEventBinding.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = FBSDKSampleEventBinding.m; sourceTree = ""; }; + C51121CA20A27EF50041DC94 /* FBSDKSampleEventBinding.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = FBSDKSampleEventBinding.swift; sourceTree = ""; }; C511229F20A4BCEB0041DC94 /* FBSDKApplicationDelegateTests.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = FBSDKApplicationDelegateTests.m; sourceTree = ""; }; C5188DF8222F388400F4D8BC /* FBSDKApplicationObserving.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = FBSDKApplicationObserving.h; sourceTree = ""; }; C5188E312231C4B500F4D8BC /* FBSDKBridgeAPI.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = FBSDKBridgeAPI.h; sourceTree = ""; }; @@ -1475,30 +1553,46 @@ C5696F32209BBC35009C931F /* FBSDKCodelessParameterComponent.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = FBSDKCodelessParameterComponent.h; sourceTree = ""; }; C5696FA1209CCEB3009C931F /* FBSDKSwizzler.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = FBSDKSwizzler.h; sourceTree = ""; }; C5696FA2209CCEB4009C931F /* FBSDKSwizzler.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = FBSDKSwizzler.m; sourceTree = ""; }; + C57044CD24E26678009637AD /* FBSDKUserDataStore.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = FBSDKUserDataStore.h; sourceTree = ""; }; + C57044CE24E26678009637AD /* FBSDKUserDataStore.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = FBSDKUserDataStore.m; sourceTree = ""; }; C5C4B3B62276B51500CA3706 /* FBSDKCoreKit.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = FBSDKCoreKit.framework; sourceTree = BUILT_PRODUCTS_DIR; }; C5C4B3CC2276B67200CA3706 /* FBSDKCoreKit.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = FBSDKCoreKit.framework; sourceTree = BUILT_PRODUCTS_DIR; }; - C5C4B3E32276B88600CA3706 /* FBSDKTypeUtility.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = FBSDKTypeUtility.h; sourceTree = ""; }; - C5C4B3E42276B88600CA3706 /* FBSDKBasicUtility.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = FBSDKBasicUtility.h; sourceTree = ""; }; - C5C4B3E52276B88600CA3706 /* FBSDKTypeUtility.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = FBSDKTypeUtility.m; sourceTree = ""; }; C5C4B4D42276BA1200CA3706 /* FBSDKCoreKit.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = FBSDKCoreKit.framework; sourceTree = BUILT_PRODUCTS_DIR; }; C5C4B4EA2276BA8D00CA3706 /* FBSDKCoreKit.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = FBSDKCoreKit.framework; sourceTree = BUILT_PRODUCTS_DIR; }; C5C7B74822D84F64004A5A0C /* FBSDKFeatureManager.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = FBSDKFeatureManager.h; sourceTree = ""; }; C5C7B74922D84F64004A5A0C /* FBSDKFeatureManager.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = FBSDKFeatureManager.m; sourceTree = ""; }; C5D25D3421795B790037B13D /* FBSDKCodelessIndexer.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = FBSDKCodelessIndexer.h; sourceTree = ""; }; C5D25D3521795B790037B13D /* FBSDKCodelessIndexer.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = FBSDKCodelessIndexer.m; sourceTree = ""; }; + C5EAFA5D255283C100458DF5 /* FBSDKUserDataStore+Internal.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "FBSDKUserDataStore+Internal.h"; sourceTree = ""; }; C5F6EC851FA24FAF009EB258 /* FBSDKPaymentObserverTests.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = FBSDKPaymentObserverTests.m; sourceTree = ""; }; + C6381619257EDF2100B7690F /* FBSDKAuthenticationTokenFactoryTests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = FBSDKAuthenticationTokenFactoryTests.m; sourceTree = ""; }; + C67BED29257F1A6300D356C9 /* ProfilePictureViewTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ProfilePictureViewTests.swift; sourceTree = ""; }; + C6A308BA257AD1FF003C52FD /* FBSDKAuthenticationTokenFactory.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = FBSDKAuthenticationTokenFactory.h; sourceTree = ""; }; + C6A308BB257AD1FF003C52FD /* FBSDKAuthenticationTokenFactory.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = FBSDKAuthenticationTokenFactory.m; sourceTree = ""; }; + C6C5CBCA2581871B00AA3BB3 /* FBSDKAuthenticationStatusUtility.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = FBSDKAuthenticationStatusUtility.m; sourceTree = ""; }; + C6C5CBCB2581871B00AA3BB3 /* FBSDKAuthenticationStatusUtility.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = FBSDKAuthenticationStatusUtility.h; sourceTree = ""; }; + C6C5CBDB2581875600AA3BB3 /* FBSDKAuthenticationStatusUtilityTests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = FBSDKAuthenticationStatusUtilityTests.m; sourceTree = ""; }; C77C07A52486BAC600345460 /* FBSDKRestrictiveDataFilterTests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = FBSDKRestrictiveDataFilterTests.m; sourceTree = ""; }; E4416BFF23F61902009CCBFA /* FBSDKModelParser.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = FBSDKModelParser.h; sourceTree = ""; }; E4416C0023F61902009CCBFA /* FBSDKModelParser.mm */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.objcpp; path = FBSDKModelParser.mm; sourceTree = ""; }; E493252C23F7C60E0000B63A /* FBSDKModelParserTests.mm */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.objcpp; path = FBSDKModelParserTests.mm; sourceTree = ""; }; E4C2B97923867327002335A4 /* FBSDKModelRuntimeTests.mm */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.objcpp; path = FBSDKModelRuntimeTests.mm; sourceTree = ""; }; + F402AFC7255B49A500473083 /* FBSDKBridgeAPITests.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = FBSDKBridgeAPITests.m; sourceTree = ""; }; + F402AFE4255B4D1C00473083 /* FBSDKBridgeAPI+Testing.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "FBSDKBridgeAPI+Testing.h"; sourceTree = ""; }; + F402AFF2255B4EE400473083 /* AuthenticationSessionSpy.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AuthenticationSessionSpy.swift; sourceTree = ""; }; + F402B060255C645600473083 /* FakeLoginManager.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = FakeLoginManager.h; sourceTree = ""; }; + F402B061255C645600473083 /* FakeLoginManager.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = FakeLoginManager.m; sourceTree = ""; }; + F402B070255C740C00473083 /* ViewControllerSpy.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ViewControllerSpy.swift; sourceTree = ""; }; + F408C6BF2563091900372D61 /* FBSDKBridgeAPIOpenBridgeRequestTests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = FBSDKBridgeAPIOpenBridgeRequestTests.m; sourceTree = ""; }; + F408C6E8256309A500372D61 /* FakeBridgeApiRequest.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FakeBridgeApiRequest.swift; sourceTree = ""; }; + F408C711256320F300372D61 /* FBSDKBridgeAPIRequestTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FBSDKBridgeAPIRequestTests.swift; sourceTree = ""; }; F40B24B1247324BB0059351C /* RawServerConfigurationResponseFixtures.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RawServerConfigurationResponseFixtures.swift; sourceTree = ""; }; F40B24C124732DD90059351C /* Fuzzer.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Fuzzer.swift; sourceTree = ""; }; F40F6550241A974400B10EAA /* FBSDKMethodUsageMonitorEntryTests.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = FBSDKMethodUsageMonitorEntryTests.m; sourceTree = ""; }; F40F655F241A99E000B10EAA /* FBSDKMethodUsageMonitorEntry.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = FBSDKMethodUsageMonitorEntry.h; sourceTree = ""; }; F40F6560241A99E000B10EAA /* FBSDKMethodUsageMonitorEntry.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = FBSDKMethodUsageMonitorEntry.m; sourceTree = ""; }; - F413881D24C76E0A001BC075 /* FBSDKSafeCast.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = FBSDKSafeCast.h; sourceTree = ""; }; - F413883424C76E78001BC075 /* FBSDKSafeCast.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = FBSDKSafeCast.m; sourceTree = ""; }; + F41200CF255F45FE009E323F /* FBSDKBridgeAPI+ApplicationOpenUrlTests.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = "FBSDKBridgeAPI+ApplicationOpenUrlTests.m"; sourceTree = ""; }; + F41200EC255F4775009E323F /* FBSDKBridgeAPITests.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = FBSDKBridgeAPITests.h; sourceTree = ""; }; F413883D24C76F3B001BC075 /* FBSDKSafeCastTests.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; name = FBSDKSafeCastTests.m; path = Internal/Basics/FBSDKSafeCastTests.m; sourceTree = ""; }; F4181B332416EC06006DB452 /* FBSDKMonitorTests.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = FBSDKMonitorTests.m; sourceTree = ""; }; F41979272475A20E003007CC /* FBSDKTypeUtilityTests.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = FBSDKTypeUtilityTests.m; sourceTree = ""; }; @@ -1511,48 +1605,114 @@ F4210E1D241AFF740061F56D /* FBSDKMethodUsageMonitorTests.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = FBSDKMethodUsageMonitorTests.m; sourceTree = ""; }; F4210E2C241B00790061F56D /* FBSDKMethodUsageMonitor.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = FBSDKMethodUsageMonitor.h; sourceTree = ""; }; F4210E2D241B00790061F56D /* FBSDKMethodUsageMonitor.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = FBSDKMethodUsageMonitor.m; sourceTree = ""; }; - F428430A246B427700CD4393 /* FBSDKServerConfigurationManagerTests.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = FBSDKServerConfigurationManagerTests.m; sourceTree = ""; }; - F43960CE2425513100C1868F /* FakeMonitorStore.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = FakeMonitorStore.h; sourceTree = ""; }; - F43960CF2425513100C1868F /* FakeMonitorStore.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = FakeMonitorStore.m; sourceTree = ""; }; - F46C4847243E34C600D03401 /* FBSDKMonitoringConfigurationTestHelper.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = FBSDKMonitoringConfigurationTestHelper.h; path = FBSDKCoreKitTests/Internal/Helpers/FBSDKMonitoringConfigurationTestHelper.h; sourceTree = SOURCE_ROOT; }; - F46C4856243E34C600D03401 /* FBSDKMonitoringConfigurationTestHelper.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = FBSDKMonitoringConfigurationTestHelper.m; path = FBSDKCoreKitTests/Internal/Helpers/FBSDKMonitoringConfigurationTestHelper.m; sourceTree = SOURCE_ROOT; }; + F428430A246B427700CD4393 /* FBSDKServerConfigurationManagerTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FBSDKServerConfigurationManagerTests.swift; sourceTree = ""; }; + F43960CF2425513100C1868F /* FakeMonitorStore.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FakeMonitorStore.swift; sourceTree = ""; }; + F44B04A8256442050059A3A6 /* FBSDKBridgeAPIOpenUrlWithSafariTests.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = FBSDKBridgeAPIOpenUrlWithSafariTests.m; sourceTree = ""; }; + F44B04EC256456140059A3A6 /* FBSDKDynamicFrameworkResolving.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = FBSDKDynamicFrameworkResolving.h; sourceTree = ""; }; + F44B051025645DAC0059A3A6 /* FakeDylibResolver.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FakeDylibResolver.swift; sourceTree = ""; }; + F44B056E256491590059A3A6 /* FBSDKBridgeAPI+SessionCompletionHandlerTests.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = "FBSDKBridgeAPI+SessionCompletionHandlerTests.m"; sourceTree = ""; }; + F468B29C24C2456F00979F8D /* FBSDKRestrictiveData.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = FBSDKRestrictiveData.h; sourceTree = ""; }; + F468B29D24C2456F00979F8D /* FBSDKRestrictiveData.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = FBSDKRestrictiveData.m; sourceTree = ""; }; + F468B2E024C25AB500979F8D /* FBSDKTypeUtility.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = FBSDKTypeUtility.m; sourceTree = ""; }; + F468B2E224C25AB500979F8D /* FBSDKURLSessionTask.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = FBSDKURLSessionTask.m; sourceTree = ""; }; + F468B2E324C25AB500979F8D /* FBSDKCrashHandler.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = FBSDKCrashHandler.m; sourceTree = ""; }; + F468B2E724C25AB500979F8D /* FBSDKBasicUtility.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = FBSDKBasicUtility.m; sourceTree = ""; }; + F468B2E824C25AB600979F8D /* FBSDKURLSession.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = FBSDKURLSession.m; sourceTree = ""; }; + F468B2E924C25AB600979F8D /* FBSDKLibAnalyzer.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = FBSDKLibAnalyzer.m; sourceTree = ""; }; + F46C4856243E34C600D03401 /* FBSDKMonitoringConfigurationTestHelper.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = FBSDKMonitoringConfigurationTestHelper.swift; path = FBSDKCoreKitTests/Internal/Helpers/FBSDKMonitoringConfigurationTestHelper.swift; sourceTree = SOURCE_ROOT; }; + F47E55FB24E1EA8D001497C9 /* FakeBundle.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = FakeBundle.m; sourceTree = ""; }; + F47E560C24E1EA8D001497C9 /* FakeBundle.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = FakeBundle.h; sourceTree = ""; }; F487DBCB231EC1A0008416A9 /* Permission.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Permission.swift; sourceTree = ""; }; F487DBCD231EC20E008416A9 /* Settings.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Settings.swift; sourceTree = ""; }; F487DBD9231EC293008416A9 /* AccessToken.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AccessToken.swift; sourceTree = ""; }; F48A6AEB24170D29002C6CA1 /* TestMonitorEntry.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = TestMonitorEntry.h; sourceTree = ""; }; F48A6AEC24170D29002C6CA1 /* TestMonitorEntry.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = TestMonitorEntry.m; sourceTree = ""; }; + F48AD2E32576FE050052C056 /* FBSDKAuthenticationToken.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = FBSDKAuthenticationToken.m; sourceTree = ""; }; + F48AD2E42576FE050052C056 /* FBSDKAuthenticationToken.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = FBSDKAuthenticationToken.h; sourceTree = ""; }; + F48AD3242576FEB20052C056 /* FBSDKAuthenticationTokenTests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = FBSDKAuthenticationTokenTests.m; sourceTree = ""; }; + F48AD347257700B80052C056 /* SampleAuthenticationToken.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SampleAuthenticationToken.swift; sourceTree = ""; }; + F48AD3702577066A0052C056 /* FBSDKAuthenticationToken+Internal.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "FBSDKAuthenticationToken+Internal.h"; sourceTree = ""; }; + F492C09F24E1F5F600BA21F7 /* UserDefaultsSpy.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = UserDefaultsSpy.h; sourceTree = ""; }; + F492C0A024E1F5F600BA21F7 /* KeychainStoreSpy.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = KeychainStoreSpy.swift; sourceTree = ""; }; + F492C0A124E1F5F600BA21F7 /* UserDefaultsSpy.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = UserDefaultsSpy.m; sourceTree = ""; }; + F494284025829B98008FE009 /* FBSDKSessionProviding.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = FBSDKSessionProviding.h; sourceTree = ""; }; + F494285C25829C72008FE009 /* FakeSessionProvider.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FakeSessionProvider.swift; sourceTree = ""; }; + F496E58E24E49DBC006231A2 /* SampleAppEvents.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; name = SampleAppEvents.swift; path = FBSDKCoreKitTests/Internal/Helpers/SampleAppEvents.swift; sourceTree = SOURCE_ROOT; }; + F496E5FD24E5D0D5006231A2 /* FakeTokenCache.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FakeTokenCache.swift; sourceTree = ""; }; + F496E60E24E5D195006231A2 /* SampleAccessToken.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SampleAccessToken.swift; sourceTree = ""; }; + F496E61124E6049A006231A2 /* AppDelegateObserverFake.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDelegateObserverFake.swift; sourceTree = ""; }; + F4A11B4824E36E5200D4C010 /* FBSDKTypeUtility.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = FBSDKTypeUtility.h; sourceTree = ""; }; + F4A11B4924E36E5200D4C010 /* FBSDKURLSessionTask.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = FBSDKURLSessionTask.h; sourceTree = ""; }; + F4A11B4A24E36E5200D4C010 /* FBSDKSafeCast.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = FBSDKSafeCast.h; sourceTree = ""; }; + F4A11B4B24E36E5200D4C010 /* FBSDKCrashHandler.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = FBSDKCrashHandler.h; sourceTree = ""; }; + F4A11B4C24E36E5200D4C010 /* FBSDKLibAnalyzer.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = FBSDKLibAnalyzer.h; sourceTree = ""; }; + F4A11B4D24E36E5200D4C010 /* FBSDKBasicUtility.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = FBSDKBasicUtility.h; sourceTree = ""; }; + F4A11B4E24E36E5200D4C010 /* FBSDKJSONValue.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = FBSDKJSONValue.h; sourceTree = ""; }; + F4A11B4F24E36E5200D4C010 /* FBSDKURLSession.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = FBSDKURLSession.h; sourceTree = ""; }; + F4A11B5024E36E5200D4C010 /* FBSDKCoreKit_Basics.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = FBSDKCoreKit_Basics.h; sourceTree = ""; }; + F4A11B5124E36E5200D4C010 /* FBSDKCrashObserving.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = FBSDKCrashObserving.h; sourceTree = ""; }; F4A52AEC242A7D12005F65CE /* FBSDKPerformanceMonitorEntryTests.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = FBSDKPerformanceMonitorEntryTests.m; sourceTree = ""; }; F4A52AFC242A80F4005F65CE /* FBSDKPerformanceMonitorEntry.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = FBSDKPerformanceMonitorEntry.h; sourceTree = ""; }; F4A52AFD242A80F4005F65CE /* FBSDKPerformanceMonitorEntry.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = FBSDKPerformanceMonitorEntry.m; sourceTree = ""; }; F4A52B0A242A99C8005F65CE /* FBSDKPerformanceMonitorTests.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = FBSDKPerformanceMonitorTests.m; sourceTree = ""; }; F4A52B0C242AA532005F65CE /* FBSDKPerformanceMonitor.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = FBSDKPerformanceMonitor.h; sourceTree = ""; }; F4A52B0D242AA532005F65CE /* FBSDKPerformanceMonitor.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = FBSDKPerformanceMonitor.m; sourceTree = ""; }; - F4B3CA3122B3FED80098ADF5 /* ExampleSwiftTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ExampleSwiftTests.swift; sourceTree = ""; }; + F4A826B624EC6FAC00EB2CD4 /* FBSDKProfileTests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = FBSDKProfileTests.m; sourceTree = ""; }; F4BD4022241EEDE500B45D39 /* FBSDKTestCoder.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = FBSDKTestCoder.h; sourceTree = ""; }; F4BD4023241EEDE500B45D39 /* FBSDKTestCoder.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = FBSDKTestCoder.m; sourceTree = ""; }; F4BF2292241954B400BFB494 /* FBSDKMonitorStoreTests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = FBSDKMonitorStoreTests.m; sourceTree = ""; }; F4BF22A1241954D800BFB494 /* FBSDKMonitorStore.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = FBSDKMonitorStore.h; sourceTree = ""; }; F4BF22A2241954D800BFB494 /* FBSDKMonitorStore.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = FBSDKMonitorStore.m; sourceTree = ""; }; + F4D80B572565D6B2008CCAF0 /* FBSDKRestrictiveDataTests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = FBSDKRestrictiveDataTests.m; sourceTree = ""; }; + F4D80B802565E1C2008CCAF0 /* FBSDKInstrumentManagerTests.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = FBSDKInstrumentManagerTests.m; sourceTree = ""; }; + F4D80C072566D469008CCAF0 /* FBSDKDrawableTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FBSDKDrawableTests.swift; sourceTree = ""; }; + F4D80C3F2566D856008CCAF0 /* TestAssets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = TestAssets.xcassets; sourceTree = ""; }; F4D8D8DA2422887600C28384 /* FBSDKMonitorNetworker.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = FBSDKMonitorNetworker.h; sourceTree = ""; }; F4D8D8DB2422887600C28384 /* FBSDKMonitorNetworker.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = FBSDKMonitorNetworker.m; sourceTree = ""; }; F4D8D8F9242293ED00C28384 /* FBSDKMonitorNetworkerTests.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = FBSDKMonitorNetworkerTests.m; sourceTree = ""; }; F4D8D8FB2422B88B00C28384 /* FBSDKMonitorHeaders.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = FBSDKMonitorHeaders.h; sourceTree = ""; }; + F4DE31F424DB695100297C18 /* FBSDKTestCase.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = FBSDKTestCase.h; sourceTree = ""; }; + F4DE31F524DB695100297C18 /* FBSDKTestCase.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = FBSDKTestCase.m; sourceTree = ""; }; F4E1EB9F2444E0AB006FB48E /* Analyzer.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; name = Analyzer.xcconfig; path = ../../../Configurations/Analyzer.xcconfig; sourceTree = ""; }; F4E50152243648A100C99262 /* FBSDKServerConfigurationFixtures.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = FBSDKServerConfigurationFixtures.h; sourceTree = ""; }; F4E50153243648A100C99262 /* FBSDKServerConfigurationFixtures.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = FBSDKServerConfigurationFixtures.m; sourceTree = ""; }; F4E50154243648A100C99262 /* FBSDKServerConfigurationTests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = FBSDKServerConfigurationTests.m; sourceTree = ""; }; - F9098FC122BC332C00857C2D /* FBSDKURLSession.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = FBSDKURLSession.h; sourceTree = ""; }; - F9098FC222BC332C00857C2D /* FBSDKURLSession.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = FBSDKURLSession.m; sourceTree = ""; }; - F9169B822155A02000FA1789 /* FBSDKUserDataStore.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = FBSDKUserDataStore.h; sourceTree = ""; }; - F9169B832155A03C00FA1789 /* FBSDKUserDataStore.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = FBSDKUserDataStore.m; sourceTree = ""; }; - F92F8F7C22BA274300494727 /* FBSDKURLSessionTask.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = FBSDKURLSessionTask.h; sourceTree = ""; }; - F92F8F7D22BA274300494727 /* FBSDKURLSessionTask.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = FBSDKURLSessionTask.m; sourceTree = ""; }; + F4EB317E2540955500736B67 /* FBSDKGraphRequestPiggybackManagerTests.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = FBSDKGraphRequestPiggybackManagerTests.m; sourceTree = ""; }; + F4EB31BA2540A1B800736B67 /* SampleGraphRequestConnection.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SampleGraphRequestConnection.swift; sourceTree = ""; }; + F4EB31D22540A35800736B67 /* SampleGraphRequest.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SampleGraphRequest.swift; sourceTree = ""; }; + F4EB33CD2542323500736B67 /* SampleRawRemotePermissions.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SampleRawRemotePermissions.swift; sourceTree = ""; }; + F4ED90DA24EDDC610048D283 /* NotificationCenterSpy.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NotificationCenterSpy.swift; sourceTree = ""; }; + F4F98C4A24CB820A00F0D6EC /* FBSDKSafeCast.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = FBSDKSafeCast.m; sourceTree = ""; }; + F4FE997D24D906B9008879A4 /* FBSDKJSONValue.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = FBSDKJSONValue.m; sourceTree = ""; }; + F916581424F6BAB200BB759A /* FBSDKSKAdNetworkConversionConfigurationTests.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = FBSDKSKAdNetworkConversionConfigurationTests.m; sourceTree = ""; }; + F931C06F24F960D00088AD5F /* FBSDKSKAdNetworkRuleTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FBSDKSKAdNetworkRuleTests.swift; sourceTree = ""; }; F93680D4249D490600446E35 /* FBSDKSettingsTests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = FBSDKSettingsTests.m; path = Internal/FBSDKSettingsTests.m; sourceTree = ""; }; + F94310F224F8D608002441F1 /* FBSDKSKAdNetworkRule.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = FBSDKSKAdNetworkRule.h; sourceTree = ""; }; + F94310F324F8D608002441F1 /* FBSDKSKAdNetworkRule.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = FBSDKSKAdNetworkRule.m; sourceTree = ""; }; + F943111F24FB1A54002441F1 /* FBSDKSKAdNetworkEvent.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = FBSDKSKAdNetworkEvent.h; sourceTree = ""; }; + F943112024FB1A54002441F1 /* FBSDKSKAdNetworkEvent.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = FBSDKSKAdNetworkEvent.m; sourceTree = ""; }; + F943113324FB2E67002441F1 /* FBSDKSKAdNetworkEventTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FBSDKSKAdNetworkEventTests.swift; sourceTree = ""; }; F952EA452339403900B20652 /* FBSDKMetadataIndexer.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = FBSDKMetadataIndexer.m; sourceTree = ""; }; F952EA462339403900B20652 /* FBSDKMetadataIndexer.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = FBSDKMetadataIndexer.h; sourceTree = ""; }; F952EA4E2339432000B20652 /* FBSDKMetadataIndexerTests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = FBSDKMetadataIndexerTests.m; sourceTree = ""; }; F96ADE6321E6ABB400F6043F /* StoreKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = StoreKit.framework; path = System/Library/Frameworks/StoreKit.framework; sourceTree = SDKROOT; }; + F98D1D3625124FAE00276B68 /* FBSDKAppEventsConfigurationFixtures.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = FBSDKAppEventsConfigurationFixtures.h; sourceTree = ""; }; + F98D1D3725124FAE00276B68 /* FBSDKAppEventsConfigurationFixtures.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = FBSDKAppEventsConfigurationFixtures.m; sourceTree = ""; }; + F98D1D5425124FCB00276B68 /* FBSDKAppEventsConfigurationTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FBSDKAppEventsConfigurationTests.swift; sourceTree = ""; }; + F98D1D63251295E900276B68 /* RawAppEventsConfigurationResponseFixtures.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RawAppEventsConfigurationResponseFixtures.swift; sourceTree = ""; }; + F98D1D72251297A900276B68 /* FBSDKAppEventsConfigurationManagerTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FBSDKAppEventsConfigurationManagerTests.swift; sourceTree = ""; }; + F9A06DB52510FAD3007E6386 /* FBSDKAppEventsConfiguration.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = FBSDKAppEventsConfiguration.h; sourceTree = ""; }; + F9A06DB62510FAD3007E6386 /* FBSDKAppEventsConfiguration.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = FBSDKAppEventsConfiguration.m; sourceTree = ""; }; + F9A06DCD2510FB0F007E6386 /* FBSDKAppEventsConfigurationManager.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = FBSDKAppEventsConfigurationManager.h; sourceTree = ""; }; + F9A06DCE2510FB0F007E6386 /* FBSDKAppEventsConfigurationManager.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = FBSDKAppEventsConfigurationManager.m; sourceTree = ""; }; + F9A18062252150610062E634 /* AdSupport.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = AdSupport.framework; path = Platforms/AppleTVOS.platform/Developer/SDKs/AppleTVOS14.0.sdk/System/Library/Frameworks/AdSupport.framework; sourceTree = DEVELOPER_DIR; }; + F9A180722521506C0062E634 /* AdSupport.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = AdSupport.framework; path = System/Library/Frameworks/AdSupport.framework; sourceTree = SDKROOT; }; F9A80C9D237D02860019D7E0 /* Accelerate.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Accelerate.framework; path = System/Library/Frameworks/Accelerate.framework; sourceTree = SDKROOT; }; + F9CBE50F24E085CD0097B442 /* FBSDKSKAdNetworkReporter.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = FBSDKSKAdNetworkReporter.h; sourceTree = ""; }; + F9CBE51024E085CD0097B442 /* FBSDKSKAdNetworkReporter.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = FBSDKSKAdNetworkReporter.m; sourceTree = ""; }; + F9CEF1EA24F769F900EB0C3D /* FBSDKSKAdNetworkReporterTests.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = FBSDKSKAdNetworkReporterTests.m; sourceTree = ""; }; + F9DE56B824F61B090009CD69 /* FBSDKSKAdNetworkConversionConfiguration.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = FBSDKSKAdNetworkConversionConfiguration.h; sourceTree = ""; }; + F9DE56B924F61B090009CD69 /* FBSDKSKAdNetworkConversionConfiguration.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = FBSDKSKAdNetworkConversionConfiguration.m; sourceTree = ""; }; F9FD9A6121659F120068DEAF /* FBSDKGateKeeperManager.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = FBSDKGateKeeperManager.h; sourceTree = ""; }; F9FD9A7C21659F320068DEAF /* FBSDKGateKeeperManager.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = FBSDKGateKeeperManager.m; sourceTree = ""; }; F9FFE01C2252D665007B2346 /* libz.tbd */ = {isa = PBXFileReference; lastKnownFileType = "sourcecode.text-based-dylib-definition"; name = libz.tbd; path = Platforms/iPhoneOS.platform/Developer/SDKs/iPhoneOS.sdk/usr/lib/libz.tbd; sourceTree = DEVELOPER_DIR; }; @@ -1571,6 +1731,7 @@ buildActionMask = 2147483647; files = ( F9FFE01F2252D66E007B2346 /* libz.tbd in Frameworks */, + F9A18071252150610062E634 /* AdSupport.framework in Frameworks */, 4AF47D131F424A8700A57A67 /* CoreImage.framework in Frameworks */, 8131FB461D261DED000350FF /* CoreGraphics.framework in Frameworks */, 8131FB4A1D261E06000350FF /* UIKit.framework in Frameworks */, @@ -1582,6 +1743,7 @@ buildActionMask = 2147483647; files = ( F9A80C9E237D02860019D7E0 /* Accelerate.framework in Frameworks */, + F9A180732521506C0062E634 /* AdSupport.framework in Frameworks */, C52C02ED2315039400F30E2A /* WebKit.framework in Frameworks */, F9FFE01D2252D666007B2346 /* libz.tbd in Frameworks */, F96ADE7A21E6ABB400F6043F /* StoreKit.framework in Frameworks */, @@ -1652,21 +1814,22 @@ /* End PBXFrameworksBuildPhase section */ /* Begin PBXGroup section */ - 5D4361132321C2F000254DF7 /* Instrument */ = { + 45540D47252717BD008E853E /* Resolver */ = { isa = PBXGroup; children = ( - 5D9A704023261D6900BF9783 /* FBSDKCrashHandler.h */, - 5D9A703F23261D6900BF9783 /* FBSDKCrashHandler.m */, - 5D9A704123261D6900BF9783 /* FBSDKLibAnalyzer.h */, - 5D9A704223261D6900BF9783 /* FBSDKLibAnalyzer.m */, - 5D4361142321C30400254DF7 /* FBSDKCrashObserving.h */, + 7E5557361A8D833100344F86 /* FBSDKAppLinkResolver.h */, + 7E5557351A8D833100344F86 /* FBSDKAppLinkResolver.m */, + 52963A69215992F100C7B252 /* FBSDKAppLinkResolving.h */, + 45540D8F25271A4B008E853E /* FBSDKAppLinkResolverRequestBuilder.h */, + 45540D9025271A4B008E853E /* FBSDKAppLinkResolverRequestBuilder.m */, ); - path = Instrument; + path = Resolver; sourceTree = ""; }; 5D497700244A3E5700BD45C6 /* Integrity */ = { isa = PBXGroup; children = ( + F4D80B572565D6B2008CCAF0 /* FBSDKRestrictiveDataTests.m */, C77C07A52486BAC600345460 /* FBSDKRestrictiveDataFilterTests.m */, 5D497701244A3E6A00BD45C6 /* FBSDKIntegrityTests.m */, ); @@ -1676,8 +1839,8 @@ 5D6DF1542398E24800AC2D6C /* EventDeactivation */ = { isa = PBXGroup; children = ( - 5D6DF1622398E28000AC2D6C /* FBSDKEventDeactivationManager.m */, 5D6DF1692398E2A200AC2D6C /* FBSDKEventDeactivationManager.h */, + 5D6DF1622398E28000AC2D6C /* FBSDKEventDeactivationManager.m */, ); path = EventDeactivation; sourceTree = ""; @@ -1685,6 +1848,8 @@ 5D9031C0233AAC5D0001450C /* Integrity */ = { isa = PBXGroup; children = ( + F468B29C24C2456F00979F8D /* FBSDKRestrictiveData.h */, + F468B29D24C2456F00979F8D /* FBSDKRestrictiveData.m */, 5D411318229F27DD002FF65A /* FBSDKRestrictiveDataFilterManager.m */, 5D411319229F27DD002FF65A /* FBSDKRestrictiveDataFilterManager.h */, 5D81B410238739B100B02B2E /* FBSDKIntegrityManager.h */, @@ -1696,7 +1861,7 @@ 5DAB023A23A1B9FC005495FB /* EventDeactivation */ = { isa = PBXGroup; children = ( - 5DAB023B23A1BA17005495FB /* FBSDKEventDeactivationTests.m */, + 5DAB023B23A1BA17005495FB /* FBSDKEventDeactivationTests.swift */, ); path = EventDeactivation; sourceTree = ""; @@ -1706,6 +1871,7 @@ children = ( 5DB612A723593AB600150851 /* FBSDKCrashShieldTests.m */, 5DAB01F023A1831A005495FB /* FBSDKCrashObserverTests.m */, + F4D80B802565E1C2008CCAF0 /* FBSDKInstrumentManagerTests.m */, ); path = Instrument; sourceTree = ""; @@ -1759,21 +1925,13 @@ path = ML; sourceTree = ""; }; - 5DF60D07225DB6B200934A0E /* Basics */ = { - isa = PBXGroup; - children = ( - 5D4361132321C2F000254DF7 /* Instrument */, - C5C4B3E22276B88600CA3706 /* Internal */, - ); - path = Basics; - sourceTree = ""; - }; 7E5557391A8D833800344F86 /* AppLinks */ = { isa = PBXGroup; children = ( 7E55573A1A8D834B00344F86 /* FBSDKAppLinkResolverTests.m */, + 45540DB025271A88008E853E /* FBSDKAppLinkResolverRequestBuilderTests.m */, ); - name = AppLinks; + path = AppLinks; sourceTree = ""; }; 7E5557401A8E89A800344F86 /* Products */ = { @@ -1871,6 +2029,8 @@ 893F44921A64448E001DB0B6 /* Internal */ = { isa = PBXGroup; children = ( + B3C20ACC250B100F00DEC008 /* FBSDKAccessToken+Internal.h */, + F48AD3702577066A0052C056 /* FBSDKAuthenticationToken+Internal.h */, 5DB7ADCA22EA59E60012E8CB /* Instrument */, 894C0B0E1A7021F8009137EF /* Base64 */, 894C0AE11A6F1D27009137EF /* BridgeAPI */, @@ -1880,6 +2040,14 @@ 52963A9E215993C100C7B252 /* FBSDKAppLink_Internal.h */, 52963A9B215993C100C7B252 /* FBSDKAppLinkReturnToRefererView_Internal.h */, 894C0ACD1A6F0D3F009137EF /* FBSDKApplicationDelegate+Internal.h */, + C6C5CBCB2581871B00AA3BB3 /* FBSDKAuthenticationStatusUtility.h */, + C6C5CBCA2581871B00AA3BB3 /* FBSDKAuthenticationStatusUtility.m */, + 5E47555B2580577B00C98E30 /* FBSDKAuthenticationTokenClaims.h */, + 5E47555C2580577C00C98E30 /* FBSDKAuthenticationTokenClaims.m */, + 5E3AFFE5258C191200BE46AB /* FBSDKAuthenticationTokenHeader.h */, + 5E3AFFE6258C191200BE46AB /* FBSDKAuthenticationTokenHeader.m */, + C6A308BA257AD1FF003C52FD /* FBSDKAuthenticationTokenFactory.h */, + C6A308BB257AD1FF003C52FD /* FBSDKAuthenticationTokenFactory.m */, C5188DF8222F388400F4D8BC /* FBSDKApplicationObserving.h */, 89D05AA71AA1134000609300 /* FBSDKAudioResourceLoader.h */, 89D05AA81AA1134000609300 /* FBSDKAudioResourceLoader.m */, @@ -1889,6 +2057,7 @@ 520223F51D83C8D200CE0AB5 /* FBSDKDeviceRequestsHelper.h */, 520223F61D83C8D200CE0AB5 /* FBSDKDeviceRequestsHelper.m */, 81C969301C114723002FC037 /* FBSDKDynamicFrameworkLoader.h */, + F44B04EC256456140059A3A6 /* FBSDKDynamicFrameworkResolving.h */, 894C0B5F1A7150AC009137EF /* FBSDKError.h */, 894C0B601A7150AC009137EF /* FBSDKError.m */, C5C7B74822D84F64004A5A0C /* FBSDKFeatureManager.h */, @@ -1906,8 +2075,6 @@ ADEA17721B7ECA1A0070EDC0 /* FBSDKMonotonicTime.m */, 9DE6C46D1AAF6E2800EC4C99 /* FBSDKProfile+Internal.h */, 9DA830421A699A2200770955 /* FBSDKSettings+Internal.h */, - 89D05A931AA0E89B00609300 /* FBSDKTriStateBOOL.h */, - 89D05A941AA0E89B00609300 /* FBSDKTriStateBOOL.m */, C5696FA1209CCEB3009C931F /* FBSDKSwizzler.h */, C5696FA2209CCEB4009C931F /* FBSDKSwizzler.m */, 52963A9D215993C100C7B252 /* FBSDKURL_Internal.h */, @@ -1917,6 +2084,8 @@ 9D32A8381A69941A000A936D /* TokenCaching */, 89FB8C3E1A842393003CAE60 /* UI */, 89FB8C431A842644003CAE60 /* WebDialog */, + B301910D253622A2001A8DB0 /* FBSDKProfilePictureView+Internal.h */, + F494284025829B98008FE009 /* FBSDKSessionProviding.h */, ); path = Internal; sourceTree = ""; @@ -1924,6 +2093,8 @@ 893F44A31A644536001DB0B6 /* Frameworks */ = { isa = PBXGroup; children = ( + F9A18062252150610062E634 /* AdSupport.framework */, + F9A180722521506C0062E634 /* AdSupport.framework */, F9A80C9D237D02860019D7E0 /* Accelerate.framework */, C52C02DF2315039400F30E2A /* WebKit.framework */, F9FFE01C2252D665007B2346 /* libz.tbd */, @@ -1947,6 +2118,9 @@ 893F44A41A644552001DB0B6 /* Internal */ = { isa = PBXGroup; children = ( + C6381619257EDF2100B7690F /* FBSDKAuthenticationTokenFactoryTests.m */, + C6C5CBDB2581875600AA3BB3 /* FBSDKAuthenticationStatusUtilityTests.m */, + 40853B9324C8C43300A7CB16 /* FBSDKJSONValueTests.m */, 9D18A8D91A95403F00A41042 /* AppEvents */, 7E5557391A8D833800344F86 /* AppLinks */, 894C0B481A705244009137EF /* Base64 */, @@ -1957,7 +2131,9 @@ F4CD7CDA243E2CF7004C27F1 /* Helpers */, 5DB6129923593AA300150851 /* Instrument */, F420048C2416BD6D00AD7006 /* Monitoring */, + F4EB317D2540952B00736B67 /* Network */, F4E50151243648A100C99262 /* ServerConfiguration */, + F4D80C242566D471008CCAF0 /* UI */, ); path = Internal; sourceTree = ""; @@ -2015,6 +2191,14 @@ isa = PBXGroup; children = ( 894C0B351A702DDD009137EF /* ProtocolVersions */, + F41200EC255F4775009E323F /* FBSDKBridgeAPITests.h */, + F402AFC7255B49A500473083 /* FBSDKBridgeAPITests.m */, + F41200CF255F45FE009E323F /* FBSDKBridgeAPI+ApplicationOpenUrlTests.m */, + F44B04A8256442050059A3A6 /* FBSDKBridgeAPIOpenUrlWithSafariTests.m */, + F408C6BF2563091900372D61 /* FBSDKBridgeAPIOpenBridgeRequestTests.m */, + F402AFE4255B4D1C00473083 /* FBSDKBridgeAPI+Testing.h */, + F408C711256320F300372D61 /* FBSDKBridgeAPIRequestTests.swift */, + F44B056E256491590059A3A6 /* FBSDKBridgeAPI+SessionCompletionHandlerTests.m */, ); path = BridgeAPI; sourceTree = ""; @@ -2030,7 +2214,7 @@ 894C0B481A705244009137EF /* Base64 */ = { isa = PBXGroup; children = ( - 894C0B491A70524F009137EF /* FBSDKBase64Tests.m */, + 894C0B491A70524F009137EF /* FBSDKBase64Tests.swift */, ); name = Base64; path = FBSDKCoreKitTests/Internal/Base64; @@ -2084,8 +2268,8 @@ 891687D11AB33CA200F55364 /* FBSDKIcon.m */, 893F44931A6444DF001DB0B6 /* FBSDKLogo.h */, 893F44941A6444DF001DB0B6 /* FBSDKLogo.m */, - 899C3D001A8C1ED200EA8658 /* FBSDKMaleSilhouetteIcon.h */, - 899C3D011A8C1ED200EA8658 /* FBSDKMaleSilhouetteIcon.m */, + 899C3D001A8C1ED200EA8658 /* FBSDKHumanSilhouetteIcon.h */, + 899C3D011A8C1ED200EA8658 /* FBSDKHumanSilhouetteIcon.m */, 89688B451AA62E3B00A98519 /* FBSDKUIUtility.h */, 89688B601AA64C5E00A98519 /* FBSDKViewImpressionTracker.h */, 89688B611AA64C5E00A98519 /* FBSDKViewImpressionTracker.m */, @@ -2107,6 +2291,7 @@ 9D18A8D91A95403F00A41042 /* AppEvents */ = { isa = PBXGroup; children = ( + F916580524F6BA9300BB759A /* SKAdNetwork */, 5D497700244A3E5700BD45C6 /* Integrity */, BF1C64E2240399180052C580 /* ViewHierarchy */, E493252B23F7C51D0000B63A /* ML */, @@ -2116,9 +2301,15 @@ C51121C720A27EF50041DC94 /* Codeless */, 9D18A8DA1A95405A00A41042 /* FBSDKAppEventsStateTests.m */, 9D18A8E81A95495D00A41042 /* FBSDKAppEventsUtilityTests.m */, + 5CD6DB3B255B1D07002C296F /* FBSDKAppEventsUtilityTests.swift */, C5F6EC851FA24FAF009EB258 /* FBSDKPaymentObserverTests.m */, 5D2165E022264453004952D8 /* FBSDKAppEventsTests.m */, 5D68D7D722BAEEF60063A3E2 /* FBSDKTimeSpentDataTests.m */, + F98D1D3625124FAE00276B68 /* FBSDKAppEventsConfigurationFixtures.h */, + F98D1D3725124FAE00276B68 /* FBSDKAppEventsConfigurationFixtures.m */, + F98D1D5425124FCB00276B68 /* FBSDKAppEventsConfigurationTests.swift */, + F98D1D72251297A900276B68 /* FBSDKAppEventsConfigurationManagerTests.swift */, + F98D1D63251295E900276B68 /* RawAppEventsConfigurationResponseFixtures.swift */, ); path = AppEvents; sourceTree = ""; @@ -2165,12 +2356,14 @@ 9D2697491A5DF40700143BFC /* FBSDKCoreKit */ = { isa = PBXGroup; children = ( + F4BD248E24C2189E00975C87 /* Basics */, E4320C3123B8943700EF2653 /* GraphAPI */, - 5DF60D07225DB6B200934A0E /* Basics */, C5188EC122371EE400F4D8BC /* AppEvents */, C5188F1C223857D700F4D8BC /* AppLink */, 9D3029081A65C4420086B9ED /* FBSDKAccessToken.h */, 9D3029091A65C4420086B9ED /* FBSDKAccessToken.m */, + F48AD2E42576FE050052C056 /* FBSDKAuthenticationToken.h */, + F48AD2E32576FE050052C056 /* FBSDKAuthenticationToken.m */, 9D34A11D1A5F038300C37317 /* FBSDKApplicationDelegate.h */, 9D34A11E1A5F038300C37317 /* FBSDKApplicationDelegate.m */, 891687EC1AB38C7C00F55364 /* FBSDKButton.h */, @@ -2217,18 +2410,20 @@ 9D2697561A5DF40700143BFC /* FBSDKCoreKitTests */ = { isa = PBXGroup; children = ( + F48AD3242576FEB20052C056 /* FBSDKAuthenticationTokenTests.m */, + F4A826B624EC6FAC00EB2CD4 /* FBSDKProfileTests.m */, F413883D24C76F3B001BC075 /* FBSDKSafeCastTests.m */, - F4B3CA3122B3FED80098ADF5 /* ExampleSwiftTests.swift */, 7E253D841A8EB78300CCCFE7 /* FBSDKCoreKitTestUtility.h */, 7E253D821A8EB76500CCCFE7 /* FBSDKCoreKitTestUtility.m */, C511229F20A4BCEB0041DC94 /* FBSDKApplicationDelegateTests.m */, 9D3AF4651A9ED47900EEF724 /* FBSDKGraphRequestConnectionTests.m */, F93680D4249D490600446E35 /* FBSDKSettingsTests.m */, - 89C8B19B1A8D7A27009B07F5 /* FBSDKUtilityTests.m */, + 89C8B19B1A8D7A27009B07F5 /* FBSDKUtilityTests.swift */, 893F44A41A644552001DB0B6 /* Internal */, 9D2697571A5DF40700143BFC /* Supporting Files */, - 2A3DA4151CB4C0F600339BD4 /* FBSDKAppLinkUtilityTests.m */, + 2A3DA4151CB4C0F600339BD4 /* FBSDKAppLinkUtilityTests.swift */, 5D2165F12229C6A3004952D8 /* FBSDKGraphRequestTests.m */, + C67BED29257F1A6300D356C9 /* ProfilePictureViewTests.swift */, ); path = FBSDKCoreKitTests; sourceTree = ""; @@ -2236,7 +2431,9 @@ 9D2697571A5DF40700143BFC /* Supporting Files */ = { isa = PBXGroup; children = ( + 5C59531F25548690000D0A3C /* FBSDKCoreKitTests-Bridging-Header.h */, 9D2697581A5DF40700143BFC /* Info.plist */, + F4D80C3F2566D856008CCAF0 /* TestAssets.xcassets */, ); name = "Supporting Files"; sourceTree = ""; @@ -2244,9 +2441,9 @@ 9D32A8381A69941A000A936D /* TokenCaching */ = { isa = PBXGroup; children = ( - 9D32A8431A699459000A936D /* FBSDKAccessTokenCache.h */, - 9D32A8441A699459000A936D /* FBSDKAccessTokenCache.m */, - 9D32A83B1A69941A000A936D /* FBSDKAccessTokenCaching.h */, + 9D32A8431A699459000A936D /* FBSDKTokenCache.h */, + 9D32A8441A699459000A936D /* FBSDKTokenCache.m */, + 9D32A83B1A69941A000A936D /* FBSDKTokenCaching.h */, 9D32A83C1A69941A000A936D /* FBSDKKeychainStore.h */, 9D32A83D1A69941A000A936D /* FBSDKKeychainStore.m */, 9DE1F3CB1A89D9CD00B54D98 /* FBSDKKeychainStoreViaBundleID.h */, @@ -2340,8 +2537,7 @@ C51121C720A27EF50041DC94 /* Codeless */ = { isa = PBXGroup; children = ( - C51121C820A27EF50041DC94 /* FBSDKSampleEventBinding.h */, - C51121CA20A27EF50041DC94 /* FBSDKSampleEventBinding.m */, + C51121CA20A27EF50041DC94 /* FBSDKSampleEventBinding.swift */, C51121C920A27EF50041DC94 /* FBSDKEventBindingTests.m */, ); path = Codeless; @@ -2360,6 +2556,7 @@ C5188EC322371F4000F4D8BC /* Internal */ = { isa = PBXGroup; children = ( + F9CBE50024E085A40097B442 /* SKAdNetwork */, 5D6DF1542398E24800AC2D6C /* EventDeactivation */, BFC6A2192387227800395451 /* ViewHierarchy */, 5DBC72C2237378F800A9D859 /* ML */, @@ -2382,8 +2579,10 @@ 9D0BC14B1A8D236200BE8BA4 /* FBSDKPaymentObserver.m */, 9D0BC14C1A8D236200BE8BA4 /* FBSDKTimeSpentData.h */, 9D0BC14D1A8D236200BE8BA4 /* FBSDKTimeSpentData.m */, - F9169B822155A02000FA1789 /* FBSDKUserDataStore.h */, - F9169B832155A03C00FA1789 /* FBSDKUserDataStore.m */, + F9A06DB52510FAD3007E6386 /* FBSDKAppEventsConfiguration.h */, + F9A06DB62510FAD3007E6386 /* FBSDKAppEventsConfiguration.m */, + F9A06DCD2510FB0F007E6386 /* FBSDKAppEventsConfigurationManager.h */, + F9A06DCE2510FB0F007E6386 /* FBSDKAppEventsConfigurationManager.m */, ); path = Internal; sourceTree = ""; @@ -2391,12 +2590,10 @@ C5188F1C223857D700F4D8BC /* AppLink */ = { isa = PBXGroup; children = ( + 45540D47252717BD008E853E /* Resolver */, C5188F1D223857D700F4D8BC /* Internal */, 52963A70215992F200C7B252 /* FBSDKAppLink.h */, 52963A6C215992F100C7B252 /* FBSDKAppLink.m */, - 7E5557361A8D833100344F86 /* FBSDKAppLinkResolver.h */, - 7E5557351A8D833100344F86 /* FBSDKAppLinkResolver.m */, - 52963A69215992F100C7B252 /* FBSDKAppLinkResolving.h */, 52963A6E215992F200C7B252 /* FBSDKAppLinkReturnToRefererController.h */, 52963A71215992F200C7B252 /* FBSDKAppLinkReturnToRefererController.m */, 52963A73215992F300C7B252 /* FBSDKAppLinkReturnToRefererView.h */, @@ -2439,23 +2636,6 @@ path = Codeless; sourceTree = ""; }; - C5C4B3E22276B88600CA3706 /* Internal */ = { - isa = PBXGroup; - children = ( - 5DEF22CE226A3E06004056C1 /* FBSDKBasicUtility.m */, - C5C4B3E42276B88600CA3706 /* FBSDKBasicUtility.h */, - C5C4B3E32276B88600CA3706 /* FBSDKTypeUtility.h */, - C5C4B3E52276B88600CA3706 /* FBSDKTypeUtility.m */, - F92F8F7C22BA274300494727 /* FBSDKURLSessionTask.h */, - F92F8F7D22BA274300494727 /* FBSDKURLSessionTask.m */, - F9098FC122BC332C00857C2D /* FBSDKURLSession.h */, - F9098FC222BC332C00857C2D /* FBSDKURLSession.m */, - F413881D24C76E0A001BC075 /* FBSDKSafeCast.h */, - F413883424C76E78001BC075 /* FBSDKSafeCast.m */, - ); - path = Internal; - sourceTree = ""; - }; E4320C3123B8943700EF2653 /* GraphAPI */ = { isa = PBXGroup; children = ( @@ -2515,8 +2695,7 @@ F4181B332416EC06006DB452 /* FBSDKMonitorTests.m */, F48A6AEB24170D29002C6CA1 /* TestMonitorEntry.h */, F48A6AEC24170D29002C6CA1 /* TestMonitorEntry.m */, - F43960CE2425513100C1868F /* FakeMonitorStore.h */, - F43960CF2425513100C1868F /* FakeMonitorStore.m */, + F43960CF2425513100C1868F /* FakeMonitorStore.swift */, F4A52AEC242A7D12005F65CE /* FBSDKPerformanceMonitorEntryTests.m */, F4A52B0A242A99C8005F65CE /* FBSDKPerformanceMonitorTests.m */, F420D1842433B73E00D4FA82 /* FBSDKMonitorConfigurationTests.m */, @@ -2534,30 +2713,117 @@ path = Swift; sourceTree = ""; }; + F4A11B4724E36E5200D4C010 /* include */ = { + isa = PBXGroup; + children = ( + C5EAFA5D255283C100458DF5 /* FBSDKUserDataStore+Internal.h */, + F4A11B4824E36E5200D4C010 /* FBSDKTypeUtility.h */, + F4A11B4924E36E5200D4C010 /* FBSDKURLSessionTask.h */, + F4A11B4A24E36E5200D4C010 /* FBSDKSafeCast.h */, + F4A11B4B24E36E5200D4C010 /* FBSDKCrashHandler.h */, + F4A11B4C24E36E5200D4C010 /* FBSDKLibAnalyzer.h */, + F4A11B4D24E36E5200D4C010 /* FBSDKBasicUtility.h */, + F4A11B4E24E36E5200D4C010 /* FBSDKJSONValue.h */, + F4A11B4F24E36E5200D4C010 /* FBSDKURLSession.h */, + F4A11B5024E36E5200D4C010 /* FBSDKCoreKit_Basics.h */, + F4A11B5124E36E5200D4C010 /* FBSDKCrashObserving.h */, + C57044CD24E26678009637AD /* FBSDKUserDataStore.h */, + ); + path = include; + sourceTree = ""; + }; + F4BD248E24C2189E00975C87 /* Basics */ = { + isa = PBXGroup; + children = ( + F4A11B4724E36E5200D4C010 /* include */, + F4FE997D24D906B9008879A4 /* FBSDKJSONValue.m */, + F4F98C4A24CB820A00F0D6EC /* FBSDKSafeCast.m */, + F468B2E724C25AB500979F8D /* FBSDKBasicUtility.m */, + F468B2E324C25AB500979F8D /* FBSDKCrashHandler.m */, + F468B2E924C25AB600979F8D /* FBSDKLibAnalyzer.m */, + F468B2E024C25AB500979F8D /* FBSDKTypeUtility.m */, + F468B2E824C25AB600979F8D /* FBSDKURLSession.m */, + F468B2E224C25AB500979F8D /* FBSDKURLSessionTask.m */, + C57044CE24E26678009637AD /* FBSDKUserDataStore.m */, + ); + name = Basics; + path = ../../Sources/FBSDKCoreKit_Basics; + sourceTree = ""; + }; F4CD7CDA243E2CF7004C27F1 /* Helpers */ = { isa = PBXGroup; children = ( + F496E58E24E49DBC006231A2 /* SampleAppEvents.swift */, + F492C0A024E1F5F600BA21F7 /* KeychainStoreSpy.swift */, + F492C09F24E1F5F600BA21F7 /* UserDefaultsSpy.h */, + F492C0A124E1F5F600BA21F7 /* UserDefaultsSpy.m */, + F47E560C24E1EA8D001497C9 /* FakeBundle.h */, + F47E55FB24E1EA8D001497C9 /* FakeBundle.m */, F4BD4022241EEDE500B45D39 /* FBSDKTestCoder.h */, F4BD4023241EEDE500B45D39 /* FBSDKTestCoder.m */, - F46C4847243E34C600D03401 /* FBSDKMonitoringConfigurationTestHelper.h */, - F46C4856243E34C600D03401 /* FBSDKMonitoringConfigurationTestHelper.m */, + F46C4856243E34C600D03401 /* FBSDKMonitoringConfigurationTestHelper.swift */, F40B24C124732DD90059351C /* Fuzzer.swift */, + F4DE31F424DB695100297C18 /* FBSDKTestCase.h */, + F4DE31F524DB695100297C18 /* FBSDKTestCase.m */, + F496E5FD24E5D0D5006231A2 /* FakeTokenCache.swift */, + F496E60E24E5D195006231A2 /* SampleAccessToken.swift */, + F48AD347257700B80052C056 /* SampleAuthenticationToken.swift */, + F496E61124E6049A006231A2 /* AppDelegateObserverFake.swift */, + F4E50153243648A100C99262 /* FBSDKServerConfigurationFixtures.m */, + F4ED90DA24EDDC610048D283 /* NotificationCenterSpy.swift */, + F4E50152243648A100C99262 /* FBSDKServerConfigurationFixtures.h */, + F4EB31D22540A35800736B67 /* SampleGraphRequest.swift */, + F4EB31BA2540A1B800736B67 /* SampleGraphRequestConnection.swift */, + F4EB33CD2542323500736B67 /* SampleRawRemotePermissions.swift */, + 5C49DF8F2554C9ED00E0FD91 /* SampleUserProfile.swift */, + F402AFF2255B4EE400473083 /* AuthenticationSessionSpy.swift */, + F402B070255C740C00473083 /* ViewControllerSpy.swift */, + F402B060255C645600473083 /* FakeLoginManager.h */, + F402B061255C645600473083 /* FakeLoginManager.m */, + F408C6E8256309A500372D61 /* FakeBridgeApiRequest.swift */, + F44B051025645DAC0059A3A6 /* FakeDylibResolver.swift */, + F494285C25829C72008FE009 /* FakeSessionProvider.swift */, ); path = Helpers; sourceTree = ""; }; + F4D80C242566D471008CCAF0 /* UI */ = { + isa = PBXGroup; + children = ( + F4D80C072566D469008CCAF0 /* FBSDKDrawableTests.swift */, + ); + path = UI; + sourceTree = ""; + }; F4E50151243648A100C99262 /* ServerConfiguration */ = { isa = PBXGroup; children = ( - F4E50152243648A100C99262 /* FBSDKServerConfigurationFixtures.h */, - F4E50153243648A100C99262 /* FBSDKServerConfigurationFixtures.m */, F4E50154243648A100C99262 /* FBSDKServerConfigurationTests.m */, - F428430A246B427700CD4393 /* FBSDKServerConfigurationManagerTests.m */, + F428430A246B427700CD4393 /* FBSDKServerConfigurationManagerTests.swift */, F40B24B1247324BB0059351C /* RawServerConfigurationResponseFixtures.swift */, ); path = ServerConfiguration; sourceTree = ""; }; + F4EB317D2540952B00736B67 /* Network */ = { + isa = PBXGroup; + children = ( + F4EB317E2540955500736B67 /* FBSDKGraphRequestPiggybackManagerTests.m */, + ); + path = Network; + sourceTree = ""; + }; + F916580524F6BA9300BB759A /* SKAdNetwork */ = { + isa = PBXGroup; + children = ( + F916581424F6BAB200BB759A /* FBSDKSKAdNetworkConversionConfigurationTests.m */, + F9CEF1EA24F769F900EB0C3D /* FBSDKSKAdNetworkReporterTests.m */, + F931C06F24F960D00088AD5F /* FBSDKSKAdNetworkRuleTests.swift */, + F943113324FB2E67002441F1 /* FBSDKSKAdNetworkEventTests.swift */, + ); + path = SKAdNetwork; + sourceTree = ""; + }; F952EA442339403900B20652 /* AAM */ = { isa = PBXGroup; children = ( @@ -2575,6 +2841,21 @@ path = AAM; sourceTree = ""; }; + F9CBE50024E085A40097B442 /* SKAdNetwork */ = { + isa = PBXGroup; + children = ( + F9CBE50F24E085CD0097B442 /* FBSDKSKAdNetworkReporter.h */, + F9CBE51024E085CD0097B442 /* FBSDKSKAdNetworkReporter.m */, + F9DE56B824F61B090009CD69 /* FBSDKSKAdNetworkConversionConfiguration.h */, + F9DE56B924F61B090009CD69 /* FBSDKSKAdNetworkConversionConfiguration.m */, + F94310F224F8D608002441F1 /* FBSDKSKAdNetworkRule.h */, + F94310F324F8D608002441F1 /* FBSDKSKAdNetworkRule.m */, + F943111F24FB1A54002441F1 /* FBSDKSKAdNetworkEvent.h */, + F943112024FB1A54002441F1 /* FBSDKSKAdNetworkEvent.m */, + ); + path = SKAdNetwork; + sourceTree = ""; + }; FD2A237F22FBF7A700DC928F /* ErrorReport */ = { isa = PBXGroup; children = ( @@ -2593,14 +2874,15 @@ files = ( 814AC8171D1B528900D61E6C /* FBSDKKeychainStoreViaBundleID.h in Headers */, 5D6DF16D2398E2A200AC2D6C /* FBSDKEventDeactivationManager.h in Headers */, + F44B04F1256456140059A3A6 /* FBSDKDynamicFrameworkResolving.h in Headers */, 814AC8181D1B528900D61E6C /* FBSDKError.h in Headers */, 814AC8191D1B528900D61E6C /* FBSDKButton.h in Headers */, 814AC81A1D1B528900D61E6C /* FBSDKErrorRecoveryConfiguration.h in Headers */, 814AC81B1D1B528900D61E6C /* FBSDKApplicationDelegate.h in Headers */, + F468B2A124C2457000979F8D /* FBSDKRestrictiveData.h in Headers */, 814AC81C1D1B528900D61E6C /* FBSDKDeviceViewControllerBase.h in Headers */, 814AC81D1D1B528900D61E6C /* FBSDKDialogConfiguration.h in Headers */, 814AC81E1D1B528900D61E6C /* FBSDKInternalUtility.h in Headers */, - F413882124C76E0A001BC075 /* FBSDKSafeCast.h in Headers */, FD147F692387215E000B216E /* FBSDKRestrictiveDataFilterManager.h in Headers */, 814AC81F1D1B528900D61E6C /* FBSDKModalFormPresentationController.h in Headers */, 9DC277931DAF4D6B004F4AB5 /* FBSDKSmartDeviceDialogView.h in Headers */, @@ -2616,10 +2898,12 @@ F462DBF123B94C1000FFCECA /* FBSDKCrypto.h in Headers */, F4D8D8DF2422887600C28384 /* FBSDKMonitorNetworker.h in Headers */, 814AC8271D1B528900D61E6C /* FBSDKDeviceButton.h in Headers */, + C5EAFAB725528EAF00458DF5 /* FBSDKUserDataStore+Internal.h in Headers */, F42004922416C30300AD7006 /* FBSDKMonitor.h in Headers */, - 814AC8291D1B528900D61E6C /* FBSDKAccessTokenCache.h in Headers */, + 814AC8291D1B528900D61E6C /* FBSDKTokenCache.h in Headers */, 814AC82A1D1B528900D61E6C /* FBSDKAppEventsState.h in Headers */, 814AC82B1D1B528900D61E6C /* FBSDKAppEvents+Internal.h in Headers */, + F48AD2EC2576FE050052C056 /* FBSDKAuthenticationToken.h in Headers */, 52D4F0D51D91A1950030B7FC /* FBSDKDeviceRequestsHelper.h in Headers */, 814AC82C1D1B528900D61E6C /* FBSDKTestUsersManager.h in Headers */, 814AC82D1D1B528900D61E6C /* _FBSDKTemporaryErrorRecoveryAttempter.h in Headers */, @@ -2642,39 +2926,34 @@ 814AC83A1D1B528900D61E6C /* FBSDKSettings.h in Headers */, 814AC83B1D1B528900D61E6C /* FBSDKAppEventsDeviceInfo.h in Headers */, F4A52B01242A80F4005F65CE /* FBSDKPerformanceMonitorEntry.h in Headers */, - 5D9A704F23261D6900BF9783 /* FBSDKCrashHandler.h in Headers */, 814AC83C1D1B528900D61E6C /* FBSDKUIUtility.h in Headers */, 814AC83D1D1B528900D61E6C /* FBSDKAppEvents.h in Headers */, F9FD9A6521659F120068DEAF /* FBSDKGateKeeperManager.h in Headers */, 814AC83E1D1B528900D61E6C /* FBSDKErrorConfiguration.h in Headers */, 814AC83F1D1B528900D61E6C /* FBSDKConstants.h in Headers */, 814AC8401D1B528900D61E6C /* FBSDKMath.h in Headers */, + F9A06DD22510FB0F007E6386 /* FBSDKAppEventsConfigurationManager.h in Headers */, 814AC8411D1B528900D61E6C /* FBSDKDynamicFrameworkLoader.h in Headers */, 814AC8421D1B528900D61E6C /* FBSDKServerConfiguration.h in Headers */, 814AC8431D1B528900D61E6C /* FBSDKGraphRequestPiggybackManager.h in Headers */, + C57044D224E26678009637AD /* FBSDKUserDataStore.h in Headers */, 814AC8441D1B528900D61E6C /* FBSDKAppEventsUtility.h in Headers */, - F9098FC622BC332C00857C2D /* FBSDKURLSession.h in Headers */, 5D90CDE92343D4D200AF326A /* FBSDKCrashShield.h in Headers */, - 5D4361182321C30400254DF7 /* FBSDKCrashObserving.h in Headers */, 814AC8471D1B528900D61E6C /* FBSDKCopying.h in Headers */, - C5C4B3F02276B88600CA3706 /* FBSDKBasicUtility.h in Headers */, - F92F8F8922BA275900494727 /* FBSDKURLSessionTask.h in Headers */, - C5C4B3EA2276B88600CA3706 /* FBSDKTypeUtility.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 */, - 5263EDDA215D98BD00FAAB0C /* FBSDKUserDataStore.h in Headers */, - F462DBFD23B9569000FFCECA /* FBSDKAccessTokenCaching.h in Headers */, + F462DBFD23B9569000FFCECA /* FBSDKTokenCaching.h in Headers */, F4210E31241B00790061F56D /* FBSDKMethodUsageMonitor.h in Headers */, 5D4360DE23219F7900254DF7 /* FBSDKCrashObserver.h in Headers */, 814AC84E1D1B528900D61E6C /* FBSDKServerConfigurationManager.h in Headers */, + F9A06DCC2510FAF7007E6386 /* FBSDKAppEventsConfiguration.h in Headers */, F462DC0223B9578F00FFCECA /* FBSDKGraphRequestConnection+Internal.h in Headers */, 814AC84F1D1B528900D61E6C /* FBSDKAppEventsStateManager.h in Headers */, 814AC8501D1B528900D61E6C /* FBSDKLogo.h in Headers */, - 5D9A705823261D6900BF9783 /* FBSDKLibAnalyzer.h in Headers */, F42004722416BCC700AD7006 /* FBSDKMonitorEntry.h in Headers */, F420D18B2433BABD00D4FA82 /* FBSDKMonitoringConfiguration.h in Headers */, 814AC8511D1B528900D61E6C /* FBSDKGraphRequestMetadata.h in Headers */, @@ -2688,15 +2967,19 @@ isa = PBXHeadersBuildPhase; buildActionMask = 2147483647; files = ( - C5C4B3E82276B88600CA3706 /* FBSDKTypeUtility.h in Headers */, + F916581624F6BB4D00BB759A /* FBSDKSKAdNetworkConversionConfiguration.h in Headers */, + F943110424F8D613002441F1 /* FBSDKSKAdNetworkRule.h in Headers */, + 5EF17EE52582DE3300FAA5D2 /* FBSDKAuthenticationTokenClaims.h in Headers */, + C638153D257EA7CC00B7690F /* FBSDKAuthenticationTokenFactory.h in Headers */, 81B71D481D19C87400933E93 /* FBSDKAppLinkUtility.h in Headers */, 81B71D491D19C87400933E93 /* FBSDKAppLinkResolver.h in Headers */, 52963AA6215993C100C7B252 /* FBSDKAppLink_Internal.h in Headers */, - 81B71D4A1D19C87400933E93 /* FBSDKAccessTokenCaching.h in Headers */, + 81B71D4A1D19C87400933E93 /* FBSDKTokenCaching.h in Headers */, 52963A81215992F400C7B252 /* FBSDKAppLinkNavigation.h in Headers */, 5D81B412238739B100B02B2E /* FBSDKIntegrityManager.h in Headers */, 81B71D4B1D19C87400933E93 /* FBSDKBridgeAPIResponse.h in Headers */, FDAF4A8E2395D27900711C4C /* FBSDKModelUtility.h in Headers */, + F9A06DB82510FAD3007E6386 /* FBSDKAppEventsConfiguration.h in Headers */, F4BF22A4241954D800BFB494 /* FBSDKMonitorStore.h in Headers */, 81B71D4C1D19C87400933E93 /* FBSDKCoreKit+Internal.h in Headers */, BFC02453237B6B8E00A596EE /* FBSDKTensor.hpp in Headers */, @@ -2709,13 +2992,13 @@ 5D90CDE72343D4D200AF326A /* FBSDKCrashShield.h in Headers */, 81B71D4F1D19C87400933E93 /* FBSDKLogo.h in Headers */, 81B71D501D19C87400933E93 /* FBSDKProfilePictureView.h in Headers */, + F9A06DD02510FB0F007E6386 /* FBSDKAppEventsConfigurationManager.h in Headers */, 81B71D511D19C87400933E93 /* FBSDKButton.h in Headers */, 52963AAD2159A16E00C7B252 /* FBSDKMeasurementEvent.h in Headers */, + F44B04EF256456140059A3A6 /* FBSDKDynamicFrameworkResolving.h in Headers */, 81B71D521D19C87400933E93 /* FBSDKBridgeAPIProtocol.h in Headers */, C5696F94209BBC35009C931F /* FBSDKEventBinding.h in Headers */, - C5C4B3EE2276B88600CA3706 /* FBSDKBasicUtility.h in Headers */, 81B71D531D19C87400933E93 /* FBSDKGraphErrorRecoveryProcessor.h in Headers */, - F413881F24C76E0A001BC075 /* FBSDKSafeCast.h in Headers */, F952EA4B2339406400B20652 /* FBSDKMetadataIndexer.h in Headers */, 52963A89215992F400C7B252 /* FBSDKAppLinkReturnToRefererController.h in Headers */, 81B71D551D19C87400933E93 /* FBSDKBase64.h in Headers */, @@ -2723,14 +3006,12 @@ 81B71D581D19C87400933E93 /* FBSDKAppEvents+Internal.h in Headers */, 9D28F1941DB14DBB0057D709 /* FBSDKImageDownloader.h in Headers */, 81B71D591D19C87400933E93 /* FBSDKErrorConfiguration.h in Headers */, - F92F8F7F22BA274300494727 /* FBSDKURLSessionTask.h in Headers */, 81B71D5A1D19C87400933E93 /* FBSDKTimeSpentData.h in Headers */, - 5D9A704D23261D6900BF9783 /* FBSDKCrashHandler.h in Headers */, 81B71D5B1D19C87400933E93 /* FBSDKIcon.h in Headers */, 81B71D5C1D19C87400933E93 /* FBSDKContainerViewController.h in Headers */, 81B71D5D1D19C87400933E93 /* FBSDKAppEventsState.h in Headers */, 81B71D5E1D19C87400933E93 /* FBSDKBridgeAPIProtocolType.h in Headers */, - 81B71D5F1D19C87400933E93 /* FBSDKMaleSilhouetteIcon.h in Headers */, + 81B71D5F1D19C87400933E93 /* FBSDKHumanSilhouetteIcon.h in Headers */, C5696FA4209CCEB4009C931F /* FBSDKSwizzler.h in Headers */, C5188E342231C4B500F4D8BC /* FBSDKBridgeAPI.h in Headers */, F42004702416BCC700AD7006 /* FBSDKMonitorEntry.h in Headers */, @@ -2738,10 +3019,13 @@ 81B71D601D19C87400933E93 /* FBSDKMutableCopying.h in Headers */, F42004902416C30300AD7006 /* FBSDKMonitor.h in Headers */, 81B71D611D19C87400933E93 /* FBSDKErrorRecoveryAttempter.h in Headers */, + 5E3AFFE8258C191200BE46AB /* FBSDKAuthenticationTokenHeader.h in Headers */, 81B71D621D19C87400933E93 /* FBSDKPaymentObserver.h in Headers */, E4416C1323F61911009CCBFA /* FBSDKModelParser.h in Headers */, F40F6562241A99E000B10EAA /* FBSDKMethodUsageMonitorEntry.h in Headers */, + F9CBE51324E085E70097B442 /* FBSDKSKAdNetworkReporter.h in Headers */, 5DB7B07D230363190012E8CB /* FBSDKInstrumentManager.h in Headers */, + C6C5CBF7258192E800AA3BB3 /* FBSDKAuthenticationStatusUtility.h in Headers */, 81B71D631D19C87400933E93 /* FBSDKProfile+Internal.h in Headers */, 5D41131F229F27DD002FF65A /* FBSDKRestrictiveDataFilterManager.h in Headers */, F4D8D8DD2422887600C28384 /* FBSDKMonitorNetworker.h in Headers */, @@ -2750,21 +3034,21 @@ 81B71D651D19C87400933E93 /* FBSDKBridgeAPIRequest.h in Headers */, 81B71D671D19C87400933E93 /* FBSDKWebDialogView.h in Headers */, 81B71D681D19C87400933E93 /* FBSDKServerConfiguration+Internal.h in Headers */, - 81B71D691D19C87400933E93 /* FBSDKTriStateBOOL.h in Headers */, 81B71D6A1D19C87400933E93 /* FBSDKCloseIcon.h in Headers */, 5DBC72D22373793400A9D859 /* FBSDKModelManager.h in Headers */, 81B71D6B1D19C87400933E93 /* _FBSDKTemporaryErrorRecoveryAttempter.h in Headers */, 52963A8D215992F400C7B252 /* FBSDKAppLink.h in Headers */, - 5D9A705623261D6900BF9783 /* FBSDKLibAnalyzer.h in Headers */, 5D4360DC23219F7900254DF7 /* FBSDKCrashObserver.h in Headers */, C5696F88209BBC35009C931F /* FBSDKViewHierarchyMacros.h in Headers */, C5696F84209BBC35009C931F /* FBSDKViewHierarchy.h in Headers */, + F943113124FB25A3002441F1 /* FBSDKSKAdNetworkEvent.h in Headers */, 81B71D6D1D19C87400933E93 /* FBSDKProfile.h in Headers */, BFC0245C237B6BA000A596EE /* FBSDKFeatureExtractor.h in Headers */, 81B71D6E1D19C87400933E93 /* FBSDKBridgeAPIProtocolWebV2.h in Headers */, 81B71D6F1D19C87400933E93 /* FBSDKInternalUtility.h in Headers */, 81B71D701D19C87400933E93 /* FBSDKConstants.h in Headers */, BFC0244A237B6B0E00A596EE /* FBSDKSuggestedEventsIndexer.h in Headers */, + 45540D9225271A4B008E853E /* FBSDKAppLinkResolverRequestBuilder.h in Headers */, 81B71D711D19C87400933E93 /* FBSDKWebDialog.h in Headers */, 52963A95215992F400C7B252 /* FBSDKWebViewAppLinkResolver.h in Headers */, 81B71D721D19C87400933E93 /* FBSDKMonotonicTime.h in Headers */, @@ -2779,8 +3063,8 @@ 81B71D791D19C87400933E93 /* FBSDKGraphRequestMetadata.h in Headers */, 52963AA92159A13400C7B252 /* FBSDKMeasurementEvent_Internal.h in Headers */, 81B71D7B1D19C87400933E93 /* FBSDKGraphRequest+Internal.h in Headers */, - 5D4361162321C30400254DF7 /* FBSDKCrashObserving.h in Headers */, 81B71D7C1D19C87400933E93 /* FBSDKUtility.h in Headers */, + F494287925829CE8008FE009 /* FBSDKSessionProviding.h in Headers */, F420D1892433BABD00D4FA82 /* FBSDKMonitoringConfiguration.h in Headers */, 81B71D7D1D19C87400933E93 /* FBSDKApplicationDelegate.h in Headers */, 81B71D7F1D19C87400933E93 /* FBSDKColor.h in Headers */, @@ -2797,19 +3081,20 @@ 81B71D871D19C87400933E93 /* FBSDKGraphRequestConnection+Internal.h in Headers */, 81B71D881D19C87400933E93 /* FBSDKButton+Subclass.h in Headers */, 81B71D8A1D19C87400933E93 /* FBSDKAccessToken.h in Headers */, - 5263EDD7215D98A400FAAB0C /* FBSDKUserDataStore.h in Headers */, 81B71D8B1D19C87400933E93 /* FBSDKCoreKit.h in Headers */, F4A52AFF242A80F4005F65CE /* FBSDKPerformanceMonitorEntry.h in Headers */, 81B71D8C1D19C87400933E93 /* FBSDKGraphRequestBody.h in Headers */, 81B71D8D1D19C87400933E93 /* FBSDKGraphRequestDataAttachment.h in Headers */, 81B71D901D19C87400933E93 /* FBSDKSettings.h in Headers */, 81B71D911D19C87400933E93 /* FBSDKServerConfiguration.h in Headers */, + C57044D024E26678009637AD /* FBSDKUserDataStore.h in Headers */, BFC02456237B6B9300A596EE /* FBSDKModelRuntime.hpp in Headers */, 81B71D921D19C87400933E93 /* FBSDKAppEventsStateManager.h in Headers */, 81B71D931D19C87400933E93 /* FBSDKErrorRecoveryConfiguration.h in Headers */, 81B71D941D19C87400933E93 /* FBSDKAppEvents.h in Headers */, C5188DFA222F388400F4D8BC /* FBSDKApplicationObserving.h in Headers */, 81B71D951D19C87400933E93 /* FBSDKGraphRequest.h in Headers */, + F48AD2EA2576FE050052C056 /* FBSDKAuthenticationToken.h in Headers */, 81B71D961D19C87400933E93 /* FBSDKBridgeAPIProtocolNativeV1.h in Headers */, F4A52B0F242AA532005F65CE /* FBSDKPerformanceMonitor.h in Headers */, 81B71D971D19C87400933E93 /* FBSDKURLOpening.h in Headers */, @@ -2818,13 +3103,13 @@ 5D6DF16B2398E2A200AC2D6C /* FBSDKEventDeactivationManager.h in Headers */, 52963AA0215993C100C7B252 /* FBSDKAppLinkReturnToRefererView_Internal.h in Headers */, C5696F78209BBC35009C931F /* FBSDKCodelessPathComponent.h in Headers */, - F9098FC422BC332C00857C2D /* FBSDKURLSession.h in Headers */, 81B71D991D19C87400933E93 /* FBSDKGraphRequestConnection.h in Headers */, + C5EAFA9B25528EA300458DF5 /* FBSDKUserDataStore+Internal.h in Headers */, 81B71D9A1D19C87400933E93 /* FBSDKKeychainStore.h in Headers */, 81B71D9B1D19C87400933E93 /* FBSDKTestUsersManager.h in Headers */, C5C7B74B22D84F64004A5A0C /* FBSDKFeatureManager.h in Headers */, 81B71D9C1D19C87400933E93 /* FBSDKAppEventsDeviceInfo.h in Headers */, - 81B71D9D1D19C87400933E93 /* FBSDKAccessTokenCache.h in Headers */, + 81B71D9D1D19C87400933E93 /* FBSDKTokenCache.h in Headers */, F9FD9A6321659F120068DEAF /* FBSDKGateKeeperManager.h in Headers */, ); runOnlyForDeploymentPostprocessing = 0; @@ -2833,13 +3118,16 @@ isa = PBXHeadersBuildPhase; buildActionMask = 2147483647; files = ( + F9A06DB72510FAD3007E6386 /* FBSDKAppEventsConfiguration.h in Headers */, 7E30917B1AA92A95004E91D5 /* FBSDKAppLinkUtility.h in Headers */, 7E5557381A8D833100344F86 /* FBSDKAppLinkResolver.h in Headers */, - 9D32A8401A69941A000A936D /* FBSDKAccessTokenCaching.h in Headers */, + 9D32A8401A69941A000A936D /* FBSDKTokenCaching.h in Headers */, + F9A06DCF2510FB0F007E6386 /* FBSDKAppEventsConfigurationManager.h in Headers */, 894C0ADF1A6F1D1B009137EF /* FBSDKBridgeAPIResponse.h in Headers */, 890414741A647D8800617215 /* FBSDKCoreKit+Internal.h in Headers */, 7EB63DA01A9BE730003A7AED /* FBSDKMeasurementEventListener.h in Headers */, 5D81B411238739B100B02B2E /* FBSDKIntegrityManager.h in Headers */, + F9DE56BA24F61B090009CD69 /* FBSDKSKAdNetworkConversionConfiguration.h in Headers */, 89D05AA91AA1134000609300 /* FBSDKAudioResourceLoader.h in Headers */, FDAF4A7E2395D1DE00711C4C /* FBSDKModelUtility.h in Headers */, F4BF22A3241954D800BFB494 /* FBSDKMonitorStore.h in Headers */, @@ -2847,31 +3135,32 @@ 52963AAC2159A16E00C7B252 /* FBSDKMeasurementEvent.h in Headers */, 899C3CF81A8BF73A00EA8658 /* FBSDKProfilePictureView.h in Headers */, 891687EE1AB38C7C00F55364 /* FBSDKButton.h in Headers */, + 45540D9125271A4B008E853E /* FBSDKAppLinkResolverRequestBuilder.h in Headers */, + F94310F424F8D608002441F1 /* FBSDKSKAdNetworkRule.h in Headers */, 52963A7E215992F400C7B252 /* FBSDKAppLinkResolving.h in Headers */, 894C0AF31A6F21A1009137EF /* FBSDKBridgeAPIProtocol.h in Headers */, C5696F93209BBC35009C931F /* FBSDKEventBinding.h in Headers */, + F494286B25829CE8008FE009 /* FBSDKSessionProviding.h in Headers */, 5D90CDE62343D4D200AF326A /* FBSDKCrashShield.h in Headers */, 9DD50A3C1A9BBA1B0088AAAA /* FBSDKGraphErrorRecoveryProcessor.h in Headers */, 894C0B111A7021F8009137EF /* FBSDKBase64.h in Headers */, 9D0BC15B1A8D427800BE8BA4 /* FBSDKAppEventsUtility.h in Headers */, + F4AE435225508FEA00FA9DE6 /* FBSDKProfilePictureView+Internal.h in Headers */, 9D0BC1541A8D23DB00BE8BA4 /* FBSDKAppEvents+Internal.h in Headers */, 9D28F1931DB14DBB0057D709 /* FBSDKImageDownloader.h in Headers */, - C5C4B3ED2276B88600CA3706 /* FBSDKBasicUtility.h in Headers */, 9D3AF4501A9EA4BE00EEF724 /* FBSDKErrorConfiguration.h in Headers */, + C6A308D8257AD227003C52FD /* FBSDKAuthenticationTokenFactory.h in Headers */, + 5E47555D2580577C00C98E30 /* FBSDKAuthenticationTokenClaims.h in Headers */, 9D0BC1511A8D236200BE8BA4 /* FBSDKTimeSpentData.h in Headers */, F952EA482339403900B20652 /* FBSDKMetadataIndexer.h in Headers */, - F413881E24C76E0A001BC075 /* FBSDKSafeCast.h in Headers */, 891687D21AB33CA200F55364 /* FBSDKIcon.h in Headers */, 52963A92215992F400C7B252 /* FBSDKAppLinkReturnToRefererView.h in Headers */, 52963AA82159A13400C7B252 /* FBSDKMeasurementEvent_Internal.h in Headers */, 9D195CC81B9FE2E000BD6BEC /* FBSDKContainerViewController.h in Headers */, 9D0BC1661A8E892C00BE8BA4 /* FBSDKAppEventsState.h in Headers */, - F92F8F7E22BA274300494727 /* FBSDKURLSessionTask.h in Headers */, 894C0AED1A6F1DAB009137EF /* FBSDKBridgeAPIProtocolType.h in Headers */, - 899C3D021A8C1ED200EA8658 /* FBSDKMaleSilhouetteIcon.h in Headers */, - 5D9A704C23261D6900BF9783 /* FBSDKCrashHandler.h in Headers */, + 899C3D021A8C1ED200EA8658 /* FBSDKHumanSilhouetteIcon.h in Headers */, 890414891A64893100617215 /* FBSDKMutableCopying.h in Headers */, - C5DFB84322CBC1EB0086E16C /* FBSDKTypeUtility.h in Headers */, C5696FA3209CCEB4009C931F /* FBSDKSwizzler.h in Headers */, 9D3AF4891A9EFA0300EEF724 /* FBSDKErrorRecoveryAttempter.h in Headers */, 9D0BC14F1A8D236200BE8BA4 /* FBSDKPaymentObserver.h in Headers */, @@ -2887,7 +3176,6 @@ 8917C4AD1B7A46C800B0B96B /* FBSDKServerConfiguration+Internal.h in Headers */, E4416C0123F61902009CCBFA /* FBSDKModelParser.h in Headers */, F40F6561241A99E000B10EAA /* FBSDKMethodUsageMonitorEntry.h in Headers */, - 89D05A951AA0E89B00609300 /* FBSDKTriStateBOOL.h in Headers */, 5D41131E229F27DD002FF65A /* FBSDKRestrictiveDataFilterManager.h in Headers */, 89D652841A855A6000BB651C /* FBSDKCloseIcon.h in Headers */, F4D8D8DC2422887600C28384 /* FBSDKMonitorNetworker.h in Headers */, @@ -2895,11 +3183,12 @@ C5696F87209BBC35009C931F /* FBSDKViewHierarchyMacros.h in Headers */, 52963A90215992F400C7B252 /* FBSDKAppLinkTarget.h in Headers */, C5696F83209BBC35009C931F /* FBSDKViewHierarchy.h in Headers */, + C5EAFA5E255283C100458DF5 /* FBSDKUserDataStore+Internal.h in Headers */, + 5E3AFFE7258C191200BE46AB /* FBSDKAuthenticationTokenHeader.h in Headers */, 9DA81AF61AA4EF3300B9FE0B /* FBSDKProfile.h in Headers */, 5DBC72D12373793300A9D859 /* FBSDKModelManager.h in Headers */, C4FC99D9202CD5590038C5ED /* FBSDKHybridAppEventsScriptMessageHandler.h in Headers */, 52963A94215992F400C7B252 /* FBSDKWebViewAppLinkResolver.h in Headers */, - 5D9A705523261D6900BF9783 /* FBSDKLibAnalyzer.h in Headers */, 5D4360DB23219F7900254DF7 /* FBSDKCrashObserver.h in Headers */, 89F2038B1AA960FA0053499E /* FBSDKBridgeAPIProtocolWebV2.h in Headers */, 893F448B1A642F11001DB0B6 /* FBSDKInternalUtility.h in Headers */, @@ -2917,23 +3206,23 @@ 89830F2B1A7805D100226ABB /* FBSDKServerConfigurationManager.h in Headers */, 033429B220894D4700C94913 /* FBSDKAccessTokenExpirer.h in Headers */, 9DF2A3FF1A70572B00DFB2FD /* FBSDKGraphRequestMetadata.h in Headers */, + B3C20ACD250B100F00DEC008 /* FBSDKAccessToken+Internal.h in Headers */, 52963AA3215993C100C7B252 /* FBSDKURL_Internal.h in Headers */, 9DC6589B1A6EE5CD00B85AAF /* FBSDKGraphRequest+Internal.h in Headers */, 89C8B1991A8D7A15009B07F5 /* FBSDKUtility.h in Headers */, 9D34A11F1A5F038300C37317 /* FBSDKApplicationDelegate.h in Headers */, 9DBA6A371A80267400B4DE6A /* FBSDKColor.h in Headers */, - 5D4361152321C30400254DF7 /* FBSDKCrashObserving.h in Headers */, FD9E155C23777AF700A005EC /* FBSDKTensor.hpp in Headers */, 89830F331A78060A00226ABB /* FBSDKDialogConfiguration.h in Headers */, 520223F71D83C8D200CE0AB5 /* FBSDKDeviceRequestsHelper.h in Headers */, F420D1882433BABC00D4FA82 /* FBSDKMonitoringConfiguration.h in Headers */, - 5263EDBE215D97ED00FAAB0C /* FBSDKUserDataStore.h in Headers */, C5696F97209BBC35009C931F /* FBSDKCodelessParameterComponent.h in Headers */, 52963A9F215993C100C7B252 /* FBSDKAppLinkReturnToRefererView_Internal.h in Headers */, 894C0B091A702194009137EF /* FBSDKCrypto.h in Headers */, 89688B471AA62E3B00A98519 /* FBSDKUIUtility.h in Headers */, 81C969321C114723002FC037 /* FBSDKDynamicFrameworkLoader.h in Headers */, 9DE1F3CD1A89D9CD00B54D98 /* FBSDKKeychainStoreViaBundleID.h in Headers */, + F9CBE51124E085CD0097B442 /* FBSDKSKAdNetworkReporter.h in Headers */, 52963AA5215993C100C7B252 /* FBSDKAppLink_Internal.h in Headers */, 894C0ACE1A6F0D3F009137EF /* FBSDKApplicationDelegate+Internal.h in Headers */, 89D4AE861A7FFEFC00DB8C72 /* FBSDKBridgeAPIRequest+Private.h in Headers */, @@ -2941,16 +3230,20 @@ 52963A8C215992F400C7B252 /* FBSDKAppLink.h in Headers */, 891687FE1AB38D2300F55364 /* FBSDKButton+Subclass.h in Headers */, 9D30290A1A65C4420086B9ED /* FBSDKAccessToken.h in Headers */, + F48AD2E92576FE050052C056 /* FBSDKAuthenticationToken.h in Headers */, 9D26974D1A5DF40700143BFC /* FBSDKCoreKit.h in Headers */, 9DC658A51A6EE7E200B85AAF /* FBSDKGraphRequestBody.h in Headers */, 896DE2341A9E509300195731 /* FBSDKGraphRequestDataAttachment.h in Headers */, F4A52AFE242A80F4005F65CE /* FBSDKPerformanceMonitorEntry.h in Headers */, + F44B04EE256456140059A3A6 /* FBSDKDynamicFrameworkResolving.h in Headers */, 9DA8303C1A6999EC00770955 /* FBSDKSettings.h in Headers */, 89830F2F1A7805E100226ABB /* FBSDKServerConfiguration.h in Headers */, 9D0BC15F1A8D428700BE8BA4 /* FBSDKAppEventsStateManager.h in Headers */, 9D3AF4601A9EC24800EEF724 /* FBSDKErrorRecoveryConfiguration.h in Headers */, FD9E154D23777AC900A005EC /* FBSDKModelRuntime.hpp in Headers */, 9D0BC1571A8D23E200BE8BA4 /* FBSDKAppEvents.h in Headers */, + C57044CF24E26678009637AD /* FBSDKUserDataStore.h in Headers */, + C6C5CBCD2581871B00AA3BB3 /* FBSDKAuthenticationStatusUtility.h in Headers */, 9DC658951A6EE5C500B85AAF /* FBSDKGraphRequest.h in Headers */, C5188DF9222F388400F4D8BC /* FBSDKApplicationObserving.h in Headers */, 894C0AF61A6F2278009137EF /* FBSDKBridgeAPIProtocolNativeV1.h in Headers */, @@ -2963,13 +3256,13 @@ 5D6DF16A2398E2A200AC2D6C /* FBSDKEventDeactivationManager.h in Headers */, 52963A88215992F400C7B252 /* FBSDKAppLinkReturnToRefererController.h in Headers */, C5696F77209BBC35009C931F /* FBSDKCodelessPathComponent.h in Headers */, - F9098FC322BC332C00857C2D /* FBSDKURLSession.h in Headers */, 9DC6589E1A6EE7D800B85AAF /* FBSDKGraphRequestConnection.h in Headers */, 9D32A8411A69941A000A936D /* FBSDKKeychainStore.h in Headers */, 9D7E7E611ADF038800F53E38 /* FBSDKTestUsersManager.h in Headers */, C5C7B74A22D84F64004A5A0C /* FBSDKFeatureManager.h in Headers */, 5F7063FB1AF733F300E42ED7 /* FBSDKAppEventsDeviceInfo.h in Headers */, - 9D32A8451A699459000A936D /* FBSDKAccessTokenCache.h in Headers */, + 9D32A8451A699459000A936D /* FBSDKTokenCache.h in Headers */, + F943112124FB1A54002441F1 /* FBSDKSKAdNetworkEvent.h in Headers */, 52963A80215992F400C7B252 /* FBSDKAppLinkNavigation.h in Headers */, ); runOnlyForDeploymentPostprocessing = 0; @@ -2980,14 +3273,15 @@ files = ( 9D6DEECE1BC23A56001A94ED /* FBSDKKeychainStoreViaBundleID.h in Headers */, 5D6DF16C2398E2A200AC2D6C /* FBSDKEventDeactivationManager.h in Headers */, + F44B04F0256456140059A3A6 /* FBSDKDynamicFrameworkResolving.h in Headers */, 9DB0FA901BC22AE9005EB8B1 /* FBSDKError.h in Headers */, 9D65383B1BF413B3008A08E9 /* FBSDKButton.h in Headers */, 9D6DEEB91BC2389F001A94ED /* FBSDKErrorRecoveryConfiguration.h in Headers */, 9DC1DD851BC462B0000D5AD5 /* FBSDKApplicationDelegate.h in Headers */, + F468B2A024C2457000979F8D /* FBSDKRestrictiveData.h in Headers */, 9D10A68F1CB38DE600F42AC1 /* FBSDKDeviceViewControllerBase.h in Headers */, 9D6DEECC1BC23A46001A94ED /* FBSDKDialogConfiguration.h in Headers */, 9DB0FA921BC22B1A005EB8B1 /* FBSDKInternalUtility.h in Headers */, - F413882024C76E0A001BC075 /* FBSDKSafeCast.h in Headers */, FD147F682387215D000B216E /* FBSDKRestrictiveDataFilterManager.h in Headers */, 9D10A6821CB3892E00F42AC1 /* FBSDKModalFormPresentationController.h in Headers */, 9DC277921DAF4D6B004F4AB5 /* FBSDKSmartDeviceDialogView.h in Headers */, @@ -2998,16 +3292,17 @@ 9DB0FA871BC1CDA7005EB8B1 /* FBSDKGraphRequestConnection.h in Headers */, 9D10A68A1CB38CE500F42AC1 /* FBSDKDeviceViewControllerBase+Internal.h in Headers */, 9DB0FAAC1BC22DA7005EB8B1 /* FBSDKCoreKit+Internal.h in Headers */, - C5C4B3E92276B88600CA3706 /* FBSDKTypeUtility.h in Headers */, 9DA1C80F1BC3003300A76C9E /* FBSDKCoreKit.h in Headers */, F462DBF023B94C0F00FFCECA /* FBSDKCrypto.h in Headers */, F4D8D8DE2422887600C28384 /* FBSDKMonitorNetworker.h in Headers */, 5DB7B07E230363190012E8CB /* FBSDKInstrumentManager.h in Headers */, F42004912416C30300AD7006 /* FBSDKMonitor.h in Headers */, + C5EAFAA925528EAE00458DF5 /* FBSDKUserDataStore+Internal.h in Headers */, 9D9E16AE1CB46C8900C8B68F /* FBSDKDeviceButton.h in Headers */, - 9D6DEEB41BC23855001A94ED /* FBSDKAccessTokenCache.h in Headers */, + 9D6DEEB41BC23855001A94ED /* FBSDKTokenCache.h in Headers */, 9D6DEEB21BC23838001A94ED /* FBSDKAppEventsState.h in Headers */, 9DB0FA8A1BC1CED0005EB8B1 /* FBSDKAppEvents+Internal.h in Headers */, + F48AD2EB2576FE050052C056 /* FBSDKAuthenticationToken.h in Headers */, 52D4F0D41D91A1950030B7FC /* FBSDKDeviceRequestsHelper.h in Headers */, 9D6DEEDB1BC24295001A94ED /* FBSDKTestUsersManager.h in Headers */, 9D6DEED11BC23A93001A94ED /* _FBSDKTemporaryErrorRecoveryAttempter.h in Headers */, @@ -3029,7 +3324,6 @@ 9D65383C1BF413CC008A08E9 /* FBSDKButton+Subclass.h in Headers */, 9DB0FA941BC22B5F005EB8B1 /* FBSDKSettings.h in Headers */, F4A52B00242A80F4005F65CE /* FBSDKPerformanceMonitorEntry.h in Headers */, - 5D9A704E23261D6900BF9783 /* FBSDKCrashHandler.h in Headers */, 9D6DEEAB1BC236D6001A94ED /* FBSDKAppEventsDeviceInfo.h in Headers */, 9D65383F1BF44F9F008A08E9 /* FBSDKUIUtility.h in Headers */, 9DB0FA8B1BC1CED8005EB8B1 /* FBSDKAppEvents.h in Headers */, @@ -3037,31 +3331,28 @@ 9DB0FA9A1BC22BE5005EB8B1 /* FBSDKErrorConfiguration.h in Headers */, 9DB0FA8E1BC22ACA005EB8B1 /* FBSDKConstants.h in Headers */, 9D6DEEBC1BC238EE001A94ED /* FBSDKMath.h in Headers */, + F9A06DD12510FB0F007E6386 /* FBSDKAppEventsConfigurationManager.h in Headers */, 81C969331C114723002FC037 /* FBSDKDynamicFrameworkLoader.h in Headers */, 9D6DEED01BC23A71001A94ED /* FBSDKServerConfiguration.h in Headers */, 9DB0FAA81BC22D6A005EB8B1 /* FBSDKGraphRequestPiggybackManager.h in Headers */, + C57044D124E26678009637AD /* FBSDKUserDataStore.h in Headers */, 9DB0FA8C1BC1CEEC005EB8B1 /* FBSDKAppEventsUtility.h in Headers */, 5D90CDE82343D4D200AF326A /* FBSDKCrashShield.h in Headers */, - 5D4361172321C30400254DF7 /* FBSDKCrashObserving.h in Headers */, - F9098FC522BC332C00857C2D /* FBSDKURLSession.h in Headers */, 9DB0FA821BC1CB93005EB8B1 /* FBSDKCopying.h in Headers */, - C5C4B3EF2276B88600CA3706 /* FBSDKBasicUtility.h in Headers */, - F92F8F8022BA274300494727 /* FBSDKURLSessionTask.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 */, - 5263EDD9215D98BC00FAAB0C /* FBSDKUserDataStore.h in Headers */, - F462DBFE23B9569000FFCECA /* FBSDKAccessTokenCaching.h in Headers */, + F462DBFE23B9569000FFCECA /* FBSDKTokenCaching.h in Headers */, F4210E30241B00790061F56D /* FBSDKMethodUsageMonitor.h in Headers */, 5D4360DD23219F7900254DF7 /* FBSDKCrashObserver.h in Headers */, 9D6DEECA1BC23A36001A94ED /* FBSDKServerConfigurationManager.h in Headers */, + F9A06DCB2510FAF6007E6386 /* FBSDKAppEventsConfiguration.h in Headers */, F462DC0123B9578E00FFCECA /* FBSDKGraphRequestConnection+Internal.h in Headers */, 9D6DEEB61BC2386C001A94ED /* FBSDKAppEventsStateManager.h in Headers */, 9DDC112B1BEC413D00A88306 /* FBSDKLogo.h in Headers */, - 5D9A705723261D6900BF9783 /* FBSDKLibAnalyzer.h in Headers */, F42004712416BCC700AD7006 /* FBSDKMonitorEntry.h in Headers */, F420D18A2433BABD00D4FA82 /* FBSDKMonitoringConfiguration.h in Headers */, 9DB0FAA61BC22D53005EB8B1 /* FBSDKGraphRequestMetadata.h in Headers */, @@ -3075,14 +3366,8 @@ isa = PBXHeadersBuildPhase; buildActionMask = 2147483647; files = ( - C5C4B3EB2276B88600CA3706 /* FBSDKTypeUtility.h in Headers */, - C5C4B3F12276B88600CA3706 /* FBSDKBasicUtility.h in Headers */, - 5D9A703A23261D4A00BF9783 /* FBSDKCrashObserving.h in Headers */, - 5D9A705023261D6900BF9783 /* FBSDKCrashHandler.h in Headers */, - F92F8F8A22BA275A00494727 /* FBSDKURLSessionTask.h in Headers */, - F9098FC722BC332C00857C2D /* FBSDKURLSession.h in Headers */, - 5D9A705923261D6900BF9783 /* FBSDKLibAnalyzer.h in Headers */, - F413882224C76E0A001BC075 /* FBSDKSafeCast.h in Headers */, + C57044D324E26678009637AD /* FBSDKUserDataStore.h in Headers */, + C5EAFAB825528EB000458DF5 /* FBSDKUserDataStore+Internal.h in Headers */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -3090,14 +3375,8 @@ isa = PBXHeadersBuildPhase; buildActionMask = 2147483647; files = ( - C5C4B3EC2276B88600CA3706 /* FBSDKTypeUtility.h in Headers */, - C5C4B3F22276B88600CA3706 /* FBSDKBasicUtility.h in Headers */, - 5D9A703C23261D4C00BF9783 /* FBSDKCrashObserving.h in Headers */, - 5D9A705223261D6900BF9783 /* FBSDKCrashHandler.h in Headers */, - F92F8F8C22BA275C00494727 /* FBSDKURLSessionTask.h in Headers */, - F9098FC922BC332C00857C2D /* FBSDKURLSession.h in Headers */, - 5D9A705B23261D6900BF9783 /* FBSDKLibAnalyzer.h in Headers */, - F413882424C76E0A001BC075 /* FBSDKSafeCast.h in Headers */, + C57044D524E26678009637AD /* FBSDKUserDataStore.h in Headers */, + C5EAFAC725528EB200458DF5 /* FBSDKUserDataStore+Internal.h in Headers */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -3105,14 +3384,8 @@ isa = PBXHeadersBuildPhase; buildActionMask = 2147483647; files = ( - C5C4B4D92276BA7000CA3706 /* FBSDKBasicUtility.h in Headers */, - C5C4B4DA2276BA7000CA3706 /* FBSDKTypeUtility.h in Headers */, - 5D9A703B23261D4B00BF9783 /* FBSDKCrashObserving.h in Headers */, - 5D9A705123261D6900BF9783 /* FBSDKCrashHandler.h in Headers */, - F92F8F8B22BA275B00494727 /* FBSDKURLSessionTask.h in Headers */, - F9098FC822BC332C00857C2D /* FBSDKURLSession.h in Headers */, - 5D9A705A23261D6900BF9783 /* FBSDKLibAnalyzer.h in Headers */, - F413882324C76E0A001BC075 /* FBSDKSafeCast.h in Headers */, + C57044D424E26678009637AD /* FBSDKUserDataStore.h in Headers */, + C5EAFAC625528EB100458DF5 /* FBSDKUserDataStore+Internal.h in Headers */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -3120,14 +3393,8 @@ isa = PBXHeadersBuildPhase; buildActionMask = 2147483647; files = ( - C5C4B4E42276BA8D00CA3706 /* FBSDKBasicUtility.h in Headers */, - C5C4B4E52276BA8D00CA3706 /* FBSDKTypeUtility.h in Headers */, - 5D9A703D23261D4D00BF9783 /* FBSDKCrashObserving.h in Headers */, - 5D9A705323261D6900BF9783 /* FBSDKCrashHandler.h in Headers */, - F92F8F8D22BA275D00494727 /* FBSDKURLSessionTask.h in Headers */, - F9098FCA22BC332C00857C2D /* FBSDKURLSession.h in Headers */, - 5D9A705C23261D6900BF9783 /* FBSDKLibAnalyzer.h in Headers */, - F413882524C76E0A001BC075 /* FBSDKSafeCast.h in Headers */, + C57044D624E26678009637AD /* FBSDKUserDataStore.h in Headers */, + C5EAFAD525528EB200458DF5 /* FBSDKUserDataStore+Internal.h in Headers */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -3496,6 +3763,7 @@ isa = PBXResourcesBuildPhase; buildActionMask = 2147483647; files = ( + F4D80C402566D856008CCAF0 /* TestAssets.xcassets in Resources */, BF5D97E72432D8650096D7AA /* FBSDKTextClassifyRules.json in Resources */, ); runOnlyForDeploymentPostprocessing = 0; @@ -3620,36 +3888,34 @@ 5D6DF1662398E28000AC2D6C /* FBSDKEventDeactivationManager.m in Sources */, 814AC7E41D1B528900D61E6C /* FBSDKAppEventsUtility.m in Sources */, 814AC7E51D1B528900D61E6C /* FBSDKServerConfigurationManager.m in Sources */, - 814AC7E61D1B528900D61E6C /* FBSDKAccessTokenCache.m in Sources */, + 814AC7E61D1B528900D61E6C /* FBSDKTokenCache.m in Sources */, + F468B35524C25AB600979F8D /* FBSDKLibAnalyzer.m in Sources */, 5D4360E323219F8E00254DF7 /* FBSDKCrashObserver.m in Sources */, 814AC7E71D1B528900D61E6C /* FBSDKUtility.m in Sources */, 814AC7E81D1B528900D61E6C /* FBSDKBase64.m in Sources */, F420D1912433BABD00D4FA82 /* FBSDKMonitoringConfiguration.m in Sources */, + F468B34524C25AB600979F8D /* FBSDKBasicUtility.m in Sources */, 814AC7E91D1B528900D61E6C /* _FBSDKTemporaryErrorRecoveryAttempter.m in Sources */, 814AC7EA1D1B528900D61E6C /* FBSDKGraphRequestMetadata.m in Sources */, 814AC7EB1D1B528900D61E6C /* FBSDKServerConfiguration.m in Sources */, 814AC7EC1D1B528900D61E6C /* FBSDKDynamicFrameworkLoader.m in Sources */, F4BF22AC241954D800BFB494 /* FBSDKMonitorStore.m in Sources */, 814AC7ED1D1B528900D61E6C /* FBSDKDialogConfiguration.m in Sources */, + F468B30D24C25AB600979F8D /* FBSDKTypeUtility.m in Sources */, 814AC7EE1D1B528900D61E6C /* FBSDKKeychainStoreViaBundleID.m in Sources */, - F9098FCE22BC332C00857C2D /* FBSDKURLSession.m in Sources */, FD147F672387215A000B216E /* FBSDKRestrictiveDataFilterManager.m in Sources */, - 5D9A706123261D6900BF9783 /* FBSDKLibAnalyzer.m in Sources */, 814AC7EF1D1B528900D61E6C /* FBSDKInternalUtility.m in Sources */, 814AC7F01D1B528900D61E6C /* FBSDKConstants.m in Sources */, - 5D9A704623261D6900BF9783 /* FBSDKCrashHandler.m in Sources */, 4AF47CF41F42468E00A57A67 /* FBSDKDeviceUtilities.m in Sources */, 814AC7F11D1B528900D61E6C /* FBSDKDeviceViewControllerBase.m in Sources */, F4A52B17242AA532005F65CE /* FBSDKPerformanceMonitor.m in Sources */, 814AC7F21D1B528900D61E6C /* FBSDKAppEventsState.m in Sources */, + F4FE998224D906BA008879A4 /* FBSDKJSONValue.m in Sources */, 814AC7F31D1B528900D61E6C /* FBSDKPaymentObserver.m in Sources */, - C5C4B3F72276B88600CA3706 /* FBSDKTypeUtility.m in Sources */, F9FD9A8021659F320068DEAF /* FBSDKGateKeeperManager.m in Sources */, F4210E37241B00790061F56D /* FBSDKMethodUsageMonitor.m in Sources */, 814AC7F41D1B528900D61E6C /* FBSDKErrorRecoveryAttempter.m in Sources */, - 5DEF22D2226A3E06004056C1 /* FBSDKBasicUtility.m in Sources */, 814AC7F51D1B528900D61E6C /* FBSDKButton.m in Sources */, - F92F8F8422BA275200494727 /* FBSDKURLSessionTask.m in Sources */, 814AC7F61D1B528900D61E6C /* FBSDKKeychainStore.m in Sources */, 814AC7F71D1B528900D61E6C /* FBSDKGraphRequestDataAttachment.m in Sources */, F4D8D8E52422887600C28384 /* FBSDKMonitorNetworker.m in Sources */, @@ -3663,20 +3929,26 @@ F42004982416C30300AD7006 /* FBSDKMonitor.m in Sources */, 814AC7FC1D1B528900D61E6C /* FBSDKAccessToken.m in Sources */, 814AC7FD1D1B528900D61E6C /* FBSDKGraphRequestBody.m in Sources */, + F468B34D24C25AB600979F8D /* FBSDKURLSession.m in Sources */, C5C7B75122D84F64004A5A0C /* FBSDKFeatureManager.m in Sources */, 814AC7FE1D1B528900D61E6C /* FBSDKErrorConfiguration.m in Sources */, + F9A06DD62510FB0F007E6386 /* FBSDKAppEventsConfigurationManager.m in Sources */, C554DB1A2304D11A00A32E8B /* FBSDKErrorReport.m in Sources */, 814AC7FF1D1B528900D61E6C /* FBSDKViewImpressionTracker.m in Sources */, 814AC8001D1B528900D61E6C /* FBSDKSettings.m in Sources */, + F468B31D24C25AB600979F8D /* FBSDKURLSessionTask.m in Sources */, 814AC8021D1B528900D61E6C /* FBSDKAppEvents.m in Sources */, 814AC8031D1B528900D61E6C /* FBSDKTestUsersManager.m in Sources */, C5696FAA209CCEB4009C931F /* FBSDKSwizzler.m in Sources */, - F413883824C76E78001BC075 /* FBSDKSafeCast.m in Sources */, 814AC8041D1B528900D61E6C /* FBSDKGraphRequest.m in Sources */, 52D4F0D21D91A18F0030B7FC /* FBSDKDeviceRequestsHelper.m in Sources */, 814AC8051D1B528900D61E6C /* FBSDKLogo.m in Sources */, + F4F98C5624CB820A00F0D6EC /* FBSDKSafeCast.m in Sources */, + C57044DA24E26678009637AD /* FBSDKUserDataStore.m in Sources */, 814AC8061D1B528900D61E6C /* FBSDKDeviceButton.m in Sources */, 814AC8071D1B528900D61E6C /* FBSDKGraphRequestConnection.m in Sources */, + F9A06DCA2510FAF0007E6386 /* FBSDKAppEventsConfiguration.m in Sources */, + F468B32524C25AB600979F8D /* FBSDKCrashHandler.m in Sources */, 814AC8081D1B528900D61E6C /* FBSDKErrorRecoveryConfiguration.m in Sources */, 814AC8091D1B528900D61E6C /* FBSDKApplicationDelegate.m in Sources */, 814AC80B1D1B528900D61E6C /* FBSDKDeviceDialogView.m in Sources */, @@ -3688,7 +3960,6 @@ 814AC80F1D1B528900D61E6C /* FBSDKTimeSpentData.m in Sources */, 814AC8121D1B528900D61E6C /* FBSDKGraphRequestPiggybackManager.m in Sources */, 814AC8131D1B528900D61E6C /* FBSDKError.m in Sources */, - 5263EDDC215D98C200FAAB0C /* FBSDKUserDataStore.m in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -3699,24 +3970,30 @@ 5D81B424238739E600B02B2E /* FBSDKIntegrityManager.m in Sources */, 81B71D011D19C87400933E93 /* FBSDKGraphRequestPiggybackManager.m in Sources */, 81B71D021D19C87400933E93 /* FBSDKAppEventsStateManager.m in Sources */, + 45540D9425271A4B008E853E /* FBSDKAppLinkResolverRequestBuilder.m in Sources */, 81B71D031D19C87400933E93 /* FBSDKServerConfigurationManager.m in Sources */, 81B71D041D19C87400933E93 /* FBSDKGraphRequest.m in Sources */, + F9CBE51424E090590097B442 /* FBSDKSKAdNetworkReporter.m in Sources */, 81B71D051D19C87400933E93 /* FBSDKContainerViewController.m in Sources */, - 81B71D061D19C87400933E93 /* FBSDKAccessTokenCache.m in Sources */, + 5E3AFFEA258C191200BE46AB /* FBSDKAuthenticationTokenHeader.m in Sources */, + 81B71D061D19C87400933E93 /* FBSDKTokenCache.m in Sources */, 81B71D071D19C87400933E93 /* FBSDKCrypto.m in Sources */, + F4FE998024D906BA008879A4 /* FBSDKJSONValue.m in Sources */, 81B71D081D19C87400933E93 /* FBSDKAppEventsState.m in Sources */, 52963A87215992F400C7B252 /* FBSDKWebViewAppLinkResolver.m in Sources */, 81B71D091D19C87400933E93 /* FBSDKCloseIcon.m in Sources */, F4BF22AA241954D800BFB494 /* FBSDKMonitorStore.m in Sources */, 5D41131B229F27DD002FF65A /* FBSDKRestrictiveDataFilterManager.m in Sources */, 81B71D0A1D19C87400933E93 /* FBSDKBase64.m in Sources */, + F468B34B24C25AB600979F8D /* FBSDKURLSession.m in Sources */, 81B71D0B1D19C87400933E93 /* FBSDKBridgeAPIProtocolWebV2.m in Sources */, 81B71D0C1D19C87400933E93 /* FBSDKGraphRequestBody.m in Sources */, + F943110524F8D615002441F1 /* FBSDKSKAdNetworkRule.m in Sources */, 81B71D0D1D19C87400933E93 /* FBSDKUtility.m in Sources */, 81B71D0E1D19C87400933E93 /* FBSDKTestUsersManager.m in Sources */, 81B71D0F1D19C87400933E93 /* FBSDKMeasurementEventListener.m in Sources */, 81B71D101D19C87400933E93 /* FBSDKProfilePictureView.m in Sources */, - 81B71D111D19C87400933E93 /* FBSDKTriStateBOOL.m in Sources */, + F9A06DD42510FB0F007E6386 /* FBSDKAppEventsConfigurationManager.m in Sources */, 5DBC72D92373795600A9D859 /* FBSDKModelManager.mm in Sources */, 81B71D121D19C87400933E93 /* FBSDKLogger.m in Sources */, 81B71D131D19C87400933E93 /* FBSDKApplicationDelegate.m in Sources */, @@ -3736,43 +4013,49 @@ 9D28F1981DB14DBB0057D709 /* FBSDKImageDownloader.m in Sources */, 81B71D1C1D19C87400933E93 /* FBSDKViewImpressionTracker.m in Sources */, C5696F54209BBC35009C931F /* FBSDKViewHierarchy.m in Sources */, - 5D9A704423261D6900BF9783 /* FBSDKCrashHandler.m in Sources */, C5696F6C209BBC35009C931F /* FBSDKCodelessParameterComponent.m in Sources */, + F916581724F6BB5100BB759A /* FBSDKSKAdNetworkConversionConfiguration.m in Sources */, 81B71D1D1D19C87400933E93 /* FBSDKBridgeAPIRequest.m in Sources */, 81B71D1E1D19C87400933E93 /* FBSDKAppEventsDeviceInfo.m in Sources */, - 5DEF22D0226A3E06004056C1 /* FBSDKBasicUtility.m in Sources */, F42004962416C30300AD7006 /* FBSDKMonitor.m in Sources */, + F468B31B24C25AB600979F8D /* FBSDKURLSessionTask.m in Sources */, 81B71D1F1D19C87400933E93 /* FBSDKError.m in Sources */, 81B71D231D19C87400933E93 /* FBSDKAudioResourceLoader.m in Sources */, 81B71D241D19C87400933E93 /* FBSDKLogo.m in Sources */, + F468B35324C25AB600979F8D /* FBSDKLibAnalyzer.m in Sources */, F4210E35241B00790061F56D /* FBSDKMethodUsageMonitor.m in Sources */, 81B71D251D19C87400933E93 /* FBSDKPaymentObserver.m in Sources */, 81B71D261D19C87400933E93 /* FBSDKBridgeAPIResponse.m in Sources */, 81B71D271D19C87400933E93 /* FBSDKErrorRecoveryConfiguration.m in Sources */, 81B71D291D19C87400933E93 /* FBSDKGraphErrorRecoveryProcessor.m in Sources */, - 5263EDD8215D98B000FAAB0C /* FBSDKUserDataStore.m in Sources */, 52963A8F215992F400C7B252 /* FBSDKAppLinkReturnToRefererController.m in Sources */, 81B71D2B1D19C87400933E93 /* FBSDKTimeSpentData.m in Sources */, + F9A06DBA2510FAD3007E6386 /* FBSDKAppEventsConfiguration.m in Sources */, 81B71D2C1D19C87400933E93 /* FBSDKBridgeAPIProtocolWebV1.m in Sources */, 81B71D2D1D19C87400933E93 /* FBSDKMath.m in Sources */, 81B71D2E1D19C87400933E93 /* FBSDKWebDialog.m in Sources */, C5696F60209BBC35009C931F /* FBSDKEventBinding.m in Sources */, 0384CEBB208E60660013D404 /* FBSDKAccessTokenExpirer.m in Sources */, - C5C4B3F42276B88600CA3706 /* FBSDKTypeUtility.m in Sources */, 81B71D2F1D19C87400933E93 /* FBSDKColor.m in Sources */, + F468B32324C25AB600979F8D /* FBSDKCrashHandler.m in Sources */, 81B71D301D19C87400933E93 /* FBSDKAppLinkResolver.m in Sources */, F420D18F2433BABD00D4FA82 /* FBSDKMonitoringConfiguration.m in Sources */, + 5EF17F172582DE3D00FAA5D2 /* FBSDKAuthenticationTokenClaims.m in Sources */, 81B71D311D19C87400933E93 /* FBSDKIcon.m in Sources */, + C638158A257EA8F000B7690F /* FBSDKAuthenticationTokenFactory.m in Sources */, + F468B2A724C2457000979F8D /* FBSDKRestrictiveData.m in Sources */, E4416C1623F61917009CCBFA /* FBSDKModelParser.mm in Sources */, 5D90CDE02343D4BD00AF326A /* FBSDKCrashShield.m in Sources */, 81B71D321D19C87400933E93 /* FBSDKConstants.m in Sources */, 81B71D331D19C87400933E93 /* FBSDKAppEventsUtility.m in Sources */, 5DB7B08F2303632E0012E8CB /* FBSDKInstrumentManager.m in Sources */, + F468B30B24C25AB600979F8D /* FBSDKTypeUtility.m in Sources */, C5696F48209BBC35009C931F /* FBSDKCodelessPathComponent.m in Sources */, F46FA64024533F690060C902 /* Settings.swift in Sources */, - 5D9A705F23261D6900BF9783 /* FBSDKLibAnalyzer.m in Sources */, 81B71D341D19C87400933E93 /* FBSDKSettings.m in Sources */, 52963A85215992F400C7B252 /* FBSDKAppLink.m in Sources */, + C57044D824E26678009637AD /* FBSDKUserDataStore.m in Sources */, + F943113224FB25A7002441F1 /* FBSDKSKAdNetworkEvent.m in Sources */, BFC02459237B6B9C00A596EE /* FBSDKFeatureExtractor.m in Sources */, 52963A77215992F400C7B252 /* FBSDKAppLinkReturnToRefererView.m in Sources */, C5D25D3921795B790037B13D /* FBSDKCodelessIndexer.m in Sources */, @@ -3788,27 +4071,28 @@ F4D8D8E32422887600C28384 /* FBSDKMonitorNetworker.m in Sources */, 5D6DF1642398E28000AC2D6C /* FBSDKEventDeactivationManager.m in Sources */, 5D4360E123219F8E00254DF7 /* FBSDKCrashObserver.m in Sources */, + F468B34324C25AB600979F8D /* FBSDKBasicUtility.m in Sources */, F9FD9A7E21659F320068DEAF /* FBSDKGateKeeperManager.m in Sources */, + F48AD2E62576FE050052C056 /* FBSDKAuthenticationToken.m in Sources */, 81B71D3D1D19C87400933E93 /* FBSDKAppEvents.m in Sources */, FDAF4A912395D27D00711C4C /* FBSDKModelUtility.m in Sources */, 81B71D3E1D19C87400933E93 /* _FBSDKTemporaryErrorRecoveryAttempter.m in Sources */, F4713D7D2375DA8200748692 /* FBSDKSuggestedEventsIndexer.m in Sources */, - F9098FCC22BC332C00857C2D /* FBSDKURLSession.m in Sources */, F4A52B05242A80F4005F65CE /* FBSDKPerformanceMonitorEntry.m in Sources */, 52963AAF2159A16E00C7B252 /* FBSDKMeasurementEvent.m in Sources */, 81B71D3F1D19C87400933E93 /* FBSDKBridgeAPIProtocolNativeV1.m in Sources */, 81B71D401D19C87400933E93 /* FBSDKWebDialogView.m in Sources */, 81B71D411D19C87400933E93 /* FBSDKErrorConfiguration.m in Sources */, C4FC99F2202CD56D0038C5ED /* FBSDKHybridAppEventsScriptMessageHandler.m in Sources */, + C6C5CC05258192ED00AA3BB3 /* FBSDKAuthenticationStatusUtility.m in Sources */, + F4F98C5424CB820A00F0D6EC /* FBSDKSafeCast.m in Sources */, 52D4F0BC1D91A18D0030B7FC /* FBSDKDeviceRequestsHelper.m in Sources */, 81B71D421D19C87400933E93 /* FBSDKKeychainStoreViaBundleID.m in Sources */, 81B71D431D19C87400933E93 /* FBSDKServerConfiguration.m in Sources */, F952EA492339405D00B20652 /* FBSDKMetadataIndexer.m in Sources */, - 81B71D441D19C87400933E93 /* FBSDKMaleSilhouetteIcon.m in Sources */, + 81B71D441D19C87400933E93 /* FBSDKHumanSilhouetteIcon.m in Sources */, C5C7B74F22D84F64004A5A0C /* FBSDKFeatureManager.m in Sources */, - F413883624C76E78001BC075 /* FBSDKSafeCast.m in Sources */, C554DB0B2304D11200A32E8B /* FBSDKErrorReport.m in Sources */, - F92F8F8222BA274300494727 /* FBSDKURLSessionTask.m in Sources */, F46FA63C24533F450060C902 /* AccessToken.swift in Sources */, ); runOnlyForDeploymentPostprocessing = 0; @@ -3820,26 +4104,32 @@ 5D81B423238739E600B02B2E /* FBSDKIntegrityManager.m in Sources */, 9DA81B2A1AA65FA200B9FE0B /* FBSDKGraphRequestPiggybackManager.m in Sources */, 9D0BC1601A8D428700BE8BA4 /* FBSDKAppEventsStateManager.m in Sources */, + 45540D9325271A4B008E853E /* FBSDKAppLinkResolverRequestBuilder.m in Sources */, 89830F2C1A7805D100226ABB /* FBSDKServerConfigurationManager.m in Sources */, 9DC658961A6EE5C500B85AAF /* FBSDKGraphRequest.m in Sources */, + F9CBE51224E085CD0097B442 /* FBSDKSKAdNetworkReporter.m in Sources */, 9D195CC91B9FE2E000BD6BEC /* FBSDKContainerViewController.m in Sources */, + 5E3AFFE9258C191200BE46AB /* FBSDKAuthenticationTokenHeader.m in Sources */, C4FC99DA202CD5590038C5ED /* FBSDKHybridAppEventsScriptMessageHandler.m in Sources */, - 9D32A8461A699459000A936D /* FBSDKAccessTokenCache.m in Sources */, + 9D32A8461A699459000A936D /* FBSDKTokenCache.m in Sources */, + F4FE997F24D906BA008879A4 /* FBSDKJSONValue.m in Sources */, 894C0B0A1A702194009137EF /* FBSDKCrypto.m in Sources */, 9D0BC1671A8E892C00BE8BA4 /* FBSDKAppEventsState.m in Sources */, + F468B31A24C25AB600979F8D /* FBSDKURLSessionTask.m in Sources */, 89D652851A855A6000BB651C /* FBSDKCloseIcon.m in Sources */, F4BF22A9241954D800BFB494 /* FBSDKMonitorStore.m in Sources */, 52963A86215992F400C7B252 /* FBSDKWebViewAppLinkResolver.m in Sources */, 5D41131A229F27DD002FF65A /* FBSDKRestrictiveDataFilterManager.m in Sources */, 894C0B121A7021F8009137EF /* FBSDKBase64.m in Sources */, 89F2038C1AA960FA0053499E /* FBSDKBridgeAPIProtocolWebV2.m in Sources */, + F94310F524F8D608002441F1 /* FBSDKSKAdNetworkRule.m in Sources */, 9DC658A61A6EE7E200B85AAF /* FBSDKGraphRequestBody.m in Sources */, 89C8B19A1A8D7A15009B07F5 /* FBSDKUtility.m in Sources */, 9D7E7E621ADF038800F53E38 /* FBSDKTestUsersManager.m in Sources */, 7EB63D9F1A9BE730003A7AED /* FBSDKMeasurementEventListener.m in Sources */, 899C3CF91A8BF73A00EA8658 /* FBSDKProfilePictureView.m in Sources */, + F9A06DD32510FB0F007E6386 /* FBSDKAppEventsConfigurationManager.m in Sources */, 5DBC72D82373795600A9D859 /* FBSDKModelManager.mm in Sources */, - 89D05A961AA0E89B00609300 /* FBSDKTriStateBOOL.m in Sources */, 9D6999FF1A76E166003AE384 /* FBSDKLogger.m in Sources */, 9D34A1201A5F038300C37317 /* FBSDKApplicationDelegate.m in Sources */, C5188E372231C4B500F4D8BC /* FBSDKBridgeAPI.m in Sources */, @@ -3853,37 +4143,43 @@ 52963A82215992F400C7B252 /* FBSDKURL.m in Sources */, 893F448C1A642F11001DB0B6 /* FBSDKInternalUtility.m in Sources */, ADEA17741B7ECA1A0070EDC0 /* FBSDKMonotonicTime.m in Sources */, + F468B32224C25AB600979F8D /* FBSDKCrashHandler.m in Sources */, F46FA63D24533F610060C902 /* Permission.swift in Sources */, 7E30917A1AA907E0004E91D5 /* FBSDKAppLinkUtility.m in Sources */, 89688B631AA64C5E00A98519 /* FBSDKViewImpressionTracker.m in Sources */, 9D28F1971DB14DBB0057D709 /* FBSDKImageDownloader.m in Sources */, - 5D9A704323261D6900BF9783 /* FBSDKCrashHandler.m in Sources */, 894C0AD21A6F15F7009137EF /* FBSDKBridgeAPIRequest.m in Sources */, C5696F53209BBC35009C931F /* FBSDKViewHierarchy.m in Sources */, C5696F6B209BBC35009C931F /* FBSDKCodelessParameterComponent.m in Sources */, F42004952416C30300AD7006 /* FBSDKMonitor.m in Sources */, - 5DEF22CF226A3E06004056C1 /* FBSDKBasicUtility.m in Sources */, 5F7064091AF7342600E42ED7 /* FBSDKAppEventsDeviceInfo.m in Sources */, 894C0B621A7150AC009137EF /* FBSDKError.m in Sources */, + F9DE56BB24F61B090009CD69 /* FBSDKSKAdNetworkConversionConfiguration.m in Sources */, F4210E34241B00790061F56D /* FBSDKMethodUsageMonitor.m in Sources */, 89D05AAA1AA1134000609300 /* FBSDKAudioResourceLoader.m in Sources */, + F468B30A24C25AB600979F8D /* FBSDKTypeUtility.m in Sources */, 893F449A1A6444DF001DB0B6 /* FBSDKLogo.m in Sources */, 9D0BC1501A8D236200BE8BA4 /* FBSDKPaymentObserver.m in Sources */, 894C0AE01A6F1D1B009137EF /* FBSDKBridgeAPIResponse.m in Sources */, 9D3AF4611A9EC24800EEF724 /* FBSDKErrorRecoveryConfiguration.m in Sources */, 9DD50A3D1A9BBA1B0088AAAA /* FBSDKGraphErrorRecoveryProcessor.m in Sources */, 52963A8E215992F400C7B252 /* FBSDKAppLinkReturnToRefererController.m in Sources */, + F9A06DB92510FAD3007E6386 /* FBSDKAppEventsConfiguration.m in Sources */, 9D0BC1521A8D236200BE8BA4 /* FBSDKTimeSpentData.m in Sources */, 89D4AEA01A803F1E00DB8C72 /* FBSDKBridgeAPIProtocolWebV1.m in Sources */, 893F44AD1A644744001DB0B6 /* FBSDKMath.m in Sources */, 89FB8C421A8425C4003CAE60 /* FBSDKWebDialog.m in Sources */, C5696F5F209BBC35009C931F /* FBSDKEventBinding.m in Sources */, 033429B320894D4700C94913 /* FBSDKAccessTokenExpirer.m in Sources */, - C5C4B3F32276B88600CA3706 /* FBSDKTypeUtility.m in Sources */, F420D18E2433BABD00D4FA82 /* FBSDKMonitoringConfiguration.m in Sources */, 9DBA6A311A80265A00B4DE6A /* FBSDKColor.m in Sources */, E4416C0223F61902009CCBFA /* FBSDKModelParser.mm in Sources */, + 5E47555E2580577C00C98E30 /* FBSDKAuthenticationTokenClaims.m in Sources */, + F468B35224C25AB600979F8D /* FBSDKLibAnalyzer.m in Sources */, + C6A308BD257AD1FF003C52FD /* FBSDKAuthenticationTokenFactory.m in Sources */, 7E5557371A8D833100344F86 /* FBSDKAppLinkResolver.m in Sources */, + F468B2A624C2457000979F8D /* FBSDKRestrictiveData.m in Sources */, + F468B34A24C25AB600979F8D /* FBSDKURLSession.m in Sources */, 891687D31AB33CA200F55364 /* FBSDKIcon.m in Sources */, 5D90CDDF2343D4BD00AF326A /* FBSDKCrashShield.m in Sources */, BFC02448237B6A9A00A596EE /* FBSDKFeatureExtractor.m in Sources */, @@ -3892,9 +4188,11 @@ 5DB7B08E2303632E0012E8CB /* FBSDKInstrumentManager.m in Sources */, F46FA63F24533F690060C902 /* Settings.swift in Sources */, C5696F47209BBC35009C931F /* FBSDKCodelessPathComponent.m in Sources */, - 5D9A705E23261D6900BF9783 /* FBSDKLibAnalyzer.m in Sources */, + C57044D724E26678009637AD /* FBSDKUserDataStore.m in Sources */, + F943112224FB1A54002441F1 /* FBSDKSKAdNetworkEvent.m in Sources */, 9DA8303D1A6999EC00770955 /* FBSDKSettings.m in Sources */, 52963A84215992F400C7B252 /* FBSDKAppLink.m in Sources */, + F468B34224C25AB600979F8D /* FBSDKBasicUtility.m in Sources */, 52963A76215992F400C7B252 /* FBSDKAppLinkReturnToRefererView.m in Sources */, 891687EF1AB38C7C00F55364 /* FBSDKButton.m in Sources */, C5D25D3821795B790037B13D /* FBSDKCodelessIndexer.m in Sources */, @@ -3909,27 +4207,26 @@ 5D6DF1632398E28000AC2D6C /* FBSDKEventDeactivationManager.m in Sources */, 9DC6589F1A6EE7D800B85AAF /* FBSDKGraphRequestConnection.m in Sources */, 5D4360E023219F8E00254DF7 /* FBSDKCrashObserver.m in Sources */, + F48AD2E52576FE050052C056 /* FBSDKAuthenticationToken.m in Sources */, 9D0BC1581A8D23E200BE8BA4 /* FBSDKAppEvents.m in Sources */, FDAF4A8D2395D21700711C4C /* FBSDKModelUtility.m in Sources */, F9FD9A7D21659F320068DEAF /* FBSDKGateKeeperManager.m in Sources */, - F9169B842155A03C00FA1789 /* FBSDKUserDataStore.m in Sources */, BF247C832374E1B200A522C0 /* FBSDKSuggestedEventsIndexer.m in Sources */, F4A52B04242A80F4005F65CE /* FBSDKPerformanceMonitorEntry.m in Sources */, - F9098FCB22BC332C00857C2D /* FBSDKURLSession.m in Sources */, 9DF18BBA1A9F1A05000F6748 /* _FBSDKTemporaryErrorRecoveryAttempter.m in Sources */, 52963AAE2159A16E00C7B252 /* FBSDKMeasurementEvent.m in Sources */, 894C0AF71A6F2278009137EF /* FBSDKBridgeAPIProtocolNativeV1.m in Sources */, 89FB8C4B1A842A8A003CAE60 /* FBSDKWebDialogView.m in Sources */, 9D3AF4511A9EA4BE00EEF724 /* FBSDKErrorConfiguration.m in Sources */, + C6C5CBCC2581871B00AA3BB3 /* FBSDKAuthenticationStatusUtility.m in Sources */, 520223F81D83C8D200CE0AB5 /* FBSDKDeviceRequestsHelper.m in Sources */, 9DE1F3CE1A89D9CD00B54D98 /* FBSDKKeychainStoreViaBundleID.m in Sources */, 89830F301A7805E100226ABB /* FBSDKServerConfiguration.m in Sources */, + F4F98C5324CB820A00F0D6EC /* FBSDKSafeCast.m in Sources */, F952EA472339403900B20652 /* FBSDKMetadataIndexer.m in Sources */, FD8E438122FBF8F1008B6DD3 /* FBSDKErrorReport.m in Sources */, - 899C3D031A8C1ED200EA8658 /* FBSDKMaleSilhouetteIcon.m in Sources */, - F413883524C76E78001BC075 /* FBSDKSafeCast.m in Sources */, + 899C3D031A8C1ED200EA8658 /* FBSDKHumanSilhouetteIcon.m in Sources */, C5C7B74E22D84F64004A5A0C /* FBSDKFeatureManager.m in Sources */, - F92F8F8122BA274300494727 /* FBSDKURLSessionTask.m in Sources */, F46FA62D24533F450060C902 /* AccessToken.swift in Sources */, ); runOnlyForDeploymentPostprocessing = 0; @@ -3939,56 +4236,101 @@ buildActionMask = 2147483647; files = ( F4A52B0B242A99C8005F65CE /* FBSDKPerformanceMonitorTests.m in Sources */, + F492C0A324E1F5F600BA21F7 /* UserDefaultsSpy.m in Sources */, 9DF18BCB1A9F2517000F6748 /* FBSDKErrorConfigurationTests.m in Sources */, 9D18A8E91A95495D00A41042 /* FBSDKAppEventsUtilityTests.m in Sources */, + 5CD6DB3C255B1D07002C296F /* FBSDKAppEventsUtilityTests.swift in Sources */, F4BD4024241EEDE500B45D39 /* FBSDKTestCoder.m in Sources */, + F98D1D5525124FCB00276B68 /* FBSDKAppEventsConfigurationTests.swift in Sources */, + F47E560D24E1EA8D001497C9 /* FakeBundle.m in Sources */, 7E55573C1A8D834B00344F86 /* FBSDKAppLinkResolverTests.m in Sources */, 5D2165E122264453004952D8 /* FBSDKAppEventsTests.m in Sources */, + F496E58F24E49DBC006231A2 /* SampleAppEvents.swift in Sources */, + C67BED2A257F1A6300D356C9 /* ProfilePictureViewTests.swift in Sources */, + F4D80C082566D469008CCAF0 /* FBSDKDrawableTests.swift in Sources */, 9D18A8DB1A95405A00A41042 /* FBSDKAppEventsStateTests.m in Sources */, + F4ED90DB24EDDC610048D283 /* NotificationCenterSpy.swift in Sources */, F93680E3249D490600446E35 /* FBSDKSettingsTests.m in Sources */, E4C2B97A23867327002335A4 /* FBSDKModelRuntimeTests.mm in Sources */, + F48AD348257700B80052C056 /* SampleAuthenticationToken.swift in Sources */, + F496E61224E6049A006231A2 /* AppDelegateObserverFake.swift in Sources */, BF5D97C3242EC2D10096D7AA /* FBSDKFeatureExtractorTests.m in Sources */, - 5DAB023C23A1BA17005495FB /* FBSDKEventDeactivationTests.m in Sources */, + 5DAB023C23A1BA17005495FB /* FBSDKEventDeactivationTests.swift in Sources */, 7E253D831A8EB76500CCCFE7 /* FBSDKCoreKitTestUtility.m in Sources */, 5D59BFFD23A705010008CA5A /* FBSDKCrashHandlerTests.m in Sources */, F4A52AED242A7D12005F65CE /* FBSDKPerformanceMonitorEntryTests.m in Sources */, + F402AFC8255B49A500473083 /* FBSDKBridgeAPITests.m in Sources */, C77C07A62486BAC600345460 /* FBSDKRestrictiveDataFilterTests.m in Sources */, - F4B3CA3222B3FED80098ADF5 /* ExampleSwiftTests.swift in Sources */, F4E50155243648A100C99262 /* FBSDKServerConfigurationFixtures.m in Sources */, F4D8D8FA242293ED00C28384 /* FBSDKMonitorNetworkerTests.m in Sources */, - 894C0B4A1A70524F009137EF /* FBSDKBase64Tests.m in Sources */, + 894C0B4A1A70524F009137EF /* FBSDKBase64Tests.swift in Sources */, F420D1852433B73E00D4FA82 /* FBSDKMonitorConfigurationTests.m in Sources */, - F43960D02425513100C1868F /* FakeMonitorStore.m in Sources */, + F43960D02425513100C1868F /* FakeMonitorStore.swift in Sources */, 894C0B3A1A702EBE009137EF /* FBSDKBridgeAPIProtocolNativeV1Tests.m in Sources */, F4E50156243648A100C99262 /* FBSDKServerConfigurationTests.m in Sources */, - 2A3DA4161CB4C0F600339BD4 /* FBSDKAppLinkUtilityTests.m in Sources */, + F496E60F24E5D195006231A2 /* SampleAccessToken.swift in Sources */, + F48AD3252576FEB20052C056 /* FBSDKAuthenticationTokenTests.m in Sources */, + 2A3DA4161CB4C0F600339BD4 /* FBSDKAppLinkUtilityTests.swift in Sources */, + F4DE31F624DB695100297C18 /* FBSDKTestCase.m in Sources */, 5D2165F22229C6A3004952D8 /* FBSDKGraphRequestTests.m in Sources */, + F402B071255C740C00473083 /* ViewControllerSpy.swift in Sources */, + F931C07024F960D00088AD5F /* FBSDKSKAdNetworkRuleTests.swift in Sources */, 7E253D811A8EAEF500CCCFE7 /* FBSDKInternalUtilityTests.m in Sources */, C5F6EC861FA24FAF009EB258 /* FBSDKPaymentObserverTests.m in Sources */, F48A6AED24170D29002C6CA1 /* TestMonitorEntry.m in Sources */, 5D497702244A3E6A00BD45C6 /* FBSDKIntegrityTests.m in Sources */, F4210E1E241AFF740061F56D /* FBSDKMethodUsageMonitorTests.m in Sources */, - F428430B246B427700CD4393 /* FBSDKServerConfigurationManagerTests.m in Sources */, + F428430B246B427700CD4393 /* FBSDKServerConfigurationManagerTests.swift in Sources */, + C6381628257EDF2100B7690F /* FBSDKAuthenticationTokenFactoryTests.m in Sources */, + F9CEF1EB24F769F900EB0C3D /* FBSDKSKAdNetworkReporterTests.m in Sources */, + F98D1D73251297A900276B68 /* FBSDKAppEventsConfigurationManagerTests.swift in Sources */, + F44B051125645DAC0059A3A6 /* FakeDylibResolver.swift in Sources */, + 45540DB125271A88008E853E /* FBSDKAppLinkResolverRequestBuilderTests.m in Sources */, + F408C712256320F300372D61 /* FBSDKBridgeAPIRequestTests.swift in Sources */, 5DAB01F123A1831A005495FB /* FBSDKCrashObserverTests.m in Sources */, F40B24C224732DD90059351C /* Fuzzer.swift in Sources */, + 40853B9424C8C43300A7CB16 /* FBSDKJSONValueTests.m in Sources */, + F408C6E9256309A500372D61 /* FakeBridgeApiRequest.swift in Sources */, F4181B342416EC06006DB452 /* FBSDKMonitorTests.m in Sources */, + F494285D25829C72008FE009 /* FakeSessionProvider.swift in Sources */, + 5C49DF9E2554C9ED00E0FD91 /* SampleUserProfile.swift in Sources */, F4BF22A0241954B400BFB494 /* FBSDKMonitorStoreTests.m in Sources */, - 89C8B19C1A8D7A27009B07F5 /* FBSDKUtilityTests.m in Sources */, + 89C8B19C1A8D7A27009B07F5 /* FBSDKUtilityTests.swift in Sources */, F41979282475A20E003007CC /* FBSDKTypeUtilityTests.m in Sources */, - C51121CC20A27EF50041DC94 /* FBSDKSampleEventBinding.m in Sources */, + C51121CC20A27EF50041DC94 /* FBSDKSampleEventBinding.swift in Sources */, + F916581524F6BAB200BB759A /* FBSDKSKAdNetworkConversionConfigurationTests.m in Sources */, + F4A826B724EC6FAD00EB2CD4 /* FBSDKProfileTests.m in Sources */, 5D59BFFF23A7505D0008CA5A /* FBSDKLibAnalyzerTests.m in Sources */, + F4EB317F2540955500736B67 /* FBSDKGraphRequestPiggybackManagerTests.m in Sources */, F40B24B2247324BB0059351C /* RawServerConfigurationResponseFixtures.swift in Sources */, - F46C4857243E34C600D03401 /* FBSDKMonitoringConfigurationTestHelper.m in Sources */, + F41200D0255F45FE009E323F /* FBSDKBridgeAPI+ApplicationOpenUrlTests.m in Sources */, + F46C4857243E34C600D03401 /* FBSDKMonitoringConfigurationTestHelper.swift in Sources */, + C6C5CBDC2581875600AA3BB3 /* FBSDKAuthenticationStatusUtilityTests.m in Sources */, + F4EB31D32540A35800736B67 /* SampleGraphRequest.swift in Sources */, + F402B062255C645600473083 /* FakeLoginManager.m in Sources */, + F4D80B582565D6B3008CCAF0 /* FBSDKRestrictiveDataTests.m in Sources */, + F4EB33CE2542323500736B67 /* SampleRawRemotePermissions.swift in Sources */, 5DBB0447227FEF700009E0A6 /* FBSDKBasicUtilityTests.m in Sources */, + F4D80B812565E1C2008CCAF0 /* FBSDKInstrumentManagerTests.m in Sources */, F952EA4F2339432100B20652 /* FBSDKMetadataIndexerTests.m in Sources */, 5DB612A823593AB600150851 /* FBSDKCrashShieldTests.m in Sources */, + F44B04B7256442050059A3A6 /* FBSDKBridgeAPIOpenUrlWithSafariTests.m in Sources */, E493252D23F7C60E0000B63A /* FBSDKModelParserTests.mm in Sources */, + F496E5FE24E5D0D5006231A2 /* FakeTokenCache.swift in Sources */, + F492C0A224E1F5F600BA21F7 /* KeychainStoreSpy.swift in Sources */, 5D68D7D822BAEEF60063A3E2 /* FBSDKTimeSpentDataTests.m in Sources */, 9D3AF4661A9ED47900EEF724 /* FBSDKGraphRequestConnectionTests.m in Sources */, BF1C64E624039AD50052C580 /* FBSDKViewHierarchyTests.m in Sources */, C51122A020A4BCEB0041DC94 /* FBSDKApplicationDelegateTests.m in Sources */, C51121CB20A27EF50041DC94 /* FBSDKEventBindingTests.m in Sources */, F413883E24C76F3B001BC075 /* FBSDKSafeCastTests.m in Sources */, + F44B056F256491590059A3A6 /* FBSDKBridgeAPI+SessionCompletionHandlerTests.m in Sources */, + F4EB31BB2540A1B800736B67 /* SampleGraphRequestConnection.swift in Sources */, + F943113424FB2E67002441F1 /* FBSDKSKAdNetworkEventTests.swift in Sources */, + F98D1D64251295E900276B68 /* RawAppEventsConfigurationResponseFixtures.swift in Sources */, + F402AFF3255B4EE400473083 /* AuthenticationSessionSpy.swift in Sources */, + F408C6C02563091900372D61 /* FBSDKBridgeAPIOpenBridgeRequestTests.m in Sources */, + F98D1D3825124FAE00276B68 /* FBSDKAppEventsConfigurationFixtures.m in Sources */, F40F6551241A974400B10EAA /* FBSDKMethodUsageMonitorEntryTests.m in Sources */, ); runOnlyForDeploymentPostprocessing = 0; @@ -4002,36 +4344,34 @@ 5D6DF1652398E28000AC2D6C /* FBSDKEventDeactivationManager.m in Sources */, 9DB0FA8D1BC1CEF4005EB8B1 /* FBSDKAppEventsUtility.m in Sources */, 9D6DEEC91BC23A30001A94ED /* FBSDKServerConfigurationManager.m in Sources */, - 9D6DEEB31BC23850001A94ED /* FBSDKAccessTokenCache.m in Sources */, + 9D6DEEB31BC23850001A94ED /* FBSDKTokenCache.m in Sources */, + F468B35424C25AB600979F8D /* FBSDKLibAnalyzer.m in Sources */, 5D4360E223219F8E00254DF7 /* FBSDKCrashObserver.m in Sources */, 9DB0FA971BC22BC0005EB8B1 /* FBSDKUtility.m in Sources */, 9DE155411C161A66005FCF5C /* FBSDKBase64.m in Sources */, F420D1902433BABD00D4FA82 /* FBSDKMonitoringConfiguration.m in Sources */, + F468B34424C25AB600979F8D /* FBSDKBasicUtility.m in Sources */, 9D6DEED21BC23ADD001A94ED /* _FBSDKTemporaryErrorRecoveryAttempter.m in Sources */, 9DB0FAA71BC22D5B005EB8B1 /* FBSDKGraphRequestMetadata.m in Sources */, 9D6DEECF1BC23A6E001A94ED /* FBSDKServerConfiguration.m in Sources */, 81C969351C114723002FC037 /* FBSDKDynamicFrameworkLoader.m in Sources */, F4BF22AB241954D800BFB494 /* FBSDKMonitorStore.m in Sources */, 9D6DEECB1BC23A43001A94ED /* FBSDKDialogConfiguration.m in Sources */, + F468B30C24C25AB600979F8D /* FBSDKTypeUtility.m in Sources */, 9D6DEECD1BC23A53001A94ED /* FBSDKKeychainStoreViaBundleID.m in Sources */, - F9098FCD22BC332C00857C2D /* FBSDKURLSession.m in Sources */, FD147F662387215A000B216E /* FBSDKRestrictiveDataFilterManager.m in Sources */, - 5D9A706023261D6900BF9783 /* FBSDKLibAnalyzer.m in Sources */, 9DB0FA931BC22B49005EB8B1 /* FBSDKInternalUtility.m in Sources */, 9DB0FA8F1BC22AD6005EB8B1 /* FBSDKConstants.m in Sources */, - 5D9A704523261D6900BF9783 /* FBSDKCrashHandler.m in Sources */, 4AF47CF31F42468E00A57A67 /* FBSDKDeviceUtilities.m in Sources */, 9D10A6901CB38DF100F42AC1 /* FBSDKDeviceViewControllerBase.m in Sources */, F4A52B16242AA532005F65CE /* FBSDKPerformanceMonitor.m in Sources */, 9D6DEEB11BC23834001A94ED /* FBSDKAppEventsState.m in Sources */, + F4FE998124D906BA008879A4 /* FBSDKJSONValue.m in Sources */, 9D6DEEBD1BC238F5001A94ED /* FBSDKPaymentObserver.m in Sources */, - C5C4B3F62276B88600CA3706 /* FBSDKTypeUtility.m in Sources */, F9FD9A7F21659F320068DEAF /* FBSDKGateKeeperManager.m in Sources */, F4210E36241B00790061F56D /* FBSDKMethodUsageMonitor.m in Sources */, 9D6DEEB71BC23890001A94ED /* FBSDKErrorRecoveryAttempter.m in Sources */, - 5DEF22D1226A3E06004056C1 /* FBSDKBasicUtility.m in Sources */, 9D65383E1BF44F88008A08E9 /* FBSDKButton.m in Sources */, - F92F8F8322BA274300494727 /* FBSDKURLSessionTask.m in Sources */, 9D6DEEC41BC239F5001A94ED /* FBSDKKeychainStore.m in Sources */, 9DB0FAA31BC22D08005EB8B1 /* FBSDKGraphRequestDataAttachment.m in Sources */, F4D8D8E42422887600C28384 /* FBSDKMonitorNetworker.m in Sources */, @@ -4045,20 +4385,25 @@ F42004972416C30300AD7006 /* FBSDKMonitor.m in Sources */, 9D6DEEAA1BC236B9001A94ED /* FBSDKAccessToken.m in Sources */, 9DB0FAA11BC22CF9005EB8B1 /* FBSDKGraphRequestBody.m in Sources */, + F468B34C24C25AB600979F8D /* FBSDKURLSession.m in Sources */, C5C7B75022D84F64004A5A0C /* FBSDKFeatureManager.m in Sources */, 9DB0FA9B1BC22BED005EB8B1 /* FBSDKErrorConfiguration.m in Sources */, + F9A06DD52510FB0F007E6386 /* FBSDKAppEventsConfigurationManager.m in Sources */, C554DB192304D11900A32E8B /* FBSDKErrorReport.m in Sources */, 9D6538411BF44FB4008A08E9 /* FBSDKViewImpressionTracker.m in Sources */, 9DB0FA951BC22B79005EB8B1 /* FBSDKSettings.m in Sources */, + F468B31C24C25AB600979F8D /* FBSDKURLSessionTask.m in Sources */, 9D6DEEA91BC2368D001A94ED /* FBSDKAppEvents.m in Sources */, 9D6DEEE81BC2429B001A94ED /* FBSDKTestUsersManager.m in Sources */, C5696FA9209CCEB4009C931F /* FBSDKSwizzler.m in Sources */, - F413883724C76E78001BC075 /* FBSDKSafeCast.m in Sources */, 9DB0FA9F1BC22CBB005EB8B1 /* FBSDKGraphRequest.m in Sources */, 52D4F0D11D91A18F0030B7FC /* FBSDKDeviceRequestsHelper.m in Sources */, 9DDC112A1BEC413900A88306 /* FBSDKLogo.m in Sources */, 9D9E16AF1CB46C8E00C8B68F /* FBSDKDeviceButton.m in Sources */, + C57044D924E26678009637AD /* FBSDKUserDataStore.m in Sources */, 9DB0FA891BC1CDD0005EB8B1 /* FBSDKGraphRequestConnection.m in Sources */, + F468B32424C25AB600979F8D /* FBSDKCrashHandler.m in Sources */, + F9A06DC92510FAF0007E6386 /* FBSDKAppEventsConfiguration.m in Sources */, 9D6DEEBA1BC238A2001A94ED /* FBSDKErrorRecoveryConfiguration.m in Sources */, 9DC1DD781BC4629F000D5AD5 /* FBSDKApplicationDelegate.m in Sources */, 9D92FC641CB320430091B6F7 /* FBSDKDeviceDialogView.m in Sources */, @@ -4066,11 +4411,11 @@ 5DB7B0902303632F0012E8CB /* FBSDKInstrumentManager.m in Sources */, F40F6569241A99E000B10EAA /* FBSDKMethodUsageMonitorEntry.m in Sources */, 9D28F1991DB14DBB0057D709 /* FBSDKImageDownloader.m in Sources */, + F4F98C5524CB820A00F0D6EC /* FBSDKSafeCast.m in Sources */, 9DB0FAA51BC22D1C005EB8B1 /* FBSDKLogger.m in Sources */, 9D6DEEB01BC2379C001A94ED /* FBSDKTimeSpentData.m in Sources */, 9DB0FAA91BC22D6F005EB8B1 /* FBSDKGraphRequestPiggybackManager.m in Sources */, 9DB0FA911BC22AF8005EB8B1 /* FBSDKError.m in Sources */, - 5263EDDB215D98C100FAAB0C /* FBSDKUserDataStore.m in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -4078,13 +4423,15 @@ isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; files = ( - 5D9A706223261D6900BF9783 /* FBSDKLibAnalyzer.m in Sources */, - F92F8F8522BA275300494727 /* FBSDKURLSessionTask.m in Sources */, - 5D9A704723261D6900BF9783 /* FBSDKCrashHandler.m in Sources */, - C5C4B3BA2276B54B00CA3706 /* FBSDKBasicUtility.m in Sources */, - C5C4B3F82276B88600CA3706 /* FBSDKTypeUtility.m in Sources */, - F9098FCF22BC332C00857C2D /* FBSDKURLSession.m in Sources */, - F413883924C76E78001BC075 /* FBSDKSafeCast.m in Sources */, + F468B35624C25AB600979F8D /* FBSDKLibAnalyzer.m in Sources */, + F468B31E24C25AB600979F8D /* FBSDKURLSessionTask.m in Sources */, + F468B32624C25AB600979F8D /* FBSDKCrashHandler.m in Sources */, + F468B34624C25AB600979F8D /* FBSDKBasicUtility.m in Sources */, + F4FE998324D906BA008879A4 /* FBSDKJSONValue.m in Sources */, + F468B30E24C25AB600979F8D /* FBSDKTypeUtility.m in Sources */, + F468B34E24C25AB600979F8D /* FBSDKURLSession.m in Sources */, + F4F98C5724CB820A00F0D6EC /* FBSDKSafeCast.m in Sources */, + C57044DB24E26678009637AD /* FBSDKUserDataStore.m in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -4092,13 +4439,15 @@ isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; files = ( - 5D9A706423261D6900BF9783 /* FBSDKLibAnalyzer.m in Sources */, - F92F8F8722BA275500494727 /* FBSDKURLSessionTask.m in Sources */, - 5D9A704923261D6900BF9783 /* FBSDKCrashHandler.m in Sources */, - C5C4B3C22276B67200CA3706 /* FBSDKBasicUtility.m in Sources */, - C5C4B3F92276B88600CA3706 /* FBSDKTypeUtility.m in Sources */, - F9098FD122BC332C00857C2D /* FBSDKURLSession.m in Sources */, - F413883B24C76E78001BC075 /* FBSDKSafeCast.m in Sources */, + F468B35824C25AB600979F8D /* FBSDKLibAnalyzer.m in Sources */, + F468B32024C25AB600979F8D /* FBSDKURLSessionTask.m in Sources */, + F468B32824C25AB600979F8D /* FBSDKCrashHandler.m in Sources */, + F468B34824C25AB600979F8D /* FBSDKBasicUtility.m in Sources */, + F4FE998524D906BA008879A4 /* FBSDKJSONValue.m in Sources */, + F468B31024C25AB600979F8D /* FBSDKTypeUtility.m in Sources */, + F468B35024C25AB600979F8D /* FBSDKURLSession.m in Sources */, + F4F98C5924CB820A00F0D6EC /* FBSDKSafeCast.m in Sources */, + C57044DD24E26678009637AD /* FBSDKUserDataStore.m in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -4106,13 +4455,15 @@ isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; files = ( - 5D9A706323261D6900BF9783 /* FBSDKLibAnalyzer.m in Sources */, - F92F8F8622BA275400494727 /* FBSDKURLSessionTask.m in Sources */, - 5D9A704823261D6900BF9783 /* FBSDKCrashHandler.m in Sources */, - C5C4B4D72276BA5100CA3706 /* FBSDKTypeUtility.m in Sources */, - C5C4B4D82276BA5100CA3706 /* FBSDKBasicUtility.m in Sources */, - F9098FD022BC332C00857C2D /* FBSDKURLSession.m in Sources */, - F413883A24C76E78001BC075 /* FBSDKSafeCast.m in Sources */, + F468B35724C25AB600979F8D /* FBSDKLibAnalyzer.m in Sources */, + F468B31F24C25AB600979F8D /* FBSDKURLSessionTask.m in Sources */, + F468B32724C25AB600979F8D /* FBSDKCrashHandler.m in Sources */, + F468B34724C25AB600979F8D /* FBSDKBasicUtility.m in Sources */, + F4FE998424D906BA008879A4 /* FBSDKJSONValue.m in Sources */, + F468B30F24C25AB600979F8D /* FBSDKTypeUtility.m in Sources */, + F468B34F24C25AB600979F8D /* FBSDKURLSession.m in Sources */, + F4F98C5824CB820A00F0D6EC /* FBSDKSafeCast.m in Sources */, + C57044DC24E26678009637AD /* FBSDKUserDataStore.m in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -4120,13 +4471,15 @@ isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; files = ( - 5D9A706523261D6900BF9783 /* FBSDKLibAnalyzer.m in Sources */, - F92F8F8822BA275600494727 /* FBSDKURLSessionTask.m in Sources */, - 5D9A704A23261D6900BF9783 /* FBSDKCrashHandler.m in Sources */, - C5C4B4DF2276BA8D00CA3706 /* FBSDKTypeUtility.m in Sources */, - C5C4B4E02276BA8D00CA3706 /* FBSDKBasicUtility.m in Sources */, - F9098FD222BC332C00857C2D /* FBSDKURLSession.m in Sources */, - F413883C24C76E78001BC075 /* FBSDKSafeCast.m in Sources */, + F468B35924C25AB600979F8D /* FBSDKLibAnalyzer.m in Sources */, + F468B32124C25AB600979F8D /* FBSDKURLSessionTask.m in Sources */, + F468B32924C25AB600979F8D /* FBSDKCrashHandler.m in Sources */, + F468B34924C25AB600979F8D /* FBSDKBasicUtility.m in Sources */, + F4FE998624D906BA008879A4 /* FBSDKJSONValue.m in Sources */, + F468B31124C25AB600979F8D /* FBSDKTypeUtility.m in Sources */, + F468B35124C25AB600979F8D /* FBSDKURLSession.m in Sources */, + F4F98C5A24CB820A00F0D6EC /* FBSDKSafeCast.m in Sources */, + C57044DE24E26678009637AD /* FBSDKUserDataStore.m in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -4309,7 +4662,6 @@ isa = XCBuildConfiguration; baseConfigurationReference = 81A4A6031D19BE0400D5BF66 /* FBSDKCoreKit.xcconfig */; buildSettings = { - MODULEMAP_FILE = "$(SRCROOT)/FBSDKCoreKit/Basics/FBSDKCoreKit.modulemap"; }; name = Debug; }; @@ -4317,7 +4669,6 @@ isa = XCBuildConfiguration; baseConfigurationReference = 81A4A6031D19BE0400D5BF66 /* FBSDKCoreKit.xcconfig */; buildSettings = { - MODULEMAP_FILE = "$(SRCROOT)/FBSDKCoreKit/Basics/FBSDKCoreKit.modulemap"; }; name = Release; }; @@ -4325,7 +4676,6 @@ isa = XCBuildConfiguration; baseConfigurationReference = 814AC8971D1B5A0600D61E6C /* FBSDKCoreKit-tvOS.xcconfig */; buildSettings = { - MODULEMAP_FILE = "$(SRCROOT)/FBSDKCoreKit/Basics/FBSDKCoreKit.modulemap"; }; name = Debug; }; @@ -4333,7 +4683,6 @@ isa = XCBuildConfiguration; baseConfigurationReference = 814AC8971D1B5A0600D61E6C /* FBSDKCoreKit-tvOS.xcconfig */; buildSettings = { - MODULEMAP_FILE = "$(SRCROOT)/FBSDKCoreKit/Basics/FBSDKCoreKit.modulemap"; }; name = Release; }; @@ -4341,7 +4690,6 @@ isa = XCBuildConfiguration; baseConfigurationReference = 81A4A6021D19BE0400D5BF66 /* FBSDKCoreKit-Dynamic.xcconfig */; buildSettings = { - MODULEMAP_FILE = "$(SRCROOT)/FBSDKCoreKit/Basics/FBSDKCoreKit.modulemap"; }; name = Debug; }; @@ -4349,7 +4697,6 @@ isa = XCBuildConfiguration; baseConfigurationReference = 81A4A6021D19BE0400D5BF66 /* FBSDKCoreKit-Dynamic.xcconfig */; buildSettings = { - MODULEMAP_FILE = "$(SRCROOT)/FBSDKCoreKit/Basics/FBSDKCoreKit.modulemap"; }; name = Release; }; @@ -4357,7 +4704,6 @@ isa = XCBuildConfiguration; baseConfigurationReference = 814AC8751D1B52DC00D61E6C /* FBSDKCoreKit-tvOS-Dynamic.xcconfig */; buildSettings = { - MODULEMAP_FILE = "$(SRCROOT)/FBSDKCoreKit/Basics/FBSDKCoreKit.modulemap"; }; name = Debug; }; @@ -4365,7 +4711,6 @@ isa = XCBuildConfiguration; baseConfigurationReference = 814AC8751D1B52DC00D61E6C /* FBSDKCoreKit-tvOS-Dynamic.xcconfig */; buildSettings = { - MODULEMAP_FILE = "$(SRCROOT)/FBSDKCoreKit/Basics/FBSDKCoreKit.modulemap"; }; name = Release; }; @@ -4373,7 +4718,6 @@ isa = XCBuildConfiguration; baseConfigurationReference = 81A4A6031D19BE0400D5BF66 /* FBSDKCoreKit.xcconfig */; buildSettings = { - MODULEMAP_FILE = "$(SRCROOT)/FBSDKCoreKit/Basics/FBSDKCoreKit.modulemap"; }; name = Debug; }; @@ -4381,7 +4725,6 @@ isa = XCBuildConfiguration; baseConfigurationReference = 81A4A6031D19BE0400D5BF66 /* FBSDKCoreKit.xcconfig */; buildSettings = { - MODULEMAP_FILE = "$(SRCROOT)/FBSDKCoreKit/Basics/FBSDKCoreKit.modulemap"; }; name = Release; }; @@ -4389,7 +4732,6 @@ isa = XCBuildConfiguration; baseConfigurationReference = 814AC8971D1B5A0600D61E6C /* FBSDKCoreKit-tvOS.xcconfig */; buildSettings = { - MODULEMAP_FILE = "$(SRCROOT)/FBSDKCoreKit/Basics/FBSDKCoreKit.modulemap"; }; name = Debug; }; @@ -4397,7 +4739,6 @@ isa = XCBuildConfiguration; baseConfigurationReference = 814AC8971D1B5A0600D61E6C /* FBSDKCoreKit-tvOS.xcconfig */; buildSettings = { - MODULEMAP_FILE = "$(SRCROOT)/FBSDKCoreKit/Basics/FBSDKCoreKit.modulemap"; }; name = Release; }; diff --git a/FBSDKCoreKit/FBSDKCoreKit/AppEvents/FBSDKAppEvents.m b/FBSDKCoreKit/FBSDKCoreKit/AppEvents/FBSDKAppEvents.m index e5be72cbef..f580c3969c 100644 --- a/FBSDKCoreKit/FBSDKCoreKit/AppEvents/FBSDKAppEvents.m +++ b/FBSDKCoreKit/FBSDKCoreKit/AppEvents/FBSDKAppEvents.m @@ -18,15 +18,20 @@ #import "FBSDKAppEvents.h" #import "FBSDKAppEvents+Internal.h" -#import "FBSDKApplicationDelegate+Internal.h" -#import +#import #import +#import + #import "FBSDKAccessToken.h" +#import "FBSDKAppEventsConfiguration.h" +#import "FBSDKAppEventsConfigurationManager.h" +#import "FBSDKAppEventsDeviceInfo.h" #import "FBSDKAppEventsState.h" #import "FBSDKAppEventsStateManager.h" #import "FBSDKAppEventsUtility.h" +#import "FBSDKApplicationDelegate+Internal.h" #import "FBSDKConstants.h" #import "FBSDKDynamicFrameworkLoader.h" #import "FBSDKError.h" @@ -35,21 +40,20 @@ #import "FBSDKGraphRequest+Internal.h" #import "FBSDKInternalUtility.h" #import "FBSDKLogger.h" -#import "FBSDKRestrictiveDataFilterManager.h" #import "FBSDKPaymentObserver.h" +#import "FBSDKRestrictiveDataFilterManager.h" #import "FBSDKServerConfiguration.h" #import "FBSDKServerConfigurationManager.h" #import "FBSDKSettings.h" #import "FBSDKTimeSpentData.h" #import "FBSDKUtility.h" -#import "FBSDKUserDataStore.h" #if !TARGET_OS_TV -#import "FBSDKIntegrityManager.h" -#import "FBSDKEventBindingManager.h" -#import "FBSDKHybridAppEventsScriptMessageHandler.h" -#import "FBSDKModelManager.h" + #import "FBSDKEventBindingManager.h" + #import "FBSDKHybridAppEventsScriptMessageHandler.h" + #import "FBSDKIntegrityManager.h" + #import "FBSDKModelManager.h" #endif @@ -58,233 +62,222 @@ // // General purpose -FBSDKAppEventName FBSDKAppEventNameCompletedRegistration = @"fb_mobile_complete_registration"; -FBSDKAppEventName FBSDKAppEventNameViewedContent = @"fb_mobile_content_view"; -FBSDKAppEventName FBSDKAppEventNameSearched = @"fb_mobile_search"; -FBSDKAppEventName FBSDKAppEventNameRated = @"fb_mobile_rate"; -FBSDKAppEventName FBSDKAppEventNameCompletedTutorial = @"fb_mobile_tutorial_completion"; -FBSDKAppEventName FBSDKAppEventNameContact = @"Contact"; -FBSDKAppEventName FBSDKAppEventNameCustomizeProduct = @"CustomizeProduct"; -FBSDKAppEventName FBSDKAppEventNameDonate = @"Donate"; -FBSDKAppEventName FBSDKAppEventNameFindLocation = @"FindLocation"; -FBSDKAppEventName FBSDKAppEventNameSchedule = @"Schedule"; -FBSDKAppEventName FBSDKAppEventNameStartTrial = @"StartTrial"; -FBSDKAppEventName FBSDKAppEventNameSubmitApplication = @"SubmitApplication"; -FBSDKAppEventName FBSDKAppEventNameSubscribe = @"Subscribe"; -FBSDKAppEventName FBSDKAppEventNameSubscriptionHeartbeat = @"SubscriptionHeartbeat"; -FBSDKAppEventName FBSDKAppEventNameAdImpression = @"AdImpression"; -FBSDKAppEventName FBSDKAppEventNameAdClick = @"AdClick"; +FBSDKAppEventName FBSDKAppEventNameCompletedRegistration = @"fb_mobile_complete_registration"; +FBSDKAppEventName FBSDKAppEventNameViewedContent = @"fb_mobile_content_view"; +FBSDKAppEventName FBSDKAppEventNameSearched = @"fb_mobile_search"; +FBSDKAppEventName FBSDKAppEventNameRated = @"fb_mobile_rate"; +FBSDKAppEventName FBSDKAppEventNameCompletedTutorial = @"fb_mobile_tutorial_completion"; +FBSDKAppEventName FBSDKAppEventNameContact = @"Contact"; +FBSDKAppEventName FBSDKAppEventNameCustomizeProduct = @"CustomizeProduct"; +FBSDKAppEventName FBSDKAppEventNameDonate = @"Donate"; +FBSDKAppEventName FBSDKAppEventNameFindLocation = @"FindLocation"; +FBSDKAppEventName FBSDKAppEventNameSchedule = @"Schedule"; +FBSDKAppEventName FBSDKAppEventNameStartTrial = @"StartTrial"; +FBSDKAppEventName FBSDKAppEventNameSubmitApplication = @"SubmitApplication"; +FBSDKAppEventName FBSDKAppEventNameSubscribe = @"Subscribe"; +FBSDKAppEventName FBSDKAppEventNameSubscriptionHeartbeat = @"SubscriptionHeartbeat"; +FBSDKAppEventName FBSDKAppEventNameAdImpression = @"AdImpression"; +FBSDKAppEventName FBSDKAppEventNameAdClick = @"AdClick"; // Ecommerce related -FBSDKAppEventName FBSDKAppEventNameAddedToCart = @"fb_mobile_add_to_cart"; -FBSDKAppEventName FBSDKAppEventNameAddedToWishlist = @"fb_mobile_add_to_wishlist"; -FBSDKAppEventName FBSDKAppEventNameInitiatedCheckout = @"fb_mobile_initiated_checkout"; -FBSDKAppEventName FBSDKAppEventNameAddedPaymentInfo = @"fb_mobile_add_payment_info"; -FBSDKAppEventName FBSDKAppEventNameProductCatalogUpdate = @"fb_mobile_catalog_update"; -FBSDKAppEventName FBSDKAppEventNamePurchased = @"fb_mobile_purchase"; +FBSDKAppEventName FBSDKAppEventNameAddedToCart = @"fb_mobile_add_to_cart"; +FBSDKAppEventName FBSDKAppEventNameAddedToWishlist = @"fb_mobile_add_to_wishlist"; +FBSDKAppEventName FBSDKAppEventNameInitiatedCheckout = @"fb_mobile_initiated_checkout"; +FBSDKAppEventName FBSDKAppEventNameAddedPaymentInfo = @"fb_mobile_add_payment_info"; +FBSDKAppEventName FBSDKAppEventNameProductCatalogUpdate = @"fb_mobile_catalog_update"; +FBSDKAppEventName FBSDKAppEventNamePurchased = @"fb_mobile_purchase"; // Gaming related -FBSDKAppEventName FBSDKAppEventNameAchievedLevel = @"fb_mobile_level_achieved"; -FBSDKAppEventName FBSDKAppEventNameUnlockedAchievement = @"fb_mobile_achievement_unlocked"; -FBSDKAppEventName FBSDKAppEventNameSpentCredits = @"fb_mobile_spent_credits"; +FBSDKAppEventName FBSDKAppEventNameAchievedLevel = @"fb_mobile_level_achieved"; +FBSDKAppEventName FBSDKAppEventNameUnlockedAchievement = @"fb_mobile_achievement_unlocked"; +FBSDKAppEventName FBSDKAppEventNameSpentCredits = @"fb_mobile_spent_credits"; // // Public event parameter names // -FBSDKAppEventParameterName FBSDKAppEventParameterNameCurrency = @"fb_currency"; -FBSDKAppEventParameterName FBSDKAppEventParameterNameRegistrationMethod = @"fb_registration_method"; -FBSDKAppEventParameterName FBSDKAppEventParameterNameContentType = @"fb_content_type"; -FBSDKAppEventParameterName FBSDKAppEventParameterNameContent = @"fb_content"; -FBSDKAppEventParameterName FBSDKAppEventParameterNameContentID = @"fb_content_id"; -FBSDKAppEventParameterName FBSDKAppEventParameterNameSearchString = @"fb_search_string"; -FBSDKAppEventParameterName FBSDKAppEventParameterNameSuccess = @"fb_success"; -FBSDKAppEventParameterName FBSDKAppEventParameterNameMaxRatingValue = @"fb_max_rating_value"; -FBSDKAppEventParameterName FBSDKAppEventParameterNamePaymentInfoAvailable = @"fb_payment_info_available"; -FBSDKAppEventParameterName FBSDKAppEventParameterNameNumItems = @"fb_num_items"; -FBSDKAppEventParameterName FBSDKAppEventParameterNameLevel = @"fb_level"; -FBSDKAppEventParameterName FBSDKAppEventParameterNameDescription = @"fb_description"; -FBSDKAppEventParameterName FBSDKAppEventParameterLaunchSource = @"fb_mobile_launch_source"; -FBSDKAppEventParameterName FBSDKAppEventParameterNameAdType = @"ad_type"; -FBSDKAppEventParameterName FBSDKAppEventParameterNameOrderID = @"fb_order_id"; +FBSDKAppEventParameterName FBSDKAppEventParameterNameCurrency = @"fb_currency"; +FBSDKAppEventParameterName FBSDKAppEventParameterNameRegistrationMethod = @"fb_registration_method"; +FBSDKAppEventParameterName FBSDKAppEventParameterNameContentType = @"fb_content_type"; +FBSDKAppEventParameterName FBSDKAppEventParameterNameContent = @"fb_content"; +FBSDKAppEventParameterName FBSDKAppEventParameterNameContentID = @"fb_content_id"; +FBSDKAppEventParameterName FBSDKAppEventParameterNameSearchString = @"fb_search_string"; +FBSDKAppEventParameterName FBSDKAppEventParameterNameSuccess = @"fb_success"; +FBSDKAppEventParameterName FBSDKAppEventParameterNameMaxRatingValue = @"fb_max_rating_value"; +FBSDKAppEventParameterName FBSDKAppEventParameterNamePaymentInfoAvailable = @"fb_payment_info_available"; +FBSDKAppEventParameterName FBSDKAppEventParameterNameNumItems = @"fb_num_items"; +FBSDKAppEventParameterName FBSDKAppEventParameterNameLevel = @"fb_level"; +FBSDKAppEventParameterName FBSDKAppEventParameterNameDescription = @"fb_description"; +FBSDKAppEventParameterName FBSDKAppEventParameterLaunchSource = @"fb_mobile_launch_source"; +FBSDKAppEventParameterName FBSDKAppEventParameterNameAdType = @"ad_type"; +FBSDKAppEventParameterName FBSDKAppEventParameterNameOrderID = @"fb_order_id"; // // Public event parameter names for DPA Catalog // -FBSDKAppEventParameterProduct FBSDKAppEventParameterProductCustomLabel0 = @"fb_product_custom_label_0"; -FBSDKAppEventParameterProduct FBSDKAppEventParameterProductCustomLabel1 = @"fb_product_custom_label_1"; -FBSDKAppEventParameterProduct FBSDKAppEventParameterProductCustomLabel2 = @"fb_product_custom_label_2"; -FBSDKAppEventParameterProduct FBSDKAppEventParameterProductCustomLabel3 = @"fb_product_custom_label_3"; -FBSDKAppEventParameterProduct FBSDKAppEventParameterProductCustomLabel4 = @"fb_product_custom_label_4"; -FBSDKAppEventParameterProduct FBSDKAppEventParameterProductCategory = @"fb_product_category"; -FBSDKAppEventParameterProduct FBSDKAppEventParameterProductAppLinkIOSUrl = @"fb_product_applink_ios_url"; -FBSDKAppEventParameterProduct FBSDKAppEventParameterProductAppLinkIOSAppStoreID = @"fb_product_applink_ios_app_store_id"; -FBSDKAppEventParameterProduct FBSDKAppEventParameterProductAppLinkIOSAppName = @"fb_product_applink_ios_app_name"; -FBSDKAppEventParameterProduct FBSDKAppEventParameterProductAppLinkIPhoneUrl = @"fb_product_applink_iphone_url"; -FBSDKAppEventParameterProduct FBSDKAppEventParameterProductAppLinkIPhoneAppStoreID = @"fb_product_applink_iphone_app_store_id"; -FBSDKAppEventParameterProduct FBSDKAppEventParameterProductAppLinkIPhoneAppName = @"fb_product_applink_iphone_app_name"; -FBSDKAppEventParameterProduct FBSDKAppEventParameterProductAppLinkIPadUrl = @"fb_product_applink_ipad_url"; -FBSDKAppEventParameterProduct FBSDKAppEventParameterProductAppLinkIPadAppStoreID = @"fb_product_applink_ipad_app_store_id"; -FBSDKAppEventParameterProduct FBSDKAppEventParameterProductAppLinkIPadAppName = @"fb_product_applink_ipad_app_name"; -FBSDKAppEventParameterProduct FBSDKAppEventParameterProductAppLinkAndroidUrl = @"fb_product_applink_android_url"; -FBSDKAppEventParameterProduct FBSDKAppEventParameterProductAppLinkAndroidPackage = @"fb_product_applink_android_package"; -FBSDKAppEventParameterProduct FBSDKAppEventParameterProductAppLinkAndroidAppName = @"fb_product_applink_android_app_name"; -FBSDKAppEventParameterProduct FBSDKAppEventParameterProductAppLinkWindowsPhoneUrl = @"fb_product_applink_windows_phone_url"; -FBSDKAppEventParameterProduct FBSDKAppEventParameterProductAppLinkWindowsPhoneAppID = @"fb_product_applink_windows_phone_app_id"; +FBSDKAppEventParameterProduct FBSDKAppEventParameterProductCustomLabel0 = @"fb_product_custom_label_0"; +FBSDKAppEventParameterProduct FBSDKAppEventParameterProductCustomLabel1 = @"fb_product_custom_label_1"; +FBSDKAppEventParameterProduct FBSDKAppEventParameterProductCustomLabel2 = @"fb_product_custom_label_2"; +FBSDKAppEventParameterProduct FBSDKAppEventParameterProductCustomLabel3 = @"fb_product_custom_label_3"; +FBSDKAppEventParameterProduct FBSDKAppEventParameterProductCustomLabel4 = @"fb_product_custom_label_4"; +FBSDKAppEventParameterProduct FBSDKAppEventParameterProductCategory = @"fb_product_category"; +FBSDKAppEventParameterProduct FBSDKAppEventParameterProductAppLinkIOSUrl = @"fb_product_applink_ios_url"; +FBSDKAppEventParameterProduct FBSDKAppEventParameterProductAppLinkIOSAppStoreID = @"fb_product_applink_ios_app_store_id"; +FBSDKAppEventParameterProduct FBSDKAppEventParameterProductAppLinkIOSAppName = @"fb_product_applink_ios_app_name"; +FBSDKAppEventParameterProduct FBSDKAppEventParameterProductAppLinkIPhoneUrl = @"fb_product_applink_iphone_url"; +FBSDKAppEventParameterProduct FBSDKAppEventParameterProductAppLinkIPhoneAppStoreID = @"fb_product_applink_iphone_app_store_id"; +FBSDKAppEventParameterProduct FBSDKAppEventParameterProductAppLinkIPhoneAppName = @"fb_product_applink_iphone_app_name"; +FBSDKAppEventParameterProduct FBSDKAppEventParameterProductAppLinkIPadUrl = @"fb_product_applink_ipad_url"; +FBSDKAppEventParameterProduct FBSDKAppEventParameterProductAppLinkIPadAppStoreID = @"fb_product_applink_ipad_app_store_id"; +FBSDKAppEventParameterProduct FBSDKAppEventParameterProductAppLinkIPadAppName = @"fb_product_applink_ipad_app_name"; +FBSDKAppEventParameterProduct FBSDKAppEventParameterProductAppLinkAndroidUrl = @"fb_product_applink_android_url"; +FBSDKAppEventParameterProduct FBSDKAppEventParameterProductAppLinkAndroidPackage = @"fb_product_applink_android_package"; +FBSDKAppEventParameterProduct FBSDKAppEventParameterProductAppLinkAndroidAppName = @"fb_product_applink_android_app_name"; +FBSDKAppEventParameterProduct FBSDKAppEventParameterProductAppLinkWindowsPhoneUrl = @"fb_product_applink_windows_phone_url"; +FBSDKAppEventParameterProduct FBSDKAppEventParameterProductAppLinkWindowsPhoneAppID = @"fb_product_applink_windows_phone_app_id"; FBSDKAppEventParameterProduct FBSDKAppEventParameterProductAppLinkWindowsPhoneAppName = @"fb_product_applink_windows_phone_app_name"; // // Public event parameter values // -FBSDKAppEventParameterValue FBSDKAppEventParameterValueNo = @"0"; -FBSDKAppEventParameterValue FBSDKAppEventParameterValueYes = @"1"; - -// -// Public event user data types -// - -FBSDKAppEventUserDataType FBSDKAppEventEmail = @"em"; -FBSDKAppEventUserDataType FBSDKAppEventFirstName = @"fn"; -FBSDKAppEventUserDataType FBSDKAppEventLastName = @"ln"; -FBSDKAppEventUserDataType FBSDKAppEventPhone = @"ph"; -FBSDKAppEventUserDataType FBSDKAppEventDateOfBirth = @"dob"; -FBSDKAppEventUserDataType FBSDKAppEventGender = @"ge"; -FBSDKAppEventUserDataType FBSDKAppEventCity = @"ct"; -FBSDKAppEventUserDataType FBSDKAppEventState = @"st"; -FBSDKAppEventUserDataType FBSDKAppEventZip = @"zp"; -FBSDKAppEventUserDataType FBSDKAppEventCountry = @"country"; +FBSDKAppEventParameterValue FBSDKAppEventParameterValueNo = @"0"; +FBSDKAppEventParameterValue FBSDKAppEventParameterValueYes = @"1"; // // Event names internal to this file // -FBSDKAppEventName FBSDKAppEventNameLoginViewUsage = @"fb_login_view_usage"; -FBSDKAppEventName FBSDKAppEventNameShareSheetLaunch = @"fb_share_sheet_launch"; -FBSDKAppEventName FBSDKAppEventNameShareSheetDismiss = @"fb_share_sheet_dismiss"; -FBSDKAppEventName FBSDKAppEventNameShareTrayDidLaunch = @"fb_share_tray_did_launch"; -FBSDKAppEventName FBSDKAppEventNameShareTrayDidSelectActivity = @"fb_share_tray_did_select_activity"; -FBSDKAppEventName FBSDKAppEventNamePermissionsUILaunch = @"fb_permissions_ui_launch"; -FBSDKAppEventName FBSDKAppEventNamePermissionsUIDismiss = @"fb_permissions_ui_dismiss"; -FBSDKAppEventName FBSDKAppEventNameFBDialogsPresentShareDialog = @"fb_dialogs_present_share"; +FBSDKAppEventName FBSDKAppEventNameLoginViewUsage = @"fb_login_view_usage"; +FBSDKAppEventName FBSDKAppEventNameShareSheetLaunch = @"fb_share_sheet_launch"; +FBSDKAppEventName FBSDKAppEventNameShareSheetDismiss = @"fb_share_sheet_dismiss"; +FBSDKAppEventName FBSDKAppEventNameShareTrayDidLaunch = @"fb_share_tray_did_launch"; +FBSDKAppEventName FBSDKAppEventNameShareTrayDidSelectActivity = @"fb_share_tray_did_select_activity"; +FBSDKAppEventName FBSDKAppEventNamePermissionsUILaunch = @"fb_permissions_ui_launch"; +FBSDKAppEventName FBSDKAppEventNamePermissionsUIDismiss = @"fb_permissions_ui_dismiss"; +FBSDKAppEventName FBSDKAppEventNameFBDialogsPresentShareDialog = @"fb_dialogs_present_share"; FBSDKAppEventName FBSDKAppEventNameFBDialogsPresentShareDialogPhoto = @"fb_dialogs_present_share_photo"; -FBSDKAppEventName FBSDKAppEventNameFBDialogsPresentShareDialogOG = @"fb_dialogs_present_share_og"; -FBSDKAppEventName FBSDKAppEventNameFBDialogsPresentLikeDialogOG = @"fb_dialogs_present_like_og"; -FBSDKAppEventName FBSDKAppEventNameFBDialogsPresentMessageDialog = @"fb_dialogs_present_message"; +FBSDKAppEventName FBSDKAppEventNameFBDialogsPresentShareDialogOG = @"fb_dialogs_present_share_og"; +FBSDKAppEventName FBSDKAppEventNameFBDialogsPresentLikeDialogOG = @"fb_dialogs_present_like_og"; +FBSDKAppEventName FBSDKAppEventNameFBDialogsPresentMessageDialog = @"fb_dialogs_present_message"; FBSDKAppEventName FBSDKAppEventNameFBDialogsPresentMessageDialogPhoto = @"fb_dialogs_present_message_photo"; -FBSDKAppEventName FBSDKAppEventNameFBDialogsNativeLoginDialogStart = @"fb_dialogs_native_login_dialog_start"; -NSString *const FBSDKAppEventsNativeLoginDialogStartTime = @"fb_native_login_dialog_start_time"; +FBSDKAppEventName FBSDKAppEventNameFBDialogsNativeLoginDialogStart = @"fb_dialogs_native_login_dialog_start"; +NSString *const FBSDKAppEventsNativeLoginDialogStartTime = @"fb_native_login_dialog_start_time"; -FBSDKAppEventName FBSDKAppEventNameFBDialogsNativeLoginDialogEnd = @"fb_dialogs_native_login_dialog_end"; -NSString *const FBSDKAppEventsNativeLoginDialogEndTime = @"fb_native_login_dialog_end_time"; +FBSDKAppEventName FBSDKAppEventNameFBDialogsNativeLoginDialogEnd = @"fb_dialogs_native_login_dialog_end"; +NSString *const FBSDKAppEventsNativeLoginDialogEndTime = @"fb_native_login_dialog_end_time"; -FBSDKAppEventName FBSDKAppEventNameFBDialogsWebLoginCompleted = @"fb_dialogs_web_login_dialog_complete"; -NSString *const FBSDKAppEventsWebLoginE2E = @"fb_web_login_e2e"; +FBSDKAppEventName FBSDKAppEventNameFBDialogsWebLoginCompleted = @"fb_dialogs_web_login_dialog_complete"; +NSString *const FBSDKAppEventsWebLoginE2E = @"fb_web_login_e2e"; -FBSDKAppEventName FBSDKAppEventNameFBSessionAuthStart = @"fb_mobile_login_start"; -FBSDKAppEventName FBSDKAppEventNameFBSessionAuthEnd = @"fb_mobile_login_complete"; -FBSDKAppEventName FBSDKAppEventNameFBSessionAuthMethodStart = @"fb_mobile_login_method_start"; -FBSDKAppEventName FBSDKAppEventNameFBSessionAuthMethodEnd = @"fb_mobile_login_method_complete"; +FBSDKAppEventName FBSDKAppEventNameFBSessionAuthStart = @"fb_mobile_login_start"; +FBSDKAppEventName FBSDKAppEventNameFBSessionAuthEnd = @"fb_mobile_login_complete"; +FBSDKAppEventName FBSDKAppEventNameFBSessionAuthMethodStart = @"fb_mobile_login_method_start"; +FBSDKAppEventName FBSDKAppEventNameFBSessionAuthMethodEnd = @"fb_mobile_login_method_complete"; +FBSDKAppEventName FBSDKAppEventNameFBSessionAuthHeartbeat = @"fb_mobile_login_heartbeat"; -FBSDKAppEventName FBSDKAppEventNameFBSDKLikeButtonImpression = @"fb_like_button_impression"; -FBSDKAppEventName FBSDKAppEventNameFBSDKLoginButtonImpression = @"fb_login_button_impression"; -FBSDKAppEventName FBSDKAppEventNameFBSDKSendButtonImpression = @"fb_send_button_impression"; -FBSDKAppEventName FBSDKAppEventNameFBSDKShareButtonImpression = @"fb_share_button_impression"; +FBSDKAppEventName FBSDKAppEventNameFBReferralStart = @"fb_referral_start"; +FBSDKAppEventName FBSDKAppEventNameFBReferralEnd = @"fb_referral_end"; + +FBSDKAppEventName FBSDKAppEventNameFBSDKLikeButtonImpression = @"fb_like_button_impression"; +FBSDKAppEventName FBSDKAppEventNameFBSDKLoginButtonImpression = @"fb_login_button_impression"; +FBSDKAppEventName FBSDKAppEventNameFBSDKSendButtonImpression = @"fb_send_button_impression"; +FBSDKAppEventName FBSDKAppEventNameFBSDKShareButtonImpression = @"fb_share_button_impression"; FBSDKAppEventName FBSDKAppEventNameFBSDKLiveStreamingButtonImpression = @"fb_live_streaming_button_impression"; -FBSDKAppEventName FBSDKAppEventNameFBSDKSmartLoginService = @"fb_smart_login_service"; +FBSDKAppEventName FBSDKAppEventNameFBSDKSmartLoginService = @"fb_smart_login_service"; -FBSDKAppEventName FBSDKAppEventNameFBSDKLikeButtonDidTap = @"fb_like_button_did_tap"; -FBSDKAppEventName FBSDKAppEventNameFBSDKLoginButtonDidTap = @"fb_login_button_did_tap"; -FBSDKAppEventName FBSDKAppEventNameFBSDKSendButtonDidTap = @"fb_send_button_did_tap"; -FBSDKAppEventName FBSDKAppEventNameFBSDKShareButtonDidTap = @"fb_share_button_did_tap"; -FBSDKAppEventName FBSDKAppEventNameFBSDKLiveStreamingButtonDidTap = @"fb_live_streaming_button_did_tap"; +FBSDKAppEventName FBSDKAppEventNameFBSDKLikeButtonDidTap = @"fb_like_button_did_tap"; +FBSDKAppEventName FBSDKAppEventNameFBSDKLoginButtonDidTap = @"fb_login_button_did_tap"; +FBSDKAppEventName FBSDKAppEventNameFBSDKSendButtonDidTap = @"fb_send_button_did_tap"; +FBSDKAppEventName FBSDKAppEventNameFBSDKShareButtonDidTap = @"fb_share_button_did_tap"; +FBSDKAppEventName FBSDKAppEventNameFBSDKLiveStreamingButtonDidTap = @"fb_live_streaming_button_did_tap"; -FBSDKAppEventName FBSDKAppEventNameFBSDKLikeControlDidDisable = @"fb_like_control_did_disable"; -FBSDKAppEventName FBSDKAppEventNameFBSDKLikeControlDidLike = @"fb_like_control_did_like"; -FBSDKAppEventName FBSDKAppEventNameFBSDKLikeControlDidPresentDialog = @"fb_like_control_did_present_dialog"; -FBSDKAppEventName FBSDKAppEventNameFBSDKLikeControlDidTap = @"fb_like_control_did_tap"; -FBSDKAppEventName FBSDKAppEventNameFBSDKLikeControlDidUnlike = @"fb_like_control_did_unlike"; -FBSDKAppEventName FBSDKAppEventNameFBSDKLikeControlError = @"fb_like_control_error"; -FBSDKAppEventName FBSDKAppEventNameFBSDKLikeControlImpression = @"fb_like_control_impression"; -FBSDKAppEventName FBSDKAppEventNameFBSDKLikeControlNetworkUnavailable = @"fb_like_control_network_unavailable"; +FBSDKAppEventName FBSDKAppEventNameFBSDKLikeControlDidDisable = @"fb_like_control_did_disable"; +FBSDKAppEventName FBSDKAppEventNameFBSDKLikeControlDidLike = @"fb_like_control_did_like"; +FBSDKAppEventName FBSDKAppEventNameFBSDKLikeControlDidPresentDialog = @"fb_like_control_did_present_dialog"; +FBSDKAppEventName FBSDKAppEventNameFBSDKLikeControlDidTap = @"fb_like_control_did_tap"; +FBSDKAppEventName FBSDKAppEventNameFBSDKLikeControlDidUnlike = @"fb_like_control_did_unlike"; +FBSDKAppEventName FBSDKAppEventNameFBSDKLikeControlError = @"fb_like_control_error"; +FBSDKAppEventName FBSDKAppEventNameFBSDKLikeControlImpression = @"fb_like_control_impression"; +FBSDKAppEventName FBSDKAppEventNameFBSDKLikeControlNetworkUnavailable = @"fb_like_control_network_unavailable"; -FBSDKAppEventName FBSDKAppEventNameFBSDKEventShareDialogResult = @"fb_dialog_share_result"; -FBSDKAppEventName FBSDKAppEventNameFBSDKEventMessengerShareDialogResult = @"fb_messenger_dialog_share_result"; -FBSDKAppEventName FBSDKAppEventNameFBSDKEventAppInviteShareDialogResult = @"fb_app_invite_dialog_share_result"; +FBSDKAppEventName FBSDKAppEventNameFBSDKEventShareDialogResult = @"fb_dialog_share_result"; +FBSDKAppEventName FBSDKAppEventNameFBSDKEventMessengerShareDialogResult = @"fb_messenger_dialog_share_result"; +FBSDKAppEventName FBSDKAppEventNameFBSDKEventAppInviteShareDialogResult = @"fb_app_invite_dialog_share_result"; -FBSDKAppEventName FBSDKAppEventNameFBSDKEventShareDialogShow = @"fb_dialog_share_show"; -FBSDKAppEventName FBSDKAppEventNameFBSDKEventMessengerShareDialogShow = @"fb_messenger_dialog_share_show"; -FBSDKAppEventName FBSDKAppEventNameFBSDKEventAppInviteShareDialogShow = @"fb_app_invite_share_show"; +FBSDKAppEventName FBSDKAppEventNameFBSDKEventShareDialogShow = @"fb_dialog_share_show"; +FBSDKAppEventName FBSDKAppEventNameFBSDKEventMessengerShareDialogShow = @"fb_messenger_dialog_share_show"; +FBSDKAppEventName FBSDKAppEventNameFBSDKEventAppInviteShareDialogShow = @"fb_app_invite_share_show"; FBSDKAppEventName FBSDKAppEventNameFBSessionFASLoginDialogResult = @"fb_mobile_login_fas_dialog_result"; -FBSDKAppEventName FBSDKAppEventNameFBSDKLiveStreamingStart = @"fb_sdk_live_streaming_start"; -FBSDKAppEventName FBSDKAppEventNameFBSDKLiveStreamingStop = @"fb_sdk_live_streaming_stop"; -FBSDKAppEventName FBSDKAppEventNameFBSDKLiveStreamingPause = @"fb_sdk_live_streaming_pause"; -FBSDKAppEventName FBSDKAppEventNameFBSDKLiveStreamingResume = @"fb_sdk_live_streaming_resume"; -FBSDKAppEventName FBSDKAppEventNameFBSDKLiveStreamingError = @"fb_sdk_live_streaming_error"; +FBSDKAppEventName FBSDKAppEventNameFBSDKLiveStreamingStart = @"fb_sdk_live_streaming_start"; +FBSDKAppEventName FBSDKAppEventNameFBSDKLiveStreamingStop = @"fb_sdk_live_streaming_stop"; +FBSDKAppEventName FBSDKAppEventNameFBSDKLiveStreamingPause = @"fb_sdk_live_streaming_pause"; +FBSDKAppEventName FBSDKAppEventNameFBSDKLiveStreamingResume = @"fb_sdk_live_streaming_resume"; +FBSDKAppEventName FBSDKAppEventNameFBSDKLiveStreamingError = @"fb_sdk_live_streaming_error"; FBSDKAppEventName FBSDKAppEventNameFBSDKLiveStreamingUpdateStatus = @"fb_sdk_live_streaming_update_status"; -FBSDKAppEventName FBSDKAppEventNameFBSDKLiveStreamingVideoID = @"fb_sdk_live_streaming_video_id"; -FBSDKAppEventName FBSDKAppEventNameFBSDKLiveStreamingMic = @"fb_sdk_live_streaming_mic"; -FBSDKAppEventName FBSDKAppEventNameFBSDKLiveStreamingCamera = @"fb_sdk_live_streaming_camera"; +FBSDKAppEventName FBSDKAppEventNameFBSDKLiveStreamingVideoID = @"fb_sdk_live_streaming_video_id"; +FBSDKAppEventName FBSDKAppEventNameFBSDKLiveStreamingMic = @"fb_sdk_live_streaming_mic"; +FBSDKAppEventName FBSDKAppEventNameFBSDKLiveStreamingCamera = @"fb_sdk_live_streaming_camera"; // Event Parameters internal to this file -NSString *const FBSDKAppEventParameterDialogOutcome = @"fb_dialog_outcome"; -NSString *const FBSDKAppEventParameterDialogErrorMessage = @"fb_dialog_outcome_error_message"; -NSString *const FBSDKAppEventParameterDialogMode = @"fb_dialog_mode"; -NSString *const FBSDKAppEventParameterDialogShareContentType = @"fb_dialog_share_content_type"; -NSString *const FBSDKAppEventParameterDialogShareContentUUID = @"fb_dialog_share_content_uuid"; -NSString *const FBSDKAppEventParameterDialogShareContentPageID = @"fb_dialog_share_content_page_id"; -NSString *const FBSDKAppEventParameterShareTrayActivityName = @"fb_share_tray_activity"; -NSString *const FBSDKAppEventParameterShareTrayResult = @"fb_share_tray_result"; +NSString *const FBSDKAppEventParameterDialogOutcome = @"fb_dialog_outcome"; +NSString *const FBSDKAppEventParameterDialogErrorMessage = @"fb_dialog_outcome_error_message"; +NSString *const FBSDKAppEventParameterDialogMode = @"fb_dialog_mode"; +NSString *const FBSDKAppEventParameterDialogShareContentType = @"fb_dialog_share_content_type"; +NSString *const FBSDKAppEventParameterDialogShareContentUUID = @"fb_dialog_share_content_uuid"; +NSString *const FBSDKAppEventParameterDialogShareContentPageID = @"fb_dialog_share_content_page_id"; +NSString *const FBSDKAppEventParameterShareTrayActivityName = @"fb_share_tray_activity"; +NSString *const FBSDKAppEventParameterShareTrayResult = @"fb_share_tray_result"; NSString *const FBSDKAppEventParameterLogTime = @"_logTime"; NSString *const FBSDKAppEventParameterEventName = @"_eventName"; NSString *const FBSDKAppEventParameterImplicitlyLogged = @"_implicitlyLogged"; NSString *const FBSDKAppEventParameterInBackground = @"_inBackground"; -NSString *const FBSDKAppEventParameterLiveStreamingPrevStatus = @"live_streaming_prev_status"; -NSString *const FBSDKAppEventParameterLiveStreamingStatus = @"live_streaming_status"; -NSString *const FBSDKAppEventParameterLiveStreamingError = @"live_streaming_error"; -NSString *const FBSDKAppEventParameterLiveStreamingVideoID = @"live_streaming_video_id"; -NSString *const FBSDKAppEventParameterLiveStreamingMicEnabled = @"live_streaming_mic_enabled"; +NSString *const FBSDKAppEventParameterLiveStreamingPrevStatus = @"live_streaming_prev_status"; +NSString *const FBSDKAppEventParameterLiveStreamingStatus = @"live_streaming_status"; +NSString *const FBSDKAppEventParameterLiveStreamingError = @"live_streaming_error"; +NSString *const FBSDKAppEventParameterLiveStreamingVideoID = @"live_streaming_video_id"; +NSString *const FBSDKAppEventParameterLiveStreamingMicEnabled = @"live_streaming_mic_enabled"; NSString *const FBSDKAppEventParameterLiveStreamingCameraEnabled = @"live_streaming_camera_enabled"; -FBSDKAppEventParameterProduct FBSDKAppEventParameterProductItemID = @"fb_product_item_id"; -FBSDKAppEventParameterProduct FBSDKAppEventParameterProductAvailability = @"fb_product_availability"; -FBSDKAppEventParameterProduct FBSDKAppEventParameterProductCondition = @"fb_product_condition"; -FBSDKAppEventParameterProduct FBSDKAppEventParameterProductDescription = @"fb_product_description"; -FBSDKAppEventParameterProduct FBSDKAppEventParameterProductImageLink = @"fb_product_image_link"; -FBSDKAppEventParameterProduct FBSDKAppEventParameterProductLink = @"fb_product_link"; -FBSDKAppEventParameterProduct FBSDKAppEventParameterProductTitle = @"fb_product_title"; -FBSDKAppEventParameterProduct FBSDKAppEventParameterProductGTIN = @"fb_product_gtin"; -FBSDKAppEventParameterProduct FBSDKAppEventParameterProductMPN = @"fb_product_mpn"; -FBSDKAppEventParameterProduct FBSDKAppEventParameterProductBrand = @"fb_product_brand"; -FBSDKAppEventParameterProduct FBSDKAppEventParameterProductPriceAmount = @"fb_product_price_amount"; -FBSDKAppEventParameterProduct FBSDKAppEventParameterProductPriceCurrency = @"fb_product_price_currency"; +FBSDKAppEventParameterProduct FBSDKAppEventParameterProductItemID = @"fb_product_item_id"; +FBSDKAppEventParameterProduct FBSDKAppEventParameterProductAvailability = @"fb_product_availability"; +FBSDKAppEventParameterProduct FBSDKAppEventParameterProductCondition = @"fb_product_condition"; +FBSDKAppEventParameterProduct FBSDKAppEventParameterProductDescription = @"fb_product_description"; +FBSDKAppEventParameterProduct FBSDKAppEventParameterProductImageLink = @"fb_product_image_link"; +FBSDKAppEventParameterProduct FBSDKAppEventParameterProductLink = @"fb_product_link"; +FBSDKAppEventParameterProduct FBSDKAppEventParameterProductTitle = @"fb_product_title"; +FBSDKAppEventParameterProduct FBSDKAppEventParameterProductGTIN = @"fb_product_gtin"; +FBSDKAppEventParameterProduct FBSDKAppEventParameterProductMPN = @"fb_product_mpn"; +FBSDKAppEventParameterProduct FBSDKAppEventParameterProductBrand = @"fb_product_brand"; +FBSDKAppEventParameterProduct FBSDKAppEventParameterProductPriceAmount = @"fb_product_price_amount"; +FBSDKAppEventParameterProduct FBSDKAppEventParameterProductPriceCurrency = @"fb_product_price_currency"; // Event parameter values internal to this file NSString *const FBSDKAppEventsDialogOutcomeValue_Completed = @"Completed"; NSString *const FBSDKAppEventsDialogOutcomeValue_Cancelled = @"Cancelled"; -NSString *const FBSDKAppEventsDialogOutcomeValue_Failed = @"Failed"; +NSString *const FBSDKAppEventsDialogOutcomeValue_Failed = @"Failed"; -NSString *const FBSDKAppEventsDialogShareModeAutomatic = @"Automatic"; -NSString *const FBSDKAppEventsDialogShareModeBrowser = @"Browser"; -NSString *const FBSDKAppEventsDialogShareModeNative = @"Native"; -NSString *const FBSDKAppEventsDialogShareModeShareSheet = @"ShareSheet"; -NSString *const FBSDKAppEventsDialogShareModeWeb = @"Web"; -NSString *const FBSDKAppEventsDialogShareModeFeedBrowser = @"FeedBrowser"; -NSString *const FBSDKAppEventsDialogShareModeFeedWeb = @"FeedWeb"; -NSString *const FBSDKAppEventsDialogShareModeUnknown = @"Unknown"; +NSString *const FBSDKAppEventsDialogShareModeAutomatic = @"Automatic"; +NSString *const FBSDKAppEventsDialogShareModeBrowser = @"Browser"; +NSString *const FBSDKAppEventsDialogShareModeNative = @"Native"; +NSString *const FBSDKAppEventsDialogShareModeShareSheet = @"ShareSheet"; +NSString *const FBSDKAppEventsDialogShareModeWeb = @"Web"; +NSString *const FBSDKAppEventsDialogShareModeFeedBrowser = @"FeedBrowser"; +NSString *const FBSDKAppEventsDialogShareModeFeedWeb = @"FeedWeb"; +NSString *const FBSDKAppEventsDialogShareModeUnknown = @"Unknown"; -NSString *const FBSDKAppEventsDialogShareContentTypeStatus = @"Status"; -NSString *const FBSDKAppEventsDialogShareContentTypePhoto = @"Photo"; -NSString *const FBSDKAppEventsDialogShareContentTypeVideo = @"Video"; -NSString *const FBSDKAppEventsDialogShareContentTypeCamera = @"Camera"; -NSString *const FBSDKAppEventsDialogShareContentTypeUnknown = @"Unknown"; +NSString *const FBSDKAppEventsDialogShareContentTypeStatus = @"Status"; +NSString *const FBSDKAppEventsDialogShareContentTypePhoto = @"Photo"; +NSString *const FBSDKAppEventsDialogShareContentTypeVideo = @"Video"; +NSString *const FBSDKAppEventsDialogShareContentTypeCamera = @"Camera"; +NSString *const FBSDKAppEventsDialogShareContentTypeUnknown = @"Unknown"; -NSString *const FBSDKGateKeeperAppEventsKillSwitch = @"app_events_killswitch"; +NSString *const FBSDKGateKeeperAppEventsKillSwitch = @"app_events_killswitch"; #if __IPHONE_OS_VERSION_MAX_ALLOWED >= __IPHONE_10_0 @@ -334,7 +327,7 @@ @interface FBSDKAppEvents () @property (nonatomic, assign) FBSDKAppEventsFlushBehavior flushBehavior; -//for testing only. +// for testing only. @property (nonatomic, assign) BOOL disableTimer; @property (nonatomic, copy) NSString *pushNotificationsDeviceTokenString; @@ -375,8 +368,8 @@ - (instancetype)init __weak FBSDKAppEvents *weakSelf = self; self.flushTimer = [FBSDKUtility startGCDTimerWithInterval:FLUSH_PERIOD_IN_SECONDS block:^{ - [weakSelf flushTimerFired:nil]; - }]; + [weakSelf flushTimerFired:nil]; + }]; NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults]; _userID = [defaults stringForKey:USER_ID_USER_DEFAULTS_KEY]; @@ -386,7 +379,8 @@ - (instancetype)init return self; } -- (void)registerNotifications { +- (void)registerNotifications +{ [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(applicationMovingFromActiveStateOrTerminating) @@ -482,7 +476,6 @@ + (void)logPurchase:(double)purchaseAmount parameters:(NSDictionary *)parameters accessToken:(FBSDKAccessToken *)accessToken { - // A purchase event is just a regular logged event with a given event name // and treating the currency value as going into the parameters dictionary. NSDictionary *newParameters; @@ -645,7 +638,9 @@ + (void)activateApp // Fetch app settings and register for transaction notifications only if app supports implicit purchase // events FBSDKAppEvents *instance = [FBSDKAppEvents singleton]; - [instance publishInstall]; + [FBSDKAppEventsConfigurationManager loadAppEventsConfigurationWithBlock:^{ + [instance publishInstall]; + }]; [instance fetchServerConfiguration:NULL]; // Restore time spent data, indicating that we're being called from "activateApp", which will, @@ -697,9 +692,11 @@ + (NSString *)loggingOverrideAppID + (void)setLoggingOverrideAppID:(NSString *)appID { if (![g_overrideAppID isEqualToString:appID]) { - FBSDKConditionalLog(![FBSDKAppEvents singleton]->_explicitEventsLoggedYet, - FBSDKLoggingBehaviorDeveloperErrors, - @"[FBSDKAppEvents setLoggingOverrideAppID:] should only be called prior to any events being logged."); + FBSDKConditionalLog( + ![FBSDKAppEvents singleton]->_explicitEventsLoggedYet, + FBSDKLoggingBehaviorDeveloperErrors, + @"[FBSDKAppEvents setLoggingOverrideAppID:] should only be called prior to any events being logged." + ); g_overrideAppID = appID; } } @@ -741,51 +738,41 @@ + (void)setUserEmail:(nullable NSString *)email zip:(nullable NSString *)zip country:(nullable NSString *)country { - [FBSDKUserDataStore setAndHashUserEmail:email - firstName:firstName - lastName:lastName - phone:phone - dateOfBirth:dateOfBirth - gender:gender - city:city - state:state - zip:zip - country:country]; + [FBSDKUserDataStore setUserEmail:email + firstName:firstName + lastName:lastName + phone:phone + dateOfBirth:dateOfBirth + gender:gender + city:city + state:state + zip:zip + country:country]; } -+ (NSString*)getUserData ++ (NSString *)getUserData { - return [FBSDKUserDataStore getHashedData]; + return [FBSDKUserDataStore getUserData]; } + (void)clearUserData { - [FBSDKUserDataStore setAndHashUserEmail:nil - firstName:nil - lastName:nil - phone:nil - dateOfBirth:nil - gender:nil - city:nil - state:nil - zip:nil - country:nil]; + [FBSDKUserDataStore clearUserData]; } + (void)setUserData:(nullable NSString *)data forType:(FBSDKAppEventUserDataType)type { - [FBSDKUserDataStore setAndHashData:data forType:type]; + [FBSDKUserDataStore setUserData:data forType:type]; } + (void)clearUserDataForType:(FBSDKAppEventUserDataType)type { - [FBSDKUserDataStore clearDataForType:type]; + [FBSDKUserDataStore clearUserDataForType:type]; } + (void)updateUserProperties:(NSDictionary *)properties handler:(FBSDKGraphRequestBlock)handler -{ -} +{} + (NSString *)anonymousID { @@ -793,7 +780,8 @@ + (NSString *)anonymousID } #if !TARGET_OS_TV -+ (void)augmentHybridWKWebView:(WKWebView *)webView { ++ (void)augmentHybridWKWebView:(WKWebView *)webView +{ // Ensure we can instantiate WebKit before trying this Class WKWebViewClass = fbsdkdfl_WKWebViewClass(); if (WKWebViewClass != nil && [webView isKindOfClass:WKWebViewClass]) { @@ -803,22 +791,22 @@ + (void)augmentHybridWKWebView:(WKWebView *)webView { FBSDKHybridAppEventsScriptMessageHandler *scriptHandler = [[FBSDKHybridAppEventsScriptMessageHandler alloc] init]; [controller addScriptMessageHandler:scriptHandler name:FBSDKAppEventsWKWebViewMessagesHandlerKey]; - NSString *js = [NSString stringWithFormat:@"window.fbmq_%@={'sendEvent': function(pixel_id,event_name,custom_data){var msg={\"%@\":pixel_id, \"%@\":event_name,\"%@\":custom_data};window.webkit.messageHandlers[\"%@\"].postMessage(msg);}, 'getProtocol':function(){return \"%@\";}}", - [[self singleton] appID], - FBSDKAppEventsWKWebViewMessagesPixelIDKey, - FBSDKAppEventsWKWebViewMessagesEventKey, - FBSDKAppEventsWKWebViewMessagesParamsKey, - FBSDKAppEventsWKWebViewMessagesHandlerKey, - FBSDKAPPEventsWKWebViewMessagesProtocolKey - ]; + NSString *js = [NSString stringWithFormat:@"window.fbmq_%@={'sendEvent': function(pixel_id,event_name,custom_data){var msg={\"%@\":pixel_id, \"%@\":event_name,\"%@\":custom_data};window.webkit.messageHandlers[\"%@\"].postMessage(msg);}, 'getProtocol':function(){return \"%@\";}}", + [[self singleton] appID], + FBSDKAppEventsWKWebViewMessagesPixelIDKey, + FBSDKAppEventsWKWebViewMessagesEventKey, + FBSDKAppEventsWKWebViewMessagesParamsKey, + FBSDKAppEventsWKWebViewMessagesHandlerKey, + FBSDKAPPEventsWKWebViewMessagesProtocolKey + ]; [controller addUserScript:[[WKUserScriptClass alloc] initWithSource:js injectionTime:WKUserScriptInjectionTimeAtDocumentStart forMainFrameOnly:NO]]; } - } - else { + } else { [FBSDKAppEventsUtility logAndNotify:@"You must call augmentHybridWKWebView with WebKit linked to your project and a WKWebView instance"]; } } + #endif + (void)setIsUnityInit:(BOOL)isUnityInit @@ -834,7 +822,7 @@ + (void)sendEventBindingsToUnity if ([FBSDKAppEvents singleton]->_isUnityInit && [FBSDKAppEvents singleton]->_serverConfiguration && [FBSDKTypeUtility isValidJSONObject:[FBSDKAppEvents singleton]->_serverConfiguration.eventBindings] - ) { + ) { NSData *jsonData = [FBSDKTypeUtility dataWithJSONObject:[FBSDKAppEvents singleton]->_serverConfiguration.eventBindings ?: @"" options:0 error:nil]; @@ -846,6 +834,7 @@ + (void)sendEventBindingsToUnity } } } + #pragma clang diagnostic pop #pragma mark - Internal Methods @@ -930,23 +919,9 @@ + (void)logImplicitEvent:(NSString *)eventName accessToken:accessToken]; } - -#ifdef DEBUG -static dispatch_once_t *onceTokenPointer; -+ (void)resetSingleton -{ - if (onceTokenPointer) { - *onceTokenPointer = 0; - } -} -#endif - + (FBSDKAppEvents *)singleton { static dispatch_once_t onceToken; -#ifdef DEBUG - onceTokenPointer = &onceToken; -#endif static FBSDKAppEvents *shared = nil; dispatch_once(&onceToken, ^{ shared = [[self alloc] init]; @@ -959,7 +934,7 @@ - (void)flushForReason:(FBSDKAppEventsFlushReason)flushReason // Always flush asynchronously, even on main thread, for two reasons: // - most consistent code path for all threads. // - allow locks being held by caller to be released prior to actual flushing work being done. - @synchronized (self) { + @synchronized(self) { if (!_appEventsState) { return; } @@ -985,14 +960,18 @@ - (void)publishInstall [FBSDKLogger singleShotLogEntry:FBSDKLoggingBehaviorDeveloperErrors logEntry:@"Missing [FBSDKAppEvents appID] for [FBSDKAppEvents publishInstall:]"]; return; } + if ([FBSDKAppEventsUtility shouldDropAppEvent]) { + return; + } NSString *lastAttributionPingString = [NSString stringWithFormat:@"com.facebook.sdk:lastAttributionPing%@", appID]; NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults]; if ([defaults objectForKey:lastAttributionPingString]) { return; } [self fetchServerConfiguration:^{ - NSDictionary *params = [FBSDKAppEventsUtility activityParametersDictionaryForEvent:@"MOBILE_APP_INSTALL" - shouldAccessAdvertisingID:self->_serverConfiguration.isAdvertisingIDEnabled]; + NSMutableDictionary *params = [FBSDKAppEventsUtility activityParametersDictionaryForEvent:@"MOBILE_APP_INSTALL" + shouldAccessAdvertisingID:self->_serverConfiguration.isAdvertisingIDEnabled]; + [self appendInstallTimestamp:params]; NSString *path = [NSString stringWithFormat:@"%@/activities", appID]; FBSDKGraphRequest *request = [[FBSDKGraphRequest alloc] initWithGraphPath:path parameters:params @@ -1010,8 +989,72 @@ - (void)publishInstall }]; } +- (void)publishATE +{ + static dispatch_once_t onceToken; + dispatch_once(&onceToken, ^{ + NSString *appID = [self appID]; + if (appID.length == 0) { + [FBSDKLogger singleShotLogEntry:FBSDKLoggingBehaviorDeveloperErrors logEntry:@"Missing [FBSDKAppEvents appID] for [FBSDKAppEvents publishATE:]"]; + return; + } + NSString *lastATEPingString = [NSString stringWithFormat:@"com.facebook.sdk:lastATEPing%@", appID]; + NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults]; + id lastPublishDate = [defaults objectForKey:lastATEPingString]; + if ([lastPublishDate isKindOfClass:[NSDate class]] && [(NSDate *)lastPublishDate timeIntervalSinceNow] * -1 < 24 * 60 * 60) { + return; + } + + NSMutableDictionary *parameters = [NSMutableDictionary dictionary]; + [FBSDKTypeUtility dictionary:parameters setObject:@"CUSTOM_APP_EVENTS" forKey:@"event"]; + + NSOperatingSystemVersion operatingSystemVersion = [FBSDKInternalUtility operatingSystemVersion]; + NSString *osVersion = [NSString stringWithFormat:@"%ti.%ti.%ti", + operatingSystemVersion.majorVersion, + operatingSystemVersion.minorVersion, + operatingSystemVersion.patchVersion]; + + NSArray *event = @[ + @{ + @"_eventName" : @"fb_mobile_ate_status", + @"ate_status" : @([FBSDKSettings getAdvertisingTrackingStatus]).stringValue, + @"os_version" : osVersion, + } + ]; + [FBSDKTypeUtility dictionary:parameters setObject:[FBSDKBasicUtility JSONStringForObject:event error:NULL invalidObjectHandler:NULL] forKey:@"custom_events"]; + + [FBSDKAppEventsDeviceInfo extendDictionaryWithDeviceInfo:parameters]; + + NSString *path = [NSString stringWithFormat:@"%@/activities", appID]; + FBSDKGraphRequest *request = [[FBSDKGraphRequest alloc] initWithGraphPath:path + parameters:parameters + tokenString:nil + HTTPMethod:FBSDKHTTPMethodPOST + flags:FBSDKGraphRequestFlagDoNotInvalidateTokenOnError | FBSDKGraphRequestFlagDisableErrorRecovery]; + [request startWithCompletionHandler:^(FBSDKGraphRequestConnection *connection, id result, NSError *error) { + if (!error) { + [defaults setObject:[NSDate date] forKey:lastATEPingString]; + } + }]; + }); +} + +- (void)appendInstallTimestamp:(NSMutableDictionary *)parameters +{ + if (@available(iOS 14.0, *)) { + if ([FBSDKSettings isSetATETimeExceedsInstallTime]) { + NSDate *setAteTimestamp = [FBSDKSettings getSetAdvertiserTrackingEnabledTimestamp]; + [FBSDKTypeUtility dictionary:parameters setObject:@([FBSDKAppEventsUtility convertToUnixTime:setAteTimestamp]) forKey:@"install_timestamp"]; + } else { + NSDate *installTimestamp = [FBSDKSettings getInstallTimestamp]; + [FBSDKTypeUtility dictionary:parameters setObject:@([FBSDKAppEventsUtility convertToUnixTime:installTimestamp]) forKey:@"install_timestamp"]; + } + } +} + #if !TARGET_OS_TV -- (void)enableCodelessEvents { +- (void)enableCodelessEvents +{ if (_serverConfiguration.isCodelessEventsEnabled) { [FBSDKCodelessIndexer enable]; @@ -1027,6 +1070,7 @@ - (void)enableCodelessEvents { } } } + #endif // app events can use a server configuration up to 24 hours old to minimize network traffic. @@ -1050,7 +1094,14 @@ - (void)fetchServerConfiguration:(FBSDKCodeBlock)callback [FBSDKEventDeactivationManager enable]; } }]; -#if !TARGET_OS_TV + if (@available(iOS 14.0, *)) { + [FBSDKFeatureManager checkFeature:FBSDKFeatureATELogging completionBlock:^(BOOL enabled) { + if (enabled) { + [self publishATE]; + } + }]; + } + #if !TARGET_OS_TV [FBSDKFeatureManager checkFeature:FBSDKFeatureCodelessEvents completionBlock:^(BOOL enabled) { if (enabled) { [self enableCodelessEvents]; @@ -1066,7 +1117,19 @@ - (void)fetchServerConfiguration:(FBSDKCodeBlock)callback [FBSDKModelManager enable]; } }]; -#endif + if (@available(iOS 11.3, *)) { + [FBSDKFeatureManager checkFeature:FBSDKFeatureSKAdNetwork completionBlock:^(BOOL SKAdNetworkEnabled) { + if (SKAdNetworkEnabled) { + [SKAdNetwork registerAppForAdNetworkAttribution]; + [FBSDKFeatureManager checkFeature:FBSDKFeatureSKAdNetworkConversionValue completionBlock:^(BOOL SKAdNetworkConversionValueEnabled) { + if (SKAdNetworkConversionValueEnabled) { + [FBSDKSKAdNetworkReporter enable]; + } + }]; + } + }]; + } + #endif if (callback) { callback(); } @@ -1090,6 +1153,15 @@ - (void)instanceLogEvent:(FBSDKAppEventName)eventName return; } +#if !TARGET_OS_TV + // Update conversion value for SKAdNetwork if needed + [FBSDKSKAdNetworkReporter recordAndUpdateEvent:eventName currency:[FBSDKTypeUtility dictionary:parameters objectForKey:FBSDKAppEventParameterNameCurrency ofType:NSString.class] value:valueToSum]; +#endif + + if ([FBSDKAppEventsUtility shouldDropAppEvent]) { + return; + } + if (isImplicitlyLogged && _serverConfiguration && !_serverConfiguration.isImplicitLoggingSupported) { return; } @@ -1165,7 +1237,7 @@ - (void)instanceLogEvent:(FBSDKAppEventName)eventName NSString *tokenString = [FBSDKAppEventsUtility tokenStringToUseFor:accessToken]; NSString *appID = [self appID]; - @synchronized (self) { + @synchronized(self) { if (!_appEventsState) { _appEventsState = [[FBSDKAppEventsState alloc] initWithToken:tokenString appID:appID]; } else if (![_appEventsState isCompatibleWithTokenString:tokenString appID:appID]) { @@ -1187,12 +1259,13 @@ - (void)instanceLogEvent:(FBSDKAppEventName)eventName [self checkPersistedEvents]; - if (_appEventsState.events.count > NUM_LOG_EVENTS_TO_TRY_TO_FLUSH_AFTER && - self.flushBehavior != FBSDKAppEventsFlushBehaviorExplicitOnly) { + if (_appEventsState.events.count > NUM_LOG_EVENTS_TO_TRY_TO_FLUSH_AFTER + && self.flushBehavior != FBSDKAppEventsFlushBehaviorExplicitOnly) { [self flushForReason:FBSDKAppEventsFlushReasonEventThreshold]; } } } + #pragma clang diagnostic pop // this fetches persisted event states. @@ -1237,7 +1310,6 @@ - (void)checkPersistedEvents - (void)flushOnMainQueue:(FBSDKAppEventsState *)appEventsState forReason:(FBSDKAppEventsFlushReason)reason { - if (appEventsState.events.count == 0) { return; } @@ -1247,6 +1319,10 @@ - (void)flushOnMainQueue:(FBSDKAppEventsState *)appEventsState return; } + if ([FBSDKAppEventsUtility shouldDropAppEvent]) { + return; + } + [FBSDKAppEventsUtility ensureOnMainThread:NSStringFromSelector(_cmd) className:NSStringFromClass([self class])]; [self fetchServerConfiguration:^(void) { @@ -1303,7 +1379,6 @@ - (void)flushOnMainQueue:(FBSDKAppEventsState *)appEventsState loggingEntry:loggingEntry appEventsState:(FBSDKAppEventsState *)appEventsState]; }]; - }]; } @@ -1314,7 +1389,7 @@ - (void)handleActivitiesPostCompletion:(NSError *)error typedef NS_ENUM(NSUInteger, FBSDKAppEventsFlushResult) { FlushResultSuccess, FlushResultServerError, - FlushResultNoConnectivity + FlushResultNoConnectivity, }; [FBSDKAppEventsUtility ensureOnMainThread:NSStringFromSelector(_cmd) className:NSStringFromClass([self class])]; @@ -1331,7 +1406,7 @@ typedef NS_ENUM(NSUInteger, FBSDKAppEventsFlushResult) { if (flushResult == FlushResultServerError) { // Only log events that developer can do something with (i.e., if parameters are incorrect). - // as opposed to cases where the token is bad. + // as opposed to cases where the token is bad. if ([error.userInfo[FBSDKGraphRequestErrorKey] unsignedIntegerValue] == FBSDKGraphRequestErrorOther) { NSString *message = [NSString stringWithFormat:@"Failed to send AppEvents: %@", error]; [FBSDKAppEventsUtility logAndNotify:message allowLogAsDeveloperError:!appEventsState.areAllEventsImplicit]; @@ -1389,7 +1464,7 @@ - (void)applicationMovingFromActiveStateOrTerminating // When moving from active state, we don't have time to wait for the result of a flush, so // just persist events to storage, and we'll process them at the next activation. FBSDKAppEventsState *copy = nil; - @synchronized (self) { + @synchronized(self) { copy = [_appEventsState copy]; _appEventsState = nil; } @@ -1407,11 +1482,11 @@ + (FBSDKGraphRequest *)requestForCustomAudienceThirdPartyIDWithAccessToken:(FBSD // Rules for how we use the attribution ID / advertiser ID for an 'custom_audience_third_party_id' Graph API request // 1) if the OS tells us that the user has Limited Ad Tracking, then just don't send, and return a nil in the token. // 2) if the app has set 'limitEventAndDataUsage', this effectively implies that app-initiated ad targeting shouldn't happen, - // so use that data here to return nil as well. + // so use that data here to return nil as well. // 3) if we have a user session token, then no need to send attribution ID / advertiser ID back as the udid parameter // 4) otherwise, send back the udid parameter. - if ([FBSDKAppEventsUtility advertisingTrackingStatus] == FBSDKAdvertisingTrackingDisallowed || FBSDKSettings.shouldLimitEventAndDataUsage) { + if ([FBSDKSettings getAdvertisingTrackingStatus] == FBSDKAdvertisingTrackingDisallowed || FBSDKSettings.shouldLimitEventAndDataUsage) { return nil; } diff --git a/FBSDKCoreKit/FBSDKCoreKit/AppEvents/Internal/AAM/FBSDKMetadataIndexer.h b/FBSDKCoreKit/FBSDKCoreKit/AppEvents/Internal/AAM/FBSDKMetadataIndexer.h index 3b97764e55..941d3cc657 100644 --- a/FBSDKCoreKit/FBSDKCoreKit/AppEvents/Internal/AAM/FBSDKMetadataIndexer.h +++ b/FBSDKCoreKit/FBSDKCoreKit/AppEvents/Internal/AAM/FBSDKMetadataIndexer.h @@ -20,7 +20,7 @@ #if !TARGET_OS_TV -#import + #import NS_ASSUME_NONNULL_BEGIN diff --git a/FBSDKCoreKit/FBSDKCoreKit/AppEvents/Internal/AAM/FBSDKMetadataIndexer.m b/FBSDKCoreKit/FBSDKCoreKit/AppEvents/Internal/AAM/FBSDKMetadataIndexer.m index a89e838fc1..a54a9547bc 100644 --- a/FBSDKCoreKit/FBSDKCoreKit/AppEvents/Internal/AAM/FBSDKMetadataIndexer.m +++ b/FBSDKCoreKit/FBSDKCoreKit/AppEvents/Internal/AAM/FBSDKMetadataIndexer.m @@ -20,23 +20,33 @@ #if !TARGET_OS_TV -#import "FBSDKMetadataIndexer.h" + #import "FBSDKMetadataIndexer.h" -#import -#import -#import + #import -#import + #import + #import + #import -#import "FBSDKCoreKit+Internal.h" + #import "FBSDKCoreKit+Internal.h" -static const int FBSDKMetadataIndexerMaxTextLength = 100; -static const int FBSDKMetadataIndexerMaxIndicatorLength = 100; -static const int FBSDKMetadataIndexerMaxValue = 5; +@interface FBSDKUserDataStore (Internal) -static NSString * const FIELD_K = @"k"; -static NSString * const FIELD_V = @"v"; -static NSString * const FIELD_K_DELIMITER = @","; ++ (void)setInternalHashData:(nullable NSString *)hashData + forType:(FBSDKAppEventUserDataType)type; ++ (void)setEnabledRules:(NSArray *)rules; + ++ (nullable NSString *)getInternalHashedDataForType:(FBSDKAppEventUserDataType)type; + +@end + +static const int FBSDKMetadataIndexerMaxTextLength = 100; +static const int FBSDKMetadataIndexerMaxIndicatorLength = 100; +static const int FBSDKMetadataIndexerMaxValue = 5; + +static NSString *const FIELD_K = @"k"; +static NSString *const FIELD_V = @"v"; +static NSString *const FIELD_K_DELIMITER = @","; static NSMutableDictionary *> *_rules; static NSMutableDictionary *> *_store; @@ -52,17 +62,21 @@ + (void)initialize + (void)enable { - if (FBSDKAdvertisingTrackingAllowed != [FBSDKAppEventsUtility advertisingTrackingStatus]) { - return; - } + @try { + if ([FBSDKAppEventsUtility shouldDropAppEvent]) { + return; + } - NSDictionary *AAMRules = [FBSDKServerConfigurationManager cachedServerConfiguration].AAMRules; - if (AAMRules) { - [FBSDKMetadataIndexer setupWithRules:AAMRules]; + NSDictionary *AAMRules = [FBSDKServerConfigurationManager cachedServerConfiguration].AAMRules; + if (AAMRules) { + [FBSDKMetadataIndexer setupWithRules:AAMRules]; + } + } @catch (NSException *exception) { + NSLog(@"Fail to enable Automatic Advanced Matching, exception reason: %@", exception.reason); } } -+ (void)setupWithRules:(NSDictionary * _Nullable)rules ++ (void)setupWithRules:(NSDictionary *_Nullable)rules { if (0 == rules.count) { return; @@ -104,7 +118,7 @@ + (void)initStore } } -+ (void)constructRules:(NSDictionary * _Nullable)rules ++ (void)constructRules:(NSDictionary *_Nullable)rules { for (NSString *key in rules) { NSDictionary *value = [FBSDKTypeUtility dictionaryValue:rules[key]]; @@ -211,10 +225,10 @@ + (void)getMetadataWithText:(NSString *)text { text = [self normalizeValue:text]; placeholder = [self normalizeField:placeholder]; - if (secureTextEntry || [placeholder containsString:@"password"] || - text.length == 0 || - text.length > FBSDKMetadataIndexerMaxTextLength || - placeholder.length >= FBSDKMetadataIndexerMaxIndicatorLength) { + if (secureTextEntry || [placeholder containsString:@"password"] + || text.length == 0 + || text.length > FBSDKMetadataIndexerMaxTextLength + || placeholder.length >= FBSDKMetadataIndexerMaxIndicatorLength) { return; } @@ -239,7 +253,7 @@ + (void)getMetadataWithText:(NSString *)text } } -#pragma mark - Helper Methods + #pragma mark - Helper Methods + (void)checkAndAppendData:(NSString *)data forKey:(NSString *)key diff --git a/FBSDKCoreKit/FBSDKCoreKit/AppEvents/Internal/Codeless/FBSDKCodelessIndexer.m b/FBSDKCoreKit/FBSDKCoreKit/AppEvents/Internal/Codeless/FBSDKCodelessIndexer.m index 781f6986c4..18b1f01f9a 100644 --- a/FBSDKCoreKit/FBSDKCoreKit/AppEvents/Internal/Codeless/FBSDKCodelessIndexer.m +++ b/FBSDKCoreKit/FBSDKCoreKit/AppEvents/Internal/Codeless/FBSDKCodelessIndexer.m @@ -20,17 +20,17 @@ #if !TARGET_OS_TV -#import "FBSDKCodelessIndexer.h" + #import "FBSDKCodelessIndexer.h" -#import -#import -#import + #import -#import + #import + #import + #import -#import "FBSDKCoreKit+Internal.h" -#import "FBSDKGraphRequest.h" -#import "FBSDKSettings.h" + #import "FBSDKCoreKit+Internal.h" + #import "FBSDKGraphRequest.h" + #import "FBSDKSettings.h" @implementation FBSDKCodelessIndexer @@ -62,8 +62,8 @@ + (void)enable #endif } -#pragma clang diagnostic push -#pragma clang diagnostic ignored "-Wdeprecated-declarations" + #pragma clang diagnostic push + #pragma clang diagnostic ignored "-Wdeprecated-declarations" // DO NOT call this function, it is only called once in the load function + (void)loadCodelessSettingWithCompletionBlock:(FBSDKCodelessSettingLoadBlock)completionBlock { @@ -119,7 +119,8 @@ + (void)loadCodelessSettingWithCompletionBlock:(FBSDKCodelessSettingLoadBlock)co } }]; } -#pragma clang diagnostic pop + + #pragma clang diagnostic pop + (FBSDKGraphRequest *)requestToLoadCodelessSetup:(NSString *)appID { @@ -129,9 +130,9 @@ + (FBSDKGraphRequest *)requestToLoadCodelessSetup:(NSString *)appID } NSDictionary *parameters = @{ - @"fields": CODELESS_SETUP_ENABLED_FIELD, - @"advertiser_id": advertiserID - }; + @"fields" : CODELESS_SETUP_ENABLED_FIELD, + @"advertiser_id" : advertiserID + }; FBSDKGraphRequest *request = [[FBSDKGraphRequest alloc] initWithGraphPath:appID parameters:parameters @@ -153,21 +154,23 @@ + (void)setupGesture Class class = [UIApplication class]; [FBSDKSwizzler swizzleSelector:@selector(motionBegan:withEvent:) onClass:class withBlock:^{ - if ([FBSDKServerConfigurationManager cachedServerConfiguration].isCodelessEventsEnabled) { - [self checkCodelessIndexingSession]; - } - } named:@"motionBegan"]; + if ([FBSDKServerConfigurationManager cachedServerConfiguration].isCodelessEventsEnabled) { + [self checkCodelessIndexingSession]; + } + } named:@"motionBegan"]; } + (void)checkCodelessIndexingSession { - if (_isCheckingSession) return; + if (_isCheckingSession) { + return; + } _isCheckingSession = YES; NSDictionary *parameters = @{ - CODELESS_INDEXING_SESSION_ID_KEY: [self currentSessionDeviceID], - CODELESS_INDEXING_EXT_INFO_KEY: [self extInfo] - }; + CODELESS_INDEXING_SESSION_ID_KEY : [self currentSessionDeviceID], + CODELESS_INDEXING_EXT_INFO_KEY : [self extInfo] + }; FBSDKGraphRequest *request = [[FBSDKGraphRequest alloc] initWithGraphPath:[NSString stringWithFormat:@"%@/%@", [FBSDKSettings appID], CODELESS_INDEXING_SESSION_ENDPOINT] @@ -181,10 +184,10 @@ + (void)checkCodelessIndexingSession _lastTreeHash = nil; if (!_appIndexingTimer) { _appIndexingTimer = [NSTimer timerWithTimeInterval:CODELESS_INDEXING_UPLOAD_INTERVAL_IN_SECONDS - target:self - selector:@selector(startIndexing) - userInfo:nil - repeats:YES]; + target:self + selector:@selector(startIndexing) + userInfo:nil + repeats:YES]; [[NSRunLoop mainRunLoop] addTimer:_appIndexingTimer forMode:NSDefaultRunLoopMode]; } @@ -235,7 +238,8 @@ + (NSString *)extInfo return extinfo ?: @""; } -+ (void)startIndexing { ++ (void)startIndexing +{ if (!_isCodelessIndexingEnabled) { return; } @@ -250,10 +254,10 @@ + (void)startIndexing { Class FBUnityUtility = objc_lookUpClass("FBUnityUtility"); SEL selector = NSSelectorFromString(@"triggerUploadViewHierarchy"); if (FBUnityUtility && selector && [FBUnityUtility respondsToSelector:selector]) { -#pragma clang diagnostic push -#pragma clang diagnostic ignored "-Warc-performSelector-leaks" + #pragma clang diagnostic push + #pragma clang diagnostic ignored "-Warc-performSelector-leaks" [FBUnityUtility performSelector:selector]; -#pragma clang diagnostic pop + #pragma clang diagnostic pop } } else { [self uploadIndexing]; @@ -263,7 +267,7 @@ + (void)startIndexing { + (void)uploadIndexing { if (_isCodelessIndexing) { - return; + return; } NSString *tree = [FBSDKCodelessIndexer currentViewTree]; @@ -273,44 +277,44 @@ + (void)uploadIndexing + (void)uploadIndexing:(NSString *)tree { - if (_isCodelessIndexing) { - return; - } + if (_isCodelessIndexing) { + return; + } - if (!tree) { - return; - } + if (!tree) { + return; + } - NSString *currentTreeHash = [FBSDKUtility SHA256Hash:tree]; - if (_lastTreeHash && [_lastTreeHash isEqualToString:currentTreeHash]) { - return; - } + NSString *currentTreeHash = [FBSDKUtility SHA256Hash:tree]; + if (_lastTreeHash && [_lastTreeHash isEqualToString:currentTreeHash]) { + return; + } - _lastTreeHash = currentTreeHash; - - NSBundle *mainBundle = [NSBundle mainBundle]; - NSString *version = [mainBundle objectForInfoDictionaryKey:@"CFBundleShortVersionString"]; - - FBSDKGraphRequest *request = [[FBSDKGraphRequest alloc] - initWithGraphPath:[NSString stringWithFormat:@"%@/%@", - [FBSDKSettings appID], CODELESS_INDEXING_ENDPOINT] - parameters:@{ - CODELESS_INDEXING_TREE_KEY: tree, - CODELESS_INDEXING_APP_VERSION_KEY: version ?: @"", - CODELESS_INDEXING_PLATFORM_KEY: @"iOS", - CODELESS_INDEXING_SESSION_ID_KEY: [self currentSessionDeviceID] - } - HTTPMethod:FBSDKHTTPMethodPOST]; - _isCodelessIndexing = YES; - [request startWithCompletionHandler:^(FBSDKGraphRequestConnection *connection, id result, NSError *error) { - _isCodelessIndexing = NO; - if ([result isKindOfClass:[NSDictionary class]]) { - _isCodelessIndexingEnabled = [result[CODELESS_INDEXING_STATUS_KEY] boolValue]; - if (!_isCodelessIndexingEnabled) { - _deviceSessionID = nil; - } - } - }]; + _lastTreeHash = currentTreeHash; + + NSBundle *mainBundle = [NSBundle mainBundle]; + NSString *version = [mainBundle objectForInfoDictionaryKey:@"CFBundleShortVersionString"]; + + FBSDKGraphRequest *request = [[FBSDKGraphRequest alloc] + initWithGraphPath:[NSString stringWithFormat:@"%@/%@", + [FBSDKSettings appID], CODELESS_INDEXING_ENDPOINT] + parameters:@{ + CODELESS_INDEXING_TREE_KEY : tree, + CODELESS_INDEXING_APP_VERSION_KEY : version ?: @"", + CODELESS_INDEXING_PLATFORM_KEY : @"iOS", + CODELESS_INDEXING_SESSION_ID_KEY : [self currentSessionDeviceID] + } + HTTPMethod:FBSDKHTTPMethodPOST]; + _isCodelessIndexing = YES; + [request startWithCompletionHandler:^(FBSDKGraphRequestConnection *connection, id result, NSError *error) { + _isCodelessIndexing = NO; + if ([result isKindOfClass:[NSDictionary class]]) { + _isCodelessIndexingEnabled = [result[CODELESS_INDEXING_STATUS_KEY] boolValue]; + if (!_isCodelessIndexingEnabled) { + _deviceSessionID = nil; + } + } + }]; } + (NSString *)currentViewTree @@ -355,7 +359,8 @@ + (NSString *)currentViewTree return tree; } -+ (UIImage *)screenshot { ++ (UIImage *)screenshot +{ UIWindow *window = [UIApplication sharedApplication].delegate.window; UIGraphicsBeginImageContext(window.bounds.size); @@ -379,18 +384,19 @@ + (UIImage *)screenshot { CGRect frame = view.frame; CGPoint offset = CGPointZero; - if ([view isKindOfClass:[UIScrollView class]]) + if ([view isKindOfClass:[UIScrollView class]]) { offset = ((UIScrollView *)view).contentOffset; + } return @{ - CODELESS_VIEW_TREE_TOP_KEY: @((int)frame.origin.y), - CODELESS_VIEW_TREE_LEFT_KEY: @((int)frame.origin.x), - CODELESS_VIEW_TREE_WIDTH_KEY: @((int)frame.size.width), - CODELESS_VIEW_TREE_HEIGHT_KEY: @((int)frame.size.height), - CODELESS_VIEW_TREE_OFFSET_X_KEY: @((int)offset.x), - CODELESS_VIEW_TREE_OFFSET_Y_KEY: @((int)offset.y), - CODELESS_VIEW_TREE_VISIBILITY_KEY: view.isHidden ? @4 : @0 - }; + CODELESS_VIEW_TREE_TOP_KEY : @((int)frame.origin.y), + CODELESS_VIEW_TREE_LEFT_KEY : @((int)frame.origin.x), + CODELESS_VIEW_TREE_WIDTH_KEY : @((int)frame.size.width), + CODELESS_VIEW_TREE_HEIGHT_KEY : @((int)frame.size.height), + CODELESS_VIEW_TREE_OFFSET_X_KEY : @((int)offset.x), + CODELESS_VIEW_TREE_OFFSET_Y_KEY : @((int)offset.y), + CODELESS_VIEW_TREE_VISIBILITY_KEY : view.isHidden ? @4 : @0 + }; } @end diff --git a/FBSDKCoreKit/FBSDKCoreKit/AppEvents/Internal/Codeless/FBSDKCodelessParameterComponent.m b/FBSDKCoreKit/FBSDKCoreKit/AppEvents/Internal/Codeless/FBSDKCodelessParameterComponent.m index 63ecb0ba2a..ff2815bc63 100644 --- a/FBSDKCoreKit/FBSDKCoreKit/AppEvents/Internal/Codeless/FBSDKCodelessParameterComponent.m +++ b/FBSDKCoreKit/FBSDKCoreKit/AppEvents/Internal/Codeless/FBSDKCodelessParameterComponent.m @@ -20,15 +20,16 @@ #if !TARGET_OS_TV -#import "FBSDKCodelessParameterComponent.h" + #import "FBSDKCodelessParameterComponent.h" -#import "FBSDKCodelessPathComponent.h" -#import "FBSDKViewHierarchyMacros.h" -#import "FBSDKTypeUtility.h" + #import "FBSDKCodelessPathComponent.h" + #import "FBSDKInternalUtility.h" + #import "FBSDKViewHierarchyMacros.h" @implementation FBSDKCodelessParameterComponent -- (instancetype)initWithJSON:(NSDictionary *)dict { +- (instancetype)initWithJSON:(NSDictionary *)dict +{ if (self = [super init]) { _name = [dict[CODELESS_MAPPING_PARAMETER_NAME_KEY] copy]; _value = [dict[CODELESS_MAPPING_PARAMETER_VALUE_KEY] copy]; diff --git a/FBSDKCoreKit/FBSDKCoreKit/AppEvents/Internal/Codeless/FBSDKCodelessPathComponent.m b/FBSDKCoreKit/FBSDKCoreKit/AppEvents/Internal/Codeless/FBSDKCodelessPathComponent.m index e093b020e5..c38d13ba6d 100644 --- a/FBSDKCoreKit/FBSDKCoreKit/AppEvents/Internal/Codeless/FBSDKCodelessPathComponent.m +++ b/FBSDKCoreKit/FBSDKCoreKit/AppEvents/Internal/Codeless/FBSDKCodelessPathComponent.m @@ -20,20 +20,20 @@ #if !TARGET_OS_TV -#import "FBSDKCodelessPathComponent.h" + #import "FBSDKCodelessPathComponent.h" -#import "FBSDKViewHierarchyMacros.h" + #import "FBSDKViewHierarchyMacros.h" @implementation FBSDKCodelessPathComponent -- (instancetype)initWithJSON:(NSDictionary *)dict { +- (instancetype)initWithJSON:(NSDictionary *)dict +{ if (self = [super init]) { _className = [dict[CODELESS_MAPPING_CLASS_NAME_KEY] copy]; _text = [dict[CODELESS_MAPPING_TEXT_KEY] copy]; _hint = [dict[CODELESS_MAPPING_HINT_KEY] copy]; _desc = [dict[CODELESS_MAPPING_DESC_KEY] copy]; - if (dict[CODELESS_MAPPING_INDEX_KEY]) { _index = [dict[CODELESS_MAPPING_INDEX_KEY] intValue]; } else { diff --git a/FBSDKCoreKit/FBSDKCoreKit/AppEvents/Internal/Codeless/FBSDKEventBinding.m b/FBSDKCoreKit/FBSDKCoreKit/AppEvents/Internal/Codeless/FBSDKEventBinding.m index cdf45a2397..e25ca5957d 100644 --- a/FBSDKCoreKit/FBSDKCoreKit/AppEvents/Internal/Codeless/FBSDKEventBinding.m +++ b/FBSDKCoreKit/FBSDKCoreKit/AppEvents/Internal/Codeless/FBSDKEventBinding.m @@ -20,22 +20,22 @@ #if !TARGET_OS_TV -#import "FBSDKEventBinding.h" - -#import "FBSDKAppEvents.h" -#import "FBSDKAppEventsUtility.h" -#import "FBSDKCodelessParameterComponent.h" -#import "FBSDKCodelessPathComponent.h" -#import "FBSDKSwizzler.h" -#import "FBSDKUtility.h" -#import "FBSDKViewHierarchy.h" -#import "FBSDKViewHierarchyMacros.h" -#import "FBSDKTypeUtility.h" - -#define CODELESS_PATH_TYPE_ABSOLUTE @"absolute" -#define CODELESS_PATH_TYPE_RELATIVE @"relative" -#define CODELESS_CODELESS_EVENT_KEY @"_is_fb_codeless" -#define PARAMETER_NAME_PRICE @"_valueToSum" + #import "FBSDKEventBinding.h" + + #import "FBSDKAppEvents.h" + #import "FBSDKAppEventsUtility.h" + #import "FBSDKCodelessParameterComponent.h" + #import "FBSDKCodelessPathComponent.h" + #import "FBSDKInternalUtility.h" + #import "FBSDKSwizzler.h" + #import "FBSDKUtility.h" + #import "FBSDKViewHierarchy.h" + #import "FBSDKViewHierarchyMacros.h" + + #define CODELESS_PATH_TYPE_ABSOLUTE @"absolute" + #define CODELESS_PATH_TYPE_RELATIVE @"relative" + #define CODELESS_CODELESS_EVENT_KEY @"_is_fb_codeless" + #define PARAMETER_NAME_PRICE @"_valueToSum" @implementation FBSDKEventBinding @@ -102,8 +102,8 @@ + (BOOL)matchAnyView:(NSArray *)views return NO; } -+ (BOOL)match:(NSObject *)view -pathComponent:(FBSDKCodelessPathComponent *)component ++ (BOOL) match:(NSObject *)view + pathComponent:(FBSDKCodelessPathComponent *)component { NSString *className = NSStringFromClass([view class]); if (![className isEqualToString:component.className]) { @@ -128,7 +128,7 @@ + (BOOL)match:(NSObject *)view if ((component.matchBitmask & FBSDKCodelessMatchBitmaskFieldText) > 0) { NSString *text = [FBSDKViewHierarchy getText:view]; BOOL match = ((text.length == 0 && component.text.length == 0) - || [text isEqualToString:component.text]); + || [text isEqualToString:component.text]); if (!match) { return NO; } @@ -143,7 +143,7 @@ + (BOOL)match:(NSObject *)view if ((component.matchBitmask & FBSDKCodelessMatchBitmaskFieldHint) > 0) { NSString *hint = [FBSDKViewHierarchy getHint:view]; BOOL match = ((hint.length == 0 && component.hint.length == 0) - || [hint isEqualToString:component.hint]); + || [hint isEqualToString:component.hint]); if (!match) { return NO; } @@ -160,7 +160,8 @@ + (BOOL)isViewMatchPath:(UIView *)view path:(NSArray *)path return isMatch; } -+ (BOOL)isPath:(NSArray *)path matchViewPath:(NSArray *)viewPath { ++ (BOOL)isPath:(NSArray *)path matchViewPath:(NSArray *)viewPath +{ for (NSInteger i = 0; i < MIN(path.count, viewPath.count); i++) { NSInteger idxPath = path.count - i - 1; NSInteger idxViewPath = viewPath.count - i - 1; @@ -180,8 +181,8 @@ + (BOOL)isPath:(NSArray *)path matchViewPath:(NSArray *)viewPath { if ((pathComponent.matchBitmask & FBSDKCodelessMatchBitmaskFieldText) > 0) { NSString *text = viewPathComponent.text; BOOL match = ((text.length == 0 && pathComponent.text.length == 0) - || [text isEqualToString:pathComponent.text] - || [[FBSDKUtility SHA256Hash:text] isEqualToString:pathComponent.text]); + || [text isEqualToString:pathComponent.text] + || [[FBSDKUtility SHA256Hash:text] isEqualToString:pathComponent.text]); if (!match) { return NO; } @@ -195,8 +196,8 @@ + (BOOL)isPath:(NSArray *)path matchViewPath:(NSArray *)viewPath { if ((pathComponent.matchBitmask & FBSDKCodelessMatchBitmaskFieldHint) > 0) { NSString *hint = viewPathComponent.hint; BOOL match = ((hint.length == 0 && pathComponent.hint.length == 0) - || [hint isEqualToString:pathComponent.hint] - || [[FBSDKUtility SHA256Hash:hint] isEqualToString:pathComponent.hint]); + || [hint isEqualToString:pathComponent.hint] + || [[FBSDKUtility SHA256Hash:hint] isEqualToString:pathComponent.hint]); if (!match) { return NO; } @@ -206,14 +207,15 @@ + (BOOL)isPath:(NSArray *)path matchViewPath:(NSArray *)viewPath { return YES; } -+ (NSObject *)findViewByPath:(NSArray *)path parent:(NSObject *)parent level:(int)level { ++ (NSObject *)findViewByPath:(NSArray *)path parent:(NSObject *)parent level:(int)level +{ if (level >= path.count) { return nil; } FBSDKCodelessPathComponent *pathComponent = [FBSDKTypeUtility array:path objectAtIndex:level]; - // If found parent, skip to next level + // If found parent, skip to next level if ([pathComponent.className isEqualToString:CODELESS_MAPPING_PARENT_CLASS_NAME]) { NSObject *nextParent = [FBSDKViewHierarchy getParent:parent]; @@ -262,8 +264,8 @@ + (NSObject *)findViewByPath:(NSArray *)path parent:(NSObject *)parent level:(in - (BOOL)isEqualToBinding:(FBSDKEventBinding *)binding { - if (_path.count != binding.path.count || - _parameters.count != binding.parameters.count) { + if (_path.count != binding.path.count + || _parameters.count != binding.parameters.count) { return NO; } @@ -296,10 +298,11 @@ - (BOOL)isEqualToBinding:(FBSDKEventBinding *)binding return YES; } -// MARK: - find event parameters via relative path +// MARK: - find event parameters via relative path + (NSString *)findParameterOfPath:(NSArray *)path pathType:(NSString *)pathType - sourceView:(UIView *)sourceView { + sourceView:(UIView *)sourceView +{ if (0 == path.count) { return nil; } diff --git a/FBSDKCoreKit/FBSDKCoreKit/AppEvents/Internal/Codeless/FBSDKEventBindingManager.m b/FBSDKCoreKit/FBSDKCoreKit/AppEvents/Internal/Codeless/FBSDKEventBindingManager.m index d409f2ce60..3c96e33989 100644 --- a/FBSDKCoreKit/FBSDKCoreKit/AppEvents/Internal/Codeless/FBSDKEventBindingManager.m +++ b/FBSDKCoreKit/FBSDKCoreKit/AppEvents/Internal/Codeless/FBSDKEventBindingManager.m @@ -20,26 +20,26 @@ #if !TARGET_OS_TV -#import "FBSDKEventBindingManager.h" + #import "FBSDKEventBindingManager.h" -#import + #import -#import + #import -#import "FBSDKCodelessPathComponent.h" -#import "FBSDKEventBinding.h" -#import "FBSDKSwizzler.h" -#import "FBSDKTypeUtility.h" -#import "FBSDKViewHierarchy.h" -#import "FBSDKViewHierarchyMacros.h" + #import "FBSDKCodelessPathComponent.h" + #import "FBSDKEventBinding.h" + #import "FBSDKInternalUtility.h" + #import "FBSDKSwizzler.h" + #import "FBSDKViewHierarchy.h" + #import "FBSDKViewHierarchyMacros.h" -#define ReactNativeTargetKey @"target" -#define ReactNativeTouchEndEventName @"touchEnd" + #define ReactNativeTargetKey @"target" + #define ReactNativeTouchEndEventName @"touchEnd" -#define ReactNativeClassRCTTextView "RCTTextView" -#define ReactNativeClassRCTImageView "RCTImageVIew" -#define ReactNativeClassRCTTouchEvent "RCTTouchEvent" -#define ReactNativeClassRCTTouchHandler "RCTTouchHandler" + #define ReactNativeClassRCTTextView "RCTTextView" + #define ReactNativeClassRCTImageView "RCTImageVIew" + #define ReactNativeClassRCTTouchEvent "RCTTouchEvent" + #define ReactNativeClassRCTTouchHandler "RCTTouchHandler" @interface FBSDKEventBindingManager () { @@ -53,7 +53,8 @@ @interface FBSDKEventBindingManager () @implementation FBSDKEventBindingManager -- (id)init { +- (id)init +{ self = [super init]; if (self) { isStarted = NO; @@ -86,7 +87,8 @@ - (id)init { return self; } -+ (NSArray *)parseArray:(NSArray *)array { ++ (NSArray *)parseArray:(NSArray *)array +{ NSMutableArray *result = [NSMutableArray array]; for (NSDictionary *json in array) { @@ -97,7 +99,7 @@ + (NSArray *)parseArray:(NSArray *)array { return [result copy]; } -- (FBSDKEventBindingManager*)initWithJSON:(NSDictionary*)dict +- (FBSDKEventBindingManager *)initWithJSON:(NSDictionary *)dict { if ((self = [super init])) { NSArray *eventBindingsDict = [FBSDKTypeUtility arrayValue:dict[@"event_bindings"]]; @@ -111,8 +113,8 @@ - (FBSDKEventBindingManager*)initWithJSON:(NSDictionary*)dict return self; } -#pragma clang diagnostic push -#pragma clang diagnostic ignored "-Wundeclared-selector" + #pragma clang diagnostic push + #pragma clang diagnostic ignored "-Wundeclared-selector" - (void)start { if (isStarted) { @@ -140,7 +142,7 @@ - (void)start Class classRCTImageView = objc_lookUpClass(ReactNativeClassRCTImageView); Class classRCTTouchHandler = objc_lookUpClass(ReactNativeClassRCTTouchHandler); - // All react-native views would be added tp RCTRootView, so no need to check didMoveToWindow + // All react-native views would be added tp RCTRootView, so no need to check didMoveToWindow [FBSDKSwizzler swizzleSelector:@selector(didMoveToWindow) onClass:classRCTView withBlock:blockToWindow @@ -155,38 +157,37 @@ - (void)start named:@"match_react_native"]; // RCTTouchHandler handles with touch events, like touchEnd and uses RCTEventDispather to dispatch events, so we can check _updateAndDispatchTouches to fire events - [FBSDKSwizzler swizzleSelector:@selector(_updateAndDispatchTouches:eventName:) onClass:classRCTTouchHandler withBlock:^(id touchHandler, SEL command, id touches, id eventName){ - if ([touches isKindOfClass:[NSSet class]] && [eventName isKindOfClass:[NSString class]]) { - @try { - NSString *reactEventName = (NSString *)eventName; - NSSet *reactTouches = (NSSet *)touches; - if ([reactEventName isEqualToString:ReactNativeTouchEndEventName]) { - for (UITouch *touch in reactTouches) { - UIView *targetView = ((UITouch *)touch).view.superview; - NSNumber *reactTag = nil; - // Find the closest React-managed touchable view like RCTTouchHandler - while(targetView) { - reactTag = [FBSDKViewHierarchy getViewReactTag:targetView]; - if (reactTag != nil && targetView.userInteractionEnabled) { - break; - } - targetView = targetView.superview; - } - FBSDKEventBinding *eventBinding = self->reactBindings[reactTag]; - if (reactTag != nil && eventBinding != nil) { - [eventBinding trackEvent:nil]; - } - } - } - } - @catch(NSException *exception) { - // Catch exception here to prevent LytroKit from crashing app - } - } - } named:@"dispatch_rn_event"]; + [FBSDKSwizzler swizzleSelector:@selector(_updateAndDispatchTouches:eventName:) onClass:classRCTTouchHandler withBlock:^(id touchHandler, SEL command, id touches, id eventName) { + if ([touches isKindOfClass:[NSSet class]] && [eventName isKindOfClass:[NSString class]]) { + @try { + NSString *reactEventName = (NSString *)eventName; + NSSet *reactTouches = (NSSet *)touches; + if ([reactEventName isEqualToString:ReactNativeTouchEndEventName]) { + for (UITouch *touch in reactTouches) { + UIView *targetView = ((UITouch *)touch).view.superview; + NSNumber *reactTag = nil; + // Find the closest React-managed touchable view like RCTTouchHandler + while (targetView) { + reactTag = [FBSDKViewHierarchy getViewReactTag:targetView]; + if (reactTag != nil && targetView.userInteractionEnabled) { + break; + } + targetView = targetView.superview; + } + FBSDKEventBinding *eventBinding = self->reactBindings[reactTag]; + if (reactTag != nil && eventBinding != nil) { + [eventBinding trackEvent:nil]; + } + } + } + } @catch (NSException *exception) { + // Catch exception here to prevent LytroKit from crashing app + } + } + } named:@"dispatch_rn_event"]; } - // UITableView + // UITableView void (^tableViewBlock)(UITableView *tableView, SEL cmd, id delegate) = @@ -201,7 +202,7 @@ - (void)start onClass:[UITableView class] withBlock:tableViewBlock named:@"match_table_view"]; - // UICollectionView + // UICollectionView void (^collectionViewBlock)(UICollectionView *collectionView, SEL cmd, id delegate) = @@ -218,7 +219,8 @@ - (void)start named:@"handle_collection_view"]; } -- (void)rematchBindings { +- (void)rematchBindings +{ if (0 == eventBindings.count) { return; } @@ -229,7 +231,8 @@ - (void)rematchBindings { } } -- (void)matchSubviewsIn:(UIView *)view { +- (void)matchSubviewsIn:(UIView *)view +{ if (!view) { return; } @@ -266,7 +269,8 @@ - (void)matchSubviewsIn:(UIView *)view { } // check if the view is matched to any event -- (void)matchView:(UIView *)view delegate:(id)delegate { +- (void)matchView:(UIView *)view delegate:(id)delegate +{ if (0 == eventBindings.count) { return; } @@ -361,7 +365,7 @@ - (void)matchView:(UIView *)view delegate:(id)delegate { for (FBSDKEventBinding *binding in bindings) { FBSDKCodelessPathComponent *component = binding.path.lastObject; if ((component.section == -1 || component.section == indexPath.section) - && (component.row == -1 || component.row == indexPath.row)) { + && (component.row == -1 || component.row == indexPath.row)) { UICollectionViewCell *cell = [collectionView cellForItemAtIndexPath:indexPath]; [binding trackEvent:cell]; } @@ -379,8 +383,9 @@ - (void)matchView:(UIView *)view delegate:(id)delegate { }); } -#pragma clang diagnostic pop -- (void)updateBindings:(NSArray *)bindings { + #pragma clang diagnostic pop +- (void)updateBindings:(NSArray *)bindings +{ if (eventBindings.count > 0 && eventBindings.count == bindings.count) { // Check whether event bindings are the same BOOL isSame = YES; diff --git a/FBSDKCoreKit/FBSDKCoreKit/AppEvents/Internal/EventDeactivation/FBSDKEventDeactivationManager.h b/FBSDKCoreKit/FBSDKCoreKit/AppEvents/Internal/EventDeactivation/FBSDKEventDeactivationManager.h index 8c958fdd41..9c15759c7e 100644 --- a/FBSDKCoreKit/FBSDKCoreKit/AppEvents/Internal/EventDeactivation/FBSDKEventDeactivationManager.h +++ b/FBSDKCoreKit/FBSDKCoreKit/AppEvents/Internal/EventDeactivation/FBSDKEventDeactivationManager.h @@ -30,4 +30,3 @@ NS_ASSUME_NONNULL_BEGIN @end NS_ASSUME_NONNULL_END - diff --git a/FBSDKCoreKit/FBSDKCoreKit/AppEvents/Internal/EventDeactivation/FBSDKEventDeactivationManager.m b/FBSDKCoreKit/FBSDKCoreKit/AppEvents/Internal/EventDeactivation/FBSDKEventDeactivationManager.m index 936601a893..41a3d04aa8 100644 --- a/FBSDKCoreKit/FBSDKCoreKit/AppEvents/Internal/EventDeactivation/FBSDKEventDeactivationManager.m +++ b/FBSDKCoreKit/FBSDKCoreKit/AppEvents/Internal/EventDeactivation/FBSDKEventDeactivationManager.m @@ -17,7 +17,8 @@ // CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. #import "FBSDKEventDeactivationManager.h" -#import "FBSDKTypeUtility.h" + +#import "FBSDKInternalUtility.h" #import "FBSDKServerConfigurationManager.h" static NSString *const DEPRECATED_PARAM_KEY = @"deprecated_param"; @@ -26,17 +27,17 @@ @interface FBSDKDeactivatedEvent : NSObject @property (nonatomic, readonly, copy) NSString *eventName; -@property (nonatomic, readonly, copy, nullable) NSSet *deactivatedParams; +@property (nullable, nonatomic, readonly, copy) NSSet *deactivatedParams; --(instancetype)initWithEventName:(NSString *)eventName - deactivatedParams:(NSSet *)deactivatedParams; +- (instancetype)initWithEventName:(NSString *)eventName + deactivatedParams:(NSSet *)deactivatedParams; @end @implementation FBSDKDeactivatedEvent --(instancetype)initWithEventName:(NSString *)eventName - deactivatedParams:(NSSet *)deactivatedParams +- (instancetype)initWithEventName:(NSString *)eventName + deactivatedParams:(NSSet *)deactivatedParams { self = [super init]; if (self) { @@ -54,21 +55,61 @@ @implementation FBSDKEventDeactivationManager static BOOL isEventDeactivationEnabled = NO; static NSMutableSet *_deactivatedEvents; -static NSMutableArray *_eventsWithDeactivatedParams; +static NSMutableArray *_eventsWithDeactivatedParams; + (void)enable { - static dispatch_once_t onceToken; - dispatch_once(&onceToken, ^{ - NSDictionary *restrictiveParams = [FBSDKServerConfigurationManager cachedServerConfiguration].restrictiveParams; - if (restrictiveParams) { - [FBSDKEventDeactivationManager updateDeactivatedEvents:restrictiveParams]; - isEventDeactivationEnabled = YES; + @try { + static dispatch_once_t onceToken; + dispatch_once(&onceToken, ^{ + NSDictionary *restrictiveParams = [FBSDKServerConfigurationManager cachedServerConfiguration].restrictiveParams; + if (restrictiveParams) { + [FBSDKEventDeactivationManager _updateDeactivatedEvents:restrictiveParams]; + isEventDeactivationEnabled = YES; + } + }); + } @catch (NSException *exception) {} +} + ++ (void)processEvents:(NSMutableArray *> *)events +{ + @try { + if (!isEventDeactivationEnabled) { + return; + } + NSArray *> *eventArray = [events copy]; + for (NSDictionary *> *event in eventArray) { + if ([_deactivatedEvents containsObject:event[@"event"][@"_eventName"]]) { + [events removeObject:event]; + } } - }); + } @catch (NSException *exception) {} +} + ++ (nullable NSDictionary *)processParameters:(nullable NSDictionary *)parameters + eventName:(NSString *)eventName +{ + @try { + if (!isEventDeactivationEnabled || parameters.count == 0 || _eventsWithDeactivatedParams.count == 0) { + return parameters; + } + NSMutableDictionary *params = [NSMutableDictionary dictionaryWithDictionary:parameters]; + for (NSString *key in [parameters keyEnumerator]) { + for (FBSDKDeactivatedEvent *event in _eventsWithDeactivatedParams) { + if ([event.eventName isEqualToString:eventName] && [event.deactivatedParams containsObject:key]) { + [params removeObjectForKey:key]; + } + } + } + return [params copy]; + } @catch (NSException *exception) { + return parameters; + } } -+ (void)updateDeactivatedEvents:(nullable NSDictionary *)events +#pragma mark - Private Method + ++ (void)_updateDeactivatedEvents:(nullable NSDictionary *)events { events = [FBSDKTypeUtility dictionaryValue:events]; if (events.count == 0) { @@ -96,34 +137,4 @@ + (void)updateDeactivatedEvents:(nullable NSDictionary *)events _eventsWithDeactivatedParams = deactivatedParamsArray; } -+ (void)processEvents:(NSMutableArray *> *)events -{ - if (!isEventDeactivationEnabled) { - return; - } - NSArray *> *eventArray = [events copy]; - for (NSDictionary *> *event in eventArray) { - if ([_deactivatedEvents containsObject:event[@"event"][@"_eventName"]]) { - [events removeObject:event]; - } - } -} - -+ (nullable NSDictionary *)processParameters:(nullable NSDictionary *)parameters - eventName:(NSString *)eventName -{ - if (!isEventDeactivationEnabled || parameters.count == 0 || _eventsWithDeactivatedParams.count == 0) { - return parameters; - } - NSMutableDictionary *params = [NSMutableDictionary dictionaryWithDictionary:parameters]; - for (NSString *key in [parameters keyEnumerator]) { - for (FBSDKDeactivatedEvent *event in _eventsWithDeactivatedParams) { - if ([event.eventName isEqualToString:eventName] && [event.deactivatedParams containsObject:key]) { - [params removeObjectForKey:key]; - } - } - } - return [params copy]; -} - @end diff --git a/FBSDKCoreKit/FBSDKCoreKit/AppEvents/Internal/FBSDKAppEvents+Internal.h b/FBSDKCoreKit/FBSDKCoreKit/AppEvents/Internal/FBSDKAppEvents+Internal.h index 432207fcbb..8a5db6607e 100644 --- a/FBSDKCoreKit/FBSDKCoreKit/AppEvents/Internal/FBSDKAppEvents+Internal.h +++ b/FBSDKCoreKit/FBSDKCoreKit/AppEvents/Internal/FBSDKAppEvents+Internal.h @@ -17,9 +17,9 @@ // CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. #if SWIFT_PACKAGE -#import "FBSDKAppEvents.h" + #import "FBSDKAppEvents.h" #else -#import + #import #endif #import "FBSDKAppEventsUtility.h" @@ -79,6 +79,15 @@ FOUNDATION_EXPORT NSString *const FBSDKAppEventNameFBSessionAuthMethodStart; /** Use to log the end of the last tried auth method as part of an auth request */ FOUNDATION_EXPORT NSString *const FBSDKAppEventNameFBSessionAuthMethodEnd; +/** Use to log the post-login heartbeat event after the end of an auth request*/ +FOUNDATION_EXPORT NSString *const FBSDKAppEventNameFBSessionAuthHeartbeat; + +/** Use to log the start of a referral request */ +FOUNDATION_EXPORT NSString *const FBSDKAppEventNameFBReferralStart; + +/** Use to log the end of a referral request */ +FOUNDATION_EXPORT NSString *const FBSDKAppEventNameFBReferralEnd; + /** Use to log the timestamp for the transition to the Facebook native login dialog */ FOUNDATION_EXPORT NSString *const FBSDKAppEventNameFBDialogsNativeLoginDialogStart; @@ -140,7 +149,6 @@ FOUNDATION_EXPORT NSString *const FBSDKAppEventsDialogShareContentTypeVideo; FOUNDATION_EXPORT NSString *const FBSDKAppEventsDialogShareContentTypeCamera; FOUNDATION_EXPORT NSString *const FBSDKAppEventsDialogShareContentTypeUnknown; - FOUNDATION_EXPORT NSString *const FBSDKAppEventsDialogShareModeAutomatic; FOUNDATION_EXPORT NSString *const FBSDKAppEventsDialogShareModeBrowser; FOUNDATION_EXPORT NSString *const FBSDKAppEventsDialogShareModeNative; @@ -193,11 +201,7 @@ FOUNDATION_EXPORT NSString *const FBSDKAppEventsWKWebViewMessagesPixelIDKey; @interface FBSDKAppEvents (Internal) -@property (class, nonatomic, strong, readonly) FBSDKAppEvents *singleton; - -#ifdef DEBUG -+ (void)resetSingleton; -#endif +@property (class, nonatomic, readonly, strong) FBSDKAppEvents *singleton; + (void)logInternalEvent:(FBSDKAppEventName)eventName isImplicitlyLogged:(BOOL)isImplicitlyLogged; @@ -224,7 +228,7 @@ FOUNDATION_EXPORT NSString *const FBSDKAppEventsWKWebViewMessagesPixelIDKey; valueToSum:(NSNumber *)valueToSum parameters:(NSDictionary *)parameters isImplicitlyLogged:(BOOL)isImplicitlyLogged - accessToken:(FBSDKAccessToken *)accessToken; + accessToken:(FBSDKAccessToken *)accessToken; + (void)logImplicitEvent:(NSString *)eventName valueToSum:(NSNumber *)valueToSum diff --git a/FBSDKCoreKit/FBSDKCoreKit/AppEvents/Internal/FBSDKAppEventsConfiguration.h b/FBSDKCoreKit/FBSDKCoreKit/AppEvents/Internal/FBSDKAppEventsConfiguration.h new file mode 100644 index 0000000000..db42b8eba8 --- /dev/null +++ b/FBSDKCoreKit/FBSDKCoreKit/AppEvents/Internal/FBSDKAppEventsConfiguration.h @@ -0,0 +1,46 @@ +// 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 + +#if SWIFT_PACKAGE +#import "FBSDKCopying.h" +#else +#import +#endif + +#import "FBSDKAppEventsUtility.h" + +NS_ASSUME_NONNULL_BEGIN + +NS_SWIFT_NAME(AppEventsConfiguration) +@interface FBSDKAppEventsConfiguration : NSObject + +@property (nonatomic, readonly, assign) FBSDKAdvertisingTrackingStatus defaultATEStatus; + +@property (nonatomic, readonly, assign) BOOL advertiserIDCollectionEnabled; + +@property (nonatomic, readonly, assign) BOOL eventCollectionEnabled; + +- (instancetype)initWithJSON:(nullable NSDictionary *)dict; + ++ (instancetype)defaultConfiguration; + +@end + +NS_ASSUME_NONNULL_END diff --git a/FBSDKCoreKit/FBSDKCoreKit/AppEvents/Internal/FBSDKAppEventsConfiguration.m b/FBSDKCoreKit/FBSDKCoreKit/AppEvents/Internal/FBSDKAppEventsConfiguration.m new file mode 100644 index 0000000000..9a58434a62 --- /dev/null +++ b/FBSDKCoreKit/FBSDKCoreKit/AppEvents/Internal/FBSDKAppEventsConfiguration.m @@ -0,0 +1,116 @@ +// 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 "FBSDKAppEventsConfiguration.h" + +#import "FBSDKCoreKit+Internal.h" + +#define FBSDK_APP_EVENTS_CONFIGURATION_DEFAULT_ATE_STATUS_KEY @"default_ate_status" +#define FBSDK_APP_EVENTS_CONFIGURATION_ADVERTISER_ID_TRACKING_ENABLED_KEY @"advertiser_id_collection_enabled" +#define FBSDK_APP_EVENTS_CONFIGURATION_EVENT_COLLECTION_ENABLED_KEY @"event_collection_enabled" + +@implementation FBSDKAppEventsConfiguration + +- (instancetype)initWithJSON:(nullable NSDictionary *)dict +{ + if ((self = [super init])) { + @try { + dict = [FBSDKTypeUtility dictionaryValue:dict]; + if (!dict) { + return FBSDKAppEventsConfiguration.defaultConfiguration; + } + NSDictionary *configs = [FBSDKTypeUtility dictionary:dict objectForKey:@"app_events_config" ofType:NSDictionary.class]; + if (!configs) { + return FBSDKAppEventsConfiguration.defaultConfiguration; + } + NSNumber *defaultATEStatus = [FBSDKTypeUtility numberValue:configs[FBSDK_APP_EVENTS_CONFIGURATION_DEFAULT_ATE_STATUS_KEY]] ?: @(FBSDKAdvertisingTrackingUnspecified); + NSNumber *advertiserIDCollectionEnabled = [FBSDKTypeUtility numberValue:configs[FBSDK_APP_EVENTS_CONFIGURATION_ADVERTISER_ID_TRACKING_ENABLED_KEY]] ?: @(YES); + NSNumber *eventCollectionEnabled = [FBSDKTypeUtility numberValue:configs[FBSDK_APP_EVENTS_CONFIGURATION_EVENT_COLLECTION_ENABLED_KEY]] ?: @(NO); + _defaultATEStatus = [defaultATEStatus integerValue]; + _advertiserIDCollectionEnabled = [advertiserIDCollectionEnabled boolValue]; + _eventCollectionEnabled = [eventCollectionEnabled boolValue]; + } @catch (NSException *exception) { + return FBSDKAppEventsConfiguration.defaultConfiguration; + } + } + return self; +} + +- (instancetype)initWithDefaultATEStatus:(FBSDKAdvertisingTrackingStatus)defaultATEStatus + advertiserIDCollectionEnabled:(BOOL)advertiserIDCollectionEnabled + eventCollectionEnabled:(BOOL)eventCollectionEnabled +{ + if ((self = [super init])) { + _defaultATEStatus = defaultATEStatus; + _advertiserIDCollectionEnabled = advertiserIDCollectionEnabled; + _eventCollectionEnabled = eventCollectionEnabled; + } + return self; +} + ++ (instancetype)defaultConfiguration +{ + FBSDKAppEventsConfiguration *config = [[FBSDKAppEventsConfiguration alloc] initWithDefaultATEStatus:FBSDKAdvertisingTrackingUnspecified + advertiserIDCollectionEnabled:YES + eventCollectionEnabled:NO]; + return config; +} + +#pragma mark - NSCoding + ++ (BOOL)supportsSecureCoding +{ + return YES; +} + +- (instancetype)initWithCoder:(NSCoder *)decoder +{ + FBSDKAdvertisingTrackingStatus defaultATEStatus = [decoder decodeIntegerForKey:FBSDK_APP_EVENTS_CONFIGURATION_DEFAULT_ATE_STATUS_KEY]; + BOOL advertisingIDCollectionEnabled = [decoder decodeBoolForKey:FBSDK_APP_EVENTS_CONFIGURATION_ADVERTISER_ID_TRACKING_ENABLED_KEY]; + BOOL eventCollectionEnabled = [decoder decodeBoolForKey:FBSDK_APP_EVENTS_CONFIGURATION_EVENT_COLLECTION_ENABLED_KEY]; + return [[FBSDKAppEventsConfiguration alloc] initWithDefaultATEStatus:defaultATEStatus + advertiserIDCollectionEnabled:advertisingIDCollectionEnabled + eventCollectionEnabled:eventCollectionEnabled]; +} + +- (void)encodeWithCoder:(NSCoder *)encoder +{ + [encoder encodeInteger:_defaultATEStatus forKey:FBSDK_APP_EVENTS_CONFIGURATION_DEFAULT_ATE_STATUS_KEY]; + [encoder encodeBool:_advertiserIDCollectionEnabled forKey:FBSDK_APP_EVENTS_CONFIGURATION_ADVERTISER_ID_TRACKING_ENABLED_KEY]; + [encoder encodeBool:_eventCollectionEnabled forKey:FBSDK_APP_EVENTS_CONFIGURATION_EVENT_COLLECTION_ENABLED_KEY]; +} + +#pragma mark - NSCopying + +- (instancetype)copyWithZone:(NSZone *)zone +{ + return self; +} + +#pragma mark - Testability + +#if DEBUG + +- (void)setDefaultATEStatus:(FBSDKAdvertisingTrackingStatus)status +{ + _defaultATEStatus = status; +} + +#endif + +@end diff --git a/FBSDKCoreKit/FBSDKCoreKit/AppEvents/Internal/FBSDKAppEventsConfigurationManager.h b/FBSDKCoreKit/FBSDKCoreKit/AppEvents/Internal/FBSDKAppEventsConfigurationManager.h new file mode 100644 index 0000000000..4f85e255d8 --- /dev/null +++ b/FBSDKCoreKit/FBSDKCoreKit/AppEvents/Internal/FBSDKAppEventsConfigurationManager.h @@ -0,0 +1,36 @@ +// 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 "FBSDKAppEventsConfiguration.h" + +typedef void (^FBSDKAppEventsConfigurationManagerBlock)(void); + +NS_ASSUME_NONNULL_BEGIN + +NS_SWIFT_NAME(AppEventsConfigurationManager) +@interface FBSDKAppEventsConfigurationManager : NSObject + ++ (FBSDKAppEventsConfiguration *)cachedAppEventsConfiguration; + ++ (void)loadAppEventsConfigurationWithBlock:(FBSDKAppEventsConfigurationManagerBlock)block; + +@end + +NS_ASSUME_NONNULL_END diff --git a/FBSDKCoreKit/FBSDKCoreKit/AppEvents/Internal/FBSDKAppEventsConfigurationManager.m b/FBSDKCoreKit/FBSDKCoreKit/AppEvents/Internal/FBSDKAppEventsConfigurationManager.m new file mode 100644 index 0000000000..28cb18d046 --- /dev/null +++ b/FBSDKCoreKit/FBSDKCoreKit/AppEvents/Internal/FBSDKAppEventsConfigurationManager.m @@ -0,0 +1,120 @@ +// 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 "FBSDKAppEventsConfigurationManager.h" + +#import "FBSDKCoreKit+Internal.h" + +static NSString *const FBSDKAppEventsConfigurationKey = @"com.facebook.sdk:FBSDKAppEventsConfiguration"; +static NSString *const FBSDKAppEventsConfigurationTimestampKey = @"com.facebook.sdk:FBSDKAppEventsConfigurationTimestamp"; +static const NSTimeInterval kTimeout = 4.0; + +static FBSDKAppEventsConfiguration *g_configuration; +static NSMutableArray *g_completionBlocks; +static NSDate *g_timestamp; +static BOOL g_requeryFinishedForAppStart; +static BOOL g_loadingConfiguration; + +@implementation FBSDKAppEventsConfigurationManager + +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wdeprecated-declarations" ++ (void)initialize +{ + if (self == FBSDKAppEventsConfigurationManager.class) { + id data = [[NSUserDefaults standardUserDefaults] objectForKey:FBSDKAppEventsConfigurationKey]; + if ([data isKindOfClass:NSData.class]) { + g_configuration = [NSKeyedUnarchiver unarchiveObjectWithData:data]; + } + if (!g_configuration) { + g_configuration = [FBSDKAppEventsConfiguration defaultConfiguration]; + } + g_completionBlocks = [NSMutableArray new]; + g_timestamp = [[NSUserDefaults standardUserDefaults] objectForKey:FBSDKAppEventsConfigurationTimestampKey]; + } +} + +#pragma clang diagnostic pop + ++ (FBSDKAppEventsConfiguration *)cachedAppEventsConfiguration +{ + return g_configuration; +} + ++ (void)loadAppEventsConfigurationWithBlock:(FBSDKAppEventsConfigurationManagerBlock)block +{ + NSString *appID = [FBSDKSettings appID]; + @synchronized(self) { + [FBSDKTypeUtility array:g_completionBlocks addObject:block]; + if (!appID || (g_requeryFinishedForAppStart && [self _isTimestampValid])) { + for (FBSDKAppEventsConfigurationManagerBlock completionBlock in g_completionBlocks) { + completionBlock(); + } + [g_completionBlocks removeAllObjects]; + return; + } + if (g_loadingConfiguration) { + return; + } + g_loadingConfiguration = true; + FBSDKGraphRequest *request = [[FBSDKGraphRequest alloc] + initWithGraphPath:appID + parameters:@{ + @"fields" : [NSString stringWithFormat:@"app_events_config.os_version(%@)", [UIDevice currentDevice].systemVersion] + }]; + FBSDKGraphRequestConnection *requestConnection = [FBSDKGraphRequestConnection new]; + requestConnection.timeout = kTimeout; + [requestConnection addRequest:request completionHandler:^(FBSDKGraphRequestConnection *connection, id result, NSError *error) { + [self _processResponse:result error:error]; + }]; + [requestConnection start]; + } +} + +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wdeprecated-declarations" ++ (void)_processResponse:(id)response + error:(NSError *)error +{ + NSDate *date = [NSDate date]; + @synchronized(self) { + g_loadingConfiguration = NO; + g_requeryFinishedForAppStart = YES; + if (error) { + return; + } + g_configuration = [[FBSDKAppEventsConfiguration alloc] initWithJSON:response]; + g_timestamp = date; + for (FBSDKAppEventsConfigurationManagerBlock completionBlock in g_completionBlocks) { + completionBlock(); + } + [g_completionBlocks removeAllObjects]; + } + NSData *data = [NSKeyedArchiver archivedDataWithRootObject:g_configuration]; + [[NSUserDefaults standardUserDefaults] setObject:data forKey:FBSDKAppEventsConfigurationKey]; + [[NSUserDefaults standardUserDefaults] setObject:date forKey:FBSDKAppEventsConfigurationTimestampKey]; +} + +#pragma clang diagnostic pop + ++ (BOOL)_isTimestampValid +{ + return g_timestamp && [[NSDate date] timeIntervalSinceDate:g_timestamp] < 3600; +} + +@end diff --git a/FBSDKCoreKit/FBSDKCoreKit/AppEvents/Internal/FBSDKAppEventsDeviceInfo.m b/FBSDKCoreKit/FBSDKCoreKit/AppEvents/Internal/FBSDKAppEventsDeviceInfo.m index 1d56868c0e..49c5a7fffb 100644 --- a/FBSDKCoreKit/FBSDKCoreKit/AppEvents/Internal/FBSDKAppEventsDeviceInfo.m +++ b/FBSDKCoreKit/FBSDKCoreKit/AppEvents/Internal/FBSDKAppEventsDeviceInfo.m @@ -22,8 +22,8 @@ #import #if !TARGET_OS_TV -#import -#import + #import + #import #endif #import @@ -36,10 +36,10 @@ #define FB_ARRAY_COUNT(x) sizeof(x) / sizeof(x[0]) -static const u_int FB_GROUP1_RECHECK_DURATION = 30 * 60; // seconds +static const u_int FB_GROUP1_RECHECK_DURATION = 30 * 60; // seconds // Apple reports storage in binary gigabytes (1024^3) in their About menus, etc. -static const u_int FB_GIGABYTE = 1024 * 1024 * 1024; // bytes +static const u_int FB_GIGABYTE = 1024 * 1024 * 1024; // bytes @implementation FBSDKAppEventsDeviceInfo { @@ -106,10 +106,9 @@ - (instancetype)init - (NSString *)encodedDeviceInfo { - @synchronized (self) { - + @synchronized(self) { BOOL isGroup1Expired = [self _isGroup1Expired]; - BOOL isEncodingExpired = isGroup1Expired; // Can || other groups in if we add them + BOOL isEncodingExpired = isGroup1Expired; // Can || other groups in if we add them // As long as group1 hasn't expired, we can just return the last generated value if (_encodedDeviceInfo && !isEncodingExpired) { @@ -131,7 +130,7 @@ - (NSString *)encodedDeviceInfo - (void)setEncodedDeviceInfo:(NSString *)encodedDeviceInfo { - @synchronized (self) { + @synchronized(self) { if (![_encodedDeviceInfo isEqualToString:encodedDeviceInfo]) { _encodedDeviceInfo = [encodedDeviceInfo copy]; } @@ -211,23 +210,23 @@ - (NSString *)_generateEncoding NSString *densityString = _density ? [NSString stringWithFormat:@"%.02f", _density] : @""; NSArray *arr = @[ - @"i2", // version - starts with 'i' for iOS, we'll use 'a' for Android - _bundleIdentifier ?: @"", - _longVersion ?: @"", - _shortVersion ?: @"", - _sysVersion ?: @"", - _machine ?: @"", - _language ?: @"", - _timeZoneAbbrev ?: @"", - _carrierName ?: @"", - _width ? @((unsigned long)_width) : @"", - _height ? @((unsigned long)_height) : @"", - densityString, - @(_coreCount) ?: @"", - @(_totalDiskSpaceGB) ?: @"", - @(_remainingDiskSpaceGB) ?: @"", - _timeZoneName ?: @"" - ]; + @"i2", // version - starts with 'i' for iOS, we'll use 'a' for Android + _bundleIdentifier ?: @"", + _longVersion ?: @"", + _shortVersion ?: @"", + _sysVersion ?: @"", + _machine ?: @"", + _language ?: @"", + _timeZoneAbbrev ?: @"", + _carrierName ?: @"", + _width ? @((unsigned long)_width) : @"", + _height ? @((unsigned long)_height) : @"", + densityString, + @(_coreCount) ?: @"", + @(_totalDiskSpaceGB) ?: @"", + @(_remainingDiskSpaceGB) ?: @"", + _timeZoneName ?: @"" + ]; return [FBSDKBasicUtility JSONStringForObject:arr error:NULL invalidObjectHandler:NULL]; } @@ -277,6 +276,7 @@ + (NSString *)_getCarrier return carrier.carrierName ?: @"NoCarrier"; #endif } + #pragma clang diagnostic pop @end diff --git a/FBSDKCoreKit/FBSDKCoreKit/AppEvents/Internal/FBSDKAppEventsState.m b/FBSDKCoreKit/FBSDKCoreKit/AppEvents/Internal/FBSDKAppEventsState.m index c2c0949778..c13b42393d 100644 --- a/FBSDKCoreKit/FBSDKCoreKit/AppEvents/Internal/FBSDKAppEventsState.m +++ b/FBSDKCoreKit/FBSDKCoreKit/AppEvents/Internal/FBSDKAppEventsState.m @@ -18,10 +18,9 @@ #import "FBSDKAppEventsState.h" -#import "FBSDKBasicUtility.h" #import "FBSDKEventDeactivationManager.h" +#import "FBSDKInternalUtility.h" #import "FBSDKRestrictiveDataFilterManager.h" -#import "FBSDKTypeUtility.h" #define FBSDK_APPEVENTSTATE_ISIMPLICIT_KEY @"isImplicit" @@ -109,30 +108,32 @@ - (void)addEventsFromAppEventState:(FBSDKAppEventsState *)appEventsState } - (void)addEvent:(NSDictionary *)eventDictionary - isImplicit:(BOOL)isImplicit { + isImplicit:(BOOL)isImplicit +{ if (_mutableEvents.count >= FBSDK_APPEVENTSSTATE_MAX_EVENTS) { _numSkipped++; } else { [FBSDKTypeUtility array:_mutableEvents addObject:@{ - @"event" : [eventDictionary mutableCopy], - FBSDK_APPEVENTSTATE_ISIMPLICIT_KEY : @(isImplicit) - }]; + @"event" : [eventDictionary mutableCopy], + FBSDK_APPEVENTSTATE_ISIMPLICIT_KEY : @(isImplicit) + }]; } } -- (NSString *)extractReceiptData { +- (NSString *)extractReceiptData +{ NSMutableString *receipts_string = [NSMutableString string]; NSInteger transactionId = 1; - for (NSMutableDictionary* events in _mutableEvents) { + for (NSMutableDictionary *events in _mutableEvents) { NSMutableDictionary *event = events[@"event"]; - NSString* receipt = event[@"receipt_data"]; + NSString *receipt = event[@"receipt_data"]; // Add receipt id as the identifier for receipt data in event parameter. // Receipt data will be sent as post parameter rather than the event parameter if (receipt) { - NSString* idKey = [NSString stringWithFormat:@"receipt_%ld", (long)transactionId]; + NSString *idKey = [NSString stringWithFormat:@"receipt_%ld", (long)transactionId]; [FBSDKTypeUtility dictionary:event setObject:idKey forKey:FBSDK_APPEVENTSTATE_RECEIPTID_KEY]; - NSString* receiptWithId = [NSString stringWithFormat:@"%@::%@;;;", idKey, receipt]; + NSString *receiptWithId = [NSString stringWithFormat:@"%@::%@;;;", idKey, receipt]; [receipts_string appendString:receiptWithId]; transactionId++; } @@ -158,10 +159,10 @@ - (BOOL)isCompatibleWithAppEventsState:(FBSDKAppEventsState *)appEventsState - (BOOL)isCompatibleWithTokenString:(NSString *)tokenString appID:(NSString *)appID { // token strings can be nil (e.g., no user token) but appIDs should not. - BOOL tokenCompatible = ([self.tokenString isEqualToString:tokenString] || - (self.tokenString == nil && tokenString == nil)); - return (tokenCompatible && - [self.appID isEqualToString:appID]); + BOOL tokenCompatible = ([self.tokenString isEqualToString:tokenString] + || (self.tokenString == nil && tokenString == nil)); + return (tokenCompatible + && [self.appID isEqualToString:appID]); } - (NSString *)JSONStringForEvents:(BOOL)includeImplicitEvents diff --git a/FBSDKCoreKit/FBSDKCoreKit/AppEvents/Internal/FBSDKAppEventsStateManager.m b/FBSDKCoreKit/FBSDKCoreKit/AppEvents/Internal/FBSDKAppEventsStateManager.m index 95741839d5..1d8de63b1f 100644 --- a/FBSDKCoreKit/FBSDKCoreKit/AppEvents/Internal/FBSDKAppEventsStateManager.m +++ b/FBSDKCoreKit/FBSDKCoreKit/AppEvents/Internal/FBSDKAppEventsStateManager.m @@ -71,6 +71,7 @@ + (NSArray *)retrievePersistedAppEventsStates } return eventsStates; } + #pragma clang diagnostic pop #pragma mark - Private Helpers @@ -79,4 +80,5 @@ + (NSString *)filePath { return [FBSDKBasicUtility persistenceFilePath:@"com-facebook-sdk-AppEventsPersistedEvents.json"]; } + @end diff --git a/FBSDKCoreKit/FBSDKCoreKit/AppEvents/Internal/FBSDKAppEventsUtility.h b/FBSDKCoreKit/FBSDKCoreKit/AppEvents/Internal/FBSDKAppEventsUtility.h index 82ac6021d4..a52202ed13 100644 --- a/FBSDKCoreKit/FBSDKCoreKit/AppEvents/Internal/FBSDKAppEventsUtility.h +++ b/FBSDKCoreKit/FBSDKCoreKit/AppEvents/Internal/FBSDKAppEventsUtility.h @@ -44,7 +44,6 @@ NS_SWIFT_NAME(AppEventsUtility) + (instancetype)new NS_UNAVAILABLE; @property (class, nonatomic, copy, readonly) NSString *advertiserID; -@property (class, nonatomic, assign, readonly) FBSDKAdvertisingTrackingStatus advertisingTrackingStatus; @property (class, nonatomic, assign, readonly) long unixTimeNow; @property (class, nonatomic, assign, readonly) BOOL isDebugBuild; @@ -57,8 +56,10 @@ NS_SWIFT_NAME(AppEventsUtility) + (void)logAndNotify:(NSString *)msg; + (NSString *)tokenStringToUseFor:(FBSDKAccessToken *)token; + (BOOL)validateIdentifier:(NSString *)identifier; -+ (id)getVariable:(NSString *)variableName fromInstance:(NSObject *)instance; + (NSNumber *)getNumberValue:(NSString *)text; ++ (BOOL)shouldDropAppEvent; + (BOOL)isSensitiveUserData:(NSString *)text; ++ (BOOL)isStandardEvent:(NSString *)event; ++ (long)convertToUnixTime:(NSDate *)date; @end diff --git a/FBSDKCoreKit/FBSDKCoreKit/AppEvents/Internal/FBSDKAppEventsUtility.m b/FBSDKCoreKit/FBSDKCoreKit/AppEvents/Internal/FBSDKAppEventsUtility.m index 29664d6720..b0393d8b30 100644 --- a/FBSDKCoreKit/FBSDKCoreKit/AppEvents/Internal/FBSDKAppEventsUtility.m +++ b/FBSDKCoreKit/FBSDKCoreKit/AppEvents/Internal/FBSDKAppEventsUtility.m @@ -18,12 +18,14 @@ #import "FBSDKAppEventsUtility.h" -#import - #import +#import + #import "FBSDKAccessToken.h" #import "FBSDKAppEvents.h" +#import "FBSDKAppEventsConfiguration.h" +#import "FBSDKAppEventsConfigurationManager.h" #import "FBSDKAppEventsDeviceInfo.h" #import "FBSDKConstants.h" #import "FBSDKDynamicFrameworkLoader.h" @@ -38,10 +40,42 @@ #define FBSDK_APPEVENTSUTILITY_ANONYMOUSID_KEY @"anon_id" #define FBSDK_APPEVENTSUTILITY_MAX_IDENTIFIER_LENGTH 40 +static NSArray *standardEvents; + @implementation FBSDKAppEventsUtility ++ (void)initialize +{ + standardEvents = @[ + FBSDKAppEventNameCompletedRegistration, + FBSDKAppEventNameViewedContent, + FBSDKAppEventNameSearched, + FBSDKAppEventNameRated, + FBSDKAppEventNameCompletedTutorial, + FBSDKAppEventNameAddedToCart, + FBSDKAppEventNameAddedToWishlist, + FBSDKAppEventNameInitiatedCheckout, + FBSDKAppEventNameAddedPaymentInfo, + FBSDKAppEventNamePurchased, + FBSDKAppEventNameAchievedLevel, + FBSDKAppEventNameUnlockedAchievement, + FBSDKAppEventNameSpentCredits, + FBSDKAppEventNameContact, + FBSDKAppEventNameCustomizeProduct, + FBSDKAppEventNameDonate, + FBSDKAppEventNameFindLocation, + FBSDKAppEventNameSchedule, + FBSDKAppEventNameStartTrial, + FBSDKAppEventNameSubmitApplication, + FBSDKAppEventNameSubscribe, + FBSDKAppEventNameAdImpression, + FBSDKAppEventNameAdClick + ]; +} + + (NSMutableDictionary *)activityParametersDictionaryForEvent:(NSString *)eventCategory - shouldAccessAdvertisingID:(BOOL)shouldAccessAdvertisingID { + shouldAccessAdvertisingID:(BOOL)shouldAccessAdvertisingID +{ NSMutableDictionary *parameters = [NSMutableDictionary dictionary]; [FBSDKTypeUtility dictionary:parameters setObject:eventCategory forKey:@"event"]; @@ -52,16 +86,14 @@ + (NSMutableDictionary *)activityParametersDictionaryForEvent:(NSString *)eventC [FBSDKTypeUtility dictionary:parameters setObject:[FBSDKBasicUtility anonymousID] forKey:FBSDK_APPEVENTSUTILITY_ANONYMOUSID_KEY]; - FBSDKAdvertisingTrackingStatus advertisingTrackingStatus = [[self class] advertisingTrackingStatus]; + FBSDKAdvertisingTrackingStatus advertisingTrackingStatus = [FBSDKSettings getAdvertisingTrackingStatus]; if (advertisingTrackingStatus != FBSDKAdvertisingTrackingUnspecified) { - BOOL allowed = (advertisingTrackingStatus == FBSDKAdvertisingTrackingAllowed); - [FBSDKTypeUtility dictionary:parameters setObject:@(allowed).stringValue forKey:@"advertiser_tracking_enabled"]; + [FBSDKTypeUtility dictionary:parameters setObject:@([FBSDKSettings isAdvertiserTrackingEnabled]).stringValue forKey:@"advertiser_tracking_enabled"]; } - if (advertisingTrackingStatus == FBSDKAdvertisingTrackingAllowed) { - NSString *userData = [FBSDKAppEvents getUserData]; - if (userData){ - [FBSDKTypeUtility dictionary:parameters setObject:userData forKey:@"ud"]; - } + + NSString *userData = [FBSDKAppEvents getUserData]; + if (userData) { + [FBSDKTypeUtility dictionary:parameters setObject:userData forKey:@"ud"]; } [FBSDKTypeUtility dictionary:parameters setObject:@(!FBSDKSettings.limitEventAndDataUsage).stringValue forKey:@"application_tracking_enabled"]; @@ -118,6 +150,12 @@ + (NSString *)advertiserID return nil; } + if (@available(iOS 14.0, *)) { + if (![FBSDKAppEventsConfigurationManager cachedAppEventsConfiguration].advertiserIDCollectionEnabled) { + return nil; + } + } + NSString *result = nil; Class ASIdentifierManagerClass = fbsdkdfl_ASIdentifierManagerClass(); @@ -129,23 +167,12 @@ + (NSString *)advertiserID return result; } -+ (FBSDKAdvertisingTrackingStatus)advertisingTrackingStatus ++ (BOOL)isStandardEvent:(nullable NSString *)event { - static dispatch_once_t fetchAdvertisingTrackingStatusOnce; - static FBSDKAdvertisingTrackingStatus status; - - dispatch_once(&fetchAdvertisingTrackingStatusOnce, ^{ - status = FBSDKAdvertisingTrackingUnspecified; - Class ASIdentifierManagerClass = fbsdkdfl_ASIdentifierManagerClass(); - if ([ASIdentifierManagerClass class]) { - ASIdentifierManager *manager = [ASIdentifierManagerClass sharedManager]; - if (manager) { - status = manager.advertisingTrackingEnabled ? FBSDKAdvertisingTrackingAllowed : FBSDKAdvertisingTrackingDisallowed; - } - } - }); - - return status; + if (!event) { + return NO; + } + return [standardEvents containsObject:event]; } #pragma mark - Internal, for testing @@ -160,11 +187,13 @@ + (void)clearLibraryFiles + (void)ensureOnMainThread:(NSString *)methodName className:(NSString *)className { - FBSDKConditionalLog([NSThread isMainThread], - FBSDKLoggingBehaviorDeveloperErrors, - @"*** <%@, %@> is not called on the main thread. This can lead to errors.", - methodName, - className); + FBSDKConditionalLog( + [NSThread isMainThread], + FBSDKLoggingBehaviorDeveloperErrors, + @"*** <%@, %@> is not called on the main thread. This can lead to errors.", + methodName, + className + ); } + (NSString *)flushReasonToString:(FBSDKAppEventsFlushReason)flushReason @@ -213,9 +242,9 @@ + (void)logAndNotify:(NSString *)msg allowLogAsDeveloperError:(BOOL)allowLogAsDe [[NSNotificationCenter defaultCenter] postNotificationName:FBSDKAppEventsLoggingResultNotification object:error]; } -+ (BOOL)matchString:(NSString *)string - firstCharacterSet:(NSCharacterSet *)firstCharacterSet -restOfStringCharacterSet:(NSCharacterSet *)restOfStringCharacterSet ++ (BOOL) matchString:(NSString *)string + firstCharacterSet:(NSCharacterSet *)firstCharacterSet + restOfStringCharacterSet:(NSCharacterSet *)restOfStringCharacterSet { if (string.length == 0) { return NO; @@ -254,8 +283,8 @@ + (BOOL)regexValidateIdentifier:(NSString *)identifier @synchronized(self) { if (![cachedIdentifiers containsObject:identifier]) { if ([self matchString:identifier - firstCharacterSet:firstCharacterSet - restOfStringCharacterSet:restOfStringCharacterSet]) { + firstCharacterSet:firstCharacterSet + restOfStringCharacterSet:restOfStringCharacterSet]) { [cachedIdentifiers addObject:identifier]; } else { return NO; @@ -290,7 +319,7 @@ + (NSString *)tokenStringToUseFor:(FBSDKAccessToken *)token // If there's an logging override app id present, then we don't want to use the client token since the client token // is intended to match up with the primary app id (and AppEvents doesn't require a client token). NSString *clientTokenString = [FBSDKSettings clientToken]; - if (clientTokenString && appID && [appID isEqualToString:token.appID]){ + if (clientTokenString && appID && [appID isEqualToString:token.appID]) { tokenString = [NSString stringWithFormat:@"%@|%@", appID, clientTokenString]; } else if (appID) { tokenString = nil; @@ -304,19 +333,13 @@ + (long)unixTimeNow return (long)round([NSDate date].timeIntervalSince1970); } -+ (id)getVariable:(NSString *)variableName fromInstance:(NSObject *)instance { - Ivar ivar = class_getInstanceVariable([instance class], variableName.UTF8String); - if (ivar != NULL) { - const char *encoding = ivar_getTypeEncoding(ivar); - if (encoding != NULL && encoding[0] == '@') { - return object_getIvar(instance, ivar); - } - } - - return nil; ++ (time_t)convertToUnixTime:(NSDate *)date +{ + return (time_t)round([date timeIntervalSince1970]); } -+ (NSNumber *)getNumberValue:(NSString *)text { ++ (NSNumber *)getNumberValue:(NSString *)text +{ NSNumber *value = @0; NSLocale *locale = [NSLocale currentLocale]; @@ -347,15 +370,15 @@ + (NSNumber *)getNumberValue:(NSString *)text { return value; } -+ (BOOL)isDebugBuild { ++ (BOOL)isDebugBuild +{ #if TARGET_IPHONE_SIMULATOR return YES; #else BOOL isDevelopment = NO; // There is no provisioning profile in AppStore Apps. - @try - { + @try { NSData *data = [NSData dataWithContentsOfFile:[NSBundle.mainBundle pathForResource:@"embedded" ofType:@"mobileprovision"]]; if (data) { const char *bytes = [data bytes]; @@ -369,16 +392,22 @@ + (BOOL)isDebugBuild { } return isDevelopment; - } - @catch(NSException *exception) - { - - } + } @catch (NSException *exception) {} return NO; #endif } ++ (BOOL)shouldDropAppEvent +{ + if (@available(iOS 14.0, *)) { + if ([FBSDKSettings getAdvertisingTrackingStatus] == FBSDKAdvertisingTrackingDisallowed && ![FBSDKAppEventsConfigurationManager cachedAppEventsConfiguration].eventCollectionEnabled) { + return YES; + } + } + return NO; +} + + (BOOL)isSensitiveUserData:(NSString *)text { if (0 == text.length) { @@ -412,10 +441,11 @@ + (BOOL)isCreditCardNumber:(NSString *)text for (int i = (int)text.length - 1; i >= 0; i--) { int digit = chars[i] - '0'; - if (isOdd) + if (isOdd) { oddSum += digit; - else + } else { evenSum += digit / 5 + (2 * digit) % 10; + } isOdd = !isOdd; } diff --git a/FBSDKCoreKit/FBSDKCoreKit/AppEvents/Internal/FBSDKHybridAppEventsScriptMessageHandler.m b/FBSDKCoreKit/FBSDKCoreKit/AppEvents/Internal/FBSDKHybridAppEventsScriptMessageHandler.m index a42268fc25..b4c594b51e 100644 --- a/FBSDKCoreKit/FBSDKCoreKit/AppEvents/Internal/FBSDKHybridAppEventsScriptMessageHandler.m +++ b/FBSDKCoreKit/FBSDKCoreKit/AppEvents/Internal/FBSDKHybridAppEventsScriptMessageHandler.m @@ -20,16 +20,16 @@ #if !TARGET_OS_TV -#import "FBSDKHybridAppEventsScriptMessageHandler.h" + #import "FBSDKHybridAppEventsScriptMessageHandler.h" -#if SWIFT_PACKAGE -#import "FBSDKAppEvents.h" -#else -#import -#endif + #if SWIFT_PACKAGE + #import "FBSDKAppEvents.h" + #else + #import + #endif -#import "FBSDKAppEvents+Internal.h" -#import "FBSDKTypeUtility.h" + #import "FBSDKAppEvents+Internal.h" + #import "FBSDKInternalUtility.h" NSString *const FBSDKAppEventsWKWebViewMessagesPixelReferralParamKey = @"_fb_pixel_referral_id"; @@ -37,19 +37,19 @@ @implementation FBSDKHybridAppEventsScriptMessageHandler -- (void)userContentController:(WKUserContentController *)userContentController didReceiveScriptMessage:(WKScriptMessage *)message { - +- (void)userContentController:(WKUserContentController *)userContentController didReceiveScriptMessage:(WKScriptMessage *)message +{ if ([message.name isEqualToString:FBSDKAppEventsWKWebViewMessagesHandlerKey]) { NSString *event = message.body[FBSDKAppEventsWKWebViewMessagesEventKey]; if (event.length > 0) { NSString *stringedParams = message.body[FBSDKAppEventsWKWebViewMessagesParamsKey]; - NSMutableDictionary *params = nil; + NSMutableDictionary *params = nil; NSError *jsonParseError = nil; if ([stringedParams isKindOfClass:[NSString class]]) { params = [FBSDKTypeUtility JSONObjectWithData:[stringedParams dataUsingEncoding:NSUTF8StringEncoding] - options:NSJSONReadingMutableContainers - error:&jsonParseError - ]; + options:NSJSONReadingMutableContainers + error:&jsonParseError + ]; } NSString *pixelID = message.body[FBSDKAppEventsWKWebViewMessagesPixelIDKey]; if (pixelID == nil) { @@ -59,8 +59,7 @@ - (void)userContentController:(WKUserContentController *)userContentController d if (jsonParseError != nil || ![params isKindOfClass:[NSDictionary class]] || params == nil) { [FBSDKAppEventsUtility logAndNotify:@"Could not find parameters for your Pixel request. Check your webview Pixel configuration."]; params = [[NSMutableDictionary alloc] initWithObjectsAndKeys:pixelID, FBSDKAppEventsWKWebViewMessagesPixelReferralParamKey, nil]; - } - else { + } else { [FBSDKTypeUtility dictionary:params setObject:pixelID forKey:FBSDKAppEventsWKWebViewMessagesPixelReferralParamKey]; } [FBSDKAppEvents logInternalEvent:event diff --git a/FBSDKCoreKit/FBSDKCoreKit/AppEvents/Internal/FBSDKPaymentObserver.m b/FBSDKCoreKit/FBSDKCoreKit/AppEvents/Internal/FBSDKPaymentObserver.m index f7b35462d5..ceb6bc294c 100644 --- a/FBSDKCoreKit/FBSDKCoreKit/AppEvents/Internal/FBSDKPaymentObserver.m +++ b/FBSDKCoreKit/FBSDKCoreKit/AppEvents/Internal/FBSDKPaymentObserver.m @@ -20,9 +20,8 @@ #import -#import "FBSDKCoreKit+Internal.h" - #import "FBSDKAppEvents+Internal.h" +#import "FBSDKCoreKit+Internal.h" #import "FBSDKDynamicFrameworkLoader.h" #import "FBSDKLogger.h" #import "FBSDKSettings.h" @@ -48,16 +47,16 @@ static NSString *const FBSDKGateKeeperAppEventsIfAutoLogSubs = @"app_events_if_auto_log_subs"; -@interface FBSDKPaymentProductRequestor : NSObject +@interface FBSDKPaymentProductRequestor : NSObject @property (nonatomic, retain) SKPaymentTransaction *transaction; -- (instancetype)initWithTransaction:(SKPaymentTransaction*)transaction; +- (instancetype)initWithTransaction:(SKPaymentTransaction *)transaction; - (void)resolveProducts; @end -@interface FBSDKPaymentObserver() +@interface FBSDKPaymentObserver () @end @implementation FBSDKPaymentObserver @@ -88,7 +87,7 @@ + (FBSDKPaymentObserver *)singleton return shared; } -- (instancetype) init +- (instancetype)init { self = [super init]; if (self) { @@ -99,7 +98,7 @@ - (instancetype) init - (void)startObservingTransactions { - @synchronized (self) { + @synchronized(self) { if (!_observingTransactions) { [(SKPaymentQueue *)[fbsdkdfl_SKPaymentQueueClass() defaultQueue] addTransactionObserver:self]; _observingTransactions = YES; @@ -109,7 +108,7 @@ - (void)startObservingTransactions - (void)stopObservingTransactions { - @synchronized (self) { + @synchronized(self) { if (_observingTransactions) { [(SKPaymentQueue *)[fbsdkdfl_SKPaymentQueueClass() defaultQueue] removeTransactionObserver:self]; _observingTransactions = NO; @@ -141,7 +140,7 @@ - (void)handleTransaction:(SKPaymentTransaction *)transaction @end -@interface FBSDKPaymentProductRequestor() +@interface FBSDKPaymentProductRequestor () @property (nonatomic, retain) SKProductsRequest *productRequest; @end @@ -159,7 +158,7 @@ + (void)initialize } } -- (instancetype)initWithTransaction:(SKPaymentTransaction*)transaction +- (instancetype)initWithTransaction:(SKPaymentTransaction *)transaction { self = [super init]; if (self) { @@ -211,9 +210,9 @@ - (NSString *)getTruncatedString:(NSString *)inputString - (void)logTransactionEvent:(SKProduct *)product { - if ([self isSubscription:product] && - [FBSDKGateKeeperManager boolForKey:FBSDKGateKeeperAppEventsIfAutoLogSubs - defaultValue:NO]) { + if ([self isSubscription:product] + && [FBSDKGateKeeperManager boolForKey:FBSDKGateKeeperAppEventsIfAutoLogSubs + defaultValue:NO]) { [self logImplicitSubscribeTransaction:self.transaction ofProduct:product]; } else { [self logImplicitPurchaseTransaction:self.transaction ofProduct:product]; @@ -252,18 +251,18 @@ - (BOOL)isSubscription:(SKProduct *)product default: break; } SKPayment *payment = transaction.payment; - NSMutableDictionary *eventParameters = [NSMutableDictionary dictionaryWithDictionary: @{ - FBSDKAppEventParameterNameContentID: payment.productIdentifier ?: @"", - FBSDKAppEventParameterNameNumItems: @(payment.quantity), - FBSDKAppEventParameterNameTransactionDate: transactionDate ?: @"", - }]; + NSMutableDictionary *eventParameters = [NSMutableDictionary dictionaryWithDictionary:@{ + FBSDKAppEventParameterNameContentID : payment.productIdentifier ?: @"", + FBSDKAppEventParameterNameNumItems : @(payment.quantity), + FBSDKAppEventParameterNameTransactionDate : transactionDate ?: @"", + }]; if (product) { - [eventParameters addEntriesFromDictionary: @{ - FBSDKAppEventParameterNameCurrency: [product.priceLocale objectForKey:NSLocaleCurrencyCode], - FBSDKAppEventParameterNameNumItems: @(payment.quantity), - FBSDKAppEventParameterNameProductTitle: [self getTruncatedString:product.localizedTitle], - FBSDKAppEventParameterNameDescription: [self getTruncatedString:product.localizedDescription], - }]; + [eventParameters addEntriesFromDictionary:@{ + FBSDKAppEventParameterNameCurrency : [product.priceLocale objectForKey:NSLocaleCurrencyCode], + FBSDKAppEventParameterNameNumItems : @(payment.quantity), + FBSDKAppEventParameterNameProductTitle : [self getTruncatedString:product.localizedTitle], + FBSDKAppEventParameterNameDescription : [self getTruncatedString:product.localizedDescription], + }]; if (transactionID) { [FBSDKTypeUtility dictionary:eventParameters setObject:transactionID forKey:FBSDKAppEventParameterNameTransactionID]; } @@ -330,8 +329,8 @@ - (BOOL)isStartTrial:(SKPaymentTransaction *)transaction if (paymentDiscount) { NSArray *discounts = product.discounts; for (SKProductDiscount *discount in discounts) { - if (discount.paymentMode == SKProductDiscountPaymentModeFreeTrial && - [paymentDiscount.identifier isEqualToString:discount.identifier]) { + if (discount.paymentMode == SKProductDiscountPaymentModeFreeTrial + && [paymentDiscount.identifier isEqualToString:discount.identifier]) { return YES; } } @@ -341,8 +340,8 @@ - (BOOL)isStartTrial:(SKPaymentTransaction *)transaction #endif // introductory offer starting from iOS 11.2 if (@available(iOS 11.2, *)) { - if (product.introductoryPrice && - product.introductoryPrice.paymentMode == SKProductDiscountPaymentModeFreeTrial) { + if (product.introductoryPrice + && product.introductoryPrice.paymentMode == SKProductDiscountPaymentModeFreeTrial) { NSString *originalTransactionID = transaction.originalTransaction.transactionIdentifier; // only consider the very first trial transaction as start trial if (!originalTransactionID) { @@ -407,8 +406,8 @@ - (NSString *)durationOfSubscriptionPeriod:(id)subcriptionPeriod - (void)productsRequest:(SKProductsRequest *)request didReceiveResponse:(SKProductsResponse *)response { - NSArray* products = response.products; - NSArray* invalidProductIdentifiers = response.invalidProductIdentifiers; + NSArray *products = response.products; + NSArray *invalidProductIdentifiers = response.invalidProductIdentifiers; if (products.count + invalidProductIdentifiers.count != 1) { [FBSDKLogger singleShotLogEntry:FBSDKLoggingBehaviorAppEvents formatString:@"FBSDKPaymentObserver: Expect to resolve one product per request"]; @@ -517,7 +516,7 @@ - (void)logImplicitTransactionEvent:(NSString *)eventName NSMutableDictionary *eventParameters = [NSMutableDictionary dictionaryWithDictionary:parameters]; if ([_eventsWithReceipt containsObject:eventName]) { - NSData* receipt = [self fetchDeviceReceipt]; + NSData *receipt = [self fetchDeviceReceipt]; if (receipt) { NSString *base64encodedReceipt = [receipt base64EncodedStringWithOptions:0]; [FBSDKTypeUtility dictionary:eventParameters setObject:base64encodedReceipt forKey:@"receipt_data"]; @@ -537,7 +536,7 @@ - (void)logImplicitTransactionEvent:(NSString *)eventName } // Fetch the current receipt for this application. -- (NSData*)fetchDeviceReceipt +- (NSData *)fetchDeviceReceipt { NSURL *receiptURL = [NSBundle bundleForClass:[self class]].appStoreReceiptURL; NSData *receipt = [NSData dataWithContentsOfURL:receiptURL]; diff --git a/FBSDKCoreKit/FBSDKCoreKit/AppEvents/Internal/FBSDKTimeSpentData.m b/FBSDKCoreKit/FBSDKCoreKit/AppEvents/Internal/FBSDKTimeSpentData.m index 7188789d20..3a461c157f 100644 --- a/FBSDKCoreKit/FBSDKCoreKit/AppEvents/Internal/FBSDKTimeSpentData.m +++ b/FBSDKCoreKit/FBSDKCoreKit/AppEvents/Internal/FBSDKTimeSpentData.m @@ -26,24 +26,22 @@ #import "FBSDKServerConfigurationManager.h" #import "FBSDKSettings.h" - // Filename and keys for session length -NSString *const FBSDKTimeSpentFilename = @"com-facebook-sdk-AppEventsTimeSpent.json"; -static NSString *const FBSDKTimeSpentPersistKeySessionSecondsSpent = @"secondsSpentInCurrentSession"; -static NSString *const FBSDKTimeSpentPersistKeySessionNumInterruptions = @"numInterruptions"; -static NSString *const FBSDKTimeSpentPersistKeyLastSuspendTime = @"lastSuspendTime"; -static NSString *const FBSDKTimeSpentPersistKeySessionID = @"sessionID"; - -static NSString *const FBSDKAppEventNameActivatedApp = @"fb_mobile_activate_app"; -static NSString *const FBSDKAppEventNameDeactivatedApp = @"fb_mobile_deactivate_app"; -static NSString *const FBSDKAppEventParameterNameSessionInterruptions = @"fb_mobile_app_interruptions"; -static NSString *const FBSDKAppEventParameterNameTimeBetweenSessions = @"fb_mobile_time_between_sessions"; -static NSString *const FBSDKAppEventParameterNameSessionID = @"_session_id"; - - -static const int SECS_PER_MIN = 60; -static const int SECS_PER_HOUR = 60 * SECS_PER_MIN; -static const int SECS_PER_DAY = 24 * SECS_PER_HOUR; +NSString *const FBSDKTimeSpentFilename = @"com-facebook-sdk-AppEventsTimeSpent.json"; +static NSString *const FBSDKTimeSpentPersistKeySessionSecondsSpent = @"secondsSpentInCurrentSession"; +static NSString *const FBSDKTimeSpentPersistKeySessionNumInterruptions = @"numInterruptions"; +static NSString *const FBSDKTimeSpentPersistKeyLastSuspendTime = @"lastSuspendTime"; +static NSString *const FBSDKTimeSpentPersistKeySessionID = @"sessionID"; + +static NSString *const FBSDKAppEventNameActivatedApp = @"fb_mobile_activate_app"; +static NSString *const FBSDKAppEventNameDeactivatedApp = @"fb_mobile_deactivate_app"; +static NSString *const FBSDKAppEventParameterNameSessionInterruptions = @"fb_mobile_app_interruptions"; +static NSString *const FBSDKAppEventParameterNameTimeBetweenSessions = @"fb_mobile_time_between_sessions"; +static NSString *const FBSDKAppEventParameterNameSessionID = @"_session_id"; + +static const int SECS_PER_MIN = 60; +static const int SECS_PER_HOUR = 60 * SECS_PER_MIN; +static const int SECS_PER_DAY = 24 * SECS_PER_HOUR; static NSString *g_sourceApplication; static BOOL g_isOpenedFromAppLink; @@ -70,7 +68,7 @@ 150 * SECS_PER_DAY, 180 * SECS_PER_DAY, 365 * SECS_PER_DAY, - LONG_MAX, // keep as LONG_MAX to guarantee loop will terminate + LONG_MAX, // keep as LONG_MAX to guarantee loop will terminate }; /** @@ -88,9 +86,9 @@ @implementation FBSDKTimeSpentData BOOL _isCurrentlyLoaded; BOOL _shouldLogActivateEvent; BOOL _shouldLogDeactivateEvent; - long _secondsSpentInCurrentSession; - long _timeSinceLastSuspend; - int _numInterruptionsInCurrentSession; + long _secondsSpentInCurrentSession; + long _timeSinceLastSuspend; + int _numInterruptionsInCurrentSession; long _lastRestoreTime; long _lastSuspendTime; NSString *_sessionID; @@ -127,7 +125,6 @@ + (FBSDKTimeSpentData *)singleton // Calculate and persist time spent data for this instance of the app activation. - (void)instanceSuspend { - [FBSDKAppEventsUtility ensureOnMainThread:NSStringFromSelector(_cmd) className:NSStringFromClass([self class])]; if (!_isCurrentlyLoaded) { FBSDKConditionalLog(YES, FBSDKLoggingBehaviorInformational, @"[FBSDKTimeSpentData suspend] invoked without corresponding restore"); @@ -152,7 +149,7 @@ - (void)instanceSuspend FBSDKTimeSpentPersistKeySessionNumInterruptions : @(_numInterruptionsInCurrentSession), FBSDKTimeSpentPersistKeyLastSuspendTime : @(now), FBSDKTimeSpentPersistKeySessionID : _sessionID, - }; + }; NSString *content = [FBSDKBasicUtility JSONStringForObject:timeSpentData error:NULL invalidObjectHandler:NULL]; @@ -167,19 +164,16 @@ - (void)instanceSuspend _isCurrentlyLoaded = NO; } - // Called during activation - either through an explicit 'activateApp' call or implicitly when the app is foregrounded. // In both cases, we restore the persisted event data. In the case of the activateApp, we log an 'app activated' // event if there's been enough time between the last deactivation and now. - (void)instanceRestore:(BOOL)calledFromActivateApp { - [FBSDKAppEventsUtility ensureOnMainThread:NSStringFromSelector(_cmd) className:NSStringFromClass([self class])]; // It's possible to call this multiple times during the time the app is in the foreground. If this is the case, // just restore persisted data the first time. if (!_isCurrentlyLoaded) { - NSString *content = [[NSString alloc] initWithContentsOfFile:[FBSDKBasicUtility persistenceFilePath:FBSDKTimeSpentFilename] usedEncoding:nil @@ -190,7 +184,6 @@ - (void)instanceRestore:(BOOL)calledFromActivateApp long now = [FBSDKAppEventsUtility unixTimeNow]; if (!content) { - // Nothing persisted, so this is the first launch. _sessionID = [NSUUID UUID].UUIDString; _secondsSpentInCurrentSession = 0; @@ -200,16 +193,14 @@ - (void)instanceRestore:(BOOL)calledFromActivateApp // We want to log the app activation event on the first launch, but not the deactivate event _shouldLogActivateEvent = YES; _shouldLogDeactivateEvent = NO; - } else { - NSDictionary *results = [FBSDKBasicUtility objectForJSONString:content error:NULL]; _lastSuspendTime = [results[FBSDKTimeSpentPersistKeyLastSuspendTime] longValue]; _timeSinceLastSuspend = now - _lastSuspendTime; _secondsSpentInCurrentSession = [results[FBSDKTimeSpentPersistKeySessionSecondsSpent] intValue]; - _sessionID = results[FBSDKTimeSpentPersistKeySessionID] ? : [NSUUID UUID].UUIDString; + _sessionID = results[FBSDKTimeSpentPersistKeySessionID] ?: [NSUUID UUID].UUIDString; _numInterruptionsInCurrentSession = [results[FBSDKTimeSpentPersistKeySessionNumInterruptions] intValue]; _shouldLogActivateEvent = (_timeSinceLastSuspend > [FBSDKServerConfigurationManager cachedServerConfiguration].sessionTimoutInterval); @@ -221,7 +212,6 @@ - (void)instanceRestore:(BOOL)calledFromActivateApp // so errant or test uses doesn't blow out the cardinality on the backend processing _numInterruptionsInCurrentSession = MIN(_numInterruptionsInCurrentSession + 1, 200); } - } _lastRestoreTime = now; @@ -256,9 +246,9 @@ - (void)instanceRestore:(BOOL)calledFromActivateApp - (NSDictionary *)appEventsParametersForActivate { return @{ - FBSDKAppEventParameterLaunchSource: [[self class] getSourceApplication], - FBSDKAppEventParameterNameSessionID: _sessionID, - }; + FBSDKAppEventParameterLaunchSource : [[self class] getSourceApplication], + FBSDKAppEventParameterNameSessionID : _sessionID, + }; } - (NSDictionary *)appEventsParametersForDeactivate @@ -270,9 +260,8 @@ - (NSDictionary *)appEventsParametersForDeactivate NSMutableDictionary *params = [@{ FBSDKAppEventParameterNameSessionInterruptions : @(_numInterruptionsInCurrentSession), FBSDKAppEventParameterNameTimeBetweenSessions : [NSString stringWithFormat:@"session_quanta_%d", quantaIndex], - FBSDKAppEventParameterLaunchSource: [[self class] getSourceApplication], - FBSDKAppEventParameterNameSessionID : _sessionID ?: @"", - } mutableCopy]; + FBSDKAppEventParameterLaunchSource : [[self class] getSourceApplication], + FBSDKAppEventParameterNameSessionID : _sessionID ?: @"", } mutableCopy]; if (_lastSuspendTime) { [FBSDKTypeUtility dictionary:params setObject:@(_lastSuspendTime) forKey:FBSDKAppEventParameterLogTime]; } @@ -282,7 +271,7 @@ - (NSDictionary *)appEventsParametersForDeactivate + (void)setSourceApplication:(NSString *)sourceApplication openURL:(NSURL *)url { [self setSourceApplication:sourceApplication - isFromAppLink:[FBSDKInternalUtility dictionaryFromFBURL:url][@"al_applink_data"] != nil]; + isFromAppLink:[FBSDKInternalUtility parametersFromFBURL:url][@"al_applink_data"] != nil]; } + (void)setSourceApplication:(NSString *)sourceApplication isFromAppLink:(BOOL)isFromAppLink @@ -297,9 +286,9 @@ + (NSString *)getSourceApplication if (g_isOpenedFromAppLink) { openType = @"AppLink"; } - return (g_sourceApplication ? - [NSString stringWithFormat:@"%@(%@)", openType, g_sourceApplication] - : openType); + return (g_sourceApplication + ? [NSString stringWithFormat:@"%@(%@)", openType, g_sourceApplication] + : openType); } + (void)resetSourceApplication diff --git a/FBSDKCoreKit/FBSDKCoreKit/AppEvents/Internal/Integrity/FBSDKIntegrityManager.h b/FBSDKCoreKit/FBSDKCoreKit/AppEvents/Internal/Integrity/FBSDKIntegrityManager.h index 8993e43781..b976866078 100644 --- a/FBSDKCoreKit/FBSDKCoreKit/AppEvents/Internal/Integrity/FBSDKIntegrityManager.h +++ b/FBSDKCoreKit/FBSDKCoreKit/AppEvents/Internal/Integrity/FBSDKIntegrityManager.h @@ -20,7 +20,7 @@ #if !TARGET_OS_TV -#import + #import @interface FBSDKIntegrityManager : NSObject diff --git a/FBSDKCoreKit/FBSDKCoreKit/AppEvents/Internal/Integrity/FBSDKIntegrityManager.m b/FBSDKCoreKit/FBSDKCoreKit/AppEvents/Internal/Integrity/FBSDKIntegrityManager.m index 1339a0a48b..3b7e749555 100644 --- a/FBSDKCoreKit/FBSDKCoreKit/AppEvents/Internal/Integrity/FBSDKIntegrityManager.m +++ b/FBSDKCoreKit/FBSDKCoreKit/AppEvents/Internal/Integrity/FBSDKIntegrityManager.m @@ -20,13 +20,12 @@ #if !TARGET_OS_TV -#import "FBSDKIntegrityManager.h" + #import "FBSDKIntegrityManager.h" -#import "FBSDKBasicUtility.h" -#import "FBSDKGateKeeperManager.h" -#import "FBSDKModelManager.h" -#import "FBSDKSettings.h" -#import "FBSDKTypeUtility.h" + #import "FBSDKGateKeeperManager.h" + #import "FBSDKInternalUtility.h" + #import "FBSDKModelManager.h" + #import "FBSDKSettings.h" static BOOL isIntegrityEnabled = NO; static BOOL isSampleEnabled = NO; @@ -48,7 +47,7 @@ + (void)enable NSMutableDictionary *restrictiveParams = [NSMutableDictionary dictionary]; for (NSString *key in [parameters keyEnumerator]) { - NSString *valueString =[FBSDKTypeUtility stringValue:parameters[key]]; + NSString *valueString = [FBSDKTypeUtility stringValue:parameters[key]]; BOOL shouldFilter = [FBSDKModelManager processIntegrity:key] || [FBSDKModelManager processIntegrity:valueString]; if (shouldFilter) { [FBSDKTypeUtility dictionary:restrictiveParams setObject:isSampleEnabled ? valueString : @"" forKey:key]; diff --git a/FBSDKCoreKit/FBSDKCoreKit/AppEvents/Internal/Integrity/FBSDKRestrictiveData.m b/FBSDKCoreKit/FBSDKCoreKit/AppEvents/Internal/Integrity/FBSDKRestrictiveData.m index c7bb652f57..a07bfa17de 100644 --- a/FBSDKCoreKit/FBSDKCoreKit/AppEvents/Internal/Integrity/FBSDKRestrictiveData.m +++ b/FBSDKCoreKit/FBSDKCoreKit/AppEvents/Internal/Integrity/FBSDKRestrictiveData.m @@ -20,7 +20,7 @@ #import -#import "FBSDKTypeUtility.h" +#import "FBSDKInternalUtility.h" #define RESTRICTIVE_PARAM @"restrictive_param" #define DEPRECATED_PARAM @"deprecated_param" diff --git a/FBSDKCoreKit/FBSDKCoreKit/AppEvents/Internal/Integrity/FBSDKRestrictiveDataFilterManager.m b/FBSDKCoreKit/FBSDKCoreKit/AppEvents/Internal/Integrity/FBSDKRestrictiveDataFilterManager.m index f907dce6e4..1ab3eb3390 100644 --- a/FBSDKCoreKit/FBSDKCoreKit/AppEvents/Internal/Integrity/FBSDKRestrictiveDataFilterManager.m +++ b/FBSDKCoreKit/FBSDKCoreKit/AppEvents/Internal/Integrity/FBSDKRestrictiveDataFilterManager.m @@ -18,8 +18,7 @@ #import "FBSDKRestrictiveDataFilterManager.h" -#import "FBSDKBasicUtility.h" -#import "FBSDKTypeUtility.h" +#import "FBSDKInternalUtility.h" #import "FBSDKServerConfigurationManager.h" @interface FBSDKRestrictiveEventFilter : NSObject @@ -30,15 +29,15 @@ @interface FBSDKRestrictiveEventFilter : NSObject - (instancetype)init NS_UNAVAILABLE; + (instancetype)new NS_UNAVAILABLE; --(instancetype)initWithEventName:(NSString *)eventName - restrictiveParams:(NSDictionary *)restrictiveParams; +- (instancetype)initWithEventName:(NSString *)eventName + restrictiveParams:(NSDictionary *)restrictiveParams; @end @implementation FBSDKRestrictiveEventFilter --(instancetype)initWithEventName:(NSString *)eventName - restrictiveParams:(NSDictionary *)restrictiveParams +- (instancetype)initWithEventName:(NSString *)eventName + restrictiveParams:(NSDictionary *)restrictiveParams { self = [super init]; if (self) { @@ -54,121 +53,129 @@ -(instancetype)initWithEventName:(NSString *)eventName @implementation FBSDKRestrictiveDataFilterManager static BOOL g_isRestrictiveEventFilterEnabled; -static NSMutableArray *_params; +static NSMutableArray *_params; static NSMutableSet *_restrictedEvents; -+ (void)updateFilters:(nullable NSDictionary *)restrictiveParams -{ - static NSString *const RESTRICTIVE_PARAM_KEY = @"restrictive_param"; - static NSString *const PROCESS_EVENT_NAME_KEY = @"process_event_name"; - - restrictiveParams = [FBSDKTypeUtility dictionaryValue:restrictiveParams]; - if (restrictiveParams.count > 0) { - @synchronized (self) { - [_params removeAllObjects]; - [_restrictedEvents removeAllObjects]; - NSMutableArray *eventFilterArray = [NSMutableArray array]; - NSMutableSet *restrictedEventSet = [NSMutableSet set]; - for (NSString *eventName in restrictiveParams.allKeys) { - NSDictionary *eventInfo = restrictiveParams[eventName]; - if (!eventInfo) { - continue; - } - if (eventInfo[RESTRICTIVE_PARAM_KEY]) { - FBSDKRestrictiveEventFilter *restrictiveEventFilter = [[FBSDKRestrictiveEventFilter alloc] initWithEventName:eventName - restrictiveParams:eventInfo[RESTRICTIVE_PARAM_KEY]]; - [FBSDKTypeUtility array:eventFilterArray addObject:restrictiveEventFilter]; - } - if (restrictiveParams[eventName][PROCESS_EVENT_NAME_KEY]) { - [restrictedEventSet addObject:eventName]; - } - } - _params = eventFilterArray; - _restrictedEvents = restrictedEventSet; - } - } -} - -+ (nullable NSString *)getMatchedDataTypeWithEventName:(NSString *)eventName - paramKey:(NSString *)paramKey ++ (void)enable { - // match by params in custom events with event name - for (FBSDKRestrictiveEventFilter *filter in _params) { - if ([filter.eventName isEqualToString:eventName]) { - NSString *type = [FBSDKTypeUtility stringValue:filter.restrictiveParams[paramKey]]; - if (type) { - return type; + @try { + static dispatch_once_t onceToken; + dispatch_once(&onceToken, ^{ + NSDictionary *restrictiveParams = [FBSDKServerConfigurationManager cachedServerConfiguration].restrictiveParams; + if (restrictiveParams) { + [FBSDKRestrictiveDataFilterManager _updateFilters:restrictiveParams]; + g_isRestrictiveEventFilterEnabled = YES; } - } - } - return nil; + }); + } @catch (NSException *exception) {} } -+ (NSDictionary *)processParameters:(NSDictionary *)parameters - eventName:(NSString *)eventName ++ (NSDictionary *)processParameters:(NSDictionary *)parameters + eventName:(NSString *)eventName { if (!g_isRestrictiveEventFilterEnabled) { return parameters; } if (parameters) { - NSMutableDictionary *params = [NSMutableDictionary dictionaryWithDictionary:parameters]; - NSMutableDictionary *restrictedParams = [NSMutableDictionary dictionary]; + @try { + NSMutableDictionary *params = [NSMutableDictionary dictionaryWithDictionary:parameters]; + NSMutableDictionary *restrictedParams = [NSMutableDictionary dictionary]; + + for (NSString *key in [parameters keyEnumerator]) { + NSString *type = [FBSDKRestrictiveDataFilterManager _getMatchedDataTypeWithEventName:eventName + paramKey:key]; + if (type) { + [FBSDKTypeUtility dictionary:restrictedParams setObject:type forKey:key]; + [params removeObjectForKey:key]; + } + } - for (NSString *key in [parameters keyEnumerator]) { - NSString *type = [FBSDKRestrictiveDataFilterManager getMatchedDataTypeWithEventName:eventName - paramKey:key]; - if (type) { - [FBSDKTypeUtility dictionary:restrictedParams setObject:type forKey:key]; - [params removeObjectForKey:key]; + if ([[restrictedParams allKeys] count] > 0) { + NSString *restrictedParamsJSONString = [FBSDKBasicUtility JSONStringForObject:restrictedParams + error:NULL + invalidObjectHandler:NULL]; + [FBSDKTypeUtility dictionary:params setObject:restrictedParamsJSONString forKey:@"_restrictedParams"]; } - } - if ([[restrictedParams allKeys] count] > 0) { - NSString *restrictedParamsJSONString = [FBSDKBasicUtility JSONStringForObject:restrictedParams - error:NULL - invalidObjectHandler:NULL]; - [FBSDKTypeUtility dictionary:params setObject:restrictedParamsJSONString forKey:@"_restrictedParams"]; + return [params copy]; + } @catch (NSException *exception) { + return parameters; } - - return [params copy]; } return nil; } -+ (void)processEvents:(NSMutableArray *> *)events ++ (void)processEvents:(NSMutableArray *> *)events { - if (!g_isRestrictiveEventFilterEnabled) { - return; - } + @try { + if (!g_isRestrictiveEventFilterEnabled) { + return; + } - static NSString *const REPLACEMENT_STRING = @"_removed_"; + static NSString *const REPLACEMENT_STRING = @"_removed_"; - for (NSDictionary *> *event in events) { - if ([FBSDKRestrictiveDataFilterManager isRestrictedEvent:event[@"event"][@"_eventName"]]) { - [event[@"event"] setValue:REPLACEMENT_STRING forKey:@"_eventName"]; + for (NSDictionary *> *event in events) { + if ([FBSDKRestrictiveDataFilterManager _isRestrictedEvent:event[@"event"][@"_eventName"]]) { + [FBSDKTypeUtility dictionary:event[@"event"] setObject:REPLACEMENT_STRING forKey:@"_eventName"]; + } } + } @catch (NSException *exception) {} +} + +#pragma mark - Private Methods + ++ (BOOL)_isRestrictedEvent:(NSString *)eventName +{ + @synchronized(self) { + return [_restrictedEvents containsObject:eventName]; } } -+ (void)enable ++ (nullable NSString *)_getMatchedDataTypeWithEventName:(NSString *)eventName + paramKey:(NSString *)paramKey { - static dispatch_once_t onceToken; - dispatch_once(&onceToken, ^{ - NSDictionary *restrictiveParams = [FBSDKServerConfigurationManager cachedServerConfiguration].restrictiveParams; - if (restrictiveParams) { - [FBSDKRestrictiveDataFilterManager updateFilters:restrictiveParams]; - g_isRestrictiveEventFilterEnabled = YES; + // match by params in custom events with event name + for (FBSDKRestrictiveEventFilter *filter in _params) { + if ([filter.eventName isEqualToString:eventName]) { + NSString *type = [FBSDKTypeUtility stringValue:filter.restrictiveParams[paramKey]]; + if (type) { + return type; + } } - }); + } + return nil; } -#pragma mark Helper functions - -+ (BOOL)isRestrictedEvent:(NSString *)eventName ++ (void)_updateFilters:(nullable NSDictionary *)restrictiveParams { - @synchronized (self) { - return [_restrictedEvents containsObject:eventName]; + static NSString *const RESTRICTIVE_PARAM_KEY = @"restrictive_param"; + static NSString *const PROCESS_EVENT_NAME_KEY = @"process_event_name"; + + restrictiveParams = [FBSDKTypeUtility dictionaryValue:restrictiveParams]; + if (restrictiveParams.count > 0) { + @synchronized(self) { + [_params removeAllObjects]; + [_restrictedEvents removeAllObjects]; + NSMutableArray *eventFilterArray = [NSMutableArray array]; + NSMutableSet *restrictedEventSet = [NSMutableSet set]; + for (NSString *eventName in restrictiveParams.allKeys) { + NSDictionary *eventInfo = restrictiveParams[eventName]; + if (!eventInfo) { + continue; + } + if (eventInfo[RESTRICTIVE_PARAM_KEY]) { + FBSDKRestrictiveEventFilter *restrictiveEventFilter = [[FBSDKRestrictiveEventFilter alloc] initWithEventName:eventName + restrictiveParams:eventInfo[RESTRICTIVE_PARAM_KEY]]; + [FBSDKTypeUtility array:eventFilterArray addObject:restrictiveEventFilter]; + } + if (restrictiveParams[eventName][PROCESS_EVENT_NAME_KEY]) { + [restrictedEventSet addObject:eventName]; + } + } + _params = eventFilterArray; + _restrictedEvents = restrictedEventSet; + } } } diff --git a/FBSDKCoreKit/FBSDKCoreKit/AppEvents/Internal/ML/FBSDKMLMacros.h b/FBSDKCoreKit/FBSDKCoreKit/AppEvents/Internal/ML/FBSDKMLMacros.h index df56151f24..991691a9c3 100644 --- a/FBSDKCoreKit/FBSDKCoreKit/AppEvents/Internal/ML/FBSDKMLMacros.h +++ b/FBSDKCoreKit/FBSDKCoreKit/AppEvents/Internal/ML/FBSDKMLMacros.h @@ -19,7 +19,7 @@ #ifndef FBSDKMLMacros_h #define FBSDKMLMacros_h -// keys for ML +// keys for ML #define MODEL_REQUEST_INTERVAL (60 * 60 * 24 * 3) #define MODEL_REQUEST_TIMESTAMP_KEY @"com.facebook.sdk:FBSDKModelRequestTimestamp" @@ -36,7 +36,7 @@ #define MTMLTaskAppEventPredKey @"MTML_APP_EVENT_PRED" #define MTMLTaskIntegrityDetectKey @"MTML_INTEGRITY_DETECT" -// keys for Suggested Event +// keys for Suggested Event #define SUGGEST_EVENT_KEY @"SUGGEST_EVENT" #define DENSE_FEATURE_KEY @"DENSE_FEATURE" #define SUGGESTED_EVENT_OTHER @"other" diff --git a/FBSDKCoreKit/FBSDKCoreKit/AppEvents/Internal/ML/FBSDKModelManager.h b/FBSDKCoreKit/FBSDKCoreKit/AppEvents/Internal/ML/FBSDKModelManager.h index 4b9873288e..307f9d75ad 100644 --- a/FBSDKCoreKit/FBSDKCoreKit/AppEvents/Internal/ML/FBSDKModelManager.h +++ b/FBSDKCoreKit/FBSDKCoreKit/AppEvents/Internal/ML/FBSDKModelManager.h @@ -20,7 +20,7 @@ #if !TARGET_OS_TV -#import + #import NS_ASSUME_NONNULL_BEGIN diff --git a/FBSDKCoreKit/FBSDKCoreKit/AppEvents/Internal/ML/FBSDKModelManager.mm b/FBSDKCoreKit/FBSDKCoreKit/AppEvents/Internal/ML/FBSDKModelManager.mm index 717b9262f3..c79d8d46fe 100644 --- a/FBSDKCoreKit/FBSDKCoreKit/AppEvents/Internal/ML/FBSDKModelManager.mm +++ b/FBSDKCoreKit/FBSDKCoreKit/AppEvents/Internal/ML/FBSDKModelManager.mm @@ -20,22 +20,21 @@ #if !TARGET_OS_TV -#import "FBSDKModelManager.h" - -#import "FBSDKAppEvents+Internal.h" -#import "FBSDKIntegrityManager.h" -#import "FBSDKFeatureExtractor.h" -#import "FBSDKFeatureManager.h" -#import "FBSDKGraphRequest.h" -#import "FBSDKGraphRequestConnection.h" -#import "FBSDKSettings.h" -#import "FBSDKSuggestedEventsIndexer.h" -#import "FBSDKTypeUtility.h" -#import "FBSDKMLMacros.h" -#import "FBSDKModelParser.h" -#import "FBSDKModelRuntime.hpp" -#import "FBSDKModelUtility.h" -#import "FBSDKTypeUtility.h" + #import "FBSDKModelManager.h" + + #import "FBSDKAppEvents+Internal.h" + #import "FBSDKFeatureExtractor.h" + #import "FBSDKFeatureManager.h" + #import "FBSDKGraphRequest.h" + #import "FBSDKGraphRequestConnection.h" + #import "FBSDKIntegrityManager.h" + #import "FBSDKInternalUtility.h" + #import "FBSDKMLMacros.h" + #import "FBSDKModelParser.h" + #import "FBSDKModelRuntime.hpp" + #import "FBSDKModelUtility.h" + #import "FBSDKSettings.h" + #import "FBSDKSuggestedEventsIndexer.h" static NSString *const INTEGRITY_NONE = @"none"; static NSString *const INTEGRITY_ADDRESS = @"address"; @@ -54,60 +53,68 @@ @implementation FBSDKModelManager -#pragma mark - Public methods + #pragma mark - Public methods + (void)enable { - static dispatch_once_t onceToken; - dispatch_once(&onceToken, ^{ - NSString *languageCode = [[NSLocale currentLocale] objectForKey:NSLocaleLanguageCode]; - // If the languageCode could not be fetched successfully, it's regarded as "en" by default. - if (languageCode && ![languageCode isEqualToString:@"en"]) { - return; - } + @try { + static dispatch_once_t onceToken; + dispatch_once(&onceToken, ^{ + NSString *languageCode = [[NSLocale currentLocale] objectForKey:NSLocaleLanguageCode]; + // If the languageCode could not be fetched successfully, it's regarded as "en" by default. + if (languageCode && ![languageCode isEqualToString:@"en"]) { + return; + } - NSString *dirPath = [NSTemporaryDirectory() stringByAppendingPathComponent:FBSDK_ML_MODEL_PATH]; - if (![[NSFileManager defaultManager] fileExistsAtPath:dirPath]) { - [[NSFileManager defaultManager] createDirectoryAtPath:dirPath withIntermediateDirectories:YES attributes:NULL error:NULL]; - } - _directoryPath = dirPath; - _modelInfo = [[NSUserDefaults standardUserDefaults] objectForKey:MODEL_INFO_KEY]; - NSDate *timestamp = [[NSUserDefaults standardUserDefaults] objectForKey:MODEL_REQUEST_TIMESTAMP_KEY]; - if ([_modelInfo count] == 0 || ![FBSDKFeatureManager isEnabled:FBSDKFeatureModelRequest] || ![self isValidTimestamp:timestamp]) { - // fetch api - FBSDKGraphRequest *request = [[FBSDKGraphRequest alloc] - initWithGraphPath:[NSString stringWithFormat:@"%@/model_asset", [FBSDKSettings appID]]]; - - [request startWithCompletionHandler:^(FBSDKGraphRequestConnection *connection, id result, NSError *error) { - if (!error) { - NSDictionary *resultDictionary = [FBSDKTypeUtility dictionaryValue:result]; - NSDictionary *modelInfo = [self convertToDictionary:resultDictionary[MODEL_DATA_KEY]]; - if (modelInfo) { - _modelInfo = [modelInfo mutableCopy]; - [self processMTML]; - // update cache for model info and timestamp - [[NSUserDefaults standardUserDefaults] setObject:_modelInfo forKey:MODEL_INFO_KEY]; - [[NSUserDefaults standardUserDefaults] setObject:[NSDate date] forKey:MODEL_REQUEST_TIMESTAMP_KEY]; + NSString *dirPath = [NSTemporaryDirectory() stringByAppendingPathComponent:FBSDK_ML_MODEL_PATH]; + if (![[NSFileManager defaultManager] fileExistsAtPath:dirPath]) { + [[NSFileManager defaultManager] createDirectoryAtPath:dirPath withIntermediateDirectories:YES attributes:NULL error:NULL]; + } + _directoryPath = dirPath; + _modelInfo = [[NSUserDefaults standardUserDefaults] objectForKey:MODEL_INFO_KEY]; + NSDate *timestamp = [[NSUserDefaults standardUserDefaults] objectForKey:MODEL_REQUEST_TIMESTAMP_KEY]; + if ([_modelInfo count] == 0 || ![FBSDKFeatureManager isEnabled:FBSDKFeatureModelRequest] || ![self isValidTimestamp:timestamp]) { + // fetch api + FBSDKGraphRequest *request = [[FBSDKGraphRequest alloc] + initWithGraphPath:[NSString stringWithFormat:@"%@/model_asset", [FBSDKSettings appID]]]; + + [request startWithCompletionHandler:^(FBSDKGraphRequestConnection *connection, id result, NSError *error) { + if (!error) { + NSDictionary *resultDictionary = [FBSDKTypeUtility dictionaryValue:result]; + NSDictionary *modelInfo = [self convertToDictionary:resultDictionary[MODEL_DATA_KEY]]; + if (modelInfo) { + _modelInfo = [modelInfo mutableCopy]; + [self processMTML]; + // update cache for model info and timestamp + [[NSUserDefaults standardUserDefaults] setObject:_modelInfo forKey:MODEL_INFO_KEY]; + [[NSUserDefaults standardUserDefaults] setObject:[NSDate date] forKey:MODEL_REQUEST_TIMESTAMP_KEY]; + } } - } + [self checkFeaturesAndExecuteForMTML]; + }]; + } else { [self checkFeaturesAndExecuteForMTML]; - }]; - } else { - [self checkFeaturesAndExecuteForMTML]; - } - }); + } + }); + } @catch (NSException *exception) { + NSLog(@"Fail to enable model manager, exception reason: %@", exception.reason); + } } + (nullable NSDictionary *)getRulesForKey:(NSString *)useCase { - NSDictionary *model = [FBSDKTypeUtility dictionary:_modelInfo objectForKey:useCase ofType:NSObject.class]; - if (model && model[VERSION_ID_KEY]) { - NSString *filePath = [_directoryPath stringByAppendingPathComponent:[NSString stringWithFormat:@"%@_%@.rules", useCase, model[VERSION_ID_KEY]]]; - if (filePath) { - NSData *ruelsData = [NSData dataWithContentsOfFile:filePath options:NSDataReadingMappedIfSafe error:nil]; - NSDictionary *rules = [FBSDKTypeUtility JSONObjectWithData:ruelsData options:0 error:nil]; - return rules; + @try { + NSDictionary *model = [FBSDKTypeUtility dictionary:_modelInfo objectForKey:useCase ofType:NSObject.class]; + if (model && model[VERSION_ID_KEY]) { + NSString *filePath = [_directoryPath stringByAppendingPathComponent:[NSString stringWithFormat:@"%@_%@.rules", useCase, model[VERSION_ID_KEY]]]; + if (filePath) { + NSData *ruelsData = [NSData dataWithContentsOfFile:filePath options:NSDataReadingMappedIfSafe error:nil]; + NSDictionary *rules = [FBSDKTypeUtility JSONObjectWithData:ruelsData options:0 error:nil]; + return rules; + } } + } @catch (NSException *exception) { + NSLog(@"Fail to get rules for usecase %@ from ml model, exception reason: %@", useCase, exception.reason); } return nil; } @@ -138,71 +145,79 @@ + (nullable NSArray *)getThresholdsForKey:(NSString *)useCase if (!_modelInfo) { return nil; } - NSDictionary * modelInfo = _modelInfo[useCase]; + NSDictionary *modelInfo = _modelInfo[useCase]; if (!modelInfo) { return nil; } return modelInfo[THRESHOLDS_KEY]; } -#pragma mark - Integrity Inferencer method + #pragma mark - Integrity Inferencer method + (BOOL)processIntegrity:(nullable NSString *)param { - if (param.length == 0 || _MTMLWeights.size() == 0) { - return false; - } - NSArray *integrityMapping = [self getIntegrityMapping]; - NSString *text = [FBSDKModelUtility normalizeText:param]; - const char *bytes = [text UTF8String]; - if ((int)strlen(bytes) == 0) { - return false; - } - NSArray *thresholds = [FBSDKModelManager getThresholdsForKey:MTMLTaskIntegrityDetectKey]; - if (thresholds.count != integrityMapping.count) { - return false; - } - const fbsdk::MTensor& res = fbsdk::predictOnMTML("integrity_detect", bytes, _MTMLWeights, nullptr); - const float *res_data = res.data(); NSString *integrityType = INTEGRITY_NONE; - for (int i = 0; i < thresholds.count; i++) { - if ((float)res_data[i] >= (float)[[FBSDKTypeUtility array:thresholds objectAtIndex:i] floatValue]) { - integrityType = [FBSDKTypeUtility array:integrityMapping objectAtIndex:i]; - break; + @try { + if (param.length == 0 || _MTMLWeights.size() == 0) { + return false; + } + NSArray *integrityMapping = [self getIntegrityMapping]; + NSString *text = [FBSDKModelUtility normalizeText:param]; + const char *bytes = [text UTF8String]; + if ((int)strlen(bytes) == 0) { + return false; } + NSArray *thresholds = [FBSDKModelManager getThresholdsForKey:MTMLTaskIntegrityDetectKey]; + if (thresholds.count != integrityMapping.count) { + return false; + } + const fbsdk::MTensor &res = fbsdk::predictOnMTML("integrity_detect", bytes, _MTMLWeights, nullptr); + const float *res_data = res.data(); + for (int i = 0; i < thresholds.count; i++) { + if ((float)res_data[i] >= (float)[[FBSDKTypeUtility array:thresholds objectAtIndex:i] floatValue]) { + integrityType = [FBSDKTypeUtility array:integrityMapping objectAtIndex:i]; + break; + } + } + } @catch (NSException *exception) { + NSLog(@"Fail to process parameter for integrity usecase, exception reason: %@", exception.reason); } return ![integrityType isEqualToString:INTEGRITY_NONE]; } -#pragma mark - SuggestedEvents Inferencer method + #pragma mark - SuggestedEvents Inferencer method + (NSString *)processSuggestedEvents:(NSString *)textFeature denseData:(nullable float *)denseData { - NSArray *eventMapping = [FBSDKModelManager getSuggestedEventsMapping]; - if (textFeature.length == 0 || _MTMLWeights.size() == 0 || !denseData) { - return SUGGESTED_EVENT_OTHER; - } - const char *bytes = [textFeature UTF8String]; - if ((int)strlen(bytes) == 0) { - return SUGGESTED_EVENT_OTHER; - } + @try { + NSArray *eventMapping = [FBSDKModelManager getSuggestedEventsMapping]; + if (textFeature.length == 0 || _MTMLWeights.size() == 0 || !denseData) { + return SUGGESTED_EVENT_OTHER; + } + const char *bytes = [textFeature UTF8String]; + if ((int)strlen(bytes) == 0) { + return SUGGESTED_EVENT_OTHER; + } - NSArray *thresholds = [FBSDKModelManager getThresholdsForKey:MTMLTaskAppEventPredKey]; - if (thresholds.count != eventMapping.count) { - return SUGGESTED_EVENT_OTHER; - } + NSArray *thresholds = [FBSDKModelManager getThresholdsForKey:MTMLTaskAppEventPredKey]; + if (thresholds.count != eventMapping.count) { + return SUGGESTED_EVENT_OTHER; + } - const fbsdk::MTensor& res = fbsdk::predictOnMTML("app_event_pred", bytes, _MTMLWeights, denseData); - const float *res_data = res.data(); - for (int i = 0; i < thresholds.count; i++) { - if ((float)res_data[i] >= (float)[[FBSDKTypeUtility array:thresholds objectAtIndex:i] floatValue]) { - return [FBSDKTypeUtility array:eventMapping objectAtIndex:i]; + const fbsdk::MTensor &res = fbsdk::predictOnMTML("app_event_pred", bytes, _MTMLWeights, denseData); + const float *res_data = res.data(); + for (int i = 0; i < thresholds.count; i++) { + if ((float)res_data[i] >= (float)[[FBSDKTypeUtility array:thresholds objectAtIndex:i] floatValue]) { + return [FBSDKTypeUtility array:eventMapping objectAtIndex:i]; + } } + } @catch (NSException *exception) { + NSLog(@"Fail to process suggested events, exception reason: %@", exception.reason); } return SUGGESTED_EVENT_OTHER; } -#pragma mark - Private methods + #pragma mark - Private methods + (BOOL)isValidTimestamp:(NSDate *)timestamp { @@ -226,10 +241,10 @@ + (void)processMTML } if (mtmlAssetUri && mtmlVersionId > 0) { [FBSDKTypeUtility dictionary:_modelInfo setObject:@{ - USE_CASE_KEY: MTMLKey, - ASSET_URI_KEY: mtmlAssetUri, - VERSION_ID_KEY: [NSNumber numberWithLong:mtmlVersionId], - } forKey:MTMLKey]; + USE_CASE_KEY : MTMLKey, + ASSET_URI_KEY : mtmlAssetUri, + VERSION_ID_KEY : [NSNumber numberWithLong:mtmlVersionId], + } forKey:MTMLKey]; } } @@ -265,7 +280,7 @@ + (void)getModelAndRules:(NSString *)useCaseKey NSDictionary *model = [FBSDKTypeUtility dictionary:_modelInfo objectForKey:useCaseKey ofType:NSObject.class]; if (!model || !_directoryPath) { - return; + return; } NSFileManager *fileManager = [NSFileManager defaultManager]; @@ -292,13 +307,14 @@ + (void)getModelAndRules:(NSString *)useCaseKey rulesFilePath = [_directoryPath stringByAppendingPathComponent:[NSString stringWithFormat:@"%@_%@.rules", useCaseKey, model[VERSION_ID_KEY]]]; [self download:rulesUrlString filePath:rulesFilePath queue:queue group:group]; } - dispatch_group_notify(group, dispatch_get_main_queue(), ^{ - if (handler) { - if ([fileManager fileExistsAtPath:assetFilePath] && (!rulesFilePath || [fileManager fileExistsAtPath:rulesFilePath])) { + dispatch_group_notify(group, + dispatch_get_main_queue(), ^{ + if (handler) { + if ([fileManager fileExistsAtPath:assetFilePath] && (!rulesFilePath || [fileManager fileExistsAtPath:rulesFilePath])) { handler(); + } } - } - }); + }); } + (void)clearCacheForModel:(NSDictionary *)model @@ -324,13 +340,14 @@ + (void)download:(NSString *)urlString if (!filePath || [[NSFileManager defaultManager] fileExistsAtPath:filePath]) { return; } - dispatch_group_async(group, queue, ^{ - NSURL *url = [NSURL URLWithString:urlString]; - NSData *urlData = [NSData dataWithContentsOfURL:url]; - if (urlData) { - [urlData writeToFile:filePath atomically:YES]; - } - }); + dispatch_group_async(group, + queue, ^{ + NSURL *url = [NSURL URLWithString:urlString]; + NSData *urlData = [NSData dataWithContentsOfURL:url]; + if (urlData) { + [urlData writeToFile:filePath atomically:YES]; + } + }); } + (nullable NSMutableDictionary *)convertToDictionary:(NSArray *> *)models @@ -341,7 +358,7 @@ + (void)download:(NSString *)urlString NSMutableDictionary *modelInfo = [NSMutableDictionary dictionary]; for (NSDictionary *model in models) { if (model[USE_CASE_KEY]) { - [modelInfo addEntriesFromDictionary:@{model[USE_CASE_KEY]:model}]; + [modelInfo addEntriesFromDictionary:@{model[USE_CASE_KEY] : model}]; } } return modelInfo; @@ -356,13 +373,12 @@ + (void)download:(NSString *)urlString { return @[SUGGESTED_EVENT_OTHER, - FBSDKAppEventNameCompletedRegistration, - FBSDKAppEventNameAddedToCart, - FBSDKAppEventNamePurchased, - FBSDKAppEventNameInitiatedCheckout]; + FBSDKAppEventNameCompletedRegistration, + FBSDKAppEventNameAddedToCart, + FBSDKAppEventNamePurchased, + FBSDKAppEventNameInitiatedCheckout]; } - @end NS_ASSUME_NONNULL_END diff --git a/FBSDKCoreKit/FBSDKCoreKit/AppEvents/Internal/ML/FBSDKModelParser.h b/FBSDKCoreKit/FBSDKCoreKit/AppEvents/Internal/ML/FBSDKModelParser.h index fa583c6e90..d1de42574e 100644 --- a/FBSDKCoreKit/FBSDKCoreKit/AppEvents/Internal/ML/FBSDKModelParser.h +++ b/FBSDKCoreKit/FBSDKCoreKit/AppEvents/Internal/ML/FBSDKModelParser.h @@ -20,9 +20,9 @@ #if !TARGET_OS_TV -#import + #import -#import "FBSDKTensor.hpp" + #import "FBSDKTensor.hpp" NS_ASSUME_NONNULL_BEGIN diff --git a/FBSDKCoreKit/FBSDKCoreKit/AppEvents/Internal/ML/FBSDKModelParser.mm b/FBSDKCoreKit/FBSDKCoreKit/AppEvents/Internal/ML/FBSDKModelParser.mm index 443d7acef9..fdf3566643 100644 --- a/FBSDKCoreKit/FBSDKCoreKit/AppEvents/Internal/ML/FBSDKModelParser.mm +++ b/FBSDKCoreKit/FBSDKCoreKit/AppEvents/Internal/ML/FBSDKModelParser.mm @@ -20,9 +20,10 @@ #if !TARGET_OS_TV -#import "FBSDKMLMacros.h" -#import "FBSDKModelParser.h" -#import "FBSDKTypeUtility.h" + #import "FBSDKModelParser.h" + + #import "FBSDKInternalUtility.h" + #import "FBSDKMLMacros.h" NS_ASSUME_NONNULL_BEGIN @@ -36,7 +37,7 @@ @implementation FBSDKModelParser } const void *data = weightsData.bytes; - NSUInteger totalLength = weightsData.length; + NSUInteger totalLength = weightsData.length; if (totalLength < 4) { // Make sure data length is valid @@ -52,9 +53,9 @@ @implementation FBSDKModelParser char *json = (char *)data + 4; NSDictionary *info = [FBSDKTypeUtility JSONObjectWithData:[NSData dataWithBytes:json length:length] - options:0 - error:nil]; - NSArray *keys = [[info allKeys] sortedArrayUsingComparator:^NSComparisonResult(NSString *key1, NSString *key2) { + options:0 + error:nil]; + NSArray *keys = [[info allKeys] sortedArrayUsingComparator:^NSComparisonResult (NSString *key1, NSString *key2) { return [key1 compare:key2]; }]; @@ -104,18 +105,19 @@ + (bool)validateWeights:(std::unordered_map)weights return [self checkWeights:weights withExpectedInfo:weightsInfoDict]; } -#pragma mark - private methods + #pragma mark - private methods + (NSDictionary *)getKeysMapping { return @{ - @"embedding.weight": @"embed.weight", - @"dense1.weight": @"fc1.weight", - @"dense2.weight": @"fc2.weight", - @"dense3.weight": @"fc3.weight", - @"dense1.bias": @"fc1.bias", - @"dense2.bias": @"fc2.bias", - @"dense3.bias": @"fc3.bias"}; + @"embedding.weight" : @"embed.weight", + @"dense1.weight" : @"fc1.weight", + @"dense2.weight" : @"fc2.weight", + @"dense3.weight" : @"fc3.weight", + @"dense1.bias" : @"fc1.bias", + @"dense2.bias" : @"fc2.bias", + @"dense3.bias" : @"fc3.bias" + }; } + (NSDictionary *)getMTMLWeightsInfo @@ -128,14 +130,15 @@ + (bool)validateWeights:(std::unordered_map)weights @"convs.1.bias" : @[@(64)], @"convs.2.weight" : @[@(64), @(64), @(3)], @"convs.2.bias" : @[@(64)], - @"fc1.weight": @[@(128), @(190)], - @"fc1.bias": @[@(128)], - @"fc2.weight": @[@(64), @(128)], - @"fc2.bias": @[@(64)], - @"integrity_detect.weight": @[@(3), @(64)], - @"integrity_detect.bias": @[@(3)], - @"app_event_pred.weight": @[@(5), @(64)], - @"app_event_pred.bias": @[@(5)]}; + @"fc1.weight" : @[@(128), @(190)], + @"fc1.bias" : @[@(128)], + @"fc2.weight" : @[@(64), @(128)], + @"fc2.bias" : @[@(64)], + @"integrity_detect.weight" : @[@(3), @(64)], + @"integrity_detect.bias" : @[@(3)], + @"app_event_pred.weight" : @[@(5), @(64)], + @"app_event_pred.bias" : @[@(5)] + }; } + (bool)checkWeights:(std::unordered_map)weights @@ -150,13 +153,13 @@ + (bool)checkWeights:(std::unordered_map)weights return false; } fbsdk::MTensor tensor = weights[std::string([key UTF8String])]; - const std::vector& actualSize = tensor.sizes(); + const std::vector &actualSize = tensor.sizes(); NSArray *expectedSize = weightsInfoDict[key]; if (actualSize.size() != expectedSize.count) { return false; } for (int i = 0; i < expectedSize.count; i++) { - if((int)actualSize[i] != (int)[[FBSDKTypeUtility array:expectedSize objectAtIndex:i] intValue]) { + if ((int)actualSize[i] != (int)[[FBSDKTypeUtility array:expectedSize objectAtIndex:i] intValue]) { return false; } } diff --git a/FBSDKCoreKit/FBSDKCoreKit/AppEvents/Internal/ML/FBSDKModelRuntime.hpp b/FBSDKCoreKit/FBSDKCoreKit/AppEvents/Internal/ML/FBSDKModelRuntime.hpp index 8f9f8a29c0..97cd4a752c 100644 --- a/FBSDKCoreKit/FBSDKCoreKit/AppEvents/Internal/ML/FBSDKModelRuntime.hpp +++ b/FBSDKCoreKit/FBSDKCoreKit/AppEvents/Internal/ML/FBSDKModelRuntime.hpp @@ -20,314 +20,328 @@ #if !TARGET_OS_TV -#include -#include -#include -#include + #include -#import + #include + #include + #include -#include "FBSDKTensor.hpp" + #import -#define SEQ_LEN 128 -#define DENSE_FEATURE_LEN 30 + #include "FBSDKTensor.hpp" -namespace fbsdk { - -static void relu(MTensor& x) { - float min = 0; - float max = FLT_MAX; - float *x_data = x.mutable_data(); - vDSP_vclip(x_data, 1, &min, &max, x_data, 1, x.count()); -} + #define SEQ_LEN 128 + #define DENSE_FEATURE_LEN 30 -static void flatten(MTensor& x, int start_dim) { - const std::vector& shape = x.sizes(); - std::vector new_shape; - for (int i = 0; i < start_dim; i++) { - new_shape.push_back(shape[i]); - } - int count = 1; - for (int i = start_dim; i < shape.size(); i++) { - count *= shape[i]; +namespace fbsdk { + static void relu(MTensor &x) + { + float min = 0; + float max = FLT_MAX; + float *x_data = x.mutable_data(); + vDSP_vclip(x_data, 1, &min, &max, x_data, 1, x.count()); } - new_shape.push_back(count); - x.Reshape(new_shape); -} -static MTensor concatenate(std::vector& tensors) { - int n_examples = tensors[0]->size(0); - int count = 0; - for (int i = 0; i < tensors.size(); i++) { - count += tensors[i]->size(1); + static void flatten(MTensor &x, int start_dim) + { + const std::vector &shape = x.sizes(); + std::vector new_shape; + for (int i = 0; i < start_dim; i++) { + new_shape.push_back(shape[i]); + } + int count = 1; + for (int i = start_dim; i < shape.size(); i++) { + count *= shape[i]; + } + new_shape.push_back(count); + x.Reshape(new_shape); } - MTensor y({n_examples, count}); - float *y_data = y.mutable_data(); - for (int i = 0; i < tensors.size(); i++) { - int this_count = (int)tensors[i]->size(1); - const float *this_data = tensors[i]->data(); - for (int n = 0; n < n_examples; n++) { - memcpy(y_data + n * count, this_data + n * this_count, this_count * sizeof(float)); + + static MTensor concatenate(std::vector &tensors) + { + int n_examples = tensors[0]->size(0); + int count = 0; + for (int i = 0; i < tensors.size(); i++) { + count += tensors[i]->size(1); } - y_data += this_count; + MTensor y({n_examples, count}); + float *y_data = y.mutable_data(); + for (int i = 0; i < tensors.size(); i++) { + int this_count = (int)tensors[i]->size(1); + const float *this_data = tensors[i]->data(); + for (int n = 0; n < n_examples; n++) { + memcpy(y_data + n * count, this_data + n * this_count, this_count * sizeof(float)); + } + y_data += this_count; + } + return y; } - return y; -} -static void softmax(MTensor& x) { - int n_examples = x.size(0); - int n_channel = x.size(1); - float *x_data = x.mutable_data(); - float max; - float sum; - for (int n = 0; n < n_examples; n++) { - vDSP_maxv(x_data, 1, &max, n_channel); - max = -max; - vDSP_vsadd(x_data, 1, &max, x_data, 1, n_channel); - vvexpf(x_data, x_data, &n_channel); - vDSP_sve(x_data, 1, &sum, n_channel); - vDSP_vsdiv(x_data, 1, &sum, x_data, 1, n_channel); - x_data += n_channel; + static void softmax(MTensor &x) + { + int n_examples = x.size(0); + int n_channel = x.size(1); + float *x_data = x.mutable_data(); + float max; + float sum; + for (int n = 0; n < n_examples; n++) { + vDSP_maxv(x_data, 1, &max, n_channel); + max = -max; + vDSP_vsadd(x_data, 1, &max, x_data, 1, n_channel); + vvexpf(x_data, x_data, &n_channel); + vDSP_sve(x_data, 1, &sum, n_channel); + vDSP_vsdiv(x_data, 1, &sum, x_data, 1, n_channel); + x_data += n_channel; + } } -} -static std::vector vectorize(const char *texts, const int seq_length) { - int str_len = (int)strlen(texts); - std::vector vec(seq_length, 0); - for (int i = 0; i < seq_length; i++) { - if (i < str_len){ - vec[i] = static_cast(texts[i]); + static std::vector vectorize(const char *texts, const int seq_length) + { + int str_len = (int)strlen(texts); + std::vector vec(seq_length, 0); + for (int i = 0; i < seq_length; i++) { + if (i < str_len) { + vec[i] = static_cast(texts[i]); + } } + return vec; } - return vec; -} -static MTensor embedding(const char *texts, const int seq_length, const MTensor& w) { - // TODO: T65152708 support batch prediction - const std::vector& vec = vectorize(texts, seq_length); - int n_examples = 1; - int embedding_size = w.size(1); - MTensor y({n_examples, seq_length, embedding_size}); - const float* w_data = w.data(); - float *y_data = y.mutable_data(); - for (int i = 0; i < n_examples; i++) { - for (int j = 0; j < seq_length; j++) { - memcpy(y_data, w_data + vec[i * seq_length + j] * embedding_size, (size_t)(embedding_size * sizeof(float))); - y_data += embedding_size; + static MTensor embedding(const char *texts, const int seq_length, const MTensor &w) + { + // TODO: T65152708 support batch prediction + const std::vector &vec = vectorize(texts, seq_length); + int n_examples = 1; + int embedding_size = w.size(1); + MTensor y({n_examples, seq_length, embedding_size}); + const float *w_data = w.data(); + float *y_data = y.mutable_data(); + for (int i = 0; i < n_examples; i++) { + for (int j = 0; j < seq_length; j++) { + memcpy(y_data, w_data + vec[i * seq_length + j] * embedding_size, (size_t)(embedding_size * sizeof(float))); + y_data += embedding_size; + } } + return y; } - return y; -} -/* - x shape: n_examples, in_vector_size - w shape: in_vector_size, out_vector_size - b shape: out_vector_size - return shape: n_examples, out_vector_size - */ -static MTensor dense(const MTensor& x, const MTensor& w, const MTensor& b) { - int n_examples = x.size(0); - int in_vector_size = x.size(1); - int out_vector_size = w.size(1); - MTensor y({n_examples, out_vector_size}); - float *y_data = y.mutable_data(); - const float *b_data = b.data(); - vDSP_mmul(x.data(), 1, w.data(), 1, y_data, 1, n_examples, out_vector_size, in_vector_size); - for (int i = 0; i < out_vector_size; i++) { - vDSP_vsadd(y_data + i, out_vector_size, b_data + i, y_data + i, out_vector_size, n_examples); + /* + x shape: n_examples, in_vector_size + w shape: in_vector_size, out_vector_size + b shape: out_vector_size + return shape: n_examples, out_vector_size + */ + static MTensor dense(const MTensor &x, const MTensor &w, const MTensor &b) + { + int n_examples = x.size(0); + int in_vector_size = x.size(1); + int out_vector_size = w.size(1); + MTensor y({n_examples, out_vector_size}); + float *y_data = y.mutable_data(); + const float *b_data = b.data(); + vDSP_mmul(x.data(), 1, w.data(), 1, y_data, 1, n_examples, out_vector_size, in_vector_size); + for (int i = 0; i < out_vector_size; i++) { + vDSP_vsadd(y_data + i, out_vector_size, b_data + i, y_data + i, out_vector_size, n_examples); + } + return y; } - return y; -} -/* - x shape: n_examples, seq_len, input_size - w shape: kernel_size, input_size, output_size - return shape: n_examples, seq_len - kernel_size + 1, output_size - */ -static MTensor conv1D(const MTensor& x, const MTensor& w) { - int n_examples = x.size(0); - int seq_len = x.size(1); - int input_size = x.size(2); - int kernel_size = w.size(0); - int output_size = w.size(2); - MTensor y({n_examples, seq_len - kernel_size + 1, output_size}); - MTensor temp_x({kernel_size, input_size}); - MTensor temp_w({kernel_size, input_size}); - const float *x_data = x.data(); - const float *w_data = w.data(); - float *y_data = y.mutable_data(); - float *temp_x_data = temp_x.mutable_data(); - float *temp_w_data = temp_w.mutable_data(); - float sum; - for (int n = 0; n < n_examples; n++){ - for (int o = 0; o < output_size; o++){ - for (int i = 0; i < seq_len - kernel_size + 1; i++) { - for (int m = 0; m < kernel_size; m++) { - for (int k = 0; k < input_size; k++) { - temp_x_data[m * input_size + k] = x_data[n * (seq_len * input_size) + (m + i) * input_size + k]; - temp_w_data[m * input_size + k] = w_data[(m * input_size + k) * output_size + o]; + /* + x shape: n_examples, seq_len, input_size + w shape: kernel_size, input_size, output_size + return shape: n_examples, seq_len - kernel_size + 1, output_size + */ + static MTensor conv1D(const MTensor &x, const MTensor &w) + { + int n_examples = x.size(0); + int seq_len = x.size(1); + int input_size = x.size(2); + int kernel_size = w.size(0); + int output_size = w.size(2); + MTensor y({n_examples, seq_len - kernel_size + 1, output_size}); + MTensor temp_x({kernel_size, input_size}); + MTensor temp_w({kernel_size, input_size}); + const float *x_data = x.data(); + const float *w_data = w.data(); + float *y_data = y.mutable_data(); + float *temp_x_data = temp_x.mutable_data(); + float *temp_w_data = temp_w.mutable_data(); + float sum; + for (int n = 0; n < n_examples; n++) { + for (int o = 0; o < output_size; o++) { + for (int i = 0; i < seq_len - kernel_size + 1; i++) { + for (int m = 0; m < kernel_size; m++) { + for (int k = 0; k < input_size; k++) { + temp_x_data[m * input_size + k] = x_data[n * (seq_len * input_size) + (m + i) * input_size + k]; + temp_w_data[m * input_size + k] = w_data[(m * input_size + k) * output_size + o]; + } } + vDSP_dotpr(temp_x_data, 1, temp_w_data, 1, &sum, (size_t)(kernel_size * input_size)); + y_data[(n * (output_size * (seq_len - kernel_size + 1)) + i * output_size + o)] = sum; } - vDSP_dotpr(temp_x_data, 1, temp_w_data, 1, &sum, (size_t)(kernel_size * input_size)); - y_data[(n * (output_size * (seq_len - kernel_size + 1)) + i * output_size + o)] = sum; } } + return y; } - return y; -} -/* - input shape: n_examples, len, n_channel - return shape: n_examples, len - pool_size + 1, n_channel - */ -static MTensor maxPool1D(const MTensor& x, const int pool_size) { - int n_examples = x.size(0); - int input_len = x.size(1); - int n_channel = x.size(2); - int output_len = input_len - pool_size + 1; - MTensor y({n_examples, output_len, n_channel}); - const float *x_data = x.data(); - float *y_data = y.mutable_data(); - for (int n = 0; n < n_examples; n++) { - for (int c = 0; c < n_channel; c++) { - for (int i = 0; i < output_len; i++) { - float this_max = -FLT_MAX; - for (int r = i; r < i + pool_size; r++) { - this_max = fmax(this_max, x_data[n * (n_channel * input_len) + r * n_channel + c]); + /* + input shape: n_examples, len, n_channel + return shape: n_examples, len - pool_size + 1, n_channel + */ + static MTensor maxPool1D(const MTensor &x, const int pool_size) + { + int n_examples = x.size(0); + int input_len = x.size(1); + int n_channel = x.size(2); + int output_len = input_len - pool_size + 1; + MTensor y({n_examples, output_len, n_channel}); + const float *x_data = x.data(); + float *y_data = y.mutable_data(); + for (int n = 0; n < n_examples; n++) { + for (int c = 0; c < n_channel; c++) { + for (int i = 0; i < output_len; i++) { + float this_max = -FLT_MAX; + for (int r = i; r < i + pool_size; r++) { + this_max = fmax(this_max, x_data[n * (n_channel * input_len) + r * n_channel + c]); + } + y_data[n * (n_channel * output_len) + i * n_channel + c] = this_max; } - y_data[n * (n_channel * output_len) + i * n_channel + c] = this_max; } } + return y; } - return y; -} -/* - input shape: m, n - return shape: n, m - */ -static MTensor transpose2D(const MTensor& x) { - int m = x.size(0); - int n = x.size(1); - MTensor y({n, m}); - float *y_data = y.mutable_data(); - const float *x_data = x.data(); - for (int i = 0; i < m; i++){ - for (int j = 0; j < n; j++) { - y_data[j * m + i] = x_data[i * n + j]; + /* + input shape: m, n + return shape: n, m + */ + static MTensor transpose2D(const MTensor &x) + { + int m = x.size(0); + int n = x.size(1); + MTensor y({n, m}); + float *y_data = y.mutable_data(); + const float *x_data = x.data(); + for (int i = 0; i < m; i++) { + for (int j = 0; j < n; j++) { + y_data[j * m + i] = x_data[i * n + j]; + } } + return y; } - return y; -} -/* - input shape: m, n, p - return shape: p, n, m - */ -static MTensor transpose3D(const MTensor& x) { - int m = x.size(0); - int n = x.size(1); - int p = x.size(2); - MTensor y({p, n, m}); - float *y_data = y.mutable_data(); - const float *x_data = x.data(); - for (int i = 0; i < m; i++){ - for (int j = 0; j < n; j++) { - for (int k = 0; k < p; k++) { - y_data[k * m * n + j * m + i] = x_data[i * n * p + j * p + k]; + /* + input shape: m, n, p + return shape: p, n, m + */ + static MTensor transpose3D(const MTensor &x) + { + int m = x.size(0); + int n = x.size(1); + int p = x.size(2); + MTensor y({p, n, m}); + float *y_data = y.mutable_data(); + const float *x_data = x.data(); + for (int i = 0; i < m; i++) { + for (int j = 0; j < n; j++) { + for (int k = 0; k < p; k++) { + y_data[k * m * n + j * m + i] = x_data[i * n * p + j * p + k]; + } } } + return y; } - return y; -} -static void addmv(MTensor& y, const MTensor& x) { - int m = y.size(0); - int n = y.size(1); - int p = y.size(2); - float *y_data = y.mutable_data(); - const float *x_data = x.data(); - for (int i = 0; i < p; i++) { - vDSP_vsadd(y_data + i, p, x_data + i, y_data + i, p, m * n); + static void addmv(MTensor &y, const MTensor &x) + { + int m = y.size(0); + int n = y.size(1); + int p = y.size(2); + float *y_data = y.mutable_data(); + const float *x_data = x.data(); + for (int i = 0; i < p; i++) { + vDSP_vsadd(y_data + i, p, x_data + i, y_data + i, p, m * n); + } } -} -static MTensor getDenseTensor(const float *df) { - MTensor dense_tensor({1, DENSE_FEATURE_LEN}); - if (df) { - memcpy(dense_tensor.mutable_data(), df, DENSE_FEATURE_LEN * sizeof(float)); - } else { - memset(dense_tensor.mutable_data(), 0, DENSE_FEATURE_LEN * sizeof(float)); + static MTensor getDenseTensor(const float *df) + { + MTensor dense_tensor({1, DENSE_FEATURE_LEN}); + if (df) { + memcpy(dense_tensor.mutable_data(), df, DENSE_FEATURE_LEN * sizeof(float)); + } else { + memset(dense_tensor.mutable_data(), 0, DENSE_FEATURE_LEN * sizeof(float)); + } + return dense_tensor; } - return dense_tensor; -} -static MTensor predictOnMTML(const std::string task, const char *texts, const std::unordered_map& weights, const float *df) { - MTensor dense_tensor = getDenseTensor(df); - std::string final_layer_weight_key = task + ".weight"; - std::string final_layer_bias_key = task + ".bias"; + static MTensor predictOnMTML(const std::string task, const char *texts, const std::unordered_map &weights, const float *df) + { + MTensor dense_tensor = getDenseTensor(df); + std::string final_layer_weight_key = task + ".weight"; + std::string final_layer_bias_key = task + ".bias"; - const MTensor& embed_t = weights.at("embed.weight"); - const MTensor& conv0w_t = weights.at("convs.0.weight"); - const MTensor& conv1w_t = weights.at("convs.1.weight"); - const MTensor& conv2w_t = weights.at("convs.2.weight"); - const MTensor& conv0b_t = weights.at("convs.0.bias"); - const MTensor& conv1b_t = weights.at("convs.1.bias"); - const MTensor& conv2b_t = weights.at("convs.2.bias"); - const MTensor& fc1w_t = weights.at("fc1.weight"); // (128, 190) - const MTensor& fc1b_t = weights.at("fc1.bias"); // 128 - const MTensor& fc2w_t = weights.at("fc2.weight"); // (64, 128) - const MTensor& fc2b_t = weights.at("fc2.bias"); // 64 - const MTensor& final_layer_weight_t = weights.at(final_layer_weight_key); // (2, 64) or (5, 64) - const MTensor& final_layer_bias_t = weights.at(final_layer_bias_key); // 2 or 5 + const MTensor &embed_t = weights.at("embed.weight"); + const MTensor &conv0w_t = weights.at("convs.0.weight"); + const MTensor &conv1w_t = weights.at("convs.1.weight"); + const MTensor &conv2w_t = weights.at("convs.2.weight"); + const MTensor &conv0b_t = weights.at("convs.0.bias"); + const MTensor &conv1b_t = weights.at("convs.1.bias"); + const MTensor &conv2b_t = weights.at("convs.2.bias"); + const MTensor &fc1w_t = weights.at("fc1.weight"); // (128, 190) + const MTensor &fc1b_t = weights.at("fc1.bias"); // 128 + const MTensor &fc2w_t = weights.at("fc2.weight"); // (64, 128) + const MTensor &fc2b_t = weights.at("fc2.bias"); // 64 + const MTensor &final_layer_weight_t = weights.at(final_layer_weight_key); // (2, 64) or (5, 64) + const MTensor &final_layer_bias_t = weights.at(final_layer_bias_key); // 2 or 5 - const MTensor& convs_0_weight = transpose3D(conv0w_t); - const MTensor& convs_1_weight = transpose3D(conv1w_t); - const MTensor& convs_2_weight = transpose3D(conv2w_t); - const MTensor& fc1_weight = transpose2D(fc1w_t); - const MTensor& fc2_weight = transpose2D(fc2w_t); - const MTensor& final_layer_weight = transpose2D(final_layer_weight_t); + const MTensor &convs_0_weight = transpose3D(conv0w_t); + const MTensor &convs_1_weight = transpose3D(conv1w_t); + const MTensor &convs_2_weight = transpose3D(conv2w_t); + const MTensor &fc1_weight = transpose2D(fc1w_t); + const MTensor &fc2_weight = transpose2D(fc2w_t); + const MTensor &final_layer_weight = transpose2D(final_layer_weight_t); - // embedding - const MTensor& embed_x = embedding(texts, SEQ_LEN, embed_t); + // embedding + const MTensor &embed_x = embedding(texts, SEQ_LEN, embed_t); - // conv0 - MTensor c0 = conv1D(embed_x, convs_0_weight); // (1, 126, 32) - addmv(c0, conv0b_t); - relu(c0); + // conv0 + MTensor c0 = conv1D(embed_x, convs_0_weight); // (1, 126, 32) + addmv(c0, conv0b_t); + relu(c0); - // conv1 - MTensor c1 = conv1D(c0, convs_1_weight); // (1, 124, 64) - addmv(c1, conv1b_t); - relu(c1); - c1 = maxPool1D(c1, 2); // (1, 123, 64) + // conv1 + MTensor c1 = conv1D(c0, convs_1_weight); // (1, 124, 64) + addmv(c1, conv1b_t); + relu(c1); + c1 = maxPool1D(c1, 2); // (1, 123, 64) - // conv2 - MTensor c2 = conv1D(c1, convs_2_weight); // (1, 121, 64) - addmv(c2, conv2b_t); - relu(c2); + // conv2 + MTensor c2 = conv1D(c1, convs_2_weight); // (1, 121, 64) + addmv(c2, conv2b_t); + relu(c2); - // max pooling - MTensor ca = maxPool1D(c0, c0.size(1)); - MTensor cb = maxPool1D(c1, c1.size(1)); - MTensor cc = maxPool1D(c2, c2.size(1)); + // max pooling + MTensor ca = maxPool1D(c0, c0.size(1)); + MTensor cb = maxPool1D(c1, c1.size(1)); + MTensor cc = maxPool1D(c2, c2.size(1)); - // concatenate - flatten(ca, 1); - flatten(cb, 1); - flatten(cc, 1); - std::vector concat_tensors { &ca, &cb, &cc, &dense_tensor }; - const MTensor& concat = concatenate(concat_tensors); + // concatenate + flatten(ca, 1); + flatten(cb, 1); + flatten(cc, 1); + std::vector concat_tensors { &ca, &cb, &cc, &dense_tensor }; + const MTensor &concat = concatenate(concat_tensors); - // dense + relu - MTensor dense1_x = dense(concat, fc1_weight, fc1b_t); - relu(dense1_x); - MTensor dense2_x = dense(dense1_x, fc2_weight, fc2b_t); - relu(dense2_x); - MTensor final_layer_dense_x = dense(dense2_x, final_layer_weight, final_layer_bias_t); - softmax(final_layer_dense_x); - return final_layer_dense_x; -} + // dense + relu + MTensor dense1_x = dense(concat, fc1_weight, fc1b_t); + relu(dense1_x); + MTensor dense2_x = dense(dense1_x, fc2_weight, fc2b_t); + relu(dense2_x); + MTensor final_layer_dense_x = dense(dense2_x, final_layer_weight, final_layer_bias_t); + softmax(final_layer_dense_x); + return final_layer_dense_x; + } } #endif diff --git a/FBSDKCoreKit/FBSDKCoreKit/AppEvents/Internal/ML/FBSDKModelUtility.h b/FBSDKCoreKit/FBSDKCoreKit/AppEvents/Internal/ML/FBSDKModelUtility.h index 3012e8e3c5..d43fcef671 100644 --- a/FBSDKCoreKit/FBSDKCoreKit/AppEvents/Internal/ML/FBSDKModelUtility.h +++ b/FBSDKCoreKit/FBSDKCoreKit/AppEvents/Internal/ML/FBSDKModelUtility.h @@ -20,7 +20,7 @@ #if !TARGET_OS_TV -#import + #import @interface FBSDKModelUtility : NSObject diff --git a/FBSDKCoreKit/FBSDKCoreKit/AppEvents/Internal/ML/FBSDKModelUtility.m b/FBSDKCoreKit/FBSDKCoreKit/AppEvents/Internal/ML/FBSDKModelUtility.m index 9c1bff6761..53e4666d98 100644 --- a/FBSDKCoreKit/FBSDKCoreKit/AppEvents/Internal/ML/FBSDKModelUtility.m +++ b/FBSDKCoreKit/FBSDKCoreKit/AppEvents/Internal/ML/FBSDKModelUtility.m @@ -20,9 +20,9 @@ #if !TARGET_OS_TV -#import + #import "FBSDKModelUtility.h" -#import "FBSDKModelUtility.h" + #import @implementation FBSDKModelUtility : NSObject @@ -30,7 +30,7 @@ + (NSString *)normalizeText:(NSString *)text { NSMutableArray *tokens = [[text componentsSeparatedByCharactersInSet:[NSCharacterSet whitespaceCharacterSet]] mutableCopy]; [tokens removeObject:@""]; - return [tokens componentsJoinedByString: @" "]; + return [tokens componentsJoinedByString:@" "]; } @end diff --git a/FBSDKCoreKit/FBSDKCoreKit/AppEvents/Internal/ML/FBSDKTensor.hpp b/FBSDKCoreKit/FBSDKCoreKit/AppEvents/Internal/ML/FBSDKTensor.hpp index d6bc8eb7bb..54f3a75a9f 100644 --- a/FBSDKCoreKit/FBSDKCoreKit/AppEvents/Internal/ML/FBSDKTensor.hpp +++ b/FBSDKCoreKit/FBSDKCoreKit/AppEvents/Internal/ML/FBSDKTensor.hpp @@ -20,102 +20,115 @@ #if !TARGET_OS_TV -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#import + #include + #include + #include + #include + #include + #include + #include -// minimal aten implementation -#define MAT_ALWAYS_INLINE inline __attribute__((always_inline)) -namespace fbsdk { + #include + #include -static void* MAllocateMemory(size_t nbytes) { - void* ptr = nullptr; - assert(nbytes > 0); -#ifdef __ANDROID__ - ptr = memalign(64, nbytes); -#else - const int ret = posix_memalign(&ptr, 64, nbytes); - (void)ret; - assert(ret == 0); -#endif - return ptr; -} + #import -static void MFreeMemory(void* ptr) { - if (ptr) { - free(ptr); +// minimal aten implementation + #define MAT_ALWAYS_INLINE inline __attribute__((always_inline)) +namespace fbsdk { + static void *MAllocateMemory(size_t nbytes) + { + void *ptr = nullptr; + assert(nbytes > 0); + #ifdef __ANDROID__ + ptr = memalign(64, nbytes); + #else + const int ret = posix_memalign(&ptr, 64, nbytes); + (void)ret; + assert(ret == 0); + #endif + return ptr; } -} -class MTensor { -public: - MTensor(): storage_(nullptr), sizes_(), strides_(), capacity_(0){}; - explicit MTensor(const std::vector& sizes) { - std::vector strides = std::vector(sizes.size()); - strides[strides.size() - 1] = 1; - for (int i = static_cast(strides.size()) - 2; i >= 0; --i) { - strides[i] = strides[i + 1] * sizes[i + 1]; - } - strides_ = strides; - sizes_ = sizes; - capacity_ = 1; - for (int size : sizes) { - capacity_ *= size; + static void MFreeMemory(void *ptr) + { + if (ptr) { + free(ptr); } - storage_ = std::shared_ptr(MAllocateMemory((size_t)capacity_ * sizeof(float)), MFreeMemory); - } - - MAT_ALWAYS_INLINE int count() const { - return capacity_; } - MAT_ALWAYS_INLINE int size(int dim) const { - return sizes_[dim]; - } + class MTensor { + public: + MTensor() : + storage_(nullptr), + sizes_(), + strides_(), + capacity_(0) {}; + explicit MTensor(const std::vector &sizes) + { + std::vector strides = std::vector(sizes.size()); + strides[strides.size() - 1] = 1; + for (int i = static_cast(strides.size()) - 2; i >= 0; --i) { + strides[i] = strides[i + 1] * sizes[i + 1]; + } + strides_ = strides; + sizes_ = sizes; + capacity_ = 1; + for (int size : sizes) { + capacity_ *= size; + } + storage_ = std::shared_ptr(MAllocateMemory((size_t)capacity_ * sizeof(float)), MFreeMemory); + } - MAT_ALWAYS_INLINE const std::vector& sizes() const { - return sizes_; - } + MAT_ALWAYS_INLINE int count() const + { + return capacity_; + } - MAT_ALWAYS_INLINE const std::vector& strides() const { - return strides_; - } + MAT_ALWAYS_INLINE int size(int dim) const + { + return sizes_[dim]; + } - MAT_ALWAYS_INLINE const float* data() const { - return (const float*)(storage_.get()); - } + MAT_ALWAYS_INLINE const std::vector &sizes() const + { + return sizes_; + } - MAT_ALWAYS_INLINE float* mutable_data() { - return static_cast(storage_.get()); - } + MAT_ALWAYS_INLINE const std::vector &strides() const + { + return strides_; + } - MAT_ALWAYS_INLINE void Reshape(const std::vector& sizes) { - int count = 1; - for (int i = 0; i < sizes.size(); i++) { - count *= sizes[i]; + MAT_ALWAYS_INLINE const float *data() const + { + return (const float *)(storage_.get()); } - if (count > capacity_) { - capacity_ = count; - storage_.reset(MAllocateMemory((size_t)capacity_ * sizeof(float)), MFreeMemory); + + MAT_ALWAYS_INLINE float *mutable_data() + { + return static_cast(storage_.get()); } - sizes_ = sizes; - } -private: - int capacity_; - std::vector sizes_; - std::vector strides_; - std::shared_ptr storage_; -}; + MAT_ALWAYS_INLINE void Reshape(const std::vector &sizes) + { + int count = 1; + for (int i = 0; i < sizes.size(); i++) { + count *= sizes[i]; + } + if (count > capacity_) { + capacity_ = count; + storage_.reset(MAllocateMemory((size_t)capacity_ * sizeof(float)), MFreeMemory); + } + sizes_ = sizes; + } + private: + int capacity_; + std::vector sizes_; + std::vector strides_; + std::shared_ptr storage_; + }; } #endif diff --git a/FBSDKCoreKit/FBSDKCoreKit/AppEvents/Internal/SKAdNetwork/FBSDKSKAdNetworkConversionConfiguration.h b/FBSDKCoreKit/FBSDKCoreKit/AppEvents/Internal/SKAdNetwork/FBSDKSKAdNetworkConversionConfiguration.h new file mode 100644 index 0000000000..3d2ea94eb7 --- /dev/null +++ b/FBSDKCoreKit/FBSDKCoreKit/AppEvents/Internal/SKAdNetwork/FBSDKSKAdNetworkConversionConfiguration.h @@ -0,0 +1,51 @@ +// 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 "TargetConditionals.h" + +#if !TARGET_OS_TV + + #import + + #import "FBSDKSKAdNetworkRule.h" + +NS_ASSUME_NONNULL_BEGIN + +@interface FBSDKSKAdNetworkConversionConfiguration : NSObject + +@property (nonatomic, readonly, assign) NSInteger timerBuckets; + +@property (nonatomic, readonly, assign) NSTimeInterval timerInterval; + +@property (nonatomic, readonly, assign) NSInteger cutoffTime; + +@property (nonatomic, readonly, copy) NSString *defaultCurrency; + +@property (nonatomic, readonly, copy) NSArray *conversionValueRules; + +@property (nonatomic, readonly, copy) NSSet *eventSet; + +@property (nonatomic, readonly, copy) NSSet *currencySet; + +- (nullable instancetype)initWithJSON:(nullable NSDictionary *)dict; + +@end + +NS_ASSUME_NONNULL_END + +#endif diff --git a/FBSDKCoreKit/FBSDKCoreKit/AppEvents/Internal/SKAdNetwork/FBSDKSKAdNetworkConversionConfiguration.m b/FBSDKCoreKit/FBSDKCoreKit/AppEvents/Internal/SKAdNetwork/FBSDKSKAdNetworkConversionConfiguration.m new file mode 100644 index 0000000000..5f24ae05e5 --- /dev/null +++ b/FBSDKCoreKit/FBSDKCoreKit/AppEvents/Internal/SKAdNetwork/FBSDKSKAdNetworkConversionConfiguration.m @@ -0,0 +1,116 @@ +// 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 "TargetConditionals.h" + +#if !TARGET_OS_TV + + #import "FBSDKSKAdNetworkConversionConfiguration.h" + + #import "FBSDKCoreKit+Internal.h" + +@implementation FBSDKSKAdNetworkConversionConfiguration + +- (nullable instancetype)initWithJSON:(nullable NSDictionary *)dict +{ + if ((self = [super init])) { + @try { + dict = [FBSDKTypeUtility dictionaryValue:dict]; + if (!dict) { + return nil; + } + NSArray *data = [FBSDKTypeUtility dictionary:dict objectForKey:@"data" ofType:NSArray.class]; + NSDictionary *conversionRules = [FBSDKTypeUtility dictionaryValue:[FBSDKTypeUtility array:data objectAtIndex:0]]; + if (!conversionRules) { + return nil; + } + _timerBuckets = [FBSDKTypeUtility integerValue:conversionRules[@"timer_buckets"]]; + _timerInterval = (NSTimeInterval)[FBSDKTypeUtility integerValue:conversionRules[@"timer_interval"]]; + _cutoffTime = [FBSDKTypeUtility integerValue:conversionRules[@"cutoff_time"]]; + _defaultCurrency = [[FBSDKTypeUtility stringValue:conversionRules[@"default_currency"]] uppercaseString]; + _conversionValueRules = [FBSDKSKAdNetworkConversionConfiguration parseRules:conversionRules[@"conversion_value_rules"]]; + if (!_conversionValueRules || !_defaultCurrency) { + return nil; + } + _eventSet = [FBSDKSKAdNetworkConversionConfiguration getEventSetFromRules:_conversionValueRules]; + _currencySet = [FBSDKSKAdNetworkConversionConfiguration getCurrencySetFromRules:_conversionValueRules]; + } @catch (NSException *exception) { + return nil; + } + } + return self; +} + ++ (NSSet *)getEventSetFromRules:(NSArray *)rules +{ + NSMutableSet *eventSet = [NSMutableSet new]; + for (FBSDKSKAdNetworkRule *rule in rules) { + if (!rule) { + continue; + } + for (FBSDKSKAdNetworkEvent *event in rule.events) { + if (event.eventName) { + [eventSet addObject:event.eventName]; + } + } + } + return [eventSet copy]; +} + ++ (NSSet *)getCurrencySetFromRules:(NSArray *)rules +{ + NSMutableSet *currencySet = [NSMutableSet new]; + for (FBSDKSKAdNetworkRule *rule in rules) { + if (!rule) { + continue; + } + for (FBSDKSKAdNetworkEvent *event in rule.events) { + for (NSString *currency in event.values) { + [currencySet addObject:[currency uppercaseString]]; + } + } + } + return [currencySet copy]; +} + ++ (nullable NSArray *)parseRules:(nullable NSArray *)rules +{ + rules = [FBSDKTypeUtility arrayValue:rules]; + if (!rules) { + return nil; + } + NSMutableArray *parsedRules = [NSMutableArray new]; + for (id ruleEntry in rules) { + FBSDKSKAdNetworkRule *rule = [[FBSDKSKAdNetworkRule alloc] initWithJSON:ruleEntry]; + [FBSDKTypeUtility array:parsedRules addObject:rule]; + } + [parsedRules sortUsingComparator:^NSComparisonResult (FBSDKSKAdNetworkRule *obj1, FBSDKSKAdNetworkRule *obj2) { + if (obj1.conversionValue < obj2.conversionValue) { + return NSOrderedDescending; + } + if (obj1.conversionValue < obj2.conversionValue) { + return NSOrderedAscending; + } + return NSOrderedSame; + }]; + return [parsedRules copy]; +} + +@end + +#endif diff --git a/FBSDKShareKit/FBSDKShareKit/FBSDKDeviceShareButton.h b/FBSDKCoreKit/FBSDKCoreKit/AppEvents/Internal/SKAdNetwork/FBSDKSKAdNetworkEvent.h similarity index 76% rename from FBSDKShareKit/FBSDKShareKit/FBSDKDeviceShareButton.h rename to FBSDKCoreKit/FBSDKCoreKit/AppEvents/Internal/SKAdNetwork/FBSDKSKAdNetworkEvent.h index a1898ac938..85e8be0b0e 100644 --- a/FBSDKShareKit/FBSDKShareKit/FBSDKDeviceShareButton.h +++ b/FBSDKCoreKit/FBSDKCoreKit/AppEvents/Internal/SKAdNetwork/FBSDKSKAdNetworkEvent.h @@ -18,23 +18,19 @@ #import "TargetConditionals.h" -#if TARGET_OS_TV +#if !TARGET_OS_TV -#import "FBSDKCoreKitImport.h" - -#import "FBSDKSharingContent.h" + #import NS_ASSUME_NONNULL_BEGIN -NS_SWIFT_NAME(FBDeviceShareButton) -@interface FBSDKDeviceShareButton : FBSDKDeviceButton +@interface FBSDKSKAdNetworkEvent : NSObject + +@property (nonatomic, readonly, copy) NSString *eventName; -/** - The required content to share. The button is disabled until this is set. +@property (nullable, nonatomic, readonly, copy) NSDictionary *values; - @see FBSDKDeviceShareViewController - */ -@property (nullable, nonatomic, strong) id shareContent; +- (nullable instancetype)initWithJSON:(NSDictionary *)dict; @end diff --git a/FBSDKCoreKit/FBSDKCoreKit/AppEvents/Internal/SKAdNetwork/FBSDKSKAdNetworkEvent.m b/FBSDKCoreKit/FBSDKCoreKit/AppEvents/Internal/SKAdNetwork/FBSDKSKAdNetworkEvent.m new file mode 100644 index 0000000000..28830075e8 --- /dev/null +++ b/FBSDKCoreKit/FBSDKCoreKit/AppEvents/Internal/SKAdNetwork/FBSDKSKAdNetworkEvent.m @@ -0,0 +1,64 @@ +// 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 "TargetConditionals.h" + +#if !TARGET_OS_TV + + #import "FBSDKSKAdNetworkEvent.h" + + #import + + #import "FBSDKCoreKit+Internal.h" + +@implementation FBSDKSKAdNetworkEvent + +- (nullable instancetype)initWithJSON:(NSDictionary *)dict +{ + if ((self = [super init])) { + dict = [FBSDKTypeUtility dictionaryValue:dict]; + if (!dict) { + return nil; + } + _eventName = [FBSDKTypeUtility dictionary:dict objectForKey:@"event_name" ofType:NSString.class]; + // Event name is a required field + if (!_eventName) { + return nil; + } + // Values is an optional field + NSArray *> *valueEntries = [FBSDKTypeUtility dictionary:dict objectForKey:@"values" ofType:NSArray.class]; + if (valueEntries) { + NSMutableDictionary *valueDict = [NSMutableDictionary new]; + for (NSDictionary *valueEntry in valueEntries) { + NSDictionary *value = [FBSDKTypeUtility dictionaryValue:valueEntry]; + NSString *currency = [FBSDKTypeUtility dictionary:value objectForKey:@"currency" ofType:NSString.class]; + NSNumber *amount = [FBSDKTypeUtility dictionary:value objectForKey:@"amount" ofType:NSNumber.class]; + if (!currency || amount == nil) { + return nil; + } + [FBSDKTypeUtility dictionary:valueDict setObject:amount forKey:[currency uppercaseString]]; + } + _values = [valueDict copy]; + } + } + return self; +} + +@end + +#endif diff --git a/FBSDKCoreKit/FBSDKCoreKit/AppEvents/Internal/SKAdNetwork/FBSDKSKAdNetworkReporter.h b/FBSDKCoreKit/FBSDKCoreKit/AppEvents/Internal/SKAdNetwork/FBSDKSKAdNetworkReporter.h new file mode 100644 index 0000000000..a33acdb1b6 --- /dev/null +++ b/FBSDKCoreKit/FBSDKCoreKit/AppEvents/Internal/SKAdNetwork/FBSDKSKAdNetworkReporter.h @@ -0,0 +1,41 @@ +// 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 "TargetConditionals.h" + +#if !TARGET_OS_TV + + #import + +NS_ASSUME_NONNULL_BEGIN + +@interface FBSDKSKAdNetworkReporter : NSObject + ++ (void)enable; + ++ (void)checkAndRevokeTimer; + ++ (void)recordAndUpdateEvent:(NSString *)event + currency:(nullable NSString *)currency + value:(nullable NSNumber *)value; + +@end + +NS_ASSUME_NONNULL_END + +#endif diff --git a/FBSDKCoreKit/FBSDKCoreKit/AppEvents/Internal/SKAdNetwork/FBSDKSKAdNetworkReporter.m b/FBSDKCoreKit/FBSDKCoreKit/AppEvents/Internal/SKAdNetwork/FBSDKSKAdNetworkReporter.m new file mode 100644 index 0000000000..b2d339cd17 --- /dev/null +++ b/FBSDKCoreKit/FBSDKCoreKit/AppEvents/Internal/SKAdNetwork/FBSDKSKAdNetworkReporter.m @@ -0,0 +1,299 @@ +// 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 "TargetConditionals.h" + +#if !TARGET_OS_TV + + #import "FBSDKSKAdNetworkReporter.h" + + #import + + #import + + #import "FBSDKCoreKit+Internal.h" + #import "FBSDKSKAdNetworkConversionConfiguration.h" + + #define FBSDK_SKADNETWORK_CONFIG_TIME_OUT 86400 + +typedef void (*send_type)(Class, SEL, NSInteger); + +typedef void (^FBSDKSKAdNetworkReporterBlock)(void); + +static NSString *const FBSDKSKAdNetworkConversionConfigurationKey = @"com.facebook.sdk:FBSDKSKAdNetworkConversionConfiguration"; +static NSString *const FBSDKSKAdNetworkReporterKey = @"com.facebook.sdk:FBSDKSKAdNetworkReporter"; + +static BOOL g_isSKAdNetworkReportEnabled = NO; +static NSMutableArray *g_completionBlocks; +static BOOL g_isRequestStarted = NO; +static dispatch_queue_t serialQueue; +static FBSDKSKAdNetworkConversionConfiguration *config; +static NSDate *g_configRefreshTimestamp; +static NSInteger g_conversionValue = 0; +static NSDate *g_timestamp = nil; +static NSMutableSet *g_recordedEvents; +static NSMutableDictionary *g_recordedValues; + +@implementation FBSDKSKAdNetworkReporter + ++ (void)enable +{ + if (@available(iOS 14.0, *)) { + static dispatch_once_t onceToken; + dispatch_once(&onceToken, ^{ + [SKAdNetwork registerAppForAdNetworkAttribution]; + [self _loadReportData]; + g_completionBlocks = [NSMutableArray new]; + serialQueue = dispatch_queue_create("com.facebook.appevents.SKAdNetwork.FBSDKSKAdNetworkReporter", DISPATCH_QUEUE_SERIAL); + [self _loadConfigurationWithBlock:^{ + [self _checkAndUpdateConversionValue]; + [self _checkAndRevokeTimer]; + }]; + g_isSKAdNetworkReportEnabled = YES; + }); + } +} + ++ (void)checkAndRevokeTimer +{ + if (@available(iOS 14.0, *)) { + if (!g_isSKAdNetworkReportEnabled) { + return; + } + [self _loadConfigurationWithBlock:^() { + [self _checkAndRevokeTimer]; + }]; + } +} + ++ (void)recordAndUpdateEvent:(NSString *)event + currency:(nullable NSString *)currency + value:(nullable NSNumber *)value +{ + if (@available(iOS 14.0, *)) { + if (!g_isSKAdNetworkReportEnabled) { + return; + } + if (!event.length) { + return; + } + [self _loadConfigurationWithBlock:^() { + [self _recordAndUpdateEvent:event currency:currency value:value]; + }]; + } +} + ++ (void)_loadConfigurationWithBlock:(FBSDKSKAdNetworkReporterBlock)block +{ + if (!serialQueue) { + return; + } + // Executes block if there is cache + if ([self _isConfigRefreshTimestampValid] && [[NSUserDefaults standardUserDefaults] objectForKey:FBSDKSKAdNetworkConversionConfigurationKey]) { + dispatch_async(serialQueue, ^() { + [FBSDKTypeUtility array:g_completionBlocks addObject:block]; + for (FBSDKSKAdNetworkReporterBlock executionBlock in g_completionBlocks) { + executionBlock(); + } + [g_completionBlocks removeAllObjects]; + }); + return; + } + dispatch_async(serialQueue, ^{ + [FBSDKTypeUtility array:g_completionBlocks addObject:block]; + if (g_isRequestStarted) { + return; + } + g_isRequestStarted = YES; + FBSDKGraphRequest *request = [[FBSDKGraphRequest alloc] + initWithGraphPath:[NSString stringWithFormat:@"%@/ios_skadnetwork_conversion_config", [FBSDKSettings appID]]]; + [request startWithCompletionHandler:^(FBSDKGraphRequestConnection *connection, id result, NSError *error) { + dispatch_async(serialQueue, ^{ + if (error) { + g_isRequestStarted = NO; + return; + } + NSDictionary *json = [FBSDKTypeUtility dictionaryValue:result]; + if (json) { + [[NSUserDefaults standardUserDefaults] setObject:json forKey:FBSDKSKAdNetworkConversionConfigurationKey]; + g_configRefreshTimestamp = [NSDate date]; + config = [[FBSDKSKAdNetworkConversionConfiguration alloc] initWithJSON:json]; + for (FBSDKSKAdNetworkReporterBlock executionBlock in g_completionBlocks) { + executionBlock(); + } + [g_completionBlocks removeAllObjects]; + g_isRequestStarted = NO; + } + }); + }]; + }); +} + ++ (void)_checkAndRevokeTimer +{ + if (!config) { + return; + } + if ([self _shouldCutoff]) { + return; + } + if (g_conversionValue > config.timerBuckets) { + return; + } + if (g_timestamp && [[NSDate date] timeIntervalSinceDate:g_timestamp] < config.timerInterval) { + return; + } + [FBSDKSKAdNetworkReporter _updateConversionValue:g_conversionValue]; +} + ++ (void)_recordAndUpdateEvent:(NSString *)event + currency:(nullable NSString *)currency + value:(nullable NSNumber *)value +{ + if (!config) { + return; + } + if ([self _shouldCutoff]) { + return; + } + if (![config.eventSet containsObject:event] && ![FBSDKAppEventsUtility isStandardEvent:event]) { + return; + } + BOOL isCacheUpdated = false; + if (![g_recordedEvents containsObject:event]) { + [g_recordedEvents addObject:event]; + isCacheUpdated = true; + } + // Change currency to default currency if currency is not found in currencySet + NSString *valueCurrency = [currency uppercaseString]; + if (![config.currencySet containsObject:valueCurrency]) { + valueCurrency = config.defaultCurrency; + } + if (value != nil) { + NSMutableDictionary *mapping = [[FBSDKTypeUtility dictionary:g_recordedValues objectForKey:event ofType:NSDictionary.class] mutableCopy] ?: [NSMutableDictionary new]; + NSNumber *valueInMapping = [FBSDKTypeUtility dictionary:mapping objectForKey:valueCurrency ofType:NSNumber.class] ?: [NSNumber numberWithDouble:0]; + [FBSDKTypeUtility dictionary:mapping setObject:[NSNumber numberWithDouble:(valueInMapping.doubleValue + value.doubleValue)] forKey:valueCurrency]; + [FBSDKTypeUtility dictionary:g_recordedValues setObject:mapping forKey:event]; + isCacheUpdated = true; + } + if (isCacheUpdated) { + [self _checkAndUpdateConversionValue]; + [self _saveReportData]; + } +} + ++ (void)_checkAndUpdateConversionValue +{ + // Update conversion value if a rule is matched + for (FBSDKSKAdNetworkRule *rule in config.conversionValueRules) { + if (rule.conversionValue < g_conversionValue) { + break; + } + if ([rule isMatchedWithRecordedEvents:g_recordedEvents recordedValues:g_recordedValues]) { + [self _updateConversionValue:rule.conversionValue]; + break; + } + } +} + ++ (void)_updateConversionValue:(NSInteger)value +{ + if (@available(iOS 14.0, *)) { + if ([self _shouldCutoff]) { + return; + } + SEL selector = NSSelectorFromString(@"updateConversionValue:"); + if (![[SKAdNetwork class] respondsToSelector:selector]) { + return; + } + send_type msgSend = (send_type)objc_msgSend; + msgSend([SKAdNetwork class], selector, value); + g_conversionValue = value + 1; + g_timestamp = [NSDate date]; + [self _saveReportData]; + } +} + ++ (BOOL)_shouldCutoff +{ + if (!config.cutoffTime) { + return true; + } + NSDate *installTimestamp = [[NSUserDefaults standardUserDefaults] objectForKey:@"com.facebook.sdk:FBSDKSettingsInstallTimestamp"]; + return [installTimestamp isKindOfClass:NSDate.class] && [[NSDate date] timeIntervalSinceDate:installTimestamp] > config.cutoffTime * 86400; +} + + #pragma clang diagnostic push + #pragma clang diagnostic ignored "-Wdeprecated-declarations" ++ (void)_loadReportData +{ + id cachedJSON = [[NSUserDefaults standardUserDefaults] objectForKey:FBSDKSKAdNetworkConversionConfigurationKey]; + config = [[FBSDKSKAdNetworkConversionConfiguration alloc] initWithJSON:cachedJSON]; + NSData *cachedReportData = [[NSUserDefaults standardUserDefaults] objectForKey:FBSDKSKAdNetworkReporterKey]; + g_recordedEvents = [NSMutableSet new]; + g_recordedValues = [NSMutableDictionary new]; + if ([cachedReportData isKindOfClass:[NSData class]]) { + NSDictionary *data = [FBSDKTypeUtility dictionaryValue:[NSKeyedUnarchiver unarchiveObjectWithData:cachedReportData]]; + if (data) { + g_conversionValue = [FBSDKTypeUtility integerValue:data[@"conversion_value"]]; + g_timestamp = [FBSDKTypeUtility dictionary:data objectForKey:@"timestamp" ofType:NSDate.class]; + g_recordedEvents = [[FBSDKTypeUtility dictionary:data objectForKey:@"recorded_events" ofType:NSSet.class] mutableCopy] ?: [NSMutableSet new]; + g_recordedValues = [[FBSDKTypeUtility dictionary:data objectForKey:@"recorded_values" ofType:NSDictionary.class] mutableCopy] ?: [NSMutableDictionary new]; + } + } +} + ++ (void)_saveReportData +{ + NSMutableDictionary *reportData = [NSMutableDictionary new]; + [FBSDKTypeUtility dictionary:reportData setObject:@(g_conversionValue) forKey:@"conversion_value"]; + [FBSDKTypeUtility dictionary:reportData setObject:g_timestamp forKey:@"timestamp"]; + [FBSDKTypeUtility dictionary:reportData setObject:g_recordedEvents forKey:@"recorded_events"]; + [FBSDKTypeUtility dictionary:reportData setObject:g_recordedValues forKey:@"recorded_values"]; + NSData *cache = [NSKeyedArchiver archivedDataWithRootObject:reportData]; + if (cache) { + [[NSUserDefaults standardUserDefaults] setObject:cache forKey:FBSDKSKAdNetworkReporterKey]; + } +} + + #pragma clang diagnostic pop + ++ (BOOL)_isConfigRefreshTimestampValid +{ + return g_configRefreshTimestamp && [[NSDate date] timeIntervalSinceDate:g_configRefreshTimestamp] < FBSDK_SKADNETWORK_CONFIG_TIME_OUT; +} + + #pragma mark - Testability + + #if DEBUG + ++ (void)setConfiguration:(FBSDKSKAdNetworkConversionConfiguration *)configuration +{ + config = configuration; +} + ++ (void)setSKAdNetworkReportEnabled:(BOOL)enabled +{ + g_isSKAdNetworkReportEnabled = enabled; +} + + #endif + +@end + +#endif diff --git a/FBSDKCoreKit/FBSDKCoreKit/Internal/FBSDKTriStateBOOL.h b/FBSDKCoreKit/FBSDKCoreKit/AppEvents/Internal/SKAdNetwork/FBSDKSKAdNetworkRule.h similarity index 68% rename from FBSDKCoreKit/FBSDKCoreKit/Internal/FBSDKTriStateBOOL.h rename to FBSDKCoreKit/FBSDKCoreKit/AppEvents/Internal/SKAdNetwork/FBSDKSKAdNetworkRule.h index d5c1dbe12c..8f8afe742c 100644 --- a/FBSDKCoreKit/FBSDKCoreKit/Internal/FBSDKTriStateBOOL.h +++ b/FBSDKCoreKit/FBSDKCoreKit/AppEvents/Internal/SKAdNetwork/FBSDKSKAdNetworkRule.h @@ -20,17 +20,25 @@ #if !TARGET_OS_TV -#import - -typedef NS_ENUM(NSInteger, FBSDKTriStateBOOL) -{ - FBSDKTriStateBOOLValueUnknown = -1, - FBSDKTriStateBOOLValueNO = 0, - FBSDKTriStateBOOLValueYES = 1, -} NS_SWIFT_NAME(TriStateBool.Value); - -FOUNDATION_EXPORT FBSDKTriStateBOOL FBSDKTriStateBOOLFromBOOL(BOOL value); -FOUNDATION_EXPORT FBSDKTriStateBOOL FBSDKTriStateBOOLFromNSNumber(NSNumber *value); -FOUNDATION_EXPORT BOOL BOOLFromFBSDKTriStateBOOL(FBSDKTriStateBOOL value, BOOL defaultValue); + #import + + #import "FBSDKSKAdNetworkEvent.h" + +NS_ASSUME_NONNULL_BEGIN + +@interface FBSDKSKAdNetworkRule : NSObject + +@property (nonatomic) NSInteger conversionValue; + +@property (nonatomic, copy) NSArray *events; + +- (nullable instancetype)initWithJSON:(NSDictionary *)dict; + +- (BOOL)isMatchedWithRecordedEvents:(NSSet *)recordedEvents + recordedValues:(NSDictionary *)recordedValues; + +@end + +NS_ASSUME_NONNULL_END #endif diff --git a/FBSDKCoreKit/FBSDKCoreKit/AppEvents/Internal/SKAdNetwork/FBSDKSKAdNetworkRule.m b/FBSDKCoreKit/FBSDKCoreKit/AppEvents/Internal/SKAdNetwork/FBSDKSKAdNetworkRule.m new file mode 100644 index 0000000000..cf4b7dcc19 --- /dev/null +++ b/FBSDKCoreKit/FBSDKCoreKit/AppEvents/Internal/SKAdNetwork/FBSDKSKAdNetworkRule.m @@ -0,0 +1,92 @@ +// 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 "TargetConditionals.h" + +#if !TARGET_OS_TV + + #import "FBSDKSKAdNetworkRule.h" + + #import "FBSDKCoreKit+Internal.h" + +@implementation FBSDKSKAdNetworkRule + +- (nullable instancetype)initWithJSON:(NSDictionary *)dict +{ + if ((self = [super init])) { + dict = [FBSDKTypeUtility dictionaryValue:dict]; + if (!dict) { + return nil; + } + NSNumber *value = [FBSDKTypeUtility dictionary:dict objectForKey:@"conversion_value" ofType:NSNumber.class]; + NSArray *events = [FBSDKSKAdNetworkRule parseEvents:[FBSDKTypeUtility dictionary:dict objectForKey:@"events" ofType:NSArray.class]]; + if (value == nil || !events) { + return nil; + } + _conversionValue = value.integerValue; + _events = events; + } + return self; +} + +- (BOOL)isMatchedWithRecordedEvents:(NSSet *)recordedEvents + recordedValues:(NSDictionary *)recordedValues +{ + for (FBSDKSKAdNetworkEvent *event in self.events) { + // Check if event name matches + if (![recordedEvents containsObject:event.eventName]) { + return NO; + } + // Check if event value matches when values is not nil + if (event.values) { + NSDictionary *recordedEventValues = [FBSDKTypeUtility dictionary:recordedValues objectForKey:event.eventName ofType:NSDictionary.class]; + if (!recordedEventValues) { + return NO; + } + for (NSString *currency in event.values) { + NSNumber *valueInMapping = [FBSDKTypeUtility dictionary:event.values objectForKey:currency ofType:NSNumber.class]; + NSNumber *value = [FBSDKTypeUtility dictionary:recordedEventValues objectForKey:currency ofType:NSNumber.class]; + if (value != nil && valueInMapping != nil && value.doubleValue > valueInMapping.doubleValue) { + return YES; + } + } + return NO; + } + } + return YES; +} + ++ (NSArray *)parseEvents:(nullable NSArray *> *)events +{ + if (!events) { + return nil; + } + NSMutableArray *parsedEvents = [NSMutableArray new]; + for (NSDictionary *eventEntry in events) { + FBSDKSKAdNetworkEvent *event = [[FBSDKSKAdNetworkEvent alloc] initWithJSON:eventEntry]; + if (!event) { + return nil; + } + [FBSDKTypeUtility array:parsedEvents addObject:event]; + } + return [parsedEvents copy]; +} + +@end + +#endif diff --git a/FBSDKCoreKit/FBSDKCoreKit/AppEvents/Internal/SuggestedEvents/FBSDKFeatureExtractor.h b/FBSDKCoreKit/FBSDKCoreKit/AppEvents/Internal/SuggestedEvents/FBSDKFeatureExtractor.h index 1feade49f4..a40114a801 100644 --- a/FBSDKCoreKit/FBSDKCoreKit/AppEvents/Internal/SuggestedEvents/FBSDKFeatureExtractor.h +++ b/FBSDKCoreKit/FBSDKCoreKit/AppEvents/Internal/SuggestedEvents/FBSDKFeatureExtractor.h @@ -20,7 +20,7 @@ #if !TARGET_OS_TV -#import + #import NS_ASSUME_NONNULL_BEGIN diff --git a/FBSDKCoreKit/FBSDKCoreKit/AppEvents/Internal/SuggestedEvents/FBSDKFeatureExtractor.m b/FBSDKCoreKit/FBSDKCoreKit/AppEvents/Internal/SuggestedEvents/FBSDKFeatureExtractor.m index f6d65f81de..3354668ccd 100644 --- a/FBSDKCoreKit/FBSDKCoreKit/AppEvents/Internal/SuggestedEvents/FBSDKFeatureExtractor.m +++ b/FBSDKCoreKit/FBSDKCoreKit/AppEvents/Internal/SuggestedEvents/FBSDKFeatureExtractor.m @@ -20,19 +20,20 @@ #if !TARGET_OS_TV -#import "FBSDKFeatureExtractor.h" + #import "FBSDKFeatureExtractor.h" -#import "FBSDKCoreKit+Internal.h" -#import "FBSDKModelManager.h" + #import "FBSDKCoreKit+Internal.h" + #import "FBSDKModelManager.h" -#define REGEX_CR_PASSWORD_FIELD @"password" -#define REGEX_CR_HAS_CONFIRM_PASSWORD_FIELD @"(?i)(confirm.*password)|(password.*(confirmation|confirm)|confirmation)" -#define REGEX_CR_HAS_LOG_IN_KEYWORDS @"(?i)(sign in)|login|signIn" -#define REGEX_CR_HAS_SIGN_ON_KEYWORDS @"(?i)(sign.*(up|now)|registration|" \ -@"register|(create|apply).*(profile|account)|open.*account|" \ -@"account.*(open|creation|application)|enroll|join.*now)" -#define REGEX_ADD_TO_CART_BUTTON_TEXT @"(?i)add to(\\s|\\Z)|update(\\s|\\Z)|cart" -#define REGEX_ADD_TO_CART_PAGE_TITLE @"(?i)add to(\\s|\\Z)|update(\\s|\\Z)|cart|shop|buy" + #define REGEX_CR_PASSWORD_FIELD @"password" + #define REGEX_CR_HAS_CONFIRM_PASSWORD_FIELD @"(?i)(confirm.*password)|(password.*(confirmation|confirm)|confirmation)" + #define REGEX_CR_HAS_LOG_IN_KEYWORDS @"(?i)(sign in)|login|signIn" + #define REGEX_CR_HAS_SIGN_ON_KEYWORDS \ + @"(?i)(sign.*(up|now)|registration|" \ + @"register|(create|apply).*(profile|account)|open.*account|" \ + @"account.*(open|creation|application)|enroll|join.*now)" + #define REGEX_ADD_TO_CART_BUTTON_TEXT @"(?i)add to(\\s|\\Z)|update(\\s|\\Z)|cart" + #define REGEX_ADD_TO_CART_PAGE_TITLE @"(?i)add to(\\s|\\Z)|update(\\s|\\Z)|cart|shop|buy" static NSDictionary *_languageInfo; static NSDictionary *_eventInfo; @@ -46,28 +47,28 @@ @implementation FBSDKFeatureExtractor + (void)initialize { _languageInfo = @{ - @"ENGLISH" : @"1", - @"GERMAN" : @"2", - @"SPANISH" : @"3", - @"JAPANESE" : @"4" - }; + @"ENGLISH" : @"1", + @"GERMAN" : @"2", + @"SPANISH" : @"3", + @"JAPANESE" : @"4" + }; _eventInfo = @{ - @"VIEW_CONTENT" : @"0", - @"SEARCH" : @"1", - @"ADD_TO_CART" : @"2", - @"ADD_TO_WISHLIST" : @"3", - @"INITIATE_CHECKOUT" : @"4", - @"ADD_PAYMENT_INFO" : @"5", - @"PURCHASE" : @"6", - @"LEAD" : @"7", - @"COMPLETE_REGISTRATION" : @"8" - }; + @"VIEW_CONTENT" : @"0", + @"SEARCH" : @"1", + @"ADD_TO_CART" : @"2", + @"ADD_TO_WISHLIST" : @"3", + @"INITIATE_CHECKOUT" : @"4", + @"ADD_PAYMENT_INFO" : @"5", + @"PURCHASE" : @"6", + @"LEAD" : @"7", + @"COMPLETE_REGISTRATION" : @"8" + }; _textTypeInfo = @{ - @"BUTTON_TEXT": @"1", - @"PAGE_TITLE": @"2", - @"RESOLVED_DOCUMENT_LINK": @"3", - @"BUTTON_ID": @"4" - }; + @"BUTTON_TEXT" : @"1", + @"PAGE_TITLE" : @"2", + @"RESOLVED_DOCUMENT_LINK" : @"3", + @"BUTTON_ID" : @"4" + }; } + (void)loadRulesForKey:(NSString *)useCaseKey @@ -96,7 +97,7 @@ + (nullable float *)getDenseFeatures:(NSDictionary *)viewHierarchy [self pruneTree:[[FBSDKTypeUtility array:viewTree objectAtIndex:0] mutableCopy] siblings:siblings]; - float *result = [self parseFeatures:[FBSDKTypeUtility array:viewTree objectAtIndex:0]]; + float *result = [self parseFeatures:[FBSDKTypeUtility array:viewTree objectAtIndex:0]]; NSMutableDictionary *interactedNode; for (NSMutableDictionary *node in siblings) { @@ -117,7 +118,7 @@ + (nullable float *)getDenseFeatures:(NSDictionary *)viewHierarchy return result; } -#pragma mark - Helper functions + #pragma mark - Helper functions + (BOOL)pruneTree:(NSMutableDictionary *)node siblings:(NSMutableArray *)siblings { // If it's interacted, don't prune away the children and just return. @@ -164,11 +165,11 @@ + (float *)nonparseFeatures:(NSMutableDictionary *)node screenname:(NSString *)screenname viewTreeString:(NSString *)viewTreeString { - float *densefeat = (float *)calloc(30, sizeof(float)); + float *densefeat = (float *)calloc(30, sizeof(float)); densefeat[3] = MAX((float)siblings.count - 1, 0); - densefeat[9] = [siblings filteredArrayUsingPredicate:[NSPredicate predicateWithBlock:^BOOL(id _Nullable evaluatedObject, NSDictionary * _Nullable bindings) { + densefeat[9] = [siblings filteredArrayUsingPredicate:[NSPredicate predicateWithBlock:^BOOL (id _Nullable evaluatedObject, NSDictionary *_Nullable bindings) { return [self isButton:evaluatedObject]; }]].count; if ([self isButton:node]) { @@ -219,7 +220,7 @@ + (float *)nonparseFeatures:(NSMutableDictionary *)node + (float *)parseFeatures:(NSMutableDictionary *)node { - float *densefeat = (float *)calloc(30, sizeof(float)); + float *densefeat = (float *)calloc(30, sizeof(float)); NSString *validText = [FBSDKTypeUtility stringValue:node[VIEW_HIERARCHY_TEXT_KEY]]; NSString *validHint = [FBSDKTypeUtility stringValue:node[VIEW_HIERARCHY_HINT_KEY]]; @@ -289,7 +290,8 @@ + (float *)parseFeatures:(NSMutableDictionary *)node return densefeat; } -void sum(float *val0, float *val1) { +void sum(float *val0, float *val1) +{ for (int i = 0; i < 30; i++) { val0[i] += val1[i]; } @@ -308,11 +310,11 @@ + (void)update:(NSDictionary *)node hint:(NSMutableString *)buttonHintString { NSString *text = [[FBSDKTypeUtility dictionary:node - objectForKey:VIEW_HIERARCHY_TEXT_KEY - ofType:NSString.class] lowercaseString]; + objectForKey:VIEW_HIERARCHY_TEXT_KEY + ofType:NSString.class] lowercaseString]; NSString *hint = [[FBSDKTypeUtility dictionary:node - objectForKey:VIEW_HIERARCHY_HINT_KEY - ofType:NSString.class] lowercaseString]; + objectForKey:VIEW_HIERARCHY_HINT_KEY + ofType:NSString.class] lowercaseString]; if (text.length > 0) { [buttonTextString appendFormat:@"%@ ", text]; } diff --git a/FBSDKCoreKit/FBSDKCoreKit/AppEvents/Internal/SuggestedEvents/FBSDKSuggestedEventsIndexer.h b/FBSDKCoreKit/FBSDKCoreKit/AppEvents/Internal/SuggestedEvents/FBSDKSuggestedEventsIndexer.h index d8eaed6462..2e6b537808 100644 --- a/FBSDKCoreKit/FBSDKCoreKit/AppEvents/Internal/SuggestedEvents/FBSDKSuggestedEventsIndexer.h +++ b/FBSDKCoreKit/FBSDKCoreKit/AppEvents/Internal/SuggestedEvents/FBSDKSuggestedEventsIndexer.h @@ -20,7 +20,7 @@ #if !TARGET_OS_TV -#import + #import NS_ASSUME_NONNULL_BEGIN diff --git a/FBSDKCoreKit/FBSDKCoreKit/AppEvents/Internal/SuggestedEvents/FBSDKSuggestedEventsIndexer.m b/FBSDKCoreKit/FBSDKCoreKit/AppEvents/Internal/SuggestedEvents/FBSDKSuggestedEventsIndexer.m index 5a13728d6e..4609fdda62 100644 --- a/FBSDKCoreKit/FBSDKCoreKit/AppEvents/Internal/SuggestedEvents/FBSDKSuggestedEventsIndexer.m +++ b/FBSDKCoreKit/FBSDKCoreKit/AppEvents/Internal/SuggestedEvents/FBSDKSuggestedEventsIndexer.m @@ -20,22 +20,22 @@ #if !TARGET_OS_TV -#import "FBSDKSuggestedEventsIndexer.h" + #import "FBSDKSuggestedEventsIndexer.h" -#import -#import -#import + #import -#import + #import + #import + #import -#import "FBSDKCoreKit+Internal.h" -#import "FBSDKFeatureExtractor.h" -#import "FBSDKMLMacros.h" -#import "FBSDKModelManager.h" -#import "FBSDKModelUtility.h" + #import "FBSDKCoreKit+Internal.h" + #import "FBSDKFeatureExtractor.h" + #import "FBSDKMLMacros.h" + #import "FBSDKModelManager.h" + #import "FBSDKModelUtility.h" -NSString * const OptInEvents = @"production_events"; -NSString * const UnconfirmedEvents = @"eligible_for_prediction_events"; +NSString *const OptInEvents = @"production_events"; +NSString *const UnconfirmedEvents = @"eligible_for_prediction_events"; static NSMutableSet *_optInEvents; static NSMutableSet *_unconfirmedEvents; @@ -76,15 +76,14 @@ + (void)setup static dispatch_once_t onceToken; dispatch_once(&onceToken, ^{ - // swizzle UIButton [FBSDKSwizzler swizzleSelector:@selector(didMoveToWindow) onClass:[UIControl class] withBlock:^(UIControl *control) { - if (control.window && [control isKindOfClass:[UIButton class]]) { - [((UIButton *)control) addTarget:self action:@selector(buttonClicked:) forControlEvents:UIControlEventTouchDown]; - } - } named:@"suggested_events"]; + if (control.window && [control isKindOfClass:[UIButton class]]) { + [((UIButton *)control) addTarget:self action:@selector(buttonClicked:) forControlEvents:UIControlEventTouchDown]; + } + } named:@"suggested_events"]; - // UITableView + // UITableView void (^tableViewBlock)(UITableView *tableView, SEL cmd, id delegate) = @@ -96,7 +95,7 @@ + (void)setup withBlock:tableViewBlock named:@"suggested_events"]; - // UICollectionView + // UICollectionView void (^collectionViewBlock)(UICollectionView *collectionView, SEL cmd, id delegate) = @@ -114,20 +113,21 @@ + (void)setup }); } -+ (void)rematchBindings { ++ (void)rematchBindings +{ NSArray *windows = [UIApplication sharedApplication].windows; for (UIWindow *window in windows) { [self matchSubviewsIn:window]; } } -+ (void)matchSubviewsIn:(UIView *)view { ++ (void)matchSubviewsIn:(UIView *)view +{ if (!view) { return; } for (UIView *subview in view.subviews) { - if ([subview isKindOfClass:[UITableView class]]) { UITableView *tableView = (UITableView *)subview; [self handleView:tableView withDelegate:tableView.delegate]; @@ -183,7 +183,7 @@ + (void)handleView:(UIView *)view withDelegate:(id)delegate + (void)predictEventWithUIResponder:(UIResponder *)uiResponder text:(NSString *)text { - if (text.length > 100 || text.length == 0 || [FBSDKAppEventsUtility isSensitiveUserData: text]) { + if (text.length > 100 || text.length == 0 || [FBSDKAppEventsUtility isSensitiveUserData:text]) { return; } @@ -226,9 +226,8 @@ + (void)predictEventWithUIResponder:(UIResponder *)uiResponder text:(NSString *) } if ([_optInEvents containsObject:event]) { [FBSDKAppEvents logEvent:event - parameters:@{@"_is_suggested_event": @"1", - @"_button_text": text - }]; + parameters:@{@"_is_suggested_event" : @"1", + @"_button_text" : text}]; } else if ([_unconfirmedEvents containsObject:event]) { // Only send back not confirmed events to advertisers [self logSuggestedEvent:event withText:text withDenseFeature:[self getDenseFeaure:denseData] ?: @""]; @@ -238,14 +237,14 @@ + (void)predictEventWithUIResponder:(UIResponder *)uiResponder text:(NSString *) }); } -#pragma mark - Helper Methods + #pragma mark - Helper Methods + (NSString *)getDenseFeaure:(float *)denseData { // Get dense feature string NSMutableArray *denseDataArray = [NSMutableArray array]; for (int i = 0; i < 30; i++) { - [FBSDKTypeUtility array:denseDataArray addObject:[NSNumber numberWithFloat: denseData[i]]]; + [FBSDKTypeUtility array:denseDataArray addObject:[NSNumber numberWithFloat:denseData[i]]]; } return [denseDataArray componentsJoinedByString:@","]; } @@ -264,9 +263,8 @@ + (NSString *)getTextFromContentView:(UIView *)contentView + (void)logSuggestedEvent:(NSString *)event withText:(NSString *)text withDenseFeature:(NSString *)denseFeature { - NSString *metadata = [FBSDKBasicUtility JSONStringForObject:@{@"button_text": text, - @"dense": denseFeature, - } + NSString *metadata = [FBSDKBasicUtility JSONStringForObject:@{@"button_text" : text, + @"dense" : denseFeature, } error:nil invalidObjectHandler:nil]; if (!metadata) { @@ -275,9 +273,10 @@ + (void)logSuggestedEvent:(NSString *)event withText:(NSString *)text withDenseF FBSDKGraphRequest *request = [[FBSDKGraphRequest alloc] initWithGraphPath:[NSString stringWithFormat:@"%@/suggested_events", [FBSDKSettings appID]] - parameters: @{@"event_name": event, - @"metadata": metadata, - } + parameters:@{ + @"event_name" : event, + @"metadata" : metadata, + } HTTPMethod:FBSDKHTTPMethodPOST]; [request startWithCompletionHandler:^(FBSDKGraphRequestConnection *connection, id result, NSError *error) {}]; return; diff --git a/FBSDKCoreKit/FBSDKCoreKit/AppEvents/Internal/ViewHierarchy/FBSDKViewHierarchy.h b/FBSDKCoreKit/FBSDKCoreKit/AppEvents/Internal/ViewHierarchy/FBSDKViewHierarchy.h index 48ecef7891..f0eac712c1 100644 --- a/FBSDKCoreKit/FBSDKCoreKit/AppEvents/Internal/ViewHierarchy/FBSDKViewHierarchy.h +++ b/FBSDKCoreKit/FBSDKCoreKit/AppEvents/Internal/ViewHierarchy/FBSDKViewHierarchy.h @@ -47,9 +47,6 @@ typedef NS_ENUM(NSUInteger, FBCodelessClassBitmask) { NS_ASSUME_NONNULL_BEGIN -extern void fb_dispatch_on_main_thread(dispatch_block_t block); -extern void fb_dispatch_on_default_thread(dispatch_block_t block); - NS_SWIFT_NAME(ViewHierarchy) @interface FBSDKViewHierarchy : NSObject diff --git a/FBSDKCoreKit/FBSDKCoreKit/AppEvents/Internal/ViewHierarchy/FBSDKViewHierarchy.m b/FBSDKCoreKit/FBSDKCoreKit/AppEvents/Internal/ViewHierarchy/FBSDKViewHierarchy.m index 9a14411c96..df11cde138 100644 --- a/FBSDKCoreKit/FBSDKCoreKit/AppEvents/Internal/ViewHierarchy/FBSDKViewHierarchy.m +++ b/FBSDKCoreKit/FBSDKCoreKit/AppEvents/Internal/ViewHierarchy/FBSDKViewHierarchy.m @@ -20,39 +20,25 @@ #if !TARGET_OS_TV -#import "FBSDKViewHierarchy.h" + #import "FBSDKViewHierarchy.h" -#import + #import -#import + #import -#import "FBSDKCodelessPathComponent.h" -#import "FBSDKCoreKit+Internal.h" -#import "FBSDKViewHierarchyMacros.h" + #import "FBSDKCodelessPathComponent.h" + #import "FBSDKCoreKit+Internal.h" + #import "FBSDKViewHierarchyMacros.h" -#define MAX_VIEW_HIERARCHY_LEVEL 35 + #define MAX_VIEW_HIERARCHY_LEVEL 35 NS_ASSUME_NONNULL_BEGIN -void fb_dispatch_on_main_thread(dispatch_block_t block) { - if (block != nil) { - if ([NSThread isMainThread]) { - block(); - } else { - dispatch_async(dispatch_get_main_queue(), block); - } - } -} - -void fb_dispatch_on_default_thread(dispatch_block_t block) { - if (block != nil) { - dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), block); - } -} +_Nullable id getVariableFromInstance(NSObject *instance, NSString *variableName); @implementation FBSDKViewHierarchy -+ (nullable NSArray*)getChildren:(NSObject*)obj ++ (nullable NSArray *)getChildren:(NSObject *)obj { if ([obj isKindOfClass:[UIControl class]]) { return nil; @@ -89,9 +75,9 @@ + (nullable NSArray*)getChildren:(NSObject*)obj } } } else if ([obj isKindOfClass:[UINavigationController class]]) { - UIViewController *vc = ((UINavigationController*)obj).visibleViewController; - UIViewController *tc = ((UINavigationController*)obj).topViewController; - NSArray *nextChildren = [FBSDKViewHierarchy getChildren:((UIViewController*)obj).view]; + UIViewController *vc = ((UINavigationController *)obj).visibleViewController; + UIViewController *tc = ((UINavigationController *)obj).topViewController; + NSArray *nextChildren = [FBSDKViewHierarchy getChildren:((UIViewController *)obj).view]; for (NSObject *child in nextChildren) { if (tc && [self isView:child superViewOfView:tc.view]) { [FBSDKTypeUtility array:children addObject:tc]; @@ -115,7 +101,7 @@ + (nullable NSArray*)getChildren:(NSObject*)obj } } else if ([obj isKindOfClass:[UITabBarController class]]) { UIViewController *vc = ((UITabBarController *)obj).selectedViewController; - NSArray *nextChildren = [FBSDKViewHierarchy getChildren:((UIViewController*)obj).view]; + NSArray *nextChildren = [FBSDKViewHierarchy getChildren:((UIViewController *)obj).view]; for (NSObject *child in nextChildren) { if (vc && [self isView:child superViewOfView:vc.view]) { [FBSDKTypeUtility array:children addObject:vc]; @@ -162,8 +148,7 @@ + (nullable NSObject *)getParent:(nullable NSObject *)obj if (superview && superview != obj) { return superview; } - } - else if ([obj isKindOfClass:[UIViewController class]]) { + } else if ([obj isKindOfClass:[UIViewController class]]) { UIViewController *vc = (UIViewController *)obj; UIViewController *parentVC = vc.parentViewController; UIViewController *presentingVC = vc.presentingViewController; @@ -219,13 +204,13 @@ + (nullable NSArray *)getPath:(NSObject *)obj limit:(int)limit NSDictionary *componentInfo = [FBSDKViewHierarchy getAttributesOf:obj parent:parent]; FBSDKCodelessPathComponent *pathComponent = [[FBSDKCodelessPathComponent alloc] - initWithJSON:componentInfo]; + initWithJSON:componentInfo]; [FBSDKTypeUtility array:path addObject:pathComponent]; return [NSArray arrayWithArray:path]; } -+ (NSDictionary *)getAttributesOf:(NSObject *)obj parent:(NSObject * _Nullable)parent ++ (NSDictionary *)getAttributesOf:(NSObject *)obj parent:(NSObject *_Nullable)parent { NSMutableDictionary *componentInfo = [NSMutableDictionary dictionary]; [FBSDKTypeUtility dictionary:componentInfo setObject:NSStringFromClass([obj class]) forKey:CODELESS_MAPPING_CLASS_NAME_KEY]; @@ -336,15 +321,33 @@ + (nullable NSIndexPath *)getIndexPath:(NSObject *)obj return indexPath; } +// This method only works for ObjC objects (whether statically typed or id) +id getVariableFromInstance(NSObject *instance, NSString *variableName) +{ + if (instance == nil || variableName.length == 0) { + return [NSNull null]; + } + + Ivar ivar = class_getInstanceVariable([instance class], variableName.UTF8String); + if (ivar != NULL) { + const char *encoding = ivar_getTypeEncoding(ivar); + if (encoding != NULL && encoding[0] == '@') { + return object_getIvar(instance, ivar); + } + } + + return [NSNull null]; +} + + (NSString *)getText:(nullable NSObject *)obj { NSString *text = nil; if ([obj isKindOfClass:[UIButton class]]) { text = ((UIButton *)obj).currentTitle; - } else if ([obj isKindOfClass:[UITextView class]] || - [obj isKindOfClass:[UITextField class]] || - [obj isKindOfClass:[UILabel class]]) { + } else if ([obj isKindOfClass:[UITextView class]] + || [obj isKindOfClass:[UITextField class]] + || [obj isKindOfClass:[UILabel class]]) { text = ((UILabel *)obj).text; } else if ([obj isKindOfClass:[UIPickerView class]]) { UIPickerView *picker = (UIPickerView *)obj; @@ -377,19 +380,19 @@ + (NSString *)getText:(nullable NSObject *)obj } } else if ([obj isKindOfClass:[UIDatePicker class]]) { UIDatePicker *picker = (UIDatePicker *)obj; - NSDateFormatter *formatter = [[NSDateFormatter alloc] init]; + NSDateFormatter *const formatter = [NSDateFormatter new]; formatter.dateFormat = @"yyyy-MM-dd HH:mm:ssZ"; text = [formatter stringFromDate:picker.date]; } else if ([obj isKindOfClass:objc_lookUpClass("RCTTextView")]) { - NSTextStorage *textStorage = [FBSDKAppEventsUtility getVariable:@"_textStorage" - fromInstance:obj]; + NSTextStorage *const textStorage = FBSDK_CAST_TO_CLASS_OR_NIL(getVariableFromInstance(obj, @"_textStorage"), NSTextStorage); if (textStorage) { - text = textStorage.string; + text = [textStorage string]; } } else if ([obj isKindOfClass:objc_lookUpClass("RCTBaseTextInputView")]) { - NSAttributedString *attributedText = [FBSDKAppEventsUtility getVariable:@"attributedText" - fromInstance:obj]; - text = attributedText.string; + NSAttributedString *const attributedText = FBSDK_CAST_TO_CLASS_OR_NIL(getVariableFromInstance(obj, @"attributedText"), NSAttributedString); + if (attributedText) { + text = [attributedText string]; + } } return text ?: @""; @@ -415,10 +418,10 @@ + (NSString *)getText:(nullable NSObject *)obj CGFloat fontSize = font.pointSize; return @{ - CODELESS_VIEW_TREE_TEXT_IS_BOLD_KEY: @(isBold), - CODELESS_VIEW_TREE_TEXT_IS_ITALIC_KEY: @(isItalic), - CODELESS_VIEW_TREE_TEXT_SIZE_KEY: @(fontSize) - }; + CODELESS_VIEW_TREE_TEXT_IS_BOLD_KEY : @(isBold), + CODELESS_VIEW_TREE_TEXT_IS_ITALIC_KEY : @(isItalic), + CODELESS_VIEW_TREE_TEXT_SIZE_KEY : @(fontSize) + }; } return nil; @@ -453,7 +456,7 @@ + (NSUInteger)getClassBitmask:(NSObject *)obj bitmask |= FBCodelessClassBitmaskUIButton; } else if ([obj isKindOfClass:[UISwitch class]]) { bitmask |= FBCodelessClassBitmaskSwitch; - }else if ([obj isKindOfClass:[UIDatePicker class]]) { + } else if ([obj isKindOfClass:[UIDatePicker class]]) { bitmask |= FBCodelessClassBitmaskPicker; } } else if ([obj isKindOfClass:[UITableViewCell class]]) { @@ -514,7 +517,7 @@ + (BOOL)isUserInputView:(NSObject *)obj } if (objAddressSet) { - if ([objAddressSet containsObject: currentNode]) { + if ([objAddressSet containsObject:currentNode]) { return nil; } [objAddressSet addObject:currentNode]; @@ -544,8 +547,8 @@ + (BOOL)isUserInputView:(NSObject *)obj return [result copy]; } -#pragma clang diagnostic push -#pragma clang diagnostic ignored "-Wundeclared-selector" + #pragma clang diagnostic push + #pragma clang diagnostic ignored "-Wundeclared-selector" + (BOOL)isRCTButton:(UIView *)view { if (view == nil) { @@ -553,10 +556,10 @@ + (BOOL)isRCTButton:(UIView *)view } Class classRCTView = objc_lookUpClass(ReactNativeClassRCTView); - if (classRCTView && [view isKindOfClass:classRCTView] && - [view respondsToSelector:@selector(reactTagAtPoint:)] && - [view respondsToSelector:@selector(reactTag)] && - view.userInteractionEnabled) { + if (classRCTView && [view isKindOfClass:classRCTView] + && [view respondsToSelector:@selector(reactTagAtPoint:)] + && [view respondsToSelector:@selector(reactTag)] + && view.userInteractionEnabled) { // We check all its subviews locations and the view is clickable if there exists one that mathces reactTagAtPoint for (UIView *subview in view.subviews) { if (subview && ![subview isKindOfClass:classRCTView]) { @@ -584,7 +587,8 @@ + (nullable NSNumber *)getViewReactTag:(UIView *)view return nil; } -#pragma clang diagnostic pop + + #pragma clang diagnostic pop + (BOOL)isView:(NSObject *)obj1 superViewOfView:(UIView *)obj2 { @@ -670,18 +674,19 @@ + (NSInteger)getTag:(NSObject *)obj CGRect frame = view.frame; CGPoint offset = CGPointZero; - if ([view isKindOfClass:[UIScrollView class]]) + if ([view isKindOfClass:[UIScrollView class]]) { offset = ((UIScrollView *)view).contentOffset; + } return @{ - CODELESS_VIEW_TREE_TOP_KEY: @((int)frame.origin.y), - CODELESS_VIEW_TREE_LEFT_KEY: @((int)frame.origin.x), - CODELESS_VIEW_TREE_WIDTH_KEY: @((int)frame.size.width), - CODELESS_VIEW_TREE_HEIGHT_KEY: @((int)frame.size.height), - CODELESS_VIEW_TREE_OFFSET_X_KEY: @((int)offset.x), - CODELESS_VIEW_TREE_OFFSET_Y_KEY: @((int)offset.y), - CODELESS_VIEW_TREE_VISIBILITY_KEY: view.isHidden ? @4 : @0 - }; + CODELESS_VIEW_TREE_TOP_KEY : @((int)frame.origin.y), + CODELESS_VIEW_TREE_LEFT_KEY : @((int)frame.origin.x), + CODELESS_VIEW_TREE_WIDTH_KEY : @((int)frame.size.width), + CODELESS_VIEW_TREE_HEIGHT_KEY : @((int)frame.size.height), + CODELESS_VIEW_TREE_OFFSET_X_KEY : @((int)offset.x), + CODELESS_VIEW_TREE_OFFSET_Y_KEY : @((int)offset.y), + CODELESS_VIEW_TREE_VISIBILITY_KEY : view.isHidden ? @4 : @0 + }; } + (NSString *)recursiveGetLabelsFromView:(UIView *)view diff --git a/FBSDKCoreKit/FBSDKCoreKit/AppEvents/Internal/ViewHierarchy/FBSDKViewHierarchyMacros.h b/FBSDKCoreKit/FBSDKCoreKit/AppEvents/Internal/ViewHierarchy/FBSDKViewHierarchyMacros.h index 5eedf9f3a0..c7a904814d 100644 --- a/FBSDKCoreKit/FBSDKCoreKit/AppEvents/Internal/ViewHierarchy/FBSDKViewHierarchyMacros.h +++ b/FBSDKCoreKit/FBSDKCoreKit/AppEvents/Internal/ViewHierarchy/FBSDKViewHierarchyMacros.h @@ -18,7 +18,7 @@ #ifndef FBSDKViewHierarchyMacros_h #define FBSDKViewHierarchyMacros_h -// keys for event binding path compoenent +// keys for event binding path compoenent #define CODELESS_MAPPING_METHOD_KEY @"method" #define CODELESS_MAPPING_EVENT_NAME_KEY @"event_name" #define CODELESS_MAPPING_EVENT_TYPE_KEY @"event_type" @@ -65,7 +65,7 @@ #define CODELESS_SETTING_TIMESTAMP_KEY @"codeless_setting_timestamp" #define CODELESS_SETTING_CACHE_TIMEOUT (7 * 24 * 60 * 60) -// keys for view tree +// keys for view tree #define CODELESS_VIEW_TREE_DESC_KEY @"description" #define CODELESS_VIEW_TREE_DIMENSION_KEY @"dimension" #define CODELESS_VIEW_TREE_TAG_KEY @"tag" @@ -84,7 +84,7 @@ #define CODELESS_VIEW_TREE_TEXT_IS_ITALIC_KEY @"is_italic" #define CODELESS_VIEW_TREE_TEXT_SIZE_KEY @"font_size" -// keys for view hierarchy +// keys for view hierarchy #define VIEW_HIERARCHY_CHILD_VIEWS_KEY @"childviews" #define VIEW_HIERARCHY_CLASS_NAME_KEY @"classname" #define VIEW_HIERARCHY_CLASS_TYPE_BITMASK_KEY @"classtypebitmask" diff --git a/FBSDKCoreKit/FBSDKCoreKit/AppLink/FBSDKAppLink.m b/FBSDKCoreKit/FBSDKCoreKit/AppLink/FBSDKAppLink.m index a1ea9d2dc6..2db4a3031d 100644 --- a/FBSDKCoreKit/FBSDKCoreKit/AppLink/FBSDKAppLink.m +++ b/FBSDKCoreKit/FBSDKCoreKit/AppLink/FBSDKAppLink.m @@ -20,7 +20,7 @@ #if !TARGET_OS_TV -#import "FBSDKAppLink_Internal.h" + #import "FBSDKAppLink_Internal.h" NSString *const FBSDKAppLinkDataParameterName = @"al_applink_data"; NSString *const FBSDKAppLinkTargetKeyName = @"target_url"; @@ -38,7 +38,7 @@ @interface FBSDKAppLink () @property (nonatomic, copy) NSArray *targets; @property (nonatomic, strong) NSURL *webURL; -@property (nonatomic, assign, getter=isBackToReferrer) BOOL backToReferrer; +@property (nonatomic, getter = isBackToReferrer, assign) BOOL backToReferrer; @end @@ -47,28 +47,31 @@ @implementation FBSDKAppLink + (instancetype)appLinkWithSourceURL:(NSURL *)sourceURL targets:(NSArray *)targets webURL:(NSURL *)webURL - isBackToReferrer:(BOOL)isBackToReferrer { - FBSDKAppLink *link = [[self alloc] initWithIsBackToReferrer:isBackToReferrer]; - link.sourceURL = sourceURL; - link.targets = [targets copy]; - link.webURL = webURL; - return link; + isBackToReferrer:(BOOL)isBackToReferrer +{ + FBSDKAppLink *link = [[self alloc] initWithIsBackToReferrer:isBackToReferrer]; + link.sourceURL = sourceURL; + link.targets = [targets copy]; + link.webURL = webURL; + return link; } + (instancetype)appLinkWithSourceURL:(NSURL *)sourceURL targets:(NSArray *)targets - webURL:(NSURL *)webURL { - return [self appLinkWithSourceURL:sourceURL - targets:targets - webURL:webURL - isBackToReferrer:NO]; + webURL:(NSURL *)webURL +{ + return [self appLinkWithSourceURL:sourceURL + targets:targets + webURL:webURL + isBackToReferrer:NO]; } -- (FBSDKAppLink *)initWithIsBackToReferrer:(BOOL)backToReferrer { - if ((self = [super init])) { - _backToReferrer = backToReferrer; - } - return self; +- (FBSDKAppLink *)initWithIsBackToReferrer:(BOOL)backToReferrer +{ + if ((self = [super init])) { + _backToReferrer = backToReferrer; + } + return self; } @end diff --git a/FBSDKCoreKit/FBSDKCoreKit/AppLink/FBSDKAppLinkNavigation.m b/FBSDKCoreKit/FBSDKCoreKit/AppLink/FBSDKAppLinkNavigation.m index 14a3eab489..f5d4afd91b 100644 --- a/FBSDKCoreKit/FBSDKCoreKit/AppLink/FBSDKAppLinkNavigation.m +++ b/FBSDKCoreKit/FBSDKCoreKit/AppLink/FBSDKAppLinkNavigation.m @@ -20,14 +20,14 @@ #if !TARGET_OS_TV -#import "FBSDKAppLinkNavigation.h" + #import "FBSDKAppLinkNavigation.h" -#import "FBSDKAppLinkTarget.h" -#import "FBSDKAppLink_Internal.h" -#import "FBSDKMeasurementEvent_Internal.h" -#import "FBSDKSettings.h" -#import "FBSDKTypeUtility.h" -#import "FBSDKWebViewAppLinkResolver.h" + #import "FBSDKAppLinkTarget.h" + #import "FBSDKAppLink_Internal.h" + #import "FBSDKInternalUtility.h" + #import "FBSDKMeasurementEvent_Internal.h" + #import "FBSDKSettings.h" + #import "FBSDKWebViewAppLinkResolver.h" FOUNDATION_EXPORT NSString *const FBSDKAppLinkDataParameterName; FOUNDATION_EXPORT NSString *const FBSDKAppLinkTargetKeyName; @@ -52,190 +52,199 @@ @implementation FBSDKAppLinkNavigation + (instancetype)navigationWithAppLink:(FBSDKAppLink *)appLink extras:(NSDictionary *)extras - appLinkData:(NSDictionary *)appLinkData { - FBSDKAppLinkNavigation *navigation = [[self alloc] init]; - navigation.appLink = appLink; - navigation.extras = extras; - navigation.appLinkData = appLinkData; - return navigation; + appLinkData:(NSDictionary *)appLinkData +{ + FBSDKAppLinkNavigation *navigation = [[self alloc] init]; + navigation.appLink = appLink; + navigation.extras = extras; + navigation.appLinkData = appLinkData; + return navigation; } + (NSDictionary *> *)callbackAppLinkDataForAppWithName:(NSString *)appName - url:(NSString *)url { - return @{FBSDKAppLinkRefererAppLink: @{FBSDKAppLinkRefererAppName: appName, FBSDKAppLinkRefererUrl: url}}; + url:(NSString *)url +{ + return @{FBSDKAppLinkRefererAppLink : @{FBSDKAppLinkRefererAppName : appName, FBSDKAppLinkRefererUrl : url}}; } -- (NSString *)stringByEscapingQueryString:(NSString *)string { - return [string stringByAddingPercentEncodingWithAllowedCharacters:[NSCharacterSet URLQueryAllowedCharacterSet]]; +- (NSString *)stringByEscapingQueryString:(NSString *)string +{ + return [string stringByAddingPercentEncodingWithAllowedCharacters:[NSCharacterSet URLQueryAllowedCharacterSet]]; } -- (NSURL *)appLinkURLWithTargetURL:(NSURL *)targetUrl error:(NSError **)error { - NSMutableDictionary *appLinkData = - [NSMutableDictionary dictionaryWithDictionary:self.appLinkData ?: @{}]; - - // Add applink protocol data - if (!appLinkData[FBSDKAppLinkUserAgentKeyName]) { - [FBSDKTypeUtility dictionary:appLinkData setObject:[NSString stringWithFormat:@"FBSDK %@", FBSDKSettings.sdkVersion] forKey:FBSDKAppLinkUserAgentKeyName]; - } - if (!appLinkData[FBSDKAppLinkVersionKeyName]) { - [FBSDKTypeUtility dictionary:appLinkData setObject:FBSDKAppLinkVersion forKey:FBSDKAppLinkVersionKeyName]; +- (NSURL *)appLinkURLWithTargetURL:(NSURL *)targetUrl error:(NSError **)error +{ + NSMutableDictionary *appLinkData = + [NSMutableDictionary dictionaryWithDictionary:self.appLinkData ?: @{}]; + + // Add applink protocol data + if (!appLinkData[FBSDKAppLinkUserAgentKeyName]) { + [FBSDKTypeUtility dictionary:appLinkData setObject:[NSString stringWithFormat:@"FBSDK %@", FBSDKSettings.sdkVersion] forKey:FBSDKAppLinkUserAgentKeyName]; + } + if (!appLinkData[FBSDKAppLinkVersionKeyName]) { + [FBSDKTypeUtility dictionary:appLinkData setObject:FBSDKAppLinkVersion forKey:FBSDKAppLinkVersionKeyName]; + } + if (self.appLink.sourceURL.absoluteString) { + [FBSDKTypeUtility dictionary:appLinkData setObject:self.appLink.sourceURL.absoluteString forKey:FBSDKAppLinkTargetKeyName]; + } + [FBSDKTypeUtility dictionary:appLinkData setObject:self.extras ?: @{} forKey:FBSDKAppLinkExtrasKeyName]; + + // JSON-ify the applink data + NSError *jsonError = nil; + NSData *jsonBlob = [FBSDKTypeUtility dataWithJSONObject:appLinkData options:0 error:&jsonError]; + if (!jsonError) { + NSString *jsonString = [[NSString alloc] initWithData:jsonBlob encoding:NSUTF8StringEncoding]; + NSString *encoded = [self stringByEscapingQueryString:jsonString]; + + NSString *endUrlString = [NSString stringWithFormat:@"%@%@%@=%@", + targetUrl.absoluteString, + targetUrl.query ? @"&" : @"?", + FBSDKAppLinkDataParameterName, + encoded]; + + return [NSURL URLWithString:endUrlString]; + } else { + if (error) { + *error = jsonError; } - if (self.appLink.sourceURL.absoluteString) { - [FBSDKTypeUtility dictionary:appLinkData setObject:self.appLink.sourceURL.absoluteString forKey:FBSDKAppLinkTargetKeyName]; - } - [FBSDKTypeUtility dictionary:appLinkData setObject:self.extras ?: @{} forKey:FBSDKAppLinkExtrasKeyName]; - - // JSON-ify the applink data - NSError *jsonError = nil; - NSData *jsonBlob = [FBSDKTypeUtility dataWithJSONObject:appLinkData options:0 error:&jsonError]; - if (!jsonError) { - NSString *jsonString = [[NSString alloc] initWithData:jsonBlob encoding:NSUTF8StringEncoding]; - NSString *encoded = [self stringByEscapingQueryString:jsonString]; - - NSString *endUrlString = [NSString stringWithFormat:@"%@%@%@=%@", - targetUrl.absoluteString, - targetUrl.query ? @"&" : @"?", - FBSDKAppLinkDataParameterName, - encoded]; - - return [NSURL URLWithString:endUrlString]; - } else { - if (error) { - *error = jsonError; - } - // If there was an error encoding the app link data, fail hard. - return nil; - } + // If there was an error encoding the app link data, fail hard. + return nil; + } } -- (FBSDKAppLinkNavigationType)navigate:(NSError **)error { -#pragma clang diagnostic push -#pragma clang diagnostic ignored "-Wdeprecated-declarations" - NSURL *openedURL = nil; - NSError *encodingError = nil; - FBSDKAppLinkNavigationType retType = FBSDKAppLinkNavigationTypeFailure; - - // Find the first eligible/launchable target in the FBSDKAppLink. - for (FBSDKAppLinkTarget *target in self.appLink.targets) { - NSURL *appLinkAppURL = [self appLinkURLWithTargetURL:target.URL error:&encodingError]; - if (encodingError || !appLinkAppURL) { - if (error) { - *error = encodingError; - } - } else if ([[UIApplication sharedApplication] openURL:appLinkAppURL]) { - retType = FBSDKAppLinkNavigationTypeApp; - openedURL = appLinkAppURL; - break; - } +- (FBSDKAppLinkNavigationType)navigate:(NSError **)error +{ + #pragma clang diagnostic push + #pragma clang diagnostic ignored "-Wdeprecated-declarations" + NSURL *openedURL = nil; + NSError *encodingError = nil; + FBSDKAppLinkNavigationType retType = FBSDKAppLinkNavigationTypeFailure; + + // Find the first eligible/launchable target in the FBSDKAppLink. + for (FBSDKAppLinkTarget *target in self.appLink.targets) { + NSURL *appLinkAppURL = [self appLinkURLWithTargetURL:target.URL error:&encodingError]; + if (encodingError || !appLinkAppURL) { + if (error) { + *error = encodingError; + } + } else if ([[UIApplication sharedApplication] openURL:appLinkAppURL]) { + retType = FBSDKAppLinkNavigationTypeApp; + openedURL = appLinkAppURL; + break; } - - if (!openedURL && self.appLink.webURL) { - // Fall back to opening the url in the browser if available. - NSURL *appLinkBrowserURL = [self appLinkURLWithTargetURL:self.appLink.webURL error:&encodingError]; - if (encodingError || !appLinkBrowserURL) { - // If there was an error encoding the app link data, fail hard. - if (error) { - *error = encodingError; - } - } else if ([[UIApplication sharedApplication] openURL:appLinkBrowserURL]) { - // This was a browser navigation. - retType = FBSDKAppLinkNavigationTypeBrowser; - openedURL = appLinkBrowserURL; - } + } + + if (!openedURL && self.appLink.webURL) { + // Fall back to opening the url in the browser if available. + NSURL *appLinkBrowserURL = [self appLinkURLWithTargetURL:self.appLink.webURL error:&encodingError]; + if (encodingError || !appLinkBrowserURL) { + // If there was an error encoding the app link data, fail hard. + if (error) { + *error = encodingError; + } + } else if ([[UIApplication sharedApplication] openURL:appLinkBrowserURL]) { + // This was a browser navigation. + retType = FBSDKAppLinkNavigationTypeBrowser; + openedURL = appLinkBrowserURL; } -#pragma clang diagnostic pop - - [self postAppLinkNavigateEventNotificationWithTargetURL:openedURL - error:error ? *error : nil - type:retType]; - return retType; -} + } + #pragma clang diagnostic pop -- (void)postAppLinkNavigateEventNotificationWithTargetURL:(NSURL *)outputURL error:(NSError *)error type:(FBSDKAppLinkNavigationType)type { - NSString *const EVENT_YES_VAL = @"1"; - NSString *const EVENT_NO_VAL = @"0"; - NSMutableDictionary *logData = - [[NSMutableDictionary alloc] init]; - - NSString *outputURLScheme = outputURL.scheme; - NSString *outputURLString = outputURL.absoluteString; - if (outputURLScheme) { - [FBSDKTypeUtility dictionary:logData setObject:outputURLScheme forKey:@"outputURLScheme"]; - } - if (outputURLString) { - [FBSDKTypeUtility dictionary:logData setObject:outputURLString forKey:@"outputURL"]; - } - - NSString *sourceURLString = self.appLink.sourceURL.absoluteString; - NSString *sourceURLHost = self.appLink.sourceURL.host; - NSString *sourceURLScheme = self.appLink.sourceURL.scheme; - if (sourceURLString) { - [FBSDKTypeUtility dictionary:logData setObject:sourceURLString forKey:@"sourceURL"]; - } - if (sourceURLHost) { - [FBSDKTypeUtility dictionary:logData setObject:sourceURLHost forKey:@"sourceHost"]; - } - if (sourceURLScheme) { - [FBSDKTypeUtility dictionary:logData setObject:sourceURLScheme forKey:@"sourceScheme"]; - } - if (error.localizedDescription) { - [FBSDKTypeUtility dictionary:logData setObject:error.localizedDescription forKey:@"error"]; - } - NSString *success = nil; //no - NSString *linkType = nil; // unknown; - switch (type) { - case FBSDKAppLinkNavigationTypeFailure: - success = EVENT_NO_VAL; - linkType = @"fail"; - break; - case FBSDKAppLinkNavigationTypeBrowser: - success = EVENT_YES_VAL; - linkType = @"web"; - break; - case FBSDKAppLinkNavigationTypeApp: - success = EVENT_YES_VAL; - linkType = @"app"; - break; - default: - break; - } - if (success) { - [FBSDKTypeUtility dictionary:logData setObject:success forKey:@"success"]; - } - if (linkType) { - [FBSDKTypeUtility dictionary:logData setObject:linkType forKey:@"type"]; - } + [self postAppLinkNavigateEventNotificationWithTargetURL:openedURL + error:error ? *error : nil + type:retType]; + return retType; +} - if (self.appLink.backToReferrer) { - [FBSDKMeasurementEvent postNotificationForEventName:FBSDKAppLinkNavigateBackToReferrerEventName args:logData]; - } else { - [FBSDKMeasurementEvent postNotificationForEventName:FBSDKAppLinkNavigateOutEventName args:logData]; - } +- (void)postAppLinkNavigateEventNotificationWithTargetURL:(NSURL *)outputURL error:(NSError *)error type:(FBSDKAppLinkNavigationType)type +{ + NSString *const EVENT_YES_VAL = @"1"; + NSString *const EVENT_NO_VAL = @"0"; + NSMutableDictionary *logData = + [[NSMutableDictionary alloc] init]; + + NSString *outputURLScheme = outputURL.scheme; + NSString *outputURLString = outputURL.absoluteString; + if (outputURLScheme) { + [FBSDKTypeUtility dictionary:logData setObject:outputURLScheme forKey:@"outputURLScheme"]; + } + if (outputURLString) { + [FBSDKTypeUtility dictionary:logData setObject:outputURLString forKey:@"outputURL"]; + } + + NSString *sourceURLString = self.appLink.sourceURL.absoluteString; + NSString *sourceURLHost = self.appLink.sourceURL.host; + NSString *sourceURLScheme = self.appLink.sourceURL.scheme; + if (sourceURLString) { + [FBSDKTypeUtility dictionary:logData setObject:sourceURLString forKey:@"sourceURL"]; + } + if (sourceURLHost) { + [FBSDKTypeUtility dictionary:logData setObject:sourceURLHost forKey:@"sourceHost"]; + } + if (sourceURLScheme) { + [FBSDKTypeUtility dictionary:logData setObject:sourceURLScheme forKey:@"sourceScheme"]; + } + if (error.localizedDescription) { + [FBSDKTypeUtility dictionary:logData setObject:error.localizedDescription forKey:@"error"]; + } + NSString *success = nil; // no + NSString *linkType = nil; // unknown; + switch (type) { + case FBSDKAppLinkNavigationTypeFailure: + success = EVENT_NO_VAL; + linkType = @"fail"; + break; + case FBSDKAppLinkNavigationTypeBrowser: + success = EVENT_YES_VAL; + linkType = @"web"; + break; + case FBSDKAppLinkNavigationTypeApp: + success = EVENT_YES_VAL; + linkType = @"app"; + break; + default: + break; + } + if (success) { + [FBSDKTypeUtility dictionary:logData setObject:success forKey:@"success"]; + } + if (linkType) { + [FBSDKTypeUtility dictionary:logData setObject:linkType forKey:@"type"]; + } + + if (self.appLink.backToReferrer) { + [FBSDKMeasurementEvent postNotificationForEventName:FBSDKAppLinkNavigateBackToReferrerEventName args:logData]; + } else { + [FBSDKMeasurementEvent postNotificationForEventName:FBSDKAppLinkNavigateOutEventName args:logData]; + } } + (void)resolveAppLink:(NSURL *)destination resolver:(id)resolver - handler:(FBSDKAppLinkBlock)handler { + handler:(FBSDKAppLinkBlock)handler +{ [resolver appLinkFromURL:destination handler:handler]; } -+ (void)resolveAppLink:(NSURL *)destination handler:(FBSDKAppLinkBlock)handler { ++ (void)resolveAppLink:(NSURL *)destination handler:(FBSDKAppLinkBlock)handler +{ [self resolveAppLink:destination resolver:[self defaultResolver] handler:handler]; } -+ (void)navigateToURL:(NSURL *)destination handler:(FBSDKAppLinkNavigationBlock)handler { ++ (void)navigateToURL:(NSURL *)destination handler:(FBSDKAppLinkNavigationBlock)handler +{ [self navigateToURL:destination resolver:[self defaultResolver] handler:handler]; } + (void)navigateToURL:(NSURL *)destination resolver:(id)resolver - handler:(FBSDKAppLinkNavigationBlock)handler { - + handler:(FBSDKAppLinkNavigationBlock)handler +{ dispatch_async(dispatch_get_main_queue(), ^{ [self resolveAppLink:destination resolver:resolver - handler:^(FBSDKAppLink * _Nullable appLink, NSError * _Nullable error) { + handler:^(FBSDKAppLink *_Nullable appLink, NSError *_Nullable error) { if (error) { handler(FBSDKAppLinkNavigationTypeFailure, error); return; @@ -248,55 +257,60 @@ + (void)navigateToURL:(NSURL *)destination }); } -+ (FBSDKAppLinkNavigationType)navigateToAppLink:(FBSDKAppLink *)link error:(NSError **)error { - return [[FBSDKAppLinkNavigation navigationWithAppLink:link - extras:@{} - appLinkData:@{}] navigate:error]; ++ (FBSDKAppLinkNavigationType)navigateToAppLink:(FBSDKAppLink *)link error:(NSError **)error +{ + return [[FBSDKAppLinkNavigation navigationWithAppLink:link + extras:@{} + appLinkData:@{}] navigate:error]; } -+ (FBSDKAppLinkNavigationType)navigationTypeForLink:(FBSDKAppLink *)link { - return [[self navigationWithAppLink:link extras:@{} appLinkData:@{}] navigationType]; ++ (FBSDKAppLinkNavigationType)navigationTypeForLink:(FBSDKAppLink *)link +{ + return [[self navigationWithAppLink:link extras:@{} appLinkData:@{}] navigationType]; } -- (FBSDKAppLinkNavigationType)navigationType { - FBSDKAppLinkTarget *eligibleTarget = nil; - for (FBSDKAppLinkTarget *target in self.appLink.targets) { - if ([[UIApplication sharedApplication] canOpenURL:target.URL]) { - eligibleTarget = target; - break; - } +- (FBSDKAppLinkNavigationType)navigationType +{ + FBSDKAppLinkTarget *eligibleTarget = nil; + for (FBSDKAppLinkTarget *target in self.appLink.targets) { + if ([[UIApplication sharedApplication] canOpenURL:target.URL]) { + eligibleTarget = target; + break; } + } - if (eligibleTarget != nil) { - NSURL *appLinkURL = [self appLinkURLWithTargetURL:eligibleTarget.URL error:nil]; - if (appLinkURL != nil) { - return FBSDKAppLinkNavigationTypeApp; - } else { - return FBSDKAppLinkNavigationTypeFailure; - } + if (eligibleTarget != nil) { + NSURL *appLinkURL = [self appLinkURLWithTargetURL:eligibleTarget.URL error:nil]; + if (appLinkURL != nil) { + return FBSDKAppLinkNavigationTypeApp; + } else { + return FBSDKAppLinkNavigationTypeFailure; } + } - if (self.appLink.webURL != nil) { - NSURL *appLinkURL = [self appLinkURLWithTargetURL:eligibleTarget.URL error:nil]; - if (appLinkURL != nil) { - return FBSDKAppLinkNavigationTypeBrowser; - } else { - return FBSDKAppLinkNavigationTypeFailure; - } + if (self.appLink.webURL != nil) { + NSURL *appLinkURL = [self appLinkURLWithTargetURL:eligibleTarget.URL error:nil]; + if (appLinkURL != nil) { + return FBSDKAppLinkNavigationTypeBrowser; + } else { + return FBSDKAppLinkNavigationTypeFailure; } + } - return FBSDKAppLinkNavigationTypeFailure; + return FBSDKAppLinkNavigationTypeFailure; } -+ (id)defaultResolver { - if (defaultResolver) { - return defaultResolver; - } - return [FBSDKWebViewAppLinkResolver sharedInstance]; ++ (id)defaultResolver +{ + if (defaultResolver) { + return defaultResolver; + } + return [FBSDKWebViewAppLinkResolver sharedInstance]; } -+ (void)setDefaultResolver:(id)resolver { - defaultResolver = resolver; ++ (void)setDefaultResolver:(id)resolver +{ + defaultResolver = resolver; } @end diff --git a/FBSDKCoreKit/FBSDKCoreKit/AppLink/FBSDKAppLinkReturnToRefererController.m b/FBSDKCoreKit/FBSDKCoreKit/AppLink/FBSDKAppLinkReturnToRefererController.m index 39ceae629c..ab6f4b3f0f 100644 --- a/FBSDKCoreKit/FBSDKCoreKit/AppLink/FBSDKAppLinkReturnToRefererController.m +++ b/FBSDKCoreKit/FBSDKCoreKit/AppLink/FBSDKAppLinkReturnToRefererController.m @@ -20,227 +20,246 @@ #if !TARGET_OS_TV -#import "FBSDKAppLinkReturnToRefererController.h" + #import "FBSDKAppLinkReturnToRefererController.h" -#import "FBSDKAppLink.h" -#import "FBSDKAppLinkReturnToRefererView_Internal.h" -#import "FBSDKURL_Internal.h" + #import "FBSDKAppLink.h" + #import "FBSDKAppLinkReturnToRefererView_Internal.h" + #import "FBSDKURL_Internal.h" static const CFTimeInterval kFBSDKViewAnimationDuration = 0.25f; -@implementation FBSDKAppLinkReturnToRefererController { - UINavigationController *_navigationController; - FBSDKAppLinkReturnToRefererView *_view; +@implementation FBSDKAppLinkReturnToRefererController +{ + UINavigationController *_navigationController; + FBSDKAppLinkReturnToRefererView *_view; } -#pragma mark - Object lifecycle + #pragma mark - Object lifecycle -- (instancetype)init { +- (instancetype)init +{ return [super init]; } -#pragma clang diagnostic push -#pragma clang diagnostic ignored "-Wdeprecated-declarations" -- (instancetype)initForDisplayAboveNavController:(UINavigationController *)navController { - self = [self init]; - if (self) { - _navigationController = navController; - - if (_navigationController != nil) { - NSNotificationCenter *nc = [NSNotificationCenter defaultCenter]; - [nc addObserver:self - selector:@selector(statusBarFrameWillChange:) - name:UIApplicationWillChangeStatusBarFrameNotification - object:nil]; - [nc addObserver:self - selector:@selector(statusBarFrameDidChange:) - name:UIApplicationDidChangeStatusBarFrameNotification - object:nil]; - [nc addObserver:self - selector:@selector(orientationDidChange:) - name:UIDeviceOrientationDidChangeNotification - object:nil]; - } + #pragma clang diagnostic push + #pragma clang diagnostic ignored "-Wdeprecated-declarations" +- (instancetype)initForDisplayAboveNavController:(UINavigationController *)navController +{ + self = [self init]; + if (self) { + _navigationController = navController; + + if (_navigationController != nil) { + NSNotificationCenter *nc = [NSNotificationCenter defaultCenter]; + [nc addObserver:self + selector:@selector(statusBarFrameWillChange:) + name:UIApplicationWillChangeStatusBarFrameNotification + object:nil]; + [nc addObserver:self + selector:@selector(statusBarFrameDidChange:) + name:UIApplicationDidChangeStatusBarFrameNotification + object:nil]; + [nc addObserver:self + selector:@selector(orientationDidChange:) + name:UIDeviceOrientationDidChangeNotification + object:nil]; } - return self; + } + return self; } -- (void)dealloc { - _view.delegate = nil; - [[NSNotificationCenter defaultCenter] removeObserver:self]; +- (void)dealloc +{ + _view.delegate = nil; + [[NSNotificationCenter defaultCenter] removeObserver:self]; } -#pragma mark - Public API + #pragma mark - Public API -- (FBSDKAppLinkReturnToRefererView *)view { - if (!_view) { - self.view = [[FBSDKAppLinkReturnToRefererView alloc] initWithFrame:CGRectZero]; - if (_navigationController) { - [_navigationController.view addSubview:_view]; - } +- (FBSDKAppLinkReturnToRefererView *)view +{ + if (!_view) { + self.view = [[FBSDKAppLinkReturnToRefererView alloc] initWithFrame:CGRectZero]; + if (_navigationController) { + [_navigationController.view addSubview:_view]; } - return _view; + } + return _view; } -- (void)setView:(FBSDKAppLinkReturnToRefererView *)view { - if (_view != view) { - _view.delegate = nil; - } +- (void)setView:(FBSDKAppLinkReturnToRefererView *)view +{ + if (_view != view) { + _view.delegate = nil; + } - _view = view; - _view.delegate = self; + _view = view; + _view.delegate = self; - if (_navigationController) { - _view.includeStatusBarInSize = FBSDKIncludeStatusBarInSizeAlways; - } + if (_navigationController) { + _view.includeStatusBarInSize = FBSDKIncludeStatusBarInSizeAlways; + } } -- (void)showViewForRefererAppLink:(FBSDKAppLink *)refererAppLink { - self.view.refererAppLink = refererAppLink; +- (void)showViewForRefererAppLink:(FBSDKAppLink *)refererAppLink +{ + self.view.refererAppLink = refererAppLink; - [_view sizeToFit]; + [_view sizeToFit]; - if (_navigationController) { - if (!_view.closed) { - dispatch_async(dispatch_get_main_queue(), ^{ - [self moveNavigationBar]; - }); - } + if (_navigationController) { + if (!_view.closed) { + dispatch_async(dispatch_get_main_queue(), ^{ + [self moveNavigationBar]; + }); } + } } -- (void)showViewForRefererURL:(NSURL *)url { - FBSDKAppLink *appLink = [FBSDKURL URLForRenderBackToReferrerBarURL:url].appLinkReferer; - [self showViewForRefererAppLink:appLink]; +- (void)showViewForRefererURL:(NSURL *)url +{ + FBSDKAppLink *appLink = [FBSDKURL URLForRenderBackToReferrerBarURL:url].appLinkReferer; + [self showViewForRefererAppLink:appLink]; } -- (void)removeFromNavController { - if (_navigationController) { - [_view removeFromSuperview]; - _navigationController = nil; - } +- (void)removeFromNavController +{ + if (_navigationController) { + [_view removeFromSuperview]; + _navigationController = nil; + } } -#pragma mark - FBSDKAppLinkReturnToRefererViewDelegate + #pragma mark - FBSDKAppLinkReturnToRefererViewDelegate -- (void)returnToRefererViewDidTapInsideCloseButton:(FBSDKAppLinkReturnToRefererView *)view { - [self closeViewAnimated:YES explicitlyClosed:YES]; +- (void)returnToRefererViewDidTapInsideCloseButton:(FBSDKAppLinkReturnToRefererView *)view +{ + [self closeViewAnimated:YES explicitlyClosed:YES]; } - (void)returnToRefererViewDidTapInsideLink:(FBSDKAppLinkReturnToRefererView *)view - link:(FBSDKAppLink *)link { - [self openRefererAppLink:link]; - [self closeViewAnimated:NO explicitlyClosed:NO]; + link:(FBSDKAppLink *)link +{ + [self openRefererAppLink:link]; + [self closeViewAnimated:NO explicitlyClosed:NO]; } -#pragma mark - Private + #pragma mark - Private -- (void)statusBarFrameWillChange:(NSNotification *)notification { - NSValue *rectValue = [notification.userInfo valueForKey:UIApplicationStatusBarFrameUserInfoKey]; - CGRect newFrame; - [rectValue getValue:&newFrame]; +- (void)statusBarFrameWillChange:(NSNotification *)notification +{ + NSValue *rectValue = [notification.userInfo valueForKey:UIApplicationStatusBarFrameUserInfoKey]; + CGRect newFrame; + [rectValue getValue:&newFrame]; - if (_navigationController && !_view.closed) { - if (CGRectGetHeight(newFrame) == 40) { - UIViewAnimationOptions options = UIViewAnimationOptionBeginFromCurrentState; - [UIView animateWithDuration:kFBSDKViewAnimationDuration delay:0.0 options:options animations:^{ - self->_view.frame = CGRectMake(0.0, 0.0, CGRectGetWidth(self->_view.bounds), 0.0); - } completion:nil]; - } + if (_navigationController && !_view.closed) { + if (CGRectGetHeight(newFrame) == 40) { + UIViewAnimationOptions options = UIViewAnimationOptionBeginFromCurrentState; + [UIView animateWithDuration:kFBSDKViewAnimationDuration delay:0.0 options:options animations:^{ + self->_view.frame = CGRectMake(0.0, 0.0, CGRectGetWidth(self->_view.bounds), 0.0); + } completion:nil]; } + } } -- (void)statusBarFrameDidChange:(NSNotification *)notification { - NSValue *rectValue = [notification.userInfo valueForKey:UIApplicationStatusBarFrameUserInfoKey]; - CGRect newFrame; - [rectValue getValue:&newFrame]; - - if (_navigationController && !_view.closed) { - if (CGRectGetHeight(newFrame) == 40) { - UIViewAnimationOptions options = UIViewAnimationOptionBeginFromCurrentState; - [UIView animateWithDuration:kFBSDKViewAnimationDuration delay:0.0 options:options animations:^{ - [self->_view sizeToFit]; - [self moveNavigationBar]; - } completion:nil]; - } +- (void)statusBarFrameDidChange:(NSNotification *)notification +{ + NSValue *rectValue = [notification.userInfo valueForKey:UIApplicationStatusBarFrameUserInfoKey]; + CGRect newFrame; + [rectValue getValue:&newFrame]; + + if (_navigationController && !_view.closed) { + if (CGRectGetHeight(newFrame) == 40) { + UIViewAnimationOptions options = UIViewAnimationOptionBeginFromCurrentState; + [UIView animateWithDuration:kFBSDKViewAnimationDuration delay:0.0 options:options animations:^{ + [self->_view sizeToFit]; + [self moveNavigationBar]; + } completion:nil]; } + } } -- (void)orientationDidChange:(NSNotificationCenter *)notification { - if (_navigationController && !_view.closed && CGRectGetHeight(_view.bounds) > 0) { - dispatch_async(dispatch_get_main_queue(), ^{ - [self moveNavigationBar]; - }); - } +- (void)orientationDidChange:(NSNotificationCenter *)notification +{ + if (_navigationController && !_view.closed && CGRectGetHeight(_view.bounds) > 0) { + dispatch_async(dispatch_get_main_queue(), ^{ + [self moveNavigationBar]; + }); + } } -- (void)moveNavigationBar { - if (_view.closed || !_view.refererAppLink) { - return; +- (void)moveNavigationBar +{ + if (_view.closed || !_view.refererAppLink) { + return; + } + + [self updateNavigationBarY:CGRectGetHeight(_view.bounds)]; +} + +- (void)updateNavigationBarY:(CGFloat)y +{ + UINavigationBar *navigationBar = _navigationController.navigationBar; + CGRect navigationBarFrame = navigationBar.frame; + CGFloat oldContainerViewY = CGRectGetMaxY(navigationBarFrame); + navigationBarFrame.origin.y = y; + navigationBar.frame = navigationBarFrame; + + CGFloat dy = CGRectGetMaxY(navigationBarFrame) - oldContainerViewY; + UIView *containerView = _navigationController.visibleViewController.view.superview; + containerView.frame = UIEdgeInsetsInsetRect(containerView.frame, UIEdgeInsetsMake(dy, 0.0, 0.0, 0.0)); +} + +- (void)closeViewAnimated:(BOOL)animated +{ + [self closeViewAnimated:animated explicitlyClosed:YES]; +} + +- (void)closeViewAnimated:(BOOL)animated explicitlyClosed:(BOOL)explicitlyClosed +{ + void (^closer)(void) = ^{ + if (self->_navigationController) { + [self updateNavigationBarY:self->_view.statusBarHeight]; } - [self updateNavigationBarY:CGRectGetHeight(_view.bounds)]; -} - -- (void)updateNavigationBarY:(CGFloat)y { - UINavigationBar *navigationBar = _navigationController.navigationBar; - CGRect navigationBarFrame = navigationBar.frame; - CGFloat oldContainerViewY = CGRectGetMaxY(navigationBarFrame); - navigationBarFrame.origin.y = y; - navigationBar.frame = navigationBarFrame; - - CGFloat dy = CGRectGetMaxY(navigationBarFrame) - oldContainerViewY; - UIView *containerView = _navigationController.visibleViewController.view.superview; - containerView.frame = UIEdgeInsetsInsetRect(containerView.frame, UIEdgeInsetsMake(dy, 0.0, 0.0, 0.0)); -} - -- (void)closeViewAnimated:(BOOL)animated { - [self closeViewAnimated:animated explicitlyClosed:YES]; -} - -- (void)closeViewAnimated:(BOOL)animated explicitlyClosed:(BOOL)explicitlyClosed { - void (^closer)(void) = ^{ - if (self->_navigationController) { - [self updateNavigationBarY:self->_view.statusBarHeight]; - } - - CGRect frame = self->_view.frame; - frame.size.height = 0.0; - self->_view.frame = frame; - }; - - if (animated) { - [UIView animateWithDuration:kFBSDKViewAnimationDuration animations:^{ - closer(); - } completion:^(BOOL finished) { - if (explicitlyClosed) { - self->_view.closed = YES; - } - }]; - } else { - closer(); - if (explicitlyClosed) { - self->_view.closed = YES; - } + CGRect frame = self->_view.frame; + frame.size.height = 0.0; + self->_view.frame = frame; + }; + + if (animated) { + [UIView animateWithDuration:kFBSDKViewAnimationDuration animations:^{ + closer(); + } completion:^(BOOL finished) { + if (explicitlyClosed) { + self->_view.closed = YES; + } + }]; + } else { + closer(); + if (explicitlyClosed) { + self->_view.closed = YES; } + } } -- (void)openRefererAppLink:(FBSDKAppLink *)refererAppLink { - if (refererAppLink) { - id delegate = _delegate; - if ([delegate respondsToSelector:@selector(returnToRefererController:willNavigateToAppLink:)]) { - [delegate returnToRefererController:self willNavigateToAppLink:refererAppLink]; - } +- (void)openRefererAppLink:(FBSDKAppLink *)refererAppLink +{ + if (refererAppLink) { + id delegate = _delegate; + if ([delegate respondsToSelector:@selector(returnToRefererController:willNavigateToAppLink:)]) { + [delegate returnToRefererController:self willNavigateToAppLink:refererAppLink]; + } - NSError *error = nil; - FBSDKAppLinkNavigationType type = [FBSDKAppLinkNavigation navigateToAppLink:refererAppLink error:&error]; + NSError *error = nil; + FBSDKAppLinkNavigationType type = [FBSDKAppLinkNavigation navigateToAppLink:refererAppLink error:&error]; - if ([delegate respondsToSelector:@selector(returnToRefererController:didNavigateToAppLink:type:)]) { - [delegate returnToRefererController:self didNavigateToAppLink:refererAppLink type:type]; - } + if ([delegate respondsToSelector:@selector(returnToRefererController:didNavigateToAppLink:type:)]) { + [delegate returnToRefererController:self didNavigateToAppLink:refererAppLink type:type]; } + } } @end -#pragma clang diagnostic pop + #pragma clang diagnostic pop #endif diff --git a/FBSDKCoreKit/FBSDKCoreKit/AppLink/FBSDKAppLinkReturnToRefererView.m b/FBSDKCoreKit/FBSDKCoreKit/AppLink/FBSDKAppLinkReturnToRefererView.m index 98fe95e1ba..ca55cf18ee 100644 --- a/FBSDKCoreKit/FBSDKCoreKit/AppLink/FBSDKAppLinkReturnToRefererView.m +++ b/FBSDKCoreKit/FBSDKCoreKit/AppLink/FBSDKAppLinkReturnToRefererView.m @@ -20,11 +20,11 @@ #if !TARGET_OS_TV -#import "FBSDKAppLinkReturnToRefererView.h" + #import "FBSDKAppLinkReturnToRefererView.h" -#import "FBSDKAppLink.h" -#import "FBSDKAppLinkTarget.h" -#import "FBSDKInternalUtility.h" + #import "FBSDKAppLink.h" + #import "FBSDKAppLinkTarget.h" + #import "FBSDKInternalUtility.h" static const CGFloat FBSDKMarginX = 8.5f; static const CGFloat FBSDKMarginY = 8.5f; @@ -43,232 +43,258 @@ @interface FBSDKAppLinkReturnToRefererView () @end -@implementation FBSDKAppLinkReturnToRefererView { - BOOL _explicitlyHidden; +@implementation FBSDKAppLinkReturnToRefererView +{ + BOOL _explicitlyHidden; } -#pragma mark - Initialization + #pragma mark - Initialization -- (instancetype)initWithFrame:(CGRect)frame { - self = [super initWithFrame:frame]; - if (self) { - [self commonInit]; - [self sizeToFit]; - } - return self; +- (instancetype)initWithFrame:(CGRect)frame +{ + self = [super initWithFrame:frame]; + if (self) { + [self commonInit]; + [self sizeToFit]; + } + return self; } -- (instancetype)initWithCoder:(NSCoder *)aDecoder { - self = [super initWithCoder:aDecoder]; - if (self) { - [self commonInit]; - } - return self; +- (instancetype)initWithCoder:(NSCoder *)aDecoder +{ + self = [super initWithCoder:aDecoder]; + if (self) { + [self commonInit]; + } + return self; } -- (void)commonInit { - // Initialization code - _includeStatusBarInSize = FBSDKIncludeStatusBarInSizeAlways; +- (void)commonInit +{ + // Initialization code + _includeStatusBarInSize = FBSDKIncludeStatusBarInSizeAlways; - // iOS 7 system blue color - self.backgroundColor = [UIColor colorWithRed:0.0f green:122.0f / 255.0f blue:1.0f alpha:1.0f]; - self.textColor = [UIColor whiteColor]; - self.clipsToBounds = YES; + // iOS 7 system blue color + self.backgroundColor = [UIColor colorWithRed:0.0f green:122.0f / 255.0f blue:1.0f alpha:1.0f]; + self.textColor = [UIColor whiteColor]; + self.clipsToBounds = YES; - [self initViews]; + [self initViews]; } -- (void)initViews { - if (!_labelView && !_closeButton) { - _closeButton = [UIButton buttonWithType:UIButtonTypeCustom]; - _closeButton.backgroundColor = [UIColor clearColor]; - _closeButton.userInteractionEnabled = YES; - _closeButton.clipsToBounds = YES; - _closeButton.autoresizingMask = UIViewAutoresizingFlexibleLeftMargin | UIViewAutoresizingFlexibleTopMargin; - _closeButton.contentMode = UIViewContentModeCenter; - [_closeButton addTarget:self action:@selector(closeButtonTapped:) forControlEvents:UIControlEventTouchUpInside]; - - [self addSubview:_closeButton]; - - _labelView = [[UILabel alloc] initWithFrame:CGRectZero]; - _labelView.font = [UIFont systemFontOfSize:[UIFont smallSystemFontSize]]; - _labelView.textColor = [UIColor whiteColor]; - _labelView.backgroundColor = [UIColor clearColor]; - _labelView.textAlignment = NSTextAlignmentCenter; - _labelView.clipsToBounds = YES; - [self updateLabelText]; - [self addSubview:_labelView]; - - _insideTapGestureRecognizer = [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(onTapInside:)]; - _labelView.userInteractionEnabled = YES; - [_labelView addGestureRecognizer:_insideTapGestureRecognizer]; - - [self updateColors]; - } -} - -#pragma mark - Layout - -- (CGSize)intrinsicContentSize { - CGSize size = self.bounds.size; - if (_closed || !self.hasRefererData) { - size.height = 0.0; - } else { - CGSize labelSize = [_labelView sizeThatFits:size]; - size = CGSizeMake(size.width, labelSize.height + 2 * FBSDKMarginY + self.statusBarHeight); - } - return size; -} +- (void)initViews +{ + if (!_labelView && !_closeButton) { + _closeButton = [UIButton buttonWithType:UIButtonTypeCustom]; + _closeButton.backgroundColor = [UIColor clearColor]; + _closeButton.userInteractionEnabled = YES; + _closeButton.clipsToBounds = YES; + _closeButton.autoresizingMask = UIViewAutoresizingFlexibleLeftMargin | UIViewAutoresizingFlexibleTopMargin; + _closeButton.contentMode = UIViewContentModeCenter; + [_closeButton addTarget:self action:@selector(closeButtonTapped:) forControlEvents:UIControlEventTouchUpInside]; + + [self addSubview:_closeButton]; + + _labelView = [[UILabel alloc] initWithFrame:CGRectZero]; + _labelView.font = [UIFont systemFontOfSize:[UIFont smallSystemFontSize]]; + _labelView.textColor = [UIColor whiteColor]; + _labelView.backgroundColor = [UIColor clearColor]; + _labelView.textAlignment = NSTextAlignmentCenter; + _labelView.clipsToBounds = YES; + [self updateLabelText]; + [self addSubview:_labelView]; -- (void)layoutSubviews { - [super layoutSubviews]; + _insideTapGestureRecognizer = [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(onTapInside:)]; + _labelView.userInteractionEnabled = YES; + [_labelView addGestureRecognizer:_insideTapGestureRecognizer]; - CGRect bounds = self.bounds; + [self updateColors]; + } +} - _labelView.preferredMaxLayoutWidth = _labelView.bounds.size.width; - CGSize labelSize = [_labelView sizeThatFits:bounds.size]; - _labelView.frame = CGRectMake(FBSDKMarginX, - CGRectGetMaxY(bounds) - labelSize.height - 1.5f * FBSDKMarginY, - CGRectGetMaxX(bounds) - FBSDKCloseButtonWidth - 3 * FBSDKMarginX, - labelSize.height + FBSDKMarginY); + #pragma mark - Layout + +- (CGSize)intrinsicContentSize +{ + CGSize size = self.bounds.size; + if (_closed || !self.hasRefererData) { + size.height = 0.0; + } else { + CGSize labelSize = [_labelView sizeThatFits:size]; + size = CGSizeMake(size.width, labelSize.height + 2 * FBSDKMarginY + self.statusBarHeight); + } + return size; +} - _closeButton.frame = CGRectMake(CGRectGetMaxX(bounds) - FBSDKCloseButtonWidth - 2 * FBSDKMarginX, - _labelView.center.y - FBSDKCloseButtonHeight / 2.0f - FBSDKMarginY, - FBSDKCloseButtonWidth + 2 * FBSDKMarginX, - FBSDKCloseButtonHeight + 2 * FBSDKMarginY); +- (void)layoutSubviews +{ + [super layoutSubviews]; + + CGRect bounds = self.bounds; + + _labelView.preferredMaxLayoutWidth = _labelView.bounds.size.width; + CGSize labelSize = [_labelView sizeThatFits:bounds.size]; + _labelView.frame = CGRectMake( + FBSDKMarginX, + CGRectGetMaxY(bounds) - labelSize.height - 1.5f * FBSDKMarginY, + CGRectGetMaxX(bounds) - FBSDKCloseButtonWidth - 3 * FBSDKMarginX, + labelSize.height + FBSDKMarginY + ); + + _closeButton.frame = CGRectMake( + CGRectGetMaxX(bounds) - FBSDKCloseButtonWidth - 2 * FBSDKMarginX, + _labelView.center.y - FBSDKCloseButtonHeight / 2.0f - FBSDKMarginY, + FBSDKCloseButtonWidth + 2 * FBSDKMarginX, + FBSDKCloseButtonHeight + 2 * FBSDKMarginY + ); } -- (CGSize)sizeThatFits:(CGSize)size { - if (_closed || !self.hasRefererData) { - size = CGSizeMake(size.width, 0.0); - } else { - CGSize labelSize = [_labelView sizeThatFits:size]; - size = CGSizeMake(size.width, labelSize.height + 2 * FBSDKMarginY + self.statusBarHeight); - } - return size; +- (CGSize)sizeThatFits:(CGSize)size +{ + if (_closed || !self.hasRefererData) { + size = CGSizeMake(size.width, 0.0); + } else { + CGSize labelSize = [_labelView sizeThatFits:size]; + size = CGSizeMake(size.width, labelSize.height + 2 * FBSDKMarginY + self.statusBarHeight); + } + return size; } -#pragma clang diagnostic push -#pragma clang diagnostic ignored "-Wdeprecated-declarations" -- (CGFloat)statusBarHeight { - UIApplication *application = [UIApplication sharedApplication]; - - BOOL include; - switch (_includeStatusBarInSize) { - case FBSDKIncludeStatusBarInSizeAlways: - include = YES; - break; - case FBSDKIncludeStatusBarInSizeNever: - include = NO; - break; - } - if (include && !application.statusBarHidden) { - BOOL landscape = UIInterfaceOrientationIsLandscape(FBSDKInternalUtility.statusBarOrientation); - CGRect statusBarFrame = application.statusBarFrame; - return landscape ? CGRectGetWidth(statusBarFrame) : CGRectGetHeight(statusBarFrame); - } - - return 0; + #pragma clang diagnostic push + #pragma clang diagnostic ignored "-Wdeprecated-declarations" +- (CGFloat)statusBarHeight +{ + UIApplication *application = [UIApplication sharedApplication]; + + BOOL include; + switch (_includeStatusBarInSize) { + case FBSDKIncludeStatusBarInSizeAlways: + include = YES; + break; + case FBSDKIncludeStatusBarInSizeNever: + include = NO; + break; + } + if (include && !application.statusBarHidden) { + BOOL landscape = UIInterfaceOrientationIsLandscape(FBSDKInternalUtility.statusBarOrientation); + CGRect statusBarFrame = application.statusBarFrame; + return landscape ? CGRectGetWidth(statusBarFrame) : CGRectGetHeight(statusBarFrame); + } + + return 0; } -#pragma clang diagnostic pop -#pragma mark - Public API + #pragma clang diagnostic pop -- (void)setIncludeStatusBarInSize:(FBSDKIncludeStatusBarInSize)includeStatusBarInSize { - _includeStatusBarInSize = includeStatusBarInSize; - [self setNeedsLayout]; - [self invalidateIntrinsicContentSize]; -} + #pragma mark - Public API -- (void)setTextColor:(UIColor *)textColor { - _textColor = textColor; - [self updateColors]; +- (void)setIncludeStatusBarInSize:(FBSDKIncludeStatusBarInSize)includeStatusBarInSize +{ + _includeStatusBarInSize = includeStatusBarInSize; + [self setNeedsLayout]; + [self invalidateIntrinsicContentSize]; } -- (void)setRefererAppLink:(FBSDKAppLink *)refererAppLink { - _refererAppLink = refererAppLink; - [self updateLabelText]; - [self updateHidden]; - [self invalidateIntrinsicContentSize]; +- (void)setTextColor:(UIColor *)textColor +{ + _textColor = textColor; + [self updateColors]; } -- (void)setClosed:(BOOL)closed { - if (_closed != closed) { - _closed = closed; - [self updateHidden]; - [self invalidateIntrinsicContentSize]; - } +- (void)setRefererAppLink:(FBSDKAppLink *)refererAppLink +{ + _refererAppLink = refererAppLink; + [self updateLabelText]; + [self updateHidden]; + [self invalidateIntrinsicContentSize]; } -- (void)setHidden:(BOOL)hidden { - _explicitlyHidden = hidden; +- (void)setClosed:(BOOL)closed +{ + if (_closed != closed) { + _closed = closed; [self updateHidden]; + [self invalidateIntrinsicContentSize]; + } } -#pragma mark - Private - -- (void)updateLabelText { - NSString *appName = (_refererAppLink && [FBSDKTypeUtility array:_refererAppLink.targets objectAtIndex:0]) ? [[FBSDKTypeUtility array:_refererAppLink.targets objectAtIndex:0] appName] : nil; - _labelView.text = [self localizedLabelForReferer:appName]; +- (void)setHidden:(BOOL)hidden +{ + _explicitlyHidden = hidden; + [self updateHidden]; } -- (void)updateColors { - UIImage *closeButtonImage = [self drawCloseButtonImageWithColor:_textColor]; + #pragma mark - Private - _labelView.textColor = _textColor; - [_closeButton setImage:closeButtonImage forState:UIControlStateNormal]; +- (void)updateLabelText +{ + NSString *appName = (_refererAppLink && [FBSDKTypeUtility array:_refererAppLink.targets objectAtIndex:0]) ? [[FBSDKTypeUtility array:_refererAppLink.targets objectAtIndex:0] appName] : nil; + _labelView.text = [self localizedLabelForReferer:appName]; } -- (UIImage *)drawCloseButtonImageWithColor:(UIColor *)color { +- (void)updateColors +{ + UIImage *closeButtonImage = [self drawCloseButtonImageWithColor:_textColor]; + + _labelView.textColor = _textColor; + [_closeButton setImage:closeButtonImage forState:UIControlStateNormal]; +} - UIGraphicsBeginImageContextWithOptions(CGSizeMake(FBSDKCloseButtonWidth, FBSDKCloseButtonHeight), NO, 0.0f); +- (UIImage *)drawCloseButtonImageWithColor:(UIColor *)color +{ + UIGraphicsBeginImageContextWithOptions(CGSizeMake(FBSDKCloseButtonWidth, FBSDKCloseButtonHeight), NO, 0.0f); - CGContextRef context = UIGraphicsGetCurrentContext(); + CGContextRef context = UIGraphicsGetCurrentContext(); - CGContextSetStrokeColorWithColor(context, color.CGColor); - CGContextSetFillColorWithColor(context, color.CGColor); + CGContextSetStrokeColorWithColor(context, color.CGColor); + CGContextSetFillColorWithColor(context, color.CGColor); - CGContextSetLineWidth(context, 1.25f); + CGContextSetLineWidth(context, 1.25f); - CGFloat inset = 0.5f; + CGFloat inset = 0.5f; - CGContextMoveToPoint(context, inset, inset); - CGContextAddLineToPoint(context, FBSDKCloseButtonWidth - inset, FBSDKCloseButtonHeight - inset); - CGContextStrokePath(context); + CGContextMoveToPoint(context, inset, inset); + CGContextAddLineToPoint(context, FBSDKCloseButtonWidth - inset, FBSDKCloseButtonHeight - inset); + CGContextStrokePath(context); - CGContextMoveToPoint(context, FBSDKCloseButtonWidth - inset, inset); - CGContextAddLineToPoint(context, inset, FBSDKCloseButtonHeight - inset); - CGContextStrokePath(context); + CGContextMoveToPoint(context, FBSDKCloseButtonWidth - inset, inset); + CGContextAddLineToPoint(context, inset, FBSDKCloseButtonHeight - inset); + CGContextStrokePath(context); - UIImage *result = UIGraphicsGetImageFromCurrentImageContext(); - UIGraphicsEndImageContext(); + UIImage *result = UIGraphicsGetImageFromCurrentImageContext(); + UIGraphicsEndImageContext(); - return result; + return result; } -- (NSString *)localizedLabelForReferer:(NSString *)refererName { - if (!refererName) { - return nil; - } +- (NSString *)localizedLabelForReferer:(NSString *)refererName +{ + if (!refererName) { + return nil; + } - NSString *format = NSLocalizedString(@"Touch to return to %1$@", @"Format for the string to return to a calling app."); - return [NSString stringWithFormat:format, refererName]; + NSString *format = NSLocalizedString(@"Touch to return to %1$@", @"Format for the string to return to a calling app."); + return [NSString stringWithFormat:format, refererName]; } -- (BOOL)hasRefererData { - return _refererAppLink && [FBSDKTypeUtility array:_refererAppLink.targets objectAtIndex:0]; +- (BOOL)hasRefererData +{ + return _refererAppLink && [FBSDKTypeUtility array:_refererAppLink.targets objectAtIndex:0]; } -- (void)closeButtonTapped:(id)sender { - [_delegate returnToRefererViewDidTapInsideCloseButton:self]; +- (void)closeButtonTapped:(id)sender +{ + [_delegate returnToRefererViewDidTapInsideCloseButton:self]; } -- (void)onTapInside:(UIGestureRecognizer *)sender { - [_delegate returnToRefererViewDidTapInsideLink:self link:_refererAppLink]; +- (void)onTapInside:(UIGestureRecognizer *)sender +{ + [_delegate returnToRefererViewDidTapInsideLink:self link:_refererAppLink]; } -- (void)updateHidden { - super.hidden = _explicitlyHidden || _closed || !self.hasRefererData; +- (void)updateHidden +{ + super.hidden = _explicitlyHidden || _closed || !self.hasRefererData; } @end diff --git a/FBSDKCoreKit/FBSDKCoreKit/AppLink/FBSDKAppLinkTarget.m b/FBSDKCoreKit/FBSDKCoreKit/AppLink/FBSDKAppLinkTarget.m index 0a98717c1e..7bc14d59dd 100644 --- a/FBSDKCoreKit/FBSDKCoreKit/AppLink/FBSDKAppLinkTarget.m +++ b/FBSDKCoreKit/FBSDKCoreKit/AppLink/FBSDKAppLinkTarget.m @@ -20,7 +20,7 @@ #if !TARGET_OS_TV -#import "FBSDKAppLinkTarget.h" + #import "FBSDKAppLinkTarget.h" @interface FBSDKAppLinkTarget () @@ -34,12 +34,13 @@ @implementation FBSDKAppLinkTarget + (instancetype)appLinkTargetWithURL:(NSURL *)url appStoreId:(NSString *)appStoreId - appName:(NSString *)appName { - FBSDKAppLinkTarget *target = [[self alloc] init]; - target.URL = url; - target.appStoreId = appStoreId; - target.appName = appName; - return target; + appName:(NSString *)appName +{ + FBSDKAppLinkTarget *target = [[self alloc] init]; + target.URL = url; + target.appStoreId = appStoreId; + target.appName = appName; + return target; } @end diff --git a/FBSDKCoreKit/FBSDKCoreKit/AppLink/FBSDKAppLinkUtility.m b/FBSDKCoreKit/FBSDKCoreKit/AppLink/FBSDKAppLinkUtility.m index 5944c567ff..9aabfbc338 100644 --- a/FBSDKCoreKit/FBSDKCoreKit/AppLink/FBSDKAppLinkUtility.m +++ b/FBSDKCoreKit/FBSDKCoreKit/AppLink/FBSDKAppLinkUtility.m @@ -20,24 +20,33 @@ #if !TARGET_OS_TV -#import "FBSDKAppLinkUtility.h" + #import "FBSDKAppLinkUtility.h" -#import "FBSDKAppEventsUtility.h" -#import "FBSDKGraphRequest.h" -#import "FBSDKInternalUtility.h" -#import "FBSDKSettings.h" -#import "FBSDKURL.h" -#import "FBSDKUtility.h" + #import "FBSDKAppEventsUtility.h" + #import "FBSDKGraphRequest.h" + #import "FBSDKInternalUtility.h" + #import "FBSDKSettings.h" + #import "FBSDKURL.h" + #import "FBSDKUtility.h" static NSString *const FBSDKLastDeferredAppLink = @"com.facebook.sdk:lastDeferredAppLink%@"; static NSString *const FBSDKDeferredAppLinkEvent = @"DEFERRED_APP_LINK"; -@implementation FBSDKAppLinkUtility {} +@implementation FBSDKAppLinkUtility +{} + (void)fetchDeferredAppLink:(FBSDKURLBlock)handler { NSAssert([NSThread isMainThread], @"FBSDKAppLink fetchDeferredAppLink: must be invoked from main thread."); + if ([FBSDKAppEventsUtility shouldDropAppEvent]) { + if (handler) { + NSError *error = [[NSError alloc] initWithDomain:@"AdvertiserTrackingEnabled must be enabled" code:-1 userInfo:nil]; + handler(nil, error); + } + return; + } + NSString *appID = [FBSDKSettings appID]; // Deferred app links are only currently used for engagement ads, thus we consider the app to be an advertising one. @@ -56,30 +65,30 @@ + (void)fetchDeferredAppLink:(FBSDKURLBlock)handler [deferredAppLinkRequest startWithCompletionHandler:^(FBSDKGraphRequestConnection *connection, id result, NSError *error) { - NSURL *applinkURL = nil; - if (!error) { - NSString *appLinkString = result[@"applink_url"]; - if (appLinkString) { - applinkURL = [NSURL URLWithString:appLinkString]; - - NSString *createTimeUtc = result[@"click_time"]; - if (createTimeUtc) { - // append/translate the create_time_utc so it can be used by clients - NSString *modifiedURLString = [applinkURL.absoluteString - stringByAppendingFormat:@"%@fb_click_time_utc=%@", - (applinkURL.query) ? @"&" : @"?" , - createTimeUtc]; - applinkURL = [NSURL URLWithString:modifiedURLString]; - } - } - } - - if (handler) { - dispatch_async(dispatch_get_main_queue(), ^{ - handler(applinkURL, error); - }); - } - }]; + NSURL *applinkURL = nil; + if (!error) { + NSString *appLinkString = result[@"applink_url"]; + if (appLinkString) { + applinkURL = [NSURL URLWithString:appLinkString]; + + NSString *createTimeUtc = result[@"click_time"]; + if (createTimeUtc) { + // append/translate the create_time_utc so it can be used by clients + NSString *modifiedURLString = [applinkURL.absoluteString + stringByAppendingFormat:@"%@fb_click_time_utc=%@", + (applinkURL.query) ? @"&" : @"?", + createTimeUtc]; + applinkURL = [NSURL URLWithString:modifiedURLString]; + } + } + } + + if (handler) { + dispatch_async(dispatch_get_main_queue(), ^{ + handler(applinkURL, error); + }); + } + }]; } + (NSString *)appInvitePromotionCodeFromURL:(NSURL *)url @@ -100,7 +109,6 @@ + (NSString *)appInvitePromotionCodeFromURL:(NSURL *)url } return nil; - } + (BOOL)isMatchURLScheme:(NSString *)scheme @@ -108,10 +116,9 @@ + (BOOL)isMatchURLScheme:(NSString *)scheme if (!scheme) { return NO; } - for(NSDictionary *urlType in [[NSBundle mainBundle] objectForInfoDictionaryKey:@"CFBundleURLTypes"]) - { - for(NSString *urlScheme in urlType[@"CFBundleURLSchemes"]) { - if([urlScheme caseInsensitiveCompare:scheme] == NSOrderedSame) { + for (NSDictionary *urlType in [[NSBundle mainBundle] objectForInfoDictionaryKey:@"CFBundleURLTypes"]) { + for (NSString *urlScheme in urlType[@"CFBundleURLSchemes"]) { + if ([urlScheme caseInsensitiveCompare:scheme] == NSOrderedSame) { return YES; } } diff --git a/FBSDKCoreKit/FBSDKCoreKit/AppLink/FBSDKWebViewAppLinkResolver.m b/FBSDKCoreKit/FBSDKCoreKit/AppLink/FBSDKWebViewAppLinkResolver.m index 18a60f9bec..3faf0ef56d 100644 --- a/FBSDKCoreKit/FBSDKCoreKit/AppLink/FBSDKWebViewAppLinkResolver.m +++ b/FBSDKCoreKit/FBSDKCoreKit/AppLink/FBSDKWebViewAppLinkResolver.m @@ -27,7 +27,7 @@ #import "FBSDKAppLink.h" #import "FBSDKAppLinkTarget.h" -#import "FBSDKTypeUtility.h" +#import "FBSDKInternalUtility.h" /** Describes the callback for appLinkFromURLInBackground. @@ -188,17 +188,10 @@ - (void)appLinkFromURL:(NSURL *)url handler:(FBSDKAppLinkBlock)handler }; webView.navigationDelegate = listener; webView.hidden = YES; - if (@available(iOS 9.0, *)) { - [webView loadData:responseData - MIMEType:response.MIMEType - characterEncodingName:response.textEncodingName - baseURL:response.URL]; - } else { - NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:url]; - [request setValue:FBSDKWebViewAppLinkResolverMetaTagPrefix forHTTPHeaderField:FBSDKWebViewAppLinkResolverPreferHeader]; - [webView loadRequest:request]; - } - + [webView loadData:responseData + MIMEType:response.MIMEType + characterEncodingName:response.textEncodingName + baseURL:response.URL]; UIWindow *window = [UIApplication sharedApplication].windows.firstObject; [window addSubview:webView]; }]; @@ -265,10 +258,7 @@ - (FBSDKAppLink *)appLinkFromALData:(NSDictionary *)appLinkDict NSMutableArray *linkTargets = [NSMutableArray array]; NSArray *platformData = nil; -#pragma clang diagnostic push -#pragma clang diagnostic ignored "-Wdeprecated-declarations" - const UIUserInterfaceIdiom idiom = UI_USER_INTERFACE_IDIOM(); -#pragma clang diagnostic pop + const UIUserInterfaceIdiom idiom = UIDevice.currentDevice.userInterfaceIdiom; if (idiom == UIUserInterfaceIdiomPad) { platformData = @[ appLinkDict[FBSDKWebViewAppLinkResolverIPadKey] ?: @{}, appLinkDict[FBSDKWebViewAppLinkResolverIOSKey] ?: @{} ]; diff --git a/FBSDKCoreKit/FBSDKCoreKit/AppLink/Internal/FBSDKMeasurementEventListener.m b/FBSDKCoreKit/FBSDKCoreKit/AppLink/Internal/FBSDKMeasurementEventListener.m index 42f1fa9420..885947cbb9 100644 --- a/FBSDKCoreKit/FBSDKCoreKit/AppLink/Internal/FBSDKMeasurementEventListener.m +++ b/FBSDKCoreKit/FBSDKCoreKit/AppLink/Internal/FBSDKMeasurementEventListener.m @@ -20,21 +20,11 @@ #if !TARGET_OS_TV -#import "FBSDKMeasurementEventListener.h" + #import "FBSDKMeasurementEventListener.h" -#import "FBSDKAppEvents+Internal.h" -#import "FBSDKTimeSpentData.h" -#import "FBSDKTypeUtility.h" - -#if __IPHONE_OS_VERSION_MAX_ALLOWED >= __IPHONE_10_0 - -static NSNotificationName const FBSDKMeasurementEventNotification = @"com.facebook.facebook-objc-sdk.measurement_event"; - -#else - -static NSString *const FBSDKMeasurementEventNotification = @"com.facebook.facebook-objc-sdk.measurement_event"; - -#endif + #import "FBSDKAppEvents+Internal.h" + #import "FBSDKInternalUtility.h" + #import "FBSDKTimeSpentData.h" static NSString *const FBSDKMeasurementEventName = @"event_name"; static NSString *const FBSDKMeasurementEventArgs = @"event_args"; @@ -44,48 +34,48 @@ @implementation FBSDKMeasurementEventListener + (instancetype)defaultListener { - static dispatch_once_t dispatchOnceLocker = 0; - static FBSDKMeasurementEventListener *defaultListener = nil; - dispatch_once(&dispatchOnceLocker, ^{ - defaultListener = [[self alloc] init]; - NSNotificationCenter *center = [NSNotificationCenter defaultCenter]; - [center addObserver:defaultListener - selector:@selector(logFBAppEventForNotification:) - name:FBSDKMeasurementEventNotification - object:nil]; - }); - return defaultListener; + static dispatch_once_t dispatchOnceLocker = 0; + static FBSDKMeasurementEventListener *defaultListener = nil; + dispatch_once(&dispatchOnceLocker, ^{ + defaultListener = [[self alloc] init]; + NSNotificationCenter *center = [NSNotificationCenter defaultCenter]; + [center addObserver:defaultListener + selector:@selector(logFBAppEventForNotification:) + name:FBSDKMeasurementEventNotification + object:nil]; + }); + return defaultListener; } - (void)logFBAppEventForNotification:(NSNotification *)note { - // when catch al_nav_in event, we set source application for FBAppEvents. - if ([note.userInfo[FBSDKMeasurementEventName] isEqualToString:@"al_nav_in"]) { - NSString *sourceApplication = note.userInfo[FBSDKMeasurementEventArgs][@"sourceApplication"]; - if (sourceApplication) { - [FBSDKTimeSpentData setSourceApplication:sourceApplication isFromAppLink:YES]; - } - } - NSDictionary *eventArgs = note.userInfo[FBSDKMeasurementEventArgs]; - NSMutableDictionary *logData = [[NSMutableDictionary alloc] init]; - for (NSString *key in eventArgs.allKeys) { - NSError *error = nil; - NSRegularExpression *regex = [NSRegularExpression regularExpressionWithPattern:@"[^0-9a-zA-Z _-]" options:0 error:&error]; - NSString *safeKey = [regex stringByReplacingMatchesInString:key - options:0 - range:NSMakeRange(0, key.length) - withTemplate:@"-"]; - safeKey = [safeKey stringByTrimmingCharactersInSet:[NSCharacterSet characterSetWithCharactersInString:@" -"]]; - [FBSDKTypeUtility dictionary:logData setObject:eventArgs[key] forKey:safeKey]; + // when catch al_nav_in event, we set source application for FBAppEvents. + if ([note.userInfo[FBSDKMeasurementEventName] isEqualToString:@"al_nav_in"]) { + NSString *sourceApplication = note.userInfo[FBSDKMeasurementEventArgs][@"sourceApplication"]; + if (sourceApplication) { + [FBSDKTimeSpentData setSourceApplication:sourceApplication isFromAppLink:YES]; } - [FBSDKAppEvents logInternalEvent:[FBSDKMeasurementEventPrefix stringByAppendingString:note.userInfo[FBSDKMeasurementEventName]] - parameters:logData - isImplicitlyLogged:YES]; + } + NSDictionary *eventArgs = note.userInfo[FBSDKMeasurementEventArgs]; + NSMutableDictionary *logData = [[NSMutableDictionary alloc] init]; + for (NSString *key in eventArgs.allKeys) { + NSError *error = nil; + NSRegularExpression *regex = [NSRegularExpression regularExpressionWithPattern:@"[^0-9a-zA-Z _-]" options:0 error:&error]; + NSString *safeKey = [regex stringByReplacingMatchesInString:key + options:0 + range:NSMakeRange(0, key.length) + withTemplate:@"-"]; + safeKey = [safeKey stringByTrimmingCharactersInSet:[NSCharacterSet characterSetWithCharactersInString:@" -"]]; + [FBSDKTypeUtility dictionary:logData setObject:eventArgs[key] forKey:safeKey]; + } + [FBSDKAppEvents logInternalEvent:[FBSDKMeasurementEventPrefix stringByAppendingString:note.userInfo[FBSDKMeasurementEventName]] + parameters:logData + isImplicitlyLogged:YES]; } - (void)dealloc { - [[NSNotificationCenter defaultCenter] removeObserver:self]; + [[NSNotificationCenter defaultCenter] removeObserver:self]; } @end diff --git a/FBSDKCoreKit/FBSDKCoreKit/AppLink/FBSDKAppLinkResolver.h b/FBSDKCoreKit/FBSDKCoreKit/AppLink/Resolver/FBSDKAppLinkResolver.h similarity index 100% rename from FBSDKCoreKit/FBSDKCoreKit/AppLink/FBSDKAppLinkResolver.h rename to FBSDKCoreKit/FBSDKCoreKit/AppLink/Resolver/FBSDKAppLinkResolver.h diff --git a/FBSDKCoreKit/FBSDKCoreKit/AppLink/FBSDKAppLinkResolver.m b/FBSDKCoreKit/FBSDKCoreKit/AppLink/Resolver/FBSDKAppLinkResolver.m similarity index 52% rename from FBSDKCoreKit/FBSDKCoreKit/AppLink/FBSDKAppLinkResolver.m rename to FBSDKCoreKit/FBSDKCoreKit/AppLink/Resolver/FBSDKAppLinkResolver.m index 97dd764af4..e8c6041925 100644 --- a/FBSDKCoreKit/FBSDKCoreKit/AppLink/FBSDKAppLinkResolver.m +++ b/FBSDKCoreKit/FBSDKCoreKit/AppLink/Resolver/FBSDKAppLinkResolver.m @@ -20,19 +20,20 @@ #if !TARGET_OS_TV -#import "FBSDKAppLinkResolver.h" + #import "FBSDKAppLinkResolver.h" -#import + #import -#import "FBSDKAccessToken.h" -#import "FBSDKAppLink.h" -#import "FBSDKAppLinkTarget.h" -#import "FBSDKGraphRequest+Internal.h" -#import "FBSDKGraphRequestConnection.h" -#import "FBSDKInternalUtility.h" -#import "FBSDKLogger.h" -#import "FBSDKSettings+Internal.h" -#import "FBSDKUtility.h" + #import "FBSDKAccessToken.h" + #import "FBSDKAppLink.h" + #import "FBSDKAppLinkResolverRequestBuilder.h" + #import "FBSDKAppLinkTarget.h" + #import "FBSDKGraphRequest+Internal.h" + #import "FBSDKGraphRequestConnection.h" + #import "FBSDKInternalUtility.h" + #import "FBSDKLogger.h" + #import "FBSDKSettings+Internal.h" + #import "FBSDKUtility.h" static NSString *const kURLKey = @"url"; static NSString *const kIOSAppStoreIdKey = @"app_store_id"; @@ -48,14 +49,14 @@ @interface FBSDKAppLinkResolver () @property (nonatomic, strong) NSMutableDictionary *cachedFBSDKAppLinks; @property (nonatomic, assign) UIUserInterfaceIdiom userInterfaceIdiom; +@property (nonatomic, strong) FBSDKAppLinkResolverRequestBuilder *requestBuilder; @end @implementation FBSDKAppLinkResolver + (void)initialize { - if (self == [FBSDKAppLinkResolver class]) { - } + if (self == [FBSDKAppLinkResolver class]) {} } - (instancetype)initWithUserInterfaceIdiom:(UIUserInterfaceIdiom)userInterfaceIdiom @@ -63,13 +64,34 @@ - (instancetype)initWithUserInterfaceIdiom:(UIUserInterfaceIdiom)userInterfaceId if (self = [super init]) { self.cachedFBSDKAppLinks = [NSMutableDictionary dictionary]; self.userInterfaceIdiom = userInterfaceIdiom; + self.requestBuilder = [FBSDKAppLinkResolverRequestBuilder new]; + } + return self; +} + +- (instancetype)initWithUserInterfaceIdiom:(UIUserInterfaceIdiom)userInterfaceIdiom andRequestBuilder:(FBSDKAppLinkResolverRequestBuilder *)builder +{ + if (self = [super init]) { + self.cachedFBSDKAppLinks = [NSMutableDictionary dictionary]; + self.userInterfaceIdiom = userInterfaceIdiom; + self.requestBuilder = builder; + } + return self; +} + +- (instancetype)initWithRequestBuilder:(FBSDKAppLinkResolverRequestBuilder *)builder +{ + if (self = [super init]) { + self.cachedFBSDKAppLinks = [NSMutableDictionary dictionary]; + self.userInterfaceIdiom = UIDevice.currentDevice.userInterfaceIdiom; + self.requestBuilder = builder; } return self; } - (void)appLinkFromURL:(NSURL *)url handler:(FBSDKAppLinkBlock)handler { - [self appLinksFromURLs:@[url] handler:^(NSDictionary *urls, NSError * _Nullable error) { + [self appLinksFromURLs:@[url] handler:^(NSDictionary *urls, NSError *_Nullable error) { handler(urls[url], error); }]; } @@ -80,103 +102,86 @@ - (void)appLinksFromURLs:(NSArray *)urls handler:(FBSDKAppLinksBlock)ha [FBSDKLogger singleShotLogEntry:FBSDKLoggingBehaviorDeveloperErrors logEntry:@"A user access token or clientToken is required to use FBAppLinkResolver"]; } + NSMutableDictionary *appLinks = [NSMutableDictionary dictionary]; NSMutableArray *toFind = [NSMutableArray array]; - NSMutableArray *toFindStrings = [NSMutableArray array]; - @synchronized (self.cachedFBSDKAppLinks) { + @synchronized(self.cachedFBSDKAppLinks) { for (NSURL *url in urls) { if (self.cachedFBSDKAppLinks[url]) { [FBSDKTypeUtility dictionary:appLinks setObject:self.cachedFBSDKAppLinks[url] forKey:url]; } else { [FBSDKTypeUtility array:toFind addObject:url]; -#pragma clang diagnostic push -#pragma clang diagnostic ignored "-Wdeprecated-declarations" - NSString *toFindString = [url.absoluteString stringByAddingPercentEscapesUsingEncoding:NSUTF8StringEncoding]; -#pragma clang diagnostic pop - if (toFindString) { - [FBSDKTypeUtility array:toFindStrings addObject:toFindString]; - } } } } if (toFind.count == 0) { // All of the URLs have already been found. - handler(_cachedFBSDKAppLinks, nil); - } - NSMutableArray *fields = [NSMutableArray arrayWithObject:kIOSKey]; - - NSString *idiomSpecificField = nil; - - switch (self.userInterfaceIdiom) { - case UIUserInterfaceIdiomPad: - idiomSpecificField = kIPadKey; - break; - case UIUserInterfaceIdiomPhone: - idiomSpecificField = kIPhoneKey; - break; - default: - break; - } - if (idiomSpecificField) { - [FBSDKTypeUtility array:fields addObject:idiomSpecificField]; + handler(appLinks, nil); + return; } - NSString *path = [NSString stringWithFormat:@"?fields=%@.fields(%@)&ids=%@", - kAppLinksKey, - [fields componentsJoinedByString:@","], - [toFindStrings componentsJoinedByString:@","]]; - FBSDKGraphRequest *request = [[FBSDKGraphRequest alloc] initWithGraphPath:path - parameters:nil - flags:FBSDKGraphRequestFlagDoNotInvalidateTokenOnError | FBSDKGraphRequestFlagDisableErrorRecovery]; + + FBSDKGraphRequest *request = [self.requestBuilder requestForURLs:urls]; [request startWithCompletionHandler:^(FBSDKGraphRequestConnection *connection, id result, NSError *error) { if (error) { handler(@{}, error); return; } - for (NSURL *url in toFind) { - id nestedObject = result[url.absoluteString][kAppLinksKey]; - NSMutableArray *rawTargets = [NSMutableArray array]; - if (idiomSpecificField) { - [rawTargets addObjectsFromArray:nestedObject[idiomSpecificField]]; - } - [rawTargets addObjectsFromArray:nestedObject[kIOSKey]]; - - NSMutableArray *targets = [NSMutableArray arrayWithCapacity:rawTargets.count]; - for (id rawTarget in rawTargets) { - [FBSDKTypeUtility array:targets addObject:[FBSDKAppLinkTarget appLinkTargetWithURL:[NSURL URLWithString:rawTarget[kURLKey]] - appStoreId:rawTarget[kIOSAppStoreIdKey] - appName:rawTarget[kIOSAppNameKey]]]; - } - - id webTarget = nestedObject[kWebKey]; - NSString *webFallbackString = webTarget[kURLKey]; - NSURL *fallbackUrl = webFallbackString ? [NSURL URLWithString:webFallbackString] : url; - NSNumber *shouldFallback = webTarget[kShouldFallbackKey]; - if (shouldFallback != nil && !shouldFallback.boolValue) { - fallbackUrl = nil; - } + for (NSURL *url in toFind) { + FBSDKAppLink *link = [self buildAppLinkForURL:url inResults:result]; - FBSDKAppLink *link = [FBSDKAppLink appLinkWithSourceURL:url - targets:targets - webURL:fallbackUrl]; - @synchronized (self.cachedFBSDKAppLinks) { + @synchronized(self.cachedFBSDKAppLinks) { [FBSDKTypeUtility dictionary:self.cachedFBSDKAppLinks setObject:link forKey:url]; } + [FBSDKTypeUtility dictionary:appLinks setObject:link forKey:url]; } handler(appLinks, nil); }]; } -#pragma clang diagnostic push -#pragma clang diagnostic ignored "-Wdeprecated-declarations" +- (FBSDKAppLink *)buildAppLinkForURL:(NSURL *)url inResults:(id)result +{ + NSString *idiomSpecificField = [self.requestBuilder getIdiomSpecificField]; + + id nestedObject = result[url.absoluteString][kAppLinksKey]; + NSMutableArray *rawTargets = [NSMutableArray array]; + if (idiomSpecificField) { + [rawTargets addObjectsFromArray:nestedObject[idiomSpecificField]]; + } + [rawTargets addObjectsFromArray:nestedObject[kIOSKey]]; + + NSMutableArray *targets = [NSMutableArray arrayWithCapacity:rawTargets.count]; + for (id rawTarget in rawTargets) { + [FBSDKTypeUtility array:targets addObject:[FBSDKAppLinkTarget appLinkTargetWithURL:[NSURL URLWithString:rawTarget[kURLKey]] + appStoreId:rawTarget[kIOSAppStoreIdKey] + appName:rawTarget[kIOSAppNameKey]]]; + } + + id webTarget = nestedObject[kWebKey]; + NSString *webFallbackString = webTarget[kURLKey]; + NSURL *fallbackUrl = webFallbackString ? [NSURL URLWithString:webFallbackString] : url; + + NSNumber *shouldFallback = webTarget[kShouldFallbackKey]; + if (shouldFallback != nil && !shouldFallback.boolValue) { + fallbackUrl = nil; + } + + return [FBSDKAppLink appLinkWithSourceURL:url + targets:targets + webURL:fallbackUrl]; +} + + #pragma clang diagnostic push + #pragma clang diagnostic ignored "-Wdeprecated-declarations" + (instancetype)resolver { - return [[self alloc] initWithUserInterfaceIdiom:UI_USER_INTERFACE_IDIOM()]; + return [[self alloc] initWithUserInterfaceIdiom:UIDevice.currentDevice.userInterfaceIdiom]; } -#pragma clang diagnostic pop + + #pragma clang diagnostic pop @end diff --git a/FBSDKCoreKit/FBSDKCoreKit/AppLink/Resolver/FBSDKAppLinkResolverRequestBuilder.h b/FBSDKCoreKit/FBSDKCoreKit/AppLink/Resolver/FBSDKAppLinkResolverRequestBuilder.h new file mode 100644 index 0000000000..9f33044f87 --- /dev/null +++ b/FBSDKCoreKit/FBSDKCoreKit/AppLink/Resolver/FBSDKAppLinkResolverRequestBuilder.h @@ -0,0 +1,49 @@ +// 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 "TargetConditionals.h" + +#if !TARGET_OS_TV + +#import + +#import "FBSDKAppLinkResolving.h" +#import "FBSDKGraphRequest.h" +NS_ASSUME_NONNULL_BEGIN + +/** + Class responsible for generating the appropriate FBSDKGraphRequest for a given set of urls + */ +NS_SWIFT_NAME(AppLinkResolverRequestBuilder) +@interface FBSDKAppLinkResolverRequestBuilder : NSObject + +/** + Generates the FBSDKGraphRequest + + @param urls The URLs to build the requests for + */ +- (FBSDKGraphRequest* _Nonnull)requestForURLs:(NSArray * _Nonnull)urls +NS_EXTENSION_UNAVAILABLE_IOS("Not available in app extension"); + +- (NSString* _Nullable)getIdiomSpecificField +NS_EXTENSION_UNAVAILABLE_IOS("Not available in app extension"); +@end + +NS_ASSUME_NONNULL_END + +#endif diff --git a/FBSDKCoreKit/FBSDKCoreKit/AppLink/Resolver/FBSDKAppLinkResolverRequestBuilder.m b/FBSDKCoreKit/FBSDKCoreKit/AppLink/Resolver/FBSDKAppLinkResolverRequestBuilder.m new file mode 100644 index 0000000000..38b29a9ac9 --- /dev/null +++ b/FBSDKCoreKit/FBSDKCoreKit/AppLink/Resolver/FBSDKAppLinkResolverRequestBuilder.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 "TargetConditionals.h" + +#if !TARGET_OS_TV + + #import "FBSDKAppLinkResolverRequestBuilder.h" + + #import + + #import "FBSDKCoreKit+Internal.h" + #import "FBSDKGraphRequest+Internal.h" + +static NSString *const kIOSKey = @"ios"; +static NSString *const kIPhoneKey = @"iphone"; +static NSString *const kIPadKey = @"ipad"; +static NSString *const kAppLinksKey = @"app_links"; + +@interface FBSDKAppLinkResolverRequestBuilder () +@property (nonatomic, assign) UIUserInterfaceIdiom userInterfaceIdiom; +@end + +@implementation FBSDKAppLinkResolverRequestBuilder + +- (instancetype)initWithUserInterfaceIdiom:(UIUserInterfaceIdiom)userInterfaceIdiom +{ + if (self = [super init]) { + self.userInterfaceIdiom = userInterfaceIdiom; + } + return self; +} + +- (instancetype)init +{ + if ((self = [super init])) { + _userInterfaceIdiom = UIDevice.currentDevice.userInterfaceIdiom; + } + + return self; +} + +- (FBSDKGraphRequest *)requestForURLs:(NSArray *)urls +{ + NSArray *fields = [self getUISpecificFields]; + NSArray *encodedURLs = [self getEncodedURLs:urls]; + + NSString *path = + [NSString stringWithFormat:@"?fields=%@.fields(%@)&ids=%@", + kAppLinksKey, + [fields componentsJoinedByString:@","], + [encodedURLs componentsJoinedByString:@","]]; + return [[FBSDKGraphRequest alloc] + initWithGraphPath:path + parameters:nil + flags:FBSDKGraphRequestFlagDoNotInvalidateTokenOnError + | FBSDKGraphRequestFlagDisableErrorRecovery]; +} + +- (NSString *_Nullable)getIdiomSpecificField +{ + NSString *idiomSpecificField = nil; + + switch (self.userInterfaceIdiom) { + case UIUserInterfaceIdiomPad: + idiomSpecificField = kIPadKey; + break; + case UIUserInterfaceIdiomPhone: + idiomSpecificField = kIPhoneKey; + break; + default: + break; + } + + return idiomSpecificField; +} + +- (NSArray *)getUISpecificFields +{ + NSMutableArray *fields = [NSMutableArray arrayWithObject:kIOSKey]; + NSString *idiomSpecificField = [self getIdiomSpecificField]; + + if (idiomSpecificField) { + [FBSDKTypeUtility array:fields addObject:idiomSpecificField]; + } + + return fields; +} + +- (NSArray *)getEncodedURLs:(NSArray *)urls +{ + NSMutableArray *encodedURLs = [NSMutableArray array]; + + for (NSURL *url in urls) { + #pragma clang diagnostic push + #pragma clang diagnostic ignored "-Wdeprecated-declarations" + NSString *encodedURL = [url.absoluteString stringByAddingPercentEscapesUsingEncoding:NSUTF8StringEncoding]; + #pragma clang diagnostic pop + if (encodedURL) { + [FBSDKTypeUtility array:encodedURLs addObject:encodedURL]; + } + } + + return encodedURLs; +} + +@end + +#endif diff --git a/FBSDKCoreKit/FBSDKCoreKit/AppLink/FBSDKAppLinkResolving.h b/FBSDKCoreKit/FBSDKCoreKit/AppLink/Resolver/FBSDKAppLinkResolving.h similarity index 100% rename from FBSDKCoreKit/FBSDKCoreKit/AppLink/FBSDKAppLinkResolving.h rename to FBSDKCoreKit/FBSDKCoreKit/AppLink/Resolver/FBSDKAppLinkResolving.h diff --git a/FBSDKCoreKit/FBSDKCoreKit/Basics/FBSDKCoreKit.modulemap b/FBSDKCoreKit/FBSDKCoreKit/Basics/FBSDKCoreKit.modulemap deleted file mode 100644 index dfbaae6eb1..0000000000 --- a/FBSDKCoreKit/FBSDKCoreKit/Basics/FBSDKCoreKit.modulemap +++ /dev/null @@ -1,6 +0,0 @@ -framework module FBSDKCoreKit { - umbrella header "FBSDKBasicUtility.h" - - export * - module * { export * } -} diff --git a/FBSDKCoreKit/FBSDKCoreKit/FBSDKAccessToken.m b/FBSDKCoreKit/FBSDKCoreKit/FBSDKAccessToken.m index 8055aec42a..ba8d71bdfc 100644 --- a/FBSDKCoreKit/FBSDKCoreKit/FBSDKAccessToken.m +++ b/FBSDKCoreKit/FBSDKCoreKit/FBSDKAccessToken.m @@ -17,6 +17,7 @@ // CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. #import "FBSDKAccessToken.h" +#import "FBSDKAccessToken+Internal.h" #import "FBSDKGraphRequestPiggybackManager.h" #import "FBSDKInternalUtility.h" @@ -56,25 +57,25 @@ @implementation FBSDKAccessToken - (instancetype)initWithTokenString:(NSString *)tokenString permissions:(NSArray *)permissions declinedPermissions:(NSArray *)declinedPermissions - expiredPermissions:(NSArray *)expiredPermissions + expiredPermissions:(NSArray *)expiredPermissions appID:(NSString *)appID userID:(NSString *)userID expirationDate:(NSDate *)expirationDate refreshDate:(NSDate *)refreshDate dataAccessExpirationDate:(NSDate *)dataAccessExpirationDate { - if ((self = [super init])) { - _tokenString = [tokenString copy]; - _permissions = [NSSet setWithArray:permissions]; - _declinedPermissions = [NSSet setWithArray:declinedPermissions]; - _expiredPermissions = [NSSet setWithArray:expiredPermissions]; - _appID = [appID copy]; - _userID = [userID copy]; - _expirationDate = [expirationDate copy] ?: [NSDate distantFuture]; - _refreshDate = [refreshDate copy] ?: [NSDate date]; - _dataAccessExpirationDate = [dataAccessExpirationDate copy] ?: [NSDate distantFuture]; - } - return self; + if ((self = [super init])) { + _tokenString = [tokenString copy]; + _permissions = [NSSet setWithArray:permissions]; + _declinedPermissions = [NSSet setWithArray:declinedPermissions]; + _expiredPermissions = [NSSet setWithArray:expiredPermissions]; + _appID = [appID copy]; + _userID = [userID copy]; + _expirationDate = [expirationDate copy] ?: [NSDate distantFuture]; + _refreshDate = [refreshDate copy] ?: [NSDate date]; + _dataAccessExpirationDate = [dataAccessExpirationDate copy] ?: [NSDate distantFuture]; + } + return self; } - (instancetype)initWithTokenString:(NSString *)tokenString @@ -110,12 +111,11 @@ - (instancetype)initWithTokenString:(NSString *)tokenString - (BOOL)hasGranted:(NSString *)permission { return [self.permissions containsObject:permission]; - } - (BOOL)isDataAccessExpired { - return [self.dataAccessExpirationDate compare:NSDate.date] == NSOrderedAscending; + return [self.dataAccessExpirationDate compare:NSDate.date] == NSOrderedAscending; } - (BOOL)isExpired @@ -129,6 +129,12 @@ + (FBSDKAccessToken *)currentAccessToken } + (void)setCurrentAccessToken:(FBSDKAccessToken *)token +{ + [FBSDKAccessToken setCurrentAccessToken:token shouldDispatchNotif:YES]; +} + ++ (void)setCurrentAccessToken:(nullable FBSDKAccessToken *)token + shouldDispatchNotif:(BOOL)shouldDispatchNotif { if (token != g_currentAccessToken) { NSMutableDictionary *userInfo = [NSMutableDictionary dictionary]; @@ -147,10 +153,12 @@ + (void)setCurrentAccessToken:(FBSDKAccessToken *)token [FBSDKInternalUtility deleteFacebookCookies]; } - [FBSDKSettings accessTokenCache].accessToken = token; - [[NSNotificationCenter defaultCenter] postNotificationName:FBSDKAccessTokenDidChangeNotification - object:[self class] - userInfo:userInfo]; + [FBSDKSettings tokenCache].accessToken = token; + if (shouldDispatchNotif) { + [[NSNotificationCenter defaultCenter] postNotificationName:FBSDKAccessTokenDidChangeNotification + object:[self class] + userInfo:userInfo]; + } } } @@ -167,9 +175,13 @@ + (void)refreshCurrentAccessToken:(FBSDKGraphRequestBlock)completionHandler [FBSDKGraphRequestPiggybackManager addRefreshPiggyback:connection permissionHandler:completionHandler]; [connection start]; } else if (completionHandler) { - completionHandler(nil, nil, [FBSDKError - errorWithCode:FBSDKErrorAccessTokenRequired - message:@"No current access token to refresh"]); + completionHandler( + nil, + nil, + [FBSDKError + errorWithCode:FBSDKErrorAccessTokenRequired + message:@"No current access token to refresh"] + ); } } @@ -205,17 +217,17 @@ - (BOOL)isEqual:(id)object - (BOOL)isEqualToAccessToken:(FBSDKAccessToken *)token { - return (token && - [FBSDKInternalUtility object:self.tokenString isEqualToObject:token.tokenString] && - [FBSDKInternalUtility object:self.permissions isEqualToObject:token.permissions] && - [FBSDKInternalUtility object:self.declinedPermissions isEqualToObject:token.declinedPermissions] && - [FBSDKInternalUtility object:self.expiredPermissions isEqualToObject:token.expiredPermissions] && - [FBSDKInternalUtility object:self.appID isEqualToObject:token.appID] && - [FBSDKInternalUtility object:self.userID isEqualToObject:token.userID] && - [FBSDKInternalUtility object:self.refreshDate isEqualToObject:token.refreshDate] && - [FBSDKInternalUtility object:self.expirationDate isEqualToObject:token.expirationDate] && - [FBSDKInternalUtility object:self.dataAccessExpirationDate isEqualToObject:token.dataAccessExpirationDate] && - [FBSDKInternalUtility object:self.graphDomain isEqualToObject:token.graphDomain]); + return (token + && [FBSDKInternalUtility object:self.tokenString isEqualToObject:token.tokenString] + && [FBSDKInternalUtility object:self.permissions isEqualToObject:token.permissions] + && [FBSDKInternalUtility object:self.declinedPermissions isEqualToObject:token.declinedPermissions] + && [FBSDKInternalUtility object:self.expiredPermissions isEqualToObject:token.expiredPermissions] + && [FBSDKInternalUtility object:self.appID isEqualToObject:token.appID] + && [FBSDKInternalUtility object:self.userID isEqualToObject:token.userID] + && [FBSDKInternalUtility object:self.refreshDate isEqualToObject:token.refreshDate] + && [FBSDKInternalUtility object:self.expirationDate isEqualToObject:token.expirationDate] + && [FBSDKInternalUtility object:self.dataAccessExpirationDate isEqualToObject:token.dataAccessExpirationDate] + && [FBSDKInternalUtility object:self.graphDomain isEqualToObject:token.graphDomain]); } #pragma mark - NSCopying @@ -274,4 +286,15 @@ - (void)encodeWithCoder:(NSCoder *)encoder [encoder encodeObject:self.graphDomain forKey:FBSDK_ACCESSTOKEN_GRAPH_DOMAIN_KEY]; } +#pragma mark - Testability + +#if DEBUG + ++ (void)resetCurrentAccessTokenCache +{ + g_currentAccessToken = nil; +} + +#endif + @end diff --git a/FBSDKCoreKit/FBSDKCoreKit/FBSDKApplicationDelegate.h b/FBSDKCoreKit/FBSDKCoreKit/FBSDKApplicationDelegate.h index f09048014e..f4bde7231d 100644 --- a/FBSDKCoreKit/FBSDKCoreKit/FBSDKApplicationDelegate.h +++ b/FBSDKCoreKit/FBSDKCoreKit/FBSDKApplicationDelegate.h @@ -99,8 +99,6 @@ didFinishLaunchingWithOptions:(nullable NSDictionary= __IPHONE_10_0 @@ -53,7 +53,7 @@ #endif static NSString *const FBSDKAppLinkInboundEvent = @"fb_al_inbound"; -static NSString *const FBSDKKitsBitmaskKey = @"com.facebook.sdk.kits.bitmask"; +static NSString *const FBSDKKitsBitmaskKey = @"com.facebook.sdk.kits.bitmask"; static BOOL g_isSDKInitialized = NO; static UIApplicationState _applicationState; @@ -65,47 +65,34 @@ @implementation FBSDKApplicationDelegate #pragma mark - Class Methods -+ (void)load -{ - if ([FBSDKSettings isAutoInitEnabled]) { - // when the app becomes active by any means, kick off the initialization. - [[NSNotificationCenter defaultCenter] addObserver:self - selector:@selector(initializeWithLaunchData:) - name:UIApplicationDidFinishLaunchingNotification - object:nil]; - } -} - -// Initialize SDK listeners -// Don't call this function in any place else. It should only be called when the class is loaded. -+ (void)initializeWithLaunchData:(NSNotification *)note -{ - [self initializeSDK:note.userInfo]; - // Remove the observer - [[NSNotificationCenter defaultCenter] removeObserver:self - name:UIApplicationDidFinishLaunchingNotification - object:nil]; -} - -+ (void)initializeSDK:(NSDictionary *)launchOptions ++ (void)initializeSDK:(NSDictionary *)launchOptions { if (g_isSDKInitialized) { - // Do nothing if initialized already + // Do nothing if initialized already return; } g_isSDKInitialized = YES; FBSDKApplicationDelegate *delegate = [self sharedInstance]; + [FBSDKSettings recordInstall]; NSNotificationCenter *defaultCenter = [NSNotificationCenter defaultCenter]; [defaultCenter addObserver:delegate selector:@selector(applicationDidEnterBackground:) name:UIApplicationDidEnterBackgroundNotification object:nil]; [defaultCenter addObserver:delegate selector:@selector(applicationDidBecomeActive:) name:UIApplicationDidBecomeActiveNotification object:nil]; + [defaultCenter addObserver:delegate selector:@selector(applicationWillResignActive:) name:UIApplicationWillResignActiveNotification object:nil]; [[FBSDKAppEvents singleton] registerNotifications]; [delegate application:[UIApplication sharedApplication] didFinishLaunchingWithOptions:launchOptions]; + // In case of sdk autoInit enabled sdk expects one appDidBecomeActive notification after app launch and has some logic to ignore it. + // if sdk autoInit disabled app won't receive appDidBecomeActive on app launch and will ignore the first one it gets instead of handling it. + // Send first applicationDidBecomeActive notification manually + if ([UIApplication sharedApplication].applicationState == UIApplicationStateActive) { + [delegate applicationDidBecomeActive:nil]; + } + [FBSDKFeatureManager checkFeature:FBSDKFeatureInstrument completionBlock:^(BOOL enabled) { if (enabled) { [FBSDKInstrumentManager enable]; @@ -114,9 +101,9 @@ + (void)initializeSDK:(NSDictionary *)launchOp [FBSDKFeatureManager checkFeature:FBSDKFeatureMonitoring completionBlock:^(BOOL enabled) { if (enabled && FBSDKSettings.isAutoLogAppEventsEnabled) { -#ifndef DEBUG + #ifndef DEBUG [FBSDKMonitor enable]; -#endif + #endif } }]; @@ -156,25 +143,24 @@ - (instancetype)init - (void)dealloc { - [[NSNotificationCenter defaultCenter] removeObserver:self]; + [[NSNotificationCenter defaultCenter] removeObserver:self]; } #pragma mark - UIApplicationDelegate -#if __IPHONE_OS_VERSION_MAX_ALLOWED > __IPHONE_9_0 +#if __IPHONE_OS_VERSION_MAX_ALLOWED> __IPHONE_9_0 - (BOOL)application:(UIApplication *)application openURL:(NSURL *)url - options:(NSDictionary *)options + options:(NSDictionary *)options { - if (@available(iOS 9.0, *)) { - return [self application:application - openURL:url - sourceApplication:options[UIApplicationOpenURLOptionsSourceApplicationKey] - annotation:options[UIApplicationOpenURLOptionsAnnotationKey]]; - } + return [self application:application + openURL:url + sourceApplication:options[UIApplicationOpenURLOptionsSourceApplicationKey] + annotation:options[UIApplicationOpenURLOptionsAnnotationKey]]; - return NO; + return NO; } + #endif - (BOOL)application:(UIApplication *)application @@ -213,22 +199,33 @@ - (BOOL)application:(UIApplication *)application - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions { - if ([self isAppLaunched]) { - return NO; - } + if (_isAppLaunched) { + return NO; + } - _isAppLaunched = YES; - FBSDKAccessToken *cachedToken = [FBSDKSettings accessTokenCache].accessToken; - [FBSDKAccessToken setCurrentAccessToken:cachedToken]; - // fetch app settings - [FBSDKServerConfigurationManager loadServerConfigurationWithCompletionBlock:NULL]; + if (!g_isSDKInitialized) { + [FBSDKApplicationDelegate initializeSDK:launchOptions]; + } - if (FBSDKSettings.isAutoLogAppEventsEnabled) { - [self _logSDKInitialize]; - } + _isAppLaunched = YES; + + // Retrieve cached tokens + FBSDKAccessToken *cachedToken = FBSDKSettings.tokenCache.accessToken; + [FBSDKAccessToken setCurrentAccessToken:cachedToken]; + + // fetch app settings + [FBSDKServerConfigurationManager loadServerConfigurationWithCompletionBlock:NULL]; + + if (FBSDKSettings.isAutoLogAppEventsEnabled) { + [self _logSDKInitialize]; + } #if !TARGET_OS_TV - FBSDKProfile *cachedProfile = [FBSDKProfile fetchCachedProfile]; - [FBSDKProfile setCurrentProfile:cachedProfile]; + FBSDKProfile *cachedProfile = [FBSDKProfile fetchCachedProfile]; + [FBSDKProfile setCurrentProfile:cachedProfile]; + + FBSDKAuthenticationToken *cachedAuthToken = FBSDKSettings.tokenCache.authenticationToken; + [FBSDKAuthenticationToken setCurrentAuthenticationToken:cachedAuthToken]; + [FBSDKAuthenticationStatusUtility checkAuthenticationStatus]; #endif NSArray> *observers = [_applicationObservers allObjects]; BOOL handled = NO; @@ -261,6 +258,9 @@ - (void)applicationDidBecomeActive:(NSNotification *)notification if (FBSDKSettings.isAutoLogAppEventsEnabled) { [FBSDKAppEvents activateApp]; } +#if !TARGET_OS_TV + [FBSDKSKAdNetworkReporter checkAndRevokeTimer]; +#endif NSArray> *observers = [_applicationObservers copy]; for (id observer in observers) { @@ -270,6 +270,17 @@ - (void)applicationDidBecomeActive:(NSNotification *)notification } } +- (void)applicationWillResignActive:(NSNotification *)notification +{ + _applicationState = UIApplicationStateInactive; + NSArray> *const observers = [_applicationObservers copy]; + for (id observer in observers) { + if ([observer respondsToSelector:@selector(applicationWillResignActive:)]) { + [observer applicationWillResignActive:notification.object]; + } + } +} + #pragma mark - Internal Methods #pragma mark - FBSDKApplicationObserving @@ -306,7 +317,7 @@ - (void)_logIfAppLinkEvent:(NSURL *)url return; } - NSDictionary *applinkData = [FBSDKBasicUtility objectForJSONString:applinkDataString error:NULL]; + NSDictionary *applinkData = [FBSDKTypeUtility dictionaryValue:[FBSDKBasicUtility objectForJSONString:applinkDataString error:NULL]]; if (!applinkData) { return; } @@ -355,7 +366,7 @@ - (void)_logSDKInitialize NSString *keyName = [FBSDKTypeUtility dictionary:metaInfo objectForKey:className ofType:NSObject.class]; if (objc_lookUpClass([className UTF8String])) { [FBSDKTypeUtility dictionary:params setObject:@1 forKey:keyName]; - bitmask |= 1 << bit; + bitmask |= 1 << bit; } bit++; } @@ -405,27 +416,50 @@ - (void)_logSwiftRuntimeAvailability if (!params[swiftUsageKey].boolValue) { double delayInSeconds = 1.0; dispatch_time_t delay = dispatch_time(DISPATCH_TIME_NOW, (int64_t)(delayInSeconds * NSEC_PER_SEC)); - dispatch_after(delay, dispatch_get_main_queue(), ^{ - UIViewController *topMostViewController = [FBSDKInternalUtility topMostViewController]; - NSString const *vcClassName = NSStringFromClass([topMostViewController class]); - if ([vcClassName componentsSeparatedByString:@"."].count > 1) { - params[swiftUsageKey] = @YES; - [FBSDKAppEvents logInternalEvent:eventName - parameters:params - isImplicitlyLogged:NO]; - } - }); - }; + dispatch_after(delay, + dispatch_get_main_queue(), ^{ + UIViewController *topMostViewController = [FBSDKInternalUtility topMostViewController]; + NSString const *vcClassName = NSStringFromClass([topMostViewController class]); + if ([vcClassName componentsSeparatedByString:@"."].count > 1) { + params[swiftUsageKey] = @YES; + [FBSDKAppEvents logInternalEvent:eventName + parameters:params + isImplicitlyLogged:NO]; + } + }); + } + ; } + (BOOL)isSDKInitialized { - return [FBSDKSettings isAutoInitEnabled] || g_isSDKInitialized; + return g_isSDKInitialized; } -// Wrapping this makes it mockable and enables testability -- (BOOL)isAppLaunched { +// MARK: - Testability + +#if DEBUG + +- (BOOL)isAppLaunched +{ return _isAppLaunched; } +- (void)setIsAppLaunched:(BOOL)isLaunched +{ + _isAppLaunched = isLaunched; +} + +- (NSHashTable> *)applicationObservers +{ + return _applicationObservers; +} + +- (void)resetApplicationObserverCache +{ + _applicationObservers = [NSHashTable new]; +} + +#endif + @end diff --git a/FBSDKCoreKit/FBSDKCoreKit/FBSDKAuthenticationToken.h b/FBSDKCoreKit/FBSDKCoreKit/FBSDKAuthenticationToken.h new file mode 100644 index 0000000000..8180ddb1dc --- /dev/null +++ b/FBSDKCoreKit/FBSDKCoreKit/FBSDKAuthenticationToken.h @@ -0,0 +1,52 @@ +// 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 + +NS_ASSUME_NONNULL_BEGIN + +/** + Represent an AuthenticationToken used for a login attempt +*/ +NS_SWIFT_NAME(AuthenticationToken) +@interface FBSDKAuthenticationToken : NSObject + +- (instancetype)init NS_UNAVAILABLE; ++ (instancetype)new NS_UNAVAILABLE; + +/** + The "global" authentication token that represents the currently logged in user. + + The `currentAuthenticationToken` represents the authentication token of the + current user and can be used by a client to verify an authentication attempt. + */ +@property (class, nonatomic, copy, nullable) FBSDKAuthenticationToken *currentAuthenticationToken; + +/** + The raw token string from the authentication response + */ +@property (nonatomic, copy, readonly) NSString *tokenString; + +/** + The nonce from the decoded authentication response + */ +@property (nonatomic, copy, readonly) NSString *nonce; + +@end + +NS_ASSUME_NONNULL_END diff --git a/FBSDKCoreKit/FBSDKCoreKit/FBSDKAuthenticationToken.m b/FBSDKCoreKit/FBSDKCoreKit/FBSDKAuthenticationToken.m new file mode 100644 index 0000000000..771d574809 --- /dev/null +++ b/FBSDKCoreKit/FBSDKCoreKit/FBSDKAuthenticationToken.m @@ -0,0 +1,141 @@ +// 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 "FBSDKAuthenticationToken.h" + +#import + +#if SWIFT_PACKAGE +@import FBSDKCoreKit; +#else + #import +#endif + +#ifdef FBSDKCOCOAPODS + #import +#else + #import "FBSDKCoreKit+Internal.h" +#endif + +#import "FBSDKAuthenticationTokenClaims.h" + +static FBSDKAuthenticationToken *g_currentAuthenticationToken; + +NSString *const FBSDKAuthenticationTokenTokenStringCodingKey = @"FBSDKAuthenticationTokenTokenStringCodingKey"; +NSString *const FBSDKAuthenticationTokenNonceCodingKey = @"FBSDKAuthenticationTokenNonceCodingKey"; +NSString *const FBSDKAuthenticationTokenJtiCodingKey = @"FBSDKAuthenticationTokenJtiCodingKey"; + +@implementation FBSDKAuthenticationToken +{ + FBSDKAuthenticationTokenClaims *_claims; + NSString *_jti; +} + +- (instancetype)initWithTokenString:(NSString *)tokenString + nonce:(NSString *)nonce + claims:(nullable FBSDKAuthenticationTokenClaims *)claims +{ + return [self initWithTokenString:tokenString + nonce:nonce + claims:claims + jti:claims.jti]; +} + +- (instancetype)initWithTokenString:(NSString *)tokenString + nonce:(NSString *)nonce + claims:(nullable FBSDKAuthenticationTokenClaims *)claims + jti:(NSString *)jti +{ + if ((self = [super init])) { + _tokenString = tokenString; + _nonce = nonce; + _claims = claims; + _jti = jti; + } + return self; +} + ++ (nullable FBSDKAuthenticationToken *)currentAuthenticationToken +{ + return g_currentAuthenticationToken; +} + ++ (void)setCurrentAuthenticationToken:(FBSDKAuthenticationToken *)token +{ + [self setCurrentAuthenticationToken:token shouldPostNotification:YES]; +} + ++ (void)setCurrentAuthenticationToken:(FBSDKAuthenticationToken *)token + shouldPostNotification:(BOOL)shouldPostNotification +{ + if (token != g_currentAuthenticationToken) { + g_currentAuthenticationToken = token; + [[self tokenCache] setAuthenticationToken:token]; + } +} + +- (NSString *)jti +{ + return _jti; +} + +- (nullable FBSDKAuthenticationTokenClaims *)claims +{ + return _claims; +} + +#pragma mark Storage + ++ (id)tokenCache +{ + return FBSDKSettings.tokenCache; +} + ++ (BOOL)supportsSecureCoding +{ + return YES; +} + +- (instancetype)initWithCoder:(NSCoder *)decoder +{ + NSString *tokenString = [decoder decodeObjectOfClass:NSString.class forKey:FBSDKAuthenticationTokenTokenStringCodingKey]; + NSString *nonce = [decoder decodeObjectOfClass:NSString.class forKey:FBSDKAuthenticationTokenNonceCodingKey]; + NSString *jti = [decoder decodeObjectOfClass:NSString.class forKey:FBSDKAuthenticationTokenJtiCodingKey]; + + return [self initWithTokenString:tokenString nonce:nonce claims:nil jti:jti]; +} + +- (void)encodeWithCoder:(NSCoder *)encoder +{ + [encoder encodeObject:self.tokenString forKey:FBSDKAuthenticationTokenTokenStringCodingKey]; + [encoder encodeObject:self.nonce forKey:FBSDKAuthenticationTokenNonceCodingKey]; + [encoder encodeObject:_jti forKey:FBSDKAuthenticationTokenJtiCodingKey]; +} + +#pragma mark - Test methods + +#if DEBUG + ++ (void)resetCurrentAuthenticationTokenCache +{ + g_currentAuthenticationToken = nil; +} + +#endif + +@end diff --git a/FBSDKCoreKit/FBSDKCoreKit/FBSDKButton.m b/FBSDKCoreKit/FBSDKCoreKit/FBSDKButton.m index 10b073f60f..699353af81 100644 --- a/FBSDKCoreKit/FBSDKCoreKit/FBSDKButton.m +++ b/FBSDKCoreKit/FBSDKCoreKit/FBSDKButton.m @@ -20,8 +20,8 @@ #import "FBSDKButton+Subclass.h" #import "FBSDKAccessToken.h" -#import "FBSDKAppEvents+Internal.h" #import "FBSDKAppEvents.h" +#import "FBSDKAppEvents+Internal.h" #import "FBSDKApplicationDelegate+Internal.h" #import "FBSDKLogo.h" #import "FBSDKUIUtility.h" @@ -147,10 +147,12 @@ - (CGRect)titleRectForContentRect:(CGRect)contentRect // to keep the text centered in the button without adding extra blank space to the right when unnecessary // 1. the text fits centered within the button without colliding with the image (imagePaddingWidth) // 2. the text would run into the image, so adjust the insets to effectively left align it (textPaddingWidth) - CGSize titleSize = FBSDKTextSize(titleLabel.text, - titleLabel.font, - titleRect.size, - titleLabel.lineBreakMode); + CGSize titleSize = FBSDKTextSize( + titleLabel.text, + titleLabel.font, + titleRect.size, + titleLabel.lineBreakMode + ); CGFloat titlePaddingWidth = (CGRectGetWidth(titleRect) - titleSize.width) / 2; CGFloat imagePaddingWidth = titleX / 2; CGFloat inset = MIN(titlePaddingWidth, imagePaddingWidth); @@ -196,42 +198,42 @@ - (void)configureWithIcon:(FBSDKIcon *)icon highlightedColor:(UIColor *)highlightedColor { [self _configureWithIcon:icon - title:title - backgroundColor:backgroundColor - highlightedColor:highlightedColor - selectedTitle:nil - selectedIcon:nil - selectedColor:nil - selectedHighlightedColor:nil]; + title:title + backgroundColor:backgroundColor + highlightedColor:highlightedColor + selectedTitle:nil + selectedIcon:nil + selectedColor:nil + selectedHighlightedColor:nil]; } -- (void)configureWithIcon:(FBSDKIcon *)icon - title:(NSString *)title - backgroundColor:(UIColor *)backgroundColor - highlightedColor:(UIColor *)highlightedColor - selectedTitle:(NSString *)selectedTitle - selectedIcon:(FBSDKIcon *)selectedIcon - selectedColor:(UIColor *)selectedColor - selectedHighlightedColor:(UIColor *)selectedHighlightedColor +- (void) configureWithIcon:(FBSDKIcon *)icon + title:(NSString *)title + backgroundColor:(UIColor *)backgroundColor + highlightedColor:(UIColor *)highlightedColor + selectedTitle:(NSString *)selectedTitle + selectedIcon:(FBSDKIcon *)selectedIcon + selectedColor:(UIColor *)selectedColor + selectedHighlightedColor:(UIColor *)selectedHighlightedColor { [self _configureWithIcon:icon - title:title - backgroundColor:backgroundColor - highlightedColor:highlightedColor - selectedTitle:selectedTitle - selectedIcon:selectedIcon - selectedColor:selectedColor - selectedHighlightedColor:selectedHighlightedColor]; + title:title + backgroundColor:backgroundColor + highlightedColor:highlightedColor + selectedTitle:selectedTitle + selectedIcon:selectedIcon + selectedColor:selectedColor + selectedHighlightedColor:selectedHighlightedColor]; } - (UIColor *)defaultBackgroundColor { - return [UIColor colorWithRed:24.0/255.0 green:119.0/255.0 blue:242.0/255.0 alpha:1.0]; + return [UIColor colorWithRed:24.0 / 255.0 green:119.0 / 255.0 blue:242.0 / 255.0 alpha:1.0]; } - (UIColor *)defaultDisabledColor { - return [UIColor colorWithRed:189.0/255.0 green:193.0/255.0 blue:201.0/255.0 alpha:1.0]; + return [UIColor colorWithRed:189.0 / 255.0 green:193.0 / 255.0 blue:201.0 / 255.0 alpha:1.0]; } - (UIFont *)defaultFont @@ -241,7 +243,7 @@ - (UIFont *)defaultFont - (UIColor *)defaultHighlightedColor { - return [UIColor colorWithRed:21.0/255.0 green:105.0/255.0 blue:214.0/255.0 alpha:1.0]; + return [UIColor colorWithRed:21.0 / 255.0 green:105.0 / 255.0 blue:214.0 / 255.0 alpha:1.0]; } - (FBSDKIcon *)defaultIcon @@ -256,7 +258,7 @@ - (UIColor *)defaultSelectedColor - (UIColor *)highlightedContentColor { - return [UIColor colorWithRed:218.0/255.0 green:221.0/255.0 blue:226.0/255.0 alpha:1.0]; + return [UIColor colorWithRed:218.0 / 255.0 green:221.0 / 255.0 blue:226.0 / 255.0 alpha:1.0]; } - (BOOL)isImplicitlyDisabled @@ -378,13 +380,13 @@ - (void)_configureWithIcon:(FBSDKIcon *)icon if (selectedHighlightedColor) { backgroundImage = [self _backgroundImageWithColor:selectedHighlightedColor cornerRadius:3.0 scale:scale]; [self setBackgroundImage:backgroundImage forState:UIControlStateSelected | UIControlStateHighlighted]; -#if TARGET_OS_TV + #if TARGET_OS_TV [self setBackgroundImage:backgroundImage forState:UIControlStateSelected | UIControlStateFocused]; -#endif + #endif } [self setTitleColor:[UIColor whiteColor] forState:UIControlStateNormal]; - [self setTitleColor:[self highlightedContentColor] forState: UIControlStateHighlighted | UIControlStateSelected]; + [self setTitleColor:[self highlightedContentColor] forState:UIControlStateHighlighted | UIControlStateSelected]; [self setTitle:title forState:UIControlStateNormal]; #if TARGET_OS_TV @@ -393,9 +395,9 @@ - (void)_configureWithIcon:(FBSDKIcon *)icon if (selectedTitle) { [self setTitle:selectedTitle forState:UIControlStateSelected]; [self setTitle:selectedTitle forState:UIControlStateSelected | UIControlStateHighlighted]; -#if TARGET_OS_TV + #if TARGET_OS_TV [self setTitle:selectedTitle forState:UIControlStateSelected | UIControlStateFocused]; -#endif + #endif } UILabel *titleLabel = self.titleLabel; @@ -417,9 +419,9 @@ - (void)_configureWithIcon:(FBSDKIcon *)icon resizingMode:UIImageResizingModeStretch]; [self setImage:selectedImage forState:UIControlStateSelected]; [self setImage:selectedImage forState:UIControlStateSelected | UIControlStateHighlighted]; -#if TARGET_OS_TV + #if TARGET_OS_TV [self setImage:selectedImage forState:UIControlStateSelected | UIControlStateFocused]; -#endif + #endif } if (forceSizeToFit) { diff --git a/FBSDKCoreKit/FBSDKCoreKit/FBSDKConstants.m b/FBSDKCoreKit/FBSDKCoreKit/FBSDKConstants.m index 756a122d7c..b3630268d1 100644 --- a/FBSDKCoreKit/FBSDKCoreKit/FBSDKConstants.m +++ b/FBSDKCoreKit/FBSDKCoreKit/FBSDKConstants.m @@ -28,7 +28,6 @@ #endif - #if __IPHONE_OS_VERSION_MAX_ALLOWED >= __IPHONE_10_0 NSErrorUserInfoKey const FBSDKErrorArgumentCollectionKey = @"com.facebook.sdk:FBSDKErrorArgumentCollectionKey"; diff --git a/FBSDKCoreKit/FBSDKCoreKit/FBSDKCoreKit.h b/FBSDKCoreKit/FBSDKCoreKit/FBSDKCoreKit.h index 77be032ac2..b6ae406a7d 100644 --- a/FBSDKCoreKit/FBSDKCoreKit/FBSDKCoreKit.h +++ b/FBSDKCoreKit/FBSDKCoreKit/FBSDKCoreKit.h @@ -20,77 +20,81 @@ #ifdef BUCK -#import -#import -#import -#import -#import -#import -#import -#import -#import -#import -#import -#import + #import + #import + #import + #import + #import + #import + #import + #import + #import + #import + #import + #import + #import -#if !TARGET_OS_TV -#import -#import -#import -#import -#import -#import -#import -#import -#import -#import -#import -#import -#import -#import -#import -#else -#import -#import -#endif + #if !TARGET_OS_TV + #import + #import + #import + #import + #import + #import + #import + #import + #import + #import + #import + #import + #import + #import + #import + #import + #else + #import + #import + #endif #else -#import "FBSDKAccessToken.h" -#import "FBSDKAppEvents.h" -#import "FBSDKApplicationDelegate.h" -#import "FBSDKButton.h" -#import "FBSDKConstants.h" -#import "FBSDKCopying.h" -#import "FBSDKGraphRequest.h" -#import "FBSDKGraphRequestConnection.h" -#import "FBSDKGraphRequestDataAttachment.h" -#import "FBSDKSettings.h" -#import "FBSDKTestUsersManager.h" -#import "FBSDKUtility.h" + #import "FBSDKAccessToken.h" + #import "FBSDKAppEvents.h" + #import "FBSDKApplicationDelegate.h" + #import "FBSDKAuthenticationToken.h" + #import "FBSDKButton.h" + #import "FBSDKConstants.h" + #import "FBSDKCopying.h" + #import "FBSDKGraphRequest.h" + #import "FBSDKGraphRequestConnection.h" + #import "FBSDKGraphRequestDataAttachment.h" + #import "FBSDKSettings.h" + #import "FBSDKTestUsersManager.h" + #import "FBSDKUtility.h" -#if !TARGET_OS_TV -#import "FBSDKAppLink.h" -#import "FBSDKAppLinkNavigation.h" -#import "FBSDKAppLinkResolver.h" -#import "FBSDKAppLinkResolving.h" -#import "FBSDKAppLinkReturnToRefererController.h" -#import "FBSDKAppLinkReturnToRefererView.h" -#import "FBSDKAppLinkTarget.h" -#import "FBSDKAppLinkUtility.h" -#import "FBSDKGraphErrorRecoveryProcessor.h" -#import "FBSDKMeasurementEvent.h" -#import "FBSDKMutableCopying.h" -#import "FBSDKProfile.h" -#import "FBSDKProfilePictureView.h" -#import "FBSDKURL.h" -#import "FBSDKWebViewAppLinkResolver.h" -#else -#import "FBSDKDeviceButton.h" -#import "FBSDKDeviceViewControllerBase.h" -#endif + #if !TARGET_OS_TV + #import "FBSDKAppLink.h" + #import "FBSDKAppLinkNavigation.h" + #import "FBSDKAppLinkResolver.h" + #import "FBSDKAppLinkResolverRequestBuilder.h" + #import "FBSDKAppLinkResolving.h" + #import "FBSDKAppLinkReturnToRefererController.h" + #import "FBSDKAppLinkReturnToRefererView.h" + #import "FBSDKAppLinkTarget.h" + #import "FBSDKAppLinkUtility.h" + #import "FBSDKGraphErrorRecoveryProcessor.h" + #import "FBSDKMeasurementEvent.h" + #import "FBSDKMutableCopying.h" + #import "FBSDKProfile.h" + #import "FBSDKProfilePictureView.h" + #import "FBSDKURL.h" + #import "FBSDKWebViewAppLinkResolver.h" + #else + #import "FBSDKDeviceButton.h" + #import "FBSDKDeviceViewControllerBase.h" + #endif #endif -#define FBSDK_VERSION_STRING @"7.1.1" -#define FBSDK_TARGET_PLATFORM_VERSION @"v7.0" +#define FBSDK_VERSION_STRING @"8.2.0" +#define FBSDK_TARGET_PLATFORM_VERSION @"v8.0" diff --git a/FBSDKCoreKit/FBSDKCoreKit/FBSDKDeviceButton.m b/FBSDKCoreKit/FBSDKCoreKit/FBSDKDeviceButton.m index 383dd42c95..0a888c5d16 100644 --- a/FBSDKCoreKit/FBSDKCoreKit/FBSDKDeviceButton.m +++ b/FBSDKCoreKit/FBSDKCoreKit/FBSDKDeviceButton.m @@ -20,11 +20,11 @@ #if TARGET_OS_TV -#import "FBSDKDeviceButton.h" + #import "FBSDKDeviceButton.h" -#import + #import -#import "FBSDKCoreKit+Internal.h" + #import "FBSDKCoreKit+Internal.h" static const CGFloat kFBLogoSize = 54.0; static const CGFloat kFBLogoLeftMargin = 36.0; @@ -33,7 +33,7 @@ @implementation FBSDKDeviceButton -#pragma mark - Layout + #pragma mark - Layout - (void)didUpdateFocusInContext:(UIFocusUpdateContext *)context withAnimationCoordinator:(UIFocusAnimationCoordinator *)coordinator { @@ -41,14 +41,14 @@ - (void)didUpdateFocusInContext:(UIFocusUpdateContext *)context withAnimationCoo if (self == context.nextFocusedView) { [coordinator addCoordinatedAnimations:^{ - self.transform = CGAffineTransformMakeScale(1.05, 1.05); - self.layer.shadowOpacity = 0.5; - } completion:NULL]; + self.transform = CGAffineTransformMakeScale(1.05, 1.05); + self.layer.shadowOpacity = 0.5; + } completion:NULL]; } else if (self == context.previouslyFocusedView) { [coordinator addCoordinatedAnimations:^{ - self.transform = CGAffineTransformMakeScale(1.0, 1.0); - self.layer.shadowOpacity = 0; - } completion:NULL]; + self.transform = CGAffineTransformMakeScale(1.0, 1.0); + self.layer.shadowOpacity = 0; + } completion:NULL]; } } @@ -70,11 +70,11 @@ - (CGRect)titleRectForContentRect:(CGRect)contentRect if (!self.layer.needsLayout) { CGSize titleSize = [FBSDKMath ceilForSize:[self.titleLabel.attributedText boundingRectWithSize:contentRect.size - options:(NSStringDrawingUsesDeviceMetrics | - NSStringDrawingUsesLineFragmentOrigin | - NSStringDrawingUsesFontLeading) + options:(NSStringDrawingUsesDeviceMetrics + | NSStringDrawingUsesLineFragmentOrigin + | NSStringDrawingUsesFontLeading) context:NULL].size]; - CGFloat titlePadding = ( CGRectGetWidth(rect) - titleSize.width ) / 2; + CGFloat titlePadding = (CGRectGetWidth(rect) - titleSize.width) / 2; if (titlePadding > titleX) { // if there's room to re-center the text, do so. rect = CGRectMake(kRightMargin, 0, CGRectGetWidth(contentRect) - kRightMargin - kRightMargin, CGRectGetHeight(contentRect)); @@ -84,7 +84,7 @@ - (CGRect)titleRectForContentRect:(CGRect)contentRect return rect; } -#pragma mark - FBSDKButton + #pragma mark - FBSDKButton - (UIFont *)defaultFont { @@ -94,14 +94,16 @@ - (UIFont *)defaultFont - (CGSize)sizeThatFits:(CGSize)size attributedTitle:(NSAttributedString *)title { CGSize titleSize = [FBSDKMath ceilForSize:[title boundingRectWithSize:size - options:(NSStringDrawingUsesDeviceMetrics | - NSStringDrawingUsesLineFragmentOrigin | - NSStringDrawingUsesFontLeading) + options:(NSStringDrawingUsesDeviceMetrics + | NSStringDrawingUsesLineFragmentOrigin + | NSStringDrawingUsesFontLeading) context:NULL].size]; CGFloat logoAndTitleWidth = kFBLogoSize + kPreferredPaddingBetweenLogoTitle + titleSize.width + kPreferredPaddingBetweenLogoTitle; CGFloat height = 108; - CGSize contentSize = CGSizeMake(kFBLogoLeftMargin + logoAndTitleWidth + kRightMargin, - height); + CGSize contentSize = CGSizeMake( + kFBLogoLeftMargin + logoAndTitleWidth + kRightMargin, + height + ); return contentSize; } @@ -110,7 +112,7 @@ - (CGSize)sizeThatFits:(CGSize)size title:(NSString *)title return [self sizeThatFits:size attributedTitle:[self attributedTitleStringFromString:title]]; } -#pragma mark - Subclasses + #pragma mark - Subclasses - (NSAttributedString *)attributedTitleStringFromString:(NSString *)string { @@ -123,10 +125,10 @@ - (NSAttributedString *)attributedTitleStringFromString:(NSString *)string NSMutableAttributedString *attributedString = [[NSMutableAttributedString alloc] initWithString:string attributes:@{ - NSParagraphStyleAttributeName: style, - NSFontAttributeName: [self defaultFont], - NSForegroundColorAttributeName: [UIColor whiteColor] - }]; + NSParagraphStyleAttributeName : style, + NSFontAttributeName : [self defaultFont], + NSForegroundColorAttributeName : [UIColor whiteColor] + }]; // Now find all the spaces and widen their kerning. NSRange range = NSMakeRange(0, string.length); while (range.location != NSNotFound) { diff --git a/FBSDKCoreKit/FBSDKCoreKit/FBSDKDeviceViewControllerBase.m b/FBSDKCoreKit/FBSDKCoreKit/FBSDKDeviceViewControllerBase.m index be6ed4906f..0c638201dd 100644 --- a/FBSDKCoreKit/FBSDKCoreKit/FBSDKDeviceViewControllerBase.m +++ b/FBSDKCoreKit/FBSDKCoreKit/FBSDKDeviceViewControllerBase.m @@ -20,12 +20,11 @@ #if TARGET_OS_TV -#import "FBSDKDeviceViewControllerBase+Internal.h" + #import "FBSDKDeviceViewControllerBase+Internal.h" -#import "FBSDKCoreKit+Internal.h" - -#import "FBSDKSmartDeviceDialogView.h" -#import "FBSDKModalFormPresentationController.h" + #import "FBSDKCoreKit+Internal.h" + #import "FBSDKModalFormPresentationController.h" + #import "FBSDKSmartDeviceDialogView.h" static const NSTimeInterval kAnimationDurationTimeInterval = .5; @@ -50,9 +49,9 @@ - (void)loadView CGRect frame = [UIScreen mainScreen].bounds; BOOL smartLoginEnabled = ([FBSDKServerConfigurationManager cachedServerConfiguration].smartLoginOptions & FBSDKServerConfigurationSmartLoginOptionsEnabled); FBSDKDeviceDialogView *deviceView = - (smartLoginEnabled ? - [[FBSDKSmartDeviceDialogView alloc] initWithFrame:frame] : - [[FBSDKDeviceDialogView alloc] initWithFrame:frame] ); + (smartLoginEnabled + ? [[FBSDKSmartDeviceDialogView alloc] initWithFrame:frame] + : [[FBSDKDeviceDialogView alloc] initWithFrame:frame]); deviceView.delegate = self; self.view = deviceView; } @@ -62,7 +61,7 @@ - (FBSDKDeviceDialogView *)deviceDialogView return (FBSDKDeviceDialogView *)self.view; } -#pragma mark - UIViewControllerAnimatedTransitioning + #pragma mark - UIViewControllerAnimatedTransitioning // Extract this out to another class if we have other similar transitions. - (NSTimeInterval)transitionDuration:(id)transitionContext @@ -104,7 +103,7 @@ - (void)animateTransition:(id)transitionCo } } -#pragma mark - UIViewControllerTransitioningDelegate + #pragma mark - UIViewControllerTransitioningDelegate - (id)animationControllerForDismissedController:(UIViewController *)dismissed { @@ -124,12 +123,13 @@ - (UIPresentationController *)presentationControllerForPresentedViewController:( presentingViewController:presenting]; } -#pragma mark - FBSDKDeviceDialogViewDelegate + #pragma mark - FBSDKDeviceDialogViewDelegate - (void)deviceDialogViewDidCancel:(FBSDKDeviceDialogView *)deviceDialogView { [self dismissViewControllerAnimated:YES completion:NULL]; } + @end #endif diff --git a/FBSDKCoreKit/FBSDKCoreKit/FBSDKMeasurementEvent.m b/FBSDKCoreKit/FBSDKCoreKit/FBSDKMeasurementEvent.m index e678777dd6..363813b6a3 100644 --- a/FBSDKCoreKit/FBSDKCoreKit/FBSDKMeasurementEvent.m +++ b/FBSDKCoreKit/FBSDKCoreKit/FBSDKMeasurementEvent.m @@ -20,20 +20,19 @@ #if !TARGET_OS_TV -#import "FBSDKMeasurementEvent_Internal.h" + #import "FBSDKLogger.h" + #import "FBSDKMeasurementEvent_Internal.h" + #import "FBSDKSettings.h" -#import "FBSDKLogger.h" -#import "FBSDKSettings.h" - -#if __IPHONE_OS_VERSION_MAX_ALLOWED >= __IPHONE_10_0 + #if __IPHONE_OS_VERSION_MAX_ALLOWED >= __IPHONE_10_0 NSNotificationName const FBSDKMeasurementEventNotification = @"com.facebook.facebook-objc-sdk.measurement_event"; -#else + #else NSString *const FBSDKMeasurementEventNotification = @"com.facebook.facebook-objc-sdk.measurement_event"; -#endif + #endif NSString *const FBSDKMeasurementEventNotificationName = @"com.facebook.facebook-objc-sdk.measurement_event"; @@ -48,39 +47,43 @@ NSString *const FBSDKAppLinkNavigateOutEventName = @"al_nav_out"; NSString *const FBSDKAppLinkNavigateBackToReferrerEventName = @"al_ref_back_out"; -@implementation FBSDKMeasurementEvent { - NSString *_name; - NSDictionary *_args; +@implementation FBSDKMeasurementEvent +{ + NSString *_name; + NSDictionary *_args; } -- (void)postNotification { - if (!_name) { - [FBSDKLogger - singleShotLogEntry:FBSDKLoggingBehaviorDeveloperErrors - logEntry:@"Warning: Missing event name when logging FBSDK measurement event.\nIgnoring this event in logging."]; - return; - } - NSNotificationCenter *center = [NSNotificationCenter defaultCenter]; - NSDictionary *userInfo = @{FBSDKMeasurementEventNameKey : _name, - FBSDKMeasurementEventArgsKey : _args}; - - [center postNotificationName:FBSDKMeasurementEventNotification - object:self - userInfo:userInfo]; +- (void)postNotification +{ + if (!_name) { + [FBSDKLogger + singleShotLogEntry:FBSDKLoggingBehaviorDeveloperErrors + logEntry:@"Warning: Missing event name when logging FBSDK measurement event.\nIgnoring this event in logging."]; + return; + } + NSNotificationCenter *center = [NSNotificationCenter defaultCenter]; + NSDictionary *userInfo = @{FBSDKMeasurementEventNameKey : _name, + FBSDKMeasurementEventArgsKey : _args}; + + [center postNotificationName:FBSDKMeasurementEventNotification + object:self + userInfo:userInfo]; } - (instancetype)initEventWithName:(NSString *)name - args:(NSDictionary *)args { - if ((self = [super init])) { - _name = name; - _args = args ? args : @{}; - } - return self; + args:(NSDictionary *)args +{ + if ((self = [super init])) { + _name = name; + _args = args ? args : @{}; + } + return self; } + (void)postNotificationForEventName:(NSString *)name - args:(NSDictionary *)args { - [[[self alloc] initEventWithName:name args:args] postNotification]; + args:(NSDictionary *)args +{ + [[[self alloc] initEventWithName:name args:args] postNotification]; } @end diff --git a/FBSDKCoreKit/FBSDKCoreKit/FBSDKProfile.h b/FBSDKCoreKit/FBSDKCoreKit/FBSDKProfile.h index d1b1551599..e85324ec5d 100644 --- a/FBSDKCoreKit/FBSDKCoreKit/FBSDKProfile.h +++ b/FBSDKCoreKit/FBSDKCoreKit/FBSDKProfile.h @@ -22,6 +22,7 @@ #import "FBSDKProfilePictureView.h" +@class FBSDKAuthenticationTokenClaims; @class FBSDKProfile; NS_ASSUME_NONNULL_BEGIN @@ -109,7 +110,30 @@ NS_SWIFT_NAME(Profile) lastName:(nullable NSString *)lastName name:(nullable NSString *)name linkURL:(nullable NSURL *)linkURL - refreshDate:(nullable NSDate *)refreshDate NS_DESIGNATED_INITIALIZER; + refreshDate:(nullable NSDate *)refreshDate; + +/** + initializes a new instance. + @param userID the user ID + @param firstName the user's first name + @param middleName the user's middle name + @param lastName the user's last name + @param name the user's complete name + @param linkURL the link for this profile + @param refreshDate the optional date this profile was fetched. Defaults to [NSDate date]. + @param imageURL an optional URL to use for fetching a user's profile image + @param email the user's email + */ +- (instancetype)initWithUserID:(NSString *)userID + firstName:(nullable NSString *)firstName + middleName:(nullable NSString *)middleName + lastName:(nullable NSString *)lastName + name:(nullable NSString *)name + linkURL:(nullable NSURL *)linkURL + refreshDate:(nullable NSDate *)refreshDate + imageURL:(nullable NSURL *)imageURL + email:(nullable NSString *)email +NS_DESIGNATED_INITIALIZER; /** The current profile instance and posts the appropriate notification @@ -145,6 +169,8 @@ NS_SWIFT_NAME(current); /** A URL to the user's profile. + IMPORTANT: This field will only be populated if your user has granted your application the 'user_link' permission + Consider using `FBSDKAppLinkResolver` to resolve this to an app link to link directly to the user's profile in the Facebook app. */ @@ -154,6 +180,16 @@ NS_SWIFT_NAME(current); The last time the profile data was fetched. */ @property (nonatomic, readonly) NSDate *refreshDate; +/** + A URL to use for fetching a user's profile image. + */ +@property (nonatomic, readonly, nullable) NSURL *imageURL; +/** + The user's email. + + IMPORTANT: This field will only be populated if your user has granted your application the 'email' permission. + */ +@property (nonatomic, copy, readonly, nullable) NSString *email; /** Indicates if `currentProfile` will automatically observe `FBSDKAccessTokenDidChangeNotification` notifications diff --git a/FBSDKCoreKit/FBSDKCoreKit/FBSDKProfile.m b/FBSDKCoreKit/FBSDKCoreKit/FBSDKProfile.m index a3930e48af..7edfac253b 100644 --- a/FBSDKCoreKit/FBSDKCoreKit/FBSDKProfile.m +++ b/FBSDKCoreKit/FBSDKCoreKit/FBSDKProfile.m @@ -20,35 +20,35 @@ #if !TARGET_OS_TV -#import "FBSDKProfile+Internal.h" + #import "FBSDKProfile+Internal.h" -#import "FBSDKCoreKit+Internal.h" - -#if __IPHONE_OS_VERSION_MAX_ALLOWED >= __IPHONE_10_0 + #if __IPHONE_OS_VERSION_MAX_ALLOWED >= __IPHONE_10_0 NSNotificationName const FBSDKProfileDidChangeNotification = @"com.facebook.sdk.FBSDKProfile.FBSDKProfileDidChangeNotification";; -#else + #else NSString *const FBSDKProfileDidChangeNotification = @"com.facebook.sdk.FBSDKProfile.FBSDKProfileDidChangeNotification";; -#endif + #endif NSString *const FBSDKProfileChangeOldKey = @"FBSDKProfileOld"; NSString *const FBSDKProfileChangeNewKey = @"FBSDKProfileNew"; static NSString *const FBSDKProfileUserDefaultsKey = @"com.facebook.sdk.FBSDKProfile.currentProfile"; static FBSDKProfile *g_currentProfile; -#define FBSDKPROFILE_USERID_KEY @"userID" -#define FBSDKPROFILE_FIRSTNAME_KEY @"firstName" -#define FBSDKPROFILE_MIDDLENAME_KEY @"middleName" -#define FBSDKPROFILE_LASTNAME_KEY @"lastName" -#define FBSDKPROFILE_NAME_KEY @"name" -#define FBSDKPROFILE_LINKURL_KEY @"linkURL" -#define FBSDKPROFILE_REFRESHDATE_KEY @"refreshDate" + #define FBSDKPROFILE_USERID_KEY @"userID" + #define FBSDKPROFILE_FIRSTNAME_KEY @"firstName" + #define FBSDKPROFILE_MIDDLENAME_KEY @"middleName" + #define FBSDKPROFILE_LASTNAME_KEY @"lastName" + #define FBSDKPROFILE_NAME_KEY @"name" + #define FBSDKPROFILE_LINKURL_KEY @"linkURL" + #define FBSDKPROFILE_REFRESHDATE_KEY @"refreshDate" + #define FBSDKPROFILE_IMAGEURL_KEY @"imageURL" + #define FBSDKPROFILE_EMAIL_KEY @"email" // Once a day -#define FBSDKPROFILE_STALE_IN_SECONDS (60 * 60 * 24) + #define FBSDKPROFILE_STALE_IN_SECONDS (60 * 60 * 24) @implementation FBSDKProfile @@ -59,6 +59,27 @@ - (instancetype)initWithUserID:(NSString *)userID name:(NSString *)name linkURL:(NSURL *)linkURL refreshDate:(NSDate *)refreshDate +{ + return [self initWithUserID:userID + firstName:firstName + middleName:middleName + lastName:lastName + name:name + linkURL:linkURL + refreshDate:refreshDate + imageURL:nil + email:nil]; +} + +- (instancetype)initWithUserID:(NSString *)userID + firstName:(NSString *)firstName + middleName:(NSString *)middleName + lastName:(NSString *)lastName + name:(NSString *)name + linkURL:(NSURL *)linkURL + refreshDate:(NSDate *)refreshDate + imageURL:(NSURL *)imageURL + email:(NSString *)email { if ((self = [super init])) { _userID = [userID copy]; @@ -68,16 +89,24 @@ - (instancetype)initWithUserID:(NSString *)userID _name = [name copy]; _linkURL = [linkURL copy]; _refreshDate = [refreshDate copy] ?: [NSDate date]; + _imageURL = [imageURL copy]; + _email = [email copy]; } return self; } -+ (FBSDKProfile *)currentProfile ++ (nullable FBSDKProfile *)currentProfile { return g_currentProfile; } -+ (void)setCurrentProfile:(FBSDKProfile *)profile ++ (void)setCurrentProfile:(nullable FBSDKProfile *)profile +{ + [self setCurrentProfile:profile shouldPostNotification:YES]; +} + ++ (void)setCurrentProfile:(nullable FBSDKProfile *)profile + shouldPostNotification:(BOOL)shouldPostNotification { if (profile != g_currentProfile && ![profile isEqualToProfile:g_currentProfile]) { [[self class] cacheProfile:profile]; @@ -86,30 +115,18 @@ + (void)setCurrentProfile:(FBSDKProfile *)profile [FBSDKTypeUtility dictionary:userInfo setObject:profile forKey:FBSDKProfileChangeNewKey]; [FBSDKTypeUtility dictionary:userInfo setObject:g_currentProfile forKey:FBSDKProfileChangeOldKey]; g_currentProfile = profile; - [[NSNotificationCenter defaultCenter] postNotificationName:FBSDKProfileDidChangeNotification - object:[self class] - userInfo:userInfo]; + + if (shouldPostNotification) { + [[NSNotificationCenter defaultCenter] postNotificationName:FBSDKProfileDidChangeNotification + object:[self class] + userInfo:userInfo]; + } } } - (NSURL *)imageURLForPictureMode:(FBSDKProfilePictureMode)mode size:(CGSize)size { - NSString *type; - switch (mode) { - case FBSDKProfilePictureModeNormal: type = @"normal"; break; - case FBSDKProfilePictureModeSquare: type = @"square"; break; - } - - NSString *path = [NSString stringWithFormat:@"%@/picture?type=%@&width=%d&height=%d", - _userID, - type, - (int) roundf(size.width), - (int) roundf(size.height)]; - - return [FBSDKInternalUtility facebookURLWithHostPrefix:@"graph" - path:path - queryParameters:@{} - error:NULL]; + return [FBSDKProfile imageURLForProfileID:_userID PictureMode:mode size:size]; } + (void)enableUpdatesOnAccessTokenChange:(BOOL)enable @@ -129,15 +146,15 @@ + (void)loadCurrentProfileWithCompletion:(FBSDKProfileBlock)completion [self loadProfileWithToken:[FBSDKAccessToken currentAccessToken] completion:completion]; } -#pragma mark - NSCopying + #pragma mark - NSCopying - (instancetype)copyWithZone:(NSZone *)zone { - //immutable + // immutable return self; } -#pragma mark - Equality + #pragma mark - Equality - (NSUInteger)hash { @@ -148,7 +165,9 @@ - (NSUInteger)hash self.lastName.hash, self.name.hash, self.linkURL.hash, - self.refreshDate.hash + self.refreshDate.hash, + self.imageURL.hash, + self.email.hash, }; return [FBSDKMath hashWithIntegerArray:subhashes count:sizeof(subhashes) / sizeof(subhashes[0])]; } @@ -158,7 +177,7 @@ - (BOOL)isEqual:(id)object if (self == object) { return YES; } - if (![object isKindOfClass:[FBSDKProfile class]]){ + if (![object isKindOfClass:[FBSDKProfile class]]) { return NO; } return [self isEqualToProfile:object]; @@ -166,15 +185,18 @@ - (BOOL)isEqual:(id)object - (BOOL)isEqualToProfile:(FBSDKProfile *)profile { - return ([_userID isEqualToString:profile.userID] && - [_firstName isEqualToString:profile.firstName] && - [_middleName isEqualToString:profile.middleName] && - [_lastName isEqualToString:profile.lastName] && - [_name isEqualToString:profile.name] && - [_linkURL isEqual:profile.linkURL] && - [_refreshDate isEqualToDate:profile.refreshDate]); + return ([_userID isEqualToString:profile.userID] + && [_firstName isEqualToString:profile.firstName] + && [_middleName isEqualToString:profile.middleName] + && [_lastName isEqualToString:profile.lastName] + && [_name isEqualToString:profile.name] + && [_linkURL isEqual:profile.linkURL] + && [_refreshDate isEqualToDate:profile.refreshDate]) + && [_imageURL isEqual:profile.imageURL] + && [_email isEqualToString:profile.email]; } -#pragma mark NSCoding + + #pragma mark NSCoding + (BOOL)supportsSecureCoding { @@ -190,13 +212,17 @@ - (instancetype)initWithCoder:(NSCoder *)decoder NSString *name = [decoder decodeObjectOfClass:[NSString class] forKey:FBSDKPROFILE_NAME_KEY]; NSURL *linkURL = [decoder decodeObjectOfClass:[NSURL class] forKey:FBSDKPROFILE_LINKURL_KEY]; NSDate *refreshDate = [decoder decodeObjectOfClass:[NSURL class] forKey:FBSDKPROFILE_REFRESHDATE_KEY]; + NSURL *imageURL = [decoder decodeObjectOfClass:[NSURL class] forKey:FBSDKPROFILE_IMAGEURL_KEY]; + NSString *email = [decoder decodeObjectOfClass:[NSString class] forKey:FBSDKPROFILE_EMAIL_KEY]; return [self initWithUserID:userID firstName:firstName middleName:middleName lastName:lastName name:name linkURL:linkURL - refreshDate:refreshDate]; + refreshDate:refreshDate + imageURL:imageURL + email:email]; } - (void)encodeWithCoder:(NSCoder *)encoder @@ -208,24 +234,140 @@ - (void)encodeWithCoder:(NSCoder *)encoder [encoder encodeObject:self.name forKey:FBSDKPROFILE_NAME_KEY]; [encoder encodeObject:self.linkURL forKey:FBSDKPROFILE_LINKURL_KEY]; [encoder encodeObject:self.refreshDate forKey:FBSDKPROFILE_REFRESHDATE_KEY]; + [encoder encodeObject:self.imageURL forKey:FBSDKPROFILE_IMAGEURL_KEY]; + [encoder encodeObject:self.email forKey:FBSDKPROFILE_EMAIL_KEY]; +} + +@end + +@implementation FBSDKProfile (Internal) + + #pragma clang diagnostic push + #pragma clang diagnostic ignored "-Wdeprecated-declarations" ++ (void)cacheProfile:(FBSDKProfile *)profile +{ + NSUserDefaults *userDefaults = [NSUserDefaults standardUserDefaults]; + if (profile) { + NSData *data = [NSKeyedArchiver archivedDataWithRootObject:profile]; + [userDefaults setObject:data forKey:FBSDKProfileUserDefaultsKey]; + } else { + [userDefaults removeObjectForKey:FBSDKProfileUserDefaultsKey]; + } + [userDefaults synchronize]; +} + ++ (FBSDKProfile *)fetchCachedProfile +{ + NSUserDefaults *userDefaults = [NSUserDefaults standardUserDefaults]; + NSData *data = [userDefaults objectForKey:FBSDKProfileUserDefaultsKey]; + if (data != nil) { + @try { + return [NSKeyedUnarchiver unarchiveObjectWithData:data]; + } @catch (NSException *exception) { + return nil; + } + } + return nil; } -#pragma mark - Private ++ (NSURL *)imageURLForProfileID:(NSString *)profileId + PictureMode:(FBSDKProfilePictureMode)mode + size:(CGSize)size +{ + NSString *const accessTokenKey = @"access_token"; + NSString *const pictureModeKey = @"type"; + NSString *const widthKey = @"width"; + NSString *const heightKey = @"height"; + + NSString *type; + switch (mode) { + case FBSDKProfilePictureModeNormal: type = @"normal"; break; + case FBSDKProfilePictureModeSquare: type = @"square"; break; + case FBSDKProfilePictureModeSmall: type = @"small"; break; + case FBSDKProfilePictureModeAlbum: type = @"album"; break; + case FBSDKProfilePictureModeLarge: type = @"large"; break; + default: type = @"normal"; + } + + NSMutableDictionary *queryParameters = [NSMutableDictionary dictionary]; + [FBSDKTypeUtility dictionary:queryParameters setObject:type forKey:pictureModeKey]; + [FBSDKTypeUtility dictionary:queryParameters setObject:@(roundf(size.width)) forKey:widthKey]; + [FBSDKTypeUtility dictionary:queryParameters setObject:@(roundf(size.height)) forKey:heightKey]; + + if (FBSDKAccessToken.currentAccessToken) { + [FBSDKTypeUtility dictionary:queryParameters setObject:FBSDKAccessToken.currentAccessToken.tokenString forKey:accessTokenKey]; + } else if (FBSDKSettings.clientToken) { + [FBSDKTypeUtility dictionary:queryParameters setObject:FBSDKSettings.clientToken forKey:accessTokenKey]; + } else { + NSLog(@"As of Graph API v8.0, profile images may not be retrieved without an access token. This can be the current access token from logging in with Facebook or it can be set via the plist or in code. Providing neither will cause this call to return a silhouette image."); + } + + NSString *path = [NSString stringWithFormat:@"%@/picture", profileId]; + + return [FBSDKInternalUtility facebookURLWithHostPrefix:@"graph" + path:path + queryParameters:queryParameters + error:NULL]; +} + (void)loadProfileWithToken:(FBSDKAccessToken *)token completion:(FBSDKProfileBlock)completion +{ + NSString *graphPath = @"me?fields=id,first_name,middle_name,last_name,name"; + if ([token.permissions containsObject:@"user_link"]) { + graphPath = [graphPath stringByAppendingString:@",link"]; + } + + if ([token.permissions containsObject:@"email"]) { + graphPath = [graphPath stringByAppendingString:@",email"]; + } + + FBSDKGraphRequest *request = [[FBSDKGraphRequest alloc] initWithGraphPath:graphPath + parameters:nil + flags:FBSDKGraphRequestFlagDoNotInvalidateTokenOnError | FBSDKGraphRequestFlagDisableErrorRecovery]; + [[self class] loadProfileWithToken:token completion:completion graphRequest:request]; +} + ++ (void)loadProfileWithToken:(FBSDKAccessToken *)token + completion:(FBSDKProfileBlock)completion + graphRequest:(FBSDKGraphRequest *)request +{ + FBSDKParseProfileBlock parseBlock = ^void (id result, FBSDKProfile **profileRef) { + if (profileRef == NULL + || result == nil + || result[@"id"] == nil + || ((NSString *) result[@"id"]).length == 0) { + return; + } + NSString *urlString = [FBSDKTypeUtility stringValue:result[@"link"]]; + NSURL *linkUrl = [FBSDKTypeUtility URLValue:[NSURL URLWithString:urlString]]; + + FBSDKProfile *profile = [[FBSDKProfile alloc] initWithUserID:result[@"id"] + firstName:result[@"first_name"] + middleName:result[@"middle_name"] + lastName:result[@"last_name"] + name:result[@"name"] + linkURL:linkUrl + refreshDate:[NSDate date] + imageURL:nil + email:result[@"email"]]; + *profileRef = [profile copy]; + }; + [[self class] loadProfileWithToken:token completion:completion graphRequest:request parseBlock:parseBlock]; +} + ++ (void)loadProfileWithToken:(FBSDKAccessToken *)token + completion:(FBSDKProfileBlock)completion + graphRequest:(FBSDKGraphRequest *)request + parseBlock:(FBSDKParseProfileBlock)parseBlock; { static FBSDKGraphRequestConnection *executingRequestConnection = nil; BOOL isStale = [[NSDate date] timeIntervalSinceDate:g_currentProfile.refreshDate] > FBSDKPROFILE_STALE_IN_SECONDS; - if (token && - (isStale || ![g_currentProfile.userID isEqualToString:token.userID])) { + if (token + && (isStale || ![g_currentProfile.userID isEqualToString:token.userID])) { FBSDKProfile *expectedCurrentProfile = g_currentProfile; - NSString *graphPath = @"me?fields=id,first_name,middle_name,last_name,name,link"; [executingRequestConnection cancel]; - FBSDKGraphRequest *request = [[FBSDKGraphRequest alloc] initWithGraphPath:graphPath - parameters:nil - flags:FBSDKGraphRequestFlagDoNotInvalidateTokenOnError | FBSDKGraphRequestFlagDisableErrorRecovery]; executingRequestConnection = [request startWithCompletionHandler:^(FBSDKGraphRequestConnection *connection, id result, NSError *error) { if (expectedCurrentProfile != g_currentProfile) { // current profile has already changed since request was started. Let's not overwrite. @@ -236,13 +378,7 @@ + (void)loadProfileWithToken:(FBSDKAccessToken *)token completion:(FBSDKProfileB } FBSDKProfile *profile = nil; if (!error) { - profile = [[FBSDKProfile alloc] initWithUserID:result[@"id"] - firstName:result[@"first_name"] - middleName:result[@"middle_name"] - lastName:result[@"last_name"] - name:result[@"name"] - linkURL:[NSURL URLWithString:result[@"link"]] - refreshDate:[NSDate date]]; + parseBlock(result, &profile); } [[self class] setCurrentProfile:profile]; if (completion != NULL) { @@ -260,38 +396,16 @@ + (void)observeChangeAccessTokenChange:(NSNotification *)notification [self loadProfileWithToken:token completion:NULL]; } + #pragma clang diagnostic pop + @end -@implementation FBSDKProfile(Internal) +@implementation FBSDKProfile (Testing) -#pragma clang diagnostic push -#pragma clang diagnostic ignored "-Wdeprecated-declarations" -+ (void)cacheProfile:(FBSDKProfile *)profile ++ (void)resetCurrentProfileCache { - NSUserDefaults *userDefaults = [NSUserDefaults standardUserDefaults]; - if (profile) { - NSData *data = [NSKeyedArchiver archivedDataWithRootObject:profile]; - [userDefaults setObject:data forKey:FBSDKProfileUserDefaultsKey]; - } else { - [userDefaults removeObjectForKey:FBSDKProfileUserDefaultsKey]; - } - [userDefaults synchronize]; -} - -+ (FBSDKProfile *)fetchCachedProfile -{ - NSUserDefaults *userDefaults = [NSUserDefaults standardUserDefaults]; - NSData *data = [userDefaults objectForKey:FBSDKProfileUserDefaultsKey]; - if (data != nil) { - @try { - return [NSKeyedUnarchiver unarchiveObjectWithData:data]; - } @catch (NSException *exception) { - return nil; - } - } - return nil; + g_currentProfile = nil; } -#pragma clang diagnostic pop @end diff --git a/FBSDKCoreKit/FBSDKCoreKit/FBSDKProfilePictureView.h b/FBSDKCoreKit/FBSDKCoreKit/FBSDKProfilePictureView.h index c5a1672b0e..cbef2ce9fe 100644 --- a/FBSDKCoreKit/FBSDKCoreKit/FBSDKProfilePictureView.h +++ b/FBSDKCoreKit/FBSDKCoreKit/FBSDKProfilePictureView.h @@ -40,6 +40,18 @@ typedef NS_ENUM(NSUInteger, FBSDKProfilePictureMode) The original picture's aspect ratio will be used for the source image in the view. */ FBSDKProfilePictureModeNormal, + /** + The original picture's aspect ratio will be used for the source image in the view. + */ + FBSDKProfilePictureModeAlbum, + /** + The original picture's aspect ratio will be used for the source image in the view. + */ + FBSDKProfilePictureModeSmall, + /** + The original picture's aspect ratio will be used for the source image in the view. + */ + FBSDKProfilePictureModeLarge, } NS_SWIFT_NAME(Profile.PictureMode); /** diff --git a/FBSDKCoreKit/FBSDKCoreKit/FBSDKProfilePictureView.m b/FBSDKCoreKit/FBSDKCoreKit/FBSDKProfilePictureView.m index f203586f73..3b3053dd61 100644 --- a/FBSDKCoreKit/FBSDKCoreKit/FBSDKProfilePictureView.m +++ b/FBSDKCoreKit/FBSDKCoreKit/FBSDKProfilePictureView.m @@ -20,13 +20,15 @@ #if !TARGET_OS_TV -#import "FBSDKProfilePictureView.h" + #import "FBSDKProfilePictureView.h" + #import "FBSDKProfilePictureView+Internal.h" -#import "FBSDKAccessToken.h" -#import "FBSDKInternalUtility.h" -#import "FBSDKMaleSilhouetteIcon.h" -#import "FBSDKMath.h" -#import "FBSDKUtility.h" + #import "FBSDKAccessToken.h" + #import "FBSDKHumanSilhouetteIcon.h" + #import "FBSDKInternalUtility.h" + #import "FBSDKMath.h" + #import "FBSDKProfile+Internal.h" + #import "FBSDKUtility.h" @interface FBSDKProfilePictureViewState : NSObject @@ -36,11 +38,11 @@ - (instancetype)initWithProfileID:(NSString *)profileID pictureMode:(FBSDKProfilePictureMode)pictureMode imageShouldFit:(BOOL)imageShouldFit; -@property (nonatomic, assign, readonly) BOOL imageShouldFit; -@property (nonatomic, assign, readonly) FBSDKProfilePictureMode pictureMode; -@property (nonatomic, copy, readonly) NSString *profileID; -@property (nonatomic, assign, readonly) CGFloat scale; -@property (nonatomic, assign, readonly) CGSize size; +@property (nonatomic, readonly, assign) BOOL imageShouldFit; +@property (nonatomic, readonly, assign) FBSDKProfilePictureMode pictureMode; +@property (nonatomic, readonly, copy) NSString *profileID; +@property (nonatomic, readonly, assign) CGFloat scale; +@property (nonatomic, readonly, assign) CGSize size; - (BOOL)isEqualToState:(FBSDKProfilePictureViewState *)other; - (BOOL)isValidForState:(FBSDKProfilePictureViewState *)other; @@ -89,17 +91,17 @@ - (BOOL)isEqual:(id)object - (BOOL)isEqualToState:(FBSDKProfilePictureViewState *)other { - return ([self isValidForState:other] && - CGSizeEqualToSize(_size, other->_size) && - (_scale == other->_scale)); + return ([self isValidForState:other] + && CGSizeEqualToSize(_size, other->_size) + && (_scale == other->_scale)); } - (BOOL)isValidForState:(FBSDKProfilePictureViewState *)other { - return (other != nil && - (_imageShouldFit == other->_imageShouldFit) && - (_pictureMode == other->_pictureMode) && - [FBSDKInternalUtility object:_profileID isEqualToObject:other->_profileID]); + return (other != nil + && (_imageShouldFit == other->_imageShouldFit) + && (_pictureMode == other->_pictureMode) + && [FBSDKInternalUtility object:_profileID isEqualToObject:other->_profileID]); } @end @@ -113,12 +115,12 @@ @implementation FBSDKProfilePictureView BOOL _placeholderImageIsValid; } -#pragma mark - Object Lifecycle + #pragma mark - Object Lifecycle - (instancetype)initWithFrame:(CGRect)frame { if ((self = [super initWithFrame:frame])) { - [self _configureProfilePictureView]; + [self configureProfilePictureView]; } return self; } @@ -126,7 +128,7 @@ - (instancetype)initWithFrame:(CGRect)frame - (instancetype)initWithCoder:(NSCoder *)decoder { if ((self = [super initWithCoder:decoder])) { - [self _configureProfilePictureView]; + [self configureProfilePictureView]; } return self; } @@ -152,7 +154,7 @@ - (void)dealloc [[NSNotificationCenter defaultCenter] removeObserver:self]; } -#pragma mark - Properties + #pragma mark - Properties - (void)setBounds:(CGRect)bounds { @@ -199,7 +201,7 @@ - (void)setProfileID:(NSString *)profileID } } -#pragma mark - Public Methods + #pragma mark - Public Methods - (void)setNeedsImageUpdate { @@ -219,22 +221,13 @@ - (void)setNeedsImageUpdate return; } self->_needsImageUpdate = YES; - [self _needsImageUpdate]; + [self _updateImage]; }); } -#pragma mark - Helper Methods + #pragma mark - Internal Methods -- (void)_accessTokenDidChangeNotification:(NSNotification *)notification -{ - if (![_profileID isEqualToString:@"me"] || !notification.userInfo[FBSDKAccessTokenDidChangeUserIDKey]) { - return; - } - _lastState = nil; - [self setNeedsImageUpdate]; -} - -- (void)_configureProfilePictureView +- (void)configureProfilePictureView { _imageView = [[UIImageView alloc] initWithFrame:self.bounds]; _imageView.autoresizingMask = (UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight); @@ -248,10 +241,109 @@ - (void)_configureProfilePictureView selector:@selector(_accessTokenDidChangeNotification:) name:FBSDKAccessTokenDidChangeNotification object:nil]; + [[NSNotificationCenter defaultCenter] addObserver:self + selector:@selector(_profileDidChangeNotification:) + name:FBSDKProfileDidChangeNotification + object:nil]; +} + + #pragma mark - Notifications + +- (void)_accessTokenDidChangeNotification:(NSNotification *)notification +{ + if (![_profileID isEqualToString:@"me"] || !notification.userInfo[FBSDKAccessTokenDidChangeUserIDKey]) { + return; + } + _lastState = nil; + [self _updateImageWithAccessToken]; +} + +- (void)_profileDidChangeNotification:(NSNotification *)notification +{ + if (![_profileID isEqualToString:@"me"]) { + return; + } + _lastState = nil; + [self _updateImageWithProfile]; +} + + #pragma mark - Image Update + +- (void)_updateImageWithProfile +{ + if (!_profileID) { + if (!_placeholderImageIsValid) { + [self _setPlaceholderImage]; + } + return; + } + + // if the current image is no longer representative of the current state, clear the current value out; otherwise, + // leave the current value until the new resolution image is downloaded + FBSDKProfilePictureViewState *state = [self _state]; + if (![_lastState isValidForState:state]) { + [self _setPlaceholderImage]; + } + + FBSDKProfile *profile = FBSDKProfile.currentProfile; + if (![state.profileID isEqualToString:@"me"] || !profile.imageURL) { + return; + } + _lastState = state; - [self setNeedsImageUpdate]; + [self _fetchAndSetImageWithURL:profile.imageURL state:state]; } +- (void)_updateImageWithAccessToken +{ + if (!_profileID) { + if (!_placeholderImageIsValid) { + [self _setPlaceholderImage]; + } + return; + } + + // if the current image is no longer representative of the current state, clear the current value out; otherwise, + // leave the current value until the new resolution image is downloaded + FBSDKProfilePictureViewState *state = [self _state]; + if (![_lastState isValidForState:state]) { + [self _setPlaceholderImage]; + } + + if ([state.profileID isEqualToString:@"me"] && !FBSDKAccessToken.currentAccessTokenIsActive) { + return; + } + _lastState = state; + + [self _fetchAndSetImageWithURL:[self _getProfileImageUrl:state] state:state]; +} + +- (void)_fetchAndSetImageWithURL:(NSURL *)imageURL state:(FBSDKProfilePictureViewState *)state +{ + __weak FBSDKProfilePictureView *weakSelf = self; + NSURLRequest *request = [[NSURLRequest alloc] initWithURL:imageURL]; + NSURLSession *session = [NSURLSession sharedSession]; + [[session + dataTaskWithRequest:request + completionHandler:^(NSData *data, NSURLResponse *response, NSError *error) { + if (!error && data.length) { + [weakSelf _updateImageWithData:data state:state]; + } + }] resume]; +} + +- (void)_updateImage +{ + _needsImageUpdate = NO; + if (![_profileID isEqualToString:@"me"] || FBSDKAccessToken.currentAccessTokenIsActive) { + [self _updateImageWithAccessToken]; + } else if (FBSDKProfile.currentProfile.imageURL) { + [self _updateImageWithProfile]; + } +} + + #pragma mark - Helper Methods + - (BOOL)_imageShouldFit { switch (self.contentMode) { @@ -278,7 +370,7 @@ - (CGSize)_imageSize:(BOOL)imageShouldFit scale:(CGFloat)scale // get the image size based on the contentMode and pictureMode CGSize size = self.bounds.size; switch (_pictureMode) { - case FBSDKProfilePictureModeSquare:{ + case FBSDKProfilePictureModeSquare: { CGFloat imageSize; if (imageShouldFit) { imageSize = MIN(size.width, size.height); @@ -289,6 +381,9 @@ - (CGSize)_imageSize:(BOOL)imageShouldFit scale:(CGFloat)scale break; } case FBSDKProfilePictureModeNormal: + case FBSDKProfilePictureModeAlbum: + case FBSDKProfilePictureModeSmall: + case FBSDKProfilePictureModeLarge: // use the bounds size break; } @@ -299,67 +394,37 @@ - (CGSize)_imageSize:(BOOL)imageShouldFit scale:(CGFloat)scale return size; } -- (void)_needsImageUpdate +- (FBSDKProfilePictureViewState *)_state { - _needsImageUpdate = NO; - - if (!_profileID) { - if (!_placeholderImageIsValid) { - [self _setPlaceholderImage]; - } - return; - } - - // if the current image is no longer representative of the current state, clear the current value out; otherwise, - // leave the current value until the new resolution image is downloaded BOOL imageShouldFit = [self _imageShouldFit]; UIScreen *screen = self.window.screen ?: [UIScreen mainScreen]; CGFloat scale = screen.scale; CGSize imageSize = [self _imageSize:imageShouldFit scale:scale]; - FBSDKProfilePictureViewState *state = [[FBSDKProfilePictureViewState alloc] initWithProfileID:_profileID - size:imageSize - scale:scale - pictureMode:_pictureMode - imageShouldFit:imageShouldFit]; - if (![_lastState isValidForState:state]) { - [self _setPlaceholderImage]; - } - _lastState = state; + return [[FBSDKProfilePictureViewState alloc] initWithProfileID:_profileID + size:imageSize + scale:scale + pictureMode:_pictureMode + imageShouldFit:imageShouldFit]; +} - FBSDKAccessToken *accessToken = [FBSDKAccessToken currentAccessToken]; - if ([state.profileID isEqualToString:@"me"] && !accessToken) { - return; +- (NSURL *)_getProfileImageUrl:(FBSDKProfilePictureViewState *)state +{ + // If there's an existing profile, use that profile's image url handler + if (FBSDKProfile.currentProfile != nil) { + return [FBSDKProfile.currentProfile imageURLForPictureMode:self.pictureMode size:state.size]; } - - NSString *path = [[NSString alloc] initWithFormat:@"/%@/picture", [FBSDKUtility URLEncode:state.profileID]]; - CGSize size = state.size; - NSMutableDictionary *parameters = [[NSMutableDictionary alloc] init]; - [FBSDKTypeUtility dictionary:parameters setObject:@(size.width) forKey:@"width"]; - [FBSDKTypeUtility dictionary:parameters setObject:@(size.height) forKey:@"height"]; - [FBSDKTypeUtility dictionary:parameters setObject:accessToken.tokenString forKey:@"access_token"]; - NSURL *imageURL = [FBSDKInternalUtility facebookURLWithHostPrefix:@"graph" path:path queryParameters:parameters error:NULL]; - - __weak FBSDKProfilePictureView *weakSelf = self; - - NSURLRequest *request = [[NSURLRequest alloc] initWithURL:imageURL]; - NSURLSession *session = [NSURLSession sharedSession]; - [[session - dataTaskWithRequest:request - completionHandler:^(NSData *data, NSURLResponse *response, NSError *error) { - if (!error && data.length) { - [weakSelf _updateImageWithData:data state:state]; - } - }] resume]; + return [FBSDKProfile imageURLForProfileID:state.profileID PictureMode:self.pictureMode size:state.size]; } - (void)_setPlaceholderImage { - UIColor *fillColor = [UIColor colorWithRed:157.0/255.0 green:177.0/255.0 blue:204.0/255.0 alpha:1.0]; + UIColor *fillColor = [UIColor colorWithRed:157.0 / 255.0 green:177.0 / 255.0 blue:204.0 / 255.0 alpha:1.0]; _placeholderImageIsValid = YES; _hasProfileImage = NO; dispatch_async(dispatch_get_main_queue(), ^{ - self->_imageView.image = [[[FBSDKMaleSilhouetteIcon alloc] initWithColor:fillColor] imageWithSize:self->_imageView.bounds.size]; + self->_imageView.image = [[FBSDKHumanSilhouetteIcon new] imageWithSize:self->_imageView.bounds.size + color:fillColor]; }); } @@ -383,6 +448,13 @@ - (void)_updateImageWithData:(NSData *)data state:(FBSDKProfilePictureViewState } } + #pragma mark - Test Helpers + +- (FBSDKProfilePictureViewState *)lastState +{ + return _lastState; +} + @end #endif diff --git a/FBSDKCoreKit/FBSDKCoreKit/FBSDKSettings.h b/FBSDKCoreKit/FBSDKCoreKit/FBSDKSettings.h index 2aa81e263f..7e337c3532 100644 --- a/FBSDKCoreKit/FBSDKCoreKit/FBSDKSettings.h +++ b/FBSDKCoreKit/FBSDKCoreKit/FBSDKSettings.h @@ -85,12 +85,6 @@ NS_SWIFT_NAME(Settings) @property (class, nonatomic, assign) CGFloat JPEGCompressionQuality NS_SWIFT_NAME(jpegCompressionQuality); -/** - Controls sdk auto initailization. - If not explicitly set, the default is true - */ -@property (class, nonatomic, assign, getter=isAutoInitEnabled) BOOL autoInitEnabled; - /** Controls the auto logging of basic app events, such as activateApp and deactivateApp. If not explicitly set, the default is true @@ -104,7 +98,7 @@ NS_SWIFT_NAME(jpegCompressionQuality); @property (class, nonatomic, assign, getter=isCodelessDebugLogEnabled) BOOL codelessDebugLogEnabled; /** - Controls the fb_codeless_debug logging event + Controls the access to IDFA If not explicitly set, the default is true */ @property (class, nonatomic, assign, getter=isAdvertiserIDCollectionEnabled) BOOL advertiserIDCollectionEnabled; @@ -187,6 +181,20 @@ NS_REFINED_FOR_SWIFT; */ @property (class, nonatomic, copy, null_resettable) NSString *graphAPIVersion; +/** + The value of the flag advertiser_tracking_enabled that controls the advertiser tracking status of the data sent to Facebook + If not explicitly set in iOS14 or above, the default is false in iOS14 or above. + */ ++ (BOOL)isAdvertiserTrackingEnabled; + +/** +Set the advertiser_tracking_enabled flag. It only works in iOS14 and above. + +@param advertiserTrackingEnabled the value of the flag +@return Whether the the value is set successfully. It will always return NO in iOS 13 and below. + */ ++ (BOOL)setAdvertiserTrackingEnabled:(BOOL)advertiserTrackingEnabled; + /** Set the data processing options. diff --git a/FBSDKCoreKit/FBSDKCoreKit/FBSDKSettings.m b/FBSDKCoreKit/FBSDKCoreKit/FBSDKSettings.m index b9a8a27e67..886cf19afe 100644 --- a/FBSDKCoreKit/FBSDKCoreKit/FBSDKSettings.m +++ b/FBSDKCoreKit/FBSDKCoreKit/FBSDKSettings.m @@ -18,35 +18,39 @@ #import "FBSDKSettings+Internal.h" -#import "FBSDKAccessTokenCache.h" +#import + #import "FBSDKAccessTokenExpirer.h" #import "FBSDKAppEvents+Internal.h" +#import "FBSDKAppEventsConfiguration.h" +#import "FBSDKAppEventsConfigurationManager.h" #import "FBSDKCoreKit.h" -#import "FBSDKTypeUtility.h" +#import "FBSDKInternalUtility.h" +#import "FBSDKTokenCache.h" #define FBSDKSETTINGS_PLIST_CONFIGURATION_SETTING_IMPL(TYPE, PLIST_KEY, GETTER, SETTER, DEFAULT_VALUE, ENABLE_CACHE) \ -static TYPE *g_##PLIST_KEY = nil; \ -+ (TYPE *)GETTER \ -{ \ - if ((g_##PLIST_KEY == nil) && ENABLE_CACHE) { \ - g_##PLIST_KEY = [[[NSUserDefaults standardUserDefaults] objectForKey:@#PLIST_KEY] copy]; \ - } \ - if (g_##PLIST_KEY == nil) { \ - g_##PLIST_KEY = [[[NSBundle mainBundle] objectForInfoDictionaryKey:@#PLIST_KEY] copy] ?: DEFAULT_VALUE; \ - } \ - return g_##PLIST_KEY; \ -} \ -+ (void)SETTER:(TYPE *)value { \ - g_##PLIST_KEY = [value copy]; \ - if (ENABLE_CACHE) { \ - if (value != nil) { \ - [[NSUserDefaults standardUserDefaults] setObject:value forKey:@#PLIST_KEY]; \ - } else { \ - [[NSUserDefaults standardUserDefaults] removeObjectForKey:@#PLIST_KEY]; \ + static TYPE *g_ ## PLIST_KEY = nil; \ + + (TYPE *)GETTER \ + { \ + if ((g_ ## PLIST_KEY == nil) && ENABLE_CACHE) { \ + g_ ## PLIST_KEY = [[[NSUserDefaults standardUserDefaults] objectForKey:@#PLIST_KEY] copy]; \ + } \ + if (g_ ## PLIST_KEY == nil) { \ + g_ ## PLIST_KEY = [[[NSBundle mainBundle] objectForInfoDictionaryKey:@#PLIST_KEY] copy] ?: DEFAULT_VALUE; \ } \ + return g_ ## PLIST_KEY; \ } \ - [FBSDKSettings _logIfSDKSettingsChanged]; \ -} + + (void)SETTER:(TYPE *)value { \ + g_ ## PLIST_KEY = [value copy]; \ + if (ENABLE_CACHE) { \ + if (value != nil) { \ + [[NSUserDefaults standardUserDefaults] setObject:value forKey:@#PLIST_KEY]; \ + } else { \ + [[NSUserDefaults standardUserDefaults] removeObjectForKey:@#PLIST_KEY]; \ + } \ + } \ + [FBSDKSettings _logIfSDKSettingsChanged]; \ + } FBSDKLoggingBehavior FBSDKLoggingBehaviorAccessTokens = @"include_access_tokens"; FBSDKLoggingBehavior FBSDKLoggingBehaviorPerformanceCharacteristics = @"perf_characteristics"; @@ -59,39 +63,43 @@ + (void)SETTER:(TYPE *)value { \ FBSDKLoggingBehavior FBSDKLoggingBehaviorGraphAPIDebugInfo = @"graph_api_debug_info"; FBSDKLoggingBehavior FBSDKLoggingBehaviorNetworkRequests = @"network_requests"; -static NSObject *g_tokenCache; +static NSObject *g_tokenCache; static NSMutableSet *g_loggingBehaviors; static NSString *const FBSDKSettingsLimitEventAndDataUsage = @"com.facebook.sdk:FBSDKSettingsLimitEventAndDataUsage"; static NSString *const FBSDKSettingsBitmask = @"com.facebook.sdk:FBSDKSettingsBitmask"; static NSString *const FBSDKSettingsDataProcessingOptions = @"com.facebook.sdk:FBSDKSettingsDataProcessingOptions"; +static NSString *const FBSDKSettingsAdvertisingTrackingStatus = @"com.facebook.sdk:FBSDKSettingsAdvertisingTrackingStatus"; +static NSString *const FBSDKSettingsInstallTimestamp = @"com.facebook.sdk:FBSDKSettingsInstallTimestamp"; +static NSString *const FBSDKSettingsSetAdvertiserTrackingEnabledTimestamp = @"com.facebook.sdk:FBSDKSettingsSetAdvertiserTrackingEnabledTimestamp"; static BOOL g_disableErrorRecovery; static NSString *g_userAgentSuffix; static NSString *g_defaultGraphAPIVersion; static FBSDKAccessTokenExpirer *g_accessTokenExpirer; static NSDictionary *g_dataProcessingOptions = nil; +static NSNumber *g_advertiserTrackingStatus = nil; // -// Warning messages for App Event Flags +// Warning messages for App Event Flags // static NSString *const autoLogAppEventsEnabledNotSetWarning = - @": Please set a value for FacebookAutoLogAppEventsEnabled. Set the flag to TRUE if you want " - "to collect app install, app launch and in-app purchase events automatically. To request user consent " - "before collecting data, set the flag value to FALSE, then change to TRUE once user consent is received. " - "Learn more: https://developers.facebook.com/docs/app-events/getting-started-app-events-ios#disable-auto-events."; +@": Please set a value for FacebookAutoLogAppEventsEnabled. Set the flag to TRUE if you want " +"to collect app install, app launch and in-app purchase events automatically. To request user consent " +"before collecting data, set the flag value to FALSE, then change to TRUE once user consent is received. " +"Learn more: https://developers.facebook.com/docs/app-events/getting-started-app-events-ios#disable-auto-events."; static NSString *const advertiserIDCollectionEnabledNotSetWarning = - @": You haven't set a value for FacebookAdvertiserIDCollectionEnabled. Set the flag to TRUE if " - "you want to collect Advertiser ID for better advertising and analytics results."; +@": You haven't set a value for FacebookAdvertiserIDCollectionEnabled. Set the flag to TRUE if " +"you want to collect Advertiser ID for better advertising and analytics results."; static NSString *const advertiserIDCollectionEnabledFalseWarning = - @": The value for FacebookAdvertiserIDCollectionEnabled is currently set to FALSE so you're sending app " - "events without collecting Advertiser ID. This can affect the quality of your advertising and analytics results."; +@": The value for FacebookAdvertiserIDCollectionEnabled is currently set to FALSE so you're sending app " +"events without collecting Advertiser ID. This can affect the quality of your advertising and analytics results."; @implementation FBSDKSettings + (void)initialize { if (self == [FBSDKSettings class]) { - g_tokenCache = [[FBSDKAccessTokenCache alloc] init]; + g_tokenCache = [FBSDKTokenCache new]; g_accessTokenExpirer = [[FBSDKAccessTokenExpirer alloc] init]; [FBSDKSettings _logWarnings]; @@ -107,12 +115,17 @@ + (void)initialize FBSDKSETTINGS_PLIST_CONFIGURATION_SETTING_IMPL(NSString, FacebookDisplayName, displayName, setDisplayName, nil, NO); FBSDKSETTINGS_PLIST_CONFIGURATION_SETTING_IMPL(NSString, FacebookDomainPart, facebookDomainPart, setFacebookDomainPart, nil, NO); FBSDKSETTINGS_PLIST_CONFIGURATION_SETTING_IMPL(NSNumber, FacebookJpegCompressionQuality, _JPEGCompressionQualityNumber, _setJPEGCompressionQualityNumber, @0.9, NO); -FBSDKSETTINGS_PLIST_CONFIGURATION_SETTING_IMPL(NSNumber, FacebookAutoInitEnabled, _autoInitEnabled, _setAutoInitEnabled, @1, YES); FBSDKSETTINGS_PLIST_CONFIGURATION_SETTING_IMPL(NSNumber, FacebookInstrumentEnabled, _instrumentEnabled, _setInstrumentEnabled, @1, YES); FBSDKSETTINGS_PLIST_CONFIGURATION_SETTING_IMPL(NSNumber, FacebookAutoLogAppEventsEnabled, _autoLogAppEventsEnabled, _setAutoLogAppEventsEnabled, @1, YES); FBSDKSETTINGS_PLIST_CONFIGURATION_SETTING_IMPL(NSNumber, FacebookAdvertiserIDCollectionEnabled, _advertiserIDCollectionEnabled, _setAdvertiserIDCollectionEnabled, @1, YES); -FBSDKSETTINGS_PLIST_CONFIGURATION_SETTING_IMPL(NSNumber, FacebookCodelessDebugLogEnabled, _codelessDebugLogEnabled, - _setCodelessDebugLogEnabled, @0, YES); +FBSDKSETTINGS_PLIST_CONFIGURATION_SETTING_IMPL( + NSNumber, + FacebookCodelessDebugLogEnabled, + _codelessDebugLogEnabled, + _setCodelessDebugLogEnabled, + @0, + YES +); + (BOOL)isGraphErrorRecoveryEnabled { @@ -134,19 +147,6 @@ + (void)setJPEGCompressionQuality:(CGFloat)JPEGCompressionQuality [self _setJPEGCompressionQualityNumber:@(JPEGCompressionQuality)]; } -+ (BOOL)isAutoInitEnabled -{ - return [self _autoInitEnabled].boolValue; -} - -+ (void)setAutoInitEnabled:(BOOL)autoInitEnabled -{ - [self _setAutoInitEnabled:@(autoInitEnabled)]; - if (autoInitEnabled) { - [FBSDKApplicationDelegate initializeSDK:nil]; - } -} - + (BOOL)isInstrumentEnabled { return [self _instrumentEnabled].boolValue; @@ -187,6 +187,41 @@ + (void)setAdvertiserIDCollectionEnabled:(BOOL)advertiserIDCollectionEnabled [self _setAdvertiserIDCollectionEnabled:@(advertiserIDCollectionEnabled)]; } ++ (BOOL)isAdvertiserTrackingEnabled +{ + return [FBSDKSettings getAdvertisingTrackingStatus] == FBSDKAdvertisingTrackingAllowed; +} + ++ (BOOL)setAdvertiserTrackingEnabled:(BOOL)enabled; +{ + if (@available(iOS 14.0, *)) { + [FBSDKSettings setAdvertiserTrackingStatus:enabled ? FBSDKAdvertisingTrackingAllowed : FBSDKAdvertisingTrackingDisallowed]; + [self recordSetAdvertiserTrackingEnabled]; + return YES; + } else { + return NO; + } +} + ++ (FBSDKAdvertisingTrackingStatus)getAdvertisingTrackingStatus +{ + if (@available(iOS 14.0, *)) { + if (g_advertiserTrackingStatus == nil) { + g_advertiserTrackingStatus = [[NSUserDefaults standardUserDefaults] objectForKey:FBSDKSettingsAdvertisingTrackingStatus] ?: @([FBSDKAppEventsConfigurationManager cachedAppEventsConfiguration].defaultATEStatus); + } + return g_advertiserTrackingStatus.unsignedIntegerValue; + } else { + // @lint-ignore CLANGTIDY + return ASIdentifierManager.sharedManager.advertisingTrackingEnabled ? FBSDKAdvertisingTrackingAllowed : FBSDKAdvertisingTrackingDisallowed; + } +} + ++ (void)setAdvertiserTrackingStatus:(FBSDKAdvertisingTrackingStatus)status +{ + g_advertiserTrackingStatus = @(status); + [[NSUserDefaults standardUserDefaults] setObject:g_advertiserTrackingStatus forKey:FBSDKSettingsAdvertisingTrackingStatus]; +} + + (BOOL)shouldLimitEventAndDataUsage { NSNumber *storedValue = [[NSUserDefaults standardUserDefaults] objectForKey:FBSDKSettingsLimitEventAndDataUsage]; @@ -230,9 +265,9 @@ + (void)setDataProcessingOptions:(nullable NSArray *)options state:(int)state { NSDictionary *json = @{ - DATA_PROCESSING_OPTIONS: options ?: @[], - DATA_PROCESSING_OPTIONS_COUNTRY: @(country), - DATA_PROCESSING_OPTIONS_STATE: @(state), + DATA_PROCESSING_OPTIONS : options ?: @[], + DATA_PROCESSING_OPTIONS_COUNTRY : @(country), + DATA_PROCESSING_OPTIONS_STATE : @(state), }; g_dataProcessingOptions = json; NSData *data = [NSKeyedArchiver archivedDataWithRootObject:g_dataProcessingOptions]; @@ -241,6 +276,7 @@ + (void)setDataProcessingOptions:(nullable NSArray *)options forKey:FBSDKSettingsDataProcessingOptions]; } } + #pragma clang diagnostic pop + (void)setLoggingBehaviors:(NSSet *)loggingBehaviors @@ -279,12 +315,12 @@ + (NSString *)sdkVersion #pragma mark - Internal -+ (NSObject *)accessTokenCache ++ (NSObject *)tokenCache { return g_tokenCache; } -+ (void)setAccessTokenCache:(NSObject *)cache ++ (void)setTokenCache:(NSObject *)cache { if (g_tokenCache != cache) { g_tokenCache = cache; @@ -305,8 +341,7 @@ + (void)setUserAgentSuffix:(NSString *)suffix + (void)setGraphAPIVersion:(NSString *)version { - if (![g_defaultGraphAPIVersion isEqualToString:version]) - { + if (![g_defaultGraphAPIVersion isEqualToString:version]) { g_defaultGraphAPIVersion = version; } } @@ -352,6 +387,7 @@ + (NSNumber *)appEventSettingsForUserDefaultsKey:(NSString *)userDefaultsKey } return g_dataProcessingOptions; } + #pragma clang diagnostic pop + (BOOL)isDataProcessingRestricted @@ -385,8 +421,8 @@ + (void)_logWarnings + (void)_logIfSDKSettingsChanged { NSInteger bitmask = 0; - NSInteger bit = 0; - bitmask |= ([FBSDKSettings isAutoInitEnabled] ? 1 : 0) << bit++; + // Starting at 1 to maintain the meaning of the bits since the autoInit flag was removed. + NSInteger bit = 1; bitmask |= ([FBSDKSettings isAutoLogAppEventsEnabled] ? 1 : 0) << bit++; bitmask |= ([FBSDKSettings isAdvertiserIDCollectionEnabled] ? 1 : 0) << bit++; @@ -394,10 +430,9 @@ + (void)_logIfSDKSettingsChanged if (previousBitmask != bitmask) { [[NSUserDefaults standardUserDefaults] setInteger:bitmask forKey:FBSDKSettingsBitmask]; - NSArray *keys = @[@"FacebookAutoInitEnabled", - @"FacebookAutoLogAppEventsEnabled", + NSArray *keys = @[@"FacebookAutoLogAppEventsEnabled", @"FacebookAdvertiserIDCollectionEnabled"]; - NSArray *defaultValues = @[@YES, @YES, @YES]; + NSArray *defaultValues = @[@YES, @YES]; NSInteger initialBitmask = 0; NSInteger usageBitmask = 0; for (int i = 0; i < keys.count; i++) { @@ -407,14 +442,56 @@ + (void)_logIfSDKSettingsChanged usageBitmask |= (plistValue != nil ? 1 : 0) << i; } [FBSDKAppEvents logInternalEvent:@"fb_sdk_settings_changed" - parameters:@{@"usage": @(usageBitmask), - @"initial": @(initialBitmask), - @"previous":@(previousBitmask), - @"current": @(bitmask)} + parameters:@{@"usage" : @(usageBitmask), + @"initial" : @(initialBitmask), + @"previous" : @(previousBitmask), + @"current" : @(bitmask)} isImplicitlyLogged:YES]; } } ++ (void)recordInstall +{ + NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults]; + if (![defaults objectForKey:FBSDKSettingsInstallTimestamp]) { + [defaults setObject:[NSDate date] forKey:FBSDKSettingsInstallTimestamp]; + } +} + ++ (void)recordSetAdvertiserTrackingEnabled +{ + [[NSUserDefaults standardUserDefaults] setObject:[NSDate date] forKey:FBSDKSettingsSetAdvertiserTrackingEnabledTimestamp]; +} + ++ (BOOL)isEventDelayTimerExpired +{ + NSDate *timestamp = [[NSUserDefaults standardUserDefaults] objectForKey:FBSDKSettingsInstallTimestamp]; + if (timestamp) { + return [[NSDate date] timeIntervalSinceDate:timestamp] > 86400; + } + return NO; +} + ++ (BOOL)isSetATETimeExceedsInstallTime +{ + NSDate *installTimestamp = [[NSUserDefaults standardUserDefaults] objectForKey:FBSDKSettingsInstallTimestamp]; + NSDate *setATETimestamp = [[NSUserDefaults standardUserDefaults] objectForKey:FBSDKSettingsSetAdvertiserTrackingEnabledTimestamp]; + if (installTimestamp && setATETimestamp) { + return [setATETimestamp timeIntervalSinceDate:installTimestamp] > 86400; + } + return NO; +} + ++ (NSDate *_Nullable)getInstallTimestamp +{ + return [[NSUserDefaults standardUserDefaults] objectForKey:FBSDKSettingsInstallTimestamp]; +} + ++ (NSDate *_Nullable)getSetAdvertiserTrackingEnabledTimestamp +{ + return [[NSUserDefaults standardUserDefaults] objectForKey:FBSDKSettingsSetAdvertiserTrackingEnabledTimestamp]; +} + #pragma mark - Internal - Graph API Debug + (void)updateGraphAPIDebugBehavior @@ -437,4 +514,85 @@ + (NSString *)graphAPIDebugParamValue return nil; } +#pragma mark - Testability + +#if DEBUG + ++ (void)resetLoggingBehaviorsCache +{ + g_loggingBehaviors = nil; +} + ++ (void)resetTokenCache +{ + g_tokenCache = nil; +} + ++ (void)resetFacebookAppIDCache +{ + g_FacebookAppID = nil; +} + ++ (void)resetFacebookUrlSchemeSuffixCache +{ + g_FacebookUrlSchemeSuffix = nil; +} + ++ (void)resetFacebookClientTokenCache +{ + g_FacebookClientToken = nil; +} + ++ (void)resetFacebookDisplayNameCache +{ + g_FacebookDisplayName = nil; +} + ++ (void)resetFacebookDomainPartCache +{ + g_FacebookDomainPart = nil; +} + ++ (void)resetFacebookJpegCompressionQualityCache +{ + g_FacebookJpegCompressionQuality = nil; +} + ++ (void)resetFacebookInstrumentEnabledCache +{ + g_FacebookInstrumentEnabled = nil; +} + ++ (void)resetFacebookAutoLogAppEventsEnabledCache +{ + g_FacebookAutoLogAppEventsEnabled = nil; +} + ++ (void)resetFacebookAdvertiserIDCollectionEnabledCache +{ + g_FacebookAdvertiserIDCollectionEnabled = nil; +} + ++ (void)resetAdvertiserTrackingStatusCache +{ + g_advertiserTrackingStatus = nil; +} + ++ (void)resetUserAgentSuffixCache +{ + g_userAgentSuffix = nil; +} + ++ (void)resetFacebookCodelessDebugLogEnabledCache +{ + g_FacebookCodelessDebugLogEnabled = nil; +} + ++ (void)resetDataProcessingOptionsCache +{ + g_dataProcessingOptions = nil; +} + +#endif + @end diff --git a/FBSDKCoreKit/FBSDKCoreKit/FBSDKTestUsersManager.m b/FBSDKCoreKit/FBSDKCoreKit/FBSDKTestUsersManager.m index ee36c081e8..85f4550c7a 100644 --- a/FBSDKCoreKit/FBSDKCoreKit/FBSDKTestUsersManager.m +++ b/FBSDKCoreKit/FBSDKCoreKit/FBSDKTestUsersManager.m @@ -25,7 +25,7 @@ static NSString *const kAccountsDictionaryPermissionsKey = @"permissions"; static NSMutableDictionary *gInstancesDictionary; -@interface FBSDKTestUsersManager() +@interface FBSDKTestUsersManager () - (instancetype)initWithAppID:(NSString *)appID appSecret:(NSString *)appSecret NS_DESIGNATED_INITIALIZER; @end @@ -35,11 +35,12 @@ @implementation FBSDKTestUsersManager NSString *_appSecret; // dictionary with format like: // { user_id : { kAccountsDictionaryTokenKey : "token", - // kAccountsDictionaryPermissionsKey : [ permissions ] } + // kAccountsDictionaryPermissionsKey : [ permissions ] } NSMutableDictionary *_accounts; } -- (instancetype)initWithAppID:(NSString *)appID appSecret:(NSString *)appSecret { +- (instancetype)initWithAppID:(NSString *)appID appSecret:(NSString *)appSecret +{ if ((self = [super init])) { _appID = [appID copy]; _appSecret = [appSecret copy]; @@ -48,7 +49,8 @@ - (instancetype)initWithAppID:(NSString *)appID appSecret:(NSString *)appSecret return self; } -+ (instancetype)sharedInstanceForAppID:(NSString *)appID appSecret:(NSString *)appSecret { ++ (instancetype)sharedInstanceForAppID:(NSString *)appID appSecret:(NSString *)appSecret +{ static dispatch_once_t onceToken; dispatch_once(&onceToken, ^{ gInstancesDictionary = [NSMutableDictionary dictionary]; @@ -63,11 +65,12 @@ + (instancetype)sharedInstanceForAppID:(NSString *)appID appSecret:(NSString *)a - (void)requestTestAccountTokensWithArraysOfPermissions:(NSArray *> *)arraysOfPermissions createIfNotFound:(BOOL)createIfNotFound - completionHandler:(FBSDKAccessTokensBlock)handler { + completionHandler:(FBSDKAccessTokensBlock)handler +{ arraysOfPermissions = arraysOfPermissions ?: @[[NSSet set]]; // wrap work in a block so that we can chain it to after a fetch of existing accounts if we need to. - void (^helper)(NSError *) = ^(NSError *error){ + void (^helper)(NSError *) = ^(NSError *error) { if (error) { if (handler) { handler(@[], error); @@ -79,7 +82,7 @@ - (void)requestTestAccountTokensWithArraysOfPermissions:(NSArray_accounts[userId][kAccountsDictionaryTokenKey] = token; expectedTestAccounts++; [permissionConnection addRequest:[[FBSDKGraphRequest alloc] initWithGraphPath:[NSString stringWithFormat:@"%@?fields=permissions", userId] - parameters:@{} + parameters:@{@"fields" : @""} tokenString:self.appAccessToken version:nil HTTPMethod:FBSDKHTTPMethodGET] @@ -310,7 +318,7 @@ - (void)fetchExistingTestAccountsWithAfterCursor:(NSString *)after handler:(FBSD } } } - ]; + ]; } } afterCursor = result[@"paging"][@"cursors"][@"after"]; @@ -327,4 +335,5 @@ - (void)fetchExistingTestAccountsWithAfterCursor:(NSString *)after handler:(FBSD }]; [connection start]; } + @end diff --git a/FBSDKCoreKit/FBSDKCoreKit/FBSDKURL.m b/FBSDKCoreKit/FBSDKCoreKit/FBSDKURL.m index 292e2afb21..cb1efc76e3 100644 --- a/FBSDKCoreKit/FBSDKCoreKit/FBSDKURL.m +++ b/FBSDKCoreKit/FBSDKCoreKit/FBSDKURL.m @@ -20,105 +20,108 @@ #if !TARGET_OS_TV -#import "FBSDKURL_Internal.h" - -#import "FBSDKAppLinkTarget.h" -#import "FBSDKAppLink_Internal.h" -#import "FBSDKCoreKit+Internal.h" -#import "FBSDKMeasurementEvent_Internal.h" -#import "FBSDKSettings.h" + #import "FBSDKAppLinkTarget.h" + #import "FBSDKAppLink_Internal.h" + #import "FBSDKCoreKit+Internal.h" + #import "FBSDKMeasurementEvent_Internal.h" + #import "FBSDKSettings.h" + #import "FBSDKURL_Internal.h" NSString *const AutoAppLinkFlagKey = @"is_auto_applink"; @implementation FBSDKURL -- (instancetype)initWithURL:(NSURL *)url forOpenInboundURL:(BOOL)forOpenURLEvent sourceApplication:(NSString *)sourceApplication forRenderBackToReferrerBar:(BOOL)forRenderBackToReferrerBar { - self = [super init]; - if (!self) return nil; - - _inputURL = url; - _targetURL = url; - - // Parse the query string parameters for the base URL - NSDictionary *baseQuery = [FBSDKURL queryParametersForURL:url]; - _inputQueryParameters = baseQuery; - _targetQueryParameters = baseQuery; - - // Check for applink_data - NSString *appLinkDataString = baseQuery[FBSDKAppLinkDataParameterName]; - if (appLinkDataString) { - // Try to parse the JSON - NSError *error = nil; - NSDictionary *applinkData = - [FBSDKTypeUtility JSONObjectWithData:[appLinkDataString dataUsingEncoding:NSUTF8StringEncoding] - options:0 - error:&error]; - if (!error && [applinkData isKindOfClass:[NSDictionary class]]) { - // If the version is not specified, assume it is 1. - NSString *version = applinkData[FBSDKAppLinkVersionKeyName] ?: @"1.0"; - NSString *target = applinkData[FBSDKAppLinkTargetKeyName]; - if ([version isKindOfClass:[NSString class]] && - [version isEqual:FBSDKAppLinkVersion]) { - // There's applink data! The target should actually be the applink target. - _appLinkData = applinkData; - id applinkExtras = applinkData[FBSDKAppLinkExtrasKeyName]; - if (applinkExtras && [applinkExtras isKindOfClass:[NSDictionary class]]) { - _appLinkExtras = applinkExtras; - } - _targetURL = ([target isKindOfClass:[NSString class]] ? [NSURL URLWithString:target] : url); - _targetQueryParameters = [FBSDKURL queryParametersForURL:_targetURL]; - - NSDictionary *refererAppLink = _appLinkData[FBSDKAppLinkRefererAppLink]; - NSString *refererURLString = refererAppLink[FBSDKAppLinkRefererUrl]; - NSString *refererAppName = refererAppLink[FBSDKAppLinkRefererAppName]; - - if (refererURLString && refererAppName) { - FBSDKAppLinkTarget *appLinkTarget = [FBSDKAppLinkTarget appLinkTargetWithURL:[NSURL URLWithString:refererURLString] - appStoreId:nil - appName:refererAppName]; - _appLinkReferer = [FBSDKAppLink appLinkWithSourceURL:[NSURL URLWithString:refererURLString] - targets:@[ appLinkTarget ] - webURL:nil - isBackToReferrer:YES]; - } - - // Raise Measurement Event - NSString *const EVENT_YES_VAL = @"1"; - NSString *const EVENT_NO_VAL = @"0"; - NSMutableDictionary *logData = [[NSMutableDictionary alloc] init]; - [FBSDKTypeUtility dictionary:logData setObject:version forKey:@"version"]; - if (refererURLString) { - [FBSDKTypeUtility dictionary:logData setObject:refererURLString forKey:@"refererURL"]; - } - if (refererAppName) { - [FBSDKTypeUtility dictionary:logData setObject:refererAppName forKey:@"refererAppName"]; - } - if (sourceApplication) { - [FBSDKTypeUtility dictionary:logData setObject:sourceApplication forKey:@"sourceApplication"]; - } - if (_targetURL.absoluteString) { - [FBSDKTypeUtility dictionary:logData setObject:_targetURL.absoluteString forKey:@"targetURL"]; - } - if (_inputURL.absoluteString) { - [FBSDKTypeUtility dictionary:logData setObject:_inputURL.absoluteString forKey:@"inputURL"]; - } - if (_inputURL.scheme) { - [FBSDKTypeUtility dictionary:logData setObject:_inputURL.scheme forKey:@"inputURLScheme"]; - } - [FBSDKTypeUtility dictionary:logData setObject:forRenderBackToReferrerBar ? EVENT_YES_VAL : EVENT_NO_VAL forKey:@"forRenderBackToReferrerBar"]; - [FBSDKTypeUtility dictionary:logData setObject:forOpenURLEvent ? EVENT_YES_VAL : EVENT_NO_VAL forKey:@"forOpenUrl"]; - [FBSDKMeasurementEvent postNotificationForEventName:FBSDKAppLinkParseEventName args:logData]; - if (forOpenURLEvent) { - [FBSDKMeasurementEvent postNotificationForEventName:FBSDKAppLinkNavigateInEventName args:logData]; - } - } +- (instancetype)initWithURL:(NSURL *)url forOpenInboundURL:(BOOL)forOpenURLEvent sourceApplication:(NSString *)sourceApplication forRenderBackToReferrerBar:(BOOL)forRenderBackToReferrerBar +{ + self = [super init]; + if (!self) { + return nil; + } + + _inputURL = url; + _targetURL = url; + + // Parse the query string parameters for the base URL + NSDictionary *baseQuery = [FBSDKURL queryParametersForURL:url]; + _inputQueryParameters = baseQuery; + _targetQueryParameters = baseQuery; + + // Check for applink_data + NSString *appLinkDataString = baseQuery[FBSDKAppLinkDataParameterName]; + if (appLinkDataString) { + // Try to parse the JSON + NSError *error = nil; + NSDictionary *applinkData = + [FBSDKTypeUtility JSONObjectWithData:[appLinkDataString dataUsingEncoding:NSUTF8StringEncoding] + options:0 + error:&error]; + if (!error && [applinkData isKindOfClass:[NSDictionary class]]) { + // If the version is not specified, assume it is 1. + NSString *version = applinkData[FBSDKAppLinkVersionKeyName] ?: @"1.0"; + NSString *target = applinkData[FBSDKAppLinkTargetKeyName]; + if ([version isKindOfClass:[NSString class]] + && [version isEqual:FBSDKAppLinkVersion]) { + // There's applink data! The target should actually be the applink target. + _appLinkData = applinkData; + id applinkExtras = applinkData[FBSDKAppLinkExtrasKeyName]; + if (applinkExtras && [applinkExtras isKindOfClass:[NSDictionary class]]) { + _appLinkExtras = applinkExtras; + } + _targetURL = ([target isKindOfClass:[NSString class]] ? [NSURL URLWithString:target] : url); + _targetQueryParameters = [FBSDKURL queryParametersForURL:_targetURL]; + + NSDictionary *refererAppLink = _appLinkData[FBSDKAppLinkRefererAppLink]; + NSString *refererURLString = refererAppLink[FBSDKAppLinkRefererUrl]; + NSString *refererAppName = refererAppLink[FBSDKAppLinkRefererAppName]; + + if (refererURLString && refererAppName) { + FBSDKAppLinkTarget *appLinkTarget = [FBSDKAppLinkTarget appLinkTargetWithURL:[NSURL URLWithString:refererURLString] + appStoreId:nil + appName:refererAppName]; + _appLinkReferer = [FBSDKAppLink appLinkWithSourceURL:[NSURL URLWithString:refererURLString] + targets:@[appLinkTarget] + webURL:nil + isBackToReferrer:YES]; + } + + // Raise Measurement Event + NSString *const EVENT_YES_VAL = @"1"; + NSString *const EVENT_NO_VAL = @"0"; + NSMutableDictionary *logData = [[NSMutableDictionary alloc] init]; + [FBSDKTypeUtility dictionary:logData setObject:version forKey:@"version"]; + if (refererURLString) { + [FBSDKTypeUtility dictionary:logData setObject:refererURLString forKey:@"refererURL"]; + } + if (refererAppName) { + [FBSDKTypeUtility dictionary:logData setObject:refererAppName forKey:@"refererAppName"]; + } + if (sourceApplication) { + [FBSDKTypeUtility dictionary:logData setObject:sourceApplication forKey:@"sourceApplication"]; } + if (_targetURL.absoluteString) { + [FBSDKTypeUtility dictionary:logData setObject:_targetURL.absoluteString forKey:@"targetURL"]; + } + if (_inputURL.absoluteString) { + [FBSDKTypeUtility dictionary:logData setObject:_inputURL.absoluteString forKey:@"inputURL"]; + } + if (_inputURL.scheme) { + [FBSDKTypeUtility dictionary:logData setObject:_inputURL.scheme forKey:@"inputURLScheme"]; + } + [FBSDKTypeUtility dictionary:logData setObject:forRenderBackToReferrerBar ? EVENT_YES_VAL : EVENT_NO_VAL forKey:@"forRenderBackToReferrerBar"]; + [FBSDKTypeUtility dictionary:logData setObject:forOpenURLEvent ? EVENT_YES_VAL : EVENT_NO_VAL forKey:@"forOpenUrl"]; + [FBSDKMeasurementEvent postNotificationForEventName:FBSDKAppLinkParseEventName args:logData]; + if (forOpenURLEvent) { + [FBSDKMeasurementEvent postNotificationForEventName:FBSDKAppLinkNavigateInEventName args:logData]; + } + } } + } - return self; + return self; } -- (BOOL)isAutoAppLink { +- (BOOL)isAutoAppLink +{ NSString *host = self.targetURL.host; NSString *scheme = self.targetURL.scheme; NSString *expectedHost = @"applinks"; @@ -127,37 +130,41 @@ - (BOOL)isAutoAppLink { return autoFlag && [expectedHost isEqual:host] && [expectedScheme isEqual:scheme]; } -+ (instancetype)URLWithURL:(NSURL *)url { - return [[FBSDKURL alloc] initWithURL:url forOpenInboundURL:NO sourceApplication:nil forRenderBackToReferrerBar:NO]; ++ (instancetype)URLWithURL:(NSURL *)url +{ + return [[FBSDKURL alloc] initWithURL:url forOpenInboundURL:NO sourceApplication:nil forRenderBackToReferrerBar:NO]; } -+ (instancetype)URLWithInboundURL:(NSURL *)url sourceApplication:(NSString *)sourceApplication { - return [[FBSDKURL alloc] initWithURL:url forOpenInboundURL:YES sourceApplication:sourceApplication forRenderBackToReferrerBar:NO]; ++ (instancetype)URLWithInboundURL:(NSURL *)url sourceApplication:(NSString *)sourceApplication +{ + return [[FBSDKURL alloc] initWithURL:url forOpenInboundURL:YES sourceApplication:sourceApplication forRenderBackToReferrerBar:NO]; } -+ (instancetype)URLForRenderBackToReferrerBarURL:(NSURL *)url { - return [[FBSDKURL alloc] initWithURL:url forOpenInboundURL:NO sourceApplication:nil forRenderBackToReferrerBar:YES]; ++ (instancetype)URLForRenderBackToReferrerBarURL:(NSURL *)url +{ + return [[FBSDKURL alloc] initWithURL:url forOpenInboundURL:NO sourceApplication:nil forRenderBackToReferrerBar:YES]; } -+ (NSDictionary *)queryParametersForURL:(NSURL *)url { - NSMutableDictionary *parameters = [NSMutableDictionary dictionary]; - NSString *query = url.query; - if ([query isEqualToString:@""]) { - return @{}; - } - NSArray *queryComponents = [query componentsSeparatedByString:@"&"]; - for (NSString *component in queryComponents) { - NSRange equalsLocation = [component rangeOfString:@"="]; - if (equalsLocation.location == NSNotFound) { - // There's no equals, so associate the key with NSNull - [FBSDKTypeUtility dictionary:parameters setObject:[NSNull null] forKey:[FBSDKBasicUtility URLDecode:component]]; - } else { - NSString *key = [FBSDKBasicUtility URLDecode:[component substringToIndex:equalsLocation.location]]; - NSString *value = [FBSDKBasicUtility URLDecode:[component substringFromIndex:equalsLocation.location + 1]]; - [FBSDKTypeUtility dictionary:parameters setObject:value forKey:key]; - } ++ (NSDictionary *)queryParametersForURL:(NSURL *)url +{ + NSMutableDictionary *parameters = [NSMutableDictionary dictionary]; + NSString *query = url.query; + if ([query isEqualToString:@""]) { + return @{}; + } + NSArray *queryComponents = [query componentsSeparatedByString:@"&"]; + for (NSString *component in queryComponents) { + NSRange equalsLocation = [component rangeOfString:@"="]; + if (equalsLocation.location == NSNotFound) { + // There's no equals, so associate the key with NSNull + [FBSDKTypeUtility dictionary:parameters setObject:[NSNull null] forKey:[FBSDKBasicUtility URLDecode:component]]; + } else { + NSString *key = [FBSDKBasicUtility URLDecode:[component substringToIndex:equalsLocation.location]]; + NSString *value = [FBSDKBasicUtility URLDecode:[component substringFromIndex:equalsLocation.location + 1]]; + [FBSDKTypeUtility dictionary:parameters setObject:value forKey:key]; } - return [NSDictionary dictionaryWithDictionary:parameters]; + } + return [NSDictionary dictionaryWithDictionary:parameters]; } @end diff --git a/FBSDKCoreKit/FBSDKCoreKit/FBSDKUtility.m b/FBSDKCoreKit/FBSDKCoreKit/FBSDKUtility.m index 8a32c665e0..84e88eb853 100644 --- a/FBSDKCoreKit/FBSDKCoreKit/FBSDKUtility.m +++ b/FBSDKCoreKit/FBSDKCoreKit/FBSDKUtility.m @@ -46,15 +46,19 @@ + (NSString *)URLEncode:(NSString *)value + (dispatch_source_t)startGCDTimerWithInterval:(double)interval block:(dispatch_block_t)block { - dispatch_source_t timer = dispatch_source_create(DISPATCH_SOURCE_TYPE_TIMER, // source type - 0, // handle - 0, // mask - dispatch_get_main_queue()); // queue - - dispatch_source_set_timer(timer, // dispatch source - dispatch_time(DISPATCH_TIME_NOW, interval * NSEC_PER_SEC), // start - interval * NSEC_PER_SEC, // interval - 0 * NSEC_PER_SEC); // leeway + dispatch_source_t timer = dispatch_source_create( + DISPATCH_SOURCE_TYPE_TIMER, // source type + 0, // handle + 0, // mask + dispatch_get_main_queue() + ); // queue + + dispatch_source_set_timer( + timer, // dispatch source + dispatch_time(DISPATCH_TIME_NOW, interval * NSEC_PER_SEC), // start + interval * NSEC_PER_SEC, // interval + 0 * NSEC_PER_SEC + ); // leeway dispatch_source_set_event_handler(timer, block); @@ -72,26 +76,7 @@ + (void)stopGCDTimer:(dispatch_source_t)timer + (NSString *)SHA256Hash:(NSObject *)input { - NSData *data = nil; - - if ([input isKindOfClass:[NSData class]]) { - data = (NSData *)input; - } else if ([input isKindOfClass:[NSString class]]) { - data = [(NSString *)input dataUsingEncoding:NSUTF8StringEncoding]; - } - - if (!data) { - return nil; - } - - uint8_t digest[CC_SHA256_DIGEST_LENGTH]; - CC_SHA256(data.bytes, (CC_LONG)data.length, digest); - NSMutableString *hashed = [NSMutableString stringWithCapacity:CC_SHA256_DIGEST_LENGTH * 2]; - for (int i = 0; i < CC_SHA256_DIGEST_LENGTH; i++) { - [hashed appendFormat:@"%02x", digest[i]]; - } - - return [hashed copy]; + return [FBSDKBasicUtility SHA256Hash:input]; } @end diff --git a/FBSDKCoreKit/FBSDKCoreKit/GraphAPI/FBSDKGraphErrorRecoveryProcessor.m b/FBSDKCoreKit/FBSDKCoreKit/GraphAPI/FBSDKGraphErrorRecoveryProcessor.m index b3b37e9682..187acd972a 100644 --- a/FBSDKCoreKit/FBSDKCoreKit/GraphAPI/FBSDKGraphErrorRecoveryProcessor.m +++ b/FBSDKCoreKit/FBSDKCoreKit/GraphAPI/FBSDKGraphErrorRecoveryProcessor.m @@ -20,18 +20,18 @@ #if !TARGET_OS_TV -#import "FBSDKGraphErrorRecoveryProcessor.h" + #import "FBSDKGraphErrorRecoveryProcessor.h" -#import "FBSDKCoreKit+Internal.h" -#import "FBSDKErrorRecoveryAttempter.h" + #import "FBSDKCoreKit+Internal.h" + #import "FBSDKErrorRecoveryAttempter.h" -@interface FBSDKGraphErrorRecoveryProcessor() +@interface FBSDKGraphErrorRecoveryProcessor () { FBSDKErrorRecoveryAttempter *_recoveryAttempter; NSError *_error; } -@property (nonatomic, strong, nullable) iddelegate; +@property (nullable, nonatomic, strong) id delegate; @end @@ -48,17 +48,17 @@ - (BOOL)processError:(NSError *)error request:(FBSDKGraphRequest *)request deleg FBSDKGraphRequestError errorCategory = [error.userInfo[FBSDKGraphRequestErrorKey] unsignedIntegerValue]; switch (errorCategory) { - case FBSDKGraphRequestErrorTransient : + case FBSDKGraphRequestErrorTransient: [self.delegate processorDidAttemptRecovery:self didRecover:YES error:nil]; self.delegate = nil; return YES; - case FBSDKGraphRequestErrorRecoverable : - if ([request.tokenString isEqualToString:[FBSDKAccessToken currentAccessToken].tokenString]) { + case FBSDKGraphRequestErrorRecoverable: + if (request.tokenString && [request.tokenString isEqualToString:[FBSDKAccessToken currentAccessToken].tokenString]) { _recoveryAttempter = error.recoveryAttempter; // Set up a block to do the typical recovery work so that we can chain it for ios auth special cases. // the block returns YES if recovery UI is started (meaning we wait for the alertviewdelegate to resume control flow). - BOOL (^standardRecoveryWork)(void) = ^BOOL{ + BOOL (^standardRecoveryWork)(void) = ^BOOL { NSArray *recoveryOptionsTitles = error.userInfo[NSLocalizedRecoveryOptionsErrorKey]; if (recoveryOptionsTitles.count > 0 && self->_recoveryAttempter) { NSString *recoverySuggestion = error.userInfo[NSLocalizedRecoverySuggestionErrorKey]; @@ -74,16 +74,20 @@ - (BOOL)processError:(NSError *)error request:(FBSDKGraphRequest *)request deleg return standardRecoveryWork(); } return NO; - case FBSDKGraphRequestErrorOther : - if ([request.tokenString isEqualToString:[FBSDKAccessToken currentAccessToken].tokenString]) { + case FBSDKGraphRequestErrorOther: + if (request.tokenString && [request.tokenString isEqualToString:[FBSDKAccessToken currentAccessToken].tokenString]) { NSString *message = error.userInfo[FBSDKErrorLocalizedDescriptionKey]; NSString *title = error.userInfo[FBSDKErrorLocalizedTitleKey]; if (message) { dispatch_async(dispatch_get_main_queue(), ^{ NSString *localizedOK = - NSLocalizedStringWithDefaultValue(@"ErrorRecovery.Alert.OK", @"FacebookSDK", [FBSDKInternalUtility bundleForStrings], - @"OK", - @"The title of the label to dismiss the alert when presenting user facing error messages"); + NSLocalizedStringWithDefaultValue( + @"ErrorRecovery.Alert.OK", + @"FacebookSDK", + [FBSDKInternalUtility bundleForStrings], + @"OK", + @"The title of the label to dismiss the alert when presenting user facing error messages" + ); [self displayAlertWithTitle:title message:message cancelButtonTitle:localizedOK]; }); } @@ -93,7 +97,7 @@ - (BOOL)processError:(NSError *)error request:(FBSDKGraphRequest *)request deleg return NO; } -#pragma mark - UIAlertController support + #pragma mark - UIAlertController support - (void)displayAlertWithRecoverySuggestion:(NSString *)recoverySuggestion recoveryOptionsTitles:(NSArray *)recoveryOptionsTitles { @@ -104,7 +108,7 @@ - (void)displayAlertWithRecoverySuggestion:(NSString *)recoverySuggestion recove NSString *title = [FBSDKTypeUtility array:recoveryOptionsTitles objectAtIndex:i]; UIAlertAction *option = [UIAlertAction actionWithTitle:title style:UIAlertActionStyleDefault - handler:^(UIAlertAction * _Nonnull action) { + handler:^(UIAlertAction *_Nonnull action) { [self->_recoveryAttempter attemptRecoveryFromError:self->_error optionIndex:i delegate:self @@ -126,7 +130,7 @@ - (void)displayAlertWithTitle:(NSString *)title message:(NSString *)message canc preferredStyle:UIAlertControllerStyleAlert]; UIAlertAction *OKAction = [UIAlertAction actionWithTitle:localizedOK style:UIAlertActionStyleCancel - handler:^(UIAlertAction * _Nonnull action) { + handler:^(UIAlertAction *_Nonnull action) { [self->_recoveryAttempter attemptRecoveryFromError:self->_error optionIndex:0 delegate:self @@ -140,7 +144,7 @@ - (void)displayAlertWithTitle:(NSString *)title message:(NSString *)message canc completion:nil]; } -#pragma mark - FBSDKErrorRecoveryAttempting "delegate" + #pragma mark - FBSDKErrorRecoveryAttempting "delegate" - (void)didPresentErrorWithRecovery:(BOOL)didRecover contextInfo:(void *)contextInfo { diff --git a/FBSDKCoreKit/FBSDKCoreKit/GraphAPI/FBSDKGraphRequest.m b/FBSDKCoreKit/FBSDKCoreKit/GraphAPI/FBSDKGraphRequest.m index 1157fcb3b0..cd0e26895a 100644 --- a/FBSDKCoreKit/FBSDKCoreKit/GraphAPI/FBSDKGraphRequest.m +++ b/FBSDKCoreKit/FBSDKCoreKit/GraphAPI/FBSDKGraphRequest.m @@ -33,26 +33,33 @@ FBSDKHTTPMethod FBSDKHTTPMethodPOST = @"POST"; FBSDKHTTPMethod FBSDKHTTPMethodDELETE = @"DELETE"; -@interface FBSDKGraphRequest() +@interface FBSDKGraphRequest () @property (nonatomic, assign) FBSDKGraphRequestFlags flags; -@property (nonatomic, copy, readwrite) FBSDKHTTPMethod HTTPMethod; +@property (nonatomic, readwrite, copy) FBSDKHTTPMethod HTTPMethod; @end @implementation FBSDKGraphRequest @synthesize HTTPMethod; -- (instancetype)initWithGraphPath:(NSString *)graphPath { - return [self initWithGraphPath:graphPath parameters:@{}]; +- (instancetype)initWithGraphPath:(NSString *)graphPath +{ + return [self initWithGraphPath:graphPath parameters:@{@"fields" : @""}]; } - (instancetype)initWithGraphPath:(NSString *)graphPath - HTTPMethod:(FBSDKHTTPMethod)method { - return [self initWithGraphPath:graphPath parameters:@{} HTTPMethod:method]; + HTTPMethod:(FBSDKHTTPMethod)method +{ + if (method == FBSDKHTTPMethodGET) { + return [self initWithGraphPath:graphPath parameters:@{@"fields" : @""} HTTPMethod:method]; + } else { + return [self initWithGraphPath:graphPath parameters:@{} HTTPMethod:method]; + } } - (instancetype)initWithGraphPath:(NSString *)graphPath - parameters:(NSDictionary *)parameters { + parameters:(NSDictionary *)parameters +{ return [self initWithGraphPath:graphPath parameters:parameters flags:FBSDKGraphRequestFlagNone]; @@ -60,7 +67,8 @@ - (instancetype)initWithGraphPath:(NSString *)graphPath - (instancetype)initWithGraphPath:(NSString *)graphPath parameters:(NSDictionary *)parameters - HTTPMethod:(FBSDKHTTPMethod)method { + HTTPMethod:(FBSDKHTTPMethod)method +{ return [self initWithGraphPath:graphPath parameters:parameters tokenString:[FBSDKAccessToken currentAccessToken].tokenString @@ -70,7 +78,8 @@ - (instancetype)initWithGraphPath:(NSString *)graphPath - (instancetype)initWithGraphPath:(NSString *)graphPath parameters:(NSDictionary *)parameters - flags:(FBSDKGraphRequestFlags)flags { + flags:(FBSDKGraphRequestFlags)flags +{ return [self initWithGraphPath:graphPath parameters:parameters tokenString:[FBSDKAccessToken currentAccessToken].tokenString @@ -82,7 +91,8 @@ - (instancetype)initWithGraphPath:(NSString *)graphPath parameters:(NSDictionary *)parameters tokenString:(NSString *)tokenString HTTPMethod:(FBSDKHTTPMethod)method - flags:(FBSDKGraphRequestFlags)flags { + flags:(FBSDKGraphRequestFlags)flags +{ if ((self = [self initWithGraphPath:graphPath parameters:parameters tokenString:tokenString @@ -97,7 +107,8 @@ - (instancetype)initWithGraphPath:(NSString *)graphPath parameters:(NSDictionary *)parameters tokenString:(NSString *)tokenString version:(NSString *)version - HTTPMethod:(FBSDKHTTPMethod)method { + HTTPMethod:(FBSDKHTTPMethod)method +{ if ((self = [super init])) { _tokenString = tokenString ? [tokenString copy] : nil; _version = version ? [version copy] : [FBSDKSettings graphAPIVersion]; @@ -139,33 +150,35 @@ - (BOOL)hasAttachments + (BOOL)isAttachment:(id)item { - return ([item isKindOfClass:[UIImage class]] || - [item isKindOfClass:[NSData class]] || - [item isKindOfClass:[FBSDKGraphRequestDataAttachment class]]); + return ([item isKindOfClass:[UIImage class]] + || [item isKindOfClass:[NSData class]] + || [item isKindOfClass:[FBSDKGraphRequestDataAttachment class]]); } - + (NSString *)serializeURL:(NSString *)baseUrl - params:(NSDictionary *)params { + params:(NSDictionary *)params +{ return [self serializeURL:baseUrl params:params httpMethod:FBSDKHTTPMethodGET]; } + (NSString *)serializeURL:(NSString *)baseUrl params:(NSDictionary *)params - httpMethod:(NSString *)httpMethod { + httpMethod:(NSString *)httpMethod +{ return [self serializeURL:baseUrl params:params httpMethod:httpMethod forBatch:NO]; } + (NSString *)serializeURL:(NSString *)baseUrl params:(NSDictionary *)params httpMethod:(NSString *)httpMethod - forBatch:(BOOL)forBatch { - params = [self preprocessParams: params]; + forBatch:(BOOL)forBatch +{ + params = [self preprocessParams:params]; -#pragma clang diagnostic push -#pragma clang diagnostic ignored "-Wdeprecated-declarations" + #pragma clang diagnostic push + #pragma clang diagnostic ignored "-Wdeprecated-declarations" NSURL *parsedURL = [NSURL URLWithString:[baseUrl stringByAddingPercentEscapesUsingEncoding:NSUTF8StringEncoding]]; -#pragma clang pop + #pragma clang diagnostic pop if ([httpMethod isEqualToString:FBSDKHTTPMethodPOST] && !forBatch) { return baseUrl; @@ -173,7 +186,7 @@ + (NSString *)serializeURL:(NSString *)baseUrl NSString *queryPrefix = parsedURL.query ? @"&" : @"?"; - NSString *query = [FBSDKBasicUtility queryStringWithDictionary:params error:NULL invalidObjectHandler:^id(id object, BOOL *stop) { + NSString *query = [FBSDKBasicUtility queryStringWithDictionary:params error:NULL invalidObjectHandler:^id (id object, BOOL *stop) { if ([self isAttachment:object]) { if ([httpMethod isEqualToString:FBSDKHTTPMethodGET]) { [FBSDKLogger singleShotLogEntry:FBSDKLoggingBehaviorDeveloperErrors logEntry:@"can not use GET to upload a file"]; diff --git a/FBSDKCoreKit/FBSDKCoreKit/GraphAPI/FBSDKGraphRequestConnection.m b/FBSDKCoreKit/FBSDKCoreKit/GraphAPI/FBSDKGraphRequestConnection.m index 1e029a5cba..81440ed262 100644 --- a/FBSDKCoreKit/FBSDKCoreKit/GraphAPI/FBSDKGraphRequestConnection.m +++ b/FBSDKCoreKit/FBSDKCoreKit/GraphAPI/FBSDKGraphRequestConnection.m @@ -31,7 +31,6 @@ #import "FBSDKInternalUtility.h" #import "FBSDKLogger.h" #import "FBSDKSettings+Internal.h" -#import "FBSDKURLSessionTask.h" NSString *const FBSDKNonJSONResponseProperty = @"FACEBOOK_NON_JSON_RESULT"; @@ -73,21 +72,21 @@ return [[FBSDKAccessToken alloc] initWithTokenString:accessToken.tokenString permissions:accessToken.permissions.allObjects declinedPermissions:accessToken.declinedPermissions.allObjects - expiredPermissions:accessToken.expiredPermissions.allObjects + expiredPermissions:accessToken.expiredPermissions.allObjects appID:accessToken.appID userID:accessToken.userID expirationDate:expirationDate refreshDate:expirationDate - dataAccessExpirationDate:expirationDate + dataAccessExpirationDate:expirationDate graphDomain:accessToken.graphDomain]; } + #endif // ---------------------------------------------------------------------------- // FBSDKGraphRequestConnectionState -typedef NS_ENUM(NSUInteger, FBSDKGraphRequestConnectionState) -{ +typedef NS_ENUM(NSUInteger, FBSDKGraphRequestConnectionState) { kStateCreated, kStateSerialized, kStateStarted, @@ -99,9 +98,9 @@ typedef NS_ENUM(NSUInteger, FBSDKGraphRequestConnectionState) // Private properties and methods @interface FBSDKGraphRequestConnection () < -NSURLSessionDataDelegate + NSURLSessionDataDelegate #if !TARGET_OS_TV -, FBSDKGraphErrorRecoveryProcessorDelegate + , FBSDKGraphErrorRecoveryProcessorDelegate #endif > @@ -153,27 +152,28 @@ + (void)setDefaultConnectionTimeout:(NSTimeInterval)defaultTimeout } } -+ (NSTimeInterval)defaultConnectionTimeout { ++ (NSTimeInterval)defaultConnectionTimeout +{ return g_defaultTimeout; } -- (void)addRequest:(FBSDKGraphRequest *)request - completionHandler:(FBSDKGraphRequestBlock)handler +- (void) addRequest:(FBSDKGraphRequest *)request + completionHandler:(FBSDKGraphRequestBlock)handler { [self addRequest:request batchEntryName:@"" completionHandler:handler]; } -- (void)addRequest:(FBSDKGraphRequest *)request - batchEntryName:(NSString *)name - completionHandler:(FBSDKGraphRequestBlock)handler +- (void) addRequest:(FBSDKGraphRequest *)request + batchEntryName:(NSString *)name + completionHandler:(FBSDKGraphRequestBlock)handler { NSDictionary *batchParams = name.length > 0 ? @{kBatchEntryName : name } : nil; [self addRequest:request batchParameters:batchParams completionHandler:handler]; } -- (void)addRequest:(FBSDKGraphRequest *)request - batchParameters:(NSDictionary *)batchParameters - completionHandler:(FBSDKGraphRequestBlock)handler +- (void) addRequest:(FBSDKGraphRequest *)request + batchParameters:(NSDictionary *)batchParameters + completionHandler:(FBSDKGraphRequestBlock)handler { if (self.state != kStateCreated) { @throw [NSException exceptionWithName:NSInternalInconsistencyException @@ -219,7 +219,7 @@ - (void)start return; } - //optimistically check for updated server configuration; + // optimistically check for updated server configuration; g_errorConfiguration = [FBSDKServerConfigurationManager cachedServerConfiguration].errorConfiguration ?: g_errorConfiguration; if (self.state != kStateCreated && self.state != kStateSerialized) { @@ -244,8 +244,8 @@ - (void)start networkError:errorV2]; }; - if(errorV1) { - [self taskDidCompleteWithError:errorV1 handler:handler]; + if (errorV1) { + [self _taskDidCompleteWithError:errorV1 handler:handler]; } else { [self taskDidCompleteWithResponse:responseV1 data:responseDataV1 requestStartTime:self.requestStartTime handler:handler]; } @@ -277,7 +277,7 @@ - (void)setDelegateQueue:(NSOperationQueue *)queue - (FBSDKURLSession *)session { - return _session; + return _session; } #pragma mark - Private methods (request generation) @@ -370,8 +370,8 @@ - (void)appendJSONRequests:(NSArray *)requests for (FBSDKGraphRequestMetadata *metadata in requests) { NSString *individualToken = [self accessTokenWithRequest:metadata.request]; BOOL isClientToken = [FBSDKSettings clientToken] && [individualToken hasSuffix:[FBSDKSettings clientToken]]; - if (!batchToken && - !isClientToken) { + if (!batchToken + && !isClientToken) { batchToken = individualToken; } [self addRequest:metadata @@ -410,10 +410,10 @@ - (void)_validateFieldsParamForGetRequests:(NSArray *)requests { for (FBSDKGraphRequestMetadata *metadata in requests) { FBSDKGraphRequest *request = metadata.request; - if ([request.HTTPMethod.uppercaseString isEqualToString:@"GET"] && - [self _shouldWarnOnMissingFieldsParam:request] && - !request.parameters[@"fields"] && - [request.graphPath rangeOfString:@"fields="].location == NSNotFound) { + if ([request.HTTPMethod.uppercaseString isEqualToString:@"GET"] + && [self _shouldWarnOnMissingFieldsParam:request] + && !request.parameters[@"fields"] + && [request.graphPath rangeOfString:@"fields="].location == NSNotFound) { [FBSDKLogger singleShotLogEntry:FBSDKLoggingBehaviorDeveloperErrors formatString:@"starting with Graph API v2.4, GET requests for /%@ should contain an explicit \"fields\" parameter", request.graphPath]; } @@ -439,7 +439,6 @@ - (NSMutableURLRequest *)requestWithBatch:(NSArray *)requests reason:@"FBSDKGraphRequestConnection: Must have at least one request or urlRequest not specified." userInfo:nil] raise]; - } [self _validateFieldsParamForGetRequests:requests]; @@ -498,10 +497,8 @@ - (NSMutableURLRequest *)requestWithBatch:(NSArray *)requests request.HTTPMethod = @"POST"; } - NSData *compressedData; - if ([request.HTTPMethod isEqualToString:@"POST"] && (compressedData = [body compressedData])) { - request.HTTPBody = compressedData; - [request setValue:@"gzip" forHTTPHeaderField:@"Content-Encoding"]; + if ([request.HTTPMethod isEqualToString:@"POST"]) { + [self addBody:body toPostRequest:request]; } else { request.HTTPBody = body.data; } @@ -514,6 +511,17 @@ - (NSMutableURLRequest *)requestWithBatch:(NSArray *)requests return request; } +- (void)addBody:(FBSDKGraphRequestBody *)body toPostRequest:(NSMutableURLRequest *)request +{ + NSData *compressedData; + if ((compressedData = [body compressedData])) { + request.HTTPBody = compressedData; + [request setValue:@"gzip" forHTTPHeaderField:@"Content-Encoding"]; + } else { + request.HTTPBody = body.data; + } +} + // // Generates a URL for a batch containing only a single request, // and names all attachments that need to go in the body of the @@ -548,8 +556,8 @@ - (NSString *)urlStringForSingleRequest:(FBSDKGraphRequest *)request forBatch:(B // We special case a graph post to /videos and send it to graph-video.facebook.com // We only do this for non batch post requests NSString *graphPath = request.graphPath.lowercaseString; - if ([request.HTTPMethod.uppercaseString isEqualToString:@"POST"] && - [graphPath hasSuffix:@"/videos"]) { + if ([request.HTTPMethod.uppercaseString isEqualToString:@"POST"] + && [graphPath hasSuffix:@"/videos"]) { graphPath = [graphPath stringByTrimmingCharactersInSet:[NSCharacterSet characterSetWithCharactersInString:@"/"]]; NSArray *components = [graphPath componentsSeparatedByString:@"/"]; if (components.count == 2) { @@ -558,11 +566,11 @@ - (NSString *)urlStringForSingleRequest:(FBSDKGraphRequest *)request forBatch:(B } baseURL = [FBSDKInternalUtility - facebookURLWithHostPrefix:prefix - path:request.graphPath - queryParameters:@{} - defaultVersion:request.version - error:NULL].absoluteString; + facebookURLWithHostPrefix:prefix + path:request.graphPath + queryParameters:@{} + defaultVersion:request.version + error:NULL].absoluteString; } NSString *url = [FBSDKGraphRequest serializeURL:baseURL @@ -579,20 +587,24 @@ - (void)completeFBSDKURLSessionWithResponse:(NSURLResponse *)response networkError:(NSError *)error { if (self.state != kStateCancelled) { - NSAssert(self.state == kStateStarted, - @"Unexpected state %lu in completeWithResponse", - (unsigned long)self.state); + NSAssert( + self.state == kStateStarted, + @"Unexpected state %lu in completeWithResponse", + (unsigned long)self.state + ); self.state = kStateCompleted; } NSArray *results = nil; _urlResponse = (NSHTTPURLResponse *)response; if (response) { - NSAssert([response isKindOfClass:[NSHTTPURLResponse class]], - @"Expected NSHTTPURLResponse, got %@", - response); + NSAssert( + [response isKindOfClass:[NSHTTPURLResponse class]], + @"Expected NSHTTPURLResponse, got %@", + response + ); -NSInteger statusCode = _urlResponse.statusCode; + NSInteger statusCode = _urlResponse.statusCode; if (!error && [response.MIMEType hasPrefix:@"image"]) { error = [FBSDKError errorWithCode:FBSDKErrorGraphRequestNonTextMimeTypeReturned @@ -629,7 +641,7 @@ - (void)completeFBSDKURLSessionWithResponse:(NSURLResponse *)response } [_logger emitToNSLog]; - [self completeWithResults:results networkError:error]; + [self _completeWithResults:results networkError:error]; [self.session invalidateAndCancel]; } @@ -638,10 +650,10 @@ - (void)completeFBSDKURLSessionWithResponse:(NSURLResponse *)response // If there is one request, the JSON is the response. // If there are multiple requests, the JSON has an array of dictionaries whose // body property is the response. -// [{ "code":200, -// "body":"JSON-response-as-a-string" }, -// { "code":200, -// "body":"JSON-response-as-a-string" }] +// [{ "code":200, +// "body":"JSON-response-as-a-string" }, +// { "code":200, +// "body":"JSON-response-as-a-string" }] // // In both cases, this function returns an NSArray containing the results. // The NSArray looks just like the multiple request case except the body @@ -668,19 +680,19 @@ - (NSArray *)parseJSONResponse:(NSData *)data NSDictionary *responseError = nil; if (!response) { if ((error != NULL) && (*error == nil)) { - *error = [self errorWithCode:FBSDKErrorUnknown - statusCode:statusCode - parsedJSONResponse:nil - innerError:nil - message:@"The server returned an unexpected response."]; + *error = [self _errorWithCode:FBSDKErrorUnknown + statusCode:statusCode + parsedJSONResponse:nil + innerError:nil + message:@"The server returned an unexpected response."]; } } else if (self.requests.count == 1) { // response is the entry, so put it in a dictionary under "body" and add // that to array of responses. [FBSDKTypeUtility array:results addObject:@{ - @"code":@(statusCode), - @"body":response - }]; + @"code" : @(statusCode), + @"body" : response + }]; } else if ([response isKindOfClass:[NSArray class]]) { // response is the array of responses, but the body element of each needs // to be decoded from JSON. @@ -701,35 +713,38 @@ - (NSArray *)parseJSONResponse:(NSData *)data *error = batchResultError; } } - } else if ([response isKindOfClass:[NSDictionary class]] && - (responseError = [FBSDKTypeUtility dictionaryValue:response[@"error"]]) != nil && - [responseError[@"type"] isEqualToString:@"OAuthException"]) { + } else if ([response isKindOfClass:[NSDictionary class]] + && (responseError = [FBSDKTypeUtility dictionaryValue:response[@"error"]]) != nil + && [responseError[@"type"] isEqualToString:@"OAuthException"]) { // if there was one request then return the only result. if there were multiple requests // but only one error then the server rejected the batch access token NSDictionary *result = @{ - @"code":@(statusCode), - @"body":response - }; + @"code" : @(statusCode), + @"body" : response + }; for (NSUInteger resultIndex = 0, resultCount = self.requests.count; resultIndex < resultCount; ++resultIndex) { [FBSDKTypeUtility array:results addObject:result]; } } else if (error != NULL) { - *error = [self errorWithCode:FBSDKErrorGraphRequestProtocolMismatch - statusCode:statusCode - parsedJSONResponse:results - innerError:nil - message:nil]; + *error = [self _errorWithCode:FBSDKErrorGraphRequestProtocolMismatch + statusCode:statusCode + parsedJSONResponse:results + innerError:nil + message:nil]; } return results; } -- (id)parseJSONOrOtherwise:(NSString *)utf8 +- (id)parseJSONOrOtherwise:(NSString *)unsafeString error:(NSError **)error { id parsed = nil; - if (!(*error) && [utf8 isKindOfClass:[NSString class]]) { + + // Historically, people have passed-in `id` here. So, gotta double-check. + NSString *const utf8 = FBSDK_CAST_TO_CLASS_OR_NIL(unsafeString, NSString); + if (!(*error) && utf8) { parsed = [FBSDKBasicUtility objectForJSONString:utf8 error:error]; // if we fail parse we attempt a re-parse of a modified input to support results in the form "foo=bar", "true", etc. // which is shouldn't be necessary since Graph API v2.1. @@ -737,10 +752,15 @@ - (id)parseJSONOrOtherwise:(NSString *)utf8 // we round-trip our hand-wired response through the parser in order to remain // consistent with the rest of the output of this function (note, if perf turns out // to be a problem -- unlikely -- we can return the following dictionary outright) - NSDictionary *original = @{ FBSDKNonJSONResponseProperty : utf8 }; - NSString *jsonrep = [FBSDKBasicUtility JSONStringForObject:original error:NULL invalidObjectHandler:NULL]; NSError *reparseError = nil; - parsed = [FBSDKBasicUtility objectForJSONString:jsonrep error:&reparseError]; + parsed = + [FBSDKBasicUtility + objectForJSONString: + [FBSDKBasicUtility JSONStringForObject:@{ FBSDKNonJSONResponseProperty : utf8 } + error:NULL + invalidObjectHandler:NULL] + error:&reparseError]; + if (!reparseError) { *error = nil; } @@ -749,8 +769,8 @@ - (id)parseJSONOrOtherwise:(NSString *)utf8 return parsed; } -- (void)completeWithResults:(NSArray *)results - networkError:(NSError *)networkError +- (void)_completeWithResults:(NSArray *)results + networkError:(NSError *)networkError { NSUInteger count = self.requests.count; _expectingResults = count; @@ -766,7 +786,7 @@ - (void)completeWithResults:(NSArray *)results [self.requests enumerateObjectsUsingBlock:^(FBSDKGraphRequestMetadata *metadata, NSUInteger i, BOOL *stop) { id result = networkError ? nil : [FBSDKTypeUtility array:results objectAtIndex:i]; - NSError *resultError = networkError ?: [self errorFromResult:result request:metadata.request]; + NSError *const resultError = networkError ?: errorFromResult(result, metadata.request); id body = nil; if (!resultError && [result isKindOfClass:[NSDictionary class]]) { @@ -774,7 +794,7 @@ - (void)completeWithResults:(NSArray *)results body = [FBSDKTypeUtility dictionaryValue:resultDictionary[@"body"]]; } -#if !TARGET_OS_TV + #if !TARGET_OS_TV if (resultError && !metadata.request.graphErrorRecoveryDisabled && isSingleRequestToRecover) { self->_recoveringRequestMetadata = metadata; self->_errorRecoveryProcessor = [[FBSDKGraphErrorRecoveryProcessor alloc] init]; @@ -782,7 +802,7 @@ - (void)completeWithResults:(NSArray *)results return; } } -#endif + #endif [self processResultBody:body error:resultError metadata:metadata canNotifyDelegate:networkError == nil]; }]; @@ -799,7 +819,7 @@ - (void)processResultBody:(NSDictionary *)body error:(NSError *)error metadata:( void (^finishAndInvokeCompletionHandler)(void) = ^{ NSDictionary *graphDebugDict = body[@"__debug__"]; if ([graphDebugDict isKindOfClass:[NSDictionary class]]) { - [self processResultDebugDictionary: graphDebugDict]; + [self processResultDebugDictionary:graphDebugDict]; } [metadata invokeCompletionHandlerForConnection:self withResults:body error:error]; @@ -811,7 +831,7 @@ - (void)processResultBody:(NSDictionary *)body error:(NSError *)error metadata:( }; #if !TARGET_OS_TV - void (^clearToken)(NSInteger) = ^(NSInteger errorSubcode){ + void (^clearToken)(NSInteger) = ^(NSInteger errorSubcode) { if (metadata.request.flags & FBSDKGraphRequestFlagDoNotInvalidateTokenOnError) { return; } @@ -820,7 +840,6 @@ - (void)processResultBody:(NSDictionary *)body error:(NSError *)error metadata:( } else { [FBSDKAccessToken setCurrentAccessToken:nil]; } - }; NSString *metadataTokenString = metadata.request.tokenString; @@ -864,58 +883,65 @@ - (void)processResultDebugDictionary:(NSDictionary *)dict [FBSDKLogger singleShotLogEntry:loggingBehavior logEntry:message]; }]; - } -- (NSError *)errorFromResult:(id)result request:(FBSDKGraphRequest *)request +static NSError *_Nullable errorFromResult(id untypedParam, FBSDKGraphRequest *request) { - if ([result isKindOfClass:[NSDictionary class]]) { - NSDictionary *errorDictionary = [FBSDKTypeUtility dictionaryValue:result[@"body"]][@"error"]; - - if ([errorDictionary isKindOfClass:[NSDictionary class]]) { - NSMutableDictionary *userInfo = [NSMutableDictionary dictionary]; - [FBSDKTypeUtility dictionary:userInfo setObject:errorDictionary[@"code"] forKey:FBSDKGraphRequestErrorGraphErrorCodeKey]; - [FBSDKTypeUtility dictionary:userInfo setObject:errorDictionary[@"error_subcode"] forKey:FBSDKGraphRequestErrorGraphErrorSubcodeKey]; - //"message" is preferred over error_msg or error_reason. - [FBSDKTypeUtility dictionary:userInfo setObject:errorDictionary[@"error_msg"] forKey:FBSDKErrorDeveloperMessageKey]; - [FBSDKTypeUtility dictionary:userInfo setObject:errorDictionary[@"error_reason"] forKey:FBSDKErrorDeveloperMessageKey]; - [FBSDKTypeUtility dictionary:userInfo setObject:errorDictionary[@"message"] forKey:FBSDKErrorDeveloperMessageKey]; - [FBSDKTypeUtility dictionary:userInfo setObject:errorDictionary[@"error_user_title"] forKey:FBSDKErrorLocalizedTitleKey]; - [FBSDKTypeUtility dictionary:userInfo setObject:errorDictionary[@"error_user_msg"] forKey:FBSDKErrorLocalizedDescriptionKey]; - [FBSDKTypeUtility dictionary:userInfo setObject:errorDictionary[@"error_user_msg"] forKey:NSLocalizedDescriptionKey]; - [FBSDKTypeUtility dictionary:userInfo setObject:result[@"code"] forKey:FBSDKGraphRequestErrorHTTPStatusCodeKey]; - [FBSDKTypeUtility dictionary:userInfo setObject:result forKey:FBSDKGraphRequestErrorParsedJSONResponseKey]; - - FBSDKErrorRecoveryConfiguration *recoveryConfiguration = [g_errorConfiguration - recoveryConfigurationForCode:[userInfo[FBSDKGraphRequestErrorGraphErrorCodeKey] stringValue] - subcode:[userInfo[FBSDKGraphRequestErrorGraphErrorSubcodeKey] stringValue] - request:request]; - if ([errorDictionary[@"is_transient"] boolValue]) { - [FBSDKTypeUtility dictionary:userInfo setObject:@(FBSDKGraphRequestErrorTransient) forKey:FBSDKGraphRequestErrorKey]; - } else { - [FBSDKTypeUtility dictionary:userInfo setObject:@(recoveryConfiguration.errorCategory) forKey:FBSDKGraphRequestErrorKey]; - } - [FBSDKTypeUtility dictionary:userInfo setObject:recoveryConfiguration.localizedRecoveryDescription forKey:NSLocalizedRecoverySuggestionErrorKey]; - [FBSDKTypeUtility dictionary:userInfo setObject:recoveryConfiguration.localizedRecoveryOptionDescriptions forKey:NSLocalizedRecoveryOptionsErrorKey]; - FBSDKErrorRecoveryAttempter *attempter = [FBSDKErrorRecoveryAttempter recoveryAttempterFromConfiguration:recoveryConfiguration]; - [FBSDKTypeUtility dictionary:userInfo setObject:attempter forKey:NSRecoveryAttempterErrorKey]; - - return [FBSDKError errorWithCode:FBSDKErrorGraphRequestGraphAPI - userInfo:userInfo - message:nil - underlyingError:nil]; - } + NSDictionary *const result = FBSDK_CAST_TO_CLASS_OR_NIL(untypedParam, NSDictionary); + if (!result) { + return nil; } - return nil; + NSDictionary *const body = FBSDK_CAST_TO_CLASS_OR_NIL(result[@"body"], NSDictionary); + if (!body) { + return nil; + } + + NSDictionary *const errorDictionary = FBSDK_CAST_TO_CLASS_OR_NIL(body[@"error"], NSDictionary); + if (!errorDictionary) { + return nil; + } + + NSMutableDictionary *userInfo = [NSMutableDictionary dictionary]; + [FBSDKTypeUtility dictionary:userInfo setObject:errorDictionary[@"code"] forKey:FBSDKGraphRequestErrorGraphErrorCodeKey]; + [FBSDKTypeUtility dictionary:userInfo setObject:errorDictionary[@"error_subcode"] forKey:FBSDKGraphRequestErrorGraphErrorSubcodeKey]; + // "message" is preferred over error_msg or error_reason. + [FBSDKTypeUtility dictionary:userInfo setObject:errorDictionary[@"error_msg"] forKey:FBSDKErrorDeveloperMessageKey]; + [FBSDKTypeUtility dictionary:userInfo setObject:errorDictionary[@"error_reason"] forKey:FBSDKErrorDeveloperMessageKey]; + [FBSDKTypeUtility dictionary:userInfo setObject:errorDictionary[@"message"] forKey:FBSDKErrorDeveloperMessageKey]; + [FBSDKTypeUtility dictionary:userInfo setObject:errorDictionary[@"error_user_title"] forKey:FBSDKErrorLocalizedTitleKey]; + [FBSDKTypeUtility dictionary:userInfo setObject:errorDictionary[@"error_user_msg"] forKey:FBSDKErrorLocalizedDescriptionKey]; + [FBSDKTypeUtility dictionary:userInfo setObject:errorDictionary[@"error_user_msg"] forKey:NSLocalizedDescriptionKey]; + [FBSDKTypeUtility dictionary:userInfo setObject:result[@"code"] forKey:FBSDKGraphRequestErrorHTTPStatusCodeKey]; + [FBSDKTypeUtility dictionary:userInfo setObject:result forKey:FBSDKGraphRequestErrorParsedJSONResponseKey]; + + FBSDKErrorRecoveryConfiguration *recoveryConfiguration = [g_errorConfiguration + recoveryConfigurationForCode:[userInfo[FBSDKGraphRequestErrorGraphErrorCodeKey] stringValue] + subcode:[userInfo[FBSDKGraphRequestErrorGraphErrorSubcodeKey] stringValue] + request:request]; + if ([errorDictionary[@"is_transient"] boolValue]) { + [FBSDKTypeUtility dictionary:userInfo setObject:@(FBSDKGraphRequestErrorTransient) forKey:FBSDKGraphRequestErrorKey]; + } else { + [FBSDKTypeUtility dictionary:userInfo setObject:@(recoveryConfiguration.errorCategory) forKey:FBSDKGraphRequestErrorKey]; + } + [FBSDKTypeUtility dictionary:userInfo setObject:recoveryConfiguration.localizedRecoveryDescription forKey:NSLocalizedRecoverySuggestionErrorKey]; + [FBSDKTypeUtility dictionary:userInfo setObject:recoveryConfiguration.localizedRecoveryOptionDescriptions forKey:NSLocalizedRecoveryOptionsErrorKey]; + FBSDKErrorRecoveryAttempter *attempter = [FBSDKErrorRecoveryAttempter recoveryAttempterFromConfiguration:recoveryConfiguration]; + [FBSDKTypeUtility dictionary:userInfo setObject:attempter forKey:NSRecoveryAttempterErrorKey]; + + return [FBSDKError errorWithCode:FBSDKErrorGraphRequestGraphAPI + userInfo:userInfo + message:nil + underlyingError:nil]; } -- (NSError *)errorWithCode:(FBSDKCoreError)code - statusCode:(NSInteger)statusCode - parsedJSONResponse:(id)response - innerError:(NSError *)innerError - message:(NSString *)message { - NSMutableDictionary *userInfo = [[NSMutableDictionary alloc] init]; +- (NSError *)_errorWithCode:(FBSDKCoreError)code + statusCode:(NSInteger)statusCode + parsedJSONResponse:(id)response + innerError:(NSError *)innerError + message:(NSString *)message +{ + NSMutableDictionary *const userInfo = [[NSMutableDictionary alloc] init]; [FBSDKTypeUtility dictionary:userInfo setObject:@(statusCode) forKey:FBSDKGraphRequestErrorHTTPStatusCodeKey]; if (response) { @@ -930,12 +956,11 @@ - (NSError *)errorWithCode:(FBSDKCoreError)code [FBSDKTypeUtility dictionary:userInfo setObject:message forKey:FBSDKErrorDeveloperMessageKey]; } - NSError *error = [[NSError alloc] - initWithDomain:FBSDKErrorDomain - code:code - userInfo:userInfo]; - - return error; + return + [[NSError alloc] + initWithDomain:FBSDKErrorDomain + code:code + userInfo:userInfo]; } #pragma mark - Private methods (logging and completion) @@ -1009,13 +1034,15 @@ - (void)taskDidCompleteWithResponse:(NSURLResponse *)response } @finally {} } -- (void)taskDidCompleteWithError:(NSError *)error - handler:(FBSDKURLSessionTaskBlock)handler +#pragma mark - Private methods (miscellaneous) + +- (void)_taskDidCompleteWithError:(NSError *)error + handler:(FBSDKURLSessionTaskBlock)handler { @try { if ([error.domain isEqualToString:NSURLErrorDomain] && error.code == kCFURLErrorSecureConnectionFailed) { NSOperatingSystemVersion iOS9Version = { .majorVersion = 9, .minorVersion = 0, .patchVersion = 0 }; - if ([FBSDKInternalUtility isOSRunTimeVersionAtLeast:iOS9Version]) { + if ([NSProcessInfo.processInfo isOperatingSystemAtLeastVersion:iOS9Version]) { [FBSDKLogger singleShotLogEntry:FBSDKLoggingBehaviorDeveloperErrors logEntry:@"WARNING: FBSDK secure network request failed. Please verify you have configured your " "app for Application Transport Security compatibility described at https://developers.facebook.com/docs/ios/ios9"]; @@ -1025,8 +1052,6 @@ - (void)taskDidCompleteWithError:(NSError *)error } @finally {} } -#pragma mark - Private methods (miscellaneous) - - (void)logRequest:(NSMutableURLRequest *)request bodyLength:(NSUInteger)bodyLength bodyLogger:(FBSDKLogger *)bodyLogger @@ -1096,15 +1121,16 @@ + (NSString *)userAgent return agentWithSuffix ?: agent; } + #pragma clang diagnostic pop #pragma mark - NSURLSessionDataDelegate -- (void)URLSession:(NSURLSession *)session - task:(NSURLSessionTask *)task - didSendBodyData:(int64_t)bytesSent - totalBytesSent:(int64_t)totalBytesSent -totalBytesExpectedToSend:(int64_t)totalBytesExpectedToSend +- (void) URLSession:(NSURLSession *)session + task:(NSURLSessionTask *)task + didSendBodyData:(int64_t)bytesSent + totalBytesSent:(int64_t)totalBytesSent + totalBytesExpectedToSend:(int64_t)totalBytesExpectedToSend { id delegate = self.delegate; @@ -1121,27 +1147,30 @@ - (void)URLSession:(NSURLSession *)session #if !TARGET_OS_TV - (void)processorDidAttemptRecovery:(FBSDKGraphErrorRecoveryProcessor *)processor didRecover:(BOOL)didRecover error:(NSError *)error { - if (didRecover) { - FBSDKGraphRequest *originalRequest = _recoveringRequestMetadata.request; - FBSDKGraphRequest *retryRequest = [[FBSDKGraphRequest alloc] initWithGraphPath:originalRequest.graphPath - parameters:originalRequest.parameters - tokenString:[FBSDKAccessToken currentAccessToken].tokenString - version:originalRequest.version - HTTPMethod:originalRequest.HTTPMethod]; - // prevent further attempts at recovery (i.e., additional retries). - [retryRequest setGraphErrorRecoveryDisabled:YES]; - FBSDKGraphRequestMetadata *retryMetadata = [[FBSDKGraphRequestMetadata alloc] initWithRequest:retryRequest completionHandler:_recoveringRequestMetadata.completionHandler batchParameters:_recoveringRequestMetadata.batchParameters]; - [retryRequest startWithCompletionHandler:^(FBSDKGraphRequestConnection *connection, id result, NSError *retriedError) { - [self processResultBody:result error:retriedError metadata:retryMetadata canNotifyDelegate:YES]; - self->_errorRecoveryProcessor = nil; - self->_recoveringRequestMetadata = nil; - }]; - } else { - [self processResultBody:nil error:error metadata:_recoveringRequestMetadata canNotifyDelegate:YES]; - _errorRecoveryProcessor = nil; - _recoveringRequestMetadata = nil; - } + @try { + if (didRecover) { + FBSDKGraphRequest *originalRequest = _recoveringRequestMetadata.request; + FBSDKGraphRequest *retryRequest = [[FBSDKGraphRequest alloc] initWithGraphPath:originalRequest.graphPath + parameters:originalRequest.parameters + tokenString:[FBSDKAccessToken currentAccessToken].tokenString + version:originalRequest.version + HTTPMethod:originalRequest.HTTPMethod]; + // prevent further attempts at recovery (i.e., additional retries). + [retryRequest setGraphErrorRecoveryDisabled:YES]; + FBSDKGraphRequestMetadata *retryMetadata = [[FBSDKGraphRequestMetadata alloc] initWithRequest:retryRequest completionHandler:_recoveringRequestMetadata.completionHandler batchParameters:_recoveringRequestMetadata.batchParameters]; + [retryRequest startWithCompletionHandler:^(FBSDKGraphRequestConnection *connection, id result, NSError *retriedError) { + [self processResultBody:result error:retriedError metadata:retryMetadata canNotifyDelegate:YES]; + self->_errorRecoveryProcessor = nil; + self->_recoveringRequestMetadata = nil; + }]; + } else { + [self processResultBody:nil error:error metadata:_recoveringRequestMetadata canNotifyDelegate:YES]; + _errorRecoveryProcessor = nil; + _recoveringRequestMetadata = nil; + } + } @catch (NSException *exception) {} } + #endif #pragma mark - Debugging helpers @@ -1163,7 +1192,6 @@ - (NSString *)description } [result appendString:@"\n)>"]; return result; - } @end diff --git a/FBSDKCoreKit/FBSDKCoreKit/Internal/Base64/FBSDKBase64.h b/FBSDKCoreKit/FBSDKCoreKit/Internal/Base64/FBSDKBase64.h index ccc976e3fa..1378c226c3 100644 --- a/FBSDKCoreKit/FBSDKCoreKit/Internal/Base64/FBSDKBase64.h +++ b/FBSDKCoreKit/FBSDKCoreKit/Internal/Base64/FBSDKBase64.h @@ -49,4 +49,11 @@ NS_SWIFT_NAME(Base64) */ + (NSString *)encodeString:(NSString *)string; +/** + Encodes URL string into a base-64 representation. + @param base64Url The URL string to be encoded. + @return The base-64 encoded string. + */ ++ (NSString *)base64FromBase64Url:(NSString *)base64Url; + @end diff --git a/FBSDKCoreKit/FBSDKCoreKit/Internal/Base64/FBSDKBase64.m b/FBSDKCoreKit/FBSDKCoreKit/Internal/Base64/FBSDKBase64.m index dd81d5a3a1..726f868780 100644 --- a/FBSDKCoreKit/FBSDKCoreKit/Internal/Base64/FBSDKBase64.m +++ b/FBSDKCoreKit/FBSDKCoreKit/Internal/Base64/FBSDKBase64.m @@ -53,6 +53,14 @@ + (NSString *)encodeString:(NSString *)string return [_encoder encodeString:string]; } ++ (NSString *)base64FromBase64Url:(NSString *)base64Url +{ + NSString *base64 = [base64Url stringByReplacingOccurrencesOfString:@"-" withString:@"+"]; + base64 = [base64 stringByReplacingOccurrencesOfString:@"_" withString:@"/"]; + + return base64; +} + #pragma mark - Object Lifecycle #pragma mark - Implementation Methods @@ -67,7 +75,7 @@ - (NSData *)decodeAsData:(NSString *)string int needPadding = string.length % 4; if (needPadding > 0) { needPadding = 4 - needPadding; - string = [string stringByPaddingToLength:string.length+needPadding withString:@"=" startingAtIndex:0]; + string = [string stringByPaddingToLength:string.length + needPadding withString:@"=" startingAtIndex:0]; } return [[NSData alloc] initWithBase64EncodedString:string options:NSDataBase64DecodingIgnoreUnknownCharacters]; diff --git a/FBSDKCoreKit/FBSDKCoreKit/Internal/BridgeAPI/FBSDKBridgeAPI+Internal.h b/FBSDKCoreKit/FBSDKCoreKit/Internal/BridgeAPI/FBSDKBridgeAPI+Internal.h new file mode 100644 index 0000000000..23bcc0e471 --- /dev/null +++ b/FBSDKCoreKit/FBSDKCoreKit/Internal/BridgeAPI/FBSDKBridgeAPI+Internal.h @@ -0,0 +1,31 @@ +// 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 "FBSDKBridgeAPI.h" + +NS_ASSUME_NONNULL_BEGIN + +@interface FBSDKBridgeAPI (Internal) + +- (void)openURLWithAuthenticationSession:(NSURL *)url; +- (void)setSessionCompletionHandlerFromHandler:(void (^)(BOOL, NSError *))handler; + +@end + +NS_ASSUME_NONNULL_END diff --git a/FBSDKCoreKit/FBSDKCoreKit/Internal/BridgeAPI/FBSDKBridgeAPI.h b/FBSDKCoreKit/FBSDKCoreKit/Internal/BridgeAPI/FBSDKBridgeAPI.h index 6a461dcd08..9159c5376a 100644 --- a/FBSDKCoreKit/FBSDKCoreKit/Internal/BridgeAPI/FBSDKBridgeAPI.h +++ b/FBSDKCoreKit/FBSDKCoreKit/Internal/BridgeAPI/FBSDKBridgeAPI.h @@ -39,22 +39,26 @@ NS_ASSUME_NONNULL_BEGIN typedef void (^FBSDKBridgeAPIResponseBlock)(FBSDKBridgeAPIResponse *response) NS_SWIFT_NAME(BridgeAPIResponseBlock); +typedef void (^FBSDKAuthenticationCompletionHandler)(NSURL *_Nullable callbackURL, NSError *_Nullable error); + @interface FBSDKBridgeAPI : NSObject -- (void)openBridgeAPIRequest:(FBSDKBridgeAPIRequest *)request +- (void)openBridgeAPIRequest:(NSObject *)request useSafariViewController:(BOOL)useSafariViewController fromViewController:(nullable UIViewController *)fromViewController completionBlock:(FBSDKBridgeAPIResponseBlock)completionBlock; - (void)openURLWithSafariViewController:(NSURL *)url sender:(nullable id)sender - fromViewController:(UIViewController *)fromViewController + fromViewController:(nullable UIViewController *)fromViewController handler:(FBSDKSuccessBlock)handler; - (void)openURL:(NSURL *)url sender:(nullable id)sender handler:(FBSDKSuccessBlock)handler; +- (FBSDKAuthenticationCompletionHandler)sessionCompletionHandler; + @property (class, nonatomic, readonly, strong) FBSDKBridgeAPI *sharedInstance NS_SWIFT_NAME(shared); @property (nonatomic, readonly, getter=isActive) BOOL active; diff --git a/FBSDKCoreKit/FBSDKCoreKit/Internal/BridgeAPI/FBSDKBridgeAPI.m b/FBSDKCoreKit/FBSDKCoreKit/Internal/BridgeAPI/FBSDKBridgeAPI.m index 9a4c9529f1..ee1e6c5eec 100644 --- a/FBSDKCoreKit/FBSDKCoreKit/Internal/BridgeAPI/FBSDKBridgeAPI.m +++ b/FBSDKCoreKit/FBSDKCoreKit/Internal/BridgeAPI/FBSDKBridgeAPI.m @@ -20,11 +20,25 @@ #if !TARGET_OS_TV -#import "FBSDKBridgeAPI.h" - -#import "FBSDKCoreKit+Internal.h" - -typedef void (^FBSDKAuthenticationCompletionHandler)(NSURL *_Nullable callbackURL, NSError *_Nullable error); + #import "FBSDKBridgeAPI.h" + + #import "FBSDKCoreKit+Internal.h" + +/** + Specifies state of FBSDKAuthenticationSession (SFAuthenticationSession (iOS 11) and ASWebAuthenticationSession (iOS 12+)) + */ +typedef NS_ENUM(NSUInteger, FBSDKAuthenticationSession) { + /** There is no active authentication session*/ + FBSDKAuthenticationSessionNone, + /** The authentication session has started*/ + FBSDKAuthenticationSessionStarted, + /** System dialog ("app wants to use facebook.com to sign in") to access facebook.com was presented to the user*/ + FBSDKAuthenticationSessionShowAlert, + /** Web browser with log in to authentication was presented to the user*/ + FBSDKAuthenticationSessionShowWebBrowser, + /** Authentication session was canceled by system. It happens when app goes to background while alert requesting access to facebook.com is presented*/ + FBSDKAuthenticationSessionCanceledBySystem, +}; @protocol FBSDKAuthenticationSession @@ -36,27 +50,28 @@ - (void)setPresentationContextProvider:(id)presentationContextProvider; @end -#if __IPHONE_OS_VERSION_MAX_ALLOWED >= 130000 -#import -@interface FBSDKBridgeAPI() -#else -@interface FBSDKBridgeAPI() -#endif + #if __IPHONE_OS_VERSION_MAX_ALLOWED >= 130000 + #import +@interface FBSDKBridgeAPI () + #else +@interface FBSDKBridgeAPI () + #endif @end -@implementation FBSDKBridgeAPI { - FBSDKBridgeAPIRequest *_pendingRequest; +@implementation FBSDKBridgeAPI +{ + NSObject *_pendingRequest; FBSDKBridgeAPIResponseBlock _pendingRequestCompletionBlock; id _pendingURLOpen; id _authenticationSession NS_AVAILABLE_IOS(11_0); FBSDKAuthenticationCompletionHandler _authenticationSessionCompletionHandler NS_AVAILABLE_IOS(11_0); BOOL _expectingBackground; - BOOL _isRequestingSFAuthenticationSession; UIViewController *_safariViewController; BOOL _isDismissingSafariViewController; BOOL _isAppLaunched; + FBSDKAuthenticationSession _authenticationSessionState; } + (void)load @@ -74,19 +89,18 @@ + (FBSDKBridgeAPI *)sharedInstance return _sharedInstance; } +- (void)applicationWillResignActive:(UIApplication *)application +{ + [self _updateAuthStateIfSystemAlertToUseWebAuthFlowPresented]; +} + - (void)applicationDidBecomeActive:(UIApplication *)application { + BOOL isRequestingWebAuthenticationSession = NO; if (@available(iOS 11.0, *)) { - if (_active && _authenticationSession) { - // applicationDidBecomeActive: is called after tapping Continue or Cancel in the "{app name} wants to use facebook.com to Sign In" alert. - // authenticationSession will be nil when it's Cancel. - _isRequestingSFAuthenticationSession = YES; - } else if (_active && !_authenticationSession) { - // In theory, this should be done whenever authenticationSession is set to nil, but just in case. - _isRequestingSFAuthenticationSession = NO; - } else if (!_active && !_isRequestingSFAuthenticationSession && _authenticationSession) { - // Handle the case where the app is backgrounded while the "ExampleApp wants to use facebook.com to Sign In" alert is still open - // Call the completion handler with a Cancel + if (_authenticationSession && _authenticationSessionState == FBSDKAuthenticationSessionShowAlert) { + _authenticationSessionState = FBSDKAuthenticationSessionShowWebBrowser; + } else if (_authenticationSession && _authenticationSessionState == FBSDKAuthenticationSessionCanceledBySystem) { [_authenticationSession cancel]; _authenticationSession = nil; NSString *errorDomain; @@ -96,13 +110,16 @@ - (void)applicationDidBecomeActive:(UIApplication *)application errorDomain = @"com.apple.SafariServices.Authentication"; } NSError *error = [FBSDKError errorWithDomain:errorDomain code:1 message:nil]; - _authenticationSessionCompletionHandler(nil, error); + if (_authenticationSessionCompletionHandler) { + _authenticationSessionCompletionHandler(nil, error); + } + isRequestingWebAuthenticationSession = [self _isRequestingWebAuthenticationSession]; } } - // _expectingBackground can be YES if the caller started doing work (like login) + // _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. - BOOL notExpectingBackground = !_expectingBackground && !_safariViewController && !_isDismissingSafariViewController && !_isRequestingSFAuthenticationSession; + BOOL notExpectingBackground = !_expectingBackground && !_safariViewController && !_isDismissingSafariViewController && !isRequestingWebAuthenticationSession; if (notExpectingBackground) { _active = YES; @@ -117,6 +134,7 @@ - (void)applicationDidEnterBackground:(UIApplication *)application { _active = NO; _expectingBackground = NO; + [self _updateAuthStateIfSystemCancelAuthSession]; } - (BOOL)application:(UIApplication *)application @@ -167,9 +185,11 @@ - (BOOL)application:(UIApplication *)application NSError *loginError = [[NSError alloc] initWithDomain:FBSDKErrorDomain code:FBSDKErrorBridgeAPIInterruption - userInfo:@{FBSDKErrorLocalizedDescriptionKey: errorMessage}]; - _authenticationSessionCompletionHandler(url, loginError); - _authenticationSessionCompletionHandler = nil; + userInfo:@{FBSDKErrorLocalizedDescriptionKey : errorMessage}]; + if (_authenticationSessionCompletionHandler) { + _authenticationSessionCompletionHandler(url, loginError); + _authenticationSessionCompletionHandler = nil; + } } } } @@ -187,14 +207,14 @@ - (BOOL)application:(UIApplication *)application return NO; } -- (BOOL)application:(UIApplication *)application -didFinishLaunchingWithOptions:(NSDictionary *)launchOptions +- (BOOL) application:(UIApplication *)application + didFinishLaunchingWithOptions:(NSDictionary *)launchOptions { NSURL *launchedURL = launchOptions[UIApplicationLaunchOptionsURLKey]; NSString *sourceApplication = launchOptions[UIApplicationLaunchOptionsSourceApplicationKey]; - if (launchedURL && - sourceApplication) { + if (launchedURL + && sourceApplication) { Class loginManagerClass = NSClassFromString(@"FBSDKLoginManager"); if (loginManagerClass) { id annotation = launchOptions[UIApplicationLaunchOptionsAnnotationKey]; @@ -209,10 +229,32 @@ - (BOOL)application:(UIApplication *)application return NO; } -#pragma mark - Internal Methods +- (void)_updateAuthStateIfSystemAlertToUseWebAuthFlowPresented +{ + if (@available(iOS 11.0, *)) { + if (_authenticationSession && _authenticationSessionState == FBSDKAuthenticationSessionStarted) { + _authenticationSessionState = FBSDKAuthenticationSessionShowAlert; + } + } +} -#pragma clang diagnostic push -#pragma clang diagnostic ignored "-Wdeprecated-declarations" +- (void)_updateAuthStateIfSystemCancelAuthSession +{ + if (@available(iOS 11.0, *)) { + if (_authenticationSession && _authenticationSessionState == FBSDKAuthenticationSessionShowAlert) { + _authenticationSessionState = FBSDKAuthenticationSessionCanceledBySystem; + } + } +} + +- (BOOL)_isRequestingWebAuthenticationSession +{ + return !(_authenticationSessionState == FBSDKAuthenticationSessionNone + || _authenticationSessionState == FBSDKAuthenticationSessionCanceledBySystem); +} + + #pragma clang diagnostic push + #pragma clang diagnostic ignored "-Wdeprecated-declarations" - (void)openURL:(NSURL *)url sender:(id)sender handler:(FBSDKSuccessBlock)handler { _expectingBackground = YES; @@ -220,32 +262,22 @@ - (void)openURL:(NSURL *)url sender:(id)sender handler:(FBSDKSu dispatch_async(dispatch_get_main_queue(), ^{ // Dispatch openURL calls to prevent hangs if we're inside the current app delegate's openURL flow already NSOperatingSystemVersion iOS10Version = { .majorVersion = 10, .minorVersion = 0, .patchVersion = 0 }; - if ([FBSDKInternalUtility isOSRunTimeVersionAtLeast:iOS10Version]) { + if ([NSProcessInfo.processInfo isOperatingSystemAtLeastVersion:iOS10Version]) { if (@available(iOS 10.0, *)) { [[UIApplication sharedApplication] openURL:url options:@{} completionHandler:^(BOOL success) { handler(success, nil); }]; } - } 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, nil); - } + } else if (handler) { + BOOL opened = [UIApplication.sharedApplication openURL:url]; + handler(opened, nil); } }); } -#pragma clang diagnostic pop -- (void)openBridgeAPIRequest:(FBSDKBridgeAPIRequest *)request + #pragma clang diagnostic pop + +- (void)openBridgeAPIRequest:(NSObject *)request useSafariViewController:(BOOL)useSafariViewController fromViewController:(UIViewController *)fromViewController completionBlock:(FBSDKBridgeAPIResponseBlock)completionBlock @@ -262,7 +294,19 @@ - (void)openBridgeAPIRequest:(FBSDKBridgeAPIRequest *)request } _pendingRequest = request; _pendingRequestCompletionBlock = [completionBlock copy]; - void (^handler)(BOOL, NSError *) = ^(BOOL openedURL, NSError *anError) { + FBSDKSuccessBlock handler = [self _bridgeAPIRequestCompletionBlockWithRequest:request + completion:completionBlock]; + if (useSafariViewController) { + [self openURLWithSafariViewController:requestURL sender:nil fromViewController:fromViewController handler:handler]; + } else { + [self openURL:requestURL sender:nil handler:handler]; + } +} + +- (FBSDKSuccessBlock)_bridgeAPIRequestCompletionBlockWithRequest:(NSObject *)request + completion:(FBSDKBridgeAPIResponseBlock)completionBlock +{ + return ^(BOOL openedURL, NSError *anError) { if (!openedURL) { self->_pendingRequest = nil; self->_pendingRequestCompletionBlock = nil; @@ -280,17 +324,25 @@ - (void)openBridgeAPIRequest:(FBSDKBridgeAPIRequest *)request return; } }; - if (useSafariViewController) { - [self openURLWithSafariViewController:requestURL sender:nil fromViewController:fromViewController handler:handler]; - } else { - [self openURL:requestURL sender:nil handler:handler]; - } } - (void)openURLWithSafariViewController:(NSURL *)url sender:(id)sender fromViewController:(UIViewController *)fromViewController handler:(FBSDKSuccessBlock)handler +{ + [self _openURLWithSafariViewController:url + sender:sender + fromViewController:fromViewController + handler:handler + dylibResolver:FBSDKDynamicFrameworkLoader.shared]; +} + +- (void)_openURLWithSafariViewController:(NSURL *)url + sender:(id)sender + fromViewController:(UIViewController *)fromViewController + handler:(FBSDKSuccessBlock)handler + dylibResolver:(id)dylibResolver { if (![url.scheme hasPrefix:@"http"]) { [self openURL:url sender:sender handler:handler]; @@ -302,8 +354,8 @@ - (void)openURLWithSafariViewController:(NSURL *)url if (@available(iOS 11.0, *)) { if ([sender isAuthenticationURL:url]) { - [self _setSessionCompletionHandlerFromHandler:handler]; - [self _openURLWithAuthenticationSession:url]; + [self setSessionCompletionHandlerFromHandler:handler]; + [self openURLWithAuthenticationSession:url]; return; } } @@ -311,7 +363,7 @@ - (void)openURLWithSafariViewController:(NSURL *)url // trying to dynamically load SFSafariViewController class // so for the cases when it is available we can send users through Safari View Controller flow // in cases it is not available regular flow will be selected - Class SFSafariViewControllerClass = fbsdkdfl_SFSafariViewControllerClass(); + Class SFSafariViewControllerClass = dylibResolver.safariViewControllerClass; if (SFSafariViewControllerClass) { UIViewController *parent = fromViewController ?: [FBSDKInternalUtility topMostViewController]; @@ -356,7 +408,7 @@ - (void)openURLWithSafariViewController:(NSURL *)url } } -- (void)_openURLWithAuthenticationSession:(NSURL *)url +- (void)openURLWithAuthenticationSession:(NSURL *)url { Class AuthenticationSessionClass = fbsdkdfl_ASWebAuthenticationSessionClass(); @@ -378,16 +430,16 @@ - (void)_openURLWithAuthenticationSession:(NSURL *)url [_authenticationSession setPresentationContextProvider:self]; } } + _authenticationSessionState = FBSDKAuthenticationSessionStarted; [_authenticationSession start]; } } -- (void)_setSessionCompletionHandlerFromHandler:(void(^)(BOOL, NSError *))handler +- (void)setSessionCompletionHandlerFromHandler:(FBSDKSuccessBlock)handler { __weak FBSDKBridgeAPI *weakSelf = self; - _authenticationSessionCompletionHandler = ^ (NSURL *aURL, NSError *error) { + _authenticationSessionCompletionHandler = ^(NSURL *aURL, NSError *error) { FBSDKBridgeAPI *strongSelf = weakSelf; - strongSelf->_isRequestingSFAuthenticationSession = NO; BOOL didSucceed = (error == nil && aURL != nil); handler(didSucceed, error); if (didSucceed) { @@ -395,10 +447,16 @@ - (void)_setSessionCompletionHandlerFromHandler:(void(^)(BOOL, NSError *))handle } strongSelf->_authenticationSession = nil; strongSelf->_authenticationSessionCompletionHandler = nil; + strongSelf->_authenticationSessionState = FBSDKAuthenticationSessionNone; }; } -#pragma mark -- SFSafariViewControllerDelegate +- (FBSDKAuthenticationCompletionHandler)sessionCompletionHandler +{ + return _authenticationSessionCompletionHandler; +} + + #pragma mark -- SFSafariViewControllerDelegate // This means the user tapped "Done" which we should treat as a cancellation. - (void)safariViewControllerDidFinish:(UIViewController *)safariViewController @@ -417,7 +475,7 @@ - (void)safariViewControllerDidFinish:(UIViewController *)safariViewController _safariViewController = nil; } -#pragma mark -- FBSDKContainerViewControllerDelegate + #pragma mark -- FBSDKContainerViewControllerDelegate - (void)viewControllerDidDisappear:(FBSDKContainerViewController *)viewController animated:(BOOL)animated { @@ -430,11 +488,11 @@ - (void)viewControllerDidDisappear:(FBSDKContainerViewController *)viewControlle } } -#pragma mark - Helper Methods + #pragma mark - Helper Methods - (BOOL)_handleBridgeAPIResponseURL:(NSURL *)responseURL sourceApplication:(NSString *)sourceApplication { - FBSDKBridgeAPIRequest *request = _pendingRequest; + NSObject *request = _pendingRequest; FBSDKBridgeAPIResponseBlock completionBlock = _pendingRequestCompletionBlock; _pendingRequest = nil; _pendingRequestCompletionBlock = NULL; @@ -475,17 +533,119 @@ - (void)_cancelBridgeRequest _pendingRequestCompletionBlock = NULL; } -#pragma clang diagnostic push -#pragma clang diagnostic ignored "-Wdeprecated-declarations" -#pragma mark - ASWebAuthenticationPresentationContextProviding -#if __IPHONE_OS_VERSION_MAX_ALLOWED >= 130000 -- (ASPresentationAnchor)presentationAnchorForWebAuthenticationSession:(ASWebAuthenticationSession *)session API_AVAILABLE(ios(13.0)){ -#else + #pragma clang diagnostic push + #pragma clang diagnostic ignored "-Wdeprecated-declarations" + #pragma mark - ASWebAuthenticationPresentationContextProviding + #if __IPHONE_OS_VERSION_MAX_ALLOWED >= 130000 +- (ASPresentationAnchor)presentationAnchorForWebAuthenticationSession:(ASWebAuthenticationSession *)session API_AVAILABLE(ios(13.0)) +{ + #else - (UIWindow *)presentationAnchorForWebAuthenticationSession:(id)session API_AVAILABLE(ios(11.0)) { #endif return UIApplication.sharedApplication.keyWindow; } -#pragma clang diagnostic pop + #pragma clang diagnostic pop + + #pragma mark - Testability + + #if DEBUG + +- (id)authenticationSession +{ + return _authenticationSession; +} + +- (void)setAuthenticationSession:(id)session +{ + _authenticationSession = session; +} + +- (FBSDKAuthenticationSession)authenticationSessionState +{ + return _authenticationSessionState; +} + +- (void)setAuthenticationSessionState:(FBSDKAuthenticationSession)state +{ + _authenticationSessionState = state; +} + +- (FBSDKAuthenticationCompletionHandler)authenticationSessionCompletionHandler +{ + return _authenticationSessionCompletionHandler; +} + +- (void)setAuthenticationSessionCompletionHandler:(FBSDKAuthenticationCompletionHandler)handler +{ + _authenticationSessionCompletionHandler = handler; +} + +- (void)setActive:(BOOL)isActive +{ + _active = isActive; +} + +- (BOOL)expectingBackground +{ + return _expectingBackground; +} + +- (void)setExpectingBackground:(BOOL)isExpectingBackground +{ + _expectingBackground = isExpectingBackground; +} + +- (id)pendingUrlOpen +{ + return _pendingURLOpen; +} + +- (void)setPendingUrlOpen:(id)opening +{ + _pendingURLOpen = opening; +} + +- (UIViewController *)safariViewController +{ + return _safariViewController; +} + +- (void)setSafariViewController:(nullable UIViewController *)controller +{ + _safariViewController = controller; +} + +- (BOOL)isDismissingSafariViewController +{ + return _isDismissingSafariViewController; +} + +- (void)setIsDismissingSafariViewController:(BOOL)isDismissing +{ + _isDismissingSafariViewController = isDismissing; +} + +- (NSObject *)pendingRequest +{ + return _pendingRequest; +} + +- (void)setPendingRequest:(NSObject *)newValue +{ + _pendingRequest = newValue; +} + +- (FBSDKBridgeAPIResponseBlock)pendingRequestCompletionBlock +{ + return _pendingRequestCompletionBlock; +} + +- (void)setPendingRequestCompletionBlock:(FBSDKBridgeAPIResponseBlock)newValue +{ + _pendingRequestCompletionBlock = newValue; +} + + #endif @end diff --git a/FBSDKCoreKit/FBSDKCoreKit/Internal/BridgeAPI/FBSDKBridgeAPIProtocolType.h b/FBSDKCoreKit/FBSDKCoreKit/Internal/BridgeAPI/FBSDKBridgeAPIProtocolType.h index 00f2bc589a..d9a08d1f5e 100644 --- a/FBSDKCoreKit/FBSDKCoreKit/Internal/BridgeAPI/FBSDKBridgeAPIProtocolType.h +++ b/FBSDKCoreKit/FBSDKCoreKit/Internal/BridgeAPI/FBSDKBridgeAPIProtocolType.h @@ -20,12 +20,11 @@ #if !TARGET_OS_TV -#import + #import -typedef NS_ENUM(NSUInteger, FBSDKBridgeAPIProtocolType) -{ +typedef NS_ENUM(NSUInteger, FBSDKBridgeAPIProtocolType) { FBSDKBridgeAPIProtocolTypeNative, FBSDKBridgeAPIProtocolTypeWeb, -} NS_SWIFT_NAME(BridgeAPIProtocol.Type); +}; #endif diff --git a/FBSDKCoreKit/FBSDKCoreKit/Internal/BridgeAPI/FBSDKBridgeAPIRequest+Private.h b/FBSDKCoreKit/FBSDKCoreKit/Internal/BridgeAPI/FBSDKBridgeAPIRequest+Private.h index 74c7809af1..77bb0f5c53 100644 --- a/FBSDKCoreKit/FBSDKCoreKit/Internal/BridgeAPI/FBSDKBridgeAPIRequest+Private.h +++ b/FBSDKCoreKit/FBSDKCoreKit/Internal/BridgeAPI/FBSDKBridgeAPIRequest+Private.h @@ -20,8 +20,8 @@ #if !TARGET_OS_TV -#import "FBSDKBridgeAPIProtocol.h" -#import "FBSDKBridgeAPIRequest.h" + #import "FBSDKBridgeAPIProtocol.h" + #import "FBSDKBridgeAPIRequest.h" @interface FBSDKBridgeAPIRequest () @@ -32,9 +32,9 @@ methodVersion:(NSString *)methodVersion parameters:(NSDictionary *)parameters userInfo:(NSDictionary *)userInfo -NS_DESIGNATED_INITIALIZER; + NS_DESIGNATED_INITIALIZER; -@property (nonatomic, strong, readonly) id protocol; +@property (nonatomic, readwrite, strong) id protocol; @end diff --git a/FBSDKCoreKit/FBSDKCoreKit/Internal/BridgeAPI/FBSDKBridgeAPIRequest.h b/FBSDKCoreKit/FBSDKCoreKit/Internal/BridgeAPI/FBSDKBridgeAPIRequest.h index bbc25d6455..dbf842ffaa 100644 --- a/FBSDKCoreKit/FBSDKCoreKit/Internal/BridgeAPI/FBSDKBridgeAPIRequest.h +++ b/FBSDKCoreKit/FBSDKCoreKit/Internal/BridgeAPI/FBSDKBridgeAPIRequest.h @@ -28,10 +28,23 @@ #import #endif +#import "FBSDKBridgeAPIProtocol.h" #import "FBSDKBridgeAPIProtocolType.h" +@protocol FBSDKBridgeAPIRequestProtocol + +@property (nonatomic, copy, readonly) NSString *scheme; +@property (nonatomic, copy, readonly) NSString *actionID; +@property (nonatomic, copy, readonly) NSString *methodName; +@property (nonatomic, assign, readonly) FBSDKBridgeAPIProtocolType protocolType; +@property (nonatomic, readonly, strong) id protocol; + +- (NSURL *)requestURL:(NSError *__autoreleasing *)errorRef; + +@end + NS_SWIFT_NAME(BridgeAPIRequest) -@interface FBSDKBridgeAPIRequest : NSObject +@interface FBSDKBridgeAPIRequest : NSObject - (instancetype)init NS_UNAVAILABLE; + (instancetype)new NS_UNAVAILABLE; diff --git a/FBSDKCoreKit/FBSDKCoreKit/Internal/BridgeAPI/FBSDKBridgeAPIRequest.m b/FBSDKCoreKit/FBSDKCoreKit/Internal/BridgeAPI/FBSDKBridgeAPIRequest.m index cbff6f8f83..b051ea46d3 100644 --- a/FBSDKCoreKit/FBSDKCoreKit/Internal/BridgeAPI/FBSDKBridgeAPIRequest.m +++ b/FBSDKCoreKit/FBSDKCoreKit/Internal/BridgeAPI/FBSDKBridgeAPIRequest.m @@ -20,14 +20,14 @@ #if !TARGET_OS_TV -#import "FBSDKBridgeAPIRequest.h" -#import "FBSDKBridgeAPIRequest+Private.h" + #import "FBSDKBridgeAPIRequest.h" + #import "FBSDKBridgeAPIRequest+Private.h" -#import "FBSDKBridgeAPIProtocolNativeV1.h" -#import "FBSDKBridgeAPIProtocolWebV1.h" -#import "FBSDKBridgeAPIProtocolWebV2.h" -#import "FBSDKInternalUtility.h" -#import "FBSDKSettings.h" + #import "FBSDKBridgeAPIProtocolNativeV1.h" + #import "FBSDKBridgeAPIProtocolWebV1.h" + #import "FBSDKBridgeAPIProtocolWebV2.h" + #import "FBSDKInternalUtility.h" + #import "FBSDKSettings.h" NSString *const FBSDKBridgeAPIAppIDKey = @"app_id"; NSString *const FBSDKBridgeAPISchemeSuffixKey = @"scheme_suffix"; @@ -35,7 +35,7 @@ @implementation FBSDKBridgeAPIRequest -#pragma mark - Class Methods + #pragma mark - Class Methods + (instancetype)bridgeAPIRequestWithProtocolType:(FBSDKBridgeAPIProtocolType)protocolType scheme:(NSString *)scheme @@ -59,21 +59,21 @@ + (NSDictionary *)protocolMap static dispatch_once_t onceToken; dispatch_once(&onceToken, ^{ _protocolMap = @{ - @(FBSDKBridgeAPIProtocolTypeNative): @{ - FBSDK_CANOPENURL_FACEBOOK:[[FBSDKBridgeAPIProtocolNativeV1 alloc] initWithAppScheme:@"fbapi20130214"], - FBSDK_CANOPENURL_MESSENGER:[[FBSDKBridgeAPIProtocolNativeV1 alloc] initWithAppScheme:@"fb-messenger-share-api"], - FBSDK_CANOPENURL_MSQRD_PLAYER:[[FBSDKBridgeAPIProtocolNativeV1 alloc] initWithAppScheme:@"msqrdplayer-api20170208"] - }, - @(FBSDKBridgeAPIProtocolTypeWeb): @{ - @"https": [[FBSDKBridgeAPIProtocolWebV1 alloc] init], - @"web": [[FBSDKBridgeAPIProtocolWebV2 alloc] init] - }, - }; + @(FBSDKBridgeAPIProtocolTypeNative) : @{ + FBSDK_CANOPENURL_FACEBOOK : [[FBSDKBridgeAPIProtocolNativeV1 alloc] initWithAppScheme:@"fbapi20130214"], + FBSDK_CANOPENURL_MESSENGER : [[FBSDKBridgeAPIProtocolNativeV1 alloc] initWithAppScheme:@"fb-messenger-share-api"], + FBSDK_CANOPENURL_MSQRD_PLAYER : [[FBSDKBridgeAPIProtocolNativeV1 alloc] initWithAppScheme:@"msqrdplayer-api20170208"] + }, + @(FBSDKBridgeAPIProtocolTypeWeb) : @{ + @"https" : [[FBSDKBridgeAPIProtocolWebV1 alloc] init], + @"web" : [[FBSDKBridgeAPIProtocolWebV2 alloc] init] + }, + }; }); return _protocolMap; } -#pragma mark - Object Lifecycle + #pragma mark - Object Lifecycle - (instancetype)initWithProtocol:(id)protocol protocolType:(FBSDKBridgeAPIProtocolType)protocolType @@ -100,7 +100,7 @@ - (instancetype)initWithProtocol:(id)protocol return self; } -#pragma mark - Public Methods + #pragma mark - Public Methods - (NSURL *)requestURL:(NSError *__autoreleasing *)errorRef { @@ -120,8 +120,8 @@ - (NSURL *)requestURL:(NSError *__autoreleasing *)errorRef NSMutableDictionary *queryParameters = [[NSMutableDictionary alloc] initWithDictionary:requestQueryParameters]; [FBSDKTypeUtility dictionary:queryParameters setObject:[FBSDKSettings appID] forKey:FBSDKBridgeAPIAppIDKey]; [FBSDKTypeUtility dictionary:queryParameters - setObject:[FBSDKSettings appURLSchemeSuffix] - forKey:FBSDKBridgeAPISchemeSuffixKey]; + setObject:[FBSDKSettings appURLSchemeSuffix] + forKey:FBSDKBridgeAPISchemeSuffixKey]; requestURL = [FBSDKInternalUtility URLWithScheme:requestURL.scheme host:requestURL.host path:requestURL.path @@ -130,7 +130,7 @@ - (NSURL *)requestURL:(NSError *__autoreleasing *)errorRef return requestURL; } -#pragma mark - NSCopying + #pragma mark - NSCopying - (id)copyWithZone:(NSZone *)zone { diff --git a/FBSDKCoreKit/FBSDKCoreKit/Internal/BridgeAPI/FBSDKBridgeAPIResponse.h b/FBSDKCoreKit/FBSDKCoreKit/Internal/BridgeAPI/FBSDKBridgeAPIResponse.h index 8cbe0cd409..a17bd03e7b 100644 --- a/FBSDKCoreKit/FBSDKCoreKit/Internal/BridgeAPI/FBSDKBridgeAPIResponse.h +++ b/FBSDKCoreKit/FBSDKCoreKit/Internal/BridgeAPI/FBSDKBridgeAPIResponse.h @@ -36,16 +36,16 @@ NS_SWIFT_NAME(BridgeAPIResponse) - (instancetype)init NS_UNAVAILABLE; + (instancetype)new NS_UNAVAILABLE; -+ (instancetype)bridgeAPIResponseWithRequest:(FBSDKBridgeAPIRequest *)request error:(NSError *)error; -+ (instancetype)bridgeAPIResponseWithRequest:(FBSDKBridgeAPIRequest *)request ++ (instancetype)bridgeAPIResponseWithRequest:(NSObject *)request error:(NSError *)error; ++ (instancetype)bridgeAPIResponseWithRequest:(NSObject *)request responseURL:(NSURL *)responseURL sourceApplication:(NSString *)sourceApplication error:(NSError *__autoreleasing *)errorRef; -+ (instancetype)bridgeAPIResponseCancelledWithRequest:(FBSDKBridgeAPIRequest *)request; ++ (instancetype)bridgeAPIResponseCancelledWithRequest:(NSObject *)request; @property (nonatomic, assign, readonly, getter=isCancelled) BOOL cancelled; @property (nonatomic, copy, readonly) NSError *error; -@property (nonatomic, copy, readonly) FBSDKBridgeAPIRequest *request; +@property (nonatomic, copy, readonly) NSObject *request; @property (nonatomic, copy, readonly) NSDictionary *responseParameters; @end diff --git a/FBSDKCoreKit/FBSDKCoreKit/Internal/BridgeAPI/FBSDKBridgeAPIResponse.m b/FBSDKCoreKit/FBSDKCoreKit/Internal/BridgeAPI/FBSDKBridgeAPIResponse.m index 9595195db0..7416dc87f5 100644 --- a/FBSDKCoreKit/FBSDKCoreKit/Internal/BridgeAPI/FBSDKBridgeAPIResponse.m +++ b/FBSDKCoreKit/FBSDKCoreKit/Internal/BridgeAPI/FBSDKBridgeAPIResponse.m @@ -20,27 +20,26 @@ #if !TARGET_OS_TV -#import "FBSDKBridgeAPIResponse.h" + #import "FBSDKBridgeAPIResponse.h" -#import "FBSDKBridgeAPIProtocol.h" -#import "FBSDKBridgeAPIProtocolType.h" -#import "FBSDKBridgeAPIRequest+Private.h" -#import "FBSDKInternalUtility.h" -#import "FBSDKTypeUtility.h" + #import "FBSDKBridgeAPIProtocol.h" + #import "FBSDKBridgeAPIProtocolType.h" + #import "FBSDKBridgeAPIRequest+Private.h" + #import "FBSDKInternalUtility.h" @interface FBSDKBridgeAPIResponse () -- (instancetype)initWithRequest:(FBSDKBridgeAPIRequest *)request +- (instancetype)initWithRequest:(id)request responseParameters:(NSDictionary *)responseParameters cancelled:(BOOL)cancelled error:(NSError *)error -NS_DESIGNATED_INITIALIZER; + NS_DESIGNATED_INITIALIZER; @end @implementation FBSDKBridgeAPIResponse -#pragma mark - Class Methods + #pragma mark - Class Methods -+ (instancetype)bridgeAPIResponseWithRequest:(FBSDKBridgeAPIRequest *)request error:(NSError *)error ++ (instancetype)bridgeAPIResponseWithRequest:(id)request error:(NSError *)error { return [[self alloc] initWithRequest:request responseParameters:nil @@ -48,7 +47,7 @@ + (instancetype)bridgeAPIResponseWithRequest:(FBSDKBridgeAPIRequest *)request er error:error]; } -+ (instancetype)bridgeAPIResponseWithRequest:(FBSDKBridgeAPIRequest *)request ++ (instancetype)bridgeAPIResponseWithRequest:(NSObject *)request responseURL:(NSURL *)responseURL sourceApplication:(NSString *)sourceApplication error:(NSError *__autoreleasing *)errorRef @@ -59,13 +58,13 @@ + (instancetype)bridgeAPIResponseWithRequest:(FBSDKBridgeAPIRequest *)request // https://forums.developer.apple.com/thread/119118 } else { switch (protocolType) { - case FBSDKBridgeAPIProtocolTypeNative:{ + case FBSDKBridgeAPIProtocolTypeNative: { if (![FBSDKInternalUtility isFacebookBundleIdentifier:sourceApplication]) { return nil; } break; } - case FBSDKBridgeAPIProtocolTypeWeb:{ + case FBSDKBridgeAPIProtocolTypeWeb: { if (![FBSDKInternalUtility isSafariBundleIdentifier:sourceApplication]) { return nil; } @@ -96,7 +95,7 @@ + (instancetype)bridgeAPIResponseWithRequest:(FBSDKBridgeAPIRequest *)request error:error]; } -+ (instancetype)bridgeAPIResponseCancelledWithRequest:(FBSDKBridgeAPIRequest *)request ++ (instancetype)bridgeAPIResponseCancelledWithRequest:(NSObject *)request { return [[self alloc] initWithRequest:request responseParameters:nil @@ -104,9 +103,9 @@ + (instancetype)bridgeAPIResponseCancelledWithRequest:(FBSDKBridgeAPIRequest *)r error:nil]; } -#pragma mark - Object Lifecycle + #pragma mark - Object Lifecycle -- (instancetype)initWithRequest:(FBSDKBridgeAPIRequest *)request +- (instancetype)initWithRequest:(NSObject *)request responseParameters:(NSDictionary *)responseParameters cancelled:(BOOL)cancelled error:(NSError *)error @@ -120,7 +119,7 @@ - (instancetype)initWithRequest:(FBSDKBridgeAPIRequest *)request return self; } -#pragma mark - NSCopying + #pragma mark - NSCopying - (id)copyWithZone:(NSZone *)zone { diff --git a/FBSDKCoreKit/FBSDKCoreKit/Internal/BridgeAPI/ProtocolVersions/FBSDKBridgeAPIProtocolNativeV1.m b/FBSDKCoreKit/FBSDKCoreKit/Internal/BridgeAPI/ProtocolVersions/FBSDKBridgeAPIProtocolNativeV1.m index b4da96ac83..94d38f9230 100644 --- a/FBSDKCoreKit/FBSDKCoreKit/Internal/BridgeAPI/ProtocolVersions/FBSDKBridgeAPIProtocolNativeV1.m +++ b/FBSDKCoreKit/FBSDKCoreKit/Internal/BridgeAPI/ProtocolVersions/FBSDKBridgeAPIProtocolNativeV1.m @@ -20,20 +20,19 @@ #if !TARGET_OS_TV -#import "FBSDKBridgeAPIProtocolNativeV1.h" + #import "FBSDKBridgeAPIProtocolNativeV1.h" -#import + #import -#import "FBSDKApplicationDelegate+Internal.h" -#import "FBSDKBase64.h" -#import "FBSDKBridgeAPIRequest.h" -#import "FBSDKConstants.h" -#import "FBSDKError.h" -#import "FBSDKInternalUtility.h" -#import "FBSDKSettings.h" -#import "FBSDKTypeUtility.h" + #import "FBSDKApplicationDelegate+Internal.h" + #import "FBSDKBase64.h" + #import "FBSDKBridgeAPIRequest.h" + #import "FBSDKConstants.h" + #import "FBSDKError.h" + #import "FBSDKInternalUtility.h" + #import "FBSDKSettings.h" -#define FBSDKBridgeAPIProtocolNativeV1BridgeMaxBase64DataLengthThreshold (1024 * 16) + #define FBSDKBridgeAPIProtocolNativeV1BridgeMaxBase64DataLengthThreshold (1024 * 16) const FBSDKBridgeAPIProtocolNativeV1OutputKeysStruct FBSDKBridgeAPIProtocolNativeV1OutputKeys = { @@ -62,8 +61,7 @@ .error = @"error", }; -static const struct -{ +static const struct { __unsafe_unretained NSString *isBase64; __unsafe_unretained NSString *isPasteboard; __unsafe_unretained NSString *tag; @@ -78,8 +76,7 @@ static NSString *const FBSDKBridgeAPIProtocolNativeV1DataPasteboardKey = @"com.facebook.Facebook.FBAppBridgeType"; -static const struct -{ +static const struct { __unsafe_unretained NSString *data; __unsafe_unretained NSString *image; } FBSDKBridgeAPIProtocolNativeV1DataTypeTags = @@ -89,8 +86,7 @@ .image = @"png", }; -static const struct -{ +static const struct { __unsafe_unretained NSString *code; __unsafe_unretained NSString *domain; __unsafe_unretained NSString *userInfo; @@ -103,7 +99,7 @@ @implementation FBSDKBridgeAPIProtocolNativeV1 -#pragma mark - Object Lifecycle + #pragma mark - Object Lifecycle - (instancetype)initWithAppScheme:(NSString *)appScheme { @@ -127,7 +123,7 @@ - (instancetype)initWithAppScheme:(NSString *)appScheme return self; } -#pragma mark - FBSDKBridgeAPIProtocol + #pragma mark - FBSDKBridgeAPIProtocol - (NSURL *)requestURLWithActionID:(NSString *)actionID scheme:(NSString *)scheme @@ -141,7 +137,7 @@ - (NSURL *)requestURLWithActionID:(NSString *)actionID NSMutableDictionary *const queryParameters = [[NSMutableDictionary alloc] init]; [FBSDKTypeUtility dictionary:queryParameters setObject:methodVersion - forKey:FBSDKBridgeAPIProtocolNativeV1OutputKeys.methodVersion]; + forKey:FBSDKBridgeAPIProtocolNativeV1OutputKeys.methodVersion]; if (parameters.count) { NSString *const parametersString = [self _JSONStringForObject:parameters enablePasteboard:YES error:errorRef]; @@ -151,11 +147,13 @@ - (NSURL *)requestURLWithActionID:(NSString *)actionID NSString *const escapedParametersString = [parametersString stringByReplacingOccurrencesOfString:@"&" withString:@"%26" options:NSCaseInsensitiveSearch - range:NSMakeRange(0, - parametersString.length)]; + range:NSMakeRange( + 0, + parametersString.length + )]; [FBSDKTypeUtility dictionary:queryParameters - setObject:escapedParametersString - forKey:FBSDKBridgeAPIProtocolNativeV1OutputKeys.methodArgs]; + setObject:escapedParametersString + forKey:FBSDKBridgeAPIProtocolNativeV1OutputKeys.methodArgs]; } NSDictionary *const bridgeParameters = [self _bridgeParametersWithActionID:actionID error:errorRef]; @@ -167,9 +165,8 @@ - (NSURL *)requestURLWithActionID:(NSString *)actionID return nil; } [FBSDKTypeUtility dictionary:queryParameters - setObject:bridgeParametersString - forKey:FBSDKBridgeAPIProtocolNativeV1OutputKeys.bridgeArgs]; - + setObject:bridgeParametersString + forKey:FBSDKBridgeAPIProtocolNativeV1OutputKeys.bridgeArgs]; return [FBSDKInternalUtility URLWithScheme:self.appScheme host:host @@ -234,7 +231,7 @@ - (NSDictionary *)responseParametersForActionID:(NSString *)actionID return resultParameters; } -#pragma mark - Helper Methods + #pragma mark - Helper Methods - (UIImage *)_appIcon { @@ -254,13 +251,13 @@ - (NSDictionary *)_bridgeParametersWithActionID:(NSString *)actionID error:(NSEr { NSMutableDictionary *bridgeParameters = [[NSMutableDictionary alloc] init]; [FBSDKTypeUtility dictionary:bridgeParameters setObject:actionID - forKey:FBSDKBridgeAPIProtocolNativeV1BridgeParameterOutputKeys.actionID]; + forKey:FBSDKBridgeAPIProtocolNativeV1BridgeParameterOutputKeys.actionID]; [FBSDKTypeUtility dictionary:bridgeParameters setObject:[self _appIcon] - forKey:FBSDKBridgeAPIProtocolNativeV1BridgeParameterOutputKeys.appIcon]; + forKey:FBSDKBridgeAPIProtocolNativeV1BridgeParameterOutputKeys.appIcon]; [FBSDKTypeUtility dictionary:bridgeParameters setObject:[FBSDKSettings displayName] - forKey:FBSDKBridgeAPIProtocolNativeV1BridgeParameterOutputKeys.appName]; + forKey:FBSDKBridgeAPIProtocolNativeV1BridgeParameterOutputKeys.appName]; [FBSDKTypeUtility dictionary:bridgeParameters setObject:[FBSDKSettings sdkVersion] - forKey:FBSDKBridgeAPIProtocolNativeV1BridgeParameterOutputKeys.sdkVersion]; + forKey:FBSDKBridgeAPIProtocolNativeV1BridgeParameterOutputKeys.sdkVersion]; return bridgeParameters; } @@ -269,20 +266,20 @@ - (NSError *)_errorWithDictionary:(NSDictionary *)dictionary if (!dictionary) { return nil; } - NSString *domain = [FBSDKTypeUtility stringValue:dictionary[FBSDKBridgeAPIProtocolNativeV1ErrorKeys.domain]] ?: - FBSDKErrorDomain; - NSInteger code = [FBSDKTypeUtility integerValue:dictionary[FBSDKBridgeAPIProtocolNativeV1ErrorKeys.code]] ?: - FBSDKErrorUnknown; + NSString *domain = [FBSDKTypeUtility stringValue:dictionary[FBSDKBridgeAPIProtocolNativeV1ErrorKeys.domain]] + ?: FBSDKErrorDomain; + NSInteger code = [FBSDKTypeUtility integerValue:dictionary[FBSDKBridgeAPIProtocolNativeV1ErrorKeys.code]] + ?: FBSDKErrorUnknown; NSDictionary *userInfo = [FBSDKTypeUtility dictionaryValue:dictionary[FBSDKBridgeAPIProtocolNativeV1ErrorKeys.userInfo]]; return [NSError errorWithDomain:domain code:code userInfo:userInfo]; } -#pragma clang diagnostic push -#pragma clang diagnostic ignored "-Wdeprecated-declarations" + #pragma clang diagnostic push + #pragma clang diagnostic ignored "-Wdeprecated-declarations" - (NSString *)_JSONStringForObject:(id)object enablePasteboard:(BOOL)enablePasteboard error:(NSError **)errorRef { __block BOOL didAddToPasteboard = NO; - return [FBSDKBasicUtility JSONStringForObject:object error:errorRef invalidObjectHandler:^id(id invalidObject, BOOL *stop) { + return [FBSDKBasicUtility JSONStringForObject:object error:errorRef invalidObjectHandler:^id (id invalidObject, BOOL *stop) { NSString *dataTag = FBSDKBridgeAPIProtocolNativeV1DataTypeTags.data; if ([invalidObject isKindOfClass:[UIImage class]]) { UIImage *image = (UIImage *)invalidObject; @@ -297,8 +294,8 @@ - (NSString *)_JSONStringForObject:(id)object enablePasteboard:(BOOL)enablePaste dictionary[FBSDKBridgeAPIProtocolNativeV1DataKeys.isBase64] = @YES; [FBSDKTypeUtility dictionary:dictionary setObject:dataTag forKey:FBSDKBridgeAPIProtocolNativeV1DataKeys.tag]; [FBSDKTypeUtility dictionary:dictionary - setObject:[FBSDKBase64 encodeData:data] - forKey:FBSDKBridgeAPIProtocolNativeV1DataKeys.value]; + setObject:[FBSDKBase64 encodeData:data] + forKey:FBSDKBridgeAPIProtocolNativeV1DataKeys.value]; } else { dictionary[FBSDKBridgeAPIProtocolNativeV1DataKeys.isPasteboard] = @YES; [FBSDKTypeUtility dictionary:dictionary setObject:dataTag forKey:FBSDKBridgeAPIProtocolNativeV1DataKeys.tag]; @@ -311,8 +308,8 @@ - (NSString *)_JSONStringForObject:(id)object enablePasteboard:(BOOL)enablePaste // the Facebook app will not clear the value with this version of the protocol, so we should do it when the app // becomes active again NSString *pasteboardName = self->_pasteboard.name; - if ([pasteboardName isEqualToString:UIPasteboardNameGeneral] || - [pasteboardName isEqualToString:UIPasteboardNameFind]) { + if ([pasteboardName isEqualToString:UIPasteboardNameGeneral] + || [pasteboardName isEqualToString:UIPasteboardNameFind]) { [[self class] clearData:data fromPasteboardOnApplicationDidBecomeActive:self->_pasteboard]; } } @@ -323,18 +320,22 @@ - (NSString *)_JSONStringForObject:(id)object enablePasteboard:(BOOL)enablePaste return invalidObject; }]; } -#pragma clang diagnostic pop + + #pragma clang diagnostic pop + (void)clearData:(NSData *)data fromPasteboardOnApplicationDidBecomeActive:(UIPasteboard *)pasteboard { - void(^notificationBlock)(NSNotification *) = ^(NSNotification *note){ + void (^notificationBlock)(NSNotification *) = ^(NSNotification *note) { + // After testing, it seems that reading the pasteboard will not result in a system dialog since + // the clipboard write originates from the app that loads the SDK NSData *pasteboardData = [pasteboard dataForPasteboardType:FBSDKBridgeAPIProtocolNativeV1DataPasteboardKey]; + // We need to compare the data to make sure we don't clear different apps data in a multi-tasking environment if ([data isEqualToData:pasteboardData]) { [pasteboard setData:[NSData data] forPasteboardType:FBSDKBridgeAPIProtocolNativeV1DataPasteboardKey]; } }; [[NSNotificationCenter defaultCenter] addObserverForName:FBSDKApplicationDidBecomeActiveNotification - object:[FBSDKApplicationDelegate sharedInstance] + object:nil queue:nil usingBlock:notificationBlock]; } diff --git a/FBSDKCoreKit/FBSDKCoreKit/Internal/BridgeAPI/ProtocolVersions/FBSDKBridgeAPIProtocolWebV1.m b/FBSDKCoreKit/FBSDKCoreKit/Internal/BridgeAPI/ProtocolVersions/FBSDKBridgeAPIProtocolWebV1.m index 9747081f71..455c567c9c 100644 --- a/FBSDKCoreKit/FBSDKCoreKit/Internal/BridgeAPI/ProtocolVersions/FBSDKBridgeAPIProtocolWebV1.m +++ b/FBSDKCoreKit/FBSDKCoreKit/Internal/BridgeAPI/ProtocolVersions/FBSDKBridgeAPIProtocolWebV1.m @@ -20,23 +20,22 @@ #if !TARGET_OS_TV -#import "FBSDKBridgeAPIProtocolWebV1.h" + #import "FBSDKBridgeAPIProtocolWebV1.h" -#import + #import -#import "FBSDKBase64.h" -#import "FBSDKBridgeAPIRequest.h" -#import "FBSDKError.h" -#import "FBSDKInternalUtility.h" -#import "FBSDKSettings.h" -#import "FBSDKTypeUtility.h" + #import "FBSDKBase64.h" + #import "FBSDKBridgeAPIRequest.h" + #import "FBSDKError.h" + #import "FBSDKInternalUtility.h" + #import "FBSDKSettings.h" -#define FBSDK_BRIDGE_API_PROTOCOL_WEB_V1_ACTION_ID_KEY @"action_id" -#define FBSDK_BRIDGE_API_PROTOCOL_WEB_V1_BRIDGE_ARGS_KEY @"bridge_args" + #define FBSDK_BRIDGE_API_PROTOCOL_WEB_V1_ACTION_ID_KEY @"action_id" + #define FBSDK_BRIDGE_API_PROTOCOL_WEB_V1_BRIDGE_ARGS_KEY @"bridge_args" @implementation FBSDKBridgeAPIProtocolWebV1 -#pragma mark - FBSDKBridgeAPIProtocol + #pragma mark - FBSDKBridgeAPIProtocol - (NSURL *)requestURLWithActionID:(NSString *)actionID scheme:(NSString *)scheme @@ -47,10 +46,10 @@ - (NSURL *)requestURLWithActionID:(NSString *)actionID { NSMutableDictionary *queryParameters = [[NSMutableDictionary alloc] initWithDictionary:parameters]; [FBSDKTypeUtility dictionary:queryParameters setObject:@"touch" forKey:@"display"]; - NSString *bridgeArgs = [FBSDKBasicUtility JSONStringForObject:@{ FBSDK_BRIDGE_API_PROTOCOL_WEB_V1_ACTION_ID_KEY: actionID } + NSString *bridgeArgs = [FBSDKBasicUtility JSONStringForObject:@{ FBSDK_BRIDGE_API_PROTOCOL_WEB_V1_ACTION_ID_KEY : actionID } error:NULL invalidObjectHandler:NULL]; - NSDictionary *redirectQueryParameters = @{ FBSDK_BRIDGE_API_PROTOCOL_WEB_V1_BRIDGE_ARGS_KEY: bridgeArgs }; + NSDictionary *redirectQueryParameters = @{ FBSDK_BRIDGE_API_PROTOCOL_WEB_V1_BRIDGE_ARGS_KEY : bridgeArgs }; NSURL *redirectURL = [FBSDKInternalUtility appURLWithHost:@"bridge" path:methodName queryParameters:redirectQueryParameters @@ -73,17 +72,17 @@ - (NSDictionary *)responseParametersForActionID:(NSString *)actionID } NSInteger errorCode = [FBSDKTypeUtility integerValue:queryParameters[@"error_code"]]; switch (errorCode) { - case 0:{ + case 0: { // good to go, handle the other codes and bail break; } - case 4201:{ + case 4201: { return @{ - @"completionGesture": @"cancel", - }; + @"completionGesture" : @"cancel", + }; break; } - default:{ + default: { if (errorRef != NULL) { *errorRef = [FBSDKError errorWithCode:errorCode message:[FBSDKTypeUtility stringValue:queryParameters[@"error_message"]]]; diff --git a/FBSDKCoreKit/FBSDKCoreKit/Internal/BridgeAPI/ProtocolVersions/FBSDKBridgeAPIProtocolWebV2.m b/FBSDKCoreKit/FBSDKCoreKit/Internal/BridgeAPI/ProtocolVersions/FBSDKBridgeAPIProtocolWebV2.m index 7f5d5d8828..83bf71fba8 100644 --- a/FBSDKCoreKit/FBSDKCoreKit/Internal/BridgeAPI/ProtocolVersions/FBSDKBridgeAPIProtocolWebV2.m +++ b/FBSDKCoreKit/FBSDKCoreKit/Internal/BridgeAPI/ProtocolVersions/FBSDKBridgeAPIProtocolWebV2.m @@ -20,21 +20,21 @@ #if !TARGET_OS_TV -#import "FBSDKBridgeAPIProtocolWebV2.h" + #import "FBSDKBridgeAPIProtocolWebV2.h" -#import "FBSDKBridgeAPIProtocolNativeV1.h" -#import "FBSDKDialogConfiguration.h" -#import "FBSDKError.h" -#import "FBSDKInternalUtility.h" -#import "FBSDKServerConfiguration.h" -#import "FBSDKServerConfigurationManager.h" + #import "FBSDKBridgeAPIProtocolNativeV1.h" + #import "FBSDKDialogConfiguration.h" + #import "FBSDKError.h" + #import "FBSDKInternalUtility.h" + #import "FBSDKServerConfiguration.h" + #import "FBSDKServerConfigurationManager.h" @implementation FBSDKBridgeAPIProtocolWebV2 { FBSDKBridgeAPIProtocolNativeV1 *_nativeProtocol; } -#pragma mark - Object Lifecycle + #pragma mark - Object Lifecycle - (instancetype)init { @@ -47,17 +47,17 @@ - (instancetype)init return self; } -#pragma mark - FBSDKBridgeAPIProtocol + #pragma mark - FBSDKBridgeAPIProtocol - (NSURL *)_redirectURLWithActionID:(NSString *)actionID methodName:(NSString *)methodName error:(NSError **)errorRef { NSDictionary *queryParameters = nil; if (actionID) { - NSDictionary *bridgeArgs = @{ FBSDKBridgeAPIProtocolNativeV1BridgeParameterInputKeys.actionID: actionID }; + NSDictionary *bridgeArgs = @{ FBSDKBridgeAPIProtocolNativeV1BridgeParameterInputKeys.actionID : actionID }; NSString *bridgeArgsString = [FBSDKBasicUtility JSONStringForObject:bridgeArgs error:NULL invalidObjectHandler:NULL]; - queryParameters = @{ FBSDKBridgeAPIProtocolNativeV1InputKeys.bridgeArgs: bridgeArgsString }; + queryParameters = @{ FBSDKBridgeAPIProtocolNativeV1InputKeys.bridgeArgs : bridgeArgsString }; } return [FBSDKInternalUtility appURLWithHost:@"bridge" path:methodName queryParameters:queryParameters error:errorRef]; } diff --git a/FBSDKCoreKit/FBSDKCoreKit/Internal/Device/FBSDKDeviceButton+Internal.h b/FBSDKCoreKit/FBSDKCoreKit/Internal/Device/FBSDKDeviceButton+Internal.h index d9c41d450f..0c930865fa 100644 --- a/FBSDKCoreKit/FBSDKCoreKit/Internal/Device/FBSDKDeviceButton+Internal.h +++ b/FBSDKCoreKit/FBSDKCoreKit/Internal/Device/FBSDKDeviceButton+Internal.h @@ -20,13 +20,13 @@ #if TARGET_OS_TV -#import + #import -#if SWIFT_PACKAGE -#import "FBSDKDeviceButton.h" -#else -#import -#endif + #if SWIFT_PACKAGE + #import "FBSDKDeviceButton.h" + #else + #import + #endif @interface FBSDKDeviceButton () diff --git a/FBSDKCoreKit/FBSDKCoreKit/Internal/Device/FBSDKDeviceDialogView.m b/FBSDKCoreKit/FBSDKCoreKit/Internal/Device/FBSDKDeviceDialogView.m index 6cdce274ec..ff18d8faa0 100644 --- a/FBSDKCoreKit/FBSDKCoreKit/Internal/Device/FBSDKDeviceDialogView.m +++ b/FBSDKCoreKit/FBSDKCoreKit/Internal/Device/FBSDKDeviceDialogView.m @@ -20,10 +20,10 @@ #if TARGET_OS_TV -#import "FBSDKDeviceDialogView.h" + #import "FBSDKDeviceDialogView.h" -#import "FBSDKCoreKit+Internal.h" -#import "FBSDKDeviceUtilities.h" + #import "FBSDKCoreKit+Internal.h" + #import "FBSDKDeviceUtilities.h" @implementation FBSDKDeviceDialogView { @@ -41,7 +41,7 @@ - (instancetype)initWithFrame:(CGRect)frame return self; } -#pragma mark - Properties + #pragma mark - Properties - (void)setConfirmationCode:(NSString *)confirmationCode { @@ -61,7 +61,12 @@ - (void)setConfirmationCode:(NSString *)confirmationCode } } -#pragma mark - Helpers + #pragma mark - Helpers + +- (UIColor *)logoColor +{ + return [UIColor colorWithRed:66.0 / 255.0 green:103.0 / 255.0 blue:178.0 / 255.0 alpha:1]; +} - (void)buildView { @@ -76,7 +81,7 @@ - (void)buildView const CGFloat kLogoMargin = 30; const CGFloat kInstructionTextHorizontalMargin = 151; const CGFloat kConfirmationCodeFontSize = 108; - const CGFloat kFontColorValue = 119.0/255.0; + const CGFloat kFontColorValue = 119.0 / 255.0; const CGFloat kInstructionFontSize = 36; const CGFloat kQRCodeMargin = 50; const CGFloat kQRCodeSize = 200; @@ -96,7 +101,7 @@ - (void)buildView // 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]; + 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; @@ -105,8 +110,8 @@ - (void)buildView // 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]; + FBSDKLogo *logoHelper = [FBSDKLogo new]; + UIImage *image = [logoHelper imageWithSize:imageSize color:self.logoColor]; image = [image resizableImageWithCapInsets:UIEdgeInsetsZero resizingMode:UIImageResizingModeStretch]; UIImageView *imageView = [[UIImageView alloc] initWithImage:image]; imageView.translatesAutoresizingMaskIntoConstraints = NO; @@ -129,7 +134,7 @@ - (void)buildView // 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.textColor = self.logoColor; _confirmationCodeLabel.font = [UIFont systemFontOfSize:kConfirmationCodeFontSize weight:UIFontWeightLight]; _confirmationCodeLabel.textAlignment = NSTextAlignmentCenter; [_confirmationCodeLabel sizeToFit]; @@ -150,16 +155,18 @@ - (void)buildView [_qrImageView.leadingAnchor constraintEqualToAnchor:dialogView.leadingAnchor constant:kQRCodeMargin].active = YES; [_qrImageView.trailingAnchor constraintEqualToAnchor:_qrImageView.leadingAnchor - constant:kQRCodeSize].active = YES; + constant:kQRCodeSize].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 *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]; NSMutableParagraphStyle *instructionLabelParagraphStyle = [[NSMutableParagraphStyle alloc] init]; @@ -200,11 +207,13 @@ - (void)buildView 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") + [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]; diff --git a/FBSDKCoreKit/FBSDKCoreKit/Internal/Device/FBSDKDeviceUtilities.m b/FBSDKCoreKit/FBSDKCoreKit/Internal/Device/FBSDKDeviceUtilities.m index e54e87c8de..9f4c2e1cb3 100644 --- a/FBSDKCoreKit/FBSDKCoreKit/Internal/Device/FBSDKDeviceUtilities.m +++ b/FBSDKCoreKit/FBSDKCoreKit/Internal/Device/FBSDKDeviceUtilities.m @@ -20,7 +20,7 @@ #if TARGET_OS_TV -#import "FBSDKDeviceUtilities.h" + #import "FBSDKDeviceUtilities.h" @implementation FBSDKDeviceUtilities @@ -41,8 +41,10 @@ + (UIImage *)buildQRCodeWithAuthorizationCode:(NSString *)authorizationCode CGSize qrOutputSize = CGSizeMake(200, 200); CIImage *resizedImage = - [qrCodeImage imageByApplyingTransform: CGAffineTransformMakeScale(qrOutputSize.width / CGRectGetWidth(qrImageSize), - qrOutputSize.height / CGRectGetHeight(qrImageSize))]; + [qrCodeImage imageByApplyingTransform:CGAffineTransformMakeScale( + qrOutputSize.width / CGRectGetWidth(qrImageSize), + qrOutputSize.height / CGRectGetHeight(qrImageSize) + )]; return [UIImage imageWithCIImage:resizedImage]; } diff --git a/FBSDKCoreKit/FBSDKCoreKit/Internal/Device/FBSDKDeviceViewControllerBase+Internal.h b/FBSDKCoreKit/FBSDKCoreKit/Internal/Device/FBSDKDeviceViewControllerBase+Internal.h index 020b4e9d13..3e6628aeac 100644 --- a/FBSDKCoreKit/FBSDKCoreKit/Internal/Device/FBSDKDeviceViewControllerBase+Internal.h +++ b/FBSDKCoreKit/FBSDKCoreKit/Internal/Device/FBSDKDeviceViewControllerBase+Internal.h @@ -20,14 +20,14 @@ #if TARGET_OS_TV -#if SWIFT_PACKAGE -#import "FBSDKDeviceViewControllerBase.h" -#else -#import -#endif + #if SWIFT_PACKAGE + #import "FBSDKDeviceViewControllerBase.h" + #else + #import + #endif -#import "FBSDKCoreKit+Internal.h" -#import "FBSDKDeviceDialogView.h" + #import "FBSDKCoreKit+Internal.h" + #import "FBSDKDeviceDialogView.h" @class FBSDKDeviceDialogView; @@ -38,13 +38,13 @@ NS_ASSUME_NONNULL_BEGIN This is an internal API that should not be used directly and is subject to change. */ -@interface FBSDKDeviceViewControllerBase()< -UIViewControllerAnimatedTransitioning, -UIViewControllerTransitioningDelegate, -FBSDKDeviceDialogViewDelegate +@interface FBSDKDeviceViewControllerBase () < + UIViewControllerAnimatedTransitioning, + UIViewControllerTransitioningDelegate, + FBSDKDeviceDialogViewDelegate > -@property (nonatomic, strong, readonly) FBSDKDeviceDialogView *deviceDialogView; +@property (nonatomic, readonly, strong) FBSDKDeviceDialogView *deviceDialogView; @end diff --git a/FBSDKCoreKit/FBSDKCoreKit/Internal/Device/FBSDKModalFormPresentationController.m b/FBSDKCoreKit/FBSDKCoreKit/Internal/Device/FBSDKModalFormPresentationController.m index e49e69a5fd..9356ebab32 100644 --- a/FBSDKCoreKit/FBSDKCoreKit/Internal/Device/FBSDKModalFormPresentationController.m +++ b/FBSDKCoreKit/FBSDKCoreKit/Internal/Device/FBSDKModalFormPresentationController.m @@ -20,9 +20,10 @@ #if TARGET_OS_TV -#import "FBSDKModalFormPresentationController.h" + #import "FBSDKModalFormPresentationController.h" -@implementation FBSDKModalFormPresentationController { +@implementation FBSDKModalFormPresentationController +{ UIView *_dimmedView; } @@ -35,16 +36,16 @@ - (UIView *)dimmedView return _dimmedView; } -#pragma mark - UIPresentationController overrides + #pragma mark - UIPresentationController overrides - (void)presentationTransitionWillBegin { [self.containerView addSubview:[self dimmedView]]; [self.containerView addSubview:[self presentedView]]; [self.presentingViewController.transitionCoordinator - animateAlongsideTransition:^(id _Nonnull context) { - [self dimmedView].alpha = 1.0; - } completion:NULL]; + animateAlongsideTransition:^(id _Nonnull context) { + [self dimmedView].alpha = 1.0; + } completion:NULL]; } - (void)presentationTransitionDidEnd:(BOOL)completed @@ -57,9 +58,9 @@ - (void)presentationTransitionDidEnd:(BOOL)completed - (void)dismissalTransitionWillBegin { [self.presentingViewController.transitionCoordinator - animateAlongsideTransition:^(id _Nonnull context) { - [self dimmedView].alpha = 0; - } completion:NULL]; + animateAlongsideTransition:^(id _Nonnull context) { + [self dimmedView].alpha = 0; + } completion:NULL]; } - (void)dismissalTransitionDidEnd:(BOOL)completed @@ -73,9 +74,9 @@ - (void)dismissalTransitionDidEnd:(BOOL)completed - (void)viewWillTransitionToSize:(CGSize)size withTransitionCoordinator:(id)coordinator { [super viewWillTransitionToSize:size withTransitionCoordinator:coordinator]; - [coordinator animateAlongsideTransition:^(id _Nonnull context) { - [self dimmedView].frame = self.containerView.bounds; - } completion:NULL]; + [coordinator animateAlongsideTransition:^(id _Nonnull context) { + [self dimmedView].frame = self.containerView.bounds; + } completion:NULL]; } @end diff --git a/FBSDKCoreKit/FBSDKCoreKit/Internal/Device/FBSDKSmartDeviceDialogView.m b/FBSDKCoreKit/FBSDKCoreKit/Internal/Device/FBSDKSmartDeviceDialogView.m index 430801257b..1f7ee28bd6 100644 --- a/FBSDKCoreKit/FBSDKCoreKit/Internal/Device/FBSDKSmartDeviceDialogView.m +++ b/FBSDKCoreKit/FBSDKCoreKit/Internal/Device/FBSDKSmartDeviceDialogView.m @@ -20,10 +20,10 @@ #if TARGET_OS_TV -#import "FBSDKSmartDeviceDialogView.h" + #import "FBSDKSmartDeviceDialogView.h" -#import "FBSDKCoreKit+Internal.h" -#import "FBSDKDeviceUtilities.h" + #import "FBSDKCoreKit+Internal.h" + #import "FBSDKDeviceUtilities.h" @implementation FBSDKSmartDeviceDialogView { @@ -40,7 +40,7 @@ - (instancetype)initWithFrame:(CGRect)frame return self; } -#pragma mark - Overrides + #pragma mark - Overrides - (void)setConfirmationCode:(NSString *)confirmationCode { @@ -62,10 +62,15 @@ - (void)setConfirmationCode:(NSString *)confirmationCode - (void)buildView { - //intentionally blank. + // intentionally blank. } -#pragma mark - Helpers + #pragma mark - Helpers + +- (UIColor *)_logoColor +{ + return [UIColor colorWithRed:66.0 / 255.0 green:103.0 / 255.0 blue:178.0 / 255.0 alpha:1]; +} - (void)_buildView { @@ -78,7 +83,7 @@ - (void)_buildView const CGFloat kLogoMargin = 30; const CGFloat kInstructionTextHorizontalMargin = 100; const CGFloat kConfirmationCodeFontSize = 108; - const CGFloat kFontColorValue = 119.0/255.0; + const CGFloat kFontColorValue = 119.0 / 255.0; const CGFloat kInstructionFontSize = 32; const CGFloat kVerticalMarginOrLabel = 40; const CGFloat kQRCodeSize = 200; @@ -98,7 +103,7 @@ - (void)_buildView // 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]; + 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; @@ -107,8 +112,8 @@ - (void)_buildView // 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]; + FBSDKLogo *logoHelper = [FBSDKLogo new]; + UIImage *image = [logoHelper imageWithSize:imageSize color:self._logoColor]; image = [image resizableImageWithCapInsets:UIEdgeInsetsZero resizingMode:UIImageResizingModeStretch]; UIImageView *imageView = [[UIImageView alloc] initWithImage:image]; imageView.translatesAutoresizingMaskIntoConstraints = NO; @@ -131,7 +136,7 @@ - (void)_buildView // 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.textColor = self._logoColor; _confirmationCodeLabel.font = [UIFont systemFontOfSize:kConfirmationCodeFontSize weight:UIFontWeightLight]; _confirmationCodeLabel.textAlignment = NSTextAlignmentCenter; [_confirmationCodeLabel sizeToFit]; @@ -143,11 +148,13 @@ - (void)_buildView // build the smartlogin instructions UILabel *smartInstructionLabel = [[UILabel alloc] init]; smartInstructionLabel.translatesAutoresizingMaskIntoConstraints = NO; - NSString *smartInstructionString = NSLocalizedStringWithDefaultValue(@"DeviceLogin.SmartLogInPrompt", - @"FacebookSDK", - [FBSDKInternalUtility bundleForStrings], - @"To connect your account, open the Facebook app on your mobile device and check for notifications.", - @"Instructions telling the user to open their Facebook app on a mobile device and check for a login notification."); + NSString *smartInstructionString = NSLocalizedStringWithDefaultValue( + @"DeviceLogin.SmartLogInPrompt", + @"FacebookSDK", + [FBSDKInternalUtility bundleForStrings], + @"To connect your account, open the Facebook app on your mobile device and check for notifications.", + @"Instructions telling the user to open their Facebook app on a mobile device and check for a login notification." + ); NSMutableParagraphStyle *instructionLabelParagraphStyle = [[NSMutableParagraphStyle alloc] init]; instructionLabelParagraphStyle.lineHeightMultiple = 1.3; @@ -164,7 +171,7 @@ - (void)_buildView smartInstructionLabel.textColor = [UIColor colorWithWhite:kFontColorValue alpha:1.0]; [dialogView addSubview:smartInstructionLabel]; [smartInstructionLabel.topAnchor constraintEqualToAnchor:dialogHeaderView.bottomAnchor - constant:kVerticalSpaceBetweenHeaderViewAndInstructionLabel].active = YES; + constant:kVerticalSpaceBetweenHeaderViewAndInstructionLabel].active = YES; [smartInstructionLabel.leadingAnchor constraintEqualToAnchor:dialogView.leadingAnchor constant:kInstructionTextHorizontalMargin].active = YES; [dialogView.trailingAnchor constraintEqualToAnchor:smartInstructionLabel.trailingAnchor constant:kInstructionTextHorizontalMargin].active = YES; @@ -172,11 +179,13 @@ - (void)_buildView 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.text = NSLocalizedStringWithDefaultValue( + @"DeviceLogin.SmartLogInOrLabel", + @"FacebookSDK", + [FBSDKInternalUtility bundleForStrings], + @"-- OR --", + @"The 'or' string for smart login instructions" + );; orInstructionLabel.numberOfLines = 0; orInstructionLabel.textAlignment = NSTextAlignmentCenter; [orInstructionLabel sizeToFit]; @@ -204,11 +213,13 @@ - (void)_buildView // build the instructions UILabel UILabel *instructionLabel = [[UILabel alloc] init]; instructionLabel.translatesAutoresizingMaskIntoConstraints = NO; - NSString *localizedFormatString = NSLocalizedStringWithDefaultValue(@"DeviceLogin.LogInPrompt", - @"FacebookSDK", - [FBSDKInternalUtility bundleForStrings], - @"Visit %@ and enter the code shown above.", - @"The format string for device login instructions"); + NSString *localizedFormatString = NSLocalizedStringWithDefaultValue( + @"DeviceLogin.LogInPrompt", + @"FacebookSDK", + [FBSDKInternalUtility bundleForStrings], + @"Visit %@ and enter the code shown above.", + @"The format string for device login instructions" + ); NSString *const deviceLoginURLString = @"facebook.com/device"; NSString *instructionString = [NSString localizedStringWithFormat:localizedFormatString, deviceLoginURLString]; @@ -249,11 +260,13 @@ - (void)_buildView 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") + [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]; diff --git a/FBSDKCoreKit/FBSDKCoreKit/Internal/ErrorRecovery/FBSDKErrorRecoveryAttempter.m b/FBSDKCoreKit/FBSDKCoreKit/Internal/ErrorRecovery/FBSDKErrorRecoveryAttempter.m index 07aa418dbc..710147b4de 100644 --- a/FBSDKCoreKit/FBSDKCoreKit/Internal/ErrorRecovery/FBSDKErrorRecoveryAttempter.m +++ b/FBSDKCoreKit/FBSDKCoreKit/Internal/ErrorRecovery/FBSDKErrorRecoveryAttempter.m @@ -18,8 +18,8 @@ #import "FBSDKErrorRecoveryAttempter.h" -#import "_FBSDKTemporaryErrorRecoveryAttempter.h" #import "FBSDKErrorRecoveryConfiguration.h" +#import "_FBSDKTemporaryErrorRecoveryAttempter.h" @implementation FBSDKErrorRecoveryAttempter @@ -43,9 +43,10 @@ - (void)attemptRecoveryFromError:(NSError *)error optionIndex:(NSUInteger)recove { // should be implemented by subclasses. } + @end -@implementation FBSDKErrorRecoveryAttempter(Protected) +@implementation FBSDKErrorRecoveryAttempter (Protected) - (void)completeRecovery:(BOOL)didRecover delegate:(id)delegate didRecoverSelector:(SEL)didRecoverSelector contextInfo:(void *)contextInfo { diff --git a/FBSDKCoreKit/FBSDKCoreKit/Internal/ErrorRecovery/_FBSDKTemporaryErrorRecoveryAttempter.m b/FBSDKCoreKit/FBSDKCoreKit/Internal/ErrorRecovery/_FBSDKTemporaryErrorRecoveryAttempter.m index bb555aa7f8..13f5272971 100644 --- a/FBSDKCoreKit/FBSDKCoreKit/Internal/ErrorRecovery/_FBSDKTemporaryErrorRecoveryAttempter.m +++ b/FBSDKCoreKit/FBSDKCoreKit/Internal/ErrorRecovery/_FBSDKTemporaryErrorRecoveryAttempter.m @@ -22,7 +22,11 @@ @implementation _FBSDKTemporaryErrorRecoveryAttempter - (void)attemptRecoveryFromError:(NSError *)error optionIndex:(NSUInteger)recoveryOptionIndex delegate:(id)delegate didRecoverSelector:(SEL)didRecoverSelector contextInfo:(void *)contextInfo { - [super completeRecovery:YES delegate:delegate didRecoverSelector:didRecoverSelector contextInfo:contextInfo]; + @try { + [super completeRecovery:YES delegate:delegate didRecoverSelector:didRecoverSelector contextInfo:contextInfo]; + } @catch (NSException *exception) { + NSLog(@"Fail to complete error recovery. Exception reason: %@", exception.reason); + } } @end diff --git a/FBSDKCoreKit/FBSDKCoreKit/Internal/FBSDKAccessToken+Internal.h b/FBSDKCoreKit/FBSDKCoreKit/Internal/FBSDKAccessToken+Internal.h new file mode 100644 index 0000000000..e761e3c801 --- /dev/null +++ b/FBSDKCoreKit/FBSDKCoreKit/Internal/FBSDKAccessToken+Internal.h @@ -0,0 +1,32 @@ +// 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. + +#if SWIFT_PACKAGE + #import "FBSDKAccessToken.h" +#else + #import +#endif + +#import "FBSDKCoreKit+Internal.h" + +@interface FBSDKAccessToken (Internal) + ++ (void)setCurrentAccessToken:(FBSDKAccessToken *)token + shouldDispatchNotif:(BOOL)shouldDispatchNotif; + +@end diff --git a/FBSDKCoreKit/FBSDKCoreKit/Internal/FBSDKAppLinkReturnToRefererView_Internal.h b/FBSDKCoreKit/FBSDKCoreKit/Internal/FBSDKAppLinkReturnToRefererView_Internal.h index 6c5d3283e5..8a26b8ebe8 100644 --- a/FBSDKCoreKit/FBSDKCoreKit/Internal/FBSDKAppLinkReturnToRefererView_Internal.h +++ b/FBSDKCoreKit/FBSDKCoreKit/Internal/FBSDKAppLinkReturnToRefererView_Internal.h @@ -20,7 +20,7 @@ #if !TARGET_OS_TV -#import "FBSDKAppLinkReturnToRefererView.h" + #import "FBSDKAppLinkReturnToRefererView.h" @interface FBSDKAppLinkReturnToRefererView (Internal) diff --git a/FBSDKCoreKit/FBSDKCoreKit/Internal/FBSDKAppLink_Internal.h b/FBSDKCoreKit/FBSDKCoreKit/Internal/FBSDKAppLink_Internal.h index 78cfe3c301..ede28823d6 100644 --- a/FBSDKCoreKit/FBSDKCoreKit/Internal/FBSDKAppLink_Internal.h +++ b/FBSDKCoreKit/FBSDKCoreKit/Internal/FBSDKAppLink_Internal.h @@ -20,7 +20,7 @@ #if !TARGET_OS_TV -#import "FBSDKAppLink.h" + #import "FBSDKAppLink.h" FOUNDATION_EXPORT NSString *const FBSDKAppLinkDataParameterName; FOUNDATION_EXPORT NSString *const FBSDKAppLinkTargetKeyName; @@ -39,7 +39,7 @@ FOUNDATION_EXPORT NSString *const FBSDKAppLinkRefererUrl; isBackToReferrer:(BOOL)isBackToReferrer; /** return if this AppLink is to go back to referrer. */ -@property (nonatomic, assign, readonly, getter=isBackToReferrer) BOOL backToReferrer; +@property (nonatomic, readonly, getter = isBackToReferrer, assign) BOOL backToReferrer; @end diff --git a/FBSDKCoreKit/FBSDKCoreKit/Internal/FBSDKApplicationDelegate+Internal.h b/FBSDKCoreKit/FBSDKCoreKit/Internal/FBSDKApplicationDelegate+Internal.h index d194a2d058..43154ca6e0 100644 --- a/FBSDKCoreKit/FBSDKCoreKit/Internal/FBSDKApplicationDelegate+Internal.h +++ b/FBSDKCoreKit/FBSDKCoreKit/Internal/FBSDKApplicationDelegate+Internal.h @@ -19,12 +19,12 @@ #import #if SWIFT_PACKAGE -#import "FBSDKApplicationDelegate.h" + #import "FBSDKApplicationDelegate.h" #else -#import + #import #endif -#import "FBSDKCoreKit+Internal.h" +#import "FBSDKApplicationObserving.h" NS_ASSUME_NONNULL_BEGIN diff --git a/FBSDKCoreKit/FBSDKCoreKit/Internal/FBSDKApplicationObserving.h b/FBSDKCoreKit/FBSDKCoreKit/Internal/FBSDKApplicationObserving.h index 9eba4ce116..d6695f51bd 100644 --- a/FBSDKCoreKit/FBSDKCoreKit/Internal/FBSDKApplicationObserving.h +++ b/FBSDKCoreKit/FBSDKCoreKit/Internal/FBSDKApplicationObserving.h @@ -24,9 +24,10 @@ NS_ASSUME_NONNULL_BEGIN @optional - (void)applicationDidBecomeActive:(nullable UIApplication *)application; +- (void)applicationWillResignActive:(nullable UIApplication *)application; - (void)applicationDidEnterBackground:(nullable UIApplication *)application; -- (BOOL)application:(UIApplication *)application -didFinishLaunchingWithOptions:(nullable NSDictionary *)launchOptions; +- (BOOL) application:(UIApplication *)application + didFinishLaunchingWithOptions:(nullable NSDictionary *)launchOptions; - (BOOL)application:(UIApplication *)application openURL:(NSURL *)url diff --git a/FBSDKCoreKit/FBSDKCoreKit/Internal/FBSDKAudioResourceLoader.h b/FBSDKCoreKit/FBSDKCoreKit/Internal/FBSDKAudioResourceLoader.h index a9875042d3..dc270a4e82 100644 --- a/FBSDKCoreKit/FBSDKCoreKit/Internal/FBSDKAudioResourceLoader.h +++ b/FBSDKCoreKit/FBSDKCoreKit/Internal/FBSDKAudioResourceLoader.h @@ -20,7 +20,7 @@ #if !TARGET_OS_TV -#import + #import NS_ASSUME_NONNULL_BEGIN @@ -35,9 +35,9 @@ NS_ASSUME_NONNULL_BEGIN @interface FBSDKAudioResourceLoader (Subclass) -@property (class, nonatomic, copy, nullable, readonly) NSString *name; -@property (class, nonatomic, copy, nullable, readonly) NSData *data; -@property (class, nonatomic, assign, readonly) NSUInteger version; +@property (class, nullable, nonatomic, readonly, copy) NSString *name; +@property (class, nullable, nonatomic, readonly, copy) NSData *data; +@property (class, nonatomic, readonly, assign) NSUInteger version; @end diff --git a/FBSDKCoreKit/FBSDKCoreKit/Internal/FBSDKAudioResourceLoader.m b/FBSDKCoreKit/FBSDKCoreKit/Internal/FBSDKAudioResourceLoader.m index e78bb21ba8..0f96cbd5a3 100644 --- a/FBSDKCoreKit/FBSDKCoreKit/Internal/FBSDKAudioResourceLoader.m +++ b/FBSDKCoreKit/FBSDKCoreKit/Internal/FBSDKAudioResourceLoader.m @@ -20,12 +20,12 @@ #if !TARGET_OS_TV -#import "FBSDKAudioResourceLoader.h" + #import "FBSDKAudioResourceLoader.h" -#import "FBSDKDynamicFrameworkLoader.h" -#import "FBSDKLogger.h" -#import "FBSDKSettings.h" -#import "FBSDKTypeUtility.h" + #import "FBSDKDynamicFrameworkLoader.h" + #import "FBSDKInternalUtility.h" + #import "FBSDKLogger.h" + #import "FBSDKSettings.h" @implementation FBSDKAudioResourceLoader { @@ -34,7 +34,7 @@ @implementation FBSDKAudioResourceLoader SystemSoundID _systemSoundID; } -#pragma mark - Class Methods + #pragma mark - Class Methods + (instancetype)sharedLoader { @@ -63,7 +63,7 @@ + (instancetype)sharedLoader return loader; } -#pragma mark - Object Lifecycle + #pragma mark - Object Lifecycle - (instancetype)init { @@ -78,7 +78,7 @@ - (void)dealloc fbsdkdfl_AudioServicesDisposeSystemSoundID(_systemSoundID); } -#pragma mark - Public API + #pragma mark - Public API - (BOOL)loadSound:(NSError **)errorRef { @@ -103,7 +103,7 @@ - (void)playSound fbsdkdfl_AudioServicesPlaySystemSound(_systemSoundID); } -#pragma mark - Helper Methods + #pragma mark - Helper Methods - (NSURL *)_fileURL:(NSError **)errorRef { @@ -136,7 +136,7 @@ - (NSURL *)_fileURL:(NSError **)errorRef @implementation FBSDKAudioResourceLoader (Subclass) -#pragma mark - Subclass Methods + #pragma mark - Subclass Methods + (NSString *)name { diff --git a/FBSDKCoreKit/FBSDKCoreKit/Internal/FBSDKAuthenticationStatusUtility.h b/FBSDKCoreKit/FBSDKCoreKit/Internal/FBSDKAuthenticationStatusUtility.h new file mode 100644 index 0000000000..c43b4ec071 --- /dev/null +++ b/FBSDKCoreKit/FBSDKCoreKit/Internal/FBSDKAuthenticationStatusUtility.h @@ -0,0 +1,30 @@ +/* FBSDKAuthenticationTokenStatusChecker_h */ +// 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 + +@interface FBSDKAuthenticationStatusUtility : NSObject + +/** + Fetches the latest authentication status from server. This will invalidate + the current user session if the returned status is not authorized. + */ ++ (void)checkAuthenticationStatus; + +@end diff --git a/FBSDKCoreKit/FBSDKCoreKit/Internal/FBSDKAuthenticationStatusUtility.m b/FBSDKCoreKit/FBSDKCoreKit/Internal/FBSDKAuthenticationStatusUtility.m new file mode 100644 index 0000000000..b57c9ea6f4 --- /dev/null +++ b/FBSDKCoreKit/FBSDKCoreKit/Internal/FBSDKAuthenticationStatusUtility.m @@ -0,0 +1,102 @@ +// 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. + +#if !TARGET_OS_TV + +#import "FBSDKAuthenticationStatusUtility.h" + +#ifdef FBSDKCOCOAPODS + #import +#else + #import "FBSDKCoreKit+Internal.h" +#endif + +#import "FBSDKAuthenticationToken+Internal.h" + +static NSString *const FBSDKOIDCStatusPath = @"/platform/oidc/status"; + +@implementation FBSDKAuthenticationStatusUtility + ++ (void)checkAuthenticationStatus +{ + NSURL *requestURL = [self _requestURL]; + if (!requestURL) { + return; + } + + NSURLRequest *request = [NSURLRequest requestWithURL:requestURL]; + if (request) { + [[NSURLSession.sharedSession dataTaskWithRequest:request + completionHandler:^(NSData *_Nullable data, NSURLResponse *_Nullable response, NSError *_Nullable error) { + if (!error) { + fb_dispatch_on_main_thread(^{ + [self _handleResponse:response]; + }); + } else { + [FBSDKLogger singleShotLogEntry:FBSDKLoggingBehaviorNetworkRequests + formatString:@"%@", [error localizedDescription]]; + } + }] resume]; + } +} + ++ (void)_handleResponse:(NSURLResponse *)response +{ + NSHTTPURLResponse *httpResponse = (NSHTTPURLResponse *)response; + + if (httpResponse.statusCode != 200) { + return; + } + + if ([httpResponse respondsToSelector:@selector(allHeaderFields)]) { + NSDictionary *header = [httpResponse allHeaderFields]; + NSString *status = [FBSDKTypeUtility dictionary:header objectForKey:@"fb-s" ofType:NSString.class]; + if ([status isEqualToString:@"not_authorized"]) { + [self _invalidateCurrentSession]; + } + } +} + ++ (NSURL *)_requestURL +{ + FBSDKAuthenticationToken *token = FBSDKAuthenticationToken.currentAuthenticationToken; + + if (!token.tokenString) { + return nil; + } + + NSDictionary *params = @{@"id_token" : token.tokenString}; + NSError *error; + + NSURL *requestURL = [FBSDKInternalUtility unversionedFacebookURLWithHostPrefix:@"m" + path:FBSDKOIDCStatusPath + queryParameters:params + error:&error]; + return error == nil ? requestURL : nil; +} + ++ (void)_invalidateCurrentSession +{ + FBSDKAccessToken.currentAccessToken = nil; + FBSDKAuthenticationToken.currentAuthenticationToken = nil; + FBSDKProfile.currentProfile = nil; +} + +@end + +#endif diff --git a/FBSDKCoreKit/FBSDKCoreKitTests/Internal/Monitoring/FakeMonitorStore.h b/FBSDKCoreKit/FBSDKCoreKit/Internal/FBSDKAuthenticationToken+Internal.h similarity index 79% rename from FBSDKCoreKit/FBSDKCoreKitTests/Internal/Monitoring/FakeMonitorStore.h rename to FBSDKCoreKit/FBSDKCoreKit/Internal/FBSDKAuthenticationToken+Internal.h index 1cd5f69a27..9e3bccf0b3 100644 --- a/FBSDKCoreKit/FBSDKCoreKitTests/Internal/Monitoring/FakeMonitorStore.h +++ b/FBSDKCoreKit/FBSDKCoreKit/Internal/FBSDKAuthenticationToken+Internal.h @@ -18,16 +18,20 @@ #import -#import "FBSDKCoreKit+Internal.h" +#if SWIFT_PACKAGE + #import "FBSDKAuthenticationToken.h" +#else + #import +#endif + +@class FBSDKAuthenticationTokenClaims; NS_ASSUME_NONNULL_BEGIN -@interface FakeMonitorStore : FBSDKMonitorStore +@interface FBSDKAuthenticationToken (Internal) -@property (nonatomic) BOOL clearWasCalled; -@property (nonatomic) BOOL retrieveEntriesWasCalled; -@property (nonatomic) BOOL persistWasCalled; -@property (nonatomic, copy) NSArray> *capturedPersistedEntries; +- (nullable FBSDKAuthenticationTokenClaims *)claims; +- (NSString *)jti; @end diff --git a/FBSDKCoreKit/FBSDKCoreKit/Internal/FBSDKAuthenticationTokenClaims.h b/FBSDKCoreKit/FBSDKCoreKit/Internal/FBSDKAuthenticationTokenClaims.h new file mode 100644 index 0000000000..b1d7cf2d5a --- /dev/null +++ b/FBSDKCoreKit/FBSDKCoreKit/Internal/FBSDKAuthenticationTokenClaims.h @@ -0,0 +1,67 @@ +// 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 + +NS_ASSUME_NONNULL_BEGIN + +@interface FBSDKAuthenticationTokenClaims : NSObject + +/// A unique identifier for the token. +@property (nonatomic, readonly, strong) NSString *jti; + +/// Issuer Identifier for the Issuer of the response. +@property (nonatomic, readonly, strong) NSString *iss; + +/// Audience(s) that this ID Token is intended for. +@property (nonatomic, readonly, strong) NSString *aud; + +/// String value used to associate a Client session with an ID Token, and to mitigate replay attacks. +@property (nonatomic, readonly, strong) NSString *nonce; + +/// Expiration time on or after which the ID Token MUST NOT be accepted for processing. +@property (nonatomic, readonly, assign) long exp; + +/// Time at which the JWT was issued. +@property (nonatomic, readonly, assign) long iat; + +/// Subject - Identifier for the End-User at the Issuer. +@property (nonatomic, readonly, strong) NSString *sub; + +/// End-User's full name in displayable form including all name parts. +@property (nullable, nonatomic, readonly, strong) NSString *name; + +/// End-User's preferred e-mail address. +@property (nullable, nonatomic, readonly, strong) NSString *email; + +/// URL of the End-User's profile picture. +@property (nullable, nonatomic, readonly, strong) NSString *picture; + +- (instancetype)init NS_UNAVAILABLE; ++ (instancetype)new NS_UNAVAILABLE; + +/** + Returns a new instance, when one can be created from the parameters given, otherwise `nil`. + @param encodedClaims Base64-encoded string of the claims. + @param nonce The expected nonce string. + */ ++ (nullable FBSDKAuthenticationTokenClaims *)validatedClaimsWithEncodedString:(NSString *)encodedClaims nonce:(NSString *)nonce; + +@end + +NS_ASSUME_NONNULL_END diff --git a/FBSDKCoreKit/FBSDKCoreKit/Internal/FBSDKAuthenticationTokenClaims.m b/FBSDKCoreKit/FBSDKCoreKit/Internal/FBSDKAuthenticationTokenClaims.m new file mode 100644 index 0000000000..135398e290 --- /dev/null +++ b/FBSDKCoreKit/FBSDKCoreKit/Internal/FBSDKAuthenticationTokenClaims.m @@ -0,0 +1,141 @@ +// 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 "FBSDKAuthenticationTokenClaims.h" + +#ifdef FBSDKCOCOAPODS + #import +#else + #import "FBSDKCoreKit+Internal.h" +#endif + +static long const MaxTimeSinceTokenIssued = 10 * 60; // 10 mins + +@implementation FBSDKAuthenticationTokenClaims + +- (instancetype)initWithJti:(NSString *)jti + iss:(NSString *)iss + aud:(NSString *)aud + nonce:(NSString *)nonce + exp:(long)exp + iat:(long)iat + sub:(NSString *)sub + name:(nullable NSString *)name + email:(nullable NSString *)email + picture:(nullable NSString *)picture +{ + if (self = [super init]) { + _jti = jti; + _iss = iss; + _aud = aud; + _nonce = nonce; + _exp = exp; + _iat = iat; + _sub = sub; + _name = name; + _email = email; + _picture = picture; + } + + return self; +} + ++ (nullable FBSDKAuthenticationTokenClaims *)validatedClaimsWithEncodedString:(NSString *)encodedClaims nonce:(NSString *)expectedNonce +{ + NSError *error; + NSData *claimsData = [FBSDKBase64 decodeAsData:[FBSDKBase64 base64FromBase64Url:encodedClaims]]; + + if (claimsData) { + NSDictionary *claimsDict = [FBSDKTypeUtility JSONObjectWithData:claimsData options:0 error:&error]; + if (!error) { + long currentTime = [[NSNumber numberWithDouble:[[NSDate date] timeIntervalSince1970]] longValue]; + + // verify claims + NSString *jti = [FBSDKTypeUtility stringValue:claimsDict[@"jti"]]; + BOOL hasJti = jti.length > 0; + + NSString *iss = [FBSDKTypeUtility stringValue:claimsDict[@"iss"]]; + BOOL isFacebook = iss.length > 0 && [[[NSURL URLWithString:iss] host] isEqualToString:@"facebook.com"]; + + NSString *aud = [FBSDKTypeUtility stringValue:claimsDict[@"aud"]]; + BOOL audMatched = [aud isEqualToString:[FBSDKSettings appID]]; + + NSNumber *expValue = [FBSDKTypeUtility numberValue:claimsDict[@"exp"]]; + long exp = [expValue doubleValue]; + BOOL isExpired = expValue == nil || exp <= currentTime; + + NSNumber *iatValue = [FBSDKTypeUtility numberValue:claimsDict[@"iat"]]; + long iat = [iatValue doubleValue]; + BOOL issuedRecently = iatValue != nil && iat >= currentTime - MaxTimeSinceTokenIssued; + + NSString *nonce = [FBSDKTypeUtility stringValue:claimsDict[@"nonce"]]; + BOOL nonceMatched = nonce.length > 0 && [nonce isEqualToString:expectedNonce]; + + NSString *sub = [FBSDKTypeUtility stringValue:claimsDict[@"sub"]]; + BOOL userIDValid = sub.length > 0; + + NSString *name = [FBSDKTypeUtility stringValue:claimsDict[@"name"]]; + NSString *email = [FBSDKTypeUtility stringValue:claimsDict[@"email"]]; + NSString *picture = [FBSDKTypeUtility stringValue:claimsDict[@"picture"]]; + + if (hasJti && isFacebook && audMatched && !isExpired && issuedRecently && nonceMatched && userIDValid) { + return [[FBSDKAuthenticationTokenClaims alloc] initWithJti:jti + iss:iss + aud:aud + nonce:nonce + exp:exp + iat:iat + sub:sub + name:name + email:email + picture:picture]; + } + } + } + + return nil; +} + +- (BOOL)isEqualToClaims:(FBSDKAuthenticationTokenClaims *)claims +{ + return [_jti isEqualToString:claims.jti] + && [_iss isEqualToString:claims.iss] + && [_aud isEqualToString:claims.aud] + && [_nonce isEqualToString:claims.nonce] + && _exp == claims.exp + && _iat == claims.iat + && [_sub isEqualToString:claims.sub] + && [_name isEqualToString:claims.name] + && [_email isEqualToString:claims.email] + && [_picture isEqualToString:claims.picture]; +} + +- (BOOL)isEqual:(id)object +{ + if (self == object) { + return YES; + } + + if (![object isKindOfClass:[FBSDKAuthenticationTokenClaims class]]) { + return NO; + } + + return [self isEqualToClaims:(FBSDKAuthenticationTokenClaims *)object]; +} + +@end diff --git a/FBSDKCoreKit/FBSDKCoreKit/AppEvents/Internal/FBSDKUserDataStore.h b/FBSDKCoreKit/FBSDKCoreKit/Internal/FBSDKAuthenticationTokenFactory.h similarity index 51% rename from FBSDKCoreKit/FBSDKCoreKit/AppEvents/Internal/FBSDKUserDataStore.h rename to FBSDKCoreKit/FBSDKCoreKit/Internal/FBSDKAuthenticationTokenFactory.h index bbef7b3c34..633da6eae1 100644 --- a/FBSDKCoreKit/FBSDKCoreKit/AppEvents/Internal/FBSDKUserDataStore.h +++ b/FBSDKCoreKit/FBSDKCoreKit/Internal/FBSDKAuthenticationTokenFactory.h @@ -18,31 +18,35 @@ #import -#import "FBSDKAppEvents+Internal.h" +#if SWIFT_PACKAGE + #import "FBSDKAuthenticationToken.h" +#else + #import +#endif NS_ASSUME_NONNULL_BEGIN -NS_SWIFT_NAME(UserDataStore) -@interface FBSDKUserDataStore : NSObject - -+ (void)setAndHashUserEmail:(nullable NSString *)email - firstName:(nullable NSString *)firstName - lastName:(nullable NSString *)lastName - phone:(nullable NSString *)phone - dateOfBirth:(nullable NSString *)dateOfBirth - gender:(nullable NSString *)gender - city:(nullable NSString *)city - state:(nullable NSString *)state - zip:(nullable NSString *)zip - country:(nullable NSString *)country; -+ (void)setAndHashData:(nullable NSString *)data - forType:(FBSDKAppEventUserDataType)type; -+ (void)setInternalHashData:(nullable NSString *)hashData - forType:(FBSDKAppEventUserDataType)type; -+ (void)setEnabledRules:(NSArray *)rules; -+ (nullable NSString *)getHashedData; -+ (nullable NSString *)getInternalHashedDataForType:(FBSDKAppEventUserDataType)type; -+ (void)clearDataForType:(FBSDKAppEventUserDataType)type; +typedef void (^FBSDKAuthenticationTokenBlock)(FBSDKAuthenticationToken *_Nullable token) +NS_SWIFT_NAME(AuthenticationTokenBlock); + +/** + Class responsible for generating an `AuthenticationToken` given a valid token string. + An `AuthenticationToken` is verified based of the OpenID Connect Protocol. + */ +NS_SWIFT_NAME(AuthenticationTokenFactory) +@interface FBSDKAuthenticationTokenFactory : NSObject + + /** + Create an `AuthenticationToken` given a valid token string. + Returns nil to the completion handler if the token string is invalid + An `AuthenticationToken` is verified based of the OpenID Connect Protocol. + @param tokenString the raw ID token string + @param nonce the nonce string used to associate a client session with the token + @param completion the completion handler +*/ +- (void)createTokenFromTokenString:(NSString * _Nonnull)tokenString + nonce:(NSString * _Nonnull)nonce + completion:(FBSDKAuthenticationTokenBlock)completion; @end diff --git a/FBSDKCoreKit/FBSDKCoreKit/Internal/FBSDKAuthenticationTokenFactory.m b/FBSDKCoreKit/FBSDKCoreKit/Internal/FBSDKAuthenticationTokenFactory.m new file mode 100644 index 0000000000..5a73592f86 --- /dev/null +++ b/FBSDKCoreKit/FBSDKCoreKit/Internal/FBSDKAuthenticationTokenFactory.m @@ -0,0 +1,258 @@ +// 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 "FBSDKAuthenticationTokenFactory.h" + +#if SWIFT_PACKAGE +@import FBSDKCoreKit; +#else + #import +#endif + +#ifdef FBSDKCOCOAPODS + #import +#else + #import "FBSDKCoreKit+Internal.h" +#endif + +#import + +#import + +#import "FBSDKAuthenticationTokenClaims.h" +#import "FBSDKSessionProviding.h" + +static NSString *const FBSDKBeginCertificate = @"-----BEGIN CERTIFICATE-----"; +static NSString *const FBSDKEndCertificate = @"-----END CERTIFICATE-----"; + +typedef void (^FBSDKPublicCertCompletionBlock)(SecCertificateRef cert); +typedef void (^FBSDKPublicKeyCompletionBlock)(SecKeyRef key); +typedef void (^FBSDKVerifySignatureCompletionBlock)(BOOL success); + +@interface FBSDKAuthenticationToken (FactoryInitializer) + +- (instancetype)initWithTokenString:(NSString *)tokenString + nonce:(NSString *)nonce + claims:(nullable FBSDKAuthenticationTokenClaims *)claims; + +@end + +@implementation FBSDKAuthenticationTokenFactory +{ + NSString *_cert; + id _sessionProvider; +} + +- (instancetype)init +{ + self = [self initWithSessionProvider:NSURLSession.sharedSession]; + return self; +} + +- (instancetype)initWithSessionProvider:(id)sessionProvider +{ + if ((self = [super init])) { + _sessionProvider = sessionProvider; + } + return self; +} + +- (void)createTokenFromTokenString:(NSString *_Nonnull)tokenString + nonce:(NSString *)nonce + completion:(FBSDKAuthenticationTokenBlock)completion +{ + if (tokenString.length == 0 || nonce.length == 0) { + completion(nil); + return; + } + + NSString *signature; + FBSDKAuthenticationTokenClaims *claims; + FBSDKAuthenticationTokenHeader *header; + + NSArray *segments = [tokenString componentsSeparatedByString:@"."]; + if (segments.count != 3) { + completion(nil); + return; + } + + NSString *encodedHeader = [FBSDKTypeUtility array:segments objectAtIndex:0]; + NSString *encodedClaims = [FBSDKTypeUtility array:segments objectAtIndex:1]; + signature = [FBSDKTypeUtility array:segments objectAtIndex:2]; + + claims = [FBSDKAuthenticationTokenClaims validatedClaimsWithEncodedString:encodedClaims nonce:nonce]; + header = [FBSDKAuthenticationTokenHeader validatedHeaderWithEncodedString:encodedHeader]; + + if (!claims || !header) { + completion(nil); + return; + } + + [self verifySignature:signature + header:encodedHeader + claims:encodedClaims + certificateKey:header.kid + completion:^(BOOL success) { + if (success) { + FBSDKAuthenticationToken *token = [[FBSDKAuthenticationToken alloc] initWithTokenString:tokenString nonce:nonce claims:claims]; + completion(token); + } else { + completion(nil); + } + }]; +} + +- (void)verifySignature:(NSString *)signature + header:(NSString *)header + claims:(NSString *)claims + certificateKey:(NSString *)certificateKey + completion:(FBSDKVerifySignatureCompletionBlock)completion +{ +#if DEBUG + // skip signature checking for tests + if (_skipSignatureVerification && completion) { + completion(YES); + } +#endif + + NSData *signatureData = [FBSDKBase64 decodeAsData:[FBSDKBase64 base64FromBase64Url:signature]]; + NSString *signedString = [NSString stringWithFormat:@"%@.%@", header, claims]; + NSData *signedData = [signedString dataUsingEncoding:NSASCIIStringEncoding]; + [self getPublicKeyWithCertificateKey:certificateKey + completion:^(SecKeyRef key) { + if (key && signatureData && signedData) { + size_t signatureBytesSize = SecKeyGetBlockSize(key); + const void *signatureBytes = signatureData.bytes; + + size_t digestSize = CC_SHA256_DIGEST_LENGTH; + uint8_t digestBytes[digestSize]; + CC_SHA256(signedData.bytes, (CC_LONG)signedData.length, digestBytes); + + OSStatus status = SecKeyRawVerify( + key, + kSecPaddingPKCS1SHA256, + digestBytes, + digestSize, + signatureBytes, + signatureBytesSize + ); + fb_dispatch_on_main_thread(^{ + completion(status == errSecSuccess); + }); + } else { + fb_dispatch_on_main_thread(^{ + completion(NO); + }); + } + }]; +} + +- (void)getPublicKeyWithCertificateKey:(NSString *)certificateKey + completion:(FBSDKPublicKeyCompletionBlock)completion +{ + [self getCertificateWithKey:certificateKey + completion:^(SecCertificateRef cert) { + SecKeyRef publicKey = nil; + + if (cert) { + SecPolicyRef policy = SecPolicyCreateBasicX509(); + OSStatus status = -1; + SecTrustRef trust; + + status = SecTrustCreateWithCertificates(cert, policy, &trust); + + if (status == errSecSuccess && trust) { + publicKey = SecTrustCopyPublicKey(trust); + } + + CFRelease(policy); + CFRelease(cert); + } + + completion(publicKey); + }]; +} + +- (void)getCertificateWithKey:(NSString *)certificateKey + completion:(FBSDKPublicCertCompletionBlock)completion +{ + NSURLRequest *request = [NSURLRequest requestWithURL:[self _certificateEndpoint]]; + [[_sessionProvider dataTaskWithRequest:request + completionHandler:^(NSData *_Nullable data, NSURLResponse *_Nullable response, NSError *_Nullable error) { + if (error || !data) { + return completion(nil); + } + + if ([response isKindOfClass:[NSHTTPURLResponse class]]) { + NSHTTPURLResponse *httpResponse = (NSHTTPURLResponse *)response; + if (httpResponse.statusCode != 200) { + return completion(nil); + } + } + + SecCertificateRef result = NULL; + NSDictionary *certs = [FBSDKTypeUtility JSONObjectWithData:data options:0 error:nil]; + NSString *certString = [FBSDKTypeUtility dictionary:certs objectForKey:certificateKey ofType:NSString.class]; + if (!certString) { + return completion(nil); + } + certString = [certString stringByReplacingOccurrencesOfString:FBSDKBeginCertificate withString:@""]; + certString = [certString stringByReplacingOccurrencesOfString:FBSDKEndCertificate withString:@""]; + certString = [certString stringByReplacingOccurrencesOfString:@"\n" withString:@""]; + + NSData *secCertificateData = [[NSData alloc] initWithBase64EncodedString:certString options:0]; + result = SecCertificateCreateWithData(kCFAllocatorDefault, (__bridge CFDataRef)secCertificateData); + completion(result); + }] resume]; +} + +- (NSURL *)_certificateEndpoint +{ + NSError *error; + NSURL *url = [FBSDKInternalUtility unversionedFacebookURLWithHostPrefix:@"m" + path:@"/.well-known/oauth/openid/certs/" + queryParameters:@{} + error:&error]; + + return url; +} + +#pragma mark - Test methods + +#if DEBUG + +static BOOL _skipSignatureVerification; + ++ (void)setSkipSignatureVerification:(BOOL)value +{ + _skipSignatureVerification = value; +} + ++ (instancetype)emptyInstance +{ + return [super new]; +} + +- (void)setCertificate:(NSString *)certificate +{ + _cert = certificate; +} + +#endif + +@end diff --git a/FBSDKTVOSKit/FBSDKTVOSKit/FBSDKTVShareButtonElement.h b/FBSDKCoreKit/FBSDKCoreKit/Internal/FBSDKAuthenticationTokenHeader.h similarity index 62% rename from FBSDKTVOSKit/FBSDKTVOSKit/FBSDKTVShareButtonElement.h rename to FBSDKCoreKit/FBSDKCoreKit/Internal/FBSDKAuthenticationTokenHeader.h index f9a62c1116..6dea5a95c7 100644 --- a/FBSDKTVOSKit/FBSDKTVOSKit/FBSDKTVShareButtonElement.h +++ b/FBSDKCoreKit/FBSDKCoreKit/Internal/FBSDKAuthenticationTokenHeader.h @@ -16,27 +16,26 @@ // 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 NS_ASSUME_NONNULL_BEGIN -/*! - @abstract Represents a tag in TVML. Requires FBSDKShareKit.framework to be linked. - @discussion You should not need to use this class directly. Instead, make sure you - initialize a `FBSDKTVInterfaceFactory` instance correctly. +@interface FBSDKAuthenticationTokenHeader : NSObject + +/// Value that represents the algorithm that was used to sign the JWT. +@property (nonatomic, readonly, strong) NSString *alg; + +/// The type of the JWT. +@property (nonatomic, readonly, strong) NSString *typ; - The '' tag must also have the following attributes to define - the share content: - - `href` the url to share +/// Key identifier used in identifying the key to be used to verify the signature. +@property (nonatomic, readonly, strong) NSString *kid; - Examples: - @code - +/** + Returns a new instance, when one can be created from the parameters given, otherwise `nil`. + @param encodedHeader Base64-encoded string of the header. */ -NS_SWIFT_NAME(FBTVShareButtonElement) -@interface FBSDKTVShareButtonElement : TVViewElement ++ (nullable FBSDKAuthenticationTokenHeader *)validatedHeaderWithEncodedString:(NSString *)encodedHeader; @end diff --git a/FBSDKCoreKit/FBSDKCoreKit/Internal/FBSDKAuthenticationTokenHeader.m b/FBSDKCoreKit/FBSDKCoreKit/Internal/FBSDKAuthenticationTokenHeader.m new file mode 100644 index 0000000000..547029ebd0 --- /dev/null +++ b/FBSDKCoreKit/FBSDKCoreKit/Internal/FBSDKAuthenticationTokenHeader.m @@ -0,0 +1,80 @@ +// 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 "FBSDKAuthenticationTokenHeader.h" + +#ifdef FBSDKCOCOAPODS + #import +#else + #import "FBSDKCoreKit+Internal.h" +#endif + +@implementation FBSDKAuthenticationTokenHeader + +- (instancetype)initWithAlg:(NSString *)alg + typ:(NSString *)typ + kid:(NSString *)kid +{ + if (self = [super init]) { + _alg = alg; + _typ = typ; + _kid = kid; + } + + return self; +} + ++ (nullable FBSDKAuthenticationTokenHeader *)validatedHeaderWithEncodedString:(NSString *)encodedHeader +{ + NSError *error; + NSData *headerData = [FBSDKBase64 decodeAsData:[FBSDKBase64 base64FromBase64Url:encodedHeader]]; + + if (headerData) { + NSDictionary *header = [FBSDKTypeUtility JSONObjectWithData:headerData options:0 error:&error]; + NSString *alg = [FBSDKTypeUtility dictionary:header objectForKey:@"alg" ofType:NSString.class]; + NSString *typ = [FBSDKTypeUtility dictionary:header objectForKey:@"typ" ofType:NSString.class]; + NSString *kid = [FBSDKTypeUtility dictionary:header objectForKey:@"kid" ofType:NSString.class]; + if (!error && [alg isEqualToString:@"RS256"] && [typ isEqualToString:@"JWT"] && kid.length > 0) { + return [[FBSDKAuthenticationTokenHeader alloc] initWithAlg:alg typ:typ kid:kid]; + } + } + + return nil; +} + +- (BOOL)isEqualToHeader:(FBSDKAuthenticationTokenHeader *)header +{ + return [_alg isEqualToString:header.alg] + && [_typ isEqualToString:header.typ] + && [_kid isEqualToString:header.kid]; +} + +- (BOOL)isEqual:(id)object +{ + if (self == object) { + return YES; + } + + if (![object isKindOfClass:[FBSDKAuthenticationTokenHeader class]]) { + return NO; + } + + return [self isEqualToHeader:(FBSDKAuthenticationTokenHeader *)object]; +} + +@end diff --git a/FBSDKCoreKit/FBSDKCoreKit/Internal/FBSDKContainerViewController.m b/FBSDKCoreKit/FBSDKCoreKit/Internal/FBSDKContainerViewController.m index b5a62eb062..a4f8267a20 100644 --- a/FBSDKCoreKit/FBSDKCoreKit/Internal/FBSDKContainerViewController.m +++ b/FBSDKCoreKit/FBSDKCoreKit/Internal/FBSDKContainerViewController.m @@ -20,7 +20,7 @@ #if !TARGET_OS_TV -#import "FBSDKContainerViewController.h" + #import "FBSDKContainerViewController.h" @implementation FBSDKContainerViewController diff --git a/FBSDKCoreKit/FBSDKCoreKit/Internal/FBSDKCoreKit+Internal.h b/FBSDKCoreKit/FBSDKCoreKit/Internal/FBSDKCoreKit+Internal.h index 48a47df606..d27b82aba1 100644 --- a/FBSDKCoreKit/FBSDKCoreKit/Internal/FBSDKCoreKit+Internal.h +++ b/FBSDKCoreKit/FBSDKCoreKit/Internal/FBSDKCoreKit+Internal.h @@ -19,157 +19,175 @@ #import #if SWIFT_PACKAGE -#import "FBSDKCoreKit.h" + #import "FBSDKCoreKit.h" #else -#import + #import +#endif + +#if defined FBSDKCOCOAPODS + #import +#elif defined BUCK + #import #endif #if defined FBSDKCOCOAPODS || defined BUCK -#if !TARGET_OS_TV -#import "FBSDKViewHierarchy.h" -#import "FBSDKViewHierarchyMacros.h" -#import "FBSDKCodelessIndexer.h" -#import "FBSDKMetadataIndexer.h" -#import "FBSDKSuggestedEventsIndexer.h" -#import "FBSDKCrypto.h" -#import "FBSDKAudioResourceLoader.h" -#import "FBSDKContainerViewController.h" -#import "FBSDKBridgeAPI.h" -#import "FBSDKMonotonicTime.h" -#import "FBSDKTriStateBOOL.h" -#import "FBSDKCloseIcon.h" -#import "FBSDKColor.h" -#import "FBSDKMaleSilhouetteIcon.h" -#import "FBSDKUIUtility.h" -#import "FBSDKViewImpressionTracker.h" -#import "FBSDKWebDialog.h" -#else -#import "FBSDKDeviceButton+Internal.h" -#import "FBSDKDeviceDialogView.h" -#import "FBSDKSmartDeviceDialogView.h" -#import "FBSDKDeviceViewControllerBase+Internal.h" -#import "FBSDKModalFormPresentationController.h" -#endif + #import "FBSDKCoreKit+Internal.h" -#import "FBSDKAppEvents+Internal.h" -#import "FBSDKAppEventsState.h" -#import "FBSDKAppEventsStateManager.h" -#import "FBSDKAppEventsUtility.h" -#import "FBSDKRestrictiveDataFilterManager.h" -#import "FBSDKTimeSpentData.h" -#import "FBSDKUserDataStore.h" -#import "FBSDKBase64.h" -#import "FBSDKErrorRecoveryAttempter.h" -#import "FBSDKDynamicFrameworkLoader.h" -#import "FBSDKApplicationObserving.h" -#import "FBSDKApplicationDelegate+Internal.h" -#import "FBSDKDeviceRequestsHelper.h" -#import "FBSDKError.h" -#import "FBSDKImageDownloader.h" -#import "FBSDKInternalUtility.h" -#import "FBSDKLogger.h" -#import "FBSDKMath.h" -#import "FBSDKMonitorHeaders.h" -#import "FBSDKSettings+Internal.h" -#import "FBSDKSwizzler.h" -#import "FBSDKTypeUtility.h" -#import "FBSDKBasicUtility.h" -#import "FBSDKSafeCast.h" -#import "FBSDKURLSession.h" -#import "FBSDKURLSessionTask.h" -#import "FBSDKCrashHandler.h" -#import "FBSDKLibAnalyzer.h" -#import "FBSDKCrashObserving.h" -#import "FBSDKGraphRequest+Internal.h" -#import "FBSDKGraphRequestConnection+Internal.h" -#import "FBSDKGraphRequestMetadata.h" -#import "FBSDKDialogConfiguration.h" -#import "FBSDKServerConfiguration+Internal.h" -#import "FBSDKServerConfiguration.h" -#import "FBSDKServerConfigurationManager+Internal.h" -#import "FBSDKServerConfigurationManager.h" -#import "FBSDKGateKeeperManager.h" -#import "FBSDKAccessTokenCache.h" -#import "FBSDKAccessTokenCaching.h" -#import "FBSDKKeychainStore.h" -#import "FBSDKKeychainStoreViaBundleID.h" -#import "FBSDKButton+Subclass.h" -#import "FBSDKIcon.h" -#import "FBSDKLogo.h" + #if !TARGET_OS_TV + #import "FBSDKAudioResourceLoader.h" + #import "FBSDKAuthenticationStatusUtility.h" + #import "FBSDKBridgeAPI.h" + #import "FBSDKBridgeAPI+Internal.h" + #import "FBSDKCloseIcon.h" + #import "FBSDKCodelessIndexer.h" + #import "FBSDKColor.h" + #import "FBSDKContainerViewController.h" + #import "FBSDKCrypto.h" + #import "FBSDKHumanSilhouetteIcon.h" + #import "FBSDKMetadataIndexer.h" + #import "FBSDKMonotonicTime.h" + #import "FBSDKSKAdNetworkReporter.h" + #import "FBSDKSuggestedEventsIndexer.h" + #import "FBSDKUIUtility.h" + #import "FBSDKViewHierarchy.h" + #import "FBSDKViewHierarchyMacros.h" + #import "FBSDKViewImpressionTracker.h" + #import "FBSDKWebDialog.h" + #else + #import "FBSDKDeviceButton+Internal.h" + #import "FBSDKDeviceDialogView.h" + #import "FBSDKDeviceViewControllerBase+Internal.h" + #import "FBSDKModalFormPresentationController.h" + #import "FBSDKSmartDeviceDialogView.h" + #endif -#else + #import "FBSDKAccessToken+Internal.h" + #import "FBSDKAppEvents+Internal.h" + #import "FBSDKAppEventsConfiguration.h" + #import "FBSDKAppEventsConfigurationManager.h" + #import "FBSDKAppEventsState.h" + #import "FBSDKAppEventsStateManager.h" + #import "FBSDKAppEventsUtility.h" + #import "FBSDKApplicationDelegate+Internal.h" + #import "FBSDKApplicationObserving.h" + #import "FBSDKAuthenticationStatusUtility.h" + #import "FBSDKAuthenticationToken+Internal.h" + #import "FBSDKAuthenticationTokenClaims.h" + #import "FBSDKAuthenticationTokenFactory.h" + #import "FBSDKAuthenticationTokenHeader.h" + #import "FBSDKBase64.h" + #import "FBSDKButton+Subclass.h" + #import "FBSDKDeviceRequestsHelper.h" + #import "FBSDKDialogConfiguration.h" + #import "FBSDKDynamicFrameworkLoader.h" + #import "FBSDKError.h" + #import "FBSDKErrorRecoveryAttempter.h" + #import "FBSDKGateKeeperManager.h" + #import "FBSDKGraphRequest+Internal.h" + #import "FBSDKGraphRequestBody.h" + #import "FBSDKGraphRequestConnection+Internal.h" + #import "FBSDKGraphRequestMetadata.h" + #import "FBSDKGraphRequestPiggybackManager.h" + #import "FBSDKIcon.h" + #import "FBSDKImageDownloader.h" + #import "FBSDKInternalUtility.h" + #import "FBSDKKeychainStore.h" + #import "FBSDKKeychainStoreViaBundleID.h" + #import "FBSDKLogger.h" + #import "FBSDKLogo.h" + #import "FBSDKMath.h" + #import "FBSDKMonitorHeaders.h" + #import "FBSDKProfile+Internal.h" + #import "FBSDKProfilePictureView+Internal.h" + #import "FBSDKRestrictiveDataFilterManager.h" + #import "FBSDKServerConfiguration.h" + #import "FBSDKServerConfiguration+Internal.h" + #import "FBSDKServerConfigurationManager.h" + #import "FBSDKServerConfigurationManager+Internal.h" + #import "FBSDKSettings+Internal.h" + #import "FBSDKSwizzler.h" + #import "FBSDKTimeSpentData.h" + #import "FBSDKTokenCache.h" + #import "FBSDKTokenCaching.h" -#if !TARGET_OS_TV -#import "../AppEvents/Internal/ViewHierarchy/FBSDKViewHierarchy.h" -#import "../AppEvents/Internal/ViewHierarchy/FBSDKViewHierarchyMacros.h" -#import "../AppEvents/Internal/Codeless/FBSDKCodelessIndexer.h" -#import "../AppEvents/Internal/AAM/FBSDKMetadataIndexer.h" -#import "../AppEvents/Internal/SuggestedEvents/FBSDKSuggestedEventsIndexer.h" -#import "Cryptography/FBSDKCrypto.h" -#import "FBSDKAudioResourceLoader.h" -#import "FBSDKContainerViewController.h" -#import "BridgeAPI/FBSDKBridgeAPI.h" -#import "FBSDKMonotonicTime.h" -#import "FBSDKTriStateBOOL.h" -#import "UI/FBSDKCloseIcon.h" -#import "UI/FBSDKColor.h" -#import "UI/FBSDKMaleSilhouetteIcon.h" -#import "UI/FBSDKUIUtility.h" -#import "UI/FBSDKViewImpressionTracker.h" -#import "WebDialog/FBSDKWebDialog.h" #else -#import "Device/FBSDKDeviceButton+Internal.h" -#import "Device/FBSDKDeviceDialogView.h" -#import "Device/FBSDKSmartDeviceDialogView.h" -#import "Device/FBSDKDeviceViewControllerBase+Internal.h" -#import "Device/FBSDKModalFormPresentationController.h" -#endif -#import "../AppEvents/Internal/FBSDKAppEvents+Internal.h" -#import "../AppEvents/Internal/FBSDKAppEventsState.h" -#import "../AppEvents/Internal/FBSDKAppEventsStateManager.h" -#import "../AppEvents/Internal/FBSDKAppEventsUtility.h" -#import "../AppEvents/Internal/Integrity/FBSDKRestrictiveDataFilterManager.h" -#import "../AppEvents/Internal/FBSDKTimeSpentData.h" -#import "../AppEvents/Internal/FBSDKUserDataStore.h" -#import "Base64/FBSDKBase64.h" -#import "ErrorRecovery/FBSDKErrorRecoveryAttempter.h" -#import "FBSDKDynamicFrameworkLoader.h" -#import "FBSDKApplicationObserving.h" -#import "FBSDKApplicationDelegate+Internal.h" -#import "FBSDKDeviceRequestsHelper.h" -#import "FBSDKError.h" -#import "FBSDKImageDownloader.h" -#import "FBSDKInternalUtility.h" -#import "FBSDKLogger.h" -#import "FBSDKMath.h" -#import "FBSDKSettings+Internal.h" -#import "FBSDKSwizzler.h" -#import "../Basics/Internal/FBSDKTypeUtility.h" -#import "../Basics/Internal/FBSDKBasicUtility.h" -#import "../Basics/Internal/FBSDKSafeCast.h" -#import "../Basics/Internal/FBSDKURLSession.h" -#import "../Basics/Internal/FBSDKURLSessionTask.h" -#import "../Basics/Instrument/FBSDKCrashHandler.h" -#import "../Basics/Instrument/FBSDKLibAnalyzer.h" -#import "../Basics/Instrument/FBSDKCrashObserving.h" -#import "Monitoring/FBSDKMonitorHeaders.h" -#import "Network/FBSDKGraphRequest+Internal.h" -#import "Network/FBSDKGraphRequestConnection+Internal.h" -#import "Network/FBSDKGraphRequestMetadata.h" -#import "ServerConfiguration/FBSDKDialogConfiguration.h" -#import "ServerConfiguration/FBSDKServerConfiguration+Internal.h" -#import "ServerConfiguration/FBSDKServerConfiguration.h" -#import "ServerConfiguration/FBSDKServerConfigurationManager+Internal.h" -#import "ServerConfiguration/FBSDKServerConfigurationManager.h" -#import "ServerConfiguration/FBSDKGateKeeperManager.h" -#import "TokenCaching/FBSDKAccessTokenCache.h" -#import "TokenCaching/FBSDKAccessTokenCaching.h" -#import "TokenCaching/FBSDKKeychainStore.h" -#import "TokenCaching/FBSDKKeychainStoreViaBundleID.h" -#import "UI/FBSDKButton+Subclass.h" -#import "UI/FBSDKIcon.h" -#import "UI/FBSDKLogo.h" + #if !TARGET_OS_TV + #import "../AppEvents/Internal/AAM/FBSDKMetadataIndexer.h" + #import "../AppEvents/Internal/Codeless/FBSDKCodelessIndexer.h" + #import "../AppEvents/Internal/SKAdNetwork/FBSDKSKAdNetworkReporter.h" + #import "../AppEvents/Internal/SuggestedEvents/FBSDKSuggestedEventsIndexer.h" + #import "../AppEvents/Internal/ViewHierarchy/FBSDKViewHierarchy.h" + #import "../AppEvents/Internal/ViewHierarchy/FBSDKViewHierarchyMacros.h" + #import "BridgeAPI/FBSDKBridgeAPI.h" + #import "BridgeAPI/FBSDKBridgeAPI+Internal.h" + #import "Cryptography/FBSDKCrypto.h" + #import "FBSDKAudioResourceLoader.h" + #import "FBSDKAuthenticationStatusUtility.h" + #import "FBSDKContainerViewController.h" + #import "FBSDKMonotonicTime.h" + #import "UI/FBSDKCloseIcon.h" + #import "UI/FBSDKColor.h" + #import "UI/FBSDKHumanSilhouetteIcon.h" + #import "UI/FBSDKUIUtility.h" + #import "UI/FBSDKViewImpressionTracker.h" + #import "WebDialog/FBSDKWebDialog.h" + #else + #import "Device/FBSDKDeviceButton+Internal.h" + #import "Device/FBSDKDeviceDialogView.h" + #import "Device/FBSDKDeviceViewControllerBase+Internal.h" + #import "Device/FBSDKModalFormPresentationController.h" + #import "Device/FBSDKSmartDeviceDialogView.h" + #endif + + #import "../../../Sources/FBSDKCoreKit_Basics/include/FBSDKCoreKit_Basics.h" + #import "../AppEvents/Internal/FBSDKAppEvents+Internal.h" + #import "../AppEvents/Internal/FBSDKAppEventsConfiguration.h" + #import "../AppEvents/Internal/FBSDKAppEventsConfigurationManager.h" + #import "../AppEvents/Internal/FBSDKAppEventsState.h" + #import "../AppEvents/Internal/FBSDKAppEventsStateManager.h" + #import "../AppEvents/Internal/FBSDKAppEventsUtility.h" + #import "../AppEvents/Internal/FBSDKTimeSpentData.h" + #import "../AppEvents/Internal/Integrity/FBSDKRestrictiveDataFilterManager.h" + #import "Base64/FBSDKBase64.h" + #import "ErrorRecovery/FBSDKErrorRecoveryAttempter.h" + #import "FBSDKAccessToken+Internal.h" + #import "FBSDKApplicationDelegate+Internal.h" + #import "FBSDKApplicationObserving.h" + #import "FBSDKAuthenticationToken+Internal.h" + #import "FBSDKAuthenticationTokenClaims.h" + #import "FBSDKAuthenticationTokenFactory.h" + #import "FBSDKAuthenticationTokenHeader.h" + #import "FBSDKDeviceRequestsHelper.h" + #import "FBSDKDynamicFrameworkLoader.h" + #import "FBSDKError.h" + #import "FBSDKImageDownloader.h" + #import "FBSDKInternalUtility.h" + #import "FBSDKLogger.h" + #import "FBSDKMath.h" + #import "FBSDKProfile+Internal.h" + #import "FBSDKProfilePictureView+Internal.h" + #import "FBSDKSettings+Internal.h" + #import "FBSDKSwizzler.h" + #import "Monitoring/FBSDKMonitorHeaders.h" + #import "Network/FBSDKGraphRequest+Internal.h" + #import "Network/FBSDKGraphRequestBody.h" + #import "Network/FBSDKGraphRequestConnection+Internal.h" + #import "Network/FBSDKGraphRequestMetadata.h" + #import "Network/FBSDKGraphRequestPiggybackManager.h" + #import "ServerConfiguration/FBSDKDialogConfiguration.h" + #import "ServerConfiguration/FBSDKGateKeeperManager.h" + #import "ServerConfiguration/FBSDKServerConfiguration.h" + #import "ServerConfiguration/FBSDKServerConfiguration+Internal.h" + #import "ServerConfiguration/FBSDKServerConfigurationManager.h" + #import "ServerConfiguration/FBSDKServerConfigurationManager+Internal.h" + #import "TokenCaching/FBSDKKeychainStore.h" + #import "TokenCaching/FBSDKKeychainStoreViaBundleID.h" + #import "TokenCaching/FBSDKTokenCache.h" + #import "TokenCaching/FBSDKTokenCaching.h" + #import "UI/FBSDKButton+Subclass.h" + #import "UI/FBSDKIcon.h" + #import "UI/FBSDKLogo.h" #endif diff --git a/FBSDKCoreKit/FBSDKCoreKit/Internal/FBSDKDeviceRequestsHelper.m b/FBSDKCoreKit/FBSDKCoreKit/Internal/FBSDKDeviceRequestsHelper.m index 4bd13d8018..f7fff410c2 100644 --- a/FBSDKCoreKit/FBSDKCoreKit/Internal/FBSDKDeviceRequestsHelper.m +++ b/FBSDKCoreKit/FBSDKCoreKit/Internal/FBSDKDeviceRequestsHelper.m @@ -18,19 +18,19 @@ #import "FBSDKDeviceRequestsHelper.h" -#import - #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" + #define FBSDK_FLAVOR @"ios" #else -#define FBSDK_FLAVOR @"tvos" + #define FBSDK_FLAVOR @"tvos" #endif #define FBSDK_SERVICE_TYPE @"_fb._tcp." @@ -40,7 +40,8 @@ @implementation FBSDKDeviceRequestsHelper #pragma mark - Class Methods -+ (void)initialize { ++ (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 @@ -54,37 +55,37 @@ + (NSString *)getDeviceInfo struct utsname systemInfo; uname(&systemInfo); NSDictionary *deviceInfo = @{ - FBSDK_DEVICE_INFO_DEVICE: @(systemInfo.machine), - FBSDK_DEVICE_INFO_MODEL: [UIDevice currentDevice].model, - }; + FBSDK_DEVICE_INFO_DEVICE : @(systemInfo.machine), + FBSDK_DEVICE_INFO_MODEL : [UIDevice currentDevice].model, + }; NSError *err; NSData *jsonDeviceInfo = [FBSDKTypeUtility dataWithJSONObject:deviceInfo - options:0 - error:&err]; + options:0 + error:&err]; return [[NSString alloc] initWithData:jsonDeviceInfo encoding:NSUTF8StringEncoding]; } + (BOOL)startAdvertisementService:(NSString *)loginCode withDelegate:(id)delegate { - static NSString *sdkVersion = nil; + static NSString *sdkVersion = nil; static dispatch_once_t onceToken; dispatch_once(&onceToken, ^{ - // Dots in the version will mess up the bonjour DNS record parsing + // 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]]) { + if (sdkVersion.length > 10 + || ![[NSCharacterSet decimalDigitCharacterSet] characterIsMember:[sdkVersion characterAtIndex:0]]) { sdkVersion = @"dev"; } }); NSString *serviceName = [NSString stringWithFormat:@"%@_%@_%@", - FBSDK_HEADER, - [NSString stringWithFormat:@"%@-%@", - FBSDK_FLAVOR, - sdkVersion - ], - loginCode - ]; + FBSDK_HEADER, + [NSString stringWithFormat:@"%@-%@", + FBSDK_FLAVOR, + sdkVersion + ], + loginCode + ]; if (serviceName.length > 60) { [FBSDKLogger singleShotLogEntry:FBSDKLoggingBehaviorDeveloperErrors logEntry:@"serviceName exceeded 60 characters"]; } diff --git a/FBSDKCoreKit/FBSDKCoreKit/Internal/FBSDKDynamicFrameworkLoader.h b/FBSDKCoreKit/FBSDKCoreKit/Internal/FBSDKDynamicFrameworkLoader.h index 35d002c16e..5847f84eab 100644 --- a/FBSDKCoreKit/FBSDKCoreKit/Internal/FBSDKDynamicFrameworkLoader.h +++ b/FBSDKCoreKit/FBSDKCoreKit/Internal/FBSDKDynamicFrameworkLoader.h @@ -20,6 +20,8 @@ #import #import +#import "FBSDKDynamicFrameworkResolving.h" + NS_ASSUME_NONNULL_BEGIN #pragma mark - Security APIs @@ -141,11 +143,14 @@ FOUNDATION_EXPORT Class fbsdkdfl_WKUserScriptClass(void); As new types are needed, they should be added and strongly typed. */ NS_SWIFT_NAME(DynamicFrameworkLoader) -@interface FBSDKDynamicFrameworkLoader : NSObject +@interface FBSDKDynamicFrameworkLoader : NSObject - (instancetype)init NS_UNAVAILABLE; + (instancetype)new NS_UNAVAILABLE; +// A shared instance to access dynamically loaded types from ++ (instancetype)shared; + #pragma mark - Security Constants /** diff --git a/FBSDKCoreKit/FBSDKCoreKit/Internal/FBSDKDynamicFrameworkResolving.h b/FBSDKCoreKit/FBSDKCoreKit/Internal/FBSDKDynamicFrameworkResolving.h new file mode 100644 index 0000000000..6514d6979a --- /dev/null +++ b/FBSDKCoreKit/FBSDKCoreKit/Internal/FBSDKDynamicFrameworkResolving.h @@ -0,0 +1,31 @@ +// 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 + +NS_ASSUME_NONNULL_BEGIN + +/// Used for defining behavior of types that provide classes for types that +/// exist in dynamically loaded frameworks. +@protocol FBSDKDynamicFrameworkResolving + +- (nullable Class)safariViewControllerClass; + +@end + +NS_ASSUME_NONNULL_END diff --git a/FBSDKCoreKit/FBSDKCoreKit/Internal/FBSDKError.m b/FBSDKCoreKit/FBSDKCoreKit/Internal/FBSDKError.m index b1ed9704c8..4603843238 100644 --- a/FBSDKCoreKit/FBSDKCoreKit/Internal/FBSDKError.m +++ b/FBSDKCoreKit/FBSDKCoreKit/Internal/FBSDKError.m @@ -106,6 +106,7 @@ + (NSError *)invalidArgumentErrorWithName:(NSString *)name message:message underlyingError:underlyingError]; } + + (NSError *)invalidArgumentErrorWithDomain:(NSErrorDomain)domain name:(NSString *)name value:(id)value @@ -141,7 +142,7 @@ + (NSError *)invalidCollectionErrorWithName:(NSString *)name { if (!message) { message = - [[NSString alloc] initWithFormat:@"Invalid item (%@) found in collection for %@: %@", item, name, collection]; + [[NSString alloc] initWithFormat:@"Invalid item (%@) found in collection for %@: %@", item, name, collection]; } NSMutableDictionary *userInfo = [[NSMutableDictionary alloc] init]; [FBSDKTypeUtility dictionary:userInfo setObject:name forKey:FBSDKErrorArgumentNameKey]; diff --git a/FBSDKCoreKit/FBSDKCoreKit/Internal/FBSDKFeatureManager.h b/FBSDKCoreKit/FBSDKCoreKit/Internal/FBSDKFeatureManager.h index 5375a3d4b9..67faaedb5e 100644 --- a/FBSDKCoreKit/FBSDKCoreKit/Internal/FBSDKFeatureManager.h +++ b/FBSDKCoreKit/FBSDKCoreKit/Internal/FBSDKFeatureManager.h @@ -52,6 +52,9 @@ typedef NS_ENUM(NSUInteger, FBSDKFeature) FBSDKFeatureIntelligentIntegrity = 0x00010402, FBSDKFeatureModelRequest = 0x00010403, FBSDKFeatureEventDeactivation = 0x00010500, + FBSDKFeatureSKAdNetwork = 0x00010600, + FBSDKFeatureSKAdNetworkConversionValue = 0x00010601, + FBSDKFeatureATELogging = 0x00010700, /** Instrument */ FBSDKFeatureInstrument = 0x00020000, FBSDKFeatureCrashReport = 0x00020100, diff --git a/FBSDKCoreKit/FBSDKCoreKit/Internal/FBSDKFeatureManager.m b/FBSDKCoreKit/FBSDKCoreKit/Internal/FBSDKFeatureManager.m index 314b61d80a..f685348ad0 100644 --- a/FBSDKCoreKit/FBSDKCoreKit/Internal/FBSDKFeatureManager.m +++ b/FBSDKCoreKit/FBSDKCoreKit/Internal/FBSDKFeatureManager.m @@ -18,9 +18,8 @@ #import "FBSDKFeatureManager.h" -#import "ServerConfiguration/FBSDKGateKeeperManager.h" - #import "FBSDKSettings.h" +#import "ServerConfiguration/FBSDKGateKeeperManager.h" static NSString *const FBSDKFeatureManagerPrefix = @"com.facebook.sdk:FBSDKFeatureManager.FBSDKFeature"; @@ -42,7 +41,7 @@ + (void)checkFeature:(FBSDKFeature)feature return; } // check gk - [FBSDKGateKeeperManager loadGateKeepers:^(NSError * _Nullable error) { + [FBSDKGateKeeperManager loadGateKeepers:^(NSError *_Nullable error) { if (completionBlock) { completionBlock([FBSDKFeatureManager isEnabled:feature]); } @@ -78,7 +77,9 @@ + (FBSDKFeature)getParentFeature:(FBSDKFeature)feature return feature & 0xFFFF0000; } else if ((feature & 0xFF0000) > 0) { return feature & 0xFF000000; - } else return 0; + } else { + return 0; + } } + (BOOL)checkGK:(FBSDKFeature)feature @@ -104,11 +105,14 @@ + (NSString *)featureName:(FBSDKFeature)feature case FBSDKFeatureIntelligentIntegrity: featureName = @"IntelligentIntegrity"; break; case FBSDKFeatureModelRequest: featureName = @"ModelRequest"; break; case FBSDKFeatureEventDeactivation: featureName = @"EventDeactivation"; break; + case FBSDKFeatureSKAdNetwork: featureName = @"SKAdNetwork"; break; + case FBSDKFeatureSKAdNetworkConversionValue: featureName = @"SKAdNetworkConversionValue"; break; case FBSDKFeatureInstrument: featureName = @"Instrument"; break; case FBSDKFeatureCrashReport: featureName = @"CrashReport"; break; case FBSDKFeatureCrashShield: featureName = @"CrashShield"; break; case FBSDKFeatureErrorReport: featureName = @"ErrorReport"; break; case FBSDKFeatureMonitoring: featureName = @"Monitoring"; break; + case FBSDKFeatureATELogging: featureName = @"ATELogging"; break; case FBSDKFeatureLogin: featureName = @"LoginKit"; break; @@ -135,6 +139,9 @@ + (BOOL)defaultStatus:(FBSDKFeature)feature case FBSDKFeatureIntelligentIntegrity: case FBSDKFeatureModelRequest: case FBSDKFeatureMonitoring: + case FBSDKFeatureATELogging: + case FBSDKFeatureSKAdNetwork: + case FBSDKFeatureSKAdNetworkConversionValue: return NO; case FBSDKFeatureLogin: case FBDSDKFeatureShare: diff --git a/FBSDKCoreKit/FBSDKCoreKit/Internal/FBSDKImageDownloader.m b/FBSDKCoreKit/FBSDKCoreKit/Internal/FBSDKImageDownloader.m index 9b8f428439..8d41cf61f3 100644 --- a/FBSDKCoreKit/FBSDKCoreKit/Internal/FBSDKImageDownloader.m +++ b/FBSDKCoreKit/FBSDKCoreKit/Internal/FBSDKImageDownloader.m @@ -21,7 +21,8 @@ static NSString *const kImageDirectory = @"fbsdkimages"; static NSString *const kCachedResponseUserInfoKeyTimestamp = @"timestamp"; -@implementation FBSDKImageDownloader { +@implementation FBSDKImageDownloader +{ NSURLCache *_urlCache; } @@ -38,15 +39,15 @@ + (FBSDKImageDownloader *)sharedInstance - (instancetype)init { if ((self = [super init])) { -#if TARGET_OS_MACCATALYST - _urlCache = [[NSURLCache alloc] initWithMemoryCapacity:1024*1024*8 - diskCapacity:1024*1024*100 + #if TARGET_OS_MACCATALYST + _urlCache = [[NSURLCache alloc] initWithMemoryCapacity:1024 * 1024 * 8 + diskCapacity:1024 * 1024 * 100 directoryURL:[NSURL URLWithString:kImageDirectory]]; -#else - _urlCache = [[NSURLCache alloc] initWithMemoryCapacity:1024*1024*8 - diskCapacity:1024*1024*100 + #else + _urlCache = [[NSURLCache alloc] initWithMemoryCapacity:1024 * 1024 * 8 + diskCapacity:1024 * 1024 * 100 diskPath:kImageDirectory]; -#endif + #endif } return self; } @@ -65,7 +66,7 @@ - (void)downloadImageWithURL:(NSURL *)url NSDate *modificationDate = cachedResponse.userInfo[kCachedResponseUserInfoKeyTimestamp]; BOOL isExpired = ([[modificationDate dateByAddingTimeInterval:ttl] compare:[NSDate date]] == NSOrderedAscending); - void (^completionWrapper)(NSCachedURLResponse *) = ^(NSCachedURLResponse *responseData){ + void (^completionWrapper)(NSCachedURLResponse *) = ^(NSCachedURLResponse *responseData) { if (completion != NULL) { UIImage *image = [UIImage imageWithData:responseData.data]; completion(image); @@ -77,10 +78,10 @@ - (void)downloadImageWithURL:(NSURL *)url 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) { + if ([response isKindOfClass:[NSHTTPURLResponse class]] + && ((NSHTTPURLResponse *)response).statusCode == 200 + && error == nil + && data != nil) { NSCachedURLResponse *responseToCache = [[NSCachedURLResponse alloc] initWithResponse:response data:data diff --git a/FBSDKCoreKit/FBSDKCoreKit/Internal/FBSDKInternalUtility.h b/FBSDKCoreKit/FBSDKCoreKit/Internal/FBSDKInternalUtility.h index 9365454e0c..d640606e9e 100644 --- a/FBSDKCoreKit/FBSDKCoreKit/Internal/FBSDKInternalUtility.h +++ b/FBSDKCoreKit/FBSDKCoreKit/Internal/FBSDKInternalUtility.h @@ -28,15 +28,6 @@ NS_ASSUME_NONNULL_BEGIN #define FBSDK_CANOPENURL_MSQRD_PLAYER @"msqrdplayer" #define FBSDK_CANOPENURL_SHARE_EXTENSION @"fbshareextension" -typedef NS_ENUM(int32_t, FBSDKUIKitVersion) -{ - FBSDKUIKitVersion_6_0 = 0x0944, - FBSDKUIKitVersion_6_1 = 0x094C, - FBSDKUIKitVersion_7_0 = 0x0B57, - FBSDKUIKitVersion_7_1 = 0x0B77, - FBSDKUIKitVersion_8_0 = 0x0CF6, -} NS_SWIFT_NAME(FBUIKit.Version); - /** Describes the callback for appLinkFromURLInBackground. @param object the FBSDKAppLink representing the deferred App Link @@ -80,19 +71,6 @@ NS_SWIFT_NAME(InternalUtility) */ @property (class, nonatomic, assign, readonly) NSOperatingSystemVersion operatingSystemVersion; -/** - Tests whether the orientation should be manually adjusted for views outside of the root view controller. - - With the legacy layout the developer must worry about device orientation when working with views outside of - the window's root view controller and apply the correct rotation transform and/or swap a view's width and height - values. If the application was linked with UIKit on iOS 7 or earlier or the application is running on iOS 7 or earlier - then we need to use the legacy layout code. Otherwise if the application was linked with UIKit on iOS 8 or later and - the application is running on iOS 8 or later, UIKit handles all of the rotation complexity and the origin is always in - the top-left and no rotation transform is necessary. - @return YES if if the orientation must be manually adjusted, otherwise NO. - */ -@property (class, nonatomic, assign, readonly) BOOL shouldManuallyAdjustOrientation; - /* Checks if the app is Unity. */ @@ -116,7 +94,7 @@ NS_SWIFT_NAME(InternalUtility) @param url The FB url. @return A dictionary with the key/value pairs. */ -+ (NSDictionary *)dictionaryFromFBURL:(NSURL *)url; ++ (NSDictionary *)parametersFromFBURL:(NSURL *)url; /** Constructs a Facebook URL. @@ -146,6 +124,19 @@ NS_SWIFT_NAME(InternalUtility) defaultVersion:(NSString *)defaultVersion error:(NSError *__autoreleasing *)errorRef; +/** + Constructs a Facebook URL that doesn't need to specify an API version. + @param hostPrefix The prefix for the host, such as 'm', 'graph', etc. + @param path The path for the URL. This may or may not include a version. + @param queryParameters The query parameters for the URL. This will be converted into a query string. + @param errorRef If an error occurs, upon return contains an NSError object that describes the problem. + @return The Facebook URL. + */ ++ (NSURL *)unversionedFacebookURLWithHostPrefix:(NSString *)hostPrefix + path:(NSString *)path + queryParameters:(NSDictionary *)queryParameters + error:(NSError *__autoreleasing *)errorRef; + /** Tests whether the supplied URL is a valid URL for opening in the browser. @param URL The URL to test. @@ -160,13 +151,6 @@ NS_SWIFT_NAME(InternalUtility) */ + (BOOL)isFacebookBundleIdentifier:(NSString *)bundleIdentifier; -/** - Tests whether the operating system is at least the specified version. - @param version The version to test against. - @return YES if the operating system is greater than or equal to the specified version, otherwise NO. - */ -+ (BOOL)isOSRunTimeVersionAtLeast:(NSOperatingSystemVersion)version; - /** Tests whether the supplied bundle identifier references the Safari app. @param bundleIdentifier The bundle identifier to test. @@ -174,20 +158,6 @@ NS_SWIFT_NAME(InternalUtility) */ + (BOOL)isSafariBundleIdentifier:(NSString *)bundleIdentifier; -/** - Tests whether the UIKit version that the current app was linked to is at least the specified version. - @param version The version to test against. - @return YES if the linked UIKit version is greater than or equal to the specified version, otherwise NO. - */ -+ (BOOL)isUIKitLinkTimeVersionAtLeast:(FBSDKUIKitVersion)version; - -/** - Tests whether the UIKit version in the runtime is at least the specified version. - @param version The version to test against. - @return YES if the runtime UIKit version is greater than or equal to the specified version, otherwise NO. - */ -+ (BOOL)isUIKitRunTimeVersionAtLeast:(FBSDKUIKitVersion)version; - /** Checks equality between 2 objects. diff --git a/FBSDKCoreKit/FBSDKCoreKit/Internal/FBSDKInternalUtility.m b/FBSDKCoreKit/FBSDKCoreKit/Internal/FBSDKInternalUtility.m index 850df429fa..ba4a21f3de 100644 --- a/FBSDKCoreKit/FBSDKCoreKit/Internal/FBSDKInternalUtility.m +++ b/FBSDKCoreKit/FBSDKCoreKit/Internal/FBSDKInternalUtility.m @@ -18,36 +18,43 @@ #import "FBSDKInternalUtility.h" -#import - #import +#import #import "FBSDKCoreKit+Internal.h" #import "FBSDKError.h" -#import "FBSDKSettings+Internal.h" #import "FBSDKSettings.h" +#import "FBSDKSettings+Internal.h" -typedef NS_ENUM(NSUInteger, FBSDKInternalUtilityVersionMask) -{ +typedef NS_ENUM(NSUInteger, FBSDKInternalUtilityVersionMask) { FBSDKInternalUtilityMajorVersionMask = 0xFFFF0000, - //FBSDKInternalUtilityMinorVersionMask = 0x0000FF00, // unused - //FBSDKInternalUtilityPatchVersionMask = 0x000000FF, // unused + // FBSDKInternalUtilityMinorVersionMask = 0x0000FF00, // unused + // FBSDKInternalUtilityPatchVersionMask = 0x000000FF, // unused }; -typedef NS_ENUM(NSUInteger, FBSDKInternalUtilityVersionShift) -{ +typedef NS_ENUM(NSUInteger, FBSDKInternalUtilityVersionShift) { FBSDKInternalUtilityMajorVersionShift = 16, - //FBSDKInternalUtilityMinorVersionShift = 8, // unused - //FBSDKInternalUtilityPatchVersionShift = 0, // unused + // FBSDKInternalUtilityMinorVersionShift = 8, // unused + // FBSDKInternalUtilityPatchVersionShift = 0, // unused }; @implementation FBSDKInternalUtility -static BOOL ShouldOverrideHostWithGamingDomain(NSString *hostPrefix) { +// These are stored at the class level so that they can be reset in unit tests +static dispatch_once_t *fetchApplicationQuerySchemesToken; +static dispatch_once_t *checkIfFacebookAppInstalledToken; +static dispatch_once_t *checkIfMessengerAppInstalledToken; +static dispatch_once_t *checkIfMSQRDPlayerAppInstalledToken; +static dispatch_once_t *checkRegisteredCanOpenUrlSchemesToken; +static dispatch_once_t *checkOperatingSystemVersionToken; +static dispatch_once_t *fetchUrlSchemesToken; + +static BOOL ShouldOverrideHostWithGamingDomain(NSString *hostPrefix) +{ return - [[FBSDKAccessToken currentAccessToken] respondsToSelector:@selector(graphDomain)] && - [[FBSDKAccessToken currentAccessToken].graphDomain isEqualToString:@"gaming"] && - ([hostPrefix isEqualToString:@"graph."] || [hostPrefix isEqualToString:@"graph-video."]); + [[FBSDKAccessToken currentAccessToken] respondsToSelector:@selector(graphDomain)] + && [[FBSDKAccessToken currentAccessToken].graphDomain isEqualToString:@"gaming"] + && ([hostPrefix isEqualToString:@"graph."] || [hostPrefix isEqualToString:@"graph-video."]); } #pragma mark - Class Methods @@ -56,7 +63,7 @@ + (NSString *)appURLScheme { NSString *appID = ([FBSDKSettings appID] ?: @""); NSString *suffix = ([FBSDKSettings appURLSchemeSuffix] ?: @""); - return [[NSString alloc] initWithFormat: @"fb%@%@", appID, suffix]; + return [[NSString alloc] initWithFormat:@"fb%@%@", appID, suffix]; } + (NSURL *)appURLWithHost:(NSString *)host @@ -71,7 +78,7 @@ + (NSURL *)appURLWithHost:(NSString *)host error:errorRef]; } -+ (NSDictionary *)dictionaryFromFBURL:(NSURL *)url ++ (NSDictionary *)parametersFromFBURL:(NSURL *)url { // version 3.2.3 of the Facebook app encodes the parameters in the query but // version 3.3 and above encode the parameters in the fragment; @@ -111,18 +118,22 @@ + (void)extractPermissionsFromResponse:(NSDictionary *)responseObject declinedPermissions:(NSMutableSet *)declinedPermissions expiredPermissions:(NSMutableSet *)expiredPermissions { - NSArray *resultData = responseObject[@"data"]; + NSArray *resultData = [FBSDKTypeUtility dictionary:responseObject objectForKey:@"data" ofType:NSArray.class]; if (resultData.count > 0) { for (NSDictionary *permissionsDictionary in resultData) { - NSString *permissionName = permissionsDictionary[@"permission"]; - NSString *status = permissionsDictionary[@"status"]; + NSString *permissionName = [FBSDKTypeUtility dictionary:permissionsDictionary objectForKey:@"permission" ofType:NSString.class]; + NSString *status = [FBSDKTypeUtility dictionary:permissionsDictionary objectForKey:@"status" ofType:NSString.class]; + + if (!permissionName || !status) { + continue; + } if ([status isEqualToString:@"granted"]) { [grantedPermissions addObject:permissionName]; } else if ([status isEqualToString:@"declined"]) { [declinedPermissions addObject:permissionName]; } else if ([status isEqualToString:@"expired"]) { - [expiredPermissions addObject:permissionName]; + [expiredPermissions addObject:permissionName]; } } } @@ -145,6 +156,36 @@ + (NSURL *)facebookURLWithHostPrefix:(NSString *)hostPrefix queryParameters:(NSDictionary *)queryParameters defaultVersion:(NSString *)defaultVersion error:(NSError *__autoreleasing *)errorRef +{ + NSString *version = (defaultVersion.length > 0) ? defaultVersion : [FBSDKSettings graphAPIVersion]; + if (version.length) { + version = [@"/" stringByAppendingString:version]; + } + + return [self _facebookURLWithHostPrefix:hostPrefix + path:path + queryParameters:queryParameters + defaultVersion:version + error:errorRef]; +} + ++ (NSURL *)unversionedFacebookURLWithHostPrefix:(NSString *)hostPrefix + path:(NSString *)path + queryParameters:(NSDictionary *)queryParameters + error:(NSError *__autoreleasing *)errorRef +{ + return [self _facebookURLWithHostPrefix:hostPrefix + path:path + queryParameters:queryParameters + defaultVersion:@"" + error:errorRef]; +} + ++ (NSURL *)_facebookURLWithHostPrefix:(NSString *)hostPrefix + path:(NSString *)path + queryParameters:(NSDictionary *)queryParameters + defaultVersion:(NSString *)version + error:(NSError *__autoreleasing *)errorRef { if (hostPrefix.length && ![hostPrefix hasSuffix:@"."]) { hostPrefix = [hostPrefix stringByAppendingString:@"."]; @@ -161,17 +202,12 @@ + (NSURL *)facebookURLWithHostPrefix:(NSString *)hostPrefix } host = [NSString stringWithFormat:@"%@%@", hostPrefix ?: @"", host ?: @""]; - NSString *version = (defaultVersion.length > 0) ? defaultVersion : [FBSDKSettings graphAPIVersion]; - if (version.length) { - version = [@"/" stringByAppendingString:version]; - } - if (path.length) { NSScanner *versionScanner = [[NSScanner alloc] initWithString:path]; - if ([versionScanner scanString:@"/v" intoString:NULL] && - [versionScanner scanInteger:NULL] && - [versionScanner scanString:@"." intoString:NULL] && - [versionScanner scanInteger:NULL]) { + if ([versionScanner scanString:@"/v" intoString:NULL] + && [versionScanner scanInteger:NULL] + && [versionScanner scanString:@"." intoString:NULL] + && [versionScanner scanInteger:NULL]) { [FBSDKLogger singleShotLogEntry:FBSDKLoggingBehaviorDeveloperErrors logEntry:[NSString stringWithFormat:@"Invalid Graph API version:%@, assuming %@ instead", version, @@ -199,54 +235,14 @@ + (BOOL)isBrowserURL:(NSURL *)URL + (BOOL)isFacebookBundleIdentifier:(NSString *)bundleIdentifier { - return ([bundleIdentifier hasPrefix:@"com.facebook."] || - [bundleIdentifier hasPrefix:@".com.facebook."]); -} - -+ (BOOL)isOSRunTimeVersionAtLeast:(NSOperatingSystemVersion)version -{ - return ([self _compareOperatingSystemVersion:[self operatingSystemVersion] toVersion:version] != NSOrderedAscending); + return ([bundleIdentifier hasPrefix:@"com.facebook."] + || [bundleIdentifier hasPrefix:@".com.facebook."]); } + (BOOL)isSafariBundleIdentifier:(NSString *)bundleIdentifier { - return ([bundleIdentifier isEqualToString:@"com.apple.mobilesafari"] || - [bundleIdentifier isEqualToString:@"com.apple.SafariViewService"]); -} - -+ (BOOL)isUIKitLinkTimeVersionAtLeast:(FBSDKUIKitVersion)version -{ - static int32_t linkTimeMajorVersion; - static dispatch_once_t getVersionOnce; - dispatch_once(&getVersionOnce, ^{ - int32_t linkTimeVersion = NSVersionOfLinkTimeLibrary("UIKit"); - linkTimeMajorVersion = [self getMajorVersionFromFullLibraryVersion:linkTimeVersion]; - }); - return (version <= linkTimeMajorVersion); -} - -+ (BOOL)isUIKitRunTimeVersionAtLeast:(FBSDKUIKitVersion)version -{ - static int32_t runTimeMajorVersion; - static dispatch_once_t getVersionOnce; - dispatch_once(&getVersionOnce, ^{ - int32_t runTimeVersion = NSVersionOfRunTimeLibrary("UIKit"); - runTimeMajorVersion = [self getMajorVersionFromFullLibraryVersion:runTimeVersion]; - }); - return (version <= runTimeMajorVersion); -} - -+ (int32_t)getMajorVersionFromFullLibraryVersion:(int32_t)version -{ - // Negative values returned by NSVersionOfRunTimeLibrary/NSVersionOfLinkTimeLibrary - // are still valid version numbers, as long as it's not -1. - // After bitshift by 16, the negatives become valid positive major version number. - // We ran into this first time with iOS 12. - if (version != -1) { - return ((version & FBSDKInternalUtilityMajorVersionMask) >> FBSDKInternalUtilityMajorVersionShift); - } else { - return 0; - } + return ([bundleIdentifier isEqualToString:@"com.apple.mobilesafari"] + || [bundleIdentifier isEqualToString:@"com.apple.SafariViewService"]); } + (BOOL)object:(id)object isEqualToObject:(id)other @@ -267,38 +263,14 @@ + (NSOperatingSystemVersion)operatingSystemVersion .minorVersion = 0, .patchVersion = 0, }; - static dispatch_once_t getVersionOnce; - dispatch_once(&getVersionOnce, ^{ - if ([NSProcessInfo instancesRespondToSelector:@selector(operatingSystemVersion)]) { - operatingSystemVersion = [NSProcessInfo processInfo].operatingSystemVersion; - } else { - NSArray *components = [[UIDevice currentDevice].systemVersion componentsSeparatedByString:@"."]; - switch (components.count) { - default: - case 3: - operatingSystemVersion.patchVersion = [[FBSDKTypeUtility array:components objectAtIndex:2] integerValue]; - // fall through - case 2: - operatingSystemVersion.minorVersion = [[FBSDKTypeUtility array:components objectAtIndex:1] integerValue]; - // fall through - case 1: - operatingSystemVersion.majorVersion = [[FBSDKTypeUtility array:components objectAtIndex:0] integerValue]; - break; - case 0: - operatingSystemVersion.majorVersion = ([self isUIKitLinkTimeVersionAtLeast:FBSDKUIKitVersion_7_0] ? 7 : 6); - break; - } - } + static dispatch_once_t once_token; + checkOperatingSystemVersionToken = &once_token; + dispatch_once(&once_token, ^{ + operatingSystemVersion = [NSProcessInfo processInfo].operatingSystemVersion; }); return operatingSystemVersion; } -+ (BOOL)shouldManuallyAdjustOrientation -{ - return (![self isUIKitLinkTimeVersionAtLeast:FBSDKUIKitVersion_8_0] || - ![self isUIKitRunTimeVersionAtLeast:FBSDKUIKitVersion_8_0]); -} - + (NSURL *)URLWithScheme:(NSString *)scheme host:(NSString *)host path:(NSString *)path @@ -312,9 +284,12 @@ + (NSURL *)URLWithScheme:(NSString *)scheme NSString *queryString = nil; if (queryParameters.count) { NSError *queryStringError; - queryString = [@"?" stringByAppendingString:[FBSDKBasicUtility queryStringWithDictionary:queryParameters - error:&queryStringError - invalidObjectHandler:NULL]]; + NSString *queryStringFromParams = [FBSDKBasicUtility queryStringWithDictionary:queryParameters + error:&queryStringError + invalidObjectHandler:NULL]; + if (queryStringFromParams) { + queryString = [@"?" stringByAppendingString:queryStringFromParams]; + } if (!queryString) { if (errorRef != NULL) { *errorRef = [FBSDKError invalidArgumentErrorWithName:@"queryParameters" @@ -401,8 +376,9 @@ + (UIViewController *)viewControllerForView:(UIView *)view + (BOOL)isFacebookAppInstalled { - static dispatch_once_t onceToken; - dispatch_once(&onceToken, ^{ + static dispatch_once_t once_token; + checkIfFacebookAppInstalledToken = &once_token; + dispatch_once(&once_token, ^{ [FBSDKInternalUtility checkRegisteredCanOpenURLScheme:FBSDK_CANOPENURL_FACEBOOK]; }); return [self _canOpenURLScheme:FBSDK_CANOPENURL_FACEBOOK]; @@ -410,8 +386,9 @@ + (BOOL)isFacebookAppInstalled + (BOOL)isMessengerAppInstalled { - static dispatch_once_t onceToken; - dispatch_once(&onceToken, ^{ + static dispatch_once_t once_token; + checkIfMessengerAppInstalledToken = &once_token; + dispatch_once(&once_token, ^{ [FBSDKInternalUtility checkRegisteredCanOpenURLScheme:FBSDK_CANOPENURL_MESSENGER]; }); return [self _canOpenURLScheme:FBSDK_CANOPENURL_MESSENGER]; @@ -419,8 +396,9 @@ + (BOOL)isMessengerAppInstalled + (BOOL)isMSQRDPlayerAppInstalled { - static dispatch_once_t onceToken; - dispatch_once(&onceToken, ^{ + static dispatch_once_t once_token; + checkIfMSQRDPlayerAppInstalledToken = &once_token; + dispatch_once(&once_token, ^{ [FBSDKInternalUtility checkRegisteredCanOpenURLScheme:FBSDK_CANOPENURL_MSQRD_PLAYER]; }); return [self _canOpenURLScheme:FBSDK_CANOPENURL_MSQRD_PLAYER]; @@ -428,30 +406,22 @@ + (BOOL)isMSQRDPlayerAppInstalled #pragma mark - Helper Methods -+ (NSComparisonResult)_compareOperatingSystemVersion:(NSOperatingSystemVersion)version1 - toVersion:(NSOperatingSystemVersion)version2 -{ - if (version1.majorVersion < version2.majorVersion) { - return NSOrderedAscending; - } else if (version1.majorVersion > version2.majorVersion) { - return NSOrderedDescending; - } else if (version1.minorVersion < version2.minorVersion) { - return NSOrderedAscending; - } else if (version1.minorVersion > version2.minorVersion) { - return NSOrderedDescending; - } else if (version1.patchVersion < version2.patchVersion) { - return NSOrderedAscending; - } else if (version1.patchVersion > version2.patchVersion) { - return NSOrderedDescending; - } else { - return NSOrderedSame; - } -} - + (BOOL)_canOpenURLScheme:(NSString *)scheme { + scheme = [FBSDKTypeUtility stringValue:scheme]; + if (!scheme) { + return NO; + } + NSURLComponents *components = [[NSURLComponents alloc] init]; - components.scheme = scheme; + @try { + components.scheme = scheme; + } @catch (NSException *exception) { + [FBSDKLogger singleShotLogEntry:FBSDKLoggingBehaviorDeveloperErrors + formatString:@"Invalid URL scheme provided: %@", scheme]; + return NO; + } + components.path = @"/"; return [[UIApplication sharedApplication] canOpenURL:components.URL]; } @@ -460,12 +430,13 @@ + (void)validateAppID { if (![FBSDKSettings appID]) { NSString *reason = @"App ID not found. Add a string value with your app ID for the key " - @"FacebookAppID to the Info.plist or call [FBSDKSettings setAppID:]."; + @"FacebookAppID to the Info.plist or call [FBSDKSettings setAppID:]."; @throw [NSException exceptionWithName:@"InvalidOperationException" reason:reason userInfo:nil]; } } -+ (NSString *)validateRequiredClientAccessToken { ++ (NSString *)validateRequiredClientAccessToken +{ if (![FBSDKSettings clientToken]) { NSString *reason = @"ClientToken is required to be set for this operation. " @"Set the FacebookClientToken in the Info.plist or call [FBSDKSettings setClientToken:]. " @@ -487,7 +458,7 @@ + (void)validateURLSchemes + (void)validateFacebookReservedURLSchemes { - for (NSString * fbUrlScheme in @[FBSDK_CANOPENURL_FACEBOOK, FBSDK_CANOPENURL_MESSENGER, FBSDK_CANOPENURL_FBAPI, FBSDK_CANOPENURL_SHARE_EXTENSION]) { + for (NSString *fbUrlScheme in @[FBSDK_CANOPENURL_FACEBOOK, FBSDK_CANOPENURL_MESSENGER, FBSDK_CANOPENURL_FBAPI, FBSDK_CANOPENURL_SHARE_EXTENSION]) { if ([self isRegisteredURLScheme:fbUrlScheme]) { NSString *reason = [NSString stringWithFormat:@"%@ is registered as a URL scheme. Please move the entry from CFBundleURLSchemes in your Info.plist to LSApplicationQueriesSchemes. If you are trying to resolve \"canOpenURL: failed\" warnings, those only indicate that the Facebook app is not installed on your device or simulator and can be ignored.", fbUrlScheme]; @throw [NSException exceptionWithName:@"InvalidOperationException" reason:reason userInfo:nil]; @@ -497,10 +468,10 @@ + (void)validateFacebookReservedURLSchemes + (UIWindow *)findWindow { -#pragma clang diagnostic push -#pragma clang diagnostic ignored "-Wdeprecated-declarations" + #pragma clang diagnostic push + #pragma clang diagnostic ignored "-Wdeprecated-declarations" UIWindow *topWindow = [UIApplication sharedApplication].keyWindow; -#pragma clang diagnostic pop + #pragma clang diagnostic pop if (topWindow == nil || topWindow.windowLevel < UIWindowLevelNormal) { for (UIWindow *window in [UIApplication sharedApplication].windows) { if (window.windowLevel >= topWindow.windowLevel && !window.isHidden) { @@ -537,7 +508,7 @@ + (UIWindow *)findWindow if (topWindow == nil) { [FBSDKLogger singleShotLogEntry:FBSDKLoggingBehaviorDeveloperErrors - formatString:@"Unable to find a valid UIWindow", nil]; + formatString:@"Unable to find a valid UIWindow", nil]; } return topWindow; } @@ -572,6 +543,7 @@ + (UIInterfaceOrientation)statusBarOrientation return UIApplication.sharedApplication.statusBarOrientation; #endif } + #endif + (NSString *)hexadecimalStringFromData:(NSData *)data @@ -582,18 +554,20 @@ + (NSString *)hexadecimalStringFromData:(NSData *)data } const unsigned char *dataBuffer = data.bytes; - NSMutableString *hexString = [NSMutableString stringWithCapacity:(dataLength * 2)]; + NSMutableString *hexString = [NSMutableString stringWithCapacity:(dataLength * 2)]; for (int i = 0; i < dataLength; ++i) { [hexString appendFormat:@"%02x", dataBuffer[i]]; } return [hexString copy]; } -+ (BOOL)isRegisteredURLScheme:(NSString *)urlScheme { - static dispatch_once_t fetchBundleOnce; ++ (BOOL)isRegisteredURLScheme:(NSString *)urlScheme +{ static NSArray *urlTypes = nil; - dispatch_once(&fetchBundleOnce, ^{ + static dispatch_once_t once_token; + fetchUrlSchemesToken = &once_token; + dispatch_once(&once_token, ^{ urlTypes = [[NSBundle mainBundle].infoDictionary valueForKey:@"CFBundleURLTypes"]; }); for (NSDictionary *urlType in urlTypes) { @@ -607,10 +581,11 @@ + (BOOL)isRegisteredURLScheme:(NSString *)urlScheme { + (void)checkRegisteredCanOpenURLScheme:(NSString *)urlScheme { - static dispatch_once_t initCheckedSchemesOnce; static NSMutableSet *checkedSchemes = nil; - dispatch_once(&initCheckedSchemesOnce, ^{ + static dispatch_once_t once_token; + checkRegisteredCanOpenUrlSchemesToken = &once_token; + dispatch_once(&once_token, ^{ checkedSchemes = [NSMutableSet set]; }); @@ -622,18 +597,19 @@ + (void)checkRegisteredCanOpenURLScheme:(NSString *)urlScheme } } - if (![self isRegisteredCanOpenURLScheme:urlScheme]){ - NSString *reason = [NSString stringWithFormat:@"%@ is missing from your Info.plist under LSApplicationQueriesSchemes and is required for iOS 9.0", urlScheme]; + if (![self isRegisteredCanOpenURLScheme:urlScheme]) { + NSString *reason = [NSString stringWithFormat:@"%@ is missing from your Info.plist under LSApplicationQueriesSchemes and is required.", urlScheme]; [FBSDKLogger singleShotLogEntry:FBSDKLoggingBehaviorDeveloperErrors logEntry:reason]; } } + (BOOL)isRegisteredCanOpenURLScheme:(NSString *)urlScheme { - static dispatch_once_t fetchBundleOnce; static NSArray *schemes = nil; - dispatch_once(&fetchBundleOnce, ^{ + static dispatch_once_t once_token; + fetchApplicationQuerySchemesToken = &once_token; + dispatch_once(&once_token, ^{ schemes = [[NSBundle mainBundle].infoDictionary valueForKey:@"LSApplicationQueriesSchemes"]; }); @@ -642,11 +618,11 @@ + (BOOL)isRegisteredCanOpenURLScheme:(NSString *)urlScheme + (BOOL)isPublishPermission:(NSString *)permission { - return [permission hasPrefix:@"publish"] || - [permission hasPrefix:@"manage"] || - [permission isEqualToString:@"ads_management"] || - [permission isEqualToString:@"create_event"] || - [permission isEqualToString:@"rsvp_event"]; + return [permission hasPrefix:@"publish"] + || [permission hasPrefix:@"manage"] + || [permission isEqualToString:@"ads_management"] + || [permission isEqualToString:@"create_event"] + || [permission isEqualToString:@"rsvp_event"]; } + (BOOL)isUnity @@ -658,4 +634,59 @@ + (BOOL)isUnity return NO; } +#pragma mark - Testability + +#if DEBUG + ++ (void)resetQuerySchemesCache +{ + if (fetchApplicationQuerySchemesToken) { + *fetchApplicationQuerySchemesToken = 0; + } +} + ++ (void)resetDidCheckRegisteredCanOpenUrlSchemes +{ + if (checkRegisteredCanOpenUrlSchemesToken) { + *checkRegisteredCanOpenUrlSchemesToken = 0; + } +} + ++ (void)resetIsFacebookAppInstalledCache +{ + if (checkIfFacebookAppInstalledToken) { + *checkIfFacebookAppInstalledToken = 0; + } +} + ++ (void)resetDidCheckIfMessengerAppInstalledCache +{ + if (checkIfMessengerAppInstalledToken) { + *checkIfMessengerAppInstalledToken = 0; + } +} + ++ (void)resetDidCheckIfMSQRDAppInstalledCache +{ + if (checkIfMSQRDPlayerAppInstalledToken) { + *checkIfMSQRDPlayerAppInstalledToken = 0; + } +} + ++ (void)resetDidCheckOperatingSystemVersion +{ + if (checkOperatingSystemVersionToken) { + *checkOperatingSystemVersionToken = 0; + } +} + ++ (void)resetFetchingUrlSchemes +{ + if (fetchUrlSchemesToken) { + *fetchUrlSchemesToken = 0; + } +} + +#endif + @end diff --git a/FBSDKCoreKit/FBSDKCoreKit/Internal/FBSDKLogger.m b/FBSDKCoreKit/FBSDKCoreKit/Internal/FBSDKLogger.m index f1eebcbdb8..38a471f80d 100644 --- a/FBSDKCoreKit/FBSDKCoreKit/Internal/FBSDKLogger.m +++ b/FBSDKCoreKit/FBSDKCoreKit/Internal/FBSDKLogger.m @@ -27,7 +27,7 @@ @interface FBSDKLogger () -@property (nonatomic, strong, readonly) NSMutableString *internalContents; +@property (nonatomic, readonly, strong) NSMutableString *internalContents; @end @@ -84,7 +84,6 @@ - (void)appendFormat:(NSString *)formatString, ... } } - - (void)appendKey:(NSString *)key value:(NSString *)value { if (_active && value.length) { @@ -95,7 +94,6 @@ - (void)appendKey:(NSString *)key value:(NSString *)value - (void)emitToNSLog { if (_active) { - for (NSString *key in [g_stringsToReplace keyEnumerator]) { [_internalContents replaceOccurrencesOfString:key withString:g_stringsToReplace[key] @@ -123,7 +121,8 @@ + (NSUInteger)generateSerialNumber } + (void)singleShotLogEntry:(NSString *)loggingBehavior - logEntry:(NSString *)logEntry { + logEntry:(NSString *)logEntry +{ if ([FBSDKSettings.loggingBehaviors containsObject:loggingBehavior]) { FBSDKLogger *logger = [[FBSDKLogger alloc] initWithLoggingBehavior:loggingBehavior]; [logger appendString:logEntry]; @@ -132,8 +131,8 @@ + (void)singleShotLogEntry:(NSString *)loggingBehavior } + (void)singleShotLogEntry:(NSString *)loggingBehavior - formatString:(NSString *)formatString, ... { - + formatString:(NSString *)formatString, ... +{ if ([FBSDKSettings.loggingBehaviors containsObject:loggingBehavior]) { va_list vaArguments; va_start(vaArguments, formatString); @@ -144,11 +143,10 @@ + (void)singleShotLogEntry:(NSString *)loggingBehavior } } - + (void)singleShotLogEntry:(NSString *)loggingBehavior timestampTag:(NSObject *)timestampTag - formatString:(NSString *)formatString, ... { - + formatString:(NSString *)formatString, ... +{ if ([FBSDKSettings.loggingBehaviors containsObject:loggingBehavior]) { va_list vaArguments; va_start(vaArguments, formatString); @@ -164,7 +162,7 @@ + (void)singleShotLogEntry:(NSString *)loggingBehavior // Only log if there's been an associated start time. if (startTimeNumber != nil) { uint64_t elapsed = [FBSDKInternalUtility currentTimeInMilliseconds] - startTimeNumber.unsignedLongLongValue; - [g_startTimesWithTags removeObjectForKey:tagAsNumber]; // served its purpose, remove + [g_startTimesWithTags removeObjectForKey:tagAsNumber]; // served its purpose, remove // Log string is appended with "%d msec", with nothing intervening. This gives the most control to the caller. logString = [NSString stringWithFormat:@"%@%llu msec", logString, elapsed]; @@ -175,10 +173,9 @@ + (void)singleShotLogEntry:(NSString *)loggingBehavior } + (void)registerCurrentTime:(NSString *)loggingBehavior - withTag:(NSObject *)timestampTag { - + withTag:(NSObject *)timestampTag +{ if ([FBSDKSettings.loggingBehaviors containsObject:loggingBehavior]) { - if (!g_startTimesWithTags) { g_startTimesWithTags = [[NSMutableDictionary alloc] init]; } @@ -197,14 +194,12 @@ + (void)registerCurrentTime:(NSString *)loggingBehavior } } - + (void)registerStringToReplace:(NSString *)replace - replaceWith:(NSString *)replaceWith { - + replaceWith:(NSString *)replaceWith +{ // Strings sent in here never get cleaned up, but that's OK, don't ever expect too many. - if (FBSDKSettings.loggingBehaviors.count > 0) { // otherwise there's no logging. - + if (FBSDKSettings.loggingBehaviors.count > 0) { // otherwise there's no logging. if (!g_stringsToReplace) { g_stringsToReplace = [[NSMutableDictionary alloc] init]; } @@ -213,6 +208,4 @@ + (void)registerStringToReplace:(NSString *)replace } } - - @end diff --git a/FBSDKCoreKit/FBSDKCoreKit/Internal/FBSDKMath.m b/FBSDKCoreKit/FBSDKCoreKit/Internal/FBSDKMath.m index 302e80b170..0117061ba7 100644 --- a/FBSDKCoreKit/FBSDKCoreKit/Internal/FBSDKMath.m +++ b/FBSDKCoreKit/FBSDKCoreKit/Internal/FBSDKMath.m @@ -117,7 +117,7 @@ + (NSUInteger)hashWithLong:(unsigned long long)value { value = (~value) + (value << 18); // key = (key << 18) - key - 1; value ^= (value >> 31); - value *= 21; // key = (key + (key << 2)) + (key << 4); + value *= 21; // key = (key + (key << 2)) + (key << 4); value ^= (value >> 11); value += (value << 6); value ^= (value >> 22); @@ -128,18 +128,18 @@ + (NSUInteger)hashWithPointer:(const void *)value { NSUInteger hash = (NSUInteger)value; #if !TARGET_RT_64_BIT - hash = ~hash + (hash << 15); // key = (key << 15) - key - 1; + hash = ~hash + (hash << 15); // key = (key << 15) - key - 1; hash ^= (hash >> 12); hash += (hash << 2); hash ^= (hash >> 4); - hash *= 2057; // key = (key + (key << 3)) + (key << 11); + hash *= 2057; // key = (key + (key << 3)) + (key << 11); hash ^= (hash >> 16); #else - hash += ~hash + (hash << 21); // key = (key << 21) - key - 1; + hash += ~hash + (hash << 21); // key = (key << 21) - key - 1; hash ^= (hash >> 24); hash = (hash + (hash << 3)) + (hash << 8); hash ^= (hash >> 14); - hash = (hash + (hash << 2)) + (hash << 4); // key * 21 + hash = (hash + (hash << 2)) + (hash << 4); // key * 21 hash ^= (hash >> 28); hash += (hash << 31); #endif diff --git a/FBSDKCoreKit/FBSDKCoreKit/Internal/FBSDKMeasurementEvent_Internal.h b/FBSDKCoreKit/FBSDKCoreKit/Internal/FBSDKMeasurementEvent_Internal.h index f90322ad2b..68b3257d02 100644 --- a/FBSDKCoreKit/FBSDKCoreKit/Internal/FBSDKMeasurementEvent_Internal.h +++ b/FBSDKCoreKit/FBSDKCoreKit/Internal/FBSDKMeasurementEvent_Internal.h @@ -20,7 +20,7 @@ #if !TARGET_OS_TV -#import "FBSDKMeasurementEvent.h" + #import "FBSDKMeasurementEvent.h" NS_ASSUME_NONNULL_BEGIN diff --git a/FBSDKCoreKit/FBSDKCoreKit/Internal/FBSDKMonotonicTime.h b/FBSDKCoreKit/FBSDKCoreKit/Internal/FBSDKMonotonicTime.h index 6717c91035..32c28ec638 100644 --- a/FBSDKCoreKit/FBSDKCoreKit/Internal/FBSDKMonotonicTime.h +++ b/FBSDKCoreKit/FBSDKCoreKit/Internal/FBSDKMonotonicTime.h @@ -20,7 +20,7 @@ #if !TARGET_OS_TV -#include + #include typedef double FBSDKMonotonicTimeSeconds; typedef uint64_t FBSDKMonotonicTimeMilliseconds; diff --git a/FBSDKCoreKit/FBSDKCoreKit/Internal/FBSDKMonotonicTime.m b/FBSDKCoreKit/FBSDKCoreKit/Internal/FBSDKMonotonicTime.m index 362834c199..49705470f4 100644 --- a/FBSDKCoreKit/FBSDKCoreKit/Internal/FBSDKMonotonicTime.m +++ b/FBSDKCoreKit/FBSDKCoreKit/Internal/FBSDKMonotonicTime.m @@ -20,13 +20,12 @@ #if !TARGET_OS_TV -#import "FBSDKMonotonicTime.h" + #import "FBSDKMonotonicTime.h" -#include -#include -#include - -#include + #include + #include + #include + #include /** * PLEASE NOTE: FBSDKSDKMonotonicTimeTests work fine, but are disabled @@ -35,59 +34,59 @@ */ static uint64_t _get_time_nanoseconds(void) { - static struct mach_timebase_info tb_info = {0}; - static dispatch_once_t onceToken; - dispatch_once(&onceToken, ^{ - int ret = mach_timebase_info(&tb_info); - assert(0 == ret); - }); + static struct mach_timebase_info tb_info = {0}; + static dispatch_once_t onceToken; + dispatch_once(&onceToken, ^{ + int ret = mach_timebase_info(&tb_info); + assert(0 == ret); + }); - return (mach_absolute_time() * tb_info.numer) / tb_info.denom; + return (mach_absolute_time() * tb_info.numer) / tb_info.denom; } FBSDKMonotonicTimeSeconds FBSDKMonotonicTimeGetCurrentSeconds(void) { - const uint64_t nowNanoSeconds = _get_time_nanoseconds(); - return (FBSDKMonotonicTimeSeconds)nowNanoSeconds / (FBSDKMonotonicTimeSeconds)1000000000.0; + const uint64_t nowNanoSeconds = _get_time_nanoseconds(); + return (FBSDKMonotonicTimeSeconds)nowNanoSeconds / (FBSDKMonotonicTimeSeconds)1000000000.0; } FBSDKMonotonicTimeMilliseconds FBSDKMonotonicTimeGetCurrentMilliseconds(void) { - const uint64_t nowNanoSeconds = _get_time_nanoseconds(); - return nowNanoSeconds / 1000000; + const uint64_t nowNanoSeconds = _get_time_nanoseconds(); + return nowNanoSeconds / 1000000; } FBSDKMonotonicTimeNanoseconds FBSDKMonotonicTimeGetCurrentNanoseconds(void) { - return _get_time_nanoseconds(); + return _get_time_nanoseconds(); } FBSDKMachAbsoluteTimeUnits FBSDKMonotonicTimeConvertSecondsToMachUnits(FBSDKMonotonicTimeSeconds seconds) { - static double ratio = 0; - static dispatch_once_t onceToken; - dispatch_once(&onceToken, ^{ - struct mach_timebase_info tb_info = {0}; - int ret = mach_timebase_info(&tb_info); - assert(0 == ret); - ratio = ((double) tb_info.denom / (double)tb_info.numer) * 1000000000.0; - }); + static double ratio = 0; + static dispatch_once_t onceToken; + dispatch_once(&onceToken, ^{ + struct mach_timebase_info tb_info = {0}; + int ret = mach_timebase_info(&tb_info); + assert(0 == ret); + ratio = ((double) tb_info.denom / (double)tb_info.numer) * 1000000000.0; + }); - return seconds * ratio; + return seconds * ratio; } FBSDKMonotonicTimeSeconds FBSDKMonotonicTimeConvertMachUnitsToSeconds(FBSDKMachAbsoluteTimeUnits machUnits) { - static double ratio = 0; - static dispatch_once_t onceToken; - dispatch_once(&onceToken, ^{ - struct mach_timebase_info tb_info = {0}; - int ret = mach_timebase_info(&tb_info); - assert(0 == ret); - ratio = ((double) tb_info.numer / (double)tb_info.denom) / 1000000000.0; - }); + static double ratio = 0; + static dispatch_once_t onceToken; + dispatch_once(&onceToken, ^{ + struct mach_timebase_info tb_info = {0}; + int ret = mach_timebase_info(&tb_info); + assert(0 == ret); + ratio = ((double) tb_info.numer / (double)tb_info.denom) / 1000000000.0; + }); - return ratio * (double)machUnits; + return ratio * (double)machUnits; } #endif diff --git a/FBSDKCoreKit/FBSDKCoreKit/Internal/FBSDKProfile+Internal.h b/FBSDKCoreKit/FBSDKCoreKit/Internal/FBSDKProfile+Internal.h index 12b07a9a2a..04bc662c6c 100644 --- a/FBSDKCoreKit/FBSDKCoreKit/Internal/FBSDKProfile+Internal.h +++ b/FBSDKCoreKit/FBSDKCoreKit/Internal/FBSDKProfile+Internal.h @@ -20,15 +20,30 @@ #if !TARGET_OS_TV -#import "FBSDKProfile.h" + #import "FBSDKCoreKit+Internal.h" NS_ASSUME_NONNULL_BEGIN -@interface FBSDKProfile(Internal) +typedef void (^FBSDKParseProfileBlock)(id result, FBSDKProfile *_Nonnull *_Nullable profileRef); + +@interface FBSDKProfile (Internal) + (void)cacheProfile:(nullable FBSDKProfile *)profile; + (nullable FBSDKProfile *)fetchCachedProfile; ++ (NSURL *)imageURLForProfileID:(NSString *)profileId + PictureMode:(FBSDKProfilePictureMode)mode + size:(CGSize)size; + ++ (void)loadProfileWithToken:(FBSDKAccessToken *)token + completion:(FBSDKProfileBlock)completion + graphRequest:(FBSDKGraphRequest *)request + parseBlock:(FBSDKParseProfileBlock)parseBlock; + ++ (void)loadProfileWithToken:(FBSDKAccessToken *)token completion:(_Nullable FBSDKProfileBlock)completion; + ++ (void)observeChangeAccessTokenChange:(NSNotification *)notification; + @end NS_ASSUME_NONNULL_END diff --git a/FBSDKCoreKit/FBSDKCoreKitTests/Internal/AppEvents/Codeless/FBSDKSampleEventBinding.h b/FBSDKCoreKit/FBSDKCoreKit/Internal/FBSDKProfilePictureView+Internal.h similarity index 83% rename from FBSDKCoreKit/FBSDKCoreKitTests/Internal/AppEvents/Codeless/FBSDKSampleEventBinding.h rename to FBSDKCoreKit/FBSDKCoreKit/Internal/FBSDKProfilePictureView+Internal.h index 8fab3f1cec..a9496c50f2 100644 --- a/FBSDKCoreKit/FBSDKCoreKitTests/Internal/AppEvents/Codeless/FBSDKSampleEventBinding.h +++ b/FBSDKCoreKit/FBSDKCoreKit/Internal/FBSDKProfilePictureView+Internal.h @@ -16,8 +16,19 @@ // 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 "FBSDKCoreKit+Internal.h" +#import "TargetConditionals.h" + +#if !TARGET_OS_TV + +NS_ASSUME_NONNULL_BEGIN + +@interface FBSDKProfilePictureView (Internal) + +- (void)configureProfilePictureView; -@interface FBSDKSampleEventBinding : NSObject -+(NSDictionary*)getSampleDictionary; @end + +NS_ASSUME_NONNULL_END + +#endif diff --git a/FBSDKCoreKit/FBSDKCoreKit/Internal/FBSDKSessionProviding.h b/FBSDKCoreKit/FBSDKCoreKit/Internal/FBSDKSessionProviding.h new file mode 100644 index 0000000000..917e9f1204 --- /dev/null +++ b/FBSDKCoreKit/FBSDKCoreKit/Internal/FBSDKSessionProviding.h @@ -0,0 +1,45 @@ +// 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 + +NS_ASSUME_NONNULL_BEGIN + +/// An internal protocol used to describe a session data task +NS_SWIFT_NAME(SessionDataTask) +@protocol FBSDKSessionDataTask +- (void)resume; +- (void)cancel; +@end + +/// An internal protocol used to describe a url session +NS_SWIFT_NAME(SessionProviding) +@protocol FBSDKSessionProviding +- (id)dataTaskWithRequest:(NSURLRequest *)request completionHandler:(void (^)(NSData * _Nullable data, NSURLResponse * _Nullable response, NSError * _Nullable error))completionHandler; +@end + +// MARK: Default Protocol Conformances + +@interface NSURLSessionDataTask (SessionDataTask) +@end + +@interface NSURLSession (SessionProviding) +@end + + +NS_ASSUME_NONNULL_END diff --git a/FBSDKCoreKit/FBSDKCoreKit/Internal/FBSDKSettings+Internal.h b/FBSDKCoreKit/FBSDKCoreKit/Internal/FBSDKSettings+Internal.h index 2e8c803220..82aa62e0a8 100644 --- a/FBSDKCoreKit/FBSDKCoreKit/Internal/FBSDKSettings+Internal.h +++ b/FBSDKCoreKit/FBSDKCoreKit/Internal/FBSDKSettings+Internal.h @@ -17,30 +17,48 @@ // CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. #if SWIFT_PACKAGE -#import "FBSDKSettings.h" + #import "FBSDKSettings.h" #else -#import + #import #endif +#import "FBSDKCoreKit+Internal.h" + #define DATA_PROCESSING_OPTIONS @"data_processing_options" #define DATA_PROCESSING_OPTIONS_COUNTRY @"data_processing_options_country" #define DATA_PROCESSING_OPTIONS_STATE @"data_processing_options_state" -@protocol FBSDKAccessTokenCaching; +@protocol FBSDKTokenCaching; + +@interface FBSDKSettings (Internal) + ++ (nullable NSObject *)tokenCache; -@interface FBSDKSettings(Internal) ++ (void)setTokenCache:(nullable NSObject *)tokenCache; -+ (nullable NSObject *)accessTokenCache; ++ (FBSDKAdvertisingTrackingStatus)getAdvertisingTrackingStatus; -+ (void)setAccessTokenCache:(nullable NSObject *)accessTokenCache; ++ (void)setAdvertiserTrackingStatus:(FBSDKAdvertisingTrackingStatus)status; + (nullable NSDictionary *)dataProcessingOptions; + (BOOL)isDataProcessingRestricted; -@property (class, nonatomic, copy, readonly, nullable) NSString *graphAPIDebugParamValue; ++ (void)recordInstall; + ++ (void)recordSetAdvertiserTrackingEnabled; + ++ (BOOL)isEventDelayTimerExpired; + ++ (BOOL)isSetATETimeExceedsInstallTime; + ++ (NSDate *_Nullable)getInstallTimestamp; + ++ (NSDate *_Nullable)getSetAdvertiserTrackingEnabledTimestamp; + +@property (class, nullable, nonatomic, readonly, copy) NSString *graphAPIDebugParamValue; // used by Unity. -@property (class, nonatomic, copy, nullable) NSString *userAgentSuffix; +@property (class, nullable, nonatomic, copy) NSString *userAgentSuffix; @end diff --git a/FBSDKCoreKit/FBSDKCoreKit/Internal/FBSDKSwizzler.m b/FBSDKCoreKit/FBSDKCoreKit/Internal/FBSDKSwizzler.m index 75f0017aca..80616a51d8 100644 --- a/FBSDKCoreKit/FBSDKCoreKit/Internal/FBSDKSwizzler.m +++ b/FBSDKCoreKit/FBSDKCoreKit/Internal/FBSDKSwizzler.m @@ -30,8 +30,6 @@ #define FB_REMOVE_SELECTOR \ [FBSDKSwizzler object:self ofClass:swizzlingOnClass.bindingClass removeSelector:_cmd]; - - @interface FBSDKSwizzle : NSObject @property (nonatomic, assign) Class class; @@ -49,7 +47,6 @@ - (instancetype)initWithBlock:(swizzleBlock)aBlock @end - @interface FBSDKSwizzlingOnClass : NSObject @property FBSDKSwizzle *bindingSwizzle; @@ -60,7 +57,6 @@ - (instancetype)initWithSwizzle:(FBSDKSwizzle *)aSwizzle @end - @interface FBSDKSwizzler () + (void)object:(id)anObject ofClass:(Class)aClass addSelector:(SEL)aSelector; @@ -69,12 +65,12 @@ + (BOOL)object:(id)anObject ofClass:(Class)aClass isCallingSelector:(SEL)aSelect @end - static NSMapTable *swizzles; static NSMutableSet *selectorCallingSet; static dispatch_queue_t swizzleQueue; -static FBSDKSwizzlingOnClass* fb_findSwizzle(id self, SEL _cmd){ +static FBSDKSwizzlingOnClass *fb_findSwizzle(id self, SEL _cmd) +{ Method aMethod = class_getInstanceMethod([self class], _cmd); Class this_class = [self class]; FBSDKSwizzle *swizzle = nil; @@ -83,7 +79,7 @@ + (BOOL)object:(id)anObject ofClass:(Class)aClass isCallingSelector:(SEL)aSelect swizzle = (FBSDKSwizzle *)[swizzles objectForKey:MAPTABLE_ID(aMethod)]; } - while (!swizzle && class_getSuperclass(this_class)){ + while (!swizzle && class_getSuperclass(this_class)) { this_class = class_getSuperclass(this_class); aMethod = class_getInstanceMethod(this_class, _cmd); @@ -104,7 +100,7 @@ static void fb_swizzledMethod_2(id self, SEL _cmd) { FB_FIND_SWIZZLE; if (swizzle) { - ((void(*)(id, SEL))swizzle.originalMethod)(self, _cmd); + ((void (*)(id, SEL))swizzle.originalMethod)(self, _cmd); NSEnumerator *blocks = [swizzle.blocks objectEnumerator]; swizzleBlock block; @@ -119,7 +115,7 @@ static void fb_swizzledMethod_3(id self, SEL _cmd, id arg) { FB_FIND_SWIZZLE; if (swizzle) { - ((void(*)(id, SEL, id))swizzle.originalMethod)(self, _cmd, arg); + ((void (*)(id, SEL, id))swizzle.originalMethod)(self, _cmd, arg); NSEnumerator *blocks = [swizzle.blocks objectEnumerator]; swizzleBlock block; @@ -134,7 +130,7 @@ static void fb_swizzledMethod_4(id self, SEL _cmd, id arg, id arg2) { FB_FIND_SWIZZLE; if (swizzle) { - ((void(*)(id, SEL, id, id))swizzle.originalMethod)(self, _cmd, arg, arg2); + ((void (*)(id, SEL, id, id))swizzle.originalMethod)(self, _cmd, arg, arg2); NSEnumerator *blocks = [swizzle.blocks objectEnumerator]; swizzleBlock block; @@ -149,7 +145,7 @@ static void fb_swizzledMethod_5(id self, SEL _cmd, id arg, id arg2, id arg3) { FB_FIND_SWIZZLE; if (swizzle) { - ((void(*)(id, SEL, id, id, id))swizzle.originalMethod)(self, _cmd, arg, arg2, arg3); + ((void (*)(id, SEL, id, id, id))swizzle.originalMethod)(self, _cmd, arg, arg2, arg3); NSEnumerator *blocks = [swizzle.blocks objectEnumerator]; swizzleBlock block; @@ -164,7 +160,7 @@ static void fb_swizzleMethod_4_io(id self, SEL _cmd, NSInteger arg, id arg2) { FB_FIND_SWIZZLE; if (swizzle) { - ((void(*)(id, SEL, NSInteger, id))swizzle.originalMethod)(self, _cmd, arg, arg2); + ((void (*)(id, SEL, NSInteger, id))swizzle.originalMethod)(self, _cmd, arg, arg2); NSEnumerator *blocks = [swizzle.blocks objectEnumerator]; swizzleBlock block; @@ -186,10 +182,10 @@ @implementation FBSDKSwizzler + (void)initialize { - swizzles = [NSMapTable mapTableWithKeyOptions:(NSPointerFunctionsOpaqueMemory | - NSPointerFunctionsOpaquePersonality) - valueOptions:(NSPointerFunctionsStrongMemory | - NSPointerFunctionsObjectPointerPersonality)]; + swizzles = [NSMapTable mapTableWithKeyOptions:(NSPointerFunctionsOpaqueMemory + | NSPointerFunctionsOpaquePersonality) + valueOptions:(NSPointerFunctionsStrongMemory + | NSPointerFunctionsObjectPointerPersonality)]; selectorCallingSet = [NSMutableSet set]; swizzleQueue = dispatch_queue_create("com.facebook.swizzler", DISPATCH_QUEUE_SERIAL); [FBSDKSwizzler resolveConflict]; @@ -247,57 +243,59 @@ + (BOOL)isLocallyDefinedMethod:(Method)aMethod onClass:(Class)aClass + (void)swizzleSelector:(SEL)aSelector onClass:(Class)aClass withBlock:(swizzleBlock)aBlock named:(NSString *)aName { dispatch_async(swizzleQueue, ^{ - Method aMethod = class_getInstanceMethod(aClass, aSelector); - if (aMethod) { - uint numArgs = method_getNumberOfArguments(aMethod); - if (numArgs >= MIN_ARGS && numArgs <= MAX_ARGS) { - - BOOL isLocal = [FBSDKSwizzler isLocallyDefinedMethod:aMethod onClass:aClass]; - IMP swizzledMethod = (IMP)fb_swizzledMethods[numArgs - 2]; - // Check whether the first parameter is integer - if (4 == numArgs) { - char *type = method_copyArgumentType(aMethod, 2); - NSString *firstType = [NSString stringWithCString:type encoding:NSUTF8StringEncoding]; - NSString *integerTypes = @"islq"; - if ([integerTypes containsString:firstType.lowercaseString]) { - swizzledMethod = (IMP)fb_swizzleMethod_4_io; + @try { + Method aMethod = class_getInstanceMethod(aClass, aSelector); + if (aMethod) { + uint numArgs = method_getNumberOfArguments(aMethod); + if (numArgs >= MIN_ARGS && numArgs <= MAX_ARGS) { + BOOL isLocal = [FBSDKSwizzler isLocallyDefinedMethod:aMethod onClass:aClass]; + IMP swizzledMethod = (IMP)fb_swizzledMethods[numArgs - 2]; + // Check whether the first parameter is integer + if (4 == numArgs) { + char *type = method_copyArgumentType(aMethod, 2); + NSString *firstType = [NSString stringWithCString:type encoding:NSUTF8StringEncoding]; + NSString *integerTypes = @"islq"; + if ([integerTypes containsString:firstType.lowercaseString]) { + swizzledMethod = (IMP)fb_swizzleMethod_4_io; + } + free(type); } - free(type); - } - - FBSDKSwizzle *swizzle = [FBSDKSwizzler swizzleForMethod:aMethod]; - if (isLocal) { - if (!swizzle) { - IMP originalMethod = method_getImplementation(aMethod); + FBSDKSwizzle *swizzle = [FBSDKSwizzler swizzleForMethod:aMethod]; - // Replace the local implementation of this method with the swizzled one - method_setImplementation(aMethod,swizzledMethod); + if (isLocal) { + if (!swizzle) { + IMP originalMethod = method_getImplementation(aMethod); - // Create and add the swizzle - swizzle = [[FBSDKSwizzle alloc] initWithBlock:aBlock named:aName forClass:aClass selector:aSelector originalMethod:originalMethod withNumArgs:numArgs]; - [FBSDKSwizzler setSwizzle:swizzle forMethod:aMethod]; + // Replace the local implementation of this method with the swizzled one + method_setImplementation(aMethod, swizzledMethod); + // Create and add the swizzle + swizzle = [[FBSDKSwizzle alloc] initWithBlock:aBlock named:aName forClass:aClass selector:aSelector originalMethod:originalMethod withNumArgs:numArgs]; + [FBSDKSwizzler setSwizzle:swizzle forMethod:aMethod]; + } else { + [swizzle.blocks setObject:aBlock forKey:aName]; + } } else { - [swizzle.blocks setObject:aBlock forKey:aName]; - } - } else { - IMP originalMethod = swizzle ? swizzle.originalMethod : method_getImplementation(aMethod); - - // Add the swizzle as a new local method on the class. - if (!class_addMethod(aClass, aSelector, swizzledMethod, method_getTypeEncoding(aMethod))) { - return; + IMP originalMethod = swizzle ? swizzle.originalMethod : method_getImplementation(aMethod); + + // Add the swizzle as a new local method on the class. + if (!class_addMethod(aClass, aSelector, swizzledMethod, method_getTypeEncoding(aMethod))) { + return; + } + // Now re-get the Method, it should be the one we just added. + Method newMethod = class_getInstanceMethod(aClass, aSelector); + if (aMethod == newMethod) { + return; + } + + FBSDKSwizzle *newSwizzle = [[FBSDKSwizzle alloc] initWithBlock:aBlock named:aName forClass:aClass selector:aSelector originalMethod:originalMethod withNumArgs:numArgs]; + [FBSDKSwizzler setSwizzle:newSwizzle forMethod:newMethod]; } - // Now re-get the Method, it should be the one we just added. - Method newMethod = class_getInstanceMethod(aClass, aSelector); - if (aMethod == newMethod) { - return; - } - - FBSDKSwizzle *newSwizzle = [[FBSDKSwizzle alloc] initWithBlock:aBlock named:aName forClass:aClass selector:aSelector originalMethod:originalMethod withNumArgs:numArgs]; - [FBSDKSwizzler setSwizzle:newSwizzle forMethod:newMethod]; } } + } @catch (NSException *exception) { + NSLog(@"Fail to swizzle selector. Exception reason: %@", exception.reason); } }); } @@ -318,23 +316,27 @@ + (void)unswizzleSelector:(SEL)aSelector onClass:(Class)aClass */ + (void)unswizzleSelector:(SEL)aSelector onClass:(Class)aClass named:(NSString *)aName { - Method aMethod = class_getInstanceMethod(aClass, aSelector); - FBSDKSwizzle *swizzle = [FBSDKSwizzler swizzleForMethod:aMethod]; - if (swizzle) { - if (aName) { - [swizzle.blocks removeObjectForKey:aName]; - } - if (!aName || swizzle.blocks.count == 0) { - method_setImplementation(aMethod, swizzle.originalMethod); - [FBSDKSwizzler removeSwizzleForMethod:aMethod]; + @try { + Method aMethod = class_getInstanceMethod(aClass, aSelector); + FBSDKSwizzle *swizzle = [FBSDKSwizzler swizzleForMethod:aMethod]; + if (swizzle) { + if (aName) { + [swizzle.blocks removeObjectForKey:aName]; + } + if (!aName || swizzle.blocks.count == 0) { + method_setImplementation(aMethod, swizzle.originalMethod); + [FBSDKSwizzler removeSwizzleForMethod:aMethod]; + } } + } @catch (NSException *exception) { + NSLog(@"Fail to remove the named swizzle from given class/selector. Exception reason: %@", exception.reason); } } + (void)object:(id)anObject ofClass:(Class)aClass addSelector:(SEL)aSelector { NSString *objectClassSelectorString = [NSString stringWithFormat:@"%p %@ %@", anObject, NSStringFromClass(aClass), NSStringFromSelector(aSelector)]; - @synchronized (selectorCallingSet) { + @synchronized(selectorCallingSet) { [selectorCallingSet addObject:objectClassSelectorString]; } } @@ -342,7 +344,7 @@ + (void)object:(id)anObject ofClass:(Class)aClass addSelector:(SEL)aSelector + (void)object:(id)anObject ofClass:(Class)aClass removeSelector:(SEL)aSelector { NSString *objectClassSelectorString = [NSString stringWithFormat:@"%p %@ %@", anObject, NSStringFromClass(aClass), NSStringFromSelector(aSelector)]; - @synchronized (selectorCallingSet) { + @synchronized(selectorCallingSet) { [selectorCallingSet removeObject:objectClassSelectorString]; } } @@ -358,16 +360,15 @@ + (BOOL)object:(id)anObject ofClass:(Class)aClass isCallingSelector:(SEL)aSelect @end - @implementation FBSDKSwizzle - (instancetype)init { if (self = [super init]) { self.blocks = [NSMapTable mapTableWithKeyOptions:(NSPointerFunctionsStrongMemory - | NSPointerFunctionsObjectPersonality) + | NSPointerFunctionsObjectPersonality) valueOptions:(NSPointerFunctionsStrongMemory - | NSPointerFunctionsObjectPointerPersonality)]; + | NSPointerFunctionsObjectPointerPersonality)]; } return self; } @@ -403,7 +404,6 @@ - (NSString *)description @end - @implementation FBSDKSwizzlingOnClass - (instancetype)initWithSwizzle:(FBSDKSwizzle *)aSwizzle diff --git a/FBSDKCoreKit/FBSDKCoreKit/Internal/FBSDKURL_Internal.h b/FBSDKCoreKit/FBSDKCoreKit/Internal/FBSDKURL_Internal.h index e5da05163c..f17259ddcb 100644 --- a/FBSDKCoreKit/FBSDKCoreKit/Internal/FBSDKURL_Internal.h +++ b/FBSDKCoreKit/FBSDKCoreKit/Internal/FBSDKURL_Internal.h @@ -20,7 +20,7 @@ #if !TARGET_OS_TV -#import "FBSDKURL.h" + #import "FBSDKURL.h" @interface FBSDKURL (Internal) + (FBSDKURL *)URLForRenderBackToReferrerBarURL:(NSURL *)url; diff --git a/FBSDKCoreKit/FBSDKCoreKit/Internal/Instrument/CrashReport/FBSDKCrashObserver.h b/FBSDKCoreKit/FBSDKCoreKit/Internal/Instrument/CrashReport/FBSDKCrashObserver.h index f7ea35bced..f53c8dcfc7 100644 --- a/FBSDKCoreKit/FBSDKCoreKit/Internal/Instrument/CrashReport/FBSDKCrashObserver.h +++ b/FBSDKCoreKit/FBSDKCoreKit/Internal/Instrument/CrashReport/FBSDKCrashObserver.h @@ -18,11 +18,11 @@ #import -#import "FBSDKCrashObserving.h" +#import "FBSDKInternalUtility.h" NS_ASSUME_NONNULL_BEGIN -@interface FBSDKCrashObserver : NSObject +@interface FBSDKCrashObserver : NSObject + (void)enable; diff --git a/FBSDKCoreKit/FBSDKCoreKit/Internal/Instrument/CrashReport/FBSDKCrashObserver.m b/FBSDKCoreKit/FBSDKCoreKit/Internal/Instrument/CrashReport/FBSDKCrashObserver.m index fcfb13e5c8..62df5eaa6b 100644 --- a/FBSDKCoreKit/FBSDKCoreKit/Internal/Instrument/CrashReport/FBSDKCrashObserver.m +++ b/FBSDKCoreKit/FBSDKCoreKit/Internal/Instrument/CrashReport/FBSDKCrashObserver.m @@ -18,15 +18,12 @@ #import "FBSDKCrashObserver.h" -#import "FBSDKCrashHandler.h" #import "FBSDKCrashShield.h" #import "FBSDKFeatureManager.h" #import "FBSDKGraphRequest.h" #import "FBSDKGraphRequestConnection.h" -#import "FBSDKLibAnalyzer.h" #import "FBSDKSettings.h" #import "FBSDKSettings+Internal.h" -#import "FBSDKTypeUtility.h" @implementation FBSDKCrashObserver diff --git a/FBSDKCoreKit/FBSDKCoreKit/Internal/Instrument/CrashReport/FBSDKCrashShield.m b/FBSDKCoreKit/FBSDKCoreKit/Internal/Instrument/CrashReport/FBSDKCrashShield.m index 1514586197..589d1c3aa0 100644 --- a/FBSDKCoreKit/FBSDKCoreKit/Internal/Instrument/CrashReport/FBSDKCrashShield.m +++ b/FBSDKCoreKit/FBSDKCoreKit/Internal/Instrument/CrashReport/FBSDKCrashShield.m @@ -21,9 +21,9 @@ #import "FBSDKFeatureManager.h" #import "FBSDKGraphRequest.h" #import "FBSDKGraphRequestConnection.h" +#import "FBSDKInternalUtility.h" #import "FBSDKSettings.h" #import "FBSDKSettings+Internal.h" -#import "FBSDKTypeUtility.h" @implementation FBSDKCrashShield @@ -35,37 +35,43 @@ + (void)initialize _featureMapping = @{ @"AAM" : @[ - @"FBSDKMetadataIndexer", + @"FBSDKMetadataIndexer", ], @"CodelessEvents" : @[ - @"FBSDKCodelessIndexer", - @"FBSDKEventBinding", - @"FBSDKEventBindingManager", - @"FBSDKViewHierarchy", - @"FBSDKCodelessPathComponent", - @"FBSDKCodelessParameterComponent", + @"FBSDKCodelessIndexer", + @"FBSDKEventBinding", + @"FBSDKEventBindingManager", + @"FBSDKViewHierarchy", + @"FBSDKCodelessPathComponent", + @"FBSDKCodelessParameterComponent", ], @"RestrictiveDataFiltering" : @[ - @"FBSDKRestrictiveDataFilterManager", + @"FBSDKRestrictiveDataFilterManager", ], @"ErrorReport" : @[ - @"FBSDKErrorReport", + @"FBSDKErrorReport", ], @"PrivacyProtection" : @[ - @"FBSDKModelManager", + @"FBSDKModelManager", ], @"SuggestedEvents" : @[ - @"FBSDKSuggestedEventsIndexer", - @"FBSDKFeatureExtractor", + @"FBSDKSuggestedEventsIndexer", + @"FBSDKFeatureExtractor", ], @"IntelligentIntegrity" : @[ - @"FBSDKIntegrityManager", + @"FBSDKIntegrityManager", ], @"EventDeactivation" : @[ - @"FBSDKEventDeactivationManager", + @"FBSDKEventDeactivationManager", + ], + @"SKAdNetworkConversionValue" : @[ + @"FBSDKSKAdNetworkReporter", + @"FBSDKSKAdNetworkConversionConfiguration", + @"FBSDKSKAdNetworkRule", + @"FBSDKSKAdNetworkEvent", ], @"Monitoring" : @[ - @"FBSDKMonitor", + @"FBSDKMonitor", ], }; } @@ -76,26 +82,25 @@ + (void)analyze:(NSArray *> *)crashLogs NSMutableSet *disabledFeatues = [NSMutableSet set]; for (NSDictionary *crashLog in crashLogs) { NSArray *callstack = crashLog[@"callstack"]; - NSString *featureName = [self getFeature:callstack]; - if (featureName) { - [FBSDKFeatureManager disableFeature:featureName]; - [disabledFeatues addObject:featureName]; - continue; - } + NSString *featureName = [self _getFeature:callstack]; + if (featureName) { + [FBSDKFeatureManager disableFeature:featureName]; + [disabledFeatues addObject:featureName]; + continue; + } } if ([FBSDKSettings isDataProcessingRestricted]) { return; } if (disabledFeatues.count > 0) { - NSDictionary *disabledFeatureLog = @{@"feature_names":[disabledFeatues allObjects], - @"timestamp":[NSString stringWithFormat:@"%.0lf", [[NSDate date] timeIntervalSince1970]], - }; + NSDictionary *disabledFeatureLog = @{@"feature_names" : [disabledFeatues allObjects], + @"timestamp" : [NSString stringWithFormat:@"%.0lf", [[NSDate date] timeIntervalSince1970]], }; NSData *jsonData = [FBSDKTypeUtility dataWithJSONObject:disabledFeatureLog options:0 error:nil]; if (jsonData) { NSString *disabledFeatureReport = [[NSString alloc] initWithData:jsonData encoding:NSUTF8StringEncoding]; if (disabledFeatureReport) { FBSDKGraphRequest *request = [[FBSDKGraphRequest alloc] initWithGraphPath:[NSString stringWithFormat:@"%@/instruments", [FBSDKSettings appID]] - parameters:@{@"crash_shield":disabledFeatureReport} + parameters:@{@"crash_shield" : disabledFeatureReport} HTTPMethod:FBSDKHTTPMethodPOST]; [request startWithCompletionHandler:nil]; @@ -104,11 +109,14 @@ + (void)analyze:(NSArray *> *)crashLogs } } -+ (nullable NSString *)getFeature:(NSArray *)callstack +#pragma mark - Private Methods + ++ (nullable NSString *)_getFeature:(NSArray *)callstack { + NSArray *validCallstack = [FBSDKTypeUtility arrayValue:callstack]; NSArray *featureNames = _featureMapping.allKeys; - for (NSString *entry in callstack) { - NSString *className = [self getClassName:entry]; + for (NSString *entry in validCallstack) { + NSString *className = [self _getClassName:[FBSDKTypeUtility stringValue:entry]]; for (NSString *featureName in featureNames) { NSArray *classArray = [FBSDKTypeUtility dictionary:_featureMapping objectForKey:featureName ofType:NSObject.class]; if (className && [classArray containsObject:className]) { @@ -119,9 +127,10 @@ + (nullable NSString *)getFeature:(NSArray *)callstack return nil; } -+ (nullable NSString *)getClassName:(NSString *)entry ++ (nullable NSString *)_getClassName:(NSString *)entry { - NSArray *items = [entry componentsSeparatedByString:@" "]; + NSString *validEntry = [FBSDKTypeUtility stringValue:entry]; + NSArray *items = [validEntry componentsSeparatedByString:@" "]; NSString *className = nil; // parse class name only from an entry in format "-[className functionName]+offset" // or "+[className functionName]+offset" diff --git a/FBSDKCoreKit/FBSDKCoreKit/Internal/Instrument/ErrorReport/FBSDKErrorReport.h b/FBSDKCoreKit/FBSDKCoreKit/Internal/Instrument/ErrorReport/FBSDKErrorReport.h index 16d1c6604e..b73dfa66bd 100644 --- a/FBSDKCoreKit/FBSDKCoreKit/Internal/Instrument/ErrorReport/FBSDKErrorReport.h +++ b/FBSDKCoreKit/FBSDKCoreKit/Internal/Instrument/ErrorReport/FBSDKErrorReport.h @@ -17,6 +17,7 @@ // CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. #import + #import "FBSDKError.h" NS_ASSUME_NONNULL_BEGIN diff --git a/FBSDKCoreKit/FBSDKCoreKit/Internal/Instrument/ErrorReport/FBSDKErrorReport.m b/FBSDKCoreKit/FBSDKCoreKit/Internal/Instrument/ErrorReport/FBSDKErrorReport.m index 2803adf994..314689ebd2 100644 --- a/FBSDKCoreKit/FBSDKCoreKit/Internal/Instrument/ErrorReport/FBSDKErrorReport.m +++ b/FBSDKCoreKit/FBSDKCoreKit/Internal/Instrument/ErrorReport/FBSDKErrorReport.m @@ -20,10 +20,10 @@ #import "FBSDKGraphRequest.h" #import "FBSDKGraphRequestConnection.h" +#import "FBSDKInternalUtility.h" #import "FBSDKLogger.h" #import "FBSDKSettings.h" #import "FBSDKSettings+Internal.h" -#import "FBSDKTypeUtility.h" #define FBSDK_MAX_ERROR_REPORT_LOGS 1000 @@ -36,7 +36,7 @@ @implementation FBSDKErrorReport NSString *const kFBSDKErrorDomain = @"domain"; NSString *const kFBSDKErrorTimestamp = @"timestamp"; -# pragma mark - Class Methods +# pragma mark - Public Methods + (void)enable { @@ -47,21 +47,35 @@ + (void)enable } } directoryPath = dirPath; - [self uploadError]; + [self _uploadError]; [FBSDKError enableErrorReport]; } -+ (void)uploadError ++ (void)saveError:(NSInteger)errorCode + errorDomain:(NSErrorDomain)errorDomain + message:(nullable NSString *)message +{ + NSString *timestamp = [NSString stringWithFormat:@"%.0lf", [[NSDate date] timeIntervalSince1970]]; + [self _saveErrorInfoToDisk:@{ + kFBSDKErrorCode : @(errorCode), + kFBSDKErrorDomain : errorDomain, + kFBSDKErrorTimestamp : timestamp, + }]; +} + +#pragma mark - Private Methods + ++ (void)_uploadError { if ([FBSDKSettings isDataProcessingRestricted]) { return; } - NSArray *> *errorReports = [self loadErrorReports]; + NSArray *> *errorReports = [self _loadErrorReports]; if ([errorReports count] == 0) { - return [self clearErrorInfo]; + return [self _clearErrorInfo]; } NSData *jsonData = [FBSDKTypeUtility dataWithJSONObject:errorReports options:0 error:nil]; - if (!jsonData){ + if (!jsonData) { return; } NSString *errorData = [[NSString alloc] initWithData:jsonData encoding:NSUTF8StringEncoding]; @@ -71,45 +85,33 @@ + (void)uploadError [request startWithCompletionHandler:^(FBSDKGraphRequestConnection *connection, id result, NSError *error) { if (!error && [result isKindOfClass:[NSDictionary class]] && result[@"success"]) { - [self clearErrorInfo]; + [self _clearErrorInfo]; } }]; } -+ (void)saveError:(NSInteger)errorCode - errorDomain:(NSErrorDomain)errorDomain - message:(nullable NSString *)message -{ - NSString *timestamp = [NSString stringWithFormat:@"%.0lf", [[NSDate date] timeIntervalSince1970]]; - [self saveErrorInfoToDisk: @{ - kFBSDKErrorCode:@(errorCode), - kFBSDKErrorDomain:errorDomain, - kFBSDKErrorTimestamp:timestamp, - }]; -} - -+ (NSArray *> *)loadErrorReports ++ (NSArray *> *)_loadErrorReports { NSMutableArray *> *errorReportArr = [NSMutableArray array]; NSArray *fileNames = [[NSFileManager defaultManager] contentsOfDirectoryAtPath:directoryPath error:NULL]; - NSPredicate *predicate = [NSPredicate predicateWithBlock:^BOOL(id _Nullable evaluatedObject, NSDictionary * _Nullable bindings) { + NSPredicate *predicate = [NSPredicate predicateWithBlock:^BOOL (id _Nullable evaluatedObject, NSDictionary *_Nullable bindings) { NSString *str = (NSString *)evaluatedObject; return [str hasPrefix:@"error_report_"] && [str hasSuffix:@".json"]; }]; fileNames = [fileNames filteredArrayUsingPredicate:predicate]; - fileNames = [fileNames sortedArrayUsingComparator:^NSComparisonResult(id _Nonnull obj1, id _Nonnull obj2){ + fileNames = [fileNames sortedArrayUsingComparator:^NSComparisonResult (id _Nonnull obj1, id _Nonnull obj2) { return [obj2 compare:obj1]; }]; - if (fileNames.count > 0){ + if (fileNames.count > 0) { fileNames = [fileNames subarrayWithRange:NSMakeRange(0, MIN(fileNames.count, FBSDK_MAX_ERROR_REPORT_LOGS))]; for (NSUInteger i = 0; i < fileNames.count; i++) { NSData *data = [NSData dataWithContentsOfFile:[directoryPath stringByAppendingPathComponent:[FBSDKTypeUtility array:fileNames objectAtIndex:i]] options:NSDataReadingMappedIfSafe error:nil]; if (data) { - NSDictionary *errorReport = [FBSDKTypeUtility JSONObjectWithData:data - options:0 - error:nil]; + NSDictionary *errorReport = [FBSDKTypeUtility JSONObjectWithData:data + options:0 + error:nil]; if (errorReport) { [FBSDKTypeUtility array:errorReportArr addObject:errorReport]; } @@ -119,7 +121,7 @@ + (void)saveError:(NSInteger)errorCode return [errorReportArr copy]; } -+ (void)clearErrorInfo ++ (void)_clearErrorInfo { NSArray *files = [[NSFileManager defaultManager] contentsOfDirectoryAtPath:directoryPath error:nil]; for (NSUInteger i = 0; i < files.count; i++) { @@ -129,20 +131,19 @@ + (void)clearErrorInfo } } -#pragma mark - disk operations - -+ (void)saveErrorInfoToDisk:(NSDictionary *)errorInfo ++ (void)_saveErrorInfoToDisk:(NSDictionary *)errorInfo { if (errorInfo.count > 0) { NSData *data = [FBSDKTypeUtility dataWithJSONObject:errorInfo options:0 error:nil]; - [data writeToFile:[self pathToErrorInfoFile] + [data writeToFile:[self _pathToErrorInfoFile] atomically:YES]; } } -+ (NSString *)pathToErrorInfoFile ++ (NSString *)_pathToErrorInfoFile { NSString *timestamp = [NSString stringWithFormat:@"%.0lf", [[NSDate date] timeIntervalSince1970]]; - return [directoryPath stringByAppendingPathComponent:[NSString stringWithFormat:@"error_report_%@.json",timestamp]]; + return [directoryPath stringByAppendingPathComponent:[NSString stringWithFormat:@"error_report_%@.json", timestamp]]; } + @end diff --git a/FBSDKCoreKit/FBSDKCoreKit/Internal/Monitoring/FBSDKMethodUsageMonitorEntry.h b/FBSDKCoreKit/FBSDKCoreKit/Internal/Monitoring/FBSDKMethodUsageMonitorEntry.h index 15ffa668a4..877ebd1921 100644 --- a/FBSDKCoreKit/FBSDKCoreKit/Internal/Monitoring/FBSDKMethodUsageMonitorEntry.h +++ b/FBSDKCoreKit/FBSDKCoreKit/Internal/Monitoring/FBSDKMethodUsageMonitorEntry.h @@ -26,7 +26,7 @@ NS_ASSUME_NONNULL_BEGIN `FBSDKMethodUsageMonitor` to record methods. That will create and persist an entry. */ -@interface FBSDKMethodUsageMonitorEntry : NSObject +@interface FBSDKMethodUsageMonitorEntry : NSObject + (instancetype)new NS_UNAVAILABLE; + (instancetype)entryFromClass:(Class)clazz withMethod:(SEL)method; diff --git a/FBSDKCoreKit/FBSDKCoreKit/Internal/Monitoring/FBSDKMethodUsageMonitorEntry.m b/FBSDKCoreKit/FBSDKCoreKit/Internal/Monitoring/FBSDKMethodUsageMonitorEntry.m index 7905d339ac..f507adeaa6 100644 --- a/FBSDKCoreKit/FBSDKCoreKit/Internal/Monitoring/FBSDKMethodUsageMonitorEntry.m +++ b/FBSDKCoreKit/FBSDKCoreKit/Internal/Monitoring/FBSDKMethodUsageMonitorEntry.m @@ -18,10 +18,11 @@ #import "FBSDKMethodUsageMonitorEntry.h" -static NSString * const FBSDKMethodUsageNameKey = @"event_name"; -static NSString * const FBSDKMethodUsageClassKey = @"method_usage_class"; +static NSString *const FBSDKMethodUsageNameKey = @"event_name"; +static NSString *const FBSDKMethodUsageClassKey = @"method_usage_class"; -@implementation FBSDKMethodUsageMonitorEntry { +@implementation FBSDKMethodUsageMonitorEntry +{ SEL _method; Class _class; NSString *_name; @@ -66,7 +67,7 @@ - (NSString *)name - (NSDictionary *)dictionaryRepresentation { - return @{FBSDKMethodUsageNameKey: [self name]}; + return @{FBSDKMethodUsageNameKey : [self name]}; } @end diff --git a/FBSDKCoreKit/FBSDKCoreKit/Internal/Monitoring/FBSDKMonitor.m b/FBSDKCoreKit/FBSDKCoreKit/Internal/Monitoring/FBSDKMonitor.m index 88a99cf6cb..d671e4f3c1 100644 --- a/FBSDKCoreKit/FBSDKCoreKit/Internal/Monitoring/FBSDKMonitor.m +++ b/FBSDKCoreKit/FBSDKCoreKit/Internal/Monitoring/FBSDKMonitor.m @@ -24,7 +24,7 @@ @interface FBSDKMonitor () -@property (class, nonatomic, copy, readonly) NSMutableArray> *entries; +@property (class, nonatomic, readonly, copy) NSMutableArray> *entries; @property (class, nonatomic) FBSDKMonitorStore *store; @end @@ -116,7 +116,7 @@ + (void)registerNotifications + (void)unregisterNotifications { - [[NSNotificationCenter defaultCenter] removeObserver: [self class]]; + [[NSNotificationCenter defaultCenter] removeObserver:[self class]]; } + (void)applicationDidBecomeActive diff --git a/FBSDKCoreKit/FBSDKCoreKit/Internal/Monitoring/FBSDKMonitorHeaders.h b/FBSDKCoreKit/FBSDKCoreKit/Internal/Monitoring/FBSDKMonitorHeaders.h index 41d7b764e5..3a5fe1c17a 100644 --- a/FBSDKCoreKit/FBSDKCoreKit/Internal/Monitoring/FBSDKMonitorHeaders.h +++ b/FBSDKCoreKit/FBSDKCoreKit/Internal/Monitoring/FBSDKMonitorHeaders.h @@ -22,10 +22,10 @@ #import "FBSDKMethodUsageMonitor.h" #import "FBSDKMethodUsageMonitorEntry.h" #import "FBSDKMonitor.h" -#import "FBSDKMonitoringConfiguration.h" #import "FBSDKMonitorEntry.h" #import "FBSDKMonitorNetworker.h" #import "FBSDKMonitorStore.h" +#import "FBSDKMonitoringConfiguration.h" #import "FBSDKPerformanceMonitor.h" #import "FBSDKPerformanceMonitorEntry.h" diff --git a/FBSDKCoreKit/FBSDKCoreKit/Internal/Monitoring/FBSDKMonitorNetworker.m b/FBSDKCoreKit/FBSDKCoreKit/Internal/Monitoring/FBSDKMonitorNetworker.m index 4507cd18bb..85a1d48f8e 100644 --- a/FBSDKCoreKit/FBSDKCoreKit/Internal/Monitoring/FBSDKMonitorNetworker.m +++ b/FBSDKCoreKit/FBSDKCoreKit/Internal/Monitoring/FBSDKMonitorNetworker.m @@ -16,17 +16,17 @@ // 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 "FBSDKMonitorNetworker.h" + #import #import "FBSDKCoreKit+Internal.h" -#import "FBSDKMonitorNetworker.h" - -static NSString * const FBSDKAppIdentifierKey = @"id"; -static NSString * const FBSDKBundleIdentifierKey = @"unique_application_identifier"; -static NSString * const FBSDKDeviceModelKey = @"device_model"; -static NSString * const FBSDKMonitoringsKey = @"monitorings"; -static NSString * const FBSDKOsVersionKey = @"device_os_version"; +static NSString *const FBSDKAppIdentifierKey = @"id"; +static NSString *const FBSDKBundleIdentifierKey = @"unique_application_identifier"; +static NSString *const FBSDKDeviceModelKey = @"device_model"; +static NSString *const FBSDKMonitoringsKey = @"monitorings"; +static NSString *const FBSDKOsVersionKey = @"device_os_version"; @interface FBSDKMonitorNetworker () @end @@ -80,7 +80,7 @@ + (nullable NSString *)JSONStringForEntries:(NSArray> *)en invalidObjectHandler:NULL]; } -+ (NSString * _Nonnull)deviceModel ++ (NSString *_Nonnull)deviceModel { struct utsname systemInfo; uname(&systemInfo); diff --git a/FBSDKCoreKit/FBSDKCoreKit/Internal/Monitoring/FBSDKMonitorStore.m b/FBSDKCoreKit/FBSDKCoreKit/Internal/Monitoring/FBSDKMonitorStore.m index 31e263fea2..062482500b 100644 --- a/FBSDKCoreKit/FBSDKCoreKit/Internal/Monitoring/FBSDKMonitorStore.m +++ b/FBSDKCoreKit/FBSDKCoreKit/Internal/Monitoring/FBSDKMonitorStore.m @@ -29,7 +29,6 @@ @implementation FBSDKMonitorStore - (instancetype)initWithFilename:(NSString *)filename { if ((self = [super init])) { - NSURL *temporaryDirectory = [NSURL fileURLWithPath:NSTemporaryDirectory() isDirectory:YES]; _filePath = [temporaryDirectory URLByAppendingPathComponent:filename]; _skipDiskCheck = YES; @@ -43,6 +42,7 @@ - (void)clear error:nil]; self.skipDiskCheck = YES; } + #pragma clang diagnostic push #pragma clang diagnostic ignored "-Wdeprecated-declarations" - (void)persist:(NSArray> *)entries @@ -55,7 +55,8 @@ - (void)persist:(NSArray> *)entries self.skipDiskCheck = NO; } -- (NSArray> *)retrieveEntries { +- (NSArray> *)retrieveEntries +{ NSMutableArray *items = [NSMutableArray array]; if (!self.skipDiskCheck) { @@ -63,9 +64,10 @@ - (void)persist:(NSArray> *)entries [self clear]; } - + return [items copy]; } + #pragma clang diagnostic pop @end diff --git a/FBSDKCoreKit/FBSDKCoreKit/Internal/Monitoring/FBSDKMonitoringConfiguration.h b/FBSDKCoreKit/FBSDKCoreKit/Internal/Monitoring/FBSDKMonitoringConfiguration.h index e7e5bd89ea..d888bc00e1 100644 --- a/FBSDKCoreKit/FBSDKCoreKit/Internal/Monitoring/FBSDKMonitoringConfiguration.h +++ b/FBSDKCoreKit/FBSDKCoreKit/Internal/Monitoring/FBSDKMonitoringConfiguration.h @@ -22,7 +22,7 @@ NS_ASSUME_NONNULL_BEGIN -@interface FBSDKMonitoringConfiguration : NSObject +@interface FBSDKMonitoringConfiguration : NSObject @property (nonatomic, readonly) int defaultSamplingRate; diff --git a/FBSDKCoreKit/FBSDKCoreKit/Internal/Monitoring/FBSDKMonitoringConfiguration.m b/FBSDKCoreKit/FBSDKCoreKit/Internal/Monitoring/FBSDKMonitoringConfiguration.m index 01905108e6..8538cb2a51 100644 --- a/FBSDKCoreKit/FBSDKCoreKit/Internal/Monitoring/FBSDKMonitoringConfiguration.m +++ b/FBSDKCoreKit/FBSDKCoreKit/Internal/Monitoring/FBSDKMonitoringConfiguration.m @@ -16,16 +16,17 @@ // 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 "FBSDKCoreKit+Internal.h" - #import "FBSDKMonitoringConfiguration.h" +#import "FBSDKCoreKit+Internal.h" + static NSString *defaultRateKey = @"default"; static NSString *sampleRatesKey = @"sample_rates"; static NSString *sampleRateNameKey = @"key"; static NSString *sampleRateValueKey = @"value"; -@implementation FBSDKMonitoringConfiguration { +@implementation FBSDKMonitoringConfiguration +{ NSDictionary *_sampleRates; } @@ -78,11 +79,13 @@ - (int)sampleRateForEntry:(nonnull id)entry return [[FBSDKTypeUtility dictionary:_sampleRates objectForKey:entry.name ofType:NSObject.class] intValue] ?: self.defaultSamplingRate; } -- (void)encodeWithCoder:(nonnull NSCoder *)encoder { +- (void)encodeWithCoder:(nonnull NSCoder *)encoder +{ [encoder encodeObject:_sampleRates forKey:sampleRatesKey]; } -- (nullable instancetype)initWithCoder:(nonnull NSCoder *)decoder { +- (nullable instancetype)initWithCoder:(nonnull NSCoder *)decoder +{ _sampleRates = [decoder decodeObjectOfClass:[SampleRates class] forKey:sampleRatesKey]; return self; } diff --git a/FBSDKCoreKit/FBSDKCoreKit/Internal/Monitoring/FBSDKPerformanceMonitor.m b/FBSDKCoreKit/FBSDKCoreKit/Internal/Monitoring/FBSDKPerformanceMonitor.m index 8f61be1470..d474f7cc29 100644 --- a/FBSDKCoreKit/FBSDKCoreKit/Internal/Monitoring/FBSDKPerformanceMonitor.m +++ b/FBSDKCoreKit/FBSDKCoreKit/Internal/Monitoring/FBSDKPerformanceMonitor.m @@ -16,6 +16,7 @@ // 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 "FBSDKPerformanceMonitor.h" + #import "FBSDKMonitorHeaders.h" @implementation FBSDKPerformanceMonitor diff --git a/FBSDKCoreKit/FBSDKCoreKit/Internal/Monitoring/FBSDKPerformanceMonitorEntry.h b/FBSDKCoreKit/FBSDKCoreKit/Internal/Monitoring/FBSDKPerformanceMonitorEntry.h index cc3cddbcf4..8c6dbb7f15 100644 --- a/FBSDKCoreKit/FBSDKCoreKit/Internal/Monitoring/FBSDKPerformanceMonitorEntry.h +++ b/FBSDKCoreKit/FBSDKCoreKit/Internal/Monitoring/FBSDKPerformanceMonitorEntry.h @@ -26,7 +26,7 @@ NS_ASSUME_NONNULL_BEGIN `FBSDKPerformanceMonitor` to record metrics with their start times and durations. That will create and persist an entry. */ -@interface FBSDKPerformanceMonitorEntry : NSObject +@interface FBSDKPerformanceMonitorEntry : NSObject + (instancetype)new NS_UNAVAILABLE; + (instancetype _Nullable)entryWithName:(NSString *)name startTime:(NSDate *)startTime endTime:(NSDate *)endTime; diff --git a/FBSDKCoreKit/FBSDKCoreKit/Internal/Monitoring/FBSDKPerformanceMonitorEntry.m b/FBSDKCoreKit/FBSDKCoreKit/Internal/Monitoring/FBSDKPerformanceMonitorEntry.m index 66947613ff..df40618d9e 100644 --- a/FBSDKCoreKit/FBSDKCoreKit/Internal/Monitoring/FBSDKPerformanceMonitorEntry.m +++ b/FBSDKCoreKit/FBSDKCoreKit/Internal/Monitoring/FBSDKPerformanceMonitorEntry.m @@ -18,14 +18,15 @@ #import "FBSDKPerformanceMonitorEntry.h" -#import "FBSDKTypeUtility.h" +#import "FBSDKInternalUtility.h" -static NSString * const FBSDKPerformanceNameKey = @"event_name"; -static NSString * const FBSDKPerformanceStartTimeKey = @"time_start"; -static NSString * const FBSDKPerformanceEndTimeKey = @"time_end"; -static NSString * const FBSDKPerformanceTimeSpentKey = @"time_spent"; +static NSString *const FBSDKPerformanceNameKey = @"event_name"; +static NSString *const FBSDKPerformanceStartTimeKey = @"time_start"; +static NSString *const FBSDKPerformanceEndTimeKey = @"time_end"; +static NSString *const FBSDKPerformanceTimeSpentKey = @"time_spent"; -@implementation FBSDKPerformanceMonitorEntry { +@implementation FBSDKPerformanceMonitorEntry +{ NSString *_name; NSDate *_startTime; NSDate *_endTime; @@ -52,7 +53,8 @@ - (NSString *)name return [_name copy]; } -- (void)encodeWithCoder:(nonnull NSCoder *)encoder { +- (void)encodeWithCoder:(nonnull NSCoder *)encoder +{ if (_name && _startTime && _endTime) { [encoder encodeObject:_name forKey:FBSDKPerformanceNameKey]; [encoder encodeObject:_startTime forKey:FBSDKPerformanceStartTimeKey]; @@ -60,7 +62,8 @@ - (void)encodeWithCoder:(nonnull NSCoder *)encoder { } } -- (nullable instancetype)initWithCoder:(nonnull NSCoder *)decoder { +- (nullable instancetype)initWithCoder:(nonnull NSCoder *)decoder +{ _name = [decoder decodeObjectOfClass:[NSString class] forKey:FBSDKPerformanceNameKey]; _startTime = [decoder decodeObjectOfClass:[NSDate class] forKey:FBSDKPerformanceStartTimeKey]; _endTime = [decoder decodeObjectOfClass:[NSDate class] forKey:FBSDKPerformanceEndTimeKey]; @@ -72,9 +75,10 @@ - (nullable instancetype)initWithCoder:(nonnull NSCoder *)decoder { return nil; } -- (nonnull NSDictionary *)dictionaryRepresentation { +- (nonnull NSDictionary *)dictionaryRepresentation +{ NSMutableDictionary *dict = [NSMutableDictionary dictionary]; - + [FBSDKTypeUtility dictionary:dict setObject:_name forKey:FBSDKPerformanceNameKey]; [FBSDKTypeUtility dictionary:dict diff --git a/FBSDKCoreKit/FBSDKCoreKit/Internal/Network/FBSDKGraphRequest+Internal.h b/FBSDKCoreKit/FBSDKCoreKit/Internal/Network/FBSDKGraphRequest+Internal.h index 11aff98515..c64c10c7c9 100644 --- a/FBSDKCoreKit/FBSDKCoreKit/Internal/Network/FBSDKGraphRequest+Internal.h +++ b/FBSDKCoreKit/FBSDKCoreKit/Internal/Network/FBSDKGraphRequest+Internal.h @@ -19,13 +19,12 @@ #import #if SWIFT_PACKAGE -#import "FBSDKGraphRequest.h" + #import "FBSDKGraphRequest.h" #else -#import + #import #endif -typedef NS_OPTIONS(NSUInteger, FBSDKGraphRequestFlags) -{ +typedef NS_OPTIONS(NSUInteger, FBSDKGraphRequestFlags) { FBSDKGraphRequestFlagNone = 0, // indicates this request should not use a client token as its token parameter FBSDKGraphRequestFlagSkipClientToken = 1 << 1, @@ -49,7 +48,7 @@ typedef NS_OPTIONS(NSUInteger, FBSDKGraphRequestFlags) // so that we don't cause a sudden change in token state or trigger recovery // out of context of any user action. @property (nonatomic, assign) FBSDKGraphRequestFlags flags; -@property (nonatomic, readonly, getter=isGraphErrorRecoveryDisabled) BOOL graphErrorRecoveryDisabled; +@property (nonatomic, readonly, getter = isGraphErrorRecoveryDisabled) BOOL graphErrorRecoveryDisabled; @property (nonatomic, readonly) BOOL hasAttachments; + (BOOL)isAttachment:(id)item; diff --git a/FBSDKCoreKit/FBSDKCoreKit/Internal/Network/FBSDKGraphRequestBody.h b/FBSDKCoreKit/FBSDKCoreKit/Internal/Network/FBSDKGraphRequestBody.h index 37bf777e45..c08f9cb364 100644 --- a/FBSDKCoreKit/FBSDKCoreKit/Internal/Network/FBSDKGraphRequestBody.h +++ b/FBSDKCoreKit/FBSDKCoreKit/Internal/Network/FBSDKGraphRequestBody.h @@ -27,6 +27,12 @@ NS_SWIFT_NAME(GraphRequestBody) @property (nonatomic, retain, readonly) NSData *data; +/** + Determines whether to use multipart/form-data or application/json as the Content-Type. + If binary attachments are added, this will default to YES. + */ +@property (nonatomic, assign) BOOL requiresMultipartDataFormat; + - (void)appendWithKey:(NSString *)key formValue:(NSString *)value logger:(FBSDKLogger *)logger; diff --git a/FBSDKCoreKit/FBSDKCoreKit/Internal/Network/FBSDKGraphRequestBody.m b/FBSDKCoreKit/FBSDKCoreKit/Internal/Network/FBSDKGraphRequestBody.m index c0c80858e5..b2d1e64844 100644 --- a/FBSDKCoreKit/FBSDKCoreKit/Internal/Network/FBSDKGraphRequestBody.m +++ b/FBSDKCoreKit/FBSDKCoreKit/Internal/Network/FBSDKGraphRequestBody.m @@ -40,6 +40,7 @@ - (instancetype)init _stringBoundary = [FBSDKCrypto randomString:32]; _data = [[NSMutableData alloc] init]; _json = [NSMutableDictionary dictionary]; + _requiresMultipartDataFormat = NO; } return self; @@ -47,10 +48,10 @@ - (instancetype)init - (NSString *)mimeContentType { - if (_json) { - return @"application/json"; - } else { + if (self.requiresMultipartDataFormat) { return [NSString stringWithFormat:@"multipart/form-data; boundary=%@", _stringBoundary]; + } else { + return @"application/json"; } } @@ -86,7 +87,7 @@ - (void)appendWithKey:(NSString *)key [self _appendWithKey:key filename:key contentType:@"image/jpeg" contentBlock:^{ [self->_data appendData:data]; }]; - _json = nil; + self.requiresMultipartDataFormat = YES; [logger appendFormat:@"\n %@:\t", key, (unsigned long)(data.length / 1024)]; } @@ -97,7 +98,7 @@ - (void)appendWithKey:(NSString *)key [self _appendWithKey:key filename:key contentType:@"content/unknown" contentBlock:^{ [self->_data appendData:data]; }]; - _json = nil; + self.requiresMultipartDataFormat = YES; [logger appendFormat:@"\n %@:\t", key, (unsigned long)(data.length / 1024)]; } @@ -111,13 +112,15 @@ - (void)appendWithKey:(NSString *)key [self _appendWithKey:key filename:filename contentType:contentType contentBlock:^{ [self->_data appendData:data]; }]; - _json = nil; + self.requiresMultipartDataFormat = YES; [logger appendFormat:@"\n %@:\t", key, (unsigned long)(data.length / 1024)]; } - (NSData *)data { - if (_json) { + if (self.requiresMultipartDataFormat) { + return [_data copy]; + } else { NSData *jsonData; if (_json.allKeys.count > 0) { jsonData = [FBSDKTypeUtility dataWithJSONObject:_json options:0 error:nil]; @@ -127,7 +130,6 @@ - (NSData *)data return jsonData; } - return [_data copy]; } - (void)_appendWithKey:(NSString *)key diff --git a/FBSDKCoreKit/FBSDKCoreKit/Internal/Network/FBSDKGraphRequestConnection+Internal.h b/FBSDKCoreKit/FBSDKCoreKit/Internal/Network/FBSDKGraphRequestConnection+Internal.h index 56d32239f8..fea0f90a82 100644 --- a/FBSDKCoreKit/FBSDKCoreKit/Internal/Network/FBSDKGraphRequestConnection+Internal.h +++ b/FBSDKCoreKit/FBSDKCoreKit/Internal/Network/FBSDKGraphRequestConnection+Internal.h @@ -16,17 +16,32 @@ // 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. +@class FBSDKGraphRequestBody; @class FBSDKURLSession; #if SWIFT_PACKAGE -#import "FBSDKGraphRequestConnection.h" + #import "FBSDKGraphRequestConnection.h" #else -#import + #import #endif -@interface FBSDKGraphRequestConnection(Internal) +@interface FBSDKGraphRequestConnection (Internal) @property (nonatomic, readonly) NSMutableArray *requests; @property (nonatomic, strong) FBSDKURLSession *session; +/** + Get the graph request url for a single graph request + @param request The Graph Request we need the url for + @param forBatch whether the request is a batch request. + */ +- (NSString *)urlStringForSingleRequest:(FBSDKGraphRequest *)request forBatch:(BOOL)forBatch; + +/** + Add the specified body as the HTTPBody of the specified request. + @param body The FBSDKGraphRequestBody to attach to the request. + @param request The NSURLRequest to attach the body to. + */ +- (void)addBody:(FBSDKGraphRequestBody *)body toPostRequest:(NSMutableURLRequest *)request; + @end diff --git a/FBSDKCoreKit/FBSDKCoreKit/Internal/Network/FBSDKGraphRequestMetadata.m b/FBSDKCoreKit/FBSDKCoreKit/Internal/Network/FBSDKGraphRequestMetadata.m index 2f09f8cd23..da018ae749 100644 --- a/FBSDKCoreKit/FBSDKCoreKit/Internal/Network/FBSDKGraphRequestMetadata.m +++ b/FBSDKCoreKit/FBSDKCoreKit/Internal/Network/FBSDKGraphRequestMetadata.m @@ -24,8 +24,8 @@ @implementation FBSDKGraphRequestMetadata - (instancetype)initWithRequest:(FBSDKGraphRequest *)request completionHandler:(FBSDKGraphRequestBlock)handler - batchParameters:(NSDictionary *)batchParameters { - + batchParameters:(NSDictionary *)batchParameters +{ if ((self = [super init])) { _request = request; _batchParameters = [batchParameters copy]; @@ -36,7 +36,8 @@ - (instancetype)initWithRequest:(FBSDKGraphRequest *)request - (void)invokeCompletionHandlerForConnection:(FBSDKGraphRequestConnection *)connection withResults:(id)results - error:(NSError *)error { + error:(NSError *)error +{ if (self.completionHandler) { self.completionHandler(connection, results, error); } diff --git a/FBSDKCoreKit/FBSDKCoreKit/Internal/Network/FBSDKGraphRequestPiggybackManager.m b/FBSDKCoreKit/FBSDKCoreKit/Internal/Network/FBSDKGraphRequestPiggybackManager.m index e3d2899ad0..14d84b35fe 100644 --- a/FBSDKCoreKit/FBSDKCoreKit/Internal/Network/FBSDKGraphRequestPiggybackManager.m +++ b/FBSDKCoreKit/FBSDKCoreKit/Internal/Network/FBSDKGraphRequestPiggybackManager.m @@ -20,18 +20,19 @@ #import "FBSDKCoreKit+Internal.h" -static int const FBSDKTokenRefreshThresholdSeconds = 24 * 60 * 60; // day -static int const FBSDKTokenRefreshRetrySeconds = 60 * 60; // hour +static int const FBSDKTokenRefreshThresholdSeconds = 24 * 60 * 60; // day +static int const FBSDKTokenRefreshRetrySeconds = 60 * 60; // hour @implementation FBSDKGraphRequestPiggybackManager +static NSDate *_lastRefreshTry = nil; + + (void)addPiggybackRequests:(FBSDKGraphRequestConnection *)connection { if ([FBSDKSettings appID].length > 0) { BOOL safeForPiggyback = YES; for (FBSDKGraphRequestMetadata *metadata in connection.requests) { - if (![metadata.request.version isEqualToString:[FBSDKSettings graphAPIVersion]] || - metadata.request.hasAttachments) { + if (![self _safeForPiggyback:metadata.request]) { safeForPiggyback = NO; break; } @@ -46,6 +47,9 @@ + (void)addPiggybackRequests:(FBSDKGraphRequestConnection *)connection + (void)addRefreshPiggyback:(FBSDKGraphRequestConnection *)connection permissionHandler:(FBSDKGraphRequestBlock)permissionHandler { FBSDKAccessToken *expectedToken = [FBSDKAccessToken currentAccessToken]; + if (!expectedToken) { + return; + } __block NSMutableSet *permissions = nil; __block NSMutableSet *declinedPermissions = nil; __block NSMutableSet *expiredPermissions = nil; @@ -59,25 +63,25 @@ + (void)addRefreshPiggyback:(FBSDKGraphRequestConnection *)connection permission FBSDKAccessToken *currentToken = [FBSDKAccessToken currentAccessToken]; NSDate *expirationDate = currentToken.expirationDate; if (expirationDateNumber != nil) { - expirationDate = (expirationDateNumber.doubleValue > 0 ? - [NSDate dateWithTimeIntervalSince1970:expirationDateNumber.doubleValue] : - [NSDate distantFuture]); + expirationDate = (expirationDateNumber.doubleValue > 0 + ? [NSDate dateWithTimeIntervalSince1970:expirationDateNumber.doubleValue] + : [NSDate distantFuture]); } NSDate *dataExpirationDate = currentToken.dataAccessExpirationDate; if (dataAccessExpirationDateNumber != nil) { - dataExpirationDate = (dataAccessExpirationDateNumber.doubleValue > 0 ? - [NSDate dateWithTimeIntervalSince1970:dataAccessExpirationDateNumber.doubleValue] : - [NSDate distantFuture]); + dataExpirationDate = (dataAccessExpirationDateNumber.doubleValue > 0 + ? [NSDate dateWithTimeIntervalSince1970:dataAccessExpirationDateNumber.doubleValue] + : [NSDate distantFuture]); } FBSDKAccessToken *refreshedToken = [[FBSDKAccessToken alloc] initWithTokenString:tokenString ?: currentToken.tokenString permissions:(permissions ?: currentToken.permissions).allObjects declinedPermissions:(declinedPermissions ?: currentToken.declinedPermissions).allObjects - expiredPermissions:(expiredPermissions ?: currentToken.expiredPermissions).allObjects + expiredPermissions:(expiredPermissions ?: currentToken.expiredPermissions).allObjects appID:currentToken.appID userID:currentToken.userID expirationDate:expirationDate refreshDate:[NSDate date] - dataAccessExpirationDate:dataExpirationDate + dataAccessExpirationDate:dataExpirationDate graphDomain:graphDomain ?: currentToken.graphDomain]; if (expectedToken == currentToken) { [FBSDKAccessToken setCurrentAccessToken:refreshedToken]; @@ -85,22 +89,21 @@ + (void)addRefreshPiggyback:(FBSDKGraphRequestConnection *)connection permission } }; FBSDKGraphRequest *extendRequest = [[FBSDKGraphRequest alloc] initWithGraphPath:@"oauth/access_token" - parameters:@{@"grant_type" : @"fb_extend_sso_token", - @"fields": @"", - @"client_id": expectedToken.appID - } - flags:FBSDKGraphRequestFlagDisableErrorRecovery]; + parameters:@{@"grant_type" : @"fb_extend_sso_token", + @"fields" : @"", + @"client_id" : expectedToken.appID} + flags:FBSDKGraphRequestFlagDisableErrorRecovery]; [connection addRequest:extendRequest completionHandler:^(FBSDKGraphRequestConnection *innerConnection, id result, NSError *error) { - tokenString = result[@"access_token"]; - expirationDateNumber = result[@"expires_at"]; - dataAccessExpirationDateNumber = result[@"data_access_expiration_time"]; - graphDomain = result[@"graph_domain"]; + tokenString = [FBSDKTypeUtility dictionary:result objectForKey:@"access_token" ofType:NSString.class]; + expirationDateNumber = [FBSDKTypeUtility dictionary:result objectForKey:@"expires_at" ofType:NSNumber.class]; + dataAccessExpirationDateNumber = [FBSDKTypeUtility dictionary:result objectForKey:@"data_access_expiration_time" ofType:NSNumber.class]; + graphDomain = [FBSDKTypeUtility dictionary:result objectForKey:@"graph_domain" ofType:NSString.class]; expectingCallbackComplete(); }]; FBSDKGraphRequest *permissionsRequest = [[FBSDKGraphRequest alloc] initWithGraphPath:@"me/permissions" - parameters:@{@"fields": @""} - flags:FBSDKGraphRequestFlagDisableErrorRecovery]; + parameters:@{@"fields" : @""} + flags:FBSDKGraphRequestFlagDisableErrorRecovery]; [connection addRequest:permissionsRequest completionHandler:^(FBSDKGraphRequestConnection *innerConnection, id result, NSError *error) { if (!error) { @@ -125,19 +128,13 @@ + (void)addRefreshPiggybackIfStale:(FBSDKGraphRequestConnection *)connection // don't piggy back more than once an hour as a cheap way of // retrying in cases of errors and preventing duplicate refreshes. // obviously this is not foolproof but is simple and sufficient. - static NSDate *lastRefreshTry; - static dispatch_once_t onceToken; - dispatch_once(&onceToken, ^{ - lastRefreshTry = [NSDate distantPast]; - }); - NSDate *now = [NSDate date]; NSDate *tokenRefreshDate = [FBSDKAccessToken currentAccessToken].refreshDate; - if (tokenRefreshDate && - [now timeIntervalSinceDate:lastRefreshTry] > FBSDKTokenRefreshRetrySeconds && - [now timeIntervalSinceDate:tokenRefreshDate] > FBSDKTokenRefreshThresholdSeconds) { + if (tokenRefreshDate + && [now timeIntervalSinceDate:[self _lastRefreshTry]] > [self _tokenRefreshRetryInSeconds] + && [now timeIntervalSinceDate:tokenRefreshDate] > [self _tokenRefreshThresholdInSeconds]) { [self addRefreshPiggyback:connection permissionHandler:NULL]; - lastRefreshTry = [NSDate date]; + [self _setLastRefreshTry:[NSDate date]]; } } @@ -156,4 +153,33 @@ + (void)addServerConfigurationPiggyback:(FBSDKGraphRequestConnection *)connectio }]; } ++ (BOOL)_safeForPiggyback:(FBSDKGraphRequest *)request +{ + return [request.version isEqualToString:[FBSDKSettings graphAPIVersion]] + && !request.hasAttachments; +} + ++ (int)_tokenRefreshThresholdInSeconds +{ + return FBSDKTokenRefreshThresholdSeconds; +} + ++ (int)_tokenRefreshRetryInSeconds +{ + return FBSDKTokenRefreshRetrySeconds; +} + ++ (NSDate *)_lastRefreshTry +{ + if (!_lastRefreshTry) { + _lastRefreshTry = [NSDate distantPast]; + } + return _lastRefreshTry; +} + ++ (void)_setLastRefreshTry:(NSDate *)date +{ + _lastRefreshTry = date; +} + @end diff --git a/FBSDKCoreKit/FBSDKCoreKit/Internal/ServerConfiguration/FBSDKErrorConfiguration.m b/FBSDKCoreKit/FBSDKCoreKit/Internal/ServerConfiguration/FBSDKErrorConfiguration.m index 7ed4443bf5..ebe63b5a3c 100644 --- a/FBSDKCoreKit/FBSDKCoreKit/Internal/ServerConfiguration/FBSDKErrorConfiguration.m +++ b/FBSDKCoreKit/FBSDKCoreKit/Internal/ServerConfiguration/FBSDKErrorConfiguration.m @@ -19,7 +19,6 @@ #import "FBSDKErrorConfiguration.h" #import "FBSDKCoreKit+Internal.h" - #import "FBSDKErrorRecoveryConfiguration.h" static NSString *const kErrorCategoryOther = @"other"; @@ -41,39 +40,53 @@ - (instancetype)initWithDictionary:(NSDictionary *)dictionary } else { _configurationDictionary = [NSMutableDictionary dictionary]; NSString *localizedOK = - NSLocalizedStringWithDefaultValue(@"ErrorRecovery.OK", @"FacebookSDK", [FBSDKInternalUtility bundleForStrings], - @"OK", - @"The title of the label to start attempting error recovery"); + NSLocalizedStringWithDefaultValue( + @"ErrorRecovery.OK", + @"FacebookSDK", + [FBSDKInternalUtility bundleForStrings], + @"OK", + @"The title of the label to start attempting error recovery" + ); NSString *localizedCancel = - NSLocalizedStringWithDefaultValue(@"ErrorRecovery.Cancel", @"FacebookSDK", [FBSDKInternalUtility bundleForStrings], - @"Cancel", - @"The title of the label to decline attempting error recovery"); + NSLocalizedStringWithDefaultValue( + @"ErrorRecovery.Cancel", + @"FacebookSDK", + [FBSDKInternalUtility bundleForStrings], + @"Cancel", + @"The title of the label to decline attempting error recovery" + ); NSString *localizedTransientSuggestion = - NSLocalizedStringWithDefaultValue(@"ErrorRecovery.Transient.Suggestion", @"FacebookSDK", [FBSDKInternalUtility bundleForStrings], - @"The server is temporarily busy, please try again.", - @"The fallback message to display to retry transient errors"); + NSLocalizedStringWithDefaultValue( + @"ErrorRecovery.Transient.Suggestion", + @"FacebookSDK", + [FBSDKInternalUtility bundleForStrings], + @"The server is temporarily busy, please try again.", + @"The fallback message to display to retry transient errors" + ); NSString *localizedLoginRecoverableSuggestion = - NSLocalizedStringWithDefaultValue(@"ErrorRecovery.Login.Suggestion", @"FacebookSDK", [FBSDKInternalUtility bundleForStrings], - @"Please log into this app again to reconnect your Facebook account.", - @"The fallback message to display to recover invalidated tokens"); + NSLocalizedStringWithDefaultValue( + @"ErrorRecovery.Login.Suggestion", + @"FacebookSDK", + [FBSDKInternalUtility bundleForStrings], + @"Please log into this app again to reconnect your Facebook account.", + @"The fallback message to display to recover invalidated tokens" + ); NSArray *fallbackArray = @[ - @{ @"name" : @"login", - @"items" : @[ @{ @"code" : @102 }, - @{ @"code" : @190 } ], - @"recovery_message" : localizedLoginRecoverableSuggestion, - @"recovery_options" : @[ localizedOK, localizedCancel] - }, - @{ @"name" : @"transient", - @"items" : @[ @{ @"code" : @1 }, - @{ @"code" : @2 }, - @{ @"code" : @4 }, - @{ @"code" : @9 }, - @{ @"code" : @17 }, - @{ @"code" : @341 } ], - @"recovery_message" : localizedTransientSuggestion, - @"recovery_options" : @[ localizedOK] - }, - ]; + @{ @"name" : @"login", + @"items" : @[@{ @"code" : @102 }, + @{ @"code" : @190 }], + @"recovery_message" : localizedLoginRecoverableSuggestion, + @"recovery_options" : @[localizedOK, localizedCancel]}, + @{ @"name" : @"transient", + @"items" : @[@{ @"code" : @1 }, + @{ @"code" : @2 }, + @{ @"code" : @4 }, + @{ @"code" : @9 }, + @{ @"code" : @17 }, + @{ @"code" : @341 }], + @"recovery_message" : localizedTransientSuggestion, + @"recovery_options" : @[localizedOK]}, + ]; [self parseArray:fallbackArray]; } } @@ -84,26 +97,26 @@ - (FBSDKErrorRecoveryConfiguration *)recoveryConfigurationForCode:(NSString *)co { code = code ?: @"*"; subcode = subcode ?: @"*"; - FBSDKErrorRecoveryConfiguration *configuration = (_configurationDictionary[code][subcode] ?: - _configurationDictionary[code][@"*"] ?: - _configurationDictionary[@"*"][subcode] ?: - _configurationDictionary[@"*"][@"*"]); - if (configuration.errorCategory == FBSDKGraphRequestErrorRecoverable && - [FBSDKSettings clientToken] && - [request.parameters[@"access_token"] hasSuffix:[FBSDKSettings clientToken]]) { + FBSDKErrorRecoveryConfiguration *configuration = (_configurationDictionary[code][subcode] + ?: _configurationDictionary[code][@"*"] + ?: _configurationDictionary[@"*"][subcode] + ?: _configurationDictionary[@"*"][@"*"]); + if (configuration.errorCategory == FBSDKGraphRequestErrorRecoverable + && [FBSDKSettings clientToken] + && [request.parameters[@"access_token"] hasSuffix:[FBSDKSettings clientToken]]) { // do not attempt to recovery client tokens. return nil; } return configuration; } -- (void)parseArray:(NSArray *)array +- (void)parseArray:(NSArray *)array { for (NSDictionary *dictionary in [FBSDKTypeUtility arrayValue:array]) { [FBSDKTypeUtility dictionary:dictionary enumerateKeysAndObjectsUsingBlock:^(NSString *key, id obj, BOOL *stop) { FBSDKGraphRequestError category; NSString *action = [FBSDKTypeUtility stringValue:dictionary[@"name"]]; - if ( [action isEqualToString:kErrorCategoryOther]) { + if ([action isEqualToString:kErrorCategoryOther]) { category = FBSDKGraphRequestErrorOther; } else if ([action isEqualToString:kErrorCategoryTransient]) { category = FBSDKGraphRequestErrorTransient; @@ -140,17 +153,17 @@ - (void)parseArray:(NSArray *)array continue; } [FBSDKTypeUtility dictionary:currentSubcodes setObject:[[FBSDKErrorRecoveryConfiguration alloc] - initWithRecoveryDescription:suggestion - optionDescriptions:options - category:category - recoveryActionName:action] forKey:validSubcodeNumber.stringValue]; + initWithRecoveryDescription:suggestion + optionDescriptions:options + category:category + recoveryActionName:action] forKey:validSubcodeNumber.stringValue]; } } else { [FBSDKTypeUtility dictionary:currentSubcodes setObject:[[FBSDKErrorRecoveryConfiguration alloc] - initWithRecoveryDescription:suggestion - optionDescriptions:options - category:category - recoveryActionName:action] forKey:@"*"]; + initWithRecoveryDescription:suggestion + optionDescriptions:options + category:category + recoveryActionName:action] forKey:@"*"]; } } }]; diff --git a/FBSDKCoreKit/FBSDKCoreKit/Internal/ServerConfiguration/FBSDKErrorRecoveryConfiguration.m b/FBSDKCoreKit/FBSDKCoreKit/Internal/ServerConfiguration/FBSDKErrorRecoveryConfiguration.m index 97eb061033..a8540426e4 100644 --- a/FBSDKCoreKit/FBSDKCoreKit/Internal/ServerConfiguration/FBSDKErrorRecoveryConfiguration.m +++ b/FBSDKCoreKit/FBSDKCoreKit/Internal/ServerConfiguration/FBSDKErrorRecoveryConfiguration.m @@ -28,7 +28,8 @@ @implementation FBSDKErrorRecoveryConfiguration - (instancetype)initWithRecoveryDescription:(NSString *)description optionDescriptions:(NSArray *)optionDescriptions category:(FBSDKGraphRequestError)category - recoveryActionName:(NSString *)recoveryActionName { + recoveryActionName:(NSString *)recoveryActionName +{ if ((self = [super init])) { _localizedRecoveryDescription = [description copy]; _localizedRecoveryOptionDescriptions = [optionDescriptions copy]; @@ -70,7 +71,7 @@ - (void)encodeWithCoder:(NSCoder *)encoder - (id)copyWithZone:(NSZone *)zone { - //immutable + // immutable return self; } diff --git a/FBSDKCoreKit/FBSDKCoreKit/Internal/ServerConfiguration/FBSDKGateKeeperManager.h b/FBSDKCoreKit/FBSDKCoreKit/Internal/ServerConfiguration/FBSDKGateKeeperManager.h index fa8a5b6593..8d5e582ee4 100644 --- a/FBSDKCoreKit/FBSDKCoreKit/Internal/ServerConfiguration/FBSDKGateKeeperManager.h +++ b/FBSDKCoreKit/FBSDKCoreKit/Internal/ServerConfiguration/FBSDKGateKeeperManager.h @@ -27,6 +27,7 @@ typedef NSString *const FBSDKGateKeeperKey NS_TYPED_EXTENSIBLE_ENUM NS_SWIFT_NAM typedef void (^FBSDKGKManagerBlock)(NSError * _Nullable error) NS_SWIFT_NAME(GKManagerBlock); +NS_SWIFT_NAME(GateKeeperManager) @interface FBSDKGateKeeperManager : NSObject - (instancetype)init NS_UNAVAILABLE; + (instancetype)new NS_UNAVAILABLE; diff --git a/FBSDKCoreKit/FBSDKCoreKit/Internal/ServerConfiguration/FBSDKGateKeeperManager.m b/FBSDKCoreKit/FBSDKCoreKit/Internal/ServerConfiguration/FBSDKGateKeeperManager.m index 6ab38a7dae..e699bf2063 100644 --- a/FBSDKCoreKit/FBSDKCoreKit/Internal/ServerConfiguration/FBSDKGateKeeperManager.m +++ b/FBSDKCoreKit/FBSDKCoreKit/Internal/ServerConfiguration/FBSDKGateKeeperManager.m @@ -16,19 +16,17 @@ // 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 "FBSDKGateKeeperManager.h" -#import - #import +#import + #import "FBSDKAppEventsUtility.h" -#import "FBSDKGraphRequest+Internal.h" #import "FBSDKGraphRequest.h" +#import "FBSDKGraphRequest+Internal.h" #import "FBSDKInternalUtility.h" #import "FBSDKSettings.h" -#import "FBSDKTypeUtility.h" #define FBSDK_GATEKEEPERS_USER_DEFAULTS_KEY @"com.facebook.sdk:GateKeepers%@" @@ -63,64 +61,67 @@ + (BOOL)boolForKey:(NSString *)key defaultValue:(BOOL)defaultValue #pragma clang diagnostic ignored "-Wdeprecated-declarations" + (void)loadGateKeepers:(FBSDKGKManagerBlock)completionBlock { - @synchronized(self) { - NSString *appID = [FBSDKSettings appID]; - if (!appID) { - _gateKeepers = nil; - if (completionBlock != NULL) { - completionBlock(nil); + @try { + @synchronized(self) { + NSString *appID = [FBSDKSettings appID]; + if (!appID) { + _gateKeepers = nil; + if (completionBlock != NULL) { + completionBlock(nil); + } + return; } - return; - } - if (!_gateKeepers) { - // load the defaults - NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults]; - NSString *defaultKey = [NSString stringWithFormat:FBSDK_GATEKEEPERS_USER_DEFAULTS_KEY, - appID]; - NSData *data = [defaults objectForKey:defaultKey]; - if ([data isKindOfClass:[NSData class]]) { - NSDictionary *gatekeeper = [NSKeyedUnarchiver unarchiveObjectWithData:data]; - if (gatekeeper != nil && [gatekeeper isKindOfClass:[NSDictionary class]]) { - _gateKeepers = gatekeeper; + if (!_gateKeepers) { + // load the defaults + NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults]; + NSString *defaultKey = [NSString stringWithFormat:FBSDK_GATEKEEPERS_USER_DEFAULTS_KEY, + appID]; + NSData *data = [defaults objectForKey:defaultKey]; + if ([data isKindOfClass:[NSData class]]) { + NSDictionary *gatekeeper = [NSKeyedUnarchiver unarchiveObjectWithData:data]; + if (gatekeeper != nil && [gatekeeper isKindOfClass:[NSDictionary class]]) { + _gateKeepers = gatekeeper; + } } } - } - // Query the server when the requery is not finished for app start or the timestamp is not valid - if ([self _gateKeeperIsValid]) { - if (completionBlock) { - completionBlock(nil); - } - } else { - [FBSDKTypeUtility array:_completionBlocks addObject:completionBlock]; - if (!_loadingGateKeepers) { - _loadingGateKeepers = YES; - FBSDKGraphRequest *request = [[self class] requestToLoadGateKeepers]; - - // start request with specified timeout instead of the default 180s - FBSDKGraphRequestConnection *requestConnection = [[FBSDKGraphRequestConnection alloc] init]; - requestConnection.timeout = kTimeout; - [requestConnection addRequest:request completionHandler:^(FBSDKGraphRequestConnection *connection, id result, NSError *error) { - _requeryFinishedForAppStart = YES; - [self processLoadRequestResponse:result error:error]; - }]; - [requestConnection start]; + // Query the server when the requery is not finished for app start or the timestamp is not valid + if ([self _gateKeeperIsValid]) { + if (completionBlock) { + completionBlock(nil); + } + } else { + [FBSDKTypeUtility array:_completionBlocks addObject:completionBlock]; + if (!_loadingGateKeepers) { + _loadingGateKeepers = YES; + FBSDKGraphRequest *request = [[self class] requestToLoadGateKeepers]; + + // start request with specified timeout instead of the default 180s + FBSDKGraphRequestConnection *requestConnection = [FBSDKGraphRequestConnection new]; + requestConnection.timeout = kTimeout; + [requestConnection addRequest:request completionHandler:^(FBSDKGraphRequestConnection *connection, id result, NSError *error) { + _requeryFinishedForAppStart = YES; + [self processLoadRequestResponse:result error:error]; + }]; + [requestConnection start]; + } } } - } + } @catch (NSException *exception) {} } + #pragma clang diagnostic pop #pragma mark - Internal Class Methods + (FBSDKGraphRequest *)requestToLoadGateKeepers { - NSString *sdkVersion = [FBSDKSettings sdkVersion]; - - NSDictionary *parameters = @{ @"platform": @"ios" , - @"sdk_version": sdkVersion, - @"fields": FBSDK_GATEKEEPER_APP_GATEKEEPER_FIELDS}; + NSMutableDictionary *parameters = [NSMutableDictionary new]; + [FBSDKTypeUtility dictionary:parameters setObject:@"ios" forKey:@"platform"]; + [FBSDKTypeUtility dictionary:parameters setObject:[FBSDKSettings sdkVersion] forKey:@"sdk_version"]; + [FBSDKTypeUtility dictionary:parameters setObject:FBSDK_GATEKEEPER_APP_GATEKEEPER_FIELDS forKey:@"fields"]; + [FBSDKTypeUtility dictionary:parameters setObject:[UIDevice currentDevice].systemVersion forKey:@"os_version"]; FBSDKGraphRequest *request = [[FBSDKGraphRequest alloc] initWithGraphPath:[NSString stringWithFormat:@"%@/%@", [FBSDKSettings appID], FBSDK_GATEKEEPER_APP_GATEKEEPER_EDGE] @@ -176,6 +177,7 @@ + (void)processLoadRequestResponse:(id)result error:(NSError *)error [self _didProcessGKFromNetwork:error]; } } + #pragma clang diagnostic pop + (void)_didProcessGKFromNetwork:(NSError *)error diff --git a/FBSDKCoreKit/FBSDKCoreKit/Internal/ServerConfiguration/FBSDKServerConfiguration+Internal.h b/FBSDKCoreKit/FBSDKCoreKit/Internal/ServerConfiguration/FBSDKServerConfiguration+Internal.h index 6163c76f13..b5c14cb2b3 100644 --- a/FBSDKCoreKit/FBSDKCoreKit/Internal/ServerConfiguration/FBSDKServerConfiguration+Internal.h +++ b/FBSDKCoreKit/FBSDKCoreKit/Internal/ServerConfiguration/FBSDKServerConfiguration+Internal.h @@ -17,6 +17,7 @@ // CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. #import + #import "FBSDKServerConfiguration.h" FOUNDATION_EXPORT NSString *const FBSDKDialogConfigurationNameDefault; diff --git a/FBSDKCoreKit/FBSDKCoreKit/Internal/ServerConfiguration/FBSDKServerConfiguration.m b/FBSDKCoreKit/FBSDKCoreKit/Internal/ServerConfiguration/FBSDKServerConfiguration.m index 6e0cd79596..1c6d224f0a 100644 --- a/FBSDKCoreKit/FBSDKCoreKit/Internal/ServerConfiguration/FBSDKServerConfiguration.m +++ b/FBSDKCoreKit/FBSDKCoreKit/Internal/ServerConfiguration/FBSDKServerConfiguration.m @@ -18,8 +18,9 @@ #import "FBSDKServerConfiguration.h" #import "FBSDKServerConfiguration+Internal.h" -#import "FBSDKMonitoringConfiguration.h" + #import "FBSDKInternalUtility.h" +#import "FBSDKMonitoringConfiguration.h" // one minute #define DEFAULT_SESSION_TIMEOUT_INTERVAL 60 @@ -82,32 +83,32 @@ @implementation FBSDKServerConfiguration #pragma mark - Object Lifecycle -- (instancetype)initWithAppID:(NSString *)appID - appName:(NSString *)appName - loginTooltipEnabled:(BOOL)loginTooltipEnabled - loginTooltipText:(NSString *)loginTooltipText - defaultShareMode:(NSString*)defaultShareMode - advertisingIDEnabled:(BOOL)advertisingIDEnabled - implicitLoggingEnabled:(BOOL)implicitLoggingEnabled -implicitPurchaseLoggingEnabled:(BOOL)implicitPurchaseLoggingEnabled - codelessEventsEnabled:(BOOL)codelessEventsEnabled - uninstallTrackingEnabled:(BOOL)uninstallTrackingEnabled - dialogConfigurations:(NSDictionary *)dialogConfigurations - dialogFlows:(NSDictionary *)dialogFlows - timestamp:(NSDate *)timestamp - errorConfiguration:(FBSDKErrorConfiguration *)errorConfiguration - sessionTimeoutInterval:(NSTimeInterval) sessionTimeoutInterval - defaults:(BOOL)defaults - loggingToken:(NSString *)loggingToken - smartLoginOptions:(FBSDKServerConfigurationSmartLoginOptions)smartLoginOptions - smartLoginBookmarkIconURL:(NSURL *)smartLoginBookmarkIconURL - smartLoginMenuIconURL:(NSURL *)smartLoginMenuIconURL - updateMessage:(NSString *)updateMessage - eventBindings:(NSArray *)eventBindings - restrictiveParams:(NSDictionary *)restrictiveParams - AAMRules:(NSDictionary *)AAMRules - suggestedEventsSetting:(NSDictionary *)suggestedEventsSetting - monitoringConfiguration:(FBSDKMonitoringConfiguration *)monitoringConfiguration +- (instancetype) initWithAppID:(NSString *)appID + appName:(NSString *)appName + loginTooltipEnabled:(BOOL)loginTooltipEnabled + loginTooltipText:(NSString *)loginTooltipText + defaultShareMode:(NSString *)defaultShareMode + advertisingIDEnabled:(BOOL)advertisingIDEnabled + implicitLoggingEnabled:(BOOL)implicitLoggingEnabled + implicitPurchaseLoggingEnabled:(BOOL)implicitPurchaseLoggingEnabled + codelessEventsEnabled:(BOOL)codelessEventsEnabled + uninstallTrackingEnabled:(BOOL)uninstallTrackingEnabled + dialogConfigurations:(NSDictionary *)dialogConfigurations + dialogFlows:(NSDictionary *)dialogFlows + timestamp:(NSDate *)timestamp + errorConfiguration:(FBSDKErrorConfiguration *)errorConfiguration + sessionTimeoutInterval:(NSTimeInterval)sessionTimeoutInterval + defaults:(BOOL)defaults + loggingToken:(NSString *)loggingToken + smartLoginOptions:(FBSDKServerConfigurationSmartLoginOptions)smartLoginOptions + smartLoginBookmarkIconURL:(NSURL *)smartLoginBookmarkIconURL + smartLoginMenuIconURL:(NSURL *)smartLoginMenuIconURL + updateMessage:(NSString *)updateMessage + eventBindings:(NSArray *)eventBindings + restrictiveParams:(NSDictionary *)restrictiveParams + AAMRules:(NSDictionary *)AAMRules + suggestedEventsSetting:(NSDictionary *)suggestedEventsSetting + monitoringConfiguration:(FBSDKMonitoringConfiguration *)monitoringConfiguration { if ((self = [super init])) { _appID = [appID copy]; @@ -148,20 +149,16 @@ + (FBSDKServerConfiguration *)defaultServerConfigurationForAppID:(NSString *)app // the server to respond. static FBSDKServerConfiguration *_defaultServerConfiguration = nil; if (![_defaultServerConfiguration.appID isEqualToString:appID]) { - // Bypass the native dialog flow for iOS 9+, as it produces a series of additional confirmation dialogs that lead to - // extra friction that is not desirable. - NSOperatingSystemVersion iOS9Version = { .majorVersion = 9, .minorVersion = 0, .patchVersion = 0 }; - BOOL useNativeFlow = ![FBSDKInternalUtility isOSRunTimeVersionAtLeast:iOS9Version]; - // Also enable SFSafariViewController by default. + // Enable SFSafariViewController by default. NSDictionary *dialogFlows = @{ - FBSDKDialogConfigurationNameDefault: @{ - FBSDKDialogConfigurationFeatureUseNativeFlow: @(useNativeFlow), - FBSDKDialogConfigurationFeatureUseSafariViewController: @YES, - }, - FBSDKDialogConfigurationNameMessage: @{ - FBSDKDialogConfigurationFeatureUseNativeFlow: @YES, - }, - }; + FBSDKDialogConfigurationNameDefault : @{ + FBSDKDialogConfigurationFeatureUseNativeFlow : @NO, + FBSDKDialogConfigurationFeatureUseSafariViewController : @YES, + }, + FBSDKDialogConfigurationNameMessage : @{ + FBSDKDialogConfigurationFeatureUseNativeFlow : @YES, + }, + }; _defaultServerConfiguration = [[FBSDKServerConfiguration alloc] initWithAppID:appID appName:nil loginTooltipEnabled:NO @@ -188,7 +185,7 @@ + (FBSDKServerConfiguration *)defaultServerConfigurationForAppID:(NSString *)app AAMRules:nil suggestedEventsSetting:nil monitoringConfiguration:FBSDKMonitoringConfiguration.defaultConfiguration - ]; + ]; } return _defaultServerConfiguration; } @@ -215,12 +212,12 @@ - (BOOL)useSafariViewControllerForDialogName:(NSString *)dialogName - (BOOL)_useFeatureWithKey:(NSString *)key dialogName:(NSString *)dialogName { if ([dialogName isEqualToString:FBSDKDialogConfigurationNameLogin]) { - return ((NSNumber *)(_dialogFlows[dialogName][key] ?: - _dialogFlows[FBSDKDialogConfigurationNameDefault][key])).boolValue; + return ((NSNumber *)(_dialogFlows[dialogName][key] + ?: _dialogFlows[FBSDKDialogConfigurationNameDefault][key])).boolValue; } else { - return ((NSNumber *)(_dialogFlows[dialogName][key] ?: - _dialogFlows[FBSDKDialogConfigurationNameSharing][key] ?: - _dialogFlows[FBSDKDialogConfigurationNameDefault][key])).boolValue; + return ((NSNumber *)(_dialogFlows[dialogName][key] + ?: _dialogFlows[FBSDKDialogConfigurationNameSharing][key] + ?: _dialogFlows[FBSDKDialogConfigurationNameDefault][key])).boolValue; } } @@ -231,7 +228,7 @@ + (BOOL)supportsSecureCoding return YES; } -- (id)initWithCoder:(NSCoder *)decoder +- (instancetype)initWithCoder:(NSCoder *)decoder { NSString *appID = [decoder decodeObjectOfClass:[NSString class] forKey:FBSDK_SERVER_CONFIGURATION_APP_ID_KEY]; NSString *appName = [decoder decodeObjectOfClass:[NSString class] forKey:FBSDK_SERVER_CONFIGURATION_APP_NAME_KEY]; @@ -276,32 +273,32 @@ - (id)initWithCoder:(NSCoder *)decoder NSInteger version = [decoder decodeIntegerForKey:FBSDK_SERVER_CONFIGURATION_VERSION_KEY]; FBSDKMonitoringConfiguration *monitoringConfiguration = [decoder decodeObjectOfClass:FBSDKMonitoringConfiguration.class forKey:FBSDK_SERVER_CONFIGURATION_MONITORING_CONFIGURATION_KEY]; FBSDKServerConfiguration *configuration = [self initWithAppID:appID - appName:appName - loginTooltipEnabled:loginTooltipEnabled - loginTooltipText:loginTooltipText - defaultShareMode:defaultShareMode - advertisingIDEnabled:advertisingIDEnabled - implicitLoggingEnabled:implicitLoggingEnabled - implicitPurchaseLoggingEnabled:implicitPurchaseLoggingEnabled - codelessEventsEnabled:codelessEventsEnabled - uninstallTrackingEnabled:uninstallTrackingEnabled - dialogConfigurations:dialogConfigurations - dialogFlows:dialogFlows - timestamp:timestamp - errorConfiguration:errorConfiguration - sessionTimeoutInterval:sessionTimeoutInterval - defaults:NO - loggingToken:loggingToken - smartLoginOptions:smartLoginOptions - smartLoginBookmarkIconURL:smartLoginBookmarkIconURL - smartLoginMenuIconURL:smartLoginMenuIconURL - updateMessage:updateMessage - eventBindings:eventBindings - restrictiveParams:restrictiveParams - AAMRules:AAMRules - suggestedEventsSetting:suggestedEventsSetting - monitoringConfiguration:monitoringConfiguration - ]; + appName:appName + loginTooltipEnabled:loginTooltipEnabled + loginTooltipText:loginTooltipText + defaultShareMode:defaultShareMode + advertisingIDEnabled:advertisingIDEnabled + implicitLoggingEnabled:implicitLoggingEnabled + implicitPurchaseLoggingEnabled:implicitPurchaseLoggingEnabled + codelessEventsEnabled:codelessEventsEnabled + uninstallTrackingEnabled:uninstallTrackingEnabled + dialogConfigurations:dialogConfigurations + dialogFlows:dialogFlows + timestamp:timestamp + errorConfiguration:errorConfiguration + sessionTimeoutInterval:sessionTimeoutInterval + defaults:NO + loggingToken:loggingToken + smartLoginOptions:smartLoginOptions + smartLoginBookmarkIconURL:smartLoginBookmarkIconURL + smartLoginMenuIconURL:smartLoginMenuIconURL + updateMessage:updateMessage + eventBindings:eventBindings + restrictiveParams:restrictiveParams + AAMRules:AAMRules + suggestedEventsSetting:suggestedEventsSetting + monitoringConfiguration:monitoringConfiguration + ]; configuration->_version = version; return configuration; } @@ -341,7 +338,7 @@ - (void)encodeWithCoder:(NSCoder *)encoder #pragma mark - NSCopying -- (id)copyWithZone:(NSZone *)zone +- (instancetype)copyWithZone:(NSZone *)zone { return self; } diff --git a/FBSDKCoreKit/FBSDKCoreKit/Internal/ServerConfiguration/FBSDKServerConfigurationManager.m b/FBSDKCoreKit/FBSDKCoreKit/Internal/ServerConfiguration/FBSDKServerConfigurationManager.m index 2cd89c1224..bb951827e4 100644 --- a/FBSDKCoreKit/FBSDKCoreKit/Internal/ServerConfiguration/FBSDKServerConfigurationManager.m +++ b/FBSDKCoreKit/FBSDKCoreKit/Internal/ServerConfiguration/FBSDKServerConfigurationManager.m @@ -22,15 +22,14 @@ #import "FBSDKAppEventsUtility.h" #import "FBSDKGateKeeperManager.h" -#import "FBSDKGraphRequest+Internal.h" #import "FBSDKGraphRequest.h" +#import "FBSDKGraphRequest+Internal.h" #import "FBSDKImageDownloader.h" #import "FBSDKInternalUtility.h" #import "FBSDKLogger.h" -#import "FBSDKServerConfiguration+Internal.h" #import "FBSDKServerConfiguration.h" +#import "FBSDKServerConfiguration+Internal.h" #import "FBSDKSettings.h" -#import "FBSDKTypeUtility.h" #define FBSDK_SERVER_CONFIGURATION_USER_DEFAULTS_KEY @"com.facebook.sdk:serverConfiguration%@" @@ -63,16 +62,19 @@ @implementation FBSDKServerConfigurationManager static NSError *_serverConfigurationError; static NSDate *_serverConfigurationErrorTimestamp; static const NSTimeInterval kTimeout = 4.0; -static BOOL _printedUpdateMessage; static BOOL _requeryFinishedForAppStart; +#if DEBUG +static BOOL _printedUpdateMessage; +#endif + typedef NS_OPTIONS(NSUInteger, FBSDKServerConfigurationManagerAppEventsFeatures) { - FBSDKServerConfigurationManagerAppEventsFeaturesNone = 0, - FBSDKServerConfigurationManagerAppEventsFeaturesAdvertisingIDEnabled = 1 << 0, - FBSDKServerConfigurationManagerAppEventsFeaturesImplicitPurchaseLoggingEnabled = 1 << 1, - FBSDKServerConfigurationManagerAppEventsFeaturesCodelessEventsTriggerEnabled = 1 << 5, - FBSDKServerConfigurationManagerAppEventsFeaturesUninstallTrackingEnabled = 1 << 7, + FBSDKServerConfigurationManagerAppEventsFeaturesNone = 0, + FBSDKServerConfigurationManagerAppEventsFeaturesAdvertisingIDEnabled = 1 << 0, + FBSDKServerConfigurationManagerAppEventsFeaturesImplicitPurchaseLoggingEnabled = 1 << 1, + FBSDKServerConfigurationManagerAppEventsFeaturesCodelessEventsTriggerEnabled = 1 << 5, + FBSDKServerConfigurationManagerAppEventsFeaturesUninstallTrackingEnabled = 1 << 7, }; #pragma mark - Public Class Methods @@ -111,159 +113,166 @@ + (FBSDKServerConfiguration *)cachedServerConfiguration #pragma clang diagnostic ignored "-Wdeprecated-declarations" + (void)loadServerConfigurationWithCompletionBlock:(FBSDKServerConfigurationBlock)completionBlock { - void (^loadBlock)(void) = nil; - NSString *appID = [FBSDKSettings appID]; - @synchronized(self) { - // validate the cached configuration has the correct appID - if (_serverConfiguration && ![_serverConfiguration.appID isEqualToString:appID]) { - _serverConfiguration = nil; - _serverConfigurationError = nil; - _serverConfigurationErrorTimestamp = nil; - } + @try { + void (^loadBlock)(void) = nil; + NSString *appID = [FBSDKSettings appID]; + @synchronized(self) { + // validate the cached configuration has the correct appID + if (_serverConfiguration && ![_serverConfiguration.appID isEqualToString:appID]) { + _serverConfiguration = nil; + _serverConfigurationError = nil; + _serverConfigurationErrorTimestamp = nil; + } - // load the configuration from NSUserDefaults - if (!_serverConfiguration) { - // load the defaults - NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults]; - NSString *defaultsKey = [NSString stringWithFormat:FBSDK_SERVER_CONFIGURATION_USER_DEFAULTS_KEY, appID]; - NSData *data = [defaults objectForKey:defaultsKey]; - if ([data isKindOfClass:[NSData class]]) { - // decode the configuration - FBSDKServerConfiguration *serverConfiguration = [NSKeyedUnarchiver unarchiveObjectWithData:data]; - if ([serverConfiguration isKindOfClass:[FBSDKServerConfiguration class]]) { - // ensure that the configuration points to the current appID - if ([serverConfiguration.appID isEqualToString:appID]) { - _serverConfiguration = serverConfiguration; + // load the configuration from NSUserDefaults + if (!_serverConfiguration) { + // load the defaults + NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults]; + NSString *defaultsKey = [NSString stringWithFormat:FBSDK_SERVER_CONFIGURATION_USER_DEFAULTS_KEY, appID]; + NSData *data = [defaults objectForKey:defaultsKey]; + if ([data isKindOfClass:[NSData class]]) { + // decode the configuration + FBSDKServerConfiguration *serverConfiguration = [NSKeyedUnarchiver unarchiveObjectWithData:data]; + if ([serverConfiguration isKindOfClass:[FBSDKServerConfiguration class]]) { + // ensure that the configuration points to the current appID + if ([serverConfiguration.appID isEqualToString:appID]) { + _serverConfiguration = serverConfiguration; + } } } } - } - if (_requeryFinishedForAppStart && - ((_serverConfiguration && [self _serverConfigurationTimestampIsValid:_serverConfiguration.timestamp] && _serverConfiguration.version >= FBSDKServerConfigurationVersion))) { - // we have a valid server configuration, use that - loadBlock = [self _wrapperBlockForLoadBlock:completionBlock]; - } else { - // hold onto the completion block - [FBSDKTypeUtility array:_completionBlocks addObject:[completionBlock copy]]; - - // check if we are already loading - if (!_loadingServerConfiguration) { - // load the configuration from the network - _loadingServerConfiguration = YES; - FBSDKGraphRequest *request = [[self class] requestToLoadServerConfiguration:appID]; - - // start request with specified timeout instead of the default 180s - FBSDKGraphRequestConnection *requestConnection = [[FBSDKGraphRequestConnection alloc] init]; - requestConnection.timeout = kTimeout; - [requestConnection addRequest:request completionHandler:^(FBSDKGraphRequestConnection *connection, id result, NSError *error) { - _requeryFinishedForAppStart = YES; - [self processLoadRequestResponse:result error:error appID:appID]; - }]; - [requestConnection start]; + if (_requeryFinishedForAppStart + && ((_serverConfiguration && [self _serverConfigurationTimestampIsValid:_serverConfiguration.timestamp] && _serverConfiguration.version >= FBSDKServerConfigurationVersion))) { + // we have a valid server configuration, use that + loadBlock = [self _wrapperBlockForLoadBlock:completionBlock]; + } else { + // hold onto the completion block + [FBSDKTypeUtility array:_completionBlocks addObject:[completionBlock copy]]; + + // check if we are already loading + if (!_loadingServerConfiguration) { + // load the configuration from the network + _loadingServerConfiguration = YES; + FBSDKGraphRequest *request = [[self class] requestToLoadServerConfiguration:appID]; + + // start request with specified timeout instead of the default 180s + FBSDKGraphRequestConnection *requestConnection = [FBSDKGraphRequestConnection new]; + requestConnection.timeout = kTimeout; + [requestConnection addRequest:request completionHandler:^(FBSDKGraphRequestConnection *connection, id result, NSError *error) { + _requeryFinishedForAppStart = YES; + [self processLoadRequestResponse:result error:error appID:appID]; + }]; + [requestConnection start]; + } } } - } - if (loadBlock) { - loadBlock(); - } + if (loadBlock) { + loadBlock(); + } - // Fetch app gatekeepers - [FBSDKGateKeeperManager loadGateKeepers:nil]; + // Fetch app gatekeepers + [FBSDKGateKeeperManager loadGateKeepers:nil]; + } @catch (NSException *exception) {} } + #pragma clang diagnostic pop #pragma mark - Internal Class Methods + (void)processLoadRequestResponse:(id)result error:(NSError *)error appID:(NSString *)appID { - if (error) { - [self _didProcessConfigurationFromNetwork:nil appID:appID error:error]; - return; - } + @try { + if (error) { + [self _didProcessConfigurationFromNetwork:nil appID:appID error:error]; + return; + } - NSDictionary *resultDictionary = [FBSDKTypeUtility dictionaryValue:result]; - NSUInteger appEventsFeatures = [FBSDKTypeUtility unsignedIntegerValue:resultDictionary[FBSDK_SERVER_CONFIGURATION_APP_EVENTS_FEATURES_FIELD]]; - BOOL advertisingIDEnabled = (appEventsFeatures & FBSDKServerConfigurationManagerAppEventsFeaturesAdvertisingIDEnabled) != 0; - BOOL implicitPurchaseLoggingEnabled = (appEventsFeatures & FBSDKServerConfigurationManagerAppEventsFeaturesImplicitPurchaseLoggingEnabled) != 0; - BOOL codelessEventsEnabled = (appEventsFeatures & FBSDKServerConfigurationManagerAppEventsFeaturesCodelessEventsTriggerEnabled) != 0; - BOOL uninstallTrackingEnabled = (appEventsFeatures & FBSDKServerConfigurationManagerAppEventsFeaturesUninstallTrackingEnabled) != 0; - NSString *appName = [FBSDKTypeUtility stringValue:resultDictionary[FBSDK_SERVER_CONFIGURATION_APP_NAME_FIELD]]; - BOOL loginTooltipEnabled = [FBSDKTypeUtility boolValue:resultDictionary[FBSDK_SERVER_CONFIGURATION_LOGIN_TOOLTIP_ENABLED_FIELD]]; - NSString *loginTooltipText = [FBSDKTypeUtility stringValue:resultDictionary[FBSDK_SERVER_CONFIGURATION_LOGIN_TOOLTIP_TEXT_FIELD]]; - NSString *defaultShareMode = [FBSDKTypeUtility stringValue:resultDictionary[FBSDK_SERVER_CONFIGURATION_DEFAULT_SHARE_MODE_FIELD]]; - BOOL implicitLoggingEnabled = [FBSDKTypeUtility boolValue:resultDictionary[FBSDK_SERVER_CONFIGURATION_IMPLICIT_LOGGING_ENABLED_FIELD]]; - NSDictionary *dialogConfigurations = [FBSDKTypeUtility dictionaryValue:resultDictionary[FBSDK_SERVER_CONFIGURATION_DIALOG_CONFIGS_FIELD]]; - dialogConfigurations = [self _parseDialogConfigurations:dialogConfigurations]; - NSDictionary *dialogFlows = [FBSDKTypeUtility dictionaryValue:resultDictionary[FBSDK_SERVER_CONFIGURATION_DIALOG_FLOWS_FIELD]]; - FBSDKErrorConfiguration *errorConfiguration = [[FBSDKErrorConfiguration alloc] initWithDictionary:nil]; - [errorConfiguration parseArray:resultDictionary[FBSDK_SERVER_CONFIGURATION_ERROR_CONFIGURATION_FIELD]]; - NSTimeInterval sessionTimeoutInterval = [FBSDKTypeUtility timeIntervalValue:resultDictionary[FBSDK_SERVER_CONFIGURATION_SESSION_TIMEOUT_FIELD]]; - 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]]; - NSString *updateMessage = [FBSDKTypeUtility stringValue:resultDictionary[FBSDK_SERVER_CONFIGURATION_UPDATE_MESSAGE_FIELD]]; - NSArray *eventBindings = [FBSDKTypeUtility arrayValue:resultDictionary[FBSDK_SERVER_CONFIGURATION_EVENT_BINDINGS_FIELD]]; - NSDictionary *restrictiveParams = [FBSDKBasicUtility objectForJSONString:resultDictionary[FBSDK_SERVER_CONFIGURATION_RESTRICTIVE_PARAMS_FIELD] error:nil]; - NSDictionary *AAMRules = [FBSDKBasicUtility objectForJSONString:resultDictionary[FBSDK_SERVER_CONFIGURATION_AAM_RULES_FIELD] error:nil]; - NSDictionary *suggestedEventsSetting = [FBSDKBasicUtility objectForJSONString:resultDictionary[FBSDK_SERVER_CONFIGURATION_SUGGESTED_EVENTS_SETTING_FIELD] error:nil]; - FBSDKMonitoringConfiguration *monitoringConfiguration = [FBSDKMonitoringConfiguration fromDictionary:resultDictionary[FBSDK_SERVER_CONFIGURATION_MONITORING_CONFIG_FIELD]]; - FBSDKServerConfiguration *serverConfiguration = [[FBSDKServerConfiguration alloc] initWithAppID:appID - appName:appName - loginTooltipEnabled:loginTooltipEnabled - loginTooltipText:loginTooltipText - defaultShareMode:defaultShareMode - advertisingIDEnabled:advertisingIDEnabled - implicitLoggingEnabled:implicitLoggingEnabled - implicitPurchaseLoggingEnabled:implicitPurchaseLoggingEnabled - codelessEventsEnabled:codelessEventsEnabled - uninstallTrackingEnabled:uninstallTrackingEnabled - dialogConfigurations:dialogConfigurations - dialogFlows:dialogFlows - timestamp:[NSDate date] - errorConfiguration:errorConfiguration - sessionTimeoutInterval:sessionTimeoutInterval - defaults:NO - loggingToken:loggingToken - smartLoginOptions:smartLoginOptions - smartLoginBookmarkIconURL:smartLoginBookmarkIconURL - smartLoginMenuIconURL:smartLoginMenuIconURL - updateMessage:updateMessage - eventBindings:eventBindings - restrictiveParams:restrictiveParams - AAMRules:AAMRules - suggestedEventsSetting:suggestedEventsSetting - monitoringConfiguration:monitoringConfiguration]; -#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) { + NSDictionary *resultDictionary = [FBSDKTypeUtility dictionaryValue:result]; + NSUInteger appEventsFeatures = [FBSDKTypeUtility unsignedIntegerValue:resultDictionary[FBSDK_SERVER_CONFIGURATION_APP_EVENTS_FEATURES_FIELD]]; + BOOL advertisingIDEnabled = (appEventsFeatures & FBSDKServerConfigurationManagerAppEventsFeaturesAdvertisingIDEnabled) != 0; + BOOL implicitPurchaseLoggingEnabled = (appEventsFeatures & FBSDKServerConfigurationManagerAppEventsFeaturesImplicitPurchaseLoggingEnabled) != 0; + BOOL codelessEventsEnabled = (appEventsFeatures & FBSDKServerConfigurationManagerAppEventsFeaturesCodelessEventsTriggerEnabled) != 0; + BOOL uninstallTrackingEnabled = (appEventsFeatures & FBSDKServerConfigurationManagerAppEventsFeaturesUninstallTrackingEnabled) != 0; + NSString *appName = [FBSDKTypeUtility stringValue:resultDictionary[FBSDK_SERVER_CONFIGURATION_APP_NAME_FIELD]]; + BOOL loginTooltipEnabled = [FBSDKTypeUtility boolValue:resultDictionary[FBSDK_SERVER_CONFIGURATION_LOGIN_TOOLTIP_ENABLED_FIELD]]; + NSString *loginTooltipText = [FBSDKTypeUtility stringValue:resultDictionary[FBSDK_SERVER_CONFIGURATION_LOGIN_TOOLTIP_TEXT_FIELD]]; + NSString *defaultShareMode = [FBSDKTypeUtility stringValue:resultDictionary[FBSDK_SERVER_CONFIGURATION_DEFAULT_SHARE_MODE_FIELD]]; + BOOL implicitLoggingEnabled = [FBSDKTypeUtility boolValue:resultDictionary[FBSDK_SERVER_CONFIGURATION_IMPLICIT_LOGGING_ENABLED_FIELD]]; + NSDictionary *dialogConfigurations = [FBSDKTypeUtility dictionaryValue:resultDictionary[FBSDK_SERVER_CONFIGURATION_DIALOG_CONFIGS_FIELD]]; + dialogConfigurations = [self _parseDialogConfigurations:dialogConfigurations]; + NSDictionary *dialogFlows = [FBSDKTypeUtility dictionaryValue:resultDictionary[FBSDK_SERVER_CONFIGURATION_DIALOG_FLOWS_FIELD]]; + FBSDKErrorConfiguration *errorConfiguration = [[FBSDKErrorConfiguration alloc] initWithDictionary:nil]; + [errorConfiguration parseArray:resultDictionary[FBSDK_SERVER_CONFIGURATION_ERROR_CONFIGURATION_FIELD]]; + NSTimeInterval sessionTimeoutInterval = [FBSDKTypeUtility timeIntervalValue:resultDictionary[FBSDK_SERVER_CONFIGURATION_SESSION_TIMEOUT_FIELD]]; + 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]]; + NSString *updateMessage = [FBSDKTypeUtility stringValue:resultDictionary[FBSDK_SERVER_CONFIGURATION_UPDATE_MESSAGE_FIELD]]; + NSArray *eventBindings = [FBSDKTypeUtility arrayValue:resultDictionary[FBSDK_SERVER_CONFIGURATION_EVENT_BINDINGS_FIELD]]; + NSDictionary *restrictiveParams = [FBSDKBasicUtility objectForJSONString:resultDictionary[FBSDK_SERVER_CONFIGURATION_RESTRICTIVE_PARAMS_FIELD] error:nil]; + NSDictionary *AAMRules = [FBSDKBasicUtility objectForJSONString:resultDictionary[FBSDK_SERVER_CONFIGURATION_AAM_RULES_FIELD] error:nil]; + NSDictionary *suggestedEventsSetting = [FBSDKBasicUtility objectForJSONString:resultDictionary[FBSDK_SERVER_CONFIGURATION_SUGGESTED_EVENTS_SETTING_FIELD] error:nil]; + FBSDKMonitoringConfiguration *monitoringConfiguration = [FBSDKMonitoringConfiguration fromDictionary:resultDictionary[FBSDK_SERVER_CONFIGURATION_MONITORING_CONFIG_FIELD]]; + FBSDKServerConfiguration *serverConfiguration = [[FBSDKServerConfiguration alloc] initWithAppID:appID + appName:appName + loginTooltipEnabled:loginTooltipEnabled + loginTooltipText:loginTooltipText + defaultShareMode:defaultShareMode + advertisingIDEnabled:advertisingIDEnabled + implicitLoggingEnabled:implicitLoggingEnabled + implicitPurchaseLoggingEnabled:implicitPurchaseLoggingEnabled + codelessEventsEnabled:codelessEventsEnabled + uninstallTrackingEnabled:uninstallTrackingEnabled + dialogConfigurations:dialogConfigurations + dialogFlows:dialogFlows + timestamp:[NSDate date] + errorConfiguration:errorConfiguration + sessionTimeoutInterval:sessionTimeoutInterval + defaults:NO + loggingToken:loggingToken + smartLoginOptions:smartLoginOptions + smartLoginBookmarkIconURL:smartLoginBookmarkIconURL + smartLoginMenuIconURL:smartLoginMenuIconURL + updateMessage:updateMessage + eventBindings:eventBindings + restrictiveParams:restrictiveParams + AAMRules:AAMRules + suggestedEventsSetting:suggestedEventsSetting + monitoringConfiguration:monitoringConfiguration]; + #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:nil]; [[FBSDKImageDownloader sharedInstance] downloadImageWithURL:serverConfiguration.smartLoginMenuIconURL ttl:kSmartLoginIconsTTL completion:nil]; - } -#endif - [self _didProcessConfigurationFromNetwork:serverConfiguration appID:appID error:nil]; + } + #endif + [self _didProcessConfigurationFromNetwork:serverConfiguration appID:appID error:nil]; + } @catch (NSException *exception) {} } + (FBSDKGraphRequest *)requestToLoadServerConfiguration:(NSString *)appID { NSOperatingSystemVersion operatingSystemVersion = [FBSDKInternalUtility operatingSystemVersion]; - NSString *dialogFlowsField = [NSString stringWithFormat:@"%@.os_version(%ti.%ti.%ti)", + NSString *osVersion = [NSString stringWithFormat:@"%ti.%ti.%ti", + operatingSystemVersion.majorVersion, + operatingSystemVersion.minorVersion, + operatingSystemVersion.patchVersion]; + NSString *dialogFlowsField = [NSString stringWithFormat:@"%@.os_version(%@)", FBSDK_SERVER_CONFIGURATION_DIALOG_FLOWS_FIELD, - operatingSystemVersion.majorVersion, - operatingSystemVersion.minorVersion, - operatingSystemVersion.patchVersion]; + osVersion]; NSArray *fields = @[FBSDK_SERVER_CONFIGURATION_APP_EVENTS_FEATURES_FIELD, FBSDK_SERVER_CONFIGURATION_APP_NAME_FIELD, FBSDK_SERVER_CONFIGURATION_DEFAULT_SHARE_MODE_FIELD, @@ -278,19 +287,20 @@ + (FBSDKGraphRequest *)requestToLoadServerConfiguration:(NSString *)appID FBSDK_SERVER_CONFIGURATION_RESTRICTIVE_PARAMS_FIELD, FBSDK_SERVER_CONFIGURATION_AAM_RULES_FIELD, FBSDK_SERVER_CONFIGURATION_SUGGESTED_EVENTS_SETTING_FIELD -#if !TARGET_OS_TV - ,FBSDK_SERVER_CONFIGURATION_EVENT_BINDINGS_FIELD -#endif -#ifdef DEBUG - ,FBSDK_SERVER_CONFIGURATION_UPDATE_MESSAGE_FIELD -#endif -#if TARGET_OS_TV - ,FBSDK_SERVER_CONFIGURATION_SMART_LOGIN_OPTIONS_FIELD, + #if !TARGET_OS_TV + , FBSDK_SERVER_CONFIGURATION_EVENT_BINDINGS_FIELD + #endif + #ifdef DEBUG + , FBSDK_SERVER_CONFIGURATION_UPDATE_MESSAGE_FIELD + #endif + #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:@","]}; + #endif + ]; + NSDictionary *parameters = @{ @"fields" : [fields componentsJoinedByString:@","], + @"os_version" : osVersion}; FBSDKGraphRequest *request = [[FBSDKGraphRequest alloc] initWithGraphPath:appID parameters:parameters @@ -327,27 +337,23 @@ + (void)_didProcessConfigurationFromNetwork:(FBSDKServerConfiguration *)serverCo _serverConfigurationError = nil; _serverConfigurationErrorTimestamp = nil; -#ifdef DEBUG + #ifdef DEBUG NSString *updateMessage = _serverConfiguration.updateMessage; if (updateMessage && updateMessage.length > 0 && !_printedUpdateMessage) { _printedUpdateMessage = YES; [FBSDKLogger singleShotLogEntry:FBSDKLoggingBehaviorInformational logEntry:updateMessage]; } -#endif - - if (!_printedUpdateMessage) { - _printedUpdateMessage = _printedUpdateMessage; - } + #endif } // update the cached copy in NSUserDefaults NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults]; NSString *defaultsKey = [NSString stringWithFormat:FBSDK_SERVER_CONFIGURATION_USER_DEFAULTS_KEY, appID]; if (serverConfiguration) { -#pragma clang diagnostic push -#pragma clang diagnostic ignored "-Wdeprecated-declarations" + #pragma clang diagnostic push + #pragma clang diagnostic ignored "-Wdeprecated-declarations" NSData *data = [NSKeyedArchiver archivedDataWithRootObject:serverConfiguration]; -#pragma clang diagnostic pop + #pragma clang diagnostic pop [defaults setObject:data forKey:defaultsKey]; } @@ -377,8 +383,8 @@ + (NSDictionary *)_parseDialogConfigurations:(NSDictionary *)dictionary NSURL *URL = [FBSDKTypeUtility URLValue:dialogConfigurationDictionary[@"url"]]; NSArray *appVersions = [FBSDKTypeUtility arrayValue:dialogConfigurationDictionary[@"versions"]]; [FBSDKTypeUtility dictionary:dialogConfigurations setObject:[[FBSDKDialogConfiguration alloc] initWithName:name - URL:URL - appVersions:appVersions] forKey:name]; + URL:URL + appVersions:appVersions] forKey:name]; } } } diff --git a/FBSDKCoreKit/FBSDKCoreKit/Internal/TokenCaching/FBSDKKeychainStore.m b/FBSDKCoreKit/FBSDKCoreKit/Internal/TokenCaching/FBSDKKeychainStore.m index 1efb30b2c3..7099c7aa5d 100644 --- a/FBSDKCoreKit/FBSDKCoreKit/Internal/TokenCaching/FBSDKKeychainStore.m +++ b/FBSDKCoreKit/FBSDKCoreKit/Internal/TokenCaching/FBSDKKeychainStore.m @@ -19,9 +19,9 @@ #import "FBSDKKeychainStore.h" #import "FBSDKDynamicFrameworkLoader.h" +#import "FBSDKInternalUtility.h" #import "FBSDKLogger.h" #import "FBSDKSettings.h" -#import "FBSDKTypeUtility.h" @implementation FBSDKKeychainStore @@ -58,6 +58,7 @@ - (NSDictionary *)dictionaryForKey:(NSString *)key return dict; } + #pragma clang diagnostic pop - (BOOL)setString:(NSString *)value forKey:(NSString *)key accessibility:(CFTypeRef)accessibility @@ -96,15 +97,15 @@ - (BOOL)setData:(NSData *)value forKey:(NSString *)key accessibility:(CFTypeRef) NSMutableDictionary *attributesToUpdate = [NSMutableDictionary dictionary]; [attributesToUpdate setObject:value forKey:[FBSDKDynamicFrameworkLoader loadkSecValueData]]; - status = fbsdkdfl_SecItemUpdate((__bridge CFDictionaryRef)query, (__bridge CFDictionaryRef)attributesToUpdate); - if (status == errSecItemNotFound) { -#if !TARGET_OS_TV - if (@available(macOS 10.9, iOS 8, *)) { - if (accessibility) { - [query setObject:(__bridge id)(accessibility) forKey:[FBSDKDynamicFrameworkLoader loadkSecAttrAccessible]]; - } + status = fbsdkdfl_SecItemUpdate((__bridge CFDictionaryRef)query, (__bridge CFDictionaryRef)attributesToUpdate); + if (status == errSecItemNotFound) { + #if !TARGET_OS_TV + if (@available(macOS 10.9, iOS 8, *)) { + if (accessibility) { + [query setObject:(__bridge id)(accessibility) forKey:[FBSDKDynamicFrameworkLoader loadkSecAttrAccessible]]; } -#endif + } + #endif [query setObject:value forKey:[FBSDKDynamicFrameworkLoader loadkSecValueData]]; status = fbsdkdfl_SecItemAdd((__bridge CFDictionaryRef)query, NULL); diff --git a/FBSDKCoreKit/FBSDKCoreKit/Internal/TokenCaching/FBSDKKeychainStoreViaBundleID.m b/FBSDKCoreKit/FBSDKCoreKit/Internal/TokenCaching/FBSDKKeychainStoreViaBundleID.m index 1aa2f5f0e0..dcd38e9a3f 100644 --- a/FBSDKCoreKit/FBSDKCoreKit/Internal/TokenCaching/FBSDKKeychainStoreViaBundleID.m +++ b/FBSDKCoreKit/FBSDKCoreKit/Internal/TokenCaching/FBSDKKeychainStoreViaBundleID.m @@ -33,7 +33,7 @@ - (instancetype)initWithService:(NSString *)service accessGroup:(NSString *)acce return [self init]; } -- (NSMutableDictionary*)queryForKey:(NSString *)key +- (NSMutableDictionary *)queryForKey:(NSString *)key { NSMutableDictionary *query = [NSMutableDictionary dictionary]; [FBSDKTypeUtility dictionary:query setObject:(__bridge id)([FBSDKDynamicFrameworkLoader loadkSecClassGenericPassword]) forKey:(__bridge id)[FBSDKDynamicFrameworkLoader loadkSecClass]]; diff --git a/FBSDKCoreKit/FBSDKCoreKit/Internal/TokenCaching/FBSDKAccessTokenCache.h b/FBSDKCoreKit/FBSDKCoreKit/Internal/TokenCaching/FBSDKTokenCache.h similarity index 90% rename from FBSDKCoreKit/FBSDKCoreKit/Internal/TokenCaching/FBSDKAccessTokenCache.h rename to FBSDKCoreKit/FBSDKCoreKit/Internal/TokenCaching/FBSDKTokenCache.h index 2176928b4e..13ff37d373 100644 --- a/FBSDKCoreKit/FBSDKCoreKit/Internal/TokenCaching/FBSDKAccessTokenCache.h +++ b/FBSDKCoreKit/FBSDKCoreKit/Internal/TokenCaching/FBSDKTokenCache.h @@ -24,9 +24,9 @@ #import #endif -#import "FBSDKAccessTokenCaching.h" +#import "FBSDKTokenCaching.h" -NS_SWIFT_NAME(AccessTokenCache) -@interface FBSDKAccessTokenCache : NSObject +NS_SWIFT_NAME(TokenCache) +@interface FBSDKTokenCache : NSObject @end diff --git a/FBSDKCoreKit/FBSDKCoreKit/Internal/TokenCaching/FBSDKAccessTokenCache.m b/FBSDKCoreKit/FBSDKCoreKit/Internal/TokenCaching/FBSDKTokenCache.m similarity index 54% rename from FBSDKCoreKit/FBSDKCoreKit/Internal/TokenCaching/FBSDKAccessTokenCache.m rename to FBSDKCoreKit/FBSDKCoreKit/Internal/TokenCaching/FBSDKTokenCache.m index 2ae910e4f7..f5c2467339 100644 --- a/FBSDKCoreKit/FBSDKCoreKit/Internal/TokenCaching/FBSDKAccessTokenCache.m +++ b/FBSDKCoreKit/FBSDKCoreKit/Internal/TokenCaching/FBSDKTokenCache.m @@ -16,7 +16,7 @@ // 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 "FBSDKAccessTokenCache.h" +#import "FBSDKTokenCache.h" #import "FBSDKDynamicFrameworkLoader.h" #import "FBSDKInternalUtility.h" @@ -24,10 +24,14 @@ static NSString *const kFBSDKAccessTokenUserDefaultsKey = @"com.facebook.sdk.v4.FBSDKAccessTokenInformationKey"; static NSString *const kFBSDKAccessTokenKeychainKey = @"com.facebook.sdk.v4.FBSDKAccessTokenInformationKeychainKey"; -static NSString *const kFBSDKAccessTokenUUIDKey = @"tokenUUID"; -static NSString *const kFBSDKAccessTokenEncodedKey = @"tokenEncoded"; -@implementation FBSDKAccessTokenCache +static NSString *const kFBSDKAuthenticationTokenUserDefaultsKey = @"com.facebook.sdk.v9.FBSDKAuthenticationTokenInformationKey"; +static NSString *const kFBSDKAuthenticationTokenKeychainKey = @"com.facebook.sdk.v9.FBSDKAuthenticationTokenInformationKeychainKey"; + +static NSString *const kFBSDKTokenUUIDKey = @"tokenUUID"; +static NSString *const kFBSDKTokenEncodedKey = @"tokenEncoded"; + +@implementation FBSDKTokenCache { FBSDKKeychainStore *_keychainStore; } @@ -43,32 +47,33 @@ - (instancetype)init #pragma clang diagnostic push #pragma clang diagnostic ignored "-Wdeprecated-declarations" + - (FBSDKAccessToken *)accessToken { NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults]; NSString *uuid = [defaults objectForKey:kFBSDKAccessTokenUserDefaultsKey]; NSDictionary *dict = [_keychainStore dictionaryForKey:kFBSDKAccessTokenKeychainKey]; - if ([dict[kFBSDKAccessTokenUUIDKey] isKindOfClass:[NSString class]]) { + if ([dict[kFBSDKTokenUUIDKey] isKindOfClass:[NSString class]]) { // there is a bug while running on simulator that the uuid stored in dict can be NSData, // do a type check to make sure it is NSString - if ([dict[kFBSDKAccessTokenUUIDKey] isEqualToString:uuid]) { - id tokenData = dict[kFBSDKAccessTokenEncodedKey]; + if ([dict[kFBSDKTokenUUIDKey] isEqualToString:uuid]) { + id tokenData = dict[kFBSDKTokenEncodedKey]; if ([tokenData isKindOfClass:[NSData class]]) { return [NSKeyedUnarchiver unarchiveObjectWithData:tokenData]; } } } // if the uuid doesn't match (including if there is no uuid in defaults which means uninstalled case) - // clear the keychain and return nil. - [self clearCache]; + // clear the access token cache and return nil. + [self clearAccessTokenCache]; return nil; } - (void)setAccessToken:(FBSDKAccessToken *)token { if (!token) { - [self clearCache]; + [self clearAccessTokenCache]; return; } NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults]; @@ -76,21 +81,76 @@ - (void)setAccessToken:(FBSDKAccessToken *)token if (!uuid) { uuid = [NSUUID UUID].UUIDString; [defaults setObject:uuid forKey:kFBSDKAccessTokenUserDefaultsKey]; - [defaults synchronize]; } NSData *tokenData = [NSKeyedArchiver archivedDataWithRootObject:token]; NSDictionary *dict = @{ - kFBSDKAccessTokenUUIDKey : uuid, - kFBSDKAccessTokenEncodedKey : tokenData - }; + kFBSDKTokenUUIDKey : uuid, + kFBSDKTokenEncodedKey : tokenData + }; [_keychainStore setDictionary:dict forKey:kFBSDKAccessTokenKeychainKey accessibility:[FBSDKDynamicFrameworkLoader loadkSecAttrAccessibleAfterFirstUnlockThisDeviceOnly]]; } + +- (FBSDKAuthenticationToken *)authenticationToken +{ + NSUserDefaults *defaults = NSUserDefaults.standardUserDefaults; + NSString *uuid = [defaults objectForKey:kFBSDKAuthenticationTokenUserDefaultsKey]; + + NSDictionary *dict = [_keychainStore dictionaryForKey:kFBSDKAuthenticationTokenKeychainKey]; + if ([dict[kFBSDKTokenUUIDKey] isKindOfClass:[NSString class]]) { + // there is a bug while running on simulator that the uuid stored in dict can be NSData, + // do a type check to make sure it is NSString + if ([dict[kFBSDKTokenUUIDKey] isEqualToString:uuid]) { + id tokenData = dict[kFBSDKTokenEncodedKey]; + if ([tokenData isKindOfClass:[NSData class]]) { + return [NSKeyedUnarchiver unarchiveObjectWithData:tokenData]; + } + } + } + // if the uuid doesn't match (including if there is no uuid in defaults which means uninstalled case) + // clear the authentication token cache and return nil. + [self clearAuthenticationTokenCache]; + return nil; +} + +- (void)setAuthenticationToken:(FBSDKAuthenticationToken *)token +{ + if (!token) { + [self clearAuthenticationTokenCache]; + return; + } + NSUserDefaults *defaults = NSUserDefaults.standardUserDefaults; + NSString *uuid = [defaults objectForKey:kFBSDKAuthenticationTokenUserDefaultsKey]; + if (!uuid) { + uuid = NSUUID.UUID.UUIDString; + [defaults setObject:uuid forKey:kFBSDKAuthenticationTokenUserDefaultsKey]; + } + NSData *tokenData = [NSKeyedArchiver archivedDataWithRootObject:token]; + NSDictionary *dict = @{ + kFBSDKTokenUUIDKey : uuid, + kFBSDKTokenEncodedKey : tokenData + }; + + [_keychainStore setDictionary:dict + forKey:kFBSDKAuthenticationTokenKeychainKey + accessibility:[FBSDKDynamicFrameworkLoader loadkSecAttrAccessibleAfterFirstUnlockThisDeviceOnly]]; +} + #pragma clang diagnostic pop -- (void)clearCache +- (void)clearAuthenticationTokenCache +{ + [_keychainStore setDictionary:nil + forKey:kFBSDKAuthenticationTokenKeychainKey + accessibility:NULL]; + NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults]; + [defaults removeObjectForKey:kFBSDKAuthenticationTokenUserDefaultsKey]; + [defaults synchronize]; +} + +- (void)clearAccessTokenCache { [_keychainStore setDictionary:nil forKey:kFBSDKAccessTokenKeychainKey @@ -99,4 +159,5 @@ - (void)clearCache [defaults removeObjectForKey:kFBSDKAccessTokenUserDefaultsKey]; [defaults synchronize]; } + @end diff --git a/FBSDKCoreKit/FBSDKCoreKit/Internal/TokenCaching/FBSDKAccessTokenCaching.h b/FBSDKCoreKit/FBSDKCoreKit/Internal/TokenCaching/FBSDKTokenCaching.h similarity index 87% rename from FBSDKCoreKit/FBSDKCoreKit/Internal/TokenCaching/FBSDKAccessTokenCaching.h rename to FBSDKCoreKit/FBSDKCoreKit/Internal/TokenCaching/FBSDKTokenCaching.h index e5595db760..f877469d6f 100644 --- a/FBSDKCoreKit/FBSDKCoreKit/Internal/TokenCaching/FBSDKAccessTokenCaching.h +++ b/FBSDKCoreKit/FBSDKCoreKit/Internal/TokenCaching/FBSDKTokenCaching.h @@ -19,12 +19,12 @@ #import @class FBSDKAccessToken; +@class FBSDKAuthenticationToken; -NS_SWIFT_NAME(AccessTokenCaching) -@protocol FBSDKAccessTokenCaching +NS_SWIFT_NAME(TokenCaching) +@protocol FBSDKTokenCaching @property (nonatomic, copy) FBSDKAccessToken *accessToken; - -- (void)clearCache; +@property (nonatomic, copy) FBSDKAuthenticationToken *authenticationToken; @end diff --git a/FBSDKCoreKit/FBSDKCoreKit/Internal/UI/FBSDKCloseIcon.m b/FBSDKCoreKit/FBSDKCoreKit/Internal/UI/FBSDKCloseIcon.m index 244efcaad8..46f22ac705 100644 --- a/FBSDKCoreKit/FBSDKCoreKit/Internal/UI/FBSDKCloseIcon.m +++ b/FBSDKCoreKit/FBSDKCoreKit/Internal/UI/FBSDKCloseIcon.m @@ -20,15 +20,25 @@ #if !TARGET_OS_TV -#import "FBSDKCloseIcon.h" + #import "FBSDKCloseIcon.h" @implementation FBSDKCloseIcon -#pragma mark - Public API + #pragma mark - Public API - (UIImage *)imageWithSize:(CGSize)size { - CGFloat scale = [UIScreen mainScreen].scale; + return [self imageWithSize:size + primaryColor:UIColor.whiteColor + secondaryColor:UIColor.blackColor + scale:UIScreen.mainScreen.scale]; +} + +- (UIImage *)imageWithSize:(CGSize)size + primaryColor:(UIColor *)primaryColor + secondaryColor:(UIColor *)secondaryColor + scale:(CGFloat)scale +{ UIGraphicsBeginImageContextWithOptions(size, NO, scale); CGContextRef context = UIGraphicsGetCurrentContext(); @@ -40,11 +50,11 @@ - (UIImage *)imageWithSize:(CGSize)size // shadow rect = CGRectIntegral(CGRectInset(rect, step, step)); NSArray *colors = @[ - (__bridge id)[UIColor colorWithWhite:0.0 alpha:0.7].CGColor, - (__bridge id)[UIColor colorWithWhite:0.0 alpha:0.3].CGColor, - (__bridge id)[UIColor colorWithWhite:0.0 alpha:0.1].CGColor, - (__bridge id)[UIColor colorWithWhite:0.0 alpha:0.0].CGColor, - ]; + (__bridge id)[UIColor colorWithWhite:0.0 alpha:0.7].CGColor, + (__bridge id)[UIColor colorWithWhite:0.0 alpha:0.3].CGColor, + (__bridge id)[UIColor colorWithWhite:0.0 alpha:0.1].CGColor, + (__bridge id)[UIColor colorWithWhite:0.0 alpha:0.0].CGColor, + ]; CGFloat locations[4] = { 0.70, 0.80, @@ -60,12 +70,12 @@ - (UIImage *)imageWithSize:(CGSize)size // outer circle rect = CGRectIntegral(CGRectInset(rect, step, step)); - [[UIColor whiteColor] setFill]; + [primaryColor setFill]; CGContextFillEllipseInRect(context, rect); // inner circle rect = CGRectIntegral(CGRectInset(rect, step, step)); - [[UIColor blackColor] setFill]; + [secondaryColor setFill]; CGContextFillEllipseInRect(context, rect); // cross @@ -73,7 +83,7 @@ - (UIImage *)imageWithSize:(CGSize)size CGFloat lineWidth = step * 5 / 4; rect.origin.y = CGRectGetMidY(rect) - lineWidth / 2; rect.size.height = lineWidth; - [[UIColor whiteColor] setFill]; + [primaryColor setFill]; CGContextTranslateCTM(context, size.width / 2, size.height / 2); CGContextRotateCTM(context, M_PI_4); CGContextTranslateCTM(context, -size.width / 2, -size.height / 2); diff --git a/FBSDKCoreKit/FBSDKCoreKit/Internal/UI/FBSDKColor.h b/FBSDKCoreKit/FBSDKCoreKit/Internal/UI/FBSDKColor.h index f18c98db8f..565ea0f18d 100644 --- a/FBSDKCoreKit/FBSDKCoreKit/Internal/UI/FBSDKColor.h +++ b/FBSDKCoreKit/FBSDKCoreKit/Internal/UI/FBSDKColor.h @@ -20,7 +20,7 @@ #if !TARGET_OS_TV -#import + #import UIColor *FBSDKUIColorWithRGBA(uint8_t r, uint8_t g, uint8_t b, CGFloat a); UIColor *FBSDKUIColorWithRGB(uint8_t r, uint8_t g, uint8_t b); diff --git a/FBSDKCoreKit/FBSDKCoreKit/Internal/UI/FBSDKColor.m b/FBSDKCoreKit/FBSDKCoreKit/Internal/UI/FBSDKColor.m index 2d27478f41..9638d5948b 100644 --- a/FBSDKCoreKit/FBSDKCoreKit/Internal/UI/FBSDKColor.m +++ b/FBSDKCoreKit/FBSDKCoreKit/Internal/UI/FBSDKColor.m @@ -20,18 +20,18 @@ #if !TARGET_OS_TV -#import "FBSDKColor.h" + #import "FBSDKColor.h" static const CGFloat kFBRGBMax = 255.0; UIColor *FBSDKUIColorWithRGBA(uint8_t r, uint8_t g, uint8_t b, CGFloat a) { - return [UIColor colorWithRed:(r / kFBRGBMax) green:(g / kFBRGBMax) blue:(b / kFBRGBMax) alpha:a]; + return [UIColor colorWithRed:(r / kFBRGBMax) green:(g / kFBRGBMax) blue:(b / kFBRGBMax) alpha:a]; } UIColor *FBSDKUIColorWithRGB(uint8_t r, uint8_t g, uint8_t b) { - return FBSDKUIColorWithRGBA(r, g, b, 1.0); + return FBSDKUIColorWithRGBA(r, g, b, 1.0); } #endif diff --git a/FBSDKCoreKit/FBSDKCoreKit/Internal/UI/FBSDKMaleSilhouetteIcon.h b/FBSDKCoreKit/FBSDKCoreKit/Internal/UI/FBSDKHumanSilhouetteIcon.h similarity index 93% rename from FBSDKCoreKit/FBSDKCoreKit/Internal/UI/FBSDKMaleSilhouetteIcon.h rename to FBSDKCoreKit/FBSDKCoreKit/Internal/UI/FBSDKHumanSilhouetteIcon.h index cad25b6bde..20f9b7a3eb 100644 --- a/FBSDKCoreKit/FBSDKCoreKit/Internal/UI/FBSDKMaleSilhouetteIcon.h +++ b/FBSDKCoreKit/FBSDKCoreKit/Internal/UI/FBSDKHumanSilhouetteIcon.h @@ -24,8 +24,8 @@ #import "FBSDKIcon.h" -NS_SWIFT_NAME(FBMaleSilhouetteIcon) -@interface FBSDKMaleSilhouetteIcon : FBSDKIcon +NS_SWIFT_NAME(HumanSilhouetteIcon) +@interface FBSDKHumanSilhouetteIcon : FBSDKIcon @end diff --git a/FBSDKCoreKit/FBSDKCoreKit/Internal/UI/FBSDKMaleSilhouetteIcon.m b/FBSDKCoreKit/FBSDKCoreKit/Internal/UI/FBSDKHumanSilhouetteIcon.m similarity index 97% rename from FBSDKCoreKit/FBSDKCoreKit/Internal/UI/FBSDKMaleSilhouetteIcon.m rename to FBSDKCoreKit/FBSDKCoreKit/Internal/UI/FBSDKHumanSilhouetteIcon.m index bdd5912524..7a7fde0fcc 100644 --- a/FBSDKCoreKit/FBSDKCoreKit/Internal/UI/FBSDKMaleSilhouetteIcon.m +++ b/FBSDKCoreKit/FBSDKCoreKit/Internal/UI/FBSDKHumanSilhouetteIcon.m @@ -20,9 +20,9 @@ #if !TARGET_OS_TV -#import "FBSDKMaleSilhouetteIcon.h" + #import "FBSDKHumanSilhouetteIcon.h" -@implementation FBSDKMaleSilhouetteIcon +@implementation FBSDKHumanSilhouetteIcon - (CGPathRef)pathWithSize:(CGSize)size { diff --git a/FBSDKCoreKit/FBSDKCoreKit/Internal/UI/FBSDKIcon.h b/FBSDKCoreKit/FBSDKCoreKit/Internal/UI/FBSDKIcon.h index ca78c86442..a51d899d59 100644 --- a/FBSDKCoreKit/FBSDKCoreKit/Internal/UI/FBSDKIcon.h +++ b/FBSDKCoreKit/FBSDKCoreKit/Internal/UI/FBSDKIcon.h @@ -19,13 +19,20 @@ #import NS_SWIFT_NAME(Icon) + @interface FBSDKIcon : NSObject -- (instancetype)initWithColor:(UIColor *)color NS_DESIGNATED_INITIALIZER; +- (UIImage *)imageWithSize:(CGSize)size +NS_SWIFT_NAME(image(size:)); + +- (UIImage *)imageWithSize:(CGSize)size scale:(CGFloat)scale +NS_SWIFT_NAME(image(size:scale:)); -@property (nonatomic, strong, readonly) UIColor *color; +- (UIImage *)imageWithSize:(CGSize)size color:(UIColor *)color +NS_SWIFT_NAME(image(size:color:)); -- (UIImage *)imageWithSize:(CGSize)size; +- (UIImage *)imageWithSize:(CGSize)size scale:(CGFloat)scale color:(UIColor *)color +NS_SWIFT_NAME(image(size:scale:color:)); - (CGPathRef)pathWithSize:(CGSize)size; diff --git a/FBSDKCoreKit/FBSDKCoreKit/Internal/UI/FBSDKIcon.m b/FBSDKCoreKit/FBSDKCoreKit/Internal/UI/FBSDKIcon.m index b46a8d6c1b..87396ed19d 100644 --- a/FBSDKCoreKit/FBSDKCoreKit/Internal/UI/FBSDKIcon.m +++ b/FBSDKCoreKit/FBSDKCoreKit/Internal/UI/FBSDKIcon.m @@ -20,34 +20,33 @@ @implementation FBSDKIcon -#pragma mark - Object Lifecycle +#pragma mark - Public API -- (instancetype)initWithColor:(UIColor *)color +- (UIImage *)imageWithSize:(CGSize)size { - if ((self = [super init])) { - _color = [color copy]; - } - return self; + return [self imageWithSize:size scale:UIScreen.mainScreen.scale color:UIColor.whiteColor]; } -- (instancetype)init +- (UIImage *)imageWithSize:(CGSize)size scale:(CGFloat)scale { - return [self initWithColor:[UIColor whiteColor]]; + return [self imageWithSize:size scale:scale color:UIColor.whiteColor]; } -#pragma mark - Public API +- (UIImage *)imageWithSize:(CGSize)size color:(UIColor *)color +{ + return [self imageWithSize:size scale:UIScreen.mainScreen.scale color:color]; +} -- (UIImage *)imageWithSize:(CGSize)size +- (UIImage *)imageWithSize:(CGSize)size scale:(CGFloat)scale color:(UIColor *)color { if ((size.width == 0) || (size.height == 0)) { return nil; } - CGFloat scale = [UIScreen mainScreen].scale; UIGraphicsBeginImageContextWithOptions(size, NO, scale); CGContextRef context = UIGraphicsGetCurrentContext(); CGPathRef path = [self pathWithSize:size]; CGContextAddPath(context, path); - CGContextSetFillColorWithColor(context, self.color.CGColor); + CGContextSetFillColorWithColor(context, color.CGColor); CGContextFillPath(context); UIImage *image = UIGraphicsGetImageFromCurrentImageContext(); UIGraphicsEndImageContext(); diff --git a/FBSDKCoreKit/FBSDKCoreKit/Internal/UI/FBSDKLogo.m b/FBSDKCoreKit/FBSDKCoreKit/Internal/UI/FBSDKLogo.m index f13364f061..88794b52b6 100644 --- a/FBSDKCoreKit/FBSDKCoreKit/Internal/UI/FBSDKLogo.m +++ b/FBSDKCoreKit/FBSDKCoreKit/Internal/UI/FBSDKLogo.m @@ -22,50 +22,50 @@ @implementation FBSDKLogo - (CGPathRef)pathWithSize:(CGSize)size { - CGFloat originalCanvasWidth = 1366; - CGFloat originalCanvasHeight = 1366; + CGFloat originalCanvasWidth = 1366; + CGFloat originalCanvasHeight = 1366; - CGAffineTransform transformValue = CGAffineTransformMakeScale(size.width / originalCanvasWidth, size.height / originalCanvasHeight); + CGAffineTransform transformValue = CGAffineTransformMakeScale(size.width / originalCanvasWidth, size.height / originalCanvasHeight); - UIBezierPath* path = [UIBezierPath bezierPath]; - [path moveToPoint: CGPointMake(1365.33, 682.67)]; - [path addCurveToPoint: CGPointMake(682.67, -0) - controlPoint1: CGPointMake(1365.33, 305.64) - controlPoint2: CGPointMake(1059.69, -0)]; - [path addCurveToPoint: CGPointMake(0, 682.67) - controlPoint1: CGPointMake(305.64, -0) - controlPoint2: CGPointMake(0, 305.64)]; - [path addCurveToPoint: CGPointMake(576, 1357.04) - controlPoint1: CGPointMake(0, 1023.41) - controlPoint2: CGPointMake(249.64, 1305.83)]; - [path addLineToPoint: CGPointMake(576, 880)]; - [path addLineToPoint: CGPointMake(402.67, 880)]; - [path addLineToPoint: CGPointMake(402.67, 682.67)]; - [path addLineToPoint: CGPointMake(576, 682.67)]; - [path addLineToPoint: CGPointMake(576, 532.27)]; - [path addCurveToPoint: CGPointMake(833.85, 266.67) - controlPoint1: CGPointMake(576, 361.17) - controlPoint2: CGPointMake(677.92, 266.67)]; - [path addCurveToPoint: CGPointMake(986.67, 280) - controlPoint1: CGPointMake(908.54, 266.67) - controlPoint2: CGPointMake(986.67, 280)]; - [path addLineToPoint: CGPointMake(986.67, 448)]; - [path addLineToPoint: CGPointMake(900.58, 448)]; - [path addCurveToPoint: CGPointMake(789.33, 554.61) - controlPoint1: CGPointMake(815.78, 448) - controlPoint2: CGPointMake(789.33, 500.62)]; - [path addLineToPoint: CGPointMake(789.33, 682.67)]; - [path addLineToPoint: CGPointMake(978.67, 682.67)]; - [path addLineToPoint: CGPointMake(948.4, 880)]; - [path addLineToPoint: CGPointMake(789.33, 880)]; - [path addLineToPoint: CGPointMake(789.33, 1357.04)]; - [path addCurveToPoint: CGPointMake(1365.33, 682.67) - controlPoint1: CGPointMake(1115.69, 1305.83) - controlPoint2: CGPointMake(1365.33, 1023.41)]; - [path closePath]; - [path applyTransform:transformValue]; + UIBezierPath *path = [UIBezierPath bezierPath]; + [path moveToPoint:CGPointMake(1365.33, 682.67)]; + [path addCurveToPoint:CGPointMake(682.67, -0) + controlPoint1:CGPointMake(1365.33, 305.64) + controlPoint2:CGPointMake(1059.69, -0)]; + [path addCurveToPoint:CGPointMake(0, 682.67) + controlPoint1:CGPointMake(305.64, -0) + controlPoint2:CGPointMake(0, 305.64)]; + [path addCurveToPoint:CGPointMake(576, 1357.04) + controlPoint1:CGPointMake(0, 1023.41) + controlPoint2:CGPointMake(249.64, 1305.83)]; + [path addLineToPoint:CGPointMake(576, 880)]; + [path addLineToPoint:CGPointMake(402.67, 880)]; + [path addLineToPoint:CGPointMake(402.67, 682.67)]; + [path addLineToPoint:CGPointMake(576, 682.67)]; + [path addLineToPoint:CGPointMake(576, 532.27)]; + [path addCurveToPoint:CGPointMake(833.85, 266.67) + controlPoint1:CGPointMake(576, 361.17) + controlPoint2:CGPointMake(677.92, 266.67)]; + [path addCurveToPoint:CGPointMake(986.67, 280) + controlPoint1:CGPointMake(908.54, 266.67) + controlPoint2:CGPointMake(986.67, 280)]; + [path addLineToPoint:CGPointMake(986.67, 448)]; + [path addLineToPoint:CGPointMake(900.58, 448)]; + [path addCurveToPoint:CGPointMake(789.33, 554.61) + controlPoint1:CGPointMake(815.78, 448) + controlPoint2:CGPointMake(789.33, 500.62)]; + [path addLineToPoint:CGPointMake(789.33, 682.67)]; + [path addLineToPoint:CGPointMake(978.67, 682.67)]; + [path addLineToPoint:CGPointMake(948.4, 880)]; + [path addLineToPoint:CGPointMake(789.33, 880)]; + [path addLineToPoint:CGPointMake(789.33, 1357.04)]; + [path addCurveToPoint:CGPointMake(1365.33, 682.67) + controlPoint1:CGPointMake(1115.69, 1305.83) + controlPoint2:CGPointMake(1365.33, 1023.41)]; + [path closePath]; + [path applyTransform:transformValue]; - return [path CGPath]; + return [path CGPath]; } @end diff --git a/FBSDKCoreKit/FBSDKCoreKit/Internal/UI/FBSDKUIUtility.h b/FBSDKCoreKit/FBSDKCoreKit/Internal/UI/FBSDKUIUtility.h index dfa023356c..759d7678fe 100644 --- a/FBSDKCoreKit/FBSDKCoreKit/Internal/UI/FBSDKUIUtility.h +++ b/FBSDKCoreKit/FBSDKCoreKit/Internal/UI/FBSDKUIUtility.h @@ -33,8 +33,10 @@ static inline CGSize FBSDKEdgeInsetsInsetSize(CGSize size, UIEdgeInsets insets) */ static inline CGSize FBSDKEdgeInsetsOutsetSize(CGSize size, UIEdgeInsets insets) { - return CGSizeMake(insets.left + size.width + insets.right, - insets.top + size.height + insets.bottom); + return CGSizeMake( + insets.left + size.width + insets.right, + insets.top + size.height + insets.bottom + ); } /** @@ -64,14 +66,14 @@ static inline CGSize FBSDKTextSize(NSString *text, NSMutableParagraphStyle *paragraphStyle = [[NSMutableParagraphStyle alloc] init]; paragraphStyle.lineBreakMode = lineBreakMode; NSDictionary *attributes = @{ - NSFontAttributeName: font, - NSParagraphStyleAttributeName: paragraphStyle, - }; + NSFontAttributeName : font, + NSParagraphStyleAttributeName : paragraphStyle, + }; NSAttributedString *attributedString = [[NSAttributedString alloc] initWithString:text attributes:attributes]; CGSize size = [attributedString boundingRectWithSize:constrainedSize - options:(NSStringDrawingUsesDeviceMetrics | - NSStringDrawingUsesLineFragmentOrigin | - NSStringDrawingUsesFontLeading) + options:(NSStringDrawingUsesDeviceMetrics + | NSStringDrawingUsesLineFragmentOrigin + | NSStringDrawingUsesFontLeading) context:NULL].size; return CGSizeMake(ceilf(size.width), ceilf(size.height)); } diff --git a/FBSDKCoreKit/FBSDKCoreKit/Internal/UI/FBSDKViewImpressionTracker.m b/FBSDKCoreKit/FBSDKCoreKit/Internal/UI/FBSDKViewImpressionTracker.m index 9bc6f1b6a3..4d25e69084 100644 --- a/FBSDKCoreKit/FBSDKCoreKit/Internal/UI/FBSDKViewImpressionTracker.m +++ b/FBSDKCoreKit/FBSDKCoreKit/Internal/UI/FBSDKViewImpressionTracker.m @@ -20,7 +20,7 @@ #import "FBSDKAccessToken.h" #import "FBSDKAppEvents+Internal.h" -#import "FBSDKTypeUtility.h" +#import "FBSDKInternalUtility.h" @implementation FBSDKViewImpressionTracker { diff --git a/FBSDKCoreKit/FBSDKCoreKit/Internal/WebDialog/FBSDKWebDialog.m b/FBSDKCoreKit/FBSDKCoreKit/Internal/WebDialog/FBSDKWebDialog.m index 07117cf8d4..7bec076f97 100644 --- a/FBSDKCoreKit/FBSDKCoreKit/Internal/WebDialog/FBSDKWebDialog.m +++ b/FBSDKCoreKit/FBSDKCoreKit/Internal/WebDialog/FBSDKWebDialog.m @@ -20,19 +20,17 @@ #if !TARGET_OS_TV -#import "FBSDKWebDialog.h" + #import "FBSDKWebDialog.h" -#import "FBSDKAccessToken.h" -#import "FBSDKDynamicFrameworkLoader.h" -#import "FBSDKInternalUtility.h" -#import "FBSDKLogger.h" -#import "FBSDKSettings.h" -#import "FBSDKTypeUtility.h" -#import "FBSDKWebDialogView.h" -#import "FBSDKInternalUtility.h" + #import "FBSDKAccessToken.h" + #import "FBSDKDynamicFrameworkLoader.h" + #import "FBSDKInternalUtility.h" + #import "FBSDKLogger.h" + #import "FBSDKSettings.h" + #import "FBSDKWebDialogView.h" -#define FBSDK_WEB_DIALOG_SHOW_ANIMATION_DURATION 0.2 -#define FBSDK_WEB_DIALOG_DISMISS_ANIMATION_DURATION 0.3 + #define FBSDK_WEB_DIALOG_SHOW_ANIMATION_DURATION 0.2 + #define FBSDK_WEB_DIALOG_DISMISS_ANIMATION_DURATION 0.3 typedef void (^FBSDKBoolBlock)(BOOL finished); @@ -47,7 +45,7 @@ @implementation FBSDKWebDialog FBSDKWebDialogView *_dialogView; } -#pragma mark - Class Methods + #pragma mark - Class Methods + (instancetype)showWithName:(NSString *)name parameters:(NSDictionary *)parameters @@ -61,7 +59,7 @@ + (instancetype)showWithName:(NSString *)name return dialog; } -#pragma mark - Object Lifecycle + #pragma mark - Object Lifecycle - (void)dealloc { @@ -71,7 +69,7 @@ - (void)dealloc [_backgroundView removeFromSuperview]; } -#pragma mark - Public Methods + #pragma mark - Public Methods - (BOOL)show { @@ -110,7 +108,7 @@ - (BOOL)show return YES; } -#pragma mark - FBSDKWebDialogViewDelegate + #pragma mark - FBSDKWebDialogViewDelegate - (void)webDialogView:(FBSDKWebDialogView *)webDialogView didCompleteWithResults:(NSDictionary *)results { @@ -130,15 +128,16 @@ - (void)webDialogViewDidCancel:(FBSDKWebDialogView *)webDialogView - (void)webDialogViewDidFinishLoad:(FBSDKWebDialogView *)webDialogView { if (_deferVisibility) { - dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(0.05 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{ - if (self->_dialogView) { - [self _showWebView]; - } - }); + dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(0.05 * NSEC_PER_SEC)), + dispatch_get_main_queue(), ^{ + if (self->_dialogView) { + [self _showWebView]; + } + }); } } -#pragma mark - Notifications + #pragma mark - Notifications - (void)_addObservers { @@ -167,7 +166,7 @@ - (void)_removeObservers [nc removeObserver:self name:UIDeviceOrientationDidChangeNotification object:nil]; } -#pragma mark - Helper Methods + #pragma mark - Helper Methods - (void)_cancel { @@ -191,15 +190,15 @@ - (void)_dismissAnimated:(BOOL)animated FBSDKWebDialogView *dialogView = _dialogView; _dialogView.delegate = nil; _dialogView = nil; - void(^didDismiss)(BOOL) = ^(BOOL finished){ + void (^didDismiss)(BOOL) = ^(BOOL finished) { [backgroundView removeFromSuperview]; [dialogView removeFromSuperview]; }; if (animated) { [UIView animateWithDuration:FBSDK_WEB_DIALOG_DISMISS_ANIMATION_DURATION animations:^{ - dialogView.alpha = 0.0; - backgroundView.alpha = 0.0; - } completion:didDismiss]; + dialogView.alpha = 0.0; + backgroundView.alpha = 0.0; + } completion:didDismiss]; } else { didDismiss(YES); } @@ -225,8 +224,8 @@ - (NSURL *)_generateURL:(NSError **)errorRef [FBSDKTypeUtility dictionary:parameters setObject:@"fbconnect://success" forKey:@"redirect_uri"]; [FBSDKTypeUtility dictionary:parameters setObject:[FBSDKSettings appID] forKey:@"app_id"]; [FBSDKTypeUtility dictionary:parameters - setObject:[FBSDKAccessToken currentAccessToken].tokenString - forKey:@"access_token"]; + setObject:[FBSDKAccessToken currentAccessToken].tokenString + forKey:@"access_token"]; [parameters addEntriesFromDictionary:self.parameters]; return [FBSDKInternalUtility facebookURLWithHostPrefix:@"m" path:[@"/dialog/" stringByAppendingString:self.name] @@ -263,27 +262,6 @@ - (BOOL)_showWebView return YES; } -- (CGAffineTransform)_transformForOrientation -{ - // iOS 8 simply adjusts the application frame to adapt to the current orientation and deprecated the concept of - // interface orientations - if ([FBSDKInternalUtility shouldManuallyAdjustOrientation]) { - switch (FBSDKInternalUtility.statusBarOrientation) { - case UIInterfaceOrientationLandscapeLeft: - return CGAffineTransformMakeRotation(M_PI * 1.5); - case UIInterfaceOrientationLandscapeRight: - return CGAffineTransformMakeRotation(M_PI/2); - case UIInterfaceOrientationPortraitUpsideDown: - return CGAffineTransformMakeRotation(-M_PI); - case UIInterfaceOrientationPortrait: - case UIInterfaceOrientationUnknown: - // don't adjust the orientation - break; - } - } - return CGAffineTransformIdentity; -} - - (CGRect)_applicationFrameForOrientation { CGRect applicationFrame = _dialogView.window.screen.bounds; @@ -296,29 +274,17 @@ - (CGRect)_applicationFrameForOrientation #endif if (insets.top == 0.0) { -#pragma clang diagnostic push -#pragma clang diagnostic ignored "-Wdeprecated-declarations" + #pragma clang diagnostic push + #pragma clang diagnostic ignored "-Wdeprecated-declarations" insets.top = [[UIApplication sharedApplication] statusBarFrame].size.height; -#pragma clang diagnostic pop + #pragma clang diagnostic pop } applicationFrame.origin.x += insets.left; applicationFrame.origin.y += insets.top; applicationFrame.size.width -= insets.left + insets.right; applicationFrame.size.height -= insets.top + insets.bottom; - if ([FBSDKInternalUtility shouldManuallyAdjustOrientation]) { - switch (FBSDKInternalUtility.statusBarOrientation) { - case UIInterfaceOrientationLandscapeLeft: - case UIInterfaceOrientationLandscapeRight: - return CGRectMake(0, 0, CGRectGetHeight(applicationFrame), CGRectGetWidth(applicationFrame)); - case UIInterfaceOrientationPortraitUpsideDown: - case UIInterfaceOrientationPortrait: - case UIInterfaceOrientationUnknown: - return applicationFrame; - } - } else { - return applicationFrame; - } + return applicationFrame; } - (void)_updateViewsWithScale:(CGFloat)scale @@ -326,19 +292,19 @@ - (void)_updateViewsWithScale:(CGFloat)scale animationDuration:(CFTimeInterval)animationDuration completion:(FBSDKBoolBlock)completion { - CGAffineTransform transform; + CGAffineTransform transform = _dialogView.transform; CGRect applicationFrame = [self _applicationFrameForOrientation]; if (scale == 1.0) { - transform = _dialogView.transform; _dialogView.transform = CGAffineTransformIdentity; _dialogView.frame = applicationFrame; _dialogView.transform = transform; } - transform = CGAffineTransformScale([self _transformForOrientation], scale, scale); - void(^updateBlock)(void) = ^{ + void (^updateBlock)(void) = ^{ self->_dialogView.transform = transform; - self->_dialogView.center = CGPointMake(CGRectGetMidX(applicationFrame), - CGRectGetMidY(applicationFrame)); + self->_dialogView.center = CGPointMake( + CGRectGetMidX(applicationFrame), + CGRectGetMidY(applicationFrame) + ); self->_dialogView.alpha = alpha; self->_backgroundView.alpha = alpha; }; diff --git a/FBSDKCoreKit/FBSDKCoreKit/Internal/WebDialog/FBSDKWebDialogView.m b/FBSDKCoreKit/FBSDKCoreKit/Internal/WebDialog/FBSDKWebDialogView.m index a5efb139a3..d2fe53c42c 100644 --- a/FBSDKCoreKit/FBSDKCoreKit/Internal/WebDialog/FBSDKWebDialogView.m +++ b/FBSDKCoreKit/FBSDKCoreKit/Internal/WebDialog/FBSDKWebDialogView.m @@ -20,16 +20,15 @@ #if !TARGET_OS_TV -#import "FBSDKWebDialogView.h" + #import "FBSDKWebDialogView.h" -#import + #import -#import "FBSDKCloseIcon.h" -#import "FBSDKError.h" -#import "FBSDKInternalUtility.h" -#import "FBSDKTypeUtility.h" + #import "FBSDKCloseIcon.h" + #import "FBSDKError.h" + #import "FBSDKInternalUtility.h" -#define FBSDK_WEB_DIALOG_VIEW_BORDER_WIDTH 10.0 + #define FBSDK_WEB_DIALOG_VIEW_BORDER_WIDTH 10.0 @interface FBSDKWebDialogView () @end @@ -41,10 +40,10 @@ @implementation FBSDKWebDialogView WKWebView *_webView; } -#pragma mark - Object Lifecycle + #pragma mark - Object Lifecycle -#pragma clang diagnostic push -#pragma clang diagnostic ignored "-Wdeprecated-declarations" + #pragma clang diagnostic push + #pragma clang diagnostic ignored "-Wdeprecated-declarations" - (instancetype)initWithFrame:(CGRect)frame { if ((self = [super initWithFrame:frame])) { @@ -58,9 +57,9 @@ - (instancetype)initWithFrame:(CGRect)frame _closeButton = [UIButton buttonWithType:UIButtonTypeCustom]; UIImage *closeImage = [[[FBSDKCloseIcon alloc] init] imageWithSize:CGSizeMake(29.0, 29.0)]; [_closeButton setImage:closeImage forState:UIControlStateNormal]; - [_closeButton setTitleColor:[UIColor colorWithRed:167.0/255.0 - green:184.0/255.0 - blue:216.0/255.0 + [_closeButton setTitleColor:[UIColor colorWithRed:167.0 / 255.0 + green:184.0 / 255.0 + blue:216.0 / 255.0 alpha:1.0] forState:UIControlStateNormal]; [_closeButton setTitleColor:[UIColor whiteColor] forState:UIControlStateHighlighted]; _closeButton.showsTouchWhenHighlighted = YES; @@ -74,14 +73,15 @@ - (instancetype)initWithFrame:(CGRect)frame } return self; } -#pragma clang diagnostic pop + + #pragma clang diagnostic pop - (void)dealloc { _webView.navigationDelegate = nil; } -#pragma mark - Public Methods + #pragma mark - Public Methods - (void)loadURL:(NSURL *)URL { @@ -94,7 +94,7 @@ - (void)stopLoading [_webView stopLoading]; } -#pragma mark - Layout + #pragma mark - Layout - (void)drawRect:(CGRect)rect { @@ -109,23 +109,25 @@ - (void)drawRect:(CGRect)rect [super drawRect:rect]; } -#pragma clang diagnostic push -#pragma clang diagnostic ignored "-Wdeprecated-declarations" + #pragma clang diagnostic push + #pragma clang diagnostic ignored "-Wdeprecated-declarations" - (void)layoutSubviews { [super layoutSubviews]; CGRect bounds = self.bounds; - if (UI_USER_INTERFACE_IDIOM() == UIUserInterfaceIdiomPad) { + if (UIDevice.currentDevice.userInterfaceIdiom == UIUserInterfaceIdiomPad) { CGFloat horizontalInset = CGRectGetWidth(bounds) * 0.2; CGFloat verticalInset = CGRectGetHeight(bounds) * 0.2; UIEdgeInsets iPadInsets = UIEdgeInsetsMake(verticalInset, horizontalInset, verticalInset, horizontalInset); bounds = UIEdgeInsetsInsetRect(bounds, iPadInsets); } - UIEdgeInsets webViewInsets = UIEdgeInsetsMake(FBSDK_WEB_DIALOG_VIEW_BORDER_WIDTH, - FBSDK_WEB_DIALOG_VIEW_BORDER_WIDTH, - FBSDK_WEB_DIALOG_VIEW_BORDER_WIDTH, - FBSDK_WEB_DIALOG_VIEW_BORDER_WIDTH); + UIEdgeInsets webViewInsets = UIEdgeInsetsMake( + FBSDK_WEB_DIALOG_VIEW_BORDER_WIDTH, + FBSDK_WEB_DIALOG_VIEW_BORDER_WIDTH, + FBSDK_WEB_DIALOG_VIEW_BORDER_WIDTH, + FBSDK_WEB_DIALOG_VIEW_BORDER_WIDTH + ); _webView.frame = CGRectIntegral(UIEdgeInsetsInsetRect(bounds, webViewInsets)); CGRect webViewBounds = _webView.bounds; @@ -140,16 +142,17 @@ - (void)layoutSubviews _closeButton.frame = CGRectIntegral(closeButtonFrame); } } -#pragma clang diagnostic pop -#pragma mark - Actions + #pragma clang diagnostic pop + + #pragma mark - Actions - (void)_close:(id)sender { [_delegate webDialogViewDidCancel:self]; } -#pragma mark - WKNavigationDelegate + #pragma mark - WKNavigationDelegate - (void)webView:(WKWebView *)webView didFailNavigation:(WKNavigation *)navigation withError:(NSError *)error { @@ -160,17 +163,17 @@ - (void)webView:(WKWebView *)webView didFailNavigation:(WKNavigation *)navigatio // away before the page has completely loaded, if we find cases where we want this to result in dialog failure // (usually this just means quick-user), then we should add something more robust here to account for differences in // application needs - if (!(([error.domain isEqualToString:NSURLErrorDomain] && error.code == NSURLErrorCancelled) || - ([error.domain isEqualToString:@"WebKitErrorDomain"] && error.code == 102))) { + if (!(([error.domain isEqualToString:NSURLErrorDomain] && error.code == NSURLErrorCancelled) + || ([error.domain isEqualToString:@"WebKitErrorDomain"] && error.code == 102))) { [_delegate webDialogView:self didFailWithError:error]; } } -#pragma clang diagnostic push -#pragma clang diagnostic ignored "-Wdeprecated-declarations" -- (void)webView:(WKWebView *)webView -decidePolicyForNavigationAction:(WKNavigationAction *)navigationAction -decisionHandler:(void (^)(WKNavigationActionPolicy))decisionHandler + #pragma clang diagnostic push + #pragma clang diagnostic ignored "-Wdeprecated-declarations" +- (void) webView:(WKWebView *)webView + decidePolicyForNavigationAction:(WKNavigationAction *)navigationAction + decisionHandler:(void (^)(WKNavigationActionPolicy))decisionHandler { NSURL *URL = navigationAction.request.URL; @@ -197,7 +200,8 @@ - (void)webView:(WKWebView *)webView decisionHandler(WKNavigationActionPolicyAllow); } } -#pragma clang diagnostic pop + + #pragma clang diagnostic pop - (void)webView:(WKWebView *)webView didFinishNavigation:(WKNavigation *)navigation { diff --git a/FBSDKCoreKit/FBSDKCoreKit/Internal_NoARC/FBSDKDynamicFrameworkLoader.m b/FBSDKCoreKit/FBSDKCoreKit/Internal_NoARC/FBSDKDynamicFrameworkLoader.m index 291cc83622..734963054c 100644 --- a/FBSDKCoreKit/FBSDKCoreKit/Internal_NoARC/FBSDKDynamicFrameworkLoader.m +++ b/FBSDKCoreKit/FBSDKCoreKit/Internal_NoARC/FBSDKDynamicFrameworkLoader.m @@ -18,12 +18,12 @@ #import "FBSDKDynamicFrameworkLoader.h" -#import - #import #import #import +#import + #import "FBSDKLogger.h" #import "FBSDKSettings.h" @@ -31,11 +31,10 @@ #pragma mark - Library and Symbol Loading -struct FBSDKDFLLoadSymbolContext -{ +struct FBSDKDFLLoadSymbolContext { void *(*library)(void); // function to retrieve the library handle (it's a function instead of void * so it can be staticlly bound) - const char *name; // name of the symbol to retrieve - void **address; // [out] address of the symbol in the process address space + const char *name; // name of the symbol to retrieve + void **address; // [out] address of the symbol in the process address space }; // Retrieves the handle for a library for framework. The paths for each are constructed @@ -60,18 +59,18 @@ // Implements the callback for dispatch_once() that loads the handle for specified framework name #define _fbsdkdfl_load_framework_once_impl_(FRAMEWORK) \ - static void fbsdkdfl_load_##FRAMEWORK##_once(void *context) { \ + static void fbsdkdfl_load_ ## FRAMEWORK ## _once(void *context) { \ *(void **)context = fbsdkdfl_load_framework_once(@#FRAMEWORK); \ } // Implements the framework/library retrieval function for the given name. // It calls the loading function once and caches the handle in a local static variable #define _fbsdkdfl_handle_get_impl_(LIBRARY) \ - static void *fbsdkdfl_handle_get_##LIBRARY(void) { \ - static void *LIBRARY##_handle; \ - static dispatch_once_t LIBRARY##_once; \ - dispatch_once_f(&LIBRARY##_once, &LIBRARY##_handle, &fbsdkdfl_load_##LIBRARY##_once); \ - return LIBRARY##_handle;\ + static void *fbsdkdfl_handle_get_ ## LIBRARY(void) { \ + static void *LIBRARY ## _handle; \ + static dispatch_once_t LIBRARY ## _once; \ + dispatch_once_f(&LIBRARY ## _once, &LIBRARY ## _handle, &fbsdkdfl_load_ ## LIBRARY ## _once); \ + return LIBRARY ## _handle; \ } // Callback from dispatch_once() to load a specific symbol @@ -84,13 +83,13 @@ static void fbsdkdfl_load_symbol_once(void *context) // The boilerplate code for loading a symbol from a given library once and caching it in a static local #define _fbsdkdfl_symbol_get(LIBRARY, PREFIX, SYMBOL, TYPE, VARIABLE_NAME) \ static TYPE VARIABLE_NAME; \ - static dispatch_once_t SYMBOL##_once; \ - static struct FBSDKDFLLoadSymbolContext ctx = { .library = &fbsdkdfl_handle_get_##LIBRARY, .name = PREFIX #SYMBOL, .address = (void *)&VARIABLE_NAME }; \ - dispatch_once_f(&SYMBOL##_once, &ctx, &fbsdkdfl_load_symbol_once) + static dispatch_once_t SYMBOL ## _once; \ + static struct FBSDKDFLLoadSymbolContext ctx = { .library = &fbsdkdfl_handle_get_ ## LIBRARY, .name = PREFIX #SYMBOL, .address = (void *)&VARIABLE_NAME }; \ + dispatch_once_f(&SYMBOL ## _once, &ctx, &fbsdkdfl_load_symbol_once) #define _fbsdkdfl_symbol_get_c(LIBRARY, SYMBOL) _fbsdkdfl_symbol_get(LIBRARY, "OBJC_CLASS_$_", SYMBOL, Class, c) // convenience symbol retrieval macro for getting an Objective-C class symbol and storing it in the local static c -#define _fbsdkdfl_symbol_get_f(LIBRARY, SYMBOL) _fbsdkdfl_symbol_get(LIBRARY, "", SYMBOL, SYMBOL##_type, f) // convenience symbol retrieval macro for getting a function pointer and storing it in the local static f -#define _fbsdkdfl_symbol_get_k(LIBRARY, SYMBOL, TYPE) _fbsdkdfl_symbol_get(LIBRARY, "", SYMBOL, TYPE, k) // convenience symbol retrieval macro for getting a pointer to a named variable and storing it in the local static k +#define _fbsdkdfl_symbol_get_f(LIBRARY, SYMBOL) _fbsdkdfl_symbol_get(LIBRARY, "", SYMBOL, SYMBOL ## _type, f) // convenience symbol retrieval macro for getting a function pointer and storing it in the local static f +#define _fbsdkdfl_symbol_get_k(LIBRARY, SYMBOL, TYPE) _fbsdkdfl_symbol_get(LIBRARY, "", SYMBOL, TYPE, k) // convenience symbol retrieval macro for getting a pointer to a named variable and storing it in the local static k // convenience macro for verifying a pointer to a named variable was successfully loaded and returns the value #define _fbsdkdfl_return_k(FRAMEWORK, SYMBOL) \ @@ -112,6 +111,21 @@ static void fbsdkdfl_load_symbol_once(void *context) @implementation FBSDKDynamicFrameworkLoader ++ (FBSDKDynamicFrameworkLoader *)shared +{ + static dispatch_once_t onceToken; + static FBSDKDynamicFrameworkLoader *shared = nil; + dispatch_once(&onceToken, ^{ + shared = [[self alloc] init]; + }); + return shared; +} + +- (Class)safariViewControllerClass +{ + return fbsdkdfl_SFSafariViewControllerClass(); +} + #define _fbsdkdfl_Security_get_k(SYMBOL) _fbsdkdfl_symbol_get_k(Security, SYMBOL, CFTypeRef *) #define _fbsdkdfl_Security_get_and_return_k(SYMBOL) \ @@ -473,8 +487,8 @@ Class fbsdkdfl_ALAssetsLibraryClass(void) Class fbsdkdfl_CTTelephonyNetworkInfoClass(void) { - _fbsdkdfl_CoreTelephonyLibrary_get_c(CTTelephonyNetworkInfo); - return c; + _fbsdkdfl_CoreTelephonyLibrary_get_c(CTTelephonyNetworkInfo); + return c; } #pragma mark - CoreImage @@ -485,7 +499,6 @@ Class fbsdkdfl_CTTelephonyNetworkInfoClass(void) #define _fbsdkdfl_CoreImage_get_c(SYMBOL) _fbsdkdfl_symbol_get_c(CoreImage, SYMBOL); #define _fbsdkdfl_CoreImage_get_and_return_NSString(SYMBOL) _fbsdkdfl_get_and_return_NSString(CoreImage, SYMBOL) - Class fbsdkdfl_CIImageClass(void) { _fbsdkdfl_CoreImage_get_c(CIImage); @@ -541,8 +554,8 @@ Class fbsdkdfl_PHAssetChangeRequest(void) #define _fbsdkdfl_MobileCoreServices_get_k(SYMBOL) _fbsdkdfl_symbol_get_k(MobileCoreServices, SYMBOL, CFStringRef *) #define _fbsdkdfl_MobileCoreServices_get_and_return_k(SYMBOL) \ -_fbsdkdfl_MobileCoreServices_get_k(SYMBOL); \ -_fbsdkdfl_return_k(MobileCoreServices, SYMBOL) + _fbsdkdfl_MobileCoreServices_get_k(SYMBOL); \ + _fbsdkdfl_return_k(MobileCoreServices, SYMBOL) #define _fbsdkdfl_MobileCoreServices_get_f(SYMBOL) _fbsdkdfl_symbol_get_f(MobileCoreServices, SYMBOL) diff --git a/FBSDKTVOSKit/FBSDKTVOSKit/FBSDKTVShareButtonElement.m b/FBSDKCoreKit/FBSDKCoreKit/Swift/Imports.swift similarity index 87% rename from FBSDKTVOSKit/FBSDKTVOSKit/FBSDKTVShareButtonElement.m rename to FBSDKCoreKit/FBSDKCoreKit/Swift/Imports.swift index df8dcb4852..ad8ed5ecf9 100644 --- a/FBSDKTVOSKit/FBSDKTVOSKit/FBSDKTVShareButtonElement.m +++ b/FBSDKCoreKit/FBSDKCoreKit/Swift/Imports.swift @@ -1,4 +1,4 @@ -// Copyright (c) 2014-present, Facebook, Inc. All rights reserved. +// Copyright (c) 2016-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 @@ -16,8 +16,4 @@ // 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 "FBSDKTVShareButtonElement.h" - -@implementation FBSDKTVShareButtonElement - -@end +import Accelerate diff --git a/FBSDKCoreKit/FBSDKCoreKit/include/FBSDKAppLinkResolver.h b/FBSDKCoreKit/FBSDKCoreKit/include/FBSDKAppLinkResolver.h index bad15cf41d..edffd6948a 120000 --- a/FBSDKCoreKit/FBSDKCoreKit/include/FBSDKAppLinkResolver.h +++ b/FBSDKCoreKit/FBSDKCoreKit/include/FBSDKAppLinkResolver.h @@ -1 +1 @@ -../AppLink/FBSDKAppLinkResolver.h \ No newline at end of file +../AppLink/Resolver/FBSDKAppLinkResolver.h \ No newline at end of file diff --git a/FBSDKCoreKit/FBSDKCoreKit/include/FBSDKAppLinkResolverRequestBuilder.h b/FBSDKCoreKit/FBSDKCoreKit/include/FBSDKAppLinkResolverRequestBuilder.h new file mode 120000 index 0000000000..e62088ccc9 --- /dev/null +++ b/FBSDKCoreKit/FBSDKCoreKit/include/FBSDKAppLinkResolverRequestBuilder.h @@ -0,0 +1 @@ +../AppLink/Resolver/FBSDKAppLinkResolverRequestBuilder.h \ No newline at end of file diff --git a/FBSDKCoreKit/FBSDKCoreKit/include/FBSDKAppLinkResolving.h b/FBSDKCoreKit/FBSDKCoreKit/include/FBSDKAppLinkResolving.h index f1abae564d..30a0800028 120000 --- a/FBSDKCoreKit/FBSDKCoreKit/include/FBSDKAppLinkResolving.h +++ b/FBSDKCoreKit/FBSDKCoreKit/include/FBSDKAppLinkResolving.h @@ -1 +1 @@ -../AppLink/FBSDKAppLinkResolving.h \ No newline at end of file +../AppLink/Resolver/FBSDKAppLinkResolving.h \ No newline at end of file diff --git a/FBSDKCoreKit/FBSDKCoreKit/include/FBSDKAuthenticationToken.h b/FBSDKCoreKit/FBSDKCoreKit/include/FBSDKAuthenticationToken.h new file mode 120000 index 0000000000..a45d0a2fcc --- /dev/null +++ b/FBSDKCoreKit/FBSDKCoreKit/include/FBSDKAuthenticationToken.h @@ -0,0 +1 @@ +../FBSDKAuthenticationToken.h \ No newline at end of file diff --git a/FBSDKCoreKit/FBSDKCoreKitTests/FBSDKAppLinkUtilityTests.swift b/FBSDKCoreKit/FBSDKCoreKitTests/FBSDKAppLinkUtilityTests.swift new file mode 100644 index 0000000000..75984a2331 --- /dev/null +++ b/FBSDKCoreKit/FBSDKCoreKitTests/FBSDKAppLinkUtilityTests.swift @@ -0,0 +1,50 @@ +// 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 FBSDKCoreKit + +class FBSDKAppLinkUtilityTests: FBSDKTestCase { + func testWithNoPromoCode() { + let url = URL(string: "myapp://somelink/?someparam=somevalue")! // swiftlint:disable:this force_unwrapping + let promoCode = AppLinkUtility.appInvitePromotionCode(from: url) + XCTAssertNil(promoCode) + } + + func testWithPromoCode() { + let url = URL(string: "myapp://somelink/?al_applink_data=%7B%22target_url%22%3Anull%2C%22extras%22%3A%7B%22deeplink_context%22%3A%22%7B%5C%22promo_code%5C%22%3A%5C%22PROMOWORKS%5C%22%7D%22%7D%7D")! // swiftlint:disable:this line_length force_unwrapping + let promoCode = AppLinkUtility.appInvitePromotionCode(from: url) + XCTAssertNotNil(promoCode) + XCTAssertEqual(promoCode, "PROMOWORKS") + } + + func testIsMatchURLScheme() { + let bundleDict = [ + "CFBundleURLTypes": [ + [ + "CFBundleURLSchemes": ["fb123"] + ] + ] + ] + + let fakeBundle = FakeBundle(dictionary: bundleDict) + stubMainBundle(with: fakeBundle) + + XCTAssertTrue(AppLinkUtility.isMatchURLScheme("fb123")) + XCTAssertFalse(AppLinkUtility.isMatchURLScheme("not_in_url_schemes")) + } +} diff --git a/FBSDKCoreKit/FBSDKCoreKitTests/FBSDKApplicationDelegateTests.m b/FBSDKCoreKit/FBSDKCoreKitTests/FBSDKApplicationDelegateTests.m index aad2c1b69c..4e8ed80ff8 100644 --- a/FBSDKCoreKit/FBSDKCoreKitTests/FBSDKApplicationDelegateTests.m +++ b/FBSDKCoreKit/FBSDKCoreKitTests/FBSDKApplicationDelegateTests.m @@ -16,96 +16,324 @@ // 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 "FBSDKCoreKit+Internal.h" #import "FBSDKCoreKitTestUtility.h" +#import "FBSDKCoreKitTests-Swift.h" +#import "FBSDKServerConfigurationFixtures.h" +#import "FBSDKTestCase.h" +#import "UserDefaultsSpy.h" + +@interface FBSDKApplicationDelegate (Testing) -// An extension that redeclares a private method so that it can be mocked -@interface FBSDKApplicationDelegate () - (BOOL)isAppLaunched; +- (void)setIsAppLaunched:(BOOL)isLaunched; +- (NSHashTable> *)applicationObservers; +- (void)resetApplicationObserverCache; +- (void)_logSDKInitialize; +- (void)applicationDidBecomeActive:(NSNotification *)notification; +- (void)applicationWillResignActive:(NSNotification *)notification; + @end -@interface FBSDKApplicationDelegateTests : XCTestCase { +@interface FBSDKBridgeAPI (ApplicationObserving) +@end + +@interface FBSDKApplicationDelegateTests : FBSDKTestCase +{ FBSDKApplicationDelegate *_delegate; - id _delegateMock; - id _settingsMock; + UserDefaultsSpy *_defaultsSpy; + FBSDKProfile *_profile; + id _partialDelegateMock; + NotificationCenterSpy *_notificationCenterSpy; } - @end -static id g_mockNSBundle; +@implementation FBSDKApplicationDelegateTests -@interface FBSDKApplicationDelegate(Test) +- (void)setUp +{ + [super setUp]; -- (void)_logSDKInitialize; -- (void)applicationDidBecomeActive:(NSNotification *)notification; + _delegate = FBSDKApplicationDelegate.sharedInstance; + _delegate.isAppLaunched = NO; -@end + _defaultsSpy = [UserDefaultsSpy new]; + [self stubUserDefaultsWith:_defaultsSpy]; -@implementation FBSDKApplicationDelegateTests + _notificationCenterSpy = [NotificationCenterSpy new]; + [self stubDefaultNotificationCenterWith:_notificationCenterSpy]; -- (void)setUp { - [super setUp]; - g_mockNSBundle = [FBSDKCoreKitTestUtility mainBundleMock]; - _settingsMock = OCMStrictClassMock([FBSDKSettings class]); + _profile = [[FBSDKProfile alloc] initWithUserID:self.name + firstName:nil + middleName:nil + lastName:nil + name:nil + linkURL:nil + refreshDate:nil]; + + // Avoid actually calling log initialize b/c of the side effects. + _partialDelegateMock = OCMPartialMock(_delegate); + OCMStub([_partialDelegateMock _logSDKInitialize]); + + [_delegate resetApplicationObserverCache]; + + [self stubLoadingAdNetworkReporterConfiguration]; + + [self stubServerConfigurationFetchingWithConfiguration:FBSDKServerConfigurationFixtures.defaultConfig error:nil]; +} + +- (void)tearDown +{ + [super tearDown]; + + _delegate = nil; + + _defaultsSpy = nil; + _profile = nil; + + [_partialDelegateMock stopMocking]; + _partialDelegateMock = nil; +} + +// MARK: - Observers + +- (void)testDefaultObservers +{ + // Note: in reality this will have one observer from the BridgeAPI load method. + // this needs to be re-architected to avoid this. + XCTAssertEqual( + _delegate.applicationObservers.count, + 0, + "Should have no observers by default" + ); +} + +- (void)testAddingNewObserver +{ + ApplicationDelegateObserverFake *observer = [ApplicationDelegateObserverFake new]; + [_delegate addObserver:observer]; + + XCTAssertEqual( + [_delegate applicationObservers].count, + 1, + "Should be able to add a single observer" + ); +} + +- (void)testAddingDuplicateObservers +{ + ApplicationDelegateObserverFake *observer = [ApplicationDelegateObserverFake new]; + [_delegate addObserver:observer]; + [_delegate addObserver:observer]; + + XCTAssertEqual( + [_delegate applicationObservers].count, + 1, + "Should only add one instance of a given observer" + ); +} + +- (void)testRemovingObserver +{ + ApplicationDelegateObserverFake *observer = [ApplicationDelegateObserverFake new]; + [_delegate addObserver:observer]; + [_delegate removeObserver:observer]; + + XCTAssertEqual( + _delegate.applicationObservers.count, + 0, + "Should be able to remove observers that are present in the stored list" + ); +} + +- (void)testRemovingMissingObserver +{ + ApplicationDelegateObserverFake *observer = [ApplicationDelegateObserverFake new]; + [_delegate removeObserver:observer]; - _delegate = [FBSDKApplicationDelegate sharedInstance]; - _delegateMock = OCMPartialMock(_delegate); - [OCMStub([_delegateMock isAppLaunched]) andReturnValue: OCMOCK_VALUE(NO)]; + XCTAssertEqual( + _delegate.applicationObservers.count, + 0, + "Should not be able to remove absent observers" + ); } -- (void)tearDown { - [g_mockNSBundle stopMocking]; - g_mockNSBundle = nil; - [_settingsMock stopMocking]; - _settingsMock = nil; - [_delegateMock stopMocking]; - _delegateMock = nil; +// MARK: - Lifecycle Methods + +- (void)testDidFinishLaunchingLaunchedApp +{ + _delegate.isAppLaunched = YES; + + XCTAssertFalse( + [_delegate application:UIApplication.sharedApplication didFinishLaunchingWithOptions:nil], + "Should return false if the application is already launched" + ); + // TODO: check that side effects do not occur +} + +- (void)testDidFinishLaunchingSetsCurrentAccessTokenWithCache +{ + FBSDKAccessToken *expected = SampleAccessToken.validToken; + FakeTokenCache *cache = [[FakeTokenCache alloc] initWithAccessToken:expected + authenticationToken:nil]; + [self stubTokenCacheWith:cache]; + + [_delegate application:UIApplication.sharedApplication didFinishLaunchingWithOptions:nil]; + + // Should set the current access token to the cached access token when it exists + OCMVerify(ClassMethod([self.accessTokenClassMock setCurrentAccessToken:expected])); +} + +- (void)testDidFinishLaunchingSetsCurrentAccessTokenWithoutCache +{ + [self stubTokenCacheWith:[[FakeTokenCache alloc] initWithAccessToken:nil authenticationToken:nil]]; + + [_delegate application:UIApplication.sharedApplication didFinishLaunchingWithOptions:nil]; + + // Should set the current access token to nil access token when there isn't a cached token + OCMVerify(ClassMethod([self.accessTokenClassMock setCurrentAccessToken:nil])); +} + +- (void)testDidFinishLaunchingSetsCurrentAuthenticationTokenWithCache +{ + FBSDKAuthenticationToken *expected = SampleAuthenticationToken.validToken; + FakeTokenCache *cache = [[FakeTokenCache alloc] initWithAccessToken:nil + authenticationToken:expected]; + [self stubTokenCacheWith:cache]; + + [_delegate application:UIApplication.sharedApplication didFinishLaunchingWithOptions:nil]; + + // Should set the current authentication token to the cached access token when it exists + OCMVerify(ClassMethod([self.authenticationTokenClassMock setCurrentAuthenticationToken:expected])); +} + +- (void)testDidFinishLaunchingSetsCurrentAuthenticationTokenWithoutCache +{ + FakeTokenCache *cache = [[FakeTokenCache alloc] initWithAccessToken:nil authenticationToken:nil]; + [self stubTokenCacheWith:cache]; + + [_delegate application:UIApplication.sharedApplication didFinishLaunchingWithOptions:nil]; + + // Should set the current authentication token to nil access token when there isn't a cached token + OCMVerify(ClassMethod([self.authenticationTokenClassMock setCurrentAuthenticationToken:nil])); +} + +- (void)testDidFinishLaunchingLoadsServerConfiguration +{ + [_delegate application:UIApplication.sharedApplication didFinishLaunchingWithOptions:nil]; + + // Should load the server configuration on finishing launching + OCMVerify(ClassMethod([self.serverConfigurationManagerClassMock loadServerConfigurationWithCompletionBlock:nil])); +} + +- (void)testDidFinishLaunchingWithAutoLogEnabled +{ + [self stubIsAutoLogAppEventsEnabled:YES]; + + [_delegate application:UIApplication.sharedApplication didFinishLaunchingWithOptions:nil]; + + // Should log initialization when auto log app events is enabled + OCMVerify([_partialDelegateMock _logSDKInitialize]); } -- (void)testAutoLogAppEventsEnabled { +- (void)testDidFinishLaunchingWithAutoLogDisabled +{ + // Should not log initialization when auto log app events are disabled + OCMReject([_partialDelegateMock _logSDKInitialize]); + + [self stubIsAutoLogAppEventsEnabled:NO]; + + [_delegate application:UIApplication.sharedApplication didFinishLaunchingWithOptions:nil]; +} + +- (void)testDidFinishLaunchingSetsProfileWithCache +{ + [self stubCachedProfileWith:_profile]; + + [_delegate application:UIApplication.sharedApplication didFinishLaunchingWithOptions:nil]; - [OCMStub(ClassMethod([_settingsMock isAutoLogAppEventsEnabled])) andReturnValue: OCMOCK_VALUE(YES)]; + // Should set the current profile to the value fetched from the cache + OCMVerify([self.profileClassMock setCurrentProfile:_profile]); +} - id app = OCMClassMock([UIApplication class]); +- (void)testDidFinishLaunchingSetsProfileWithoutCache +{ + [self stubCachedProfileWith:nil]; - [_delegate application:app didFinishLaunchingWithOptions:nil]; + [_delegate application:UIApplication.sharedApplication didFinishLaunchingWithOptions:nil]; - OCMVerify([_delegateMock _logSDKInitialize]); + // Should set the current profile to nil when the cache is empty + OCMVerify([self.profileClassMock setCurrentProfile:nil]); } -- (void)testAutoLogAppEventsDisabled { - [OCMStub(ClassMethod([_settingsMock isAutoLogAppEventsEnabled])) andReturnValue: OCMOCK_VALUE(NO)]; +- (void)testDidFinishLaunchingWithObservers +{ + ApplicationDelegateObserverFake *observer1 = [ApplicationDelegateObserverFake new]; + ApplicationDelegateObserverFake *observer2 = [ApplicationDelegateObserverFake new]; + + [_delegate addObserver:observer1]; + [_delegate addObserver:observer2]; - OCMReject([_delegateMock _logSDKInitialize]); + BOOL notifiedObservers = [_delegate application:UIApplication.sharedApplication didFinishLaunchingWithOptions:nil]; - id app = OCMClassMock([UIApplication class]); - [_delegate application:app didFinishLaunchingWithOptions:nil]; + XCTAssertEqual( + observer1.didFinishLaunchingCallCount, + 1, + "Should invoke did finish launching on all observers" + ); + XCTAssertEqual( + observer2.didFinishLaunchingCallCount, + 1, + "Should invoke did finish launching on all observers" + ); + XCTAssertTrue(notifiedObservers, "Should indicate if observers were notified"); } -- (void)testAppEventsEnabled { +- (void)testDidFinishLaunchingWithoutObservers +{ + BOOL notifiedObservers = [_delegate application:UIApplication.sharedApplication didFinishLaunchingWithOptions:nil]; - [OCMStub(ClassMethod([_settingsMock isAutoLogAppEventsEnabled])) andReturnValue: OCMOCK_VALUE(YES)]; + XCTAssertFalse(notifiedObservers, "Should indicate if no observers were notified"); +} - id appEvents = OCMClassMock([FBSDKAppEvents class]); +- (void)testAppEventsEnabled +{ + [self stubIsAutoLogAppEventsEnabled:YES]; + OCMStub(ClassMethod([self.appEventsMock activateApp])); id notification = OCMClassMock([NSNotification class]); [_delegate applicationDidBecomeActive:notification]; - OCMVerify([appEvents activateApp]); + OCMVerify([self.appEventsMock activateApp]); } --(void)testAppEventsDisabled { +- (void)testAppEventsDisabled +{ + [self stubIsAutoLogAppEventsEnabled:NO]; - [OCMStub(ClassMethod([_settingsMock isAutoLogAppEventsEnabled])) andReturnValue: OCMOCK_VALUE(NO)]; - - id appEvents = OCMStrictClassMock([FBSDKAppEvents class]); - OCMReject([appEvents activateApp]); + OCMReject([self.appEventsMock activateApp]); + OCMStub(ClassMethod([self.appEventsMock activateApp])); id notification = OCMClassMock([NSNotification class]); [_delegate applicationDidBecomeActive:notification]; } + +- (void)testAppNotifyObserversWhenAppWillResignActive +{ + id observer = OCMStrictProtocolMock(@protocol(FBSDKApplicationObserving)); + [_delegate addObserver:observer]; + + NSNotification *notification = OCMClassMock([NSNotification class]); + id application = OCMClassMock([UIApplication class]); + [OCMStub([notification object]) andReturn:application]; + OCMExpect([observer applicationWillResignActive:application]); + + [_delegate applicationWillResignActive:notification]; + + OCMVerify([observer applicationWillResignActive:application]); +} + @end diff --git a/FBSDKCoreKit/FBSDKCoreKitTests/FBSDKAuthenticationTokenTests.m b/FBSDKCoreKit/FBSDKCoreKitTests/FBSDKAuthenticationTokenTests.m new file mode 100644 index 0000000000..7a858d536a --- /dev/null +++ b/FBSDKCoreKit/FBSDKCoreKitTests/FBSDKAuthenticationTokenTests.m @@ -0,0 +1,119 @@ +// 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 "FBSDKCoreKit+Internal.h" +#import "FBSDKCoreKitTests-Swift.h" +#import "FBSDKTestCase.h" +#import "FBSDKTestCoder.h" + +@interface FBSDKAuthenticationTokenTests : FBSDKTestCase + +@end + +@implementation FBSDKAuthenticationTokenTests +{ + FBSDKAuthenticationToken *_token; + NotificationCenterSpy *_notificationCenterSpy; +} + +- (void)setUp +{ + [super setUp]; + + _notificationCenterSpy = [NotificationCenterSpy new]; + [self stubDefaultNotificationCenterWith:_notificationCenterSpy]; +} + +// MARK: - Persistence + +- (void)testRetrievingCurrentToken +{ + FakeTokenCache *cache = [[FakeTokenCache alloc] initWithAccessToken:nil authenticationToken:nil]; + _token = SampleAuthenticationToken.validToken; + id partialTokenMock = OCMPartialMock(_token); + OCMStub([partialTokenMock tokenCache]).andReturn(cache); + + FBSDKAuthenticationToken.currentAuthenticationToken = _token; + XCTAssertEqualObjects( + cache.authenticationToken, + _token, + "Setting the global authentication token should invoke the cache" + ); + + [partialTokenMock stopMocking]; + partialTokenMock = nil; +} + +- (void)testEncoding +{ + NSString *expectedTokenString = @"expectedTokenString"; + NSString *expectedNonce = @"expectedNonce"; + NSString *expectedJTI = @"expectedJTI"; + + FBSDKTestCoder *coder = [FBSDKTestCoder new]; + _token = [[FBSDKAuthenticationToken alloc] initWithTokenString:expectedTokenString + nonce:expectedNonce + claims:nil + jti:expectedJTI]; + [_token encodeWithCoder:coder]; + + XCTAssertEqualObjects( + coder.encodedObject[@"FBSDKAuthenticationTokenTokenStringCodingKey"], + expectedTokenString, + @"Should encode the expected token string" + ); + XCTAssertEqualObjects( + coder.encodedObject[@"FBSDKAuthenticationTokenNonceCodingKey"], + expectedNonce, + @"Should encode the expected nonce string" + ); + XCTAssertEqualObjects( + coder.encodedObject[@"FBSDKAuthenticationTokenJtiCodingKey"], + expectedJTI, + @"Should encode the expected JTI" + ); +} + +- (void)testDecodingEntryWithMethodName +{ + FBSDKTestCoder *coder = [FBSDKTestCoder new]; + _token = [[FBSDKAuthenticationToken alloc] initWithCoder:coder]; + + XCTAssertEqualObjects( + coder.decodedObject[@"FBSDKAuthenticationTokenTokenStringCodingKey"], + [NSString class], + @"Initializing from a decoder should attempt to decode a String for the token string key" + ); + XCTAssertEqualObjects( + coder.decodedObject[@"FBSDKAuthenticationTokenNonceCodingKey"], + [NSString class], + @"Initializing from a decoder should attempt to decode a String for the nonce key" + ); + XCTAssertEqualObjects( + coder.decodedObject[@"FBSDKAuthenticationTokenJtiCodingKey"], + [NSString class], + @"Initializing from a decoder should attempt to decode a String for the jti key" + ); +} + +@end diff --git a/FBSDKCoreKit/FBSDKCoreKitTests/FBSDKCoreKitTestUtility.h b/FBSDKCoreKit/FBSDKCoreKitTests/FBSDKCoreKitTestUtility.h index b679315ad0..9814b33e70 100644 --- a/FBSDKCoreKit/FBSDKCoreKitTests/FBSDKCoreKitTestUtility.h +++ b/FBSDKCoreKit/FBSDKCoreKitTests/FBSDKCoreKitTestUtility.h @@ -18,6 +18,17 @@ #import +/// OCMock helper for testing methods with non-object args. +#define OCMStubIgnoringNonObjectArgs(invocation) \ + ({ \ + _OCMSilenceWarnings( \ + [OCMMacroState beginStubMacro]; \ + [[[OCMMacroState globalState] recorder] ignoringNonObjectArgs]; \ + invocation; \ + [OCMMacroState endStubMacro]; \ + ); \ +}) + @interface FBSDKCoreKitTestUtility : NSObject /** diff --git a/FBSDKCoreKit/FBSDKCoreKitTests/FBSDKCoreKitTests-Bridging-Header.h b/FBSDKCoreKit/FBSDKCoreKitTests/FBSDKCoreKitTests-Bridging-Header.h index 21d5f8989f..1a6bf81ea9 100644 --- a/FBSDKCoreKit/FBSDKCoreKitTests/FBSDKCoreKitTests-Bridging-Header.h +++ b/FBSDKCoreKit/FBSDKCoreKitTests/FBSDKCoreKitTests-Bridging-Header.h @@ -17,3 +17,80 @@ // CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. #import + +#import "FBSDKAppEventsConfigurationFixtures.h" +#import "FBSDKBridgeAPI+Testing.h" +#import "FBSDKCloseIcon.h" +#import "FBSDKEventDeactivationManager.h" +#import "FBSDKSKAdNetworkEvent.h" +#import "FBSDKSKAdNetworkRule.h" +#import "FBSDKServerConfigurationFixtures.h" +#import "FBSDKSessionProviding.h" +#import "FBSDKTestCase.h" +#import "FakeBundle.h" + +NS_ASSUME_NONNULL_BEGIN + +// Interfaces for Swift extensions on Objective-C Test classes +@interface FBSDKAppEventsUtilityTests : FBSDKTestCase +@end + +// Categories needed to expose private methods to Swift +@interface FBSDKAppEventsConfigurationManager (Testing) + ++ (void)_processResponse:(id)response error:(nullable NSError *)error; + +@end + +@interface FBSDKCloseIcon (Testing) + +- (nullable UIImage *)imageWithSize:(CGSize)size + primaryColor:(UIColor *)primaryColor + secondaryColor:(UIColor *)secondaryColor + scale:(CGFloat)scale; + +@end + +NS_SWIFT_NAME(FBProfilePictureViewState) +@interface FBSDKProfilePictureViewState +@end + +@interface FBSDKProfilePictureView (Testing) + +- (void)_accessTokenDidChangeNotification:(NSNotification *)notification; +- (void)_profileDidChangeNotification:(NSNotification *)notification; +- (void)_updateImageWithProfile; +- (void)_updateImageWithAccessToken; +- (void)_updateImage; +- (void)_fetchAndSetImageWithURL:(NSURL *)imageURL state:(FBSDKProfilePictureViewState *)state; +- (nullable FBSDKProfilePictureViewState *)lastState; + +@end + +@interface FBSDKAccessToken (Testing) + ++ (void)setCurrentAccessToken:(nullable FBSDKAccessToken *)token + shouldDispatchNotif:(BOOL)shouldDispatchNotif; + +@end + +@interface FBSDKProfile (Testing) + ++ (void)setCurrentProfile:(nullable FBSDKProfile *)profile + shouldPostNotification:(BOOL)shouldPostNotification; + +@end + +@interface FBSDKAuthenticationToken (Testing) + +- (instancetype)initWithTokenString:(NSString *)tokenString + nonce:(NSString *)nonce + claims:(nullable FBSDKAuthenticationTokenClaims *)claims + jti:(NSString *)jti; + ++ (void)setCurrentAuthenticationToken:(nullable FBSDKAuthenticationToken *)token + shouldPostNotification:(BOOL)shouldPostNotification; + +@end + +NS_ASSUME_NONNULL_END diff --git a/FBSDKCoreKit/FBSDKCoreKitTests/FBSDKGraphRequestConnectionTests.m b/FBSDKCoreKit/FBSDKCoreKitTests/FBSDKGraphRequestConnectionTests.m index eba1ec80f8..8e25188e40 100644 --- a/FBSDKCoreKit/FBSDKCoreKitTests/FBSDKGraphRequestConnectionTests.m +++ b/FBSDKCoreKit/FBSDKCoreKitTests/FBSDKGraphRequestConnectionTests.m @@ -16,30 +16,33 @@ // 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 #import #import "FBSDKCoreKit.h" #import "FBSDKCoreKitTestUtility.h" +#import "FBSDKFeatureManager.h" #import "FBSDKGraphRequest+Internal.h" #import "FBSDKGraphRequestPiggybackManager.h" #import "FBSDKSettings+Internal.h" +#import "FBSDKTestCase.h" + +@interface FBSDKGraphRequestConnection (Testing) + +- (NSMutableURLRequest *)requestWithBatch:(NSArray *)requests + timeout:(NSTimeInterval)timeout; -@interface FBSDKGraphRequestConnectionTests : XCTestCase { - id _settingsMock; -} -@property (nonatomic, copy) void (^requestConnectionStartingCallback)(FBSDKGraphRequestConnection *connection); -@property (nonatomic, copy) void (^requestConnectionCallback)(FBSDKGraphRequestConnection *connection, NSError *error); @end -static id g_mockNSBundle; +@interface FBSDKGraphRequestConnectionTests : FBSDKTestCase -static NSString *const _mockMobileAppInstallEventName = @"MOBILE_APP_INSTALL"; +@property (nonatomic, copy) void (^requestConnectionStartingCallback)(FBSDKGraphRequestConnection *connection); +@property (nonatomic, copy) void (^requestConnectionCallback)(FBSDKGraphRequestConnection *connection, NSError *error); +@end @implementation FBSDKGraphRequestConnectionTests @@ -47,24 +50,25 @@ @implementation FBSDKGraphRequestConnectionTests - (void)setUp { - [FBSDKSettings setAppID:@"appid"]; - [FBSDKApplicationDelegate initializeSDK:nil]; - g_mockNSBundle = [FBSDKCoreKitTestUtility mainBundleMock]; - _settingsMock = OCMStrictClassMock([FBSDKSettings class]); + [super setUp]; + + [self stubAppID:self.appID]; + [self stubCheckingFeatures]; + [self stubIsSDKInitialized:YES]; + [self stubLoadingGateKeepers]; + [self stubFetchingCachedServerConfiguration]; } - (void)tearDown { + [super tearDown]; + [OHHTTPStubs removeAllStubs]; - [g_mockNSBundle stopMocking]; - g_mockNSBundle = nil; - [_settingsMock stopMocking]; - _settingsMock = nil; } #pragma mark - Helpers -//to prevent piggybacking of server config fetching +// to prevent piggybacking of server config fetching + (id)mockCachedServerConfiguration { id mockPiggybackManager = [OCMockObject niceMockForClass:[FBSDKGraphRequestPiggybackManager class]]; @@ -74,7 +78,6 @@ + (id)mockCachedServerConfiguration #pragma mark - FBSDKGraphRequestConnectionDelegate - - (void)requestConnection:(FBSDKGraphRequestConnection *)connection didFailWithError:(NSError *)error { if (self.requestConnectionCallback) { @@ -104,28 +107,28 @@ - (void)requestConnectionWillBeginLoading:(FBSDKGraphRequestConnection *)connect - (void)_testClientToken { // if it's a batch request the body will be zipped so make sure we don't do that - id mockUtility = [OCMockObject niceMockForClass:[FBSDKBasicUtility class]]; + id mockUtility = [OCMockObject niceMockForClass:[FBSDKBasicUtility class]]; [[[mockUtility stub] andReturn:nil] gzip:[OCMArg any]]; XCTestExpectation *exp = [self expectationWithDescription:@"completed request"]; - [FBSDKAccessToken setCurrentAccessToken:nil]; - [FBSDKSettings setClientToken:@"clienttoken"]; - [OHHTTPStubs stubRequestsPassingTest:^BOOL(NSURLRequest *request) { - // If it's a batch request, the token will be in the body. If it's a single request it will be in the url - // we should check that it's in one or the other. - NSString *url = request.URL.absoluteString; - NSString *body = [[NSString alloc] initWithData:request.OHHTTPStubs_HTTPBody encoding:NSUTF8StringEncoding]; - u_long tokenLength = ([body rangeOfString:@"access_token"].length + [url rangeOfString:@"access_token"].length); - XCTAssertTrue(tokenLength > 0); - return YES; - } withStubResponse:^OHHTTPStubsResponse *(NSURLRequest *request) { - NSData *data = [@"{\"error\": {\"message\": \"Token is broke\",\"code\": 190,\"error_subcode\": 463, \"type\":\"OAuthException\"}}" dataUsingEncoding:NSUTF8StringEncoding]; - - return [OHHTTPStubsResponse responseWithData:data - statusCode:400 - headers:nil]; - }]; - [[[FBSDKGraphRequest alloc] initWithGraphPath:@"me" parameters:@{@"fields":@""}] startWithCompletionHandler:^(FBSDKGraphRequestConnection *connection, id result, NSError *error) { + [self stubCurrentAccessTokenWith:nil]; + [self stubClientTokenWith:@"clienttoken"]; + [OHHTTPStubs stubRequestsPassingTest:^BOOL (NSURLRequest *request) { + // If it's a batch request, the token will be in the body. If it's a single request it will be in the url + // we should check that it's in one or the other. + NSString *url = request.URL.absoluteString; + NSString *body = [[NSString alloc] initWithData:request.OHHTTPStubs_HTTPBody encoding:NSUTF8StringEncoding]; + u_long tokenLength = ([body rangeOfString:@"access_token"].length + [url rangeOfString:@"access_token"].length); + XCTAssertTrue(tokenLength > 0); + return YES; + } withStubResponse:^OHHTTPStubsResponse *(NSURLRequest *request) { + NSData *data = [@"{\"error\": {\"message\": \"Token is broke\",\"code\": 190,\"error_subcode\": 463, \"type\":\"OAuthException\"}}" dataUsingEncoding:NSUTF8StringEncoding]; + + return [OHHTTPStubsResponse responseWithData:data + statusCode:400 + headers:nil]; + }]; + [[[FBSDKGraphRequest alloc] initWithGraphPath:@"me" parameters:@{@"fields" : @""}] startWithCompletionHandler:^(FBSDKGraphRequestConnection *connection, id result, NSError *error) { // make sure there is no recovery info for client token failures. XCTAssertNil(error.localizedRecoverySuggestion); [exp fulfill]; @@ -141,17 +144,17 @@ - (void)testClientTokenSkipped XCTestExpectation *exp = [self expectationWithDescription:@"completed request"]; [FBSDKAccessToken setCurrentAccessToken:nil]; [FBSDKSettings setClientToken:@"clienttoken"]; - [OHHTTPStubs stubRequestsPassingTest:^BOOL(NSURLRequest *request) { - XCTAssertTrue([[request.URL absoluteString] rangeOfString:@"access_token"].location == NSNotFound); - return YES; - } withStubResponse:^OHHTTPStubsResponse *(NSURLRequest *request) { - NSData *data = [@"{\"error\": {\"message\": \"Token is broke\",\"code\": 190,\"error_subcode\": 463}}" dataUsingEncoding:NSUTF8StringEncoding]; - - return [OHHTTPStubsResponse responseWithData:data - statusCode:400 - headers:nil]; - }]; - [[[FBSDKGraphRequest alloc] initWithGraphPath:@"me" parameters:@{@"fields":@""} flags:FBSDKGraphRequestFlagSkipClientToken] + [OHHTTPStubs stubRequestsPassingTest:^BOOL (NSURLRequest *request) { + XCTAssertTrue([[request.URL absoluteString] rangeOfString:@"access_token"].location == NSNotFound); + return YES; + } withStubResponse:^OHHTTPStubsResponse *(NSURLRequest *request) { + NSData *data = [@"{\"error\": {\"message\": \"Token is broke\",\"code\": 190,\"error_subcode\": 463}}" dataUsingEncoding:NSUTF8StringEncoding]; + + return [OHHTTPStubsResponse responseWithData:data + statusCode:400 + headers:nil]; + }]; + [[[FBSDKGraphRequest alloc] initWithGraphPath:@"me" parameters:@{@"fields" : @""} flags:FBSDKGraphRequestFlagSkipClientToken] startWithCompletionHandler:^(FBSDKGraphRequestConnection *connection, id result, NSError *error) { [exp fulfill]; }]; @@ -165,24 +168,24 @@ - (void)testConnectionDelegate { id mockPiggybackManager = [[self class] mockCachedServerConfiguration]; // stub out a batch response that returns /me.id twice - [OHHTTPStubs stubRequestsPassingTest:^BOOL(NSURLRequest *request) { - return YES; - } withStubResponse:^OHHTTPStubsResponse *(NSURLRequest *request) { - NSString *meResponse = [@"{ \"id\":\"userid\"}" stringByReplacingOccurrencesOfString:@"\"" withString:@"\\\""]; - NSString *responseString = [NSString stringWithFormat:@"[ {\"code\":200,\"body\": \"%@\" }, {\"code\":200,\"body\": \"%@\" } ]", meResponse, meResponse]; - NSData *data = [responseString dataUsingEncoding:NSUTF8StringEncoding]; - return [OHHTTPStubsResponse responseWithData:data - statusCode:200 - headers:nil]; - }]; + [OHHTTPStubs stubRequestsPassingTest:^BOOL (NSURLRequest *request) { + return YES; + } withStubResponse:^OHHTTPStubsResponse *(NSURLRequest *request) { + NSString *meResponse = [@"{ \"id\":\"userid\"}" stringByReplacingOccurrencesOfString:@"\"" withString:@"\\\""]; + NSString *responseString = [NSString stringWithFormat:@"[ {\"code\":200,\"body\": \"%@\" }, {\"code\":200,\"body\": \"%@\" } ]", meResponse, meResponse]; + NSData *data = [responseString dataUsingEncoding:NSUTF8StringEncoding]; + return [OHHTTPStubsResponse responseWithData:data + statusCode:200 + headers:nil]; + }]; FBSDKGraphRequestConnection *connection = [[FBSDKGraphRequestConnection alloc] init]; __block int actualCallbacksCount = 0; XCTestExpectation *expectation = [self expectationWithDescription:@"expected to receive delegate completion"]; - [connection addRequest:[[FBSDKGraphRequest alloc] initWithGraphPath:@"me" parameters:@{@"fields":@""}] + [connection addRequest:[[FBSDKGraphRequest alloc] initWithGraphPath:@"me" parameters:@{@"fields" : @""}] completionHandler:^(FBSDKGraphRequestConnection *conn, id result, NSError *error) { XCTAssertEqual(1, actualCallbacksCount++, @"this should have been the second callback"); }]; - [connection addRequest:[[FBSDKGraphRequest alloc] initWithGraphPath:@"me" parameters:@{@"fields":@""}] + [connection addRequest:[[FBSDKGraphRequest alloc] initWithGraphPath:@"me" parameters:@{@"fields" : @""}] completionHandler:^(FBSDKGraphRequestConnection *conn, id result, NSError *error) { XCTAssertEqual(2, actualCallbacksCount++, @"this should have been the third callback"); }]; @@ -206,23 +209,23 @@ - (void)testConnectionDelegate - (void)testNonErrorEmptyDictionaryOrNullResponse { id mockPiggybackManager = [[self class] mockCachedServerConfiguration]; - [OHHTTPStubs stubRequestsPassingTest:^BOOL(NSURLRequest *request) { - return YES; - } withStubResponse:^OHHTTPStubsResponse *(NSURLRequest *request) { - NSString *responseString = [NSString stringWithFormat:@"[ {\"code\":200,\"body\": null }, {\"code\":200,\"body\": {} } ]"]; - NSData *data = [responseString dataUsingEncoding:NSUTF8StringEncoding]; - return [OHHTTPStubsResponse responseWithData:data - statusCode:200 - headers:nil]; - }]; + [OHHTTPStubs stubRequestsPassingTest:^BOOL (NSURLRequest *request) { + return YES; + } withStubResponse:^OHHTTPStubsResponse *(NSURLRequest *request) { + NSString *responseString = [NSString stringWithFormat:@"[ {\"code\":200,\"body\": null }, {\"code\":200,\"body\": {} } ]"]; + NSData *data = [responseString dataUsingEncoding:NSUTF8StringEncoding]; + return [OHHTTPStubsResponse responseWithData:data + statusCode:200 + headers:nil]; + }]; FBSDKGraphRequestConnection *connection = [[FBSDKGraphRequestConnection alloc] init]; __block int actualCallbacksCount = 0; XCTestExpectation *expectation = [self expectationWithDescription:@"expected not to crash on null or empty dict responses"]; - [connection addRequest:[[FBSDKGraphRequest alloc] initWithGraphPath:@"me" parameters:@{@"fields":@""}] + [connection addRequest:[[FBSDKGraphRequest alloc] initWithGraphPath:@"me" parameters:@{@"fields" : @""}] completionHandler:^(FBSDKGraphRequestConnection *conn, id result, NSError *error) { XCTAssertEqual(1, actualCallbacksCount++, @"this should have been the second callback"); }]; - [connection addRequest:[[FBSDKGraphRequest alloc] initWithGraphPath:@"me" parameters:@{@"fields":@""}] + [connection addRequest:[[FBSDKGraphRequest alloc] initWithGraphPath:@"me" parameters:@{@"fields" : @""}] completionHandler:^(FBSDKGraphRequestConnection *conn, id result, NSError *error) { XCTAssertEqual(2, actualCallbacksCount++, @"this should have been the third callback"); }]; @@ -246,15 +249,15 @@ - (void)testNonErrorEmptyDictionaryOrNullResponse - (void)testConnectionDelegateWithNetworkError { id mockPiggybackManager = [[self class] mockCachedServerConfiguration]; - [OHHTTPStubs stubRequestsPassingTest:^BOOL(NSURLRequest *request) { - return YES; - } withStubResponse:^OHHTTPStubsResponse *(NSURLRequest *request) { - // stub a response indicating a disconnected network - return [OHHTTPStubsResponse responseWithError:[NSError errorWithDomain:@"NSURLErrorDomain" code:-1009 userInfo:nil]]; - }]; + [OHHTTPStubs stubRequestsPassingTest:^BOOL (NSURLRequest *request) { + return YES; + } withStubResponse:^OHHTTPStubsResponse *(NSURLRequest *request) { + // stub a response indicating a disconnected network + return [OHHTTPStubsResponse responseWithError:[NSError errorWithDomain:@"NSURLErrorDomain" code:-1009 userInfo:nil]]; + }]; FBSDKGraphRequestConnection *connection = [[FBSDKGraphRequestConnection alloc] init]; XCTestExpectation *expectation = [self expectationWithDescription:@"expected to receive network error"]; - [connection addRequest:[[FBSDKGraphRequest alloc] initWithGraphPath:@"me" parameters:@{@"fields":@""}] + [connection addRequest:[[FBSDKGraphRequest alloc] initWithGraphPath:@"me" parameters:@{@"fields" : @""}] completionHandler:^(FBSDKGraphRequestConnection *conn, id result, NSError *error) {}]; self.requestConnectionCallback = ^(FBSDKGraphRequestConnection *conn, NSError *error) { NSCAssert(error != nil, @"didFinishLoading shouldn't have been called"); @@ -275,23 +278,23 @@ - (void)testTokenPiggyback id mockPiggybackManager = [[self class] mockCachedServerConfiguration]; [FBSDKAccessToken setCurrentAccessToken:nil]; // use stubs because test tokens are not refreshable. - [OHHTTPStubs stubRequestsPassingTest:^BOOL(NSURLRequest *request) { - return YES; - } withStubResponse:^OHHTTPStubsResponse *(NSURLRequest *request) { - NSString *meResponse = [@"{ \"id\":\"userid\"}" stringByReplacingOccurrencesOfString:@"\"" withString:@"\\\""]; - NSString *refreshResponse = [[NSString stringWithFormat:@"{ \"access_token\":\"123\", \"expires_at\":%.0f }", [NSDate dateWithTimeIntervalSinceNow:60].timeIntervalSince1970] stringByReplacingOccurrencesOfString:@"\"" withString:@"\\\""]; - NSString *permissionsResponse = [@"{ \"data\": [ { \"permission\" : \"public_profile\", \"status\" : \"granted\" }, { \"permission\" : \"email\", \"status\" : \"granted\" }, { \"permission\" : \"user_friends\", \"status\" : \"declined\" } ] }" stringByReplacingOccurrencesOfString:@"\"" withString:@"\\\""]; - NSString *responseString = [NSString stringWithFormat:@"[ {\"code\":200,\"body\": \"%@\" }," - @"{\"code\":200,\"body\": \"%@\" }," - @"{\"code\":200,\"body\": \"%@\" } ]", - meResponse, - refreshResponse, - permissionsResponse]; - NSData *data = [responseString dataUsingEncoding:NSUTF8StringEncoding]; - return [OHHTTPStubsResponse responseWithData:data - statusCode:200 - headers:nil]; - }]; + [OHHTTPStubs stubRequestsPassingTest:^BOOL (NSURLRequest *request) { + return YES; + } withStubResponse:^OHHTTPStubsResponse *(NSURLRequest *request) { + NSString *meResponse = [@"{ \"id\":\"userid\"}" stringByReplacingOccurrencesOfString:@"\"" withString:@"\\\""]; + NSString *refreshResponse = [[NSString stringWithFormat:@"{ \"access_token\":\"123\", \"expires_at\":%.0f }", [NSDate dateWithTimeIntervalSinceNow:60].timeIntervalSince1970] stringByReplacingOccurrencesOfString:@"\"" withString:@"\\\""]; + NSString *permissionsResponse = [@"{ \"data\": [ { \"permission\" : \"public_profile\", \"status\" : \"granted\" }, { \"permission\" : \"email\", \"status\" : \"granted\" }, { \"permission\" : \"user_friends\", \"status\" : \"declined\" } ] }" stringByReplacingOccurrencesOfString:@"\"" withString:@"\\\""]; + NSString *responseString = [NSString stringWithFormat:@"[ {\"code\":200,\"body\": \"%@\" }," + @"{\"code\":200,\"body\": \"%@\" }," + @"{\"code\":200,\"body\": \"%@\" } ]", + meResponse, + refreshResponse, + permissionsResponse]; + NSData *data = [responseString dataUsingEncoding:NSUTF8StringEncoding]; + return [OHHTTPStubsResponse responseWithData:data + statusCode:200 + headers:nil]; + }]; FBSDKAccessToken *tokenThatNeedsRefresh = [[FBSDKAccessToken alloc] initWithTokenString:@"token" permissions:@[] @@ -303,7 +306,7 @@ - (void)testTokenPiggyback refreshDate:[NSDate distantPast] dataAccessExpirationDate:[NSDate distantPast]]; [FBSDKAccessToken setCurrentAccessToken:tokenThatNeedsRefresh]; - FBSDKGraphRequest *request = [[FBSDKGraphRequest alloc] initWithGraphPath:@"me" parameters:@{@"fields":@""}]; + FBSDKGraphRequest *request = [[FBSDKGraphRequest alloc] initWithGraphPath:@"me" parameters:@{@"fields" : @""}]; XCTestExpectation *exp = [self expectationWithDescription:@"completed request"]; [request startWithCompletionHandler:^(FBSDKGraphRequestConnection *connection, id result, NSError *error) { XCTAssertEqualObjects(tokenThatNeedsRefresh.userID, result[@"id"]); @@ -337,19 +340,19 @@ - (void)testTokenPiggybackSkipped refreshDate:[NSDate date] dataAccessExpirationDate:[NSDate distantPast]]; [FBSDKAccessToken setCurrentAccessToken:tokenNoRefresh]; - FBSDKGraphRequest *request = [[FBSDKGraphRequest alloc] initWithGraphPath:@"me" parameters:@{@"fields":@""}]; + FBSDKGraphRequest *request = [[FBSDKGraphRequest alloc] initWithGraphPath:@"me" parameters:@{@"fields" : @""}]; XCTestExpectation *exp = [self expectationWithDescription:@"completed request"]; - [OHHTTPStubs stubRequestsPassingTest:^BOOL(NSURLRequest *r) { - // assert the path of r is "me"; since piggyback would go to root batch endpoint. - XCTAssertTrue([r.URL.path hasSuffix:@"me"]); - return YES; - } withStubResponse:^OHHTTPStubsResponse *(NSURLRequest *r) { - NSString *responseString = @"{ \"id\" : \"userid\"}"; - NSData *data = [responseString dataUsingEncoding:NSUTF8StringEncoding]; - return [OHHTTPStubsResponse responseWithData:data - statusCode:200 - headers:nil]; - }]; + [OHHTTPStubs stubRequestsPassingTest:^BOOL (NSURLRequest *r) { + // assert the path of r is "me"; since piggyback would go to root batch endpoint. + XCTAssertTrue([r.URL.path hasSuffix:@"me"]); + return YES; + } withStubResponse:^OHHTTPStubsResponse *(NSURLRequest *r) { + NSString *responseString = @"{ \"id\" : \"userid\"}"; + NSData *data = [responseString dataUsingEncoding:NSUTF8StringEncoding]; + return [OHHTTPStubsResponse responseWithData:data + statusCode:200 + headers:nil]; + }]; [request startWithCompletionHandler:^(FBSDKGraphRequestConnection *connection, id result, NSError *error) { XCTAssertEqualObjects(tokenNoRefresh.userID, result[@"id"]); [exp fulfill]; @@ -368,7 +371,7 @@ - (void)testUnsettingAccessToken __block int tokenChangeCount = 0; [self expectationForNotification:FBSDKAccessTokenDidChangeNotification object:nil - handler:^BOOL(NSNotification *notification) { + handler:^BOOL (NSNotification *notification) { if (++tokenChangeCount == 2) { XCTAssertNil(notification.userInfo[FBSDKAccessTokenChangeNewKey]); XCTAssertNotNil(notification.userInfo[FBSDKAccessTokenChangeOldKey]); @@ -379,23 +382,23 @@ - (void)testUnsettingAccessToken FBSDKAccessToken *accessToken = [[FBSDKAccessToken alloc] initWithTokenString:@"token" permissions:@[@"public_profile"] declinedPermissions:@[] - expiredPermissions:@[] + expiredPermissions:@[] appID:@"appid" userID:@"userid" expirationDate:nil refreshDate:nil dataAccessExpirationDate:nil]; [FBSDKAccessToken setCurrentAccessToken:accessToken]; - [OHHTTPStubs stubRequestsPassingTest:^BOOL(NSURLRequest *request) { - return YES; - } withStubResponse:^OHHTTPStubsResponse *(NSURLRequest *request) { - NSData *data = [@"{\"error\": {\"message\": \"Token is broke\",\"code\": 190,\"error_subcode\": 463}}" dataUsingEncoding:NSUTF8StringEncoding]; - - return [OHHTTPStubsResponse responseWithData:data - statusCode:400 - headers:nil]; - }]; - [[[FBSDKGraphRequest alloc] initWithGraphPath:@"me" parameters:@{@"fields":@""}] + [OHHTTPStubs stubRequestsPassingTest:^BOOL (NSURLRequest *request) { + return YES; + } withStubResponse:^OHHTTPStubsResponse *(NSURLRequest *request) { + NSData *data = [@"{\"error\": {\"message\": \"Token is broke\",\"code\": 190,\"error_subcode\": 463}}" dataUsingEncoding:NSUTF8StringEncoding]; + + return [OHHTTPStubsResponse responseWithData:data + statusCode:400 + headers:nil]; + }]; + [[[FBSDKGraphRequest alloc] initWithGraphPath:@"me" parameters:@{@"fields" : @""}] startWithCompletionHandler:^(FBSDKGraphRequestConnection *connection, id result, NSError *error) { XCTAssertNil(result); XCTAssertEqualObjects(@"Token is broke", error.userInfo[FBSDKErrorDeveloperMessageKey]); @@ -415,7 +418,7 @@ - (void)testUnsettingAccessTokenSkipped XCTestExpectation *expectation = [self expectationWithDescription:@"completed request"]; [self expectationForNotification:FBSDKAccessTokenDidChangeNotification object:nil - handler:^BOOL(NSNotification *notification) { + handler:^BOOL (NSNotification *notification) { XCTAssertNotNil(notification.userInfo[FBSDKAccessTokenChangeNewKey]); return YES; }]; @@ -430,17 +433,17 @@ - (void)testUnsettingAccessTokenSkipped dataAccessExpirationDate:nil]; [FBSDKAccessToken setCurrentAccessToken:accessToken]; - [OHHTTPStubs stubRequestsPassingTest:^BOOL(NSURLRequest *request) { - return YES; - } withStubResponse:^OHHTTPStubsResponse *(NSURLRequest *request) { - NSData *data = [@"{\"error\": {\"message\": \"Token is broke\",\"code\": 190,\"error_subcode\": 463}}" dataUsingEncoding:NSUTF8StringEncoding]; - - return [OHHTTPStubsResponse responseWithData:data - statusCode:400 - headers:nil]; - }]; + [OHHTTPStubs stubRequestsPassingTest:^BOOL (NSURLRequest *request) { + return YES; + } withStubResponse:^OHHTTPStubsResponse *(NSURLRequest *request) { + NSData *data = [@"{\"error\": {\"message\": \"Token is broke\",\"code\": 190,\"error_subcode\": 463}}" dataUsingEncoding:NSUTF8StringEncoding]; + + return [OHHTTPStubsResponse responseWithData:data + statusCode:400 + headers:nil]; + }]; [[[FBSDKGraphRequest alloc] initWithGraphPath:@"me" - parameters:@{@"fields":@""} + parameters:@{@"fields" : @""} tokenString:@"notCurrentToken" version:nil HTTPMethod:@""] @@ -463,7 +466,7 @@ - (void)testUnsettingAccessTokenFlag XCTestExpectation *expectation = [self expectationWithDescription:@"completed request"]; [self expectationForNotification:FBSDKAccessTokenDidChangeNotification object:nil - handler:^BOOL(NSNotification *notification) { + handler:^BOOL (NSNotification *notification) { XCTAssertNotNil(notification.userInfo[FBSDKAccessTokenChangeNewKey]); return YES; }]; @@ -478,16 +481,16 @@ - (void)testUnsettingAccessTokenFlag dataAccessExpirationDate:nil]; [FBSDKAccessToken setCurrentAccessToken:accessToken]; - [OHHTTPStubs stubRequestsPassingTest:^BOOL(NSURLRequest *request) { - return YES; - } withStubResponse:^OHHTTPStubsResponse *(NSURLRequest *request) { - NSData *data = [@"{\"error\": {\"message\": \"Token is broke\",\"code\": 190,\"error_subcode\": 463}}" dataUsingEncoding:NSUTF8StringEncoding]; - - return [OHHTTPStubsResponse responseWithData:data - statusCode:400 - headers:nil]; - }]; - [[[FBSDKGraphRequest alloc] initWithGraphPath:@"me" parameters:@{@"fields":@""} flags:FBSDKGraphRequestFlagDoNotInvalidateTokenOnError] + [OHHTTPStubs stubRequestsPassingTest:^BOOL (NSURLRequest *request) { + return YES; + } withStubResponse:^OHHTTPStubsResponse *(NSURLRequest *request) { + NSData *data = [@"{\"error\": {\"message\": \"Token is broke\",\"code\": 190,\"error_subcode\": 463}}" dataUsingEncoding:NSUTF8StringEncoding]; + + return [OHHTTPStubsResponse responseWithData:data + statusCode:400 + headers:nil]; + }]; + [[[FBSDKGraphRequest alloc] initWithGraphPath:@"me" parameters:@{@"fields" : @""} flags:FBSDKGraphRequestFlagDoNotInvalidateTokenOnError] startWithCompletionHandler:^(FBSDKGraphRequestConnection *connection, id result, NSError *error) { XCTAssertNil(result); XCTAssertEqualObjects(@"Token is broke", error.userInfo[FBSDKErrorDeveloperMessageKey]); @@ -504,40 +507,40 @@ - (void)testUnsettingAccessTokenFlag - (void)testUserAgentSuffix { // Disable compressing network request - id mockUtility = [OCMockObject niceMockForClass:[FBSDKBasicUtility class]]; + id mockUtility = [OCMockObject niceMockForClass:[FBSDKBasicUtility class]]; [[[mockUtility stub] andReturn:nil] gzip:[OCMArg any]]; XCTestExpectation *exp = [self expectationWithDescription:@"completed request"]; XCTestExpectation *exp2 = [self expectationWithDescription:@"completed request 2"]; [FBSDKAccessToken setCurrentAccessToken:nil]; [FBSDKSettings setUserAgentSuffix:@"UnitTest.1.0.0"]; - [OHHTTPStubs stubRequestsPassingTest:^BOOL(NSURLRequest *request) { - NSString *actualUserAgent = [request valueForHTTPHeaderField:@"User-Agent"]; - NSString *body = [[NSString alloc] initWithData:request.OHHTTPStubs_HTTPBody encoding:NSUTF8StringEncoding]; - if ([body containsString:@"with_suffix"] || [body containsString:@"without_suffix"]) { - BOOL expectUserAgentSuffix = [body containsString:@"fields=with_suffix"]; - if (expectUserAgentSuffix) { - XCTAssertTrue([actualUserAgent hasSuffix:@"/UnitTest.1.0.0"], @"unexpected user agent %@", actualUserAgent); - } else { - XCTAssertFalse([actualUserAgent hasSuffix:@"/UnitTest.1.0.0"], @"unexpected user agent %@", actualUserAgent); - } - } - - return YES; - } withStubResponse:^OHHTTPStubsResponse *(NSURLRequest *request) { - NSData *data = [@"{\"error\": {\"message\": \"Missing oktne\",\"code\": 190, \"type\":\"OAuthException\"}}" dataUsingEncoding:NSUTF8StringEncoding]; - - return [OHHTTPStubsResponse responseWithData:data - statusCode:400 - headers:nil]; - }]; - [[[FBSDKGraphRequest alloc] initWithGraphPath:@"me" parameters:@{@"fields":@"with_suffix"}] startWithCompletionHandler:^(FBSDKGraphRequestConnection *connection, id result, NSError *error) { + [OHHTTPStubs stubRequestsPassingTest:^BOOL (NSURLRequest *request) { + NSString *actualUserAgent = [request valueForHTTPHeaderField:@"User-Agent"]; + NSString *body = [[NSString alloc] initWithData:request.OHHTTPStubs_HTTPBody encoding:NSUTF8StringEncoding]; + if ([body containsString:@"with_suffix"] || [body containsString:@"without_suffix"]) { + BOOL expectUserAgentSuffix = [body containsString:@"fields=with_suffix"]; + if (expectUserAgentSuffix) { + XCTAssertTrue([actualUserAgent hasSuffix:@"/UnitTest.1.0.0"], @"unexpected user agent %@", actualUserAgent); + } else { + XCTAssertFalse([actualUserAgent hasSuffix:@"/UnitTest.1.0.0"], @"unexpected user agent %@", actualUserAgent); + } + } + + return YES; + } withStubResponse:^OHHTTPStubsResponse *(NSURLRequest *request) { + NSData *data = [@"{\"error\": {\"message\": \"Missing oktne\",\"code\": 190, \"type\":\"OAuthException\"}}" dataUsingEncoding:NSUTF8StringEncoding]; + + return [OHHTTPStubsResponse responseWithData:data + statusCode:400 + headers:nil]; + }]; + [[[FBSDKGraphRequest alloc] initWithGraphPath:@"me" parameters:@{@"fields" : @"with_suffix"}] startWithCompletionHandler:^(FBSDKGraphRequestConnection *connection, id result, NSError *error) { [exp fulfill]; }]; [FBSDKSettings setUserAgentSuffix:nil]; // issue a second request o verify clearing out of user agent suffix, passing a field=name to uniquely identify the request. - [[[FBSDKGraphRequest alloc] initWithGraphPath:@"me" parameters:@{@"fields":@"without_suffix"}] startWithCompletionHandler:^(FBSDKGraphRequestConnection *connection, id result, NSError *error) { + [[[FBSDKGraphRequest alloc] initWithGraphPath:@"me" parameters:@{@"fields" : @"without_suffix"}] startWithCompletionHandler:^(FBSDKGraphRequestConnection *connection, id result, NSError *error) { [exp2 fulfill]; }]; @@ -553,14 +556,14 @@ - (void)testNonDictionaryInError [FBSDKAccessToken setCurrentAccessToken:nil]; [FBSDKSettings setClientToken:@"clienttoken"]; - [OHHTTPStubs stubRequestsPassingTest:^BOOL(NSURLRequest *request) { - return YES; - } withStubResponse:^OHHTTPStubsResponse *(NSURLRequest *request) { - NSData *data = [@"{\"error\": \"a-non-dictionary\"}" dataUsingEncoding:NSUTF8StringEncoding]; - return [OHHTTPStubsResponse responseWithData:data - statusCode:200 - headers:nil]; - }]; + [OHHTTPStubs stubRequestsPassingTest:^BOOL (NSURLRequest *request) { + return YES; + } withStubResponse:^OHHTTPStubsResponse *(NSURLRequest *request) { + NSData *data = [@"{\"error\": \"a-non-dictionary\"}" dataUsingEncoding:NSUTF8StringEncoding]; + return [OHHTTPStubsResponse responseWithData:data + statusCode:200 + headers:nil]; + }]; // adding fresh token to avoid piggybacking a token refresh FBSDKAccessToken *tokenNoRefresh = [[FBSDKAccessToken alloc] @@ -575,7 +578,7 @@ - (void)testNonDictionaryInError dataAccessExpirationDate:[NSDate distantPast]]; [FBSDKAccessToken setCurrentAccessToken:tokenNoRefresh]; - [[[FBSDKGraphRequest alloc] initWithGraphPath:@"me" parameters:@{@"fields":@""}] startWithCompletionHandler:^(FBSDKGraphRequestConnection *connection, id result, NSError *error) { + [[[FBSDKGraphRequest alloc] initWithGraphPath:@"me" parameters:@{@"fields" : @""}] startWithCompletionHandler:^(FBSDKGraphRequestConnection *connection, id result, NSError *error) { // should not crash when receiving something other than a dictionary within the response. [exp fulfill]; }]; @@ -585,6 +588,40 @@ - (void)testNonDictionaryInError [mockPiggybackManager stopMocking]; } +- (void)testRequestWithBatchConstructionWithSingleGetRequest +{ + FBSDKGraphRequest *singleRequest = [[FBSDKGraphRequest alloc] initWithGraphPath:@"me" parameters:@{@"fields" : @"with_suffix"}]; + FBSDKGraphRequestConnection *connection = [FBSDKGraphRequestConnection new]; + [connection addRequest:singleRequest completionHandler:^(FBSDKGraphRequestConnection *_Nullable _connection, id _Nullable result, NSError *_Nullable error) {}]; + NSURLRequest *request = [connection requestWithBatch:connection.requests timeout:0]; + + NSURLComponents *urlComponents = [NSURLComponents componentsWithString:request.URL.absoluteString]; + XCTAssertEqualObjects(urlComponents.host, @"graph.facebook.com"); + XCTAssertTrue([urlComponents.path containsString:@"me"]); + XCTAssertEqualObjects(request.HTTPMethod, @"GET"); + XCTAssertTrue(request.HTTPBody.length == 0); + XCTAssertEqualObjects([request valueForHTTPHeaderField:@"Content-Type"], @"application/json"); +} + +- (void)testRequestWithBatchConstructionWithSinglePostRequest +{ + NSDictionary *parameters = @{ + @"first_key" : @"first_value", + }; + FBSDKGraphRequest *singleRequest = [[FBSDKGraphRequest alloc] initWithGraphPath:@"activities" parameters:parameters HTTPMethod:FBSDKHTTPMethodPOST]; + FBSDKGraphRequestConnection *connection = [FBSDKGraphRequestConnection new]; + [connection addRequest:singleRequest completionHandler:^(FBSDKGraphRequestConnection *_Nullable _connection, id _Nullable result, NSError *_Nullable error) {}]; + NSURLRequest *request = [connection requestWithBatch:connection.requests timeout:0]; + + NSURLComponents *urlComponents = [NSURLComponents componentsWithString:request.URL.absoluteString]; + XCTAssertEqualObjects(urlComponents.host, @"graph.facebook.com"); + XCTAssertTrue([urlComponents.path containsString:@"activities"]); + XCTAssertEqualObjects(request.HTTPMethod, @"POST"); + XCTAssertTrue(request.HTTPBody.length > 0); + XCTAssertEqualObjects([request valueForHTTPHeaderField:@"Content-Encoding"], @"gzip"); + XCTAssertEqualObjects([request valueForHTTPHeaderField:@"Content-Type"], @"application/json"); +} + #pragma mark - Error recovery. // verify we do a single retry. @@ -593,21 +630,21 @@ - (void)testRetry id mockPiggybackManager = [[self class] mockCachedServerConfiguration]; __block int requestCount = 0; XCTestExpectation *expectation = [self expectationWithDescription:@"completed request"]; - [OHHTTPStubs stubRequestsPassingTest:^BOOL(NSURLRequest *request) { - return YES; - } withStubResponse:^OHHTTPStubsResponse *(NSURLRequest *request) { - XCTAssertLessThanOrEqual(++requestCount, 2); - NSString *responseJSON = (requestCount == 1 ? - @"{\"error\": {\"message\": \"Server is busy\",\"code\": 1,\"error_subcode\": 463}}" - : @"{\"error\": {\"message\": \"Server is busy\",\"code\": 2,\"error_subcode\": 463}}" ); - NSData *data = [responseJSON dataUsingEncoding:NSUTF8StringEncoding]; - - return [OHHTTPStubsResponse responseWithData:data - statusCode:400 - headers:nil]; - }]; - [[[FBSDKGraphRequest alloc] initWithGraphPath:@"me" parameters:@{@"fields":@""}] startWithCompletionHandler:^(FBSDKGraphRequestConnection *connection, id result, NSError *error) { - //verify we get the second error instance. + [OHHTTPStubs stubRequestsPassingTest:^BOOL (NSURLRequest *request) { + return YES; + } withStubResponse:^OHHTTPStubsResponse *(NSURLRequest *request) { + XCTAssertLessThanOrEqual(++requestCount, 2); + NSString *responseJSON = (requestCount == 1 + ? @"{\"error\": {\"message\": \"Server is busy\",\"code\": 1,\"error_subcode\": 463}}" + : @"{\"error\": {\"message\": \"Server is busy\",\"code\": 2,\"error_subcode\": 463}}"); + NSData *data = [responseJSON dataUsingEncoding:NSUTF8StringEncoding]; + + return [OHHTTPStubsResponse responseWithData:data + statusCode:400 + headers:nil]; + }]; + [[[FBSDKGraphRequest alloc] initWithGraphPath:@"me" parameters:@{@"fields" : @""}] startWithCompletionHandler:^(FBSDKGraphRequestConnection *connection, id result, NSError *error) { + // verify we get the second error instance. XCTAssertEqual(2, [error.userInfo[FBSDKGraphRequestErrorGraphErrorCodeKey] integerValue]); [expectation fulfill]; }]; @@ -626,24 +663,24 @@ - (void)_testRetryDisabled __block int requestCount = 0; XCTestExpectation *expectation = [self expectationWithDescription:@"completed request"]; - [OHHTTPStubs stubRequestsPassingTest:^BOOL(NSURLRequest *request) { - return YES; - } withStubResponse:^OHHTTPStubsResponse *(NSURLRequest *request) { - XCTAssertLessThanOrEqual(++requestCount, 1); - NSString *responseJSON = (requestCount == 1 ? - @"{\"error\": {\"message\": \"Server is busy\",\"code\": 1,\"error_subcode\": 463}}" - : @"{\"error\": {\"message\": \"Server is busy\",\"code\": 2,\"error_subcode\": 463}}" ); - NSData *data = [responseJSON dataUsingEncoding:NSUTF8StringEncoding]; - - return [OHHTTPStubsResponse responseWithData:data - statusCode:400 - headers:nil]; - }]; - - FBSDKGraphRequest *request = [[FBSDKGraphRequest alloc] initWithGraphPath:@"me" parameters:@{@"fields":@""}]; + [OHHTTPStubs stubRequestsPassingTest:^BOOL (NSURLRequest *request) { + return YES; + } withStubResponse:^OHHTTPStubsResponse *(NSURLRequest *request) { + XCTAssertLessThanOrEqual(++requestCount, 1); + NSString *responseJSON = (requestCount == 1 + ? @"{\"error\": {\"message\": \"Server is busy\",\"code\": 1,\"error_subcode\": 463}}" + : @"{\"error\": {\"message\": \"Server is busy\",\"code\": 2,\"error_subcode\": 463}}"); + NSData *data = [responseJSON dataUsingEncoding:NSUTF8StringEncoding]; + + return [OHHTTPStubsResponse responseWithData:data + statusCode:400 + headers:nil]; + }]; + + FBSDKGraphRequest *request = [[FBSDKGraphRequest alloc] initWithGraphPath:@"me" parameters:@{@"fields" : @""}]; [request startWithCompletionHandler:^(FBSDKGraphRequestConnection *connection, id result, NSError *error) { - //verify we don't get the second error instance. + // verify we don't get the second error instance. XCTAssertEqual(1, [error.userInfo[FBSDKGraphRequestErrorGraphErrorCodeKey] integerValue]); [expectation fulfill]; }]; @@ -656,40 +693,4 @@ - (void)_testRetryDisabled [mockPiggybackManager stopMocking]; } -//- (void)testGraphRequestWithIDFATrackingEnabled -//{ -// id mockUtility = [OCMockObject niceMockForClass:[FBSDKBasicUtility class]]; -// [[[mockUtility stub] andReturn:nil] gzip:[OCMArg any]]; -// [OCMStub(ClassMethod([_settingsMock isAdvertiserIDCollectionEnabled])) andReturnValue: OCMOCK_VALUE(YES)]; -// -// XCTestExpectation *exp = [self expectationWithDescription:@"completed request"]; -// -// [OHHTTPStubs stubRequestsPassingTest:^BOOL(NSURLRequest *request) { -// NSString *body = [[NSString alloc] initWithData:request.OHHTTPStubs_HTTPBody encoding:NSUTF8StringEncoding]; -// XCTAssertTrue([body containsString:_mockMobileAppInstallEventName]); -// XCTAssertTrue([body containsString:@"advertiser_tracking_enabled"]); -// XCTAssertTrue([body containsString:@"application_tracking_enabled"]); -// XCTAssertTrue([body containsString:@"advertiser_id"]); -// return NO; -// } withStubResponse:^OHHTTPStubsResponse *(NSURLRequest *request) { -// return [OHHTTPStubsResponse responseWithData:[NSData data] -// statusCode:200 -// headers:nil]; -// }]; -// NSMutableDictionary *params = [FBSDKAppEventsUtility activityParametersDictionaryForEvent:_mockMobileAppInstallEventName -// shouldAccessAdvertisingID:YES]; -// [[[FBSDKGraphRequest alloc] initWithGraphPath:[NSString stringWithFormat:@"%@/activities", @"mockAppID"] -// parameters:params -// tokenString:nil -// HTTPMethod:FBSDKHTTPMethodPOST -// flags:FBSDKGraphRequestFlagDoNotInvalidateTokenOnError | FBSDKGraphRequestFlagDisableErrorRecovery] startWithCompletionHandler:^(FBSDKGraphRequestConnection *connection, id result, NSError *error) { -// [exp fulfill]; -// }]; -// -// [self waitForExpectationsWithTimeout:5 handler:^(NSError *error) { -// XCTAssertNil(error); -// }]; -// [mockUtility stopMocking]; -//} - @end diff --git a/FBSDKCoreKit/FBSDKCoreKitTests/FBSDKGraphRequestTests.m b/FBSDKCoreKit/FBSDKCoreKitTests/FBSDKGraphRequestTests.m index ef2f052b69..35490f4125 100644 --- a/FBSDKCoreKit/FBSDKCoreKitTests/FBSDKGraphRequestTests.m +++ b/FBSDKCoreKit/FBSDKCoreKitTests/FBSDKGraphRequestTests.m @@ -16,28 +16,27 @@ // 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 "FBSDKGraphRequest+Internal.h" #import "FBSDKGraphRequest.h" -#import "FBSDKGraphRequestConnection+Internal.h" +#import "FBSDKGraphRequest+Internal.h" #import "FBSDKGraphRequestConnection.h" +#import "FBSDKGraphRequestConnection+Internal.h" #import "FBSDKGraphRequestDataAttachment.h" #import "FBSDKGraphRequestMetadata.h" #import "FBSDKInternalUtility.h" #import "FBSDKUtility.h" static NSString *const _mockGraphPath = @"me"; -static NSString *const _mockDefaultVersion = @"v7.0"; +static NSString *const _mockDefaultVersion = @"v8.0"; static NSString *const _mockPrefix = @"graph."; static NSDictionary *const _mockParameters(void) { - return @{@"fields":@""}; + return @{@"fields" : @""}; } -static NSDictionary *const _mockEmptyParamter(void) +static NSDictionary *const _mockEmptyParameters(void) { return @{}; } @@ -58,32 +57,33 @@ - (void)setUp #pragma mark - Tests +- (void)testDefaultGETParameters +{ + FBSDKGraphRequest *request = [[FBSDKGraphRequest alloc] initWithGraphPath:_mockGraphPath]; + + [_mockConnection addRequest:request + completionHandler:^(FBSDKGraphRequestConnection *conn, id result, NSError *error) {}]; + [self verifyRequest:request expectedGraphPath:_mockGraphPath expectedParameters:_mockParameters() expectedTokenString:nil expectedVersion:_mockDefaultVersion expectedMethod:FBSDKHTTPMethodGET]; +} + - (void)testGraphRequestGETWithEmptyParameters { - FBSDKGraphRequest *request1 = [[FBSDKGraphRequest alloc] initWithGraphPath:_mockGraphPath]; - FBSDKGraphRequest *request2 = [[FBSDKGraphRequest alloc] initWithGraphPath:_mockGraphPath parameters:_mockEmptyParamter()]; - FBSDKGraphRequest *request3 = [[FBSDKGraphRequest alloc] initWithGraphPath:_mockGraphPath parameters:_mockEmptyParamter() flags:FBSDKGraphRequestFlagNone]; - FBSDKGraphRequest *request4 = [[FBSDKGraphRequest alloc] initWithGraphPath:_mockGraphPath parameters:_mockEmptyParamter() tokenString:nil version:_mockDefaultVersion HTTPMethod:FBSDKHTTPMethodGET]; - FBSDKGraphRequest *request5 = [[FBSDKGraphRequest alloc] initWithGraphPath:_mockGraphPath parameters:_mockEmptyParamter() tokenString:nil version:_mockDefaultVersion HTTPMethod:FBSDKHTTPMethodGET]; + FBSDKGraphRequest *request1 = [[FBSDKGraphRequest alloc] initWithGraphPath:_mockGraphPath parameters:_mockEmptyParameters()]; + FBSDKGraphRequest *request2 = [[FBSDKGraphRequest alloc] initWithGraphPath:_mockGraphPath parameters:_mockEmptyParameters() flags:FBSDKGraphRequestFlagNone]; + FBSDKGraphRequest *request3 = [[FBSDKGraphRequest alloc] initWithGraphPath:_mockGraphPath parameters:_mockEmptyParameters() tokenString:nil version:_mockDefaultVersion HTTPMethod:FBSDKHTTPMethodGET]; + FBSDKGraphRequest *request4 = [[FBSDKGraphRequest alloc] initWithGraphPath:_mockGraphPath parameters:_mockEmptyParameters() tokenString:nil version:_mockDefaultVersion HTTPMethod:FBSDKHTTPMethodGET]; [_mockConnection addRequest:request1 - completionHandler:^(FBSDKGraphRequestConnection *conn, id result, NSError *error) { - }]; + completionHandler:^(FBSDKGraphRequestConnection *conn, id result, NSError *error) {}]; [_mockConnection addRequest:request2 - completionHandler:^(FBSDKGraphRequestConnection *conn, id result, NSError *error) { - }]; + completionHandler:^(FBSDKGraphRequestConnection *conn, id result, NSError *error) {}]; [_mockConnection addRequest:request3 - completionHandler:^(FBSDKGraphRequestConnection *conn, id result, NSError *error) { - }]; + completionHandler:^(FBSDKGraphRequestConnection *conn, id result, NSError *error) {}]; [_mockConnection addRequest:request4 - completionHandler:^(FBSDKGraphRequestConnection *conn, id result, NSError *error) { - }]; - [_mockConnection addRequest:request5 - completionHandler:^(FBSDKGraphRequestConnection *conn, id result, NSError *error) { - }]; + completionHandler:^(FBSDKGraphRequestConnection *conn, id result, NSError *error) {}]; for (FBSDKGraphRequestMetadata *metadata in _mockConnection.requests) { - [self verifyRequest:metadata.request expectedGraphPath:_mockGraphPath expectedParameters:_mockEmptyParamter() expectedTokenString:nil expectedVersion:_mockDefaultVersion expectedMethod:FBSDKHTTPMethodGET]; + [self verifyRequest:metadata.request expectedGraphPath:_mockGraphPath expectedParameters:_mockEmptyParameters() expectedTokenString:nil expectedVersion:_mockDefaultVersion expectedMethod:FBSDKHTTPMethodGET]; } } @@ -95,41 +95,40 @@ - (void)testGraphRequestGETWithNonEmptyParameters FBSDKGraphRequest *request4 = [[FBSDKGraphRequest alloc] initWithGraphPath:_mockGraphPath parameters:_mockParameters() tokenString:nil version:_mockDefaultVersion HTTPMethod:FBSDKHTTPMethodGET]; [_mockConnection addRequest:request1 - completionHandler:^(FBSDKGraphRequestConnection *conn, id result, NSError *error) { - }]; + completionHandler:^(FBSDKGraphRequestConnection *conn, id result, NSError *error) {}]; [_mockConnection addRequest:request2 - completionHandler:^(FBSDKGraphRequestConnection *conn, id result, NSError *error) { - }]; + completionHandler:^(FBSDKGraphRequestConnection *conn, id result, NSError *error) {}]; [_mockConnection addRequest:request3 - completionHandler:^(FBSDKGraphRequestConnection *conn, id result, NSError *error) { - }]; + completionHandler:^(FBSDKGraphRequestConnection *conn, id result, NSError *error) {}]; [_mockConnection addRequest:request4 - completionHandler:^(FBSDKGraphRequestConnection *conn, id result, NSError *error) { - }]; + completionHandler:^(FBSDKGraphRequestConnection *conn, id result, NSError *error) {}]; for (FBSDKGraphRequestMetadata *metadata in _mockConnection.requests) { [self verifyRequest:metadata.request expectedGraphPath:_mockGraphPath expectedParameters:_mockParameters() expectedTokenString:nil expectedVersion:_mockDefaultVersion expectedMethod:FBSDKHTTPMethodGET]; } } +- (void)testDefaultPOSTParameters +{ + FBSDKGraphRequest *request = [[FBSDKGraphRequest alloc] initWithGraphPath:_mockGraphPath HTTPMethod:FBSDKHTTPMethodPOST]; + + [_mockConnection addRequest:request + completionHandler:^(FBSDKGraphRequestConnection *conn, id result, NSError *error) {}]; + [self verifyRequest:request expectedGraphPath:_mockGraphPath expectedParameters:_mockEmptyParameters() expectedTokenString:nil expectedVersion:_mockDefaultVersion expectedMethod:FBSDKHTTPMethodPOST]; +} + - (void)testGraphRequestPOSTWithEmptyParameters { - FBSDKGraphRequest *request1 = [[FBSDKGraphRequest alloc] initWithGraphPath:_mockGraphPath HTTPMethod:FBSDKHTTPMethodPOST]; - FBSDKGraphRequest *request2 = [[FBSDKGraphRequest alloc] initWithGraphPath:_mockGraphPath parameters:_mockEmptyParamter() HTTPMethod:FBSDKHTTPMethodPOST]; - FBSDKGraphRequest *request3 = [[FBSDKGraphRequest alloc] initWithGraphPath:_mockGraphPath parameters:_mockEmptyParamter() tokenString:nil version:_mockDefaultVersion HTTPMethod:FBSDKHTTPMethodPOST]; + FBSDKGraphRequest *request1 = [[FBSDKGraphRequest alloc] initWithGraphPath:_mockGraphPath parameters:_mockEmptyParameters() HTTPMethod:FBSDKHTTPMethodPOST]; + FBSDKGraphRequest *request2 = [[FBSDKGraphRequest alloc] initWithGraphPath:_mockGraphPath parameters:_mockEmptyParameters() tokenString:nil version:_mockDefaultVersion HTTPMethod:FBSDKHTTPMethodPOST]; [_mockConnection addRequest:request1 - completionHandler:^(FBSDKGraphRequestConnection *conn, id result, NSError *error) { - }]; + completionHandler:^(FBSDKGraphRequestConnection *conn, id result, NSError *error) {}]; [_mockConnection addRequest:request2 - completionHandler:^(FBSDKGraphRequestConnection *conn, id result, NSError *error) { - }]; - [_mockConnection addRequest:request3 - completionHandler:^(FBSDKGraphRequestConnection *conn, id result, NSError *error) { - }]; + completionHandler:^(FBSDKGraphRequestConnection *conn, id result, NSError *error) {}]; for (FBSDKGraphRequestMetadata *metadata in _mockConnection.requests) { - [self verifyRequest:metadata.request expectedGraphPath:_mockGraphPath expectedParameters:_mockEmptyParamter() expectedTokenString:nil expectedVersion:_mockDefaultVersion expectedMethod:FBSDKHTTPMethodPOST]; + [self verifyRequest:metadata.request expectedGraphPath:_mockGraphPath expectedParameters:_mockEmptyParameters() expectedTokenString:nil expectedVersion:_mockDefaultVersion expectedMethod:FBSDKHTTPMethodPOST]; } } @@ -139,11 +138,9 @@ - (void)testGraphRequestPOSTWithNonEmptyParameters FBSDKGraphRequest *request2 = [[FBSDKGraphRequest alloc] initWithGraphPath:_mockGraphPath parameters:_mockParameters() tokenString:nil version:_mockDefaultVersion HTTPMethod:FBSDKHTTPMethodPOST]; [_mockConnection addRequest:request1 - completionHandler:^(FBSDKGraphRequestConnection *conn, id result, NSError *error) { - }]; + completionHandler:^(FBSDKGraphRequestConnection *conn, id result, NSError *error) {}]; [_mockConnection addRequest:request2 - completionHandler:^(FBSDKGraphRequestConnection *conn, id result, NSError *error) { - }]; + completionHandler:^(FBSDKGraphRequestConnection *conn, id result, NSError *error) {}]; for (FBSDKGraphRequestMetadata *metadata in _mockConnection.requests) { [self verifyRequest:metadata.request expectedGraphPath:_mockGraphPath expectedParameters:_mockParameters() expectedTokenString:nil expectedVersion:_mockDefaultVersion expectedMethod:FBSDKHTTPMethodPOST]; @@ -155,19 +152,19 @@ - (void)testSerializeURL NSString *baseURL = [FBSDKInternalUtility facebookURLWithHostPrefix:_mockPrefix path:_mockGraphPath - queryParameters:_mockEmptyParamter() + queryParameters:_mockEmptyParameters() defaultVersion:_mockDefaultVersion error:NULL].absoluteString; NSString *url = [FBSDKGraphRequest serializeURL:baseURL params:_mockParameters() httpMethod:FBSDKHTTPMethodPOST forBatch:YES]; - NSString *expectedURL = @"https://graph.facebook.com/v7.0/me?fields="; + NSString *expectedURL = @"https://graph.facebook.com/v8.0/me?fields="; XCTAssertEqualObjects(url, expectedURL); // Test URLEncode and URLDecode - NSString *expectedEncodedURL = @"https%3A%2F%2Fgraph.facebook.com%2Fv7.0%2Fme%3Ffields%3D"; + NSString *expectedEncodedURL = @"https%3A%2F%2Fgraph.facebook.com%2Fv8.0%2Fme%3Ffields%3D"; NSString *encodedSerializedURL = [FBSDKUtility URLEncode:expectedURL]; XCTAssertEqualObjects(encodedSerializedURL, expectedEncodedURL); @@ -185,15 +182,19 @@ - (void)testIsAttachments XCTAssertTrue([FBSDKGraphRequest isAttachment:mockDataAttachments]); id mockString = [OCMockObject niceMockForClass:[NSString class]]; - XCTAssertTrue(![mockString isKindOfClass:[UIImage class]] && - ![mockString isKindOfClass:[NSData class]] && - ![mockString isKindOfClass:[FBSDKGraphRequestDataAttachment class]]); + XCTAssertTrue( + ![mockString isKindOfClass:[UIImage class]] + && ![mockString isKindOfClass:[NSData class]] + && ![mockString isKindOfClass:[FBSDKGraphRequestDataAttachment class]] + ); XCTAssertFalse([FBSDKGraphRequest isAttachment:mockString]); id mockDate = [OCMockObject niceMockForClass:[NSDate class]]; - XCTAssertTrue(![mockDate isKindOfClass:[UIImage class]] && - ![mockDate isKindOfClass:[NSData class]] && - ![mockDate isKindOfClass:[FBSDKGraphRequestDataAttachment class]]); + XCTAssertTrue( + ![mockDate isKindOfClass:[UIImage class]] + && ![mockDate isKindOfClass:[NSData class]] + && ![mockDate isKindOfClass:[FBSDKGraphRequestDataAttachment class]] + ); XCTAssertFalse([FBSDKGraphRequest isAttachment:mockDate]); } diff --git a/FBSDKCoreKit/FBSDKCoreKitTests/FBSDKProfileTests.m b/FBSDKCoreKit/FBSDKCoreKitTests/FBSDKProfileTests.m new file mode 100644 index 0000000000..eaaf1dd671 --- /dev/null +++ b/FBSDKCoreKit/FBSDKCoreKitTests/FBSDKProfileTests.m @@ -0,0 +1,546 @@ +// 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 "FBSDKCoreKit.h" +#import "FBSDKCoreKitTests-Swift.h" +#import "FBSDKProfile.h" +#import "FBSDKProfile+Internal.h" +#import "FBSDKTestCase.h" + +@interface FBSDKSettings (Testing) ++ (void)resetFacebookClientTokenCache; +@end + +@interface FBSDKProfile (Testing) ++ (void)resetCurrentProfileCache; ++ (void)loadProfileWithToken:(FBSDKAccessToken *)token + completion:(FBSDKProfileBlock)completion + graphRequest:(FBSDKGraphRequest *)request; +@end + +@interface FBSDKProfileTests : FBSDKTestCase + +@end + +@implementation FBSDKProfileTests +{ + FBSDKProfile *_profile; + NSString *_sdkVersion; + CGSize _validNonSquareSize; + CGSize _validSquareSize; + NSString *_validClientToken; +} + +NSString *const accessTokenKey = @"access_token"; +NSString *const pictureModeKey = @"type"; +NSString *const widthKey = @"width"; +NSString *const heightKey = @"height"; + +- (void)setUp +{ + [super setUp]; + + _sdkVersion = @"100"; + _profile = SampleUserProfile.valid; + _validClientToken = @"Foo"; + _validSquareSize = CGSizeMake(100, 100); + _validNonSquareSize = CGSizeMake(10, 20); + + [self stubGraphAPIVersionWith:_sdkVersion]; + [self resetCaches]; +} + +- (void)tearDown +{ + [self resetCaches]; + _profile = nil; + + [super tearDown]; +} + +- (void)resetCaches +{ + [FBSDKProfile resetCurrentProfileCache]; + [FBSDKSettings resetFacebookClientTokenCache]; +} + +// MARK: - Creating Image URL + +- (void)testCreatingImageURL +{ + NSURL *url = [_profile imageURLForPictureMode:FBSDKProfilePictureModeNormal size:_validSquareSize]; + + NSString *expectedPath = [NSString stringWithFormat:@"/%@/%@/picture", _sdkVersion, _profile.userID]; + XCTAssertEqualObjects( + url.path, + expectedPath, + "Should add the graph api version and the identifier of the current user when creating a url for for fetching a profile image" + ); +} + +- (void)testCreatingImageURLWithNoAccessTokenNoClientToken +{ + NSURL *url = [_profile imageURLForPictureMode:FBSDKProfilePictureModeNormal size:_validSquareSize]; + NSURLComponents *components = [NSURLComponents componentsWithURL:url resolvingAgainstBaseURL:true]; + + NSSet *expectedQueryItems = [NSSet setWithArray:@[ + [[NSURLQueryItem alloc] initWithName:pictureModeKey value:@"normal"], + [[NSURLQueryItem alloc] initWithName:widthKey value:@"100"], + [[NSURLQueryItem alloc] initWithName:heightKey value:@"100"], + ]]; + + XCTAssertEqualObjects( + [NSSet setWithArray:components.queryItems], + expectedQueryItems, + "Should add the expected query items to a url when creating a url for fetching a profile image" + ); +} + +- (void)testCreatingImageURLWithClientTokenNoAccessToken +{ + [self stubClientTokenWith:_validClientToken]; + + NSURL *url = [_profile imageURLForPictureMode:FBSDKProfilePictureModeNormal size:_validSquareSize]; + NSURLComponents *components = [NSURLComponents componentsWithURL:url resolvingAgainstBaseURL:true]; + + NSSet *expectedQueryItems = [NSSet setWithArray:@[ + [[NSURLQueryItem alloc] initWithName:pictureModeKey value:@"normal"], + [[NSURLQueryItem alloc] initWithName:widthKey value:@"100"], + [[NSURLQueryItem alloc] initWithName:heightKey value:@"100"], + [[NSURLQueryItem alloc] initWithName:accessTokenKey value:_validClientToken], + ]]; + + XCTAssertEqualObjects( + [NSSet setWithArray:components.queryItems], + expectedQueryItems, + "Should use the current client token as the 'access token' when there is no true access token available" + ); +} + +- (void)testCreatingImageURLWithAccessTokenNoClientToken +{ + [self stubCurrentAccessTokenWith:SampleAccessToken.validToken]; + + NSURL *url = [_profile imageURLForPictureMode:FBSDKProfilePictureModeNormal size:_validSquareSize]; + NSURLComponents *components = [NSURLComponents componentsWithURL:url resolvingAgainstBaseURL:true]; + + NSSet *expectedQueryItems = [NSSet setWithArray:@[ + [[NSURLQueryItem alloc] initWithName:pictureModeKey value:@"normal"], + [[NSURLQueryItem alloc] initWithName:widthKey value:@"100"], + [[NSURLQueryItem alloc] initWithName:heightKey value:@"100"], + [[NSURLQueryItem alloc] initWithName:accessTokenKey value:SampleAccessToken.validToken.tokenString], + ]]; + + XCTAssertEqualObjects( + [NSSet setWithArray:components.queryItems], + expectedQueryItems, + "Should use the current client token as the 'access token' when there is no true access token available" + ); +} + +- (void)testCreatingImageURLWithAccessTokenAndClientToken +{ + [self stubCurrentAccessTokenWith:SampleAccessToken.validToken]; + [self stubClientTokenWith:_validClientToken]; + + NSURL *url = [_profile imageURLForPictureMode:FBSDKProfilePictureModeNormal size:_validSquareSize]; + NSURLComponents *components = [NSURLComponents componentsWithURL:url resolvingAgainstBaseURL:true]; + + NSSet *expectedQueryItems = [NSSet setWithArray:@[ + [[NSURLQueryItem alloc] initWithName:pictureModeKey value:@"normal"], + [[NSURLQueryItem alloc] initWithName:widthKey value:@"100"], + [[NSURLQueryItem alloc] initWithName:heightKey value:@"100"], + [[NSURLQueryItem alloc] initWithName:accessTokenKey value:SampleAccessToken.validToken.tokenString], + ]]; + + XCTAssertEqualObjects( + [NSSet setWithArray:components.queryItems], + expectedQueryItems, + "Should use the current access token as the 'access_token' parameter when available" + ); +} + +- (void)testCreatingEnumWithSmallMode +{ + NSURL *url = [_profile imageURLForPictureMode:FBSDKProfilePictureModeSmall size:_validNonSquareSize]; + NSURLComponents *components = [NSURLComponents componentsWithURL:url resolvingAgainstBaseURL:true]; + + NSSet *expectedQueryItems = [NSSet setWithArray:@[ + [[NSURLQueryItem alloc] initWithName:pictureModeKey value:@"small"], + [[NSURLQueryItem alloc] initWithName:widthKey value:@"10"], + [[NSURLQueryItem alloc] initWithName:heightKey value:@"20"], + ]]; + + XCTAssertEqualObjects( + [NSSet setWithArray:components.queryItems], + expectedQueryItems, + "Should add the expected query items to a url when creating a url for fetching a profile image" + ); +} + +- (void)testCreatingEnumWithAlbumMode +{ + NSURL *url = [_profile imageURLForPictureMode:FBSDKProfilePictureModeAlbum size:_validNonSquareSize]; + NSURLComponents *components = [NSURLComponents componentsWithURL:url resolvingAgainstBaseURL:true]; + + NSSet *expectedQueryItems = [NSSet setWithArray:@[ + [[NSURLQueryItem alloc] initWithName:pictureModeKey value:@"album"], + [[NSURLQueryItem alloc] initWithName:widthKey value:@"10"], + [[NSURLQueryItem alloc] initWithName:heightKey value:@"20"], + ]]; + + XCTAssertEqualObjects( + [NSSet setWithArray:components.queryItems], + expectedQueryItems, + "Should add the expected query items to a url when creating a url for fetching a profile image" + ); +} + +- (void)testCreatingEnumWithLargeMode +{ + NSURL *url = [_profile imageURLForPictureMode:FBSDKProfilePictureModeLarge size:_validNonSquareSize]; + NSURLComponents *components = [NSURLComponents componentsWithURL:url resolvingAgainstBaseURL:true]; + + NSSet *expectedQueryItems = [NSSet setWithArray:@[ + [[NSURLQueryItem alloc] initWithName:pictureModeKey value:@"large"], + [[NSURLQueryItem alloc] initWithName:widthKey value:@"10"], + [[NSURLQueryItem alloc] initWithName:heightKey value:@"20"], + ]]; + + XCTAssertEqualObjects( + [NSSet setWithArray:components.queryItems], + expectedQueryItems, + "Should add the expected query items to a url when creating a url for fetching a profile image" + ); +} + +- (void)testCreatingEnumWithSquareMode +{ + NSURL *url = [_profile imageURLForPictureMode:FBSDKProfilePictureModeSquare size:_validNonSquareSize]; + NSURLComponents *components = [NSURLComponents componentsWithURL:url resolvingAgainstBaseURL:true]; + + NSSet *expectedQueryItems = [NSSet setWithArray:@[ + [[NSURLQueryItem alloc] initWithName:pictureModeKey value:@"square"], + [[NSURLQueryItem alloc] initWithName:widthKey value:@"10"], + [[NSURLQueryItem alloc] initWithName:heightKey value:@"20"], + ]]; + + XCTAssertEqualObjects( + [NSSet setWithArray:components.queryItems], + expectedQueryItems, + "Should add the expected query items to a url when creating a url for fetching a profile image" + ); +} + +- (void)testCreatingImageURLWithUnknownMode +{ + NSURL *url = [_profile imageURLForPictureMode:400 size:_validSquareSize]; + NSURLComponents *components = [NSURLComponents componentsWithURL:url resolvingAgainstBaseURL:true]; + + NSSet *expectedQueryItems = [NSSet setWithArray:@[ + [[NSURLQueryItem alloc] initWithName:pictureModeKey value:@"normal"], + [[NSURLQueryItem alloc] initWithName:widthKey value:@"100"], + [[NSURLQueryItem alloc] initWithName:heightKey value:@"100"], + ]]; + + XCTAssertEqualObjects( + [NSSet setWithArray:components.queryItems], + expectedQueryItems, + "The picture mode for an invalid enum value should default to 'normal'" + ); +} + +// MARK: - Size Validations + +- (void)testCreatingImageURLWithNoSize +{ + NSURL *url = [_profile imageURLForPictureMode:FBSDKProfilePictureModeNormal size:CGSizeZero]; + NSURLComponents *components = [NSURLComponents componentsWithURL:url resolvingAgainstBaseURL:true]; + + NSSet *expectedQueryItems = [NSSet setWithArray:@[ + [[NSURLQueryItem alloc] initWithName:pictureModeKey value:@"normal"], + [[NSURLQueryItem alloc] initWithName:widthKey value:@"0"], + [[NSURLQueryItem alloc] initWithName:heightKey value:@"0"], + ]]; + + XCTAssertNotNil(url, "Should not create a url for fetching a profile picture with zero size but it will"); + XCTAssertEqualObjects( + [NSSet setWithArray:components.queryItems], + expectedQueryItems, + "Should add the expected query items to a url when creating a url for fetching a profile image" + ); +} + +- (void)testCreatingSquareImageURLWithNonSquareSize +{ + NSURL *url = [_profile imageURLForPictureMode:FBSDKProfilePictureModeSquare size:_validNonSquareSize]; + NSURLComponents *components = [NSURLComponents componentsWithURL:url resolvingAgainstBaseURL:true]; + + NSSet *expectedQueryItems = [NSSet setWithArray:@[ + [[NSURLQueryItem alloc] initWithName:pictureModeKey value:@"square"], + [[NSURLQueryItem alloc] initWithName:widthKey value:@"10"], + [[NSURLQueryItem alloc] initWithName:heightKey value:@"20"], + ]]; + + XCTAssertNotNil(url, "Should not create a url for a square image with non-square size but it will"); + XCTAssertEqualObjects( + [NSSet setWithArray:components.queryItems], + expectedQueryItems, + "Should add the expected query items to a url when creating a url for fetching a profile image" + ); +} + +- (void)testCreatingSquareImageURLWithNegativeSize +{ + NSURL *url = [_profile imageURLForPictureMode:FBSDKProfilePictureModeSquare size:CGSizeMake(-10, -10)]; + NSURLComponents *components = [NSURLComponents componentsWithURL:url resolvingAgainstBaseURL:true]; + + NSSet *expectedQueryItems = [NSSet setWithArray:@[ + [[NSURLQueryItem alloc] initWithName:pictureModeKey value:@"square"], + [[NSURLQueryItem alloc] initWithName:widthKey value:@"-10"], + [[NSURLQueryItem alloc] initWithName:heightKey value:@"-10"], + ]]; + + XCTAssertNotNil(url, "Should not create a url for a square image with a negative size but it will"); + XCTAssertEqualObjects( + [NSSet setWithArray:components.queryItems], + expectedQueryItems, + "Should add the expected query items to a url when creating a url for fetching a profile image" + ); +} + +// MARK: - Profile Loading + +- (void)testGraphPathForProfileLoadWithLinkPermission +{ + id profileMock = OCMClassMock([FBSDKProfile class]); + FBSDKAccessToken *token = [SampleAccessToken validTokenWithPermissions:@[@"user_link"]]; + NSString *graphPath = @"me?fields=id,first_name,middle_name,last_name,name,link"; + __block BOOL graphRequestMethodInvoked = false; + OCMStub([profileMock loadProfileWithToken:OCMOCK_ANY completion:OCMOCK_ANY graphRequest:OCMOCK_ANY]).andDo(^(NSInvocation *invocation) { + __unsafe_unretained FBSDKGraphRequest *request; + [invocation getArgument:&request atIndex:4]; + graphRequestMethodInvoked = true; + XCTAssertEqualObjects(request.graphPath, graphPath); + }); + OCMStub([profileMock loadProfileWithToken:OCMOCK_ANY completion:OCMOCK_ANY]).andForwardToRealObject(); + [FBSDKProfile loadProfileWithToken:token completion:nil]; + XCTAssertTrue(graphRequestMethodInvoked); +} + +- (void)testGraphPathForProfileLoadWithoutLinkPermission +{ + id profileMock = OCMClassMock([FBSDKProfile class]); + NSString *graphPath = @"me?fields=id,first_name,middle_name,last_name,name"; + __block BOOL graphRequestMethodInvoked = false; + OCMStub([profileMock loadProfileWithToken:OCMOCK_ANY completion:OCMOCK_ANY graphRequest:OCMOCK_ANY]).andDo(^(NSInvocation *invocation) { + __unsafe_unretained FBSDKGraphRequest *request; + [invocation getArgument:&request atIndex:4]; + graphRequestMethodInvoked = true; + XCTAssertEqualObjects(request.graphPath, graphPath); + }); + OCMStub([profileMock loadProfileWithToken:OCMOCK_ANY completion:OCMOCK_ANY]).andForwardToRealObject(); + [FBSDKProfile loadProfileWithToken:SampleAccessToken.validToken completion:nil]; + XCTAssertTrue(graphRequestMethodInvoked); +} + +- (void)testGraphPathForProfileLoadWithEmailPermission +{ + id profileMock = OCMClassMock([FBSDKProfile class]); + FBSDKAccessToken *token = [SampleAccessToken validTokenWithPermissions:@[@"email"]]; + NSString *graphPath = @"me?fields=id,first_name,middle_name,last_name,name,email"; + __block BOOL graphRequestMethodInvoked = false; + OCMStub([profileMock loadProfileWithToken:OCMOCK_ANY completion:OCMOCK_ANY graphRequest:OCMOCK_ANY]).andDo(^(NSInvocation *invocation) { + __unsafe_unretained FBSDKGraphRequest *request; + [invocation getArgument:&request atIndex:4]; + graphRequestMethodInvoked = true; + XCTAssertEqualObjects(request.graphPath, graphPath); + }); + OCMStub([profileMock loadProfileWithToken:OCMOCK_ANY completion:OCMOCK_ANY]).andForwardToRealObject(); + [FBSDKProfile loadProfileWithToken:token completion:nil]; + XCTAssertTrue(graphRequestMethodInvoked); +} + +- (void)testGraphPathForProfileLoadWithoutEmailPermission +{ + id profileMock = OCMClassMock([FBSDKProfile class]); + NSString *graphPath = @"me?fields=id,first_name,middle_name,last_name,name"; + __block BOOL graphRequestMethodInvoked = false; + OCMStub([profileMock loadProfileWithToken:OCMOCK_ANY completion:OCMOCK_ANY graphRequest:OCMOCK_ANY]).andDo(^(NSInvocation *invocation) { + __unsafe_unretained FBSDKGraphRequest *request; + [invocation getArgument:&request atIndex:4]; + graphRequestMethodInvoked = true; + XCTAssertEqualObjects(request.graphPath, graphPath); + }); + OCMStub([profileMock loadProfileWithToken:OCMOCK_ANY completion:OCMOCK_ANY]).andForwardToRealObject(); + [FBSDKProfile loadProfileWithToken:SampleAccessToken.validToken completion:nil]; + XCTAssertTrue(graphRequestMethodInvoked); +} + +- (void)testLoadingProfile +{ + id result = @{ + @"id" : SampleUserProfile.valid.userID, + @"first_name" : SampleUserProfile.valid.firstName, + @"middle_name" : SampleUserProfile.valid.middleName, + @"last_name" : SampleUserProfile.valid.lastName, + @"name" : SampleUserProfile.valid.name, + @"link" : SampleUserProfile.valid.linkURL, + @"email" : SampleUserProfile.valid.email + }; + [self stubGraphRequestWithResult:result error:nil connection:nil]; + + [FBSDKProfile loadProfileWithToken:SampleAccessToken.validToken completion:^(FBSDKProfile *_Nullable profile, NSError *_Nullable error) { + XCTAssertEqualObjects(profile.firstName, SampleUserProfile.valid.firstName); + XCTAssertEqualObjects(profile.middleName, SampleUserProfile.valid.middleName); + XCTAssertEqualObjects(profile.lastName, SampleUserProfile.valid.lastName); + XCTAssertEqualObjects(profile.name, SampleUserProfile.valid.name); + XCTAssertEqualObjects(profile.userID, SampleUserProfile.valid.userID); + XCTAssertEqualObjects(profile.linkURL, SampleUserProfile.valid.linkURL); + XCTAssertEqualObjects(profile.email, SampleUserProfile.valid.email); + } graphRequest:self.graphRequestMock]; +} + +- (void)testLoadingProfileWithInvalidLink +{ + id result = @{ + @"id" : SampleUserProfile.valid.userID, + @"first_name" : SampleUserProfile.valid.firstName, + @"middle_name" : SampleUserProfile.valid.middleName, + @"last_name" : SampleUserProfile.valid.lastName, + @"name" : SampleUserProfile.valid.name, + @"link" : @" ", + @"email" : SampleUserProfile.valid.email + }; + [self stubGraphRequestWithResult:result error:nil connection:nil]; + + [FBSDKProfile loadProfileWithToken:SampleAccessToken.validToken completion:^(FBSDKProfile *_Nullable profile, NSError *_Nullable error) { + XCTAssertNil(profile.linkURL); + } graphRequest:self.graphRequestMock]; +} + +- (void)testProfileNilWithNilAccessToken +{ + id result = @{ + @"id" : SampleUserProfile.valid.userID, + @"first_name" : SampleUserProfile.valid.firstName, + @"middle_name" : SampleUserProfile.valid.middleName, + @"last_name" : SampleUserProfile.valid.lastName, + @"name" : SampleUserProfile.valid.name + }; + [self stubGraphRequestWithResult:result error:nil connection:nil]; + + [FBSDKProfile loadProfileWithToken:nil completion:^(FBSDKProfile *_Nullable profile, NSError *_Nullable error) { + XCTAssertNil(profile); + } graphRequest:self.graphRequestMock]; +} + +- (void)testProfileNotRefreshedIfNotStale +{ + [FBSDKProfile setCurrentProfile:SampleUserProfile.valid]; + id result = @{ + @"id" : SampleUserProfile.valid.userID, + @"first_name" : @"firstname", + @"middle_name" : @"middlename", + @"last_name" : @"lastname", + @"name" : @"name" + }; + [self stubGraphRequestWithResult:result error:nil connection:nil]; + + [FBSDKProfile loadProfileWithToken:nil completion:^(FBSDKProfile *_Nullable profile, NSError *_Nullable error) { + XCTAssertEqualObjects(profile.firstName, SampleUserProfile.valid.firstName); + XCTAssertEqualObjects(profile.middleName, SampleUserProfile.valid.middleName); + XCTAssertEqualObjects(profile.lastName, SampleUserProfile.valid.lastName); + XCTAssertEqualObjects(profile.name, SampleUserProfile.valid.name); + XCTAssertEqualObjects(profile.userID, SampleUserProfile.valid.userID); + XCTAssertEqualObjects(profile.linkURL, SampleUserProfile.valid.linkURL); + XCTAssertEqualObjects(profile.email, SampleUserProfile.valid.email); + } graphRequest:self.graphRequestMock]; +} + +- (void)testProfileParseBlockInvokedOnSuccessfulGraphRequest +{ + id result = @{}; + [self stubGraphRequestWithResult:result error:nil connection:nil]; + + __block BOOL parseBlockInvoked = false; + + [FBSDKProfile loadProfileWithToken:SampleAccessToken.validToken + completion:^void (FBSDKProfile *profile, NSError *error) {} + graphRequest:self.graphRequestMock + parseBlock:^void (id parseResult, FBSDKProfile **profileRef) { + parseBlockInvoked = true; + }]; + XCTAssertTrue(parseBlockInvoked); +} + +- (void)testProfileParseBlockShouldHaveNonNullPointer +{ + id result = @{}; + [self stubGraphRequestWithResult:result error:nil connection:nil]; + + [FBSDKProfile loadProfileWithToken:SampleAccessToken.validToken + completion:^void (FBSDKProfile *profile, NSError *error) {} + graphRequest:self.graphRequestMock + parseBlock:^void (id parseResult, FBSDKProfile **profileRef) { + XCTAssertTrue(profileRef != NULL); + }]; +} + +- (void)testProfileParseBlockReturnsNilIfResultIsEmpty +{ + id result = @{}; + [self stubGraphRequestWithResult:result error:nil connection:nil]; + + [FBSDKProfile loadProfileWithToken:SampleAccessToken.validToken completion:nil graphRequest:self.graphRequestMock]; + XCTAssertNil(FBSDKProfile.currentProfile); +} + +- (void)testProfileParseBlockReturnsNilIfResultHasNoId +{ + id result = @{ + @"first_name" : @"firstname", + @"middle_name" : @"middlename", + @"last_name" : @"lastname", + @"name" : @"name" + }; + [self stubGraphRequestWithResult:result error:nil connection:nil]; + + [FBSDKProfile loadProfileWithToken:SampleAccessToken.validToken completion:nil graphRequest:self.graphRequestMock]; + XCTAssertNil(FBSDKProfile.currentProfile); +} + +- (void)testProfileParseBlockReturnsNilIfResultHasEmptyId +{ + id result = @{ + @"id" : @"", + @"first_name" : @"firstname", + @"middle_name" : @"middlename", + @"last_name" : @"lastname", + @"name" : @"name" + }; + [self stubGraphRequestWithResult:result error:nil connection:nil]; + + [FBSDKProfile loadProfileWithToken:SampleAccessToken.validToken completion:nil graphRequest:self.graphRequestMock]; + XCTAssertNil(FBSDKProfile.currentProfile); +} + +@end diff --git a/FBSDKCoreKit/FBSDKCoreKitTests/FBSDKUtilityTests.m b/FBSDKCoreKit/FBSDKCoreKitTests/FBSDKUtilityTests.m deleted file mode 100644 index 9df834cc9d..0000000000 --- a/FBSDKCoreKit/FBSDKCoreKitTests/FBSDKUtilityTests.m +++ /dev/null @@ -1,79 +0,0 @@ -// 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 "FBSDKCoreKit+Internal.h" - -@interface FBSDKUtilityTests : XCTestCase - -@end - -@implementation FBSDKUtilityTests - -- (void)testSHA256Hash { - NSString *hashed = [FBSDKUtility SHA256Hash:@"facebook"]; - - XCTAssertEqualObjects(hashed, @"3d59f7548e1af2151b64135003ce63c0a484c26b9b8b166a7b1c1805ec34b00a"); -} - -- (void)testURLDecodeShouldNotModifyUnencodedUrlString { - NSString *unencoded = @"https://www.facebook.com/index.html?a=b&c=d"; - - XCTAssertEqualObjects(unencoded, [FBSDKUtility URLDecode:unencoded]); -} - -- (void)testURLEncode { - NSString *unencoded = @"https://www.facebook.com/index.html?a=b&c=d"; - NSString *encoded = @"https%3A%2F%2Fwww.facebook.com%2Findex.html%3Fa%3Db%26c%3Dd"; - - XCTAssertEqualObjects(encoded, [FBSDKUtility URLEncode:unencoded]); - XCTAssertEqualObjects(unencoded, [FBSDKUtility URLDecode:encoded]); -} - -- (void)testURLEncodeWithJSON { - NSString *url = @"https://m.facebook.com/v3.2/dialog/oauth?auth_type=rerequest&client_id=123456789&default_audience=friends&display=touch&e2e={\"init\":123456.1234567890}&fbapp_pres=0&redirect_uri=fb111111111111111://authorize/&response_type=token,signed_request&return_scopes=true&scope=&sdk=ios&sdk_version=4.39.0&state={\"challenge\":\"aBcDeFghiJKlmnOpQRS%tU\",\"0_auth_logger_id\":\"01234ABC-12AB-34DE-1234-ABCDEFG12345\",\"com.facebook.some_identifier\":true,\"3_method\":\"sfvc_auth\"}"; - NSString *encoded = @"https%3A%2F%2Fm.facebook.com%2Fv3.2%2Fdialog%2Foauth%3Fauth_type%3Drerequest%26client_id%3D123456789%26default_audience%3Dfriends%26display%3Dtouch%26e2e%3D%7B%22init%22%3A123456.1234567890%7D%26fbapp_pres%3D0%26redirect_uri%3Dfb111111111111111%3A%2F%2Fauthorize%2F%26response_type%3Dtoken%2Csigned_request%26return_scopes%3Dtrue%26scope%3D%26sdk%3Dios%26sdk_version%3D4.39.0%26state%3D%7B%22challenge%22%3A%22aBcDeFghiJKlmnOpQRS%25tU%22%2C%220_auth_logger_id%22%3A%2201234ABC-12AB-34DE-1234-ABCDEFG12345%22%2C%22com.facebook.some_identifier%22%3Atrue%2C%223_method%22%3A%22sfvc_auth%22%7D"; - - XCTAssertEqualObjects(encoded, [FBSDKUtility URLEncode:url]); - XCTAssertEqualObjects(url, [FBSDKUtility URLDecode:encoded]); -} - -- (void)testNewEncodeWorksLikeLegacy { - for (int i = 0; i < 256; i++) { - NSString *str = [NSString stringWithFormat:@"%c", (char)i]; - if ([str isEqualToString:@"{"] || [str isEqualToString:@"}"]) { - continue; // Curly braces were not included in legacy URL encode - } - XCTAssertEqualObjects([FBSDKUtility URLEncode:str], [self legacyURLEncode:str]); - } -} - -#pragma clang diagnostic push -#pragma clang diagnostic ignored "-Wdeprecated-declarations" -- (NSString *)legacyURLEncode:(NSString *)value -{ - return (__bridge_transfer NSString *)CFURLCreateStringByAddingPercentEscapes(NULL, - (CFStringRef)value, - NULL, // characters to leave unescaped - CFSTR(":!*();@/&?+$,='"), - kCFStringEncodingUTF8); -} -#pragma clang diagnostic pop - -@end diff --git a/FBSDKCoreKit/FBSDKCoreKitTests/FBSDKUtilityTests.swift b/FBSDKCoreKit/FBSDKCoreKitTests/FBSDKUtilityTests.swift new file mode 100644 index 0000000000..18c2efe8ec --- /dev/null +++ b/FBSDKCoreKit/FBSDKCoreKitTests/FBSDKUtilityTests.swift @@ -0,0 +1,69 @@ +// 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 XCTest + +class FBSDKUtilityTests: XCTestCase { + func testSHA256Hash() { + let hashed = Utility.sha256Hash("facebook" as NSObject) + + XCTAssertEqual(hashed, "3d59f7548e1af2151b64135003ce63c0a484c26b9b8b166a7b1c1805ec34b00a") + } + + func testURLDecodeShouldNotModifyUnencodedUrlString() { + let unencoded = "https://www.facebook.com/index.html?a=b&c=d" + + XCTAssertEqual(unencoded, Utility.decode(urlString: unencoded)) + } + + func testURLEncode() { + let unencoded = "https://www.facebook.com/index.html?a=b&c=d" + let encoded = "https%3A%2F%2Fwww.facebook.com%2Findex.html%3Fa%3Db%26c%3Dd" + + XCTAssertEqual(encoded, Utility.encode(urlString: unencoded)) + XCTAssertEqual(unencoded, Utility.decode(urlString: encoded)) + } + + func testURLEncodeWithJSON() { + let url = "https://m.facebook.com/v3.2/dialog/oauth?auth_type=rerequest&client_id=123456789&default_audience=friends&display=touch&e2e={\"init\":123456.1234567890}&fbapp_pres=0&redirect_uri=fb111111111111111://authorize/&response_type=token,signed_request&return_scopes=true&scope=&sdk=ios&sdk_version=4.39.0&state={\"challenge\":\"aBcDeFghiJKlmnOpQRS%tU\",\"0_auth_logger_id\":\"01234ABC-12AB-34DE-1234-ABCDEFG12345\",\"com.facebook.some_identifier\":true,\"3_method\":\"sfvc_auth\"}" // swiftlint:disable:this line_length + let encoded = "https%3A%2F%2Fm.facebook.com%2Fv3.2%2Fdialog%2Foauth%3Fauth_type%3Drerequest%26client_id%3D123456789%26default_audience%3Dfriends%26display%3Dtouch%26e2e%3D%7B%22init%22%3A123456.1234567890%7D%26fbapp_pres%3D0%26redirect_uri%3Dfb111111111111111%3A%2F%2Fauthorize%2F%26response_type%3Dtoken%2Csigned_request%26return_scopes%3Dtrue%26scope%3D%26sdk%3Dios%26sdk_version%3D4.39.0%26state%3D%7B%22challenge%22%3A%22aBcDeFghiJKlmnOpQRS%25tU%22%2C%220_auth_logger_id%22%3A%2201234ABC-12AB-34DE-1234-ABCDEFG12345%22%2C%22com.facebook.some_identifier%22%3Atrue%2C%223_method%22%3A%22sfvc_auth%22%7D" // swiftlint:disable:this line_length + XCTAssertEqual(encoded, Utility.encode(urlString: url)) + XCTAssertEqual(url, Utility.decode(urlString: encoded)) + } + + @available(iOS, deprecated: 9.0) + func testNewEncodeWorksLikeLegacy() { + for num in 0..<256 { + let str = String(Character(UnicodeScalar(num)!)) // swiftlint:disable:this force_unwrapping + if str == "{" || str == "}" { + continue // Curly braces were not included in legacy URL encode + } + XCTAssertEqual(Utility.encode(urlString: str), legacyURLEncode(str)) + } + } + + @available(iOS, deprecated: 9.0) // Needed to disable warning on CFURLCreateStringByAddingPercentEscapes + func legacyURLEncode(_ value: String) -> String? { + return CFURLCreateStringByAddingPercentEscapes( + nil, + value as CFString, + nil, + ":!*();@/&?+$,='" as CFString, + CFStringBuiltInEncodings.UTF8.rawValue) as String + } +} diff --git a/FBSDKCoreKit/FBSDKCoreKitTests/Info.plist b/FBSDKCoreKit/FBSDKCoreKitTests/Info.plist index ba72822e87..d508ba9c00 100644 --- a/FBSDKCoreKit/FBSDKCoreKitTests/Info.plist +++ b/FBSDKCoreKit/FBSDKCoreKitTests/Info.plist @@ -2,23 +2,34 @@ + CFBundleURLTypes + + + CFBundleURLSchemes + + example.com + + + CFBundleDevelopmentRegion en CFBundleExecutable $(EXECUTABLE_NAME) CFBundleIdentifier - $(PRODUCT_BUNDLE_IDENTIFIER) + com.facebook.sdk.FBSDKCoreKitTests CFBundleInfoDictionaryVersion 6.0 CFBundleName - $(PRODUCT_NAME) + FBSDKCoreKitTests CFBundlePackageType BNDL CFBundleShortVersionString 1.0 - CFBundleSignature - ???? CFBundleVersion 1 + FacebookLoggingBehavior + + informational + diff --git a/FBSDKCoreKit/FBSDKCoreKitTests/Internal/AppEvents/AAM/FBSDKMetadataIndexerTests.m b/FBSDKCoreKit/FBSDKCoreKitTests/Internal/AppEvents/AAM/FBSDKMetadataIndexerTests.m index fa29b20078..7bb3aadbad 100644 --- a/FBSDKCoreKit/FBSDKCoreKitTests/Internal/AppEvents/AAM/FBSDKMetadataIndexerTests.m +++ b/FBSDKCoreKit/FBSDKCoreKitTests/Internal/AppEvents/AAM/FBSDKMetadataIndexerTests.m @@ -16,9 +16,8 @@ // 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 @@ -42,7 +41,8 @@ + (void)getMetadataWithText:(NSString *)text + (void)checkAndAppendData:(NSString *)data forKey:(NSString *)key; @end -@interface FBSDKMetadataIndexerTests : XCTestCase { +@interface FBSDKMetadataIndexerTests : XCTestCase +{ id _mockMetadataIndexer; UITextField *_emailField; UITextView *_emailView; @@ -60,53 +60,53 @@ - (void)setUp _mockMetadataIndexer = OCMClassMock([FBSDKMetadataIndexer class]); [FBSDKMetadataIndexer initStore]; [FBSDKMetadataIndexer constructRules:@{ - @"r1": @{ - @"k": @"email,e-mail,em,electronicmail", - @"v": @"^([A-Z0-9a-z._%+-]+@[A-Za-z0-9.-]+\\.[A-Za-z]{2,})$", - }, - @"r2": @{ - @"k": @"phone,mobile,contact", - @"v": @"^([0-9]{5,15})$", - }, - @"r3": @{ - @"k": @"gender,gen,sex", - @"v": @"^(male|boy|man|female|girl|woman)$", - }, - @"r4": @{ - @"k": @"city", - @"v": @"", - }, - @"r5": @{ - @"k": @"state,province", - @"v": @"", - }, - @"r6": @{ - @"k": @"zip,zcode,pincode,pcode,postalcode,postcode", - @"v": @"(^\\d{5}$)|(^\\d{9}$)|(^\\d{5}-\\d{4}$)", - }, - @"r7": @{ - @"k": @"firstname,first name,fn,fname,givenname,forename", - @"v": @"", - }, - @"r8": @{ - @"k": @"lastname,last name,ln,lname,surname,sname,familyname", - @"v": @"", - }, - }]; + @"r1" : @{ + @"k" : @"email,e-mail,em,electronicmail", + @"v" : @"^([A-Z0-9a-z._%+-]+@[A-Za-z0-9.-]+\\.[A-Za-z]{2,})$", + }, + @"r2" : @{ + @"k" : @"phone,mobile,contact", + @"v" : @"^([0-9]{5,15})$", + }, + @"r3" : @{ + @"k" : @"gender,gen,sex", + @"v" : @"^(male|boy|man|female|girl|woman)$", + }, + @"r4" : @{ + @"k" : @"city", + @"v" : @"", + }, + @"r5" : @{ + @"k" : @"state,province", + @"v" : @"", + }, + @"r6" : @{ + @"k" : @"zip,zcode,pincode,pcode,postalcode,postcode", + @"v" : @"(^\\d{5}$)|(^\\d{9}$)|(^\\d{5}-\\d{4}$)", + }, + @"r7" : @{ + @"k" : @"firstname,first name,fn,fname,givenname,forename", + @"v" : @"", + }, + @"r8" : @{ + @"k" : @"lastname,last name,ln,lname,surname,sname,familyname", + @"v" : @"", + }, + }]; _emailField = [[UITextField alloc] init]; - _emailField.placeholder = @"Enter your email"; + _emailField.placeholder = NSLocalizedString(@"Enter your email", nil); _emailField.keyboardType = UIKeyboardTypeEmailAddress; _emailView = [[UITextView alloc] init]; _emailView.keyboardType = UIKeyboardTypeEmailAddress; _phoneField = [[UITextField alloc] init]; - _phoneField.placeholder = @"Enter your phone"; + _phoneField.placeholder = NSLocalizedString(@"Enter your phone", nil); _phoneField.keyboardType = UIKeyboardTypePhonePad; _pwdField = [[UITextField alloc] init]; - _pwdField.placeholder = @"Enter your password"; + _pwdField.placeholder = NSLocalizedString(@"Enter your password", nil); _pwdField.secureTextEntry = YES; _pwdView = [[UITextView alloc] init]; @@ -122,20 +122,26 @@ - (void)tearDown - (void)testCheckSecureTextEntryOfTextField { // without secure text - XCTAssertFalse([FBSDKMetadataIndexer checkSecureTextEntry:_emailField], - @"test for UITextField without secure text"); + XCTAssertFalse( + [FBSDKMetadataIndexer checkSecureTextEntry:_emailField], + @"test for UITextField without secure text" + ); // with secure text - XCTAssertTrue([FBSDKMetadataIndexer checkSecureTextEntry:_pwdField], - @"test for UITextField with secure text"); + XCTAssertTrue( + [FBSDKMetadataIndexer checkSecureTextEntry:_pwdField], + @"test for UITextField with secure text" + ); } // test for geting secure text entry in UITextView - (void)testCheckSecureTextEntryOfTextView { // without secure text - XCTAssertFalse([FBSDKMetadataIndexer checkSecureTextEntry:_emailView], - @"test for UITextView without secure text"); + XCTAssertFalse( + [FBSDKMetadataIndexer checkSecureTextEntry:_emailView], + @"test for UITextView without secure text" + ); // with secure text XCTAssertTrue([FBSDKMetadataIndexer checkSecureTextEntry:_pwdView], @"test for UITextView with secure text"); @@ -144,17 +150,21 @@ - (void)testCheckSecureTextEntryOfTextView // test for geting keyboard type from UITextField - (void)testGetKeyboardTypeOfTextField { - XCTAssertEqual(_emailField.keyboardType, - [FBSDKMetadataIndexer getKeyboardType:_emailField], - @"test for geting keyboard type from UITextField"); + XCTAssertEqual( + _emailField.keyboardType, + [FBSDKMetadataIndexer getKeyboardType:_emailField], + @"test for geting keyboard type from UITextField" + ); } // test for geting keyboard type from UITextView - (void)testGetKeyboardTypeOfTextView { - XCTAssertEqual(_emailView.keyboardType, - [FBSDKMetadataIndexer getKeyboardType:_emailView], - @"test for geting keyboard type from UITextView"); + XCTAssertEqual( + _emailView.keyboardType, + [FBSDKMetadataIndexer getKeyboardType:_emailView], + @"test for geting keyboard type from UITextView" + ); } // test for geting metadata with valid email @@ -307,7 +317,7 @@ - (void)testGetMetadataWithNoText // test for geting metadata with too long text - (void)testGetMetadataWithTooLongText { - NSString *text = [NSString stringWithFormat:@"%@%@", [@"" stringByPaddingToLength:1000 withString: @"a" startingAtIndex:0], @"@fb.com"]; + NSString *text = [NSString stringWithFormat:@"%@%@", [@"" stringByPaddingToLength:1000 withString:@"a" startingAtIndex:0], @"@fb.com"]; [FBSDKMetadataIndexer getMetadataWithText:text placeholder:@"Enter your Email" labels:nil @@ -321,7 +331,7 @@ - (void)testGetMetadataWithTooLongText - (void)testGetMetadataWithTooLongPlaceholder { NSString *text = @"test@fb.com"; - NSString *indicator = [NSString stringWithFormat:@"%@", [@"" stringByPaddingToLength:1000 withString: @"enter your email " startingAtIndex:0]]; + NSString *indicator = [NSString stringWithFormat:@"%@", [@"" stringByPaddingToLength:1000 withString:@"enter your email " startingAtIndex:0]]; [FBSDKMetadataIndexer getMetadataWithText:text placeholder:indicator labels:nil diff --git a/FBSDKCoreKit/FBSDKCoreKitTests/Internal/AppEvents/Codeless/FBSDKEventBindingTests.m b/FBSDKCoreKit/FBSDKCoreKitTests/Internal/AppEvents/Codeless/FBSDKEventBindingTests.m index fe96e794d7..12298a932d 100644 --- a/FBSDKCoreKit/FBSDKCoreKitTests/Internal/AppEvents/Codeless/FBSDKEventBindingTests.m +++ b/FBSDKCoreKit/FBSDKCoreKitTests/Internal/AppEvents/Codeless/FBSDKEventBindingTests.m @@ -19,12 +19,12 @@ #import #import "FBSDKCodelessParameterComponent.h" +#import "FBSDKCoreKitTests-Swift.h" #import "FBSDKEventBinding.h" #import "FBSDKEventBindingManager.h" -#import "FBSDKSampleEventBinding.h" -#import "FBSDKCoreKitTests-Swift.h" -@interface FBSDKEventBindingTests : XCTestCase { +@interface FBSDKEventBindingTests : XCTestCase +{ UIWindow *window; FBSDKEventBindingManager *eventBindingManager; UIButton *btnBuy; @@ -34,7 +34,7 @@ @interface FBSDKEventBindingTests : XCTestCase { @end -@interface FBSDKEventBinding(Testing) +@interface FBSDKEventBinding (Testing) + (NSString *)findParameterOfPath:(NSArray *)path pathType:(NSString *)pathType @@ -44,52 +44,53 @@ + (NSString *)findParameterOfPath:(NSArray *)path @implementation FBSDKEventBindingTests -- (void)setUp { +- (void)setUp +{ [super setUp]; - if (@available(iOS 9.0, *)) { - eventBindingManager = [[FBSDKEventBindingManager alloc] - initWithJSON:[FBSDKSampleEventBinding getSampleDictionary]]; - window = [[UIWindow alloc] init]; - UIViewController *vc = [[UIViewController alloc] init]; - UINavigationController *nav = [[UINavigationController alloc] initWithRootViewController:vc]; + eventBindingManager = [[FBSDKEventBindingManager alloc] + initWithJSON:[FBSDKSampleEventBinding getSampleDictionary]]; + window = [[UIWindow alloc] init]; + UIViewController *vc = [[UIViewController alloc] init]; + UINavigationController *nav = [[UINavigationController alloc] initWithRootViewController:vc]; - UITabBarController *tab = [[UITabBarController alloc] init]; - tab.viewControllers = @[nav]; - window.rootViewController = tab; + UITabBarController *tab = [[UITabBarController alloc] init]; + tab.viewControllers = @[nav]; + window.rootViewController = tab; - UIStackView *firstStackView = [[UIStackView alloc] init]; - [vc.view addSubview:firstStackView]; - UIStackView *secondStackView = [[UIStackView alloc] init]; - [firstStackView addSubview:secondStackView]; + UIStackView *firstStackView = [[UIStackView alloc] init]; + [vc.view addSubview:firstStackView]; + UIStackView *secondStackView = [[UIStackView alloc] init]; + [firstStackView addSubview:secondStackView]; - btnBuy = [UIButton buttonWithType:UIButtonTypeCustom]; - [btnBuy setTitle:@"Buy" forState:UIControlStateNormal]; - [firstStackView addSubview:btnBuy]; + btnBuy = [UIButton buttonWithType:UIButtonTypeCustom]; + [btnBuy setTitle:NSLocalizedString(@"Buy", nil) forState:UIControlStateNormal]; + [firstStackView addSubview:btnBuy]; - UILabel *lblPrice = [[UILabel alloc] init]; - lblPrice.text = @"$2.0"; - [firstStackView addSubview:lblPrice]; + UILabel *lblPrice = [[UILabel alloc] init]; + lblPrice.text = NSLocalizedString(@"$2.0", nil); + [firstStackView addSubview:lblPrice]; - btnConfirm = [UIButton buttonWithType:UIButtonTypeCustom]; - [btnConfirm setTitle:@"Confirm" forState:UIControlStateNormal]; - [firstStackView addSubview:btnConfirm]; + btnConfirm = [UIButton buttonWithType:UIButtonTypeCustom]; + [btnConfirm setTitle:NSLocalizedString(@"Confirm", nil) forState:UIControlStateNormal]; + [firstStackView addSubview:btnConfirm]; - lblPrice = [[UILabel alloc] init]; - lblPrice.text = @"$3.0"; - [secondStackView addSubview:lblPrice]; + lblPrice = [[UILabel alloc] init]; + lblPrice.text = NSLocalizedString(@"$3.0", nil); + [secondStackView addSubview:lblPrice]; - stepper = [[UIStepper alloc] init]; - [secondStackView addSubview:stepper]; - } + stepper = [[UIStepper alloc] init]; + [secondStackView addSubview:stepper]; } -- (void)tearDown { - // Put teardown code here. This method is called after the invocation of each test method in the class. - [super tearDown]; +- (void)tearDown +{ + // Put teardown code here. This method is called after the invocation of each test method in the class. + [super tearDown]; } -- (void)testMatching { +- (void)testMatching +{ NSArray *bindings = [FBSDKEventBindingManager parseArray:[FBSDKSampleEventBinding getSampleDictionary][@"event_bindings"]]; FBSDKEventBinding *binding = bindings[0]; XCTAssertTrue([FBSDKEventBinding isViewMatchPath:stepper path:binding.path]); @@ -97,7 +98,7 @@ - (void)testMatching { binding = bindings[1]; FBSDKCodelessParameterComponent *component = binding.parameters[0]; XCTAssertTrue([FBSDKEventBinding isViewMatchPath:btnBuy path:binding.path]); - NSString *price = [FBSDKEventBinding findParameterOfPath:component.path pathType:component.pathType sourceView:btnBuy]; + NSString *price = [FBSDKEventBinding findParameterOfPath:component.path pathType:component.pathType sourceView:btnBuy]; XCTAssertEqual(price, @"$2.0"); binding = bindings[2]; @@ -110,7 +111,8 @@ - (void)testMatching { XCTAssertEqual(action, @"Confirm"); } -- (void)testEventBindingEquation { +- (void)testEventBindingEquation +{ NSArray *bindings = [FBSDKEventBindingManager parseArray:[FBSDKSampleEventBinding getSampleDictionary][@"event_bindings"]]; XCTAssertTrue([bindings[0] isEqualToBinding:bindings[0]]); XCTAssertFalse([bindings[0] isEqualToBinding:bindings[1]]); @@ -118,9 +120,9 @@ - (void)testEventBindingEquation { - (void)testParsing { - for (int i = 0; i < 1000; i++) { + for (int i = 0; i < 100; i++) { NSDictionary *sampleData = [FBSDKSampleEventBinding getSampleDictionary]; - [FBSDKEventBindingManager parseArray: @[[Fuzzer randomizeWithJson:sampleData]]]; + [FBSDKEventBindingManager parseArray:@[[Fuzzer randomizeWithJson:sampleData]]]; } } diff --git a/FBSDKCoreKit/FBSDKCoreKitTests/Internal/AppEvents/Codeless/FBSDKSampleEventBinding.m b/FBSDKCoreKit/FBSDKCoreKitTests/Internal/AppEvents/Codeless/FBSDKSampleEventBinding.m deleted file mode 100644 index b212a62bf6..0000000000 --- a/FBSDKCoreKit/FBSDKCoreKitTests/Internal/AppEvents/Codeless/FBSDKSampleEventBinding.m +++ /dev/null @@ -1,140 +0,0 @@ -// 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 "FBSDKSampleEventBinding.h" - -@implementation FBSDKSampleEventBinding -+(NSDictionary*)getSampleDictionary -{ - return @{ - @"event_bindings": @[ - @{ - @"event_name": @"Quantity Changed", - @"event_type": @"click", - @"app_version": @"1.2", - @"path": @[ - @{ - @"class_name":@"UIWindow", - }, - @{ - @"class_name":@"UITabBarController", - }, - @{ - @"class_name":@"UINavigationController", - }, - @{ - @"class_name":@"UIViewController", - }, - @{ - @"class_name":@"UIStackView", - }, - @{ - @"class_name":@"UIStackView", - }, - @{ - @"class_name":@"UIStepper", - } - ] - }, - @{ - @"event_name": @"Add To Cart", - @"event_type": @"click", - @"app_version": @"1.2", - @"path": @[ - @{ - @"class_name":@"UIViewController", - }, - @{ - @"class_name":@"UIStackView", - }, - @{ - @"class_name":@"UIButton", - @"text": @"Buy" - } - ], - @"parameters": @[ - @{ - @"parameter_name": @"price", - @"path_type": @"relative", - @"path": @[ - @{ - @"class_name": @"..", - }, - @{ - @"class_name": @"UILabel", - @"index": @2, - }, - ] - } - ] - }, - @{ - @"event_name": @"Purchase", - @"event_type": @"click", - @"app_version": @"1.2", - @"path": @[ - @{ - @"class_name":@"UIWindow", - }, - @{ - @"class_name":@"UITabBarController", - }, - @{ - @"class_name":@"UINavigationController", - }, - @{ - @"class_name":@"UIViewController", - }, - @{ - @"class_name":@"UIStackView", - }, - @{ - @"class_name": @"UIButton", - @"text": @"Confirm" - }, - ], - @"parameters": @[ - @{ - @"parameter_name":@"price", - @"path_type": @"relative", - @"path": @[ - @{ - @"class_name":@"..", - }, - @{ - @"class_name":@"UIStackView", - }, - @{ - @"class_name":@"UILabel", - @"index": @0, - } - ] - }, - @{ - @"parameter_name": @"action", - @"path_type": @"relative", - @"path": @[ - @{@"class_name": @"."} - ], - }, - ] - } - ], - }; -} -@end diff --git a/FBSDKCoreKit/FBSDKCoreKitTests/Internal/AppEvents/Codeless/FBSDKSampleEventBinding.swift b/FBSDKCoreKit/FBSDKCoreKitTests/Internal/AppEvents/Codeless/FBSDKSampleEventBinding.swift new file mode 100644 index 0000000000..3c365fb5c4 --- /dev/null +++ b/FBSDKCoreKit/FBSDKCoreKitTests/Internal/AppEvents/Codeless/FBSDKSampleEventBinding.swift @@ -0,0 +1,140 @@ +// 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. + +@objcMembers +class FBSDKSampleEventBinding: NSObject { + class func getSampleDictionary() -> [String: Any] { // swiftlint:disable:this function_body_length + return [ + "event_bindings": [ + [ + "event_name": "Quantity Changed", + "event_type": "click", + "app_version": "1.2", + "path": [ + [ + "class_name": "UIWindow" + ], + [ + "class_name": "UITabBarController" + ], + [ + "class_name": "UINavigationController" + ], + [ + "class_name": "UIViewController" + ], + [ + "class_name": "UIStackView" + ], + [ + "class_name": "UIStackView" + ], + [ + "class_name": "UIStepper" + ], + ], + ], + [ + "event_name": "Add To Cart", + "event_type": "click", + "app_version": "1.2", + "path": [ + [ + "class_name": "UIViewController" + ], + [ + "class_name": "UIStackView" + ], + [ + "class_name": "UIButton", + "text": "Buy", + ], + ], + "parameters": [ + [ + "parameter_name": "price", + "path_type": "relative", + "path": [ + [ + "class_name": ".." + ], + [ + "class_name": "UILabel", + "index": 2, + ] + ] + ] + ] + ], + [ + "event_name": "Purchase", + "event_type": "click", + "app_version": "1.2", + "path": [ + [ + "class_name": "UIWindow" + ], + [ + "class_name": "UITabBarController" + ], + [ + "class_name": "UINavigationController" + ], + [ + "class_name": "UIViewController" + ], + [ + "class_name": "UIStackView" + ], + [ + "class_name": "UIButton", + "text": "Confirm", + ], + ], + "parameters": [ + [ + "parameter_name": "price", + "path_type": "relative", + "path": [ + [ + "class_name": ".." + ], + [ + "class_name": "UIStackView" + ], + [ + "class_name": "UILabel", + "index": 0, + ], + ], + ], + [ + "parameter_name": "action", + "path_type": "relative", + "path": [ + [ + "class_name": "." + ] + ] + ] + ] + ] + ] + ] + } +} diff --git a/FBSDKCoreKit/FBSDKCoreKitTests/Internal/AppEvents/EventDeactivation/FBSDKEventDeactivationTests.m b/FBSDKCoreKit/FBSDKCoreKitTests/Internal/AppEvents/EventDeactivation/FBSDKEventDeactivationTests.m deleted file mode 100644 index 7edb8d459f..0000000000 --- a/FBSDKCoreKit/FBSDKCoreKitTests/Internal/AppEvents/EventDeactivation/FBSDKEventDeactivationTests.m +++ /dev/null @@ -1,64 +0,0 @@ -// 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 "FBSDKServerConfigurationManager.h" -#import "FBSDKRestrictiveDataFilterManager.h" -#import "FBSDKEventDeactivationManager.h" - -@interface FBSDKEventDeactivationTests : XCTestCase -@end - -@implementation FBSDKEventDeactivationTests - -- (void)setUp -{ - NSDictionary *events = @{ - @"fb_mobile_catalog_update" : @{ @"restrictive_param" : @{@"first_name" : @"6"}}, - @"manual_initiated_checkout" : @{ @"deprecated_param" : @[@"deprecated_3"]}, - }; - - id mockServerConfiguration = OCMClassMock([FBSDKServerConfiguration class]); - OCMStub([mockServerConfiguration restrictiveParams]).andReturn(events); - id mockServerConfigurationManager = OCMClassMock([FBSDKServerConfigurationManager class]); - OCMStub([mockServerConfigurationManager cachedServerConfiguration]).andReturn(mockServerConfiguration); - - [FBSDKEventDeactivationManager enable]; -} - -- (void)testProcessParameters -{ - NSDictionary *parameters = @{@"_ui" : @"UITabBarController", - @"_logTime" : @1576109848, - @"_session_id" : @"30AF582C-0225-40A4-B3EE-2A571AB926F3", - @"fb_mobile_launch_source" : @"Unclassified", - @"deprecated_3" : @"test", - }; - NSDictionary *result = [FBSDKEventDeactivationManager processParameters:parameters eventName:@"manual_initiated_checkout"]; - XCTAssertNil(result[@"deprecated_3"]); - XCTAssertNotNil(result[@"_ui"]); - XCTAssertNotNil(result[@"_logTime"]); - XCTAssertNotNil(result[@"_session_id"]); - XCTAssertNotNil(result[@"fb_mobile_launch_source"]); -} - -@end - diff --git a/FBSDKCoreKit/FBSDKCoreKitTests/Internal/AppEvents/EventDeactivation/FBSDKEventDeactivationTests.swift b/FBSDKCoreKit/FBSDKCoreKitTests/Internal/AppEvents/EventDeactivation/FBSDKEventDeactivationTests.swift new file mode 100644 index 0000000000..f18d4d8aa3 --- /dev/null +++ b/FBSDKCoreKit/FBSDKCoreKitTests/Internal/AppEvents/EventDeactivation/FBSDKEventDeactivationTests.swift @@ -0,0 +1,63 @@ +// 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 XCTest + +class FBSDKEventDeactivationTests: FBSDKTestCase { + override func setUp() { + super.setUp() + + let events = [ + "fb_mobile_catalog_update": [ + "restrictive_param": ["first_name": "6"] + ], + "manual_initiated_checkout": [ + "deprecated_param": ["deprecated_3"] + ] + ] + + let serverConfiguration = FBSDKServerConfigurationFixtures.config(with: ["restrictiveParams": events]) + stubCachedServerConfiguration(with: serverConfiguration) + + FBSDKEventDeactivationManager.enable() + } + + func testProcessParameters() { + let parameters: [String: Any] = [ + "_ui": "UITabBarController", + "_logTime": 1_576_109_848, + "_session_id": "30AF582C-0225-40A4-B3EE-2A571AB926F3", + "fb_mobile_launch_source": "Unclassified", + "deprecated_3": "test", + ] + + guard let result = FBSDKEventDeactivationManager.processParameters( + parameters, + eventName: "manual_initiated_checkout" + ) else { + XCTFail("Result must not be nil") + return + } + + XCTAssertNil(result["deprecated_3"]) + XCTAssertNotNil(result["_ui"]) + XCTAssertNotNil(result["_logTime"]) + XCTAssertNotNil(result["_session_id"]) + XCTAssertNotNil(result["fb_mobile_launch_source"]) + } +} diff --git a/FBSDKCoreKit/FBSDKCoreKitTests/Internal/AppEvents/FBSDKAppEventsConfigurationFixtures.h b/FBSDKCoreKit/FBSDKCoreKitTests/Internal/AppEvents/FBSDKAppEventsConfigurationFixtures.h new file mode 100644 index 0000000000..85d19c5903 --- /dev/null +++ b/FBSDKCoreKit/FBSDKCoreKitTests/Internal/AppEvents/FBSDKAppEventsConfigurationFixtures.h @@ -0,0 +1,36 @@ +// 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 "FBSDKAppEventsConfiguration.h" + +NS_ASSUME_NONNULL_BEGIN + +@interface FBSDKAppEventsConfigurationFixtures : NSObject + +/// A default configuration with valid inputs. This is the same default configuration used in production code ++ (FBSDKAppEventsConfiguration *)defaultConfig; + +/// A default configuration with custom values passed by dictionary. +/// To use: Include a dictionary with the keys and values you want to override on the default configuration ++ (FBSDKAppEventsConfiguration *)configWithDictionary:(NSDictionary *)dict; + +@end + +NS_ASSUME_NONNULL_END diff --git a/FBSDKCoreKit/FBSDKCoreKitTests/Internal/AppEvents/FBSDKAppEventsConfigurationFixtures.m b/FBSDKCoreKit/FBSDKCoreKitTests/Internal/AppEvents/FBSDKAppEventsConfigurationFixtures.m new file mode 100644 index 0000000000..84d12e6576 --- /dev/null +++ b/FBSDKCoreKit/FBSDKCoreKitTests/Internal/AppEvents/FBSDKAppEventsConfigurationFixtures.m @@ -0,0 +1,61 @@ +// 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 "FBSDKAppEventsConfigurationFixtures.h" + +#import "FBSDKAppEventsConfiguration.h" + +@interface FBSDKAppEventsConfiguration (Testing) + ++ (FBSDKAppEventsConfiguration *)defaultConfiguration; + +- (instancetype)initWithDefaultATEStatus:(FBSDKAdvertisingTrackingStatus)defaultATEStatus + advertiserIDCollectionEnabled:(BOOL)advertiserIDCollectionEnabled + eventCollectionEnabled:(BOOL)eventCollectionEnabled; + +@end + +@implementation FBSDKAppEventsConfigurationFixtures + ++ (FBSDKAppEventsConfiguration *)defaultConfig; +{ + return [FBSDKAppEventsConfiguration defaultConfiguration]; +} + ++ (FBSDKAppEventsConfiguration *)configWithDictionary:(NSDictionary *)dict +{ + FBSDKAppEventsConfiguration *defaultConfig = [FBSDKAppEventsConfiguration defaultConfiguration]; + FBSDKAdvertisingTrackingStatus defaultATEStatus = defaultConfig.defaultATEStatus; + if (dict[@"default_ate_status"]) { + defaultATEStatus = [dict[@"default_ate_status"] intValue]; + } + BOOL advertiserIDCollectionEnabled = defaultConfig.advertiserIDCollectionEnabled; + if (dict[@"advertiser_id_collection_enabled"]) { + advertiserIDCollectionEnabled = [dict[@"advertiser_id_collection_enabled"] boolValue]; + } + BOOL eventCollectionEnabled = defaultConfig.eventCollectionEnabled; + if (dict[@"event_collection_enabled"]) { + eventCollectionEnabled = [dict[@"event_collection_enabled"] boolValue]; + } + return [[FBSDKAppEventsConfiguration alloc] + initWithDefaultATEStatus:defaultATEStatus + advertiserIDCollectionEnabled:advertiserIDCollectionEnabled + eventCollectionEnabled:eventCollectionEnabled]; +} + +@end diff --git a/FBSDKCoreKit/FBSDKCoreKitTests/Internal/AppEvents/FBSDKAppEventsConfigurationManagerTests.swift b/FBSDKCoreKit/FBSDKCoreKitTests/Internal/AppEvents/FBSDKAppEventsConfigurationManagerTests.swift new file mode 100644 index 0000000000..c012dcc8cf --- /dev/null +++ b/FBSDKCoreKit/FBSDKCoreKitTests/Internal/AppEvents/FBSDKAppEventsConfigurationManagerTests.swift @@ -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. + +class FBSDKAppEventsConfigurationManagerTests: XCTestCase { + func testParsingResponses() { + for _ in 0..<100 { + AppEventsConfigurationManager._processResponse(RawAppEventsConfigurationResponseFixtures.random, error: nil) + } + } +} diff --git a/FBSDKCoreKit/FBSDKCoreKitTests/Internal/AppEvents/FBSDKAppEventsConfigurationTests.swift b/FBSDKCoreKit/FBSDKCoreKitTests/Internal/AppEvents/FBSDKAppEventsConfigurationTests.swift new file mode 100644 index 0000000000..8ea2908ba8 --- /dev/null +++ b/FBSDKCoreKit/FBSDKCoreKitTests/Internal/AppEvents/FBSDKAppEventsConfigurationTests.swift @@ -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 XCTest + +class FBSDKAppEventsConfigurationTests: XCTestCase { + typealias Fixtures = FBSDKAppEventsConfigurationFixtures + + private var config = Fixtures.defaultConfig() + + override func setUp() { + super.setUp() + config = Fixtures.defaultConfig() + } + + func testCreatingWithDefaultATEStatus() { + XCTAssertEqual(config.defaultATEStatus, .unspecified, "Default ATE Status should be unspecified") + } + + func testCreatingWithKnownDefaultATEStatus() { + config = Fixtures.config(with: ["default_ate_status": AppEventsUtility.AdvertisingTrackingStatus.allowed.rawValue]) + XCTAssertEqual(config.defaultATEStatus, .allowed, "Default ATE Status should be settable") + } + + func testCreatingWithDefaultAdvertisingIDCollectionEnabled() { + XCTAssertTrue(config.advertiserIDCollectionEnabled, + "Advertising identifier collection enabled should default to true") + } + + func testCreatingWithKnownAdvertisingIDCollectionEnabled() { + config = Fixtures.config(with: ["advertiser_id_collection_enabled": false]) + XCTAssertFalse(config.advertiserIDCollectionEnabled, "Advertising identifier collection enabled should be settable") + } + + func testCreatingWithDefaultEventCollectionEnabled() { + XCTAssertFalse(config.eventCollectionEnabled, "Event collection enabled should default to false") + } + + func testCreatingWithKnownEventCollectionEnabled() { + config = Fixtures.config(with: ["event_collection_enabled": true]) + XCTAssertTrue(config.eventCollectionEnabled, "Event collection enabled should be settable") + } +} diff --git a/FBSDKCoreKit/FBSDKCoreKitTests/Internal/AppEvents/FBSDKAppEventsStateTests.m b/FBSDKCoreKit/FBSDKCoreKitTests/Internal/AppEvents/FBSDKAppEventsStateTests.m index 26c99c6898..ac5359b0f8 100644 --- a/FBSDKCoreKit/FBSDKCoreKitTests/Internal/AppEvents/FBSDKAppEventsStateTests.m +++ b/FBSDKCoreKit/FBSDKCoreKitTests/Internal/AppEvents/FBSDKAppEventsStateTests.m @@ -16,113 +16,445 @@ // 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 "FBSDKAppEventsState.h" -#import "FBSDKBasicUtility.h" +#import "FBSDKCoreKitTests-Swift.h" +#import "FBSDKInternalUtility.h" +#import "FBSDKTestCase.h" #define FBSDK_APPEVENTSSTATE_MAX_EVENTS 1000 -@interface FBSDKAppEventsStateTests : XCTestCase +@interface FBSDKAppEventsStateTests : FBSDKTestCase @end @implementation FBSDKAppEventsStateTests +{ + FBSDKAppEventsState *_state; + FBSDKAppEventsState *_partiallyFullState; + FBSDKAppEventsState *_fullState; +} -- (void)testAppEventsStateAddSimple +- (void)setUp { - FBSDKAppEventsState *target = [[FBSDKAppEventsState alloc] initWithToken:@"token" appID:@"app"]; - XCTAssertEqual(0, target.events.count); - XCTAssertEqual(0, target.numSkipped); - XCTAssertTrue([target areAllEventsImplicit]); + [super setUp]; - [target addEvent:@{ @"event1" : @1 } isImplicit:YES]; - XCTAssertEqual(1, target.events.count); - XCTAssertEqual(0, target.numSkipped); - XCTAssertTrue([target areAllEventsImplicit]); + [self.appEventStatesMock stopMocking]; + [self setUpFixtures]; +} - [target addEvent:@{ @"event2" : @2 } isImplicit:NO]; - XCTAssertEqual(2, target.events.count); - XCTAssertEqual(0, target.numSkipped); - XCTAssertFalse([target areAllEventsImplicit]); +- (void)setUpFixtures +{ + _state = [[FBSDKAppEventsState alloc] initWithToken:self.name appID:self.appID]; + XCTAssertEqual(0, _state.events.count, "sanity check"); - NSString *expectedJSON = @"[{\"event1\":1},{\"event2\":2}]"; - XCTAssertEqualObjects(expectedJSON, [target JSONStringForEvents:YES]); + _partiallyFullState = [[FBSDKAppEventsState alloc] initWithToken:self.name appID:self.appID]; + [_partiallyFullState addEvent:SampleAppEvents.validEvent isImplicit:NO]; + XCTAssertEqual(1, _partiallyFullState.events.count, "sanity check"); - FBSDKAppEventsState *copy = [target copy]; - [copy addEvent:@{ @"copy1" : @3 } isImplicit:YES]; - XCTAssertEqual(2, target.events.count); - XCTAssertEqual(3, copy.events.count); + _fullState = [[FBSDKAppEventsState alloc] initWithToken:self.name appID:self.appID]; + for (size_t i = 0; i < FBSDK_APPEVENTSSTATE_MAX_EVENTS; ++i) { + [_fullState addEvent:SampleAppEvents.validEvent isImplicit:NO]; + } + XCTAssertEqual(FBSDK_APPEVENTSSTATE_MAX_EVENTS, _fullState.events.count, "sanity check"); +} - [target addEventsFromAppEventState:copy]; - XCTAssertEqual(5, target.events.count); - XCTAssertFalse([target areAllEventsImplicit]); +- (void)testDefaults +{ + XCTAssertEqual(0, _state.events.count, "Should have no events by default"); + XCTAssertEqual(0, _state.numSkipped, "Should have no skipped events by default"); + XCTAssertTrue( + [_state areAllEventsImplicit], + "Should consider all events to be implicit when there are no events" + ); } -- (void)testisCompatibleWithAppEventsState1 +- (void)testCreatingWithNilTokenNilAppID { - FBSDKAppEventsState *eventState = [[FBSDKAppEventsState alloc] initWithToken:@"token" appID:@"app"]; - FBSDKAppEventsState *target1 = [[FBSDKAppEventsState alloc] initWithToken:@"token" appID:@"app"]; - XCTAssertTrue([eventState isCompatibleWithAppEventsState:target1]); + XCTAssertNotNil( + [[FBSDKAppEventsState alloc] initWithToken:nil appID:nil], + "Should not create app events state with missing token and app id but you can" + ); +} - FBSDKAppEventsState *target2 = [[FBSDKAppEventsState alloc] initWithToken:@"token1" appID:@"app"]; - XCTAssertFalse([eventState isCompatibleWithAppEventsState:target2]); +- (void)testCreatingWithNilTokenInvalidAppID +{ + XCTAssertNotNil( + [[FBSDKAppEventsState alloc] initWithToken:nil appID:@""], + "Should not create app events state with missing token and empty app id but you can" + ); + XCTAssertNotNil( + [[FBSDKAppEventsState alloc] initWithToken:nil appID:@" "], + "Should not create app events state with missing token and whitespace only app id but you can" + ); } -- (void)testIsCompatibleWithAppEventsState2 +- (void)testCreatingWithNilTokenValidAppID { - FBSDKAppEventsState *target = [[FBSDKAppEventsState alloc] initWithToken:@"token" appID:@"app"]; + XCTAssertNotNil( + [[FBSDKAppEventsState alloc] initWithToken:nil appID:self.appID], + "Should not create app events state with missing token and valid app id but you can" + ); +} - FBSDKAppEventsState *testTarget1 = [[FBSDKAppEventsState alloc] initWithToken:@"token" appID:nil]; - FBSDKAppEventsState *testTarget2 = [[FBSDKAppEventsState alloc] initWithToken:nil appID:@"app"]; - FBSDKAppEventsState *testTarget3 = [[FBSDKAppEventsState alloc] initWithToken:nil appID:nil]; - FBSDKAppEventsState *testTarget4 = [[FBSDKAppEventsState alloc] initWithToken:@"token" appID:@"app"]; +- (void)testCreatingWithInvalidTokenNilAppID +{ + XCTAssertNotNil( + [[FBSDKAppEventsState alloc] initWithToken:@"" appID:nil], + "Should not create app events state with empty token and missing app id but you can" + ); + XCTAssertNotNil( + [[FBSDKAppEventsState alloc] initWithToken:@" " appID:nil], + "Should not create app events state with whitespace only token and missing app id but you can" + ); +} - XCTAssertFalse([target isCompatibleWithAppEventsState: testTarget1]); - XCTAssertFalse([target isCompatibleWithAppEventsState: testTarget2]); - XCTAssertFalse([target isCompatibleWithAppEventsState: testTarget3]); - XCTAssertTrue([target isCompatibleWithAppEventsState: testTarget4]); +- (void)testCreatingWithInvalidTokenInvalidAppID +{ + XCTAssertNotNil( + [[FBSDKAppEventsState alloc] initWithToken:@"" appID:@""], + "Should not create app events state with invalid token and invalid app id but you can" + ); } -- (void)testAddEvent +- (void)testCreatingWithInvalidTokenValidAppID { - FBSDKAppEventsState *target = [[FBSDKAppEventsState alloc] initWithToken:@"token" appID:@"app"]; - for(size_t i = 0; i < FBSDK_APPEVENTSSTATE_MAX_EVENTS; ++i) { - [target addEvent:@{} isImplicit:NO]; - } - [target addEvent:@{} isImplicit:NO]; - XCTAssertEqual(1, target.numSkipped); + XCTAssertNotNil( + [[FBSDKAppEventsState alloc] initWithToken:@"" appID:self.appID], + "Should not create app events state with empty token and valid app id but you can" + ); + XCTAssertNotNil( + [[FBSDKAppEventsState alloc] initWithToken:@" " appID:nil], + "Should not create app events state with whitespace only token and valid app id but you can" + ); +} + +- (void)testCreatingWithValidTokenNilAppID +{ + XCTAssertNotNil( + [[FBSDKAppEventsState alloc] initWithToken:self.name appID:nil], + "Should not create app events state with valid token and missing app id but you can" + ); +} + +- (void)testCreatingWithValidTokenInvalidAppID +{ + XCTAssertNotNil( + [[FBSDKAppEventsState alloc] initWithToken:self.name appID:@""], + "Should not create app events state with valid token and empty app id but you can" + ); + XCTAssertNotNil( + [[FBSDKAppEventsState alloc] initWithToken:self.name appID:@" "], + "Should not create app events state with valid token and whitespace only app id but you can" + ); +} + +- (void)testCreatingWithValidTokenValidAppID +{ + XCTAssertNotNil( + [[FBSDKAppEventsState alloc] initWithToken:self.name appID:self.appID], + "Should be able to create app events state with valid token and app id" + ); +} + +// MARK: - Adding Events + +- (void)testAddingDuplicateEvents +{ + [_state addEvent:SampleAppEvents.validEvent isImplicit:YES]; + [_state addEvent:SampleAppEvents.validEvent isImplicit:YES]; + + XCTAssertEqual(2, _state.events.count, "Should be able to add duplicate events"); + XCTAssertEqual(0, _state.numSkipped, "Should not skip valid events"); +} + +- (void)testAddingSingleImplicitEvent +{ + [_state addEvent:SampleAppEvents.validEvent isImplicit:YES]; + + XCTAssertEqual(1, _state.events.count, "Should be able to add a valid event"); + XCTAssertEqual(0, _state.numSkipped, "Should not skip valid events"); + XCTAssertTrue( + [_state areAllEventsImplicit], + "Should consider all events to be implicit when all events were added as implicit" + ); } -- (void)testAddEventsFromAppEventState +- (void)testAddingMultipleImplicitEvents { - FBSDKAppEventsState *target = [[FBSDKAppEventsState alloc] initWithToken:@"token" appID:@"app"]; - for(size_t i = 0; i < FBSDK_APPEVENTSSTATE_MAX_EVENTS * 2; ++i) { - [target addEvent:@{} isImplicit:NO]; + [_state addEvent:SampleAppEvents.validEvent isImplicit:YES]; + [_state addEvent:[SampleAppEvents validEventWithName:@"event2"] isImplicit:YES]; + + XCTAssertEqual(2, _state.events.count, "Should be able to add multiple valid events"); + XCTAssertEqual(0, _state.numSkipped, "Should not skip valid events"); + XCTAssertTrue( + [_state areAllEventsImplicit], + "Should consider all events to be implicit when all events were added as implicit" + ); +} + +- (void)testAddingSingleNonImplicitEvents +{ + [_state addEvent:SampleAppEvents.validEvent isImplicit:NO]; + + XCTAssertEqual(1, _state.events.count, "Should be able to add a valid event"); + XCTAssertEqual(0, _state.numSkipped, "Should not skip valid events"); + XCTAssertFalse( + [_state areAllEventsImplicit], + "Should not consider all events to be implicit when no events were added as implicit" + ); +} + +- (void)testAddingMultipleNonImplicitEvents +{ + [_state addEvent:SampleAppEvents.validEvent isImplicit:NO]; + [_state addEvent:[SampleAppEvents validEventWithName:@"event2"] isImplicit:NO]; + + XCTAssertEqual(2, _state.events.count, "Should be able to add multiple valid events"); + XCTAssertEqual(0, _state.numSkipped, "Should not skip valid events"); + XCTAssertFalse( + [_state areAllEventsImplicit], + "Should not consider all events to be implicit when no events were added as implicit" + ); +} + +- (void)testAddingMixtureOfImplicitNonImplicitEvents +{ + [_state addEvent:SampleAppEvents.validEvent isImplicit:YES]; + [_state addEvent:[SampleAppEvents validEventWithName:@"event2"] isImplicit:NO]; + + XCTAssertEqual(2, _state.events.count, "Should be able to mix implicit and explicit events"); + XCTAssertEqual(0, _state.numSkipped, "Should not skip valid events"); + XCTAssertFalse( + [_state areAllEventsImplicit], + "Should not consider all events to be implicit when at least one event is non-implicit" + ); +} + +- (void)testAddingEventAtMaxCapacity +{ + [_fullState addEvent:SampleAppEvents.validEvent isImplicit:NO]; + [_fullState addEvent:SampleAppEvents.validEvent isImplicit:NO]; + + XCTAssertEqual(2, _fullState.numSkipped, "Should skip any events added after the max size is reached"); +} + +// MARK: - Events from AppEventState + +- (void)testAddingEventsToDuplicateEvents +{ + [_partiallyFullState addEventsFromAppEventState:_partiallyFullState]; + + XCTAssertEqual(2, _partiallyFullState.events.count, "Duplicate event states should not be addable but they are"); + XCTAssertEqual(0, _partiallyFullState.numSkipped, "Duplicate event states should not be addable but they are"); +} + +- (void)testAddingEventsFromEmptyStateToEmptyState +{ + FBSDKAppEventsState *state2 = [[FBSDKAppEventsState alloc] initWithToken:self.name appID:self.appID]; + + [_state addEventsFromAppEventState:state2]; + + XCTAssertEqual(0, _state.events.count, "Adding an empty state to an empty state should have no effect"); + XCTAssertEqual(0, _state.numSkipped, "Adding an empty state to an empty state should have no effect"); +} + +- (void)testAddEventsFromFullStateToEmptyState +{ + [_state addEventsFromAppEventState:_fullState]; + + XCTAssertEqual( + FBSDK_APPEVENTSSTATE_MAX_EVENTS, + _state.events.count, + "Should add all the events from the other state" + ); + XCTAssertEqual( + 0, + _state.numSkipped, + "Should not skip events when there is room in the state to hold them" + ); +} + +- (void)testAddEventsFromEmptyStateToPartiallyFilledState +{ + FBSDKAppEventsState *emptyState = [[FBSDKAppEventsState alloc] initWithToken:self.name appID:self.appID]; + [_state addEvent:SampleAppEvents.validEvent isImplicit:YES]; + + [_state addEventsFromAppEventState:emptyState]; + + XCTAssertEqual(1, _state.events.count, "Adding an empty state to a partially filled state should have no effect"); + XCTAssertEqual(0, _state.numSkipped, "Adding an empty state to a partially filled state should have no effect"); +} + +- (void)testAddEventsFromPartiallyFilledStateToEmptyState +{ + [_state addEventsFromAppEventState:_partiallyFullState]; + + XCTAssertEqual(1, _state.events.count, "Should add all the events in the partially filled state to the empty state"); + XCTAssertEqual(0, _state.numSkipped, "Adding a partially filled state to an empty state should have no effect"); +} + +- (void)testAddEventsFromPartiallyFilledStateToFullState +{ + [_fullState addEventsFromAppEventState:_partiallyFullState]; + + XCTAssertEqual(FBSDK_APPEVENTSSTATE_MAX_EVENTS, _fullState.events.count, "Adding to a full state should have no effect on the event count"); + XCTAssertEqual(1, _fullState.numSkipped, "Should skip events in excess of a state's capacity"); +} + +- (void)testAddEventsFromFullStateToPartiallyFilledState +{ + [_partiallyFullState addEventsFromAppEventState:_fullState]; + + XCTAssertEqual( + FBSDK_APPEVENTSSTATE_MAX_EVENTS, + _partiallyFullState.events.count, + "Adding a full state to a partially filled state should add as many events as possible" + ); + XCTAssertEqual(1, _partiallyFullState.numSkipped, "Should skip events in excess of a state's capacity"); +} + +- (void)testAddEventsFromFullStateToFullState +{ + FBSDKAppEventsState *otherFullState = [[FBSDKAppEventsState alloc] initWithToken:self.name appID:self.appID]; + + for (size_t i = 0; i < FBSDK_APPEVENTSSTATE_MAX_EVENTS * 2; ++i) { + [otherFullState addEvent:SampleAppEvents.validEvent isImplicit:NO]; } - FBSDKAppEventsState *event = [[FBSDKAppEventsState alloc] initWithToken:@"token" appID:@"app"]; - [event addEvent:@{} isImplicit:NO]; - [target addEventsFromAppEventState:event]; - XCTAssertEqual(FBSDK_APPEVENTSSTATE_MAX_EVENTS + 1, target.numSkipped); - XCTAssertEqual(FBSDK_APPEVENTSSTATE_MAX_EVENTS, target.events.count); + [_fullState addEventsFromAppEventState:otherFullState]; + + XCTAssertEqual( + FBSDK_APPEVENTSSTATE_MAX_EVENTS, + _fullState.events.count, + "Should not add additional events to a full state" + ); + XCTAssertEqual(FBSDK_APPEVENTSSTATE_MAX_EVENTS, _fullState.events.count, "Adding to a full state should have no effect on the event count"); +} + +- (void)testAddEventsToPreviouslyOverflownState +{ + // Fills + [_state addEventsFromAppEventState:_fullState]; + // Overflows + [_state addEventsFromAppEventState:_fullState]; + // Double overflows + [_state addEventsFromAppEventState:_fullState]; + + XCTAssertEqual( + FBSDK_APPEVENTSSTATE_MAX_EVENTS * 2, + _state.numSkipped, + "Should keep a running count of skipped states" + ); + XCTAssertEqual( + FBSDK_APPEVENTSSTATE_MAX_EVENTS, + _state.events.count, + "Should not add additional events to a full state" + ); +} + +// MARK: - Compatibility + +- (void)testCompatibilityWithMatchingTokenMatchingAppID +{ + FBSDKAppEventsState *state2 = [[FBSDKAppEventsState alloc] initWithToken:self.name appID:self.appID]; + XCTAssertTrue( + [_state isCompatibleWithAppEventsState:state2], + "States with matching tokens and matching app ids should be compatible" + ); +} + +- (void)testMatchingTokenNonMatchingAppID +{ + FBSDKAppEventsState *state2 = [[FBSDKAppEventsState alloc] initWithToken:self.name appID:self.name]; + XCTAssertFalse( + [_state isCompatibleWithAppEventsState:state2], + "States with matching tokens and non-matching app ids should not be compatible" + ); +} + +- (void)testNonMatchingTokenMatchingAppID +{ + FBSDKAppEventsState *state2 = [[FBSDKAppEventsState alloc] initWithToken:self.appID appID:self.appID]; + XCTAssertFalse( + [_state isCompatibleWithAppEventsState:state2], + "States with matching non-matching tokens and matching app ids should not be compatible" + ); +} + +- (void)testNonMatchingTokenNonMatchingAppID +{ + FBSDKAppEventsState *state2 = [[FBSDKAppEventsState alloc] initWithToken:self.appID appID:self.name]; + XCTAssertFalse( + [_state isCompatibleWithAppEventsState:state2], + "States with matching non-matching tokens and non matching app ids should not be compatible" + ); +} + +- (void)testNilTokensMatchingAppID +{ + FBSDKAppEventsState *state1 = [[FBSDKAppEventsState alloc] initWithToken:nil appID:self.appID]; + FBSDKAppEventsState *state2 = [[FBSDKAppEventsState alloc] initWithToken:nil appID:self.appID]; + + XCTAssertTrue( + [state1 isCompatibleWithAppEventsState:state2], + "States with nil tokens and matching app ids should be compatible" + ); +} + +- (void)testNilTokensNonMatchingAppID +{ + FBSDKAppEventsState *state1 = [[FBSDKAppEventsState alloc] initWithToken:nil appID:self.appID]; + FBSDKAppEventsState *state2 = [[FBSDKAppEventsState alloc] initWithToken:nil appID:self.name]; + + XCTAssertFalse( + [state1 isCompatibleWithAppEventsState:state2], + "States with nil tokens and non-matching app ids should not be compatible" + ); } +// MARK: - Extract Receipt Data + - (void)testExtractReceiptData { - FBSDKAppEventsState *target = [[FBSDKAppEventsState alloc] initWithToken:@"token" appID:@"app"]; - [target addEvent:@{@"receipt_data":@"some_data"} isImplicit:NO]; - NSString* extractString = [target extractReceiptData]; - XCTAssertTrue([extractString isEqualToString: @"receipt_1::some_data;;;"]); + [_state addEvent:@{@"receipt_data" : @"some_data"} isImplicit:NO]; + NSString *extracted = [_state extractReceiptData]; + + XCTAssertTrue([extracted isEqualToString:@"receipt_1::some_data;;;"]); +} + +// MARK: - JSONString For Events + +- (void)testJSONStringForEventsWithNoEvents +{ + NSString *json = [_state JSONStringForEvents:YES]; + NSString *expected = [FBSDKBasicUtility JSONStringForObject:@[] error:nil invalidObjectHandler:nil]; + + XCTAssertEqualObjects(json, expected, "Should represent events as empty json array when there are no events"); } -- (void)testJSONStringForEvents +- (void)testJSONStringForEventsIncludingImplicitEvents { - FBSDKAppEventsState *target = [[FBSDKAppEventsState alloc] initWithToken:@"token" appID:@"app"]; - NSDictionary* someEvent = @{ @"receipt_data":@"some_receipt_data",@"data":@"mock_data"}; - [target addEvent:someEvent isImplicit:YES]; - NSString* jsonString = [target JSONStringForEvents:YES]; - NSString* expectedString = [FBSDKBasicUtility JSONStringForObject:@[@{@"data":@"mock_data"}] error:nil invalidObjectHandler:nil]; - XCTAssertTrue([jsonString isEqualToString:expectedString]); + [_state addEvent:SampleAppEvents.validEvent isImplicit:YES]; + [_state addEvent:SampleAppEvents.validEvent isImplicit:YES]; + + NSString *json = [_state JSONStringForEvents:YES]; + NSString *expected = [FBSDKBasicUtility JSONStringForObject:@[SampleAppEvents.validEvent, SampleAppEvents.validEvent] error:nil invalidObjectHandler:nil]; + + XCTAssertEqualObjects(json, expected, "Should represent events as empty json array when there are no events"); +} + +- (void)testJSONStringForEventsExcludingImplicitEvents +{ + [_state addEvent:SampleAppEvents.validEvent isImplicit:YES]; + [_state addEvent:SampleAppEvents.validEvent isImplicit:NO]; + + NSString *json = [_state JSONStringForEvents:NO]; + + NSString *expected = [FBSDKBasicUtility JSONStringForObject:@[SampleAppEvents.validEvent] error:nil invalidObjectHandler:nil]; + + XCTAssertEqualObjects(json, expected, "Should represent events as empty json array when there are no events"); } @end diff --git a/FBSDKCoreKit/FBSDKCoreKitTests/Internal/AppEvents/FBSDKAppEventsTests.m b/FBSDKCoreKit/FBSDKCoreKitTests/Internal/AppEvents/FBSDKAppEventsTests.m index 5005514268..5de660147d 100644 --- a/FBSDKCoreKit/FBSDKCoreKitTests/Internal/AppEvents/FBSDKAppEventsTests.m +++ b/FBSDKCoreKit/FBSDKCoreKitTests/Internal/AppEvents/FBSDKAppEventsTests.m @@ -16,9 +16,8 @@ // 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 @@ -29,13 +28,17 @@ #import "FBSDKAppEventsUtility.h" #import "FBSDKApplicationDelegate.h" #import "FBSDKConstants.h" +#import "FBSDKCoreKitTests-Swift.h" #import "FBSDKGateKeeperManager.h" -#import "FBSDKGraphRequest+Internal.h" #import "FBSDKGraphRequest.h" +#import "FBSDKGraphRequest+Internal.h" #import "FBSDKInternalUtility.h" #import "FBSDKLogger.h" +#import "FBSDKServerConfigurationFixtures.h" #import "FBSDKSettings.h" +#import "FBSDKTestCase.h" #import "FBSDKUtility.h" +#import "UserDefaultsSpy.h" static NSString *const _mockAppID = @"mockAppID"; static NSString *const _mockUserID = @"mockUserID"; @@ -49,6 +52,7 @@ @interface FBSDKAppEvents () @property (nonatomic, copy) NSString *pushNotificationsDeviceTokenString; - (void)checkPersistedEvents; - (void)publishInstall; +- (void)publishATE; - (void)flushForReason:(FBSDKAppEventsFlushReason)flushReason; - (void)fetchServerConfiguration:(FBSDKCodeBlock)callback; - (void)instanceLogEvent:(FBSDKAppEventName)eventName @@ -93,12 +97,10 @@ + (void)logImplicitEvent:(NSString *)eventName @end -@interface FBSDKAppEventsTests : XCTestCase +@interface FBSDKAppEventsTests : FBSDKTestCase { - id _partialMockAppEvents; - id _mockAppStates; NSString *_mockEventName; - NSDictionary *_mockPayload; + NSDictionary *_mockPayload; double _mockPurchaseAmount; NSString *_mockCurrency; } @@ -108,59 +110,70 @@ @implementation FBSDKAppEventsTests - (void)setUp { - [FBSDKAppEvents resetSingleton]; - _partialMockAppEvents = OCMPartialMock([FBSDKAppEvents singleton]); - OCMStub([_partialMockAppEvents singleton]).andReturn(_partialMockAppEvents); - _mockAppStates = OCMClassMock([FBSDKAppEventsState class]); - OCMStub([_mockAppStates alloc]).andReturn(_mockAppStates); - OCMStub([_mockAppStates initWithToken:[OCMArg any] appID:[OCMArg any]]).andReturn(_mockAppStates); + self.shouldAppEventsMockBePartial = YES; + + [super setUp]; + + [self stubLoadingAdNetworkReporterConfiguration]; + [self stubServerConfigurationFetchingWithConfiguration:FBSDKServerConfigurationFixtures.defaultConfig error:nil]; + _mockEventName = @"fb_mock_event"; - _mockPayload = @{@"fb_push_payload" : @{@"campaign" : @"testCampaign"}}; + _mockPayload = @{@"fb_push_payload" : @{@"campaign" : @"testCampaign"}}; _mockPurchaseAmount = 1.0; _mockCurrency = @"USD"; [FBSDKAppEvents setLoggingOverrideAppID:_mockAppID]; + + // Mock FBSDKAppEventsUtility methods + [self stubAppEventsUtilityShouldDropAppEventWith:NO]; + + // This should be removed when these tests are updated to check the actual requests that are created + [self stubAllocatingGraphRequestConnection]; } - (void)tearDown { - [FBSDKAppEvents resetSingleton]; - [_partialMockAppEvents stopMocking]; - [_mockAppStates stopMocking]; + [super tearDown]; + [OHHTTPStubs removeAllStubs]; } +- (void)testAppEventsMockIsSingleton +{ + XCTAssertEqual(self.appEventsMock, [FBSDKAppEvents singleton]); +} + - (void)testLogPurchaseFlush { - OCMExpect([_partialMockAppEvents flushForReason:FBSDKAppEventsFlushReasonEagerlyFlushingEvent]); + OCMExpect([self.appEventsMock flushForReason:FBSDKAppEventsFlushReasonEagerlyFlushingEvent]); - OCMStub([_partialMockAppEvents flushBehavior]).andReturn(FBSDKAppEventsFlushReasonEagerlyFlushingEvent); + OCMStub([self.appEventsMock flushBehavior]).andReturn(FBSDKAppEventsFlushReasonEagerlyFlushingEvent); [FBSDKAppEvents logPurchase:_mockPurchaseAmount currency:_mockCurrency]; - OCMVerifyAll(_partialMockAppEvents); + OCMVerifyAll(self.appEventsMock); } - (void)testLogPurchase { - OCMExpect([_partialMockAppEvents logPurchase:_mockPurchaseAmount currency:_mockCurrency parameters:[OCMArg any]]).andForwardToRealObject(); - OCMExpect([_partialMockAppEvents logPurchase:_mockPurchaseAmount currency:_mockCurrency parameters:[OCMArg any] accessToken:[OCMArg any]]).andForwardToRealObject(); - OCMExpect([_partialMockAppEvents logEvent:FBSDKAppEventNamePurchased valueToSum:@(_mockPurchaseAmount) parameters:[OCMArg any] accessToken:[OCMArg any]]).andForwardToRealObject(); - OCMExpect([_mockAppStates addEvent:[OCMArg any] isImplicit:NO]); + OCMExpect([self.appEventsMock logPurchase:_mockPurchaseAmount currency:_mockCurrency parameters:[OCMArg any]]).andForwardToRealObject(); + OCMExpect([self.appEventsMock logPurchase:_mockPurchaseAmount currency:_mockCurrency parameters:[OCMArg any] accessToken:[OCMArg any]]).andForwardToRealObject(); + OCMExpect([self.appEventsMock logEvent:FBSDKAppEventNamePurchased valueToSum:@(_mockPurchaseAmount) parameters:[OCMArg any] accessToken:[OCMArg any]]).andForwardToRealObject(); + OCMExpect([self.appEventStatesMock addEvent:[OCMArg any] isImplicit:NO]); [FBSDKAppEvents logPurchase:_mockPurchaseAmount currency:_mockCurrency]; - OCMVerifyAll(_partialMockAppEvents); - [_mockAppStates verify]; + OCMVerifyAll(self.appEventsMock); + [self.appEventStatesMock verify]; } - (void)testFlush { - OCMExpect([_partialMockAppEvents flushForReason:FBSDKAppEventsFlushReasonExplicit]); + OCMExpect([self.appEventsMock flushForReason:FBSDKAppEventsFlushReasonExplicit]); [FBSDKAppEvents flush]; - OCMVerifyAll(_partialMockAppEvents); + OCMVerifyAll(self.appEventsMock); } #pragma mark Tests for log product item @@ -168,21 +181,23 @@ - (void)testFlush - (void)testLogProductItemNonNil { NSDictionary *expectedDict = @{ - @"fb_product_availability":@"IN_STOCK", - @"fb_product_brand":@"PHILZ", - @"fb_product_condition":@"NEW", - @"fb_product_description":@"description", - @"fb_product_gtin":@"BLUE MOUNTAIN", - @"fb_product_image_link":@"https://www.sample.com", - @"fb_product_item_id":@"F40CEE4E-471E-45DB-8541-1526043F4B21", - @"fb_product_link":@"https://www.sample.com", - @"fb_product_mpn":@"BLUE MOUNTAIN", - @"fb_product_price_amount":@"1.000", - @"fb_product_price_currency":@"USD", - @"fb_product_title":@"title", + @"fb_product_availability" : @"IN_STOCK", + @"fb_product_brand" : @"PHILZ", + @"fb_product_condition" : @"NEW", + @"fb_product_description" : @"description", + @"fb_product_gtin" : @"BLUE MOUNTAIN", + @"fb_product_image_link" : @"https://www.sample.com", + @"fb_product_item_id" : @"F40CEE4E-471E-45DB-8541-1526043F4B21", + @"fb_product_link" : @"https://www.sample.com", + @"fb_product_mpn" : @"BLUE MOUNTAIN", + @"fb_product_price_amount" : @"1.000", + @"fb_product_price_currency" : @"USD", + @"fb_product_title" : @"title", }; - OCMExpect([_partialMockAppEvents logEvent:@"fb_mobile_catalog_update" - parameters:expectedDict]); + OCMExpect( + [self.appEventsMock logEvent:@"fb_mobile_catalog_update" + parameters:expectedDict] + ); [FBSDKAppEvents logProductItem:@"F40CEE4E-471E-45DB-8541-1526043F4B21" availability:FBSDKProductAvailabilityInStock @@ -198,24 +213,26 @@ - (void)testLogProductItemNonNil brand:@"PHILZ" parameters:@{}]; - OCMVerifyAll(_partialMockAppEvents); + OCMVerifyAll(self.appEventsMock); } - (void)testLogProductItemNilGtinMpnBrand { NSDictionary *expectedDict = @{ - @"fb_product_availability":@"IN_STOCK", - @"fb_product_condition":@"NEW", - @"fb_product_description":@"description", - @"fb_product_image_link":@"https://www.sample.com", - @"fb_product_item_id":@"F40CEE4E-471E-45DB-8541-1526043F4B21", - @"fb_product_link":@"https://www.sample.com", - @"fb_product_price_amount":@"1.000", - @"fb_product_price_currency":@"USD", - @"fb_product_title":@"title", + @"fb_product_availability" : @"IN_STOCK", + @"fb_product_condition" : @"NEW", + @"fb_product_description" : @"description", + @"fb_product_image_link" : @"https://www.sample.com", + @"fb_product_item_id" : @"F40CEE4E-471E-45DB-8541-1526043F4B21", + @"fb_product_link" : @"https://www.sample.com", + @"fb_product_price_amount" : @"1.000", + @"fb_product_price_currency" : @"USD", + @"fb_product_title" : @"title", }; - OCMReject([_partialMockAppEvents logEvent:@"fb_mobile_catalog_update" - parameters:expectedDict]); + OCMReject( + [self.appEventsMock logEvent:@"fb_mobile_catalog_update" + parameters:expectedDict] + ); [FBSDKAppEvents logProductItem:@"F40CEE4E-471E-45DB-8541-1526043F4B21" availability:FBSDKProductAvailabilityInStock @@ -236,7 +253,7 @@ - (void)testLogProductItemNilGtinMpnBrand - (void)testSetAndClearUserData { - NSString *mockEmail= @"test_em"; + NSString *mockEmail = @"test_em"; NSString *mockFirstName = @"test_fn"; NSString *mockLastName = @"test_ln"; NSString *mockPhone = @"123"; @@ -252,14 +269,13 @@ - (void)testSetAndClearUserData zip:nil country:nil]; - NSDictionary *expectedUserData = @{@"em":[FBSDKUtility SHA256Hash:mockEmail], - @"fn":[FBSDKUtility SHA256Hash:mockFirstName], - @"ln":[FBSDKUtility SHA256Hash:mockLastName], - @"ph":[FBSDKUtility SHA256Hash:mockPhone], - }; + NSDictionary *expectedUserData = @{@"em" : [FBSDKUtility SHA256Hash:mockEmail], + @"fn" : [FBSDKUtility SHA256Hash:mockFirstName], + @"ln" : [FBSDKUtility SHA256Hash:mockLastName], + @"ph" : [FBSDKUtility SHA256Hash:mockPhone], }; NSDictionary *userData = (NSDictionary *)[FBSDKTypeUtility JSONObjectWithData:[[FBSDKAppEvents getUserData] dataUsingEncoding:NSUTF8StringEncoding] - options:NSJSONReadingMutableContainers - error:nil]; + options: NSJSONReadingMutableContainers + error: nil]; XCTAssertEqualObjects(userData, expectedUserData); [FBSDKAppEvents clearUserData]; @@ -303,17 +319,21 @@ - (void)testSetPushNotificationsDeviceTokenString NSString *mockDeviceTokenString = @"testDeviceTokenString"; NSString *eventName = @"fb_mobile_obtain_push_token"; - OCMExpect([_partialMockAppEvents logEvent:eventName]).andForwardToRealObject(); - OCMExpect([_partialMockAppEvents logEvent:eventName - parameters:@{}]).andForwardToRealObject(); - OCMExpect([_partialMockAppEvents logEvent:eventName - valueToSum:nil - parameters:@{} - accessToken:nil]).andForwardToRealObject(); + OCMExpect([self.appEventsMock logEvent:eventName]).andForwardToRealObject(); + OCMExpect( + [self.appEventsMock logEvent:eventName + parameters:@{}] + ).andForwardToRealObject(); + OCMExpect( + [self.appEventsMock logEvent:eventName + valueToSum:nil + parameters:@{} + accessToken:nil] + ).andForwardToRealObject(); [FBSDKAppEvents setPushNotificationsDeviceTokenString:mockDeviceTokenString]; - OCMVerifyAll(_partialMockAppEvents); + OCMVerifyAll(self.appEventsMock); XCTAssertEqualObjects([FBSDKAppEvents singleton].pushNotificationsDeviceTokenString, mockDeviceTokenString); } @@ -326,23 +346,25 @@ - (void)testLogInitialize NSUserDefaults *userDefaults = [NSUserDefaults standardUserDefaults]; id userDefaultsMock = OCMPartialMock(userDefaults); OCMStub([userDefaultsMock integerForKey:[OCMArg any]]).andReturn(1); - OCMExpect([_partialMockAppEvents logInternalEvent:@"fb_sdk_initialize" - parameters:[OCMArg any] - isImplicitlyLogged:NO]); + OCMExpect( + [self.appEventsMock logInternalEvent:@"fb_sdk_initialize" + parameters:[OCMArg any] + isImplicitlyLogged:NO] + ); [delegateMock _logSDKInitialize]; - OCMVerifyAll(_partialMockAppEvents); + OCMVerifyAll(self.appEventsMock); } - (void)testActivateApp { - OCMExpect([_partialMockAppEvents publishInstall]); - OCMExpect([_partialMockAppEvents fetchServerConfiguration:NULL]); + OCMExpect([self.appEventsMock publishInstall]); + OCMExpect([self.appEventsMock fetchServerConfiguration:NULL]); [FBSDKAppEvents activateApp]; - OCMVerifyAll(_partialMockAppEvents); + OCMVerifyAll(self.appEventsMock); } #pragma mark Test for log push notification @@ -351,29 +373,29 @@ - (void)testLogPushNotificationOpen { NSString *eventName = @"fb_mobile_push_opened"; // with action and campaign - NSDictionary *expectedParams1 = @{ - @"fb_push_action":@"testAction", - @"fb_push_campaign":@"testCampaign", + NSDictionary *expectedParams1 = @{ + @"fb_push_action" : @"testAction", + @"fb_push_campaign" : @"testCampaign", }; - OCMExpect([_partialMockAppEvents logEvent:eventName parameters:expectedParams1]); + OCMExpect([self.appEventsMock logEvent:eventName parameters:expectedParams1]); [FBSDKAppEvents logPushNotificationOpen:_mockPayload action:@"testAction"]; - OCMVerifyAll(_partialMockAppEvents); + OCMVerifyAll(self.appEventsMock); // empty action - NSDictionary *expectedParams2 = @{ - @"fb_push_campaign":@"testCampaign", + NSDictionary *expectedParams2 = @{ + @"fb_push_campaign" : @"testCampaign", }; - OCMExpect([_partialMockAppEvents logEvent:eventName parameters:expectedParams2]); + OCMExpect([self.appEventsMock logEvent:eventName parameters:expectedParams2]); [FBSDKAppEvents logPushNotificationOpen:_mockPayload]; - OCMVerifyAll(_partialMockAppEvents); + OCMVerifyAll(self.appEventsMock); // empty payload - OCMReject([_partialMockAppEvents logEvent:eventName parameters:[OCMArg any]]); + OCMReject([self.appEventsMock logEvent:eventName parameters:[OCMArg any]]); [FBSDKAppEvents logPushNotificationOpen:@{}]; // empty campaign - NSDictionary *mockPayload = @{@"fb_push_payload" : @{@"campaign" : @""}}; - OCMReject([_partialMockAppEvents logEvent:eventName parameters:[OCMArg any]]); + NSDictionary *mockPayload = @{@"fb_push_payload" : @{@"campaign" : @""}}; + OCMReject([self.appEventsMock logEvent:eventName parameters:[OCMArg any]]); [FBSDKAppEvents logPushNotificationOpen:mockPayload]; } @@ -388,54 +410,205 @@ - (void)testSetFlushBehavior - (void)testCheckPersistedEventsCalledWhenLogEvent { + OCMExpect([self.appEventsMock checkPersistedEvents]); - OCMExpect([_partialMockAppEvents checkPersistedEvents]); - - OCMStub([_partialMockAppEvents flushBehavior]).andReturn(FBSDKAppEventsFlushReasonEagerlyFlushingEvent); + OCMStub([self.appEventsMock flushBehavior]).andReturn(FBSDKAppEventsFlushReasonEagerlyFlushingEvent); [FBSDKAppEvents logEvent:FBSDKAppEventNamePurchased valueToSum:@(_mockPurchaseAmount) parameters:@{} accessToken:nil]; - OCMVerifyAll(_partialMockAppEvents); + OCMVerifyAll(self.appEventsMock); } -- (void)testRequestForCustomAudienceThirdPartyIDWithAccessToken +- (void)testRequestForCustomAudienceThirdPartyIDWithTrackingDisallowed { - id mockAccessToken = [OCMockObject niceMockForClass:[FBSDKAccessToken class]]; - id mockAppEventsUtility = [OCMockObject niceMockForClass:[FBSDKAppEventsUtility class]]; - NSString *tokenString = [FBSDKAppEventsUtility tokenStringToUseFor:mockAccessToken]; - NSString *graphPath = [NSString stringWithFormat:@"%@/custom_audience_third_party_id", _mockAppID]; - FBSDKGraphRequest *expectedRequest = [[FBSDKGraphRequest alloc] initWithGraphPath:graphPath - parameters:@{} - tokenString:tokenString - HTTPMethod:nil - flags:FBSDKGraphRequestFlagDoNotInvalidateTokenOnError | FBSDKGraphRequestFlagDisableErrorRecovery]; - - // without access token - [[mockAppEventsUtility expect] advertiserID]; + [self stubUserDefaultsWith:[UserDefaultsSpy new]]; + [self stubAdvertisingTrackingStatusWith:FBSDKAdvertisingTrackingDisallowed]; + + XCTAssertNil( + [FBSDKAppEvents requestForCustomAudienceThirdPartyIDWithAccessToken:SampleAccessToken.validToken], + "Should not create a request for third party id if tracking is disallowed even if there is a current access token" + ); + XCTAssertNil( + [FBSDKAppEvents requestForCustomAudienceThirdPartyIDWithAccessToken:nil], + "Should not create a request for third party id if tracking is disallowed" + ); +} - FBSDKGraphRequest *request = [FBSDKAppEvents requestForCustomAudienceThirdPartyIDWithAccessToken:nil]; +- (void)testRequestForCustomAudienceThirdPartyIDWithLimitedEventAndDataUsage +{ + [self stubSettingsShouldLimitEventAndDataUsageWith:YES]; + [self stubAdvertisingTrackingStatusWith:FBSDKAdvertisingTrackingAllowed]; + + XCTAssertNil( + [FBSDKAppEvents requestForCustomAudienceThirdPartyIDWithAccessToken:SampleAccessToken.validToken], + "Should not create a request for third party id if event and data usage is limited even if there is a current access token" + ); + XCTAssertNil( + [FBSDKAppEvents requestForCustomAudienceThirdPartyIDWithAccessToken:nil], + "Should not create a request for third party id if event and data usage is limited" + ); +} - [mockAppEventsUtility verify]; +- (void)testRequestForCustomAudienceThirdPartyIDWithoutAccessTokenWithoutAdvertiserID +{ + [self stubSettingsShouldLimitEventAndDataUsageWith:NO]; + [self stubAdvertisingTrackingStatusWith:FBSDKAdvertisingTrackingAllowed]; + [self stubAppEventsUtilityAdvertiserIDWith:nil]; + + XCTAssertNil( + [FBSDKAppEvents requestForCustomAudienceThirdPartyIDWithAccessToken:nil], + "Should not create a request for third party id if there is no access token or advertiser id" + ); +} - XCTAssertNil(request); +- (void)testRequestForCustomAudienceThirdPartyIDWithoutAccessTokenWithAdvertiserID +{ + NSString *advertiserID = @"abc123"; + [self stubSettingsShouldLimitEventAndDataUsageWith:NO]; + [self stubAdvertisingTrackingStatusWith:FBSDKAdvertisingTrackingAllowed]; + [self stubAppEventsUtilityAdvertiserIDWith:advertiserID]; - // with access token - [[mockAppEventsUtility reject] advertiserID]; + FBSDKGraphRequest *request = [FBSDKAppEvents requestForCustomAudienceThirdPartyIDWithAccessToken:nil]; + XCTAssertEqualObjects( + request.parameters, + @{ @"udid" : advertiserID }, + "Should include the udid in the request when there is no access token available" + ); +} - request = [FBSDKAppEvents requestForCustomAudienceThirdPartyIDWithAccessToken:mockAccessToken]; +- (void)testRequestForCustomAudienceThirdPartyIDWithAccessTokenWithoutAdvertiserID +{ + FBSDKAccessToken *token = SampleAccessToken.validToken; + [self stubSettingsShouldLimitEventAndDataUsageWith:NO]; + [self stubAdvertisingTrackingStatusWith:FBSDKAdvertisingTrackingAllowed]; + [self stubAppEventsUtilityAdvertiserIDWith:nil]; + [self stubAppEventsUtilityTokenStringToUseForTokenWith:token.tokenString]; + + FBSDKGraphRequest *request = [FBSDKAppEvents requestForCustomAudienceThirdPartyIDWithAccessToken:token]; + XCTAssertEqualObjects( + request.tokenString, + token.tokenString, + "Should include the access token in the request when there is one available" + ); + XCTAssertNil( + request.parameters[@"udid"], + "Should not include the udid in the request when there is none available" + ); +} - XCTAssertEqualObjects(expectedRequest.graphPath, request.graphPath); - XCTAssertEqualObjects(expectedRequest.HTTPMethod, request.HTTPMethod); - XCTAssertEqualObjects(expectedRequest.parameters, expectedRequest.parameters); +- (void)testRequestForCustomAudienceThirdPartyIDWithAccessTokenWithAdvertiserID +{ + NSString *expectedGraphPath = [NSString stringWithFormat:@"%@/custom_audience_third_party_id", _mockAppID]; + + FBSDKAccessToken *token = SampleAccessToken.validToken; + NSString *advertiserID = @"abc123"; + + [self stubSettingsShouldLimitEventAndDataUsageWith:NO]; + [self stubAdvertisingTrackingStatusWith:FBSDKAdvertisingTrackingAllowed]; + [self stubAppEventsUtilityTokenStringToUseForTokenWith:token.tokenString]; + [self stubAppEventsUtilityAdvertiserIDWith:advertiserID]; + + FBSDKGraphRequest *request = [FBSDKAppEvents requestForCustomAudienceThirdPartyIDWithAccessToken:token]; + + XCTAssertEqualObjects( + request.tokenString, + token.tokenString, + "Should include the access token in the request when there is one available" + ); + XCTAssertNil( + request.parameters[@"udid"], + "Should not include the udid in the request when there is an access token available" + ); + XCTAssertEqualObjects( + request.graphPath, + expectedGraphPath, + "Should use the expected graph path for the request" + ); + XCTAssertEqual( + request.HTTPMethod, + FBSDKHTTPMethodGET, + "Should use the expected http method for the request" + ); + XCTAssertEqual( + request.flags, + FBSDKGraphRequestFlagDoNotInvalidateTokenOnError | FBSDKGraphRequestFlagDisableErrorRecovery, + "Should use the expected flags for the request" + ); } - (void)testPublishInstall { - OCMExpect([_partialMockAppEvents fetchServerConfiguration:[OCMArg any]]); + [self stubUserDefaultsWith:[UserDefaultsSpy new]]; + [self stubAppID:self.appID]; + OCMExpect([self.appEventsMock fetchServerConfiguration:[OCMArg any]]); - [_partialMockAppEvents publishInstall]; + [self.appEventsMock publishInstall]; - OCMVerifyAll(_partialMockAppEvents); + OCMVerifyAll(self.appEventsMock); +} + +- (void)testPublishATEWithNoPing +{ + [self stubAppID:@"mockAppID"]; + [self stubUserDefaultsWith:[UserDefaultsSpy new]]; + [self stubAdvertisingTrackingStatusWith:FBSDKAdvertisingTrackingAllowed]; + + id graphRequestMock = OCMClassMock([FBSDKGraphRequest class]); + OCMStub([graphRequestMock alloc]).andReturn(graphRequestMock); + OCMStub( + [graphRequestMock initWithGraphPath:[OCMArg any] + parameters:[OCMArg any] + tokenString:nil + HTTPMethod:FBSDKHTTPMethodPOST + flags:FBSDKGraphRequestFlagDoNotInvalidateTokenOnError | FBSDKGraphRequestFlagDisableErrorRecovery] + ).andReturn(graphRequestMock); + + [self.appEventsMock publishATE]; + + OCMVerify([graphRequestMock startWithCompletionHandler:[OCMArg any]]); + + [graphRequestMock stopMocking]; + graphRequestMock = nil; +} + +- (void)testPublishATEWithPingLessThan24Hours +{ + [self stubAppID:@"mockAppID"]; + UserDefaultsSpy *userDefault = [UserDefaultsSpy new]; + [userDefault setObject:[NSDate dateWithTimeIntervalSinceNow:-12 * 60 * 60] forKey:[NSString stringWithFormat:@"com.facebook.sdk:lastATEPing%@", @"mockAppID"]]; + [self stubUserDefaultsWith:userDefault]; + [self stubAdvertisingTrackingStatusWith:FBSDKAdvertisingTrackingAllowed]; + + id graphRequestMock = OCMClassMock([FBSDKGraphRequest class]); + OCMStub([graphRequestMock alloc]).andReturn(graphRequestMock); + OCMStub( + [graphRequestMock initWithGraphPath:[OCMArg any] + parameters:[OCMArg any] + tokenString:nil + HTTPMethod:FBSDKHTTPMethodPOST + flags:FBSDKGraphRequestFlagDoNotInvalidateTokenOnError | FBSDKGraphRequestFlagDisableErrorRecovery] + ).andReturn(graphRequestMock); + + [self.appEventsMock publishATE]; + + OCMReject([graphRequestMock startWithCompletionHandler:[OCMArg any]]); + + [graphRequestMock stopMocking]; + graphRequestMock = nil; +} + +- (void)testPublishATEWithVerifyingParams +{ + [self stubAppID:@"mockAppID"]; + [self stubUserDefaultsWith:[UserDefaultsSpy new]]; + [self stubAdvertisingTrackingStatusWith:FBSDKAdvertisingTrackingAllowed]; + + [self.appEventsMock publishATE]; + + OCMReject( + [self.appEventsUtilityClassMock activityParametersDictionaryForEvent:[OCMArg any] + shouldAccessAdvertisingID:[OCMArg any]] + ); } #pragma mark Tests for Kill Switch @@ -443,164 +616,167 @@ - (void)testPublishInstall - (void)testAppEventsKillSwitchDisabled { id mockGateKeeperManager = OCMClassMock([FBSDKGateKeeperManager class]); - OCMStub([mockGateKeeperManager boolForKey:[OCMArg any] - defaultValue:NO]).andReturn(NO); + OCMStub( + [mockGateKeeperManager boolForKey:[OCMArg any] + defaultValue:NO] + ).andReturn(NO); + + OCMExpect([self.appEventStatesMock addEvent:[OCMArg any] isImplicit:NO]); - OCMExpect([_mockAppStates addEvent:[OCMArg any] isImplicit:NO]); + [self.appEventsMock instanceLogEvent:_mockEventName + valueToSum:@(_mockPurchaseAmount) + parameters:nil + isImplicitlyLogged:NO + accessToken:nil]; - [_partialMockAppEvents instanceLogEvent:_mockEventName - valueToSum:@(_mockPurchaseAmount) - parameters:nil - isImplicitlyLogged:NO - accessToken:nil]; + [self.appEventStatesMock verify]; - [_mockAppStates verify]; + [mockGateKeeperManager stopMocking]; + mockGateKeeperManager = nil; } - (void)testAppEventsKillSwitchEnabled { id mockGateKeeperManager = OCMClassMock([FBSDKGateKeeperManager class]); - OCMStub([mockGateKeeperManager boolForKey:[OCMArg any] - defaultValue:NO]).andReturn(YES); - - OCMReject([_mockAppStates addEvent:[OCMArg any] isImplicit:NO]); - - [_partialMockAppEvents instanceLogEvent:_mockEventName - valueToSum:@(_mockPurchaseAmount) - parameters:nil - isImplicitlyLogged:NO - accessToken:nil]; -} - -- (void)testGraphRequestBannedWithAutoInitDisabled -{ - //test when autoInitEnabled is set to be NO - __block int activiesEndpointCalledCountDisabled = 0; - NSString *urlString = [NSString stringWithFormat:@"%@/activities", _mockAppID]; - XCTestExpectation *expectation = [self expectationWithDescription:@"No Graph Request is sent"]; - - [OHHTTPStubs stubRequestsPassingTest:^BOOL(NSURLRequest *request) { - XCTAssertNotNil(request); - if ([request.URL.absoluteString rangeOfString:urlString].location != NSNotFound) { - ++activiesEndpointCalledCountDisabled; - } - return NO; - } withStubResponse:^OHHTTPStubsResponse *(NSURLRequest *request) { - return [OHHTTPStubsResponse responseWithData:[NSData data] - statusCode:200 - headers:nil]; - }]; - - [FBSDKSettings setAutoInitEnabled:NO]; - [FBSDKAppEvents logPurchase:_mockPurchaseAmount currency:_mockCurrency]; - [expectation fulfill]; - [self waitForExpectationsWithTimeout:3 handler:^(NSError *error) { - XCTAssertNil(error); - }]; - XCTAssertEqual(0, activiesEndpointCalledCountDisabled, @"No Graph Request is sent"); + OCMStub( + [mockGateKeeperManager boolForKey:[OCMArg any] + defaultValue:NO] + ).andReturn(YES); + + OCMReject([self.appEventStatesMock addEvent:[OCMArg any] isImplicit:NO]); + + [self.appEventsMock instanceLogEvent:_mockEventName + valueToSum:@(_mockPurchaseAmount) + parameters:nil + isImplicitlyLogged:NO + accessToken:nil]; + + [mockGateKeeperManager stopMocking]; + mockGateKeeperManager = nil; } #pragma mark Tests for log event - (void)testLogEventWithValueToSum { - OCMExpect([_partialMockAppEvents logEvent:_mockEventName - valueToSum:_mockPurchaseAmount - parameters:@{}]).andForwardToRealObject(); - OCMExpect([_partialMockAppEvents logEvent:_mockEventName - valueToSum:@(_mockPurchaseAmount) - parameters:@{} - accessToken:nil]).andForwardToRealObject(); + OCMExpect( + [self.appEventsMock logEvent:_mockEventName + valueToSum:_mockPurchaseAmount + parameters:@{}] + ).andForwardToRealObject(); + OCMExpect( + [self.appEventsMock logEvent:_mockEventName + valueToSum:@(_mockPurchaseAmount) + parameters:@{} + accessToken:nil] + ).andForwardToRealObject(); [FBSDKAppEvents logEvent:_mockEventName valueToSum:_mockPurchaseAmount]; - OCMVerifyAll(_partialMockAppEvents); + OCMVerifyAll(self.appEventsMock); } - (void)testLogInternalEvents { - OCMExpect([_partialMockAppEvents logInternalEvent:_mockEventName - parameters:@{} - isImplicitlyLogged:NO]).andForwardToRealObject(); - OCMExpect([_partialMockAppEvents logInternalEvent:_mockEventName - valueToSum:nil - parameters:@{} - isImplicitlyLogged:NO - accessToken:nil]).andForwardToRealObject(); + OCMExpect( + [self.appEventsMock logInternalEvent:_mockEventName + parameters:@{} + isImplicitlyLogged:NO] + ).andForwardToRealObject(); + OCMExpect( + [self.appEventsMock logInternalEvent:_mockEventName + valueToSum:nil + parameters:@{} + isImplicitlyLogged:NO + accessToken:nil] + ).andForwardToRealObject(); [FBSDKAppEvents logInternalEvent:_mockEventName isImplicitlyLogged:NO]; - OCMVerifyAll(_partialMockAppEvents); + OCMVerifyAll(self.appEventsMock); } - (void)testLogInternalEventsWithValue { - OCMExpect([_partialMockAppEvents logInternalEvent:_mockEventName - valueToSum:_mockPurchaseAmount - parameters:@{} - isImplicitlyLogged:NO]).andForwardToRealObject(); - OCMExpect([_partialMockAppEvents logInternalEvent:_mockEventName - valueToSum:@(_mockPurchaseAmount) - parameters:@{} - isImplicitlyLogged:NO - accessToken:nil]).andForwardToRealObject(); + OCMExpect( + [self.appEventsMock logInternalEvent:_mockEventName + valueToSum:_mockPurchaseAmount + parameters:@{} + isImplicitlyLogged:NO] + ).andForwardToRealObject(); + OCMExpect( + [self.appEventsMock logInternalEvent:_mockEventName + valueToSum:@(_mockPurchaseAmount) + parameters:@{} + isImplicitlyLogged:NO + accessToken:nil] + ).andForwardToRealObject(); [FBSDKAppEvents logInternalEvent:_mockEventName valueToSum:_mockPurchaseAmount isImplicitlyLogged:NO]; - OCMVerifyAll(_partialMockAppEvents); + OCMVerifyAll(self.appEventsMock); } - (void)testLogInternalEventWithAccessToken { id mockAccessToken = [OCMockObject niceMockForClass:[FBSDKAccessToken class]]; - OCMExpect([_partialMockAppEvents logInternalEvent:_mockEventName - valueToSum:nil - parameters:@{} - isImplicitlyLogged:NO - accessToken:mockAccessToken]).andForwardToRealObject(); + OCMExpect( + [self.appEventsMock logInternalEvent:_mockEventName + valueToSum:nil + parameters:@{} + isImplicitlyLogged:NO + accessToken:mockAccessToken] + ).andForwardToRealObject(); [FBSDKAppEvents logInternalEvent:_mockEventName parameters:@{} isImplicitlyLogged:NO accessToken:mockAccessToken]; - OCMVerifyAll(_partialMockAppEvents); + OCMVerifyAll(self.appEventsMock); + + [mockAccessToken stopMocking]; + mockAccessToken = nil; } - (void)testInstanceLogEventWhenAutoLogAppEventsDisabled { - id mockSetting = OCMClassMock([FBSDKSettings class]); - OCMStub([mockSetting isAutoLogAppEventsEnabled]).andReturn(NO); - OCMReject([_partialMockAppEvents instanceLogEvent:_mockEventName - valueToSum:@(_mockPurchaseAmount) - parameters:@{} - isImplicitlyLogged:NO - accessToken:nil]).andForwardToRealObject(); + [self stubIsAutoLogAppEventsEnabled:NO]; + OCMReject( + [self.appEventsMock instanceLogEvent:_mockEventName + valueToSum:@(_mockPurchaseAmount) + parameters:@{} + isImplicitlyLogged:NO + accessToken:nil] + ).andForwardToRealObject(); [FBSDKAppEvents logInternalEvent:_mockEventName valueToSum:_mockPurchaseAmount isImplicitlyLogged:NO]; } - (void)testInstanceLogEventWhenAutoLogAppEventsEnabled { - id mockSetting = OCMClassMock([FBSDKSettings class]); - OCMStub([mockSetting isAutoLogAppEventsEnabled]).andReturn(YES); - OCMExpect([_partialMockAppEvents instanceLogEvent:_mockEventName - valueToSum:@(_mockPurchaseAmount) - parameters:@{} - isImplicitlyLogged:NO - accessToken:nil]).andForwardToRealObject(); + [self stubIsAutoLogAppEventsEnabled:YES]; + OCMExpect( + [self.appEventsMock instanceLogEvent:_mockEventName + valueToSum:@(_mockPurchaseAmount) + parameters:@{} + isImplicitlyLogged:NO + accessToken:nil] + ).andForwardToRealObject(); [FBSDKAppEvents logInternalEvent:_mockEventName valueToSum:_mockPurchaseAmount isImplicitlyLogged:NO]; - OCMVerifyAll(_partialMockAppEvents); + OCMVerifyAll(self.appEventsMock); } - (void)testLogImplicitEvent { - OCMExpect([_partialMockAppEvents instanceLogEvent:_mockEventName - valueToSum:@(_mockPurchaseAmount) - parameters:@{} - isImplicitlyLogged:YES - accessToken:nil]); + OCMExpect( + [self.appEventsMock instanceLogEvent:_mockEventName + valueToSum:@(_mockPurchaseAmount) + parameters:@{} + isImplicitlyLogged:YES + accessToken:nil] + ); [FBSDKAppEvents logImplicitEvent:_mockEventName valueToSum:@(_mockPurchaseAmount) parameters:@{} accessToken:nil]; - OCMVerifyAll(_partialMockAppEvents); + OCMVerifyAll(self.appEventsMock); } @end diff --git a/FBSDKCoreKit/FBSDKCoreKitTests/Internal/AppEvents/FBSDKAppEventsUtilityTests.m b/FBSDKCoreKit/FBSDKCoreKitTests/Internal/AppEvents/FBSDKAppEventsUtilityTests.m index 587c3b6b9d..d4f1a0370a 100644 --- a/FBSDKCoreKit/FBSDKCoreKitTests/Internal/AppEvents/FBSDKAppEventsUtilityTests.m +++ b/FBSDKCoreKit/FBSDKCoreKitTests/Internal/AppEvents/FBSDKAppEventsUtilityTests.m @@ -16,18 +16,27 @@ // 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. +// @lint-ignore-every CLANGTIDY +#import #import #import #import -#import -#import +#import "FBSDKCoreKit+Internal.h" +#import "FBSDKTestCase.h" -#import "FBSDKAppEventsUtility.h" -#import "FBSDKTypeUtility.h" -#import "FBSDKUtility.h" +static NSString *const FBSDKSettingsInstallTimestamp = @"com.facebook.sdk:FBSDKSettingsInstallTimestamp"; +static NSString *const FBSDKSettingsAdvertisingTrackingStatus = @"com.facebook.sdk:FBSDKSettingsAdvertisingTrackingStatus"; -@interface FBSDKAppEventsUtilityTests : XCTestCase +@interface FBSDKSettings () ++ (void)resetAdvertiserTrackingStatusCache; +@end + +@interface FBSDKAppEventsConfiguration () +- (void)setDefaultATEStatus:(FBSDKAdvertisingTrackingStatus)status; +@end + +@interface FBSDKAppEventsUtilityTests : FBSDKTestCase @end @@ -39,11 +48,18 @@ @implementation FBSDKAppEventsUtilityTests - (void)setUp { + self.shouldAppEventsMockBePartial = YES; + [super setUp]; + + [self stubServerConfigurationFetchingWithConfiguration:[FBSDKServerConfiguration defaultServerConfigurationForAppID:nil] error:nil]; + _mockAppEventsUtility = OCMClassMock([FBSDKAppEventsUtility class]); - OCMStub([_mockAppEventsUtility advertiserID]).andReturn([NSUUID UUID].UUIDString); [FBSDKAppEvents setUserID:@"test-user-id"]; _mockNSLocale = OCMClassMock([NSLocale class]); + + // This should be removed when these tests are updated to check the actual requests that are created + [self stubAllocatingGraphRequestConnection]; } - (void)tearDown @@ -75,6 +91,11 @@ - (void)testValidation - (void)testParamsDictionary { + OCMStub([_mockAppEventsUtility advertiserID]).andReturn([NSUUID UUID].UUIDString); + id mockFBSDKSettings = OCMClassMock([FBSDKSettings class]); + OCMStub([mockFBSDKSettings isAdvertiserTrackingEnabled]).andReturn(YES); + + OCMStub([_mockAppEventsUtility advertiserID]).andReturn([NSUUID UUID].UUIDString); NSDictionary *dict = [FBSDKAppEventsUtility activityParametersDictionaryForEvent:@"event" shouldAccessAdvertisingID:YES]; XCTAssertEqualObjects(@"event", dict[@"event"]); @@ -162,7 +183,7 @@ - (void)testGetNumberValue XCTAssertTrue([str isEqualToString:@"1234.56"]); } -#if BUCK +#ifdef BUCK - (void)testGetNumberValueWithLocaleFR { OCMStub(ClassMethod([_mockNSLocale currentLocale])).andReturn([NSLocale localeWithLocaleIdentifier:@"fr"]); @@ -186,26 +207,121 @@ - (void)testGetNumberValueWithLocaleIT XCTAssertEqualObjects(str, @"1234.56"); } -- (void)testIsSensitiveUserData +- (void)testGetAdvertiserIDOniOS14WithCollectionEnabled { - NSString *text = @"test@sample.com"; - XCTAssertTrue([FBSDKAppEventsUtility isSensitiveUserData:text]); + id mockFBSDKSettings = OCMClassMock([FBSDKSettings class]); + OCMStub([mockFBSDKSettings isAdvertiserIDCollectionEnabled]).andReturn(YES); + + id mockAppEventsConfiguration = OCMClassMock([FBSDKAppEventsConfiguration class]); + OCMStub([mockAppEventsConfiguration advertiserIDCollectionEnabled]).andReturn(YES); + id mockAppEventsConfigurationManager = OCMClassMock([FBSDKAppEventsConfigurationManager class]); + OCMStub([mockAppEventsConfigurationManager cachedAppEventsConfiguration]).andReturn(mockAppEventsConfiguration); + id mockASIdentifierManager = OCMClassMock([ASIdentifierManager class]); + OCMStub([mockASIdentifierManager advertisingIdentifier]).andReturn([NSUUID UUID]); + OCMStub([mockASIdentifierManager sharedManager]).andReturn(mockASIdentifierManager); + + if (@available(iOS 14.0, *)) { + XCTAssertNotNil( + [FBSDKAppEventsUtility advertiserID], + "Advertiser id should not be nil when collection is enabled" + ); + } +} - text = @"4716 5255 0221 9085"; - XCTAssertTrue([FBSDKAppEventsUtility isSensitiveUserData:text]); +- (void)testGetAdvertiserIDOniOS14WithCollectionDisabled +{ + id mockFBSDKSettings = OCMClassMock([FBSDKSettings class]); + OCMStub([mockFBSDKSettings isAdvertiserIDCollectionEnabled]).andReturn(YES); + + id mockAppEventsConfiguration = OCMClassMock([FBSDKAppEventsConfiguration class]); + OCMStub([mockAppEventsConfiguration advertiserIDCollectionEnabled]).andReturn(NO); + id mockAppEventsConfigurationManager = OCMClassMock([FBSDKAppEventsConfigurationManager class]); + OCMStub([mockAppEventsConfigurationManager cachedAppEventsConfiguration]).andReturn(mockAppEventsConfiguration); + OCMStub([mockAppEventsConfigurationManager cachedAppEventsConfiguration]).andReturn(mockAppEventsConfiguration); + id mockASIdentifierManager = OCMClassMock([ASIdentifierManager class]); + OCMStub([mockASIdentifierManager advertisingIdentifier]).andReturn([NSUUID UUID]); + OCMStub([mockASIdentifierManager sharedManager]).andReturn(mockASIdentifierManager); + + if (@available(iOS 14.0, *)) { + XCTAssertNil([FBSDKAppEventsUtility advertiserID]); + } +} - text = @"4716525502219085"; - XCTAssertTrue([FBSDKAppEventsUtility isSensitiveUserData:text]); +- (void)testShouldDropAppEvent +{ + id mockFBSDKSettings = OCMClassMock([FBSDKSettings class]); + OCMStub([mockFBSDKSettings getAdvertisingTrackingStatus]).andReturn(FBSDKAdvertisingTrackingDisallowed); + + id mockAppEventsConfiguration = OCMClassMock([FBSDKAppEventsConfiguration class]); + OCMStub([mockAppEventsConfiguration eventCollectionEnabled]).andReturn(NO); + id mockAppEventsConfigurationManager = OCMClassMock([FBSDKAppEventsConfigurationManager class]); + OCMStub([mockAppEventsConfigurationManager cachedAppEventsConfiguration]).andReturn(mockAppEventsConfiguration); + + if (@available(iOS 14.0, *)) { + XCTAssertTrue([FBSDKAppEventsUtility shouldDropAppEvent]); + } else { + XCTAssertFalse([FBSDKAppEventsUtility shouldDropAppEvent]); + } +} - text = @"4716525502219086"; - XCTAssertFalse([FBSDKAppEventsUtility isSensitiveUserData:text]); +- (void)testAdvertiserTrackingEnabledInAppEventPayload +{ + FBSDKAppEventsConfiguration *configuration = [[FBSDKAppEventsConfiguration alloc] initWithJSON:@{}]; + id mockAppEventsConfigurationManager = OCMClassMock([FBSDKAppEventsConfigurationManager class]); + OCMStub([mockAppEventsConfigurationManager cachedAppEventsConfiguration]).andReturn(configuration); + NSArray *statusList = @[@(FBSDKAdvertisingTrackingAllowed), @(FBSDKAdvertisingTrackingDisallowed), @(FBSDKAdvertisingTrackingUnspecified)]; + for (NSNumber *defaultATEStatus in statusList) { + [configuration setDefaultATEStatus:defaultATEStatus.unsignedIntegerValue]; + for (NSNumber *status in statusList) { + [FBSDKSettings resetAdvertiserTrackingStatusCache]; + [[NSUserDefaults standardUserDefaults] removeObjectForKey:FBSDKSettingsAdvertisingTrackingStatus]; + if ([status unsignedIntegerValue] != FBSDKAdvertisingTrackingUnspecified) { + [FBSDKSettings setAdvertiserTrackingStatus:[status unsignedIntegerValue]]; + } + NSDictionary *dict = [FBSDKAppEventsUtility activityParametersDictionaryForEvent:@"event" + shouldAccessAdvertisingID:YES]; + if (@available(iOS 14.0, *)) { + // If status is unspecified, ATE will be defaultATEStatus + if ([status unsignedIntegerValue] == FBSDKAdvertisingTrackingUnspecified) { + if ([defaultATEStatus unsignedIntegerValue] == FBSDKAdvertisingTrackingUnspecified) { + XCTAssertNil(dict[@"advertiser_tracking_enabled"], @"advertiser_tracking_enabled should not be attached to event payload if ATE is unspecified"); + } else { + BOOL advertiserTrackingEnabled = defaultATEStatus.unsignedIntegerValue == FBSDKAdvertisingTrackingAllowed; + XCTAssertTrue([@(advertiserTrackingEnabled).stringValue isEqualToString:[FBSDKTypeUtility dictionary:dict objectForKey:@"advertiser_tracking_enabled" ofType:NSString.class]], @"advertiser_tracking_enabled should be default value when ATE is not set"); + } + } else { + BOOL advertiserTrackingEnabled = status.unsignedIntegerValue == FBSDKAdvertisingTrackingAllowed; + XCTAssertTrue([@(advertiserTrackingEnabled).stringValue isEqualToString:[FBSDKTypeUtility dictionary:dict objectForKey:@"advertiser_tracking_enabled" ofType:NSString.class]], @"advertiser_tracking_enabled should be equal to ATE explicitly setted via setAdvertiserTrackingStatus"); + } + } else { + XCTAssertNotNil(dict[@"advertiser_tracking_enabled"]); + } + } + } +} - text = @""; - XCTAssertFalse([FBSDKAppEventsUtility isSensitiveUserData:text]); +- (void)testDropAppEvent +{ + id mockAppEventsState = OCMClassMock([FBSDKAppEventsState class]); + OCMStub([mockAppEventsState alloc]).andReturn(mockAppEventsState); + OCMStub([mockAppEventsState initWithToken:OCMArg.any appID:OCMArg.any]).andReturn(mockAppEventsState); + [FBSDKSettings setAppID:@"123"]; + + OCMStub([_mockAppEventsUtility shouldDropAppEvent]).andReturn(YES); + [FBSDKAppEvents logEvent:@"event"]; + OCMReject([mockAppEventsState addEvent:OCMArg.any isImplicit:NO]); +} - // number of digits less than 9 will not be considered as credit card number - text = @"4716525"; - XCTAssertFalse([FBSDKAppEventsUtility isSensitiveUserData:text]); +- (void)testSendAppEvent +{ + id mockAppEventsState = OCMClassMock([FBSDKAppEventsState class]); + OCMStub([mockAppEventsState alloc]).andReturn(mockAppEventsState); + OCMStub([mockAppEventsState initWithToken:OCMArg.any appID:OCMArg.any]).andReturn(mockAppEventsState); + [FBSDKSettings setAppID:@"123"]; + + OCMStub([_mockAppEventsUtility shouldDropAppEvent]).andReturn(NO); + [FBSDKAppEvents logEvent:@"event"]; + OCMVerify([mockAppEventsState addEvent:OCMArg.any isImplicit:NO]); } - (void)testFlushReasonToString @@ -229,4 +345,36 @@ - (void)testFlushReasonToString XCTAssertEqualObjects(@"EagerlyFlushingEvent", result6); } +- (void)testGetStandardEvents +{ + NSArray *standardEvents = @[ + @"fb_mobile_complete_registration", + @"fb_mobile_content_view", + @"fb_mobile_search", + @"fb_mobile_rate", + @"fb_mobile_tutorial_completion", + @"fb_mobile_add_to_cart", + @"fb_mobile_add_to_wishlist", + @"fb_mobile_initiated_checkout", + @"fb_mobile_add_payment_info", + @"fb_mobile_purchase", + @"fb_mobile_level_achieved", + @"fb_mobile_achievement_unlocked", + @"fb_mobile_spent_credits", + @"Contact", + @"CustomizeProduct", + @"Donate", + @"FindLocation", + @"Schedule", + @"StartTrial", + @"SubmitApplication", + @"Subscribe", + @"AdImpression", + @"AdClick", + ]; + for (NSString *event in standardEvents) { + XCTAssertTrue([FBSDKAppEventsUtility isStandardEvent:event]); + } +} + @end diff --git a/FBSDKCoreKit/FBSDKCoreKit/Internal/FBSDKTriStateBOOL.m b/FBSDKCoreKit/FBSDKCoreKitTests/Internal/AppEvents/FBSDKAppEventsUtilityTests.swift similarity index 60% rename from FBSDKCoreKit/FBSDKCoreKit/Internal/FBSDKTriStateBOOL.m rename to FBSDKCoreKit/FBSDKCoreKitTests/Internal/AppEvents/FBSDKAppEventsUtilityTests.swift index 727752a8d8..da3f92b088 100644 --- a/FBSDKCoreKit/FBSDKCoreKit/Internal/FBSDKTriStateBOOL.m +++ b/FBSDKCoreKit/FBSDKCoreKitTests/Internal/AppEvents/FBSDKAppEventsUtilityTests.swift @@ -16,34 +16,28 @@ // 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 "TargetConditionals.h" +import Foundation -#if !TARGET_OS_TV +extension FBSDKAppEventsUtilityTests { -#import "FBSDKTriStateBOOL.h" + func testIsSensitiveUserData() { + var text = "test@sample.com" + XCTAssertTrue(AppEventsUtility.isSensitiveUserData(text)) -FBSDKTriStateBOOL FBSDKTriStateBOOLFromBOOL(BOOL value) -{ - return value ? FBSDKTriStateBOOLValueYES : FBSDKTriStateBOOLValueNO; -} + text = "4716 5255 0221 9085" + XCTAssertTrue(AppEventsUtility.isSensitiveUserData(text)) -FBSDKTriStateBOOL FBSDKTriStateBOOLFromNSNumber(NSNumber *value) -{ - return ([value isKindOfClass:[NSNumber class]] ? - FBSDKTriStateBOOLFromBOOL(value.boolValue) : - FBSDKTriStateBOOLValueUnknown); -} + text = "4716525502219085" + XCTAssertTrue(AppEventsUtility.isSensitiveUserData(text)) + + text = "4716525502219086" + XCTAssertFalse(AppEventsUtility.isSensitiveUserData(text)) -BOOL BOOLFromFBSDKTriStateBOOL(FBSDKTriStateBOOL value, BOOL defaultValue) -{ - switch (value) { - case FBSDKTriStateBOOLValueYES: - return YES; - case FBSDKTriStateBOOLValueNO: - return NO; - case FBSDKTriStateBOOLValueUnknown: - return defaultValue; + text = "" + XCTAssertFalse(AppEventsUtility.isSensitiveUserData(text)) + + // number of digits less than 9 will not be considered as credit card number + text = "4716525" + XCTAssertFalse(AppEventsUtility.isSensitiveUserData(text)) } } - -#endif diff --git a/FBSDKCoreKit/FBSDKCoreKitTests/Internal/AppEvents/FBSDKPaymentObserverTests.m b/FBSDKCoreKit/FBSDKCoreKitTests/Internal/AppEvents/FBSDKPaymentObserverTests.m index 49d1dd1928..c17d815ec1 100644 --- a/FBSDKCoreKit/FBSDKCoreKitTests/Internal/AppEvents/FBSDKPaymentObserverTests.m +++ b/FBSDKCoreKit/FBSDKCoreKitTests/Internal/AppEvents/FBSDKPaymentObserverTests.m @@ -16,11 +16,10 @@ // 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 "FBSDKPaymentObserver.h" @interface FBSDKPaymentObserver () @@ -37,7 +36,8 @@ @interface FBSDKPaymentObserverTests : XCTestCase @implementation FBSDKPaymentObserverTests -- (void)testPaymentObserverAddRemove { +- (void)testPaymentObserverAddRemove +{ FBSDKPaymentObserver *observer = [FBSDKPaymentObserver singleton]; BOOL isObserving = [[observer valueForKeyPath:@"_observingTransactions"] boolValue]; @@ -66,5 +66,4 @@ - (void)testPaymenQueueUpdateTransactions [partialMockObserver verify]; } - @end diff --git a/FBSDKCoreKit/FBSDKCoreKitTests/Internal/AppEvents/FBSDKTimeSpentDataTests.m b/FBSDKCoreKit/FBSDKCoreKitTests/Internal/AppEvents/FBSDKTimeSpentDataTests.m index 930c482624..f4a55ec6fd 100644 --- a/FBSDKCoreKit/FBSDKCoreKitTests/Internal/AppEvents/FBSDKTimeSpentDataTests.m +++ b/FBSDKCoreKit/FBSDKCoreKitTests/Internal/AppEvents/FBSDKTimeSpentDataTests.m @@ -16,9 +16,8 @@ // 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 "FBSDKTimeSpentData.h" diff --git a/FBSDKCoreKit/FBSDKCoreKitTests/Internal/AppEvents/Integrity/FBSDKIntegrityTests.m b/FBSDKCoreKit/FBSDKCoreKitTests/Internal/AppEvents/Integrity/FBSDKIntegrityTests.m index a6c79565e2..17123dadd8 100644 --- a/FBSDKCoreKit/FBSDKCoreKitTests/Internal/AppEvents/Integrity/FBSDKIntegrityTests.m +++ b/FBSDKCoreKit/FBSDKCoreKitTests/Internal/AppEvents/Integrity/FBSDKIntegrityTests.m @@ -16,18 +16,14 @@ // 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 "FBSDKIntegrityManager.h" #import "FBSDKModelManager.h" +#import "FBSDKTestCase.h" -@interface FBSDKIntegrityTests : XCTestCase -{ - id _mockModelManager; -} - +@interface FBSDKIntegrityTests : FBSDKTestCase @end @implementation FBSDKIntegrityTests @@ -36,34 +32,38 @@ - (void)setUp { [super setUp]; [FBSDKIntegrityManager enable]; - _mockModelManager = OCMClassMock([FBSDKModelManager class]); } - (void)testProcessParameters1 { + // Parameter contains restrictive data NSDictionary *parameters = @{ - @"address" : @"2301 N Highland Ave, Los Angeles, CA 90068", - @"period_starts" : @"2020-02-03", + @"address" : @"2301 N Highland Ave, Los Angeles, CA 90068", // address + @"period_starts" : @"2020-02-03", // health }; - OCMStub([_mockModelManager processIntegrity:[OCMArg any]]).andReturn(YES); + OCMStub([self.modelManagerClassMock processIntegrity:[OCMArg any]]).andReturn(YES); NSDictionary *processed = [FBSDKIntegrityManager processParameters:parameters]; XCTAssertNil(processed[@"address"]); XCTAssertNil(processed[@"period_starts"]); XCTAssertNotNil(processed[@"_onDeviceParams"]); + XCTAssertTrue([processed[@"_onDeviceParams"] containsString:@"address"]); + XCTAssertTrue([processed[@"_onDeviceParams"] containsString:@"period_starts"]); } - (void)testProcessParameters2 { + // Parameter does not contain any restrictive data NSDictionary *parameters = @{ @"_valueToSum" : @1, - @"_session_id" :@"12345", + @"_session_id" : @"12345", }; - OCMStub([_mockModelManager processIntegrity:[OCMArg any]]).andReturn(NO); + OCMStub([self.modelManagerClassMock processIntegrity:[OCMArg any]]).andReturn(NO); NSDictionary *processed = [FBSDKIntegrityManager processParameters:parameters]; XCTAssertNotNil(processed[@"_valueToSum"]); + XCTAssertNotNil(processed[@"_session_id"]); XCTAssertNil(processed[@"_onDeviceParams"]); } diff --git a/FBSDKCoreKit/FBSDKCoreKitTests/Internal/AppEvents/Integrity/FBSDKRestrictiveDataFilterTests.m b/FBSDKCoreKit/FBSDKCoreKitTests/Internal/AppEvents/Integrity/FBSDKRestrictiveDataFilterTests.m index b3c0cae473..625b4d8e36 100644 --- a/FBSDKCoreKit/FBSDKCoreKitTests/Internal/AppEvents/Integrity/FBSDKRestrictiveDataFilterTests.m +++ b/FBSDKCoreKit/FBSDKCoreKitTests/Internal/AppEvents/Integrity/FBSDKRestrictiveDataFilterTests.m @@ -16,52 +16,67 @@ // 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 "FBSDKAppEvents.h" #import "FBSDKAppEventsState.h" #import "FBSDKInternalUtility.h" +#import "FBSDKRestrictiveDataFilterManager.h" #import "FBSDKServerConfiguration.h" +#import "FBSDKServerConfigurationFixtures.h" #import "FBSDKServerConfigurationManager.h" -#import "FBSDKRestrictiveDataFilterManager.h" +#import "FBSDKTestCase.h" + +typedef void (^FBSDKSKAdNetworkReporterBlock)(void); +@interface FBSDKSKAdNetworkReporter (Testing) ++ (void)_loadConfigurationWithBlock:(FBSDKSKAdNetworkReporterBlock)block; +@end + +@interface FBSDKAppEvents (Testing) +@property (nonatomic, assign) BOOL disableTimer; +@end @interface FBSDKRestrictiveDataFilterManager () -+ (void)updateFilters:(nullable NSDictionary *)restrictiveParams; -+ (NSString *)getMatchedDataTypeWithEventName:(NSString *)eventName - paramKey:(NSString *)paramKey; ++ (NSString *)_getMatchedDataTypeWithEventName:(NSString *)eventName + paramKey:(NSString *)paramKey; @end -@interface FBSDKRestrictiveDataFilterTests : XCTestCase +@interface FBSDKRestrictiveDataFilterTests : FBSDKTestCase @end @implementation FBSDKRestrictiveDataFilterTests - (void)setUp { - [super setUp]; + self.shouldAppEventsMockBePartial = YES; - NSMutableDictionary *params = [NSMutableDictionary dictionaryWithDictionary: @{ - @"test_event_name" : @{ - @"restrictive_param" : @{ - @"first name" : @"6", - @"last name" : @"7" - } - }, - @"restrictive_event_name" : @{ - @"restrictive_param" : @{ - @"dob" : @4 - } - } - }]; + [super setUp]; - id mockServerConfiguration = OCMClassMock([FBSDKServerConfiguration class]); - OCMStub([mockServerConfiguration restrictiveParams]).andReturn(params); - id mockServerConfigurationManager = OCMClassMock([FBSDKServerConfigurationManager class]); - OCMStub([mockServerConfigurationManager cachedServerConfiguration]).andReturn(mockServerConfiguration); + NSMutableDictionary *params = [NSMutableDictionary dictionaryWithDictionary:@{ + @"test_event_name" : @{ + @"restrictive_param" : @{ + @"first name" : @"6", + @"last name" : @"7" + } + }, + @"restrictive_event_name" : @{ + @"restrictive_param" : @{ + @"dob" : @4 + } + } + }]; + + FBSDKServerConfiguration *config = [FBSDKServerConfigurationFixtures configWithDictionary:@{ @"restrictiveParams" : params }]; + [self stubCachedServerConfigurationWithServerConfiguration:config]; + [self stubServerConfigurationFetchingWithConfiguration:config + error:nil]; + [self stubAllocatingGraphRequestConnection]; + [self stubLoadingAdNetworkReporterConfiguration]; + + [self.appEventsMock setDisableTimer:YES]; [FBSDKRestrictiveDataFilterManager enable]; } @@ -69,38 +84,36 @@ - (void)setUp - (void)testFilterByParams { NSString *testEventName = @"restrictive_event_name"; - id mockAppStates = [OCMockObject niceMockForClass:[FBSDKAppEventsState class]]; - OCMStub([mockAppStates alloc]).andReturn(mockAppStates); - OCMStub([mockAppStates initWithToken:[OCMArg any] appID:[OCMArg any]]).andReturn(mockAppStates); + OCMStub([self.appEventsUtilityClassMock shouldDropAppEvent]).andReturn(NO); // filtered by param key - [[mockAppStates expect] addEvent:[OCMArg checkWithBlock:^(id value){ + [[self.appEventStatesMock expect] addEvent:[OCMArg checkWithBlock:^(id value) { XCTAssertEqualObjects(value[@"_eventName"], testEventName); XCTAssertNil(value[@"dob"]); XCTAssertEqualObjects(value[@"_restrictedParams"], @"{\"dob\":\"4\"}"); return YES; }] isImplicit:NO]; - [FBSDKAppEvents logEvent:testEventName parameters:@{@"dob": @"06-29-2019"}]; - [mockAppStates verify]; + [FBSDKAppEvents logEvent:testEventName parameters:@{@"dob" : @"06-29-2019"}]; + [self.appEventStatesMock verify]; // should not be filtered - [[mockAppStates expect] addEvent:[OCMArg checkWithBlock:^(id value){ + [[self.appEventStatesMock expect] addEvent:[OCMArg checkWithBlock:^(id value) { XCTAssertEqualObjects(value[@"_eventName"], testEventName); XCTAssertEqualObjects(value[@"test_key"], @66666); XCTAssertNil(value[@"_restrictedParams"]); return YES; }] isImplicit:NO]; - [FBSDKAppEvents logEvent:testEventName parameters:@{@"test_key": @66666}]; - [mockAppStates verify]; + [FBSDKAppEvents logEvent:testEventName parameters:@{@"test_key" : @66666}]; + [self.appEventStatesMock verify]; } - (void)testGetMatchedDataTypeByParam { NSString *testEventName = @"test_event_name"; - NSString *type1 = [FBSDKRestrictiveDataFilterManager getMatchedDataTypeWithEventName:testEventName paramKey:@"first name"]; + NSString *type1 = [FBSDKRestrictiveDataFilterManager _getMatchedDataTypeWithEventName:testEventName paramKey:@"first name"]; XCTAssertEqualObjects(type1, @"6"); - NSString *type2= [FBSDKRestrictiveDataFilterManager getMatchedDataTypeWithEventName:testEventName paramKey:@"reservation number"]; + NSString *type2 = [FBSDKRestrictiveDataFilterManager _getMatchedDataTypeWithEventName:testEventName paramKey:@"reservation number"]; XCTAssertNil(type2); } @@ -113,27 +126,32 @@ - (void)testProcessEventCanHandleAnEmptyArray - (void)testProcessEventCanHandleMissingKeys { NSDictionary *> *event = @{ - @"some_event": @{} + @"some_event" : @{} }; NSMutableArray *eventArray = [[NSMutableArray alloc] initWithObjects:event, nil]; - XCTAssertNoThrow([FBSDKRestrictiveDataFilterManager processEvents:eventArray], - "Data filter manager should be able to process events with missing keys"); + XCTAssertNoThrow( + [FBSDKRestrictiveDataFilterManager processEvents:eventArray], + "Data filter manager should be able to process events with missing keys" + ); } - (void)testProcessEventDoesntReplaceEventNameIfNotRestricted { NSDictionary *> *event = @{ - @"event": @{ - @"_eventName": [NSNull null], + @"event" : @{ + @"_eventName" : [NSNull null], } }; NSMutableArray *eventArray = [[NSMutableArray alloc] initWithObjects:event, nil]; [FBSDKRestrictiveDataFilterManager processEvents:eventArray]; - XCTAssertEqual(event[@"event"][@"_eventName"], [NSNull null], - "Non-restricted event names should not be replaced"); + XCTAssertEqual( + event[@"event"][@"_eventName"], + [NSNull null], + "Non-restricted event names should not be replaced" + ); } @end diff --git a/FBSDKCoreKit/FBSDKCoreKitTests/Internal/AppEvents/Integrity/FBSDKRestrictiveDataTests.m b/FBSDKCoreKit/FBSDKCoreKitTests/Internal/AppEvents/Integrity/FBSDKRestrictiveDataTests.m index e082a8b114..4156e3b59a 100644 --- a/FBSDKCoreKit/FBSDKCoreKitTests/Internal/AppEvents/Integrity/FBSDKRestrictiveDataTests.m +++ b/FBSDKCoreKit/FBSDKCoreKitTests/Internal/AppEvents/Integrity/FBSDKRestrictiveDataTests.m @@ -18,10 +18,11 @@ #import +#import "FBSDKInternalUtility.h" #import "FBSDKRestrictiveData.h" -#import "FBSDKTypeUtility.h" +#import "FBSDKTestCase.h" -@interface FBSDKRestrictiveDataTests : XCTestCase +@interface FBSDKRestrictiveDataTests : FBSDKTestCase @end diff --git a/FBSDKCoreKit/FBSDKCoreKitTests/Internal/AppEvents/ML/FBSDKModelParserTests.mm b/FBSDKCoreKit/FBSDKCoreKitTests/Internal/AppEvents/ML/FBSDKModelParserTests.mm index c488fcd252..769c477d7e 100644 --- a/FBSDKCoreKit/FBSDKCoreKitTests/Internal/AppEvents/ML/FBSDKModelParserTests.mm +++ b/FBSDKCoreKit/FBSDKCoreKitTests/Internal/AppEvents/ML/FBSDKModelParserTests.mm @@ -38,15 +38,18 @@ @interface FBSDKModelParserTests : XCTestCase @implementation FBSDKModelParserTests -- (void)setUp { +- (void)setUp +{ _mockWeightsInfoDict = [[NSMutableDictionary alloc] init]; } -- (void)tearDown { +- (void)tearDown +{ [_mockWeightsInfoDict removeAllObjects]; } -- (void)testValidWeightsForMTML { +- (void)testValidWeightsForMTML +{ [_mockWeightsInfoDict addEntriesFromDictionary:[FBSDKModelParser getMTMLWeightsInfo]]; bool validatedRes = [FBSDKModelParser validateWeights:[self _mockWeightsWithRefDict:_mockWeightsInfoDict] @@ -55,7 +58,8 @@ - (void)testValidWeightsForMTML { XCTAssertTrue(validatedRes); } -- (void)testWeightsForMissingInfo { +- (void)testWeightsForMissingInfo +{ [_mockWeightsInfoDict removeAllObjects]; bool validatedRes = [FBSDKModelParser validateWeights:[self _mockWeightsWithRefDict:_mockWeightsInfoDict] @@ -64,7 +68,8 @@ - (void)testWeightsForMissingInfo { XCTAssertFalse(validatedRes); } -- (void)testWeightsForWrongInfo { +- (void)testWeightsForWrongInfo +{ [_mockWeightsInfoDict addEntriesFromDictionary:[FBSDKModelParser getMTMLWeightsInfo]]; [_mockWeightsInfoDict addEntriesFromDictionary:@{@"embed.weight" : @[@(1), @(1)]}]; @@ -74,9 +79,10 @@ - (void)testWeightsForWrongInfo { XCTAssertFalse(validatedRes); } -- (unordered_map)_mockWeightsWithRefDict:(NSDictionary *)dict { - unordered_map weights; - for (NSString* key in dict) { +- (unordered_map)_mockWeightsWithRefDict:(NSDictionary *)dict +{ + unordered_map weights; + for (NSString *key in dict) { NSArray *values = dict[key]; vector shape; for (NSNumber *val in values) { diff --git a/FBSDKCoreKit/FBSDKCoreKitTests/Internal/AppEvents/ML/FBSDKModelRuntimeTests.mm b/FBSDKCoreKit/FBSDKCoreKitTests/Internal/AppEvents/ML/FBSDKModelRuntimeTests.mm index f5db6c1322..260b2129d2 100644 --- a/FBSDKCoreKit/FBSDKCoreKitTests/Internal/AppEvents/ML/FBSDKModelRuntimeTests.mm +++ b/FBSDKCoreKit/FBSDKCoreKitTests/Internal/AppEvents/ML/FBSDKModelRuntimeTests.mm @@ -20,9 +20,9 @@ #if !TARGET_OS_TV -#import + #import -#include "FBSDKModelRuntime.hpp" + #include "FBSDKModelRuntime.hpp" @interface FBSDKModelRuntimeTests : XCTestCase @@ -30,7 +30,8 @@ @interface FBSDKModelRuntimeTests : XCTestCase @implementation FBSDKModelRuntimeTests -- (void)testReLU { +- (void)testReLU +{ float input_data[2][4] = { {-1, -2, 1, 2}, {1, -0.2, 3.1, -0.5}, @@ -47,7 +48,8 @@ - (void)testReLU { [self AssertEqual:expected input:input]; } -- (void)testFlatten { +- (void)testFlatten +{ float input_data[2][2][5] = { { {1, 2, 3, 4, 5}, @@ -70,7 +72,8 @@ - (void)testFlatten { [self AssertEqual:expected input:input]; } -- (void)testConcatenate { +- (void)testConcatenate +{ float tensor1_data[2][2] = { {1, 2}, {3, 4}, @@ -99,7 +102,8 @@ - (void)testConcatenate { [self AssertEqual:expected input:fbsdk::concatenate(concat_tensors)]; } -- (void)testSoftMax { +- (void)testSoftMax +{ float input_data[2][2] = { {1, 1}, {1, 3}, @@ -116,7 +120,8 @@ - (void)testSoftMax { [self AssertEqual:expected input:input]; } -- (void)testEmbedding { +- (void)testEmbedding +{ char text[] = {"\1\2"}; float embeddings_data[3][3] = { {1, 0, 0}, @@ -136,9 +141,10 @@ - (void)testEmbedding { [self AssertEqual:expected input:fbsdk::embedding(text, 2, embeddings)]; } -- (void)testDenseExample1 { +- (void)testDenseExample1 +{ float input_data[2][3] = {{1, 2, 3}, {4, 5, 6}}; - float weight_data[3][2] = {{0, 1}, {1, 0},{-1, 1}}; + float weight_data[3][2] = {{0, 1}, {1, 0}, {-1, 1}}; float bias_data[2] = {100, 200}; float expected_data[2][2] = {{99, 204}, {99, 210}}; fbsdk::MTensor input({2, 3}); @@ -152,7 +158,8 @@ - (void)testDenseExample1 { [self AssertEqual:expected input:fbsdk::dense(input, weight, bias)]; } -- (void)testDenseExample2 { +- (void)testDenseExample2 +{ float input_data[1][2] = {{1, 2}}; float weight_data[2][3] = {{0, 3, -1}, {1, 0, -2}}; float bias_data[3] = {100, 200, 5}; @@ -168,7 +175,8 @@ - (void)testDenseExample2 { [self AssertEqual:expected input:fbsdk::dense(input, weight, bias)]; } -- (void)testConv1DExample1 { +- (void)testConv1DExample1 +{ float input_data[4][2][3] = { { {1, 2, 3}, @@ -214,7 +222,8 @@ - (void)testConv1DExample1 { [self AssertEqual:expected input:fbsdk::conv1D(input, conv)]; } -- (void)testConv1DExample2 { +- (void)testConv1DExample2 +{ float input_data[1][5][3] = { { {1, 2, 3}, @@ -257,7 +266,8 @@ - (void)testConv1DExample2 { [self AssertEqual:expected input:fbsdk::conv1D(input, conv)]; } -- (void)testConv1DExample3 { +- (void)testConv1DExample3 +{ float input_data[1][2][3] = { { {-1, -1, -1}, @@ -286,21 +296,24 @@ - (void)testConv1DExample3 { [self AssertEqual:expected input:fbsdk::conv1D(input, conv)]; } -- (void)testTextVectorizationLessThanMaxLen { +- (void)testTextVectorizationLessThanMaxLen +{ char strs[] = {"0123456"}; const std::vector expected{48, 49, 50, 51, 52, 53, 54, 0, 0, 0}; - const std::vector& res = fbsdk::vectorize(strs, 10); + const std::vector &res = fbsdk::vectorize(strs, 10); XCTAssertEqual(expected, res); } -- (void)testTextVectorizationLargerThanMaxLen { +- (void)testTextVectorizationLargerThanMaxLen +{ char strs[] = {"0123456"}; const std::vector expected{48, 49, 50}; - const std::vector& res = fbsdk::vectorize(strs, 3); + const std::vector &res = fbsdk::vectorize(strs, 3); XCTAssertEqual(expected, res); } -- (void)testTranspose3D { +- (void)testTranspose3D +{ float input_data[2][3][4] = { { {0, 1, 2, 3}, @@ -342,8 +355,9 @@ - (void)testTranspose3D { [self AssertEqual:expected input:fbsdk::transpose3D(input)]; } -- (void)testTranspose2D { - float input_data[3][4]= { +- (void)testTranspose2D +{ + float input_data[3][4] = { {0, 1, 2, 3}, {4, 5, 6, 7}, {8, 9, 10, 11}, @@ -361,7 +375,8 @@ - (void)testTranspose2D { [self AssertEqual:expected input:fbsdk::transpose2D(input)]; } -- (void)testAddmv { +- (void)testAddmv +{ float input_data[2][3][2] = { { {0, 12}, @@ -397,7 +412,8 @@ - (void)testAddmv { [self AssertEqual:expected input:input]; } -- (void)testMaxPool1DExample1 { +- (void)testMaxPool1DExample1 +{ float input_data[2][2][3] = { { {-1, 2, 3}, @@ -419,7 +435,8 @@ - (void)testMaxPool1DExample1 { [self AssertEqual:expected input:fbsdk::maxPool1D(input, 2)]; } -- (void)testMaxPool1DExample2 { +- (void)testMaxPool1DExample2 +{ float input_data[2][2][3] = { { {-1, -2, -3}, @@ -441,7 +458,8 @@ - (void)testMaxPool1DExample2 { [self AssertEqual:expected input:fbsdk::maxPool1D(input, 2)]; } -- (void)testMaxPool1DExample3 { +- (void)testMaxPool1DExample3 +{ float input_data[3][3][4] = { { {-1, -2, -3, 3}, @@ -471,11 +489,11 @@ - (void)testMaxPool1DExample3 { [self AssertEqual:expected input:fbsdk::maxPool1D(input, 3)]; } -- (void)AssertEqual:(const fbsdk::MTensor&)expected - input:(const fbsdk::MTensor&)input +- (void)AssertEqual:(const fbsdk::MTensor &)expected + input:(const fbsdk::MTensor &)input { - const std::vector& expected_sizes = expected.sizes(); - const std::vector& input_sizes = input.sizes(); + const std::vector &expected_sizes = expected.sizes(); + const std::vector &input_sizes = input.sizes(); XCTAssertEqual(expected_sizes, input_sizes); const float *expected_data = expected.data(); const float *input_data = input.data(); diff --git a/FBSDKCoreKit/FBSDKCoreKitTests/Internal/AppEvents/RawAppEventsConfigurationResponseFixtures.swift b/FBSDKCoreKit/FBSDKCoreKitTests/Internal/AppEvents/RawAppEventsConfigurationResponseFixtures.swift new file mode 100644 index 0000000000..e40a184e1c --- /dev/null +++ b/FBSDKCoreKit/FBSDKCoreKitTests/Internal/AppEvents/RawAppEventsConfigurationResponseFixtures.swift @@ -0,0 +1,39 @@ +// 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 Foundation + +@objc +public class RawAppEventsConfigurationResponseFixtures: NSObject { + + enum Keys { + static let defaultATEStatus = "default_ate_status" + static let advertiserIDCollectionEnabled = "advertiser_id_collection_enabled" + static let eventCollectionEnabled = "event_collection_enabled" + } + + /// Provides a dictionary with well-known keys and random values for a network provided app events configuration + @objc + public class var random: [AnyHashable: Any] { + return [ + Keys.defaultATEStatus: Fuzzer.random, + Keys.advertiserIDCollectionEnabled: Fuzzer.random, + Keys.eventCollectionEnabled: Fuzzer.random, + ] + } +} diff --git a/FBSDKCoreKit/FBSDKCoreKitTests/Internal/AppEvents/SKAdNetwork/FBSDKSKAdNetworkConversionConfigurationTests.m b/FBSDKCoreKit/FBSDKCoreKitTests/Internal/AppEvents/SKAdNetwork/FBSDKSKAdNetworkConversionConfigurationTests.m new file mode 100644 index 0000000000..4afd96586f --- /dev/null +++ b/FBSDKCoreKit/FBSDKCoreKitTests/Internal/AppEvents/SKAdNetwork/FBSDKSKAdNetworkConversionConfigurationTests.m @@ -0,0 +1,385 @@ +// 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 + +#if !TARGET_OS_TV + + #import "FBSDKSKAdNetworkConversionConfiguration.h" + #import "FBSDKSKAdNetworkRule.h" + #import "FBSDKTestCase.h" + #import "FBSDKTypeUtility.h" + +@interface FBSDKSKAdNetworkConversionConfiguration () + ++ (nullable NSArray *)parseRules:(nullable NSArray *)rules; + +@end + +@interface FBSDKSKAdNetworkConversionConfigurationTests : FBSDKTestCase + +@end + +@implementation FBSDKSKAdNetworkConversionConfigurationTests + +- (void)setUp +{ + [super setUp]; +} + +- (void)tearDown +{ + [super tearDown]; +} + +- (void)testInit +{ + // Init with nil + FBSDKSKAdNetworkConversionConfiguration *config = [[FBSDKSKAdNetworkConversionConfiguration alloc] initWithJSON:nil]; + XCTAssertNil(config); + + // Init with invalid data + id invalidData = @[]; + config = [[FBSDKSKAdNetworkConversionConfiguration alloc] initWithJSON:(NSDictionary *)invalidData]; + XCTAssertNil(config); + + invalidData = @{ + @"data" : @[@{ + @"timer_buckets" : @(1), + @"timer_interval" : @(1000), + @"default_currency" : @"usd", + @"cutoff_time" : @(2), + }] + }; + config = [[FBSDKSKAdNetworkConversionConfiguration alloc] initWithJSON:(NSDictionary *)invalidData]; + XCTAssertNil(config); + + invalidData = @{ + @"data" : @[@{ + @"timer_buckets" : @(1), + @"timer_interval" : @(1000), + @"cutoff_time" : @(2), + @"conversion_value_rules" : @[], + }] + }; + config = [[FBSDKSKAdNetworkConversionConfiguration alloc] initWithJSON:(NSDictionary *)invalidData]; + XCTAssertNil(config); + + // Init with valid data + NSDictionary *validData = @{ + @"data" : @[@{ + @"timer_buckets" : @(1), + @"timer_interval" : @(1000), + @"default_currency" : @"usd", + @"cutoff_time" : @(2), + @"conversion_value_rules" : @[], + }] + }; + config = [[FBSDKSKAdNetworkConversionConfiguration alloc] initWithJSON:validData]; + XCTAssertEqual(1, config.timerBuckets); + XCTAssertEqual(2, config.cutoffTime); + XCTAssertTrue([config.defaultCurrency isEqualToString:@"USD"]); + XCTAssertEqualWithAccuracy(1000, config.timerInterval, 0.001); +} + +- (void)testParseRules +{ + NSArray *> *rules = @[ + @{ + @"conversion_value" : @(2), + @"events" : @[ + @{ + @"event_name" : @"fb_mobile_purchase", + } + ], + }, + @{ + @"conversion_value" : @(4), + @"events" : @[ + @{ + @"event_name" : @"fb_mobile_purchase", + @"values" : @[ + @{ + @"currency" : @"USD", + @"amount" : @(100) + } + ] + } + ], + }, + @{ + @"conversion_value" : @(3), + @"events" : @[ + @{ + @"event_name" : @"fb_mobile_purchase", + @"values" : @[ + @{ + @"currency" : @"USD", + @"amount" : @(100) + }, + @{ + @"currency" : @"JPY", + @"amount" : @(100) + } + ] + } + ], + }, + ]; + + NSArray *conversionBitRules = [FBSDKSKAdNetworkConversionConfiguration parseRules:rules]; + NSMutableArray *expected = [NSMutableArray new]; + [FBSDKTypeUtility array:expected addObject:[[FBSDKSKAdNetworkRule alloc] initWithJSON:@{ + @"conversion_value" : @(4), + @"events" : @[ + @{ + @"event_name" : @"fb_mobile_purchase", + @"values" : @[ + @{ + @"currency" : @"USD", + @"amount" : @(100) + } + ] + } + ], + }]]; + [FBSDKTypeUtility array:expected addObject:[[FBSDKSKAdNetworkRule alloc] initWithJSON:@{ + @"conversion_value" : @(3), + @"events" : @[ + @{ + @"event_name" : @"fb_mobile_purchase", + @"values" : @[ + @{ + @"currency" : @"USD", + @"amount" : @(100) + }, + @{ + @"currency" : @"JPY", + @"amount" : @(100) + } + ] + } + ], + }]]; + [FBSDKTypeUtility array:expected addObject:[[FBSDKSKAdNetworkRule alloc] initWithJSON:@{ + @"conversion_value" : @(2), + @"events" : @[ + @{ + @"event_name" : @"fb_mobile_purchase", + } + ], + }]]; + for (NSUInteger i = 0; i < expected.count; i++) { + FBSDKSKAdNetworkRule *expectedRule = [FBSDKTypeUtility array:expected objectAtIndex:i]; + FBSDKSKAdNetworkRule *parsedRule = [FBSDKTypeUtility array:conversionBitRules objectAtIndex:i]; + XCTAssertNotNil(parsedRule); + XCTAssertEqual(expectedRule.conversionValue, parsedRule.conversionValue); + XCTAssertEqual(expectedRule.events.count, parsedRule.events.count); + for (NSUInteger j = 0; j < expectedRule.events.count; j++) { + FBSDKSKAdNetworkEvent *expectedEvent = [FBSDKTypeUtility array:expectedRule.events objectAtIndex:j]; + FBSDKSKAdNetworkEvent *parsedEvent = [FBSDKTypeUtility array:parsedRule.events objectAtIndex:j]; + XCTAssertTrue([expectedEvent.eventName isEqualToString:parsedEvent.eventName]); + if (expectedEvent.values) { + XCTAssertTrue([expectedEvent.values isEqualToDictionary:parsedEvent.values]); + } else { + XCTAssertNil(parsedEvent.values); + } + } + } + + // Invalid cases + id invalidData = nil; + XCTAssertNil([FBSDKSKAdNetworkConversionConfiguration parseRules:invalidData]); + invalidData = @{}; + XCTAssertNil([FBSDKSKAdNetworkConversionConfiguration parseRules:invalidData]); + invalidData = @[ + @{ + @"conversion_value" : @(2), + @"events" : @[ + @{ + @"event_name" : @"fb_mobile_purchase", + @"values" : @[ + @{ + @"amount" : @(100) + }, + ] + } + ], + }, + @{ + @"conversion_value" : @(3), + @"events" : @[ + @{ + @"event_name" : @"fb_mobile_purchase", + @"values" : @[ + @{ + @"currency" : @"USD", + @"amount" : @(100) + }, + ] + } + ], + }, + ]; + XCTAssertEqual(1, [FBSDKSKAdNetworkConversionConfiguration parseRules:invalidData].count); +} + +- (void)testEventSet +{ + NSDictionary *data = @{ + @"data" : @[@{ + @"timer_buckets" : @(1), + @"timer_interval" : @(1000), + @"cutoff_time" : @(2), + @"default_currency" : @"usd", + @"conversion_value_rules" : @[ + @{ + @"conversion_value" : @(2), + @"events" : @[ + @{ + @"event_name" : @"fb_mobile_purchase", + } + ], + }, + @{ + @"conversion_value" : @(4), + @"events" : @[ + @{ + @"event_name" : @"fb_mobile_purchase", + @"values" : @[ + @{ + @"currency" : @"USD", + @"amount" : @(100) + } + ] + }, + @{ + @"event_name" : @"fb_mobile_complete_registration", + @"values" : @[ + @{ + @"currency" : @"EU", + @"amount" : @(100) + } + ] + }, + ], + }, + @{ + @"conversion_value" : @(3), + @"events" : @[ + @{ + @"event_name" : @"fb_mobile_purchase", + @"values" : @[ + @{ + @"currency" : @"USD", + @"amount" : @(100) + }, + @{ + @"currency" : @"JPY", + @"amount" : @(100) + } + ] + }, + @{ + @"event_name" : @"fb_mobile_search", + } + ], + }, + ] + }] + }; + + FBSDKSKAdNetworkConversionConfiguration *config = [[FBSDKSKAdNetworkConversionConfiguration alloc] initWithJSON:data]; + NSSet *expected = [NSSet setWithArray:@[@"fb_mobile_search", @"fb_mobile_purchase", @"fb_mobile_complete_registration"]]; + XCTAssertTrue([config.eventSet isEqualToSet:expected]); +} + +- (void)testCurrencySet +{ + NSDictionary *data = @{ + @"data" : @[@{ + @"timer_buckets" : @(1), + @"timer_interval" : @(1000), + @"cutoff_time" : @(2), + @"default_currency" : @"usd", + @"conversion_value_rules" : @[ + @{ + @"conversion_value" : @(2), + @"events" : @[ + @{ + @"event_name" : @"fb_mobile_purchase", + } + ], + }, + @{ + @"conversion_value" : @(4), + @"events" : @[ + @{ + @"event_name" : @"fb_mobile_purchase", + @"values" : @[ + @{ + @"currency" : @"USD", + @"amount" : @(100) + } + ] + }, + @{ + @"event_name" : @"fb_mobile_complete_registration", + @"values" : @[ + @{ + @"currency" : @"eu", + @"amount" : @(100) + } + ] + }, + ], + }, + @{ + @"conversion_value" : @(3), + @"events" : @[ + @{ + @"event_name" : @"fb_mobile_purchase", + @"values" : @[ + @{ + @"currency" : @"usd", + @"amount" : @(100) + }, + @{ + @"currency" : @"jpy", + @"amount" : @(100) + } + ] + }, + @{ + @"event_name" : @"fb_mobile_search", + } + ], + }, + ] + }] + }; + + FBSDKSKAdNetworkConversionConfiguration *config = [[FBSDKSKAdNetworkConversionConfiguration alloc] initWithJSON:data]; + NSSet *expected = [NSSet setWithArray:@[@"USD", @"EU", @"JPY"]]; + XCTAssertTrue([config.currencySet isEqualToSet:expected]); +} + +@end + +#endif diff --git a/FBSDKCoreKit/FBSDKCoreKitTests/Internal/AppEvents/SKAdNetwork/FBSDKSKAdNetworkEventTests.swift b/FBSDKCoreKit/FBSDKCoreKitTests/Internal/AppEvents/SKAdNetwork/FBSDKSKAdNetworkEventTests.swift new file mode 100644 index 0000000000..475e10a399 --- /dev/null +++ b/FBSDKCoreKit/FBSDKCoreKitTests/Internal/AppEvents/SKAdNetwork/FBSDKSKAdNetworkEventTests.swift @@ -0,0 +1,85 @@ +// 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. + +#if !os(tvOS) + +import XCTest + +class FBSDKSKAdNetworkEventTests: FBSDKTestCase { + + func testValidCases() { + var event = FBSDKSKAdNetworkEvent(json: ["event_name": "fb_mobile_purchase"]) + XCTAssertTrue(event?.eventName == "fb_mobile_purchase") + XCTAssertNil(event?.values) + event = FBSDKSKAdNetworkEvent( + json: [ + "event_name": "fb_mobile_purchase", + "values": [ + [ + "currency": "usd", + "amount": 100 + ], + [ + "currency": "JPY", + "amount": 1000 + ] + ] + ] + ) + XCTAssertTrue(event?.eventName == "fb_mobile_purchase") + let expectedValues: [String: NSNumber] = [ + "USD": 100, + "JPY": 1000 + ] + XCTAssertTrue(event?.values == expectedValues) + } + + func testInvalidCases() { + var invalidData: [String: Any] = [:] + XCTAssertNil(FBSDKSKAdNetworkEvent(json: invalidData)) + invalidData = [ + "values": [ + [ + "currency": "usd", + "amount": 100 + ], + [ + "currency": "JPY", + "amount": 1000 + ] + ] + ] + XCTAssertNil(FBSDKSKAdNetworkEvent(json: invalidData)) + invalidData = [ + "event_name": "fb_mobile_purchase", + "values": [ + [ + "currency": 100, + "amount": "usd" + ], + [ + "currency": 1000, + "amount": "jpy" + ] + ] + ] + XCTAssertNil(FBSDKSKAdNetworkEvent(json: invalidData)) + } +} + +#endif diff --git a/FBSDKCoreKit/FBSDKCoreKitTests/Internal/AppEvents/SKAdNetwork/FBSDKSKAdNetworkReporterTests.m b/FBSDKCoreKit/FBSDKCoreKitTests/Internal/AppEvents/SKAdNetwork/FBSDKSKAdNetworkReporterTests.m new file mode 100644 index 0000000000..3082c50d25 --- /dev/null +++ b/FBSDKCoreKit/FBSDKCoreKitTests/Internal/AppEvents/SKAdNetwork/FBSDKSKAdNetworkReporterTests.m @@ -0,0 +1,221 @@ +// 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 + +#if !TARGET_OS_TV + + #import + + #import "FBSDKSKAdNetworkConversionConfiguration.h" + #import "FBSDKSKAdNetworkReporter.h" + #import "FBSDKSettings+Internal.h" + #import "FBSDKTestCase.h" + #import "UserDefaultsSpy.h" + +static NSString *const FBSDKSettingsInstallTimestamp = @"com.facebook.sdk:FBSDKSettingsInstallTimestamp"; +static NSString *const FBSDKSKAdNetworkReporterKey = @"com.facebook.sdk:FBSDKSKAdNetworkReporter"; + +@interface FBSDKSKAdNetworkReporter () + ++ (void)setConfiguration:(FBSDKSKAdNetworkConversionConfiguration *)configuration; ++ (void)_loadReportData; ++ (BOOL)_shouldCutoff; ++ (void)_recordAndUpdateEvent:(NSString *)event + currency:(nullable NSString *)currency + value:(nullable NSNumber *)value; ++ (void)_updateConversionValue:(NSInteger)value; + ++ (void)setSKAdNetworkReportEnabled:(BOOL)enabled; + +@end + +@interface FBSDKSKAdNetworkReporterTests : FBSDKTestCase + +@end + +@implementation FBSDKSKAdNetworkReporterTests +{ + UserDefaultsSpy *userDefaultsSpy; + FBSDKSKAdNetworkConversionConfiguration *defaultConfiguration; +} + +- (void)setUp +{ + [super setUp]; + + userDefaultsSpy = [UserDefaultsSpy new]; + [self stubUserDefaultsWith:userDefaultsSpy]; + + NSDictionary *json = @{ + @"data" : @[@{ + @"timer_buckets" : @(1), + @"timer_interval" : @(1000), + @"cutoff_time" : @(1), + @"default_currency" : @"usd", + @"conversion_value_rules" : @[], + }] + }; + defaultConfiguration = [[FBSDKSKAdNetworkConversionConfiguration alloc] initWithJSON:json]; + + [FBSDKSKAdNetworkReporter _loadReportData]; + [FBSDKSKAdNetworkReporter setSKAdNetworkReportEnabled:YES]; + + [self stubLoadingAdNetworkReporterConfiguration]; +} + +- (void)tearDown +{ + [super tearDown]; +} + +- (void)testShouldCutoffWithoutTimestampWithoutCutoffTime +{ + XCTAssertTrue([FBSDKSKAdNetworkReporter _shouldCutoff], "Should cut off reporting when there is no install timestamp or cutoff time"); +} + +- (void)testShouldCutoffWithoutTimestampWithCutoffTime +{ + [FBSDKSKAdNetworkReporter setConfiguration:defaultConfiguration]; + + XCTAssertFalse([FBSDKSKAdNetworkReporter _shouldCutoff], "Should not cut off reporting when there is no install timestamp"); +} + +- (void)testShouldCutoffWithTimestampWithoutCutoffTime +{ + [userDefaultsSpy setObject:NSDate.distantPast forKey:FBSDKSettingsInstallTimestamp]; + XCTAssertTrue( + [FBSDKSKAdNetworkReporter _shouldCutoff], + "Should cut off reporting when when the timestamp is earlier than the current date and there's no cutoff date provided" + ); + [userDefaultsSpy setObject:NSDate.distantFuture forKey:FBSDKSettingsInstallTimestamp]; + XCTAssertTrue( + [FBSDKSKAdNetworkReporter _shouldCutoff], + "Should cut off reporting when the timestamp is later than the current date and there's no cutoff date provided" + ); +} + +- (void)testShouldCutoffWhenTimestampEarlierThanCutoffTime +{ + [FBSDKSKAdNetworkReporter setConfiguration:defaultConfiguration]; + [userDefaultsSpy setObject:NSDate.distantPast forKey:FBSDKSettingsInstallTimestamp]; + + XCTAssertTrue( + [FBSDKSKAdNetworkReporter _shouldCutoff], + "Should cut off reporting when the install timestamp is one day before the cutoff date" + ); +} + +- (void)testShouldCutoffWhenTimestampLaterThanCutoffTime +{ + [FBSDKSKAdNetworkReporter setConfiguration:defaultConfiguration]; + [userDefaultsSpy setObject:NSDate.distantFuture forKey:FBSDKSettingsInstallTimestamp]; + + XCTAssertFalse( + [FBSDKSKAdNetworkReporter _shouldCutoff], + "Should not cut off reporting when the install timestamp is more than one day later than the cutoff date" + ); +} + +- (void)testShouldCutoff +{ + [FBSDKSKAdNetworkReporter setConfiguration:defaultConfiguration]; + + // Case 1: refresh install + [FBSDKSettings recordInstall]; + XCTAssertFalse([FBSDKSKAdNetworkReporter _shouldCutoff]); + + // Case 2: timestamp is already expired + NSCalendar *calendar = [[NSCalendar alloc] initWithCalendarIdentifier:NSCalendarIdentifierGregorian]; + NSDateComponents *addComponents = [NSDateComponents new]; + addComponents.day = -2; + NSDate *expiredDate = [calendar dateByAddingComponents:addComponents toDate:[NSDate date] options:0]; + [[NSUserDefaults standardUserDefaults] setObject:expiredDate forKey:FBSDKSettingsInstallTimestamp]; + XCTAssertTrue([FBSDKSKAdNetworkReporter _shouldCutoff]); + + [[NSUserDefaults standardUserDefaults] removeObjectForKey:FBSDKSettingsInstallTimestamp]; +} + +- (void)testCutoffWhenTimeBucketIsAvailable +{ + if (@available(iOS 14, *)) { + id mock = OCMClassMock([SKAdNetwork class]); + [FBSDKSKAdNetworkReporter setConfiguration:defaultConfiguration]; + NSDate *today = [NSDate date]; + NSCalendar *calendar = [[NSCalendar alloc] initWithCalendarIdentifier:NSCalendarIdentifierGregorian]; + NSDateComponents *addComponents = [NSDateComponents new]; + addComponents.day = -2; + NSDate *expiredDate = [calendar dateByAddingComponents:addComponents toDate:today options:0]; + [userDefaultsSpy setObject:expiredDate forKey:FBSDKSettingsInstallTimestamp]; + + XCTAssertTrue([FBSDKSKAdNetworkReporter _shouldCutoff]); + [FBSDKSKAdNetworkReporter checkAndRevokeTimer]; + XCTAssertNil([userDefaultsSpy objectForKey:FBSDKSKAdNetworkReporterKey]); + + [mock reject]; + + [userDefaultsSpy removeObjectForKey:FBSDKSettingsInstallTimestamp]; + } +} + +- (void)testRecord +{ + if (@available(iOS 14, *)) { + NSDictionary *json = @{ + @"data" : @[@{ + @"timer_buckets" : @(1), + @"timer_interval" : @(1000), + @"cutoff_time" : @(1), + @"default_currency" : @"USD", + @"conversion_value_rules" : @[ + @{ + @"conversion_value" : @(2), + @"events" : @[ + @{ + @"event_name" : @"fb_test", + } + ], + }], + }] + }; + FBSDKSKAdNetworkConversionConfiguration *config = [[FBSDKSKAdNetworkConversionConfiguration alloc] initWithJSON:json]; + [FBSDKSKAdNetworkReporter setConfiguration:config]; + [FBSDKSKAdNetworkReporter _recordAndUpdateEvent:@"fb_test" currency:nil value:nil]; + [FBSDKSKAdNetworkReporter _recordAndUpdateEvent:@"fb_mobile_purchase" currency:@"USD" value:@(100)]; + [FBSDKSKAdNetworkReporter _recordAndUpdateEvent:@"fb_mobile_purchase" currency:@"USD" value:@(201)]; + [FBSDKSKAdNetworkReporter _recordAndUpdateEvent:@"test" currency:nil value:nil]; + NSData *cache = [[NSUserDefaults standardUserDefaults] objectForKey:FBSDKSKAdNetworkReporterKey]; + XCTAssertNotNil(cache); + NSDictionary *data = [FBSDKTypeUtility dictionaryValue:[NSKeyedUnarchiver unarchiveObjectWithData:cache]]; + NSMutableSet *recordedEvents = [FBSDKTypeUtility dictionary:data objectForKey:@"recorded_events" ofType:NSMutableSet.class]; + NSSet *expectedEvents = [NSSet setWithArray:@[@"fb_test", @"fb_mobile_purchase"]]; + XCTAssertTrue([expectedEvents isEqualToSet:recordedEvents]); + NSMutableDictionary *recordedValues = [FBSDKTypeUtility dictionary:data objectForKey:@"recorded_values" ofType:NSMutableDictionary.class]; + NSDictionary *expectedValues = @{ + @"fb_mobile_purchase" : @{ + @"USD" : @(301) + } + }; + XCTAssertTrue([expectedValues isEqualToDictionary:recordedValues]); + } +} + +@end + +#endif diff --git a/FBSDKCoreKit/FBSDKCoreKitTests/Internal/AppEvents/SKAdNetwork/FBSDKSKAdNetworkRuleTests.swift b/FBSDKCoreKit/FBSDKCoreKitTests/Internal/AppEvents/SKAdNetwork/FBSDKSKAdNetworkRuleTests.swift new file mode 100644 index 0000000000..a22125fd23 --- /dev/null +++ b/FBSDKCoreKit/FBSDKCoreKitTests/Internal/AppEvents/SKAdNetwork/FBSDKSKAdNetworkRuleTests.swift @@ -0,0 +1,151 @@ +// 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. + +#if !os(tvOS) + +class FBSDKSKAdNetworkRuleTests: FBSDKTestCase { + func testValidCase1() { + let validData: [String: Any] = [ + "conversion_value": 2, + "events": [ + [ + "event_name": "fb_mobile_purchase", + ], + [ + "event_name": "Donate", + ], + ], + ] + + guard let rule = FBSDKSKAdNetworkRule(json: validData) else { return XCTFail("Unwraping Error") } + XCTAssertEqual(2, rule.conversionValue) + XCTAssertEqual(2, rule.events.count) + + let event1 = rule.events[0] + XCTAssertEqual(event1.eventName, "fb_mobile_purchase") + XCTAssertNil(event1.values) + + let event2 = rule.events[1] + XCTAssertEqual(event2.eventName, "Donate") + XCTAssertNil(event2.values) + } + + func testValidCase2() { + let validData: [String: Any] = [ + "conversion_value": 2, + "events": [ + [ + "event_name": "fb_mobile_purchase", + "values": [ + [ + "currency": "USD", + "amount": 100, + ], + ], + ], + ], + ] + + guard let rule = FBSDKSKAdNetworkRule(json: validData) else { return XCTFail("Unwraping Error") } + XCTAssertEqual(2, rule.conversionValue) + XCTAssertEqual(1, rule.events.count) + XCTAssertEqual(1, rule.events.count) + + let event = rule.events[0] + XCTAssertEqual(event.eventName, "fb_mobile_purchase") + XCTAssertEqual(event.values, ["USD": 100]) + } + + func testInvalidCases() { + var invalidData: [String: Any] = [:] + XCTAssertNil(FBSDKSKAdNetworkRule(json: invalidData)) + + invalidData = ["conversion_value": 2] + XCTAssertNil(FBSDKSKAdNetworkRule(json: invalidData)) + + invalidData = [ + "events": [ + [ + "event_name": "fb_mobile_purchase", + "values": [ + [ + "currency": "USD", + "amount": 100, + ], + ], + ], + ], + ] + XCTAssertNil(FBSDKSKAdNetworkRule(json: invalidData)) + + invalidData = [ + "conversion_value": 2, + "events": [ + [ + "event_name": "fb_mobile_purchase", + "values": [ + [ + "currency": 100, + "amount": "USD", + ], + ], + ], + ], + ] + XCTAssertNil(FBSDKSKAdNetworkRule(json: invalidData)) + } + + func testRuleMatch() { + let ruleData: [String: Any] = [ + "conversion_value": 2, + "events": [ + [ + "event_name": "fb_skadnetwork_test1", + ], + [ + "event_name": "fb_mobile_purchase", + "values": [ + [ + "currency": "USD", + "amount": 100, + ], + ], + ], + ], + ] + + guard let rule = FBSDKSKAdNetworkRule(json: ruleData) else { return XCTFail("Unwraping Error") } + let matchedEventSet: Set = ["fb_mobile_purchase", "fb_skadnetwork_test1", "fb_adnetwork_test2"] + let unmatchedEventSet: Set = ["fb_mobile_purchase", "fb_skadnetwork_test2"] + + XCTAssertTrue(rule.isMatched(withRecordedEvents: matchedEventSet, + recordedValues: ["fb_mobile_purchase": ["USD": 1000]])) + XCTAssertFalse(rule.isMatched(withRecordedEvents: [], + recordedValues: [:])) + XCTAssertFalse(rule.isMatched(withRecordedEvents: matchedEventSet, + recordedValues: [:])) + XCTAssertFalse(rule.isMatched(withRecordedEvents: matchedEventSet, + recordedValues: ["fb_mobile_purchase": ["USD": 50]])) + XCTAssertFalse(rule.isMatched(withRecordedEvents: matchedEventSet, + recordedValues: ["fb_mobile_purchase": ["JPY": 1000]])) + XCTAssertFalse(rule.isMatched(withRecordedEvents: unmatchedEventSet, + recordedValues: ["fb_mobile_purchase": ["USD": 1000]])) + } +} + +#endif diff --git a/FBSDKCoreKit/FBSDKCoreKitTests/Internal/AppEvents/SuggestedEvent/FBSDKFeatureExtractorTests.m b/FBSDKCoreKit/FBSDKCoreKitTests/Internal/AppEvents/SuggestedEvent/FBSDKFeatureExtractorTests.m index 8d9e79dd20..02e7056ce5 100644 --- a/FBSDKCoreKit/FBSDKCoreKitTests/Internal/AppEvents/SuggestedEvent/FBSDKFeatureExtractorTests.m +++ b/FBSDKCoreKit/FBSDKCoreKitTests/Internal/AppEvents/SuggestedEvent/FBSDKFeatureExtractorTests.m @@ -16,15 +16,14 @@ // 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 "FBSDKCoreKitTests-Swift.h" #import "FBSDKFeatureExtractor.h" +#import "FBSDKInternalUtility.h" #import "FBSDKModelManager.h" #import "FBSDKViewHierarchyMacros.h" -#import "FBSDKCoreKitTests-Swift.h" -#import "FBSDKTypeUtility.h" @interface FBSDKFeatureExtractor () + (BOOL)pruneTree:(NSMutableDictionary *)node @@ -51,7 +50,8 @@ + (float)regextMatch:(NSString *)pattern @end -@interface FBSDKFeatureExtractorTests : XCTestCase { +@interface FBSDKFeatureExtractorTests : XCTestCase +{ NSDictionary *_rules; NSDictionary *_viewHierarchy; NSDictionary *_interactedNode; @@ -75,171 +75,171 @@ - (void)setUp [FBSDKFeatureExtractor loadRulesForKey:@"MTML"]; _viewHierarchy = @{ - @"screenname": @"UITabBarController", - @"view": @[ - @{ - @"classname": @"UIWindow", - @"classtypebitmask": @"0", - @"childviews": @[ + @"screenname" : @"UITabBarController", + @"view" : @[ + @{ + @"classname" : @"UIWindow", + @"classtypebitmask" : @"0", + @"childviews" : @[ + @{ + @"classname" : @"UITabBarController", + @"classtypebitmask" : @"131072", + @"childviews" : @[ @{ - @"classname": @"UITabBarController", - @"classtypebitmask": @"131072", - @"childviews": @[ - @{ - @"classname": @"UINavigationController", - @"classtypebitmask": @"131072", - @"childviews": @[ + @"classname" : @"UINavigationController", + @"classtypebitmask" : @"131072", + @"childviews" : @[ + @{ + @"classname" : @"CheckoutViewController", + @"classtypebitmask" : @"131072", + @"childviews" : @[ + @{ + @"classname" : @"UIStackView", + @"classtypebitmask" : @"0", + @"childviews" : @[ @{ - @"classname": @"CheckoutViewController", - @"classtypebitmask": @"131072", - @"childviews": @[ - @{ - @"classname": @"UIStackView", - @"classtypebitmask": @"0", - @"childviews": @[ - @{ - @"classname": @"UILabel", - @"classtypebitmask": @"1024", - @"text": @"Order Summary", - }, - @{ - @"classname": @"UIStackView", - @"classtypebitmask": @"0", - @"childviews": @[ - @{ - @"classname": @"UIView", - @"classtypebitmask": @"0", - }, - @{ - @"classname": @"UILabel", - @"classtypebitmask": @"1024", - @"text": @"Coffee 5", - }, - @{ - @"classname": @"UILabel", - @"classtypebitmask": @"Price: $5.99", - }, - ] - }, - @{ - @"classname": @"UIStackView", - @"classtypebitmask": @"0", - @"childviews": @[ - @{ - @"classname": @"UIView", - @"classtypebitmask": @"0", - }, - @{ - @"classname": @"UILabel", - @"classtypebitmask": @"1024", - @"text": @"Quantity", - }, - @{ - @"classname": @"UILabel", - @"classtypebitmask": @"1", - }, - ] - }, - @{ - @"classname": @"UITextField", - @"classtypebitmask": @"2056", - @"hint": @"Credit Card Credit Card", - }, - @{ - @"classname": @"UITextField", - @"classtypebitmask": @"2056", - @"hint": @"Shipping Address Shipping Address", - }, - @{ - @"classname": @"UIButton", - @"classtypebitmask": @"24", - @"is_interacted": @1, - @"hint": @"Confirm Order", - }, - ] - } + @"classname" : @"UILabel", + @"classtypebitmask" : @"1024", + @"text" : @"Order Summary", + }, + @{ + @"classname" : @"UIStackView", + @"classtypebitmask" : @"0", + @"childviews" : @[ + @{ + @"classname" : @"UIView", + @"classtypebitmask" : @"0", + }, + @{ + @"classname" : @"UILabel", + @"classtypebitmask" : @"1024", + @"text" : @"Coffee 5", + }, + @{ + @"classname" : @"UILabel", + @"classtypebitmask" : @"Price: $5.99", + }, + ] + }, + @{ + @"classname" : @"UIStackView", + @"classtypebitmask" : @"0", + @"childviews" : @[ + @{ + @"classname" : @"UIView", + @"classtypebitmask" : @"0", + }, + @{ + @"classname" : @"UILabel", + @"classtypebitmask" : @"1024", + @"text" : @"Quantity", + }, + @{ + @"classname" : @"UILabel", + @"classtypebitmask" : @"1", + }, ] - } - ] - }, - @{ - @"classname": @"UITabBar", - @"classtypebitmask": @"0", - } + }, + @{ + @"classname" : @"UITextField", + @"classtypebitmask" : @"2056", + @"hint" : @"Credit Card Credit Card", + }, + @{ + @"classname" : @"UITextField", + @"classtypebitmask" : @"2056", + @"hint" : @"Shipping Address Shipping Address", + }, + @{ + @"classname" : @"UIButton", + @"classtypebitmask" : @"24", + @"is_interacted" : @1, + @"hint" : @"Confirm Order", + }, + ] + } + ] + } ] + }, + @{ + @"classname" : @"UITabBar", + @"classtypebitmask" : @"0", } - ] - } + ] + } + ] + } ] }; _interactedNode = @{ - @"classname": @"UIButton", - @"classtypebitmask": @"24", - @"is_interacted": @1, - @"hint": @"Confirm Order", + @"classname" : @"UIButton", + @"classtypebitmask" : @"24", + @"is_interacted" : @1, + @"hint" : @"Confirm Order", }; _siblings = @[ - @{ - @"classname": @"UILabel", - @"classtypebitmask": @"1024", - @"text": @"Order Summary", - }, - @{ - @"classname": @"UIStackView", - @"classtypebitmask": @"0", - @"childviews": @[ - @{ - @"classname": @"UIView", - @"classtypebitmask": @"0", - }, - @{ - @"classname": @"UILabel", - @"classtypebitmask": @"1024", - @"text": @"Coffee 5", - }, - @{ - @"classname": @"UILabel", - @"classtypebitmask": @"Price: $5.99", - }, - ] - }, - @{ - @"classname": @"UIStackView", - @"classtypebitmask": @"0", - @"childviews": @[ - @{ - @"classname": @"UIView", - @"classtypebitmask": @"0", - }, - @{ - @"classname": @"UILabel", - @"classtypebitmask": @"1024", - @"text": @"Quantity", - }, - @{ - @"classname": @"UILabel", - @"classtypebitmask": @"1", - }, - ] - }, - @{ - @"classname": @"UITextField", - @"classtypebitmask": @"2056", - @"hint": @"Credit Card Credit Card", - }, - @{ - @"classname": @"UITextField", - @"classtypebitmask": @"2056", - @"hint": @"Shipping Address Shipping Address", - }, - @{ - @"classname": @"UIButton", - @"classtypebitmask": @"24", - @"is_interacted": @1, - @"hint": @"Confirm Order", - }, + @{ + @"classname" : @"UILabel", + @"classtypebitmask" : @"1024", + @"text" : @"Order Summary", + }, + @{ + @"classname" : @"UIStackView", + @"classtypebitmask" : @"0", + @"childviews" : @[ + @{ + @"classname" : @"UIView", + @"classtypebitmask" : @"0", + }, + @{ + @"classname" : @"UILabel", + @"classtypebitmask" : @"1024", + @"text" : @"Coffee 5", + }, + @{ + @"classname" : @"UILabel", + @"classtypebitmask" : @"Price: $5.99", + }, + ] + }, + @{ + @"classname" : @"UIStackView", + @"classtypebitmask" : @"0", + @"childviews" : @[ + @{ + @"classname" : @"UIView", + @"classtypebitmask" : @"0", + }, + @{ + @"classname" : @"UILabel", + @"classtypebitmask" : @"1024", + @"text" : @"Quantity", + }, + @{ + @"classname" : @"UILabel", + @"classtypebitmask" : @"1", + }, + ] + }, + @{ + @"classname" : @"UITextField", + @"classtypebitmask" : @"2056", + @"hint" : @"Credit Card Credit Card", + }, + @{ + @"classname" : @"UITextField", + @"classtypebitmask" : @"2056", + @"hint" : @"Shipping Address Shipping Address", + }, + @{ + @"classname" : @"UIButton", + @"classtypebitmask" : @"24", + @"is_interacted" : @1, + @"hint" : @"Confirm Order", + }, ]; } @@ -253,8 +253,8 @@ - (void)testGetDenseFeature // Get dense feature string NSMutableArray *denseFeatureArray = [NSMutableArray array]; - for (int i=0; i < 30; i++) { - [denseFeatureArray addObject:[NSNumber numberWithFloat: denseFeature[i]]]; + for (int i = 0; i < 30; i++) { + [denseFeatureArray addObject:[NSNumber numberWithFloat:denseFeature[i]]]; } XCTAssertEqualObjects([denseFeatureArray componentsJoinedByString:@","], @"0,0,0,5,0,0,0,0,0,0,0,0,0,-1,-1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0"); @@ -262,7 +262,7 @@ - (void)testGetDenseFeature - (void)testGetDenseFeatureParsing { - for (int i = 0; i < 1000; i++) { + for (int i = 0; i < 100; i++) { NSDictionary *viewHierarchy = [_viewHierarchy copy]; [FBSDKFeatureExtractor getDenseFeatures:[Fuzzer randomizeWithJson:viewHierarchy]]; } @@ -289,8 +289,8 @@ - (void)testNonparseFeature } NSString *viewTreeString = [[NSString alloc] initWithData:[FBSDKTypeUtility dataWithJSONObject:[_viewHierarchy[VIEW_HIERARCHY_VIEW_KEY] mutableCopy] - options:0 - error:nil] + options:0 + error:nil] encoding:NSUTF8StringEncoding]; float *nonParseFeature = [FBSDKFeatureExtractor nonparseFeatures:[_interactedNode mutableCopy] siblings:[_siblings mutableCopy] @@ -299,8 +299,8 @@ - (void)testNonparseFeature // Get non-parsed feature string NSMutableArray *nonParseFeatureArray = [NSMutableArray array]; - for (int i=0; i < 30; i++) { - [nonParseFeatureArray addObject:[NSNumber numberWithFloat: nonParseFeature[i]]]; + for (int i = 0; i < 30; i++) { + [nonParseFeatureArray addObject:[NSNumber numberWithFloat:nonParseFeature[i]]]; } XCTAssertEqualObjects([nonParseFeatureArray componentsJoinedByString:@","], @"0,0,0,5,0,0,0,0,0,0,0,0,0,-1,-1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0"); @@ -316,8 +316,8 @@ - (void)testParseFeature // Get parsed feature string NSMutableArray *parseFeatureArray = [NSMutableArray array]; - for (int i=0; i < 30; i++) { - [parseFeatureArray addObject:[NSNumber numberWithFloat: parseFeature[i]]]; + for (int i = 0; i < 30; i++) { + [parseFeatureArray addObject:[NSNumber numberWithFloat:parseFeature[i]]]; } XCTAssertEqualObjects([parseFeatureArray componentsJoinedByString:@","], @"0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0"); @@ -326,9 +326,9 @@ - (void)testParseFeature - (void)testIsButton { NSDictionary *labelNode = @{ - @"classname": @"UILabel", - @"classtypebitmask": @"1024", - @"text": @"Coffee 5", + @"classname" : @"UILabel", + @"classtypebitmask" : @"1024", + @"text" : @"Coffee 5", }; XCTAssertEqual([FBSDKFeatureExtractor isButton:_interactedNode], true); XCTAssertEqual([FBSDKFeatureExtractor isButton:labelNode], false); @@ -363,7 +363,7 @@ - (void)testRegextMatch - (void)testLoadRulesForKey { - for (int i = 0; i < 1000; i++) { + for (int i = 0; i < 100; i++) { [FBSDKFeatureExtractor loadRulesForKey:Fuzzer.random]; } } diff --git a/FBSDKCoreKit/FBSDKCoreKitTests/Internal/AppEvents/ViewHierarchy/FBSDKViewHierarchyTests.m b/FBSDKCoreKit/FBSDKCoreKitTests/Internal/AppEvents/ViewHierarchy/FBSDKViewHierarchyTests.m index a0406f4baa..c77fc635fa 100644 --- a/FBSDKCoreKit/FBSDKCoreKitTests/Internal/AppEvents/ViewHierarchy/FBSDKViewHierarchyTests.m +++ b/FBSDKCoreKit/FBSDKCoreKitTests/Internal/AppEvents/ViewHierarchy/FBSDKViewHierarchyTests.m @@ -16,15 +16,15 @@ // 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 "FBSDKViewHierarchy.h" -@interface FBSDKViewHierarchyTests : XCTestCase { +@interface FBSDKViewHierarchyTests : XCTestCase +{ UIScrollView *scrollview; UILabel *label; UITextField *textField; @@ -33,6 +33,23 @@ @interface FBSDKViewHierarchyTests : XCTestCase { } @end +id getVariableFromInstance(NSObject *instance, NSString *variableName); + +@interface TestObj : NSObject @end +@implementation TestObj +{ + NSString *_a; +} +- (instancetype)init +{ + if (self = [super init]) { + _a = @"BLAH"; + } + return self; +} + +@end + @implementation FBSDKViewHierarchyTests - (void)setUp @@ -40,20 +57,20 @@ - (void)setUp scrollview = [[UIScrollView alloc] init]; label = [[UILabel alloc] init]; - label.text = @"I am a label"; + label.text = NSLocalizedString(@"I am a label", nil); [scrollview addSubview:label]; textField = [[UITextField alloc] init]; - textField.text = @"I am a text field"; - textField.placeholder = @"text field placeholder"; + textField.text = NSLocalizedString(@"I am a text field", nil); + textField.placeholder = NSLocalizedString(@"text field placeholder", nil); [scrollview addSubview:textField]; textView = [[UITextView alloc] init]; - textView.text = @"I am a text view"; + textView.text = NSLocalizedString(@"I am a text view", nil); [scrollview addSubview:textView]; btn = [[UIButton alloc] initWithFrame:CGRectMake(0, 0, 5, 20)]; - [btn setTitle:@"I am a button" forState:UIControlStateNormal]; + [btn setTitle:NSLocalizedString(@"I am a button", nil) forState:UIControlStateNormal]; [scrollview addSubview:btn]; } @@ -101,4 +118,17 @@ - (void)testGetHint XCTAssertEqualObjects([FBSDKViewHierarchy getHint:NC], @"UIViewController"); } +- (void)testGetInstanceVariable +{ + // empty args should cause no-op. + XCTAssertEqualObjects(getVariableFromInstance(nil, @"anything prop"), NSNull.null); + XCTAssertEqualObjects(getVariableFromInstance([NSObject new], nil), NSNull.null); + + // If there is no ivar, should return nil. + XCTAssertEqualObjects(getVariableFromInstance([NSObject new], @"some_made_up_property_123456"), NSNull.null); + + // this should work. + XCTAssertEqualObjects(getVariableFromInstance([TestObj new], @"_a"), @"BLAH"); +} + @end diff --git a/FBSDKCoreKit/FBSDKCoreKitTests/Internal/AppLinks/FBSDKAppLinkResolverRequestBuilderTests.m b/FBSDKCoreKit/FBSDKCoreKitTests/Internal/AppLinks/FBSDKAppLinkResolverRequestBuilderTests.m new file mode 100644 index 0000000000..b56b46234a --- /dev/null +++ b/FBSDKCoreKit/FBSDKCoreKitTests/Internal/AppLinks/FBSDKAppLinkResolverRequestBuilderTests.m @@ -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 + +#import "FBSDKAppLinkResolverRequestBuilder.h" +#import "FBSDKTestCase.h" + +@interface FBSDKAppLinkResolverRequestBuilder (FBSDKAppLinkResolverTests) + +- (instancetype)initWithUserInterfaceIdiom:(UIUserInterfaceIdiom)userInterfaceIdiom; + +@end + +@interface FBSDKAppLinkResolverRequestBuilderTests : FBSDKTestCase +@end + +@implementation FBSDKAppLinkResolverRequestBuilderTests +#pragma mark - test cases + +- (void)testAsksForPhoneDataOnPhone +{ + FBSDKAppLinkResolverRequestBuilder *builder = [[FBSDKAppLinkResolverRequestBuilder alloc] initWithUserInterfaceIdiom:UIUserInterfaceIdiomPhone]; + + FBSDKGraphRequest *request = [builder requestForURLs:@[]]; + BOOL askedForPhone = [request.parameters[@"fields"] rangeOfString:@"iphone"].location != NSNotFound; + + XCTAssertTrue(askedForPhone); +} + +- (void)testAsksForPadDataOnPad +{ + FBSDKAppLinkResolverRequestBuilder *builder = [[FBSDKAppLinkResolverRequestBuilder alloc] initWithUserInterfaceIdiom:UIUserInterfaceIdiomPad]; + + FBSDKGraphRequest *request = [builder requestForURLs:@[]]; + BOOL askedForPad = [request.parameters[@"fields"] rangeOfString:@"iphone"].location != NSNotFound; + + XCTAssertTrue(askedForPad); +} + +@end diff --git a/FBSDKCoreKit/FBSDKCoreKitTests/Internal/AppLinks/FBSDKAppLinkResolverTests.m b/FBSDKCoreKit/FBSDKCoreKitTests/Internal/AppLinks/FBSDKAppLinkResolverTests.m index a4f4df0c8b..74b1d181ce 100644 --- a/FBSDKCoreKit/FBSDKCoreKitTests/Internal/AppLinks/FBSDKAppLinkResolverTests.m +++ b/FBSDKCoreKit/FBSDKCoreKitTests/Internal/AppLinks/FBSDKAppLinkResolverTests.m @@ -16,24 +16,19 @@ // 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 #import "FBSDKAppLinkResolver.h" -#import "FBSDKCoreKitTestUtility.h" -#import "FBSDKInternalUtility.h" -#import "FBSDKSettings.h" +#import "FBSDKTestCase.h" static NSString *const kAppLinkURLString = @"http://example.com/1234567890"; static NSString *const kAppLinkURL2String = @"http://example.com/0987654321"; static NSString *const kAppLinksKey = @"app_links"; - -typedef void (^HTTPStubCallback)(NSURLRequest *request); -typedef _Nullable id (^StringURLBlock)(NSString *urlString); +static NSString *const kIphoneKey = @"iphone"; +static NSString *const kIpadKey = @"ipad"; @interface NSURL (FBSDKAppLinkResolverTests) @@ -44,147 +39,87 @@ @interface NSURL (FBSDKAppLinkResolverTests) @interface FBSDKAppLinkResolver (FBSDKAppLinkResolverTests) - (instancetype)initWithUserInterfaceIdiom:(UIUserInterfaceIdiom)userInterfaceIdiom; +- (instancetype)initWithRequestBuilder:(FBSDKAppLinkResolverRequestBuilder *)builder; +- (instancetype)initWithUserInterfaceIdiom:(UIUserInterfaceIdiom)userInterfaceIdiom andRequestBuilder:(FBSDKAppLinkResolverRequestBuilder *)builder; @end -@interface FBSDKAppLinkResolverTests : XCTestCase +@interface FBSDKAppLinkResolverTests : FBSDKTestCase @end @implementation FBSDKAppLinkResolverTests -{ - id _mockNSBundle; -} -#pragma mark - HTTP stubbing helpers +#pragma mark - Mock requests -- (void)stubAllResponsesWithResult:(id)result +- (void)mockAppLinkRequestWithResult:(id)result { - [self stubAllResponsesWithResult:result statusCode:200]; + [self mockAppLinkRequestWithResult:result error:nil idiomSpecificField:kIphoneKey]; } -- (void)stubAllResponsesWithResult:(id)result - statusCode:(int)statusCode +- (void)mockAppLinkRequestWithError { - [self stubAllResponsesWithResult:result statusCode:statusCode callback:nil]; + [self mockAppLinkRequestWithResult:@{@"error" : @{}} + error:[[NSError alloc] + initWithDomain:FBSDKErrorDomain + code:-1 + userInfo:nil] + idiomSpecificField:kIphoneKey]; } -- (void)stubAllResponsesWithResult:(id)result - statusCode:(int)statusCode - callback:(HTTPStubCallback)callback +- (void)mockAppLinkRequestWithResult:(id)result idiomSpecificField:(NSString *)field { - return [self stubMatchingRequestsWithResponses:@{@"" : result} - statusCode:statusCode - callback:callback]; + [self mockAppLinkRequestWithResult:result error:nil idiomSpecificField:field]; } -- (void)stubMatchingRequestsWithResponses:(NSDictionary *)requestsAndResponses - statusCode:(int)statusCode - callback:(HTTPStubCallback)callback +- (void)mockAppLinkRequestWithResult:(id)result error:(NSError *)error idiomSpecificField:(NSString *)field { - StringURLBlock matchingKey = ^id (NSString *urlString) { - for (NSString *substring in requestsAndResponses.allKeys) { - // The first @"" always matches - if (substring.length == 0 || - [urlString rangeOfString:substring].location != NSNotFound) { - return substring; - } - } - return nil; - }; - - [OHHTTPStubs stubRequestsPassingTest:^BOOL(NSURLRequest *request) { - if (callback) { - callback(request); - } + [self mockAppLinkRequestWithResult:result error:error idiomSpecificField:field andDo:nil]; +} - return matchingKey(request.URL.absoluteString) != nil; - } withStubResponse:^OHHTTPStubsResponse *(NSURLRequest *request) { - id result = requestsAndResponses[matchingKey(request.URL.absoluteString)]; - NSData *data = [[FBSDKBasicUtility JSONStringForObject:result - error:NULL - invalidObjectHandler:NULL] dataUsingEncoding:NSUTF8StringEncoding]; +- (void)mockAppLinkRequestWithResult:(id)result error:(NSError *)error idiomSpecificField:(NSString *)field andDo:(void (^_Nullable)(NSInvocation *))block +{ + [self stubGraphRequestWithResult:result error:error connection:nil]; + [self stubAppLinkResolverRequestBuilderWithIdiomSpecificField:field]; - return [OHHTTPStubsResponse responseWithData:data - statusCode:statusCode - headers:nil]; - }]; + OCMStub([self.appLinkResolverRequestBuilderMock requestForURLs:[OCMArg any]]).andReturn(self.graphRequestMock).andDo(block); } #pragma mark - test cases -- (void)setUp +- (void)testUsesPhoneDataOnPhone { - _mockNSBundle = [FBSDKCoreKitTestUtility mainBundleMock]; -} + XCTestExpectation *expectation = [self expectationWithDescription:@"usesPhoneDataOnPhone"]; + [self stubClientTokenWith:@"clienttoken"]; + + id result = @{ + kAppLinkURLString : @{ + kAppLinksKey : @{ + kIphoneKey : @[ + @{ + @"app_name" : @"Example", + @"app_store_id" : @"456", + @"url" : @"example://things/1234567890" + } + ], + @"ios" : @[ + @{ + @"app_name" : @"Example", + @"app_store_id" : @"123", + @"url" : @"example://things/1234567890" + } + ], + }, + @"id" : kAppLinkURLString + } + }; -/* -- (void)testAsksForPhoneDataOnPhone -{ - XCTestExpectation *expectation = [self expectationWithDescription:@"asksForPhoneDataOnPhone"]; - [FBSDKAccessToken setCurrentAccessToken:nil]; - [FBSDKSettings setClientToken:@"clienttoken"]; - - __block BOOL askedForPhone = NO; - [OHHTTPStubs stubRequestsPassingTest:^BOOL(NSURLRequest *request) { - NSDictionary *queryParameters = - [FBSDKInternalUtility dictionaryFromFBURL:request.URL]; - askedForPhone = [queryParameters[@"fields"] rangeOfString:@"iphone"].location != NSNotFound; - return YES; - } withStubResponse:^OHHTTPStubsResponse *(NSURLRequest *request) { - return [OHHTTPStubsResponse responseWithData:[NSData data] - statusCode:200 - headers:nil]; - }]; + [self mockAppLinkRequestWithResult:result idiomSpecificField:kIphoneKey]; - FBSDKAppLinkResolver *resolver = [[FBSDKAppLinkResolver alloc] initWithUserInterfaceIdiom:UIUserInterfaceIdiomPhone]; - [resolver - appLinkFromURL:[NSURL URLWithString:kAppLinkURLString] - handler:^(FBSDKAppLink * _Nullable appLink, NSError * _Nullable error) { - XCTAssertTrue(askedForPhone); - [expectation fulfill]; - }]; + FBSDKAppLinkResolver *resolver = [[FBSDKAppLinkResolver alloc] initWithUserInterfaceIdiom:UIUserInterfaceIdiomPhone andRequestBuilder:self.appLinkResolverRequestBuilderMock]; - [self waitForExpectationsWithTimeout:2 handler:^(NSError * _Nullable error) { - XCTAssertNil(error); - }]; - [FBSDKSettings setClientToken:nil]; -} -*/ - -/* -- (void)testUsesPhoneDataOnPhone -{ - XCTestExpectation *expectation = [self expectationWithDescription:@"usesPhoneDataOnPhone"]; - [FBSDKAccessToken setCurrentAccessToken:nil]; - [FBSDKSettings setClientToken:@"clienttoken"]; - - [self stubAllResponsesWithResult:@{ - kAppLinkURLString : @{ - kAppLinksKey: @{ - @"iphone": @[ - @{ - @"app_name": @"Example", - @"app_store_id": @"456", - @"url": @"example://things/1234567890" - } - ], - @"ios": @[ - @{ - @"app_name": @"Example", - @"app_store_id": @"123", - @"url": @"example://things/1234567890" - } - ], - }, - @"id": kAppLinkURLString - } - }]; - - FBSDKAppLinkResolver *resolver = [[FBSDKAppLinkResolver alloc] initWithUserInterfaceIdiom:UIUserInterfaceIdiomPhone]; [resolver appLinkFromURL:[NSURL URLWithString:kAppLinkURLString] - handler:^(FBSDKAppLink * _Nullable link, NSError * _Nullable error) { - + handler:^(FBSDKAppLink *_Nullable link, NSError *_Nullable error) { XCTAssertNotNil(link); XCTAssertEqual(link.sourceURL.absoluteString, kAppLinkURLString); XCTAssertEqualObjects([link.targets[0] appStoreId], @"456"); @@ -193,82 +128,45 @@ - (void)testUsesPhoneDataOnPhone [expectation fulfill]; }]; - [self waitForExpectationsWithTimeout:2 handler:^(NSError * _Nullable error) { + [self waitForExpectationsWithTimeout:2 handler:^(NSError *_Nullable error) { XCTAssertNil(error); }]; - [FBSDKSettings setClientToken:nil]; } -*/ -/* -- (void)testAsksForPadDataOnPad +- (void)testUsesPadDataOnPad { - XCTestExpectation *expectation = [self expectationWithDescription:@"asksForPadDataOnPad"]; - [FBSDKAccessToken setCurrentAccessToken:nil]; - [FBSDKSettings setClientToken:@"clienttoken"]; - - __block BOOL askedForPad = NO; - [OHHTTPStubs stubRequestsPassingTest:^BOOL(NSURLRequest *request) { - NSDictionary *queryParameters = - [FBSDKInternalUtility dictionaryFromFBURL:request.URL]; - // do an "OR" because we only need to verify we asked for it once (in cases where unrelated network requests - // were resetting the flag incorrectly back to NO). - askedForPad |= [queryParameters[@"fields"] rangeOfString:@"ipad"].location != NSNotFound; - return YES; - } withStubResponse:^OHHTTPStubsResponse *(NSURLRequest *request) { - return [OHHTTPStubsResponse responseWithData:[NSData data] - statusCode:200 - headers:nil]; - }]; + XCTestExpectation *expectation = [self expectationWithDescription:@"usesPadDataOnPad"]; + [self stubClientTokenWith:@"clienttoken"]; + + id result = @{ + kAppLinkURLString : @{ + kAppLinksKey : @{ + @"ipad" : @[ + @{ + @"app_name" : @"Example", + @"app_store_id" : @"456", + @"url" : @"example://things/1234567890" + } + ], + @"ios" : @[ + @{ + @"app_name" : @"Example", + @"app_store_id" : @"123", + @"url" : @"example://things/1234567890" + } + ], + }, + @"id" : kAppLinkURLString + } + }; - FBSDKAppLinkResolver *resolver = [[FBSDKAppLinkResolver alloc] initWithUserInterfaceIdiom:UIUserInterfaceIdiomPad]; - [resolver - appLinkFromURL:[NSURL URLWithString:kAppLinkURLString] - handler:^(FBSDKAppLink * _Nullable link, NSError * _Nullable error) { - XCTAssertTrue(askedForPad); - [expectation fulfill]; - }]; + [self mockAppLinkRequestWithResult:result idiomSpecificField:kIpadKey]; - [self waitForExpectationsWithTimeout:2 handler:^(NSError * _Nullable error) { - XCTAssertNil(error); - }]; - [FBSDKSettings setClientToken:nil]; -} -*/ + FBSDKAppLinkResolver *resolver = [[FBSDKAppLinkResolver alloc] initWithUserInterfaceIdiom:UIUserInterfaceIdiomPad andRequestBuilder:self.appLinkResolverRequestBuilderMock]; -/* -- (void)testUsesPadDataOnPad -{ - XCTestExpectation *expectation = [self expectationWithDescription:@"usesPadDataOnPad"]; - [FBSDKAccessToken setCurrentAccessToken:nil]; - [FBSDKSettings setClientToken:@"clienttoken"]; - - [self stubAllResponsesWithResult:@{ - kAppLinkURLString : @{ - kAppLinksKey: @{ - @"ipad": @[ - @{ - @"app_name": @"Example", - @"app_store_id": @"456", - @"url": @"example://things/1234567890" - } - ], - @"ios": @[ - @{ - @"app_name": @"Example", - @"app_store_id": @"123", - @"url": @"example://things/1234567890" - } - ], - }, - @"id": kAppLinkURLString - } - }]; - - FBSDKAppLinkResolver *resolver = [[FBSDKAppLinkResolver alloc] initWithUserInterfaceIdiom:UIUserInterfaceIdiomPad]; [resolver appLinkFromURL:[NSURL URLWithString:kAppLinkURLString] - handler:^(FBSDKAppLink * _Nullable link, NSError * _Nullable error) { + handler:^(FBSDKAppLink *_Nullable link, NSError *_Nullable error) { XCTAssertNotNil(link); XCTAssertEqual(link.sourceURL.absoluteString, kAppLinkURLString); XCTAssertEqualObjects([link.targets[0] appStoreId], @"456"); @@ -277,76 +175,73 @@ - (void)testUsesPadDataOnPad [expectation fulfill]; }]; - [self waitForExpectationsWithTimeout:2 handler:^(NSError * _Nullable error) { + [self waitForExpectationsWithTimeout:2 handler:^(NSError *_Nullable error) { XCTAssertNil(error); }]; - [FBSDKSettings setClientToken:nil]; } - - (void)testIgnoresAndroidData { XCTestExpectation *expectation = [self expectationWithDescription:@"ignoresAndroidData"]; - [FBSDKAccessToken setCurrentAccessToken:nil]; - [FBSDKSettings setClientToken:@"clienttoken"]; + [self stubClientTokenWith:@"clienttoken"]; // We are not asking for it, but just make sure we ignore any non-iOS-platform data we get, to be safe. - [self stubAllResponsesWithResult:@{ - kAppLinkURLString : @{ - kAppLinksKey: @{ - @"android": @[ - @{ - @"app_name": @"Example", - @"package": @"com.example.app", - @"url": @"example://things/1234567890" - } - ], - @"ios": @[ - @{ - @"app_name": @"Example", - @"app_store_id": @"123", - @"url": @"example://things/1234567890" - } - ], - }, - @"id": kAppLinkURLString - } - }]; - - FBSDKAppLinkResolver *resolver = [FBSDKAppLinkResolver resolver]; + id result = @{ + kAppLinkURLString : @{ + kAppLinksKey : @{ + @"android" : @[ + @{ + @"app_name" : @"Example", + @"package" : @"com.example.app", + @"url" : @"example://things/1234567890" + } + ], + @"ios" : @[ + @{ + @"app_name" : @"Example", + @"app_store_id" : @"123", + @"url" : @"example://things/1234567890" + } + ], + }, + @"id" : kAppLinkURLString + } + }; + + [self mockAppLinkRequestWithResult:result]; + FBSDKAppLinkResolver *resolver = [[FBSDKAppLinkResolver alloc] initWithRequestBuilder:self.appLinkResolverRequestBuilderMock]; + [resolver appLinkFromURL:[NSURL URLWithString:kAppLinkURLString] - handler:^(FBSDKAppLink * _Nullable link, NSError * _Nullable error) { + handler:^(FBSDKAppLink *_Nullable link, NSError *_Nullable error) { XCTAssertNotNil(link); XCTAssertEqualObjects([link.targets[0] appStoreId], @"123"); [expectation fulfill]; }]; - [self waitForExpectationsWithTimeout:2 handler:^(NSError * _Nullable error) { + [self waitForExpectationsWithTimeout:2 handler:^(NSError *_Nullable error) { XCTAssertNil(error); }]; - [FBSDKSettings setClientToken:nil]; } -*/ -/* - (void)testHandlesNoTargets { XCTestExpectation *expectation = [self expectationWithDescription:@"handlesNoTargets"]; - [FBSDKAccessToken setCurrentAccessToken:nil]; - [FBSDKSettings setClientToken:@"clienttoken"]; + [self stubClientTokenWith:@"clienttoken"]; + + id result = @{ + kAppLinkURLString : @{ + @"id" : kAppLinkURLString + } + }; - [self stubAllResponsesWithResult:@{ - kAppLinkURLString : @{ - @"id": kAppLinkURLString - } - }]; + [self mockAppLinkRequestWithResult:result]; + FBSDKAppLinkResolver *resolver = [[FBSDKAppLinkResolver alloc] initWithRequestBuilder:self.appLinkResolverRequestBuilderMock]; - FBSDKAppLinkResolver *resolver = [FBSDKAppLinkResolver resolver]; [resolver appLinkFromURL:[NSURL URLWithString:kAppLinkURLString] - handler:^(FBSDKAppLink * _Nullable link, NSError * _Nullable error) { + handler:^(FBSDKAppLink *_Nullable link, NSError *_Nullable error) { XCTAssertNotNil(link); XCTAssertEqual(link.sourceURL.absoluteString, kAppLinkURLString); XCTAssertEqual(link.targets.count, 0); @@ -354,33 +249,31 @@ - (void)testHandlesNoTargets [expectation fulfill]; }]; - [self waitForExpectationsWithTimeout:2 handler:^(NSError * _Nullable error) { + [self waitForExpectationsWithTimeout:2 handler:^(NSError *_Nullable error) { XCTAssertNil(error); }]; - [FBSDKSettings setClientToken:nil]; } -*/ -/* - (void)testHandlesMultipleURLs { XCTestExpectation *expectation = [self expectationWithDescription:@"handlesMultipleURLs"]; - [FBSDKAccessToken setCurrentAccessToken:nil]; - [FBSDKSettings setClientToken:@"clienttoken"]; - - [self stubAllResponsesWithResult:@{ - kAppLinkURLString : @{ - @"id": kAppLinkURLString - }, - kAppLinkURL2String : @{ - @"id": kAppLinkURL2String - } - }]; - - FBSDKAppLinkResolver *resolver = [FBSDKAppLinkResolver resolver]; + [self stubClientTokenWith:@"clienttoken"]; + + id result = @{ + kAppLinkURLString : @{ + @"id" : kAppLinkURLString + }, + kAppLinkURL2String : @{ + @"id" : kAppLinkURL2String + } + }; + + [self mockAppLinkRequestWithResult:result]; + FBSDKAppLinkResolver *resolver = [[FBSDKAppLinkResolver alloc] initWithRequestBuilder:self.appLinkResolverRequestBuilderMock]; + [resolver appLinksFromURLs:@[[NSURL URLWithString:kAppLinkURLString], [NSURL URLWithString:kAppLinkURL2String]] - handler:^(NSDictionary * _Nonnull links, NSError * _Nullable error) { + handler:^(NSDictionary *_Nonnull links, NSError *_Nullable error) { XCTAssertNotNil(links); XCTAssertEqual(links.count, 2); XCTAssertEqual([links[[NSURL URLWithString:kAppLinkURLString]] sourceURL].absoluteString, kAppLinkURLString); @@ -389,311 +282,204 @@ - (void)testHandlesMultipleURLs [expectation fulfill]; }]; - [self waitForExpectationsWithTimeout:2 handler:^(NSError * _Nullable error) { + [self waitForExpectationsWithTimeout:2 handler:^(NSError *_Nullable error) { XCTAssertNil(error); }]; - [FBSDKSettings setClientToken:nil]; } -*/ -/* - (void)testSetsFallbackIfNotSpecified { XCTestExpectation *expectation = [self expectationWithDescription:@"setsFallbackIfNotSpecified"]; - [FBSDKAccessToken setCurrentAccessToken:nil]; - [FBSDKSettings setClientToken:@"clienttoken"]; + [self stubClientTokenWith:@"clienttoken"]; - [self stubAllResponsesWithResult:@{ - kAppLinkURLString : @{ - @"id": kAppLinkURLString - } - }]; + id result = @{ + kAppLinkURLString : @{ + @"id" : kAppLinkURLString + } + }; + + [self mockAppLinkRequestWithResult:result]; + FBSDKAppLinkResolver *resolver = [[FBSDKAppLinkResolver alloc] initWithRequestBuilder:self.appLinkResolverRequestBuilderMock]; - FBSDKAppLinkResolver *resolver = [FBSDKAppLinkResolver resolver]; [resolver appLinkFromURL:[NSURL URLWithString:kAppLinkURLString] - handler:^(FBSDKAppLink * _Nullable link, NSError * _Nullable error) { + handler:^(FBSDKAppLink *_Nullable link, NSError *_Nullable error) { XCTAssertNotNil(link); XCTAssertEqual(link.webURL.absoluteString, kAppLinkURLString); [expectation fulfill]; }]; - [self waitForExpectationsWithTimeout:2 handler:^(NSError * _Nullable error) { + [self waitForExpectationsWithTimeout:2 handler:^(NSError *_Nullable error) { XCTAssertNil(error); }]; - [FBSDKSettings setClientToken:nil]; } -*/ -/* - (void)testSetsFallbackIfSpecified { XCTestExpectation *expectation = [self expectationWithDescription:@"setsFallbackIfSpecified"]; + [self stubClientTokenWith:@"clienttoken"]; + + id result = @{ + kAppLinkURLString : @{ + kAppLinksKey : @{ + @"web" : @{ + @"url" : @"http://www.example.com/somethingelse", + @"should_fallback" : @"true" + } + }, + @"id" : kAppLinkURLString + } + }; + + [self mockAppLinkRequestWithResult:result]; + FBSDKAppLinkResolver *resolver = [[FBSDKAppLinkResolver alloc] initWithRequestBuilder:self.appLinkResolverRequestBuilderMock]; - [FBSDKAccessToken setCurrentAccessToken:nil]; - [FBSDKSettings setClientToken:@"clienttoken"]; - - [self stubAllResponsesWithResult:@{ - kAppLinkURLString : @{ - kAppLinksKey : @{ - @"web": @{ - @"url" : @"http://www.example.com/somethingelse", - @"should_fallback": @"true" - } - }, - @"id": kAppLinkURLString - } - }]; - - FBSDKAppLinkResolver *resolver = [FBSDKAppLinkResolver resolver]; [resolver appLinkFromURL:[NSURL URLWithString:kAppLinkURLString] - handler:^(FBSDKAppLink * _Nullable link, NSError * _Nullable error) { + handler:^(FBSDKAppLink *_Nullable link, NSError *_Nullable error) { XCTAssertNotNil(link); XCTAssertEqualObjects(link.webURL.absoluteString, @"http://www.example.com/somethingelse"); [expectation fulfill]; }]; - [self waitForExpectationsWithTimeout:2 handler:^(NSError * _Nullable error) { + [self waitForExpectationsWithTimeout:2 handler:^(NSError *_Nullable error) { XCTAssertNil(error); }]; - [FBSDKSettings setClientToken:nil]; } -*/ -/* - (void)testUsesSourceAsFallbackIfSpecified { XCTestExpectation *expectation = [self expectationWithDescription:@"usesSourceAsFallbackIfSpecified"]; + [self stubClientTokenWith:@"clienttoken"]; + + id result = @{ + kAppLinkURLString : @{ + kAppLinksKey : @{ + @"web" : @{ + @"should_fallback" : @"true" + } + }, + @"id" : kAppLinkURLString, + } + }; + + [self mockAppLinkRequestWithResult:result]; + FBSDKAppLinkResolver *resolver = [[FBSDKAppLinkResolver alloc] initWithRequestBuilder:self.appLinkResolverRequestBuilderMock]; - [FBSDKAccessToken setCurrentAccessToken:nil]; - [FBSDKSettings setClientToken:@"clienttoken"]; - - [self stubAllResponsesWithResult:@{ - kAppLinkURLString : @{ - kAppLinksKey: @{ - @"web": @{ - @"should_fallback": @"true" - } - }, - @"id": kAppLinkURLString, - } - }]; - - FBSDKAppLinkResolver *resolver = [FBSDKAppLinkResolver resolver]; [resolver appLinkFromURL:[NSURL URLWithString:kAppLinkURLString] - handler:^(FBSDKAppLink * _Nullable link, NSError * _Nullable error) { + handler:^(FBSDKAppLink *_Nullable link, NSError *_Nullable error) { XCTAssertNotNil(link); XCTAssertEqual(link.webURL.absoluteString, kAppLinkURLString); [expectation fulfill]; }]; - [self waitForExpectationsWithTimeout:2 handler:^(NSError * _Nullable error) { + [self waitForExpectationsWithTimeout:2 handler:^(NSError *_Nullable error) { XCTAssertNil(error); }]; - [FBSDKSettings setClientToken:nil]; } -*/ -/* - (void)testSetsNoFallbackIfSpecified { XCTestExpectation *expectation = [self expectationWithDescription:@"setsNoFallbackIfSpecified"]; + [self stubClientTokenWith:@"clienttoken"]; + + id result = @{ + kAppLinkURLString : @{ + kAppLinksKey : @{ + @"web" : @{ + @"url" : @"http://www.example.com/somethingelse", + @"should_fallback" : @"false" + } + }, + @"id" : kAppLinkURLString + } + }; + + [self mockAppLinkRequestWithResult:result]; + FBSDKAppLinkResolver *resolver = [[FBSDKAppLinkResolver alloc] initWithRequestBuilder:self.appLinkResolverRequestBuilderMock]; - [FBSDKAccessToken setCurrentAccessToken:nil]; - [FBSDKSettings setClientToken:@"clienttoken"]; - - [self stubAllResponsesWithResult:@{ - kAppLinkURLString : @{ - kAppLinksKey : @{ - @"web": @{ - @"url" : @"http://www.example.com/somethingelse", - @"should_fallback": @"false" - } - }, - @"id": kAppLinkURLString - } - }]; - - FBSDKAppLinkResolver *resolver = [FBSDKAppLinkResolver resolver]; [resolver appLinkFromURL:[NSURL URLWithString:kAppLinkURLString] - handler:^(FBSDKAppLink * _Nullable link, NSError * _Nullable error) { + handler:^(FBSDKAppLink *_Nullable link, NSError *_Nullable error) { XCTAssertNotNil(link); XCTAssertNil(link.webURL.absoluteString); [expectation fulfill]; }]; - [self waitForExpectationsWithTimeout:2 handler:^(NSError * _Nullable error) { + [self waitForExpectationsWithTimeout:2 handler:^(NSError *_Nullable error) { XCTAssertNil(error); }]; - [FBSDKSettings setClientToken:nil]; } -*/ -/* - (void)testHandlesError { XCTestExpectation *expectation = [self expectationWithDescription:@"handlesError"]; + [self stubClientTokenWith:@"clienttoken"]; - [FBSDKAccessToken setCurrentAccessToken:nil]; - [FBSDKSettings setClientToken:@"clienttoken"]; - - // We are not asking for it, but just make sure we ignore any non-iOS-platform data we get, to be safe. - [self stubAllResponsesWithResult:@{ - @"error" : @{} - } - statusCode:404]; + [self mockAppLinkRequestWithError]; + FBSDKAppLinkResolver *resolver = [[FBSDKAppLinkResolver alloc] initWithRequestBuilder:self.appLinkResolverRequestBuilderMock]; - FBSDKAppLinkResolver *resolver = [FBSDKAppLinkResolver resolver]; [resolver appLinkFromURL:[NSURL URLWithString:kAppLinkURLString] - handler:^(FBSDKAppLink * _Nullable link, NSError * _Nullable error) { + handler:^(FBSDKAppLink *_Nullable link, NSError *_Nullable error) { XCTAssertNil(link); XCTAssertNotNil(error); [expectation fulfill]; }]; - [self waitForExpectationsWithTimeout:2 handler:^(NSError * _Nullable error) { + [self waitForExpectationsWithTimeout:2 handler:^(NSError *_Nullable error) { XCTAssertNil(error); }]; - [FBSDKSettings setClientToken:nil]; } -*/ -/* - (void)testResultsAreCachedAndCacheIsUsed { XCTestExpectation *expectation = [self expectationWithDescription:@"handlesCache"]; + [self stubClientTokenWith:@"clienttoken"]; + + id result = @{ + kAppLinkURLString : @{ + kAppLinksKey : @{ + @"iphone" : @[ + @{ + @"app_name" : @"Example", + @"app_store_id" : @"456", + @"url" : @"example://things/1234567890" + } + ], + }, + @"id" : kAppLinkURLString + } + }; - [FBSDKAccessToken setCurrentAccessToken:nil]; - [FBSDKSettings setClientToken:@"clienttoken"]; - - __block NSUInteger callCount = 0; - - [self stubAllResponsesWithResult:@{ - kAppLinkURLString : @{ - kAppLinksKey : @{ - @"iphone": @[ - @{ - @"app_name": @"Example", - @"app_store_id": @"456", - @"url": @"example://things/1234567890" - } - ], - }, - @"id": kAppLinkURLString - } - } - statusCode:200 - callback:^(NSURLRequest *request) { - ++callCount; - }]; - - FBSDKAppLinkResolver *resolver = [FBSDKAppLinkResolver resolver]; - [resolver - appLinkFromURL:[NSURL URLWithString:kAppLinkURLString] - handler:^(FBSDKAppLink * _Nullable link1, NSError * _Nullable error1) { - // Note: callCount is not necessarily 1, as the callback may be called multiple times during processing of the request. - NSUInteger expectedCallCount = callCount; - - [resolver - appLinkFromURL:[NSURL URLWithString:kAppLinkURLString] - handler:^(FBSDKAppLink * _Nullable link2, NSError * _Nullable error2) { - XCTAssertEqual(callCount, expectedCallCount); - [expectation fulfill]; - }]; - }]; - - [self waitForExpectationsWithTimeout:2 handler:^(NSError * _Nullable error) { - XCTAssertNil(error); + // We can change the approach and do OCMVerify(exactly(1), [_mockRequestBuilder requestForURLs]); + // in the inner block of the double call instead of using this counter. + __block int callCount = 0; + [self mockAppLinkRequestWithResult:result error:nil idiomSpecificField:kIphoneKey andDo:^(NSInvocation *invocation) { + callCount++; }]; - [FBSDKSettings setClientToken:nil]; -} -*/ -/* -- (void)testMixOfCachedAndUncached -{ - XCTestExpectation *expectation = [self expectationWithDescription:@"mixOfCachedAndUncached"]; - - [FBSDKAccessToken setCurrentAccessToken:nil]; - [FBSDKSettings setClientToken:@"clienttoken"]; - - __block NSMutableDictionary *callCounts = - [NSMutableDictionary dictionary]; - - [self stubMatchingRequestsWithResponses:@{ - @"1234567890" : @{ - kAppLinkURLString : @{ - kAppLinksKey : @{ - @"iphone": @[ - @{ - @"app_name": @"Example", - @"app_store_id": @"456", - @"url": @"example://things/1234567890" - } - ], - }, - @"id": kAppLinkURLString - } - }, - @"0987654321" : @{ - kAppLinkURL2String : @{ - kAppLinksKey : @{ - @"iphone": @[ - @{ - @"app_name": @"Example", - @"app_store_id": @"456", - @"url": @"example://things/1234567890" - } - ], - }, - @"id": kAppLinkURL2String - } - } - } - statusCode:200 - callback:^(NSURLRequest *request) { - NSUInteger callCount = callCounts[request.URL.absoluteString].unsignedIntegerValue; - ++callCount; - callCounts[request.URL.absoluteString] = @(callCount); - }]; - - FBSDKAppLinkResolver *resolver = [FBSDKAppLinkResolver resolver]; - - // Prime the cache with kAppLinkURL + FBSDKAppLinkResolver *resolver = [[FBSDKAppLinkResolver alloc] initWithRequestBuilder:self.appLinkResolverRequestBuilderMock]; + [resolver appLinkFromURL:[NSURL URLWithString:kAppLinkURLString] - handler:^(FBSDKAppLink * _Nullable appLink, NSError * _Nullable error1) { - XCTAssertEqual(callCounts.count, 1); - - // Note: callCount is not necessarily 1, as the callback may be called multiple times during processing of the request. - NSString *firstCallKey = callCounts.allKeys[0]; - NSUInteger expectedCallCount = callCounts[firstCallKey].unsignedIntegerValue; - - // Now request them both; we expect the call count for kAppLinkURL to be unchanged. + handler:^(FBSDKAppLink *_Nullable link1, NSError *_Nullable error1) { [resolver - appLinksFromURLs:@[[NSURL URLWithString:kAppLinkURLString], [NSURL URLWithString:kAppLinkURL2String]] - handler:^(NSDictionary * _Nonnull links, NSError * _Nullable error2) { - XCTAssertEqual(callCounts.count, 2); - XCTAssertEqual([callCounts[firstCallKey] unsignedIntegerValue], expectedCallCount); - - XCTAssertEqual(links.count, 2); + appLinkFromURL:[NSURL URLWithString:kAppLinkURLString] + handler:^(FBSDKAppLink *_Nullable link2, NSError *_Nullable error2) { + XCTAssertEqual(callCount, 1); [expectation fulfill]; - }]; + }]; }]; - [self waitForExpectationsWithTimeout:2 handler:^(NSError * _Nullable error) { + [self waitForExpectationsWithTimeout:2 handler:^(NSError *_Nullable error) { XCTAssertNil(error); }]; - [FBSDKSettings setClientToken:nil]; } -*/ @end diff --git a/FBSDKCoreKit/FBSDKCoreKitTests/Internal/Base64/FBSDKBase64Tests.m b/FBSDKCoreKit/FBSDKCoreKitTests/Internal/Base64/FBSDKBase64Tests.m deleted file mode 100644 index fe8d3bbbd0..0000000000 --- a/FBSDKCoreKit/FBSDKCoreKitTests/Internal/Base64/FBSDKBase64Tests.m +++ /dev/null @@ -1,69 +0,0 @@ -// 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 "FBSDKBase64.h" - -@interface FBSDKBase64Tests : XCTestCase - -@end - -@implementation FBSDKBase64Tests - -- (void)runTests:(NSDictionary *)tests -{ - [tests enumerateKeysAndObjectsUsingBlock:^(NSString *plainString, NSString *base64String, BOOL *stop) { - XCTAssertEqualObjects([FBSDKBase64 encodeString:plainString], base64String); - XCTAssertEqualObjects([FBSDKBase64 decodeAsString:base64String], plainString); - }]; -} - -- (void)testRFC4648TestVectors -{ - [self runTests:@{ - @"": @"", - @"f": @"Zg==", - @"fo": @"Zm8=", - @"foo": @"Zm9v", - @"foob": @"Zm9vYg==", - @"fooba": @"Zm9vYmE=", - @"foobar": @"Zm9vYmFy", - }]; -} - -- (void)testDecodeVariations -{ - XCTAssertEqualObjects([FBSDKBase64 decodeAsString:@"aGVsbG8gd29ybGQh"], @"hello world!"); - XCTAssertEqualObjects([FBSDKBase64 decodeAsString:@"a GVs\tb\r\nG8gd2\n9y\rbGQ h"], @"hello world!"); - XCTAssertEqualObjects([FBSDKBase64 decodeAsString:@"aGVsbG8gd29ybGQh"], @"hello world!"); - XCTAssertEqualObjects([FBSDKBase64 decodeAsString:@"aGVs#bG8*gd^29yb$GQh"], @"hello world!"); -} - -- (void)testEncodeDecode -{ - [self runTests:@{ - @"Hello World": @"SGVsbG8gV29ybGQ=", - @"ABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890!%^&*(){}[]": @"QUJDREVGR0hJSktMTU5PUFFSU1RVVldYWVoxMjM0NTY3ODkwISVeJiooKXt9W10=", - @"\n\t Line with control characters\r\n": @"CgkgTGluZSB3aXRoIGNvbnRyb2wgY2hhcmFjdGVycw0K", - }]; -} - -@end diff --git a/FBSDKCoreKit/FBSDKCoreKitTests/Internal/Base64/FBSDKBase64Tests.swift b/FBSDKCoreKit/FBSDKCoreKitTests/Internal/Base64/FBSDKBase64Tests.swift new file mode 100644 index 0000000000..7204ce7ce4 --- /dev/null +++ b/FBSDKCoreKit/FBSDKCoreKitTests/Internal/Base64/FBSDKBase64Tests.swift @@ -0,0 +1,64 @@ +// 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 XCTest + +class FBSDKBase64Tests: XCTestCase { + + func runTests(_ testsDict: [String: String]) { + for (plainString, base64String) in testsDict { + XCTAssertEqual(Base64.encode(plainString), base64String) + XCTAssertEqual(Base64.decode(as: base64String), plainString) + } + } + + func testRFC4648TestVectors() { + let testsDict = [ + "": "", + "f": "Zg==", + "fo": "Zm8=", + "foo": "Zm9v", + "foob": "Zm9vYg==", + "fooba": "Zm9vYmE=", + "foobar": "Zm9vYmFy", + ] + runTests(testsDict) + } + + func testDecodeVariations() { + XCTAssertEqual(Base64.decode(as: "aGVsbG8gd29ybGQh"), "hello world!") + XCTAssertEqual(Base64.decode(as: "a GVs\tb\r\nG8gd2\n9y\rbGQ h"), "hello world!") + XCTAssertEqual(Base64.decode(as: "aGVsbG8gd29ybGQh"), "hello world!") + XCTAssertEqual(Base64.decode(as: "aGVs#bG8*gd^29yb$GQh"), "hello world!") + } + + func testEncodeDecode() { + let testsDict = [ + "Hello World": "SGVsbG8gV29ybGQ=", + "ABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890!%^&*(){}[]": "QUJDREVGR0hJSktMTU5PUFFSU1RVVldYWVoxMjM0NTY3ODkwISVeJiooKXt9W10=", // swiftlint:disable:this line_length + "\n\t Line with control characters\r\n": "CgkgTGluZSB3aXRoIGNvbnRyb2wgY2hhcmFjdGVycw0K", + ] + runTests(testsDict) + } + + func testBase64URLEncode() { + let urlString = "https://www.example.com/some-path-with-dashes-in-it/" + let encodedString = Base64.base64(fromBase64Url: urlString) + XCTAssertEqual(encodedString, "https://www.example.com/some+path+with+dashes+in+it/") + } +} diff --git a/FBSDKCoreKit/FBSDKCoreKitTests/Internal/Basics/FBSDKBasicUtilityTests.m b/FBSDKCoreKit/FBSDKCoreKitTests/Internal/Basics/FBSDKBasicUtilityTests.m index d92d2f522a..ce11689a53 100644 --- a/FBSDKCoreKit/FBSDKCoreKitTests/Internal/Basics/FBSDKBasicUtilityTests.m +++ b/FBSDKCoreKit/FBSDKCoreKitTests/Internal/Basics/FBSDKBasicUtilityTests.m @@ -17,8 +17,8 @@ // CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. #import -#import "FBSDKBasicUtility.h" #import "FBSDKCoreKit.h" +#import "FBSDKInternalUtility.h" @interface FBSDKBasicUtilityTests : XCTestCase @end @@ -30,8 +30,8 @@ - (void)testJSONString NSString *URLString = @"https://www.facebook.com"; NSURL *URL = [NSURL URLWithString:URLString]; NSDictionary *dictionary = @{ - @"url": URL, - }; + @"url" : URL, + }; NSError *error; NSString *JSONString = [FBSDKBasicUtility JSONStringForObject:dictionary error:&error invalidObjectHandler:NULL]; XCTAssertNil(error); @@ -49,7 +49,7 @@ - (void)testConvertRequestValue XCTAssertTrue([result1 isKindOfClass:[NSString class]]); XCTAssertEqualObjects(result1, @"1"); - NSURL *value2= [NSURL URLWithString:@"https://test"]; + NSURL *value2 = [NSURL URLWithString:@"https://test"]; id result2 = [FBSDKBasicUtility convertRequestValue:value2]; XCTAssertTrue([result2 isKindOfClass:[NSString class]]); XCTAssertEqualObjects(result2, @"https://test"); @@ -64,10 +64,10 @@ - (void)testQueryString NSURL *URL = [NSURL URLWithString:@"http://example.com/path/to/page.html?key1&key2=value2&key3=value+3%20%3D%20foo#fragment=go"]; NSDictionary *dictionary = [FBSDKBasicUtility dictionaryWithQueryString:URL.query]; NSDictionary *expectedDictionary = @{ - @"key1": @"", - @"key2": @"value2", - @"key3": @"value 3 = foo", - }; + @"key1" : @"", + @"key2" : @"value2", + @"key3" : @"value 3 = foo", + }; XCTAssertEqualObjects(dictionary, expectedDictionary); NSString *queryString = [FBSDKBasicUtility queryStringWithDictionary:dictionary error:NULL invalidObjectHandler:NULL]; NSString *expectedQueryString = @"key1=&key2=value2&key3=value%203%20%3D%20foo"; diff --git a/FBSDKCoreKit/FBSDKCoreKitTests/Internal/Basics/FBSDKCrashHandlerTests.m b/FBSDKCoreKit/FBSDKCoreKitTests/Internal/Basics/FBSDKCrashHandlerTests.m index 75ed8a48d6..dd86a35072 100644 --- a/FBSDKCoreKit/FBSDKCoreKitTests/Internal/Basics/FBSDKCrashHandlerTests.m +++ b/FBSDKCoreKit/FBSDKCoreKitTests/Internal/Basics/FBSDKCrashHandlerTests.m @@ -16,42 +16,39 @@ // 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 "FBSDKCrashHandler.h" +#import "FBSDKCrashObserver.h" +#import "FBSDKInternalUtility.h" #import "FBSDKSettings.h" +#import "FBSDKTestCase.h" @interface FBSDKCrashHandler () -+ (void)uninstallExceptionsHandler; -+ (NSArray *)getCrashLogFileNames:(NSArray *)files; -+ (NSString *)getPathToCrashFile:(NSString *)timestamp; -+ (NSString *)getPathToLibDataFile:(NSString *)identifier; -+ (BOOL)callstack:(NSArray *)callstack - containsPrefix:(NSArray *)prefixList; -+ (NSArray *> *)filterCrashLogs:(NSArray *)prefixList - processedCrashLogs:(NSArray *> *)processedCrashLogs; ++ (void)_uninstallExceptionsHandler; ++ (NSArray *)_getCrashLogFileNames:(NSArray *)files; ++ (NSString *)_getPathToCrashFile:(NSString *)timestamp; ++ (NSString *)_getPathToLibDataFile:(NSString *)identifier; ++ (BOOL)_callstack:(NSArray *)callstack + containsPrefix:(NSArray *)prefixList; ++ (NSArray *> *)_filterCrashLogs:(NSArray *)prefixList + processedCrashLogs:(NSArray *> *)processedCrashLogs; ++ (void)_saveCrashLog:(NSDictionary *)crashLog; @end -@interface FBSDKCrashHandlerTests : XCTestCase +@interface FBSDKCrashHandlerTests : FBSDKTestCase @end @implementation FBSDKCrashHandlerTests - (void)setUp { - [FBSDKCrashHandler initialize]; -} + [super setUp]; -- (void)testDisable -{ - id hanlderMock = [OCMockObject niceMockForClass:[FBSDKCrashHandler class]]; - [[hanlderMock expect] uninstallExceptionsHandler]; - [FBSDKCrashHandler disable]; - [hanlderMock verify]; + // This should be removed when these tests are updated to check the actual requests that are created + [self stubAllocatingGraphRequestConnection]; } - (void)testGetFBSDKVersion @@ -70,7 +67,7 @@ - (void)testGetCrashLogFileNames @"SUGGEST_EVENT_3.rules", @"crash.text", ]; - NSArray *result1 = [FBSDKCrashHandler getCrashLogFileNames:files]; + NSArray *result1 = [FBSDKCrashHandler _getCrashLogFileNames:files]; XCTAssertTrue([result1 containsObject:@"crash_log_1576471375.json"]); XCTAssertFalse([result1 containsObject:@"crash_lib_data_05DEDC8AFC724E09A5E68190C492B92B.json"]); @@ -80,15 +77,15 @@ - (void)testGetCrashLogFileNames XCTAssertFalse([result1 containsObject:@"crash.text"]); files = [NSArray array]; - NSArray *result2 = [FBSDKCrashHandler getCrashLogFileNames:files]; + NSArray *result2 = [FBSDKCrashHandler _getCrashLogFileNames:files]; XCTAssertTrue(result2.count == 0); } - (void)testGetPathToCrashFile { NSString *timestampMock = @"test_timestamp"; - NSString *crashLogFileName = [NSString stringWithFormat:@"crash_log_%@.json", timestampMock]; - NSString *pathToCrashFile = [FBSDKCrashHandler getPathToCrashFile:timestampMock]; + NSString *crashLogFileName = [NSString stringWithFormat:@"crash_log_%@.json", timestampMock]; + NSString *pathToCrashFile = [FBSDKCrashHandler _getPathToCrashFile:timestampMock]; XCTAssertTrue([pathToCrashFile hasSuffix:crashLogFileName]); } @@ -97,7 +94,7 @@ - (void)testGetPathToLibDataFile { NSString *identifierMock = @"test_identifier"; NSString *libDataFileName = [NSString stringWithFormat:@"crash_lib_data_%@.json", identifierMock]; - NSString *pathToLibDataFile = [FBSDKCrashHandler getPathToLibDataFile:identifierMock]; + NSString *pathToLibDataFile = [FBSDKCrashHandler _getPathToLibDataFile:identifierMock]; XCTAssertTrue([pathToLibDataFile hasSuffix:libDataFileName]); } @@ -111,19 +108,19 @@ - (void)testCallStackContainsPrefix @"-[FBSDKWebViewAppLinkResolver appLinkFromALData:destination:]+10540", @"(14 DEV METHODS)", ]; - XCTAssertTrue([FBSDKCrashHandler callstack:callStack1 containsPrefix:prefixList]); + XCTAssertTrue([FBSDKCrashHandler _callstack:callStack1 containsPrefix:prefixList]); NSArray *callStack2 = @[ @"(2 DEV METHODS)", @"-[FBAdPersistentCacheImpl storeAssetInMemory:forKey:expiration:]+14455428", @"(12 DEV METHODS)", ]; - XCTAssertFalse([FBSDKCrashHandler callstack:callStack2 containsPrefix:prefixList]); + XCTAssertFalse([FBSDKCrashHandler _callstack:callStack2 containsPrefix:prefixList]); } - (void)testFilterCrashLogs { - NSArray *filteredCrashLogs = [FBSDKCrashHandler filterCrashLogs:@[@"FBSDK", @"_FBSDK"] processedCrashLogs:[self mockProcessedCrashLogs]]; + NSArray *filteredCrashLogs = [FBSDKCrashHandler _filterCrashLogs:@[@"FBSDK", @"_FBSDK"] processedCrashLogs:[self mockProcessedCrashLogs]]; XCTAssertEqual(1, filteredCrashLogs.count); @@ -143,13 +140,13 @@ - (void)testFilterCrashLogs - (NSArray *> *)mockProcessedCrashLogs { NSDictionary *crashLog1 = @{ - @"app_version" : @"4.16(4)", - @"callstack" : @[ - @"(2 DEV METHODS)", - @"-[FBSDKWebViewAppLinkResolver appLinkFromALData:destination:]+2110632", - @"-[FBSDKWebViewAppLinkResolver appLinkFromALData:destination:]+10540", - @"(14 DEV METHODS)", - ], + @"app_version" : @"4.16(4)", + @"callstack" : @[ + @"(2 DEV METHODS)", + @"-[FBSDKWebViewAppLinkResolver appLinkFromALData:destination:]+2110632", + @"-[FBSDKWebViewAppLinkResolver appLinkFromALData:destination:]+10540", + @"(14 DEV METHODS)", + ], @"reason" : @"InvalidOperationException", @"timestamp" : @"1585764970", @"device_model" : @"iPhone7,2", @@ -157,12 +154,12 @@ - (void)testFilterCrashLogs }; NSDictionary *crashLog2 = @{ - @"app_version" : @"1.173.0(2)", - @"callstack" : @[ - @"(3 DEV METHODS)", - @"-[SettingsItemViewController imageWithImage:destination:]+2110632", - @"(6 DEV METHODS)", - ], + @"app_version" : @"1.173.0(2)", + @"callstack" : @[ + @"(3 DEV METHODS)", + @"-[SettingsItemViewController imageWithImage:destination:]+2110632", + @"(6 DEV METHODS)", + ], @"reason" : @"NSInvalidArgumentException", @"timestamp" : @"1585764970", @"device_model" : @"iPad4,1", diff --git a/FBSDKCoreKit/FBSDKCoreKitTests/Internal/Basics/FBSDKLibAnalyzerTests.m b/FBSDKCoreKit/FBSDKCoreKitTests/Internal/Basics/FBSDKLibAnalyzerTests.m index 2b340ea9a5..beb10efc8c 100644 --- a/FBSDKCoreKit/FBSDKCoreKitTests/Internal/Basics/FBSDKLibAnalyzerTests.m +++ b/FBSDKCoreKit/FBSDKCoreKitTests/Internal/Basics/FBSDKLibAnalyzerTests.m @@ -16,17 +16,16 @@ // 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 "FBSDKLibAnalyzer.h" +#import "FBSDKCoreKit+Internal.h" @interface FBSDKLibAnalyzer () -+ (NSArray *)getClassNames:(NSArray *)prefixes - frameworks:(NSArray *)frameworks; -+ (NSString *)getAddress:(NSString *)callstackEntry; ++ (NSArray *)_getClassNames:(NSArray *)prefixes + frameworks:(NSArray *)frameworks; ++ (NSString *)_getAddress:(NSString *)callstackEntry; @end @@ -48,7 +47,7 @@ - (void)testGetMethodsTableFromPrefixesAndFrameworks @"FBSDKShareKit", @"FBSDKTVOSKit"]; id analyzerMock = [OCMockObject niceMockForClass:[FBSDKLibAnalyzer class]]; - [[analyzerMock expect] getClassNames:[OCMArg any] frameworks:[OCMArg any]]; + [[analyzerMock expect] _getClassNames:[OCMArg any] frameworks:[OCMArg any]]; NSDictionary *result = [FBSDKLibAnalyzer getMethodsTable:prefixes frameworks:frameworks]; XCTAssertNotNil(result); @@ -59,13 +58,12 @@ - (void)testGetMethodsTableFromPrefixesAndFrameworks - (void)testGetAddress { NSString *callstackEntry = @"0 CoreFoundation 0x0000000104cbd02e __exceptionPreprocess + 350"; - NSString *result1 = [FBSDKLibAnalyzer getAddress:callstackEntry]; + NSString *result1 = [FBSDKLibAnalyzer _getAddress:callstackEntry]; XCTAssertTrue([result1 isEqualToString:@"0x0000000104cbd02e"]); callstackEntry = @"0 CoreFoundation 0000000104cbd02e __exceptionPreprocess + 350"; - NSString *result2 = [FBSDKLibAnalyzer getAddress:callstackEntry]; + NSString *result2 = [FBSDKLibAnalyzer _getAddress:callstackEntry]; XCTAssertNil(result2); - } @end diff --git a/FBSDKCoreKit/FBSDKCoreKitTests/Internal/Basics/FBSDKSafeCastTests.m b/FBSDKCoreKit/FBSDKCoreKitTests/Internal/Basics/FBSDKSafeCastTests.m index 023eff6885..183859560e 100644 --- a/FBSDKCoreKit/FBSDKCoreKitTests/Internal/Basics/FBSDKSafeCastTests.m +++ b/FBSDKCoreKit/FBSDKCoreKitTests/Internal/Basics/FBSDKSafeCastTests.m @@ -17,6 +17,7 @@ // CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. #import + #import "FBSDKInternalUtility.h" @protocol ProtocolA @@ -45,45 +46,63 @@ @implementation FBSDKSafeCastTests - (void)testCastingToNonMatchingClass { ClassA *a = [ClassA new]; - XCTAssertNil(FBSDK_CAST_TO_CLASS_OR_NIL(a, ClassB), - "Casting from a known class to a non-matching class should fail and return nil"); + XCTAssertNil( + FBSDK_CAST_TO_CLASS_OR_NIL(a, ClassB), + "Casting from a known class to a non-matching class should fail and return nil" + ); id idA = a; - XCTAssertNil(FBSDK_CAST_TO_CLASS_OR_NIL(idA, ClassB), - "Casting from an unknown class to a non-matching class should fail and return nil"); + XCTAssertNil( + FBSDK_CAST_TO_CLASS_OR_NIL(idA, ClassB), + "Casting from an unknown class to a non-matching class should fail and return nil" + ); } - (void)testCastingToMatchingClass { ClassA *a = [ClassA new]; - XCTAssertEqual(FBSDK_CAST_TO_CLASS_OR_NIL(a, ClassA), a, - "Casting from a known class to a matching class should return the same instance of that class"); + XCTAssertEqual( + FBSDK_CAST_TO_CLASS_OR_NIL(a, ClassA), + a, + "Casting from a known class to a matching class should return the same instance of that class" + ); id idA = a; - XCTAssertEqual(FBSDK_CAST_TO_CLASS_OR_NIL(idA, ClassA), idA, - "Casting from an unknown class to a matching class should return the same instance of that class"); + XCTAssertEqual( + FBSDK_CAST_TO_CLASS_OR_NIL(idA, ClassA), + idA, + "Casting from an unknown class to a matching class should return the same instance of that class" + ); } - (void)testCastingToNonConformingProtocol { id a = [ClassA new]; - XCTAssertNil(FBSDK_CAST_TO_PROTOCOL_OR_NIL(a, ProtocolB), - "Should not return an object if it does not conform to the stated protocol"); + XCTAssertNil( + FBSDK_CAST_TO_PROTOCOL_OR_NIL(a, ProtocolB), + "Should not return an object if it does not conform to the stated protocol" + ); id idA = a; - XCTAssertNil(FBSDK_CAST_TO_PROTOCOL_OR_NIL(idA, ProtocolB), - "Should not return an object if it does not conform to the stated protocol"); + XCTAssertNil( + FBSDK_CAST_TO_PROTOCOL_OR_NIL(idA, ProtocolB), + "Should not return an object if it does not conform to the stated protocol" + ); } - (void)testCastingToConformingMatchingProtocol { ClassA *a = [ClassA new]; - XCTAssertNil(FBSDK_CAST_TO_PROTOCOL_OR_NIL(a, ProtocolB), - "Should return an object if it conforms to the stated protocol"); + XCTAssertNil( + FBSDK_CAST_TO_PROTOCOL_OR_NIL(a, ProtocolB), + "Should return an object if it conforms to the stated protocol" + ); id idA = a; - XCTAssertNil(FBSDK_CAST_TO_PROTOCOL_OR_NIL(idA, ProtocolB), - "Should return an object if it conforms to the stated protocol"); + XCTAssertNil( + FBSDK_CAST_TO_PROTOCOL_OR_NIL(idA, ProtocolB), + "Should return an object if it conforms to the stated protocol" + ); } - (void)testCastingNil diff --git a/FBSDKCoreKit/FBSDKCoreKitTests/Internal/Basics/FBSDKTypeUtilityTests.m b/FBSDKCoreKit/FBSDKCoreKitTests/Internal/Basics/FBSDKTypeUtilityTests.m index 797f49bfc4..8db33c0309 100644 --- a/FBSDKCoreKit/FBSDKCoreKitTests/Internal/Basics/FBSDKTypeUtilityTests.m +++ b/FBSDKCoreKit/FBSDKCoreKitTests/Internal/Basics/FBSDKTypeUtilityTests.m @@ -17,6 +17,7 @@ // CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. #import + #import "FBSDKCoreKit+Internal.h" #import "FBSDKCoreKitTests-Swift.h" @@ -24,7 +25,8 @@ @interface FBSDKTypeUtilityTests : XCTestCase @end -@implementation FBSDKTypeUtilityTests { +@implementation FBSDKTypeUtilityTests +{ NSArray *validJSONObjects; NSArray *invalidJSONObjects; } @@ -34,7 +36,7 @@ - (void)setUp [super setUp]; validJSONObjects = @[ - @{ @"foo": @"bar" }, + @{ @"foo" : @"bar" }, @[@1, @2, @3], @[], @{}, @@ -42,10 +44,9 @@ - (void)setUp invalidJSONObjects = @[ @"SomeString", - @{ @1: @"one" }, + @{ @1 : @"one" }, @"", ]; - } - (void)testIsValidJSONWithValidJSON @@ -65,7 +66,7 @@ - (void)testIsValidJSONWithInvalidJSON - (void)testIsValidJSONWithRandomValues { // Should not crash for any given value - for (int i = 0; i < 1000; i++) { + for (int i = 0; i < 100; i++) { [FBSDKTypeUtility isValidJSONObject:Fuzzer.random]; } } @@ -73,16 +74,22 @@ - (void)testIsValidJSONWithRandomValues - (void)testDataWithJSONObjectWithValidJSON { for (id object in validJSONObjects) { - XCTAssertNotNil([FBSDKTypeUtility dataWithJSONObject:object options:NSJSONWritingPrettyPrinted error:nil], - "Valid json object %@ should produce data", object); + XCTAssertNotNil( + [FBSDKTypeUtility dataWithJSONObject:object options:NSJSONWritingPrettyPrinted error:nil], + "Valid json object %@ should produce data", + object + ); } } - (void)testDataWithJSONObjectWithInvalidJSON { for (id object in invalidJSONObjects) { - XCTAssertNil([FBSDKTypeUtility dataWithJSONObject:object options:NSJSONWritingPrettyPrinted error:nil], - "Valid json object %@ should produce data", object); + XCTAssertNil( + [FBSDKTypeUtility dataWithJSONObject:object options:NSJSONWritingPrettyPrinted error:nil], + "Valid json object %@ should produce data", + object + ); } } @@ -91,8 +98,11 @@ - (void)testJSONObjectWithDataWithValidData for (id object in validJSONObjects) { NSData *data = [FBSDKTypeUtility dataWithJSONObject:object options:NSJSONWritingPrettyPrinted error:nil]; - XCTAssertEqualObjects([FBSDKTypeUtility JSONObjectWithData:data options:NSJSONReadingAllowFragments error:nil], object, - "Should be able to create objects from valid serialized JSON data"); + XCTAssertEqualObjects( + [FBSDKTypeUtility JSONObjectWithData:data options:NSJSONReadingAllowFragments error:nil], + object, + "Should be able to create objects from valid serialized JSON data" + ); } } @@ -100,15 +110,17 @@ - (void)testJSONObjectWithDataWithInvalidData { NSArray *invalidData = @[ [@"SomeString" dataUsingEncoding:NSUTF8StringEncoding], - [[@{ @1: @"one" } description] dataUsingEncoding:NSUTF8StringEncoding], + [[@{ @1 : @"one" } description] dataUsingEncoding:NSUTF8StringEncoding], [@"" dataUsingEncoding:NSUTF8StringEncoding], [NSData data], [NSDate date], ]; for (NSData *data in invalidData) { - XCTAssertNil([FBSDKTypeUtility JSONObjectWithData:data options:NSJSONReadingAllowFragments error:nil], - "Should not be able to create a JSON objrct from invalid data"); + XCTAssertNil( + [FBSDKTypeUtility JSONObjectWithData:data options:NSJSONReadingAllowFragments error:nil], + "Should not be able to create a JSON objrct from invalid data" + ); } } @@ -116,32 +128,42 @@ - (void)testArrayAccessEmptyArray { NSArray *array = @[]; - XCTAssertNil([FBSDKTypeUtility array:array objectAtIndex:5], - "Should return nil and not crash when accessing invalid indices"); + XCTAssertNil( + [FBSDKTypeUtility array:array objectAtIndex:5], + "Should return nil and not crash when accessing invalid indices" + ); } - (void)testArrayAccessNonEmptyArrayInvalidIndex { NSArray *array = @[@1, @2, @3]; - XCTAssertNil([FBSDKTypeUtility array:array objectAtIndex:5], - "Should return nil and not crash when accessing invalid indices"); + XCTAssertNil( + [FBSDKTypeUtility array:array objectAtIndex:5], + "Should return nil and not crash when accessing invalid indices" + ); } - (void)testArrayAccessNonEmptyArrayZeroIndex { NSArray *array = @[@1, @2, @3]; - XCTAssertEqualObjects([array objectAtIndex:0], @1, - "Should be able to retrive a valid object at the first index of an array"); + XCTAssertEqualObjects( + [array objectAtIndex:0], + @1, + "Should be able to retrive a valid object at the first index of an array" + ); } - (void)testArrayAccessNonEmptyArrayValidIndex { NSArray *array = @[@1, @2, @3]; - XCTAssertEqualObjects([array objectAtIndex:2], @3, - "Should be able to retrive a valid object at a valid index of an array"); + XCTAssertEqualObjects( + [array objectAtIndex:2], + @3, + "Should be able to retrive a valid object at a valid index of an array" + ); } - (void)testAddingArrayObjectAtIndexEmptyArray @@ -149,8 +171,11 @@ - (void)testAddingArrayObjectAtIndexEmptyArray NSMutableArray *array = [NSMutableArray array]; [FBSDKTypeUtility array:array addObject:@"foo" atIndex:0]; - XCTAssertEqualObjects([array objectAtIndex:0], @"foo", - "Should be able to insert a valid object into an empty array"); + XCTAssertEqualObjects( + [array objectAtIndex:0], + @"foo", + "Should be able to insert a valid object into an empty array" + ); } - (void)testAddingArrayObjectAtIndexNonEmptyArray @@ -159,8 +184,11 @@ - (void)testAddingArrayObjectAtIndexNonEmptyArray [FBSDKTypeUtility array:array addObject:@"foo" atIndex:0]; [FBSDKTypeUtility array:array addObject:@"bar" atIndex:1]; - XCTAssertEqualObjects([array objectAtIndex:1], @"bar", - "Should be able to insert a valid object into an available position in a non empty array"); + XCTAssertEqualObjects( + [array objectAtIndex:1], + @"bar", + "Should be able to insert a valid object into an available position in a non empty array" + ); } - (void)testAddingArrayObjectAtDuplicateIndex @@ -169,8 +197,11 @@ - (void)testAddingArrayObjectAtDuplicateIndex [FBSDKTypeUtility array:array addObject:@"foo" atIndex:0]; [FBSDKTypeUtility array:array addObject:@"bar" atIndex:0]; - XCTAssertEqualObjects([array objectAtIndex:0], @"bar", - "Should be able to insert a valid object at a non-empty index"); + XCTAssertEqualObjects( + [array objectAtIndex:0], + @"bar", + "Should be able to insert a valid object at a non-empty index" + ); } - (void)testAddingArrayObjectAtUnavailableIndex diff --git a/FBSDKCoreKit/FBSDKCoreKitTests/Internal/BridgeAPI/FBSDKBridgeAPI+ApplicationOpenUrlTests.m b/FBSDKCoreKit/FBSDKCoreKitTests/Internal/BridgeAPI/FBSDKBridgeAPI+ApplicationOpenUrlTests.m new file mode 100644 index 0000000000..64a4373d3c --- /dev/null +++ b/FBSDKCoreKit/FBSDKCoreKitTests/Internal/BridgeAPI/FBSDKBridgeAPI+ApplicationOpenUrlTests.m @@ -0,0 +1,967 @@ +// 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 "FBSDKBridgeAPITests.h" + +@implementation FBSDKBridgeAPITests (ApplicationOpenURLTests) + +// MARK: - Url Opening + +- (void)testOpenUrl_ShouldStopPropagationPendingUrlCanOpenUrlWithoutSafariVcWhileDismissingSafariVcWithoutAuthSessionCompletionHandlerAbleToHandleBridgeApiResponse +{ + [self verifyOpenUrlWithShouldStopPropagationOfUrl:YES + pendingUrlCanOpenUrl:YES + isDismissingSafariViewController:YES + authSessionCompletionHandlerExists:NO + canHandleBridgeApiResponse:YES + expectedAuthSessionCompletionExists:NO]; +} + +- (void)testOpenUrl_ShouldStopPropagationPendingUrlCanOpenUrlWithoutSafariVcWhileDismissingSafariVcWithoutAuthSessionCompletionHandlerUnableToHandleBridgeApiResponse +{ + [self verifyOpenUrlWithShouldStopPropagationOfUrl:YES + pendingUrlCanOpenUrl:YES + isDismissingSafariViewController:YES + authSessionCompletionHandlerExists:NO + canHandleBridgeApiResponse:NO + expectedAuthSessionCompletionExists:NO]; +} + +- (void)testOpenUrl_ShouldStopPropagationPendingUrlCanOpenUrlWithoutSafariVcWhileDismissingSafariVcWithAuthSessionCompletionHandlerAbleToHandleBridgeApiResponse +{ + [self verifyOpenUrlWithShouldStopPropagationOfUrl:YES + pendingUrlCanOpenUrl:YES + isDismissingSafariViewController:YES + authSessionCompletionHandlerExists:YES + canHandleBridgeApiResponse:YES + expectedAuthSessionCompletionExists:YES]; +} + +- (void)testOpenUrl_ShouldStopPropagationPendingUrlCanOpenUrlWithoutSafariVcWhileDismissingSafariVcWithAuthSessionCompletionHandlerUnableToHandleBridgeApiResponse +{ + [self verifyOpenUrlWithShouldStopPropagationOfUrl:YES + pendingUrlCanOpenUrl:YES + isDismissingSafariViewController:YES + authSessionCompletionHandlerExists:YES + canHandleBridgeApiResponse:NO + expectedAuthSessionCompletionExists:YES]; +} + +- (void)testOpenUrl_ShouldStopPropagationPendingUrlCanOpenUrlWithoutSafariVcWhilePresentingSafariVcWithoutAuthSessionCompletionHandlerAbleToHandleBridgeApiResponse +{ + [self verifyOpenUrlWithShouldStopPropagationOfUrl:YES + pendingUrlCanOpenUrl:YES + isDismissingSafariViewController:NO + authSessionCompletionHandlerExists:NO + canHandleBridgeApiResponse:YES + expectedAuthSessionCompletionExists:NO]; +} + +- (void)testOpenUrl_ShouldStopPropagationPendingUrlCanOpenUrlWithoutSafariVcWhilePresentingSafariVcWithoutAuthSessionCompletionHandlerUnableToHandleBridgeApiResponse +{ + [self verifyOpenUrlWithShouldStopPropagationOfUrl:YES + pendingUrlCanOpenUrl:YES + isDismissingSafariViewController:NO + authSessionCompletionHandlerExists:NO + canHandleBridgeApiResponse:NO + expectedAuthSessionCompletionExists:NO]; +} + +- (void)testOpenUrl_ShouldStopPropagationPendingUrlCanOpenUrlWithoutSafariVcWhilePresentingSafariVcWithAuthSessionCompletionHandlerAbleToHandleBridgeApiResponse +{ + [self verifyOpenUrlWithShouldStopPropagationOfUrl:YES + pendingUrlCanOpenUrl:YES + isDismissingSafariViewController:NO + authSessionCompletionHandlerExists:YES + canHandleBridgeApiResponse:YES + expectedAuthSessionCompletionExists:YES]; +} + +- (void)testOpenUrl_ShouldStopPropagationPendingUrlCanOpenUrlWithoutSafariVcWhilePresentingSafariVcWithAuthSessionCompletionHandlerUnableToHandleBridgeApiResponse +{ + [self verifyOpenUrlWithShouldStopPropagationOfUrl:YES + pendingUrlCanOpenUrl:YES + isDismissingSafariViewController:NO + authSessionCompletionHandlerExists:YES + canHandleBridgeApiResponse:NO + expectedAuthSessionCompletionExists:YES]; +} + +- (void)testOpenUrl_ShouldStopPropagationPendingUrlCanOpenUrlWithSafariVcWhileDismissingSafariVcWithoutAuthSessionCompletionHandlerAbleToHandleBridgeApiResponse +{ + [self verifyOpenUrlWithShouldStopPropagationOfUrl:YES + pendingUrlCanOpenUrl:YES + hasSafariViewController:YES + isDismissingSafariViewController:YES + authSessionCompletionHandlerExists:NO + canHandleBridgeApiResponse:YES + expectedAuthSessionCompletionExists:NO]; +} + +- (void)testOpenUrl_ShouldStopPropagationPendingUrlCanOpenUrlWithSafariVcWhileDismissingSafariVcWithoutAuthSessionCompletionHandlerUnableToHandleBridgeApiResponse +{ + [self verifyOpenUrlWithShouldStopPropagationOfUrl:YES + pendingUrlCanOpenUrl:YES + hasSafariViewController:YES + isDismissingSafariViewController:YES + authSessionCompletionHandlerExists:NO + canHandleBridgeApiResponse:NO + expectedAuthSessionCompletionExists:NO]; +} + +- (void)testOpenUrl_ShouldStopPropagationPendingUrlCanOpenUrlWithSafariVcWhileDismissingSafariVcWithAuthSessionCompletionHandlerAbleToHandleBridgeApiResponse +{ + [self verifyOpenUrlWithShouldStopPropagationOfUrl:YES + pendingUrlCanOpenUrl:YES + hasSafariViewController:YES + isDismissingSafariViewController:YES + authSessionCompletionHandlerExists:YES + canHandleBridgeApiResponse:YES + expectedAuthSessionCompletionExists:YES]; +} + +- (void)testOpenUrl_ShouldStopPropagationPendingUrlCanOpenUrlWithSafariVcWhileDismissingSafariVcWithAuthSessionCompletionHandlerUnableToHandleBridgeApiResponse +{ + [self verifyOpenUrlWithShouldStopPropagationOfUrl:YES + pendingUrlCanOpenUrl:YES + hasSafariViewController:YES + isDismissingSafariViewController:YES + authSessionCompletionHandlerExists:YES + canHandleBridgeApiResponse:NO + expectedAuthSessionCompletionExists:YES]; +} + +- (void)testOpenUrl_ShouldStopPropagationPendingUrlCanOpenUrlWithSafariVcWhilePresentingSafariVcWithoutAuthSessionCompletionHandlerAbleToHandleBridgeApiResponse +{ + [self verifyOpenUrlWithShouldStopPropagationOfUrl:YES + pendingUrlCanOpenUrl:YES + hasSafariViewController:YES + isDismissingSafariViewController:NO + authSessionCompletionHandlerExists:NO + canHandleBridgeApiResponse:YES + expectedAuthSessionCompletionExists:NO]; +} + +- (void)testOpenUrl_ShouldStopPropagationPendingUrlCanOpenUrlWithSafariVcWhilePresentingSafariVcWithoutAuthSessionCompletionHandlerUnableToHandleBridgeApiResponse +{ + [self verifyOpenUrlWithShouldStopPropagationOfUrl:YES + pendingUrlCanOpenUrl:YES + hasSafariViewController:YES + isDismissingSafariViewController:NO + authSessionCompletionHandlerExists:NO + canHandleBridgeApiResponse:NO + expectedAuthSessionCompletionExists:NO]; +} + +- (void)testOpenUrl_ShouldStopPropagationPendingUrlCanOpenUrlWithSafariVcWhilePresentingSafariVcWithAuthSessionCompletionHandlerAbleToHandleBridgeApiResponse +{ + [self verifyOpenUrlWithShouldStopPropagationOfUrl:YES + pendingUrlCanOpenUrl:YES + hasSafariViewController:YES + isDismissingSafariViewController:NO + authSessionCompletionHandlerExists:YES + canHandleBridgeApiResponse:YES + expectedAuthSessionCompletionExists:YES]; +} + +- (void)testOpenUrl_ShouldStopPropagationPendingUrlCanOpenUrlWithSafariVcWhilePresentingSafariVcWithAuthSessionCompletionHandlerUnableToHandleBridgeApiResponse +{ + [self verifyOpenUrlWithShouldStopPropagationOfUrl:YES + pendingUrlCanOpenUrl:YES + hasSafariViewController:YES + isDismissingSafariViewController:NO + authSessionCompletionHandlerExists:YES + canHandleBridgeApiResponse:NO + expectedAuthSessionCompletionExists:YES]; +} + +- (void)testOpenUrl_ShouldStopPropagationPendingUrlCannotOpenUrlWithoutSafariVcWhileDismissingSafariVcWithoutAuthSessionCompletionHandlerAbleToHandleBridgeApiResponse +{ + [self verifyOpenUrlWithShouldStopPropagationOfUrl:YES + pendingUrlCanOpenUrl:NO + isDismissingSafariViewController:YES + authSessionCompletionHandlerExists:NO + canHandleBridgeApiResponse:YES + expectedAuthSessionCompletionExists:NO]; +} + +- (void)testOpenUrl_ShouldStopPropagationPendingUrlCannotOpenUrlWithoutSafariVcWhileDismissingSafariVcWithoutAuthSessionCompletionHandlerUnableToHandleBridgeApiResponse +{ + [self verifyOpenUrlWithShouldStopPropagationOfUrl:YES + pendingUrlCanOpenUrl:NO + isDismissingSafariViewController:YES + authSessionCompletionHandlerExists:NO + canHandleBridgeApiResponse:NO + expectedAuthSessionCompletionExists:NO]; +} + +- (void)testOpenUrl_ShouldStopPropagationPendingUrlCannotOpenUrlWithoutSafariVcWhileDismissingSafariVcWithAuthSessionCompletionHandlerAbleToHandleBridgeApiResponse +{ + [self verifyOpenUrlWithShouldStopPropagationOfUrl:YES + pendingUrlCanOpenUrl:NO + isDismissingSafariViewController:YES + authSessionCompletionHandlerExists:YES + canHandleBridgeApiResponse:YES + expectedAuthSessionCompletionExists:YES]; +} + +- (void)testOpenUrl_ShouldStopPropagationPendingUrlCannotOpenUrlWithoutSafariVcWhileDismissingSafariVcWithAuthSessionCompletionHandlerUnableToHandleBridgeApiResponse +{ + [self verifyOpenUrlWithShouldStopPropagationOfUrl:YES + pendingUrlCanOpenUrl:NO + isDismissingSafariViewController:YES + authSessionCompletionHandlerExists:YES + canHandleBridgeApiResponse:NO + expectedAuthSessionCompletionExists:YES]; +} + +- (void)testOpenUrl_ShouldStopPropagationPendingUrlCannotOpenUrlWithoutSafariVcWhilePresentingSafariVcWithoutAuthSessionCompletionHandlerAbleToHandleBridgeApiResponse +{ + [self verifyOpenUrlWithShouldStopPropagationOfUrl:YES + pendingUrlCanOpenUrl:NO + isDismissingSafariViewController:NO + authSessionCompletionHandlerExists:NO + canHandleBridgeApiResponse:YES + expectedAuthSessionCompletionExists:NO]; +} + +- (void)testOpenUrl_ShouldStopPropagationPendingUrlCannotOpenUrlWithoutSafariVcWhilePresentingSafariVcWithoutAuthSessionCompletionHandlerUnableToHandleBridgeApiResponse +{ + [self verifyOpenUrlWithShouldStopPropagationOfUrl:YES + pendingUrlCanOpenUrl:NO + isDismissingSafariViewController:NO + authSessionCompletionHandlerExists:NO + canHandleBridgeApiResponse:NO + expectedAuthSessionCompletionExists:NO]; +} + +- (void)testOpenUrl_ShouldStopPropagationPendingUrlCannotOpenUrlWithoutSafariVcWhilePresentingSafariVcWithAuthSessionCompletionHandlerAbleToHandleBridgeApiResponse +{ + [self verifyOpenUrlWithShouldStopPropagationOfUrl:YES + pendingUrlCanOpenUrl:NO + isDismissingSafariViewController:NO + authSessionCompletionHandlerExists:YES + canHandleBridgeApiResponse:YES + expectedAuthSessionCompletionExists:YES]; +} + +- (void)testOpenUrl_ShouldStopPropagationPendingUrlCannotOpenUrlWithoutSafariVcWhilePresentingSafariVcWithAuthSessionCompletionHandlerUnableToHandleBridgeApiResponse +{ + [self verifyOpenUrlWithShouldStopPropagationOfUrl:YES + pendingUrlCanOpenUrl:NO + isDismissingSafariViewController:NO + authSessionCompletionHandlerExists:YES + canHandleBridgeApiResponse:NO + expectedAuthSessionCompletionExists:YES]; +} + +- (void)testOpenUrl_ShouldStopPropagationPendingUrlCannotOpenUrlWithSafariVcWhileDismissingSafariVcWithoutAuthSessionCompletionHandlerAbleToHandleBridgeApiResponse +{ + [self verifyOpenUrlWithShouldStopPropagationOfUrl:YES + pendingUrlCanOpenUrl:NO + hasSafariViewController:YES + isDismissingSafariViewController:YES + authSessionCompletionHandlerExists:NO + canHandleBridgeApiResponse:YES + expectedAuthSessionCompletionExists:NO]; +} + +- (void)testOpenUrl_ShouldStopPropagationPendingUrlCannotOpenUrlWithSafariVcWhileDismissingSafariVcWithoutAuthSessionCompletionHandlerUnableToHandleBridgeApiResponse +{ + [self verifyOpenUrlWithShouldStopPropagationOfUrl:YES + pendingUrlCanOpenUrl:NO + hasSafariViewController:YES + isDismissingSafariViewController:YES + authSessionCompletionHandlerExists:NO + canHandleBridgeApiResponse:NO + expectedAuthSessionCompletionExists:NO]; +} + +- (void)testOpenUrl_ShouldStopPropagationPendingUrlCannotOpenUrlWithSafariVcWhileDismissingSafariVcWithAuthSessionCompletionHandlerAbleToHandleBridgeApiResponse +{ + [self verifyOpenUrlWithShouldStopPropagationOfUrl:YES + pendingUrlCanOpenUrl:NO + hasSafariViewController:YES + isDismissingSafariViewController:YES + authSessionCompletionHandlerExists:YES + canHandleBridgeApiResponse:YES + expectedAuthSessionCompletionExists:YES]; +} + +- (void)testOpenUrl_ShouldStopPropagationPendingUrlCannotOpenUrlWithSafariVcWhileDismissingSafariVcWithAuthSessionCompletionHandlerUnableToHandleBridgeApiResponse +{ + [self verifyOpenUrlWithShouldStopPropagationOfUrl:YES + pendingUrlCanOpenUrl:NO + hasSafariViewController:YES + isDismissingSafariViewController:YES + authSessionCompletionHandlerExists:YES + canHandleBridgeApiResponse:NO + expectedAuthSessionCompletionExists:YES]; +} + +- (void)testOpenUrl_ShouldStopPropagationPendingUrlCannotOpenUrlWithSafariVcWhilePresentingSafariVcWithoutAuthSessionCompletionHandlerAbleToHandleBridgeApiResponse +{ + [self verifyOpenUrlWithShouldStopPropagationOfUrl:YES + pendingUrlCanOpenUrl:NO + hasSafariViewController:YES + isDismissingSafariViewController:NO + authSessionCompletionHandlerExists:NO + canHandleBridgeApiResponse:YES + expectedAuthSessionCompletionExists:NO]; +} + +- (void)testOpenUrl_ShouldStopPropagationPendingUrlCannotOpenUrlWithSafariVcWhilePresentingSafariVcWithoutAuthSessionCompletionHandlerUnableToHandleBridgeApiResponse +{ + [self verifyOpenUrlWithShouldStopPropagationOfUrl:YES + pendingUrlCanOpenUrl:NO + hasSafariViewController:YES + isDismissingSafariViewController:NO + authSessionCompletionHandlerExists:NO + canHandleBridgeApiResponse:NO + expectedAuthSessionCompletionExists:NO]; +} + +- (void)testOpenUrl_ShouldStopPropagationPendingUrlCannotOpenUrlWithSafariVcWhilePresentingSafariVcWithAuthSessionCompletionHandlerAbleToHandleBridgeApiResponse +{ + [self verifyOpenUrlWithShouldStopPropagationOfUrl:YES + pendingUrlCanOpenUrl:NO + hasSafariViewController:YES + isDismissingSafariViewController:NO + authSessionCompletionHandlerExists:YES + canHandleBridgeApiResponse:YES + expectedAuthSessionCompletionExists:YES]; +} + +- (void)testOpenUrl_ShouldStopPropagationPendingUrlCannotOpenUrlWithSafariVcWhilePresentingSafariVcWithAuthSessionCompletionHandlerUnableToHandleBridgeApiResponse +{ + [self verifyOpenUrlWithShouldStopPropagationOfUrl:YES + pendingUrlCanOpenUrl:NO + hasSafariViewController:YES + isDismissingSafariViewController:NO + authSessionCompletionHandlerExists:YES + canHandleBridgeApiResponse:NO + expectedAuthSessionCompletionExists:YES]; +} + +- (void)testOpenUrl_PendingUrlCanOpenUrlWithoutSafariVcWhileDismissingSafariVcWithoutAuthSessionCompletionHandlerAbleToHandleBridgeApiResponse +{ + [self verifyOpenUrlWithPendingUrlCanOpenUrl:YES + hasSafariViewController:NO + isDismissingSafariViewController:YES + authSessionCompletionHandlerExists:NO + canHandleBridgeApiResponse:YES + expectedAuthCancelCallCount:1 + expectedIsDismissingSafariVc:NO + expectedAuthSessionCompletionExists:NO]; +} + +- (void)testOpenUrl_PendingUrlCanOpenUrlWithoutSafariVcWhileDismissingSafariVcWithoutAuthSessionCompletionHandlerUnableToHandleBridgeApiResponse +{ + [self verifyOpenUrlWithPendingUrlCanOpenUrl:YES + hasSafariViewController:NO + isDismissingSafariViewController:YES + authSessionCompletionHandlerExists:NO + canHandleBridgeApiResponse:NO + expectedAuthCancelCallCount:1 + expectedIsDismissingSafariVc:NO + expectedAuthSessionCompletionExists:NO]; +} + +- (void)testOpenUrl_PendingUrlCanOpenUrlWithoutSafariVcWhileDismissingSafariVcWithAuthSessionCompletionHandlerAbleToHandleBridgeApiResponse +{ + [self verifyOpenUrlWithPendingUrlCanOpenUrl:YES + hasSafariViewController:NO + isDismissingSafariViewController:YES + authSessionCompletionHandlerExists:NO + canHandleBridgeApiResponse:YES + expectedAuthCancelCallCount:1 + expectedIsDismissingSafariVc:NO + expectedAuthSessionCompletionExists:NO]; +} + +- (void)testOpenUrl_PendingUrlCanOpenUrlWithoutSafariVcWhileDismissingSafariVcWithAuthSessionCompletionHandlerUnableToHandleBridgeApiResponse +{ + [self verifyOpenUrlWithPendingUrlCanOpenUrl:YES + hasSafariViewController:NO + isDismissingSafariViewController:YES + authSessionCompletionHandlerExists:YES + canHandleBridgeApiResponse:NO + expectedAuthCancelCallCount:1 + expectedIsDismissingSafariVc:NO + expectedAuthSessionCompletionExists:YES]; +} + +- (void)testOpenUrl_PendingUrlCanOpenUrlWithoutSafariVcWhilePresentingSafariVcWithoutAuthSessionCompletionHandlerAbleToHandleBridgeApiResponse +{ + [self verifyOpenUrlWithPendingUrlCanOpenUrl:YES + hasSafariViewController:NO + isDismissingSafariViewController:NO + authSessionCompletionHandlerExists:NO + canHandleBridgeApiResponse:YES + expectedAuthCancelCallCount:1 + expectedIsDismissingSafariVc:NO + expectedAuthSessionCompletionExists:NO]; +} + +- (void)testOpenUrl_PendingUrlCanOpenUrlWithoutSafariVcWhilePresentingSafariVcWithoutAuthSessionCompletionHandlerUnableToHandleBridgeApiResponse +{ + [self verifyOpenUrlWithPendingUrlCanOpenUrl:YES + hasSafariViewController:NO + isDismissingSafariViewController:NO + authSessionCompletionHandlerExists:NO + canHandleBridgeApiResponse:NO + expectedAuthCancelCallCount:1 + expectedIsDismissingSafariVc:NO + expectedAuthSessionCompletionExists:NO]; +} + +- (void)testOpenUrl_PendingUrlCanOpenUrlWithoutSafariVcWhilePresentingSafariVcWithAuthSessionCompletionHandlerAbleToHandleBridgeApiResponse +{ + [self verifyOpenUrlWithPendingUrlCanOpenUrl:YES + hasSafariViewController:NO + isDismissingSafariViewController:NO + authSessionCompletionHandlerExists:YES + canHandleBridgeApiResponse:YES + expectedAuthCancelCallCount:1 + expectedIsDismissingSafariVc:NO + expectedAuthSessionCompletionExists:YES]; +} + +- (void)testOpenUrl_PendingUrlCanOpenUrlWithoutSafariVcWhilePresentingSafariVcWithAuthSessionCompletionHandlerUnableToHandleBridgeApiResponse +{ + [self verifyOpenUrlWithPendingUrlCanOpenUrl:YES + hasSafariViewController:NO + isDismissingSafariViewController:NO + authSessionCompletionHandlerExists:YES + canHandleBridgeApiResponse:NO + expectedAuthCancelCallCount:1 + expectedIsDismissingSafariVc:NO + expectedAuthSessionCompletionExists:YES]; +} + +- (void)testOpenUrl_PendingUrlCanOpenUrlWithSafariVcWhileDismissingSafariVcWithoutAuthSessionCompletionHandlerAbleToHandleBridgeApiResponse +{ + [self verifyOpenUrlWithPendingUrlCanOpenUrl:YES + isDismissingSafariViewController:YES + authSessionCompletionHandlerExists:NO + canHandleBridgeApiResponse:YES + expectedIsDismissingSafariVc:YES + expectedAuthSessionCompletionExists:NO]; +} + +- (void)testOpenUrl_PendingUrlCanOpenUrlWithSafariVcWhileDismissingSafariVcWithoutAuthSessionCompletionHandlerUnableToHandleBridgeApiResponse +{ + [self verifyOpenUrlWithPendingUrlCanOpenUrl:YES + isDismissingSafariViewController:YES + authSessionCompletionHandlerExists:NO + canHandleBridgeApiResponse:NO + expectedIsDismissingSafariVc:YES + expectedAuthSessionCompletionExists:NO]; +} + +- (void)testOpenUrl_PendingUrlCanOpenUrlWithSafariVcWhileDismissingSafariVcWithAuthSessionCompletionHandlerAbleToHandleBridgeApiResponse +{ + [self verifyOpenUrlWithPendingUrlCanOpenUrl:YES + isDismissingSafariViewController:YES + authSessionCompletionHandlerExists:YES + canHandleBridgeApiResponse:YES + expectedIsDismissingSafariVc:YES + expectedAuthSessionCompletionExists:YES]; +} + +- (void)testOpenUrl_PendingUrlCanOpenUrlWithSafariVcWhileDismissingSafariVcWithAuthSessionCompletionHandlerUnableToHandleBridgeApiResponse +{ + [self verifyOpenUrlWithPendingUrlCanOpenUrl:YES + isDismissingSafariViewController:YES + authSessionCompletionHandlerExists:YES + canHandleBridgeApiResponse:NO + expectedIsDismissingSafariVc:YES + expectedAuthSessionCompletionExists:YES]; +} + +- (void)testOpenUrl_PendingUrlCanOpenUrlWithSafariVcWhilePresentingSafariVcWithoutAuthSessionCompletionHandlerAbleToHandleBridgeApiResponse +{ + [self verifyOpenUrlWithPendingUrlCanOpenUrl:YES + isDismissingSafariViewController:NO + authSessionCompletionHandlerExists:NO + canHandleBridgeApiResponse:YES + expectedIsDismissingSafariVc:YES + expectedAuthSessionCompletionExists:NO]; +} + +- (void)testOpenUrl_PendingUrlCanOpenUrlWithSafariVcWhilePresentingSafariVcWithoutAuthSessionCompletionHandlerUnableToHandleBridgeApiResponse +{ + [self verifyOpenUrlWithPendingUrlCanOpenUrl:YES + isDismissingSafariViewController:NO + authSessionCompletionHandlerExists:NO + canHandleBridgeApiResponse:NO + expectedIsDismissingSafariVc:YES + expectedAuthSessionCompletionExists:NO]; +} + +- (void)testOpenUrl_PendingUrlCanOpenUrlWithSafariVcWhilePresentingSafariVcWithAuthSessionCompletionHandlerAbleToHandleBridgeApiResponse +{ + [self verifyOpenUrlWithPendingUrlCanOpenUrl:YES + isDismissingSafariViewController:NO + authSessionCompletionHandlerExists:YES + canHandleBridgeApiResponse:YES + expectedIsDismissingSafariVc:YES + expectedAuthSessionCompletionExists:YES]; +} + +- (void)testOpenUrl_PendingUrlCanOpenUrlWithSafariVcWhilePresentingSafariVcWithAuthSessionCompletionHandlerUnableToHandleBridgeApiResponse +{ + [self verifyOpenUrlWithPendingUrlCanOpenUrl:YES + isDismissingSafariViewController:NO + authSessionCompletionHandlerExists:YES + canHandleBridgeApiResponse:NO + expectedIsDismissingSafariVc:YES + expectedAuthSessionCompletionExists:YES]; +} + +- (void)testOpenUrl_PendingUrlCannotOpenUrlWithoutSafariVcWhileDismissingSafariVcWithoutAuthSessionCompletionHandlerAbleToHandleBridgeApiResponse +{ + [self verifyOpenURLWithoutSafariVcWhileDismissingSafariViewController:YES + authSessionCompletionHandlerExists:YES + canHandleBridgeApiResponse:NO + expectedIsDismissingSafariVc:NO + expectedAuthSessionCompletionExists:NO + expectedReturnValue:NO]; +} + +- (void)testOpenUrl_PendingUrlCannotOpenUrlWithoutSafariVcWhileDismissingSafariVcWithoutAuthSessionCompletionHandlerUnableToHandleBridgeApiResponse +{ + [self verifyOpenURLWithoutSafariVcWhileDismissingSafariViewController:YES + authSessionCompletionHandlerExists:NO + canHandleBridgeApiResponse:NO + expectedIsDismissingSafariVc:NO + expectedAuthSessionCompletionExists:NO + expectedReturnValue:NO]; +} + +- (void)testOpenUrl_PendingUrlCannotOpenUrlWithoutSafariVcWhileDismissingSafariVcWithAuthSessionCompletionHandlerAbleToHandleBridgeApiResponse +{ + [self verifyOpenURLWithoutSafariVcWhileDismissingSafariViewController:YES + authSessionCompletionHandlerExists:NO + canHandleBridgeApiResponse:YES + expectedIsDismissingSafariVc:NO + expectedAuthSessionCompletionExists:NO + expectedReturnValue:YES]; +} + +- (void)testOpenUrl_PendingUrlCannotOpenUrlWithoutSafariVcWhileDismissingSafariVcWithAuthSessionCompletionHandlerUnableToHandleBridgeApiResponse +{ + [self verifyOpenURLWithoutSafariVcWhileDismissingSafariViewController:YES + authSessionCompletionHandlerExists:YES + canHandleBridgeApiResponse:NO + expectedIsDismissingSafariVc:NO + expectedAuthSessionCompletionExists:NO + expectedReturnValue:NO]; +} + +- (void)testOpenUrl_PendingUrlCannotOpenUrlWithoutSafariVcWhilePresentingSafariVcWithoutAuthSessionCompletionHandlerAbleToHandleBridgeApiResponse +{ + [self verifyOpenURLWithoutSafariVcWhileDismissingSafariViewController:NO + authSessionCompletionHandlerExists:NO + canHandleBridgeApiResponse:YES + expectedIsDismissingSafariVc:NO + expectedAuthSessionCompletionExists:NO + expectedReturnValue:YES]; +} + +- (void)testOpenUrl_PendingUrlCannotOpenUrlWithoutSafariVcWhilePresentingSafariVcWithoutAuthSessionCompletionHandlerUnableToHandleBridgeApiResponse +{ + [self verifyOpenURLWithoutSafariVcWhileDismissingSafariViewController:NO + authSessionCompletionHandlerExists:NO + canHandleBridgeApiResponse:NO + expectedIsDismissingSafariVc:NO + expectedAuthSessionCompletionExists:NO + expectedReturnValue:NO]; +} + +- (void)testOpenUrl_PendingUrlCannotOpenUrlWithoutSafariVcWhilePresentingSafariVcWithAuthSessionCompletionHandlerAbleToHandleBridgeApiResponse +{ + [self verifyOpenURLWithoutSafariVcWhileDismissingSafariViewController:NO + authSessionCompletionHandlerExists:YES + canHandleBridgeApiResponse:YES + expectedIsDismissingSafariVc:NO + expectedAuthSessionCompletionExists:NO + expectedReturnValue:YES]; +} + +- (void)testOpenUrl_PendingUrlCannotOpenUrlWithoutSafariVcWhilePresentingSafariVcWithAuthSessionCompletionHandlerUnableToHandleBridgeApiResponse +{ + [self verifyOpenURLWithoutSafariVcWhileDismissingSafariViewController:NO + authSessionCompletionHandlerExists:YES + canHandleBridgeApiResponse:NO + expectedIsDismissingSafariVc:NO + expectedAuthSessionCompletionExists:NO + expectedReturnValue:NO]; +} + +// MARK: - Helpers + +/// Assumes should not stop propagation of url +/// Assumes SafariViewController is not nil +- (void)verifyOpenUrlWithPendingUrlCanOpenUrl:(BOOL)pendingUrlCanOpenUrl + isDismissingSafariViewController:(BOOL)isDismissingSafariViewController + authSessionCompletionHandlerExists:(BOOL)authSessionCompletionHandlerExists + canHandleBridgeApiResponse:(BOOL)canHandleBridgeApiResponse + expectedIsDismissingSafariVc:(BOOL)expectedIsDismissingSafariVc + expectedAuthSessionCompletionExists:(BOOL)expectedAuthSessionCompletionExists +{ + [self verifyOpenUrlWithShouldStopPropagationOfUrl:NO + pendingUrlCanOpenUrl:pendingUrlCanOpenUrl + hasSafariViewController:YES + isDismissingSafariViewController:isDismissingSafariViewController + authSessionCompletionHandlerExists:authSessionCompletionHandlerExists + canHandleBridgeApiResponse:canHandleBridgeApiResponse + expectedAuthSessionCompletionHandlerUrl:nil + expectedAuthSessionCompletionHandlerError:nil + expectAuthSessionCompletionHandlerInvoked:NO + expectedAuthCancelCallCount:0 + expectedAuthSessionExists:YES + expectedAuthSessionCompletionExists:expectedAuthSessionCompletionExists + expectedCanOpenUrlUrl:self.sampleUrl + expectedCanOpenUrlSource:sampleSource + expectedCanOpenUrlAnnotation:sampleAnnotation + expectedOpenUrlUrl:nil + expectedOpenUrlSource:nil + expectedOpenUrlAnnotation:nil + expectedPendingUrlOpenExists:YES + expectedIsDismissingSafariVc:expectedIsDismissingSafariVc + expectedSafariVcExists:NO + expectedReturnValue:YES]; +} + +/// Assumes should not stop propagation of url +/// Assumes Pending Url cannot open +/// Assumes SafariViewController is nil +- (void)verifyOpenURLWithoutSafariVcWhileDismissingSafariViewController:(BOOL)isDismissingSafariViewController + authSessionCompletionHandlerExists:(BOOL)authSessionCompletionHandlerExists + canHandleBridgeApiResponse:(BOOL)canHandleBridgeApiResponse + expectedIsDismissingSafariVc:(BOOL)expectedIsDismissingSafariVc + expectedAuthSessionCompletionExists:(BOOL)expectedAuthSessionCompletionExists + expectedReturnValue:(BOOL)expectedReturnValue +{ + [self verifyOpenUrlWithShouldStopPropagationOfUrl:NO + pendingUrlCanOpenUrl:NO + hasSafariViewController:NO + isDismissingSafariViewController:isDismissingSafariViewController + authSessionCompletionHandlerExists:authSessionCompletionHandlerExists + canHandleBridgeApiResponse:canHandleBridgeApiResponse + expectedAuthSessionCompletionHandlerUrl:self.sampleUrl + expectedAuthSessionCompletionHandlerError:self.loginInterruptionError + expectAuthSessionCompletionHandlerInvoked:YES + expectedAuthCancelCallCount:1 + expectedAuthSessionExists:NO + expectedAuthSessionCompletionExists:expectedAuthSessionCompletionExists + expectedCanOpenUrlUrl:self.sampleUrl + expectedCanOpenUrlSource:sampleSource + expectedCanOpenUrlAnnotation:sampleAnnotation + expectedOpenUrlUrl:self.sampleUrl + expectedOpenUrlSource:sampleSource + expectedOpenUrlAnnotation:sampleAnnotation + expectedPendingUrlOpenExists:NO + expectedIsDismissingSafariVc:expectedIsDismissingSafariVc + expectedSafariVcExists:NO + expectedReturnValue:expectedReturnValue]; +} + +/// Assumes should not stop propagation of url +- (void)verifyOpenUrlWithPendingUrlCanOpenUrl:(BOOL)pendingUrlCanOpenUrl + hasSafariViewController:(BOOL)hasSafariViewController + isDismissingSafariViewController:(BOOL)isDismissingSafariViewController + authSessionCompletionHandlerExists:(BOOL)authSessionCompletionHandlerExists + canHandleBridgeApiResponse:(BOOL)canHandleBridgeApiResponse + expectedAuthCancelCallCount:(int)expectedAuthCancelCallCount + expectedIsDismissingSafariVc:(BOOL)expectedIsDismissingSafariVc + expectedAuthSessionCompletionExists:(BOOL)expectedAuthSessionCompletionExists +{ + [self verifyOpenUrlWithShouldStopPropagationOfUrl:NO + pendingUrlCanOpenUrl:pendingUrlCanOpenUrl + hasSafariViewController:hasSafariViewController + isDismissingSafariViewController:isDismissingSafariViewController + authSessionCompletionHandlerExists:authSessionCompletionHandlerExists + canHandleBridgeApiResponse:canHandleBridgeApiResponse + expectedAuthSessionCompletionHandlerUrl:nil + expectedAuthSessionCompletionHandlerError:nil + expectAuthSessionCompletionHandlerInvoked:NO + expectedAuthCancelCallCount:expectedAuthCancelCallCount + expectedAuthSessionExists:NO + expectedAuthSessionCompletionExists:expectedAuthSessionCompletionExists + expectedCanOpenUrlUrl:self.sampleUrl + expectedCanOpenUrlSource:sampleSource + expectedCanOpenUrlAnnotation:sampleAnnotation + expectedOpenUrlUrl:self.sampleUrl + expectedOpenUrlSource:sampleSource + expectedOpenUrlAnnotation:sampleAnnotation + expectedPendingUrlOpenExists:NO + expectedIsDismissingSafariVc:expectedIsDismissingSafariVc + expectedSafariVcExists:hasSafariViewController + expectedReturnValue:YES]; +} + +- (void)verifyOpenUrlWithShouldStopPropagationOfUrl:(BOOL)shouldStopPropagationOfURL + pendingUrlCanOpenUrl:(BOOL)pendingUrlCanOpenUrl + hasSafariViewController:(BOOL)hasSafariViewController + isDismissingSafariViewController:(BOOL)isDismissingSafariViewController + authSessionCompletionHandlerExists:(BOOL)authSessionCompletionHandlerExists + canHandleBridgeApiResponse:(BOOL)canHandleBridgeApiResponse + expectedAuthSessionCompletionExists:(BOOL)expectedAuthSessionCompletionExists +{ + [self verifyOpenUrlWithShouldStopPropagationOfUrl:shouldStopPropagationOfURL + pendingUrlCanOpenUrl:pendingUrlCanOpenUrl + hasSafariViewController:hasSafariViewController + isDismissingSafariViewController:isDismissingSafariViewController + authSessionCompletionHandlerExists:authSessionCompletionHandlerExists + canHandleBridgeApiResponse:canHandleBridgeApiResponse + expectedAuthSessionCompletionHandlerUrl:nil + expectedAuthSessionCompletionHandlerError:nil + expectAuthSessionCompletionHandlerInvoked:NO + expectedAuthCancelCallCount:0 + expectedAuthSessionExists:YES + expectedAuthSessionCompletionExists:expectedAuthSessionCompletionExists + expectedCanOpenUrlUrl:nil + expectedCanOpenUrlSource:nil + expectedCanOpenUrlAnnotation:nil + expectedOpenUrlUrl:nil + expectedOpenUrlSource:nil + expectedOpenUrlAnnotation:nil + expectedPendingUrlOpenExists:YES + expectedIsDismissingSafariVc:isDismissingSafariViewController // Should not change the value + expectedSafariVcExists:hasSafariViewController + expectedReturnValue:YES]; +} + +/// Assumes SafariViewController is nil +- (void)verifyOpenUrlWithShouldStopPropagationOfUrl:(BOOL)shouldStopPropagationOfURL + pendingUrlCanOpenUrl:(BOOL)pendingUrlCanOpenUrl + isDismissingSafariViewController:(BOOL)isDismissingSafariViewController + authSessionCompletionHandlerExists:(BOOL)authSessionCompletionHandlerExists + canHandleBridgeApiResponse:(BOOL)canHandleBridgeApiResponse + expectedAuthSessionCompletionExists:(BOOL)expectedAuthSessionCompletionExists +{ + [self verifyOpenUrlWithShouldStopPropagationOfUrl:shouldStopPropagationOfURL + pendingUrlCanOpenUrl:pendingUrlCanOpenUrl + hasSafariViewController:NO + isDismissingSafariViewController:isDismissingSafariViewController + authSessionCompletionHandlerExists:authSessionCompletionHandlerExists + canHandleBridgeApiResponse:canHandleBridgeApiResponse + expectedAuthSessionCompletionHandlerUrl:nil + expectedAuthSessionCompletionHandlerError:nil + expectAuthSessionCompletionHandlerInvoked:NO + expectedAuthCancelCallCount:0 + expectedAuthSessionExists:YES + expectedAuthSessionCompletionExists:expectedAuthSessionCompletionExists + expectedCanOpenUrlUrl:nil + expectedCanOpenUrlSource:nil + expectedCanOpenUrlAnnotation:nil + expectedOpenUrlUrl:nil + expectedOpenUrlSource:nil + expectedOpenUrlAnnotation:nil + expectedPendingUrlOpenExists:YES + expectedIsDismissingSafariVc:isDismissingSafariViewController // Should not change this value + expectedSafariVcExists:NO + expectedReturnValue:YES]; +} + +- (void)verifyOpenUrlWithShouldStopPropagationOfUrl:(BOOL)shouldStopPropagationOfURL + pendingUrlCanOpenUrl:(BOOL)pendingUrlCanOpenUrl + hasSafariViewController:(BOOL)hasSafariViewController + isDismissingSafariViewController:(BOOL)isDismissingSafariViewController + authSessionCompletionHandlerExists:(BOOL)authSessionCompletionHandlerExists + canHandleBridgeApiResponse:(BOOL)canHandleBridgeApiResponse + expectedAuthSessionCompletionHandlerUrl:(NSURL *)expectedAuthSessionCompletionHandlerUrl + expectedAuthSessionCompletionHandlerError:(NSError *)expectedAuthSessionCompletionHandlerError + expectAuthSessionCompletionHandlerInvoked:(BOOL)expectAuthSessionCompletionHandlerInvoked + expectedAuthCancelCallCount:(int)expectedAuthCancelCallCount + expectedAuthSessionExists:(BOOL)expectedAuthSessionExists + expectedAuthSessionCompletionExists:(BOOL)expectedAuthSessionCompletionExists + expectedCanOpenUrlUrl:(NSURL *)expectedCanOpenUrlCalledWithUrl + expectedCanOpenUrlSource:(NSString *)expectedCanOpenUrlSource + expectedCanOpenUrlAnnotation:(NSString *)expectedCanOpenUrlAnnotation + expectedOpenUrlUrl:(NSURL *)expectedOpenUrlUrl + expectedOpenUrlSource:(NSString *)expectedOpenUrlSource + expectedOpenUrlAnnotation:(NSString *)expectedOpenUrlAnnotation + expectedPendingUrlOpenExists:(BOOL)expectedPendingUrlOpenExists + expectedIsDismissingSafariVc:(BOOL)expectedIsDismissingSafariVc + expectedSafariVcExists:(BOOL)expectedSafariVcExists + expectedReturnValue:(BOOL)expectedReturnValue +{ + FBSDKLoginManager *urlOpener = [FBSDKLoginManager new]; + AuthenticationSessionSpy *authSessionSpy = [[AuthenticationSessionSpy alloc] initWithURL:self.sampleUrl + callbackURLScheme:nil + completionHandler:^(NSURL *_Nullable callbackURL, NSError *_Nullable error) { + XCTFail("Should not invoke the completion for the authentication session"); + }]; + [urlOpener stubShouldStopPropagationOfURL:self.sampleUrl withValue:shouldStopPropagationOfURL]; + urlOpener.stubbedCanOpenUrl = pendingUrlCanOpenUrl; + + self.api.pendingUrlOpen = urlOpener; + + if (hasSafariViewController) { + ViewControllerSpy *viewControllerSpy = ViewControllerSpy.makeDefaultSpy; + self.api.safariViewController = viewControllerSpy; + } + self.api.isDismissingSafariViewController = isDismissingSafariViewController; + self.api.authenticationSessionState = FBSDKAuthenticationSessionNone; + self.api.pendingRequest = self.sampleBridgeApiRequest; + self.api.authenticationSession = authSessionSpy; + if (authSessionCompletionHandlerExists) { + self.api.authenticationSessionCompletionHandler = ^(NSURL *callbackURL, NSError *error) { + if (!expectAuthSessionCompletionHandlerInvoked) { + XCTFail("Should not invoke the authentication session completion handler"); + } + XCTAssertEqualObjects( + callbackURL, + expectedAuthSessionCompletionHandlerUrl, + "Should invoke the authentication session completion handler with the expected URL" + ); + XCTAssertEqualObjects( + error, + expectedAuthSessionCompletionHandlerError, + "Should invoke the authentication session completion handler with the expected error" + ); + }; + } + self.api.pendingRequestCompletionBlock = ^(FBSDKBridgeAPIResponse *response) { + XCTFail("Should not invoke the pending request completion block"); + }; + [self stubHandleBridgeApiResponseAndReturn:canHandleBridgeApiResponse]; + + BOOL returnValue = [self.api application:UIApplication.sharedApplication + openURL:self.sampleUrl + sourceApplication:sampleSource + annotation:sampleAnnotation]; + XCTAssertEqual( + returnValue, + expectedReturnValue, + "The return value for the overall method should be %@", + StringFromBool(expectedReturnValue) + ); + if (expectedAuthSessionExists) { + XCTAssertNotNil(self.api.authenticationSession, "The authentication session should not be nil"); + } else { + XCTAssertNil(self.api.authenticationSession, "The authentication session should be nil"); + } + if (expectedAuthSessionCompletionExists) { + XCTAssertNotNil( + self.api.authenticationSessionCompletionHandler, + "The authentication session completion handler should not be nil" + ); + } else { + XCTAssertNil( + self.api.authenticationSessionCompletionHandler, + "The authentication session completion handler should be nil" + ); + } + XCTAssertEqual( + authSessionSpy.cancelCallCount, + expectedAuthCancelCallCount, + "The authentication session should be cancelled the expected number of times" + ); + XCTAssertEqual( + self.api.authenticationSessionState, + FBSDKAuthenticationSessionNone, + "The authentication session state should not change" + ); + XCTAssertEqualObjects( + urlOpener.capturedCanOpenUrl, + expectedCanOpenUrlCalledWithUrl, + "The url opener's can open url method should be called with the expected URL" + ); + XCTAssertEqualObjects( + urlOpener.capturedCanOpenSourceApplication, + expectedCanOpenUrlSource, + "The url opener's can open url method should be called with the expected source application" + ); + XCTAssertEqualObjects( + urlOpener.capturedCanOpenAnnotation, + expectedCanOpenUrlAnnotation, + "The url opener's can open url method should be called with the expected annotation" + ); + + XCTAssertEqualObjects( + FBSDKLoginManager.capturedOpenUrl, + expectedOpenUrlUrl, + "The url opener's open url method should be called with the expected URL" + ); + XCTAssertEqualObjects( + FBSDKLoginManager.capturedSourceApplication, + expectedOpenUrlSource, + "The url opener's open url method should be called with the expected source application" + ); + XCTAssertEqualObjects( + FBSDKLoginManager.capturedAnnotation, + expectedOpenUrlAnnotation, + "The url opener's open url method should be called with the expected annotation" + ); + + XCTAssertNotNil(self.api.pendingRequest, "The pending request should not be nil"); + XCTAssertNotNil(self.api.pendingRequestCompletionBlock, "The pending request completion block should not be nil"); + if (expectedPendingUrlOpenExists) { + XCTAssertNotNil(self.api.pendingUrlOpen, "The reference to the url opener should not be nil"); + } else { + XCTAssertNil(self.api.pendingUrlOpen, "The reference to the url opener should be nil"); + } + if (expectedSafariVcExists) { + XCTAssertNotNil(self.api.safariViewController, "Safari view controller should not be nil"); + } else { + XCTAssertNil(self.api.safariViewController, "Safari view controller should be nil"); + } + XCTAssertEqual( + self.api.isDismissingSafariViewController, + expectedIsDismissingSafariVc, + "Should set isDismissingSafariViewController to the expected value" + ); +} + +/// Stubs `FBSDKBridgeAPI`'s `_handleBridgeAPIResponseURL:sourceApplication:` method to return the provided value +- (void)stubHandleBridgeApiResponseAndReturn:(BOOL)value +{ + OCMStub([self.partialMock _handleBridgeAPIResponseURL:OCMArg.any sourceApplication:OCMArg.any]).andReturn(value); +} + +- (FBSDKBridgeAPIRequest *)sampleBridgeApiRequest +{ + return [FBSDKBridgeAPIRequest bridgeAPIRequestWithProtocolType:FBSDKBridgeAPIProtocolTypeWeb + scheme:@"https" + methodName:nil + methodVersion:nil + parameters:nil + userInfo:nil]; +} + +- (NSError *)loginInterruptionError +{ + NSString *errorMessage = [[NSString alloc] + initWithFormat:@"Login attempt cancelled by alternate call to openURL from: %@", + self.sampleUrl]; + return [[NSError alloc] + initWithDomain:FBSDKErrorDomain + code:FBSDKErrorBridgeAPIInterruption + userInfo:@{FBSDKErrorLocalizedDescriptionKey : errorMessage}]; +} + +static inline NSString *StringFromBool(BOOL value) +{ + return value ? @"YES" : @"NO"; +} + +@end diff --git a/FBSDKCoreKit/FBSDKCoreKitTests/Internal/BridgeAPI/FBSDKBridgeAPI+SessionCompletionHandlerTests.m b/FBSDKCoreKit/FBSDKCoreKitTests/Internal/BridgeAPI/FBSDKBridgeAPI+SessionCompletionHandlerTests.m new file mode 100644 index 0000000000..0e50ad5af6 --- /dev/null +++ b/FBSDKCoreKit/FBSDKCoreKitTests/Internal/BridgeAPI/FBSDKBridgeAPI+SessionCompletionHandlerTests.m @@ -0,0 +1,163 @@ +// 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 "FBSDKBridgeAPITests.h" + +@implementation FBSDKBridgeAPITests (SessionCompletionTests) + +// MARK: - Setting Session Completion Handler + +- (void)testInvokingAuthSessionCompletionHandlerFromHandlerWithValidUrlWithoutError +{ + OCMStub( + [self.partialMock application:OCMArg.any + openURL:OCMArg.any + sourceApplication:OCMArg.any + annotation:OCMArg.any] + ); + + FBSDKSuccessBlock handler = ^(BOOL success, NSError *_Nullable error) { + XCTAssertTrue(success); + XCTAssertNil(error); + }; + self.api.authenticationSession = AuthenticationSessionSpy.makeDefaultSpy; + self.api.authenticationSessionState = FBSDKAuthenticationSessionStarted; + [self.api setSessionCompletionHandlerFromHandler:handler]; + self.api.authenticationSessionCompletionHandler(self.sampleUrl, nil); + + OCMVerify( + [self.partialMock application:OCMArg.any + openURL:self.sampleUrl + sourceApplication:@"com.apple" + annotation:OCMArg.any] + ); + [self verifyAuthenticationPropertiesReset]; +} + +- (void)testInvokingAuthSessionCompletionHandlerFromHandlerWithInvalidUrlWithoutError +{ + NSURL *url = [NSURL URLWithString:@" "]; + FBSDKSuccessBlock handler = ^(BOOL success, NSError *_Nullable error) { + XCTAssertFalse(success); + XCTAssertNil(error); + }; + self.api.authenticationSession = AuthenticationSessionSpy.makeDefaultSpy; + self.api.authenticationSessionState = FBSDKAuthenticationSessionStarted; + + OCMReject( + [self.partialMock application:OCMArg.any + openURL:OCMArg.any + sourceApplication:OCMArg.any + annotation:OCMArg.any] + ); + [self.api setSessionCompletionHandlerFromHandler:handler]; + self.api.authenticationSessionCompletionHandler(url, nil); + [self verifyAuthenticationPropertiesReset]; +} + +- (void)testInvokingAuthSessionCompletionHandlerFromHandlerWithoutUrlWithoutError +{ + FBSDKSuccessBlock handler = ^(BOOL success, NSError *_Nullable error) { + XCTAssertFalse(success); + XCTAssertNil(error); + }; + self.api.authenticationSession = AuthenticationSessionSpy.makeDefaultSpy; + self.api.authenticationSessionState = FBSDKAuthenticationSessionStarted; + OCMReject( + [self.partialMock application:OCMArg.any + openURL:OCMArg.any + sourceApplication:OCMArg.any + annotation:OCMArg.any] + ); + + [self.api setSessionCompletionHandlerFromHandler:handler]; + self.api.authenticationSessionCompletionHandler(nil, nil); + [self verifyAuthenticationPropertiesReset]; +} + +- (void)testInvokingAuthSessionCompletionHandlerFromHandlerWithValidUrlWithError +{ + FBSDKSuccessBlock handler = ^(BOOL success, NSError *_Nullable error) { + XCTAssertFalse(success); + XCTAssertEqualObjects(error, self.sampleError); + }; + self.api.authenticationSession = AuthenticationSessionSpy.makeDefaultSpy; + self.api.authenticationSessionState = FBSDKAuthenticationSessionStarted; + OCMReject( + [self.partialMock application:OCMArg.any + openURL:OCMArg.any + sourceApplication:OCMArg.any + annotation:OCMArg.any] + ); + + [self.api setSessionCompletionHandlerFromHandler:handler]; + self.api.authenticationSessionCompletionHandler(self.sampleUrl, self.sampleError); + [self verifyAuthenticationPropertiesReset]; +} + +- (void)testInvokingAuthSessionCompletionHandlerFromHandlerWithInvalidUrlWithError +{ + NSURL *url = [NSURL URLWithString:@" "]; + FBSDKSuccessBlock handler = ^(BOOL success, NSError *_Nullable error) { + XCTAssertFalse(success); + XCTAssertEqualObjects(error, self.sampleError); + }; + self.api.authenticationSession = AuthenticationSessionSpy.makeDefaultSpy; + self.api.authenticationSessionState = FBSDKAuthenticationSessionStarted; + + OCMReject( + [self.partialMock application:OCMArg.any + openURL:OCMArg.any + sourceApplication:OCMArg.any + annotation:OCMArg.any] + ); + [self.api setSessionCompletionHandlerFromHandler:handler]; + self.api.authenticationSessionCompletionHandler(url, self.sampleError); + [self verifyAuthenticationPropertiesReset]; +} + +- (void)testInvokingAuthSessionCompletionHandlerFromHandlerWithoutUrlWithError +{ + FBSDKSuccessBlock handler = ^(BOOL success, NSError *_Nullable error) { + XCTAssertFalse(success); + XCTAssertEqualObjects(error, self.sampleError); + }; + self.api.authenticationSession = AuthenticationSessionSpy.makeDefaultSpy; + self.api.authenticationSessionState = FBSDKAuthenticationSessionStarted; + + OCMReject( + [self.partialMock application:OCMArg.any + openURL:OCMArg.any + sourceApplication:OCMArg.any + annotation:OCMArg.any] + ); + [self.api setSessionCompletionHandlerFromHandler:handler]; + self.api.authenticationSessionCompletionHandler(nil, self.sampleError); + [self verifyAuthenticationPropertiesReset]; +} + +// MARK: - Helpers + +- (void)verifyAuthenticationPropertiesReset +{ + XCTAssertNil(self.api.authenticationSession); + XCTAssertNil(self.api.authenticationSessionCompletionHandler); + XCTAssertEqual(self.api.authenticationSessionState, FBSDKAuthenticationSessionNone); +} + +@end diff --git a/FBSDKCoreKit/FBSDKCoreKitTests/Internal/BridgeAPI/FBSDKBridgeAPI+Testing.h b/FBSDKCoreKit/FBSDKCoreKitTests/Internal/BridgeAPI/FBSDKBridgeAPI+Testing.h new file mode 100644 index 0000000000..9a7ffbc00e --- /dev/null +++ b/FBSDKCoreKit/FBSDKCoreKitTests/Internal/BridgeAPI/FBSDKBridgeAPI+Testing.h @@ -0,0 +1,98 @@ +// 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 "FBSDKBridgeAPI.h" +#import "FBSDKContainerViewController.h" + +NS_ASSUME_NONNULL_BEGIN + +typedef void (^FBSDKAuthenticationCompletionHandler)(NSURL *_Nullable callbackURL, NSError *_Nullable error); + +NS_SWIFT_NAME(AuthenticationSessionHandling) +@protocol FBSDKAuthenticationSession + +- (instancetype)initWithURL:(NSURL *)URL callbackURLScheme:(nullable NSString *)callbackURLScheme completionHandler:(FBSDKAuthenticationCompletionHandler)completionHandler; +- (BOOL)start; +- (void)cancel; +@optional +- (void)setPresentationContextProvider:(id)presentationContextProvider; + +@end + +/** + Specifies state of FBSDKAuthenticationSession (SFAuthenticationSession (iOS 11) and ASWebAuthenticationSession (iOS 12+)) + */ +typedef NS_ENUM(NSUInteger, FBSDKAuthenticationSession) { + /** There is no active authentication session*/ + FBSDKAuthenticationSessionNone, + /** The authentication session has started*/ + FBSDKAuthenticationSessionStarted, + /** System dialog ("app wants to use facebook.com to sign in") to access facebook.com was presented to the user*/ + FBSDKAuthenticationSessionShowAlert, + /** Web browser with log in to authentication was presented to the user*/ + FBSDKAuthenticationSessionShowWebBrowser, + /** Authentication session was canceled by system. It happens when app goes to background while alert requesting access to facebook.com is presented*/ + FBSDKAuthenticationSessionCanceledBySystem, +}; + +@interface FBSDKBridgeAPI (Testing) + +- (id)authenticationSession; +- (FBSDKAuthenticationSession)authenticationSessionState; +- (FBSDKAuthenticationCompletionHandler)authenticationSessionCompletionHandler; +- (BOOL)expectingBackground; +- (id)pendingUrlOpen; +- (UIViewController *)safariViewController; +- (BOOL)isDismissingSafariViewController; +- (NSObject *)pendingRequest; +- (FBSDKBridgeAPIResponseBlock)pendingRequestCompletionBlock; + +- (void)applicationWillResignActive:(UIApplication *)application; +- (void)applicationDidBecomeActive:(UIApplication *)application; +- (void)applicationDidEnterBackground:(UIApplication *)application; +- (BOOL) application:(UIApplication *)application + didFinishLaunchingWithOptions:(NSDictionary *)launchOptions; + +- (BOOL)application:(UIApplication *)application + openURL:(NSURL *)url + sourceApplication:(nullable NSString *)sourceApplication + annotation:(nullable id)annotation; + +- (void)setAuthenticationSession:(id)session; +- (void)setAuthenticationSessionState:(FBSDKAuthenticationSession)state; +- (void)setAuthenticationSessionCompletionHandler:(nullable FBSDKAuthenticationCompletionHandler)handler; +- (void)setActive:(BOOL)isActive; +- (void)setExpectingBackground:(BOOL)isExpectingBackground; +- (void)setPendingUrlOpen:(id)opening; +- (void)setSafariViewController:(nullable UIViewController *)controller; +- (void)setIsDismissingSafariViewController:(BOOL)isDismissing; +- (void)setPendingRequest:(NSObject *)newValue; +- (void)setPendingRequestCompletionBlock:(FBSDKBridgeAPIResponseBlock)newValue; + +- (BOOL)_handleBridgeAPIResponseURL:(NSURL *)responseURL sourceApplication:(NSString *)sourceApplication; +- (FBSDKSuccessBlock)_bridgeAPIRequestCompletionBlockWithRequest:(NSObject *)request + completion:(FBSDKBridgeAPIResponseBlock)completionBlock; +- (void)_cancelBridgeRequest; + +- (void)safariViewControllerDidFinish:(UIViewController *)safariViewController; +- (void)viewControllerDidDisappear:(FBSDKContainerViewController *)viewController animated:(BOOL)animated; + +@end + +NS_ASSUME_NONNULL_END diff --git a/FBSDKCoreKit/FBSDKCoreKitTests/Internal/BridgeAPI/FBSDKBridgeAPIOpenBridgeRequestTests.m b/FBSDKCoreKit/FBSDKCoreKitTests/Internal/BridgeAPI/FBSDKBridgeAPIOpenBridgeRequestTests.m new file mode 100644 index 0000000000..b02619b9d4 --- /dev/null +++ b/FBSDKCoreKit/FBSDKCoreKitTests/Internal/BridgeAPI/FBSDKBridgeAPIOpenBridgeRequestTests.m @@ -0,0 +1,278 @@ +// 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 "FBSDKCoreKitTests-Swift.h" +#import "FBSDKTestCase.h" + +@interface FBSDKBridgeAPIOpenBridgeRequestTests : FBSDKTestCase + +@property FBSDKBridgeAPI *api; +@property id partialMock; +@property (readonly) NSURL *sampleUrl; + +@end + +@implementation FBSDKBridgeAPIOpenBridgeRequestTests + +- (void)setUp +{ + [super setUp]; + + _api = [FBSDKBridgeAPI new]; + _partialMock = OCMPartialMock(self.api); + + OCMStub( + [_partialMock _bridgeAPIRequestCompletionBlockWithRequest:OCMArg.any + completion:OCMArg.any] + ); + OCMStub( + [_partialMock openURLWithSafariViewController:OCMArg.any + sender:OCMArg.any + fromViewController:OCMArg.any + handler:OCMArg.any] + ); + OCMStub([_partialMock openURL:OCMArg.any sender:OCMArg.any handler:OCMArg.any]); +} + +- (void)tearDown +{ + _api = nil; + + [_partialMock stopMocking]; + _partialMock = nil; + + [super tearDown]; +} + +// MARK: - Url Opening + +- (void)testOpeningBridgeRequestWithRequestUrlUsingSafariVcWithFromVc +{ + ViewControllerSpy *spy = [ViewControllerSpy makeDefaultSpy]; + FakeBridgeApiRequest *request = [FakeBridgeApiRequest requestWithURL:self.sampleUrl]; + [self.api openBridgeAPIRequest:request + useSafariViewController:YES + fromViewController:spy + completionBlock:self.uninvokedCompletionHandler]; + + XCTAssertEqualObjects(self.api.pendingRequest, request); + XCTAssertEqualObjects(self.api.pendingRequestCompletionBlock, self.uninvokedCompletionHandler); + OCMVerify( + [_partialMock _bridgeAPIRequestCompletionBlockWithRequest:request + completion:self.uninvokedCompletionHandler] + ); + OCMVerify( + [_partialMock openURLWithSafariViewController:self.sampleUrl + sender:nil + fromViewController:spy + handler:OCMArg.any] + ); +} + +- (void)testOpeningBridgeRequestWithRequestUrlUsingSafariVcWithoutFromVc +{ + FakeBridgeApiRequest *request = [FakeBridgeApiRequest requestWithURL:self.sampleUrl]; + [self.api openBridgeAPIRequest:request + useSafariViewController:YES + fromViewController:nil + completionBlock:self.uninvokedCompletionHandler]; + + XCTAssertEqualObjects(self.api.pendingRequest, request); + XCTAssertEqualObjects(self.api.pendingRequestCompletionBlock, self.uninvokedCompletionHandler); + OCMVerify( + [_partialMock _bridgeAPIRequestCompletionBlockWithRequest:request + completion:self.uninvokedCompletionHandler] + ); + OCMVerify( + [_partialMock openURLWithSafariViewController:self.sampleUrl + sender:nil + fromViewController:nil + handler:OCMArg.any] + ); +} + +- (void)testOpeningBridgeRequestWithRequestUrlNotUsingSafariVcWithFromVc +{ + ViewControllerSpy *spy = [ViewControllerSpy makeDefaultSpy]; + FakeBridgeApiRequest *request = [FakeBridgeApiRequest requestWithURL:self.sampleUrl]; + [self.api openBridgeAPIRequest:request + useSafariViewController:NO + fromViewController:spy + completionBlock:self.uninvokedCompletionHandler]; + + XCTAssertEqualObjects(self.api.pendingRequest, request); + XCTAssertEqualObjects(self.api.pendingRequestCompletionBlock, self.uninvokedCompletionHandler); + OCMVerify( + [_partialMock _bridgeAPIRequestCompletionBlockWithRequest:request + completion:self.uninvokedCompletionHandler] + ); + OCMVerify([_partialMock openURL:self.sampleUrl sender:nil handler:OCMArg.any]); +} + +- (void)testOpeningBridgeRequestWithRequestUrlNotUsingSafariVcWithoutFromVc +{ + FakeBridgeApiRequest *request = [FakeBridgeApiRequest requestWithURL:self.sampleUrl]; + [self.api openBridgeAPIRequest:request + useSafariViewController:NO + fromViewController:nil + completionBlock:self.uninvokedCompletionHandler]; + + XCTAssertEqualObjects(self.api.pendingRequest, request); + XCTAssertEqualObjects(self.api.pendingRequestCompletionBlock, self.uninvokedCompletionHandler); + OCMVerify( + [_partialMock _bridgeAPIRequestCompletionBlockWithRequest:request + completion:self.uninvokedCompletionHandler] + ); + OCMVerify([_partialMock openURL:self.sampleUrl sender:nil handler:OCMArg.any]); +} + +- (void)testOpeningBridgeRequestWithoutRequestUrlUsingSafariVcWithFromVc +{ + ViewControllerSpy *spy = [ViewControllerSpy makeDefaultSpy]; + FakeBridgeApiRequest *request = [FakeBridgeApiRequest requestWithURL:nil]; + FBSDKBridgeAPIResponseBlock completionHandler = ^(FBSDKBridgeAPIResponse *_Nonnull response) { + XCTAssertEqualObjects( + response.request, + request, + "Should call the completion with a response that includes the original request" + ); + XCTAssertTrue( + [response.error isKindOfClass:FakeBridgeApiRequestError.class], + "Should call the completion with an error if the request cannot provide a url" + ); + }; + [self rejectApiOpeningBridgeRequest]; + [self.api openBridgeAPIRequest:request + useSafariViewController:YES + fromViewController:spy + completionBlock:completionHandler]; + [self assertPendingPropertiesNotSet]; +} + +- (void)testOpeningBridgeRequestWithoutRequestUrlUsingSafariVcWithoutFromVc +{ + FakeBridgeApiRequest *request = [FakeBridgeApiRequest requestWithURL:nil]; + FBSDKBridgeAPIResponseBlock completionHandler = ^(FBSDKBridgeAPIResponse *_Nonnull response) { + XCTAssertEqualObjects( + response.request, + request, + "Should call the completion with a response that includes the original request" + ); + XCTAssertTrue( + [response.error isKindOfClass:FakeBridgeApiRequestError.class], + "Should call the completion with an error if the request cannot provide a url" + ); + }; + [self rejectApiOpeningBridgeRequest]; + [self.api openBridgeAPIRequest:request + useSafariViewController:YES + fromViewController:nil + completionBlock:completionHandler]; + [self assertPendingPropertiesNotSet]; +} + +- (void)testOpeningBridgeRequestWithoutRequestUrlNotUsingSafariVcWithFromVc +{ + ViewControllerSpy *spy = [ViewControllerSpy makeDefaultSpy]; + FakeBridgeApiRequest *request = [FakeBridgeApiRequest requestWithURL:nil]; + FBSDKBridgeAPIResponseBlock completionHandler = ^(FBSDKBridgeAPIResponse *_Nonnull response) { + XCTAssertEqualObjects( + response.request, + request, + "Should call the completion with a response that includes the original request" + ); + XCTAssertTrue( + [response.error isKindOfClass:FakeBridgeApiRequestError.class], + "Should call the completion with an error if the request cannot provide a url" + ); + }; + [self rejectApiOpeningBridgeRequest]; + [self.api openBridgeAPIRequest:request + useSafariViewController:NO + fromViewController:spy + completionBlock:completionHandler]; + [self assertPendingPropertiesNotSet]; +} + +- (void)testOpeningBridgeRequestWithoutRequestUrlNotUsingSafariVcWithoutFromVc +{ + FakeBridgeApiRequest *request = [FakeBridgeApiRequest requestWithURL:nil]; + FBSDKBridgeAPIResponseBlock completionHandler = ^(FBSDKBridgeAPIResponse *_Nonnull response) { + XCTAssertEqualObjects( + response.request, + request, + "Should call the completion with a response that includes the original request" + ); + XCTAssertTrue( + [response.error isKindOfClass:FakeBridgeApiRequestError.class], + "Should call the completion with an error if the request cannot provide a url" + ); + }; + [self rejectApiOpeningBridgeRequest]; + [self.api openBridgeAPIRequest:request + useSafariViewController:NO + fromViewController:nil + completionBlock:completionHandler]; + [self assertPendingPropertiesNotSet]; +} + +// MARK: - Helpers + +- (void)rejectApiOpeningBridgeRequest +{ + OCMReject([_partialMock _bridgeAPIRequestCompletionBlockWithRequest:OCMArg.any completion:OCMArg.any]); + OCMReject([_partialMock openURL:OCMArg.any sender:OCMArg.any handler:OCMArg.any]); + OCMReject( + [_partialMock openURLWithSafariViewController:OCMArg.any + sender:OCMArg.any + fromViewController:OCMArg.any + handler:OCMArg.any] + ); +} + +- (void)assertPendingPropertiesNotSet +{ + XCTAssertNil( + self.api.pendingRequest, + "Should not set a pending request if the bridge request does not have a request url" + ); + XCTAssertNil( + self.api.pendingRequestCompletionBlock, + "Should not set a pending request completion block if the bridge request does not have a request url" + ); +} + +- (FBSDKBridgeAPIResponseBlock)uninvokedCompletionHandler +{ + return ^(FBSDKBridgeAPIResponse *_Nonnull response) { + XCTFail("Should not invoke the completion handler"); + }; +} + +- (NSURL *)sampleUrl +{ + return [NSURL URLWithString:@"http://example.com"]; +} + +@end diff --git a/FBSDKCoreKit/FBSDKCoreKitTests/Internal/BridgeAPI/FBSDKBridgeAPIOpenUrlWithSafariTests.m b/FBSDKCoreKit/FBSDKCoreKitTests/Internal/BridgeAPI/FBSDKBridgeAPIOpenUrlWithSafariTests.m new file mode 100644 index 0000000000..ed9a993204 --- /dev/null +++ b/FBSDKCoreKit/FBSDKCoreKitTests/Internal/BridgeAPI/FBSDKBridgeAPIOpenUrlWithSafariTests.m @@ -0,0 +1,274 @@ +// 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 "FBSDKCoreKitTests-Swift.h" +#import "FBSDKTestCase.h" +#import "FakeLoginManager.h" + +@interface FBSDKBridgeAPI (Testing) +- (void)_openURLWithSafariViewController:(NSURL *)url + sender:(id)sender + fromViewController:(UIViewController *)fromViewController + handler:(FBSDKSuccessBlock)handler + dylibResolver:(id)dylibResolver; +@end + +@interface FBSDKBridgeAPIOpenUrlWithSafariTests : FBSDKTestCase + +@property (nonatomic) FBSDKBridgeAPI *api; +@property (nonatomic) id partialMock; +@property (nonatomic, readonly) NSURL *sampleUrl; +@property (nonatomic) FBSDKLoginManager *urlOpener; + +@end + +@implementation FBSDKBridgeAPIOpenUrlWithSafariTests + +- (void)setUp +{ + [super setUp]; + + [FBSDKLoginManager resetTestEvidence]; + _api = [FBSDKBridgeAPI new]; + _partialMock = OCMPartialMock(self.api); + _urlOpener = [FBSDKLoginManager new]; + + OCMStub([_partialMock setSessionCompletionHandlerFromHandler:OCMArg.any]); + OCMStub([_partialMock openURLWithAuthenticationSession:OCMArg.any]); + OCMStub([_partialMock openURL:OCMArg.any sender:OCMArg.any handler:OCMArg.any]); +} + +- (void)tearDown +{ + _api = nil; + _urlOpener = nil; + + [_partialMock stopMocking]; + _partialMock = nil; + + [super tearDown]; +} + +// MARK: - Url Opening + +- (void)testWithNonHttpUrlScheme +{ + NSURL *url = [NSURL URLWithString:@"file://example.com"]; + self.api.expectingBackground = YES; // So we can check that it's unchanged + + [self.api openURLWithSafariViewController:url + sender:nil + fromViewController:nil + handler:self.uninvokedSuccessBlock]; + OCMVerify([_partialMock openURL:url sender:nil handler:self.uninvokedSuccessBlock]); + XCTAssertTrue(self.api.expectingBackground, "Should not modify whether the background is expected to change"); + XCTAssertNil(self.api.pendingUrlOpen, "Should not set a pending url opener"); +} + +- (void)testWithAuthenticationURL +{ + self.urlOpener.stubbedIsAuthenticationUrl = YES; + self.api.expectingBackground = YES; + + OCMReject([_partialMock openURL:OCMArg.any sender:OCMArg.any handler:OCMArg.any]); + + [self.api openURLWithSafariViewController:self.sampleUrl + sender:self.urlOpener + fromViewController:nil + handler:self.uninvokedSuccessBlock]; + + OCMVerify([_partialMock setSessionCompletionHandlerFromHandler:self.uninvokedSuccessBlock]); + OCMVerify([_partialMock openURLWithAuthenticationSession:self.sampleUrl]); + [self assertExpectingBackgroundAndPendingUrlOpener]; +} + +- (void)testWithNonAuthenticationURL +{ + self.urlOpener.stubbedIsAuthenticationUrl = NO; + self.api.expectingBackground = YES; + + OCMReject([_partialMock openURL:OCMArg.any sender:OCMArg.any handler:OCMArg.any]); + OCMReject([_partialMock setSessionCompletionHandlerFromHandler:OCMArg.any]); + OCMReject([_partialMock openURLWithAuthenticationSession:OCMArg.any]); + + [self.api openURLWithSafariViewController:self.sampleUrl + sender:self.urlOpener + fromViewController:nil + handler:self.uninvokedSuccessBlock]; + + [self assertExpectingBackgroundAndPendingUrlOpener]; +} + +- (void)testWithoutSafariVcAvailable +{ + FakeDylibResolver *resolver = [FakeDylibResolver new]; + self.urlOpener.stubbedIsAuthenticationUrl = NO; + self.api.expectingBackground = YES; + + OCMReject([_partialMock setSessionCompletionHandlerFromHandler:OCMArg.any]); + OCMReject([_partialMock openURLWithAuthenticationSession:OCMArg.any]); + + [self.api _openURLWithSafariViewController:self.sampleUrl + sender:self.urlOpener + fromViewController:nil + handler:self.uninvokedSuccessBlock + dylibResolver:resolver]; + + OCMVerify( + [_partialMock openURL:self.sampleUrl + sender:self.urlOpener + handler:self.uninvokedSuccessBlock] + ); + [self assertExpectingBackgroundAndPendingUrlOpener]; +} + +- (void)testWithoutFromViewController +{ + self.urlOpener.stubbedIsAuthenticationUrl = NO; + self.api.expectingBackground = YES; + + OCMReject([_partialMock setSessionCompletionHandlerFromHandler:OCMArg.any]); + OCMReject([_partialMock openURLWithAuthenticationSession:OCMArg.any]); + OCMReject([_partialMock openURL:OCMArg.any sender:OCMArg.any handler:OCMArg.any]); + + [self.api openURLWithSafariViewController:self.sampleUrl + sender:self.urlOpener + fromViewController:nil + handler:self.uninvokedSuccessBlock]; + + OCMVerify( + [self.loggerClassMock singleShotLogEntry:FBSDKLoggingBehaviorDeveloperErrors + logEntry:@"There are no valid ViewController to present SafariViewController with"] + ); + [self assertExpectingBackgroundAndPendingUrlOpener]; +} + +- (void)testWithFromViewControllerMissingTransitionCoordinator +{ + ViewControllerSpy *spy = ViewControllerSpy.makeDefaultSpy; + self.urlOpener.stubbedIsAuthenticationUrl = NO; + self.api.expectingBackground = YES; + FBSDKSuccessBlock handler = ^(BOOL success, NSError *_Nullable error) { + XCTAssertTrue(success, "Should call the handler with success"); + XCTAssertNil(error, "Should not call the handler with an error"); + }; + + OCMReject([_partialMock setSessionCompletionHandlerFromHandler:OCMArg.any]); + OCMReject([_partialMock openURLWithAuthenticationSession:OCMArg.any]); + OCMReject([_partialMock openURL:OCMArg.any sender:OCMArg.any handler:OCMArg.any]); + + [self.api openURLWithSafariViewController:self.sampleUrl + sender:self.urlOpener + fromViewController:spy + handler:handler]; + + SFSafariViewController *safariVc = (SFSafariViewController *)self.api.safariViewController; + + XCTAssertNotNil(safariVc, "Should create and set a safari view controller for display"); + XCTAssertEqual( + safariVc.modalPresentationStyle, + UIModalPresentationOverFullScreen, + "Should set the correct modal presentation style" + ); + XCTAssertEqualObjects( + safariVc.delegate, + self.api, + "Should set the safari view controller delegate to the bridge api" + ); + XCTAssertEqualObjects( + spy.capturedPresentViewController, + safariVc.parentViewController, + "Should present the view controller containing the safari view controller" + ); + XCTAssertTrue(spy.capturedPresentViewControllerAnimated, "Should animate presenting the safari view controller"); + XCTAssertNil(spy.capturedPresentViewControllerCompletion, "Should not pass a completion handler to the safari vc presentation"); + [self assertExpectingBackgroundAndPendingUrlOpener]; +} + +- (void)testWithFromViewControllerWithTransitionCoordinator +{ + ViewControllerSpy *spy = ViewControllerSpy.makeDefaultSpy; + spy.stubbedTransitionCoordinator = self.transitionCoordinatorMock; + self.urlOpener.stubbedIsAuthenticationUrl = NO; + self.api.expectingBackground = YES; + FBSDKSuccessBlock handler = ^(BOOL success, NSError *_Nullable error) { + XCTAssertTrue(success, "Should call the handler with success"); + XCTAssertNil(error, "Should not call the handler with an error"); + }; + + OCMReject([_partialMock setSessionCompletionHandlerFromHandler:OCMArg.any]); + OCMReject([_partialMock openURLWithAuthenticationSession:OCMArg.any]); + OCMReject([_partialMock openURL:OCMArg.any sender:OCMArg.any handler:OCMArg.any]); + + OCMStub([self.transitionCoordinatorMock animateAlongsideTransition:nil completion:[OCMArg invokeBlock]]); + + [self.api openURLWithSafariViewController:self.sampleUrl + sender:self.urlOpener + fromViewController:spy + handler:handler]; + + SFSafariViewController *safariVc = (SFSafariViewController *)self.api.safariViewController; + + XCTAssertNotNil(safariVc, "Should create and set a safari view controller for display"); + XCTAssertEqual( + safariVc.modalPresentationStyle, + UIModalPresentationOverFullScreen, + "Should set the correct modal presentation style" + ); + XCTAssertEqualObjects( + safariVc.delegate, + self.api, + "Should set the safari view controller delegate to the bridge api" + ); + XCTAssertEqualObjects( + spy.capturedPresentViewController, + safariVc.parentViewController, + "Should present the view controller containing the safari view controller" + ); + XCTAssertTrue(spy.capturedPresentViewControllerAnimated, "Should animate presenting the safari view controller"); + XCTAssertNil(spy.capturedPresentViewControllerCompletion, "Should not pass a completion handler to the safari vc presentation"); + [self assertExpectingBackgroundAndPendingUrlOpener]; +} + +// MARK: - Helpers + +- (void)assertExpectingBackgroundAndPendingUrlOpener +{ + XCTAssertFalse(self.api.expectingBackground, "Should set expecting background to false"); + XCTAssertEqualObjects(self.api.pendingUrlOpen, self.urlOpener, "Should set the pending url opener to the passed in sender"); +} + +- (NSURL *)sampleUrl +{ + return [NSURL URLWithString:@"http://example.com"]; +} + +- (FBSDKSuccessBlock)uninvokedSuccessBlock +{ + return ^(BOOL success, NSError *_Nullable error) { + XCTFail("Should not invoke the completion handler"); + }; +} + +@end diff --git a/FBSDKCoreKit/FBSDKCoreKitTests/Internal/BridgeAPI/FBSDKBridgeAPIRequestTests.swift b/FBSDKCoreKit/FBSDKCoreKitTests/Internal/BridgeAPI/FBSDKBridgeAPIRequestTests.swift new file mode 100644 index 0000000000..469ece8407 --- /dev/null +++ b/FBSDKCoreKit/FBSDKCoreKitTests/Internal/BridgeAPI/FBSDKBridgeAPIRequestTests.swift @@ -0,0 +1,36 @@ +// 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 XCTest + +class FBSDKBridgeAPIRequestTests: FBSDKTestCase { + + func testDefaultProtocolConformance() { + let request: Any = BridgeAPIRequest( + protocolType: .web, + scheme: "https", + methodName: nil, + methodVersion: nil, + parameters: [:], + userInfo: [:] + ) as Any + let conformedRequest = request as? FBSDKBridgeAPIRequestProtocol + + XCTAssertNotNil(conformedRequest, "BridgeAPIRequest should conform to the expected protocol") + } +} diff --git a/FBSDKCoreKit/FBSDKCoreKitTests/Internal/BridgeAPI/FBSDKBridgeAPITests.h b/FBSDKCoreKit/FBSDKCoreKitTests/Internal/BridgeAPI/FBSDKBridgeAPITests.h new file mode 100644 index 0000000000..3e1e136bd5 --- /dev/null +++ b/FBSDKCoreKit/FBSDKCoreKitTests/Internal/BridgeAPI/FBSDKBridgeAPITests.h @@ -0,0 +1,41 @@ +// 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 + +#import "FBSDKBridgeAPI+Testing.h" +#import "FBSDKCoreKitTests-Swift.h" +#import "FBSDKTestCase.h" +#import "FakeLoginManager.h" + +@interface FBSDKBridgeAPITests : FBSDKTestCase + +@property FBSDKBridgeAPI *api; +@property id partialMock; +@property (readonly) NSURL *sampleUrl; +@property (readonly) NSError *sampleError; + +extern NSString *const sampleSource; +extern NSString *const sampleAnnotation; + +@end diff --git a/FBSDKCoreKit/FBSDKCoreKitTests/Internal/BridgeAPI/FBSDKBridgeAPITests.m b/FBSDKCoreKit/FBSDKCoreKitTests/Internal/BridgeAPI/FBSDKBridgeAPITests.m new file mode 100644 index 0000000000..2701a5d5bc --- /dev/null +++ b/FBSDKCoreKit/FBSDKCoreKitTests/Internal/BridgeAPI/FBSDKBridgeAPITests.m @@ -0,0 +1,736 @@ +// 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 "FBSDKBridgeAPITests.h" + +@implementation FBSDKBridgeAPITests + +- (void)setUp +{ + [super setUp]; + + [FBSDKLoginManager resetTestEvidence]; + _api = [FBSDKBridgeAPI new]; + _partialMock = OCMPartialMock(_api); +} + +- (void)tearDown +{ + _api = nil; + [_partialMock stopMocking]; + _partialMock = nil; + [FBSDKLoginManager resetTestEvidence]; + + [super tearDown]; +} + +// MARK: - Lifecycle Methods + +// MARK: Will Resign Active + +- (void)testWillResignActiveWithoutAuthSessionWithoutAuthSessionState +{ + [self.api applicationWillResignActive:UIApplication.sharedApplication]; + + XCTAssertEqual( + self.api.authenticationSessionState, + FBSDKAuthenticationSessionNone, + "Should not modify the auth session state if there is no auth session" + ); +} + +- (void)testWillResignActiveWithAuthSessionWithoutAuthSessionState +{ + self.api.authenticationSession = AuthenticationSessionSpy.makeDefaultSpy; + + [self.api applicationWillResignActive:UIApplication.sharedApplication]; + + XCTAssertEqual( + self.api.authenticationSessionState, + FBSDKAuthenticationSessionNone, + "Should not modify the auth session state unless the current state is 'started'" + ); +} + +- (void)testWillResignActiveWithAuthSessionWithNonStartedAuthSessionState +{ + self.api.authenticationSession = AuthenticationSessionSpy.makeDefaultSpy; + + NSArray *states = @[ + @(FBSDKAuthenticationSessionNone), + @(FBSDKAuthenticationSessionShowAlert), + @(FBSDKAuthenticationSessionShowWebBrowser), + @(FBSDKAuthenticationSessionCanceledBySystem) + ]; + + for (NSNumber *state in states) { + self.api.authenticationSessionState = state.intValue; + [self.api applicationWillResignActive:UIApplication.sharedApplication]; + XCTAssertEqual( + self.api.authenticationSessionState, + state.intValue, + "Should not modify the auth session state unless the current state is 'started'" + ); + } +} + +- (void)testWillResignActiveWithAuthSessionWithStartedAuthSessionState +{ + self.api.authenticationSession = AuthenticationSessionSpy.makeDefaultSpy; + + self.api.authenticationSessionState = FBSDKAuthenticationSessionStarted; + [self.api applicationWillResignActive:UIApplication.sharedApplication]; + XCTAssertEqual( + self.api.authenticationSessionState, + FBSDKAuthenticationSessionShowAlert, + "Should change the auth state from 'started' to 'alert' before resigning activity" + ); +} + +// MARK: Did Become Active + +- (void)testUpdatingShowAlertStateForDidBecomeActiveWithoutAuthSession +{ + NSArray *states = @[ + @(FBSDKAuthenticationSessionNone), + @(FBSDKAuthenticationSessionStarted), + @(FBSDKAuthenticationSessionShowAlert), + @(FBSDKAuthenticationSessionShowWebBrowser), + @(FBSDKAuthenticationSessionCanceledBySystem) + ]; + + for (NSNumber *state in states) { + self.api.authenticationSessionState = state.intValue; + [self.api applicationDidBecomeActive:UIApplication.sharedApplication]; + XCTAssertEqual( + self.api.authenticationSessionState, + state.intValue, + "Should not modify the auth session state if there is no auth session" + ); + } +} + +- (void)testUpdatingShowAlertStateForDidBecomeActive +{ + AuthenticationSessionSpy *authSessionSpy = AuthenticationSessionSpy.makeDefaultSpy; + self.api.authenticationSession = authSessionSpy; + self.api.authenticationSessionState = FBSDKAuthenticationSessionShowAlert; + + [self.api applicationDidBecomeActive:UIApplication.sharedApplication]; + + XCTAssertEqual( + self.api.authenticationSessionState, + FBSDKAuthenticationSessionShowWebBrowser, + "Becoming active when the state is 'showAlert' should set the state to be 'showWebBrowser'" + ); + XCTAssertEqual(authSessionSpy.cancelCallCount, 0, "Becoming active when the state is 'showAlert' should not cancel the session"); + XCTAssertNotNil(self.api.authenticationSession, "Becoming active when the state is 'showAlert' should not destroy the session"); +} + +- (void)testUpdatingCancelledBySystemStateForDidBecomeActive +{ + AuthenticationSessionSpy *authSessionSpy = AuthenticationSessionSpy.makeDefaultSpy; + self.api.authenticationSession = authSessionSpy; + self.api.authenticationSessionState = FBSDKAuthenticationSessionCanceledBySystem; + + [self.api applicationDidBecomeActive:UIApplication.sharedApplication]; + + XCTAssertEqual( + self.api.authenticationSessionState, + FBSDKAuthenticationSessionCanceledBySystem, + "Becoming active when the state is 'canceledBySystem' should not change the state" + ); + XCTAssertNil( + self.api.authenticationSession, + "Becoming active when the state is 'canceledBySystem' should destroy the session" + ); + XCTAssertEqual(authSessionSpy.cancelCallCount, 1, "Becoming active when the state is 'canceledBySystem' should cancel the session"); +} + +- (void)testCompletingWithCancelledBySystemStateForDidBecomeActive +{ + AuthenticationSessionSpy *authSessionSpy = AuthenticationSessionSpy.makeDefaultSpy; + self.api.authenticationSession = authSessionSpy; + self.api.authenticationSessionState = FBSDKAuthenticationSessionCanceledBySystem; + self.api.authenticationSessionCompletionHandler = ^(NSURL *callbackURL, NSError *error) { + XCTAssertNil(callbackURL, "A completion triggered by becoming active in a canceled state should not have a callback URL"); + XCTAssertEqualObjects( + error.domain, + @"com.apple.AuthenticationServices.WebAuthenticationSession", + "A completion triggered by becoming active in a canceled state should include an error" + ); + }; + + [self.api applicationDidBecomeActive:UIApplication.sharedApplication]; +} + +// MARK: Did Enter Background + +- (void)testDidEnterBackgroundWithoutAuthSession +{ + self.api.active = YES; + self.api.expectingBackground = YES; + + NSArray *states = @[ + @(FBSDKAuthenticationSessionNone), + @(FBSDKAuthenticationSessionStarted), + @(FBSDKAuthenticationSessionShowAlert), + @(FBSDKAuthenticationSessionShowWebBrowser), + @(FBSDKAuthenticationSessionCanceledBySystem) + ]; + + for (NSNumber *state in states) { + self.api.authenticationSessionState = state.intValue; + [self.api applicationDidEnterBackground:UIApplication.sharedApplication]; + XCTAssertFalse( + self.api.active, + "Should mark a bridge api inactive when entering the background" + ); + XCTAssertFalse( + self.api.expectingBackground, + "Should mark a bridge api as not expecting backgrounding when entering the background" + ); + } +} + +- (void)testDidEnterBackgroundInShowAlertState +{ + self.api.authenticationSession = AuthenticationSessionSpy.makeDefaultSpy; + self.api.authenticationSessionState = FBSDKAuthenticationSessionShowAlert; + + [self.api applicationDidEnterBackground:UIApplication.sharedApplication]; + XCTAssertEqual( + self.api.authenticationSessionState, + FBSDKAuthenticationSessionCanceledBySystem, + "Should cancel the session when entering the background while showing an alert" + ); +} + +- (void)testDidEnterBackgroundInNonShowAlertState +{ + self.api.authenticationSession = AuthenticationSessionSpy.makeDefaultSpy; + + NSArray *states = @[ + @(FBSDKAuthenticationSessionNone), + @(FBSDKAuthenticationSessionStarted), + @(FBSDKAuthenticationSessionShowWebBrowser), + @(FBSDKAuthenticationSessionCanceledBySystem) + ]; + + for (NSNumber *state in states) { + self.api.authenticationSessionState = state.intValue; + [self.api applicationDidEnterBackground:UIApplication.sharedApplication]; + XCTAssertEqual( + self.api.authenticationSessionState, + state.intValue, + "Should only modify the auth session state on backgrounding if it is showing an alert" + ); + } +} + +// MARK: Did Finish Launching With Options + +- (void)testDidFinishLaunchingWithoutLaunchedUrlWithoutSourceApplication +{ + XCTAssertFalse( + [self.api application:UIApplication.sharedApplication didFinishLaunchingWithOptions:@{}], + "Should not consider it a successful launch if there is no launch url or source application" + ); +} + +- (void)testDidFinishLaunchingWithoutLaunchedUrlWithSourceApplication +{ + NSDictionary *options = @{ UIApplicationLaunchOptionsSourceApplicationKey : @"com.example" }; + XCTAssertFalse( + [self.api application:UIApplication.sharedApplication didFinishLaunchingWithOptions:options], + "Should not consider it a successful launch if there is no launch url" + ); +} + +- (void)testDidFinishLaunchingWithLaunchedUrlWithoutSourceApplication +{ + NSDictionary *options = @{ UIApplicationLaunchOptionsURLKey : self.sampleUrl }; + XCTAssertFalse( + [self.api application:UIApplication.sharedApplication didFinishLaunchingWithOptions:options], + "Should not consider it a successful launch if there is no source application" + ); +} + +- (void)testDidFinishLaunchingWithLaunchedUrlWithSourceApplication +{ + NSDictionary *options = @{ + UIApplicationLaunchOptionsURLKey : self.sampleUrl, + UIApplicationLaunchOptionsSourceApplicationKey : sampleSource, + UIApplicationLaunchOptionsAnnotationKey : sampleAnnotation + }; + + FBSDKLoginManager.stubbedOpenUrlSuccess = YES; + + XCTAssertTrue( + [self.api application:UIApplication.sharedApplication didFinishLaunchingWithOptions:options], + "Should return the success value determined by the login manager's open url method" + ); + + XCTAssertEqualObjects(FBSDKLoginManager.capturedOpenUrl, self.sampleUrl, "Should pass the launch url to the login manager"); + XCTAssertEqualObjects(FBSDKLoginManager.capturedSourceApplication, sampleSource, "Should pass the source application to the login manager"); + XCTAssertEqualObjects(FBSDKLoginManager.capturedAnnotation, sampleAnnotation, "Should pass the annotation to the login manager"); +} + +// MARK: - Open Url + +- (void)testOpenUrlWithMissingSender +{ + [self.api openURL:self.sampleUrl + sender:nil + handler:^(BOOL _success, NSError *_Nullable error) {}]; + + XCTAssertTrue(self.api.expectingBackground, "Should set expecting background to true when opening a URL"); + XCTAssertNil(self.api.pendingUrlOpen, "Should not set the pending url opener if there is no sender"); +} + +- (void)testOpenUrlWithSender +{ + FBSDKLoginManager *urlOpener = [FBSDKLoginManager new]; + [self.api openURL:self.sampleUrl + sender:urlOpener + handler:^(BOOL _success, NSError *_Nullable error) {}]; + + XCTAssertTrue(self.api.expectingBackground, "Should set expecting background to true when opening a URL"); + XCTAssertEqual(self.api.pendingUrlOpen, urlOpener, "Should set the pending url opener to the sender"); +} + +- (void)testOpenUrlWithVersionBelow10WhenApplicationOpens +{ + XCTestExpectation *expectation = [self expectationWithDescription:self.name]; + + BOOL applicationOpensSuccessfully = YES; + [self stubIsOperatingSystemVersionAtLeast:iOS10Version with:NO]; + [self stubOpenURLWith:applicationOpensSuccessfully]; + + [self.api openURL:self.sampleUrl sender:nil handler:^(BOOL _success, NSError *_Nullable error) { + XCTAssertEqual( + _success, + applicationOpensSuccessfully, + "Should call the completion handler with the expected value" + ); + XCTAssertNil(error, "Should not call the completion handler with an error"); + [expectation fulfill]; + }]; + + [self waitForExpectationsWithTimeout:1 handler:nil]; +} + +- (void)testOpenUrlWithVersionBelow10WhenApplicationDoesNotOpen +{ + XCTestExpectation *expectation = [self expectationWithDescription:self.name]; + + BOOL applicationOpensSuccessfully = NO; + [self stubIsOperatingSystemVersionAtLeast:iOS10Version with:NO]; + [self stubOpenURLWith:applicationOpensSuccessfully]; + + [self.api openURL:self.sampleUrl sender:nil handler:^(BOOL _success, NSError *_Nullable error) { + XCTAssertEqual( + _success, + applicationOpensSuccessfully, + "Should call the completion handler with the expected value" + ); + XCTAssertNil(error, "Should not call the completion handler with an error"); + [expectation fulfill]; + }]; + + [self waitForExpectationsWithTimeout:1 handler:nil]; +} + +- (void)testOpenUrlWhenApplicationOpens +{ + XCTestExpectation *expectation = [self expectationWithDescription:self.name]; + + BOOL applicationOpensSuccessfully = YES; + [self stubIsOperatingSystemVersionAtLeast:iOS10Version with:YES]; + [self stubOpenUrlOptionsCompletionHandlerWithPerformCompletion:YES + completionSuccess:applicationOpensSuccessfully]; + + [self.api openURL:self.sampleUrl sender:nil handler:^(BOOL _success, NSError *_Nullable error) { + XCTAssertEqual( + _success, + applicationOpensSuccessfully, + "Should call the completion handler with the expected value" + ); + XCTAssertNil(error, "Should not call the completion handler with an error"); + [expectation fulfill]; + }]; + + [self waitForExpectationsWithTimeout:1 handler:nil]; +} + +- (void)testOpenUrlWhenApplicationDoesNotOpen +{ + XCTestExpectation *expectation = [self expectationWithDescription:self.name]; + + BOOL applicationOpensSuccessfully = NO; + [self stubIsOperatingSystemVersionAtLeast:iOS10Version with:YES]; + [self stubOpenUrlOptionsCompletionHandlerWithPerformCompletion:YES + completionSuccess:applicationOpensSuccessfully]; + + [self.api openURL:self.sampleUrl sender:nil handler:^(BOOL _success, NSError *_Nullable error) { + XCTAssertEqual( + _success, + applicationOpensSuccessfully, + "Should call the completion handler with the expected value" + ); + XCTAssertNil(error, "Should not call the completion handler with an error"); + [expectation fulfill]; + }]; + + [self waitForExpectationsWithTimeout:1 handler:nil]; +} + +// MARK: - Request completion block + +- (void)testRequestCompletionBlockCalledWithSuccess +{ + FakeBridgeApiRequest *request = [FakeBridgeApiRequest requestWithURL:self.sampleUrl]; + FBSDKBridgeAPIResponseBlock responseBlock = ^void (FBSDKBridgeAPIResponse *response) { + XCTFail("Should not call the response block when the request completion is called with success"); + }; + self.api.pendingRequest = request; + self.api.pendingRequestCompletionBlock = ^(FBSDKBridgeAPIResponse *response) {}; + + FBSDKSuccessBlock completion = [self.api _bridgeAPIRequestCompletionBlockWithRequest:request + completion:responseBlock]; + // With Error + completion(true, self.sampleError); + [self assertPendingPropertiesNotCleared]; + + // Without Error + completion(true, nil); + [self assertPendingPropertiesNotCleared]; +} + +- (void)testRequestCompletionBlockWithNonHttpRequestCalledWithoutSuccess +{ + FakeBridgeApiRequest *request = [FakeBridgeApiRequest requestWithURL:self.sampleUrl scheme:@"file"]; + FBSDKBridgeAPIResponseBlock responseBlock = ^void (FBSDKBridgeAPIResponse *response) { + XCTAssertEqualObjects(response.request, request, "The response should contain the original request"); + XCTAssertEqual( + response.error.code, + FBSDKErrorAppVersionUnsupported, + "The response should contain the expected error code" + ); + XCTAssertEqualObjects( + response.error.userInfo[FBSDKErrorDeveloperMessageKey], + @"the app switch failed because the destination app is out of date", + "The response should contain the expected error message" + ); + }; + self.api.pendingRequest = request; + self.api.pendingRequestCompletionBlock = ^(FBSDKBridgeAPIResponse *response) {}; + + FBSDKSuccessBlock completion = [self.api _bridgeAPIRequestCompletionBlockWithRequest:request + completion:responseBlock]; + // With Error + completion(false, self.sampleError); + [self assertPendingPropertiesCleared]; + + // Without Error + completion(false, nil); + [self assertPendingPropertiesCleared]; +} + +- (void)testRequestCompletionBlockWithHttpRequestCalledWithoutSuccess +{ + FakeBridgeApiRequest *request = [FakeBridgeApiRequest requestWithURL:self.sampleUrl scheme:@"https"]; + FBSDKBridgeAPIResponseBlock responseBlock = ^void (FBSDKBridgeAPIResponse *response) { + XCTAssertEqualObjects(response.request, request, "The response should contain the original request"); + XCTAssertEqual( + response.error.code, + FBSDKErrorBrowserUnavailable, + "The response should contain the expected error code" + ); + XCTAssertEqualObjects( + response.error.userInfo[FBSDKErrorDeveloperMessageKey], + @"the app switch failed because the browser is unavailable", + "The response should contain the expected error message" + ); + }; + self.api.pendingRequest = request; + self.api.pendingRequestCompletionBlock = ^(FBSDKBridgeAPIResponse *response) {}; + + FBSDKSuccessBlock completion = [self.api _bridgeAPIRequestCompletionBlockWithRequest:request + completion:responseBlock]; + // With Error + completion(false, self.sampleError); + [self assertPendingPropertiesCleared]; + + // Without Error + completion(false, nil); + [self assertPendingPropertiesCleared]; +} + +// MARK: - Safari View Controller Delegate Methods + +- (void)testSafariVcDidFinishWithPendingUrlOpener +{ + FBSDKLoginManager *urlOpener = [FBSDKLoginManager new]; + self.api.pendingUrlOpen = urlOpener; + self.api.safariViewController = ViewControllerSpy.makeDefaultSpy; + + // Funny enough there's no check that the safari view controller from the delegate + // is the same instance stored in the safariViewController property + [self.api safariViewControllerDidFinish:self.api.safariViewController]; + + XCTAssertNil(self.api.pendingUrlOpen, "Should remove the reference to the pending url opener"); + XCTAssertNil( + self.api.safariViewController, + "Should remove the reference to the safari view controller when the delegate method is called" + ); + + OCMVerify([_partialMock _cancelBridgeRequest]); + XCTAssertTrue(urlOpener.openUrlWasCalled, "Should ask the opener to open a url (even though there is not one provided"); + XCTAssertNil(FBSDKLoginManager.capturedOpenUrl, "The url opener should be called with nil arguments"); + XCTAssertNil(FBSDKLoginManager.capturedSourceApplication, "The url opener should be called with nil arguments"); + XCTAssertNil(FBSDKLoginManager.capturedAnnotation, "The url opener should be called with nil arguments"); +} + +- (void)testSafariVcDidFinishWithoutPendingUrlOpener +{ + self.api.safariViewController = ViewControllerSpy.makeDefaultSpy; + + // Funny enough there's no check that the safari view controller from the delegate + // is the same instance stored in the safariViewController property + [self.api safariViewControllerDidFinish:self.api.safariViewController]; + + XCTAssertNil(self.api.pendingUrlOpen, "Should remove the reference to the pending url opener"); + XCTAssertNil( + self.api.safariViewController, + "Should remove the reference to the safari view controller when the delegate method is called" + ); + + OCMVerify([_partialMock _cancelBridgeRequest]); + XCTAssertNil(FBSDKLoginManager.capturedOpenUrl, "The url opener should not be called"); + XCTAssertNil(FBSDKLoginManager.capturedSourceApplication, "The url opener should not be called"); + XCTAssertNil(FBSDKLoginManager.capturedAnnotation, "The url opener should not be called"); +} + +// MARK: - ContainerViewController Delegate Methods + +- (void)testViewControllerDidDisappearWithSafariViewController +{ + UIViewController *viewControllerSpy = ViewControllerSpy.makeDefaultSpy; + self.api.safariViewController = viewControllerSpy; + FBSDKContainerViewController *container = [FBSDKContainerViewController new]; + + [self.api viewControllerDidDisappear:container animated:NO]; + + OCMVerify([self.loggerClassMock singleShotLogEntry:FBSDKLoggingBehaviorDeveloperErrors logEntry:@"**ERROR**:\n The SFSafariViewController's parent view controller was dismissed.\nThis can happen if you are triggering login from a UIAlertController. Instead, make sure your top most view controller will not be prematurely dismissed."]); + OCMVerify([_partialMock safariViewControllerDidFinish:viewControllerSpy]); +} + +- (void)testViewControllerDidDisappearWithoutSafariViewController +{ + FBSDKContainerViewController *container = [FBSDKContainerViewController new]; + + OCMReject([self.loggerClassMock singleShotLogEntry:FBSDKLoggingBehaviorDeveloperErrors logEntry:OCMArg.any]); + OCMReject([_partialMock safariViewControllerDidFinish:OCMArg.any]); + + [self.api viewControllerDidDisappear:container animated:NO]; +} + +// MARK: - Bridge Response Url Handling + +- (void)testHandlingBridgeResponseWithInvalidScheme +{ + [self stubBridgeApiResponseWithUrlCreation]; + [self stubAppUrlSchemeWith:@"foo"]; + + BOOL result = [self.api _handleBridgeAPIResponseURL:self.sampleUrl sourceApplication:@""]; + + XCTAssertFalse(result, "Should not successfully handle bridge api response url with an invalid url scheme"); + [self assertPendingPropertiesCleared]; +} + +- (void)testHandlingBridgeResponseWithInvalidHost +{ + [self stubBridgeApiResponseWithUrlCreation]; + [self stubAppUrlSchemeWith:self.sampleUrl.scheme]; + + BOOL result = [self.api _handleBridgeAPIResponseURL:self.sampleUrl sourceApplication:@""]; + + XCTAssertFalse(result, "Should not successfully handle bridge api response url with an invalid url host"); + [self assertPendingPropertiesCleared]; +} + +- (void)testHandlingBridgeResponseWithMissingRequest +{ + [self stubBridgeApiResponseWithUrlCreation]; + [self stubAppUrlSchemeWith:self.validBridgeResponseUrl.scheme]; + + BOOL result = [self.api _handleBridgeAPIResponseURL:self.validBridgeResponseUrl sourceApplication:@""]; + + XCTAssertFalse(result, "Should not successfully handle bridge api response url with a missing request"); + [self assertPendingPropertiesCleared]; +} + +- (void)testHandlingBridgeResponseWithMissingCompletionBlock +{ + [self stubBridgeApiResponseWithUrlCreation]; + [self stubAppUrlSchemeWith:self.validBridgeResponseUrl.scheme]; + self.api.pendingRequest = [FakeBridgeApiRequest requestWithURL:self.sampleUrl]; + + BOOL result = [self.api _handleBridgeAPIResponseURL:self.validBridgeResponseUrl sourceApplication:@""]; + + XCTAssertTrue(result, "Should successfully handle bridge api response url with a missing completion block"); + [self assertPendingPropertiesCleared]; +} + +- (void)testHandlingBridgeResponseWithBridgeResponse +{ + FBSDKBridgeAPIResponse *response = (FBSDKBridgeAPIResponse *)NSObject.new; + OCMStub( + ClassMethod( + [self.bridgeApiResponseClassMock bridgeAPIResponseWithRequest:OCMArg.any + responseURL:OCMArg.any + sourceApplication:OCMArg.any + error:[OCMArg setTo:nil]] + ) + ) + .andReturn(response); + + [self stubAppUrlSchemeWith:self.validBridgeResponseUrl.scheme]; + self.api.pendingRequest = [FakeBridgeApiRequest requestWithURL:self.sampleUrl]; + self.api.pendingRequestCompletionBlock = ^(FBSDKBridgeAPIResponse *_response) { + XCTAssertEqualObjects(_response, response, "Should invoke the completion with the expected bridge api response"); + }; + BOOL result = [self.api _handleBridgeAPIResponseURL:self.validBridgeResponseUrl sourceApplication:@""]; + + XCTAssertTrue(result, "Should successfully handle creation of a bridge api response"); + [self assertPendingPropertiesCleared]; +} + +- (void)testHandlingBridgeResponseWithBridgeError +{ + FBSDKBridgeAPIResponse *response = (FBSDKBridgeAPIResponse *)NSObject.new; + + // First attempt to create response populates error and returns nil response. + OCMStub( + ClassMethod( + [self.bridgeApiResponseClassMock bridgeAPIResponseWithRequest:OCMArg.any + responseURL:OCMArg.any + sourceApplication:OCMArg.any + error:[OCMArg setTo:self.sampleError]] + ) + ); + + // Second (different) attempt to create response takes error and returns response + OCMStub(ClassMethod([self.bridgeApiResponseClassMock bridgeAPIResponseWithRequest:OCMArg.any error:self.sampleError])) + .andReturn(response); + + [self stubAppUrlSchemeWith:self.validBridgeResponseUrl.scheme]; + self.api.pendingRequest = [FakeBridgeApiRequest requestWithURL:self.sampleUrl]; + self.api.pendingRequestCompletionBlock = ^(FBSDKBridgeAPIResponse *_response) { + XCTAssertEqualObjects(_response, response, "Should invoke the completion with the expected bridge api response"); + }; + BOOL result = [self.api _handleBridgeAPIResponseURL:self.validBridgeResponseUrl sourceApplication:@""]; + + XCTAssertTrue(result, "Should retry creation of a bridge api response if the first attempt has an error"); + [self assertPendingPropertiesCleared]; +} + +- (void)testHandlingBridgeResponseWithMissingResponseMissingError +{ + // First attempt to create response returns nil + [self stubBridgeApiResponseWithUrlCreation]; + + // Second (different) attempt to create response returns nil + OCMStub(ClassMethod([self.bridgeApiResponseClassMock bridgeAPIResponseWithRequest:OCMArg.any error:OCMArg.any])); + + [self stubAppUrlSchemeWith:self.validBridgeResponseUrl.scheme]; + self.api.pendingRequest = [FakeBridgeApiRequest requestWithURL:self.sampleUrl]; + self.api.pendingRequestCompletionBlock = ^(FBSDKBridgeAPIResponse *response) { + XCTFail("Should not invoke pending completion handler"); + }; + BOOL result = [self.api _handleBridgeAPIResponseURL:self.validBridgeResponseUrl sourceApplication:@""]; + + XCTAssertFalse(result, "Should return false when a bridge response cannot be created"); + [self assertPendingPropertiesCleared]; +} + +// MARK: - Helpers + +- (void)assertPendingPropertiesCleared +{ + XCTAssertNil( + self.api.pendingRequest, + "Should clear the pending request" + ); + XCTAssertNil( + self.api.pendingRequestCompletionBlock, + "Should clear the pending request completion block" + ); +} + +- (void)assertPendingPropertiesNotCleared +{ + XCTAssertNotNil( + self.api.pendingRequest, + "Should not clear the pending request" + ); + XCTAssertNotNil( + self.api.pendingRequestCompletionBlock, + "Should not clear the pending request completion block" + ); +} + +/// Stubs `FBSDKBridgeAPI`'s `bridgeAPIResponseWithRequest:responseURL:sourceApplication:error:` to keep it from being called +- (void)stubBridgeApiResponseWithUrlCreation +{ + OCMStub( + ClassMethod( + [self.bridgeApiResponseClassMock bridgeAPIResponseWithRequest:OCMArg.any + responseURL:OCMArg.any + sourceApplication:OCMArg.any + error:[OCMArg setTo:nil]] + ) + ); +} + +- (NSURL *)sampleUrl +{ + return [NSURL URLWithString:@"http://example.com"]; +} + +- (NSError *)sampleError +{ + return [NSError errorWithDomain:self.name code:0 userInfo:nil]; +} + +static inline NSString *StringFromBool(BOOL value) +{ + return value ? @"YES" : @"NO"; +} + +- (NSURL *)validBridgeResponseUrl +{ + return [NSURL URLWithString:@"http://bridge"]; +} + +NSString *const sampleSource = @"com.example"; +NSString *const sampleAnnotation = @"foo"; +NSOperatingSystemVersion const iOS10Version = { .majorVersion = 10, .minorVersion = 0, .patchVersion = 0 }; + +@end diff --git a/FBSDKCoreKit/FBSDKCoreKitTests/Internal/BridgeAPI/ProtocolVersions/FBSDKBridgeAPIProtocolNativeV1Tests.m b/FBSDKCoreKit/FBSDKCoreKitTests/Internal/BridgeAPI/ProtocolVersions/FBSDKBridgeAPIProtocolNativeV1Tests.m index 1008ff12e2..bf0a12cc4d 100644 --- a/FBSDKCoreKit/FBSDKCoreKitTests/Internal/BridgeAPI/ProtocolVersions/FBSDKBridgeAPIProtocolNativeV1Tests.m +++ b/FBSDKCoreKit/FBSDKCoreKitTests/Internal/BridgeAPI/ProtocolVersions/FBSDKBridgeAPIProtocolNativeV1Tests.m @@ -17,13 +17,11 @@ // CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. #import - #import +#import #import -#import - #import "FBSDKBridgeAPIProtocolNativeV1.h" #import "FBSDKCoreKit+Internal.h" @@ -52,9 +50,9 @@ - (void)setUp - (void)testRequestURL { NSDictionary *parameters = @{ - @"api_key_1": @"value1", - @"api_key_2": @"value2", - }; + @"api_key_1" : @"value1", + @"api_key_2" : @"value2", + }; NSError *error; NSURL *requestURL = [self.protocol requestURLWithActionID:self.actionID scheme:self.scheme @@ -77,17 +75,21 @@ - (void)testNilResponseParameters BOOL cancelled = YES; NSError *error; - XCTAssertNil([self.protocol responseParametersForActionID:self.actionID - queryParameters:nil - cancelled:&cancelled - error:&error]); + XCTAssertNil( + [self.protocol responseParametersForActionID:self.actionID + queryParameters:nil + cancelled:&cancelled + error:&error] + ); XCTAssertFalse(cancelled); XCTAssertNil(error); - XCTAssertNil([self.protocol responseParametersForActionID:self.actionID - queryParameters:@{} - cancelled:&cancelled - error:&error]); + XCTAssertNil( + [self.protocol responseParametersForActionID:self.actionID + queryParameters:@{} + cancelled:&cancelled + error:&error] + ); XCTAssertFalse(cancelled); XCTAssertNil(error); } @@ -98,16 +100,19 @@ - (void)testEmptyResponseParameters NSError *error; NSDictionary *queryParameters = @{ - @"bridge_args": @{ - @"action_id": self.actionID, - }, - @"method_results": @{}, - }; + @"bridge_args" : @{ + @"action_id" : self.actionID, + }, + @"method_results" : @{}, + }; queryParameters = [self _encodeQueryParameters:queryParameters]; - XCTAssertEqualObjects([self.protocol responseParametersForActionID:self.actionID - queryParameters:queryParameters - cancelled:&cancelled - error:&error], @{}); + XCTAssertEqualObjects( + [self.protocol responseParametersForActionID:self.actionID + queryParameters:queryParameters + cancelled:&cancelled + error:&error], + @{} + ); XCTAssertFalse(cancelled); XCTAssertNil(error); } @@ -118,24 +123,27 @@ - (void)testResponseParameters NSError *error; NSDictionary *responseParameters = @{ - @"result_key_1": @1, - @"result_key_2": @"two", - @"result_key_3": @{ - @"result_key_4": @4, - @"result_key_5": @"five", - }, - }; + @"result_key_1" : @1, + @"result_key_2" : @"two", + @"result_key_3" : @{ + @"result_key_4" : @4, + @"result_key_5" : @"five", + }, + }; NSDictionary *queryParameters = @{ - @"bridge_args": @{ - @"action_id": self.actionID, - }, - @"method_results": responseParameters, - }; + @"bridge_args" : @{ + @"action_id" : self.actionID, + }, + @"method_results" : responseParameters, + }; queryParameters = [self _encodeQueryParameters:queryParameters]; - XCTAssertEqualObjects([self.protocol responseParametersForActionID:self.actionID - queryParameters:queryParameters - cancelled:&cancelled - error:&error], responseParameters); + XCTAssertEqualObjects( + [self.protocol responseParametersForActionID:self.actionID + queryParameters:queryParameters + cancelled:&cancelled + error:&error], + responseParameters + ); XCTAssertFalse(cancelled); XCTAssertNil(error); } @@ -146,24 +154,26 @@ - (void)testInvalidActionID NSError *error; NSDictionary *responseParameters = @{ - @"result_key_1": @1, - @"result_key_2": @"two", - @"result_key_3": @{ - @"result_key_4": @4, - @"result_key_5": @"five", - }, - }; + @"result_key_1" : @1, + @"result_key_2" : @"two", + @"result_key_3" : @{ + @"result_key_4" : @4, + @"result_key_5" : @"five", + }, + }; NSDictionary *queryParameters = @{ - @"bridge_args": @{ - @"action_id": [NSUUID UUID].UUIDString, - }, - @"method_results": responseParameters, - }; + @"bridge_args" : @{ + @"action_id" : [NSUUID UUID].UUIDString, + }, + @"method_results" : responseParameters, + }; queryParameters = [self _encodeQueryParameters:queryParameters]; - XCTAssertNil([self.protocol responseParametersForActionID:self.actionID - queryParameters:queryParameters - cancelled:&cancelled - error:&error]); + XCTAssertNil( + [self.protocol responseParametersForActionID:self.actionID + queryParameters:queryParameters + cancelled:&cancelled + error:&error] + ); XCTAssertFalse(cancelled); XCTAssertNil(error); } @@ -175,17 +185,19 @@ - (void)testInvalidBridgeArgs NSString *bridgeArgs = @"this is an invalid bridge_args value"; NSDictionary *queryParameters = @{ - @"bridge_args": bridgeArgs, - @"method_results": @{ - @"result_key_1": @1, - @"result_key_2": @"two", - }, - }; + @"bridge_args" : bridgeArgs, + @"method_results" : @{ + @"result_key_1" : @1, + @"result_key_2" : @"two", + }, + }; queryParameters = [self _encodeQueryParameters:queryParameters]; - XCTAssertNil([self.protocol responseParametersForActionID:self.actionID - queryParameters:queryParameters - cancelled:&cancelled - error:&error]); + XCTAssertNil( + [self.protocol responseParametersForActionID:self.actionID + queryParameters:queryParameters + cancelled:&cancelled + error:&error] + ); XCTAssertFalse(cancelled); XCTAssertNotNil(error); XCTAssertEqual(error.code, FBSDKErrorInvalidArgument); @@ -203,16 +215,18 @@ - (void)testInvalidMethodResults NSString *methodResults = @"this is an invalid method_results value"; NSDictionary *queryParameters = @{ - @"bridge_args": @{ - @"action_id": self.actionID, - }, - @"method_results": methodResults, - }; + @"bridge_args" : @{ + @"action_id" : self.actionID, + }, + @"method_results" : methodResults, + }; queryParameters = [self _encodeQueryParameters:queryParameters]; - XCTAssertNil([self.protocol responseParametersForActionID:self.actionID - queryParameters:queryParameters - cancelled:&cancelled - error:&error]); + XCTAssertNil( + [self.protocol responseParametersForActionID:self.actionID + queryParameters:queryParameters + cancelled:&cancelled + error:&error] + ); XCTAssertFalse(cancelled); XCTAssertNotNil(error); XCTAssertEqual(error.code, FBSDKErrorInvalidArgument); @@ -231,28 +245,30 @@ - (void)testResultError NSInteger code = 42; NSString *domain = @"my custom error domain"; NSDictionary *userInfo = @{ - @"key_1": @1, - @"key_2": @"two", - }; + @"key_1" : @1, + @"key_2" : @"two", + }; NSDictionary *queryParameters = @{ - @"bridge_args": @{ - @"action_id": self.actionID, - @"error": @{ - @"code": @(code), - @"domain": domain, - @"user_info": userInfo, - }, - }, - @"method_results": @{ - @"result_key_1": @1, - @"result_key_2": @"two", - }, - }; + @"bridge_args" : @{ + @"action_id" : self.actionID, + @"error" : @{ + @"code" : @(code), + @"domain" : domain, + @"user_info" : userInfo, + }, + }, + @"method_results" : @{ + @"result_key_1" : @1, + @"result_key_2" : @"two", + }, + }; queryParameters = [self _encodeQueryParameters:queryParameters]; - XCTAssertNil([self.protocol responseParametersForActionID:self.actionID - queryParameters:queryParameters - cancelled:&cancelled - error:&error]); + XCTAssertNil( + [self.protocol responseParametersForActionID:self.actionID + queryParameters:queryParameters + cancelled:&cancelled + error:&error] + ); XCTAssertFalse(cancelled); XCTAssertNotNil(error); XCTAssertEqual(error.code, code); @@ -266,18 +282,20 @@ - (void)testResultCancel NSError *error; NSDictionary *queryParameters = @{ - @"bridge_args": @{ - @"action_id": self.actionID, - }, - @"method_results": @{ - @"completionGesture": @"cancel", - }, - }; + @"bridge_args" : @{ + @"action_id" : self.actionID, + }, + @"method_results" : @{ + @"completionGesture" : @"cancel", + }, + }; queryParameters = [self _encodeQueryParameters:queryParameters]; - XCTAssertNotNil([self.protocol responseParametersForActionID:self.actionID - queryParameters:queryParameters - cancelled:&cancelled - error:&error]); + XCTAssertNotNil( + [self.protocol responseParametersForActionID:self.actionID + queryParameters:queryParameters + cancelled:&cancelled + error:&error] + ); XCTAssertTrue(cancelled); XCTAssertNil(error); } @@ -289,10 +307,10 @@ - (void)testRequestParametersWithDataJSON dataLengthThreshold:NSUIntegerMax includeAppIcon:NO]; NSDictionary *parameters = @{ - @"api_key_1": @"value1", - @"api_key_2": @"value2", - @"data": [self _testData], - }; + @"api_key_1" : @"value1", + @"api_key_2" : @"value2", + @"data" : [self _testData], + }; NSError *error; NSURL *requestURL = [protocol requestURLWithActionID:self.actionID scheme:self.scheme @@ -322,10 +340,10 @@ - (void)testRequestParametersWithImageJSON dataLengthThreshold:NSUIntegerMax includeAppIcon:NO]; NSDictionary *parameters = @{ - @"api_key_1": @"value1", - @"api_key_2": @"value2", - @"image": [self _testImage], - }; + @"api_key_1" : @"value1", + @"api_key_2" : @"value2", + @"image" : [self _testImage], + }; NSError *error; NSURL *requestURL = [protocol requestURLWithActionID:self.actionID scheme:self.scheme @@ -360,10 +378,10 @@ - (void)testRequestParametersWithDataPasteboard dataLengthThreshold:0 includeAppIcon:NO]; NSDictionary *parameters = @{ - @"api_key_1": @"value1", - @"api_key_2": @"value2", - @"data": data, - }; + @"api_key_1" : @"value1", + @"api_key_2" : @"value2", + @"data" : data, + }; NSError *error; NSURL *requestURL = [protocol requestURLWithActionID:self.actionID scheme:self.scheme @@ -398,10 +416,10 @@ - (void)testRequestParametersWithImagePasteboard dataLengthThreshold:0 includeAppIcon:NO]; NSDictionary *parameters = @{ - @"api_key_1": @"value1", - @"api_key_2": @"value2", - @"image": image, - }; + @"api_key_1" : @"value1", + @"api_key_2" : @"value2", + @"image" : image, + }; NSError *error; NSURL *requestURL = [protocol requestURLWithActionID:self.actionID scheme:self.scheme @@ -444,10 +462,10 @@ - (NSData *)_testData - (NSDictionary *)_testDataContainerWithPasteboardName:(NSString *)pasteboardName tag:(NSString *)tag { return @{ - @"isPasteboard": @YES, - @"tag": tag, - @"fbAppBridgeType_jsonReadyValue": pasteboardName, - }; + @"isPasteboard" : @YES, + @"tag" : tag, + @"fbAppBridgeType_jsonReadyValue" : pasteboardName, + }; } - (NSDictionary *)_testDataSerialized:(NSData *)data @@ -459,10 +477,10 @@ - (NSDictionary *)_testDataSerialized:(NSData *)data tag:(NSString *)tag { NSString *string = [FBSDKBase64 encodeData:data]; return @{ - @"isBase64": @YES, - @"tag": tag, - @"fbAppBridgeType_jsonReadyValue": string, - }; + @"isBase64" : @YES, + @"tag" : tag, + @"fbAppBridgeType_jsonReadyValue" : string, + }; } - (NSData *)_testDataWithImage:(UIImage *)image diff --git a/FBSDKCoreKit/FBSDKCoreKitTests/Internal/FBSDKAuthenticationStatusUtilityTests.m b/FBSDKCoreKit/FBSDKCoreKitTests/Internal/FBSDKAuthenticationStatusUtilityTests.m new file mode 100644 index 0000000000..7905bdd513 --- /dev/null +++ b/FBSDKCoreKit/FBSDKCoreKitTests/Internal/FBSDKAuthenticationStatusUtilityTests.m @@ -0,0 +1,170 @@ +// 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 "FBSDKAuthenticationToken+Internal.h" +#import "FBSDKCoreKitTests-Swift.h" +#import "FBSDKTestCase.h" + +@interface FBSDKAuthenticationToken (Testing) + ++ (void)setCurrentAuthenticationToken:(FBSDKAuthenticationToken *)token + shouldPostNotification:(BOOL)shouldPostNotification; + +@end + +@interface FBSDKProfile (Testing) + ++ (void)setCurrentProfile:(nullable FBSDKProfile *)profile + shouldPostNotification:(BOOL)shouldPostNotification; + +@end + +@interface FBSDKAuthenticationStatusUtility (Testing) + ++ (void)_handleResponse:(NSURLResponse *)response; ++ (NSURL *)_requestURL; + +@end + +@interface FBSDKAuthenticationStatusUtilityTests : FBSDKTestCase + +@end + +@implementation FBSDKAuthenticationStatusUtilityTests + +- (void)setUp +{ + [super setUp]; + [FBSDKAuthenticationToken setCurrentAuthenticationToken:SampleAuthenticationToken.validToken shouldPostNotification:NO]; + [FBSDKAccessToken setCurrentAccessToken:SampleAccessToken.validToken shouldDispatchNotif:NO]; + [FBSDKProfile setCurrentProfile:SampleUserProfile.valid shouldPostNotification:NO]; +} + +// MARK: checkAuthenticationStatus + +- (void)testCheckAuthenticationStatusWithNoToken +{ + [FBSDKAuthenticationToken setCurrentAuthenticationToken:nil shouldPostNotification:NO]; + + [FBSDKAuthenticationStatusUtility checkAuthenticationStatus]; + + XCTAssertNotNil(FBSDKAccessToken.currentAccessToken, @"Access token should not be cleared"); + XCTAssertNotNil(FBSDKProfile.currentProfile, @"Profile should not be cleared"); +} + +// MARK: _requestURL + +- (void)testRequestURL +{ + NSURL *url = [FBSDKAuthenticationStatusUtility _requestURL]; + + XCTAssertEqualObjects(url.host, @"m.facebook.com"); + XCTAssertEqualObjects(url.path, @"/platform/oidc/status"); + + NSDictionary *params = [FBSDKInternalUtility parametersFromFBURL:url]; + XCTAssertEqualObjects(params[@"id_token"], FBSDKAuthenticationToken.currentAuthenticationToken.tokenString, @"Incorrect ID token parameter in request url"); +} + +// MARK: _handleResponse + +- (void)testHandleNotAuthorizedResponse +{ + NSURL *url = [NSURL URLWithString:@"m.facebook.com/platform/oidc/status/"]; + NSDictionary *header = @{@"fb-s" : @"not_authorized"}; + NSHTTPURLResponse *response = [[NSHTTPURLResponse alloc] initWithURL:url + statusCode:200 + HTTPVersion:nil + headerFields:header]; + + [FBSDKAuthenticationStatusUtility _handleResponse:response]; + + XCTAssertNil(FBSDKAuthenticationToken.currentAuthenticationToken, @"Authentication token should be cleared when not authorized"); + XCTAssertNil(FBSDKAccessToken.currentAccessToken, @"Access token should be cleared when not authorized"); + XCTAssertNil(FBSDKProfile.currentProfile, @"Profile should be cleared when not authorized"); +} + +- (void)testHandleConnectedResponse +{ + NSURL *url = [NSURL URLWithString:@"m.facebook.com/platform/oidc/status/"]; + NSDictionary *header = @{@"fb-s" : @"connected"}; + NSHTTPURLResponse *response = [[NSHTTPURLResponse alloc] initWithURL:url + statusCode:200 + HTTPVersion:nil + headerFields:header]; + + [FBSDKAuthenticationStatusUtility _handleResponse:response]; + + XCTAssertNotNil(FBSDKAuthenticationToken.currentAuthenticationToken, @"Authentication token should not be cleared when connected"); + XCTAssertNotNil(FBSDKAccessToken.currentAccessToken, @"Access token should not be cleared when connected"); + XCTAssertNotNil(FBSDKProfile.currentProfile, @"Profile should not be cleared when connected"); +} + +- (void)testHandleNoStatusResponse +{ + NSURL *url = [NSURL URLWithString:@"m.facebook.com/platform/oidc/status/"]; + NSHTTPURLResponse *response = [[NSHTTPURLResponse alloc] initWithURL:url + statusCode:200 + HTTPVersion:nil + headerFields:@{}]; + + [FBSDKAuthenticationStatusUtility _handleResponse:response]; + + XCTAssertNotNil(FBSDKAuthenticationToken.currentAuthenticationToken, @"Authentication token should not be cleared when no status returned"); + XCTAssertNotNil(FBSDKAccessToken.currentAccessToken, @"Access token should not be cleared when no status returned"); + XCTAssertNotNil(FBSDKProfile.currentProfile, @"Profile should not be cleared when no status returned"); +} + +- (void)testHandleFailedResponse +{ + NSURL *url = [NSURL URLWithString:@"m.facebook.com/platform/oidc/status/"]; + NSDictionary *header = @{@"fb-s" : @"connected"}; + NSHTTPURLResponse *response = [[NSHTTPURLResponse alloc] initWithURL:url + statusCode:401 + HTTPVersion:nil + headerFields:header]; + + [FBSDKAuthenticationStatusUtility _handleResponse:response]; + + XCTAssertNotNil(FBSDKAuthenticationToken.currentAuthenticationToken, @"Authentication token should not be cleared when the request failed"); + XCTAssertNotNil(FBSDKAccessToken.currentAccessToken, @"Access token should not be cleared when the request failed"); + XCTAssertNotNil(FBSDKProfile.currentProfile, @"Profile should not be cleared when the request failed"); +} + +- (void)testHandleResponseWithFuzzyData +{ + NSURL *url = [NSURL URLWithString:@"m.facebook.com/platform/oidc/status/"]; + + for (int i = 0; i < 100; i++) { + // only strings allowed in HTTP header + NSDictionary *header = @{ + @"fb-s" : [[Fuzzer random] description], + @"some_header_key" : [[Fuzzer random] description], + }; + + NSHTTPURLResponse *response = [[NSHTTPURLResponse alloc] initWithURL:url + statusCode:200 + HTTPVersion:nil + headerFields:header]; + + [FBSDKAuthenticationStatusUtility _handleResponse:response]; + } +} + +@end diff --git a/FBSDKCoreKit/FBSDKCoreKitTests/Internal/FBSDKAuthenticationTokenFactoryTests.m b/FBSDKCoreKit/FBSDKCoreKitTests/Internal/FBSDKAuthenticationTokenFactoryTests.m new file mode 100644 index 0000000000..38326043d2 --- /dev/null +++ b/FBSDKCoreKit/FBSDKCoreKitTests/Internal/FBSDKAuthenticationTokenFactoryTests.m @@ -0,0 +1,595 @@ +// 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 "FBSDKCoreKit+Internal.h" +#import "FBSDKCoreKitTests-Swift.h" +#import "FBSDKSessionProviding.h" +#import "FBSDKTestCase.h" + +static NSString *const _certificate = @"-----BEGIN CERTIFICATE-----\nMIIDgjCCAmoCCQDMso+U6N9AMjANBgkqhkiG9w0BAQsFADCBgjELMAkGA1UEBhMCVVMxCzAJBgNVBAgMAldBMRAwDgYDVQQHDAdTZWF0dGxlMREwDwYDVQQKDAhGYWNlYm9vazEMMAoGA1UECwwDRW5nMRIwEAYDVQQDDAlwYW5zeTA0MTkxHzAdBgkqhkiG9w0BCQEWEHBhbnN5MDQxOUBmYi5jb20wHhcNMjAxMTAzMDAzNTI1WhcNMzAxMTAxMDAzNTI1WjCBgjELMAkGA1UEBhMCVVMxCzAJBgNVBAgMAldBMRAwDgYDVQQHDAdTZWF0dGxlMREwDwYDVQQKDAhGYWNlYm9vazEMMAoGA1UECwwDRW5nMRIwEAYDVQQDDAlwYW5zeTA0MTkxHzAdBgkqhkiG9w0BCQEWEHBhbnN5MDQxOUBmYi5jb20wggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQD0R8/zzuJ5SM+8KBgshg+sKARfm4Ad7Qv7Vi0L8xoXpReXxefDHF7jI9o6pLsp5OIEmnhRjTlbdT7APK1pZ8dHjOdod6xWSoQigUplYOqa5iuVx7IqD15PUhx6/LqcAtHFKDtKOPuIc8CqkmVUyGRMq2OxdCoiWix5z79pSDILmlRWsn4UOCpFU/Ix75YL/JD19IHgwgh4XCxDwUVhmpgG+jI5l9a3ZCBx7JwZAoJ/Z/OpVbguAlBnxIpi8Qk5VKdHzLHvkrdGXGFMzao6bReXX3KNrYrurAgd7fD2TAQo8EH5rgB7ewxtCIlHRoXJPSdVKpTPwx4c7Mfu2EMpx66pAgMBAAEwDQYJKoZIhvcNAQELBQADggEBAPKMCK6mlLIFxMvIa4lT3fYY+APPhMazHtiPJ+279dkhzGmugD3x+mWvd+OzdmWlW/bvZWLbG3UXA166FK8ZcYyuTYdhCxP3vRNqBWNC65qURnIYyUK2DT09WrvBWLZqhv/mJFfijnGqvkKA1k3rVtgCGNDEnezmC9uuO8P17y3+/RZY8dBfvd8lkdCyTCFnKHNyKAE83qnqAJwgbc7cv7IKwAYsDdr4u38GFayBdTzCatTVrQDTYZbJDJLx+BcvHw8pdhthsX7wpGbFH5++Y5G4hRF2vGenzLFIHthxFnpgiZO3VjloPB57awA4jmJY9DjsOZNhZT+RbnCO9AQlCZE=\n-----END CERTIFICATE-----"; +static NSString *const _incorrectCertificate = @"-----BEGIN CERTIFICATE-----\nMIIDATCCAemgAwIBAgIJAO+h3vH3X1puMA0GCSqGSIb3DQEBCwUAMBcxFTATBgNV\nBAMMDGZhY2Vib29rLmNvbTAeFw0yMDExMTAwMTUzMTFaFw0yMTA1MDkwMTUzMTFa\nMBcxFTATBgNVBAMMDGZhY2Vib29rLmNvbTCCASIwDQYJKoZIhvcNAQEBBQADggEP\nADCCAQoCggEBAOZH/FVV1nsdlg6vhjuQlK8VYbN7F+aFAnkMFKQV+MQ88qj/zyBS\nAGZy5MTB3zHjCjw0IhJxTYoESxLy12T7UWqM7ltyKgEO0d8lLbIXR07QWziMd1Q+\n1AlTG9Yj6cMzQGFceB9x09MrOz/Gg+YrIzuRI2TXCaDW7j4LBhqLAlVrK8aMOVHJ\nFDWVCxuwdSNuJ+FNo/bvUqAWVQtn7KNoOcbot5Y4KAVQ16nufH0dJtRcOHzNELYB\nbxmtLWC8eKNn3H8Yw4whZV2BCVZJ/dQ1HZVlSktSs1wE5amg4wm3rHffyN1fpTah\nvN6bjMCQHrpBH2r0BSrkai/joh2ZeWZC068CAwEAAaNQME4wHQYDVR0OBBYEFIYZ\nJeio2kloli49hq+idEeGz3WwMB8GA1UdIwQYMBaAFIYZJeio2kloli49hq+idEeG\nz3WwMAwGA1UdEwQFMAMBAf8wDQYJKoZIhvcNAQELBQADggEBADU9ODtwRL7YDCJ6\naem7juewkgnXx48Tzcl6JtJijIl+IK0Phzb9r/GYrSC+H/N5rWCK5Ur55owXidb9\nXuLysM9xfHBUv91BK03XpevA0bwXCfRk0KPgyc744b8Qb636QiUOzF2aQTYxXbSF\nmXj1HdREsKow0202LfhjKtQWbL+7Q3lpiOFFOkkEVCBu42LT/Ix8VuL/RF3I2xS0\nBhO7FK6Y+ppw33lcmwfP7lLROpeowZA1WeF6tDsqBYivGg8G+9abAMnW0s4ZZSGD\ncDpGIcIlBRhr4nNo0u11BYuxcY8fukYkHvDYygrNhLVNme7JO3Iix7SOyxeMgT9t\ntBi+u9M=\n-----END CERTIFICATE-----"; +static NSString *const _encodedHeader = @"eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9"; +static NSString *const _encodedClaims = @"eyJzdWIiOiIxMjM0IiwibmFtZSI6IlRlc3QgVXNlciIsImlzcyI6Imh0dHBzOi8vZmFjZWJvb2suY29tL2RpYWxvZy9vYXV0aCIsImF1ZCI6IjQzMjEiLCJub25jZSI6InNvbWVfbm9uY2UiLCJleHAiOjE1MTYyNTkwMjIsImVtYWlsIjoiZW1haWxAZW1haWwuY29tIiwicGljdHVyZSI6Imh0dHBzOi8vd3d3LmZhY2Vib29rLmNvbS9zb21lX3BpY3R1cmUiLCJpYXQiOjE1MTYyMzkwMjJ9"; +static NSString *const _signature = @"rTaqfx5Dz0UbzxZ3vBhitgtetWKBJ3-egz5n6l4ngLYqQ7ywapDvS7cM1NRGAh9drT8QeoxKPm0H_1B1LJBNyx-Fiseetfs7XANuocwTx9k7so3bi_EW0V-RYoDTgg5asS9Ra2qYM829xMYkhBHXp1HwHo0uHz1tafQ1hTsxtzH29t23_EnPpnVx5jvu-UeAEL4Q7VeIIfkweQYzuT3cowWAs-Vhyvl9I39Z4Uh_3ZhkpBJW1CblPW3ekHoySC61qwePM9Fk0q3N7K45LtktIMR5biV0RvJceTGOssHGhjaQ3hzpRq318MZKfBtg6C-Ryhh8SmOkuDrrj-VNdoVHKg"; +static NSString *const _certificateKey = @"some_key"; +static NSString *const _mockAppID = @"4321"; +static NSString *const _mockJTI = @"some_jti"; +static NSString *const _mockNonce = @"some_nonce"; +static NSString *const _facebookURL = @"https://facebook.com/dialog/oauth"; + +typedef void (^FBSDKVerifySignatureCompletionBlock)(BOOL success); + +@interface FBSDKAuthenticationTokenFactory (Testing) + +- (instancetype)initWithSessionProvider:(id)sessionProvider; +- (void)setCertificate:(NSString *)certificate; +- (BOOL)verifySignature:(NSString *)signature + header:(NSString *)header + claims:(NSString *)claims + certificateKey:(NSString *)key + completion:(FBSDKVerifySignatureCompletionBlock)completion; +- (NSDictionary *)claims; +- (NSURL *)_certificateEndpoint; + +@end + +@interface FBSDKAuthenticationTokenClaims (Testing) + +- (instancetype)initWithJti:(NSString *)jti + iss:(NSString *)iss + aud:(NSString *)aud + nonce:(NSString *)nonce + exp:(long)exp + iat:(long)iat + sub:(NSString *)sub + name:(nullable NSString *)name + email:(nullable NSString *)email + picture:(nullable NSString *)picture; + +@end + +@interface FBSDKAuthenticationTokenHeader (Testing) + +- (instancetype)initWithAlg:(NSString *)alg + typ:(NSString *)typ + kid:(NSString *)kid; + +@end + +@interface FBSDKAuthenticationTokenFactoryTests : FBSDKTestCase + +@end + +@implementation FBSDKAuthenticationTokenFactoryTests +{ + FBSDKAuthenticationTokenClaims *_claims; + NSDictionary *_claimsDict; + FBSDKAuthenticationTokenHeader *_header; + NSDictionary *_headerDict; +} + +- (void)setUp +{ + [super setUp]; + + [self stubAppID:_mockAppID]; + + long currentTime = [[NSNumber numberWithDouble:[[NSDate date] timeIntervalSince1970]] longValue]; + + _claims = [[FBSDKAuthenticationTokenClaims alloc] initWithJti:_mockJTI + iss:_facebookURL + aud:_mockAppID + nonce:_mockNonce + exp:currentTime + 60 * 60 * 48 // 2 days later + iat:currentTime - 60 // 1 min ago + sub:@"1234" + name:@"Test User" + email:@"email@email.com" + picture:@"https://www.facebook.com/some_picture"]; + + _claimsDict = @{ + @"iss" : _facebookURL, + @"aud" : _mockAppID, + @"nonce" : _mockNonce, + @"exp" : @(currentTime + 60 * 60 * 48), // 2 days later + @"iat" : @(currentTime - 60), // 1 min ago + @"jti" : _mockJTI, + @"sub" : @"1234", + @"name" : @"Test User", + @"email" : @"email@email.com", + @"picture" : @"https://www.facebook.com/some_picture", + }; + + _header = [[FBSDKAuthenticationTokenHeader alloc] initWithAlg:@"RS256" + typ:@"JWT" + kid:@"abcd1234"]; + + _headerDict = @{ + @"alg" : @"RS256", + @"typ" : @"JWT", + @"kid" : @"abcd1234", + }; +} + +// MARK: - Creation + +- (void)testCreateWithInvalidFormatToken +{ + __block BOOL wasCalled = NO; + FBSDKAuthenticationTokenBlock completion = ^(FBSDKAuthenticationToken *token) { + XCTAssertNil(token); + wasCalled = YES; + }; + + [[FBSDKAuthenticationTokenFactory new] createTokenFromTokenString:@"invalid_token" nonce:@"123456789" completion:completion]; + + XCTAssertTrue(wasCalled, @"Completion handler should be called syncronously"); +} + +// MARK: - Decoding Claims + +- (void)testDecodeValidClaims +{ + NSData *claimsData = [FBSDKTypeUtility dataWithJSONObject:_claimsDict options:0 error:nil]; + NSString *encodedClaims = [self base64URLEncodeData:claimsData]; + + FBSDKAuthenticationTokenClaims *claims = [FBSDKAuthenticationTokenClaims validatedClaimsWithEncodedString:encodedClaims nonce:_mockNonce]; + XCTAssertEqualObjects(claims, _claims); +} + +- (void)testDecodeInvalidFormatClaims +{ + NSData *claimsData = [@"invalid_claims" dataUsingEncoding:NSUTF8StringEncoding]; + NSString *encodedClaims = [self base64URLEncodeData:claimsData]; + + XCTAssertNil([FBSDKAuthenticationTokenClaims validatedClaimsWithEncodedString:encodedClaims nonce:_mockNonce]); +} + +- (void)testDecodeInvalidClaims +{ + long currentTime = [[NSNumber numberWithDouble:[[NSDate date] timeIntervalSince1970]] longValue]; + + // non facebook issuer + [self assertDecodeClaimsFailWithInvalidEntry:@"iss" + value:@"https://notfacebook.com"]; + [self assertDecodeClaimsFailWithInvalidEntry:@"iss" + value:nil]; + [self assertDecodeClaimsFailWithInvalidEntry:@"iss" + value:@""]; + + // incorrect audience + [self assertDecodeClaimsFailWithInvalidEntry:@"aud" + value:@"wrong_app_id"]; + [self assertDecodeClaimsFailWithInvalidEntry:@"aud" + value:nil]; + [self assertDecodeClaimsFailWithInvalidEntry:@"aud" + value:@""]; + + // expired + [self assertDecodeClaimsFailWithInvalidEntry:@"exp" + value:@(currentTime - 60 * 60)]; + [self assertDecodeClaimsFailWithInvalidEntry:@"exp" + value:nil]; + [self assertDecodeClaimsFailWithInvalidEntry:@"exp" + value:@""]; + + // issued too long ago + [self assertDecodeClaimsFailWithInvalidEntry:@"iat" + value:@(currentTime - 60 * 60)]; + [self assertDecodeClaimsFailWithInvalidEntry:@"iat" + value:nil]; + [self assertDecodeClaimsFailWithInvalidEntry:@"iat" + value:@""]; + + // incorrect nonce + [self assertDecodeClaimsFailWithInvalidEntry:@"nonce" + value:@"incorrect_nonce"]; + [self assertDecodeClaimsFailWithInvalidEntry:@"nonce" + value:nil]; + [self assertDecodeClaimsFailWithInvalidEntry:@"nonce" + value:@""]; + + // invalid user ID + [self assertDecodeClaimsFailWithInvalidEntry:@"sub" + value:nil]; + [self assertDecodeClaimsFailWithInvalidEntry:@"sub" + value:@""]; + + // invalid JIT + [self assertDecodeClaimsFailWithInvalidEntry:@"jti" + value:nil]; + [self assertDecodeClaimsFailWithInvalidEntry:@"jti" + value:@""]; +} + +- (void)testDecodeEmptyClaims +{ + NSDictionary *claims = @{}; + NSData *claimsData = [FBSDKTypeUtility dataWithJSONObject:claims options:0 error:nil]; + NSString *encodedClaims = [self base64URLEncodeData:claimsData]; + + XCTAssertNil([FBSDKAuthenticationTokenClaims validatedClaimsWithEncodedString:encodedClaims nonce:_mockNonce]); +} + +- (void)testDecodeRandomClaims +{ + for (int i = 0; i < 100; i++) { + NSDictionary *randomizedClaims = [Fuzzer randomizeWithJson:_claims]; + NSData *claimsData = [FBSDKTypeUtility dataWithJSONObject:randomizedClaims options:0 error:nil]; + NSString *encodedClaims = [self base64URLEncodeData:claimsData]; + + [FBSDKAuthenticationTokenClaims validatedClaimsWithEncodedString:encodedClaims nonce:_mockNonce]; + } +} + +// MARK: - Decoding Header + +- (void)testDecodeValidHeader +{ + NSData *headerData = [FBSDKTypeUtility dataWithJSONObject:_headerDict options:0 error:nil]; + NSString *encodedHeader = [self base64URLEncodeData:headerData]; + + FBSDKAuthenticationTokenHeader *header = [FBSDKAuthenticationTokenHeader validatedHeaderWithEncodedString:encodedHeader]; + XCTAssertEqualObjects(header, _header); +} + +- (void)testDecodeInvalidFormatHeader +{ + NSData *headerData = [@"invalid_header" dataUsingEncoding:NSUTF8StringEncoding]; + NSString *encodedHeader = [self base64URLEncodeData:headerData]; + + XCTAssertNil([FBSDKAuthenticationTokenHeader validatedHeaderWithEncodedString:encodedHeader]); +} + +- (void)testDecodeInvalidHeader +{ + [self assertDecodeHeaderFailWithInvalidEntry:@"alg" value:@"wrong_algorithm"]; + [self assertDecodeHeaderFailWithInvalidEntry:@"alg" value:nil]; + [self assertDecodeHeaderFailWithInvalidEntry:@"alg" value:@""]; + + [self assertDecodeHeaderFailWithInvalidEntry:@"typ" value:@"some_type"]; + [self assertDecodeHeaderFailWithInvalidEntry:@"typ" value:nil]; + [self assertDecodeHeaderFailWithInvalidEntry:@"typ" value:@""]; + + [self assertDecodeHeaderFailWithInvalidEntry:@"kid" value:nil]; + [self assertDecodeHeaderFailWithInvalidEntry:@"kid" value:@""]; +} + +- (void)testDecodeEmptyHeader +{ + NSDictionary *header = @{}; + NSData *headerData = [FBSDKTypeUtility dataWithJSONObject:header options:0 error:nil]; + NSString *encodedHeader = [self base64URLEncodeData:headerData]; + + XCTAssertNil([FBSDKAuthenticationTokenHeader validatedHeaderWithEncodedString:encodedHeader]); +} + +- (void)testDecodeRandomHeader +{ + for (int i = 0; i < 100; i++) { + NSDictionary *randomizedHeader = [Fuzzer randomizeWithJson:_headerDict]; + NSData *headerData = [FBSDKTypeUtility dataWithJSONObject:randomizedHeader options:0 error:nil]; + NSString *encodedHeader = [self base64URLEncodeData:headerData]; + + [FBSDKAuthenticationTokenHeader validatedHeaderWithEncodedString:encodedHeader]; + } +} + +// MARK: - Verifying Signature + +- (void)testCertificateEndpointURL +{ + NSURL *url = FBSDKAuthenticationTokenFactory.new._certificateEndpoint; + XCTAssertEqualObjects(url.absoluteString, @"https://m.facebook.com/.well-known/oauth/openid/certs/"); +} + +- (void)testVerifySignatureWithoutDataWithoutResponseWithoutError +{ + FakeSessionDataTask *dataTask = [FakeSessionDataTask new]; + FakeSessionProvider *session = [FakeSessionProvider new]; + session.stubbedDataTask = dataTask; + FBSDKAuthenticationTokenFactory *factory = [[FBSDKAuthenticationTokenFactory alloc] initWithSessionProvider:session]; + + __block BOOL wasCalled = NO; + [factory verifySignature:_signature + header:_encodedHeader + claims:_encodedClaims + certificateKey:_certificateKey + completion:^(BOOL success) { + XCTAssertFalse( + success, + "A signature cannot be verified if the certificate request returns no data" + ); + wasCalled = YES; + }]; + + XCTAssertEqual( + dataTask.resumeCallCount, + 1, + "Should start the session data task when verifying a signature" + ); + XCTAssertTrue(wasCalled); +} + +- (void)testVerifySignatureWithDataWithInvalidResponseWithoutError +{ + FakeSessionDataTask *dataTask = [FakeSessionDataTask new]; + FakeSessionProvider *session = [FakeSessionProvider new]; + session.data = [@"foo" dataUsingEncoding:NSUTF8StringEncoding]; + session.urlResponse = [[NSHTTPURLResponse alloc] initWithURL:self.sampleURL statusCode:401 HTTPVersion:nil headerFields:nil]; + session.stubbedDataTask = dataTask; + FBSDKAuthenticationTokenFactory *factory = [[FBSDKAuthenticationTokenFactory alloc] initWithSessionProvider:session]; + + __block BOOL wasCalled = NO; + [factory verifySignature:_signature + header:_encodedHeader + claims:_encodedClaims + certificateKey:_certificateKey + completion:^(BOOL success) { + XCTAssertFalse( + success, + "A signature cannot be verified if the certificate request returns a non-200 response" + ); + wasCalled = YES; + }]; + + XCTAssertEqual( + dataTask.resumeCallCount, + 1, + "Should start the session data task when verifying a signature" + ); + XCTAssertTrue(wasCalled); +} + +- (void)testVerifySignatureWithInvalidDataWithValidResponseWithoutError +{ + FakeSessionDataTask *dataTask = [FakeSessionDataTask new]; + FakeSessionProvider *session = [FakeSessionProvider new]; + session.data = [@"foo" dataUsingEncoding:NSUTF8StringEncoding]; + session.urlResponse = [[NSHTTPURLResponse alloc] initWithURL:self.sampleURL statusCode:200 HTTPVersion:nil headerFields:nil]; + session.stubbedDataTask = dataTask; + FBSDKAuthenticationTokenFactory *factory = [[FBSDKAuthenticationTokenFactory alloc] initWithSessionProvider:session]; + + __block BOOL wasCalled = NO; + [factory verifySignature:_signature + header:_encodedHeader + claims:_encodedClaims + certificateKey:_certificateKey + completion:^(BOOL success) { + XCTAssertFalse( + success, + "A signature cannot be verified if the certificate request returns invalid data" + ); + wasCalled = YES; + }]; + + XCTAssertEqual( + dataTask.resumeCallCount, + 1, + "Should start the session data task when verifying a signature" + ); + XCTAssertTrue(wasCalled); +} + +- (void)testVerifySignatureWithValidDataWithValidResponseWithError +{ + FakeSessionDataTask *dataTask = [FakeSessionDataTask new]; + FakeSessionProvider *session = [FakeSessionProvider new]; + session.data = [self validCertificateData]; + session.urlResponse = [[NSHTTPURLResponse alloc] initWithURL:self.sampleURL statusCode:200 HTTPVersion:nil headerFields:nil]; + session.error = [self sampleError]; + session.stubbedDataTask = dataTask; + FBSDKAuthenticationTokenFactory *factory = [[FBSDKAuthenticationTokenFactory alloc] initWithSessionProvider:session]; + + __block BOOL wasCalled = NO; + [factory verifySignature:_signature + header:_encodedHeader + claims:_encodedClaims + certificateKey:_certificateKey + completion:^(BOOL success) { + XCTAssertFalse( + success, + "A signature cannot be verified if the certificate request returns an error" + ); + wasCalled = YES; + }]; + + XCTAssertEqual( + dataTask.resumeCallCount, + 1, + "Should start the session data task when verifying a signature" + ); + XCTAssertTrue(wasCalled); +} + +- (void)testVerifySignatureWithValidDataWithValidResponseWithoutError +{ + FakeSessionDataTask *dataTask = [FakeSessionDataTask new]; + FakeSessionProvider *session = [FakeSessionProvider new]; + session.data = [self validCertificateData]; + session.urlResponse = [[NSHTTPURLResponse alloc] initWithURL:self.sampleURL statusCode:200 HTTPVersion:nil headerFields:nil]; + session.stubbedDataTask = dataTask; + FBSDKAuthenticationTokenFactory *factory = [[FBSDKAuthenticationTokenFactory alloc] initWithSessionProvider:session]; + + __block BOOL wasCalled = NO; + [factory verifySignature:_signature + header:_encodedHeader + claims:_encodedClaims + certificateKey:_certificateKey + completion:^(BOOL success) { + XCTAssertTrue( + success, + "Should verify a signature when the response contains the expected key" + ); + wasCalled = YES; + }]; + + XCTAssertEqual( + dataTask.resumeCallCount, + 1, + "Should start the session data task when verifying a signature" + ); + XCTAssertTrue(wasCalled); +} + +- (void)testVerifySignatureWithInvalidCertificates +{ + NSArray *certificates = @[ + [self mangledCertificateData], + [self validIncorrectCertificateData] + ]; + + for (NSData *certificateData in certificates) { + FakeSessionDataTask *dataTask = [FakeSessionDataTask new]; + FakeSessionProvider *session = [FakeSessionProvider new]; + session.data = certificateData; + session.urlResponse = [[NSHTTPURLResponse alloc] initWithURL:self.sampleURL statusCode:200 HTTPVersion:nil headerFields:nil]; + session.stubbedDataTask = dataTask; + FBSDKAuthenticationTokenFactory *factory = [[FBSDKAuthenticationTokenFactory alloc] initWithSessionProvider:session]; + + __block BOOL wasCalled = NO; + [factory verifySignature:_signature + header:_encodedHeader + claims:_encodedClaims + certificateKey:_certificateKey + completion:^(BOOL success) { + XCTAssertFalse( + success, + "Should not verify a signature for an incorrect or invalid certificate" + ); + wasCalled = YES; + }]; + XCTAssertEqual( + dataTask.resumeCallCount, + 1, + "Should start the session data task when verifying a signature" + ); + XCTAssertTrue(wasCalled); + } +} + +- (void)testVerifySignatureWithFuzzyData +{ + FakeSessionDataTask *dataTask = [FakeSessionDataTask new]; + FakeSessionProvider *session = [FakeSessionProvider new]; + session.urlResponse = [[NSHTTPURLResponse alloc] initWithURL:self.sampleURL statusCode:200 HTTPVersion:nil headerFields:nil]; + session.stubbedDataTask = dataTask; + FBSDKAuthenticationTokenFactory *factory = [[FBSDKAuthenticationTokenFactory alloc] initWithSessionProvider:session]; + + for (int i = 0; i < 100; i++) { + NSDictionary *randomizedCertificates = [Fuzzer randomizeWithJson:self.validRawCertificateResponse]; + NSData *data = [FBSDKTypeUtility dataWithJSONObject:randomizedCertificates options:0 error:nil]; + session.data = data; + + __block BOOL wasCalled = NO; + [factory verifySignature:_signature + header:_encodedHeader + claims:_encodedClaims + certificateKey:_certificateKey + completion:^(BOOL success) { + wasCalled = YES; + }]; + XCTAssertTrue(wasCalled); + } +} + +// MARK: - Helpers + +- (void)assertDecodeClaimsFailWithInvalidEntry:(NSString *)key value:(id)value +{ + NSMutableDictionary *invalidClaims = [_claimsDict mutableCopy]; + if (value) { + [FBSDKTypeUtility dictionary:invalidClaims setObject:value forKey:key]; + } else { + [invalidClaims removeObjectForKey:key]; + } + + NSData *claimsData = [FBSDKTypeUtility dataWithJSONObject:invalidClaims options:0 error:nil]; + NSString *encodedClaims = [self base64URLEncodeData:claimsData]; + + XCTAssertNil([FBSDKAuthenticationTokenClaims validatedClaimsWithEncodedString:encodedClaims nonce:_mockNonce]); +} + +- (void)assertDecodeHeaderFailWithInvalidEntry:(NSString *)key value:(id)value +{ + NSMutableDictionary *invalidHeader = [_headerDict mutableCopy]; + if (value) { + [FBSDKTypeUtility dictionary:invalidHeader setObject:value forKey:key]; + } else { + [invalidHeader removeObjectForKey:key]; + } + NSData *headerData = [FBSDKTypeUtility dataWithJSONObject:invalidHeader options:0 error:nil]; + NSString *encodedHeader = [self base64URLEncodeData:headerData]; + + XCTAssertNil([FBSDKAuthenticationTokenHeader validatedHeaderWithEncodedString:encodedHeader]); +} + +- (NSString *)base64URLEncodeData:(NSData *)data +{ + NSString *base64 = [FBSDKBase64 encodeData:data]; + NSString *base64URL = [base64 stringByReplacingOccurrencesOfString:@"+" withString:@"-"]; + base64URL = [base64URL stringByReplacingOccurrencesOfString:@"/" withString:@"_"]; + return [base64URL stringByReplacingOccurrencesOfString:@"=" withString:@""]; +} + +- (NSDictionary *)validRawCertificateResponse +{ + return @{ + _certificateKey : _certificate, + @"foo" : @"Not a certificate" + }; +} + +- (NSData *)mangledCertificateData +{ + NSString *mangledCertificate = [_certificate stringByReplacingOccurrencesOfString:@"a" withString:@"b"]; + NSDictionary *certificates = @{ + _certificateKey : mangledCertificate + }; + + return [FBSDKTypeUtility dataWithJSONObject:certificates options:0 error:nil]; +} + +- (NSData *)validCertificateData +{ + return [FBSDKTypeUtility dataWithJSONObject:self.validRawCertificateResponse options:0 error:nil]; +} + +- (NSData *)validIncorrectCertificateData +{ + NSDictionary *certificates = @{ + _certificateKey : _incorrectCertificate + }; + + return [FBSDKTypeUtility dataWithJSONObject:certificates options:0 error:nil]; +} + +- (NSURL *)sampleURL +{ + return [NSURL URLWithString:@"https://example.com"]; +} + +- (NSError *)sampleError +{ + return [NSError errorWithDomain:self.name code:0 userInfo:nil]; +} + +@end diff --git a/FBSDKCoreKit/FBSDKCoreKitTests/Internal/FBSDKErrorConfigurationTests.m b/FBSDKCoreKit/FBSDKCoreKitTests/Internal/FBSDKErrorConfigurationTests.m index 451c677f97..19b144f592 100644 --- a/FBSDKCoreKit/FBSDKCoreKitTests/Internal/FBSDKErrorConfigurationTests.m +++ b/FBSDKCoreKit/FBSDKCoreKitTests/Internal/FBSDKErrorConfigurationTests.m @@ -18,14 +18,15 @@ #import -#import "FBSDKErrorConfiguration.h" #import "FBSDKCoreKitTests-Swift.h" +#import "FBSDKErrorConfiguration.h" @interface FBSDKErrorConfigurationTests : XCTestCase @end -@implementation FBSDKErrorConfigurationTests { +@implementation FBSDKErrorConfigurationTests +{ NSArray *rawErrorCodeConfiguration; } @@ -35,13 +36,11 @@ - (void)setUp rawErrorCodeConfiguration = @[ @{ @"name" : @"other", - @"items" : @[ @{ @"code" : @190, @"subcodes": @[ @459 ] } ], - }, + @"items" : @[@{ @"code" : @190, @"subcodes" : @[@459] }], }, @{ @"name" : @"login", - @"items" : @[ @{ @"code" : @1, @"subcodes": @[ @12312 ] } ], + @"items" : @[@{ @"code" : @1, @"subcodes" : @[@12312] }], @"recovery_message" : @"somemessage", - @"recovery_options" : @[ @"Yes", @"No thanks" ] - }, + @"recovery_options" : @[@"Yes", @"No thanks"]}, ]; } @@ -80,17 +79,14 @@ - (void)testErrorConfigurationAdditonalArray - (void)testParsingRandomName { - for (int i = 0; i < 1000; i++) { - + for (int i = 0; i < 100; i++) { NSArray *array = @[ @{ @"name" : Fuzzer.random, - @"items" : @[ @{ @"code" : @190, @"subcodes": @[ @459 ] } ], - }, + @"items" : @[@{ @"code" : @190, @"subcodes" : @[@459] }], }, @{ @"name" : @"login", - @"items" : @[ @{ @"code" : @1, @"subcodes": @[ @12312 ] } ], + @"items" : @[@{ @"code" : @1, @"subcodes" : @[@12312] }], @"recovery_message" : @"somemessage", - @"recovery_options" : @[ @"Yes", @"No thanks" ] - }, + @"recovery_options" : @[@"Yes", @"No thanks"]}, ]; FBSDKErrorConfiguration *configuration = [[FBSDKErrorConfiguration alloc] initWithDictionary:nil]; @@ -100,16 +96,14 @@ - (void)testParsingRandomName - (void)testParsingRandomSubcodes { - for (int i = 0; i < 1000; i++) { + for (int i = 0; i < 100; i++) { NSArray *array = @[ @{ @"name" : @"other", - @"items" : @[ @{ @"code" : @190, @"subcodes": @[ Fuzzer.random ] } ], - }, + @"items" : @[@{ @"code" : @190, @"subcodes" : @[Fuzzer.random] }], }, @{ @"name" : @"login", - @"items" : @[ @{ @"code" : @1, @"subcodes": @[ Fuzzer.random ] } ], + @"items" : @[@{ @"code" : @1, @"subcodes" : @[Fuzzer.random] }], @"recovery_message" : @"somemessage", - @"recovery_options" : @[ @"Yes", @"No thanks" ] - }, + @"recovery_options" : @[@"Yes", @"No thanks"]}, ]; FBSDKErrorConfiguration *configuration = [[FBSDKErrorConfiguration alloc] initWithDictionary:nil]; @@ -119,16 +113,14 @@ - (void)testParsingRandomSubcodes - (void)testParsingRandomCodes { - for (int i = 0; i < 1000; i++) { + for (int i = 0; i < 100; i++) { NSArray *array = @[ - @{ @"name" : @"other", - @"items" : @[ @{ @"code" : Fuzzer.random, @"subcodes": @[ @459 ] } ], - }, - @{ @"name" : @"login", - @"items" : @[ @{ @"code" : Fuzzer.random, @"subcodes": @[ @12312 ] } ], - @"recovery_message" : @"somemessage", - @"recovery_options" : @[ @"Yes", @"No thanks" ] - }, + @{ @"name" : @"other", + @"items" : @[@{ @"code" : Fuzzer.random, @"subcodes" : @[@459] }], }, + @{ @"name" : @"login", + @"items" : @[@{ @"code" : Fuzzer.random, @"subcodes" : @[@12312] }], + @"recovery_message" : @"somemessage", + @"recovery_options" : @[@"Yes", @"No thanks"]}, ]; FBSDKErrorConfiguration *configuration = [[FBSDKErrorConfiguration alloc] initWithDictionary:nil]; @@ -138,16 +130,14 @@ - (void)testParsingRandomCodes - (void)testParsingRandomItemDictionaries { - for (int i = 0; i < 1000; i++) { + for (int i = 0; i < 100; i++) { NSArray *array = @[ @{ @"name" : @"other", - @"items" : @[ Fuzzer.random ], - }, + @"items" : @[Fuzzer.random], }, @{ @"name" : @"login", - @"items" : @[ Fuzzer.random ], + @"items" : @[Fuzzer.random], @"recovery_message" : @"somemessage", - @"recovery_options" : @[ @"Yes", @"No thanks" ] - }, + @"recovery_options" : @[@"Yes", @"No thanks"]}, ]; FBSDKErrorConfiguration *configuration = [[FBSDKErrorConfiguration alloc] initWithDictionary:nil]; @@ -157,16 +147,14 @@ - (void)testParsingRandomItemDictionaries - (void)testParsingRandomItems { - for (int i = 0; i < 1000; i++) { + for (int i = 0; i < 100; i++) { NSArray *array = @[ @{ @"name" : @"other", - @"items" : Fuzzer.random, - }, + @"items" : Fuzzer.random, }, @{ @"name" : @"login", @"items" : Fuzzer.random, @"recovery_message" : @"somemessage", - @"recovery_options" : @[ @"Yes", @"No thanks" ] - }, + @"recovery_options" : @[@"Yes", @"No thanks"]}, ]; FBSDKErrorConfiguration *configuration = [[FBSDKErrorConfiguration alloc] initWithDictionary:nil]; @@ -176,16 +164,14 @@ - (void)testParsingRandomItems - (void)testParsingRandomRecoveryMessage { - for (int i = 0; i < 1000; i++) { + for (int i = 0; i < 100; i++) { NSArray *array = @[ @{ @"name" : @"other", - @"items" : @[ @{ @"code" : @190, @"subcodes": @[ @459 ] } ], - }, + @"items" : @[@{ @"code" : @190, @"subcodes" : @[@459] }], }, @{ @"name" : @"login", - @"items" : @[ @{ @"code" : @1, @"subcodes": @[ @12312 ] } ], + @"items" : @[@{ @"code" : @1, @"subcodes" : @[@12312] }], @"recovery_message" : Fuzzer.random, - @"recovery_options" : @[ @"Yes", @"No thanks" ] - }, + @"recovery_options" : @[@"Yes", @"No thanks"]}, ]; FBSDKErrorConfiguration *configuration = [[FBSDKErrorConfiguration alloc] initWithDictionary:nil]; @@ -195,16 +181,14 @@ - (void)testParsingRandomRecoveryMessage - (void)testParsingRandomRecoveryOptionsArray { - for (int i = 0; i < 1000; i++) { + for (int i = 0; i < 100; i++) { NSArray *array = @[ @{ @"name" : @"other", - @"items" : @[ @{ @"code" : @190, @"subcodes": @[ @459 ] } ], - }, + @"items" : @[@{ @"code" : @190, @"subcodes" : @[@459] }], }, @{ @"name" : @"login", - @"items" : @[ @{ @"code" : @1, @"subcodes": @[ @12312 ] } ], + @"items" : @[@{ @"code" : @1, @"subcodes" : @[@12312] }], @"recovery_message" : @"somemessage", - @"recovery_options" : @[ Fuzzer.random, Fuzzer.random ] - }, + @"recovery_options" : @[Fuzzer.random, Fuzzer.random]}, ]; FBSDKErrorConfiguration *configuration = [[FBSDKErrorConfiguration alloc] initWithDictionary:nil]; @@ -214,16 +198,14 @@ - (void)testParsingRandomRecoveryOptionsArray - (void)testParsingRandomRecoveryOptions { - for (int i = 0; i < 1000; i++) { + for (int i = 0; i < 100; i++) { NSArray *array = @[ @{ @"name" : @"other", - @"items" : @[ @{ @"code" : @190, @"subcodes": @[ @459 ] } ], - }, + @"items" : @[@{ @"code" : @190, @"subcodes" : @[@459] }], }, @{ @"name" : @"login", - @"items" : @[ @{ @"code" : @1, @"subcodes": @[ @12312 ] } ], + @"items" : @[@{ @"code" : @1, @"subcodes" : @[@12312] }], @"recovery_message" : @"somemessage", - @"recovery_options" : Fuzzer.random - }, + @"recovery_options" : Fuzzer.random}, ]; FBSDKErrorConfiguration *configuration = [[FBSDKErrorConfiguration alloc] initWithDictionary:nil]; @@ -233,15 +215,13 @@ - (void)testParsingRandomRecoveryOptions - (void)testParsingRecoveryMessageWithoutOptions { - for (int i = 0; i < 1000; i++) { + for (int i = 0; i < 100; i++) { NSArray *array = @[ @{ @"name" : @"other", - @"items" : @[ @{ @"code" : @190, @"subcodes": @[ @459 ] } ], - }, + @"items" : @[@{ @"code" : @190, @"subcodes" : @[@459] }], }, @{ @"name" : @"login", - @"items" : @[ @{ @"code" : @1, @"subcodes": @[ @12312 ] } ], - @"recovery_message" : @"somemessage", - }, + @"items" : @[@{ @"code" : @1, @"subcodes" : @[@12312] }], + @"recovery_message" : @"somemessage", }, ]; FBSDKErrorConfiguration *configuration = [[FBSDKErrorConfiguration alloc] initWithDictionary:nil]; @@ -251,15 +231,13 @@ - (void)testParsingRecoveryMessageWithoutOptions - (void)testParsingRecoveryOptionsWithoutMessage { - for (int i = 0; i < 1000; i++) { + for (int i = 0; i < 100; i++) { NSArray *array = @[ @{ @"name" : @"other", - @"items" : @[ @{ @"code" : @190, @"subcodes": @[ @459 ] } ], - }, + @"items" : @[@{ @"code" : @190, @"subcodes" : @[@459] }], }, @{ @"name" : @"login", - @"items" : @[ @{ @"code" : @1, @"subcodes": @[ @12312 ] } ], - @"recovery_options" : @[ @"Yes", @"No thanks" ] - }, + @"items" : @[@{ @"code" : @1, @"subcodes" : @[@12312] }], + @"recovery_options" : @[@"Yes", @"No thanks"]}, ]; FBSDKErrorConfiguration *configuration = [[FBSDKErrorConfiguration alloc] initWithDictionary:nil]; @@ -269,7 +247,7 @@ - (void)testParsingRecoveryOptionsWithoutMessage - (void)testParsingRandomEntries { - for (int i = 0; i < 1000; i++) { + for (int i = 0; i < 100; i++) { NSArray *array = [Fuzzer randomizeWithJson:rawErrorCodeConfiguration]; FBSDKErrorConfiguration *configuration = [[FBSDKErrorConfiguration alloc] initWithDictionary:nil]; @@ -277,5 +255,4 @@ - (void)testParsingRandomEntries } } - @end diff --git a/FBSDKCoreKit/FBSDKCoreKitTests/Internal/FBSDKInternalUtilityTests.m b/FBSDKCoreKit/FBSDKCoreKitTests/Internal/FBSDKInternalUtilityTests.m index 3f1b7aca3b..82a4d2b0db 100644 --- a/FBSDKCoreKit/FBSDKCoreKitTests/Internal/FBSDKInternalUtilityTests.m +++ b/FBSDKCoreKit/FBSDKCoreKitTests/Internal/FBSDKInternalUtilityTests.m @@ -16,23 +16,67 @@ // 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 "FBSDKCoreKit.h" +#import "FBSDKCoreKitTests-Swift.h" #import "FBSDKInternalUtility.h" +#import "FBSDKTestCase.h" +#import "FakeBundle.h" + +@interface FBSDKInternalUtility (Testing) + ++ (BOOL)_canOpenURLScheme:(NSString *)scheme; ++ (void)resetQuerySchemesCache; ++ (void)resetDidCheckRegisteredCanOpenUrlSchemes; ++ (void)resetIsFacebookAppInstalledCache; ++ (void)resetDidCheckIfMessengerAppInstalledCache; ++ (void)resetDidCheckIfMSQRDAppInstalledCache; ++ (void)resetDidCheckOperatingSystemVersion; ++ (void)resetFetchingUrlSchemes; -@interface FBSDKInternalUtilityTests : XCTestCase +@end + +@interface FBSDKInternalUtilityTests : FBSDKTestCase @end @implementation FBSDKInternalUtilityTests +{ + id _notificationCenterSpy; +} + +- (void)setUp +{ + [super setUp]; + + [self resetCachedSettings]; + + [FBSDKInternalUtility resetQuerySchemesCache]; + [FBSDKInternalUtility resetIsFacebookAppInstalledCache]; + [FBSDKInternalUtility resetDidCheckIfMessengerAppInstalledCache]; + [FBSDKInternalUtility resetDidCheckIfMSQRDAppInstalledCache]; + [FBSDKInternalUtility resetDidCheckRegisteredCanOpenUrlSchemes]; + [FBSDKInternalUtility resetDidCheckOperatingSystemVersion]; + [FBSDKInternalUtility resetFetchingUrlSchemes]; + [FBSDKInternalUtility deleteFacebookCookies]; +} + +- (void)tearDown +{ + [self resetCachedSettings]; + + [super tearDown]; +} + +// MARK: - Facebook URL - (void)testFacebookURL { + [self stubFacebookDomainPartWith:@""]; NSString *URLString; NSString *tier = [FBSDKSettings facebookDomainPart]; - [FBSDKSettings setFacebookDomainPart:@""]; URLString = [FBSDKInternalUtility facebookURLWithHostPrefix:@"" @@ -69,10 +113,12 @@ - (void)testFacebookURL URLString = [FBSDKInternalUtility facebookURLWithHostPrefix:@"m" path:@"dialog/share" - queryParameters:@{ @"key": @"value" } + queryParameters:@{ @"key" : @"value" } error:NULL].absoluteString; - XCTAssertEqualObjects(URLString, - @"https://m.facebook.com/" FBSDK_TARGET_PLATFORM_VERSION @"/dialog/share?key=value"); + XCTAssertEqualObjects( + URLString, + @"https://m.facebook.com/" FBSDK_TARGET_PLATFORM_VERSION @"/dialog/share?key=value" + ); URLString = [FBSDKInternalUtility facebookURLWithHostPrefix:@"m" path:@"/v1.0/dialog/share" @@ -129,7 +175,1053 @@ - (void)testFacebookURL defaultVersion:@"" error:NULL].absoluteString; XCTAssertEqualObjects(URLString, @"https://m.facebook.com/" FBSDK_TARGET_PLATFORM_VERSION @"/dialog/share"); +} + +// MARK: - Extracting Permissions + +- (void)testParsingPermissionsWithFuzzyValues +{ + NSMutableSet *grantedPermissions = [NSMutableSet set]; + NSMutableSet *declinedPermissions = [NSMutableSet set]; + NSMutableSet *expiredPermissions = [NSMutableSet set]; + + // A lack of a runtime crash is considered a success here. + for (int i = 0; i < 100; i++) { + [FBSDKInternalUtility extractPermissionsFromResponse:SampleRawRemotePermissionList.randomValues + grantedPermissions:grantedPermissions + declinedPermissions:declinedPermissions + expiredPermissions:expiredPermissions]; + } +} + +- (void)testExtractingPermissionsFromResponseWithInvalidTopLevelKey +{ + NSMutableSet *grantedPermissions = [NSMutableSet set]; + NSMutableSet *declinedPermissions = [NSMutableSet set]; + NSMutableSet *expiredPermissions = [NSMutableSet set]; + + [FBSDKInternalUtility extractPermissionsFromResponse:SampleRawRemotePermissionList.missingTopLevelKey + grantedPermissions:grantedPermissions + declinedPermissions:declinedPermissions + expiredPermissions:expiredPermissions]; + XCTAssertEqual(grantedPermissions.count, 0, "Should not add granted permissions if top level key is missing"); + XCTAssertEqual(declinedPermissions.count, 0, "Should not add declined permissions if top level key is missing"); + XCTAssertEqual(expiredPermissions.count, 0, "Should not add expired permissions if top level key is missing"); +} + +- (void)testExtractingPermissionsFromResponseWithMissingPermissions +{ + NSMutableSet *grantedPermissions = [NSMutableSet set]; + NSMutableSet *declinedPermissions = [NSMutableSet set]; + NSMutableSet *expiredPermissions = [NSMutableSet set]; + + [FBSDKInternalUtility extractPermissionsFromResponse:SampleRawRemotePermissionList.missingPermissions + grantedPermissions:grantedPermissions + declinedPermissions:declinedPermissions + expiredPermissions:expiredPermissions]; + XCTAssertEqual(grantedPermissions.count, 0, "Should not add missing granted permissions"); + XCTAssertEqual(declinedPermissions.count, 0, "Should not add missing declined permissions"); + XCTAssertEqual(expiredPermissions.count, 0, "Should not add missing expired permissions"); +} + +- (void)testExtractingPermissionsFromResponseWithMissingStatus +{ + NSMutableSet *grantedPermissions = [NSMutableSet set]; + NSMutableSet *declinedPermissions = [NSMutableSet set]; + NSMutableSet *expiredPermissions = [NSMutableSet set]; + + [FBSDKInternalUtility extractPermissionsFromResponse:SampleRawRemotePermissionList.missingStatus + grantedPermissions:grantedPermissions + declinedPermissions:declinedPermissions + expiredPermissions:expiredPermissions]; + XCTAssertEqual(grantedPermissions.count, 0, "Should not add a permission with a missing status"); + XCTAssertEqual(declinedPermissions.count, 0, "Should not add a permission with a missing status"); + XCTAssertEqual(expiredPermissions.count, 0, "Should not add a permission with a missing status"); +} + +- (void)testExtractingPermissionsFromResponseWithValidPermissions +{ + NSMutableSet *grantedPermissions = [NSMutableSet set]; + NSMutableSet *declinedPermissions = [NSMutableSet set]; + NSMutableSet *expiredPermissions = [NSMutableSet set]; + + [FBSDKInternalUtility extractPermissionsFromResponse:SampleRawRemotePermissionList.validAllStatuses + grantedPermissions:grantedPermissions + declinedPermissions:declinedPermissions + expiredPermissions:expiredPermissions]; + XCTAssertEqual(grantedPermissions.count, 1, "Should add granted permissions when available"); + XCTAssertTrue([grantedPermissions containsObject:@"email"], "Should add the correct permission to granted permissions"); + + XCTAssertEqual(declinedPermissions.count, 1, "Should add declined permissions when available"); + XCTAssertTrue([declinedPermissions containsObject:@"birthday"], "Should add the correct permission to declined permissions"); + + XCTAssertEqual(expiredPermissions.count, 1, "Should add expired permissions when available"); + XCTAssertTrue([expiredPermissions containsObject:@"first_name"], "Should add the correct permission to expired permissions"); +} + +// MARK: - Can open URL scheme + +- (void)testCanOpenUrlSchemeWithMissingScheme +{ + // Should not even bother checking a nil scheme + OCMReject([self.sharedApplicationMock canOpenURL:OCMArg.any]); + + XCTAssertFalse( + [FBSDKInternalUtility _canOpenURLScheme:nil], + "Should not be able to open a missing scheme" + ); +} + +- (void)testCanOpenUrlSchemeWithInvalidSchemes +{ + // Should not even bother checking invalid schemes + OCMReject([self.sharedApplicationMock canOpenURL:OCMArg.any]); + + NSArray *invalidSchemes = @[ + @"http:", + @"", + @" ", + @"#@%(*&#$(^#@!$", + @"////foo", + @"foo:", + @"foo:/" + ]; + + for (NSString *scheme in invalidSchemes) { + [FBSDKInternalUtility _canOpenURLScheme:scheme]; + } +} + +- (void)testCanOpenUrlSchemeWithValidSchemes +{ + NSArray *validSchemes = @[ + @"foo", + @"FOO", + @"foo+bar", + @"foo-bar", + @"foo.bar" + ]; + + for (NSString *scheme in validSchemes) { + [FBSDKInternalUtility _canOpenURLScheme:scheme]; + + NSURL *url = [NSURL URLWithString:[NSString stringWithFormat:@"%@:/", scheme]]; + OCMVerify([self.sharedApplicationMock canOpenURL:url]); + } +} + +// MARK: - App URL Scheme + +- (void)testAppURLSchemeWithMissingAppIdMissingSuffix +{ + [self stubAppID:nil]; + [self stubAppUrlSchemeSuffixWith:nil]; + // This is not desired behavior but accurately reflects what is currently written. + XCTAssertEqualObjects( + [FBSDKInternalUtility appURLScheme], + @"fb", + "Should return an app url scheme derived from the app id and app url scheme suffix" + ); +} + +- (void)testAppURLSchemeWithMissingAppIdInvalidSuffix +{ + [self stubAppID:nil]; + [self stubAppUrlSchemeSuffixWith:@" "]; + // This is not desired behavior but accurately reflects what is currently written. + XCTAssertEqualObjects( + [FBSDKInternalUtility appURLScheme], + @"fb ", + "Should return an app url scheme derived from the app id and app url scheme suffix" + ); +} + +- (void)testAppURLSchemeWithMissingAppIdValidSuffix +{ + [self stubAppID:nil]; + [self stubAppUrlSchemeSuffixWith:@"foo"]; + // This is not desired behavior but accurately reflects what is currently written. + XCTAssertEqualObjects( + [FBSDKInternalUtility appURLScheme], + @"fbfoo", + "Should return an app url scheme derived from the app id and app url scheme suffix" + ); +} + +- (void)testAppURLSchemeWithInvalidAppIdMissingSuffix +{ + [self stubAppID:@" "]; + [self stubAppUrlSchemeSuffixWith:nil]; + NSString *expected = [NSString stringWithFormat:@"fb%@", FBSDKSettings.appID]; + + // This is not desired behavior but accurately reflects what is currently written. + XCTAssertEqualObjects( + [FBSDKInternalUtility appURLScheme], + expected, + "Should return an app url scheme derived app id and app url scheme suffix defined in settings" + ); +} + +- (void)testAppURLSchemeWithInvalidAppIdInvalidSuffix +{ + [self stubAppID:@" "]; + [self stubAppUrlSchemeSuffixWith:@" "]; + NSString *expected = [NSString stringWithFormat:@"fb%@%@", FBSDKSettings.appID, FBSDKSettings.appURLSchemeSuffix]; + + // This is not desired behavior but accurately reflects what is currently written. + XCTAssertEqualObjects( + [FBSDKInternalUtility appURLScheme], + expected, + "Should return an app url scheme derived app id and app url scheme suffix defined in settings" + ); +} + +- (void)testAppURLSchemeWithInvalidAppIdValidSuffix +{ + [self stubAppID:@" "]; + [self stubAppUrlSchemeSuffixWith:@"foo"]; + NSString *expected = [NSString stringWithFormat:@"fb %@", FBSDKSettings.appURLSchemeSuffix]; + + // This is not desired behavior but accurately reflects what is currently written. + XCTAssertEqualObjects( + [FBSDKInternalUtility appURLScheme], + expected, + "Should return an app url scheme derived app id and app url scheme suffix defined in settings" + ); +} + +- (void)testAppURLSchemeWithValidAppIdMissingSuffix +{ + [self stubAppID:self.appID]; + [self stubAppUrlSchemeSuffixWith:nil]; + NSString *expected = [NSString stringWithFormat:@"fb%@", FBSDKSettings.appID]; + + XCTAssertEqualObjects( + [FBSDKInternalUtility appURLScheme], + expected, + "Should return an app url scheme derived app id and app url scheme suffix defined in settings" + ); +} + +- (void)testAppURLSchemeWithValidAppIdInvalidSuffix +{ + [self stubAppID:self.appID]; + [self stubAppUrlSchemeSuffixWith:@" "]; + NSString *expected = [NSString stringWithFormat:@"fb%@%@", FBSDKSettings.appID, FBSDKSettings.appURLSchemeSuffix]; + + // This is not desired behavior but accurately reflects what is currently written. + XCTAssertEqualObjects( + [FBSDKInternalUtility appURLScheme], + expected, + "Should return an app url scheme derived app id and app url scheme suffix defined in settings" + ); +} + +- (void)testAppURLSchemeWithValidAppIdValidSuffix +{ + [self stubAppID:self.appID]; + [self stubAppUrlSchemeSuffixWith:@"foo"]; + NSString *expected = [NSString stringWithFormat:@"fb%@%@", FBSDKSettings.appID, FBSDKSettings.appURLSchemeSuffix]; + + XCTAssertEqualObjects( + [FBSDKInternalUtility appURLScheme], + expected, + "Should return an app url scheme derived app id and app url scheme suffix defined in settings" + ); +} + +// MARK: - App URL with host + +- (void)testAppUrlWithEmptyHost +{ + [self stubAppID:self.appID]; + [self stubAppUrlSchemeSuffixWith:@"foo"]; + + NSURL *url = [FBSDKInternalUtility appURLWithHost:@"" path:validPath queryParameters:self.validParameters error:nil]; + + XCTAssertNil(url.host, "Should not set an empty host."); +} + +- (void)testAppUrlWithValidHost +{ + [self stubAppID:self.appID]; + [self stubAppUrlSchemeSuffixWith:@"foo"]; + + NSURL *url = [FBSDKInternalUtility appURLWithHost:@"facebook" path:validPath queryParameters:self.validParameters error:nil]; + + XCTAssertEqualObjects(url.host, @"facebook", "Should set the expected host."); +} + +// MARK: - Check registered can open url scheme + +- (void)testCheckRegisteredCanOpenURLScheme +{ + NSString *scheme = @"foo"; + + [FBSDKInternalUtility checkRegisteredCanOpenURLScheme:scheme]; + + OCMVerify([self.internalUtilityClassMock isRegisteredCanOpenURLScheme:scheme]); +} + +- (void)testCheckRegisteredCanOpenURLSchemeMultipleTimes +{ + NSString *scheme = @"foo"; + + [FBSDKInternalUtility checkRegisteredCanOpenURLScheme:scheme]; + + // Should only check for a scheme a single time. + OCMReject([self.internalUtilityClassMock isRegisteredCanOpenURLScheme:scheme]); + + [FBSDKInternalUtility checkRegisteredCanOpenURLScheme:scheme]; +} + +// MARK: - Dictionary from FBURL + +- (void)testWithAuthorizeHostNoParameters +{ + NSURL *url = [[NSURL alloc] initWithString:@"foo://authorize"]; + NSDictionary *parameters = [FBSDKInternalUtility parametersFromFBURL:url]; + + XCTAssertEqualObjects( + parameters, + @{}, + "Should not extract parameters from a url if there are none" + ); +} + +- (void)testWithAuthorizeHostNoFragment +{ + NSURL *url = [[NSURL alloc] initWithString:@"foo://authorize?foo=bar"]; + NSDictionary *parameters = [FBSDKInternalUtility parametersFromFBURL:url]; + + XCTAssertEqualObjects( + parameters, + @{@"foo" : @"bar"}, + "Should extract parameters from a url" + ); +} + +- (void)testWithAuthorizeHostAndFragment +{ + NSURL *url = [[NSURL alloc] initWithString:@"foo://authorize?foo=bar#param1=value1¶m2=value2"]; + NSDictionary *parameters = [FBSDKInternalUtility parametersFromFBURL:url]; + NSDictionary *expectedParameters = @{ + @"foo" : @"bar", + @"param1" : @"value1", + @"param2" : @"value2" + }; + + XCTAssertEqualObjects( + parameters, + expectedParameters, + "Extracted parameters from an auth url should include fragment parameters" + ); +} + +- (void)testWithoutAuthorizeHostNoParameters +{ + NSURL *url = [[NSURL alloc] initWithString:@"foo://example"]; + NSDictionary *parameters = [FBSDKInternalUtility parametersFromFBURL:url]; + + XCTAssertEqualObjects( + parameters, + @{}, + "Should not extract parameters from a url if there are none" + ); +} + +- (void)testWithoutAuthorizeHostNoFragment +{ + NSURL *url = [[NSURL alloc] initWithString:@"foo://example?foo=bar"]; + NSDictionary *parameters = [FBSDKInternalUtility parametersFromFBURL:url]; + + XCTAssertEqualObjects( + parameters, + @{@"foo" : @"bar"}, + "Should extract parameters from a url" + ); +} + +- (void)testWithoutAuthorizeHostWithFragment +{ + NSURL *url = [[NSURL alloc] initWithString:@"foo://example?foo=bar#param1=value1¶m2=value2"]; + NSDictionary *parameters = [FBSDKInternalUtility parametersFromFBURL:url]; + NSDictionary *expectedParameters = @{ @"foo" : @"bar" }; + + XCTAssertEqualObjects( + parameters, + expectedParameters, + "Extracted parameters from a non auth url should not include fragment parameters" + ); +} + +// MARK: - Cookies + +- (void)testDeletingDialogCookies +{ + NSHTTPCookie *cookie1 = [self cookieForUrl:self.dialogUrl]; + NSHTTPCookie *cookie2 = [self cookieForUrl:self.dialogUrl name:@"cookie 2"]; + + [NSHTTPCookieStorage.sharedHTTPCookieStorage setCookie:cookie1]; + [NSHTTPCookieStorage.sharedHTTPCookieStorage setCookie:cookie2]; + + NSArray *cookies = [NSHTTPCookieStorage.sharedHTTPCookieStorage cookiesForURL:self.dialogUrl]; + NSArray *expectedCookies = @[cookie1, cookie2]; + + XCTAssertEqualObjects(cookies, expectedCookies, "Sanity check that there are cookies to delete"); + + [FBSDKInternalUtility deleteFacebookCookies]; + XCTAssertEqual( + [NSHTTPCookieStorage.sharedHTTPCookieStorage cookiesForURL:self.dialogUrl], + @[], + "All cookies for the facebook dialog url should be deleted" + ); +} + +- (void)testDeletingNonDialogCookies +{ + NSHTTPCookie *cookie = [self cookieForUrl:self.nonDialogUrl]; + [NSHTTPCookieStorage.sharedHTTPCookieStorage setCookie:cookie]; + + NSArray *cookies = [NSHTTPCookieStorage.sharedHTTPCookieStorage cookiesForURL:self.nonDialogUrl]; + XCTAssertEqualObjects(cookies, @[cookie], "Sanity check that there are cookies to delete"); + + [FBSDKInternalUtility deleteFacebookCookies]; + XCTAssertEqualObjects( + [NSHTTPCookieStorage.sharedHTTPCookieStorage cookiesForURL:self.nonDialogUrl], + @[cookie], + "Should only delete cookies for the dialog url" + ); +} + +- (void)testDeletingMixOfCookies +{ + NSHTTPCookie *dialogCookie = [self cookieForUrl:self.dialogUrl]; + NSHTTPCookie *nonDialogCookie = [self cookieForUrl:self.nonDialogUrl]; + + [NSHTTPCookieStorage.sharedHTTPCookieStorage setCookie:dialogCookie]; + [NSHTTPCookieStorage.sharedHTTPCookieStorage setCookie:nonDialogCookie]; + + [FBSDKInternalUtility deleteFacebookCookies]; + + XCTAssertEqualObjects( + [NSHTTPCookieStorage.sharedHTTPCookieStorage cookiesForURL:self.dialogUrl], + @[], + "Should delete cookies for the dialog url" + ); + XCTAssertEqualObjects( + [NSHTTPCookieStorage.sharedHTTPCookieStorage cookiesForURL:self.nonDialogUrl], + @[nonDialogCookie], + "Should only delete cookies for the dialog url" + ); +} + +// MARK: - App Installation + +- (void)testIsRegisteredCanOpenURLSchemeWithMissingScheme +{ + NSArray *querySchemes = @[]; + id bundle = [FakeBundle bundleWithDictionary:@{@"LSApplicationQueriesSchemes" : querySchemes}]; + [self stubMainBundleWith:bundle]; + + XCTAssertFalse( + [FBSDKInternalUtility isRegisteredCanOpenURLScheme:self.name], + "Should not be consider a scheme to be registered if it's missing from the application query schemes" + ); +} + +- (void)testIsRegisteredCanOpenURLSchemeWithScheme +{ + NSArray *querySchemes = @[self.name]; + id bundle = [FakeBundle bundleWithDictionary:@{@"LSApplicationQueriesSchemes" : querySchemes}]; + [self stubMainBundleWith:bundle]; + + XCTAssertTrue( + [FBSDKInternalUtility isRegisteredCanOpenURLScheme:self.name], + "Should consider a scheme to be registered if it exists in the application query schemes" + ); +} + +- (void)testIsRegisteredCanOpenURLSchemeCache +{ + NSArray *querySchemes = @[self.name]; + id bundle = [FakeBundle bundleWithDictionary:@{@"LSApplicationQueriesSchemes" : querySchemes}]; + [self stubMainBundleWith:bundle]; + + XCTAssertTrue([FBSDKInternalUtility isRegisteredCanOpenURLScheme:self.name], "Sanity check"); + + [bundle setInfoDictionary:@{}]; + + XCTAssertTrue([FBSDKInternalUtility isRegisteredCanOpenURLScheme:self.name], "Should return the cached value of the main bundle and not the updated values"); +} + +- (void)testFacebookAppInstalledMissingQuerySchemes +{ + id bundle = [FakeBundle bundleWithDictionary:@{}]; + [self stubMainBundleWith:bundle]; + + [FBSDKInternalUtility isFacebookAppInstalled]; + + OCMVerify([self.loggerClassMock singleShotLogEntry:FBSDKLoggingBehaviorDeveloperErrors logEntry:facebookUrlSchemeMissingMessage]); +} + +- (void)testFacebookAppInstalledEmptyQuerySchemes +{ + NSArray *querySchemes = @[]; + id bundle = [FakeBundle bundleWithDictionary:@{@"LSApplicationQueriesSchemes" : querySchemes}]; + [self stubMainBundleWith:bundle]; + + [FBSDKInternalUtility isFacebookAppInstalled]; + + OCMVerify([self.loggerClassMock singleShotLogEntry:FBSDKLoggingBehaviorDeveloperErrors logEntry:facebookUrlSchemeMissingMessage]); +} + +- (void)testFacebookAppInstalledMissingQueryScheme +{ + NSArray *querySchemes = @[@"Foo"]; + id bundle = [FakeBundle bundleWithDictionary:@{@"LSApplicationQueriesSchemes" : querySchemes}]; + [self stubMainBundleWith:bundle]; + + [FBSDKInternalUtility isFacebookAppInstalled]; + + OCMVerify([self.loggerClassMock singleShotLogEntry:FBSDKLoggingBehaviorDeveloperErrors logEntry:facebookUrlSchemeMissingMessage]); +} + +- (void)testFacebookAppInstalledValidQueryScheme +{ + NSArray *querySchemes = @[@"fbauth2"]; + id bundle = [FakeBundle bundleWithDictionary:@{@"LSApplicationQueriesSchemes" : querySchemes}]; + [self stubMainBundleWith:bundle]; + + OCMReject([self.loggerClassMock singleShotLogEntry:FBSDKLoggingBehaviorDeveloperErrors logEntry:OCMArg.any]); + + [FBSDKInternalUtility isFacebookAppInstalled]; +} + +- (void)testFacebookAppInstalledCache +{ + id bundle = [FakeBundle bundleWithDictionary:@{}]; + [self stubMainBundleWith:bundle]; + + [FBSDKInternalUtility isFacebookAppInstalled]; + + OCMVerify([self.loggerClassMock singleShotLogEntry:FBSDKLoggingBehaviorDeveloperErrors logEntry:facebookUrlSchemeMissingMessage]); + OCMReject([self.loggerClassMock singleShotLogEntry:FBSDKLoggingBehaviorDeveloperErrors logEntry:OCMArg.any]); + + [FBSDKInternalUtility isFacebookAppInstalled]; +} + +- (void)testMessengerAppInstalledMissingQuerySchemes +{ + id bundle = [FakeBundle bundleWithDictionary:@{}]; + [self stubMainBundleWith:bundle]; + + [FBSDKInternalUtility isMessengerAppInstalled]; + + OCMVerify([self.loggerClassMock singleShotLogEntry:FBSDKLoggingBehaviorDeveloperErrors logEntry:messengerUrlSchemeMissingMessage]); +} + +- (void)testMessengerAppInstalledEmptyQuerySchemes +{ + NSArray *querySchemes = @[]; + id bundle = [FakeBundle bundleWithDictionary:@{@"LSApplicationQueriesSchemes" : querySchemes}]; + [self stubMainBundleWith:bundle]; + + [FBSDKInternalUtility isMessengerAppInstalled]; + + OCMVerify([self.loggerClassMock singleShotLogEntry:FBSDKLoggingBehaviorDeveloperErrors logEntry:messengerUrlSchemeMissingMessage]); +} + +- (void)testMessengerAppInstalledMissingQueryScheme +{ + NSArray *querySchemes = @[@"Foo"]; + id bundle = [FakeBundle bundleWithDictionary:@{@"LSApplicationQueriesSchemes" : querySchemes}]; + [self stubMainBundleWith:bundle]; + + [FBSDKInternalUtility isMessengerAppInstalled]; + + OCMVerify([self.loggerClassMock singleShotLogEntry:FBSDKLoggingBehaviorDeveloperErrors logEntry:messengerUrlSchemeMissingMessage]); +} + +- (void)testMessengerAppInstalledValidQueryScheme +{ + NSArray *querySchemes = @[@"fb-messenger-share-api"]; + id bundle = [FakeBundle bundleWithDictionary:@{@"LSApplicationQueriesSchemes" : querySchemes}]; + [self stubMainBundleWith:bundle]; + + OCMReject([self.loggerClassMock singleShotLogEntry:FBSDKLoggingBehaviorDeveloperErrors logEntry:OCMArg.any]); + + [FBSDKInternalUtility isMessengerAppInstalled]; +} + +- (void)testMessengerAppInstalledCache +{ + id bundle = [FakeBundle bundleWithDictionary:@{}]; + [self stubMainBundleWith:bundle]; + + [FBSDKInternalUtility isMessengerAppInstalled]; + + OCMVerify([self.loggerClassMock singleShotLogEntry:FBSDKLoggingBehaviorDeveloperErrors logEntry:messengerUrlSchemeMissingMessage]); + OCMReject([self.loggerClassMock singleShotLogEntry:FBSDKLoggingBehaviorDeveloperErrors logEntry:OCMArg.any]); + + [FBSDKInternalUtility isMessengerAppInstalled]; +} + +- (void)testMSQRDPlayerAppInstalledMissingQuerySchemes +{ + id bundle = [FakeBundle bundleWithDictionary:@{}]; + [self stubMainBundleWith:bundle]; + + [FBSDKInternalUtility isMSQRDPlayerAppInstalled]; + + OCMVerify([self.loggerClassMock singleShotLogEntry:FBSDKLoggingBehaviorDeveloperErrors logEntry:msqrdPlayerUrlSchemeMissingMessage]); +} + +- (void)testMSQRDPlayerAppInstalledEmptyQuerySchemes +{ + NSArray *querySchemes = @[]; + id bundle = [FakeBundle bundleWithDictionary:@{@"LSApplicationQueriesSchemes" : querySchemes}]; + [self stubMainBundleWith:bundle]; + + [FBSDKInternalUtility isMSQRDPlayerAppInstalled]; + + OCMVerify([self.loggerClassMock singleShotLogEntry:FBSDKLoggingBehaviorDeveloperErrors logEntry:msqrdPlayerUrlSchemeMissingMessage]); +} + +- (void)testMSQRDPlayerAppInstalledMissingQueryScheme +{ + NSArray *querySchemes = @[@"Foo"]; + id bundle = [FakeBundle bundleWithDictionary:@{@"LSApplicationQueriesSchemes" : querySchemes}]; + [self stubMainBundleWith:bundle]; + + [FBSDKInternalUtility isMSQRDPlayerAppInstalled]; + + OCMVerify([self.loggerClassMock singleShotLogEntry:FBSDKLoggingBehaviorDeveloperErrors logEntry:msqrdPlayerUrlSchemeMissingMessage]); +} + +- (void)testMSQRDPlayerAppInstalledValidQueryScheme +{ + NSArray *querySchemes = @[@"msqrdplayer"]; + id bundle = [FakeBundle bundleWithDictionary:@{@"LSApplicationQueriesSchemes" : querySchemes}]; + [self stubMainBundleWith:bundle]; + + OCMReject([self.loggerClassMock singleShotLogEntry:FBSDKLoggingBehaviorDeveloperErrors logEntry:OCMArg.any]); + [FBSDKInternalUtility isMSQRDPlayerAppInstalled]; +} + +- (void)testMSQRDPlayerAppInstalledCache +{ + id bundle = [FakeBundle bundleWithDictionary:@{}]; + [self stubMainBundleWith:bundle]; + + [FBSDKInternalUtility isMSQRDPlayerAppInstalled]; + + OCMVerify([self.loggerClassMock singleShotLogEntry:FBSDKLoggingBehaviorDeveloperErrors logEntry:msqrdPlayerUrlSchemeMissingMessage]); + OCMReject([self.loggerClassMock singleShotLogEntry:FBSDKLoggingBehaviorDeveloperErrors logEntry:OCMArg.any]); + + [FBSDKInternalUtility isMSQRDPlayerAppInstalled]; +} + +// MARK: - Random Utility Methods + +- (void)testIsBrowserURLWithNonBrowserURL +{ + NSArray *urls = @[ + [NSURL URLWithString:@"file://foo"], + [NSURL URLWithString:@"example://bar"] + ]; + + for (NSURL *url in urls) { + XCTAssertFalse([FBSDKInternalUtility isBrowserURL:url], "%@ should not be considered a browser url", url.absoluteString); + } +} + +- (void)testIsBrowserURLWithBrowserURL +{ + NSArray *urls = @[ + [NSURL URLWithString:@"HTTPS://example.com"], + [NSURL URLWithString:@"HTTP://example.com"], + [NSURL URLWithString:@"https://example.com"], + [NSURL URLWithString:@"http://example.com"], + ]; + + for (NSURL *url in urls) { + XCTAssertTrue([FBSDKInternalUtility isBrowserURL:url], "%@ should be considered a browser url", url.absoluteString); + } +} + +- (void)testIsFacebookBundleIdentifierWithInvalidIdentifiers +{ + NSArray *identifiers = @[ + @"", + @"foo", + @"com.foo.bar", + @"com.facebook" + ]; + + for (NSString *identifier in identifiers) { + XCTAssertFalse([FBSDKInternalUtility isFacebookBundleIdentifier:identifier], "%@ should not be considered a facebook bundle indentifier", identifier); + } +} + +- (void)testIsFacebookBundleIdentifierWithValidIdentifiers +{ + NSArray *identifiers = @[ + @"com.facebook.", + @"com.facebook.foo", + @".com.facebook.", + @".com.facebook.foo" + ]; + + for (NSString *identifier in identifiers) { + XCTAssertTrue([FBSDKInternalUtility isFacebookBundleIdentifier:identifier], "%@ should be considered a facebook bundle indentifier", identifier); + } +} + +- (void)testNonSafariBundleIdentifiers +{ + NSArray *identifiers = @[ + @"", + @" ", + @"com.foo" + ]; + + for (NSString *identifier in identifiers) { + XCTAssertFalse( + [FBSDKInternalUtility isSafariBundleIdentifier:identifier], + "%@ should not be considered a safari bundle identifier", + identifier + ); + } +} + +- (void)testSafariBundleIdentifiers +{ + NSArray *identifiers = @[ + @"com.apple.mobilesafari", + @"com.apple.SafariViewService", + ]; + + for (NSString *identifier in identifiers) { + XCTAssertTrue( + [FBSDKInternalUtility isSafariBundleIdentifier:identifier], + "%@ should be considered a safari bundle identifier", + identifier + ); + } +} + +- (void)testValidatingAppID +{ + [self stubAppID:nil]; + + XCTAssertThrows([FBSDKInternalUtility validateAppID]); +} + +- (void)testValidateClientAccessTokenWithoutClientTokenWithoutAppID +{ + [self stubAppID:nil]; + [self stubClientTokenWith:nil]; + + XCTAssertThrows([FBSDKInternalUtility validateRequiredClientAccessToken]); +} + +- (void)testValidateClientAccessTokenWithClientTokenWithoutAppID +{ + [self stubAppID:nil]; + [self stubClientTokenWith:@"client123"]; + + XCTAssertEqualObjects( + [FBSDKInternalUtility validateRequiredClientAccessToken], + @"(null)|client123", + "A valid client-access token should include the app identifier and the client token" + ); +} + +- (void)testValidateClientAccessTokenWithClientTokenWithAppID +{ + [self stubAppID:self.appID]; + [self stubClientTokenWith:@"client123"]; + + XCTAssertEqualObjects( + [FBSDKInternalUtility validateRequiredClientAccessToken], + @"appid|client123", + "A valid client-access token should include the app identifier and the client token" + ); +} + +- (void)testValidateClientAccessTokenWithoutClientTokenWithAppID +{ + [self stubAppID:self.appID]; + [self stubClientTokenWith:nil]; + + XCTAssertThrows([FBSDKInternalUtility validateRequiredClientAccessToken]); +} + +- (void)testIsRegisteredUrlSchemeWithRegisteredScheme +{ + FakeBundle *bundle = [self bundleWithRegisteredUrlSchemes:@[@"com.foo.bar"]]; + [self stubMainBundleWith:bundle]; + + XCTAssertTrue([FBSDKInternalUtility isRegisteredURLScheme:@"com.foo.bar"], "Schemes in the bundle should be considered registered"); +} + +- (void)testIsRegisteredUrlSchemeWithoutRegisteredScheme +{ + FakeBundle *bundle = [self bundleWithRegisteredUrlSchemes:@[@"com.foo.bar"]]; + [self stubMainBundleWith:bundle]; + + XCTAssertFalse([FBSDKInternalUtility isRegisteredURLScheme:@"com.facebook"], "Schemes absent from the bundle should not be considered registered"); +} + +- (void)testIsRegisteredUrlSchemeCaching +{ + // Should fetch bundle + [FBSDKInternalUtility isRegisteredURLScheme:@"com.facebook"]; + + OCMVerify(ClassMethod([self.nsBundleClassMock mainBundle])); + OCMReject(ClassMethod([self.nsBundleClassMock mainBundle])); + + // Should not fetch bundle + [FBSDKInternalUtility isRegisteredURLScheme:@"com.facebook"]; +} + +- (void)testValidatingUrlSchemesWithoutAppID +{ + [self stubAppID:nil]; + + XCTAssertThrows( + [FBSDKInternalUtility validateURLSchemes], + "Cannot validate url schemes without an app identifier" + ); +} + +- (void)testValidatingUrlSchemesWithAppIdMatchingBundleEntry +{ + [self stubAppID:self.appID]; + [self stubAppUrlSchemeSuffixWith:nil]; + + FakeBundle *bundle = [self bundleWithRegisteredUrlSchemes:@[@"fbappid"]]; + [self stubMainBundleWith:bundle]; + + XCTAssertNoThrow( + [FBSDKInternalUtility validateURLSchemes], + "The registered app url scheme must match the app id and url scheme suffix prepended with 'fb'" + ); +} + +- (void)testValidatingUrlSchemesWithNonAppIdMatchingBundleEntry +{ + [self stubAppID:self.appID]; + [self stubAppUrlSchemeSuffixWith:nil]; + + FakeBundle *bundle = [self bundleWithRegisteredUrlSchemes:@[@"fb123"]]; + [self stubMainBundleWith:bundle]; + + XCTAssertThrows( + [FBSDKInternalUtility validateURLSchemes], + "The registered app url scheme must match the app id and url scheme suffix prepended with 'fb'" + ); +} + +// We can't loop through these because of how stubbing works. +- (void)testValidatingFacebookUrlSchemes_auth +{ + FakeBundle *bundle = [self bundleWithRegisteredUrlSchemes:@[@"fbauth2"]]; + [self stubMainBundleWith:bundle]; + XCTAssertThrows([FBSDKInternalUtility validateFacebookReservedURLSchemes], "Should throw an error if fbauth2 is present in the bundle url schemes"); +} + +// We can't loop through these because of how stubbing works. +- (void)testValidatingFacebookUrlSchemes_api +{ + FakeBundle *bundle = [self bundleWithRegisteredUrlSchemes:@[@"fbapi"]]; + [self stubMainBundleWith:bundle]; + XCTAssertThrows([FBSDKInternalUtility validateFacebookReservedURLSchemes], "Should throw an error if fbapi is present in the bundle url schemes"); +} + +// We can't loop through these because of how stubbing works. +- (void)testValidatingFacebookUrlSchemes_messenger +{ + FakeBundle *bundle = [self bundleWithRegisteredUrlSchemes:@[@"fb-messenger-share-api"]]; + [self stubMainBundleWith:bundle]; + XCTAssertThrows([FBSDKInternalUtility validateFacebookReservedURLSchemes], "Should throw an error if fb-messenger-share-api is present in the bundle url schemes"); +} + +// We can't loop through these because of how stubbing works. +- (void)testValidatingFacebookUrlSchemes_shareextension +{ + FakeBundle *bundle = [self bundleWithRegisteredUrlSchemes:@[@"fbshareextension"]]; + [self stubMainBundleWith:bundle]; + XCTAssertThrows([FBSDKInternalUtility validateFacebookReservedURLSchemes], "Should throw an error if fbshareextension is present in the bundle url schemes"); +} + +- (void)testIsPublishPermission +{ + NSArray *publishPermissions = @[ + @"publish", + @"publishSomething", + @"manage", + @"manageSomething", + @"ads_management", + @"create_event", + @"rsvp_event" + ]; + for (NSString *permission in publishPermissions) { + XCTAssertTrue([FBSDKInternalUtility isPublishPermission:permission]); + } + + NSArray *nonPublishPermissions = @[ + @"", + @"email", + @"_publish" + ]; + for (NSString *permission in nonPublishPermissions) { + XCTAssertFalse([FBSDKInternalUtility isPublishPermission:permission]); + } +} + +- (void)testIsUnityWithMissingSuffix +{ + [self stubUserAgentSuffixWith:nil]; + XCTAssertFalse([FBSDKInternalUtility isUnity], "User agent should determine whether an app is Unity"); +} + +- (void)testIsUnityWithNonUnitySuffix +{ + [self stubUserAgentSuffixWith:@"Foo"]; + XCTAssertFalse([FBSDKInternalUtility isUnity], "User agent should determine whether an app is Unity"); +} + +- (void)testIsUnityWithUnitySuffix +{ + [self stubUserAgentSuffixWith:@"__Unity__"]; + XCTAssertTrue([FBSDKInternalUtility isUnity], "User agent should determine whether an app is Unity"); +} + +- (void)testHexadecimalStringFromData +{ + XCTAssertNil([FBSDKInternalUtility hexadecimalStringFromData:NSData.data]); + + NSString *foo = @"foo"; + NSData *stringData = [foo dataUsingEncoding:NSUTF8StringEncoding]; + NSString *expected = @"666f6f"; + + XCTAssertEqualObjects([FBSDKInternalUtility hexadecimalStringFromData:stringData], expected); +} + +- (void)testObjectIsEqualToObject +{ + id obj1 = @"foo"; + id obj2 = @"foo"; + id obj3 = @"bar"; + + XCTAssertTrue([FBSDKInternalUtility object:obj1 isEqualToObject:obj1]); + XCTAssertTrue([FBSDKInternalUtility object:obj1 isEqualToObject:obj2]); + XCTAssertFalse([FBSDKInternalUtility object:obj1 isEqualToObject:obj3]); + + obj1 = nil; + XCTAssertFalse([FBSDKInternalUtility object:obj1 isEqualToObject:obj2]); + XCTAssertFalse([FBSDKInternalUtility object:obj2 isEqualToObject:obj1]); +} + +- (void)testCreatingUrlWithUnknownError // InvalidQueryString +{ + NSError *error = [NSError errorWithDomain:@"test" code:1 userInfo:nil]; + + [FBSDKInternalUtility URLWithScheme:@"https" host:@"example" path:@"/foo" queryParameters:@{@"a date" : NSDate.date} error:&error]; + + XCTAssertEqualObjects( + error.domain, + @"com.facebook.sdk.core", + "Creating a url with an error reference should repopulate the error domain correctly" + ); + XCTAssertEqual( + error.code, + 3, + "Creating a url with an error reference should repopulate the error code correctly" + ); + XCTAssertEqualObjects( + [error.userInfo objectForKey:FBSDKErrorDeveloperMessageKey], + @"Unknown error building URL.", + "Creating a url with an error reference should repopulate the error message correctly" + ); +} + +- (void)testCreatingUrlWithInvalidQueryString +{ + NSError *error = [NSError errorWithDomain:@"test" code:1 userInfo:nil]; + + [FBSDKInternalUtility URLWithScheme:@"https" host:@"example" path:@"/foo" queryParameters:@{@[] : @"foo"} error:&error]; + + XCTAssertEqualObjects( + error.domain, + @"com.facebook.sdk.core", + "Creating a url with invalid parameters should repopulate the error domain correctly" + ); + XCTAssertEqual( + error.code, + 2, + "Creating a url with invalid parameters should repopulate the error code correctly" + ); + + XCTAssertTrue( + [[error.userInfo objectForKey:FBSDKErrorDeveloperMessageKey] hasPrefix:@"Invalid value for queryParameters:"], + "Creating a url with invalid parameters should repopulate the error message correctly" + ); +} + +// MARK: - Helpers + +- (NSURL *)dialogUrl +{ + return [FBSDKInternalUtility facebookURLWithHostPrefix:@"m." + path:@"/dialog/" + queryParameters:@{} + error:NULL]; +} + +- (NSURL *)nonDialogUrl +{ + return [FBSDKInternalUtility facebookURLWithHostPrefix:@"m." + path:@"/foo/" + queryParameters:@{} + error:NULL]; +} + +- (NSHTTPCookie *)cookieForUrl:(NSURL *)url name:(NSString *)name +{ + return [NSHTTPCookie cookieWithProperties:@{ + NSHTTPCookieOriginURL : url, + NSHTTPCookiePath : url.path, + NSHTTPCookieName : name, + NSHTTPCookieValue : @"Is good" + }]; +} + +- (NSHTTPCookie *)cookieForUrl:(NSURL *)url +{ + return [self cookieForUrl:url name:@"MyCookie"]; +} + +- (NSDictionary *)validParameters +{ + return @{@"foo" : @"bar"}; +} + +NSString *const validPath = @"example"; +NSString *const facebookUrlSchemeMissingMessage = @"fbauth2 is missing from your Info.plist under LSApplicationQueriesSchemes and is required."; +NSString *const messengerUrlSchemeMissingMessage = @"fb-messenger-share-api is missing from your Info.plist under LSApplicationQueriesSchemes and is required."; +NSString *const msqrdPlayerUrlSchemeMissingMessage = @"msqrdplayer is missing from your Info.plist under LSApplicationQueriesSchemes and is required."; + +- (FakeBundle *)bundleWithRegisteredUrlSchemes:(NSArray *)schemes +{ + return [FakeBundle bundleWithDictionary:@{ + @"CFBundleURLTypes" : @[ + @{ @"CFBundleURLSchemes" : schemes } + ] + }]; } @end diff --git a/FBSDKCoreKit/FBSDKCoreKitTests/Internal/FBSDKJSONValueTests.m b/FBSDKCoreKit/FBSDKCoreKitTests/Internal/FBSDKJSONValueTests.m new file mode 100644 index 0000000000..d12613e655 --- /dev/null +++ b/FBSDKCoreKit/FBSDKCoreKitTests/Internal/FBSDKJSONValueTests.m @@ -0,0 +1,137 @@ +// 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 "FBSDKJSONValue.h" + +@interface FBSDKJSONValueTests : XCTestCase +@end + +@implementation FBSDKJSONValueTests + +- (void)testReturnsNilForBadInputs +{ + NSError *e; + XCTAssertNil(FBSDKCreateJSONFromString(nil, &e)); + XCTAssertNil(FBSDKCreateJSONFromString(@"THIS IS NOT JSON", &e)); + XCTAssertNil(FBSDKCreateJSONFromString(@"null", &e)); + + // NSData should not be a valid entry in the dictionary to become JSON. + XCTAssertNil( + [[FBSDKJSONValue alloc] initWithPotentialJSONObject:@{ + @"id" : [@"BLAH" dataUsingEncoding:NSUTF8StringEncoding] + }] + ); +} + +- (void)testArrayMatcher +{ + NSError *e; + FBSDKJSONValue *const v = FBSDKCreateJSONFromString(@"[1,2,3,4]", &e); + + __block NSArray *actual = nil; + [v matchArray:^(NSArray *a) { + actual = a; + } dictionary:nil]; + + int i = 1; + for (FBSDKJSONField *field in actual) { + XCTAssertEqualObjects(field.rawObject, @(i++)); + } +} + +- (void)testDictMatcher +{ + NSError *e; + FBSDKJSONValue *const v = FBSDKCreateJSONFromString(@"{\"id\":5}", &e); + + __block NSDictionary *actual = nil; + [v matchArray:nil dictionary:^(NSDictionary *d) {actual = d; }]; + + XCTAssertEqualObjects(actual[@"id"].rawObject, @5); +} + +- (void)testDictMatchersThatDontUseBlocks +{ + FBSDKJSONValue *const v = FBSDKCreateJSONFromString(@"{\"id\":5}", nil); + XCTAssertEqualObjects([v matchDictionaryOrNil][@"id"].rawObject, @5); + XCTAssertEqualObjects([v unsafe_matchDictionaryOrNil][@"id"], @5); + + XCTAssertNil([v unsafe_matchArrayOrNil]); + XCTAssertNil([v matchArrayOrNil]); +} + +- (void)testArrayMatchersThatDontUseBlocks +{ + FBSDKJSONValue *const v = FBSDKCreateJSONFromString(@"[5]", nil); + XCTAssertEqualObjects([v matchArrayOrNil][0].rawObject, @5); + XCTAssertEqualObjects([v unsafe_matchArrayOrNil][0], @5); + + XCTAssertNil([v unsafe_matchDictionaryOrNil]); + XCTAssertNil([v matchDictionaryOrNil]); +} + +#pragma mark - FBSDKJSONField + +- (void)testFieldMatchers +{ + NSError *e; + FBSDKJSONValue *const v = FBSDKCreateJSONFromString(@"[1,\"hi\",null,[1,2,3],{\"key\": \"value\"}]", &e); + + __block NSArray *actual = nil; + [v matchArray:^(NSArray *a) { actual = a; } dictionary:nil]; + + NSArray *const a = @[@1, @2, @3]; + NSDictionary *const d = @{@"key" : @"value"}; + XCTAssertEqualObjects(actual[0].rawObject, @(1)); + XCTAssertEqualObjects([actual[0] numberOrNil], @(1)); + XCTAssertNil([actual[0] stringOrNil]); + + XCTAssertEqualObjects(actual[1].rawObject, @"hi"); + XCTAssertEqualObjects([actual[1] stringOrNil], @"hi"); + XCTAssertNil([actual[1] numberOrNil]); + + XCTAssertEqualObjects(actual[2].rawObject, [NSNull null]); + XCTAssertEqualObjects([actual[2] nullOrNil], [NSNull null]); + XCTAssertNil([actual[2] stringOrNil]); + + XCTAssertEqualObjects(actual[3].rawObject, a); + XCTAssertTrue([actual[3] arrayOrNil].count == 3); + XCTAssertNil([actual[3] stringOrNil]); + + XCTAssertEqualObjects(actual[4].rawObject, d); + XCTAssertTrue([actual[4] dictionaryOrNil].count == 1); + XCTAssertNil([actual[4] stringOrNil]); +} + +- (void)testMatchingDictionaryField +{ + NSError *e; + FBSDKJSONValue *const value = FBSDKCreateJSONFromString(@"{\"oh\":\"hi\"}", &e); + + [value matchArray:^(NSArray *match) { + XCTFail("Should not match an array when none exists in the json string"); + } dictionary:^(NSDictionary *match) { + XCTAssertEqualObjects(match.allKeys.firstObject, @"oh", "Should return a parsed dictionary with the expected key"); + XCTAssertEqualObjects(match[@"oh"].rawObject, @"hi", "Should return valid JSON fields for parsed dictionary values"); + }]; +} + +@end diff --git a/FBSDKCoreKit/FBSDKCoreKitTests/Internal/FBSDKSettingsTests.m b/FBSDKCoreKit/FBSDKCoreKitTests/Internal/FBSDKSettingsTests.m index b1f3fb944b..f66f22ff14 100644 --- a/FBSDKCoreKit/FBSDKCoreKitTests/Internal/FBSDKSettingsTests.m +++ b/FBSDKCoreKit/FBSDKCoreKitTests/Internal/FBSDKSettingsTests.m @@ -16,26 +16,1255 @@ // 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 "FBSDKAppEventsUtility.h" #import "FBSDKCoreKit.h" #import "FBSDKSettings.h" #import "FBSDKSettings+Internal.h" +#import "FBSDKTestCase.h" +#import "FakeBundle.h" +#import "UserDefaultsSpy.h" @interface FBSDKSettings () + (NSString *)userAgentSuffix; + (void)setUserAgentSuffix:(NSString *)suffix; @end -@interface FBSDKSettingsTests : XCTestCase -{ - id _mockSDKSettings; -} +@interface FBSDKSettingsTests : FBSDKTestCase + @end +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wdeprecated-declarations" + @implementation FBSDKSettingsTests +{ + id _mockAppEventsUtility; + UserDefaultsSpy *userDefaultsSpy; +} + +static NSString *const emptyString = @""; +static NSString *const whiteSpaceToken = @" "; + +- (void)setUp +{ + [super setUp]; + + [self resetCachedSettings]; + + // Reset user defaults spy + userDefaultsSpy = [UserDefaultsSpy new]; + [self stubUserDefaultsWith:userDefaultsSpy]; + [self stubLoggingIfUserSettingsChanged]; +} + +- (void)tearDown +{ + [super tearDown]; + + [self resetCachedSettings]; +} + +- (void)testDefaultGraphAPIVersion +{ + XCTAssertEqualObjects( + FBSDKSettings.graphAPIVersion, + FBSDK_TARGET_PLATFORM_VERSION, + "Settings should provide a default graph api version" + ); +} + +// MARK: Logging Behaviors + +- (void)testSettingsBehaviorsFromMissingPlistEntry +{ + NSBundle *bundle = [FakeBundle bundleWithDictionary:@{}]; + [self stubMainBundleWith:bundle]; + + NSSet *expected = [NSSet setWithArray:@[FBSDKLoggingBehaviorDeveloperErrors]]; + XCTAssertEqualObjects( + FBSDKSettings.loggingBehaviors, + expected, + "Logging behaviors should default to developer errors when there is no plist entry" + ); +} + +- (void)testSettingBehaviorsFromEmptyPlistEntry +{ + NSBundle *bundle = [FakeBundle bundleWithDictionary:@{}]; + [self stubMainBundleWith:bundle]; + + NSSet *expected = [NSSet setWithArray:@[FBSDKLoggingBehaviorDeveloperErrors]]; + + XCTAssertEqualObjects( + FBSDKSettings.loggingBehaviors, + expected, + "Logging behaviors should default to developer errors when settings are created with an empty plist entry" + ); +} + +- (void)testSettingBehaviorsFromPlistWithInvalidEntry +{ + NSBundle *bundle = [FakeBundle bundleWithDictionary:@{@"FacebookLoggingBehavior" : @[@"Foo"]}]; + [self stubMainBundleWith:bundle]; + + NSSet *expected = [NSSet setWithArray:@[@"Foo"]]; + XCTAssertEqualObjects( + FBSDKSettings.loggingBehaviors, + expected, + "Logging behaviors should default to developer errors when settings are created with a plist that only has invalid entries but it does not" + ); +} + +- (void)testSettingBehaviorsFromPlistWithValidEntry +{ + NSBundle *bundle = [NSBundle bundleForClass:FBSDKTestCase.class]; + [self stubMainBundleWith:bundle]; + + NSSet *expected = [NSSet setWithArray:@[FBSDKLoggingBehaviorInformational]]; + XCTAssertEqualObjects( + FBSDKSettings.loggingBehaviors, + expected, + "Settings should pull information from the bundle" + ); +} + +- (void)testLoggingBehaviorsInternalStorage +{ + FakeBundle *bundle = [FakeBundle bundleWithDictionary:@{}]; + [self stubMainBundleWith:bundle]; + + FBSDKSettings.loggingBehaviors = [NSSet setWithArray:@[FBSDKLoggingBehaviorInformational]]; + + XCTAssertNotNil(FBSDKSettings.loggingBehaviors, "sanity check"); + XCTAssertNil( + userDefaultsSpy.capturedObjectRetrievalKey, + "Should not attempt to access the cache to retrieve objects that have a current value" + ); + XCTAssertNil( + bundle.capturedKeys.lastObject, + "Should not attempt to access the plist to retrieve objects that have a current value" + ); +} + +// MARK: Domain Prefix + +- (void)testSettingDomainPrefixFromMissingPlistEntry +{ + NSBundle *bundle = [FakeBundle bundleWithDictionary:@{}]; + [self stubMainBundleWith:bundle]; + + XCTAssertNil( + FBSDKSettings.facebookDomainPart, + "There should be no default value for a facebook domain prefix" + ); +} + +- (void)testSettingDomainPrefixFromEmptyPlistEntry +{ + NSBundle *bundle = [FakeBundle bundleWithDictionary:@{@"FacebookDomainPart" : emptyString}]; + [self stubMainBundleWith:bundle]; + + XCTAssertEqualObjects( + FBSDKSettings.facebookDomainPart, + emptyString, + "Should not use an empty string as a facebook domain prefix but it does" + ); +} + +- (void)testSettingFacebookDomainPrefixFromPlist +{ + NSBundle *bundle = [FakeBundle bundleWithDictionary:@{@"FacebookDomainPart" : @"beta"}]; + [self stubMainBundleWith:bundle]; + + XCTAssertEqualObjects( + FBSDKSettings.facebookDomainPart, + @"beta", + "A developer should be able to set any string as the facebook domain prefix to use in building urls" + ); +} + +- (void)testSettingDomainPrefixWithPlistEntry +{ + NSString *domainPrefix = @"abc123"; + NSBundle *bundle = [FakeBundle bundleWithDictionary:@{@"FacebookDomainPart" : domainPrefix}]; + [self stubMainBundleWith:bundle]; + + [FBSDKSettings setFacebookDomainPart:@"foo"]; + + XCTAssertNil( + userDefaultsSpy.capturedValues[@"FacebookDomainPart"], + "Should not persist the value of a non-cachable property when setting it" + ); + + XCTAssertEqualObjects( + FBSDKSettings.facebookDomainPart, + @"foo", + "Settings should return the explicitly set domain prefix over one gleaned from a plist entry" + ); +} + +- (void)testSettingDomainPrefixWithoutPlistEntry +{ + [FBSDKSettings setFacebookDomainPart:@"foo"]; + + XCTAssertNil( + userDefaultsSpy.capturedValues[@"FacebookDomainPart"], + "Should not persist the value of a non-cachable property when setting it" + ); + XCTAssertEqualObjects( + FBSDKSettings.facebookDomainPart, + @"foo", + "Settings should return the explicitly set domain prefix" + ); +} + +- (void)testSettingEmptyDomainPrefix +{ + [FBSDKSettings setFacebookDomainPart:emptyString]; + + XCTAssertNil( + userDefaultsSpy.capturedValues[@"FacebookDomainPart"], + "Should not persist the value of a non-cachable property when setting it" + ); + XCTAssertEqualObjects( + FBSDKSettings.facebookDomainPart, + emptyString, + "Should not store an invalid domain prefix but it does" + ); +} + +- (void)testSettingWhitespaceOnlyDomainPrefix +{ + [FBSDKSettings setFacebookDomainPart:whiteSpaceToken]; + + XCTAssertNil( + userDefaultsSpy.capturedValues[@"FacebookDomainPart"], + "Should not persist the value of a non-cachable property when setting it" + ); + XCTAssertEqualObjects( + FBSDKSettings.facebookDomainPart, + whiteSpaceToken, + "Should not store a whitespace only domain prefix but it does" + ); +} + +- (void)testDomainPartInternalStorage +{ + FakeBundle *bundle = [FakeBundle bundleWithDictionary:@{}]; + [self stubMainBundleWith:bundle]; + + FBSDKSettings.facebookDomainPart = @"foo"; + + XCTAssertNotNil(FBSDKSettings.facebookDomainPart, "sanity check"); + XCTAssertNil( + userDefaultsSpy.capturedObjectRetrievalKey, + "Should not attempt to access the cache to retrieve objects that have a current value" + ); + XCTAssertNil( + bundle.capturedKeys.lastObject, + "Should not attempt to access the plist to retrieve objects that have a current value" + ); +} + +// MARK: Client Token + +- (void)testClientTokenFromPlist +{ + NSString *clientToken = @"abc123"; + NSBundle *bundle = [FakeBundle bundleWithDictionary:@{@"FacebookClientToken" : clientToken}]; + [self stubMainBundleWith:bundle]; + + XCTAssertEqualObjects( + FBSDKSettings.clientToken, + clientToken, + "A developer should be able to set any string as the client token" + ); +} + +- (void)testClientTokenFromMissingPlistEntry +{ + XCTAssertNil( + FBSDKSettings.clientToken, + "A client token should not have a default value if it is not available in the plist" + ); +} + +- (void)testSettingClientTokenFromEmptyPlistEntry +{ + NSBundle *bundle = [FakeBundle bundleWithDictionary:@{@"FacebookClientToken" : emptyString}]; + [self stubMainBundleWith:bundle]; + + XCTAssertEqualObjects( + FBSDKSettings.clientToken, + emptyString, + "Should not use an empty string as a facebook client token but it will" + ); +} + +- (void)testSettingClientTokenWithPlistEntry +{ + NSString *clientToken = @"abc123"; + NSBundle *bundle = [FakeBundle bundleWithDictionary:@{@"FacebookClientToken" : clientToken}]; + [self stubMainBundleWith:bundle]; + + FBSDKSettings.clientToken = @"foo"; + + XCTAssertNil( + userDefaultsSpy.capturedValues[@"FacebookClientToken"], + "Should not persist the value of a non-cachable property when setting it" + ); + + XCTAssertNil( + userDefaultsSpy.capturedValues[@"FacebookClientToken"], + "Should not persist the value of a non-cachable property when setting it" + ); + XCTAssertEqual( + FBSDKSettings.clientToken, + @"foo", + "Settings should return the explicitly set client token over one gleaned from a plist entry" + ); +} + +- (void)testSettingClientTokenWithoutPlistEntry +{ + FBSDKSettings.clientToken = @"foo"; + + XCTAssertNil( + userDefaultsSpy.capturedValues[@"FacebookClientToken"], + "Should not persist the value of a non-cachable property when setting it" + ); + XCTAssertEqual( + FBSDKSettings.clientToken, + @"foo", + "Settings should return the explicitly set client token" + ); +} + +- (void)testSettingEmptyClientToken +{ + FBSDKSettings.clientToken = emptyString; + + XCTAssertNil( + userDefaultsSpy.capturedValues[@"FacebookClientToken"], + "Should not persist the value of a non-cachable property when setting it" + ); + XCTAssertEqualObjects( + FBSDKSettings.clientToken, + emptyString, + "Should not store an invalid token but it will" + ); +} + +- (void)testSettingWhitespaceOnlyClientToken +{ + FBSDKSettings.clientToken = whiteSpaceToken; + + XCTAssertNil( + userDefaultsSpy.capturedValues[@"FacebookClientToken"], + "Should not persist the value of a non-cachable property when setting it" + ); + XCTAssertEqualObjects( + FBSDKSettings.clientToken, + whiteSpaceToken, + "Should not store a whitespace only client token but it will" + ); +} + +- (void)testClientTokenInternalStorage +{ + FakeBundle *bundle = [FakeBundle bundleWithDictionary:@{}]; + [self stubMainBundleWith:bundle]; + + FBSDKSettings.clientToken = @"foo"; + + XCTAssertNotNil(FBSDKSettings.clientToken, "sanity check"); + XCTAssertNil( + userDefaultsSpy.capturedObjectRetrievalKey, + "Should not attempt to access the cache to retrieve objects that have a current value" + ); + XCTAssertNil( + bundle.capturedKeys.lastObject, + "Should not attempt to access the plist to retrieve objects that have a current value" + ); +} + +// MARK: App Identifier + +- (void)testAppIdentifierFromPlist +{ + NSString *appIdentifier = @"abc1234"; + NSBundle *bundle = [FakeBundle bundleWithDictionary:@{@"FacebookAppID" : appIdentifier}]; + [self stubMainBundleWith:bundle]; + + XCTAssertEqualObjects( + FBSDKSettings.appID, + appIdentifier, + "A developer should be able to set any string as the app identifier" + ); +} + +- (void)testAppIdentifierFromMissingPlistEntry +{ + XCTAssertNil( + FBSDKSettings.appID, + "An app identifier should not have a default value if it is not available in the plist" + ); +} + +- (void)testSettingAppIdentifierFromEmptyPlistEntry +{ + NSBundle *bundle = [FakeBundle bundleWithDictionary:@{@"FacebookAppID" : emptyString}]; + [self stubMainBundleWith:bundle]; + + XCTAssertEqualObjects( + FBSDKSettings.appID, + emptyString, + "Should not use an empty string as an app identifier but it will" + ); +} + +- (void)testSettingAppIdentifierWithPlistEntry +{ + NSString *appIdentifier = @"abc123"; + NSBundle *bundle = [FakeBundle bundleWithDictionary:@{@"FacebookAppID" : appIdentifier}]; + [self stubMainBundleWith:bundle]; + + FBSDKSettings.appID = @"foo"; + + XCTAssertNil( + userDefaultsSpy.capturedValues[@"FacebookAppID"], + "Should not persist the value of a non-cachable property when setting it" + ); + XCTAssertEqualObjects( + FBSDKSettings.appID, + @"foo", + "Settings should return the explicitly set app identifier over one gleaned from a plist entry" + ); +} + +- (void)testSettingAppIdentifierWithoutPlistEntry +{ + FBSDKSettings.appID = @"foo"; + + XCTAssertNil( + userDefaultsSpy.capturedValues[@"FacebookAppID"], + "Should not persist the value of a non-cachable property when setting it" + ); + XCTAssertEqualObjects( + FBSDKSettings.appID, + @"foo", + "Settings should return the explicitly set app identifier" + ); +} + +- (void)testSettingEmptyAppIdentifier +{ + FBSDKSettings.appID = emptyString; + + XCTAssertNil( + userDefaultsSpy.capturedValues[@"FacebookAppID"], + "Should not persist the value of a non-cachable property when setting it" + ); + XCTAssertEqualObjects( + FBSDKSettings.appID, + emptyString, + "Should not store an empty app identifier but it will" + ); +} + +- (void)testSettingWhitespaceOnlyAppIdentifier +{ + FBSDKSettings.appID = whiteSpaceToken; + + XCTAssertNil( + userDefaultsSpy.capturedValues[@"FacebookAppID"], + "Should not persist the value of a non-cachable property when setting it" + ); + XCTAssertEqualObjects( + FBSDKSettings.appID, + whiteSpaceToken, + "Should not store a whitespace only app identifier but it will" + ); +} + +- (void)testAppIdentifierInternalStorage +{ + FakeBundle *bundle = [FakeBundle bundleWithDictionary:@{}]; + [self stubMainBundleWith:bundle]; + + FBSDKSettings.appID = @"foo"; + + XCTAssertNotNil(FBSDKSettings.appID, "sanity check"); + XCTAssertNil( + userDefaultsSpy.capturedObjectRetrievalKey, + "Should not attempt to access the cache to retrieve objects that have a current value" + ); + XCTAssertNil( + bundle.capturedKeys.lastObject, + "Should not attempt to access the plist to retrieve objects that have a current value" + ); +} + +// MARK: Display Name + +- (void)testDisplayNameFromPlist +{ + NSString *displayName = @"abc123"; + NSBundle *bundle = [FakeBundle bundleWithDictionary:@{@"FacebookDisplayName" : displayName}]; + [self stubMainBundleWith:bundle]; + + XCTAssertEqualObjects( + FBSDKSettings.displayName, + displayName, + "A developer should be able to set any string as the display name" + ); +} + +- (void)testDisplayNameFromMissingPlistEntry +{ + XCTAssertNil( + FBSDKSettings.displayName, + "A display name should not have a default value if it is not available in the plist" + ); +} + +- (void)testSettingDisplayNameFromEmptyPlistEntry +{ + NSBundle *bundle = [FakeBundle bundleWithDictionary:@{@"FacebookDisplayName" : emptyString}]; + [self stubMainBundleWith:bundle]; + + XCTAssertEqualObjects( + FBSDKSettings.displayName, + emptyString, + "Should not use an empty string as a display name but it will" + ); +} + +- (void)testSettingDisplayNameWithPlistEntry +{ + NSString *displayName = @"abc123"; + NSBundle *bundle = [FakeBundle bundleWithDictionary:@{@"FacebookDisplayName" : displayName}]; + [self stubMainBundleWith:bundle]; + + FBSDKSettings.displayName = @"foo"; + + XCTAssertNil( + userDefaultsSpy.capturedValues[@"FacebookDisplayName"], + "Should not persist the value of a non-cachable property when setting it" + ); + XCTAssertEqual( + FBSDKSettings.displayName, + @"foo", + "Settings should return the explicitly set display name over one gleaned from a plist entry" + ); +} + +- (void)testSettingDisplayNameWithoutPlistEntry +{ + FBSDKSettings.displayName = @"foo"; + + XCTAssertNil( + userDefaultsSpy.capturedValues[@"FacebookDisplayName"], + "Should not persist the value of a non-cachable property when setting it" + ); + XCTAssertEqual( + FBSDKSettings.displayName, + @"foo", + "Settings should return the explicitly set display name" + ); +} + +- (void)testSettingEmptyDisplayName +{ + FBSDKSettings.displayName = emptyString; + + XCTAssertNil( + userDefaultsSpy.capturedValues[@"FacebookDisplayName"], + "Should not persist the value of a non-cachable property when setting it" + ); + XCTAssertEqualObjects( + FBSDKSettings.displayName, + emptyString, + "Should not store an empty display name but it will" + ); +} + +- (void)testSettingWhitespaceOnlyDisplayName +{ + FBSDKSettings.displayName = whiteSpaceToken; + + XCTAssertNil( + userDefaultsSpy.capturedValues[@"FacebookDisplayName"], + "Should not persist the value of a non-cachable property when setting it" + ); + XCTAssertEqualObjects( + FBSDKSettings.displayName, + whiteSpaceToken, + "Should not store a whitespace only display name but it will" + ); +} + +- (void)testDisplayNameInternalStorage +{ + FakeBundle *bundle = [FakeBundle bundleWithDictionary:@{}]; + [self stubMainBundleWith:bundle]; + + FBSDKSettings.displayName = @"foo"; + + XCTAssertNotNil(FBSDKSettings.displayName, "sanity check"); + XCTAssertNil( + userDefaultsSpy.capturedObjectRetrievalKey, + "Should not attempt to access the cache to retrieve objects that have a current value" + ); + XCTAssertNil( + bundle.capturedKeys.lastObject, + "Should not attempt to access the plist to retrieve objects that have a current value" + ); +} + +// MARK: JPEG Compression Quality + +- (void)testJPEGCompressionQualityFromPlist +{ + NSNumber *jpegCompressionQuality = @0.1; + NSBundle *bundle = [FakeBundle bundleWithDictionary:@{@"FacebookJpegCompressionQuality" : jpegCompressionQuality}]; + [self stubMainBundleWith:bundle]; + + XCTAssertEqualWithAccuracy( + FBSDKSettings.JPEGCompressionQuality, + jpegCompressionQuality.doubleValue, + 0.01, + "A developer should be able to set a jpeg compression quality via the plist" + ); +} + +- (void)testJPEGCompressionQualityFromMissingPlistEntry +{ + XCTAssertEqualWithAccuracy( + FBSDKSettings.JPEGCompressionQuality, + 0.9, + 0.01, + "There should be a known default value for jpeg compression quality" + ); +} + +- (void)testSettingJPEGCompressionQualityFromInvalidPlistEntry +{ + NSBundle *bundle = [FakeBundle bundleWithDictionary:@{@"FacebookJpegCompressionQuality" : @-2.0}]; + [self stubMainBundleWith:bundle]; + + XCTAssertNotEqual( + FBSDKSettings.JPEGCompressionQuality, + -0.2, + "Should not use a negative value as a jpeg compression quality" + ); +} + +- (void)testSettingJPEGCompressionQualityWithPlistEntry +{ + NSBundle *bundle = [FakeBundle bundleWithDictionary:@{@"FacebookJpegCompressionQuality" : @0.2}]; + [self stubMainBundleWith:bundle]; + + FBSDKSettings.JPEGCompressionQuality = 0.3; + + XCTAssertNil( + userDefaultsSpy.capturedValues[@"FacebookJpegCompressionQuality"], + "Should not persist the value of a non-cachable property when setting it" + ); + XCTAssertEqualWithAccuracy( + FBSDKSettings.JPEGCompressionQuality, + @(0.3).doubleValue, + 0.01, + "Settings should return the explicitly set jpeg compression quality over one gleaned from a plist entry" + ); +} + +- (void)testSettingJPEGCompressionQualityWithoutPlistEntry +{ + FBSDKSettings.JPEGCompressionQuality = 1.0; + + XCTAssertNil( + userDefaultsSpy.capturedValues[@"FacebookJpegCompressionQuality"], + "Should not persist the value of a non-cachable property when setting it" + ); + XCTAssertEqual( + FBSDKSettings.JPEGCompressionQuality, + 1.0, + "Settings should return the explicitly set jpeg compression quality" + ); +} + +- (void)testSettingJPEGCompressionQualityTooLow +{ + FBSDKSettings.JPEGCompressionQuality = -0.1; + + XCTAssertNil( + userDefaultsSpy.capturedValues[@"FacebookJpegCompressionQuality"], + "Should not persist the value of a non-cachable property when setting it" + ); + XCTAssertNotEqual( + FBSDKSettings.JPEGCompressionQuality, + -0.1, + "Should not store a negative jpeg compression quality" + ); +} + +- (void)testSettingJPEGCompressionQualityTooHigh +{ + FBSDKSettings.JPEGCompressionQuality = 1.1; + + XCTAssertNil( + userDefaultsSpy.capturedValues[@"FacebookJpegCompressionQuality"], + "Should not persist the value of a non-cachable property when setting it" + ); + XCTAssertNotEqual( + FBSDKSettings.JPEGCompressionQuality, + 1.1, + "Should not store a jpeg compression quality that is larger than 1.0" + ); +} + +- (void)testJPEGCompressionQualityInternalStorage +{ + FakeBundle *bundle = [FakeBundle bundleWithDictionary:@{}]; + [self stubMainBundleWith:bundle]; + + FBSDKSettings.JPEGCompressionQuality = 1; + + XCTAssertEqual(FBSDKSettings.JPEGCompressionQuality, 1, "Sanity check"); + XCTAssertNil( + userDefaultsSpy.capturedObjectRetrievalKey, + "Should not attempt to access the cache to retrieve objects that have a current value" + ); + XCTAssertNil( + bundle.capturedKeys.lastObject, + "Should not attempt to access the plist to retrieve objects that have a current value" + ); +} + +// MARK: URL Scheme Suffix + +- (void)testURLSchemeSuffixFromPlist +{ + NSString *urlSchemeSuffix = @"abc123"; + NSBundle *bundle = [FakeBundle bundleWithDictionary:@{@"FacebookUrlSchemeSuffix" : urlSchemeSuffix}]; + [self stubMainBundleWith:bundle]; + + XCTAssertEqual( + FBSDKSettings.appURLSchemeSuffix, + urlSchemeSuffix, + "A developer should be able to set any string as the url scheme suffix" + ); +} + +- (void)testURLSchemeSuffixFromMissingPlistEntry +{ + XCTAssertNil( + FBSDKSettings.appURLSchemeSuffix, + "A url scheme suffix should not have a default value if it is not available in the plist" + ); +} + +- (void)testSettingURLSchemeSuffixFromEmptyPlistEntry +{ + NSBundle *bundle = [FakeBundle bundleWithDictionary:@{@"FacebookUrlSchemeSuffix" : emptyString}]; + [self stubMainBundleWith:bundle]; + + XCTAssertEqualObjects( + FBSDKSettings.appURLSchemeSuffix, + emptyString, + "Should not use an empty string as a url scheme suffix but it will" + ); +} + +- (void)testSettingURLSchemeSuffixWithPlistEntry +{ + NSString *urlSchemeSuffix = @"abc123"; + NSBundle *bundle = [FakeBundle bundleWithDictionary:@{@"FacebookUrlSchemeSuffix" : urlSchemeSuffix}]; + [self stubMainBundleWith:bundle]; + + FBSDKSettings.appURLSchemeSuffix = @"foo"; + + XCTAssertNil( + userDefaultsSpy.capturedValues[@"FacebookUrlSchemeSuffix"], + "Should not persist the value of a non-cachable property when setting it" + ); + XCTAssertEqual( + FBSDKSettings.appURLSchemeSuffix, + @"foo", + "Settings should return the explicitly set url scheme suffix over one gleaned from a plist entry" + ); +} + +- (void)testSettingURLSchemeSuffixWithoutPlistEntry +{ + FBSDKSettings.appURLSchemeSuffix = @"foo"; + + XCTAssertNil( + userDefaultsSpy.capturedValues[@"FacebookUrlSchemeSuffix"], + "Should not persist the value of a non-cachable property when setting it" + ); + XCTAssertEqual( + FBSDKSettings.appURLSchemeSuffix, + @"foo", + "Settings should return the explicitly set url scheme suffix" + ); +} + +- (void)testSettingEmptyURLSchemeSuffix +{ + FBSDKSettings.appURLSchemeSuffix = emptyString; + + XCTAssertNil( + userDefaultsSpy.capturedValues[@"FacebookUrlSchemeSuffix"], + "Should not persist the value of a non-cachable property when setting it" + ); + XCTAssertEqualObjects( + FBSDKSettings.appURLSchemeSuffix, + emptyString, + "Should not store an empty url scheme suffix but it will" + ); +} + +- (void)testSettingWhitespaceOnlyURLSchemeSuffix +{ + FBSDKSettings.appURLSchemeSuffix = whiteSpaceToken; + + XCTAssertNil( + userDefaultsSpy.capturedValues[@"FacebookUrlSchemeSuffix"], + "Should not persist the value of a non-cachable property when setting it" + ); + XCTAssertEqualObjects( + FBSDKSettings.appURLSchemeSuffix, + whiteSpaceToken, + "Should not store a whitespace only url scheme suffix but it will" + ); +} + +- (void)testURLSchemeSuffixInternalStorage +{ + FakeBundle *bundle = [FakeBundle bundleWithDictionary:@{}]; + [self stubMainBundleWith:bundle]; + + FBSDKSettings.appURLSchemeSuffix = @"foo"; + + XCTAssertNotNil(FBSDKSettings.appURLSchemeSuffix, "sanity check"); + XCTAssertNil( + userDefaultsSpy.capturedObjectRetrievalKey, + "Should not attempt to access the cache to retrieve objects that have a current value" + ); + XCTAssertNil( + bundle.capturedKeys.lastObject, + "Should not attempt to access the plist to retrieve objects that have a current value" + ); +} + +// MARK: Auto Log App Events Enabled + +- (void)testAutoLogAppEventsEnabledFromPlist +{ + NSBundle *bundle = [FakeBundle bundleWithDictionary:@{@"FacebookAutoLogAppEventsEnabled" : @NO}]; + [self stubMainBundleWith:bundle]; + + XCTAssertFalse( + FBSDKSettings.isAutoLogAppEventsEnabled, + "A developer should be able to set the value of auto log app events from the plist" + ); +} + +- (void)testAutoLogAppEventsEnabledDefaultValue +{ + XCTAssertTrue( + FBSDKSettings.isAutoLogAppEventsEnabled, + "Auto logging of app events should default to true when there is no plist value given" + ); +} + +- (void)testAutoLogAppEventsEnabledInvalidPlistEntry +{ + NSBundle *bundle = [FakeBundle bundleWithDictionary:@{@"FacebookAutoLogAppEventsEnabled" : emptyString}]; + [self stubMainBundleWith:bundle]; + + XCTAssertFalse( + FBSDKSettings.isAutoLogAppEventsEnabled, + "Auto logging of app events should default to true when there is an invalid plist value given but it does not" + ); +} + +- (void)testSettingAutoLogAppEventsEnabled +{ + FBSDKSettings.autoLogAppEventsEnabled = false; + + XCTAssertNotNil( + userDefaultsSpy.capturedValues[@"FacebookAutoLogAppEventsEnabled"], + "Should persist the value of a cachable property when setting it" + ); + XCTAssertFalse( + FBSDKSettings.autoLogAppEventsEnabled, + "Should use the explicitly set property" + ); +} + +- (void)testOverridingCachedAutoLogAppEventsEnabled +{ + [self stubInitializeSDKWith:@{}]; + + XCTAssertTrue(FBSDKSettings.isAutoLogAppEventsEnabled); + + NSBundle *bundle = [FakeBundle bundleWithDictionary:@{@"FacebookAutoLogAppEventsEnabled" : @NO}]; + [self stubMainBundleWith:bundle]; + + XCTAssertTrue( + FBSDKSettings.isAutoLogAppEventsEnabled, + "Should favor cached properties over those set in the plist" + ); +} + +- (void)testAutoLogAppEventsEnabledInternalStorage +{ + FakeBundle *bundle = [FakeBundle bundleWithDictionary:@{}]; + [self stubMainBundleWith:bundle]; + + FBSDKSettings.autoLogAppEventsEnabled = @YES; + + XCTAssertTrue(FBSDKSettings.autoLogAppEventsEnabled, "sanity check"); + XCTAssertNil( + userDefaultsSpy.capturedObjectRetrievalKey, + "Should not attempt to access the cache to retrieve objects that have a current value" + ); + XCTAssertNil( + bundle.capturedKeys.lastObject, + "Should not attempt to access the plist to retrieve objects that have a current value" + ); +} + +// MARK: Advertiser Identifier Collection Enabled + +- (void)testFacebookAdvertiserIDCollectionEnabled +{ + NSBundle *bundle = [FakeBundle bundleWithDictionary:@{@"FacebookAdvertiserIDCollectionEnabled" : @NO}]; + [self stubMainBundleWith:bundle]; + + XCTAssertFalse( + FBSDKSettings.isAdvertiserIDCollectionEnabled, + "A developer should be able to set whether advertiser ID collection is enabled from the plist" + ); +} + +- (void)testFacebookAdvertiserIDCollectionEnabledDefaultValue +{ + XCTAssertTrue( + FBSDKSettings.isAdvertiserIDCollectionEnabled, + "Auto collection of advertiser id should default to true when there is no plist value given" + ); +} + +- (void)testFacebookAdvertiserIDCollectionEnabledInvalidPlistEntry +{ + NSBundle *bundle = [FakeBundle bundleWithDictionary:@{@"FacebookAdvertiserIDCollectionEnabled" : emptyString}]; + [self stubMainBundleWith:bundle]; + + XCTAssertFalse( + FBSDKSettings.isAdvertiserIDCollectionEnabled, + "Auto collection of advertiser id should default to true when there is an invalid plist value given but it does not" + ); +} + +- (void)testSettingFacebookAdvertiserIDCollectionEnabled +{ + FBSDKSettings.advertiserIDCollectionEnabled = false; + + XCTAssertNotNil( + userDefaultsSpy.capturedValues[@"FacebookAdvertiserIDCollectionEnabled"], + "Should persist the value of a cachable property when setting it" + ); + XCTAssertFalse( + FBSDKSettings.advertiserIDCollectionEnabled, + "Should use the explicitly set property" + ); +} + +- (void)testOverridingCachedFacebookAdvertiserIDCollectionEnabled +{ + [self stubInitializeSDKWith:@{}]; + + FBSDKSettings.advertiserIDCollectionEnabled = true; + XCTAssertTrue(FBSDKSettings.isAdvertiserIDCollectionEnabled); + + NSBundle *bundle = [FakeBundle bundleWithDictionary:@{@"FacebookAdvertiserIDCollectionEnabled" : @NO}]; + [self stubMainBundleWith:bundle]; + + XCTAssertTrue( + FBSDKSettings.isAdvertiserIDCollectionEnabled, + "Should favor cached properties over those set in the plist" + ); +} + +- (void)testAdvertiserIDCollectionEnabledInternalStorage +{ + FakeBundle *bundle = [FakeBundle bundleWithDictionary:@{}]; + [self stubMainBundleWith:bundle]; + + FBSDKSettings.advertiserIDCollectionEnabled = @YES; + + XCTAssertTrue(FBSDKSettings.advertiserIDCollectionEnabled, "sanity check"); + XCTAssertNil( + userDefaultsSpy.capturedObjectRetrievalKey, + "Should not attempt to access the cache to retrieve objects that have a current value" + ); + XCTAssertNil( + bundle.capturedKeys.lastObject, + "Should not attempt to access the plist to retrieve objects that have a current value" + ); +} + +// MARK: Advertiser Tracking Status + +- (void)testFacebookAdvertiserTrackingStatusDefaultValue +{ + if (@available(iOS 14.0, *)) { + XCTAssertTrue( + [FBSDKSettings getAdvertisingTrackingStatus] == FBSDKAdvertisingTrackingUnspecified, + "Advertiser tracking status should default to Unspecified when there is no plist value given" + ); + } +} + +- (void)testSettingFacebookAdvertiserTrackingStatus +{ + if (@available(iOS 14.0, *)) { + XCTAssertTrue([FBSDKSettings setAdvertiserTrackingEnabled:YES]); + XCTAssertTrue( + [FBSDKSettings getAdvertisingTrackingStatus] == FBSDKAdvertisingTrackingAllowed, + "Should use the explicitly set property" + ); + } else { + XCTAssertFalse([FBSDKSettings setAdvertiserTrackingEnabled:YES]); + XCTAssertNil( + userDefaultsSpy.capturedValues[@"FacebookAdvertiserTrackingStatus"], + "Should be no-op in iOS13 and below" + ); + } +} + +- (void)testAdvertiserTrackingStatusInternalStorage +{ + FakeBundle *bundle = [FakeBundle bundleWithDictionary:@{}]; + [self stubMainBundleWith:bundle]; + + if (@available(iOS 14.0, *)) { + [FBSDKSettings setAdvertiserTrackingStatus:FBSDKAdvertisingTrackingUnspecified]; + + XCTAssertTrue([FBSDKSettings getAdvertisingTrackingStatus] == FBSDKAdvertisingTrackingUnspecified, "sanity check"); + XCTAssertNil( + userDefaultsSpy.capturedObjectRetrievalKey, + "Should not attempt to access the cache to retrieve objects that have a current value" + ); + } +} + +// MARK: Codeless Debug Log Enabled + +- (void)testFacebookCodelessDebugLogEnabled +{ + NSBundle *bundle = [FakeBundle bundleWithDictionary:@{@"FacebookCodelessDebugLogEnabled" : @NO}]; + [self stubMainBundleWith:bundle]; + + XCTAssertFalse( + FBSDKSettings.isCodelessDebugLogEnabled, + "A developer should be able to set whether codeless debug logging is enabled from the plist" + ); +} + +- (void)testFacebookCodelessDebugLogEnabledDefaultValue +{ + NSBundle *bundle = [FakeBundle bundleWithDictionary:@{}]; + [self stubMainBundleWith:bundle]; + + XCTAssertFalse( + FBSDKSettings.isCodelessDebugLogEnabled, + "Codeless debug logging enabled should default to false when there is no plist value given" + ); +} + +- (void)testFacebookCodelessDebugLogEnabledInvalidPlistEntry +{ + NSBundle *bundle = [FakeBundle bundleWithDictionary:@{@"FacebookCodelessDebugLogEnabled" : emptyString}]; + [self stubMainBundleWith:bundle]; + + XCTAssertFalse( + FBSDKSettings.isCodelessDebugLogEnabled, + "Codeless debug logging enabled should default to true when there is an invalid plist value given but it does not" + ); +} + +- (void)testSettingFacebookCodelessDebugLogEnabled +{ + FBSDKSettings.codelessDebugLogEnabled = false; + + XCTAssertNotNil( + userDefaultsSpy.capturedValues[@"FacebookCodelessDebugLogEnabled"], + "Should persist the value of a cachable property when setting it" + ); + XCTAssertFalse( + FBSDKSettings.codelessDebugLogEnabled, + "Should use the explicitly set property" + ); +} + +- (void)testOverridingCachedFacebookCodelessDebugLogEnabled +{ + [self stubInitializeSDKWith:@{}]; + + FBSDKSettings.codelessDebugLogEnabled = true; + XCTAssertTrue(FBSDKSettings.isCodelessDebugLogEnabled); + + NSBundle *bundle = [FakeBundle bundleWithDictionary:@{@"FacebookCodelessDebugLogEnabled" : @NO}]; + [self stubMainBundleWith:bundle]; + + XCTAssertTrue( + FBSDKSettings.isCodelessDebugLogEnabled, + "Should favor cached properties over those set in the plist" + ); +} + +- (void)testCachedFacebookCodelessDebugLogEnabledInternalStorage +{ + FakeBundle *bundle = [FakeBundle bundleWithDictionary:@{}]; + [self stubMainBundleWith:bundle]; + + FBSDKSettings.codelessDebugLogEnabled = @YES; + + XCTAssertTrue(FBSDKSettings.codelessDebugLogEnabled, "sanity check"); + XCTAssertNil( + userDefaultsSpy.capturedObjectRetrievalKey, + "Should not attempt to access the cache to retrieve objects that have a current value" + ); + XCTAssertNil( + bundle.capturedKeys.lastObject, + "Should not attempt to access the plist to retrieve objects that have a current value" + ); +} + +// MARK: Caching Properties + +- (void)testInitialAccessForCachablePropertyWithNonEmptyCache +{ + // Using false because it is not the default value for `isAutoInitializationEnabled` + userDefaultsSpy.capturedValues = @{ @"FacebookAutoLogAppEventsEnabled" : @NO }; + [self stubUserDefaultsWith:userDefaultsSpy]; + + FakeBundle *bundle = [FakeBundle bundleWithDictionary:@{}]; + [self stubMainBundleWith:bundle]; + + XCTAssertFalse( + FBSDKSettings.isAutoLogAppEventsEnabled, + "Should retrieve an initial value for a cachable property when there is a non-empty cache" + ); + + XCTAssertEqualObjects( + userDefaultsSpy.capturedObjectRetrievalKey, + @"FacebookAutoLogAppEventsEnabled", + "Should attempt to access the cache to retrieve the initial value for a cachable property" + ); + XCTAssertFalse( + [bundle.capturedKeys containsObject:@"FacebookAutoLogAppEventsEnabled"], + "Should not attempt to access the plist for cachable properties that have a value in the cache" + ); +} + +- (void)testInitialAccessForCachablePropertyWithEmptyCacheNonEmptyPlist +{ + // Using false because it is not the default value for `isAutoInitializationEnabled` + FakeBundle *bundle = [FakeBundle bundleWithDictionary:@{@"FacebookAutoLogAppEventsEnabled" : @NO}]; + [self stubMainBundleWith:bundle]; + + XCTAssertFalse( + FBSDKSettings.isAutoLogAppEventsEnabled, + "Should retrieve an initial value from the property list" + ); + + XCTAssertEqualObjects( + userDefaultsSpy.capturedObjectRetrievalKey, + @"FacebookAutoLogAppEventsEnabled", + "Should attempt to access the cache to retrieve the initial value for a cachable property" + ); + XCTAssertEqualObjects( + bundle.capturedKeys.lastObject, + @"FacebookAutoLogAppEventsEnabled", + "Should attempt to access the plist for cachable properties that have no value in the cache" + ); +} + +- (void)testInitialAccessForCachablePropertyWithEmptyCacheEmptyPlistAndDefaultValue +{ + FakeBundle *bundle = [FakeBundle bundleWithDictionary:@{}]; + [self stubMainBundleWith:bundle]; + + XCTAssertTrue( + FBSDKSettings.isAutoLogAppEventsEnabled, + "Should use the default value for a property when there are no values in the cache or plist" + ); + + XCTAssertEqualObjects( + userDefaultsSpy.capturedObjectRetrievalKey, + @"FacebookAutoLogAppEventsEnabled", + "Should attempt to access the cache to retrieve the initial value for a cachable property" + ); + XCTAssertEqualObjects( + bundle.capturedKeys.lastObject, + @"FacebookAutoLogAppEventsEnabled", + "Should attempt to access the plist for cachable properties that have no value in the cache" + ); +} + +- (void)testInitialAccessForNonCachablePropertyWithEmptyPlist +{ + FakeBundle *bundle = [FakeBundle bundleWithDictionary:@{}]; + [self stubMainBundleWith:bundle]; + + XCTAssertNil( + FBSDKSettings.clientToken, + "A non-cachable property with no default value and no plist entry should not have a value" + ); + + XCTAssertNil( + userDefaultsSpy.capturedObjectRetrievalKey, + "Should not attempt to access the cache for a non-cachable property" + ); + XCTAssertEqualObjects( + bundle.capturedKeys.lastObject, + @"FacebookClientToken", + "Should attempt to access the plist for non-cachable properties" + ); +} + +- (void)testInitialAccessForNonCachablePropertyWithNonEmptyPlist +{ + FakeBundle *bundle = [FakeBundle bundleWithDictionary:@{@"FacebookClientToken" : @"abc123"}]; + [self stubMainBundleWith:bundle]; + + XCTAssertEqualObjects( + FBSDKSettings.clientToken, + @"abc123", + "Should retrieve the initial value from the property list" + ); + + XCTAssertNil( + userDefaultsSpy.capturedObjectRetrievalKey, + "Should not attempt to access the cache for a non-cachable property" + ); + XCTAssertEqualObjects( + bundle.capturedKeys.lastObject, + @"FacebookClientToken", + "Should attempt to access the plist for non-cachable properties" + ); +} + +// MARK: Graph Error Recovery Enabled - (void)testSetGraphErrorRecoveryEnabled { @@ -46,55 +1275,247 @@ - (void)testSetGraphErrorRecoveryEnabled XCTAssertFalse([FBSDKSettings isGraphErrorRecoveryEnabled]); } -- (void)testSetCodelessDebugLogEnabled +// MARK: Limit Event and Data Usage + +- (void)testSetLimitEventAndDataUsageDefault { - [FBSDKSettings setCodelessDebugLogEnabled:YES]; - XCTAssertTrue([FBSDKSettings isCodelessDebugLogEnabled]); + XCTAssertFalse( + FBSDKSettings.shouldLimitEventAndDataUsage, + "Should limit event data usage by default" + ); +} - [FBSDKSettings setCodelessDebugLogEnabled:NO]; - XCTAssertFalse([FBSDKSettings isCodelessDebugLogEnabled]); +- (void)testSetLimitEventAndDataUsageWithEmptyCache +{ + FBSDKSettings.limitEventAndDataUsage = YES; + + XCTAssertEqualObjects( + userDefaultsSpy.capturedValues[@"com.facebook.sdk:FBSDKSettingsLimitEventAndDataUsage"], + @YES, + "Should store whether or not to limit event and data usage in the user defaults" + ); + XCTAssertTrue( + FBSDKSettings.shouldLimitEventAndDataUsage, + "Should be able to set whether event data usage is limited" + ); } -- (void)testSetAutoLogAppEventsEnabled +- (void)testSetLimitEventAndDataUsageWithNonEmptyCache { - [FBSDKSettings setAutoLogAppEventsEnabled:YES]; - XCTAssertTrue([FBSDKSettings isAutoLogAppEventsEnabled]); + FBSDKSettings.limitEventAndDataUsage = YES; + XCTAssertTrue(FBSDKSettings.shouldLimitEventAndDataUsage, "sanity check"); - [FBSDKSettings setAutoLogAppEventsEnabled:NO]; - XCTAssertFalse([FBSDKSettings isAutoLogAppEventsEnabled]); + FBSDKSettings.limitEventAndDataUsage = NO; + XCTAssertFalse( + FBSDKSettings.shouldLimitEventAndDataUsage, + "Should be able to override the existing value of should limit event data usage" + ); + XCTAssertEqualObjects( + userDefaultsSpy.capturedValues[@"com.facebook.sdk:FBSDKSettingsLimitEventAndDataUsage"], + @NO, + "Should store the overridden preference for limiting event data usage in the user defaults" + ); } -- (void)testSetAdvertiserIDCollectionEnabled +// MARK: Data Processing Options + +- (void)testDataProcessingOptionDefaults { - [FBSDKSettings setAdvertiserIDCollectionEnabled:YES]; - XCTAssertTrue([FBSDKSettings isAdvertiserIDCollectionEnabled]); + FBSDKSettings.dataProcessingOptions = @[]; - [FBSDKSettings setAdvertiserIDCollectionEnabled:NO]; - XCTAssertFalse([FBSDKSettings isAdvertiserIDCollectionEnabled]); + XCTAssertEqualObjects( + FBSDKSettings.dataProcessingOptions[DATA_PROCESSING_OPTIONS_COUNTRY], + @0, + "Country should default to zero when not provided" + ); + XCTAssertEqualObjects( + FBSDKSettings.dataProcessingOptions[DATA_PROCESSING_OPTIONS_STATE], + @0, + "State should default to zero when not provided" + ); } -- (void)testSetLimitEventAndDataUsage +- (void)testSettingEmptyDataProcessingOptions { - [FBSDKSettings setLimitEventAndDataUsage:YES]; - XCTAssertTrue([FBSDKSettings shouldLimitEventAndDataUsage]); + FBSDKSettings.dataProcessingOptions = @[]; + + XCTAssertNotNil( + FBSDKSettings.dataProcessingOptions, + "Should not be able to set data processing options to an empty list of options but you can" + ); +} + +- (void)testSettingInvalidDataProcessOptions +{ + FBSDKSettings.dataProcessingOptions = @[@"Foo", @"Bar"]; + + XCTAssertNotNil( + FBSDKSettings.dataProcessingOptions, + "Should not be able to set data processing options to invalid list of options but you can" + ); - [FBSDKSettings setLimitEventAndDataUsage:NO]; - XCTAssertFalse([FBSDKSettings shouldLimitEventAndDataUsage]); + NSData *data = [NSKeyedArchiver archivedDataWithRootObject:FBSDKSettings.dataProcessingOptions]; - //test when NSUserDefaults does not contain FBSDKSettingsLimitEventAndDataUsage - id mockUserDefaults = [OCMockObject niceMockForClass:[NSUserDefaults class]]; - OCMStub([[mockUserDefaults standardUserDefaults] objectForKey:[OCMArg any]]).andReturn(nil); - [FBSDKSettings setLimitEventAndDataUsage:YES]; - XCTAssertFalse([FBSDKSettings shouldLimitEventAndDataUsage]); + XCTAssertEqualObjects( + userDefaultsSpy.capturedValues[@"com.facebook.sdk:FBSDKSettingsDataProcessingOptions"], + data, + "Should store the data processing options in the user defaults as data" + ); } -- (void)testSetDataProecssingOptions +- (void)testSettingDataProcessingOptionsWithCountryAndState { - [FBSDKSettings setDataProcessingOptions:@[@"LDU"] country:1 state:1000]; - NSDictionary *dataProcessingOptions = [FBSDKSettings dataProcessingOptions]; - NSSet *actualSet = [NSSet setWithArray:dataProcessingOptions[DATA_PROCESSING_OPTIONS]]; - NSSet *expectedSet = [NSSet setWithArray:@[@"LDU"]]; - XCTAssertTrue([expectedSet isEqualToSet:actualSet]); + int countryCode = -1000000000; + int stateCode = 100000000; + [FBSDKSettings setDataProcessingOptions:@[] country:countryCode state:stateCode]; + + XCTAssertEqualObjects( + FBSDKSettings.dataProcessingOptions[DATA_PROCESSING_OPTIONS], + @[], + "Should use the provided array of processing options" + ); + XCTAssertEqualObjects( + FBSDKSettings.dataProcessingOptions[DATA_PROCESSING_OPTIONS_COUNTRY], + @(countryCode), + "Should use the provided country code" + ); + XCTAssertEqualObjects( + FBSDKSettings.dataProcessingOptions[DATA_PROCESSING_OPTIONS_STATE], + @(stateCode), + "Should use the provided state code" + ); +} + +- (void)testDataProcessingOptionsWithEmptyCache +{ + FakeBundle *bundle = [FakeBundle bundleWithDictionary:@{}]; + [self stubMainBundleWith:bundle]; + + XCTAssertNil( + FBSDKSettings.dataProcessingOptions, + "Should not be able to get data processing options if there is none cached" + ); + XCTAssertEqualObjects( + userDefaultsSpy.capturedObjectRetrievalKey, + @"com.facebook.sdk:FBSDKSettingsDataProcessingOptions", + "Should attempt to access the cache to retrieve the initial value for a cachable property" + ); + XCTAssertNil( + bundle.capturedKeys.lastObject, + "Should not attempt to access the plist for data processing options" + ); +} + +- (void)testDataProcessingOptionsWithNonEmptyCache +{ + FakeBundle *bundle = [FakeBundle bundleWithDictionary:@{}]; + [self stubMainBundleWith:bundle]; + + FBSDKSettings.dataProcessingOptions = @[]; + + // Reset internal storage + [self resetCachedSettings]; + + XCTAssertNotNil( + FBSDKSettings.dataProcessingOptions, + "Should be able to retrieve data processing options from the cache" + ); + XCTAssertEqualObjects( + userDefaultsSpy.capturedObjectRetrievalKey, + @"com.facebook.sdk:FBSDKSettingsDataProcessingOptions", + "Should attempt to access the cache to retrieve the initial value for a cachable property" + ); + XCTAssertNil( + bundle.capturedKeys.lastObject, + "Should not attempt to access the plist for data processing options" + ); +} + +- (void)testDataProcessingOptionsInternalStorage +{ + FakeBundle *bundle = [FakeBundle bundleWithDictionary:@{}]; + [self stubMainBundleWith:bundle]; + + FBSDKSettings.dataProcessingOptions = @[]; + + XCTAssertNotNil(FBSDKSettings.dataProcessingOptions, "sanity check"); + XCTAssertNil( + userDefaultsSpy.capturedObjectRetrievalKey, + "Should not attempt to access the cache to retrieve objects that have a current value" + ); + XCTAssertNil( + bundle.capturedKeys.lastObject, + "Should not attempt to access the plist to retrieve objects that have a current value" + ); +} + +- (void)testRecordInstall +{ + XCTAssertNil( + userDefaultsSpy.capturedValues[@"com.facebook.sdk:FBSDKSettingsInstallTimestamp"], + "Should not persist the value of before setting it" + ); + [FBSDKSettings recordInstall]; + XCTAssertNotNil( + userDefaultsSpy.capturedValues[@"com.facebook.sdk:FBSDKSettingsInstallTimestamp"], + "Should persist the value after setting it" + ); + NSDate *date = userDefaultsSpy.capturedValues[@"com.facebook.sdk:FBSDKSettingsInstallTimestamp"]; + [FBSDKSettings recordInstall]; + XCTAssertEqual(date, userDefaultsSpy.capturedValues[@"com.facebook.sdk:FBSDKSettingsInstallTimestamp"], "Should not change the cached install timesstamp"); +} + +- (void)testRecordSetAdvertiserTrackingEnabled +{ + [FBSDKSettings recordSetAdvertiserTrackingEnabled]; + XCTAssertNotNil( + userDefaultsSpy.capturedValues[@"com.facebook.sdk:FBSDKSettingsSetAdvertiserTrackingEnabledTimestamp"], + "Should persist the value after setting it" + ); + NSDate *date = userDefaultsSpy.capturedValues[@"com.facebook.sdk:FBSDKSettingsSetAdvertiserTrackingEnabledTimestamp"]; + [FBSDKSettings recordSetAdvertiserTrackingEnabled]; + XCTAssertNotEqual(date, userDefaultsSpy.capturedValues[@"com.facebook.sdk:FBSDKSettingsSetAdvertiserTrackingEnabledTimestamp"], "Should update set advertiser tracking enabled timesstamp"); +} + +- (void)testIsEventDelayTimerExpired +{ + [[NSUserDefaults standardUserDefaults] removeObjectForKey:@"com.facebook.sdk:FBSDKSettingsInstallTimestamp"]; + [FBSDKSettings recordInstall]; + XCTAssertFalse([FBSDKSettings isEventDelayTimerExpired]); + + [[NSUserDefaults standardUserDefaults] removeObjectForKey:@"com.facebook.sdk:FBSDKSettingsInstallTimestamp"]; + NSDate *today = [NSDate new]; + NSCalendar *calendar = [[NSCalendar alloc] initWithCalendarIdentifier:NSCalendarIdentifierGregorian]; + NSDateComponents *addComponents = [NSDateComponents new]; + addComponents.month = -1; + NSDate *expiredDate = [calendar dateByAddingComponents:addComponents toDate:today options:0]; + [[NSUserDefaults standardUserDefaults] setObject:expiredDate forKey:@"com.facebook.sdk:FBSDKSettingsInstallTimestamp"]; + XCTAssertTrue([FBSDKSettings isEventDelayTimerExpired]); + + [[NSUserDefaults standardUserDefaults] removeObjectForKey:@"com.facebook.sdk:FBSDKSettingsInstallTimestamp"]; +} + +- (void)testIsSetATETimeExceedsInstallTime +{ + [[NSUserDefaults standardUserDefaults] removeObjectForKey:@"com.facebook.sdk:FBSDKSettingsInstallTimestamp"]; + [[NSUserDefaults standardUserDefaults] removeObjectForKey:@"com.facebook.sdk:FBSDKSettingsSetAdvertiserTrackingEnabledTimestamp"]; + [FBSDKSettings recordInstall]; + [FBSDKSettings recordSetAdvertiserTrackingEnabled]; + XCTAssertFalse([FBSDKSettings isSetATETimeExceedsInstallTime]); + + [[NSUserDefaults standardUserDefaults] removeObjectForKey:@"com.facebook.sdk:FBSDKSettingsInstallTimestamp"]; + [[NSUserDefaults standardUserDefaults] removeObjectForKey:@"com.facebook.sdk:FBSDKSettingsSetAdvertiserTrackingEnabledTimestamp"]; + [FBSDKSettings recordSetAdvertiserTrackingEnabled]; + NSDate *today = [NSDate new]; + NSCalendar *calendar = [[NSCalendar alloc] initWithCalendarIdentifier:NSCalendarIdentifierGregorian]; + NSDateComponents *addComponents = [NSDateComponents new]; + addComponents.month = -1; + NSDate *expiredDate = [calendar dateByAddingComponents:addComponents toDate:today options:0]; + [[NSUserDefaults standardUserDefaults] setObject:expiredDate forKey:@"com.facebook.sdk:FBSDKSettingsInstallTimestamp"]; + XCTAssertTrue([FBSDKSettings isSetATETimeExceedsInstallTime]); + + [[NSUserDefaults standardUserDefaults] removeObjectForKey:@"com.facebook.sdk:FBSDKSettingsInstallTimestamp"]; + [[NSUserDefaults standardUserDefaults] removeObjectForKey:@"com.facebook.sdk:FBSDKSettingsSetAdvertiserTrackingEnabledTimestamp"]; } - (void)testLoggingBehaviors @@ -105,22 +1526,58 @@ - (void)testLoggingBehaviors [FBSDKSettings setLoggingBehaviors:mockLoggingBehaviors]; XCTAssertEqualObjects(mockLoggingBehaviors, [FBSDKSettings loggingBehaviors]); - //test enable logging behavior - [FBSDKSettings enableLoggingBehavior: FBSDKLoggingBehaviorInformational]; + // test enable logging behavior + [FBSDKSettings enableLoggingBehavior:FBSDKLoggingBehaviorInformational]; XCTAssertTrue([[FBSDKSettings loggingBehaviors] containsObject:FBSDKLoggingBehaviorInformational]); - //test disable logging behavior - [FBSDKSettings disableLoggingBehavior: FBSDKLoggingBehaviorInformational]; + // test disable logging behavior + [FBSDKSettings disableLoggingBehavior:FBSDKLoggingBehaviorInformational]; XCTAssertFalse([[FBSDKSettings loggingBehaviors] containsObject:FBSDKLoggingBehaviorInformational]); } #pragma mark - test for internal functions -- (void)testSetUserAgentSuffix +// MARK: User Agent Suffix + +- (void)testUserAgentSuffix { - NSString *mockUserAgentSuffix = @"mockUserAgentSuffix"; - [FBSDKSettings setUserAgentSuffix:mockUserAgentSuffix]; - XCTAssertEqualObjects(mockUserAgentSuffix, [FBSDKSettings userAgentSuffix]); + XCTAssertNil( + FBSDKSettings.userAgentSuffix, + "User agent suffix should be nil by default" + ); +} + +- (void)testSettingUserAgentSuffix +{ + FBSDKSettings.userAgentSuffix = @"foo"; + + XCTAssertEqual( + FBSDKSettings.userAgentSuffix, + @"foo", + "Settings should return the explicitly set user agent suffix" + ); +} + +- (void)testSettingEmptyUserAgentSuffix +{ + FBSDKSettings.userAgentSuffix = emptyString; + + XCTAssertEqualObjects( + FBSDKSettings.userAgentSuffix, + emptyString, + "Should not store an empty user agent suffix but it will" + ); +} + +- (void)testSettingWhitespaceOnlyUserAgentSuffix +{ + FBSDKSettings.userAgentSuffix = whiteSpaceToken; + + XCTAssertEqualObjects( + FBSDKSettings.userAgentSuffix, + whiteSpaceToken, + "Should not store a whitespace only user agent suffix but it will" + ); } - (void)testSetGraphAPIVersion @@ -146,3 +1603,5 @@ - (void)testIsDataProcessingRestricted } @end + +#pragma clang diagnostic pop diff --git a/FBSDKCoreKit/FBSDKCoreKitTests/Internal/Helpers/AppDelegateObserverFake.swift b/FBSDKCoreKit/FBSDKCoreKitTests/Internal/Helpers/AppDelegateObserverFake.swift new file mode 100644 index 0000000000..441d951ab1 --- /dev/null +++ b/FBSDKCoreKit/FBSDKCoreKitTests/Internal/Helpers/AppDelegateObserverFake.swift @@ -0,0 +1,32 @@ +// 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. + +@objcMembers +public class ApplicationDelegateObserverFake: NSObject, FBSDKApplicationObserving { + public private(set) var didFinishLaunchingCallCount = 0 + public private(set) var capturedLaunchOptions: [UIApplication.LaunchOptionsKey: Any]? + + public func application( + _ application: UIApplication, + didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]? = nil + ) -> Bool { + didFinishLaunchingCallCount += 1 + capturedLaunchOptions = launchOptions + return true + } +} diff --git a/FBSDKCoreKit/FBSDKCoreKitTests/Internal/Helpers/AuthenticationSessionSpy.swift b/FBSDKCoreKit/FBSDKCoreKitTests/Internal/Helpers/AuthenticationSessionSpy.swift new file mode 100644 index 0000000000..95907f3e7a --- /dev/null +++ b/FBSDKCoreKit/FBSDKCoreKitTests/Internal/Helpers/AuthenticationSessionSpy.swift @@ -0,0 +1,54 @@ +// 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 Foundation + +@objcMembers +public class AuthenticationSessionSpy: NSObject, AuthenticationSessionHandling { + + public var capturedUrl: URL + public var capturedCallbackUrlScheme: String? + public var capturedCompletionHandler: FBSDKAuthenticationCompletionHandler? + public var startCallCount = 0 + public var cancelCallCount = 0 + + public static var makeDefaultSpy: AuthenticationSessionSpy { + guard let url = URL(string: "http://example.com") else { fatalError("Url creation failed") } + + return AuthenticationSessionSpy(url: url, callbackURLScheme: nil) { _, _ in } + } + + public required init( + url: URL, + callbackURLScheme: String?, + completionHandler: @escaping FBSDKAuthenticationCompletionHandler + ) { + capturedUrl = url + capturedCallbackUrlScheme = callbackURLScheme + capturedCompletionHandler = completionHandler + } + + public func start() -> Bool { + startCallCount += 1 + return true + } + + public func cancel() { + cancelCallCount += 1 + } +} diff --git a/FBSDKCoreKit/FBSDKCoreKitTests/Internal/Helpers/FBSDKMonitoringConfigurationTestHelper.swift b/FBSDKCoreKit/FBSDKCoreKitTests/Internal/Helpers/FBSDKMonitoringConfigurationTestHelper.swift new file mode 100644 index 0000000000..0de6f44b63 --- /dev/null +++ b/FBSDKCoreKit/FBSDKCoreKitTests/Internal/Helpers/FBSDKMonitoringConfigurationTestHelper.swift @@ -0,0 +1,38 @@ +// 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 Foundation + +@objcMembers +public class FBSDKMonitoringConfigurationTestHelper: NSObject { + + /// Bundles key value pairs in the same format they're returned from the graph. + /// ex: + /// { + /// "sample_rates": [ + /// { + /// "key": "foo", + /// "value": 1 + /// } + /// ] + /// } + public static func sampleRates(withEntryPairs pairs: [String: Any]) -> [String: Any] { + let sampleRateDicts = pairs.map { ["key": $0, "value": $1] } + return ["sample_rates": sampleRateDicts] + } +} diff --git a/FBSDKCoreKit/FBSDKCoreKitTests/Internal/ServerConfiguration/FBSDKServerConfigurationFixtures.h b/FBSDKCoreKit/FBSDKCoreKitTests/Internal/Helpers/FBSDKServerConfigurationFixtures.h similarity index 100% rename from FBSDKCoreKit/FBSDKCoreKitTests/Internal/ServerConfiguration/FBSDKServerConfigurationFixtures.h rename to FBSDKCoreKit/FBSDKCoreKitTests/Internal/Helpers/FBSDKServerConfigurationFixtures.h diff --git a/FBSDKCoreKit/FBSDKCoreKitTests/Internal/ServerConfiguration/FBSDKServerConfigurationFixtures.m b/FBSDKCoreKit/FBSDKCoreKitTests/Internal/Helpers/FBSDKServerConfigurationFixtures.m similarity index 100% rename from FBSDKCoreKit/FBSDKCoreKitTests/Internal/ServerConfiguration/FBSDKServerConfigurationFixtures.m rename to FBSDKCoreKit/FBSDKCoreKitTests/Internal/Helpers/FBSDKServerConfigurationFixtures.m diff --git a/FBSDKCoreKit/FBSDKCoreKitTests/Internal/Helpers/FBSDKTestCase.h b/FBSDKCoreKit/FBSDKCoreKitTests/Internal/Helpers/FBSDKTestCase.h new file mode 100644 index 0000000000..24796085b5 --- /dev/null +++ b/FBSDKCoreKit/FBSDKCoreKitTests/Internal/Helpers/FBSDKTestCase.h @@ -0,0 +1,304 @@ +// 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 "FBSDKAccessToken.h" +#import "FBSDKAppEvents.h" +#import "FBSDKAppEventsUtility.h" +#import "FBSDKApplicationDelegate.h" +#import "FBSDKGraphRequestConnection.h" +#import "FBSDKKeychainStore.h" +#import "FBSDKMeasurementEventListener.h" +#import "FBSDKProfile+Internal.h" +#import "FBSDKServerConfiguration.h" +#import "FBSDKServerConfigurationManager.h" + +@class FakeTokenCache; + +NS_ASSUME_NONNULL_BEGIN + +/** + This shared test case class is intended to provide commonly mocked objects and methods for stubbing out common side effects such as + fetching from the network when objects are missing from a given cache. Additionally this class will handle stopping mocking and invalidating + mock objects to avoid potential shared global state between tests. + + In general there are three broad use cases for mocks. These include: + + 1) stubbing out a method in order to avoid calling it or to provide a known return value. + + 2) stubbing out a method (usually an initializer or a singleton) to replace an object with a test object. + + 3) stubbing out a method on the object you're testing. ie. use the real implementation for method a but stub out the implementation for method b. + + 4) verifying behavior - something was called or something was not called etc... + +Before you write a new class mock. Check to see if there's already an implementation in this class. +Also, to get a better understanding of mocking, please read the documentation at https://ocmock.org/ +*/ +@interface FBSDKTestCase : XCTestCase + +/// Used for sharing an `FBSDKAccessToken` class mock between tests +@property (nullable, assign) id accessTokenClassMock; + +/// Used for sharing an `FBSDKAuthenticationToken` class mock between tests +@property (nullable, assign) id authenticationTokenClassMock; + +/// Used for sharing a common app identifier between tests. This is not a valid FB App ID +@property (nullable, assign) NSString *appID; + +/// Used during `-setUp` to determine the type of mock for `appEventsMock` (partial or nice), default is `NO` +@property (assign) BOOL shouldAppEventsMockBePartial; + +/// Used for sharing an `FBSDKAppEvents` mock between tests +@property (nullable, assign) id appEventsMock; + +/// Used for mocking `FBSDKAppEventState` between tests +@property (nullable, assign) id appEventStatesMock; + +/// Used for sharing a `FBSDKAppEventsUtility` class mock between tests +@property (nullable, nonatomic, assign) id appEventsUtilityClassMock; + +/// Used for sharing an `FBSDKAppLinkResolverRequestBuilder` class mock between tests +@property (nullable, assign) id appLinkResolverRequestBuilderMock; + +/// Used for sharing an `FBSDKApplicationDelegate` class mock between tests +@property (nullable, assign) id fbApplicationDelegateClassMock; + +/// Used for sharing an `FBSDKFeatureManager` class mock between tests +@property (nullable, assign) id featureManagerClassMock; + +/// Used for sharing an `FBSDKGatekeeperManager` class mock between tests +@property (nullable, assign) id gatekeeperManagerClassMock; + +/// Used for sharing an `FBSDKGraphRequest` class mock between tests +@property (nullable, assign) id graphRequestMock; + +/// Used for sharing an `NSBundle` class mock between tests +@property (nullable, assign) id nsBundleClassMock; + +/// Used for sharing an `NSUserDefaults` class mock between tests +@property (nullable, assign) id nsUserDefaultsClassMock; + +/// Used for sharing an `FBSDKProfile` class mock between tests +@property (nullable, assign) id profileClassMock; + +/// Used for sharing an `FBSDKServerConfigurationManager` class mock between tests +@property (nullable, assign) id serverConfigurationManagerClassMock; + +/// Used for sharing an `FBSDKSettings` class mock between tests +@property (nullable, assign) id settingsClassMock; + +/// Used for sharing an `SKAdNetwork` class mock between tests +@property (nullable, nonatomic, assign) id skAdNetworkClassMock; + +/// Used for sharing an `NSNotificationCenter` class mock between tests +@property (nullable, nonatomic, assign) id nsNotificationCenterClassMock; + +/// Used for sharing an `FBSDKMeasurementEventListener` class mock between tests +@property (nullable, nonatomic, assign) id measurementEventListenerClassMock; + +/// Used for sharing a `FBSDKTimeSpentData` class mock between tests +@property (nullable, nonatomic, assign) id timeSpentDataClassMock; + +/// Used for sharing a `FBSDKInternalUtility` class mock between tests +@property (nullable, nonatomic, assign) id internalUtilityClassMock; + +/// Used for sharing a `FBSDKSKAdNetworkReporter` class mock between tests +@property (nullable, nonatomic, assign) id adNetworkReporterClassMock; + +/// Used for sharing a `FBSDKModelManager` class mock between tests +@property (nullable, nonatomic, assign) id modelManagerClassMock; + +/// Used for sharing a `FBSDKGraphRequestPiggybackManager` class mock between tests +@property (nullable, nonatomic, assign) id graphRequestPiggybackManagerMock; + +/// Used for sharing a `FBSDKGraphRequestConnection` class mock between tests +@property (nullable, nonatomic, assign) id graphRequestConnectionClassMock; + +/// Used for sharing a `FBSDKCrashShield` class mock between tests +@property (nullable, nonatomic, assign) id crashShieldClassMock; + +/// Used for sharing a `NSDate` class mock between tests +@property (nullable, nonatomic, assign) id nsDateClassMock; + +/// Used for sharing a `UIApplication.sharedApplication` mock between tests +@property (nullable, nonatomic, assign) id sharedApplicationMock; + +/// Used for sharing a `FBSDKLogger` class mock between tests +@property (nullable, nonatomic, assign) id loggerClassMock; + +/// Used for sharing an `NSProcessInfo.processInfo` mock between tests +@property (nullable, nonatomic, assign) id processInfoMock; + +/// Used for stubbing any instance that conforms to the `UIViewControllerTransitionCoordinator` protocol +@property (nullable, nonatomic, assign) id transitionCoordinatorMock; + +/// Used for sharing a `FBSDKBridgeAPIResponse` class mock between tests +@property (nullable, nonatomic, assign) id bridgeApiResponseClassMock; + +/// Used for sharing a `FBSDKCrashObserver` class mock between tests +@property (nullable, nonatomic, assign) id crashObserverClassMock; + +/// Used for sharing a `FBSDKErrorReport` class mock between tests +@property (nullable, nonatomic, assign) id errorReportClassMock; + +/// Stubs `FBSDKSettings.appID` and return the provided value +- (void)stubAppID:(nullable NSString *)appID; + +/// Stubs `FBSDKSettings.isSDKInitialized` and return the provided value +- (void)stubIsSDKInitialized:(BOOL)initialized; + +/// Stubs `FBSDKSettings.isAutoLogAppEventsEnabled` and return the provided value +- (void)stubIsAutoLogAppEventsEnabled:(BOOL)isEnabled; + +/// Stubs `FBSDKGateKeeperManager.loadGateKeepers:` to avoid the side effect of a network fetch +- (void)stubLoadingGateKeepers; + +/// Stubs `FBSDKFeatureManager.checkFeature:` for any feature requested by FeatureManager. +- (void)stubCheckingFeatures; + +/// Stubs `FBSDKServerConfigurationManager.cachedServerConfiguration` and returns the default server configuration. +/// Use this when you don't care what the actual configuration is and want to avoid a network call. +- (void)stubFetchingCachedServerConfiguration; + +/// Stubs `FBSDKServerConfigurationManager.cachedServerConfiguration` with a specific configuration +- (void)stubCachedServerConfigurationWithServerConfiguration:(FBSDKServerConfiguration *)serverConfiguration; + +/// Stubs `FBSDKServerConfiguratinManager.loadServerConfigurationWithCompletionBlock:` with arguments to invoke the completion with. +/// If the completion is nil then this will ignore any arguments passed to it. +- (void)stubServerConfigurationFetchingWithConfiguration:(nullable FBSDKServerConfiguration *)configuration + error:(nullable NSError *)error; + +/// Stubs `NSBundle.mainBundle` with the provided NSBundle +- (void)stubMainBundleWith:(NSBundle *)bundle; + +/// Stubs `NSUserDefaults.standardUserDefaults` with the provided NSUserDefaults +- (void)stubUserDefaultsWith:(NSUserDefaults *)defaults; + +/// Stubs `FBSDKApplicationDelegate.initializeSDK` with a dictionary of launch options +- (void)stubInitializeSDKWith:(NSDictionary *)launchOptions; + +/// Prevents logging on changes to Settings properties +- (void)stubLoggingIfUserSettingsChanged; + +/// Stubs `FBSDKSettings.tokenCache` +- (void)stubTokenCacheWith:(FakeTokenCache *)cache; + +/// Stubs `FBSDKProfile.fetchCachedProfile` +- (void)stubCachedProfileWith:(FBSDKProfile *__nullable)profile; + +/// Stubs `FBSDKApplicationDelegate.sharedInstance` +- (void)stubFBApplicationDelegateSharedInstanceWith:(FBSDKApplicationDelegate *)delegate; + +/// Stubs `SKAdNetwork.registerAppForAdNetworkAttribution` +- (void)stubRegisterAppForAdNetworkAttribution; + +/// Stubs `NSNotificationCenter.defaultCenter` and returns the provided notification center +- (void)stubDefaultNotificationCenterWith:(NSNotificationCenter *)notificationCenter; + +/// Stubs `MeasurementEventListener.defaultListener` and returns the provided listener. +- (void)stubDefaultMeasurementEventListenerWith:(FBSDKMeasurementEventListener *)eventListener; + +/// Stubs `FBSDKSettings.graphAPIVersion` with the provided version string +- (void)stubGraphAPIVersionWith:(NSString *)version; + +/// Stubs `FBSDKAccessToken.currentAccessToken` with the provided token +- (void)stubCurrentAccessTokenWith:(nullable FBSDKAccessToken *)token; + +/// Stubs `FBSDKAuthenticationToken.currentAuthenticationToken` with the provided token +- (void)stubCurrentAuthenticationTokenWith:(nullable FBSDKAuthenticationToken *)token; + +/// Stubs `FBSDKSettings.clientToken` with the provided token string +- (void)stubClientTokenWith:(nullable NSString *)token; + +/// Stubs `FBSDKSettings.getAdvertisingTrackingStatus` with the provided value +- (void)stubAdvertisingTrackingStatusWith:(FBSDKAdvertisingTrackingStatus)trackingStatus; + +/// Stubs `FBSDKSKAdNetworkReporter._loadConfigurationWithBlock` +- (void)stubLoadingAdNetworkReporterConfiguration; + +/// Stubs `FBSDKAppEventsUtility.shouldDropAppEvent` with the provided value +- (void)stubAppEventsUtilityShouldDropAppEventWith:(BOOL)shouldDropEvent; + +/// Stubs `FBSDKSettings.shouldLimitEventAndDataUsage` with the provided value +- (void)stubSettingsShouldLimitEventAndDataUsageWith:(BOOL)shouldLimit; + +/// Stubs `FBSDKAppEventsUtility.advertiserID` with the provided value +- (void)stubAppEventsUtilityAdvertiserIDWith:(nullable NSString *)identifier; + +/// Stubs `FBSDKAppEventsUtility.tokenStringToUseFor:` and returns the provided string +- (void)stubAppEventsUtilityTokenStringToUseForTokenWith:(NSString *)tokenString; + +/// Stubs `FBSDKGraphRequest.startWithCompletionHandler:` and returns the provided result, error and connection +- (void)stubGraphRequestWithResult:(id)result error:(nullable NSError *)error connection:(nullable FBSDKGraphRequestConnection *)connection; + +/// Stubs `FBSDKGraphRequest.startWithCompletionHandler:` and returns the provided result, error and connection +- (void)stubAppLinkResolverRequestBuilderWithIdiomSpecificField:(nullable NSString *)field; + +/// Stubs `FBSDKGraphRequestPiggybackManager._lastRefreshTry` and returns the provided `NSDate` +- (void)stubGraphRequestPiggybackManagerLastRefreshTryWith:(NSDate *)date; + +/// Disables creation of graph request connections so that they cannot be started. +/// This is the nuclear option. It should be removed as soon as possible so that we can test important things +/// like whether or not a given method actually started a graph request. +/// This should be used only as needed as a stopgap to keep tests +/// from hitting the network while proper mocks are being written. +- (void)stubAllocatingGraphRequestConnection; + +/// Stubs `FBSDKFeatureManager.disableFeature:` for the provided feature +- (void)stubDisableFeature:(NSString *)feature; + +/// Stubs `FBSDKSettings.isDataProcessingRestricted` and returns the provided value +- (void)stubIsDataProcessingRestricted:(BOOL)isRestricted; + +/// Stubs `FBSDKSettings.facebookDomainPart` with the provided value +- (void)stubFacebookDomainPartWith:(NSString *)domainPart; + +/// Stubs `UIApplication.sharedApplication`'s `canOpenURL:` method and returns the provided value +- (void)stubCanOpenURLWith:(BOOL)canOpenURL; + +/// Stubs `UIApplication.sharedApplication`'s `openURL:` method and returns the provided value +- (void)stubOpenURLWith:(BOOL)openURL; + +/// Stubs `UIApplication.sharedApplication`'s `openURL:options:completionHandler:` method +/// +/// - Parameters: +/// - performCompletion: Whether to invoke the completion handler +/// - completionSuccess: The value to pass for the success parameter of the completion handler +- (void)stubOpenUrlOptionsCompletionHandlerWithPerformCompletion:(BOOL)performCompletion + completionSuccess:(BOOL)completionSuccess; + +/// Stubs `FBSDKSettings.appURLSchemeSuffix` and return the provided value +- (void)stubAppUrlSchemeSuffixWith:(nullable NSString *)suffix; + +/// Resets cached properties in `FBSDKSettings` +- (void)resetCachedSettings; + +/// Stubs `FBSDKSettings.userAgentSuffix` and returns the provided value +- (void)stubUserAgentSuffixWith:(nullable NSString *)suffix; + +/// Stubs `NSProcessInfo`'s `isOperatingSystemVersionAtLeast:` and returns the provided value +- (void)stubIsOperatingSystemVersionAtLeast:(NSOperatingSystemVersion)version with:(BOOL)returnValue; + +/// Stubs `FBSDKInternalUtility`'s `appURLScheme` property to return the provided scheme +- (void)stubAppUrlSchemeWith:(nullable NSString *)scheme; + +@end + +NS_ASSUME_NONNULL_END diff --git a/FBSDKCoreKit/FBSDKCoreKitTests/Internal/Helpers/FBSDKTestCase.m b/FBSDKCoreKit/FBSDKCoreKitTests/Internal/Helpers/FBSDKTestCase.m new file mode 100644 index 0000000000..bc58108794 --- /dev/null +++ b/FBSDKCoreKit/FBSDKCoreKitTests/Internal/Helpers/FBSDKTestCase.m @@ -0,0 +1,655 @@ +// 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 ser`vices 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 "FBSDKTestCase.h" + +#import + +// For mocking SKAdNetwork +#import + +#import "FBSDKAppEvents.h" +#import "FBSDKAppEvents+Internal.h" +#import "FBSDKAppEventsState.h" +#import "FBSDKApplicationDelegate+Internal.h" +#import "FBSDKCoreKit+Internal.h" +#import "FBSDKCoreKitTestUtility.h" +#import "FBSDKCrashObserver.h" +#import "FBSDKCrashShield.h" +#import "FBSDKErrorReport.h" +#import "FBSDKFeatureManager.h" +#import "FBSDKGraphRequestPiggybackManager.h" +#import "FBSDKInternalUtility.h" +#import "FBSDKKeychainStore.h" +#import "FBSDKModelManager.h" +#import "FBSDKSKAdNetworkReporter.h" +#import "FBSDKSettings.h" +#import "FBSDKTimeSpentData.h" + +@interface FBSDKAppEvents (Testing) ++ (FBSDKAppEvents *)singleton; +@end + +@interface FBSDKGraphRequestPiggybackManager (Testing) ++ (NSDate *)_lastRefreshTry; +@end + +@interface FBSDKSettings (Testing) ++ (void)_logIfSDKSettingsChanged; ++ (void)resetLoggingBehaviorsCache; ++ (void)resetFacebookAppIDCache; ++ (void)resetFacebookUrlSchemeSuffixCache; ++ (void)resetFacebookClientTokenCache; ++ (void)resetFacebookDisplayNameCache; ++ (void)resetFacebookDomainPartCache; ++ (void)resetFacebookJpegCompressionQualityCache; ++ (void)resetFacebookInstrumentEnabledCache; ++ (void)resetFacebookAutoLogAppEventsEnabledCache; ++ (void)resetFacebookAdvertiserIDCollectionEnabledCache; ++ (void)resetAdvertiserTrackingStatusCache; ++ (void)resetUserAgentSuffixCache; ++ (void)resetFacebookCodelessDebugLogEnabledCache; ++ (void)resetDataProcessingOptionsCache; +@end + +typedef void (^FBSDKSKAdNetworkReporterBlock)(void); +@interface FBSDKSKAdNetworkReporter (Testing) ++ (void)_loadConfigurationWithBlock:(FBSDKSKAdNetworkReporterBlock)block; +@end + +@implementation FBSDKTestCase + +- (void)setUp +{ + [super setUp]; + + _appID = @"appid"; + + [self setUpNSBundleMock]; + [self setUpSettingsMock]; + [self setUpServerConfigurationManagerMock]; + [self setUpAppEventsMock]; + [self setUpAppEventsUtilityMock]; + [self setUpFBApplicationDelegateMock]; + [self setUpGateKeeperManagerMock]; + [self setUpFeatureManagerMock]; + [self setUpNSUserDefaultsMock]; + [self setUpAccessTokenMock]; + [self setUpAuthenticationTokenClassMock]; + [self setUpProfileMock]; + [self setUpSKAdNetworkMock]; + [self setUpNSNotificationCenterMock]; + [self setUpMeasurementEventListenerMock]; + [self setUpTimeSpendDataMock]; + [self setUpInternalUtilityMock]; + [self setUpAdNetworkReporterMock]; + [self setUpAppLinkResolverRequestBuilderMock]; + [self setUpGraphRequestMock]; + [self setUpModelManagerClassMock]; + [self setUpGraphRequestPiggybackManagerMock]; + [self setUpGraphRequestConnectionClassMock]; + [self setUpCrashShieldClassMock]; + [self setUpNSDateClassMock]; + [self setUpSharedApplicationMock]; + [self setUpLoggerClassMock]; + [self setUpProcessInfoMock]; + [self setUpTransitionCoordinatorMock]; + [self setUpBridgeApiClassMock]; + [self setUpCrashObserverClassMock]; + [self setUpErrorReportClassMock]; +} + +- (void)tearDown +{ + [super tearDown]; + + [_appEventsMock stopMocking]; + _appEventsMock = nil; + + [_appEventStatesMock stopMocking]; + _appEventStatesMock = nil; + + [_appEventsUtilityClassMock stopMocking]; + _appEventsUtilityClassMock = nil; + + [_fbApplicationDelegateClassMock stopMocking]; + _fbApplicationDelegateClassMock = nil; + + [_featureManagerClassMock stopMocking]; + _featureManagerClassMock = nil; + + [_gatekeeperManagerClassMock stopMocking]; + _gatekeeperManagerClassMock = nil; + + [_serverConfigurationManagerClassMock stopMocking]; + _serverConfigurationManagerClassMock = nil; + + [_settingsClassMock stopMocking]; + _settingsClassMock = nil; + + [_nsBundleClassMock stopMocking]; + _nsBundleClassMock = nil; + + [_nsUserDefaultsClassMock stopMocking]; + _nsUserDefaultsClassMock = nil; + + [_accessTokenClassMock stopMocking]; + _accessTokenClassMock = nil; + + [_authenticationTokenClassMock stopMocking]; + _authenticationTokenClassMock = nil; + + [_profileClassMock stopMocking]; + _profileClassMock = nil; + + [_skAdNetworkClassMock stopMocking]; + _skAdNetworkClassMock = nil; + + [_nsNotificationCenterClassMock stopMocking]; + _nsNotificationCenterClassMock = nil; + + [_measurementEventListenerClassMock stopMocking]; + _measurementEventListenerClassMock = nil; + + [_timeSpentDataClassMock stopMocking]; + _timeSpentDataClassMock = nil; + + [_internalUtilityClassMock stopMocking]; + _internalUtilityClassMock = nil; + + [_adNetworkReporterClassMock stopMocking]; + _adNetworkReporterClassMock = nil; + + [_appLinkResolverRequestBuilderMock stopMocking]; + _appLinkResolverRequestBuilderMock = nil; + + [_graphRequestMock stopMocking]; + _graphRequestMock = nil; + + [_modelManagerClassMock stopMocking]; + _modelManagerClassMock = nil; + + [_graphRequestPiggybackManagerMock stopMocking]; + _graphRequestPiggybackManagerMock = nil; + + [_graphRequestConnectionClassMock stopMocking]; + _graphRequestConnectionClassMock = nil; + + [_crashShieldClassMock stopMocking]; + _crashShieldClassMock = nil; + + [_nsDateClassMock stopMocking]; + _nsDateClassMock = nil; + + [_sharedApplicationMock stopMocking]; + _sharedApplicationMock = nil; + + [_loggerClassMock stopMocking]; + _loggerClassMock = nil; + + [_processInfoMock stopMocking]; + _processInfoMock = nil; + + [_transitionCoordinatorMock stopMocking]; + _transitionCoordinatorMock = nil; + + [_bridgeApiResponseClassMock stopMocking]; + _bridgeApiResponseClassMock = nil; + + [_crashObserverClassMock stopMocking]; + _crashObserverClassMock = nil; + + [_errorReportClassMock stopMocking]; + _errorReportClassMock = nil; +} + +- (void)setUpSettingsMock +{ + _settingsClassMock = OCMStrictClassMock(FBSDKSettings.class); +} + +- (void)setUpFBApplicationDelegateMock +{ + _fbApplicationDelegateClassMock = OCMStrictClassMock(FBSDKApplicationDelegate.class); +} + +- (void)setUpGateKeeperManagerMock +{ + _gatekeeperManagerClassMock = OCMClassMock(FBSDKGateKeeperManager.class); +} + +- (void)setUpFeatureManagerMock +{ + _featureManagerClassMock = [OCMockObject niceMockForClass:[FBSDKFeatureManager class]]; +} + +- (void)setUpServerConfigurationManagerMock +{ + self.serverConfigurationManagerClassMock = OCMStrictClassMock(FBSDKServerConfigurationManager.class); +} + +- (void)setUpAppEventsMock +{ + if (self.shouldAppEventsMockBePartial) { + // Since the `init` method is marked unavailable but just as a measure to prevent creating multiple + // instances and enforce the singleton pattern, we will circumvent that by casting to a plain `NSObject` + // after `alloc` in order to call `init`. + _appEventsMock = OCMPartialMock([(NSObject *)[FBSDKAppEvents alloc] init]); + } else { + _appEventsMock = OCMClassMock([FBSDKAppEvents class]); + } + + // Since numerous areas in FBSDK can end up calling `[FBSDKAppEvents singleton]`, + // we will stub the singleton accessor out for our mock instance. + OCMStub([_appEventsMock singleton]).andReturn(_appEventsMock); + + _appEventStatesMock = OCMClassMock([FBSDKAppEventsState class]); + OCMStub([_appEventStatesMock alloc]).andReturn(_appEventStatesMock); + OCMStub([_appEventStatesMock initWithToken:[OCMArg any] appID:[OCMArg any]]).andReturn(_appEventStatesMock); +} + +- (void)setUpAppEventsUtilityMock +{ + _appEventsUtilityClassMock = OCMStrictClassMock(FBSDKAppEventsUtility.class); +} + +- (void)setUpNSBundleMock +{ + self.nsBundleClassMock = OCMStrictClassMock(NSBundle.class); +} + +- (void)setUpNSUserDefaultsMock +{ + self.nsUserDefaultsClassMock = OCMStrictClassMock(NSUserDefaults.class); +} + +- (void)setUpAccessTokenMock +{ + self.accessTokenClassMock = OCMStrictClassMock(FBSDKAccessToken.class); +} + +- (void)setUpAuthenticationTokenClassMock +{ + self.authenticationTokenClassMock = OCMStrictClassMock(FBSDKAuthenticationToken.class); +} + +- (void)setUpProfileMock +{ + self.profileClassMock = OCMStrictClassMock(FBSDKProfile.class); +} + +- (void)setUpSKAdNetworkMock +{ + if (@available(iOS 11.3, *)) { + self.skAdNetworkClassMock = OCMStrictClassMock(SKAdNetwork.class); + } +} + +- (void)setUpNSNotificationCenterMock +{ + self.nsNotificationCenterClassMock = OCMClassMock(NSNotificationCenter.class); +} + +- (void)setUpMeasurementEventListenerMock +{ + self.measurementEventListenerClassMock = OCMStrictClassMock(FBSDKMeasurementEventListener.class); +} + +- (void)setUpTimeSpendDataMock +{ + self.timeSpentDataClassMock = OCMStrictClassMock(FBSDKTimeSpentData.class); +} + +- (void)setUpInternalUtilityMock +{ + self.internalUtilityClassMock = OCMStrictClassMock(FBSDKInternalUtility.class); +} + +- (void)setUpAdNetworkReporterMock +{ + self.adNetworkReporterClassMock = OCMClassMock(FBSDKSKAdNetworkReporter.class); +} + +- (void)setUpAppLinkResolverRequestBuilderMock +{ + _appLinkResolverRequestBuilderMock = OCMStrictClassMock(FBSDKAppLinkResolverRequestBuilder.class); +} + +- (void)setUpGraphRequestMock +{ + _graphRequestMock = OCMStrictClassMock(FBSDKGraphRequest.class); +} + +- (void)setUpModelManagerClassMock +{ + self.modelManagerClassMock = OCMClassMock(FBSDKModelManager.class); +} + +- (void)setUpGraphRequestPiggybackManagerMock +{ + self.graphRequestPiggybackManagerMock = OCMClassMock(FBSDKGraphRequestPiggybackManager.class); +} + +- (void)setUpGraphRequestConnectionClassMock +{ + self.graphRequestConnectionClassMock = OCMClassMock(FBSDKGraphRequestConnection.class); +} + +- (void)setUpCrashShieldClassMock +{ + self.crashShieldClassMock = OCMClassMock(FBSDKCrashShield.class); +} + +- (void)setUpNSDateClassMock +{ + self.nsDateClassMock = OCMClassMock(NSDate.class); +} + +- (void)setUpSharedApplicationMock +{ + self.sharedApplicationMock = OCMClassMock(UIApplication.class); + OCMStub(ClassMethod([_sharedApplicationMock sharedApplication])).andReturn(_sharedApplicationMock); +} + +- (void)setUpLoggerClassMock +{ + self.loggerClassMock = OCMClassMock(FBSDKLogger.class); +} + +- (void)setUpProcessInfoMock +{ + self.processInfoMock = OCMClassMock(NSProcessInfo.class); + OCMStub(ClassMethod([_processInfoMock processInfo])).andReturn(_processInfoMock); +} + +- (void)setUpTransitionCoordinatorMock +{ + self.transitionCoordinatorMock = [OCMockObject + mockForProtocol:@protocol(UIViewControllerTransitionCoordinator)]; +} + +- (void)setUpBridgeApiClassMock +{ + _bridgeApiResponseClassMock = OCMClassMock(FBSDKBridgeAPIResponse.class); +} + +- (void)setUpCrashObserverClassMock +{ + _crashObserverClassMock = OCMClassMock(FBSDKCrashObserver.class); +} + +- (void)setUpErrorReportClassMock +{ + _errorReportClassMock = OCMClassMock(FBSDKErrorReport.class); +} + +#pragma mark - Public Methods + +- (void)stubAppID:(NSString *)appID +{ + [OCMStub(ClassMethod([_settingsClassMock appID])) andReturn:appID]; +} + +- (void)stubIsSDKInitialized:(BOOL)initialized +{ + [OCMStub(ClassMethod([_fbApplicationDelegateClassMock isSDKInitialized])) andReturnValue:OCMOCK_VALUE(initialized)]; +} + +- (void)stubLoadingGateKeepers +{ + OCMStub(ClassMethod([_gatekeeperManagerClassMock loadGateKeepers:OCMArg.any])); +} + +- (void)stubCheckingFeatures +{ + OCMStubIgnoringNonObjectArgs([_featureManagerClassMock checkFeature:FBSDKFeatureInstrument completionBlock:OCMArg.any]); +} + +- (void)stubFetchingCachedServerConfiguration +{ + FBSDKServerConfiguration *configuration = [FBSDKServerConfiguration defaultServerConfigurationForAppID:_appID]; + OCMStub(ClassMethod([_serverConfigurationManagerClassMock cachedServerConfiguration])).andReturn(configuration); +} + +- (void)stubCachedServerConfigurationWithServerConfiguration:(FBSDKServerConfiguration *)serverConfiguration +{ + OCMStub(ClassMethod([_serverConfigurationManagerClassMock cachedServerConfiguration])).andReturn(serverConfiguration); +} + +- (void)stubMainBundleWith:(NSBundle *)bundle +{ + OCMStub(ClassMethod([_nsBundleClassMock mainBundle])).andReturn(bundle); +} + +- (void)stubUserDefaultsWith:(NSUserDefaults *)defaults +{ + OCMStub(ClassMethod([_nsUserDefaultsClassMock standardUserDefaults])).andReturn(defaults); +} + +- (void)stubInitializeSDKWith:(NSDictionary *)launchOptions +{ + OCMStub(ClassMethod([_fbApplicationDelegateClassMock initializeSDK:OCMArg.any])); +} + +- (void)stubLoggingIfUserSettingsChanged +{ + OCMStub(ClassMethod([_settingsClassMock _logIfSDKSettingsChanged])); +} + +- (void)stubTokenCacheWith:(FakeTokenCache *)cache +{ + OCMStub(ClassMethod([_settingsClassMock tokenCache])).andReturn(cache); +} + +- (void)stubIsAutoLogAppEventsEnabled:(BOOL)isEnabled +{ + OCMStub(ClassMethod([_settingsClassMock isAutoLogAppEventsEnabled])).andReturn(isEnabled); +} + +- (void)stubCachedProfileWith:(FBSDKProfile *__nullable)profile +{ + OCMStub(ClassMethod([_profileClassMock fetchCachedProfile])).andReturn(profile); +} + +- (void)stubFBApplicationDelegateSharedInstanceWith:(FBSDKApplicationDelegate *)delegate +{ + OCMStub(ClassMethod([_fbApplicationDelegateClassMock sharedInstance])).andReturn(delegate); +} + +- (void)stubRegisterAppForAdNetworkAttribution +{ + if (@available(iOS 11.3, *)) { + OCMStub(ClassMethod([_skAdNetworkClassMock registerAppForAdNetworkAttribution])); + } +} + +- (void)stubDefaultNotificationCenterWith:(NSNotificationCenter *)notificationCenter +{ + OCMStub(ClassMethod([_nsNotificationCenterClassMock defaultCenter])).andReturn(notificationCenter); +} + +- (void)stubDefaultMeasurementEventListenerWith:(FBSDKMeasurementEventListener *)eventListener +{ + OCMStub([_measurementEventListenerClassMock defaultListener]).andReturn(eventListener); +} + +- (void)stubCurrentAccessTokenWith:(FBSDKAccessToken *)token +{ + OCMStub(ClassMethod([_accessTokenClassMock currentAccessToken])).andReturn(token); +} + +- (void)stubCurrentAuthenticationTokenWith:(FBSDKAuthenticationToken *)token +{ + OCMStub(ClassMethod([_authenticationTokenClassMock currentAuthenticationToken])).andReturn(token); +} + +- (void)stubGraphAPIVersionWith:(NSString *)version +{ + OCMStub(ClassMethod([_settingsClassMock graphAPIVersion])).andReturn(version); +} + +- (void)stubClientTokenWith:(nullable NSString *)token +{ + OCMStub(ClassMethod([_settingsClassMock clientToken])).andReturn(token); +} + +- (void)stubAppEventsUtilityShouldDropAppEventWith:(BOOL)shouldDropEvent +{ + OCMStub(ClassMethod([_appEventsUtilityClassMock shouldDropAppEvent])).andReturn(shouldDropEvent); +} + +- (void)stubAdvertisingTrackingStatusWith:(FBSDKAdvertisingTrackingStatus)trackingStatus +{ + OCMStub(ClassMethod([_settingsClassMock getAdvertisingTrackingStatus])).andReturn(trackingStatus); +} + +- (void)stubLoadingAdNetworkReporterConfiguration +{ + OCMStub(ClassMethod([_adNetworkReporterClassMock _loadConfigurationWithBlock:OCMArg.any])); +} + +- (void)stubSettingsShouldLimitEventAndDataUsageWith:(BOOL)shouldLimit +{ + OCMStub(ClassMethod([_settingsClassMock shouldLimitEventAndDataUsage])).andReturn(shouldLimit); +} + +- (void)stubAppEventsUtilityAdvertiserIDWith:(nullable NSString *)identifier +{ + OCMStub(ClassMethod([_appEventsUtilityClassMock advertiserID])).andReturn(identifier); +} + +- (void)stubAppEventsUtilityTokenStringToUseForTokenWith:(NSString *)tokenString +{ + OCMStub(ClassMethod([_appEventsUtilityClassMock tokenStringToUseFor:OCMArg.any])).andReturn(tokenString); +} + +- (void)stubServerConfigurationFetchingWithConfiguration:(nullable FBSDKServerConfiguration *)configuration error:(nullable NSError *)error +{ + OCMStub(ClassMethod([_serverConfigurationManagerClassMock loadServerConfigurationWithCompletionBlock:OCMArg.isNotNil])).andDo(^(NSInvocation *invocation) { + void (^completion)(FBSDKServerConfiguration *serverConfiguration, NSError *error); + [invocation getArgument:&completion atIndex:2]; + completion(configuration, error); + }); + OCMStub(ClassMethod([_serverConfigurationManagerClassMock loadServerConfigurationWithCompletionBlock:OCMArg.isNil])); +} + +- (void)stubGraphRequestWithResult:(id)result error:(nullable NSError *)error connection:(nullable FBSDKGraphRequestConnection *)connection +{ + OCMStub([_graphRequestMock startWithCompletionHandler:([OCMArg invokeBlockWithArgs:[self nsNullIfNil:connection], [self nsNullIfNil:result], [self nsNullIfNil:error], nil])]); +} + +- (void)stubAppLinkResolverRequestBuilderWithIdiomSpecificField:(nullable NSString *)field +{ + OCMStub([_appLinkResolverRequestBuilderMock getIdiomSpecificField]).andReturn(field); +} + +- (void)stubGraphRequestPiggybackManagerLastRefreshTryWith:(NSDate *)date +{ + OCMStub(ClassMethod([_graphRequestPiggybackManagerMock _lastRefreshTry])).andReturn(date); +} + +- (void)stubAllocatingGraphRequestConnection +{ + OCMStub(ClassMethod([_graphRequestConnectionClassMock alloc])); +} + +- (void)stubDisableFeature:(NSString *)feature +{ + OCMStub(ClassMethod([_featureManagerClassMock disableFeature:feature])); +} + +- (void)stubIsDataProcessingRestricted:(BOOL)isRestricted +{ + OCMStub(ClassMethod([_settingsClassMock isDataProcessingRestricted])).andReturn(isRestricted); +} + +- (void)stubFacebookDomainPartWith:(NSString *)domainPart +{ + OCMStub(ClassMethod([_settingsClassMock facebookDomainPart])).andReturn(domainPart); +} + +- (void)stubCanOpenURLWith:(BOOL)canOpenURL +{ + OCMStub([_sharedApplicationMock canOpenURL:OCMArg.any]).andReturn(canOpenURL); +} + +- (void)stubOpenURLWith:(BOOL)openURL +{ + OCMStub([_sharedApplicationMock openURL:OCMArg.any]).andReturn(openURL); +} + +- (void)stubOpenUrlOptionsCompletionHandlerWithPerformCompletion:(BOOL)performCompletion + completionSuccess:(BOOL)completionSuccess +{ + if (performCompletion) { + OCMStub([_sharedApplicationMock openURL:OCMArg.any options:OCMArg.any completionHandler:([OCMArg invokeBlockWithArgs:@(completionSuccess), nil])]); + } else { + OCMStub([_sharedApplicationMock openURL:OCMArg.any options:OCMArg.any completionHandler:OCMArg.any]); + } +} + +- (void)stubAppUrlSchemeSuffixWith:(NSString *)suffix +{ + OCMStub(ClassMethod([_settingsClassMock appURLSchemeSuffix])).andReturn(suffix); +} + +- (void)stubUserAgentSuffixWith:(nullable NSString *)suffix +{ + OCMStub(ClassMethod([self.settingsClassMock userAgentSuffix])).andReturn(suffix); +} + +- (void)stubIsOperatingSystemVersionAtLeast:(NSOperatingSystemVersion)version with:(BOOL)returnValue +{ + OCMStub([self.processInfoMock isOperatingSystemAtLeastVersion:version]).andReturn(returnValue); +} + +- (void)stubAppUrlSchemeWith:(nullable NSString *)scheme +{ + OCMStub([self.internalUtilityClassMock appURLScheme]).andReturn(scheme); +} + +// MARK: - Helpers + +- (void)resetCachedSettings +{ + [FBSDKSettings resetLoggingBehaviorsCache]; + [FBSDKSettings resetFacebookAppIDCache]; + [FBSDKSettings resetFacebookUrlSchemeSuffixCache]; + [FBSDKSettings resetFacebookClientTokenCache]; + [FBSDKSettings resetFacebookDisplayNameCache]; + [FBSDKSettings resetFacebookDomainPartCache]; + [FBSDKSettings resetFacebookJpegCompressionQualityCache]; + [FBSDKSettings resetFacebookInstrumentEnabledCache]; + [FBSDKSettings resetFacebookAutoLogAppEventsEnabledCache]; + [FBSDKSettings resetFacebookAdvertiserIDCollectionEnabledCache]; + [FBSDKSettings resetAdvertiserTrackingStatusCache]; + [FBSDKSettings resetUserAgentSuffixCache]; + [FBSDKSettings resetFacebookCodelessDebugLogEnabledCache]; + [FBSDKSettings resetDataProcessingOptionsCache]; +} + +- (id)nsNullIfNil:(id)nilValue +{ + id converted = nilValue; + if (!nilValue) { + converted = [NSNull null]; + } + return converted; +} + +@end diff --git a/FBSDKCoreKit/FBSDKCoreKitTests/Internal/Helpers/FakeBridgeApiRequest.swift b/FBSDKCoreKit/FBSDKCoreKitTests/Internal/Helpers/FakeBridgeApiRequest.swift new file mode 100644 index 0000000000..61fb7242f3 --- /dev/null +++ b/FBSDKCoreKit/FBSDKCoreKitTests/Internal/Helpers/FakeBridgeApiRequest.swift @@ -0,0 +1,56 @@ +// 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. + +@objcMembers +public class FakeBridgeApiRequest: NSObject, FBSDKBridgeAPIRequestProtocol { + public var actionID: String? + public var methodName: String? + public var protocolType: FBSDKBridgeAPIProtocolType + public var `protocol`: BridgeAPIProtocol? + public var scheme: String? + + let url: URL? + + init(url: URL?, protocolType: FBSDKBridgeAPIProtocolType = .native, scheme: String? = nil) { + self.url = url + self.protocolType = protocolType + self.scheme = scheme + } + + public func copy(with zone: NSZone? = nil) -> Any { + return self + } + + public func requestURL() throws -> URL { + guard let url = url else { + throw FakeBridgeApiRequestError(domain: "tests", code: 0, userInfo: [:]) + } + return url + } + + public static func request(withURL url: URL?) -> FakeBridgeApiRequest { + return FakeBridgeApiRequest(url: url) + } + + public static func request(withURL url: URL, scheme: String) -> FakeBridgeApiRequest { + return FakeBridgeApiRequest(url: url, scheme: scheme) + } +} + +@objc +public class FakeBridgeApiRequestError: NSError {} diff --git a/FBSDKCoreKit/FBSDKCoreKitTests/Internal/Helpers/FBSDKMonitoringConfigurationTestHelper.h b/FBSDKCoreKit/FBSDKCoreKitTests/Internal/Helpers/FakeBundle.h similarity index 77% rename from FBSDKCoreKit/FBSDKCoreKitTests/Internal/Helpers/FBSDKMonitoringConfigurationTestHelper.h rename to FBSDKCoreKit/FBSDKCoreKitTests/Internal/Helpers/FakeBundle.h index 4b19623112..9cf0d08ca6 100644 --- a/FBSDKCoreKit/FBSDKCoreKitTests/Internal/Helpers/FBSDKMonitoringConfigurationTestHelper.h +++ b/FBSDKCoreKit/FBSDKCoreKitTests/Internal/Helpers/FakeBundle.h @@ -20,19 +20,14 @@ NS_ASSUME_NONNULL_BEGIN -@interface FBSDKMonitoringConfigurationTestHelper : NSObject - -/// Bundles key value pairs in the same format they're returned from the graph: -/// ex: -/// { -/// "sample_rates": [ -/// { -/// "key": "foo", -/// "value": 1 -/// } -/// ] -/// } -+ (NSDictionary *)sampleRatesWithEntryPairs:(NSDictionary *)pairs; +@interface FakeBundle : NSBundle + +@property (nonatomic, copy) NSArray *capturedKeys; + ++ (instancetype)bundleWithDictionary:(NSDictionary *)dictionary; + +/// Overrides the current value set for the `infoDictionary` property with the new value +- (void)setInfoDictionary:(NSDictionary *)newValue; @end diff --git a/FBSDKCoreKit/FBSDKCoreKitTests/Internal/Helpers/FakeBundle.m b/FBSDKCoreKit/FBSDKCoreKitTests/Internal/Helpers/FakeBundle.m new file mode 100644 index 0000000000..0bc93096c6 --- /dev/null +++ b/FBSDKCoreKit/FBSDKCoreKitTests/Internal/Helpers/FakeBundle.m @@ -0,0 +1,62 @@ +// 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 "FakeBundle.h" + +#import "FBSDKCoreKit+Internal.h" + +@implementation FakeBundle +{ + NSDictionary *_dictionary; +} + +- (instancetype)initWithDictionary:(NSDictionary *)dictionary +{ + if (self = [super init]) { + _dictionary = dictionary; + } + + return self; +} + ++ (instancetype)bundleWithDictionary:(NSDictionary *)dictionary +{ + return [[FakeBundle alloc] initWithDictionary:dictionary]; +} + +- (NSDictionary *)infoDictionary +{ + return [_dictionary copy]; +} + +- (void)setInfoDictionary:(NSDictionary *)newValue +{ + _dictionary = newValue; +} + +- (id)objectForInfoDictionaryKey:(NSString *)key +{ + if (!_capturedKeys) { + _capturedKeys = [NSMutableArray array]; + } + _capturedKeys = [_capturedKeys arrayByAddingObject:key]; + + return [_dictionary objectForKey:key]; +} + +@end diff --git a/FBSDKCoreKit/FBSDKCoreKitTests/ExampleSwiftTests.swift b/FBSDKCoreKit/FBSDKCoreKitTests/Internal/Helpers/FakeDylibResolver.swift similarity index 82% rename from FBSDKCoreKit/FBSDKCoreKitTests/ExampleSwiftTests.swift rename to FBSDKCoreKit/FBSDKCoreKitTests/Internal/Helpers/FakeDylibResolver.swift index 788b765f3d..02b5c863f5 100644 --- a/FBSDKCoreKit/FBSDKCoreKitTests/ExampleSwiftTests.swift +++ b/FBSDKCoreKit/FBSDKCoreKitTests/Internal/Helpers/FakeDylibResolver.swift @@ -16,14 +16,11 @@ // 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 FBSDKCoreKit -import XCTest +@objcMembers +public class FakeDylibResolver: NSObject, FBSDKDynamicFrameworkResolving { + var stubSafariViewControllerClass: AnyClass? -class ExampleSwiftTests: XCTestCase { - func testCanAccessCoreKit() { - XCTAssertNil( - Settings.appID, - "Should not have default app ID" - ) + public func safariViewControllerClass() -> AnyClass? { + return stubSafariViewControllerClass } } diff --git a/FBSDKCoreKit/FBSDKCoreKitTests/Internal/Helpers/FakeLoginManager.h b/FBSDKCoreKit/FBSDKCoreKitTests/Internal/Helpers/FakeLoginManager.h new file mode 100644 index 0000000000..8c303d8223 --- /dev/null +++ b/FBSDKCoreKit/FBSDKCoreKitTests/Internal/Helpers/FakeLoginManager.h @@ -0,0 +1,53 @@ +// 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 "FBSDKURLOpening.h" + +NS_ASSUME_NONNULL_BEGIN + +/// Duplicate minimal interface for `FBSDKLoginManager` that can fulfill the `FBSDKBridgeAPI`'s runtime requirement. +/// Used in the `FBSDKBridgeAPITests` as a spy since it will be discovered by the real class and used instead of the actual +/// `FBSDKLoginManager`. +@interface FBSDKLoginManager : NSObject + +// The reason some of these properties are static is that there are cases where we dynamically +// check for the existence of the login manager at runtime. Since we don't have a handle to the +// instance we can make sure the properties are set on the type so that we can examine them there +// ultimately this is a pattern we need to get away from but this allows it to be tested while +// we refactor. +@property (class, nullable, copy) NSURL *capturedOpenUrl; +@property (class, nullable, copy) NSString *capturedSourceApplication; +@property (class, nullable, copy) NSString *capturedAnnotation; +@property (class) BOOL stubbedOpenUrlSuccess; +@property BOOL openUrlWasCalled; +@property (nullable, copy) NSURL *capturedCanOpenUrl; +@property (nullable, copy) NSString *capturedCanOpenSourceApplication; +@property (nullable, copy) NSString *capturedCanOpenAnnotation; +@property BOOL stubbedCanOpenUrl; +@property BOOL stubbedIsAuthenticationUrl; + ++ (void)resetTestEvidence; + +- (void)stubShouldStopPropagationOfURL:(NSURL *)url withValue:(BOOL)shouldStop; +- (BOOL)shouldStopPropagationOfURL:(NSURL *)url; + +@end + +NS_ASSUME_NONNULL_END diff --git a/FBSDKCoreKit/FBSDKCoreKitTests/Internal/Helpers/FakeLoginManager.m b/FBSDKCoreKit/FBSDKCoreKitTests/Internal/Helpers/FakeLoginManager.m new file mode 100644 index 0000000000..f08130a984 --- /dev/null +++ b/FBSDKCoreKit/FBSDKCoreKitTests/Internal/Helpers/FakeLoginManager.m @@ -0,0 +1,100 @@ +// 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 "FakeLoginManager.h" + +@implementation FBSDKLoginManager +{ + NSMutableDictionary *_urlPropagationMap; +} + +static NSURL *_capturedOpenUrl; +static NSString *_capturedSourceApplication; +static NSString *_capturedAnnotation; +static BOOL _stubbedOpenUrlSuccess; + ++ (NSURL *)capturedOpenUrl { return _capturedOpenUrl; } + ++ (NSString *)capturedSourceApplication { return _capturedSourceApplication; } + ++ (NSString *)capturedAnnotation { return _capturedAnnotation; } + ++ (void)setCapturedOpenUrl:(NSURL *)url { _capturedOpenUrl = url; } + ++ (void)setCapturedSourceApplication:(NSString *)source { _capturedSourceApplication = source; } + ++ (void)setCapturedAnnotation:(NSString *)annotation { _capturedAnnotation = annotation; } + ++ (BOOL)stubbedOpenUrlSuccess { return _stubbedOpenUrlSuccess; } + ++ (void)setStubbedOpenUrlSuccess:(BOOL)success { _stubbedOpenUrlSuccess = success; } + ++ (void)resetTestEvidence +{ + FBSDKLoginManager.capturedOpenUrl = nil; + FBSDKLoginManager.capturedSourceApplication = nil; + FBSDKLoginManager.capturedAnnotation = nil; + FBSDKLoginManager.stubbedOpenUrlSuccess = NO; +} + +- (void)stubShouldStopPropagationOfURL:(NSURL *)url withValue:(BOOL)shouldStop +{ + if (!_urlPropagationMap) { + _urlPropagationMap = [NSMutableDictionary dictionary]; + } + [_urlPropagationMap setObject:@(shouldStop) forKey:url.absoluteString]; +} + +- (BOOL)shouldStopPropagationOfURL:(NSURL *)url +{ + if (!_urlPropagationMap) { + return NO; + } + + return [[_urlPropagationMap objectForKey:url.absoluteString] boolValue]; +} + +// MARK: - FBSDKURLOpening + +- (BOOL)application:(UIApplication *)application openURL:(NSURL *)url sourceApplication:(NSString *)sourceApplication annotation:(id)annotation +{ + _openUrlWasCalled = YES; + FBSDKLoginManager.capturedOpenUrl = url; + FBSDKLoginManager.capturedSourceApplication = sourceApplication; + FBSDKLoginManager.capturedAnnotation = annotation; + + return FBSDKLoginManager.stubbedOpenUrlSuccess; +} + +- (void)applicationDidBecomeActive:(UIApplication *)application {} + +- (BOOL)canOpenURL:(NSURL *)url forApplication:(UIApplication *)application sourceApplication:(NSString *)sourceApplication annotation:(id)annotation +{ + _capturedCanOpenUrl = url; + _capturedCanOpenSourceApplication = sourceApplication; + _capturedCanOpenAnnotation = annotation; + + return _stubbedCanOpenUrl; +} + +- (BOOL)isAuthenticationURL:(NSURL *)url +{ + return _stubbedIsAuthenticationUrl; +} + +@end diff --git a/FBSDKCoreKit/FBSDKCoreKitTests/FBSDKAppLinkUtilityTests.m b/FBSDKCoreKit/FBSDKCoreKitTests/Internal/Helpers/FakeSessionProvider.swift similarity index 54% rename from FBSDKCoreKit/FBSDKCoreKitTests/FBSDKAppLinkUtilityTests.m rename to FBSDKCoreKit/FBSDKCoreKitTests/Internal/Helpers/FakeSessionProvider.swift index c6920b2ff3..c59bd3c46d 100644 --- a/FBSDKCoreKit/FBSDKCoreKitTests/FBSDKAppLinkUtilityTests.m +++ b/FBSDKCoreKit/FBSDKCoreKitTests/Internal/Helpers/FakeSessionProvider.swift @@ -16,29 +16,36 @@ // 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 +@objcMembers +public class FakeSessionDataTask: NSObject, SessionDataTask { + var resumeCallCount = 0 + var cancelCallCount = 0 -#import + public func resume() { + resumeCallCount += 1 + } -#import "FBSDKAppLinkUtility.h" - -@interface FBSDKURLAppInvitesTests : XCTestCase -@end - -@implementation FBSDKURLAppInvitesTests - -- (void)testWithNoPromoCode -{ - NSURL *url = [NSURL URLWithString:@"myapp://somelink/?someparam=somevalue"]; - id promoCode = [FBSDKAppLinkUtility appInvitePromotionCodeFromURL:url]; - XCTAssertNil(promoCode); + public func cancel() { + cancelCallCount += 1 + } } -- (void)testWithPromoCode -{ - NSURL *url = [NSURL URLWithString:@"myapp://somelink/?al_applink_data=%7B%22target_url%22%3Anull%2C%22extras%22%3A%7B%22deeplink_context%22%3A%22%7B%5C%22promo_code%5C%22%3A%5C%22PROMOWORKS%5C%22%7D%22%7D%7D"]; - NSString *promoCode = [FBSDKAppLinkUtility appInvitePromotionCodeFromURL:url]; - XCTAssertNotNil(promoCode); - XCTAssertEqualObjects(promoCode, @"PROMOWORKS"); +@objcMembers +public class FakeSessionProvider: NSObject, SessionProviding { + /// Data to return in a data task completion handler + var data: Data? + /// A url response to return in a data task completion handler + var urlResponse: URLResponse? + /// An error to return in a data task completion handler + var error: Error? + /// A data task to return from `dataTask(with:completion:)` + var stubbedDataTask: SessionDataTask? + + public func dataTask( + with request: URLRequest, + completionHandler: @escaping (Data?, URLResponse?, Error?) -> Void + ) -> SessionDataTask { + completionHandler(data, urlResponse, error) + return stubbedDataTask ?? FakeSessionDataTask() + } } -@end diff --git a/FBSDKCoreKit/FBSDKCoreKitTests/Internal/Helpers/FakeTokenCache.swift b/FBSDKCoreKit/FBSDKCoreKitTests/Internal/Helpers/FakeTokenCache.swift new file mode 100644 index 0000000000..9171ffcdd4 --- /dev/null +++ b/FBSDKCoreKit/FBSDKCoreKitTests/Internal/Helpers/FakeTokenCache.swift @@ -0,0 +1,33 @@ +// 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 FBSDKCoreKit + +@objcMembers +public class FakeTokenCache: NSObject, TokenCaching { + public var accessToken: AccessToken? + public var authenticationToken: AuthenticationToken? + + public init( + accessToken: AccessToken?, + authenticationToken: AuthenticationToken? + ) { + self.accessToken = accessToken + self.authenticationToken = authenticationToken + } +} diff --git a/FBSDKCoreKit/FBSDKCoreKitTests/Internal/Helpers/Fuzzer.swift b/FBSDKCoreKit/FBSDKCoreKitTests/Internal/Helpers/Fuzzer.swift index 5a70b06c5d..68ab0b6c3a 100644 --- a/FBSDKCoreKit/FBSDKCoreKitTests/Internal/Helpers/Fuzzer.swift +++ b/FBSDKCoreKit/FBSDKCoreKitTests/Internal/Helpers/Fuzzer.swift @@ -120,6 +120,10 @@ public class Fuzzer: NSObject { if Bool.random() { json[key] = random } + + else if Bool.random() { + json.removeValue(forKey: key) + } } } diff --git a/FBSDKCoreKit/FBSDKCoreKitTests/Internal/Helpers/KeychainStoreSpy.swift b/FBSDKCoreKit/FBSDKCoreKitTests/Internal/Helpers/KeychainStoreSpy.swift new file mode 100644 index 0000000000..3638212c7e --- /dev/null +++ b/FBSDKCoreKit/FBSDKCoreKitTests/Internal/Helpers/KeychainStoreSpy.swift @@ -0,0 +1,36 @@ +// 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. + +@objcMembers +class KeychainStoreSpy: KeychainStore { + + override func setData(_ value: Data, forKey key: String, accessibility: CFTypeRef) -> Bool { + // Right now just return true. Later will add actual spying + return true + } + + override func setDictionary(_ value: [AnyHashable: Any], forKey key: String, accessibility: CFTypeRef) -> Bool { + // Right now just return true. Later will add actual spying + return true + } + + override func setString(_ value: String, forKey key: String, accessibility: CFTypeRef) -> Bool { + // Right now just return true. Later will add actual spying + return true + } +} diff --git a/FBSDKCoreKit/FBSDKCoreKitTests/Internal/Helpers/NotificationCenterSpy.swift b/FBSDKCoreKit/FBSDKCoreKitTests/Internal/Helpers/NotificationCenterSpy.swift new file mode 100644 index 0000000000..3b346fb6fe --- /dev/null +++ b/FBSDKCoreKit/FBSDKCoreKitTests/Internal/Helpers/NotificationCenterSpy.swift @@ -0,0 +1,60 @@ +// 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 Foundation + +@objcMembers +class NotificationCenterSpy: NotificationCenter { + var capturedRemovedObservers = [Any]() + var capturedPostNames = [NSNotification.Name]() + var capturedPostObjects = [Any]() + var capturedPostUserInfos = [[AnyHashable : Any]]() + + // MARK: Posting + + override func post( + name: NSNotification.Name, + object: Any?, + userInfo: [AnyHashable : Any]? = nil + ) { + self.capturedPostNames.append(name) + self.capturedPostObjects.append(object as Any) + self.capturedPostUserInfos.append(userInfo ?? [:]) + } + + // MARK: Observing + override func removeObserver(_ observer: Any) { + capturedRemovedObservers.append(observer) + } + + override func addObserver( + _ observer: Any, + selector: Selector, + name: NSNotification.Name?, + object: Any? + ) { + // TODO: capture values + } + + func clearTestEvidence() { + capturedRemovedObservers = [] + capturedPostNames = [] + capturedPostObjects = [] + capturedPostUserInfos = [] + } +} diff --git a/FBSDKCoreKit/FBSDKCoreKitTests/Internal/Helpers/SampleAccessToken.swift b/FBSDKCoreKit/FBSDKCoreKitTests/Internal/Helpers/SampleAccessToken.swift new file mode 100644 index 0000000000..34369f827e --- /dev/null +++ b/FBSDKCoreKit/FBSDKCoreKitTests/Internal/Helpers/SampleAccessToken.swift @@ -0,0 +1,95 @@ +// 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. + +@objcMembers +public class SampleAccessToken: NSObject { + + public static var validToken: AccessToken { + return AccessToken( + tokenString: "123", + permissions: [], + declinedPermissions: [], + expiredPermissions: [], + appID: "123", + userID: "user123", + expirationDate: nil, + refreshDate: nil, + dataAccessExpirationDate: nil + ) + } + + public static var expiredToken: AccessToken { + return AccessToken( + tokenString: "123", + permissions: [], + declinedPermissions: [], + expiredPermissions: [], + appID: "123", + userID: "user123", + expirationDate: .distantPast, + refreshDate: nil, + dataAccessExpirationDate: nil + ) + } + + public static func valid(withRefreshDate date: Date?) -> AccessToken { + return AccessToken( + tokenString: "123", + permissions: [], + declinedPermissions: [], + expiredPermissions: [], + appID: "123", + userID: "user123", + expirationDate: nil, + refreshDate: date, + dataAccessExpirationDate: nil + ) + } + + public static func validToken(withPermissions permissions: [String]) -> AccessToken { + return AccessToken( + tokenString: "123", + permissions: permissions, + declinedPermissions: [], + expiredPermissions: [], + appID: "123", + userID: "user123", + expirationDate: nil, + refreshDate: nil, + dataAccessExpirationDate: nil + ) + } + + public static func validToken( + withPermissions permissions: [String], + declinedPermissions: [String] = [], + expiredPermissions: [String] = [] + ) -> AccessToken { + return AccessToken( + tokenString: "123", + permissions: permissions, + declinedPermissions: declinedPermissions, + expiredPermissions: expiredPermissions, + appID: "123", + userID: "user123", + expirationDate: nil, + refreshDate: nil, + dataAccessExpirationDate: nil + ) + } +} diff --git a/FBSDKCoreKit/FBSDKCoreKitTests/Internal/Helpers/SampleAppEvents.swift b/FBSDKCoreKit/FBSDKCoreKitTests/Internal/Helpers/SampleAppEvents.swift new file mode 100644 index 0000000000..84f9e223ec --- /dev/null +++ b/FBSDKCoreKit/FBSDKCoreKitTests/Internal/Helpers/SampleAppEvents.swift @@ -0,0 +1,31 @@ +// 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 Foundation + +@objcMembers +public class SampleAppEvents: NSObject { + + public static var validEvent: [String: String] { + return ["_eventName": "event1"] + } + + public static func validEvent(withName name: String) -> [String: String] { + return ["_eventName": name] + } +} diff --git a/FBSDKCoreKit/FBSDKCoreKitTests/Internal/Helpers/SampleAuthenticationToken.swift b/FBSDKCoreKit/FBSDKCoreKitTests/Internal/Helpers/SampleAuthenticationToken.swift new file mode 100644 index 0000000000..1fe1a5fa8c --- /dev/null +++ b/FBSDKCoreKit/FBSDKCoreKitTests/Internal/Helpers/SampleAuthenticationToken.swift @@ -0,0 +1,31 @@ +// 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. + +@objcMembers +public class SampleAuthenticationToken: NSObject { + + public static var validToken: AuthenticationToken { + return AuthenticationToken( + tokenString: "fakeTokenString", + nonce: "fakeNonce", + claims: nil, + jti: "fakeJTI" + ) + } + +} diff --git a/FBSDKCoreKit/FBSDKCoreKitTests/Internal/Helpers/SampleGraphRequest.swift b/FBSDKCoreKit/FBSDKCoreKitTests/Internal/Helpers/SampleGraphRequest.swift new file mode 100644 index 0000000000..e5aaf4ee35 --- /dev/null +++ b/FBSDKCoreKit/FBSDKCoreKitTests/Internal/Helpers/SampleGraphRequest.swift @@ -0,0 +1,46 @@ +// 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 Foundation + +@testable import FBSDKCoreKit + +@objc public class SampleGraphRequest: NSObject { + + @objc public static let valid = create() + @objc public static var withOutdatedVersionWithAttachment = create( + parameters: ["attachment": Data(count: 8)], + version: "3.0" + ) + @objc public static var withOutdatedVersion = create(version: "3.0") + @objc public static var withAttachment = create(parameters: ["attachment": Data(count: 8)]) + + @objc public static func create( + path: String = "/me", + parameters: [String: Any] = [:], + version: String = Settings.graphAPIVersion + ) -> GraphRequest { + return GraphRequest( + graphPath: path, + parameters: parameters, + tokenString: nil, + version: version, + httpMethod: HTTPMethod.get + ) + } +} diff --git a/FBSDKCoreKit/FBSDKCoreKitTests/Internal/Monitoring/FakeMonitorStore.m b/FBSDKCoreKit/FBSDKCoreKitTests/Internal/Helpers/SampleGraphRequestConnection.swift similarity index 71% rename from FBSDKCoreKit/FBSDKCoreKitTests/Internal/Monitoring/FakeMonitorStore.m rename to FBSDKCoreKit/FBSDKCoreKitTests/Internal/Helpers/SampleGraphRequestConnection.swift index e0614beebd..947f2bae6d 100644 --- a/FBSDKCoreKit/FBSDKCoreKitTests/Internal/Monitoring/FakeMonitorStore.m +++ b/FBSDKCoreKit/FBSDKCoreKitTests/Internal/Helpers/SampleGraphRequestConnection.swift @@ -16,30 +16,21 @@ // 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 "FakeMonitorStore.h" +import Foundation -@implementation FakeMonitorStore +@testable import FBSDKCoreKit -- (void)persist:(NSArray> *)entries -{ - [super persist:entries]; +@objc public class SampleGraphRequestConnection: NSObject { - self.persistWasCalled = true; - self.capturedPersistedEntries = entries; -} - -- (NSArray> *)retrieveEntries -{ - self.retrieveEntriesWasCalled = true; - - return [super retrieveEntries]; -} - -- (void)clear -{ - [super clear]; + @objc public static var empty: GraphRequestConnection { + GraphRequestConnection() + } - self.clearWasCalled = true; + @objc public static func with(requests: [GraphRequest]) -> GraphRequestConnection { + let connection = GraphRequestConnection() + requests.forEach { + connection.add($0) { _, _, _ in } + } + return connection + } } - -@end diff --git a/FBSDKCoreKit/FBSDKCoreKitTests/Internal/Helpers/SampleRawRemotePermissions.swift b/FBSDKCoreKit/FBSDKCoreKitTests/Internal/Helpers/SampleRawRemotePermissions.swift new file mode 100644 index 0000000000..119611c949 --- /dev/null +++ b/FBSDKCoreKit/FBSDKCoreKitTests/Internal/Helpers/SampleRawRemotePermissions.swift @@ -0,0 +1,124 @@ +// 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 Foundation + +@objc +public class SampleRawRemotePermissionList: NSObject { + + @objc + public static var missingPermissions: [String: Any] { + [ + "data": [ + [ + "permission": nil, + "status": "granted" + ], + [ + "permission": nil, + "status": "declined" + ], + [ + "permission": nil, + "status": "expired" + ] + ] + ] + } + + @objc + public static var missingStatus: [String: Any] { + [ + "data": [ + [ + "permission": "email", + "status": nil + ] + ] + ] + } + + @objc + public static let missingTopLevelKey: [String: Any] = [:] + + @objc + public static var randomValues: Any { + let json: Any = [ + "data": [ + [ + "permission": "foo", + "status": "granted" + ] + ] + ] + return Fuzzer.randomize(json: json) + } + + @objc + public static var validAllStatuses: [String: Any] { + [ + "data": [ + [ + "permission": "email", + "status": "granted" + ], + [ + "permission": "birthday", + "status": "declined" + ], + [ + "permission": "first_name", + "status": "expired" + ] + ] + ] + } + + @objc + public static func with( + granted: [String] = [], + declined: [String] = [], + expired: [String] = [] + ) -> [String: Any] { + let grantedPermissions = granted.map { + return [ + "permission": $0, + "status": "granted" + ] + } + let declinedPermissions = declined.map { + return [ + "permission": $0, + "status": "declined" + ] + } + let expiredPermissions = expired.map { + return [ + "permission": $0, + "status": "expired" + ] + } + return ["data": grantedPermissions + expiredPermissions + declinedPermissions] + } + +} + +@objc public class SampleRawRemotePermission: NSObject { + + @objc public static let missingTopLevelKey: [String: Any] = [:] +} diff --git a/FBSDKCoreKit/FBSDKCoreKitTests/Internal/Helpers/SampleUserProfile.swift b/FBSDKCoreKit/FBSDKCoreKitTests/Internal/Helpers/SampleUserProfile.swift new file mode 100644 index 0000000000..de0faddf14 --- /dev/null +++ b/FBSDKCoreKit/FBSDKCoreKitTests/Internal/Helpers/SampleUserProfile.swift @@ -0,0 +1,35 @@ +// 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 FBSDKCoreKit + +@objc public class SampleUserProfile: NSObject { + @objc public static var valid: Profile { + return Profile( + userID: "123", + firstName: "John", + middleName: "K", + lastName: "Smith", + name: "John Smith", + linkURL: URL(string: "http://www.example.com"), + refreshDate: .distantFuture, + imageURL: URL(string: "http://www.example.com/image.jpg"), + email: "example@example.com" + ) + } +} diff --git a/FBSDKCoreKit/FBSDKCoreKitTests/Internal/Helpers/UserDefaultsSpy.h b/FBSDKCoreKit/FBSDKCoreKitTests/Internal/Helpers/UserDefaultsSpy.h new file mode 100644 index 0000000000..e711ec7405 --- /dev/null +++ b/FBSDKCoreKit/FBSDKCoreKitTests/Internal/Helpers/UserDefaultsSpy.h @@ -0,0 +1,30 @@ +// 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 + +NS_ASSUME_NONNULL_BEGIN + +@interface UserDefaultsSpy : NSUserDefaults + +@property (nonatomic, copy) NSString *capturedObjectRetrievalKey; +@property (nonatomic, copy) NSDictionary *capturedValues; + +@end + +NS_ASSUME_NONNULL_END diff --git a/FBSDKCoreKit/FBSDKCoreKitTests/Internal/Helpers/FBSDKMonitoringConfigurationTestHelper.m b/FBSDKCoreKit/FBSDKCoreKitTests/Internal/Helpers/UserDefaultsSpy.m similarity index 60% rename from FBSDKCoreKit/FBSDKCoreKitTests/Internal/Helpers/FBSDKMonitoringConfigurationTestHelper.m rename to FBSDKCoreKit/FBSDKCoreKitTests/Internal/Helpers/UserDefaultsSpy.m index 4130cb94c1..dafcfd2098 100644 --- a/FBSDKCoreKit/FBSDKCoreKitTests/Internal/Helpers/FBSDKMonitoringConfigurationTestHelper.m +++ b/FBSDKCoreKit/FBSDKCoreKitTests/Internal/Helpers/UserDefaultsSpy.m @@ -16,28 +16,44 @@ // 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 "FBSDKMonitoringConfigurationTestHelper.h" +#import "UserDefaultsSpy.h" -@implementation FBSDKMonitoringConfigurationTestHelper +#import "FBSDKTypeUtility.h" -+ (NSDictionary *)sampleRatesWithEntryPairs:(NSDictionary *)pairs +@implementation UserDefaultsSpy + ++ (instancetype)new +{ + return [[UserDefaultsSpy alloc] init]; +} + +- (instancetype)init { - NSMutableArray *sampleRateDicts = [NSMutableArray array]; + self = [super init]; + if (self) { + _capturedValues = [NSDictionary dictionary]; + } - NSMutableDictionary *tmp = [NSMutableDictionary dictionary]; - [pairs enumerateKeysAndObjectsUsingBlock:^(NSString * _Nonnull key, id _Nonnull obj, BOOL * _Nonnull stop) { - [sampleRateDicts addObject: [self sampleRateWithEntryName:key rate:obj]]; - }]; + return self; +} - [tmp setObject:sampleRateDicts forKey:@"sample_rates"]; - return tmp; +- (id)objectForKey:(NSString *)defaultName +{ + _capturedObjectRetrievalKey = defaultName; + return [_capturedValues objectForKey:defaultName]; } -+ (NSDictionary *)sampleRateWithEntryName:(NSString *)name rate:(NSNumber *)rate +- (void)setObject:(id)value forKey:(NSString *)defaultName { - NSMutableDictionary *tmp = [NSMutableDictionary dictionaryWithObject:name forKey:@"key"]; - [tmp setObject:rate forKey:@"value"]; - return tmp; + NSMutableDictionary *tmp = [NSMutableDictionary dictionaryWithDictionary:_capturedValues]; + + if (value) { + [FBSDKTypeUtility dictionary:tmp setObject:value forKey:defaultName]; + } else { + [tmp removeObjectForKey:defaultName]; + } + + _capturedValues = tmp; } @end diff --git a/FBSDKCoreKit/FBSDKCoreKitTests/Internal/Helpers/ViewControllerSpy.swift b/FBSDKCoreKit/FBSDKCoreKitTests/Internal/Helpers/ViewControllerSpy.swift new file mode 100644 index 0000000000..8054e641f7 --- /dev/null +++ b/FBSDKCoreKit/FBSDKCoreKitTests/Internal/Helpers/ViewControllerSpy.swift @@ -0,0 +1,64 @@ +// 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 UIKit + +@objcMembers +public class ViewControllerSpy: UIViewController { + + public var capturedDismissCompletion: (() -> Void)? + public var dismissWasCalled = false + public var capturedPresentViewController: UIViewController? + public var capturedPresentViewControllerAnimated = false + public var capturedPresentViewControllerCompletion: (() -> Void)? + + /// Used for providing a value to return for the readonly `transitionCoordinator` property + public var stubbedTransitionCoordinator: UIViewControllerTransitionCoordinator? = nil + + // Overriding with no implementation to stub the property + public override var transitionCoordinator: UIViewControllerTransitionCoordinator? { + return stubbedTransitionCoordinator + } + + private lazy var presenting = { + ViewControllerSpy.makeDefaultSpy() + }() + + public override var presentingViewController: UIViewController? { + return presenting + } + + public override func dismiss(animated flag: Bool, completion: (() -> Void)? = nil) { + dismissWasCalled = true + capturedDismissCompletion = completion + } + + public static func makeDefaultSpy() -> ViewControllerSpy { + return ViewControllerSpy() + } + + // Overriding with no implementation to stub the method + public override func present( + _ viewControllerToPresent: UIViewController, + animated: Bool, + completion: (() -> Void)? = nil) { + capturedPresentViewController = viewControllerToPresent + capturedPresentViewControllerAnimated = animated + capturedPresentViewControllerCompletion = completion + } +} diff --git a/FBSDKCoreKit/FBSDKCoreKitTests/Internal/Instrument/FBSDKCrashObserverTests.m b/FBSDKCoreKit/FBSDKCoreKitTests/Internal/Instrument/FBSDKCrashObserverTests.m index 366c383f9e..d5209347c0 100644 --- a/FBSDKCoreKit/FBSDKCoreKitTests/Internal/Instrument/FBSDKCrashObserverTests.m +++ b/FBSDKCoreKit/FBSDKCoreKitTests/Internal/Instrument/FBSDKCrashObserverTests.m @@ -16,13 +16,12 @@ // 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 "FBSDKCrashHandler.h" #import "FBSDKCrashObserver.h" #import "FBSDKFeatureManager.h" +#import "FBSDKTestCase.h" @interface FBSDKCrashObserver () @@ -30,11 +29,19 @@ + (FBSDKCrashObserver *)sharedInstance; @end -@interface FBSDKCrashObserverTests : XCTestCase +@interface FBSDKCrashObserverTests : FBSDKTestCase @end @implementation FBSDKCrashObserverTests +- (void)setUp +{ + [super setUp]; + + // This should be removed when these tests are updated to check the actual requests that are created + [self stubAllocatingGraphRequestConnection]; +} + - (void)testDidReceiveCrashLogs { FBSDKCrashObserver *crashObserver = [FBSDKCrashObserver sharedInstance]; @@ -61,7 +68,7 @@ - (void)testDidReceiveCrashLogs { NSArray *callstack = @[@"(4 DEV METHODS)", @"+[FBSDKCodelessIndexer crash]+84", - @"(22 DEV METHODS)"] ; + @"(22 DEV METHODS)"]; NSArray *> *crashLogs = @[@{ @"callstack" : callstack, @"reason" : @"NSInternalInconsistencyException", @@ -71,9 +78,8 @@ - (void)testDidReceiveCrashLogs @"device_model" : @"iPad5,3", @"device_os" : @"ios", @"device_os_version" : @"13.1.3", - }]; + }]; return crashLogs; } @end - diff --git a/FBSDKCoreKit/FBSDKCoreKitTests/Internal/Instrument/FBSDKCrashShieldTests.m b/FBSDKCoreKit/FBSDKCoreKitTests/Internal/Instrument/FBSDKCrashShieldTests.m index 232894cc9d..63f6799d6c 100644 --- a/FBSDKCoreKit/FBSDKCoreKitTests/Internal/Instrument/FBSDKCrashShieldTests.m +++ b/FBSDKCoreKit/FBSDKCoreKitTests/Internal/Instrument/FBSDKCrashShieldTests.m @@ -16,25 +16,28 @@ // 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 "FBSDKCoreKitTests-Swift.h" #import "FBSDKCrashShield.h" #import "FBSDKFeatureManager.h" +#import "FBSDKTestCase.h" -@interface FBSDKCrashShield () +@interface FBSDKCrashShield (Testing) -+ (nullable NSString *)getFeature:(NSArray *)callstack; -+ (nullable NSString *)getClassName:(NSString *)entry; ++ (nullable NSString *)_getFeature:(NSArray *)callstack; ++ (nullable NSString *)_getClassName:(NSString *)entry; @end -@interface FBSDKCrashShieldTests : XCTestCase +@interface FBSDKCrashShieldTests : FBSDKTestCase @end @implementation FBSDKCrashShieldTests +// MARK: - Get Feature + - (void)testGetFeature { // gated feature in corekit @@ -42,28 +45,28 @@ - (void)testGetFeature @"+[FBSDKMetadataIndexer crash]+84", @"(22 DEV METHODS)"]; - NSString *featureName1 = [FBSDKCrashShield getFeature:callstack1]; + NSString *featureName1 = [FBSDKCrashShield _getFeature:callstack1]; XCTAssertTrue([featureName1 isEqualToString:@"AAM"]); NSArray *callstack2 = @[@"(4 DEV METHODS)", @"+[FBSDKCodelessIndexer crash]+84", @"(22 DEV METHODS)"]; - NSString *featureName2 = [FBSDKCrashShield getFeature:callstack2]; + NSString *featureName2 = [FBSDKCrashShield _getFeature:callstack2]; XCTAssertTrue([featureName2 isEqualToString:@"CodelessEvents"]); NSArray *callstack3 = @[@"(4 DEV METHODS)", @"+[FBSDKRestrictiveDataFilterManager crash]+84", @"(22 DEV METHODS)"]; - NSString *featureName3 = [FBSDKCrashShield getFeature:callstack3]; + NSString *featureName3 = [FBSDKCrashShield _getFeature:callstack3]; XCTAssertTrue([featureName3 isEqualToString:@"RestrictiveDataFiltering"]); NSArray *callstack4 = @[@"(4 DEV METHODS)", @"+[FBSDKErrorReport crash]+84", @"(22 DEV METHODS)"]; - NSString *featureName4 = [FBSDKCrashShield getFeature:callstack4]; + NSString *featureName4 = [FBSDKCrashShield _getFeature:callstack4]; XCTAssertTrue([featureName4 isEqualToString:@"ErrorReport"]); // feature in other kit @@ -71,54 +74,192 @@ - (void)testGetFeature @"+[FBSDKVideoUploader crash]+84", @"(22 DEV METHODS)"]; - NSString *featureName5 = [FBSDKCrashShield getFeature:callstack5]; + NSString *featureName5 = [FBSDKCrashShield _getFeature:callstack5]; XCTAssertNil(featureName5); } +- (void)testParsingFeatureFromValidCallstack +{ + NSArray *callstack = @[@"(4 DEV METHODS)", + @"+[FBSDKVideoUploader crash]+84", + @"(22 DEV METHODS)"]; + for (int i = 0; i < 100; i++) { + [FBSDKCrashShield _getFeature:[Fuzzer randomizeWithJson:callstack]]; + } +} + +- (void)testParsingFeatureFromGarbage +{ + for (int i = 0; i < 100; i++) { + [FBSDKCrashShield _getFeature:Fuzzer.random]; + } +} + +// MARK: - Get Class Name + - (void)testGetClassName { // class method NSString *entry1 = @"+[FBSDKRestrictiveDataFilterManager crash]+84"; - NSString *className1 = [FBSDKCrashShield getClassName:entry1]; + NSString *className1 = [FBSDKCrashShield _getClassName:entry1]; XCTAssertTrue([className1 isEqualToString:@"FBSDKRestrictiveDataFilterManager"]); // instance method NSString *entry2 = @"-[FBSDKRestrictiveDataFilterManager crash]+84"; - NSString *className2 = [FBSDKCrashShield getClassName:entry2]; + NSString *className2 = [FBSDKCrashShield _getClassName:entry2]; XCTAssertTrue([className2 isEqualToString:@"FBSDKRestrictiveDataFilterManager"]); // ineligible format NSString *entry3 = @"(6 DEV METHODS)"; - NSString *className3 = [FBSDKCrashShield getClassName:entry3]; + NSString *className3 = [FBSDKCrashShield _getClassName:entry3]; XCTAssertNil(className3); } -- (void)testAnalyzeCrashCausedByCoreKitFeature +- (void)testParsingClassName +{ + for (int i = 0; i < 100; i++) { + [FBSDKCrashShield _getClassName:Fuzzer.random]; + } +} + +- (void)testAnalyzingEmptyCrashLogs +{ + // Should not create a graph request for posting a non-existent crash + OCMReject(ClassMethod([self.graphRequestMock alloc])); + + [FBSDKCrashShield analyze:@[]]; +} + +- (void)testAnalyzingInvalidCrashLogs +{ + for (int i = 0; i < 100; i++) { + [FBSDKCrashShield _getClassName:[Fuzzer randomizeWithJson:self.coreKitCrashLogs]]; + } +} + +// MARK: - Analyze: Disabling Features + +- (void)testDisablingCoreKitFeatureWithDataProcessingRestricted { - NSArray *> *crashLogs = [FBSDKCrashShieldTests getCrashLogs:YES]; - id mockCrashShield = [OCMockObject niceMockForClass:[FBSDKCrashShield class]]; - id mockFeatureManager = [OCMockObject niceMockForClass:[FBSDKFeatureManager class]]; + [self stubIsDataProcessingRestricted:YES]; + [self preventGraphRequest]; - [[[mockCrashShield expect] andForwardToRealObject] getFeature:crashLogs[0][@"callstack"]]; - [[[mockFeatureManager expect] andForwardToRealObject] disableFeature:@"CodelessEvents"]; + [FBSDKCrashShield analyze:self.coreKitCrashLogs]; + + // Should disable a core feature found in a crashlog regardless of data processing permissions + OCMVerify(ClassMethod([self.featureManagerClassMock disableFeature:@"CodelessEvents"])); +} + +- (void)testDisablingNonCoreKitFeatureWithDataProcessingRestricted +{ + [self stubIsDataProcessingRestricted:YES]; + [self preventGraphRequest]; - [FBSDKCrashShield analyze:crashLogs]; + // Should not disable a non core feature found in a crashlog regardless of data processing permissions + OCMReject(ClassMethod([self.featureManagerClassMock disableFeature:OCMArg.any])); - [mockFeatureManager verify]; - [mockCrashShield verify]; + [FBSDKCrashShield analyze:self.nonCoreKitCrashLogs]; } -- (void)testAnalyzeCrashCausedByNonCoreKitFeature +- (void)testDisablingCoreKitFeatureWithDataProcessingUnrestricted { - NSArray *> *crashLogs = [FBSDKCrashShieldTests getCrashLogs:NO]; - id mockCrashShield = [OCMockObject niceMockForClass:[FBSDKCrashShield class]]; - id mockFeatureManager = [OCMockObject niceMockForClass:[FBSDKFeatureManager class]]; + [self stubIsDataProcessingRestricted:NO]; + [self preventGraphRequest]; - [[[mockCrashShield expect] andForwardToRealObject] getFeature:crashLogs[0][@"callstack"]]; - [[[mockFeatureManager reject] andForwardToRealObject] disableFeature:[OCMArg any]]; + [FBSDKCrashShield analyze:self.coreKitCrashLogs]; - [FBSDKCrashShield analyze:crashLogs]; - [mockCrashShield verify]; + // Should disable a core feature found in a crashlog regardless of data processing permissions + OCMVerify(ClassMethod([self.featureManagerClassMock disableFeature:@"CodelessEvents"])); +} + +- (void)testDisablingNonCoreKitFeatureWithDataProcessingUnrestricted +{ + [self stubIsDataProcessingRestricted:NO]; + [self preventGraphRequest]; + + // Should not disable a non core feature found in a crashlog regardless of data processing permissions + OCMReject(ClassMethod([self.featureManagerClassMock disableFeature:OCMArg.any])); + + [FBSDKCrashShield analyze:self.nonCoreKitCrashLogs]; +} + +// MARK: - Analyze: Posting Crash Logs + +- (void)testPostingCoreKitCrashLogsWithDataProcessingRestricted +{ + [self stubIsDataProcessingRestricted:YES]; + + OCMReject([self.graphRequestMock alloc]); + + [FBSDKCrashShield analyze:self.coreKitCrashLogs]; +} + +- (void)testPostingNonCoreKitCrashLogsWithDataProcessingRestricted +{ + [self stubIsDataProcessingRestricted:YES]; + + OCMReject([self.graphRequestMock alloc]); + + [FBSDKCrashShield analyze:self.nonCoreKitCrashLogs]; +} + +- (void)testPostingCoreKitCrashLogsWithDataProcessingUnrestricted +{ + // Setup + [self stubIsDataProcessingRestricted:NO]; + [self stubAppID:self.appID]; + [self preventGraphRequest]; + + // Act + [FBSDKCrashShield analyze:self.coreKitCrashLogs]; + + // Assert + NSString *expectedPath = [NSString stringWithFormat:@"%@/instruments", self.appID]; + + OCMVerify( + (void)[self.graphRequestMock initWithGraphPath:expectedPath + parameters:OCMArg.any + HTTPMethod:FBSDKHTTPMethodPOST] + ); + OCMVerify([self.graphRequestMock startWithCompletionHandler:nil]); +} + +- (void)testPostingNonCoreKitCrashLogsWithDataProcessingUnrestricted +{ + [self stubIsDataProcessingRestricted:NO]; + [self stubAppID:self.appID]; + + [self preventGraphRequest]; + + OCMReject( + [self.graphRequestMock initWithGraphPath:OCMArg.any + parameters:OCMArg.any + HTTPMethod:FBSDKHTTPMethodPOST] + ); + OCMReject([self.graphRequestMock startWithCompletionHandler:nil]); + + [FBSDKCrashShield analyze:self.nonCoreKitCrashLogs]; +} + +// MARK: - Helpers + +- (NSString *)encodedCoreKitFeatureDataWithTimestamp:(NSString *)timestamp +{ + NSData *featureData = [FBSDKTypeUtility dataWithJSONObject:@{ + @"feature_names" : @[@"CodelessEvents"], + @"timestamp" : timestamp + } options:0 error:nil]; + return [[NSString alloc] initWithData:featureData encoding:NSUTF8StringEncoding]; +} + +- (NSArray *> *)coreKitCrashLogs +{ + return [FBSDKCrashShieldTests getCrashLogs:YES]; +} + +- (NSArray *> *)nonCoreKitCrashLogs +{ + return [FBSDKCrashShieldTests getCrashLogs:NO]; } + (NSArray *> *)getCrashLogs:(BOOL)isCoreKitFeature @@ -137,8 +278,19 @@ - (void)testAnalyzeCrashCausedByNonCoreKitFeature @"device_model" : @"iPad5,3", @"device_os" : @"ios", @"device_os_version" : @"13.1.3", - }]; + }]; return crashLogs; } +- (void)preventGraphRequest +{ + OCMStub(ClassMethod([self.graphRequestMock alloc])).andReturn(self.graphRequestMock); + OCMStub( + [self.graphRequestMock initWithGraphPath:OCMArg.any + parameters:OCMArg.any + HTTPMethod:FBSDKHTTPMethodPOST] + ).andReturn(self.graphRequestMock); + OCMStub([self.graphRequestMock startWithCompletionHandler:nil]); +} + @end diff --git a/FBSDKCoreKit/FBSDKCoreKitTests/Internal/Instrument/FBSDKInstrumentManagerTests.m b/FBSDKCoreKit/FBSDKCoreKitTests/Internal/Instrument/FBSDKInstrumentManagerTests.m new file mode 100644 index 0000000000..8535708ee1 --- /dev/null +++ b/FBSDKCoreKit/FBSDKCoreKitTests/Internal/Instrument/FBSDKInstrumentManagerTests.m @@ -0,0 +1,97 @@ +// 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 "FBSDKCoreKitTestUtility.h" +#import "FBSDKFeatureManager.h" +#import "FBSDKInstrumentManager.h" +#import "FBSDKTestCase.h" + +@interface FBSDKInstrumentManagerTests : FBSDKTestCase +@end + +@implementation FBSDKInstrumentManagerTests + +- (void)testEnablingWithAutoLogEventsEnabledFeaturesEnabled +{ + [self stubIsAutoLogAppEventsEnabled:YES]; + [self stubFeatureChecksCompleteWith:YES]; + + OCMStub(ClassMethod([self.crashObserverClassMock enable])); + OCMStub(ClassMethod([self.errorReportClassMock enable])); + + [FBSDKInstrumentManager enable]; + + OCMVerify([self.crashObserverClassMock enable]); + OCMVerify([self.errorReportClassMock enable]); +} + +- (void)testEnablingWithAutoLogEventsEnabledFeaturesDisabled +{ + [self stubIsAutoLogAppEventsEnabled:YES]; + [self stubFeatureChecksCompleteWith:NO]; + [self rejectFeatureEnabling]; + + [FBSDKInstrumentManager enable]; +} + +- (void)testEnablingWithAutoLogEventsDisabledFeaturesEnabled +{ + [self stubIsAutoLogAppEventsEnabled:NO]; + [self stubFeatureChecksCompleteWith:YES]; + [self rejectFeatureEnabling]; + + [FBSDKInstrumentManager enable]; +} + +- (void)testEnablingWithAutoLogEventsDisabledFeaturesDisabled +{ + [self stubIsAutoLogAppEventsEnabled:YES]; + [self stubFeatureChecksCompleteWith:NO]; + [self rejectFeatureEnabling]; + + [FBSDKInstrumentManager enable]; +} + +// MARK: - Helpers + +- (void)stubFeatureChecksCompleteWith:(BOOL)value +{ + OCMStub( + ClassMethod( + [self.featureManagerClassMock checkFeature:FBSDKFeatureCrashReport + completionBlock:([OCMArg invokeBlockWithArgs:@(value), nil])] + ) + ); + OCMStub( + ClassMethod( + [self.featureManagerClassMock checkFeature:FBSDKFeatureErrorReport + completionBlock:([OCMArg invokeBlockWithArgs:@(value), nil])] + ) + ); +} + +- (void)rejectFeatureEnabling +{ + OCMReject([self.crashObserverClassMock enable]); + OCMReject([self.errorReportClassMock enable]); +} + +@end diff --git a/FBSDKCoreKit/FBSDKCoreKitTests/Internal/Monitoring/FBSDKMethodUsageMonitorEntryTests.m b/FBSDKCoreKit/FBSDKCoreKitTests/Internal/Monitoring/FBSDKMethodUsageMonitorEntryTests.m index 4851a0c9d4..dc35a6a060 100644 --- a/FBSDKCoreKit/FBSDKCoreKitTests/Internal/Monitoring/FBSDKMethodUsageMonitorEntryTests.m +++ b/FBSDKCoreKit/FBSDKCoreKitTests/Internal/Monitoring/FBSDKMethodUsageMonitorEntryTests.m @@ -24,7 +24,8 @@ @interface FBSDKMethodUsageMonitorEntryTests : XCTestCase @end -@implementation FBSDKMethodUsageMonitorEntryTests { +@implementation FBSDKMethodUsageMonitorEntryTests +{ FBSDKMethodUsageMonitorEntry *entry; } @@ -34,8 +35,11 @@ - (void)testCreatingEntryWithMethodName entry = [FBSDKMethodUsageMonitorEntry entryFromClass:self.class withMethod:_cmd]; - XCTAssertEqualObjects(entry.name, expectedName, - @"Should use the name of the class and method as the entry name"); + XCTAssertEqualObjects( + entry.name, + expectedName, + @"Should use the name of the class and method as the entry name" + ); } - (void)testEncodingEntryWithMethodName @@ -46,10 +50,16 @@ - (void)testEncodingEntryWithMethodName [entry encodeWithCoder:coder]; - XCTAssertEqualObjects(coder.encodedObject[@"event_name"], NSStringFromSelector(_cmd), - @"Should use the name of the method as the event name for encoding"); - XCTAssertEqualObjects(coder.encodedObject[@"method_usage_class"], NSStringFromClass(self.class), - @"Should use the name of the class as the class name for encoding"); + XCTAssertEqualObjects( + coder.encodedObject[@"event_name"], + NSStringFromSelector(_cmd), + @"Should use the name of the method as the event name for encoding" + ); + XCTAssertEqualObjects( + coder.encodedObject[@"method_usage_class"], + NSStringFromClass(self.class), + @"Should use the name of the class as the class name for encoding" + ); } - (void)testDecodingEntryWithMethodName @@ -58,10 +68,16 @@ - (void)testDecodingEntryWithMethodName entry = [[FBSDKMethodUsageMonitorEntry alloc] initWithCoder:coder]; - XCTAssertEqualObjects(coder.decodedObject[@"event_name"], [NSString class], - @"Initializing from a decoder should attempt to decode a String for the event name key"); - XCTAssertEqualObjects(coder.decodedObject[@"method_usage_class"], [NSString class], - @"Initializing from a decoder should attempt to decode a String for the defining class key"); + XCTAssertEqualObjects( + coder.decodedObject[@"event_name"], + [NSString class], + @"Initializing from a decoder should attempt to decode a String for the event name key" + ); + XCTAssertEqualObjects( + coder.decodedObject[@"method_usage_class"], + [NSString class], + @"Initializing from a decoder should attempt to decode a String for the defining class key" + ); } @end diff --git a/FBSDKCoreKit/FBSDKCoreKitTests/Internal/Monitoring/FBSDKMethodUsageMonitorTests.m b/FBSDKCoreKit/FBSDKCoreKitTests/Internal/Monitoring/FBSDKMethodUsageMonitorTests.m index dc1d2617e5..1a75249ad1 100644 --- a/FBSDKCoreKit/FBSDKCoreKitTests/Internal/Monitoring/FBSDKMethodUsageMonitorTests.m +++ b/FBSDKCoreKit/FBSDKCoreKitTests/Internal/Monitoring/FBSDKMethodUsageMonitorTests.m @@ -16,11 +16,11 @@ // 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 "FBSDKCoreKit+Internal.h" +#import "FBSDKTestCase.h" @interface FBSDKMonitor (Testing) @@ -31,7 +31,7 @@ + (void)flush; @end -@interface FBSDKMethodUsageMonitorTests : XCTestCase +@interface FBSDKMethodUsageMonitorTests : FBSDKTestCase @end @implementation FBSDKMethodUsageMonitorTests @@ -40,15 +40,18 @@ - (void)setUp { [super setUp]; + // This should be removed when these tests are updated to check the actual requests that are created + [self stubAllocatingGraphRequestConnection]; + [FBSDKMonitor enable]; } - (void)tearDown { - [super tearDown]; - [FBSDKMonitor flush]; [FBSDKMonitor disable]; + + [super tearDown]; } - (void)testRecordingMethodUsage @@ -59,8 +62,11 @@ - (void)testRecordingMethodUsage FBSDKMethodUsageMonitorEntry *entry = (FBSDKMethodUsageMonitorEntry *) FBSDKMonitor.entries.firstObject; - XCTAssertEqualObjects(entry.dictionaryRepresentation[@"event_name"], expectedName, - @"Entry should contain the captured method name"); + XCTAssertEqualObjects( + entry.dictionaryRepresentation[@"event_name"], + expectedName, + @"Entry should contain the captured method name" + ); } @end diff --git a/FBSDKCoreKit/FBSDKCoreKitTests/Internal/Monitoring/FBSDKMonitorConfigurationTests.m b/FBSDKCoreKit/FBSDKCoreKitTests/Internal/Monitoring/FBSDKMonitorConfigurationTests.m index 04bbe30505..f79faee885 100644 --- a/FBSDKCoreKit/FBSDKCoreKitTests/Internal/Monitoring/FBSDKMonitorConfigurationTests.m +++ b/FBSDKCoreKit/FBSDKCoreKitTests/Internal/Monitoring/FBSDKMonitorConfigurationTests.m @@ -18,11 +18,10 @@ #import +#import "FBSDKCoreKitTests-Swift.h" #import "FBSDKMonitoringConfiguration.h" - #import "FBSDKTestCoder.h" #import "TestMonitorEntry.h" -#import "FBSDKMonitoringConfigurationTestHelper.h" @interface FBSDKMonitoringConfiguration (Testing) @@ -44,20 +43,32 @@ - (void)testDefaultMonitoringConfiguration { self.config = FBSDKMonitoringConfiguration.defaultConfiguration; - XCTAssertEqual(self.config.defaultSamplingRate, 0, - @"Default sampling rate should be zero if unspecified"); - XCTAssertEqual(self.config.sampleRates.count, 0, - @"A config should not have any sample rates by default"); + XCTAssertEqual( + self.config.defaultSamplingRate, + 0, + @"Default sampling rate should be zero if unspecified" + ); + XCTAssertEqual( + self.config.sampleRates.count, + 0, + @"A config should not have any sample rates by default" + ); } - (void)testCreatingWithMissingSamplingRates { self.config = [FBSDKMonitoringConfiguration fromDictionary:@{}]; - XCTAssertEqual(self.config.defaultSamplingRate, 0, - @"Default sampling rate should be zero if unspecified"); - XCTAssertEqual(self.config.sampleRates.count, 0, - @"A config should not have any sample rates by default"); + XCTAssertEqual( + self.config.defaultSamplingRate, + 0, + @"Default sampling rate should be zero if unspecified" + ); + XCTAssertEqual( + self.config.sampleRates.count, + 0, + @"A config should not have any sample rates by default" + ); } - (void)testCreatingWithEmptySamplingRates @@ -66,104 +77,133 @@ - (void)testCreatingWithEmptySamplingRates self.config = [FBSDKMonitoringConfiguration fromDictionary:dict]; - XCTAssertEqual(self.config.defaultSamplingRate, 0, - @"Default sampling rate should be zero if missing from the sampling rates"); - XCTAssertEqual(self.config.sampleRates.count, 0, - @"A config should not have any sample rates by default"); + XCTAssertEqual( + self.config.defaultSamplingRate, + 0, + @"Default sampling rate should be zero if missing from the sampling rates" + ); + XCTAssertEqual( + self.config.sampleRates.count, + 0, + @"A config should not have any sample rates by default" + ); } - - (void)testCreatingWithMissingDefaultSamplingRate { NSDictionary *expectedSampleRates = @{ - @"foo": @1 + @"foo" : @1 }; - NSDictionary *dict = [MonitoringConfiguration sampleRatesWithEntryPairs:@{ @"foo": @1 }]; + NSDictionary *dict = [MonitoringConfiguration sampleRatesWithEntryPairs:@{ @"foo" : @1 }]; self.config = [FBSDKMonitoringConfiguration fromDictionary:dict]; - XCTAssertEqual(self.config.defaultSamplingRate, 0, - @"Default sampling rate should be zero if missing from the sampling rates"); - XCTAssertTrue([self.config.sampleRates isEqualToDictionary:expectedSampleRates], - @"A config should set sample rates correctly based on the input rates"); + XCTAssertEqual( + self.config.defaultSamplingRate, + 0, + @"Default sampling rate should be zero if missing from the sampling rates" + ); + XCTAssertTrue( + [self.config.sampleRates isEqualToDictionary:expectedSampleRates], + @"A config should set sample rates correctly based on the input rates" + ); } - (void)testCreatingWithValidDefaultSamplingRate { - NSDictionary *dict = [MonitoringConfiguration sampleRatesWithEntryPairs:@{ @"default": @100 }]; + NSDictionary *dict = [MonitoringConfiguration sampleRatesWithEntryPairs:@{ @"default" : @100 }]; self.config = [FBSDKMonitoringConfiguration fromDictionary:dict]; - XCTAssertEqual(self.config.defaultSamplingRate, 100, - @"Default sampling rate should be gleaned from the initializing dictionary"); + XCTAssertEqual( + self.config.defaultSamplingRate, + 100, + @"Default sampling rate should be gleaned from the initializing dictionary" + ); } - (void)testCreatingWithOnlyInvalidSamplingRates { NSDictionary *dict = [MonitoringConfiguration sampleRatesWithEntryPairs:@{ - @"default": @-1, - @"foo": @"bar" - }]; + @"default" : @-1, + @"foo" : @"bar" + }]; self.config = [FBSDKMonitoringConfiguration fromDictionary:dict]; - XCTAssertEqual(self.config.defaultSamplingRate, 0, - @"Default sampling rate should be zero if no valid default is given"); - XCTAssertTrue([self.config.sampleRates isEqualToDictionary:@{}], - @"Should not store invalid sample rates"); + XCTAssertEqual( + self.config.defaultSamplingRate, + 0, + @"Default sampling rate should be zero if no valid default is given" + ); + XCTAssertTrue( + [self.config.sampleRates isEqualToDictionary:@{}], + @"Should not store invalid sample rates" + ); } - (void)testCreatingWihSomeInvalidSampleRates { NSDictionary *dict = [MonitoringConfiguration sampleRatesWithEntryPairs:@{ - @"default": @-1, - @"foo": @50 - }]; + @"default" : @-1, + @"foo" : @50 + }]; self.config = [FBSDKMonitoringConfiguration fromDictionary:dict]; - XCTAssertEqual(self.config.defaultSamplingRate, 0, - @"Default sampling rate should be gleaned from the initializing dictionary"); - + XCTAssertEqual( + self.config.defaultSamplingRate, + 0, + @"Default sampling rate should be gleaned from the initializing dictionary" + ); } - (void)testGettingSampleRate { - NSDictionary *dict = [MonitoringConfiguration sampleRatesWithEntryPairs:@{ @"foo": @50 }]; + NSDictionary *dict = [MonitoringConfiguration sampleRatesWithEntryPairs:@{ @"foo" : @50 }]; self.config = [FBSDKMonitoringConfiguration fromDictionary:dict]; - XCTAssertEqual([self.config sampleRateForEntry: [TestMonitorEntry testEntryWithName:@"foo"]], 50, - @"Should retrieve the sample rate for the entry with the matching name"); + XCTAssertEqual( + [self.config sampleRateForEntry:[TestMonitorEntry testEntryWithName:@"foo"]], + 50, + @"Should retrieve the sample rate for the entry with the matching name" + ); } - (void)testEncoding { FBSDKTestCoder *coder = [FBSDKTestCoder new]; - NSDictionary *expectedSampleRates = @{@"foo": @50}; - NSDictionary *dict = [MonitoringConfiguration sampleRatesWithEntryPairs:@{ @"foo": @50 }]; + NSDictionary *expectedSampleRates = @{@"foo" : @50}; + NSDictionary *dict = [MonitoringConfiguration sampleRatesWithEntryPairs:@{ @"foo" : @50 }]; self.config = [FBSDKMonitoringConfiguration fromDictionary:dict]; [self.config encodeWithCoder:coder]; - XCTAssertEqualObjects(coder.encodedObject[@"sample_rates"], expectedSampleRates, - @"Should encode the sample rates"); + XCTAssertEqualObjects( + coder.encodedObject[@"sample_rates"], + expectedSampleRates, + @"Should encode the sample rates" + ); } - (void)testDecoding { FBSDKTestCoder *decoder = [FBSDKTestCoder new]; - NSDictionary *dict = [MonitoringConfiguration sampleRatesWithEntryPairs:@{ @"foo": @50 }]; + NSDictionary *dict = [MonitoringConfiguration sampleRatesWithEntryPairs:@{ @"foo" : @50 }]; self.config = [FBSDKMonitoringConfiguration fromDictionary:dict]; self.config = [self.config initWithCoder:decoder]; - XCTAssertEqualObjects(decoder.decodedObject[@"sample_rates"], [NSDictionary class], - @"Should attempt to decode a dictionary of sample rates when initializing from a decoder"); + XCTAssertEqualObjects( + decoder.decodedObject[@"sample_rates"], + [NSDictionary class], + @"Should attempt to decode a dictionary of sample rates when initializing from a decoder" + ); } #pragma mark Helpers diff --git a/FBSDKCoreKit/FBSDKCoreKitTests/Internal/Monitoring/FBSDKMonitorNetworkerTests.m b/FBSDKCoreKit/FBSDKCoreKitTests/Internal/Monitoring/FBSDKMonitorNetworkerTests.m index 8ff1160637..4bb6a5449c 100644 --- a/FBSDKCoreKit/FBSDKCoreKitTests/Internal/Monitoring/FBSDKMonitorNetworkerTests.m +++ b/FBSDKCoreKit/FBSDKCoreKitTests/Internal/Monitoring/FBSDKMonitorNetworkerTests.m @@ -16,11 +16,10 @@ // 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 #import "FBSDKCoreKit+Internal.h" #import "TestMonitorEntry.h" @@ -31,7 +30,8 @@ @interface FBSDKMonitorNetworkerTests : XCTestCase @end -@implementation FBSDKMonitorNetworkerTests { +@implementation FBSDKMonitorNetworkerTests +{ id graphRequestMock; } @@ -59,11 +59,13 @@ - (void)testSendingEntryWithMissingAppID OCMStub([graphRequestMock alloc]).andReturn(graphRequestMock); - OCMReject([graphRequestMock initWithGraphPath:[OCMArg any] - parameters:[OCMArg any] - tokenString:nil - HTTPMethod:[OCMArg any] - flags:FBSDKGraphRequestFlagDoNotInvalidateTokenOnError | FBSDKGraphRequestFlagDisableErrorRecovery]); + OCMReject( + [graphRequestMock initWithGraphPath:[OCMArg any] + parameters:[OCMArg any] + tokenString:nil + HTTPMethod:[OCMArg any] + flags:FBSDKGraphRequestFlagDoNotInvalidateTokenOnError | FBSDKGraphRequestFlagDisableErrorRecovery] + ); [FBSDKMonitorNetworker sendEntries:@[self.entry]]; } @@ -75,13 +77,16 @@ - (void)testSendingEntriesCreatesGraphRequest OCMStub([graphRequestMock alloc]).andReturn(graphRequestMock); - BOOL (^verifyPath)(id) = ^BOOL(id path) { - XCTAssertEqualObjects(path, @"/monitorings", - @"Graph path should be a known string well known"); + BOOL (^verifyPath)(id) = ^BOOL (id path) { + XCTAssertEqualObjects( + path, + @"/monitorings", + @"Graph path should be a known string well known" + ); return YES; }; - BOOL (^verifyParameters)(id) = ^BOOL(id parameters) { + BOOL (^verifyParameters)(id) = ^BOOL (id parameters) { [self assertAppIdInParameters:parameters]; [self assertDeviceModelInParameters:parameters]; [self assertOSVersionInParameters:parameters]; @@ -90,29 +95,36 @@ - (void)testSendingEntriesCreatesGraphRequest return YES; }; - BOOL (^verifyHTTPMethod)(id) = ^BOOL(id method) { - XCTAssertEqualObjects(method, FBSDKHTTPMethodPOST, - @"Networker should use POST when sending entries"); + BOOL (^verifyHTTPMethod)(id) = ^BOOL (id method) { + XCTAssertEqualObjects( + method, + FBSDKHTTPMethodPOST, + @"Networker should use POST when sending entries" + ); return YES; }; [FBSDKMonitorNetworker sendEntries:entries]; - OCMVerify([graphRequestMock initWithGraphPath:[OCMArg checkWithBlock:verifyPath] - parameters:[OCMArg checkWithBlock:verifyParameters] - tokenString:[OCMArg isNil] - HTTPMethod:[OCMArg checkWithBlock:verifyHTTPMethod] - flags:FBSDKGraphRequestFlagDoNotInvalidateTokenOnError | FBSDKGraphRequestFlagDisableErrorRecovery]); + OCMVerifyAll( + [graphRequestMock initWithGraphPath:[OCMArg checkWithBlock:verifyPath] + parameters:[OCMArg checkWithBlock:verifyParameters] + tokenString:[OCMArg isNil] + HTTPMethod:[OCMArg checkWithBlock:verifyHTTPMethod] + flags:FBSDKGraphRequestFlagDoNotInvalidateTokenOnError | FBSDKGraphRequestFlagDisableErrorRecovery] + ); } - (void)testSendingEntriesStartsGraphRequest { OCMStub([graphRequestMock alloc]).andReturn(graphRequestMock); - OCMStub([graphRequestMock initWithGraphPath:[OCMArg any] - parameters:[OCMArg any] - tokenString:nil - HTTPMethod:[OCMArg any] - flags:FBSDKGraphRequestFlagDoNotInvalidateTokenOnError | FBSDKGraphRequestFlagDisableErrorRecovery]).andReturn(graphRequestMock); + OCMStub( + [graphRequestMock initWithGraphPath:[OCMArg any] + parameters:[OCMArg any] + tokenString:nil + HTTPMethod:[OCMArg any] + flags:FBSDKGraphRequestFlagDoNotInvalidateTokenOnError | FBSDKGraphRequestFlagDisableErrorRecovery] + ).andReturn(graphRequestMock); [FBSDKMonitorNetworker sendEntries:@[[TestMonitorEntry testEntry]]]; @@ -123,33 +135,45 @@ - (void)testSendingEntriesStartsGraphRequest - (void)assertBundleIdInParameters:(NSDictionary *)parameters { - XCTAssertEqualObjects(parameters[@"unique_application_identifier"], NSBundle.mainBundle.bundleIdentifier, - @"A monitor POST body should include the main bundle identifier."); + XCTAssertEqualObjects( + parameters[@"unique_application_identifier"], + NSBundle.mainBundle.bundleIdentifier, + @"A monitor POST body should include the main bundle identifier." + ); } - (void)assertOSVersionInParameters:(NSDictionary *)parameters { - XCTAssertEqualObjects(parameters[@"device_os_version"], UIDevice.currentDevice.systemVersion, - @"A monitor POST body should include the current device's os version."); + XCTAssertEqualObjects( + parameters[@"device_os_version"], + UIDevice.currentDevice.systemVersion, + @"A monitor POST body should include the current device's os version." + ); } - (void)assertAppIdInParameters:(NSDictionary *)parameters { - XCTAssertEqualObjects(parameters[@"id"], [FBSDKSettings appID], - @"A monitor POST body should include the current facebook app identifier."); + XCTAssertEqualObjects( + parameters[@"id"], + [FBSDKSettings appID], + @"A monitor POST body should include the current facebook app identifier." + ); } - (void)assertDeviceModelInParameters:(NSDictionary *)parameters { - XCTAssertEqualObjects(parameters[@"device_model"], [self.class deviceModel], - @"A monitor POST body should include the current device model."); + XCTAssertEqualObjects( + parameters[@"device_model"], + [self.class deviceModel], + @"A monitor POST body should include the current device model." + ); } - (void)assertParameters:(NSDictionary *)parameters includeEntries:(NSArray> *)entries { // Networker should send the array of provided entries as a UTF8 encoded JSON string keyed under 'monitorings' NSString *capturedEntriesJSON = parameters[@"monitorings"]; - NSArray *capturedEntryDictionaries = [FBSDKBasicUtility objectForJSONString:capturedEntriesJSON error:nil]; + NSArray *capturedEntryDictionaries = [FBSDKBasicUtility objectForJSONString:capturedEntriesJSON error:nil]; for (int i = 0; i < capturedEntryDictionaries.count; i++) { XCTAssertTrue([capturedEntryDictionaries[i] isEqualToDictionary:entries[i].dictionaryRepresentation]); diff --git a/FBSDKCoreKit/FBSDKCoreKitTests/Internal/Monitoring/FBSDKMonitorStoreTests.m b/FBSDKCoreKit/FBSDKCoreKitTests/Internal/Monitoring/FBSDKMonitorStoreTests.m index 067583e901..a392a8d78b 100644 --- a/FBSDKCoreKit/FBSDKCoreKitTests/Internal/Monitoring/FBSDKMonitorStoreTests.m +++ b/FBSDKCoreKit/FBSDKCoreKitTests/Internal/Monitoring/FBSDKMonitorStoreTests.m @@ -16,8 +16,8 @@ // 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 "FBSDKCoreKit+Internal.h" #import "TestMonitorEntry.h" @@ -57,16 +57,22 @@ - (void)testCreatingWithFilePath NSURL *temporaryDirectory = [NSURL fileURLWithPath:NSTemporaryDirectory() isDirectory:YES]; NSURL *file = [temporaryDirectory URLByAppendingPathComponent:self.filename]; - XCTAssertEqualObjects(self.store.filePath, file, - @"Should store entries in the temporary directory"); + XCTAssertEqualObjects( + self.store.filePath, + file, + @"Should store entries in the temporary directory" + ); } - (void)testPersistingEmptyEntries { [self.store persist:@[]]; - XCTAssertEqualObjects([self.store retrieveEntries], @[], - @"Should persist an empty list of entries"); + XCTAssertEqualObjects( + [self.store retrieveEntries], + @[], + @"Should persist an empty list of entries" + ); } - (void)testPersistingEntries @@ -75,18 +81,25 @@ - (void)testPersistingEntries [self.store persist:@[self.entry]]; - XCTAssertEqualObjects([self entriesFromDisk], expectedEntries, - @"Should persist entries correctly"); + XCTAssertEqualObjects( + [self entriesFromDisk], + expectedEntries, + @"Should persist entries correctly" + ); } -- (void)testPersistingDuplicateEntriesWithEmptyStore { +- (void)testPersistingDuplicateEntriesWithEmptyStore +{ TestMonitorEntry *entry2 = [TestMonitorEntry testEntry]; NSArray *entries = @[self.entry, entry2]; [self.store persist:entries]; - XCTAssertEqualObjects([self entriesFromDisk], entries, - @"Should allow persisting duplicate entries"); + XCTAssertEqualObjects( + [self entriesFromDisk], + entries, + @"Should allow persisting duplicate entries" + ); } - (void)testPersistingDuplicateEntriesWithNonEmptyStore @@ -97,8 +110,11 @@ - (void)testPersistingDuplicateEntriesWithNonEmptyStore [self.store persist:@[self.entry]]; [self.store persist:entries]; - XCTAssertEqualObjects([self entriesFromDisk], entries, - @"Should overwrite any existing stored entries when persisting"); + XCTAssertEqualObjects( + [self entriesFromDisk], + entries, + @"Should overwrite any existing stored entries when persisting" + ); } - (void)testPersistingUniqueEntriesWithEmptyStore @@ -108,8 +124,11 @@ - (void)testPersistingUniqueEntriesWithEmptyStore [self.store persist:entries]; - XCTAssertEqualObjects([self entriesFromDisk], entries, - @"Should allow persisting unique entries"); + XCTAssertEqualObjects( + [self entriesFromDisk], + entries, + @"Should allow persisting unique entries" + ); } - (void)testPersistingUniqueEntriesWithNonEmptyStore @@ -120,16 +139,22 @@ - (void)testPersistingUniqueEntriesWithNonEmptyStore [self.store persist:@[self.entry]]; [self.store persist:entries]; - XCTAssertEqualObjects([self entriesFromDisk], entries, - @"Should allow persisting unique entries"); + XCTAssertEqualObjects( + [self entriesFromDisk], + entries, + @"Should allow persisting unique entries" + ); } - (void)testRetrievingWithoutPersistedEntries { NSArray> *retrievedEntries = [self.store retrieveEntries]; - XCTAssertEqualObjects(retrievedEntries, @[], - @"Retrieving entries should return an empty array when no items are persisted"); + XCTAssertEqualObjects( + retrievedEntries, + @[], + @"Retrieving entries should return an empty array when no items are persisted" + ); } - (void)testRetrievingClearsStore @@ -139,19 +164,24 @@ - (void)testRetrievingClearsStore NSArray> *retrieved = [self entriesFromDisk]; - XCTAssertNil(retrieved, - @"Retrieving should clear existing entries"); + XCTAssertNil( + retrieved, + @"Retrieving should clear existing entries" + ); } -- (void)testClearingStore { +- (void)testClearingStore +{ [self.store persist:@[self.entry]]; [self.store clear]; NSArray> *retrieved = [self entriesFromDisk]; - XCTAssertNil(retrieved, - @"A cleared store should be empty"); + XCTAssertNil( + retrieved, + @"A cleared store should be empty" + ); } // MARK: - Helpers diff --git a/FBSDKCoreKit/FBSDKCoreKitTests/Internal/Monitoring/FBSDKMonitorTests.m b/FBSDKCoreKit/FBSDKCoreKitTests/Internal/Monitoring/FBSDKMonitorTests.m index 39ec843d77..8a7540e99b 100644 --- a/FBSDKCoreKit/FBSDKCoreKitTests/Internal/Monitoring/FBSDKMonitorTests.m +++ b/FBSDKCoreKit/FBSDKCoreKitTests/Internal/Monitoring/FBSDKMonitorTests.m @@ -16,12 +16,12 @@ // 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 "FBSDKCoreKit+Internal.h" -#import "FakeMonitorStore.h" +#import "FBSDKCoreKitTests-Swift.h" +#import "FBSDKTestCase.h" #import "TestMonitorEntry.h" @interface FBSDKMonitor (Testing) @@ -34,14 +34,15 @@ + (void)applicationMovingFromActiveStateOrTerminating; @end -@interface FBSDKMonitorTests : XCTestCase +@interface FBSDKMonitorTests : FBSDKTestCase @property (nonatomic) id entry; @property (nonatomic) FakeMonitorStore *store; @end -@implementation FBSDKMonitorTests { +@implementation FBSDKMonitorTests +{ int flushLimit; double flushInterval; id networkerMock; @@ -62,25 +63,31 @@ - (void)setUp networkerMock = OCMClassMock([FBSDKMonitorNetworker class]); notificationCenterMock = OCMClassMock([NSNotificationCenter class]); timerMock = OCMClassMock([NSTimer class]); + + [self stubAllocatingGraphRequestConnection]; } - (void)tearDown { - [super tearDown]; - [networkerMock stopMocking]; [notificationCenterMock stopMocking]; [timerMock stopMocking]; [FBSDKMonitor flush]; [FBSDKMonitor disable]; [FBSDKMonitor setStore:nil]; + + [super tearDown]; } -- (void)testRecordingWhenDisabled { +- (void)testRecordingWhenDisabled +{ [FBSDKMonitor record:self.entry]; - XCTAssertEqual(FBSDKMonitor.entries.count, 0, - @"Should not record entries before monitor is enabled"); + XCTAssertEqual( + FBSDKMonitor.entries.count, + 0, + @"Should not record entries before monitor is enabled" + ); } - (void)testEnabling @@ -89,8 +96,11 @@ - (void)testEnabling [FBSDKMonitor record:self.entry]; - XCTAssertEqualObjects(FBSDKMonitor.entries, @[self.entry], - @"Should record entries when monitor is enabled"); + XCTAssertEqualObjects( + FBSDKMonitor.entries, + @[self.entry], + @"Should record entries when monitor is enabled" + ); } - (void)testEnablingWhenEnabled @@ -100,16 +110,20 @@ - (void)testEnablingWhenEnabled [FBSDKMonitor enable]; // Should register for notifications - OCMVerify([notificationCenterMock addObserver:[FBSDKMonitor class] - selector:@selector(applicationMovingFromActiveStateOrTerminating) - name:[OCMArg any] - object:NULL]); + OCMVerify( + [notificationCenterMock addObserver:[FBSDKMonitor class] + selector:@selector(applicationMovingFromActiveStateOrTerminating) + name:[OCMArg any] + object:NULL] + ); // Should not re-register for notifications if still enabled - OCMReject([notificationCenterMock addObserver:[FBSDKMonitor class] - selector:@selector(applicationMovingFromActiveStateOrTerminating) - name:[OCMArg any] - object:NULL]); + OCMReject( + [notificationCenterMock addObserver:[FBSDKMonitor class] + selector:@selector(applicationMovingFromActiveStateOrTerminating) + name:[OCMArg any] + object:NULL] + ); [FBSDKMonitor enable]; } @@ -129,10 +143,15 @@ - (void)testDisabling [FBSDKMonitor disable]; - XCTAssertTrue(self.store.clearWasCalled, - @"Disabling monitoring should clear the persistent store"); - XCTAssertEqual(FBSDKMonitor.entries.count, 0, - @"Disabling monitoring should clear the locally stored entries"); + XCTAssertTrue( + self.store.clearWasCalled, + @"Disabling monitoring should clear the persistent store" + ); + XCTAssertEqual( + FBSDKMonitor.entries.count, + 0, + @"Disabling monitoring should clear the locally stored entries" + ); OCMVerify([notificationCenterMock removeObserver:[FBSDKMonitor class]]); } @@ -147,7 +166,7 @@ - (void)testDisablingUnregistersNotifications [FBSDKMonitor disable]; // Should unregister for notifications when disabling - OCMVerify([notificationCenterMock removeObserver: [FBSDKMonitor class]]); + OCMVerify([notificationCenterMock removeObserver:[FBSDKMonitor class]]); } - (void)testDisablingWhenDisabled @@ -156,7 +175,7 @@ - (void)testDisablingWhenDisabled OCMStub([notificationCenterMock defaultCenter]).andReturn(notificationCenterMock); // Should not unregister for notifications if already disabled - OCMReject([notificationCenterMock removeObserver: [FBSDKMonitor class]]); + OCMReject([notificationCenterMock removeObserver:[FBSDKMonitor class]]); [FBSDKMonitor disable]; } @@ -171,10 +190,10 @@ - (void)testReDisabling [FBSDKMonitor disable]; // Should unregister for notifications when disabling - OCMVerify([notificationCenterMock removeObserver: [FBSDKMonitor class]]); + OCMVerify([notificationCenterMock removeObserver:[FBSDKMonitor class]]); // Should not unregister for notifications if already disabled - OCMReject([notificationCenterMock removeObserver: [FBSDKMonitor class]]); + OCMReject([notificationCenterMock removeObserver:[FBSDKMonitor class]]); [FBSDKMonitor disable]; } @@ -188,8 +207,11 @@ - (void)testFlushing [FBSDKMonitor flush]; - XCTAssertEqual(FBSDKMonitor.entries.count, 0, - @"Flushing should clear all entries"); + XCTAssertEqual( + FBSDKMonitor.entries.count, + 0, + @"Flushing should clear all entries" + ); } - (void)testFlushingWithoutEntries @@ -209,10 +231,14 @@ - (void)testFlushingWithEntries [FBSDKMonitor record:entry2]; [FBSDKMonitor flush]; - OCMVerify(ClassMethod([networkerMock sendEntries:[OCMArg checkWithBlock:^BOOL(id obj) { - XCTAssertEqualObjects(obj, expectedEntries); - return YES; - }]])); + OCMVerify( + ClassMethod( + [networkerMock sendEntries:[OCMArg checkWithBlock:^BOOL (id obj) { + XCTAssertEqualObjects(obj, expectedEntries); + return YES; + }]] + ) + ); } - (void)testRecordingAtOneBelowFlushLimit @@ -228,8 +254,11 @@ - (void)testRecordingAtOneBelowFlushLimit [FBSDKMonitor record:[TestMonitorEntry testEntry]]; } - XCTAssertEqual(expectedEntries.count, flushLimit - 1, - @"Sanity check failed"); + XCTAssertEqual( + expectedEntries.count, + flushLimit - 1, + @"Sanity check failed" + ); } - (void)testRecordingAtFlushLimit @@ -242,14 +271,24 @@ - (void)testRecordingAtFlushLimit [FBSDKMonitor record:[TestMonitorEntry testEntry]]; } - XCTAssertEqual(expectedEntries.count, flushLimit, - @"Sanity check failed"); - - OCMVerify(ClassMethod([networkerMock sendEntries:[OCMArg checkWithBlock:^BOOL(id obj) { - XCTAssertEqual([obj count], expectedEntries.count, - @"Should send the correct number of entries when the flush limit is reached"); - return YES; - }]])); + XCTAssertEqual( + expectedEntries.count, + flushLimit, + @"Sanity check failed" + ); + + OCMVerify( + ClassMethod( + [networkerMock sendEntries:[OCMArg checkWithBlock:^BOOL (id obj) { + XCTAssertEqual( + [obj count], + expectedEntries.count, + @"Should send the correct number of entries when the flush limit is reached" + ); + return YES; + }]] + ) + ); } - (void)testRecordingPastFlushLimit @@ -260,13 +299,19 @@ - (void)testRecordingPastFlushLimit [FBSDKMonitor record:[TestMonitorEntry testEntry]]; } - XCTAssertEqual(FBSDKMonitor.entries.count, 0, - @"Should flush entries after reaching threshold"); + XCTAssertEqual( + FBSDKMonitor.entries.count, + 0, + @"Should flush entries after reaching threshold" + ); [FBSDKMonitor record:self.entry]; - XCTAssertEqual(FBSDKMonitor.entries.count, 1, - @"Should continue to record entries after surpassing the flush limit"); + XCTAssertEqual( + FBSDKMonitor.entries.count, + 1, + @"Should continue to record entries after surpassing the flush limit" + ); } - (void)testEnablingStartsFlushTimer @@ -274,21 +319,29 @@ - (void)testEnablingStartsFlushTimer [FBSDKMonitor enable]; [FBSDKMonitor record:self.entry]; - OCMVerify(ClassMethod([timerMock scheduledTimerWithTimeInterval:flushInterval - target:[FBSDKMonitor class] - selector:@selector(flush) - userInfo:nil - repeats:YES])); + OCMVerify( + ClassMethod( + [timerMock scheduledTimerWithTimeInterval:flushInterval + target:[FBSDKMonitor class] + selector:@selector(flush) + userInfo:nil + repeats:YES] + ) + ); [timerMock stopMocking]; } - (void)testDisablingInvalidatesFlushTimer { - OCMStub(ClassMethod([timerMock scheduledTimerWithTimeInterval:flushInterval - target:[FBSDKMonitor class] - selector:@selector(flush) - userInfo:nil - repeats:YES])).andReturn(timerMock); + OCMStub( + ClassMethod( + [timerMock scheduledTimerWithTimeInterval:flushInterval + target:[FBSDKMonitor class] + selector:@selector(flush) + userInfo:nil + repeats:YES] + ) + ).andReturn(timerMock); [FBSDKMonitor enable]; [FBSDKMonitor disable]; @@ -303,10 +356,12 @@ - (void)testBackgroundingWhenEnabledWithNoLocalEntries [FBSDKMonitor enable]; [NSNotificationCenter.defaultCenter - postNotificationName:UIApplicationWillResignActiveNotification object: nil]; + postNotificationName:UIApplicationWillResignActiveNotification object:nil]; - XCTAssertFalse(self.store.persistWasCalled, - @"Should not attempt to persist empty list of entries on backgrounding"); + XCTAssertFalse( + self.store.persistWasCalled, + @"Should not attempt to persist empty list of entries on backgrounding" + ); } - (void)testBackgroundingWhenEnabledWithLocalEntries @@ -315,10 +370,12 @@ - (void)testBackgroundingWhenEnabledWithLocalEntries [FBSDKMonitor record:self.entry]; [NSNotificationCenter.defaultCenter - postNotificationName:UIApplicationWillResignActiveNotification object: nil]; + postNotificationName:UIApplicationWillResignActiveNotification object:nil]; - XCTAssertTrue(self.store.persistWasCalled, - @"Should persist entries on backgrounding"); + XCTAssertTrue( + self.store.persistWasCalled, + @"Should persist entries on backgrounding" + ); } - (void)testBackgroundingWhenDisabled @@ -326,10 +383,12 @@ - (void)testBackgroundingWhenDisabled [FBSDKMonitor record:self.entry]; [NSNotificationCenter.defaultCenter - postNotificationName:UIApplicationWillResignActiveNotification object: nil]; + postNotificationName:UIApplicationWillResignActiveNotification object:nil]; - XCTAssertFalse(self.store.persistWasCalled, - @"Should not persist entries on backgrounding if the monitor is disabled"); + XCTAssertFalse( + self.store.persistWasCalled, + @"Should not persist entries on backgrounding if the monitor is disabled" + ); } - (void)testTerminatingWhenEnabledWithNoLocalEntries @@ -337,10 +396,12 @@ - (void)testTerminatingWhenEnabledWithNoLocalEntries [FBSDKMonitor enable]; [NSNotificationCenter.defaultCenter - postNotificationName:UIApplicationWillTerminateNotification object: nil]; + postNotificationName:UIApplicationWillTerminateNotification object:nil]; - XCTAssertFalse(self.store.persistWasCalled, - @"Should not attempt to persist empty list of entries on termination"); + XCTAssertFalse( + self.store.persistWasCalled, + @"Should not attempt to persist empty list of entries on termination" + ); } - (void)testTerminatingWhenEnabledWithLocalEntries @@ -349,10 +410,12 @@ - (void)testTerminatingWhenEnabledWithLocalEntries [FBSDKMonitor record:self.entry]; [NSNotificationCenter.defaultCenter - postNotificationName:UIApplicationWillTerminateNotification object: nil]; + postNotificationName:UIApplicationWillTerminateNotification object:nil]; - XCTAssertTrue(self.store.persistWasCalled, - @"Should persist entries on termination"); + XCTAssertTrue( + self.store.persistWasCalled, + @"Should persist entries on termination" + ); } - (void)testTerminatingWhenDisabled @@ -362,10 +425,12 @@ - (void)testTerminatingWhenDisabled [FBSDKMonitor record:self.entry]; [NSNotificationCenter.defaultCenter - postNotificationName:UIApplicationWillTerminateNotification object: nil]; + postNotificationName:UIApplicationWillTerminateNotification object:nil]; - XCTAssertFalse(self.store.persistWasCalled, - @"Should not persist entries on backgrounding if the monitor is disabled"); + XCTAssertFalse( + self.store.persistWasCalled, + @"Should not persist entries on backgrounding if the monitor is disabled" + ); } - (void)testForegroundingWhenEnabledWithNoLocalEntriesNoPersistedEntries @@ -378,10 +443,15 @@ - (void)testForegroundingWhenEnabledWithNoLocalEntriesNoPersistedEntries [NSNotificationCenter.defaultCenter postNotificationName:UIApplicationDidBecomeActiveNotification object:nil]; - XCTAssertTrue(self.store.retrieveEntriesWasCalled, - @"Should attempt to retrieve entries on foregrounding"); - XCTAssertEqual(FBSDKMonitor.entries.count, 0, - @"Should set entries to the retrieved empty array"); + XCTAssertTrue( + self.store.retrieveEntriesWasCalled, + @"Should attempt to retrieve entries on foregrounding" + ); + XCTAssertEqual( + FBSDKMonitor.entries.count, + 0, + @"Should set entries to the retrieved empty array" + ); } - (void)testForegroundingWhenEnabledWithLocalEntriesNoPersistedEntries @@ -394,13 +464,23 @@ - (void)testForegroundingWhenEnabledWithLocalEntriesNoPersistedEntries postNotificationName:UIApplicationDidBecomeActiveNotification object:nil]; // Should invoke networker for locally persisted entry - OCMVerify(ClassMethod([networkerMock sendEntries:[OCMArg checkWithBlock:^BOOL(id obj) { - XCTAssertEqualObjects(obj, entries, - @"Should send the local entries upon foregrounding"); - XCTAssertEqual(FBSDKMonitor.entries.count, 0, - @"Should clear local entries after sending them"); - return YES; - }]])); + OCMVerify( + ClassMethod( + [networkerMock sendEntries:[OCMArg checkWithBlock:^BOOL (id obj) { + XCTAssertEqualObjects( + obj, + entries, + @"Should send the local entries upon foregrounding" + ); + XCTAssertEqual( + FBSDKMonitor.entries.count, + 0, + @"Should clear local entries after sending them" + ); + return YES; + }]] + ) + ); } - (void)testForegroundingWhenEnabledWithNoLocalEntriesAndPersistedEntries @@ -415,13 +495,23 @@ - (void)testForegroundingWhenEnabledWithNoLocalEntriesAndPersistedEntries postNotificationName:UIApplicationDidBecomeActiveNotification object:nil]; // Should invoke networker if entries are retrieved - OCMVerify(ClassMethod([networkerMock sendEntries:[OCMArg checkWithBlock:^BOOL(id obj) { - XCTAssertEqualObjects(obj, entries, - @"Should send the retrieved entries upon foregrounding"); - XCTAssertEqual(FBSDKMonitor.entries.count, 0, - @"Should not persist retrieved entries on foregrounding"); - return YES; - }]])); + OCMVerify( + ClassMethod( + [networkerMock sendEntries:[OCMArg checkWithBlock:^BOOL (id obj) { + XCTAssertEqualObjects( + obj, + entries, + @"Should send the retrieved entries upon foregrounding" + ); + XCTAssertEqual( + FBSDKMonitor.entries.count, + 0, + @"Should not persist retrieved entries on foregrounding" + ); + return YES; + }]] + ) + ); } - (void)testForegroundingWhenEnabledWithLocalEntriesAndPersistedEntries @@ -437,13 +527,23 @@ - (void)testForegroundingWhenEnabledWithLocalEntriesAndPersistedEntries postNotificationName:UIApplicationDidBecomeActiveNotification object:nil]; // Should invoke networker if entries are retrieved - OCMVerify(ClassMethod([networkerMock sendEntries:[OCMArg checkWithBlock:^BOOL(id obj) { - XCTAssertEqualObjects(obj, expectedEntries, - @"Should send the local and retrieved entries upon foregrounding"); - XCTAssertEqual(FBSDKMonitor.entries.count, 0, - @"Should not persist retrieved entries on foregrounding"); - return YES; - }]])); + OCMVerify( + ClassMethod( + [networkerMock sendEntries:[OCMArg checkWithBlock:^BOOL (id obj) { + XCTAssertEqualObjects( + obj, + expectedEntries, + @"Should send the local and retrieved entries upon foregrounding" + ); + XCTAssertEqual( + FBSDKMonitor.entries.count, + 0, + @"Should not persist retrieved entries on foregrounding" + ); + return YES; + }]] + ) + ); } - (void)testForegroundingWhenDisabledWithNoEntries diff --git a/FBSDKCoreKit/FBSDKCoreKitTests/Internal/Monitoring/FBSDKPerformanceMonitorEntryTests.m b/FBSDKCoreKit/FBSDKCoreKitTests/Internal/Monitoring/FBSDKPerformanceMonitorEntryTests.m index 21b05477a8..cdcd074ad6 100644 --- a/FBSDKCoreKit/FBSDKCoreKitTests/Internal/Monitoring/FBSDKPerformanceMonitorEntryTests.m +++ b/FBSDKCoreKit/FBSDKCoreKitTests/Internal/Monitoring/FBSDKPerformanceMonitorEntryTests.m @@ -25,7 +25,8 @@ @interface FBSDKPerformanceMonitorEntryTests : XCTestCase @end -@implementation FBSDKPerformanceMonitorEntryTests { +@implementation FBSDKPerformanceMonitorEntryTests +{ FBSDKPerformanceMonitorEntry *entry; } @@ -60,12 +61,21 @@ - (void)testCreatingEntryWithValidRange NSDictionary *actual = [entry dictionaryRepresentation]; - XCTAssertEqualObjects(actual[@"event_name"], @"Foo", - @"Should use the entry name as the event name"); - XCTAssertEqualObjects(actual[@"time_start"], expectedStartTime, - @"Should use unix time for the start time of the metric"); - XCTAssertEqualObjects(actual[@"time_spent"], @1, - @"Should capture the difference between the start and end-time of the metric"); + XCTAssertEqualObjects( + actual[@"event_name"], + @"Foo", + @"Should use the entry name as the event name" + ); + XCTAssertEqualObjects( + actual[@"time_start"], + expectedStartTime, + @"Should use unix time for the start time of the metric" + ); + XCTAssertEqualObjects( + actual[@"time_spent"], + @1, + @"Should capture the difference between the start and end-time of the metric" + ); } - (void)testEntryName @@ -74,8 +84,11 @@ - (void)testEntryName startTime:[NSDate date] endTime:[[NSDate date] dateByAddingTimeInterval:1]]; - XCTAssertEqualObjects(entry.name, @"Foo", - @"The entry name should be easily accessible"); + XCTAssertEqualObjects( + entry.name, + @"Foo", + @"The entry name should be easily accessible" + ); } - (void)testEncodingEntry @@ -89,12 +102,21 @@ - (void)testEncodingEntry [entry encodeWithCoder:coder]; - XCTAssertEqualObjects(coder.encodedObject[@"event_name"], @"Foo", - @"Should use the entry name as the event name for encoding"); - XCTAssertEqualObjects(coder.encodedObject[@"time_start"], startTime, - @"Should use unix time for encoding the start time of the metric"); - XCTAssertEqualObjects(coder.encodedObject[@"time_end"], [startTime dateByAddingTimeInterval:1], - @"Should encode the difference between the start and end-time of the metric"); + XCTAssertEqualObjects( + coder.encodedObject[@"event_name"], + @"Foo", + @"Should use the entry name as the event name for encoding" + ); + XCTAssertEqualObjects( + coder.encodedObject[@"time_start"], + startTime, + @"Should use unix time for encoding the start time of the metric" + ); + XCTAssertEqualObjects( + coder.encodedObject[@"time_end"], + [startTime dateByAddingTimeInterval:1], + @"Should encode the difference between the start and end-time of the metric" + ); } - (void)testDecodingEntry @@ -103,12 +125,21 @@ - (void)testDecodingEntry entry = [[FBSDKPerformanceMonitorEntry alloc] initWithCoder:coder]; - XCTAssertEqualObjects(coder.decodedObject[@"event_name"], [NSString class], - @"Initializing from a decoder should attempt to decode a String for the event name key"); - XCTAssertEqualObjects(coder.decodedObject[@"time_start"], [NSDate class], - @"Initializing from a decoder should attempt to decode a number for the time start key"); - XCTAssertEqualObjects(coder.decodedObject[@"time_end"], [NSDate class], - @"Initializing from a decoder should attempt to decode a number for the time spent key"); + XCTAssertEqualObjects( + coder.decodedObject[@"event_name"], + [NSString class], + @"Initializing from a decoder should attempt to decode a String for the event name key" + ); + XCTAssertEqualObjects( + coder.decodedObject[@"time_start"], + [NSDate class], + @"Initializing from a decoder should attempt to decode a number for the time start key" + ); + XCTAssertEqualObjects( + coder.decodedObject[@"time_end"], + [NSDate class], + @"Initializing from a decoder should attempt to decode a number for the time spent key" + ); } @end diff --git a/FBSDKCoreKit/FBSDKCoreKitTests/Internal/Monitoring/FBSDKPerformanceMonitorTests.m b/FBSDKCoreKit/FBSDKCoreKitTests/Internal/Monitoring/FBSDKPerformanceMonitorTests.m index e2af7ec732..b431d7fcec 100644 --- a/FBSDKCoreKit/FBSDKCoreKitTests/Internal/Monitoring/FBSDKPerformanceMonitorTests.m +++ b/FBSDKCoreKit/FBSDKCoreKitTests/Internal/Monitoring/FBSDKPerformanceMonitorTests.m @@ -16,11 +16,11 @@ // 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 "FBSDKCoreKit+Internal.h" +#import "FBSDKTestCase.h" @interface FBSDKMonitor (Testing) @@ -31,7 +31,7 @@ + (void)flush; @end -@interface FBSDKPerformanceMonitorTests : XCTestCase +@interface FBSDKPerformanceMonitorTests : FBSDKTestCase @end @implementation FBSDKPerformanceMonitorTests @@ -41,14 +41,17 @@ - (void)setUp [super setUp]; [FBSDKMonitor enable]; + + // This should be removed when these tests are updated to check the actual requests that are created + [self stubAllocatingGraphRequestConnection]; } - (void)tearDown { - [super tearDown]; - [FBSDKMonitor flush]; [FBSDKMonitor disable]; + + [super tearDown]; } - (void)testRecordingPerformance @@ -64,12 +67,22 @@ - (void)testRecordingPerformance FBSDKPerformanceMonitorEntry *entry = (FBSDKPerformanceMonitorEntry *) FBSDKMonitor.entries.firstObject; - XCTAssertEqualObjects(entry.dictionaryRepresentation[@"event_name"], @"Foo", - @"Entry should contain the event name"); - XCTAssertEqualObjects(entry.dictionaryRepresentation[@"time_start"], expectedStartTime, - @"Entry should contain the start time of the metric"); - XCTAssertEqualWithAccuracy([entry.dictionaryRepresentation[@"time_spent"] doubleValue], [@1 doubleValue], 0.1, - @"Entry should contain the difference between the start and end-time of the metric"); + XCTAssertEqualObjects( + entry.dictionaryRepresentation[@"event_name"], + @"Foo", + @"Entry should contain the event name" + ); + XCTAssertEqualObjects( + entry.dictionaryRepresentation[@"time_start"], + expectedStartTime, + @"Entry should contain the start time of the metric" + ); + XCTAssertEqualWithAccuracy( + [entry.dictionaryRepresentation[@"time_spent"] doubleValue], + [@1 doubleValue], + 0.1, + @"Entry should contain the difference between the start and end-time of the metric" + ); } - (void)testRecordingPerformanceWithInvalidInterval @@ -79,8 +92,11 @@ - (void)testRecordingPerformanceWithInvalidInterval [FBSDKPerformanceMonitor record:@"Foo" startTime:date]; - XCTAssertEqual(FBSDKMonitor.entries.count, 0, - @"Should not add invalid entries to the monitor"); + XCTAssertEqual( + FBSDKMonitor.entries.count, + 0, + @"Should not add invalid entries to the monitor" + ); } @end diff --git a/FBSDKCoreKit/FBSDKCoreKitTests/Internal/ServerConfiguration/FBSDKServerConfigurationManagerTests.m b/FBSDKCoreKit/FBSDKCoreKitTests/Internal/Monitoring/FakeMonitorStore.swift similarity index 63% rename from FBSDKCoreKit/FBSDKCoreKitTests/Internal/ServerConfiguration/FBSDKServerConfigurationManagerTests.m rename to FBSDKCoreKit/FBSDKCoreKitTests/Internal/Monitoring/FakeMonitorStore.swift index 43d770ff59..0f3db980f0 100644 --- a/FBSDKCoreKit/FBSDKCoreKitTests/Internal/ServerConfiguration/FBSDKServerConfigurationManagerTests.m +++ b/FBSDKCoreKit/FBSDKCoreKitTests/Internal/Monitoring/FakeMonitorStore.swift @@ -16,31 +16,30 @@ // 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 +@objcMembers +public class FakeMonitorStore: FBSDKMonitorStore { -#import -#import + public var clearWasCalled = false + public var retrieveEntriesWasCalled = false + public var persistWasCalled = false + public var capturedPersistedEntries: [FBSDKMonitorEntry] = [] -#import "FBSDKCoreKit+Internal.h" -#import "FBSDKCoreKitTests-Swift.h" + public override func persist(_ entries: [FBSDKMonitorEntry]) { + super.persist(entries) -@interface FBSDKServerConfigurationManager (Testing) - -+ (void)processLoadRequestResponse:(id)result error:(NSError *)error appID:(NSString *)appID; - -@end + persistWasCalled = true + capturedPersistedEntries = entries + } -@interface FBSDKServerConfigurationManagerTests : XCTestCase + public override func retrieveEntries() -> [FBSDKMonitorEntry] { + retrieveEntriesWasCalled = true -@end + return super.retrieveEntries() + } -@implementation FBSDKServerConfigurationManagerTests + public override func clear() { + super.clear() -- (void)testParsingResponses -{ - for (int i = 0; i < 1000; i++) { - [FBSDKServerConfigurationManager processLoadRequestResponse:RawServerConfigurationResponseFixtures.random error:nil appID:nil]; + clearWasCalled = true } } - -@end diff --git a/FBSDKCoreKit/FBSDKCoreKitTests/Internal/Monitoring/TestMonitorEntry.h b/FBSDKCoreKit/FBSDKCoreKitTests/Internal/Monitoring/TestMonitorEntry.h index ce8ba77512..8a659ec0b2 100644 --- a/FBSDKCoreKit/FBSDKCoreKitTests/Internal/Monitoring/TestMonitorEntry.h +++ b/FBSDKCoreKit/FBSDKCoreKitTests/Internal/Monitoring/TestMonitorEntry.h @@ -20,7 +20,7 @@ NS_ASSUME_NONNULL_BEGIN -@interface TestMonitorEntry : NSObject +@interface TestMonitorEntry : NSObject @property (nonatomic, copy) NSString *name; diff --git a/FBSDKCoreKit/FBSDKCoreKitTests/Internal/Monitoring/TestMonitorEntry.m b/FBSDKCoreKit/FBSDKCoreKitTests/Internal/Monitoring/TestMonitorEntry.m index e609cca807..3eca7df108 100644 --- a/FBSDKCoreKit/FBSDKCoreKitTests/Internal/Monitoring/TestMonitorEntry.m +++ b/FBSDKCoreKit/FBSDKCoreKitTests/Internal/Monitoring/TestMonitorEntry.m @@ -84,5 +84,4 @@ - (NSUInteger)hash return [self.name hash]; } - @end diff --git a/FBSDKCoreKit/FBSDKCoreKitTests/Internal/Network/FBSDKGraphRequestBodyTests.m b/FBSDKCoreKit/FBSDKCoreKitTests/Internal/Network/FBSDKGraphRequestBodyTests.m new file mode 100644 index 0000000000..ea5443fb1a --- /dev/null +++ b/FBSDKCoreKit/FBSDKCoreKitTests/Internal/Network/FBSDKGraphRequestBodyTests.m @@ -0,0 +1,91 @@ +// 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 "FBSDKGraphRequestBody.h" +#import "FBSDKGraphRequestDataAttachment.h" + +@interface FBSDKGraphRequestBodyTests : XCTestCase +@end + +@implementation FBSDKGraphRequestBodyTests + +- (void)testBodyCreationWithFormValue +{ + FBSDKGraphRequestBody *body = [FBSDKGraphRequestBody new]; + [body appendWithKey:@"first_key" formValue:@"first_value" logger:nil]; + [body appendWithKey:@"second_key" formValue:@"second_value" logger:nil]; + + NSString *decodedData = [[NSString alloc] initWithData:body.data encoding:NSUTF8StringEncoding]; + + XCTAssertEqualObjects(body.mimeContentType, @"application/json"); + XCTAssertTrue([decodedData containsString:@"\"first_key\":\"first_value\""]); + XCTAssertTrue([decodedData containsString:@"\"second_key\":\"second_value\""]); +} + +- (void)testBodyCreationWithFormValueWithMultipartType +{ + FBSDKGraphRequestBody *body = [FBSDKGraphRequestBody new]; + [body appendWithKey:@"first_key" formValue:@"first_value" logger:nil]; + [body appendWithKey:@"second_key" formValue:@"second_value" logger:nil]; + body.requiresMultipartDataFormat = YES; + + NSString *decodedData = [[NSString alloc] initWithData:body.data encoding:NSUTF8StringEncoding]; + + XCTAssertTrue([body.mimeContentType containsString:@"multipart/form-data"]); + XCTAssertTrue([decodedData containsString:@"Content-Disposition: form-data"]); + XCTAssertTrue([decodedData containsString:@"name=\"first_key\"\r\n\r\nfirst_value\r\n"]); + XCTAssertTrue([decodedData containsString:@"name=\"second_key\"\r\n\r\nsecond_value\r\n"]); +} + +- (void)testBodyCreationWithImageValue +{ + FBSDKGraphRequestBody *body = [FBSDKGraphRequestBody new]; + [body appendWithKey:@"image_key" imageValue:[UIImage new] logger:nil]; + + NSString *decodedData = [[NSString alloc] initWithData:body.data encoding:NSUTF8StringEncoding]; + XCTAssertTrue([body.mimeContentType containsString:@"multipart/form-data"]); + XCTAssertTrue([decodedData containsString:@"Content-Type: image/jpeg"]); +} + +- (void)testBodyCreationWithDataValue +{ + FBSDKGraphRequestBody *body = [FBSDKGraphRequestBody new]; + [body appendWithKey:@"data_key" dataValue:[NSData data] logger:nil]; + + NSString *decodedData = [[NSString alloc] initWithData:body.data encoding:NSUTF8StringEncoding]; + XCTAssertTrue([body.mimeContentType containsString:@"multipart/form-data"]); + XCTAssertTrue([decodedData containsString:@"Content-Type: content/unknown"]); +} + +- (void)testBodyCreationWithAttachmentValue +{ + FBSDKGraphRequestBody *body = [FBSDKGraphRequestBody new]; + FBSDKGraphRequestDataAttachment *attachment = [[FBSDKGraphRequestDataAttachment alloc] initWithData:[NSData data] + filename:@"test_filename" + contentType:@"test_content_type"]; + [body appendWithKey:@"attachment_key" dataAttachmentValue:attachment logger:nil]; + + NSString *decodedData = [[NSString alloc] initWithData:body.data encoding:NSUTF8StringEncoding]; + XCTAssertTrue([body.mimeContentType containsString:@"multipart/form-data"]); + XCTAssertTrue([decodedData containsString:@"filename=\"test_filename\""]); + XCTAssertTrue([decodedData containsString:@"Content-Type: test_content_type"]); +} + +@end diff --git a/FBSDKCoreKit/FBSDKCoreKitTests/Internal/Network/FBSDKGraphRequestPiggybackManagerTests.m b/FBSDKCoreKit/FBSDKCoreKitTests/Internal/Network/FBSDKGraphRequestPiggybackManagerTests.m new file mode 100644 index 0000000000..1dac43590e --- /dev/null +++ b/FBSDKCoreKit/FBSDKCoreKitTests/Internal/Network/FBSDKGraphRequestPiggybackManagerTests.m @@ -0,0 +1,1064 @@ +// 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 "FBSDKCoreKitTests-Swift.h" +#import "FBSDKGraphRequestPiggybackManager.h" +#import "FBSDKServerConfigurationFixtures.h" +#import "FBSDKTestCase.h" + +@interface FBSDKAccessToken (Testing) ++ (void)resetCurrentAccessTokenCache; +@end + +@interface FBSDKGraphRequestPiggybackManager (Testing) + ++ (int)_tokenRefreshThresholdInSeconds; ++ (int)_tokenRefreshRetryInSeconds; ++ (BOOL)_safeForPiggyback:(FBSDKGraphRequest *)request; ++ (void)_setLastRefreshTry:(NSDate *)date; + +@end + +@interface FBSDKGraphRequestPiggybackManagerTests : FBSDKTestCase + +@end + +@implementation FBSDKGraphRequestPiggybackManagerTests + +typedef FBSDKGraphRequestPiggybackManager Manager; + +- (void)setUp +{ + [super setUp]; + + [self resetCaches]; +} + +- (void)tearDown +{ + [super tearDown]; + + [self resetCaches]; +} + +- (void)resetCaches +{ + [FBSDKAccessToken resetCurrentAccessTokenCache]; +} + +// MARK: - Defaults + +- (void)testRefreshThresholdInSeconds +{ + int oneDayInSeconds = 24 * 60 * 60; + XCTAssertEqual( + [Manager _tokenRefreshThresholdInSeconds], + oneDayInSeconds, + "There should be a well-known value for the token refresh threshold" + ); +} + +- (void)testRefreshRetryInSeconds +{ + int oneHourInSeconds = 60 * 60; + XCTAssertEqual( + [Manager _tokenRefreshRetryInSeconds], + oneHourInSeconds, + "There should be a well-known value for the token refresh retry threshold" + ); +} + +// MARK: - Request Eligibility + +- (void)testSafeForAddingWithMatchingGraphVersionWithAttachment +{ + XCTAssertFalse( + [Manager _safeForPiggyback:SampleGraphRequest.withAttachment], + "A request with an attachment is not considered safe for piggybacking" + ); +} + +- (void)testSafeForAddingWithMatchingGraphVersionWithoutAttachment +{ + XCTAssertTrue( + [Manager _safeForPiggyback:SampleGraphRequest.valid], + "A request without an attachment is considered safe for piggybacking" + ); +} + +- (void)testSafeForAddingWithoutMatchingGraphVersionWithAttachment +{ + XCTAssertFalse( + [Manager _safeForPiggyback:SampleGraphRequest.withOutdatedVersionWithAttachment], + "A request with an attachment and outdated version is not considered safe for piggybacking" + ); +} + +- (void)testSafeForAddingWithoutMatchingGraphVersionWithoutAttachment +{ + XCTAssertFalse( + [Manager _safeForPiggyback:SampleGraphRequest.withOutdatedVersion], + "A request with an outdated version is not considered safe for piggybacking" + ); +} + +// MARK: - Adding Requests + +- (void)testAddingRequestsWithoutAppID +{ + [self stubAppID:@""]; + + OCMReject(ClassMethod([self.graphRequestPiggybackManagerMock addRefreshPiggybackIfStale:OCMArg.any])); + OCMReject(ClassMethod([self.graphRequestPiggybackManagerMock addServerConfigurationPiggyback:OCMArg.any])); + + [Manager addPiggybackRequests:SampleGraphRequestConnection.empty]; +} + +- (void)testAddingRequestsForConnectionWithSafeRequests +{ + [self stubAppID:@"abc123"]; + [self stubFetchingCachedServerConfiguration]; + + FBSDKGraphRequestConnection *connection = [SampleGraphRequestConnection withRequests:@[SampleGraphRequest.valid]]; + + [Manager addPiggybackRequests:connection]; + + OCMVerify(ClassMethod([self.graphRequestPiggybackManagerMock addRefreshPiggybackIfStale:connection])); + OCMVerify(ClassMethod([self.graphRequestPiggybackManagerMock addServerConfigurationPiggyback:connection])); +} + +- (void)testAddingRequestsForConnectionWithUnsafeRequests +{ + [self stubAppID:@"abc123"]; + [self stubFetchingCachedServerConfiguration]; + FBSDKGraphRequestConnection *connection = [SampleGraphRequestConnection withRequests:@[SampleGraphRequest.withAttachment]]; + + OCMReject(ClassMethod([self.graphRequestPiggybackManagerMock addRefreshPiggybackIfStale:connection])); + OCMReject(ClassMethod([self.graphRequestPiggybackManagerMock addServerConfigurationPiggyback:connection])); + + [Manager addPiggybackRequests:connection]; +} + +- (void)testAddingRequestsForConnectionWithSafeAndUnsafeRequests +{ + [self stubAppID:@"abc123"]; + [self stubFetchingCachedServerConfiguration]; + FBSDKGraphRequestConnection *connection = [SampleGraphRequestConnection withRequests:@[ + SampleGraphRequest.valid, + SampleGraphRequest.withAttachment + ]]; + + // No requests are piggybacked if any are invalid + OCMReject(ClassMethod([self.graphRequestPiggybackManagerMock addRefreshPiggybackIfStale:connection])); + OCMReject(ClassMethod([self.graphRequestPiggybackManagerMock addServerConfigurationPiggyback:connection])); + + [Manager addPiggybackRequests:connection]; +} + +// MARK: - Adding Token Extension Piggyback + +- (void)testAddsTokenExtensionRequest +{ + [self stubAppID:@"abc123"]; + [self stubCurrentAccessTokenWith:SampleAccessToken.validToken]; + FBSDKGraphRequestConnection *connection = [FBSDKGraphRequestConnection new]; + + [Manager addRefreshPiggyback:connection permissionHandler:nil]; + + FBSDKGraphRequestMetadata *metadata = connection.requests.firstObject; + FBSDKGraphRequest *request = metadata.request; + XCTAssertNotNil(request, "Adding a refresh piggyback to a connection should add a request for refreshing the access token"); + + XCTAssertEqualObjects( + request.graphPath, + @"oauth/access_token", + "Should add a request with the correct graph path for refreshing a token" + ); + NSDictionary *expectedParameters = @{ + @"grant_type" : @"fb_extend_sso_token", + @"fields" : @"", + @"client_id" : SampleAccessToken.validToken.appID + }; + XCTAssertTrue( + [request.parameters isEqualToDictionary:expectedParameters], + "Should add a request with the correct parameters for refreshing a token" + ); + XCTAssertEqual( + request.flags, + FBSDKGraphRequestFlagDisableErrorRecovery, + "Should add a request with the correct flags" + ); +} + +- (void)testCompletingTokenExtensionRequestWithDefaultValues +{ + [self completeTokenRefreshForAccessToken:SampleAccessToken.validToken results:nil]; + + // Check that an access token with the expected field values was set + OCMVerify( + ClassMethod( + [self.accessTokenClassMock setCurrentAccessToken:[OCMArg checkWithBlock:^BOOL (id obj) { + [self validateRefreshedToken:obj + withExpectedTokenString:SampleAccessToken.validToken.tokenString]; + return true; + }]] + ) + ); +} + +- (void)testCompletingTokenExtensionRequestWithUpdatedEmptyTokenString +{ + [self completeTokenRefreshForAccessToken:SampleAccessToken.validToken + results:@{@"access_token" : @""}]; + + // Check that an access token with the expected field values was set + OCMVerify( + ClassMethod( + [self.accessTokenClassMock setCurrentAccessToken:[OCMArg checkWithBlock:^BOOL (id obj) { + [self validateRefreshedToken:obj withExpectedTokenString:@""]; + return true; + }]] + ) + ); +} + +- (void)testCompletingTokenExtensionRequestWithUpdatedWhitespaceOnlyTokenString +{ + [self completeTokenRefreshForAccessToken:SampleAccessToken.validToken + results:@{@"access_token" : @" "}]; + + // Check that an access token with the expected field values was set + OCMVerify( + ClassMethod( + [self.accessTokenClassMock setCurrentAccessToken:[OCMArg checkWithBlock:^BOOL (id obj) { + [self validateRefreshedToken:obj withExpectedTokenString:@" "]; + return true; + }]] + ) + ); +} + +- (void)testCompletingTokenExtensionRequestWithInvalidExpirationDate +{ + [self completeTokenRefreshForAccessToken:SampleAccessToken.validToken + results:@{@"expires_at" : @"0"}]; + + // Check that an access token with the expected field values was set + OCMVerify( + ClassMethod( + [self.accessTokenClassMock setCurrentAccessToken:[OCMArg checkWithBlock:^BOOL (id obj) { + [self validateRefreshedToken:obj]; + return true; + }]] + ) + ); + + [self completeTokenRefreshForAccessToken:SampleAccessToken.validToken + results:@{@"expires_at" : @"-1000"}]; + + // Check that an access token with the expected field values was set + OCMVerify( + ClassMethod( + [self.accessTokenClassMock setCurrentAccessToken:[OCMArg checkWithBlock:^BOOL (id obj) { + [self validateRefreshedToken:obj]; + return true; + }]] + ) + ); +} + +- (void)testCompletingTokenExtensionRequestWithUnreasonableValidExpirationDate +{ + [self completeTokenRefreshForAccessToken:SampleAccessToken.validToken + results:@{@"expires_at" : @100}]; + + NSDate *expectedExpirationDate = [NSDate dateWithTimeIntervalSince1970:100]; + + // Check that an access token with the expected field values was set + OCMVerify( + ClassMethod( + [self.accessTokenClassMock setCurrentAccessToken:[OCMArg checkWithBlock:^BOOL (id obj) { + [self validateRefreshedToken:obj expectedExpirationDate:expectedExpirationDate]; + return true; + }]] + ) + ); +} + +- (void)testCompletingTokenExtensionRequestWithReasonableValidExpirationDate +{ + NSTimeInterval oneWeek = 60 * 60 * 24 * 7; + NSDate *oneWeekFromNow = [NSDate dateWithTimeIntervalSinceNow:oneWeek]; + // This is an acceptable value but really should not be. + [self completeTokenRefreshForAccessToken:SampleAccessToken.validToken + results:@{@"expires_at" : @(oneWeekFromNow.timeIntervalSince1970)}]; + + NSDate *expectedExpirationDate = [NSDate dateWithTimeIntervalSince1970:oneWeekFromNow.timeIntervalSince1970]; + + // Check that an access token with the expected field values was set + OCMVerify( + ClassMethod( + [self.accessTokenClassMock setCurrentAccessToken:[OCMArg checkWithBlock:^BOOL (id obj) { + [self validateRefreshedToken:obj expectedExpirationDate:expectedExpirationDate]; + return true; + }]] + ) + ); +} + +- (void)testCompletingTokenExtensionRequestWithInvalidDataExpirationDate +{ + [self completeTokenRefreshForAccessToken:SampleAccessToken.validToken results:@{@"data_access_expiration_time" : @"0"}]; + + // Check that an access token with the expected field values was set + OCMVerify( + ClassMethod( + [self.accessTokenClassMock setCurrentAccessToken:[OCMArg checkWithBlock:^BOOL (id obj) { + [self validateRefreshedToken:obj]; + return true; + }]] + ) + ); + + [self completeTokenRefreshForAccessToken:SampleAccessToken.validToken results:@{@"data_access_expiration_time" : @"-1000"}]; + + // Check that an access token with the expected field values was set + OCMVerify( + ClassMethod( + [self.accessTokenClassMock setCurrentAccessToken:[OCMArg checkWithBlock:^BOOL (id obj) { + [self validateRefreshedToken:obj]; + return true; + }]] + ) + ); +} + +- (void)testCompletingTokenExtensionRequestWithUnreasonableValidDataExpirationDate +{ + [self completeTokenRefreshForAccessToken:SampleAccessToken.validToken results:@{@"data_access_expiration_time" : @100}]; + + NSDate *expectedExpirationDate = [NSDate dateWithTimeIntervalSince1970:100]; + + // Check that an access token with the expected field values was set + OCMVerify( + ClassMethod( + [self.accessTokenClassMock setCurrentAccessToken:[OCMArg checkWithBlock:^BOOL (id obj) { + [self validateRefreshedToken:obj expectedDataExpirationDate:expectedExpirationDate]; + return true; + }]] + ) + ); +} + +- (void)testCompletingTokenExtensionRequestWithReasonableValidDataExpirationDate +{ + NSTimeInterval oneWeek = 60 * 60 * 24 * 7; + NSDate *oneWeekFromNow = [NSDate dateWithTimeIntervalSinceNow:oneWeek]; + // This is an acceptable value but really should not be. + [self completeTokenRefreshForAccessToken:SampleAccessToken.validToken results:@{@"data_access_expiration_time" : @(oneWeekFromNow.timeIntervalSince1970)}]; + + NSDate *expectedExpirationDate = [NSDate dateWithTimeIntervalSince1970:oneWeekFromNow.timeIntervalSince1970]; + + // Check that an access token with the expected field values was set + OCMVerify( + ClassMethod( + [self.accessTokenClassMock setCurrentAccessToken:[OCMArg checkWithBlock:^BOOL (id obj) { + [self validateRefreshedToken:obj expectedDataExpirationDate:expectedExpirationDate]; + return true; + }]] + ) + ); +} + +- (void)testCompletingTokenExtensionRequestWithUpdatedEmptyGraphDomain +{ + [self completeTokenRefreshForAccessToken:SampleAccessToken.validToken results:@{@"graph_domain" : @""}]; + + // Check that an access token with the expected field values was set + OCMVerify( + ClassMethod( + [self.accessTokenClassMock setCurrentAccessToken:[OCMArg checkWithBlock:^BOOL (id obj) { + [self validateRefreshedToken:obj withExpectedGraphDomain:@""]; + return true; + }]] + ) + ); +} + +- (void)testCompletingTokenExtensionRequestWithUpdatedWhitespaceOnlyGraphDomain +{ + [self completeTokenRefreshForAccessToken:SampleAccessToken.validToken results:@{@"graph_domain" : @" "}]; + + // Check that an access token with the expected field values was set + OCMVerify( + ClassMethod( + [self.accessTokenClassMock setCurrentAccessToken:[OCMArg checkWithBlock:^BOOL (id obj) { + [self validateRefreshedToken:obj withExpectedGraphDomain:@" "]; + return true; + }]] + ) + ); +} + +- (void)testCompletingTokenExtensionRequestWithFuzzyValues +{ + for (int i = 0; i < 100; i++) { + [self completeTokenRefreshForAccessToken:SampleAccessToken.validToken results:@{ + @"access_token" : [Fuzzer random], + @"expires_at" : [Fuzzer random], + @"data_access_expiration_time" : [Fuzzer random], + @"graph_domain" : [Fuzzer random] + }]; + } +} + +// MARK: - Adding Permissions Refresh Piggyback + +- (void)testAddsPermissionsRefreshRequest +{ + [self stubAppID:@"abc123"]; + [self stubCurrentAccessTokenWith:SampleAccessToken.validToken]; + FBSDKGraphRequestConnection *connection = [FBSDKGraphRequestConnection new]; + + [Manager addRefreshPiggyback:connection permissionHandler:nil]; + + FBSDKGraphRequestMetadata *metadata = connection.requests.lastObject; + FBSDKGraphRequest *request = metadata.request; + XCTAssertNotNil(request, "Adding a refresh piggyback to a connection should add a request for refreshing permissions"); + + XCTAssertEqualObjects( + request.graphPath, + @"me/permissions", + "Should add a request with the correct graph path for refreshing permissions" + ); + + NSDictionary *expectedParameters = @{@"fields" : @""}; + XCTAssertTrue( + [request.parameters isEqualToDictionary:expectedParameters], + "Should add a request with the correct parameters for refreshing permissions" + ); + XCTAssertEqual( + request.flags, + FBSDKGraphRequestFlagDisableErrorRecovery, + "Should add a request with the correct flags for refreshing permissions" + ); +} + +- (void)testCompletingPermissionsRefreshRequestWithEmptyResults +{ + FBSDKAccessToken *token = [SampleAccessToken validTokenWithPermissions:@[@"email"] + declinedPermissions:@[@"publish"] + expiredPermissions:@[@"friends"]]; + + [self completePermissionsRefreshForAccessToken:token results:nil]; + + // Refreshed token clears permissions when there is no error + OCMVerify( + ClassMethod( + [self.accessTokenClassMock setCurrentAccessToken:[OCMArg checkWithBlock:^BOOL (id obj) { + [self validateRefreshedToken:obj + withExpectedPermissions:@[] + expectedDeclinedPermissions:@[] + expectedExpiredPermissions:@[] + ]; + return true; + }]] + ) + ); +} + +- (void)testCompletingPermissionsRefreshRequestWithEmptyResultsWithError +{ + FBSDKAccessToken *token = [SampleAccessToken validTokenWithPermissions:@[@"email"] + declinedPermissions:@[@"publish"] + expiredPermissions:@[@"friends"]]; + + [self completePermissionsRefreshForAccessToken:token results:nil error:[NSError new]]; + + // Refreshed token uses permissions from current access token when there is an error on permissions refresh + OCMVerify( + ClassMethod( + [self.accessTokenClassMock setCurrentAccessToken:[OCMArg checkWithBlock:^BOOL (id obj) { + [self validateRefreshedToken:obj + withExpectedPermissions:token.permissions.allObjects + expectedDeclinedPermissions:token.declinedPermissions.allObjects + expectedExpiredPermissions:token.expiredPermissions.allObjects + ]; + return true; + }]] + ) + ); +} + +- (void)testCompletingPermissionsRefreshRequestWithNewGrantedPermissions +{ + FBSDKAccessToken *token = [SampleAccessToken validTokenWithPermissions:@[@"email"] + declinedPermissions:@[@"publish"] + expiredPermissions:@[@"friends"]]; + + NSDictionary *results = [SampleRawRemotePermissionList withGranted:@[@"foo"] declined:@[] expired:@[]]; + + [self completePermissionsRefreshForAccessToken:token results:results]; + + // Refreshed token clears unspecified permissions when there are newly specified permissions in the response + OCMVerify( + ClassMethod( + [self.accessTokenClassMock setCurrentAccessToken:[OCMArg checkWithBlock:^BOOL (id obj) { + [self validateRefreshedToken:obj + withExpectedPermissions:@[@"foo"] + expectedDeclinedPermissions:@[] + expectedExpiredPermissions:@[] + ]; + return true; + }]] + ) + ); +} + +- (void)testCompletingPermissionsRefreshRequestWithNewDeclinedPermissions +{ + FBSDKAccessToken *token = [SampleAccessToken validTokenWithPermissions:@[@"email"] + declinedPermissions:@[@"publish"] + expiredPermissions:@[@"friends"]]; + + NSDictionary *results = [SampleRawRemotePermissionList withGranted:@[] declined:@[@"foo"] expired:@[]]; + + [self completePermissionsRefreshForAccessToken:token results:results]; + + // Refreshed token clears unspecified permissions when there are newly specified permissions in the response + OCMVerify( + ClassMethod( + [self.accessTokenClassMock setCurrentAccessToken:[OCMArg checkWithBlock:^BOOL (id obj) { + [self validateRefreshedToken:obj + withExpectedPermissions:@[] + expectedDeclinedPermissions:@[@"foo"] + expectedExpiredPermissions:@[] + ]; + return true; + }]] + ) + ); +} + +- (void)testCompletingPermissionsRefreshRequestWithNewExpiredPermissions +{ + FBSDKAccessToken *token = [SampleAccessToken validTokenWithPermissions:@[@"email"] + declinedPermissions:@[@"publish"] + expiredPermissions:@[@"friends"]]; + [self stubCurrentAccessTokenWith:token]; + + NSDictionary *results = [SampleRawRemotePermissionList withGranted:@[] declined:@[] expired:@[@"foo"]]; + + [self completePermissionsRefreshForAccessToken:SampleAccessToken.validToken results:results]; + + // Refreshed token clears unspecified permissions when there are newly specified permissions in the response + OCMVerify( + ClassMethod( + [self.accessTokenClassMock setCurrentAccessToken:[OCMArg checkWithBlock:^BOOL (id obj) { + [self validateRefreshedToken:obj + withExpectedPermissions:@[] + expectedDeclinedPermissions:@[] + expectedExpiredPermissions:@[@"foo"] + ]; + return true; + }]] + ) + ); +} + +- (void)testCompletingPermissionsRefreshRequestWithNewPermissions +{ + FBSDKAccessToken *token = [SampleAccessToken validTokenWithPermissions:@[@"email"] + declinedPermissions:@[@"publish"] + expiredPermissions:@[@"friends"]]; + + NSDictionary *results = [SampleRawRemotePermissionList withGranted:@[@"foo"] + declined:@[@"bar"] + expired:@[@"baz"]]; + + [self completePermissionsRefreshForAccessToken:token results:results]; + + // Refreshed token clears unspecified permissions when there are newly specified permissions in the response + OCMVerify( + ClassMethod( + [self.accessTokenClassMock setCurrentAccessToken:[OCMArg checkWithBlock:^BOOL (id obj) { + [self validateRefreshedToken:obj + withExpectedPermissions:@[@"foo"] + expectedDeclinedPermissions:@[@"bar"] + expectedExpiredPermissions:@[@"baz"] + ]; + return true; + }]] + ) + ); +} + +- (void)testCompletingPermissionsRefreshRequestWithPermissionsHandlerWithoutError +{ + XCTestExpectation *expectation = [[XCTestExpectation alloc] initWithDescription:self.name]; + FBSDKAccessToken *expectedToken = [SampleAccessToken validTokenWithPermissions:@[@"email"] + declinedPermissions:@[@"publish"] + expiredPermissions:@[@"friends"]]; + [self stubCurrentAccessTokenWith:expectedToken]; + + NSDictionary *results = [SampleRawRemotePermissionList withGranted:@[@"foo"] + declined:@[@"bar"] + expired:@[@"baz"]]; + + [self completePermissionsRefreshForAccessToken:SampleAccessToken.validToken + results:results + permissionHandler:^(FBSDKGraphRequestConnection *connection, id result, NSError *error) { + XCTAssertEqualObjects( + result, + results, + "Should pass the raw results to the provided permissions handler" + ); + XCTAssertNil( + error, + "Should invoke the permissions handler regardless of error state" + ); + [expectation fulfill]; + }]; + + [self waitForExpectations:@[expectation] timeout:1]; +} + +- (void)testCompletingPermissionsRefreshRequestWithPermissionsHandlerWithError +{ + XCTestExpectation *expectation = [[XCTestExpectation alloc] initWithDescription:self.name]; + FBSDKAccessToken *expectedToken = [SampleAccessToken validTokenWithPermissions:@[@"email"] + declinedPermissions:@[@"publish"] + expiredPermissions:@[@"friends"]]; + [self stubCurrentAccessTokenWith:expectedToken]; + + NSDictionary *results = [SampleRawRemotePermissionList withGranted:@[@"foo"] + declined:@[@"bar"] + expired:@[@"baz"]]; + NSError *expectedError = [NSError new]; + + [self completePermissionsRefreshForAccessToken:SampleAccessToken.validToken + results:results + error:expectedError + permissionHandler:^(FBSDKGraphRequestConnection *connection, id result, NSError *error) { + XCTAssertEqualObjects( + result, + results, + "Should pass the raw results to the provided permissions handler" + ); + XCTAssertEqualObjects( + error, + expectedError, + "Should pass the error through to the permissions handler" + ); + [expectation fulfill]; + }]; + + [self waitForExpectations:@[expectation] timeout:1]; +} + +// MARK: - Refreshing if Stale + +- (void)testRefreshIfStaleWithoutAccessToken +{ + [self stubCurrentAccessTokenWith:nil]; + + // Shouldn't add the refresh if there's no access token + OCMReject(ClassMethod([self.graphRequestPiggybackManagerMock addRefreshPiggyback:OCMArg.any permissionHandler:NULL])); + + [Manager addRefreshPiggybackIfStale:SampleGraphRequestConnection.empty]; +} + +- (void)testRefreshIfStaleWithAccessTokenWithoutRefreshDate +{ + [self stubCurrentAccessTokenWith:SampleAccessToken.validToken]; + + // Should not add the refresh if the access token is missing a refresh date + OCMReject(ClassMethod([self.graphRequestPiggybackManagerMock addRefreshPiggyback:OCMArg.any permissionHandler:NULL])); + + [Manager addRefreshPiggybackIfStale:SampleGraphRequestConnection.empty]; +} + +// | Last refresh try > an hour ago | Token refresh date > a day ago | should refresh | +// | true | true | true | +- (void)testRefreshIfStaleWithOldRefreshWithOldTokenRefresh +{ + [self stubGraphRequestPiggybackManagerLastRefreshTryWith:NSDate.distantPast]; + [self stubCurrentAccessTokenWith:self.twoDayOldToken]; + + [Manager addRefreshPiggybackIfStale:SampleGraphRequestConnection.empty]; + + OCMVerify(ClassMethod([self.graphRequestPiggybackManagerMock addRefreshPiggyback:OCMArg.any permissionHandler:NULL])); +} + +// | Last refresh try > an hour ago | Token refresh date > a day ago | should refresh | +// | true | false | false | +- (void)testRefreshIfStaleWithOldLastRefreshWithRecentTokenRefresh +{ + [self stubGraphRequestPiggybackManagerLastRefreshTryWith:NSDate.distantPast]; + [self stubCurrentAccessTokenWith:SampleAccessToken.validToken]; + + OCMReject(ClassMethod([self.graphRequestPiggybackManagerMock addRefreshPiggyback:OCMArg.any permissionHandler:NULL])); + + [Manager addRefreshPiggybackIfStale:SampleGraphRequestConnection.empty]; +} + +// | Last refresh try > an hour ago | Token refresh date > a day ago | should refresh | +// | false | false | false | +- (void)testRefreshIfStaleWithRecentLastRefreshWithRecentTokenRefresh +{ + // Used for manipulating the initial value of the method scoped constant `lastRefreshTry` + [self stubGraphRequestPiggybackManagerLastRefreshTryWith:NSDate.distantFuture]; + + OCMReject(ClassMethod([self.graphRequestPiggybackManagerMock addRefreshPiggyback:OCMArg.any permissionHandler:NULL])); + + [Manager addRefreshPiggybackIfStale:SampleGraphRequestConnection.empty]; +} + +// | Last refresh try > an hour ago | Token refresh date > a day ago | should refresh | +// | false | true | false | +- (void)testRefreshIfStaleWithRecentLastRefreshOldTokenRefresh +{ + // Used for manipulating the initial value of the method scoped constant `lastRefreshTry` + [self stubGraphRequestPiggybackManagerLastRefreshTryWith:NSDate.distantFuture]; + [self stubCurrentAccessTokenWith:self.twoDayOldToken]; + + OCMReject(ClassMethod([self.graphRequestPiggybackManagerMock addRefreshPiggyback:OCMArg.any permissionHandler:NULL])); + + [Manager addRefreshPiggybackIfStale:SampleGraphRequestConnection.empty]; +} + +- (void)testRefreshIfStaleSideEffects +{ + // Used for manipulating the initial value of the method scoped constant `lastRefreshTry` + [self stubGraphRequestPiggybackManagerLastRefreshTryWith:NSDate.distantPast]; + [self stubCurrentAccessTokenWith:self.twoDayOldToken]; + + [Manager addRefreshPiggybackIfStale:SampleGraphRequestConnection.empty]; + + OCMVerify(ClassMethod([self.graphRequestPiggybackManagerMock addRefreshPiggyback:OCMArg.any permissionHandler:NULL])); + // Should update last refresh try + OCMVerify(ClassMethod([self.graphRequestPiggybackManagerMock _setLastRefreshTry:OCMArg.any])); +} + +// MARK: - Server Configuration Piggyback + +- (void)testAddingServerConfigurationPiggybackWithDefaultConfigurationExpiredCache +{ + FBSDKServerConfiguration *config = [FBSDKServerConfigurationFixtures configWithDictionary:@{ + @"defaults" : @YES, + @"timestamp" : self.twoDaysAgo + }]; + [self stubCachedServerConfigurationWithServerConfiguration:config]; + [self stubAppID:config.appID]; + + FBSDKGraphRequestConnection *connection = [FBSDKGraphRequestConnection new]; + [Manager addServerConfigurationPiggyback:connection]; + FBSDKGraphRequestMetadata *requestMetadata = connection.requests.firstObject; + FBSDKGraphRequest *expectedServerConfigurationRequest = [FBSDKServerConfigurationManager requestToLoadServerConfiguration:nil]; + + [self validateServerConfigurationRequest:requestMetadata.request isEqualTo:expectedServerConfigurationRequest]; +} + +- (void)testAddingServerConfigurationPiggybackWithDefaultConfigurationNonExpiredCache +{ + FBSDKServerConfiguration *config = [FBSDKServerConfigurationFixtures configWithDictionary:@{ + @"defaults" : @YES, + @"timestamp" : NSDate.date + }]; + [self stubCachedServerConfigurationWithServerConfiguration:config]; + + FBSDKGraphRequestConnection *connection = [FBSDKGraphRequestConnection new]; + [Manager addServerConfigurationPiggyback:connection]; + + XCTAssertEqual( + connection.requests.count, + 1, + "Should add a server configuration request for a default config with a non-expired cache" + ); +} + +- (void)testAddingServerConfigurationPiggybackWithCustomConfigurationExpiredCache +{ + FBSDKServerConfiguration *config = [FBSDKServerConfigurationFixtures configWithDictionary:@{ + @"defaults" : @YES, + @"timestamp" : self.twoDaysAgo + }]; + [self stubCachedServerConfigurationWithServerConfiguration:config]; + + FBSDKGraphRequestConnection *connection = [FBSDKGraphRequestConnection new]; + [Manager addServerConfigurationPiggyback:connection]; + + XCTAssertEqual( + connection.requests.count, + 1, + "Should add a server configuration request for a default config with an expired cached" + ); +} + +- (void)testAddingServerConfigurationPiggybackWithCustomConfigurationNonExpiredCache +{ + FBSDKServerConfiguration *config = [FBSDKServerConfigurationFixtures configWithDictionary:@{ + @"defaults" : @NO, + @"timestamp" : NSDate.date + }]; + [self stubCachedServerConfigurationWithServerConfiguration:config]; + + FBSDKGraphRequestConnection *connection = [FBSDKGraphRequestConnection new]; + [Manager addServerConfigurationPiggyback:connection]; + + XCTAssertEqual( + connection.requests.count, + 0, + "Should not add a server configuration request for a custom configuration with a non-expired cache" + ); +} + +- (void)testAddingServerConfigurationPiggybackWithCustomConfigurationMissingTimeout +{ + // Esoterica - the default timeout is nil in the default configuration + FBSDKServerConfiguration *config = [FBSDKServerConfigurationFixtures configWithDictionary:@{ + @"defaults" : @NO + }]; + [self stubCachedServerConfigurationWithServerConfiguration:config]; + + FBSDKGraphRequestConnection *connection = [FBSDKGraphRequestConnection new]; + [Manager addServerConfigurationPiggyback:connection]; + + XCTAssertEqual( + connection.requests.count, + 1, + "Should add a server configuration request for a custom configuration with a missing cache timeout" + ); +} + +- (void)testAddingServerConfigurationPiggybackWithDefaultConfigurationMissingTimeout +{ + // Esoterica - the default timeout is nil in the default configuration + FBSDKServerConfiguration *config = [FBSDKServerConfigurationFixtures configWithDictionary:@{ + @"defaults" : @YES + }]; + [self stubCachedServerConfigurationWithServerConfiguration:config]; + + FBSDKGraphRequestConnection *connection = [FBSDKGraphRequestConnection new]; + [Manager addServerConfigurationPiggyback:connection]; + + XCTAssertEqual( + connection.requests.count, + 1, + "Should add a server configuration request for a default configuration with a missing cache timeout" + ); +} + +// MARK: - Helpers + +- (NSDate *)twoDaysAgo +{ + int twoDaysInSeconds = 60 * 60 * 48; + return [NSDate dateWithTimeIntervalSinceNow:-twoDaysInSeconds]; +} + +- (FBSDKAccessToken *)twoDayOldToken +{ + return [SampleAccessToken validWithRefreshDate:self.twoDaysAgo]; +} + +- (void)validateServerConfigurationRequest:(FBSDKGraphRequest *)request isEqualTo:(FBSDKGraphRequest *)expectedRequest +{ + XCTAssertNotNil(request, "Adding a server configuration piggyback should add a request to fetch the server configuration"); + + XCTAssertEqualObjects( + request.graphPath, + expectedRequest.graphPath, + "Should add a request with the expected graph path for fetching a server configuration" + ); + XCTAssertEqualObjects( + request.parameters, + expectedRequest.parameters, + "Should add a request with the correct parameters for fetching a server configuration" + ); + XCTAssertEqual( + request.flags, + expectedRequest.flags, + "Should add a request with the correct flags for fetching a server configuration" + ); +} + +- (void)validateRefreshedToken:(FBSDKAccessToken *)token +{ + [self validateRefreshedToken:token + withExpectedTokenString:SampleAccessToken.validToken.tokenString + expectedRefreshDate:[NSDate date] + expectedExpirationDate:NSDate.distantFuture + expectedDataExpirationDate:NSDate.distantFuture + expectedGraphDomain:SampleAccessToken.validToken.graphDomain + expectedPermissions:[NSArray array] + expectedDeclinedPermissions:[NSArray array] + expectedExpiredPermissions:[NSArray array]]; +} + +- (void)validateRefreshedToken:(FBSDKAccessToken *)token + withExpectedTokenString:(NSString *)expectedTokenString +{ + [self validateRefreshedToken:token + withExpectedTokenString:expectedTokenString + expectedRefreshDate:[NSDate date] + expectedExpirationDate:NSDate.distantFuture + expectedDataExpirationDate:NSDate.distantFuture + expectedGraphDomain:SampleAccessToken.validToken.graphDomain + expectedPermissions:[NSArray array] + expectedDeclinedPermissions:[NSArray array] + expectedExpiredPermissions:[NSArray array]]; +} + +- (void)validateRefreshedToken:(FBSDKAccessToken *)token + expectedExpirationDate:(NSDate *)expectedExpirationDate +{ + [self validateRefreshedToken:token + withExpectedTokenString:SampleAccessToken.validToken.tokenString + expectedRefreshDate:[NSDate date] + expectedExpirationDate:expectedExpirationDate + expectedDataExpirationDate:NSDate.distantFuture + expectedGraphDomain:SampleAccessToken.validToken.graphDomain + expectedPermissions:[NSArray array] + expectedDeclinedPermissions:[NSArray array] + expectedExpiredPermissions:[NSArray array]]; +} + +- (void)validateRefreshedToken:(FBSDKAccessToken *)token + expectedDataExpirationDate:(NSDate *)expectedDataExpirationDate +{ + [self validateRefreshedToken:token + withExpectedTokenString:SampleAccessToken.validToken.tokenString + expectedRefreshDate:[NSDate date] + expectedExpirationDate:NSDate.distantFuture + expectedDataExpirationDate:expectedDataExpirationDate + expectedGraphDomain:SampleAccessToken.validToken.graphDomain + expectedPermissions:[NSArray array] + expectedDeclinedPermissions:[NSArray array] + expectedExpiredPermissions:[NSArray array]]; +} + +- (void)validateRefreshedToken:(FBSDKAccessToken *)token + withExpectedGraphDomain:(NSString *)expectedGraphDomain +{ + [self validateRefreshedToken:token + withExpectedTokenString:SampleAccessToken.validToken.tokenString + expectedRefreshDate:[NSDate date] + expectedExpirationDate:NSDate.distantFuture + expectedDataExpirationDate:NSDate.distantFuture + expectedGraphDomain:expectedGraphDomain + expectedPermissions:[NSArray array] + expectedDeclinedPermissions:[NSArray array] + expectedExpiredPermissions:[NSArray array]]; +} + +- (void)validateRefreshedToken:(FBSDKAccessToken *)token + withExpectedPermissions:(NSArray *)expectedPermissions + expectedDeclinedPermissions:(NSArray *)expectedDeclinedPermissions + expectedExpiredPermissions:(NSArray *)expectedExpiredPermissions +{ + [self validateRefreshedToken:token + withExpectedTokenString:SampleAccessToken.validToken.tokenString + expectedRefreshDate:[NSDate date] + expectedExpirationDate:NSDate.distantFuture + expectedDataExpirationDate:NSDate.distantFuture + expectedGraphDomain:SampleAccessToken.validToken.graphDomain + expectedPermissions:expectedPermissions + expectedDeclinedPermissions:expectedDeclinedPermissions + expectedExpiredPermissions:expectedExpiredPermissions]; +} + +- (void)validateRefreshedToken:(FBSDKAccessToken *)token + withExpectedTokenString:(NSString *)expectedTokenString + expectedRefreshDate:(NSDate *)expectedRefreshDate + expectedExpirationDate:(NSDate *)expectedExpirationDate + expectedDataExpirationDate:(NSDate *)expectedDataExpirationDate + expectedGraphDomain:(NSString *)expectedGraphDomain + expectedPermissions:(NSArray *)expectedPermissions + expectedDeclinedPermissions:(NSArray *)expectedDeclinedPermissions + expectedExpiredPermissions:(NSArray *)expectedExpiredPermissions +{ + XCTAssertEqualObjects(token.tokenString, expectedTokenString, "A refreshed token should have the expected token string"); + XCTAssertEqualWithAccuracy(token.refreshDate.timeIntervalSince1970, expectedRefreshDate.timeIntervalSince1970, 1, "A refreshed token should have the expected refresh date"); + XCTAssertEqualObjects(token.expirationDate, expectedExpirationDate, "A refreshed token should have the expected expiration date"); + XCTAssertEqualObjects(token.dataAccessExpirationDate, expectedDataExpirationDate, "A refreshed token should have the expected data access expiration date"); + XCTAssertEqualObjects(token.graphDomain, expectedGraphDomain, "A refreshed token should have the expected graph domain"); + XCTAssertEqualObjects(token.permissions.allObjects, expectedPermissions, "A refreshed token should have the expected permissions"); + XCTAssertEqualObjects(token.declinedPermissions.allObjects, expectedDeclinedPermissions, "A refreshed token should have the expected declined permissions"); + XCTAssertEqualObjects(token.expiredPermissions.allObjects, expectedExpiredPermissions, "A refreshed token should have the expected expired permissions"); +} + +- (void)completeTokenRefreshForAccessToken:(FBSDKAccessToken *)token results:(NSDictionary *)results +{ + [self stubAppID:token.appID]; + [self stubCurrentAccessTokenWith:token]; + FBSDKGraphRequestConnection *connection = [FBSDKGraphRequestConnection new]; + + [Manager addRefreshPiggyback:connection permissionHandler:nil]; + + FBSDKGraphRequestMetadata *metadata = connection.requests.firstObject; + + // The callback that sets the token ignores the first call to it + // because it's waiting on the permissions call to complete first. + // We can get around this for now by invoking the handler twice. + metadata.completionHandler(connection, @{}, nil); + metadata.completionHandler(connection, results, nil); +} + +- (void)completePermissionsRefreshForAccessToken:(FBSDKAccessToken *)token + results:(NSDictionary *)results +{ + [self completePermissionsRefreshForAccessToken:token results:results error:nil]; +} + +- (void)completePermissionsRefreshForAccessToken:(FBSDKAccessToken *)token + results:(NSDictionary *)results + error:(NSError *)error +{ + [self completePermissionsRefreshForAccessToken:token results:results error:error permissionHandler:nil]; +} + +- (void)completePermissionsRefreshForAccessToken:(FBSDKAccessToken *)token + results:(NSDictionary *)results + permissionHandler:(FBSDKGraphRequestBlock)permissionHandler +{ + [self completePermissionsRefreshForAccessToken:token results:results error:nil permissionHandler:permissionHandler]; +} + +- (void)completePermissionsRefreshForAccessToken:(FBSDKAccessToken *)token + results:(NSDictionary *)results + error:(NSError *)error + permissionHandler:(FBSDKGraphRequestBlock)permissionHandler +{ + [self stubAppID:token.appID]; + [self stubCurrentAccessTokenWith:token]; + FBSDKGraphRequestConnection *connection = [FBSDKGraphRequestConnection new]; + + [Manager addRefreshPiggyback:connection permissionHandler:permissionHandler]; + + FBSDKGraphRequestMetadata *tokenRefreshRequestMetadata = connection.requests.firstObject; + FBSDKGraphRequestMetadata *permissionsRequestMetadata = connection.requests.lastObject; + + tokenRefreshRequestMetadata.completionHandler(connection, nil, nil); + permissionsRequestMetadata.completionHandler(connection, results, error); +} + +@end diff --git a/FBSDKCoreKit/FBSDKCoreKitTests/Internal/ServerConfiguration/FBSDKServerConfigurationManagerTests.swift b/FBSDKCoreKit/FBSDKCoreKitTests/Internal/ServerConfiguration/FBSDKServerConfigurationManagerTests.swift new file mode 100644 index 0000000000..6ccaa80a03 --- /dev/null +++ b/FBSDKCoreKit/FBSDKCoreKitTests/Internal/ServerConfiguration/FBSDKServerConfigurationManagerTests.swift @@ -0,0 +1,31 @@ +// 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 XCTest + +class FBSDKServerConfigurationManagerTests: XCTestCase { + func testParsingResponses() { + for _ in 0..<100 { + ServerConfigurationManager.processLoadRequestResponse( + RawServerConfigurationResponseFixtures.random, + error: nil, + appID: nil + ) + } + } +} diff --git a/FBSDKCoreKit/FBSDKCoreKitTests/Internal/ServerConfiguration/FBSDKServerConfigurationTests.m b/FBSDKCoreKit/FBSDKCoreKitTests/Internal/ServerConfiguration/FBSDKServerConfigurationTests.m index 023990f944..c2a1b67967 100644 --- a/FBSDKCoreKit/FBSDKCoreKitTests/Internal/ServerConfiguration/FBSDKServerConfigurationTests.m +++ b/FBSDKCoreKit/FBSDKCoreKitTests/Internal/ServerConfiguration/FBSDKServerConfigurationTests.m @@ -16,14 +16,14 @@ // 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 "FBSDKCoreKit+Internal.h" -#import "FBSDKServerConfigurationFixtures.h" +#import "FBSDKCoreKitTests-Swift.h" #import "FBSDKEventBinding.h" +#import "FBSDKServerConfigurationFixtures.h" #import "FBSDKTestCoder.h" -#import "FBSDKMonitoringConfigurationTestHelper.h" @interface FBSDKServerConfiguration (Testing) @@ -41,7 +41,8 @@ @interface FBSDKMonitoringConfiguration (Testing) @interface FBSDKServerConfigurationTests : XCTestCase @end -@implementation FBSDKServerConfigurationTests { +@implementation FBSDKServerConfigurationTests +{ FBSDKServerConfiguration *config; } @@ -66,7 +67,7 @@ - (void)testUsingDefaults { XCTAssertTrue(config.defaults, "Should assume defaults are being used unless otherwise expressed"); - config = [Fixtures configWithDictionary:@{@"defaults": @NO}]; + config = [Fixtures configWithDictionary:@{@"defaults" : @NO}]; XCTAssertFalse(config.defaults, "Should store whether or not defaults are being used"); } @@ -77,74 +78,97 @@ - (void)testCreatingWithoutAppID - (void)testCreatingWithEmptyAppID { - config = [Fixtures configWithDictionary:@{@"appID": @""}]; - XCTAssertEqualObjects(config.appID, @"", - "Should use the given app identifier regardless of value"); + config = [Fixtures configWithDictionary:@{@"appID" : @""}]; + XCTAssertEqualObjects( + config.appID, + @"", + "Should use the given app identifier regardless of value" + ); } - (void)testCreatingWithDefaultAdvertisingIdEnabled { - XCTAssertFalse(config.isAdvertisingIDEnabled, - "Advertising identifier enabled should default to false"); + XCTAssertFalse( + config.isAdvertisingIDEnabled, + "Advertising identifier enabled should default to false" + ); } - (void)testCreatingWithKnownAdvertisingIdEnabled { - config = [Fixtures configWithDictionary:@{@"advertisingIDEnabled": @YES}]; - XCTAssertTrue(config.isAdvertisingIDEnabled, - "Advertising identifier enabled should be settable"); + config = [Fixtures configWithDictionary:@{@"advertisingIDEnabled" : @YES}]; + XCTAssertTrue( + config.isAdvertisingIDEnabled, + "Advertising identifier enabled should be settable" + ); } - (void)testCreatingWithDefaultImplicitPurchaseLoggingEnabled { - XCTAssertFalse(config.implicitPurchaseLoggingEnabled, - "Implicit purchase logging enabled should default to false"); + XCTAssertFalse( + config.implicitPurchaseLoggingEnabled, + "Implicit purchase logging enabled should default to false" + ); } - (void)testCreatingWithKnownImplicitPurchaseLoggingEnabled { - config = [Fixtures configWithDictionary:@{@"implicitPurchaseLoggingEnabled": @YES}]; - XCTAssertTrue(config.implicitPurchaseLoggingEnabled, - "Implicit purchase logging enabled should be settable"); + config = [Fixtures configWithDictionary:@{@"implicitPurchaseLoggingEnabled" : @YES}]; + XCTAssertTrue( + config.implicitPurchaseLoggingEnabled, + "Implicit purchase logging enabled should be settable" + ); } - (void)testCreatingWithDefaultImplicitLoggingEnabled { - XCTAssertFalse(config.implicitLoggingEnabled, - "Implicit logging enabled should default to false"); + XCTAssertFalse( + config.implicitLoggingEnabled, + "Implicit logging enabled should default to false" + ); } - (void)testCreatingWithKnownImplicitLoggingEnabled { - config = [Fixtures configWithDictionary:@{@"implicitLoggingEnabled": @YES}]; - XCTAssertTrue(config.implicitLoggingEnabled, - "Implicit logging enabled should be settable"); + config = [Fixtures configWithDictionary:@{@"implicitLoggingEnabled" : @YES}]; + XCTAssertTrue( + config.implicitLoggingEnabled, + "Implicit logging enabled should be settable" + ); } - (void)testCreatingWithDefaultCodelessEventsEnabled { - XCTAssertFalse(config.codelessEventsEnabled, - "Codeless events enabled should default to false"); + XCTAssertFalse( + config.codelessEventsEnabled, + "Codeless events enabled should default to false" + ); } - (void)testCreatingWithKnownCodelessEventsEnabled { - config = [Fixtures configWithDictionary:@{@"codelessEventsEnabled": @YES}]; - XCTAssertTrue(config.codelessEventsEnabled, - "Codeless events enabled should be settable"); + config = [Fixtures configWithDictionary:@{@"codelessEventsEnabled" : @YES}]; + XCTAssertTrue( + config.codelessEventsEnabled, + "Codeless events enabled should be settable" + ); } - (void)testCreatingWithDefaultUninstallTrackingEnabled { - XCTAssertFalse(config.uninstallTrackingEnabled, - "Uninstall tracking enabled should default to false"); + XCTAssertFalse( + config.uninstallTrackingEnabled, + "Uninstall tracking enabled should default to false" + ); } - (void)testCreatingWithKnownUninstallTrackingEnabled { - config = [Fixtures configWithDictionary:@{@"uninstallTrackingEnabled": @YES}]; - XCTAssertTrue(config.uninstallTrackingEnabled, - "Uninstall tracking enabled should be settable"); + config = [Fixtures configWithDictionary:@{@"uninstallTrackingEnabled" : @YES}]; + XCTAssertTrue( + config.uninstallTrackingEnabled, + "Uninstall tracking enabled should be settable" + ); } - (void)testCreatingWithoutAppName @@ -154,49 +178,67 @@ - (void)testCreatingWithoutAppName - (void)testCreatingWithEmptyAppName { - config = [Fixtures configWithDictionary:@{@"appName": @""}]; - XCTAssertEqualObjects(config.appName, @"", - "Should use the given app name regardless of value"); + config = [Fixtures configWithDictionary:@{@"appName" : @""}]; + XCTAssertEqualObjects( + config.appName, + @"", + "Should use the given app name regardless of value" + ); } - (void)testCreatingWithKnownAppName { - config = [Fixtures configWithDictionary:@{@"appName": @"foo"}]; - XCTAssertEqual(config.appName, @"foo", - "App name should be settable"); + config = [Fixtures configWithDictionary:@{@"appName" : @"foo"}]; + XCTAssertEqual( + config.appName, + @"foo", + "App name should be settable" + ); } - (void)testCreatingWithoutDefaultShareMode { - XCTAssertNil(config.defaultShareMode, - "Should not provide a default for the default share mode"); + XCTAssertNil( + config.defaultShareMode, + "Should not provide a default for the default share mode" + ); } - (void)testCreatingWithKnownDefaultShareMode { - config = [Fixtures configWithDictionary:@{@"defaultShareMode": @"native"}]; - XCTAssertEqual(config.defaultShareMode, @"native", - "Default share mode should be settable"); + config = [Fixtures configWithDictionary:@{@"defaultShareMode" : @"native"}]; + XCTAssertEqual( + config.defaultShareMode, + @"native", + "Default share mode should be settable" + ); } - (void)testCreatingWithEmptyDefaultShareMode { - config = [Fixtures configWithDictionary:@{@"defaultShareMode": @""}]; - XCTAssertEqual(config.defaultShareMode, @"", - "Should use the given share mode regardless of value"); + config = [Fixtures configWithDictionary:@{@"defaultShareMode" : @""}]; + XCTAssertEqual( + config.defaultShareMode, + @"", + "Should use the given share mode regardless of value" + ); } - (void)testCreatingWithDefaultLoginTooltipEnabled { - XCTAssertFalse(config.loginTooltipEnabled, - "Login tooltip enabled should default to false"); + XCTAssertFalse( + config.loginTooltipEnabled, + "Login tooltip enabled should default to false" + ); } - (void)testCreatingWithKnownLoginTooltipEnabled { - config = [Fixtures configWithDictionary:@{@"loginTooltipEnabled": @YES}]; - XCTAssertTrue(config.loginTooltipEnabled, - "Login tooltip enabled should be settable"); + config = [Fixtures configWithDictionary:@{@"loginTooltipEnabled" : @YES}]; + XCTAssertTrue( + config.loginTooltipEnabled, + "Login tooltip enabled should be settable" + ); } - (void)testCreatingWithoutLoginTooltipText @@ -206,16 +248,22 @@ - (void)testCreatingWithoutLoginTooltipText - (void)testCreatingWithEmptyLoginTooltipText { - config = [Fixtures configWithDictionary:@{@"loginTooltipText": @""}]; - XCTAssertEqualObjects(config.loginTooltipText, @"", - "Should use the given login tooltip text regardless of value"); + config = [Fixtures configWithDictionary:@{@"loginTooltipText" : @""}]; + XCTAssertEqualObjects( + config.loginTooltipText, + @"", + "Should use the given login tooltip text regardless of value" + ); } - (void)testCreatingWithKnownLoginTooltipText { - config = [Fixtures configWithDictionary:@{@"loginTooltipText": @"foo"}]; - XCTAssertEqual(config.loginTooltipText, @"foo", - "Login tooltip text should be settable"); + config = [Fixtures configWithDictionary:@{@"loginTooltipText" : @"foo"}]; + XCTAssertEqual( + config.loginTooltipText, + @"foo", + "Login tooltip text should be settable" + ); } - (void)testCreatingWithoutTimestamp @@ -226,73 +274,106 @@ - (void)testCreatingWithoutTimestamp - (void)testCreatingWithTimestamp { NSDate *date = [NSDate date]; - config = [Fixtures configWithDictionary:@{@"timestamp": date}]; - XCTAssertEqualObjects(config.timestamp, date, - "Should use the timestamp given during creation"); + config = [Fixtures configWithDictionary:@{@"timestamp" : date}]; + XCTAssertEqualObjects( + config.timestamp, + date, + "Should use the timestamp given during creation" + ); } - (void)testCreatingWithDefaultSessionTimeoutInterval { - XCTAssertEqual(config.sessionTimoutInterval, 60, - "Should set the correct default timeout interval"); + XCTAssertEqual( + config.sessionTimoutInterval, + 60, + "Should set the correct default timeout interval" + ); } - (void)testCreatingWithSessionTimeoutInterval { - config = [Fixtures configWithDictionary:@{@"sessionTimeoutInterval": @200}]; - XCTAssertEqual(config.sessionTimoutInterval, 200, - "Should set the session timeout interval from the remote"); + config = [Fixtures configWithDictionary:@{@"sessionTimeoutInterval" : @200}]; + XCTAssertEqual( + config.sessionTimoutInterval, + 200, + "Should set the session timeout interval from the remote" + ); } -- (void)testCreatingWithoutLoggingToken { - XCTAssertNil(config.loggingToken, - "Should not provide a default for the logging token"); +- (void)testCreatingWithoutLoggingToken +{ + XCTAssertNil( + config.loggingToken, + "Should not provide a default for the logging token" + ); } -- (void)testCreatingWithEmptyLoggingToken { - config = [Fixtures configWithDictionary:@{@"loggingToken": @""}]; - XCTAssertEqualObjects(config.loggingToken, @"", - "Should use the logging token given during creation"); +- (void)testCreatingWithEmptyLoggingToken +{ + config = [Fixtures configWithDictionary:@{@"loggingToken" : @""}]; + XCTAssertEqualObjects( + config.loggingToken, + @"", + "Should use the logging token given during creation" + ); } -- (void)testCreatingWithKnownLoggingToken { - config = [Fixtures configWithDictionary:@{@"loggingToken": @"foo"}]; - XCTAssertEqualObjects(config.loggingToken, @"foo", - "Should use the logging token given during creation"); +- (void)testCreatingWithKnownLoggingToken +{ + config = [Fixtures configWithDictionary:@{@"loggingToken" : @"foo"}]; + XCTAssertEqualObjects( + config.loggingToken, + @"foo", + "Should use the logging token given during creation" + ); } - (void)testCreatingWithoutSmartLoginBookmarkUrl { - XCTAssertNil(config.smartLoginBookmarkIconURL, - "Should not provide a default url for the smart login bookmark icon"); + XCTAssertNil( + config.smartLoginBookmarkIconURL, + "Should not provide a default url for the smart login bookmark icon" + ); } - (void)testCreatingWithInvalidSmartLoginBookmarkUrl { - config = [Fixtures configWithDictionary:@{@"smartLoginBookmarkIconURL": [NSURL URLWithString:@""]}]; - XCTAssertEqualObjects(config.smartLoginBookmarkIconURL, [NSURL URLWithString:@""], - "Should use the url given during creation"); + config = [Fixtures configWithDictionary:@{@"smartLoginBookmarkIconURL" : [NSURL URLWithString:@""]}]; + XCTAssertEqualObjects( + config.smartLoginBookmarkIconURL, + [NSURL URLWithString:@""], + "Should use the url given during creation" + ); } - (void)testCreatingWithValidSmartBookmarkUrl { - config = [Fixtures configWithDictionary:@{@"smartLoginBookmarkIconURL": [NSURL URLWithString:@"http://www.example.com"]}]; - XCTAssertEqualObjects(config.smartLoginBookmarkIconURL, [NSURL URLWithString:@"http://www.example.com"], - "Should use the url given during creation"); + config = [Fixtures configWithDictionary:@{@"smartLoginBookmarkIconURL" : [NSURL URLWithString:@"http://www.example.com"]}]; + XCTAssertEqualObjects( + config.smartLoginBookmarkIconURL, + [NSURL URLWithString:@"http://www.example.com"], + "Should use the url given during creation" + ); } - - (void)testCreatingWithSmartLoginOptionsDefault { - XCTAssertEqual(config.smartLoginOptions, FBSDKServerConfigurationSmartLoginOptionsUnknown, - "Should default smart login options to unknown"); + XCTAssertEqual( + config.smartLoginOptions, + FBSDKServerConfigurationSmartLoginOptionsUnknown, + "Should default smart login options to unknown" + ); } - (void)testCreatingWithSmartLoginOptionsEnabled { - config = [Fixtures configWithDictionary:@{@"smartLoginOptions": [NSNumber numberWithInt:FBSDKServerConfigurationSmartLoginOptionsEnabled]}]; - XCTAssertEqual(config.smartLoginOptions, FBSDKServerConfigurationSmartLoginOptionsEnabled, - "Should use the smartLoginOptions given during creation"); + config = [Fixtures configWithDictionary:@{@"smartLoginOptions" : [NSNumber numberWithInt:FBSDKServerConfigurationSmartLoginOptionsEnabled]}]; + XCTAssertEqual( + config.smartLoginOptions, + FBSDKServerConfigurationSmartLoginOptionsEnabled, + "Should use the smartLoginOptions given during creation" + ); } - (void)testCreatingWithoutErrorConfiguration @@ -303,258 +384,288 @@ - (void)testCreatingWithoutErrorConfiguration - (void)testCreatingWithErrorConfiguration { FBSDKErrorConfiguration *errorConfig = [[FBSDKErrorConfiguration alloc] initWithDictionary:nil]; - config = [Fixtures configWithDictionary:@{@"errorConfiguration": errorConfig}]; + config = [Fixtures configWithDictionary:@{@"errorConfiguration" : errorConfig}]; - XCTAssertEqualObjects(config.errorConfiguration, errorConfig, - @"Error configuration should be settable"); + XCTAssertEqualObjects( + config.errorConfiguration, + errorConfig, + @"Error configuration should be settable" + ); } - (void)testCreatingWithoutSmartLoginMenuUrl { - XCTAssertNil(config.smartLoginMenuIconURL, - "Should not provide a default url for the smart login menu icon"); + XCTAssertNil( + config.smartLoginMenuIconURL, + "Should not provide a default url for the smart login menu icon" + ); } - (void)testCreatingWithInvalidSmartLoginMenuUrl { - config = [Fixtures configWithDictionary:@{@"smartLoginMenuIconURL": [NSURL URLWithString:@""]}]; - XCTAssertEqualObjects(config.smartLoginMenuIconURL, [NSURL URLWithString:@""], - "Should use the url given during creation"); + config = [Fixtures configWithDictionary:@{@"smartLoginMenuIconURL" : [NSURL URLWithString:@""]}]; + XCTAssertEqualObjects( + config.smartLoginMenuIconURL, + [NSURL URLWithString:@""], + "Should use the url given during creation" + ); } - (void)testCreatingWithValidSmartLoginMenuUrl { - config = [Fixtures configWithDictionary:@{@"smartLoginMenuIconURL": [NSURL URLWithString:@"http://www.example.com"]}]; - XCTAssertEqualObjects(config.smartLoginMenuIconURL, [NSURL URLWithString:@"http://www.example.com"], - "Should use the url given during creation"); + config = [Fixtures configWithDictionary:@{@"smartLoginMenuIconURL" : [NSURL URLWithString:@"http://www.example.com"]}]; + XCTAssertEqualObjects( + config.smartLoginMenuIconURL, + [NSURL URLWithString:@"http://www.example.com"], + "Should use the url given during creation" + ); } - (void)testCreatingWithoutUpdateMessage { - XCTAssertNil(config.updateMessage, - "Should not provide a default for the update message"); + XCTAssertNil( + config.updateMessage, + "Should not provide a default for the update message" + ); } - (void)testCreatingWithEmptyUpdateMessage { - config = [Fixtures configWithDictionary:@{@"updateMessage": @""}]; - XCTAssertEqualObjects(config.updateMessage, @"", - "Should use the update message given during creation"); + config = [Fixtures configWithDictionary:@{@"updateMessage" : @""}]; + XCTAssertEqualObjects( + config.updateMessage, + @"", + "Should use the update message given during creation" + ); } - (void)testCreatingWithKnownUpdateMessage { - config = [Fixtures configWithDictionary:@{@"updateMessage": @"foo"}]; - XCTAssertEqualObjects(config.updateMessage, @"foo", - "Should use the update message given during creation"); + config = [Fixtures configWithDictionary:@{@"updateMessage" : @"foo"}]; + XCTAssertEqualObjects( + config.updateMessage, + @"foo", + "Should use the update message given during creation" + ); } - (void)testCreatingWithoutEventBindings { - XCTAssertNil(config.eventBindings, - "Should not provide default event bindings"); + XCTAssertNil( + config.eventBindings, + "Should not provide default event bindings" + ); } - (void)testCreatingWithEmptyEventBindings { - config = [Fixtures configWithDictionary:@{@"eventBindings": @[]}]; + config = [Fixtures configWithDictionary:@{@"eventBindings" : @[]}]; XCTAssertNotNil(config.eventBindings, "Should use the empty list of event bindings it was created with"); - XCTAssertEqual(config.eventBindings.count, 0, - "Should use the empty list of event bindings it was created with"); + XCTAssertEqual( + config.eventBindings.count, + 0, + "Should use the empty list of event bindings it was created with" + ); } - (void)testCreatingWithEventBindings { NSArray *bindings = @[[FBSDKEventBinding new]]; - config = [Fixtures configWithDictionary:@{@"eventBindings": bindings}]; + config = [Fixtures configWithDictionary:@{@"eventBindings" : bindings}]; - XCTAssertEqualObjects(config.eventBindings, bindings, - @"Event binding should be settable"); + XCTAssertEqualObjects( + config.eventBindings, + bindings, + @"Event binding should be settable" + ); } - (void)testCreatingWithoutDialogConfigurations { - XCTAssertNil(config.dialogConfigurations, - @"Should not have dialog configurations by default"); + XCTAssertNil( + config.dialogConfigurations, + @"Should not have dialog configurations by default" + ); } - (void)testCreatingWithDialogConfigurations { NSDictionary *dialogConfigurations = @{ - @"dialog": @"Hello", - @"dialog2": @"World" - }; - - config = [Fixtures configWithDictionary:@{@"dialogConfigurations": dialogConfigurations}]; - XCTAssertEqualObjects(config.dialogConfigurations, dialogConfigurations, - "Should set the exact dialog configurations it was created with"); -} - -- (void)testCreatingWithoutDialogFlowsBelowIosVersion9 -{ - NSOperatingSystemVersion iOS9Version = { .majorVersion = 9, .minorVersion = 0, .patchVersion = 0 }; - - id internalUtilityMock = OCMClassMock([FBSDKInternalUtility class]); - OCMStub([internalUtilityMock isOSRunTimeVersionAtLeast:iOS9Version]).andReturn(YES); - - // Need to recreate with a new appID to invalidate cache of default configuration - config = [FBSDKServerConfiguration defaultServerConfigurationForAppID:self.name]; - - NSDictionary *expectedDefaultDialogFlows = @{ - FBSDKDialogConfigurationNameDefault: @{ - FBSDKDialogConfigurationFeatureUseNativeFlow: @NO, - FBSDKDialogConfigurationFeatureUseSafariViewController: @YES, - }, - FBSDKDialogConfigurationNameMessage: @{ - FBSDKDialogConfigurationFeatureUseNativeFlow: @YES, - }, + @"dialog" : @"Hello", + @"dialog2" : @"World" }; - XCTAssertEqualObjects(config.dialogFlows, expectedDefaultDialogFlows); + config = [Fixtures configWithDictionary:@{@"dialogConfigurations" : dialogConfigurations}]; + XCTAssertEqualObjects( + config.dialogConfigurations, + dialogConfigurations, + "Should set the exact dialog configurations it was created with" + ); } -- (void)testCreatingWithoutDialogFlowsAboveIosVersion9 +- (void)testCreatingWithoutDialogFlows { - NSOperatingSystemVersion iOS9Version = { .majorVersion = 9, .minorVersion = 0, .patchVersion = 0 }; - - id internalUtilityMock = OCMClassMock([FBSDKInternalUtility class]); - OCMStub([internalUtilityMock isOSRunTimeVersionAtLeast:iOS9Version]).andReturn(NO); - // Need to recreate with a new appID to invalidate cache of default configuration config = [FBSDKServerConfiguration defaultServerConfigurationForAppID:self.name]; NSDictionary *expectedDefaultDialogFlows = @{ - FBSDKDialogConfigurationNameDefault: @{ - FBSDKDialogConfigurationFeatureUseNativeFlow: @YES, - FBSDKDialogConfigurationFeatureUseSafariViewController: @YES, + FBSDKDialogConfigurationNameDefault : @{ + FBSDKDialogConfigurationFeatureUseNativeFlow : @NO, + FBSDKDialogConfigurationFeatureUseSafariViewController : @YES, }, - FBSDKDialogConfigurationNameMessage: @{ - FBSDKDialogConfigurationFeatureUseNativeFlow: @YES, + FBSDKDialogConfigurationNameMessage : @{ + FBSDKDialogConfigurationFeatureUseNativeFlow : @YES, }, }; - XCTAssertEqualObjects(config.dialogFlows, expectedDefaultDialogFlows); + XCTAssertEqualObjects( + config.dialogFlows, + expectedDefaultDialogFlows, + "Should use the expected default dialog flow" + ); } - - (void)testCreatingWithDialogFlows { - NSDictionary *dialogFlows = @{ - @"foo": @{ - FBSDKDialogConfigurationFeatureUseNativeFlow: @YES, - FBSDKDialogConfigurationFeatureUseSafariViewController: @YES, + NSDictionary *dialogFlows = @{ + @"foo" : @{ + FBSDKDialogConfigurationFeatureUseNativeFlow : @YES, + FBSDKDialogConfigurationFeatureUseSafariViewController : @YES, }, - @"bar": @{ - FBSDKDialogConfigurationFeatureUseNativeFlow: @NO, + @"bar" : @{ + FBSDKDialogConfigurationFeatureUseNativeFlow : @NO, } }; - config = [Fixtures configWithDictionary:@{@"dialogFlows": dialogFlows}]; + config = [Fixtures configWithDictionary:@{@"dialogFlows" : dialogFlows}]; - XCTAssertEqualObjects(config.dialogFlows, dialogFlows, - "Should set the exact dialog flows it was created with"); + XCTAssertEqualObjects( + config.dialogFlows, + dialogFlows, + "Should set the exact dialog flows it was created with" + ); } - (void)testCreatingWithoutAAMRules { - XCTAssertNil(config.AAMRules, - @"Should not have aam rules by default"); + XCTAssertNil( + config.AAMRules, + @"Should not have aam rules by default" + ); } - (void)testCreatingWithAAMRules { - NSDictionary *rules = @{ @"foo": @"bar" }; + NSDictionary *rules = @{ @"foo" : @"bar" }; - config = [Fixtures configWithDictionary:@{@"aamRules": rules}]; + config = [Fixtures configWithDictionary:@{@"aamRules" : rules}]; - XCTAssertEqualObjects(config.AAMRules, rules, - "Should set the exact aam rules it was created with"); + XCTAssertEqualObjects( + config.AAMRules, + rules, + "Should set the exact aam rules it was created with" + ); } - (void)testCreatingWithoutRestrictiveParams { - XCTAssertNil(config.restrictiveParams, - @"Should not have restrictive params by default"); + XCTAssertNil( + config.restrictiveParams, + @"Should not have restrictive params by default" + ); } - (void)testCreatingWithRestrictiveParams { - NSDictionary *params = @{ @"foo": @"bar" }; + NSDictionary *params = @{ @"foo" : @"bar" }; - config = [Fixtures configWithDictionary:@{@"restrictiveParams": params}]; + config = [Fixtures configWithDictionary:@{@"restrictiveParams" : params}]; - XCTAssertEqualObjects(config.restrictiveParams, params, - "Should set the exact restrictive params it was created with"); + XCTAssertEqualObjects( + config.restrictiveParams, + params, + "Should set the exact restrictive params it was created with" + ); } - (void)testCreatingWithoutSuggestedEventSetting { - XCTAssertNil(config.suggestedEventsSetting, - @"Should not have a suggested events setting by default"); + XCTAssertNil( + config.suggestedEventsSetting, + @"Should not have a suggested events setting by default" + ); } - (void)testCreatingWithSuggestedEventSetting { - NSDictionary *setting = @{ @"foo": @"bar" }; + NSDictionary *setting = @{ @"foo" : @"bar" }; - config = [Fixtures configWithDictionary:@{@"suggestedEventsSetting": setting}]; + config = [Fixtures configWithDictionary:@{@"suggestedEventsSetting" : setting}]; - XCTAssertEqualObjects(config.suggestedEventsSetting, setting, - "Should set the exact suggested events setting it was created with"); + XCTAssertEqualObjects( + config.suggestedEventsSetting, + setting, + "Should set the exact suggested events setting it was created with" + ); } - (void)testCreatingWithoutMonitoringConfiguration { - XCTAssertEqualObjects(config.monitoringConfiguration.sampleRates, - FBSDKMonitoringConfiguration.defaultConfiguration.sampleRates, - @"Should use the default monitoring configuration if none is provided"); + XCTAssertEqualObjects( + config.monitoringConfiguration.sampleRates, + FBSDKMonitoringConfiguration.defaultConfiguration.sampleRates, + @"Should use the default monitoring configuration if none is provided" + ); } - (void)testCreatingWithMonitoringConfiguration { - NSDictionary *sampleRates = [MonitoringConfiguration sampleRatesWithEntryPairs: @{ @"foo": @1 }]; + NSDictionary *sampleRates = [MonitoringConfiguration sampleRatesWithEntryPairs:@{ @"foo" : @1 }]; FBSDKMonitoringConfiguration *monitoringConfiguration = [FBSDKMonitoringConfiguration fromDictionary:sampleRates]; - config = [Fixtures configWithDictionary:@{@"monitoringConfiguration": monitoringConfiguration}]; + config = [Fixtures configWithDictionary:@{@"monitoringConfiguration" : monitoringConfiguration}]; - XCTAssertEqualObjects(config.monitoringConfiguration, monitoringConfiguration, - "Should set the exact monitoring configuration it was created with"); + XCTAssertEqualObjects( + config.monitoringConfiguration, + monitoringConfiguration, + "Should set the exact monitoring configuration it was created with" + ); } - (void)testEncoding { FBSDKTestCoder *coder = [FBSDKTestCoder new]; FBSDKErrorConfiguration *errorConfig = [[FBSDKErrorConfiguration alloc] initWithDictionary:nil]; - NSDictionary *sampleRates = [MonitoringConfiguration sampleRatesWithEntryPairs: @{ @"foo": @1 }]; + NSDictionary *sampleRates = [MonitoringConfiguration sampleRatesWithEntryPairs:@{ @"foo" : @1 }]; FBSDKMonitoringConfiguration *monitoringConfiguration = [FBSDKMonitoringConfiguration fromDictionary:sampleRates]; config = [Fixtures configWithDictionary:@{ - @"appID": @"appID", - @"appName": @"appName", - @"loginTooltipEnabled": @YES, - @"loginTooltipText": @"loginTooltipText", - @"defaultShareMode": @"defaultShareMode", - @"advertisingIDEnabled": @YES, - @"implicitLoggingEnabled": @YES, - @"implicitPurchaseLoggingEnabled": @YES, - @"codelessEventsEnabled" : @YES, - @"uninstallTrackingEnabled" : @YES, - @"dialogFlows": @{@"Foo": @"Bar"}, - @"timestamp": [NSDate date], - @"errorConfiguration": errorConfig, - @"sessionTimeoutInterval": @100, - @"defaults": @NO, - @"loggingToken": @"loggingToken", - @"smartLoginOptions": [NSNumber numberWithInt: FBSDKServerConfigurationSmartLoginOptionsEnabled], - @"smartLoginBookmarkIconURL": [NSURL URLWithString:@"https://example.com"], - @"smartLoginMenuIconURL": [NSURL URLWithString:@"https://example.com"], - @"updateMessage": @"updateMessage", - @"eventBindings": @{ @"foo": @"bar" }, - @"restrictiveParams": @{ @"restrictiveParams": @"foo" }, - @"AAMRules": @{ @"AAMRules": @"foo" }, - @"suggestedEventsSetting": @{ @"suggestedEventsSetting": @"foo" }, - @"monitoringConfiguration": monitoringConfiguration - }]; + @"appID" : @"appID", + @"appName" : @"appName", + @"loginTooltipEnabled" : @YES, + @"loginTooltipText" : @"loginTooltipText", + @"defaultShareMode" : @"defaultShareMode", + @"advertisingIDEnabled" : @YES, + @"implicitLoggingEnabled" : @YES, + @"implicitPurchaseLoggingEnabled" : @YES, + @"codelessEventsEnabled" : @YES, + @"uninstallTrackingEnabled" : @YES, + @"dialogFlows" : @{@"Foo" : @"Bar"}, + @"timestamp" : [NSDate date], + @"errorConfiguration" : errorConfig, + @"sessionTimeoutInterval" : @100, + @"defaults" : @NO, + @"loggingToken" : @"loggingToken", + @"smartLoginOptions" : [NSNumber numberWithInt:FBSDKServerConfigurationSmartLoginOptionsEnabled], + @"smartLoginBookmarkIconURL" : [NSURL URLWithString:@"https://example.com"], + @"smartLoginMenuIconURL" : [NSURL URLWithString:@"https://example.com"], + @"updateMessage" : @"updateMessage", + @"eventBindings" : @{ @"foo" : @"bar" }, + @"restrictiveParams" : @{ @"restrictiveParams" : @"foo" }, + @"AAMRules" : @{ @"AAMRules" : @"foo" }, + @"suggestedEventsSetting" : @{ @"suggestedEventsSetting" : @"foo" }, + @"monitoringConfiguration" : monitoringConfiguration + }]; [config encodeWithCoder:coder]; @@ -572,8 +683,10 @@ - (void)testEncoding XCTAssertEqualObjects(coder.encodedObject[@"timestamp"], config.timestamp); XCTAssertEqualObjects(coder.encodedObject[@"errorConfigs"], config.errorConfiguration); XCTAssertEqual([coder.encodedObject[@"sessionTimeoutInterval"] intValue], config.sessionTimoutInterval); - XCTAssertNil(coder.encodedObject[@"defaults"], - @"Should not encode whether default values were used to create server configuration"); + XCTAssertNil( + coder.encodedObject[@"defaults"], + @"Should not encode whether default values were used to create server configuration" + ); XCTAssertEqualObjects(coder.encodedObject[@"loggingToken"], config.loggingToken); XCTAssertEqual([coder.encodedObject[@"smartLoginEnabled"] intValue], config.smartLoginOptions); XCTAssertEqualObjects(coder.encodedObject[@"smarstLoginBookmarkIconURL"], config.smartLoginBookmarkIconURL); @@ -599,30 +712,56 @@ - (void)testDecoding XCTAssertEqualObjects(decoder.decodedObject[@"appID"], NSString.class); XCTAssertEqualObjects(decoder.decodedObject[@"appName"], NSString.class); - XCTAssertEqualObjects(decoder.decodedObject[@"loginTooltipEnabled"], @"decodeBoolForKey", - @"Should decode loginTooltipEnabled as a BOOL"); + XCTAssertEqualObjects( + decoder.decodedObject[@"loginTooltipEnabled"], + @"decodeBoolForKey", + @"Should decode loginTooltipEnabled as a BOOL" + ); XCTAssertEqualObjects(decoder.decodedObject[@"loginTooltipText"], NSString.class); XCTAssertEqualObjects(decoder.decodedObject[@"defaultShareMode"], NSString.class); - XCTAssertEqualObjects(decoder.decodedObject[@"advertisingIDEnabled"], @"decodeBoolForKey", - @"Should decode advertisingIDEnabled as a BOOL"); - XCTAssertEqualObjects(decoder.decodedObject[@"implicitLoggingEnabled"], @"decodeBoolForKey", - @"Should decode implicitLoggingEnabled as a BOOL"); - XCTAssertEqualObjects(decoder.decodedObject[@"implicitPurchaseLoggingEnabled"], @"decodeBoolForKey", - @"Should decode implicitPurchaseLoggingEnabled as a BOOL"); - XCTAssertEqualObjects(decoder.decodedObject[@"codelessEventsEnabled"], @"decodeBoolForKey", - @"Should decode codelessEventsEnabled as a BOOL"); - XCTAssertEqualObjects(decoder.decodedObject[@"trackAppUninstallEnabled"], @"decodeBoolForKey", - @"Should decode trackAppUninstallEnabled as a BOOL"); + XCTAssertEqualObjects( + decoder.decodedObject[@"advertisingIDEnabled"], + @"decodeBoolForKey", + @"Should decode advertisingIDEnabled as a BOOL" + ); + XCTAssertEqualObjects( + decoder.decodedObject[@"implicitLoggingEnabled"], + @"decodeBoolForKey", + @"Should decode implicitLoggingEnabled as a BOOL" + ); + XCTAssertEqualObjects( + decoder.decodedObject[@"implicitPurchaseLoggingEnabled"], + @"decodeBoolForKey", + @"Should decode implicitPurchaseLoggingEnabled as a BOOL" + ); + XCTAssertEqualObjects( + decoder.decodedObject[@"codelessEventsEnabled"], + @"decodeBoolForKey", + @"Should decode codelessEventsEnabled as a BOOL" + ); + XCTAssertEqualObjects( + decoder.decodedObject[@"trackAppUninstallEnabled"], + @"decodeBoolForKey", + @"Should decode trackAppUninstallEnabled as a BOOL" + ); XCTAssertEqualObjects(decoder.decodedObject[@"dialogFlows"], dialogFlowsClasses); XCTAssertEqualObjects(decoder.decodedObject[@"timestamp"], NSDate.class); XCTAssertEqualObjects(decoder.decodedObject[@"errorConfigs"], FBSDKErrorConfiguration.class); - XCTAssertEqualObjects(decoder.decodedObject[@"sessionTimeoutInterval"], @"decodeDoubleForKey", - @"Should decode implicitLoggingEnabled as a double"); - XCTAssertNil(decoder.decodedObject[@"defaults"], - @"Should not encode whether default values were used to create server configuration"); + XCTAssertEqualObjects( + decoder.decodedObject[@"sessionTimeoutInterval"], + @"decodeDoubleForKey", + @"Should decode implicitLoggingEnabled as a double" + ); + XCTAssertNil( + decoder.decodedObject[@"defaults"], + @"Should not encode whether default values were used to create server configuration" + ); XCTAssertEqualObjects(decoder.decodedObject[@"loggingToken"], NSString.class); - XCTAssertEqualObjects(decoder.decodedObject[@"smartLoginEnabled"], @"decodeIntegerForKey", - @"Should decode smartLoginEnabled as an integer"); + XCTAssertEqualObjects( + decoder.decodedObject[@"smartLoginEnabled"], + @"decodeIntegerForKey", + @"Should decode smartLoginEnabled as an integer" + ); XCTAssertEqualObjects(decoder.decodedObject[@"smarstLoginBookmarkIconURL"], NSURL.class); XCTAssertEqualObjects(decoder.decodedObject[@"smarstLoginBookmarkMenuURL"], NSURL.class); XCTAssertEqualObjects(decoder.decodedObject[@"SDKUpdateMessage"], NSString.class); @@ -636,13 +775,16 @@ - (void)testDecoding - (void)testRetrievingInvalidDialogConfigurationForDialogName { config = [Fixtures configWithDictionary:@{ - @"dialogConfigurations": @{ - @"foo": @"bar" - } - }]; + @"dialogConfigurations" : @{ + @"foo" : @"bar" + } + }]; - XCTAssertEqualObjects([config dialogConfigurationForDialogName:@"foo"], @"bar", - @"Should be able to retrieve an invalid dialog configuration by name"); + XCTAssertEqualObjects( + [config dialogConfigurationForDialogName:@"foo"], + @"bar", + @"Should be able to retrieve an invalid dialog configuration by name" + ); } - (void)testRetrievingValidDialogConfigurationForDialogName @@ -652,13 +794,16 @@ - (void)testRetrievingValidDialogConfigurationForDialogName appVersions:nil]; config = [Fixtures configWithDictionary:@{ - @"dialogConfigurations": @{ - @"foo": fooConfig - } - }]; + @"dialogConfigurations" : @{ + @"foo" : fooConfig + } + }]; - XCTAssertEqualObjects([config dialogConfigurationForDialogName:@"foo"], fooConfig, - @"Should be able to retrieve a valid dialog configuration by name"); + XCTAssertEqualObjects( + [config dialogConfigurationForDialogName:@"foo"], + fooConfig, + @"Should be able to retrieve a valid dialog configuration by name" + ); } #pragma mark Native Dialog Checks @@ -1864,21 +2009,21 @@ - (void)setupConfigWithDialogFlowKey:(NSString *)flowKey NSMutableDictionary *dialogFlows = [NSMutableDictionary dictionary]; if (featureValue != nil) { dialogFlows[name] = @{ - flowKey: featureValue + flowKey : featureValue }; } if (sharingValue != nil) { dialogFlows[@"sharing"] = @{ - flowKey: sharingValue + flowKey : sharingValue }; } if (defaultValue != nil) { dialogFlows[@"default"] = @{ - flowKey: defaultValue + flowKey : defaultValue }; } - config = [Fixtures configWithDictionary:@{ @"dialogFlows": dialogFlows }]; + config = [Fixtures configWithDictionary:@{ @"dialogFlows" : dialogFlows }]; } - (void)assertSafariVcIs:(BOOL)expected @@ -1892,10 +2037,15 @@ - (void)assertSafariVcIs:(BOOL)expected withFeatureValue:featureValue sharingValue:sharingValue defaultValue:defaultValue]; - XCTAssertEqual([config useSafariViewControllerForDialogName:name], - expected, - "Use safari view controller for dialog name: %@, should return true when the feature value is: %@, the sharing value is: %@, and the default value is: %@", - name, featureValue, sharingValue, defaultValue); + XCTAssertEqual( + [config useSafariViewControllerForDialogName:name], + expected, + "Use safari view controller for dialog name: %@, should return true when the feature value is: %@, the sharing value is: %@, and the default value is: %@", + name, + featureValue, + sharingValue, + defaultValue + ); } - (void)assertNativeDialogIs:(BOOL)expected @@ -1910,11 +2060,15 @@ - (void)assertNativeDialogIs:(BOOL)expected sharingValue:sharingValue defaultValue:defaultValue]; - XCTAssertEqual([config useNativeDialogForDialogName:name], - expected, - "Use native dialog for dialog name: %@, should return true when the feature value is: %@, the sharing value is: %@, and the default value is: %@", - name, featureValue, sharingValue, defaultValue); - + XCTAssertEqual( + [config useNativeDialogForDialogName:name], + expected, + "Use native dialog for dialog name: %@, should return true when the feature value is: %@, the sharing value is: %@, and the default value is: %@", + name, + featureValue, + sharingValue, + defaultValue + ); } @end diff --git a/FBSDKCoreKit/FBSDKCoreKitTests/Internal/UI/FBSDKDrawableTests.swift b/FBSDKCoreKit/FBSDKCoreKitTests/Internal/UI/FBSDKDrawableTests.swift new file mode 100644 index 0000000000..cf377e13e4 --- /dev/null +++ b/FBSDKCoreKit/FBSDKCoreKitTests/Internal/UI/FBSDKDrawableTests.swift @@ -0,0 +1,164 @@ +// 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 XCTest + +class FBSDKDrawableTests: FBSDKTestCase { + let size = CGSize(width: 100, height: 100) + let placeholderImageColor = UIColor( + red: 157.0 / 255.0, + green: 177.0 / 255.0, + blue: 204.0 / 255.0, + alpha: 1.0 + ) + + func testBaseClassPathWithSize() { + XCTAssertNil(Icon().path(with: size)) + } + + func testDefaultScale() { + XCTAssertEqual( + HumanSilhouetteIcon().image(size: size).scale, + UIScreen.main.scale, + "Icons should default their scale to the scale of the main screen" + ) + } + + func testDefaultScaleWithColor() { + XCTAssertEqual( + HumanSilhouetteIcon().image(size: size, color: .red).scale, + UIScreen.main.scale, + "Scale should not be affected by the color" + ) + } + + func testCustomScale() { + XCTAssertEqual( + HumanSilhouetteIcon().image( + size: size, + scale: 2.0 + ).scale, + 2.0, + "Icons should accept a custom scale" + ) + } + + func testSystemColor() throws { + let potentialImage = HumanSilhouetteIcon().image( + size: size, + scale: 2.0, + color: .red + ) + + guard let image = potentialImage else { + return XCTFail("Should be able to create an image with a valid size") + } + + let redIcon = UIImage( + named: "redSilhouette.png", + in: Bundle(for: FBSDKTestCase.self), + compatibleWith: nil + ) + + XCTAssertEqual( + image.pngData(), + redIcon?.pngData(), + "Should create the expected image for the size and color" + ) + } + + // MARK: Human Silhouette Icon + + func testImageWithInvalidSize() { + XCTAssertNil(HumanSilhouetteIcon().image(size: .zero), + "An image must have a non-zero size") + } + + func testPlaceholderImageColor() { + let potentialImage = HumanSilhouetteIcon().image( + size: size, + scale: 2.0, + color: placeholderImageColor + ) + let customIcon = UIImage( + named: "customColorSilhouette.png", + in: Bundle(for: FBSDKTestCase.self), + compatibleWith: nil + ) + + guard let image = potentialImage else { + return XCTFail("Should be able to create an image with a valid size") + } + + XCTAssertEqual( + image.pngData(), + customIcon?.pngData(), + "Should create the expected image for the size and color" + ) + } + + // MARK: Logo Icon + + func testLogo() { + let potentialImage = FBLogo().image( + size: size, + scale: 2.0, + color: .red + ) + let storedImage = UIImage( + named: "redLogo.png", + in: Bundle(for: FBSDKTestCase.self), + compatibleWith: nil + ) + + guard let image = potentialImage else { + return XCTFail("Should be able to create an image with a valid size") + } + + XCTAssertEqual( + image.pngData(), + storedImage?.pngData(), + "Should create the expected image" + ) + } + + // MARK: Close Icon + + func testCloseIcon() { + guard let image = FBCloseIcon().image( + with: size, + primaryColor: .red, + secondaryColor: .green, + scale: 2.0 + ) else { + return XCTFail("Should be able to create an image with a valid size") + } + + let storedImage = UIImage( + named: "closeIcon.png", + in: Bundle(for: FBSDKTestCase.self), + compatibleWith: nil + ) + + XCTAssertEqual( + image.pngData(), + storedImage?.pngData(), + "Should create the expected image" + ) + } +} diff --git a/FBSDKCoreKit/FBSDKCoreKitTests/ProfilePictureViewTests.swift b/FBSDKCoreKit/FBSDKCoreKitTests/ProfilePictureViewTests.swift new file mode 100644 index 0000000000..626214f56c --- /dev/null +++ b/FBSDKCoreKit/FBSDKCoreKitTests/ProfilePictureViewTests.swift @@ -0,0 +1,267 @@ +// 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 FBSDKCoreKit +import XCTest + +class ProfilePictureView: XCTestCase { + var sampleProfile: Profile { + return Profile( + userID: "Sample ID", + firstName: nil, + middleName: nil, + lastName: nil, + name: "Sample Name", + linkURL: nil, + refreshDate: nil, + imageURL: URL(string:"https://www.facebook.com/"), + email:nil + ) + } + var sampleProfileWithoutImageURL: Profile { + return Profile( + userID: "Sample ID", + firstName: nil, + middleName: nil, + lastName: nil, + name: "Sample Name", + linkURL: nil, + refreshDate: nil + ) + } + + override func setUp() { + super.setUp() + + AccessToken.setCurrent(nil, shouldDispatchNotif: false) + Profile.setCurrent(nil, shouldPostNotification: false) + } + + // MARK: - Update Image + + func testImageUpdateWithoutAccessTokenWithProfile() { + let view = TestView() + AccessToken.setCurrent(nil, shouldDispatchNotif: false) + Profile.setCurrent(sampleProfile, shouldPostNotification: false) + + view._updateImage() + + XCTAssertEqual( + view.updateImageWithProfileCount, + 1, + "Should use the profile when there is no access token" + ) + XCTAssertEqual( + view.updateImageWithAccessTokenCount, + 0, + "Should not use the access token when there is no access token" + ) + } + + func testImageUpdateWithAccessTokenWithProfile() { + let view = TestView() + AccessToken.setCurrent(SampleAccessToken.validToken, shouldDispatchNotif: false) + Profile.setCurrent(sampleProfile, shouldPostNotification: false) + + view._updateImage() + + XCTAssertEqual( + view.updateImageWithProfileCount, + 0, + "Should not use the profile when there is an access token" + ) + XCTAssertEqual( + view.updateImageWithAccessTokenCount, + 1, + "Should use the access token when there is one available" + ) + } + + func testImageUpdateWithoutAccessTokenWithoutProfile() { + let view = TestView() + AccessToken.setCurrent(nil, shouldDispatchNotif: false) + Profile.setCurrent(nil, shouldPostNotification: false) + + view._updateImage() + + XCTAssertEqual( + view.updateImageWithProfileCount, + 0, + "Should not use the profile when there is no access token or current profile" + ) + XCTAssertEqual( + view.updateImageWithAccessTokenCount, + 0, + "Should not use the access token when there is no access token or current profile" + ) + } + + func testImageUpdateWithoutAccessTokenWithProfileNoImageURL() { + let view = TestView() + AccessToken.setCurrent(nil, shouldDispatchNotif: false) + Profile.setCurrent(sampleProfileWithoutImageURL, shouldPostNotification: false) + + view._updateImage() + + XCTAssertEqual( + view.updateImageWithProfileCount, + 0, + "Should not use the profile when there is no image url available" + ) + XCTAssertEqual( + view.updateImageWithAccessTokenCount, + 0, + "Should not use the access token when there is no access token" + ) + } + + // MARK: - Token Notifications + + func testReceivingAccessTokenNotificationWithDidChangeUserIdKey() { + let view = TestView() + let notification = Notification( + name: .AccessTokenDidChange, + object: nil, + userInfo: [AccessTokenDidChangeUserIDKey: "foo"] + ) + + view._accessTokenDidChange(notification) + + XCTAssertEqual( + view.updateImageWithAccessTokenCount, + 1, + "An access token notification with a changed user id key should trigger an image update" + ) + } + + + func testReceivingAccessTokenNotificationWithoutRelevantUserInfo() { + let view = TestView() + let notification = Notification( + name: .AccessTokenDidChange, + object: nil, + userInfo: nil + ) + + view._accessTokenDidChange(notification) + + XCTAssertEqual( + view.updateImageWithAccessTokenCount, + 0, + "An access token notification without relevant user info should not trigger an image update" + ) + } + + func testReceivingProfileNotification() { + let view = TestView() + Profile.setCurrent(sampleProfile, shouldPostNotification: false) + let notification = Notification( + name: .ProfileDidChange, + object: nil, + userInfo: nil + ) + + view._profileDidChange(notification) + + XCTAssertEqual( + view.updateImageWithProfileCount, + 1, + "An profile change should trigger an image update" + ) + } + + // MARK: - Updating Content + + func testUpdatinImageWithProfileWithImageURL() { + let view = TestView() + Profile.setCurrent(sampleProfile, shouldPostNotification: false) + + view._updateImageWithProfile() + + XCTAssertEqual( + view.fetchAndSetImageCount, + 1, + "Should try to fetch image for a profile that have an imageURL" + ) + XCTAssertNotNil(view.lastState(), "Should update state when profile has an imageURL"); + } + + func testUpdatinImageWithProfileWithoutImageURL() { + let view = TestView() + Profile.setCurrent(sampleProfileWithoutImageURL, shouldPostNotification: false) + + view._updateImageWithProfile() + + XCTAssertEqual( + view.fetchAndSetImageCount, + 0, + "Should try to fetch image for a profile that does not have an imageURL" + ) + XCTAssertNil(view.lastState(), "Should not update state when profile does not have an imageURL") + } + + func testUpdatingImageWithValidAccessToken() { + let view = TestView() + AccessToken.setCurrent(SampleAccessToken.validToken, shouldDispatchNotif: false) + + view._updateImageWithAccessToken() + + XCTAssertEqual( + view.fetchAndSetImageCount, + 1, + "Should try to fetch image for a valid access token" + ) + XCTAssertNotNil(view.lastState(), "Should update state when access token is valid"); + } + + func testUpdatingImageWithInvalidAccessToken() { + let view = TestView() + AccessToken.setCurrent(SampleAccessToken.expiredToken, shouldDispatchNotif: false) + + view._updateImageWithAccessToken() + + XCTAssertEqual( + view.fetchAndSetImageCount, + 0, + "Should not try to fetch image for an invalid access token" + ) + XCTAssertNil(view.lastState(), "Should not update state when access token is not valid") + } +} + +class TestView: FBProfilePictureView { + var updateImageWithAccessTokenCount = 0 + var updateImageWithProfileCount = 0 + var fetchAndSetImageCount = 0 + + override func _updateImageWithAccessToken() { + updateImageWithAccessTokenCount += 1 + + super._updateImageWithAccessToken() + } + + override func _updateImageWithProfile() { + updateImageWithProfileCount += 1 + + super._updateImageWithProfile() + } + + override func _fetchAndSetImage(with url:URL?, state:FBProfilePictureViewState) { + fetchAndSetImageCount += 1 + } +} diff --git a/FBSDKCoreKit/FBSDKCoreKitTests/TestAssets.xcassets/Contents.json b/FBSDKCoreKit/FBSDKCoreKitTests/TestAssets.xcassets/Contents.json new file mode 100644 index 0000000000..73c00596a7 --- /dev/null +++ b/FBSDKCoreKit/FBSDKCoreKitTests/TestAssets.xcassets/Contents.json @@ -0,0 +1,6 @@ +{ + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/FBSDKCoreKit/FBSDKCoreKitTests/TestAssets.xcassets/closeIcon.imageset/Contents.json b/FBSDKCoreKit/FBSDKCoreKitTests/TestAssets.xcassets/closeIcon.imageset/Contents.json new file mode 100644 index 0000000000..101b8da9a1 --- /dev/null +++ b/FBSDKCoreKit/FBSDKCoreKitTests/TestAssets.xcassets/closeIcon.imageset/Contents.json @@ -0,0 +1,21 @@ +{ + "images" : [ + { + "idiom" : "universal", + "scale" : "1x" + }, + { + "filename" : "closeIcon.png", + "idiom" : "universal", + "scale" : "2x" + }, + { + "idiom" : "universal", + "scale" : "3x" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/FBSDKCoreKit/FBSDKCoreKitTests/TestAssets.xcassets/closeIcon.imageset/closeIcon.png b/FBSDKCoreKit/FBSDKCoreKitTests/TestAssets.xcassets/closeIcon.imageset/closeIcon.png new file mode 100644 index 0000000000..7802500d99 Binary files /dev/null and b/FBSDKCoreKit/FBSDKCoreKitTests/TestAssets.xcassets/closeIcon.imageset/closeIcon.png differ diff --git a/FBSDKCoreKit/FBSDKCoreKitTests/TestAssets.xcassets/customColorSilhouette.imageset/Contents.json b/FBSDKCoreKit/FBSDKCoreKitTests/TestAssets.xcassets/customColorSilhouette.imageset/Contents.json new file mode 100644 index 0000000000..ea81a4b2ba --- /dev/null +++ b/FBSDKCoreKit/FBSDKCoreKitTests/TestAssets.xcassets/customColorSilhouette.imageset/Contents.json @@ -0,0 +1,21 @@ +{ + "images" : [ + { + "idiom" : "universal", + "scale" : "1x" + }, + { + "filename" : "customColorSilhouette.png", + "idiom" : "universal", + "scale" : "2x" + }, + { + "idiom" : "universal", + "scale" : "3x" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/FBSDKCoreKit/FBSDKCoreKitTests/TestAssets.xcassets/customColorSilhouette.imageset/customColorSilhouette.png b/FBSDKCoreKit/FBSDKCoreKitTests/TestAssets.xcassets/customColorSilhouette.imageset/customColorSilhouette.png new file mode 100644 index 0000000000..07e2056bbc Binary files /dev/null and b/FBSDKCoreKit/FBSDKCoreKitTests/TestAssets.xcassets/customColorSilhouette.imageset/customColorSilhouette.png differ diff --git a/FBSDKCoreKit/FBSDKCoreKitTests/TestAssets.xcassets/redLogo.imageset/Contents.json b/FBSDKCoreKit/FBSDKCoreKitTests/TestAssets.xcassets/redLogo.imageset/Contents.json new file mode 100644 index 0000000000..ffa2bf4f70 --- /dev/null +++ b/FBSDKCoreKit/FBSDKCoreKitTests/TestAssets.xcassets/redLogo.imageset/Contents.json @@ -0,0 +1,21 @@ +{ + "images" : [ + { + "idiom" : "universal", + "scale" : "1x" + }, + { + "filename" : "redLogo.png", + "idiom" : "universal", + "scale" : "2x" + }, + { + "idiom" : "universal", + "scale" : "3x" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/FBSDKCoreKit/FBSDKCoreKitTests/TestAssets.xcassets/redLogo.imageset/redLogo.png b/FBSDKCoreKit/FBSDKCoreKitTests/TestAssets.xcassets/redLogo.imageset/redLogo.png new file mode 100644 index 0000000000..e60ad08a16 Binary files /dev/null and b/FBSDKCoreKit/FBSDKCoreKitTests/TestAssets.xcassets/redLogo.imageset/redLogo.png differ diff --git a/FBSDKCoreKit/FBSDKCoreKitTests/TestAssets.xcassets/redSilhouette.imageset/Contents.json b/FBSDKCoreKit/FBSDKCoreKitTests/TestAssets.xcassets/redSilhouette.imageset/Contents.json new file mode 100644 index 0000000000..0d754a3f77 --- /dev/null +++ b/FBSDKCoreKit/FBSDKCoreKitTests/TestAssets.xcassets/redSilhouette.imageset/Contents.json @@ -0,0 +1,21 @@ +{ + "images" : [ + { + "idiom" : "universal", + "scale" : "1x" + }, + { + "filename" : "redSilhouette.png", + "idiom" : "universal", + "scale" : "2x" + }, + { + "idiom" : "universal", + "scale" : "3x" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/FBSDKCoreKit/FBSDKCoreKitTests/TestAssets.xcassets/redSilhouette.imageset/redSilhouette.png b/FBSDKCoreKit/FBSDKCoreKitTests/TestAssets.xcassets/redSilhouette.imageset/redSilhouette.png new file mode 100644 index 0000000000..e15f5badd8 Binary files /dev/null and b/FBSDKCoreKit/FBSDKCoreKitTests/TestAssets.xcassets/redSilhouette.imageset/redSilhouette.png differ diff --git a/FBSDKCoreKit/FBSDKCoreKitTests/TestAssets.xcassets/whiteSilhouette.imageset/Contents.json b/FBSDKCoreKit/FBSDKCoreKitTests/TestAssets.xcassets/whiteSilhouette.imageset/Contents.json new file mode 100644 index 0000000000..d009133f57 --- /dev/null +++ b/FBSDKCoreKit/FBSDKCoreKitTests/TestAssets.xcassets/whiteSilhouette.imageset/Contents.json @@ -0,0 +1,21 @@ +{ + "images" : [ + { + "idiom" : "universal", + "scale" : "1x" + }, + { + "filename" : "whiteSilhouette.png", + "idiom" : "universal", + "scale" : "2x" + }, + { + "idiom" : "universal", + "scale" : "3x" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/FBSDKCoreKit/FBSDKCoreKitTests/TestAssets.xcassets/whiteSilhouette.imageset/whiteSilhouette.png b/FBSDKCoreKit/FBSDKCoreKitTests/TestAssets.xcassets/whiteSilhouette.imageset/whiteSilhouette.png new file mode 100644 index 0000000000..ced10cb20c Binary files /dev/null and b/FBSDKCoreKit/FBSDKCoreKitTests/TestAssets.xcassets/whiteSilhouette.imageset/whiteSilhouette.png differ diff --git a/FBSDKGamingServicesKit.podspec b/FBSDKGamingServicesKit.podspec index 231fe54893..88e4d5ac0a 100644 --- a/FBSDKGamingServicesKit.podspec +++ b/FBSDKGamingServicesKit.podspec @@ -3,7 +3,7 @@ Pod::Spec.new do |s| s.name = 'FBSDKGamingServicesKit' - s.version = '7.1.1' + s.version = '8.2.0' s.summary = 'Official Facebook SDK for iOS to access Facebook Gaming Services' s.description = <<-DESC @@ -16,7 +16,7 @@ Pod::Spec.new do |s| s.author = 'Facebook' s.platform = :ios - s.ios.deployment_target = '8.0' + s.ios.deployment_target = '9.0' s.swift_version = '5.0' @@ -33,6 +33,6 @@ Pod::Spec.new do |s| s.source_files = 'FBSDKGamingServicesKit/FBSDKGamingServicesKit/**/*.{h,m}' s.public_header_files = 'FBSDKGamingServicesKit/FBSDKGamingServicesKit/*.{h}' - s.dependency 'FBSDKShareKit', "~> #{s.version}" + s.dependency 'FBSDKCoreKit', "~> #{s.version}" end diff --git a/FBSDKGamingServicesKit/FBSDKGamingServicesKit.xcodeproj/project.pbxproj b/FBSDKGamingServicesKit/FBSDKGamingServicesKit.xcodeproj/project.pbxproj index 577196a7d0..87733daf9d 100644 --- a/FBSDKGamingServicesKit/FBSDKGamingServicesKit.xcodeproj/project.pbxproj +++ b/FBSDKGamingServicesKit/FBSDKGamingServicesKit.xcodeproj/project.pbxproj @@ -39,6 +39,17 @@ 9FC20034247D8D170016A053 /* FBSDKShareKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 9FC2001D247D83400016A053 /* FBSDKShareKit.framework */; }; F4234613244A2D2D006C9836 /* FBSDKCoreKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = F4F7CA5123F49C110030A346 /* FBSDKCoreKit.framework */; }; F4234614244A2D2D006C9836 /* FBSDKCoreKit.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = F4F7CA5123F49C110030A346 /* FBSDKCoreKit.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; + F46A224524D9F14D005878A8 /* FBSDKGamingServicesKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = F4F7CA1823F4902B0030A346 /* FBSDKGamingServicesKit.framework */; }; + F46A225024D9F184005878A8 /* FBSDKGamingServicesKitTestUtility.m in Sources */ = {isa = PBXBuildFile; fileRef = F46A224B24D9F184005878A8 /* FBSDKGamingServicesKitTestUtility.m */; }; + F4DC057F2519499A0073B380 /* FBSDKCoreKitInternalImport.h in Headers */ = {isa = PBXBuildFile; fileRef = F4DC056E2519498C0073B380 /* FBSDKCoreKitInternalImport.h */; }; + F4DC05832519499B0073B380 /* FBSDKCoreKitInternalImport.h in Headers */ = {isa = PBXBuildFile; fileRef = F4DC056E2519498C0073B380 /* FBSDKCoreKitInternalImport.h */; }; + F4DE318124D9F4A700297C18 /* XCTest.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = F4DE318024D9F4A700297C18 /* XCTest.framework */; }; + F4DE31A324D9FA5A00297C18 /* FBSDKFriendFinderDialogTests.m in Sources */ = {isa = PBXBuildFile; fileRef = F46A224D24D9F184005878A8 /* FBSDKFriendFinderDialogTests.m */; }; + F4DE31A424D9FA6400297C18 /* FBSDKGamingImageUploaderTests.m in Sources */ = {isa = PBXBuildFile; fileRef = F46A224E24D9F184005878A8 /* FBSDKGamingImageUploaderTests.m */; }; + F4DE31A524D9FA6F00297C18 /* FBSDKGamingVideoUploaderTests.m in Sources */ = {isa = PBXBuildFile; fileRef = F46A224F24D9F184005878A8 /* FBSDKGamingVideoUploaderTests.m */; }; + F4DE31AD24D9FD6100297C18 /* FBSDKVideoUploader.m in Sources */ = {isa = PBXBuildFile; fileRef = F4DE31AC24D9FD6000297C18 /* FBSDKVideoUploader.m */; }; + F4DE31AE24D9FD6100297C18 /* FBSDKVideoUploader.m in Sources */ = {isa = PBXBuildFile; fileRef = F4DE31AC24D9FD6000297C18 /* FBSDKVideoUploader.m */; }; + F4DE31D024DA0DD400297C18 /* OCMock.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = F4DE31CF24DA0DD400297C18 /* OCMock.framework */; }; F4F7C9E323F48F2C0030A346 /* FBSDKGamingImageUploaderConfiguration.m in Sources */ = {isa = PBXBuildFile; fileRef = F4F7C9B623F48F2C0030A346 /* FBSDKGamingImageUploaderConfiguration.m */; }; F4F7C9E423F48F2C0030A346 /* FBSDKGamingServiceCompletionHandler.h in Headers */ = {isa = PBXBuildFile; fileRef = F4F7C9B723F48F2C0030A346 /* FBSDKGamingServiceCompletionHandler.h */; settings = {ATTRIBUTES = (Public, ); }; }; F4F7C9E523F48F2C0030A346 /* FBSDKFriendFinderDialog.m in Sources */ = {isa = PBXBuildFile; fileRef = F4F7C9B823F48F2C0030A346 /* FBSDKFriendFinderDialog.m */; }; @@ -61,7 +72,6 @@ F4F7CA7723F49D6F0030A346 /* FBSDKGamingImageUploader.m in Sources */ = {isa = PBXBuildFile; fileRef = F4F7C9C223F48F2C0030A346 /* FBSDKGamingImageUploader.m */; }; F4F7CA7823F49D6F0030A346 /* FBSDKGamingImageUploaderConfiguration.m in Sources */ = {isa = PBXBuildFile; fileRef = F4F7C9B623F48F2C0030A346 /* FBSDKGamingImageUploaderConfiguration.m */; }; F4F7CA7923F49D6F0030A346 /* FBSDKGamingServiceController.m in Sources */ = {isa = PBXBuildFile; fileRef = F4F7C9BA23F48F2C0030A346 /* FBSDKGamingServiceController.m */; }; - F4F7CAA223F4A1390030A346 /* UIKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = F4F7CAA123F4A1390030A346 /* UIKit.framework */; }; F4F7CAA323F4A1470030A346 /* UIKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = F4F7CAA123F4A1390030A346 /* UIKit.framework */; }; /* End PBXBuildFile section */ @@ -115,6 +125,27 @@ remoteGlobalIDString = 81A07EDF1D1A2E6A0041A29C; remoteInfo = "FBSDKShareKit-Dynamic"; }; + F46A225424D9F1C7005878A8 /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = F4F7C9A123F48ED80030A346 /* Project object */; + proxyType = 1; + remoteGlobalIDString = F4F7CA6A23F49D6F0030A346; + remoteInfo = "FBSDKGamingServicesKit-Dynamic"; + }; + F4DE319F24D9F9FC00297C18 /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = 9FC20012247D83400016A053 /* FBSDKShareKit.xcodeproj */; + proxyType = 1; + remoteGlobalIDString = 81A07EDF1D1A2E6A0041A29C; + remoteInfo = "FBSDKShareKit-Dynamic"; + }; + F4DE31A124D9FA0100297C18 /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = F4F7CA3D23F49C110030A346 /* FBSDKCoreKit.xcodeproj */; + proxyType = 1; + remoteGlobalIDString = 81B71CFC1D19C87400933E93; + remoteInfo = "FBSDKCoreKit-Dynamic"; + }; F4F7CA2823F497410030A346 /* PBXContainerItemProxy */ = { isa = PBXContainerItemProxy; containerPortal = F4F7C9A123F48ED80030A346 /* Project object */; @@ -223,8 +254,19 @@ 9FC2000C247D82A00016A053 /* FBSDKGamingVideoUploader.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = FBSDKGamingVideoUploader.h; sourceTree = ""; }; 9FC2000D247D82A00016A053 /* FBSDKGamingVideoUploaderConfiguration.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = FBSDKGamingVideoUploaderConfiguration.h; sourceTree = ""; }; 9FC20012247D83400016A053 /* FBSDKShareKit.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = FBSDKShareKit.xcodeproj; path = ../FBSDKShareKit/FBSDKShareKit.xcodeproj; sourceTree = ""; }; - 9FC2002C247D8ACF0016A053 /* FBSDKVideoUploader.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = FBSDKVideoUploader.h; path = ../../../FBSDKShareKit/FBSDKShareKit/Internal/FBSDKVideoUploader.h; sourceTree = ""; }; + 9FC2002C247D8ACF0016A053 /* FBSDKVideoUploader.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = FBSDKVideoUploader.h; sourceTree = ""; }; + F46A224024D9F14D005878A8 /* FBSDKGamingServicesKitTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = FBSDKGamingServicesKitTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; + F46A224424D9F14D005878A8 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; + F46A224B24D9F184005878A8 /* FBSDKGamingServicesKitTestUtility.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = FBSDKGamingServicesKitTestUtility.m; sourceTree = ""; }; + F46A224C24D9F184005878A8 /* FBSDKGamingServicesKitTestUtility.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = FBSDKGamingServicesKitTestUtility.h; sourceTree = ""; }; + F46A224D24D9F184005878A8 /* FBSDKFriendFinderDialogTests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = FBSDKFriendFinderDialogTests.m; sourceTree = ""; }; + F46A224E24D9F184005878A8 /* FBSDKGamingImageUploaderTests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = FBSDKGamingImageUploaderTests.m; sourceTree = ""; }; + F46A224F24D9F184005878A8 /* FBSDKGamingVideoUploaderTests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = FBSDKGamingVideoUploaderTests.m; sourceTree = ""; }; F4A54C8A244619630063015F /* FBSDKGamingServicesKit-Common.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = "FBSDKGamingServicesKit-Common.xcconfig"; sourceTree = ""; }; + F4DC056E2519498C0073B380 /* FBSDKCoreKitInternalImport.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = FBSDKCoreKitInternalImport.h; sourceTree = ""; }; + F4DE318024D9F4A700297C18 /* XCTest.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = XCTest.framework; path = Platforms/iPhoneOS.platform/Developer/Library/Frameworks/XCTest.framework; sourceTree = DEVELOPER_DIR; }; + F4DE31AC24D9FD6000297C18 /* FBSDKVideoUploader.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = FBSDKVideoUploader.m; sourceTree = ""; }; + F4DE31CF24DA0DD400297C18 /* OCMock.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = OCMock.framework; path = ../Carthage/Build/iOS/OCMock.framework; sourceTree = ""; }; F4F7C9B623F48F2C0030A346 /* FBSDKGamingImageUploaderConfiguration.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = FBSDKGamingImageUploaderConfiguration.m; sourceTree = ""; }; F4F7C9B723F48F2C0030A346 /* FBSDKGamingServiceCompletionHandler.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = FBSDKGamingServiceCompletionHandler.h; sourceTree = ""; }; F4F7C9B823F48F2C0030A346 /* FBSDKFriendFinderDialog.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = FBSDKFriendFinderDialog.m; sourceTree = ""; }; @@ -261,22 +303,29 @@ F4F7C9DE23F48F2C0030A346 /* FacebookSDK-Project-TVOS-Debug.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; path = "FacebookSDK-Project-TVOS-Debug.xcconfig"; sourceTree = ""; }; F4F7C9DF23F48F2C0030A346 /* FBSDKGamingServicesKit.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; path = FBSDKGamingServicesKit.xcconfig; sourceTree = ""; }; F4F7C9E023F48F2C0030A346 /* FBSDKGamingServicesKit-Dynamic.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; path = "FBSDKGamingServicesKit-Dynamic.xcconfig"; sourceTree = ""; }; - F4F7C9E223F48F2C0030A346 /* Info.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; F4F7CA1823F4902B0030A346 /* FBSDKGamingServicesKit.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = FBSDKGamingServicesKit.framework; sourceTree = BUILT_PRODUCTS_DIR; }; F4F7CA3523F499600030A346 /* FBSDKCoreKit+Internal.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = "FBSDKCoreKit+Internal.h"; path = "../../../FBSDKCoreKit/FBSDKCoreKit/Internal/FBSDKCoreKit+Internal.h"; sourceTree = ""; }; F4F7CA3D23F49C110030A346 /* FBSDKCoreKit.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = FBSDKCoreKit.xcodeproj; path = ../FBSDKCoreKit/FBSDKCoreKit.xcodeproj; sourceTree = ""; }; F4F7CA6823F49D1B0030A346 /* Accelerate.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Accelerate.framework; path = System/Library/Frameworks/Accelerate.framework; sourceTree = SDKROOT; }; F4F7CA9923F49D6F0030A346 /* FBSDKGamingServicesKit.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = FBSDKGamingServicesKit.framework; sourceTree = BUILT_PRODUCTS_DIR; }; - F4F7CA9A23F49D6F0030A346 /* FBSDKGamingServicesKit copy-Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; name = "FBSDKGamingServicesKit copy-Info.plist"; path = "/Users/joesusnick/fbsource/fbobjc/ios-sdk/FBSDKGamingServicesKit/FBSDKGamingServicesKit copy-Info.plist"; sourceTree = ""; }; F4F7CAA123F4A1390030A346 /* UIKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = UIKit.framework; path = System/Library/Frameworks/UIKit.framework; sourceTree = SDKROOT; }; /* End PBXFileReference section */ /* Begin PBXFrameworksBuildPhase section */ + F46A223D24D9F14D005878A8 /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + F4DE318124D9F4A700297C18 /* XCTest.framework in Frameworks */, + F4DE31D024DA0DD400297C18 /* OCMock.framework in Frameworks */, + F46A224524D9F14D005878A8 /* FBSDKGamingServicesKit.framework in Frameworks */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; F4F7C9A723F48ED80030A346 /* Frameworks */ = { isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; files = ( - F4F7CAA223F4A1390030A346 /* UIKit.framework in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -305,16 +354,29 @@ name = Products; sourceTree = ""; }; + F46A224124D9F14D005878A8 /* FBSDKGamingServicesKitTests */ = { + isa = PBXGroup; + children = ( + F46A224D24D9F184005878A8 /* FBSDKFriendFinderDialogTests.m */, + F46A224E24D9F184005878A8 /* FBSDKGamingImageUploaderTests.m */, + F46A224C24D9F184005878A8 /* FBSDKGamingServicesKitTestUtility.h */, + F46A224B24D9F184005878A8 /* FBSDKGamingServicesKitTestUtility.m */, + F46A224F24D9F184005878A8 /* FBSDKGamingVideoUploaderTests.m */, + F46A224424D9F14D005878A8 /* Info.plist */, + ); + path = FBSDKGamingServicesKitTests; + sourceTree = ""; + }; F4F7C9A023F48ED80030A346 = { isa = PBXGroup; children = ( F4F7C9C323F48F2C0030A346 /* Configurations */, F4F7C9B523F48F2C0030A346 /* FBSDKGamingServicesKit */, - F4F7C9E123F48F2C0030A346 /* FBSDKGamingServicesKitTests */, F4F7CA1823F4902B0030A346 /* FBSDKGamingServicesKit.framework */, + F46A224124D9F14D005878A8 /* FBSDKGamingServicesKitTests */, F4F7CA3223F498ED0030A346 /* Frameworks */, F4F7CA9923F49D6F0030A346 /* FBSDKGamingServicesKit.framework */, - F4F7CA9A23F49D6F0030A346 /* FBSDKGamingServicesKit copy-Info.plist */, + F46A224024D9F14D005878A8 /* FBSDKGamingServicesKitTests.xctest */, ); sourceTree = ""; }; @@ -345,8 +407,10 @@ F4F7C9B923F48F2C0030A346 /* Internal */ = { isa = PBXGroup; children = ( + F4DE31AC24D9FD6000297C18 /* FBSDKVideoUploader.m */, 9FC2002C247D8ACF0016A053 /* FBSDKVideoUploader.h */, F4F7CA3523F499600030A346 /* FBSDKCoreKit+Internal.h */, + F4DC056E2519498C0073B380 /* FBSDKCoreKitInternalImport.h */, F4F7C9BA23F48F2C0030A346 /* FBSDKGamingServiceController.m */, F4F7C9BB23F48F2C0030A346 /* FBSDKGamingServiceController.h */, ); @@ -417,21 +481,15 @@ path = Platform; sourceTree = ""; }; - F4F7C9E123F48F2C0030A346 /* FBSDKGamingServicesKitTests */ = { - isa = PBXGroup; - children = ( - F4F7C9E223F48F2C0030A346 /* Info.plist */, - ); - path = FBSDKGamingServicesKitTests; - sourceTree = ""; - }; F4F7CA3223F498ED0030A346 /* Frameworks */ = { isa = PBXGroup; children = ( - 9FC20012247D83400016A053 /* FBSDKShareKit.xcodeproj */, - F4F7CAA123F4A1390030A346 /* UIKit.framework */, + F4DE31CF24DA0DD400297C18 /* OCMock.framework */, F4F7CA6823F49D1B0030A346 /* Accelerate.framework */, F4F7CA3D23F49C110030A346 /* FBSDKCoreKit.xcodeproj */, + 9FC20012247D83400016A053 /* FBSDKShareKit.xcodeproj */, + F4F7CAA123F4A1390030A346 /* UIKit.framework */, + F4DE318024D9F4A700297C18 /* XCTest.framework */, ); name = Frameworks; sourceTree = ""; @@ -468,6 +526,7 @@ F4F7C9EA23F48F2C0030A346 /* FBSDKFriendFinderDialog.h in Headers */, F4F7C9E423F48F2C0030A346 /* FBSDKGamingServiceCompletionHandler.h in Headers */, F4F7C9E823F48F2C0030A346 /* FBSDKGamingImageUploader.h in Headers */, + F4DC057F2519499A0073B380 /* FBSDKCoreKitInternalImport.h in Headers */, F4F7C9EB23F48F2C0030A346 /* FBSDKGamingServicesKit.h in Headers */, F4F7CA3623F499600030A346 /* FBSDKCoreKit+Internal.h in Headers */, ); @@ -486,6 +545,7 @@ F4F7CA7123F49D6F0030A346 /* FBSDKGamingServiceCompletionHandler.h in Headers */, F4F7CA7223F49D6F0030A346 /* FBSDKGamingImageUploader.h in Headers */, F4F7CA7323F49D6F0030A346 /* FBSDKGamingServicesKit.h in Headers */, + F4DC05832519499B0073B380 /* FBSDKCoreKitInternalImport.h in Headers */, F4F7CA7423F49D6F0030A346 /* FBSDKCoreKit+Internal.h in Headers */, 9FC2002E247D8ACF0016A053 /* FBSDKVideoUploader.h in Headers */, ); @@ -494,6 +554,26 @@ /* End PBXHeadersBuildPhase section */ /* Begin PBXNativeTarget section */ + F46A223F24D9F14D005878A8 /* FBSDKGamingServicesKitTests */ = { + isa = PBXNativeTarget; + buildConfigurationList = F46A224824D9F14D005878A8 /* Build configuration list for PBXNativeTarget "FBSDKGamingServicesKitTests" */; + buildPhases = ( + F46A223C24D9F14D005878A8 /* Sources */, + F46A223D24D9F14D005878A8 /* Frameworks */, + F46A223E24D9F14D005878A8 /* Resources */, + ); + buildRules = ( + ); + dependencies = ( + F4DE31A224D9FA0100297C18 /* PBXTargetDependency */, + F4DE31A024D9F9FC00297C18 /* PBXTargetDependency */, + F46A225524D9F1C7005878A8 /* PBXTargetDependency */, + ); + name = FBSDKGamingServicesKitTests; + productName = FBSDKGamingServicesKitTests; + productReference = F46A224024D9F14D005878A8 /* FBSDKGamingServicesKitTests.xctest */; + productType = "com.apple.product-type.bundle.unit-test"; + }; F4F7C9A923F48ED80030A346 /* FBSDKGamingServicesKit */ = { isa = PBXNativeTarget; buildConfigurationList = F4F7C9B223F48ED80030A346 /* Build configuration list for PBXNativeTarget "FBSDKGamingServicesKit" */; @@ -542,9 +622,13 @@ F4F7C9A123F48ED80030A346 /* Project object */ = { isa = PBXProject; attributes = { + LastSwiftUpdateCheck = 1160; LastUpgradeCheck = 1140; ORGANIZATIONNAME = facebook; TargetAttributes = { + F46A223F24D9F14D005878A8 = { + CreatedOnToolsVersion = 11.6; + }; F4F7C9A923F48ED80030A346 = { CreatedOnToolsVersion = 10.1; }; @@ -579,6 +663,7 @@ F4F7C9A923F48ED80030A346 /* FBSDKGamingServicesKit */, F4F7CA6A23F49D6F0030A346 /* FBSDKGamingServicesKit-Dynamic */, F4F7CA2323F497200030A346 /* FBSDKGamingServicesKit-Universal */, + F46A223F24D9F14D005878A8 /* FBSDKGamingServicesKitTests */, ); }; /* End PBXProject section */ @@ -685,6 +770,13 @@ /* End PBXReferenceProxy section */ /* Begin PBXResourcesBuildPhase section */ + F46A223E24D9F14D005878A8 /* Resources */ = { + isa = PBXResourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; F4F7C9A823F48ED80030A346 /* Resources */ = { isa = PBXResourcesBuildPhase; buildActionMask = 2147483647; @@ -741,6 +833,17 @@ /* End PBXShellScriptBuildPhase section */ /* Begin PBXSourcesBuildPhase section */ + F46A223C24D9F14D005878A8 /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + F4DE31A324D9FA5A00297C18 /* FBSDKFriendFinderDialogTests.m in Sources */, + F4DE31A524D9FA6F00297C18 /* FBSDKGamingVideoUploaderTests.m in Sources */, + F46A225024D9F184005878A8 /* FBSDKGamingServicesKitTestUtility.m in Sources */, + F4DE31A424D9FA6400297C18 /* FBSDKGamingImageUploaderTests.m in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; F4F7C9A623F48ED80030A346 /* Sources */ = { isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; @@ -749,6 +852,7 @@ F4F7C9ED23F48F2C0030A346 /* FBSDKGamingImageUploader.m in Sources */, 9FC2000E247D82A00016A053 /* FBSDKGamingVideoUploaderConfiguration.m in Sources */, F4F7C9E323F48F2C0030A346 /* FBSDKGamingImageUploaderConfiguration.m in Sources */, + F4DE31AD24D9FD6100297C18 /* FBSDKVideoUploader.m in Sources */, 9FB51BA024B8BD4700059B77 /* FBSDKGamingGroupIntegration.m in Sources */, F4F7C9E623F48F2C0030A346 /* FBSDKGamingServiceController.m in Sources */, 9FC2000F247D82A00016A053 /* FBSDKGamingVideoUploader.m in Sources */, @@ -763,6 +867,7 @@ 9FC2002B247D83FF0016A053 /* FBSDKGamingVideoUploaderConfiguration.m in Sources */, F4F7CA7623F49D6F0030A346 /* FBSDKFriendFinderDialog.m in Sources */, F4F7CA7723F49D6F0030A346 /* FBSDKGamingImageUploader.m in Sources */, + F4DE31AE24D9FD6100297C18 /* FBSDKVideoUploader.m in Sources */, 9FB51BA324B8BD6100059B77 /* FBSDKGamingGroupIntegration.m in Sources */, F4F7CA7823F49D6F0030A346 /* FBSDKGamingImageUploaderConfiguration.m in Sources */, F4F7CA7923F49D6F0030A346 /* FBSDKGamingServiceController.m in Sources */, @@ -782,6 +887,21 @@ name = "FBSDKShareKit-Dynamic"; targetProxy = 9FC20028247D834D0016A053 /* PBXContainerItemProxy */; }; + F46A225524D9F1C7005878A8 /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + target = F4F7CA6A23F49D6F0030A346 /* FBSDKGamingServicesKit-Dynamic */; + targetProxy = F46A225424D9F1C7005878A8 /* PBXContainerItemProxy */; + }; + F4DE31A024D9F9FC00297C18 /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + name = "FBSDKShareKit-Dynamic"; + targetProxy = F4DE319F24D9F9FC00297C18 /* PBXContainerItemProxy */; + }; + F4DE31A224D9FA0100297C18 /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + name = "FBSDKCoreKit-Dynamic"; + targetProxy = F4DE31A124D9FA0100297C18 /* PBXContainerItemProxy */; + }; F4F7CA2923F497410030A346 /* PBXTargetDependency */ = { isa = PBXTargetDependency; target = F4F7C9A923F48ED80030A346 /* FBSDKGamingServicesKit */; @@ -800,6 +920,136 @@ /* End PBXTargetDependency section */ /* Begin XCBuildConfiguration section */ + F46A224924D9F14D005878A8 /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = YES; + CLANG_ANALYZER_NONNULL = YES; + CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; + CLANG_ENABLE_MODULES = YES; + CLANG_ENABLE_OBJC_ARC = YES; + CLANG_ENABLE_OBJC_WEAK = YES; + CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; + CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_COMMA = YES; + CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + CLANG_WARN_DOCUMENTATION_COMMENTS = YES; + CLANG_WARN_EMPTY_BODY = YES; + CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INFINITE_RECURSION = YES; + CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; + CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; + CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; + CLANG_WARN_STRICT_PROTOTYPES = YES; + CLANG_WARN_SUSPICIOUS_MOVE = YES; + CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; + CLANG_WARN_UNREACHABLE_CODE = YES; + CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; + CODE_SIGN_STYLE = Automatic; + ENABLE_STRICT_OBJC_MSGSEND = YES; + ENABLE_TESTABILITY = YES; + GCC_C_LANGUAGE_STANDARD = gnu11; + GCC_DYNAMIC_NO_PIC = NO; + GCC_NO_COMMON_BLOCKS = YES; + GCC_OPTIMIZATION_LEVEL = 0; + GCC_PREPROCESSOR_DEFINITIONS = ( + "DEBUG=1", + "$(inherited)", + ); + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; + GCC_WARN_UNDECLARED_SELECTOR = YES; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + INFOPLIST_FILE = FBSDKGamingServicesKitTests/Info.plist; + IPHONEOS_DEPLOYMENT_TARGET = 13.6; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + "@loader_path/Frameworks", + ); + MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE; + MTL_FAST_MATH = YES; + ONLY_ACTIVE_ARCH = YES; + OTHER_LDFLAGS = "-ObjC"; + PRODUCT_BUNDLE_IDENTIFIER = com.facebook.FBSDKGamingServicesKitTests; + PRODUCT_NAME = "$(TARGET_NAME)"; + SDKROOT = iphoneos; + SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG; + SWIFT_OPTIMIZATION_LEVEL = "-Onone"; + SWIFT_VERSION = 5.0; + TARGETED_DEVICE_FAMILY = "1,2"; + }; + name = Debug; + }; + F46A224A24D9F14D005878A8 /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = YES; + CLANG_ANALYZER_NONNULL = YES; + CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; + CLANG_ENABLE_MODULES = YES; + CLANG_ENABLE_OBJC_ARC = YES; + CLANG_ENABLE_OBJC_WEAK = YES; + CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; + CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_COMMA = YES; + CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + CLANG_WARN_DOCUMENTATION_COMMENTS = YES; + CLANG_WARN_EMPTY_BODY = YES; + CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INFINITE_RECURSION = YES; + CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; + CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; + CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; + CLANG_WARN_STRICT_PROTOTYPES = YES; + CLANG_WARN_SUSPICIOUS_MOVE = YES; + CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; + CLANG_WARN_UNREACHABLE_CODE = YES; + CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; + CODE_SIGN_STYLE = Automatic; + ENABLE_STRICT_OBJC_MSGSEND = YES; + GCC_C_LANGUAGE_STANDARD = gnu11; + GCC_NO_COMMON_BLOCKS = YES; + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; + GCC_WARN_UNDECLARED_SELECTOR = YES; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + INFOPLIST_FILE = FBSDKGamingServicesKitTests/Info.plist; + IPHONEOS_DEPLOYMENT_TARGET = 13.6; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + "@loader_path/Frameworks", + ); + MTL_ENABLE_DEBUG_INFO = NO; + MTL_FAST_MATH = YES; + OTHER_LDFLAGS = "-ObjC"; + PRODUCT_BUNDLE_IDENTIFIER = com.facebook.FBSDKGamingServicesKitTests; + PRODUCT_NAME = "$(TARGET_NAME)"; + SDKROOT = iphoneos; + SWIFT_COMPILATION_MODE = wholemodule; + SWIFT_OPTIMIZATION_LEVEL = "-O"; + SWIFT_VERSION = 5.0; + TARGETED_DEVICE_FAMILY = "1,2"; + }; + name = Release; + }; F4F7C9B023F48ED80030A346 /* Debug */ = { isa = XCBuildConfiguration; baseConfigurationReference = F4F7C9C623F48F2C0030A346 /* Debug.xcconfig */; @@ -913,6 +1163,15 @@ /* End XCBuildConfiguration section */ /* Begin XCConfigurationList section */ + F46A224824D9F14D005878A8 /* Build configuration list for PBXNativeTarget "FBSDKGamingServicesKitTests" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + F46A224924D9F14D005878A8 /* Debug */, + F46A224A24D9F14D005878A8 /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; F4F7C9A423F48ED80030A346 /* Build configuration list for PBXProject "FBSDKGamingServicesKit" */ = { isa = XCConfigurationList; buildConfigurations = ( diff --git a/FBSDKGamingServicesKit/FBSDKGamingServicesKit.xcodeproj/xcshareddata/xcschemes/FBSDKGamingServicesKit-Dynamic.xcscheme b/FBSDKGamingServicesKit/FBSDKGamingServicesKit.xcodeproj/xcshareddata/xcschemes/FBSDKGamingServicesKit-Dynamic.xcscheme index 5910dc9ad1..4e36fb9a2e 100644 --- a/FBSDKGamingServicesKit/FBSDKGamingServicesKit.xcodeproj/xcshareddata/xcschemes/FBSDKGamingServicesKit-Dynamic.xcscheme +++ b/FBSDKGamingServicesKit/FBSDKGamingServicesKit.xcodeproj/xcshareddata/xcschemes/FBSDKGamingServicesKit-Dynamic.xcscheme @@ -26,8 +26,19 @@ buildConfiguration = "Debug" selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB" selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB" - shouldUseLaunchSchemeArgsEnv = "YES"> + shouldUseLaunchSchemeArgsEnv = "YES" + codeCoverageEnabled = "YES"> + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/FBSDKGamingServicesKit/FBSDKGamingServicesKit/FBSDKFriendFinderDialog.m b/FBSDKGamingServicesKit/FBSDKGamingServicesKit/FBSDKFriendFinderDialog.m index 80b76ca934..f603b65eac 100644 --- a/FBSDKGamingServicesKit/FBSDKGamingServicesKit/FBSDKFriendFinderDialog.m +++ b/FBSDKGamingServicesKit/FBSDKGamingServicesKit/FBSDKFriendFinderDialog.m @@ -18,7 +18,7 @@ #import "FBSDKFriendFinderDialog.h" -#import "FBSDKCoreKit+Internal.h" +#import "FBSDKCoreKitInternalImport.h" #import "FBSDKGamingServiceController.h" @interface FBSDKFriendFinderDialog () @@ -34,10 +34,12 @@ - (instancetype)init + (void)launchFriendFinderDialogWithCompletionHandler:(FBSDKGamingServiceCompletionHandler _Nonnull)completionHandler { if ([FBSDKAccessToken currentAccessToken] == nil) { - completionHandler(false, - [FBSDKError - errorWithCode:FBSDKErrorAccessTokenRequired - message:@"A valid access token is required to launch the Friend Finder"]); + completionHandler( + false, + [FBSDKError + errorWithCode:FBSDKErrorAccessTokenRequired + message:@"A valid access token is required to launch the Friend Finder"] + ); return; } @@ -45,11 +47,11 @@ + (void)launchFriendFinderDialogWithCompletionHandler:(FBSDKGamingServiceComplet FBSDKGamingServiceController *const controller = [[FBSDKGamingServiceController alloc] initWithServiceType:FBSDKGamingServiceTypeFriendFinder - completionHandler:^(BOOL success, id _Nullable result, NSError * _Nullable error) { - if (completionHandler) { - completionHandler(success, error); - } - } + completionHandler:^(BOOL success, id _Nullable result, NSError *_Nullable error) { + if (completionHandler) { + completionHandler(success, error); + } + } pendingResult:nil]; [controller callWithArgument:FBSDKAccessToken.currentAccessToken.appID]; diff --git a/FBSDKGamingServicesKit/FBSDKGamingServicesKit/FBSDKGamingGroupIntegration.m b/FBSDKGamingServicesKit/FBSDKGamingServicesKit/FBSDKGamingGroupIntegration.m index 056051281b..4aa21c22ec 100644 --- a/FBSDKGamingServicesKit/FBSDKGamingServicesKit/FBSDKGamingGroupIntegration.m +++ b/FBSDKGamingServicesKit/FBSDKGamingServicesKit/FBSDKGamingGroupIntegration.m @@ -18,7 +18,7 @@ #import "FBSDKGamingGroupIntegration.h" -#import "FBSDKCoreKit+Internal.h" +#import "FBSDKCoreKitInternalImport.h" #import "FBSDKGamingServiceController.h" @implementation FBSDKGamingGroupIntegration @@ -28,11 +28,11 @@ + (void)openGroupPageWithCompletionHandler:(FBSDKGamingServiceCompletionHandler FBSDKGamingServiceController *const controller = [[FBSDKGamingServiceController alloc] initWithServiceType:FBSDKGamingServiceTypeCommunity - completionHandler:^(BOOL success, id _Nullable result, NSError * _Nullable error) { - if (completionHandler) { - completionHandler(success, error); - } - } + completionHandler:^(BOOL success, id _Nullable result, NSError *_Nullable error) { + if (completionHandler) { + completionHandler(success, error); + } + } pendingResult:nil]; [controller callWithArgument:FBSDKSettings.appID]; diff --git a/FBSDKGamingServicesKit/FBSDKGamingServicesKit/FBSDKGamingImageUploader.m b/FBSDKGamingServicesKit/FBSDKGamingServicesKit/FBSDKGamingImageUploader.m index 0905fab6bb..f7e20d9a9f 100644 --- a/FBSDKGamingServicesKit/FBSDKGamingServicesKit/FBSDKGamingImageUploader.m +++ b/FBSDKGamingServicesKit/FBSDKGamingServicesKit/FBSDKGamingImageUploader.m @@ -18,7 +18,7 @@ #import "FBSDKGamingImageUploader.h" -#import "FBSDKCoreKit+Internal.h" +#import "FBSDKCoreKitInternalImport.h" #import "FBSDKGamingImageUploaderConfiguration.h" #import "FBSDKGamingServiceController.h" @@ -36,21 +36,21 @@ - (instancetype)init return [super init]; } -+ (void)uploadImageWithConfiguration:(FBSDKGamingImageUploaderConfiguration * _Nonnull)configuration ++ (void)uploadImageWithConfiguration:(FBSDKGamingImageUploaderConfiguration *_Nonnull)configuration andCompletionHandler:(FBSDKGamingServiceCompletionHandler _Nonnull)completionHandler { return [self uploadImageWithConfiguration:configuration - completionHandler:^(BOOL success, id _Nullable result, NSError * _Nullable error) { - if (completionHandler) { - completionHandler(success, error); - } - } + completionHandler:^(BOOL success, id _Nullable result, NSError *_Nullable error) { + if (completionHandler) { + completionHandler(success, error); + } + } andProgressHandler:nil]; } -+ (void)uploadImageWithConfiguration:(FBSDKGamingImageUploaderConfiguration * _Nonnull)configuration ++ (void)uploadImageWithConfiguration:(FBSDKGamingImageUploaderConfiguration *_Nonnull)configuration andResultCompletionHandler:(FBSDKGamingServiceResultCompletionHandler _Nonnull)completionHandler { return @@ -60,26 +60,30 @@ + (void)uploadImageWithConfiguration:(FBSDKGamingImageUploaderConfiguration * _N andProgressHandler:nil]; } -+ (void)uploadImageWithConfiguration:(FBSDKGamingImageUploaderConfiguration * _Nonnull)configuration ++ (void)uploadImageWithConfiguration:(FBSDKGamingImageUploaderConfiguration *_Nonnull)configuration completionHandler:(FBSDKGamingServiceResultCompletionHandler _Nonnull)completionHandler andProgressHandler:(FBSDKGamingServiceProgressHandler _Nullable)progressHandler { if ([FBSDKAccessToken currentAccessToken] == nil) { - completionHandler(false, - nil, - [FBSDKError - errorWithCode:FBSDKErrorAccessTokenRequired - message:@"A valid access token is required to upload Images"]); + completionHandler( + false, + nil, + [FBSDKError + errorWithCode:FBSDKErrorAccessTokenRequired + message:@"A valid access token is required to upload Images"] + ); return; } if (configuration.image == nil) { - completionHandler(false, - nil, - [FBSDKError - errorWithCode:FBSDKErrorInvalidArgument - message:@"Attempting to upload a nil image"]); + completionHandler( + false, + nil, + [FBSDKError + errorWithCode:FBSDKErrorInvalidArgument + message:@"Attempting to upload a nil image"] + ); return; } @@ -99,36 +103,38 @@ + (void)uploadImageWithConfiguration:(FBSDKGamingImageUploaderConfiguration * _N [[FBSDKGraphRequest alloc] initWithGraphPath:@"me/photos" parameters:@{ - @"caption": configuration.caption ?: @"", - @"picture": UIImagePNGRepresentation(configuration.image) + @"caption" : configuration.caption ?: @"", + @"picture" : UIImagePNGRepresentation(configuration.image) } HTTPMethod:FBSDKHTTPMethodPOST] - completionHandler:^(FBSDKGraphRequestConnection * _Nullable graphConnection, id _Nullable result, NSError * _Nullable error) { - [FBSDKInternalUtility unregisterTransientObject:graphConnection.delegate]; - - if (error || !result) { - completionHandler(false, - nil, - [FBSDKError - errorWithCode:FBSDKErrorGraphRequestGraphAPI - message:@"Image upload failed" - underlyingError:error]); - return; - } - - if (!configuration.shouldLaunchMediaDialog) { - completionHandler(true, result, nil); - return; - } - - FBSDKGamingServiceController *const controller = - [[FBSDKGamingServiceController alloc] - initWithServiceType:FBSDKGamingServiceTypeMediaAsset - completionHandler:completionHandler - pendingResult:result]; - - [controller callWithArgument:result[@"id"]]; - }]; + completionHandler:^(FBSDKGraphRequestConnection *_Nullable graphConnection, id _Nullable result, NSError *_Nullable error) { + [FBSDKInternalUtility unregisterTransientObject:graphConnection.delegate]; + + if (error || !result) { + completionHandler( + false, + nil, + [FBSDKError + errorWithCode:FBSDKErrorGraphRequestGraphAPI + message:@"Image upload failed" + underlyingError:error] + ); + return; + } + + if (!configuration.shouldLaunchMediaDialog) { + completionHandler(true, result, nil); + return; + } + + FBSDKGamingServiceController *const controller = + [[FBSDKGamingServiceController alloc] + initWithServiceType:FBSDKGamingServiceTypeMediaAsset + completionHandler:completionHandler + pendingResult:result]; + + [controller callWithArgument:result[@"id"]]; + }]; [connection start]; } @@ -143,10 +149,10 @@ - (instancetype)initWithProgressHandler:(FBSDKGamingServiceProgressHandler _Null #pragma mark - FBSDKGraphRequestConnectionDelegate -- (void)requestConnection:(FBSDKGraphRequestConnection *)connection - didSendBodyData:(NSInteger)bytesWritten - totalBytesWritten:(NSInteger)totalBytesWritten -totalBytesExpectedToWrite:(NSInteger)totalBytesExpectedToWrite +- (void) requestConnection:(FBSDKGraphRequestConnection *)connection + didSendBodyData:(NSInteger)bytesWritten + totalBytesWritten:(NSInteger)totalBytesWritten + totalBytesExpectedToWrite:(NSInteger)totalBytesExpectedToWrite { if (!_progressHandler) { return; diff --git a/FBSDKGamingServicesKit/FBSDKGamingServicesKit/FBSDKGamingImageUploaderConfiguration.m b/FBSDKGamingServicesKit/FBSDKGamingServicesKit/FBSDKGamingImageUploaderConfiguration.m index 0921c39165..296fc34358 100644 --- a/FBSDKGamingServicesKit/FBSDKGamingServicesKit/FBSDKGamingImageUploaderConfiguration.m +++ b/FBSDKGamingServicesKit/FBSDKGamingServicesKit/FBSDKGamingImageUploaderConfiguration.m @@ -25,8 +25,8 @@ - (instancetype)init return [super init]; } -- (instancetype)initWithImage:(UIImage * _Nonnull)image - caption:(NSString * _Nullable)caption +- (instancetype)initWithImage:(UIImage *_Nonnull)image + caption:(NSString *_Nullable)caption shouldLaunchMediaDialog:(BOOL)shouldLaunchMediaDialog { if (self = [super init]) { diff --git a/FBSDKGamingServicesKit/FBSDKGamingServicesKit/FBSDKGamingServicesKit.h b/FBSDKGamingServicesKit/FBSDKGamingServicesKit/FBSDKGamingServicesKit.h index 8c8d347e9f..3a20b286da 100644 --- a/FBSDKGamingServicesKit/FBSDKGamingServicesKit/FBSDKGamingServicesKit.h +++ b/FBSDKGamingServicesKit/FBSDKGamingServicesKit/FBSDKGamingServicesKit.h @@ -18,23 +18,22 @@ #if defined FBSDKCOCOAPODS || defined BUCK -#import -#import -#import -#import -#import -#import -#import + #import + #import + #import + #import + #import + #import + #import #else -#import "FBSDKFriendFinderDialog.h" -#import "FBSDKGamingImageUploader.h" -#import "FBSDKGamingImageUploaderConfiguration.h" -#import "FBSDKGamingServiceCompletionHandler.h" -#import "FBSDKGamingVideoUploader.h" -#import "FBSDKGamingVideoUploaderConfiguration.h" -#import "FBSDKGamingGroupIntegration.h" + #import "FBSDKFriendFinderDialog.h" + #import "FBSDKGamingGroupIntegration.h" + #import "FBSDKGamingImageUploader.h" + #import "FBSDKGamingImageUploaderConfiguration.h" + #import "FBSDKGamingServiceCompletionHandler.h" + #import "FBSDKGamingVideoUploader.h" + #import "FBSDKGamingVideoUploaderConfiguration.h" #endif - diff --git a/FBSDKGamingServicesKit/FBSDKGamingServicesKit/FBSDKGamingVideoUploader.m b/FBSDKGamingServicesKit/FBSDKGamingServicesKit/FBSDKGamingVideoUploader.m index 66f40268d5..8d6a1e6b48 100644 --- a/FBSDKGamingServicesKit/FBSDKGamingServicesKit/FBSDKGamingVideoUploader.m +++ b/FBSDKGamingServicesKit/FBSDKGamingServicesKit/FBSDKGamingVideoUploader.m @@ -18,11 +18,11 @@ #import "FBSDKGamingVideoUploader.h" -#import "FBSDKCoreKit+Internal.h" +#import "FBSDKCoreKitInternalImport.h" #import "FBSDKGamingVideoUploaderConfiguration.h" #import "FBSDKVideoUploader.h" -@interface FBSDKGamingVideoUploader() +@interface FBSDKGamingVideoUploader () { NSFileHandle *_fileHandle; FBSDKGamingServiceResultCompletionHandler _completionHandler; @@ -39,21 +39,21 @@ - (instancetype)init return [super init]; } -+ (void)uploadVideoWithConfiguration:(FBSDKGamingVideoUploaderConfiguration * _Nonnull)configuration ++ (void)uploadVideoWithConfiguration:(FBSDKGamingVideoUploaderConfiguration *_Nonnull)configuration andCompletionHandler:(FBSDKGamingServiceCompletionHandler _Nonnull)completionHandler { return [self uploadVideoWithConfiguration:configuration - completionHandler:^(BOOL success, id _Nullable result, NSError * _Nullable error) { - if (completionHandler) { - completionHandler(success, error); - } - } + completionHandler:^(BOOL success, id _Nullable result, NSError *_Nullable error) { + if (completionHandler) { + completionHandler(success, error); + } + } andProgressHandler:nil]; } -+ (void)uploadVideoWithConfiguration:(FBSDKGamingVideoUploaderConfiguration * _Nonnull)configuration ++ (void)uploadVideoWithConfiguration:(FBSDKGamingVideoUploaderConfiguration *_Nonnull)configuration andResultCompletionHandler:(FBSDKGamingServiceResultCompletionHandler _Nonnull)completionHandler { return @@ -63,26 +63,30 @@ + (void)uploadVideoWithConfiguration:(FBSDKGamingVideoUploaderConfiguration * _N andProgressHandler:nil]; } -+ (void)uploadVideoWithConfiguration:(FBSDKGamingVideoUploaderConfiguration * _Nonnull)configuration ++ (void)uploadVideoWithConfiguration:(FBSDKGamingVideoUploaderConfiguration *_Nonnull)configuration completionHandler:(FBSDKGamingServiceResultCompletionHandler _Nonnull)completionHandler andProgressHandler:(FBSDKGamingServiceProgressHandler _Nullable)progressHandler { if ([FBSDKAccessToken currentAccessToken] == nil) { - completionHandler(false, - nil, - [FBSDKError - errorWithCode:FBSDKErrorAccessTokenRequired - message:@"A valid access token is required to upload Images"]); + completionHandler( + false, + nil, + [FBSDKError + errorWithCode:FBSDKErrorAccessTokenRequired + message:@"A valid access token is required to upload Images"] + ); return; } if (configuration.videoURL == nil) { - completionHandler(false, - nil, - [FBSDKError - errorWithCode:FBSDKErrorInvalidArgument - message:@"Attempting to upload a nil videoURL"]); + completionHandler( + false, + nil, + [FBSDKError + errorWithCode:FBSDKErrorInvalidArgument + message:@"Attempting to upload a nil videoURL"] + ); return; } @@ -93,16 +97,17 @@ + (void)uploadVideoWithConfiguration:(FBSDKGamingVideoUploaderConfiguration * _N error:nil]; if ((unsigned long)[fileHandle seekToEndOfFile] == 0) { - completionHandler(false, - nil, - [FBSDKError - errorWithCode:FBSDKErrorInvalidArgument - message:@"Attempting to upload an empty video file"]); + completionHandler( + false, + nil, + [FBSDKError + errorWithCode:FBSDKErrorInvalidArgument + message:@"Attempting to upload an empty video file"] + ); return; } - const NSUInteger fileSize = (unsigned long)[fileHandle seekToEndOfFile]; FBSDKGamingVideoUploader *const uploader = @@ -141,11 +146,10 @@ - (instancetype)initWithFileHandle:(NSFileHandle *)fileHandle - (void)safeCompleteWithSuccess:(BOOL)success error:(NSError *)error result:(id)result - { NSError *finalError = error; - if (success == false && error == nil) { + if (success == false && error == nil) { finalError = [FBSDKError errorWithCode:FBSDKErrorUnknown @@ -189,15 +193,15 @@ - (NSData *)videoChunkDataForVideoUploader:(FBSDKVideoUploader *)videoUploader return videoChunkData; } -- (void)videoUploader:(FBSDKVideoUploader *)videoUploader -didCompleteWithResults:(NSDictionary *)results +- (void) videoUploader:(FBSDKVideoUploader *)videoUploader + didCompleteWithResults:(NSDictionary *)results { [self safeProgressWithTotalBytesSent:_totalBytesExpectedToSend]; [self safeCompleteWithSuccess:[results[@"success"] boolValue] error:nil - result:@{@"video_id": results[@"video_id"] ?: @""}]; + result:@{@"video_id" : results[@"video_id"] ?: @""}]; } - (void)videoUploader:(FBSDKVideoUploader *)videoUploader diff --git a/FBSDKGamingServicesKit/FBSDKGamingServicesKit/FBSDKGamingVideoUploaderConfiguration.m b/FBSDKGamingServicesKit/FBSDKGamingServicesKit/FBSDKGamingVideoUploaderConfiguration.m index f0544d1502..4b5f9cdb29 100644 --- a/FBSDKGamingServicesKit/FBSDKGamingServicesKit/FBSDKGamingVideoUploaderConfiguration.m +++ b/FBSDKGamingServicesKit/FBSDKGamingServicesKit/FBSDKGamingVideoUploaderConfiguration.m @@ -25,8 +25,8 @@ - (instancetype)init return [super init]; } -- (instancetype)initWithVideoURL:(NSURL * _Nonnull)videoURL - caption:(NSString * _Nullable)caption; +- (instancetype)initWithVideoURL:(NSURL *_Nonnull)videoURL + caption:(NSString *_Nullable)caption; { if (self = [super init]) { _videoURL = videoURL; diff --git a/FBSDKGamingServicesKit/FBSDKGamingServicesKit/Internal/FBSDKCoreKitInternalImport.h b/FBSDKGamingServicesKit/FBSDKGamingServicesKit/Internal/FBSDKCoreKitInternalImport.h new file mode 100644 index 0000000000..2e3713615b --- /dev/null +++ b/FBSDKGamingServicesKit/FBSDKGamingServicesKit/Internal/FBSDKCoreKitInternalImport.h @@ -0,0 +1,26 @@ +// 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. + +// Importing FBSDKCoreKit+Internal is tricky due to build variants. +// SPM, xcodebuild, and BUCK require that it is imported as "FBSDKCoreKit+Internal.h" +// CocoaPods requires that is is imported as +#if defined FBSDKCOCOAPODS + #import +#else + #import "FBSDKCoreKit+Internal.h" +#endif diff --git a/FBSDKGamingServicesKit/FBSDKGamingServicesKit/Internal/FBSDKGamingServiceController.h b/FBSDKGamingServicesKit/FBSDKGamingServicesKit/Internal/FBSDKGamingServiceController.h index 1dd60fb497..7c9e436dc1 100644 --- a/FBSDKGamingServicesKit/FBSDKGamingServicesKit/Internal/FBSDKGamingServiceController.h +++ b/FBSDKGamingServicesKit/FBSDKGamingServicesKit/Internal/FBSDKGamingServiceController.h @@ -18,9 +18,10 @@ #import -#import "FBSDKCoreKit+Internal.h" #import +#import "FBSDKCoreKitInternalImport.h" + typedef NS_ENUM(NSUInteger, FBSDKGamingServiceType) { FBSDKGamingServiceTypeFriendFinder, FBSDKGamingServiceTypeMediaAsset, diff --git a/FBSDKGamingServicesKit/FBSDKGamingServicesKit/Internal/FBSDKGamingServiceController.m b/FBSDKGamingServicesKit/FBSDKGamingServicesKit/Internal/FBSDKGamingServiceController.m index 3d02ff71a6..951779a023 100644 --- a/FBSDKGamingServicesKit/FBSDKGamingServicesKit/Internal/FBSDKGamingServiceController.m +++ b/FBSDKGamingServicesKit/FBSDKGamingServicesKit/Internal/FBSDKGamingServiceController.m @@ -18,13 +18,12 @@ #import "FBSDKGamingServiceController.h" -#import "FBSDKCoreKit+Internal.h" +#import "FBSDKCoreKitInternalImport.h" static NSString *const kServiceTypeStringFriendFinder = @"friendfinder"; static NSString *const kServiceTypeStringMediaAsset = @"media_asset"; static NSString *const kServiceTypeStringCommunity = @"community"; - static NSString *FBSDKGamingServiceTypeString(FBSDKGamingServiceType type) { switch (type) { @@ -39,7 +38,7 @@ } } -static NSURL* FBSDKGamingServicesUrl(FBSDKGamingServiceType serviceType, NSString *argument) +static NSURL *FBSDKGamingServicesUrl(FBSDKGamingServiceType serviceType, NSString *argument) { return [NSURL URLWithString: @@ -50,7 +49,6 @@ argument]]; } - @implementation FBSDKGamingServiceController { FBSDKGamingServiceType _serviceType; @@ -76,11 +74,11 @@ - (void)callWithArgument:(NSString *)argument [[FBSDKBridgeAPI sharedInstance] openURL:FBSDKGamingServicesUrl(_serviceType, argument) sender:weakSelf - handler:^(BOOL success, NSError * _Nullable error) { - if (!success) { - [weakSelf handleBridgeAPIError:error]; - } - }]; + handler:^(BOOL success, NSError *_Nullable error) { + if (!success) { + [weakSelf handleBridgeAPIError:error]; + } + }]; } - (void)handleBridgeAPIError:(NSError *)error @@ -90,18 +88,22 @@ - (void)handleBridgeAPIError:(NSError *)error } if (error) { - _completionHandler(false, - nil, - [FBSDKError - errorWithCode:FBSDKErrorBridgeAPIInterruption - message:@"Error occured while interacting with Gaming Services" - underlyingError:error]); + _completionHandler( + false, + nil, + [FBSDKError + errorWithCode:FBSDKErrorBridgeAPIInterruption + message:@"Error occured while interacting with Gaming Services" + underlyingError:error] + ); } else { - _completionHandler(false, - nil, - [FBSDKError - errorWithCode:FBSDKErrorBridgeAPIInterruption - message:@"An Unknown error occured while interacting with Gaming Services"]); + _completionHandler( + false, + nil, + [FBSDKError + errorWithCode:FBSDKErrorBridgeAPIInterruption + message:@"An Unknown error occured while interacting with Gaming Services"] + ); } _completionHandler = nil; @@ -133,10 +135,10 @@ - (BOOL)application:(UIApplication *)application return isGamingUrl; } -- (BOOL)canOpenURL:(NSURL *)url - forApplication:(UIApplication *)application - sourceApplication:(NSString *)sourceApplication - annotation:(id)annotation +- (BOOL) canOpenURL:(NSURL *)url + forApplication:(UIApplication *)application + sourceApplication:(NSString *)sourceApplication + annotation:(id)annotation { return [self @@ -156,15 +158,14 @@ - (BOOL)isAuthenticationURL:(NSURL *)url return false; } - #pragma mark - Helpers - (BOOL)isValidCallbackURL:(NSURL *)url forService:(NSString *)service { // verify the URL is intended as a callback for the SDK's friend finder return - [url.scheme hasPrefix:[NSString stringWithFormat:@"fb%@", [FBSDKSettings appID]]] && - [url.host isEqualToString:service]; + [url.scheme hasPrefix:[NSString stringWithFormat:@"fb%@", [FBSDKSettings appID]]] + && [url.host isEqualToString:service]; } @end diff --git a/FBSDKShareKit/FBSDKShareKit/Internal/FBSDKVideoUploader.h b/FBSDKGamingServicesKit/FBSDKGamingServicesKit/Internal/FBSDKVideoUploader.h similarity index 100% rename from FBSDKShareKit/FBSDKShareKit/Internal/FBSDKVideoUploader.h rename to FBSDKGamingServicesKit/FBSDKGamingServicesKit/Internal/FBSDKVideoUploader.h diff --git a/FBSDKShareKit/FBSDKShareKit/Internal/FBSDKVideoUploader.m b/FBSDKGamingServicesKit/FBSDKGamingServicesKit/Internal/FBSDKVideoUploader.m similarity index 59% rename from FBSDKShareKit/FBSDKShareKit/Internal/FBSDKVideoUploader.m rename to FBSDKGamingServicesKit/FBSDKGamingServicesKit/Internal/FBSDKVideoUploader.m index e2dc1abfc2..24579b2e5f 100644 --- a/FBSDKShareKit/FBSDKShareKit/Internal/FBSDKVideoUploader.m +++ b/FBSDKGamingServicesKit/FBSDKGamingServicesKit/Internal/FBSDKVideoUploader.m @@ -20,14 +20,31 @@ #import -#ifdef FBSDKCOCOAPODS -#import +#import "FBSDKCoreKitInternalImport.h" + +#define FBSDK_GAMING_RESULT_COMPLETION_GESTURE_KEY @"completionGesture" +#define FBSDK_GAMING_RESULT_COMPLETION_GESTURE_VALUE_POST @"post" +#define FBSDK_GAMING_VIDEO_END_OFFSET @"end_offset" +#define FBSDK_GAMING_VIDEO_FILE_CHUNK @"video_file_chunk" +#define FBSDK_GAMING_VIDEO_ID @"video_id" +#define FBSDK_GAMING_VIDEO_SIZE @"file_size" +#define FBSDK_GAMING_VIDEO_START_OFFSET @"start_offset" +#define FBSDK_GAMING_VIDEO_UPLOAD_PHASE @"upload_phase" +#define FBSDK_GAMING_VIDEO_UPLOAD_PHASE_FINISH @"finish" +#define FBSDK_GAMING_VIDEO_UPLOAD_PHASE_START @"start" +#define FBSDK_GAMING_VIDEO_UPLOAD_PHASE_TRANSFER @"transfer" +#define FBSDK_GAMING_VIDEO_UPLOAD_SESSION_ID @"upload_session_id" +#define FBSDK_GAMING_VIDEO_UPLOAD_SUCCESS @"success" + +#if __IPHONE_OS_VERSION_MAX_ALLOWED >= __IPHONE_10_0 + +static NSErrorDomain const FBSDKGamingVideoUploadErrorDomain = @"com.facebook.sdk.gaming.videoupload"; + #else -#import "FBSDKCoreKit+Internal.h" -#endif -#import "FBSDKShareDefines.h" -#import "FBSDKShareConstants.h" +static NSString *const FBSDKGamingVideoUploadErrorDomain = @"com.facebook.sdk.gaming.videoupload"; + +#endif static NSString *const FBSDKVideoUploaderDefaultGraphNode = @"me"; static NSString *const FBSDKVideoUploaderEdge = @"videos"; @@ -42,7 +59,7 @@ @implementation FBSDKVideoUploader NSUInteger _videoSize; } -#pragma Public Method +#pragma mark Public Method - (instancetype)initWithVideoName:(NSString *)videoName videoSize:(NSUInteger)videoSize parameters:(NSDictionary *)parameters delegate:(id)delegate { self = [super init]; @@ -58,28 +75,27 @@ - (instancetype)initWithVideoName:(NSString *)videoName videoSize:(NSUInteger)vi - (void)start { - _graphPath = [self _graphPathWithSuffix:FBSDKVideoUploaderEdge, nil]; - [self _postStartRequest]; + _graphPath = [self _graphPathWithSuffix:FBSDKVideoUploaderEdge, nil]; + [self _postStartRequest]; } -#pragma Helper Method +#pragma mark Helper Method - (void)_postStartRequest { - FBSDKGraphRequestBlock startRequestCompletionHandler = ^(FBSDKGraphRequestConnection *connection, id result, NSError *error) - { + FBSDKGraphRequestBlock startRequestCompletionHandler = ^(FBSDKGraphRequestConnection *connection, id result, NSError *error) { if (error) { [self.delegate videoUploader:self didFailWithError:error]; return; } else { result = [FBSDKTypeUtility dictionaryValue:result]; - NSNumber *uploadSessionID = [self.numberFormatter numberFromString:result[FBSDK_SHARE_VIDEO_UPLOAD_SESSION_ID]]; - NSNumber *videoID = [self.numberFormatter numberFromString:result[FBSDK_SHARE_VIDEO_ID]]; + NSNumber *uploadSessionID = [self.numberFormatter numberFromString:result[FBSDK_GAMING_VIDEO_UPLOAD_SESSION_ID]]; + NSNumber *videoID = [self.numberFormatter numberFromString:result[FBSDK_GAMING_VIDEO_ID]]; NSDictionary *offsetDictionary = [self _extractOffsetsFromResultDictionary:result]; if (uploadSessionID == nil || videoID == nil) { [self.delegate videoUploader:self didFailWithError: - [FBSDKError errorWithDomain:FBSDKShareErrorDomain - code:FBSDKShareErrorUnknown + [FBSDKError errorWithDomain:FBSDKGamingVideoUploadErrorDomain + code:0 message:@"Failed to get valid upload_session_id or video_id."]]; return; } else if (offsetDictionary == nil) { @@ -92,16 +108,16 @@ - (void)_postStartRequest }; if (_videoSize == 0) { [self.delegate videoUploader:self didFailWithError: - [FBSDKError errorWithDomain:FBSDKShareErrorDomain - code:FBSDKShareErrorUnknown + [FBSDKError errorWithDomain:FBSDKGamingVideoUploadErrorDomain + code:0 message:[NSString stringWithFormat:@"Invalid video size: %lu", (unsigned long)_videoSize]]]; return; } [[[FBSDKGraphRequest alloc] initWithGraphPath:_graphPath parameters:@{ - FBSDK_SHARE_VIDEO_UPLOAD_PHASE: FBSDK_SHARE_VIDEO_UPLOAD_PHASE_START, - FBSDK_SHARE_VIDEO_SIZE: [NSString stringWithFormat:@"%tu", _videoSize], - } + FBSDK_GAMING_VIDEO_UPLOAD_PHASE : FBSDK_GAMING_VIDEO_UPLOAD_PHASE_START, + FBSDK_GAMING_VIDEO_SIZE : [NSString stringWithFormat:@"%tu", _videoSize], + } HTTPMethod:@"POST"] startWithCompletionHandler:startRequestCompletionHandler]; } @@ -109,13 +125,13 @@ - (void)_startTransferRequestWithOffsetDictionary:(NSDictionary *)offsetDictiona { dispatch_queue_t dataQueue; NSOperatingSystemVersion iOS8Version = { .majorVersion = 8, .minorVersion = 0, .patchVersion = 0 }; - if ([FBSDKInternalUtility isOSRunTimeVersionAtLeast:iOS8Version]) { + if ([NSProcessInfo.processInfo isOperatingSystemAtLeastVersion:iOS8Version]) { dataQueue = dispatch_get_global_queue(QOS_CLASS_USER_INITIATED, 0); } else { dataQueue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0); } - NSUInteger startOffset = [offsetDictionary[FBSDK_SHARE_VIDEO_START_OFFSET] unsignedIntegerValue]; - NSUInteger endOffset = [offsetDictionary[FBSDK_SHARE_VIDEO_END_OFFSET] unsignedIntegerValue]; + NSUInteger startOffset = [offsetDictionary[FBSDK_GAMING_VIDEO_START_OFFSET] unsignedIntegerValue]; + NSUInteger endOffset = [offsetDictionary[FBSDK_GAMING_VIDEO_END_OFFSET] unsignedIntegerValue]; if (startOffset == endOffset) { [self _postFinishRequest]; return; @@ -125,12 +141,12 @@ - (void)_startTransferRequestWithOffsetDictionary:(NSDictionary *)offsetDictiona NSData *data = [self.delegate videoChunkDataForVideoUploader:self startOffset:startOffset endOffset:endOffset]; if (data == nil || data.length != chunkSize) { [self.delegate videoUploader:self didFailWithError: - [FBSDKError errorWithDomain:FBSDKShareErrorDomain - code:FBSDKShareErrorUnknown + [FBSDKError errorWithDomain:FBSDKGamingVideoUploadErrorDomain + code:0 message:[NSString - stringWithFormat:@"Fail to get video chunk with start offset: %lu, end offset : %lu.", - (unsigned long)startOffset, - (unsigned long)endOffset]]]; + stringWithFormat:@"Fail to get video chunk with start offset: %lu, end offset : %lu.", + (unsigned long)startOffset, + (unsigned long)endOffset]]]; return; } dispatch_async(dispatch_get_main_queue(), ^{ @@ -139,11 +155,11 @@ - (void)_startTransferRequestWithOffsetDictionary:(NSDictionary *)offsetDictiona contentType:@""]; FBSDKGraphRequest *request = [[FBSDKGraphRequest alloc] initWithGraphPath:self->_graphPath parameters:@{ - FBSDK_SHARE_VIDEO_UPLOAD_PHASE: FBSDK_SHARE_VIDEO_UPLOAD_PHASE_TRANSFER, - FBSDK_SHARE_VIDEO_START_OFFSET: offsetDictionary[FBSDK_SHARE_VIDEO_START_OFFSET], - FBSDK_SHARE_VIDEO_UPLOAD_SESSION_ID: self->_uploadSessionID, - FBSDK_SHARE_VIDEO_FILE_CHUNK: dataAttachment, - } + FBSDK_GAMING_VIDEO_UPLOAD_PHASE : FBSDK_GAMING_VIDEO_UPLOAD_PHASE_TRANSFER, + FBSDK_GAMING_VIDEO_START_OFFSET : offsetDictionary[FBSDK_GAMING_VIDEO_START_OFFSET], + FBSDK_GAMING_VIDEO_UPLOAD_SESSION_ID : self->_uploadSessionID, + FBSDK_GAMING_VIDEO_FILE_CHUNK : dataAttachment, + } HTTPMethod:@"POST"]; [request startWithCompletionHandler:^(FBSDKGraphRequestConnection *connection, id result, NSError *innerError) { if (innerError) { @@ -163,67 +179,67 @@ - (void)_startTransferRequestWithOffsetDictionary:(NSDictionary *)offsetDictiona - (void)_postFinishRequest { - NSMutableDictionary *parameters = [[NSMutableDictionary alloc] init]; + NSMutableDictionary *parameters = [NSMutableDictionary new]; [FBSDKTypeUtility dictionary:parameters - setObject:FBSDK_SHARE_VIDEO_UPLOAD_PHASE_FINISH - forKey: FBSDK_SHARE_VIDEO_UPLOAD_PHASE]; + setObject:FBSDK_GAMING_VIDEO_UPLOAD_PHASE_FINISH + forKey:FBSDK_GAMING_VIDEO_UPLOAD_PHASE]; [FBSDKTypeUtility dictionary:parameters setObject:_uploadSessionID - forKey:FBSDK_SHARE_VIDEO_UPLOAD_SESSION_ID]; + forKey:FBSDK_GAMING_VIDEO_UPLOAD_SESSION_ID]; [parameters addEntriesFromDictionary:self.parameters]; [[[FBSDKGraphRequest alloc] initWithGraphPath:_graphPath parameters:parameters HTTPMethod:@"POST"] startWithCompletionHandler:^(FBSDKGraphRequestConnection *connection, id result, NSError *error) { - if (error) { - [self.delegate videoUploader:self didFailWithError:error]; - } else { - result = [FBSDKTypeUtility dictionaryValue:result]; - if (result[FBSDK_SHARE_VIDEO_UPLOAD_SUCCESS] == nil) { - [self.delegate videoUploader:self didFailWithError: - [FBSDKError errorWithDomain:FBSDKShareErrorDomain - code:FBSDKShareErrorUnknown - message:@"Failed to finish uploading."]]; - return; - } - NSMutableDictionary *shareResult = [[NSMutableDictionary alloc] init]; - [FBSDKTypeUtility dictionary:shareResult setObject:result[FBSDK_SHARE_VIDEO_UPLOAD_SUCCESS] forKey:FBSDK_SHARE_VIDEO_UPLOAD_SUCCESS]; - [FBSDKTypeUtility dictionary:shareResult setObject:FBSDK_SHARE_RESULT_COMPLETION_GESTURE_VALUE_POST forKey:FBSDK_SHARE_RESULT_COMPLETION_GESTURE_KEY]; - [FBSDKTypeUtility dictionary:shareResult setObject:self->_videoID forKey:FBSDK_SHARE_VIDEO_ID]; - [self.delegate videoUploader:self didCompleteWithResults:shareResult]; - } - }]; + if (error) { + [self.delegate videoUploader:self didFailWithError:error]; + } else { + result = [FBSDKTypeUtility dictionaryValue:result]; + if (result[FBSDK_GAMING_VIDEO_UPLOAD_SUCCESS] == nil) { + [self.delegate videoUploader:self didFailWithError: + [FBSDKError errorWithDomain:FBSDKGamingVideoUploadErrorDomain + code:0 + message:@"Failed to finish uploading."]]; + return; + } + NSMutableDictionary *shareResult = [NSMutableDictionary new]; + [FBSDKTypeUtility dictionary:shareResult setObject:result[FBSDK_GAMING_VIDEO_UPLOAD_SUCCESS] forKey:FBSDK_GAMING_VIDEO_UPLOAD_SUCCESS]; + [FBSDKTypeUtility dictionary:shareResult setObject:FBSDK_GAMING_RESULT_COMPLETION_GESTURE_VALUE_POST forKey:FBSDK_GAMING_RESULT_COMPLETION_GESTURE_KEY]; + [FBSDKTypeUtility dictionary:shareResult setObject:self->_videoID forKey:FBSDK_GAMING_VIDEO_ID]; + [self.delegate videoUploader:self didCompleteWithResults:shareResult]; + } + }]; } - (NSDictionary *)_extractOffsetsFromResultDictionary:(id)result { result = [FBSDKTypeUtility dictionaryValue:result]; - NSNumber *startNum = [self.numberFormatter numberFromString:result[FBSDK_SHARE_VIDEO_START_OFFSET]]; - NSNumber *endNum = [self.numberFormatter numberFromString:result[FBSDK_SHARE_VIDEO_END_OFFSET]]; + NSNumber *startNum = [self.numberFormatter numberFromString:result[FBSDK_GAMING_VIDEO_START_OFFSET]]; + NSNumber *endNum = [self.numberFormatter numberFromString:result[FBSDK_GAMING_VIDEO_END_OFFSET]]; if (startNum == nil || endNum == nil) { [self.delegate videoUploader:self didFailWithError: - [FBSDKError errorWithDomain:FBSDKShareErrorDomain - code:FBSDKShareErrorUnknown + [FBSDKError errorWithDomain:FBSDKGamingVideoUploadErrorDomain + code:0 message:@"Fail to get valid start_offset or end_offset."]]; return nil; } if ([startNum compare:endNum] == NSOrderedDescending) { [self.delegate videoUploader:self didFailWithError: - [FBSDKError errorWithDomain:FBSDKShareErrorDomain - code:FBSDKShareErrorUnknown + [FBSDKError errorWithDomain:FBSDKGamingVideoUploadErrorDomain + code:0 message:@"Invalid offset: start_offset is greater than end_offset."]]; return nil; } - NSMutableDictionary *shareResults = [[NSMutableDictionary alloc] init]; - [FBSDKTypeUtility dictionary:shareResults setObject:startNum forKey:FBSDK_SHARE_VIDEO_START_OFFSET]; - [FBSDKTypeUtility dictionary:shareResults setObject:endNum forKey:FBSDK_SHARE_VIDEO_END_OFFSET]; + NSMutableDictionary *shareResults = [NSMutableDictionary new]; + [FBSDKTypeUtility dictionary:shareResults setObject:startNum forKey:FBSDK_GAMING_VIDEO_START_OFFSET]; + [FBSDKTypeUtility dictionary:shareResults setObject:endNum forKey:FBSDK_GAMING_VIDEO_END_OFFSET]; return shareResults; } - (NSNumberFormatter *)numberFormatter { if (!_numberFormatter) { - _numberFormatter = [[NSNumberFormatter alloc] init]; + _numberFormatter = [NSNumberFormatter new]; _numberFormatter.numberStyle = NSNumberFormatterDecimalStyle; } return _numberFormatter; diff --git a/FBSDKGamingServicesKit/FBSDKGamingServicesKitTests/FBSDKFriendFinderDialogTests.m b/FBSDKGamingServicesKit/FBSDKGamingServicesKitTests/FBSDKFriendFinderDialogTests.m index 50da53799a..5efc5a1e1e 100644 --- a/FBSDKGamingServicesKit/FBSDKGamingServicesKitTests/FBSDKFriendFinderDialogTests.m +++ b/FBSDKGamingServicesKit/FBSDKGamingServicesKitTests/FBSDKFriendFinderDialogTests.m @@ -16,9 +16,8 @@ // 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 @@ -51,10 +50,10 @@ - (void)testFailureWhenNoValidAccessTokenPresent __block BOOL actioned = false; [FBSDKFriendFinderDialog - launchFriendFinderDialogWithCompletionHandler:^(BOOL success, NSError * _Nullable error) { - XCTAssert(error.code == FBSDKErrorAccessTokenRequired, "Expected error requiring a valid access token"); - actioned = true; - }]; + launchFriendFinderDialogWithCompletionHandler:^(BOOL success, NSError *_Nullable error) { + XCTAssert(error.code == FBSDKErrorAccessTokenRequired, "Expected error requiring a valid access token"); + actioned = true; + }]; XCTAssertTrue(actioned); } @@ -62,21 +61,23 @@ - (void)testFailureWhenNoValidAccessTokenPresent - (void)testServiceIsCalledCorrectly { OCMStub([_mockToken appID]).andReturn(@"123"); - OCMStub([_mockApp - openURL:[OCMArg any] - options:[OCMArg any] - completionHandler:([OCMArg invokeBlockWithArgs:@(false), nil])]); + OCMStub( + [_mockApp + openURL:[OCMArg any] + options:[OCMArg any] + completionHandler:([OCMArg invokeBlockWithArgs:@(false), nil])] + ); id expectation = [self expectationWithDescription:@"callback"]; [FBSDKFriendFinderDialog - launchFriendFinderDialogWithCompletionHandler:^(BOOL success, NSError * _Nullable error) { - [expectation fulfill]; - }]; + launchFriendFinderDialogWithCompletionHandler:^(BOOL success, NSError *_Nullable error) { + [expectation fulfill]; + }]; [self waitForExpectationsWithTimeout:1 handler:nil]; - id urlCheck = [OCMArg checkWithBlock:^BOOL(id obj) { + id urlCheck = [OCMArg checkWithBlock:^BOOL (id obj) { return [[(NSURL *)obj absoluteString] isEqualToString:@"https://fb.gg/me/friendfinder/123"]; }]; OCMVerify([_mockApp openURL:urlCheck options:[OCMArg any] completionHandler:[OCMArg any]]); @@ -84,18 +85,20 @@ - (void)testServiceIsCalledCorrectly - (void)testFailuresReturnAnError { - OCMStub([_mockApp - openURL:[OCMArg any] - options:[OCMArg any] - completionHandler:([OCMArg invokeBlockWithArgs:@(false), nil])]); + OCMStub( + [_mockApp + openURL:[OCMArg any] + options:[OCMArg any] + completionHandler:([OCMArg invokeBlockWithArgs:@(false), nil])] + ); id expectation = [self expectationWithDescription:@"callback"]; [FBSDKFriendFinderDialog - launchFriendFinderDialogWithCompletionHandler:^(BOOL success, NSError * _Nullable error) { - XCTAssertFalse(success); - XCTAssert(error.code == FBSDKErrorBridgeAPIInterruption); - [expectation fulfill]; - }]; + launchFriendFinderDialogWithCompletionHandler:^(BOOL success, NSError *_Nullable error) { + XCTAssertFalse(success); + XCTAssert(error.code == FBSDKErrorBridgeAPIInterruption); + [expectation fulfill]; + }]; [self waitForExpectationsWithTimeout:1 handler:nil]; } @@ -112,10 +115,10 @@ - (void)testHandlingOfCallbackURL __block BOOL actioned = false; [FBSDKFriendFinderDialog - launchFriendFinderDialogWithCompletionHandler:^(BOOL success, NSError * _Nullable error) { - XCTAssertTrue(success); - actioned = true; - }]; + launchFriendFinderDialogWithCompletionHandler:^(BOOL success, NSError *_Nullable error) { + XCTAssertTrue(success); + actioned = true; + }]; [delegate application:_mockApp openURL:[NSURL URLWithString:@"fb123://friendfinder"] sourceApplication:@"foo" annotation:nil]; @@ -131,10 +134,10 @@ - (void)testHandlingOfUserManuallyReturningToOriginalApp __block BOOL actioned = false; [FBSDKFriendFinderDialog - launchFriendFinderDialogWithCompletionHandler:^(BOOL success, NSError * _Nullable error) { - XCTAssertTrue(success); - actioned = true; - }]; + launchFriendFinderDialogWithCompletionHandler:^(BOOL success, NSError *_Nullable error) { + XCTAssertTrue(success); + actioned = true; + }]; [delegate applicationDidBecomeActive:_mockApp]; diff --git a/FBSDKGamingServicesKit/FBSDKGamingServicesKitTests/FBSDKGamingImageUploaderTests.m b/FBSDKGamingServicesKit/FBSDKGamingServicesKitTests/FBSDKGamingImageUploaderTests.m index c8f73731ab..b4fd50dfe3 100644 --- a/FBSDKGamingServicesKit/FBSDKGamingServicesKitTests/FBSDKGamingImageUploaderTests.m +++ b/FBSDKGamingServicesKit/FBSDKGamingServicesKitTests/FBSDKGamingImageUploaderTests.m @@ -16,9 +16,8 @@ // 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 @@ -72,10 +71,10 @@ - (void)testFailureWhenNoValidAccessTokenPresent __block BOOL actioned = false; [FBSDKGamingImageUploader uploadImageWithConfiguration:_mockConfig - andResultCompletionHandler:^(BOOL success, id result, NSError * _Nullable error) { - XCTAssert(error.code == FBSDKErrorAccessTokenRequired, "Expected error requiring a valid access token"); - actioned = true; - }]; + andResultCompletionHandler:^(BOOL success, id result, NSError *_Nullable error) { + XCTAssert(error.code == FBSDKErrorAccessTokenRequired, "Expected error requiring a valid access token"); + actioned = true; + }]; XCTAssertTrue(actioned); } @@ -87,10 +86,10 @@ - (void)testNilImageFails __block BOOL actioned = false; [FBSDKGamingImageUploader uploadImageWithConfiguration:nilImageConfig - andResultCompletionHandler:^(BOOL success, id result, NSError * _Nullable error) { - XCTAssert(error.code == FBSDKErrorInvalidArgument, "Expected error requiring a non nil image"); - actioned = true; - }]; + andResultCompletionHandler:^(BOOL success, id result, NSError *_Nullable error) { + XCTAssert(error.code == FBSDKErrorInvalidArgument, "Expected error requiring a non nil image"); + actioned = true; + }]; XCTAssertTrue(actioned); } @@ -102,54 +101,56 @@ - (void)testGraphErrorsAreHandled __block BOOL actioned = false; [FBSDKGamingImageUploader uploadImageWithConfiguration:_mockConfig - andResultCompletionHandler:^(BOOL success, id result, NSError * _Nullable error) { - XCTAssert(error.code == FBSDKErrorGraphRequestGraphAPI, "Expected error from Graph API"); - actioned = true; - }]; + andResultCompletionHandler:^(BOOL success, id result, NSError *_Nullable error) { + XCTAssert(error.code == FBSDKErrorGraphRequestGraphAPI, "Expected error from Graph API"); + actioned = true; + }]; XCTAssertTrue(actioned); } - (void)testGraphResponsesTriggerCompletionIfDialogNotRequested { - [self stubGraphRequestWithResult:@{@"id": @"123"} error:nil]; + [self stubGraphRequestWithResult:@{@"id" : @"123"} error:nil]; __block BOOL actioned = false; [FBSDKGamingImageUploader uploadImageWithConfiguration:_mockConfig - andResultCompletionHandler:^(BOOL success, id result, NSError * _Nullable error) { - XCTAssertTrue(success); - XCTAssertNil(error); - actioned = true; - }]; + andResultCompletionHandler:^(BOOL success, id result, NSError *_Nullable error) { + XCTAssertTrue(success); + XCTAssertNil(error); + actioned = true; + }]; XCTAssertTrue(actioned); } - (void)testGraphResponsesDoNotTriggerCompletionIfDialogIsRequested { - [self stubGraphRequestWithResult:@{@"id": @"123"} error:nil]; + [self stubGraphRequestWithResult:@{@"id" : @"123"} error:nil]; OCMStub([_mockConfig shouldLaunchMediaDialog]).andReturn(true); __block BOOL actioned = false; [FBSDKGamingImageUploader uploadImageWithConfiguration:_mockConfig - andResultCompletionHandler:^(BOOL success, id result, NSError * _Nullable error) { - actioned = true; - }]; + andResultCompletionHandler:^(BOOL success, id result, NSError *_Nullable error) { + actioned = true; + }]; XCTAssertFalse(actioned, "Callback should not have been called because there was more work to do"); } - (void)testGraphResponsesTriggerDialogIfDialogIsRequested { - [self stubGraphRequestWithResult:@{@"id": @"111"} error:nil]; + [self stubGraphRequestWithResult:@{@"id" : @"111"} error:nil]; OCMStub([_mockConfig shouldLaunchMediaDialog]).andReturn(true); - OCMStub([_mockApp - openURL:[OCMArg any] - options:[OCMArg any] - completionHandler:([OCMArg invokeBlockWithArgs:@(false), nil])]); + OCMStub( + [_mockApp + openURL:[OCMArg any] + options:[OCMArg any] + completionHandler:([OCMArg invokeBlockWithArgs:@(false), nil])] + ); // This will cause an lint warning, ignore it, this is an external sdk test // @lint-ignore FBOBJCDISCOURAGEDFUNCTION @@ -157,13 +158,13 @@ - (void)testGraphResponsesTriggerDialogIfDialogIsRequested [FBSDKGamingImageUploader uploadImageWithConfiguration:_mockConfig - andResultCompletionHandler:^(BOOL success, id result, NSError * _Nullable error) { - [expectation fulfill]; - }]; + andResultCompletionHandler:^(BOOL success, id result, NSError *_Nullable error) { + [expectation fulfill]; + }]; [self waitForExpectationsWithTimeout:1 handler:nil]; - id urlCheck = [OCMArg checkWithBlock:^BOOL(id obj) { + id urlCheck = [OCMArg checkWithBlock:^BOOL (id obj) { return [[(NSURL *)obj absoluteString] isEqualToString:@"https://fb.gg/me/media_asset/111"]; }]; OCMVerify([_mockApp openURL:urlCheck options:[OCMArg any] completionHandler:[OCMArg any]]); @@ -174,7 +175,7 @@ - (void)testDialogCompletionOnURLCallback id settings = OCMClassMock([FBSDKSettings class]); OCMStub([settings appID]).andReturn(@"123"); - [self stubGraphRequestWithResult:@{@"id": @"111"} error:nil]; + [self stubGraphRequestWithResult:@{@"id" : @"111"} error:nil]; OCMStub([_mockConfig shouldLaunchMediaDialog]).andReturn(true); __block id delegate; @@ -185,10 +186,10 @@ - (void)testDialogCompletionOnURLCallback __block BOOL actioned = false; [FBSDKGamingImageUploader uploadImageWithConfiguration:_mockConfig - andResultCompletionHandler:^(BOOL success, id result, NSError * _Nullable error) { - XCTAssertTrue(success); - actioned = true; - }]; + andResultCompletionHandler:^(BOOL success, id result, NSError *_Nullable error) { + XCTAssertTrue(success); + actioned = true; + }]; [delegate application:_mockApp @@ -201,7 +202,7 @@ - (void)testDialogCompletionOnURLCallback - (void)testDialogCompletionOnApplicationBecameActive { - [self stubGraphRequestWithResult:@{@"id": @"111"} error:nil]; + [self stubGraphRequestWithResult:@{@"id" : @"111"} error:nil]; OCMStub([_mockConfig shouldLaunchMediaDialog]).andReturn(true); __block id delegate; @@ -212,10 +213,10 @@ - (void)testDialogCompletionOnApplicationBecameActive __block BOOL actioned = false; [FBSDKGamingImageUploader uploadImageWithConfiguration:_mockConfig - andResultCompletionHandler:^(BOOL success, id result, NSError * _Nullable error) { - XCTAssertTrue(success); - actioned = true; - }]; + andResultCompletionHandler:^(BOOL success, id result, NSError *_Nullable error) { + XCTAssertTrue(success); + actioned = true; + }]; [delegate applicationDidBecomeActive:_mockApp]; @@ -227,31 +228,31 @@ - (void)testUploadProgress __block id delegate; __block FBSDKGraphRequestBlock completion; id mockConnection = [self stubGraphRequestWithDelegateCapture:^(id obj) { - delegate = obj; - } andCompletionCapture:^(FBSDKGraphRequestBlock obj) { - completion = obj; - }]; + delegate = obj; + } andCompletionCapture:^(FBSDKGraphRequestBlock obj) { + completion = obj; + }]; __block BOOL completionActioned = false; __block BOOL progressActioned = false; [FBSDKGamingImageUploader uploadImageWithConfiguration:_mockConfig - completionHandler:^(BOOL success, id result, NSError * _Nullable error) { - XCTAssert(success); - XCTAssertEqual(result[@"id"], @"foo"); - completionActioned = true; - } + completionHandler:^(BOOL success, id result, NSError *_Nullable error) { + XCTAssert(success); + XCTAssertEqual(result[@"id"], @"foo"); + completionActioned = true; + } andProgressHandler:^(int64_t bytesSent, int64_t totalBytesSent, int64_t totalBytesExpectedToSend) { - XCTAssertEqual(bytesSent, 123); - XCTAssertEqual(totalBytesSent, 456); - XCTAssertEqual(totalBytesExpectedToSend, 789); - progressActioned = true; - }]; + XCTAssertEqual(bytesSent, 123); + XCTAssertEqual(totalBytesSent, 456); + XCTAssertEqual(totalBytesExpectedToSend, 789); + progressActioned = true; + }]; [delegate requestConnection:mockConnection didSendBodyData:123 totalBytesWritten:456 totalBytesExpectedToWrite:789]; XCTAssertTrue(progressActioned); - completion(mockConnection, @{@"id": @"foo"}, nil); + completion(mockConnection, @{@"id" : @"foo"}, nil); XCTAssertTrue(completionActioned); } @@ -276,14 +277,18 @@ - (id)stubGraphRequestWithDelegateCapture:(void (^)(id))captureHa id mock = OCMClassMock([FBSDKBridgeAPI class]); OCMStub([mock sharedInstance]).andReturn(mock); - OCMStub([mock openURL:[OCMArg any] sender:[OCMArg checkWithBlock:^BOOL(id obj) { - captureHandler(obj); - return true; - }] handler:[OCMArg any]]); + OCMStub( + [mock openURL:[OCMArg any] sender:[OCMArg checkWithBlock:^BOOL (id obj) { + captureHandler(obj); + return true; + }] handler:[OCMArg any]] + ); } @end diff --git a/FBSDKGamingServicesKit/FBSDKGamingServicesKitTests/FBSDKGamingVideoUploaderTests.m b/FBSDKGamingServicesKit/FBSDKGamingServicesKitTests/FBSDKGamingVideoUploaderTests.m index f3a1f41c42..f0955e03c4 100644 --- a/FBSDKGamingServicesKit/FBSDKGamingServicesKitTests/FBSDKGamingVideoUploaderTests.m +++ b/FBSDKGamingServicesKit/FBSDKGamingServicesKitTests/FBSDKGamingVideoUploaderTests.m @@ -16,9 +16,8 @@ // 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 @@ -66,10 +65,10 @@ - (void)testFailureWhenNoValidAccessTokenPresent __block BOOL actioned = false; [FBSDKGamingVideoUploader uploadVideoWithConfiguration:_mockConfig - andResultCompletionHandler:^(BOOL success, id result, NSError * _Nullable error) { - XCTAssert(error.code == FBSDKErrorAccessTokenRequired, "Expected error requiring a valid access token"); - actioned = true; - }]; + andResultCompletionHandler:^(BOOL success, id result, NSError *_Nullable error) { + XCTAssert(error.code == FBSDKErrorAccessTokenRequired, "Expected error requiring a valid access token"); + actioned = true; + }]; XCTAssertTrue(actioned); } @@ -81,10 +80,10 @@ - (void)testNilVideoURLFails __block BOOL actioned = false; [FBSDKGamingVideoUploader uploadVideoWithConfiguration:nilVideoConfig - andResultCompletionHandler:^(BOOL success, id result, NSError * _Nullable error) { - XCTAssert(error.code == FBSDKErrorInvalidArgument, "Expected error requiring a non nil video url"); - actioned = true; - }]; + andResultCompletionHandler:^(BOOL success, id result, NSError *_Nullable error) { + XCTAssert(error.code == FBSDKErrorInvalidArgument, "Expected error requiring a non nil video url"); + actioned = true; + }]; XCTAssertTrue(actioned); } @@ -97,10 +96,10 @@ - (void)testBadVideoURLFails __block BOOL actioned = false; [FBSDKGamingVideoUploader uploadVideoWithConfiguration:badVideoConfig - andResultCompletionHandler:^(BOOL success, id result, NSError * _Nullable error) { - XCTAssert(error.code == FBSDKErrorInvalidArgument, "Expected error requiring a non nil video url"); - actioned = true; - }]; + andResultCompletionHandler:^(BOOL success, id result, NSError *_Nullable error) { + XCTAssert(error.code == FBSDKErrorInvalidArgument, "Expected error requiring a non nil video url"); + actioned = true; + }]; XCTAssertTrue(actioned); } @@ -117,10 +116,10 @@ - (void)testVideoUploaderErrorsAreHandled __block BOOL actioned = false; [FBSDKGamingVideoUploader uploadVideoWithConfiguration:_mockConfig - andResultCompletionHandler:^(BOOL success, id result, NSError * _Nullable error) { - XCTAssert(error.code == expectedError.code); - actioned = true; - }]; + andResultCompletionHandler:^(BOOL success, id result, NSError *_Nullable error) { + XCTAssert(error.code == expectedError.code); + actioned = true; + }]; [delegate videoUploader:mockUploader didFailWithError:expectedError]; @@ -137,15 +136,15 @@ - (void)testVideoUploaderErrorOnUnsuccessful __block BOOL actioned = false; [FBSDKGamingVideoUploader uploadVideoWithConfiguration:_mockConfig - andResultCompletionHandler:^(BOOL success, id result, NSError * _Nullable error) { - XCTAssert(error.code == FBSDKErrorUnknown); - actioned = true; - }]; + andResultCompletionHandler:^(BOOL success, id result, NSError *_Nullable error) { + XCTAssert(error.code == FBSDKErrorUnknown); + actioned = true; + }]; [delegate videoUploader:mockUploader didCompleteWithResults:@{ - @"success": @(false) + @"success" : @(false) }]; XCTAssertTrue(actioned); @@ -161,15 +160,15 @@ - (void)testVideoUploaderSucceeds __block BOOL actioned = false; [FBSDKGamingVideoUploader uploadVideoWithConfiguration:_mockConfig - andResultCompletionHandler:^(BOOL success, id result, NSError * _Nullable error) { - XCTAssertTrue(success); - actioned = true; - }]; + andResultCompletionHandler:^(BOOL success, id result, NSError *_Nullable error) { + XCTAssertTrue(success); + actioned = true; + }]; [delegate videoUploader:mockUploader didCompleteWithResults:@{ - @"success": @(true) + @"success" : @(true) }]; XCTAssertTrue(actioned); @@ -184,7 +183,7 @@ - (void)testVideoUploaderProgress __block BOOL actionedFirst = false; __block void (^ProgressCheck)(int64_t bytesSent, int64_t totalBytesSent, int64_t totalBytesExpectedToSend) = - ^void(int64_t bytesSent, int64_t totalBytesSent, int64_t totalBytesExpectedToSend) { + ^void (int64_t bytesSent, int64_t totalBytesSent, int64_t totalBytesExpectedToSend) { XCTAssertEqual(bytesSent, 0); XCTAssertEqual(totalBytesSent, 0); XCTAssertEqual(totalBytesExpectedToSend, 999); @@ -193,10 +192,10 @@ __block void (^ProgressCheck)(int64_t bytesSent, int64_t totalBytesSent, int64_t [FBSDKGamingVideoUploader uploadVideoWithConfiguration:_mockConfig - completionHandler:^(BOOL success, id result, NSError * _Nullable error) {} + completionHandler:^(BOOL success, id result, NSError *_Nullable error) {} andProgressHandler:^(int64_t bytesSent, int64_t totalBytesSent, int64_t totalBytesExpectedToSend) { - ProgressCheck(bytesSent, totalBytesSent, totalBytesExpectedToSend); - }]; + ProgressCheck(bytesSent, totalBytesSent, totalBytesExpectedToSend); + }]; // Send some bytes [delegate @@ -206,7 +205,7 @@ __block void (^ProgressCheck)(int64_t bytesSent, int64_t totalBytesSent, int64_t __block BOOL actionedSecond = false; ProgressCheck = - ^void(int64_t bytesSent, int64_t totalBytesSent, int64_t totalBytesExpectedToSend) { + ^void (int64_t bytesSent, int64_t totalBytesSent, int64_t totalBytesExpectedToSend) { XCTAssertEqual(bytesSent, 500); XCTAssertEqual(totalBytesSent, 500); XCTAssertEqual(totalBytesExpectedToSend, 999); @@ -215,24 +214,24 @@ __block void (^ProgressCheck)(int64_t bytesSent, int64_t totalBytesSent, int64_t // Send some more bytes [delegate - videoChunkDataForVideoUploader:mockUploader - startOffset:500 - endOffset:999]; + videoChunkDataForVideoUploader:mockUploader + startOffset:500 + endOffset:999]; __block BOOL actionedThird = false; ProgressCheck = - ^void(int64_t bytesSent, int64_t totalBytesSent, int64_t totalBytesExpectedToSend) { - XCTAssertEqual(bytesSent, 499); - XCTAssertEqual(totalBytesSent, 999); - XCTAssertEqual(totalBytesExpectedToSend, 999); - actionedThird = true; - }; + ^void (int64_t bytesSent, int64_t totalBytesSent, int64_t totalBytesExpectedToSend) { + XCTAssertEqual(bytesSent, 499); + XCTAssertEqual(totalBytesSent, 999); + XCTAssertEqual(totalBytesExpectedToSend, 999); + actionedThird = true; + }; // Finish upload [delegate videoUploader:mockUploader didCompleteWithResults:@{ - @"success": @(true) + @"success" : @(true) }]; XCTAssertTrue(actionedFirst); @@ -258,10 +257,12 @@ - (id)mockVideoUploaderWithDelegateCapture:(void (^)(id + shouldUseLaunchSchemeArgsEnv = "YES" + codeCoverageEnabled = "YES"> @@ -38,6 +39,16 @@ ReferencedContainer = "container:FBSDKLoginKit.xcodeproj"> + + + + + #import #else -#import + #import #endif diff --git a/FBSDKLoginKit/FBSDKLoginKit/FBSDKDeviceLoginCodeInfo.m b/FBSDKLoginKit/FBSDKLoginKit/FBSDKDeviceLoginCodeInfo.m index 4f22fce519..ebe3a3b365 100644 --- a/FBSDKLoginKit/FBSDKLoginKit/FBSDKDeviceLoginCodeInfo.m +++ b/FBSDKLoginKit/FBSDKLoginKit/FBSDKDeviceLoginCodeInfo.m @@ -21,10 +21,10 @@ @implementation FBSDKDeviceLoginCodeInfo - (instancetype)initWithIdentifier:(NSString *)identifier - loginCode:(NSString *)loginCode - verificationURL:(NSURL *)verificationURL - expirationDate:(NSDate *)expirationDate - pollingInterval:(NSUInteger)pollingInterval + loginCode:(NSString *)loginCode + verificationURL:(NSURL *)verificationURL + expirationDate:(NSDate *)expirationDate + pollingInterval:(NSUInteger)pollingInterval { if ((self = [super init])) { _identifier = [identifier copy]; @@ -35,4 +35,5 @@ - (instancetype)initWithIdentifier:(NSString *)identifier } return self; } + @end diff --git a/FBSDKLoginKit/FBSDKLoginKit/FBSDKDeviceLoginManager.m b/FBSDKLoginKit/FBSDKLoginKit/FBSDKDeviceLoginManager.m index 7acc8731b0..6c802b64a8 100644 --- a/FBSDKLoginKit/FBSDKLoginKit/FBSDKDeviceLoginManager.m +++ b/FBSDKLoginKit/FBSDKLoginKit/FBSDKDeviceLoginManager.m @@ -17,13 +17,14 @@ // CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. #import "FBSDKDeviceLoginManager.h" + #import "FBSDKDeviceLoginManagerResult+Internal.h" #ifdef FBSDKCOCOAPODS -#import -#import + #import + #import #else -#import "FBSDKCoreKit+Internal.h" + #import "FBSDKCoreKit+Internal.h" #endif #import "FBSDKDeviceLoginCodeInfo+Internal.h" @@ -31,10 +32,11 @@ static NSMutableArray *g_loginManagerInstances; -@implementation FBSDKDeviceLoginManager { +@implementation FBSDKDeviceLoginManager +{ FBSDKDeviceLoginCodeInfo *_codeInfo; BOOL _isCancelled; - NSNetService * _loginAdvertisementService; + NSNetService *_loginAdvertisementService; BOOL _isSmartLoginEnabled; } @@ -60,10 +62,10 @@ - (void)start [FBSDKTypeUtility array:g_loginManagerInstances addObject:self]; NSDictionary *parameters = @{ - @"scope": [self.permissions componentsJoinedByString:@","] ?: @"", - @"redirect_uri": self.redirectURL.absoluteString ?: @"", - FBSDK_DEVICE_INFO_PARAM: [FBSDKDeviceRequestsHelper getDeviceInfo], - }; + @"scope" : [self.permissions componentsJoinedByString:@","] ?: @"", + @"redirect_uri" : self.redirectURL.absoluteString ?: @"", + FBSDK_DEVICE_INFO_PARAM : [FBSDKDeviceRequestsHelper getDeviceInfo], + }; FBSDKGraphRequest *request = [[FBSDKGraphRequest alloc] initWithGraphPath:@"device/login" parameters:parameters tokenString:[FBSDKInternalUtility validateRequiredClientAccessToken] @@ -77,11 +79,11 @@ - (void)start } self->_codeInfo = [[FBSDKDeviceLoginCodeInfo alloc] - initWithIdentifier:result[@"code"] - loginCode:result[@"user_code"] - verificationURL:[NSURL URLWithString:result[@"verification_uri"]] - expirationDate:[[NSDate date] dateByAddingTimeInterval:[result[@"expires_in"] doubleValue]] - pollingInterval:[result[@"interval"] integerValue]]; + initWithIdentifier:result[@"code"] + loginCode:result[@"user_code"] + verificationURL:[NSURL URLWithString:result[@"verification_uri"]] + expirationDate:[[NSDate date] dateByAddingTimeInterval:[result[@"expires_in"] doubleValue]] + pollingInterval:[result[@"interval"] integerValue]]; if (self->_isSmartLoginEnabled) { [FBSDKDeviceRequestsHelper startAdvertisementService:self->_codeInfo.loginCode @@ -92,7 +94,7 @@ - (void)start [self.delegate deviceLoginManager:self startedWithCodeInfo:self->_codeInfo]; [self _schedulePoll:self->_codeInfo.pollingInterval]; }]; - } +} - (void)cancel { @@ -112,10 +114,10 @@ - (void)_notifyError:(NSError *)error [g_loginManagerInstances removeObject:self]; } -- (void)_notifyToken:(NSString *)tokenString +- (void)_notifyToken:(NSString *)tokenString withExpirationDate:(NSDate *)expirationDate withDataAccessExpirationDate:(NSDate *)dataAccessExpirationDate { [FBSDKDeviceRequestsHelper cleanUpAdvertisementService:self]; - void(^completeWithResult)(FBSDKDeviceLoginManagerResult *) = ^(FBSDKDeviceLoginManagerResult *result) { + void (^completeWithResult)(FBSDKDeviceLoginManagerResult *) = ^(FBSDKDeviceLoginManagerResult *result) { [self.delegate deviceLoginManager:self completedWithResult:result error:nil]; [g_loginManagerInstances removeObject:self]; }; @@ -123,27 +125,27 @@ - (void)_notifyToken:(NSString *)tokenString if (tokenString) { FBSDKGraphRequest *permissionsRequest = [[FBSDKGraphRequest alloc] initWithGraphPath:@"me" - parameters:@{@"fields": @"id,permissions"} + parameters:@{@"fields" : @"id,permissions"} tokenString:tokenString HTTPMethod:@"GET" flags:FBSDKGraphRequestFlagDisableErrorRecovery]; [permissionsRequest startWithCompletionHandler:^(FBSDKGraphRequestConnection *connection, id permissionRawResult, NSError *error) { NSString *userID = permissionRawResult[@"id"]; NSDictionary *permissionResult = permissionRawResult[@"permissions"]; - if (error || - !userID || - !permissionResult) { -#if TARGET_TV_OS + if (error + || !userID + || !permissionResult) { + #if TARGET_TV_OS NSError *wrappedError = [FBSDKError errorWithDomain:FBSDKShareErrorDomain code:FBSDKErrorTVOSUnknown message:@"Unable to fetch permissions for token" underlyingError:error]; -#else + #else NSError *wrappedError = [FBSDKError errorWithDomain:FBSDKLoginErrorDomain code:FBSDKErrorUnknown message:@"Unable to fetch permissions for token" underlyingError:error]; -#endif + #endif [self _notifyError:wrappedError]; } else { NSMutableSet *permissions = [NSMutableSet set]; @@ -153,19 +155,20 @@ - (void)_notifyToken:(NSString *)tokenString [FBSDKInternalUtility extractPermissionsFromResponse:permissionResult grantedPermissions:permissions declinedPermissions:declinedPermissions - expiredPermissions:expiredPermissions]; + expiredPermissions:expiredPermissions]; FBSDKAccessToken *accessToken = [[FBSDKAccessToken alloc] initWithTokenString:tokenString permissions:permissions.allObjects declinedPermissions:declinedPermissions.allObjects expiredPermissions:expiredPermissions.allObjects appID:[FBSDKSettings appID] userID:userID - expirationDate:nil + expirationDate:expirationDate refreshDate:nil - dataAccessExpirationDate:nil + dataAccessExpirationDate:dataAccessExpirationDate graphDomain:nil]; FBSDKDeviceLoginManagerResult *result = [[FBSDKDeviceLoginManagerResult alloc] initWithToken:accessToken isCancelled:NO]; + [FBSDKAccessToken setCurrentAccessToken:accessToken]; completeWithResult(result); } }]; @@ -185,7 +188,7 @@ - (void)_processError:(NSError *)error break; case FBSDKDeviceLoginErrorCodeExpired: case FBSDKDeviceLoginErrorAuthorizationDeclined: - [self _notifyToken:nil]; + [self _notifyToken:nil withExpirationDate:nil withDataAccessExpirationDate:nil]; break; case FBSDKDeviceLoginErrorExcessivePolling: [self _schedulePoll:_codeInfo.pollingInterval * 2]; @@ -197,45 +200,55 @@ - (void)_processError:(NSError *)error - (void)_schedulePoll:(NSUInteger)interval { - dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(interval * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{ - if (self->_isCancelled) { - return; - } - - NSDictionary *parameters = @{ @"code": self->_codeInfo.identifier }; - FBSDKGraphRequest *request = [[FBSDKGraphRequest alloc] initWithGraphPath:@"device/login_status" - parameters:parameters - tokenString:[FBSDKInternalUtility validateRequiredClientAccessToken] - HTTPMethod:@"POST" - flags:FBSDKGraphRequestFlagNone]; - [request setGraphErrorRecoveryDisabled:YES]; - [request startWithCompletionHandler:^(FBSDKGraphRequestConnection *connection, id result, NSError *error) { + dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(interval * NSEC_PER_SEC)), + dispatch_get_main_queue(), ^{ if (self->_isCancelled) { return; } - if (error) { - [self _processError:error]; - } else { - NSString *tokenString = result[@"access_token"]; - if (tokenString) { - [self _notifyToken:tokenString]; + + NSDictionary *parameters = @{ @"code" : self->_codeInfo.identifier }; + FBSDKGraphRequest *request = [[FBSDKGraphRequest alloc] initWithGraphPath:@"device/login_status" + parameters:parameters + tokenString:[FBSDKInternalUtility validateRequiredClientAccessToken] + HTTPMethod:@"POST" + flags:FBSDKGraphRequestFlagNone]; + [request setGraphErrorRecoveryDisabled:YES]; + [request startWithCompletionHandler:^(FBSDKGraphRequestConnection *connection, id result, NSError *error) { + if (self->_isCancelled) { + return; + } + if (error) { + [self _processError:error]; } else { - NSError *unknownError = [FBSDKError errorWithDomain:FBSDKLoginErrorDomain - code:FBSDKErrorUnknown - message:@"Device Login poll failed. No token nor error was found."]; - [self _notifyError:unknownError]; + NSString *tokenString = result[@"access_token"]; + NSDate *expirationDate = [NSDate distantFuture]; + if ([result[@"expires_in"] integerValue] > 0) { + expirationDate = [NSDate dateWithTimeIntervalSinceNow:[result[@"expires_in"] integerValue]]; + } + + NSDate *dataAccessExpirationDate = [NSDate distantFuture]; + if ([result[@"data_access_expiration_time"] integerValue] > 0) { + dataAccessExpirationDate = [NSDate dateWithTimeIntervalSince1970:[result[@"data_access_expiration_time"] integerValue]]; + } + + if (tokenString) { + [self _notifyToken:tokenString withExpirationDate:expirationDate withDataAccessExpirationDate:dataAccessExpirationDate]; + } else { + NSError *unknownError = [FBSDKError errorWithDomain:FBSDKLoginErrorDomain + code:FBSDKErrorUnknown + message:@"Device Login poll failed. No token nor error was found."]; + [self _notifyError:unknownError]; + } } - } - }]; - }); + }]; + }); } - (void)netService:(NSNetService *)sender didNotPublish:(NSDictionary *)errorDict { // Only cleanup if the publish error is from our advertising service - if ([FBSDKDeviceRequestsHelper isDelegate:self forAdvertisementService:sender]) - { + if ([FBSDKDeviceRequestsHelper isDelegate:self forAdvertisementService:sender]) { [FBSDKDeviceRequestsHelper cleanUpAdvertisementService:self]; } } diff --git a/FBSDKLoginKit/FBSDKLoginKit/FBSDKLoginButton.h b/FBSDKLoginKit/FBSDKLoginKit/FBSDKLoginButton.h index c3adca5d32..4be58012a1 100644 --- a/FBSDKLoginKit/FBSDKLoginKit/FBSDKLoginButton.h +++ b/FBSDKLoginKit/FBSDKLoginKit/FBSDKLoginButton.h @@ -58,7 +58,7 @@ typedef NS_ENUM(NSUInteger, FBSDKLoginButtonTooltipBehavior) /** A button that initiates a log in or log out flow upon tapping. - `FBSDKLoginButton` works with `[FBSDKAccessToken currentAccessToken]` to + `FBSDKLoginButton` works with `FBSDKProfile.currentProfile` to determine what to display, and automatically starts authentication when tapped (i.e., you do not need to manually subscribe action targets). @@ -98,6 +98,15 @@ NS_SWIFT_NAME(FBLoginButton) Gets or sets the desired tooltip color style. */ @property (assign, nonatomic) FBSDKTooltipColorStyle tooltipColorStyle; +/** + Gets or sets the desired tracking preference to use for login attempts. Defaults to `.enabled` + */ +@property (assign, nonatomic) FBSDKLoginTracking loginTracking; +/** + Gets or sets an optional nonce to use for login attempts. A valid nonce must be a non-empty string without whitespace. + An invalid nonce will not be set. Instead, default unique nonces will be used for login attempts. + */ +@property (assign, nonatomic, nullable) NSString *nonce; @end diff --git a/FBSDKLoginKit/FBSDKLoginKit/FBSDKLoginButton.m b/FBSDKLoginKit/FBSDKLoginKit/FBSDKLoginButton.m index 933e21d18a..d66d1c2b78 100644 --- a/FBSDKLoginKit/FBSDKLoginKit/FBSDKLoginButton.m +++ b/FBSDKLoginKit/FBSDKLoginKit/FBSDKLoginButton.m @@ -20,15 +20,16 @@ #if !TARGET_OS_TV -#import "FBSDKLoginButton.h" + #import "FBSDKLoginButton.h" -#ifdef FBSDKCOCOAPODS -#import -#else -#import "FBSDKCoreKit+Internal.h" -#endif + #ifdef FBSDKCOCOAPODS + #import + #else + #import "FBSDKCoreKit+Internal.h" + #endif -#import "FBSDKLoginTooltipView.h" + #import "FBSDKLoginTooltipView.h" + #import "FBSDKNonceUtility.h" static const CGFloat kFBLogoSize = 16.0; static const CGFloat kFBLogoLeftMargin = 6.0; @@ -36,7 +37,7 @@ static const CGFloat kRightMargin = 8.0; static const CGFloat kPaddingBetweenLogoTitle = 8.0; -@interface FBSDKLoginButton() +@interface FBSDKLoginButton () @end @implementation FBSDKLoginButton @@ -47,14 +48,14 @@ @implementation FBSDKLoginButton NSString *_userName; } -#pragma mark - Object Lifecycle + #pragma mark - Object Lifecycle - (void)dealloc { - [[NSNotificationCenter defaultCenter] removeObserver:self]; + [self _unsubscribeFromNotifications]; } -#pragma mark - Properties + #pragma mark - Properties - (FBSDKDefaultAudience)defaultAudience { @@ -66,6 +67,12 @@ - (void)setDefaultAudience:(FBSDKDefaultAudience)defaultAudience _loginManager.defaultAudience = defaultAudience; } +- (void)setLoginTracking:(FBSDKLoginTracking)loginTracking +{ + _loginTracking = loginTracking; + [self _updateNotificationObservers]; +} + - (UIFont *)defaultFont { CGFloat size = 15; @@ -77,20 +84,31 @@ - (UIFont *)defaultFont } } -#pragma mark - UIView +- (void)setNonce:(NSString *)nonce +{ + if ([FBSDKNonceUtility isValidNonce:nonce]) { + _nonce = nonce; + } else { + _nonce = nil; + [FBSDKLogger singleShotLogEntry:FBSDKLoggingBehaviorDeveloperErrors + formatString:@"Unable to set invalid nonce: %@ on FBSDKLoginButton", nonce]; + } +} + + #pragma mark - UIView - (void)didMoveToWindow { [super didMoveToWindow]; - if (self.window && - ((self.tooltipBehavior == FBSDKLoginButtonTooltipBehaviorForceDisplay) || !_hasShownTooltipBubble)) { + if (self.window + && ((self.tooltipBehavior == FBSDKLoginButtonTooltipBehaviorForceDisplay) || !_hasShownTooltipBubble)) { [self performSelector:@selector(_showTooltipIfNeeded) withObject:nil afterDelay:0]; _hasShownTooltipBubble = YES; } } -#pragma mark - Layout + #pragma mark - Layout - (CGRect)imageRectForContentRect:(CGRect)contentRect { @@ -115,9 +133,9 @@ - (void)layoutSubviews { CGSize size = self.bounds.size; CGSize longTitleSize = [self sizeThatFits:size title:[self _longLogInTitle]]; - NSString *title = (longTitleSize.width <= size.width ? - [self _longLogInTitle] : - [self _shortLogInTitle]); + NSString *title = (longTitleSize.width <= size.width + ? [self _longLogInTitle] + : [self _shortLogInTitle]); if (![title isEqualToString:[self titleForState:UIControlStateNormal]]) { [self setTitle:title forState:UIControlStateNormal]; } @@ -143,7 +161,7 @@ - (CGSize)sizeThatFits:(CGSize)size return CGSizeMake(buttonWidth, kButtonHeight); } -#pragma mark - FBSDKButtonImpressionTracking + #pragma mark - FBSDKButtonImpressionTracking - (NSDictionary *)analyticsParameters { @@ -160,7 +178,7 @@ - (NSString *)impressionTrackingIdentifier return @"login"; } -#pragma mark - FBSDKButton + #pragma mark - FBSDKButton - (void)configureButton { @@ -170,13 +188,13 @@ - (void)configureButton NSString *logOutTitle = [self _logOutTitle]; [self configureWithIcon:nil - title:logInTitle - backgroundColor:self.backgroundColor - highlightedColor:nil - selectedTitle:logOutTitle - selectedIcon:nil - selectedColor:self.backgroundColor - selectedHighlightedColor:nil]; + title:logInTitle + backgroundColor:self.backgroundColor + highlightedColor:nil + selectedTitle:logOutTitle + selectedIcon:nil + selectedColor:self.backgroundColor + selectedHighlightedColor:nil]; self.titleLabel.textAlignment = NSTextAlignmentCenter; [self addConstraint:[NSLayoutConstraint constraintWithItem:self attribute:NSLayoutAttributeHeight @@ -185,51 +203,87 @@ - (void)configureButton attribute:NSLayoutAttributeNotAnAttribute multiplier:1 constant:kButtonHeight]]; - [self _updateContent]; + [self _initializeContent]; [self addTarget:self action:@selector(_buttonPressed:) forControlEvents:UIControlEventTouchUpInside]; + + [self _updateNotificationObservers]; +} + + #pragma mark - Helper Methods + +- (void)_unsubscribeFromNotifications +{ + [[NSNotificationCenter defaultCenter] removeObserver:self]; +} + +- (void)_updateNotificationObservers +{ + [[NSNotificationCenter defaultCenter] addObserver:self + selector:@selector(_profileDidChangeNotification:) + name:FBSDKProfileDidChangeNotification + object:nil]; [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(_accessTokenDidChangeNotification:) name:FBSDKAccessTokenDidChangeNotification object:nil]; } -#pragma mark - Helper Methods - - (void)_accessTokenDidChangeNotification:(NSNotification *)notification { if (notification.userInfo[FBSDKAccessTokenDidChangeUserIDKey] || notification.userInfo[FBSDKAccessTokenDidExpireKey]) { - [self _updateContent]; + [self _updateContentForAccessToken]; } } +- (void)_profileDidChangeNotification:(NSNotification *)notification +{ + [self _updateContentForUserProfile:FBSDKProfile.currentProfile]; +} + - (void)_buttonPressed:(id)sender { [self logTapEventWithEventName:FBSDKAppEventNameFBSDKLoginButtonDidTap parameters:self.analyticsParameters]; - if (FBSDKAccessToken.isCurrentAccessTokenActive) { + if (self._isAuthenticated) { NSString *title = nil; if (_userName) { NSString *localizedFormatString = - NSLocalizedStringWithDefaultValue(@"LoginButton.LoggedInAs", @"FacebookSDK", [FBSDKInternalUtility bundleForStrings], - @"Logged in as %@", - @"The format string for the FBSDKLoginButton label when the user is logged in"); + NSLocalizedStringWithDefaultValue( + @"LoginButton.LoggedInAs", + @"FacebookSDK", + [FBSDKInternalUtility bundleForStrings], + @"Logged in as %@", + @"The format string for the FBSDKLoginButton label when the user is logged in" + ); title = [NSString localizedStringWithFormat:localizedFormatString, _userName]; } else { NSString *localizedLoggedIn = - NSLocalizedStringWithDefaultValue(@"LoginButton.LoggedIn", @"FacebookSDK", [FBSDKInternalUtility bundleForStrings], - @"Logged in using Facebook", - @"The fallback string for the FBSDKLoginButton label when the user name is not available yet"); + NSLocalizedStringWithDefaultValue( + @"LoginButton.LoggedIn", + @"FacebookSDK", + [FBSDKInternalUtility bundleForStrings], + @"Logged in using Facebook", + @"The fallback string for the FBSDKLoginButton label when the user name is not available yet" + ); title = localizedLoggedIn; } NSString *cancelTitle = - NSLocalizedStringWithDefaultValue(@"LoginButton.CancelLogout", @"FacebookSDK", [FBSDKInternalUtility bundleForStrings], - @"Cancel", - @"The label for the FBSDKLoginButton action sheet to cancel logging out"); + NSLocalizedStringWithDefaultValue( + @"LoginButton.CancelLogout", + @"FacebookSDK", + [FBSDKInternalUtility bundleForStrings], + @"Cancel", + @"The label for the FBSDKLoginButton action sheet to cancel logging out" + ); NSString *logOutTitle = - NSLocalizedStringWithDefaultValue(@"LoginButton.ConfirmLogOut", @"FacebookSDK", [FBSDKInternalUtility bundleForStrings], - @"Log Out", - @"The label for the FBSDKLoginButton action sheet to confirm logging out"); + NSLocalizedStringWithDefaultValue( + @"LoginButton.ConfirmLogOut", + @"FacebookSDK", + [FBSDKInternalUtility bundleForStrings], + @"Log Out", + @"The label for the FBSDKLoginButton action sheet to confirm logging out" + ); UIAlertController *alertController = [UIAlertController alertControllerWithTitle:title message:nil preferredStyle:UIAlertControllerStyleActionSheet]; @@ -240,7 +294,7 @@ - (void)_buttonPressed:(id)sender handler:nil]; UIAlertAction *logout = [UIAlertAction actionWithTitle:logOutTitle style:UIAlertActionStyleDestructive - handler:^(UIAlertAction * _Nonnull action) { + handler:^(UIAlertAction *_Nonnull action) { [self->_loginManager logOut]; [self.delegate loginButtonDidLogOut:self]; }]; @@ -263,37 +317,62 @@ - (void)_buttonPressed:(id)sender } }; - [_loginManager logInWithPermissions:self.permissions - fromViewController:[FBSDKInternalUtility viewControllerForView:self] - handler:handler]; + FBSDKLoginConfiguration *loginConfig = [self loginConfiguration]; + [_loginManager logInFromViewController:[FBSDKInternalUtility viewControllerForView:self] + configuration:loginConfig + completion:handler]; + } +} + +- (FBSDKLoginConfiguration *)loginConfiguration +{ + if (self.nonce) { + return [[FBSDKLoginConfiguration alloc] initWithPermissions:self.permissions + tracking:self.loginTracking + nonce:self.nonce]; + } else { + return [[FBSDKLoginConfiguration alloc] initWithPermissions:self.permissions + tracking:self.loginTracking]; } } - (NSString *)_logOutTitle { - return NSLocalizedStringWithDefaultValue(@"LoginButton.LogOut", @"FacebookSDK", [FBSDKInternalUtility bundleForStrings], - @"Log out", - @"The label for the FBSDKLoginButton when the user is currently logged in"); + return NSLocalizedStringWithDefaultValue( + @"LoginButton.LogOut", + @"FacebookSDK", + [FBSDKInternalUtility bundleForStrings], + @"Log out", + @"The label for the FBSDKLoginButton when the user is currently logged in" + ); } - (NSString *)_longLogInTitle { - return NSLocalizedStringWithDefaultValue(@"LoginButton.LogInContinue", @"FacebookSDK", [FBSDKInternalUtility bundleForStrings], - @"Continue with Facebook", - @"The long label for the FBSDKLoginButton when the user is currently logged out"); + return NSLocalizedStringWithDefaultValue( + @"LoginButton.LogInContinue", + @"FacebookSDK", + [FBSDKInternalUtility bundleForStrings], + @"Continue with Facebook", + @"The long label for the FBSDKLoginButton when the user is currently logged out" + ); } - (NSString *)_shortLogInTitle { - return NSLocalizedStringWithDefaultValue(@"LoginButton.LogIn", @"FacebookSDK", [FBSDKInternalUtility bundleForStrings], - @"Log in", - @"The short label for the FBSDKLoginButton when the user is currently logged out"); + return NSLocalizedStringWithDefaultValue( + @"LoginButton.LogIn", + @"FacebookSDK", + [FBSDKInternalUtility bundleForStrings], + @"Log in", + @"The short label for the FBSDKLoginButton when the user is currently logged out" + ); } - (void)_showTooltipIfNeeded { - if ([FBSDKAccessToken currentAccessToken] || self.tooltipBehavior == FBSDKLoginButtonTooltipBehaviorDisable) { + if (self._isAuthenticated || self.tooltipBehavior == FBSDKLoginButtonTooltipBehaviorDisable) { return; } else { FBSDKLoginTooltipView *tooltipView = [[FBSDKLoginTooltipView alloc] init]; @@ -305,26 +384,85 @@ - (void)_showTooltipIfNeeded } } -- (void)_updateContent +// On initial setting of button state. We want to update the button's user +// information using the most comprehensive available. +// If access token is available use that. +// If only profile is available, use that. +- (void)_initializeContent +{ + FBSDKAccessToken *accessToken = FBSDKAccessToken.currentAccessToken; + FBSDKProfile *profile = FBSDKProfile.currentProfile; + + if (accessToken) { + [self _updateContentForAccessToken]; + } else if (profile) { + [self _updateContentForUserProfile:profile]; + } else { + self.selected = NO; + } +} + +- (void)_updateContentForAccessToken { BOOL accessTokenIsValid = FBSDKAccessToken.isCurrentAccessTokenActive; self.selected = accessTokenIsValid; if (accessTokenIsValid) { - if (![[FBSDKAccessToken currentAccessToken].userID isEqualToString:_userID]) { - FBSDKGraphRequest *request = [[FBSDKGraphRequest alloc] initWithGraphPath:@"me?fields=id,name" - parameters:nil - flags:FBSDKGraphRequestFlagDisableErrorRecovery]; - [request startWithCompletionHandler:^(FBSDKGraphRequestConnection *connection, id result, NSError *error) { - NSString *userID = [FBSDKTypeUtility stringValue:result[@"id"]]; - if (!error && [[FBSDKAccessToken currentAccessToken].userID isEqualToString:userID]) { - self->_userName = [FBSDKTypeUtility stringValue:result[@"name"]]; - self->_userID = userID; - } - }]; + if (![FBSDKAccessToken.currentAccessToken.userID isEqualToString:_userID]) { + [self _fetchAndSetContent]; } } } +- (void)_fetchAndSetContent +{ + FBSDKGraphRequest *request = [[FBSDKGraphRequest alloc] initWithGraphPath:@"me?fields=id,name" + parameters:nil + flags:FBSDKGraphRequestFlagDisableErrorRecovery]; + [request startWithCompletionHandler:^(FBSDKGraphRequestConnection *connection, id result, NSError *error) { + NSString *userID = [FBSDKTypeUtility stringValue:result[@"id"]]; + if (!error && [FBSDKAccessToken.currentAccessToken.userID isEqualToString:userID]) { + self->_userName = [FBSDKTypeUtility stringValue:result[@"name"]]; + self->_userID = userID; + } + }]; +} + +- (void)_updateContentForUserProfile:(nullable FBSDKProfile *)profile +{ + self.selected = profile != nil; + + if (profile && [self _userInformationDoesNotMatchProfile:profile]) { + _userName = profile.name; + _userID = profile.userID; + } +} + +- (BOOL)_userInformationDoesNotMatchProfile:(FBSDKProfile *)profile +{ + return (profile.userID != _userID) || (profile.name != _userName); +} + +- (BOOL)_isAuthenticated +{ + return (FBSDKAccessToken.currentAccessToken || FBSDKAuthenticationToken.currentAuthenticationToken); +} + +// MARK: - Testability + + #if DEBUG + +- (NSString *)userName +{ + return _userName; +} + +- (NSString *)userID +{ + return _userID; +} + + #endif + @end #endif diff --git a/FBSDKLoginKit/FBSDKLoginKit/FBSDKLoginConfiguration.h b/FBSDKLoginKit/FBSDKLoginKit/FBSDKLoginConfiguration.h new file mode 100644 index 0000000000..1c9c42217f --- /dev/null +++ b/FBSDKLoginKit/FBSDKLoginKit/FBSDKLoginConfiguration.h @@ -0,0 +1,85 @@ +// 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 + +NS_ASSUME_NONNULL_BEGIN + +@class FBSDKPermission; + +/// The login tracking preference to use for a login attempt. For more information on the differences between +/// `enabled` and `limited` see: https://developers.facebook.com/docs/facebook-login/ios/limited-login/ +typedef NS_ENUM(NSUInteger, FBSDKLoginTracking) +{ + FBSDKLoginTrackingEnabled, + FBSDKLoginTrackingLimited, +} NS_SWIFT_NAME(LoginTracking); + +/// A configuration to use for modifying the behavior of a login attempt. +NS_SWIFT_NAME(LoginConfiguration) +@interface FBSDKLoginConfiguration : NSObject + +/// The nonce that the configuration was created with. +/// A unique nonce will be used if none is provided to the initializer. +@property (nonatomic, readonly, copy) NSString *nonce; + +/// The tracking preference. Defaults to `.enabled`. +@property (nonatomic, readonly) FBSDKLoginTracking tracking; + +/// The requested permissions for the login attempt. Defaults to an empty set. +@property (nonatomic, readonly, copy) NSSet *requestedPermissions; + +- (instancetype)init NS_UNAVAILABLE; ++ (instancetype)new NS_UNAVAILABLE; + +/** + Attempts to initialize a new configuration with the expected parameters. + + @param permissions the requested permissions for a login attempt. Permissions must be an array of strings that do not contain whitespace. + The only permissions allowed when the `loginTracking` is `.limited` are 'email', 'public_profile', 'gaming_profile' and 'gaming_user_picture' + @param tracking the tracking preference to use for a login attempt. + @param nonce an optional nonce to use for the login attempt. A valid nonce must be a non-empty string without whitespace. + Creation of the configuration will fail if the nonce is invalid. + */ +- (nullable instancetype)initWithPermissions:(NSArray *)permissions + tracking:(FBSDKLoginTracking)tracking + nonce:(NSString *)nonce +NS_REFINED_FOR_SWIFT; + +/** + Attempts to initialize a new configuration with the expected parameters. + + @param permissions the requested permissions for the login attempt. Permissions must be an array of strings that do not contain whitespace. + The only permissions allowed when the `loginTracking` is `.limited` are 'email', 'public_profile', 'gaming_profile' and 'gaming_user_picture' + @param tracking the tracking preference to use for a login attempt. + */ +- (nullable instancetype)initWithPermissions:(NSArray *)permissions + tracking:(FBSDKLoginTracking)tracking +NS_REFINED_FOR_SWIFT; + +/** + Attempts to initialize a new configuration with the expected parameters. + + @param tracking the login tracking preference to use for a login attempt. + */ +- (nullable instancetype)initWithTracking:(FBSDKLoginTracking)tracking +NS_REFINED_FOR_SWIFT; + +@end + +NS_ASSUME_NONNULL_END diff --git a/FBSDKLoginKit/FBSDKLoginKit/FBSDKLoginConfiguration.m b/FBSDKLoginKit/FBSDKLoginKit/FBSDKLoginConfiguration.m new file mode 100644 index 0000000000..57270837b7 --- /dev/null +++ b/FBSDKLoginKit/FBSDKLoginKit/FBSDKLoginConfiguration.m @@ -0,0 +1,115 @@ +// 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 "TargetConditionals.h" + +#if !TARGET_OS_TV + + #import "FBSDKLoginConfiguration.h" + + #import "FBSDKNonceUtility.h" + + #ifdef FBSDKCOCOAPODS + #import + #else + #import "FBSDKCoreKit+Internal.h" + #endif + + #import "FBSDKPermission.h" + +@implementation FBSDKLoginConfiguration + +- (nullable instancetype)initWithTracking:(FBSDKLoginTracking)tracking +{ + return [[FBSDKLoginConfiguration alloc] initWithPermissions:@[] + tracking:tracking]; +} + +- (nullable instancetype)initWithPermissions:(NSArray *)permissions + tracking:(FBSDKLoginTracking)tracking +{ + return [[FBSDKLoginConfiguration alloc] initWithPermissions:permissions + tracking:tracking + nonce:NSUUID.UUID.UUIDString]; +} + +- (nullable instancetype)initWithPermissions:(NSArray *)permissions + tracking:(FBSDKLoginTracking)tracking + nonce:(NSString *)nonce +{ + if (![FBSDKNonceUtility isValidNonce:nonce]) { + [FBSDKLogger singleShotLogEntry:FBSDKLoggingBehaviorDeveloperErrors + formatString:@"Invalid nonce:%@ provided to login configuration. Returning nil.", nonce]; + return nil; + } + + NSSet *permissionsSet = [FBSDKLoginConfiguration permissionsFromRawPermissions:[NSSet setWithArray:permissions] forTrackingPreference:tracking]; + if (!permissionsSet) { + [FBSDKLogger singleShotLogEntry:FBSDKLoggingBehaviorDeveloperErrors + formatString:@"Invalid combination of permissions and tracking preference provided to login configuration. The only permissions allowed when `tracking` is `.limited` are 'email', 'public_profile', 'gaming_profile' and 'gaming_user_picture'. Returning nil."]; + return nil; + } + + if ((self = [super init])) { + _requestedPermissions = permissionsSet; + _tracking = tracking; + _nonce = nonce; + } + + return self; +} + +- (instancetype)init +{ + if ((self = [super init])) { + _requestedPermissions = [NSSet set]; + _tracking = FBSDKLoginTrackingEnabled; + _nonce = NSUUID.UUID.UUIDString; + } + + return self; +} + ++ (NSSet *)permissionsFromRawPermissions:(NSSet *)permissions + forTrackingPreference:(FBSDKLoginTracking)tracking +{ + switch (tracking) { + case FBSDKLoginTrackingLimited: { + NSSet *validPermissions = [NSSet setWithArray:@[ + @"email", + @"public_profile", + @"gaming_profile", + @"gaming_user_picture", + ]]; + NSSet *combined = [permissions setByAddingObjectsFromSet:validPermissions]; + + if (permissions.count != 0 && combined.count > validPermissions.count) { + return nil; + } + break; + } + case FBSDKLoginTrackingEnabled: + break; + } + + return [FBSDKPermission permissionsFromRawPermissions:permissions]; +} + +@end + +#endif diff --git a/FBSDKLoginKit/FBSDKLoginKit/FBSDKLoginConstants.h b/FBSDKLoginKit/FBSDKLoginKit/FBSDKLoginConstants.h index 471bfdcab3..85bab47c9c 100644 --- a/FBSDKLoginKit/FBSDKLoginKit/FBSDKLoginConstants.h +++ b/FBSDKLoginKit/FBSDKLoginKit/FBSDKLoginConstants.h @@ -100,6 +100,16 @@ typedef NS_ERROR_ENUM(FBSDKLoginErrorDomain, FBSDKLoginError) The login response was missing a valid challenge string. */ FBSDKLoginErrorBadChallengeString, + + /** + The ID token returned in login response was invalid + */ + FBSDKLoginErrorInvalidIDToken, + + /** + A current access token was required and not provided + */ + FBSDKLoginErrorMissingAccessToken, } NS_SWIFT_NAME(LoginError); /** diff --git a/FBSDKLoginKit/FBSDKLoginKit/FBSDKLoginKit.h b/FBSDKLoginKit/FBSDKLoginKit/FBSDKLoginKit.h index f19b1accbe..f27581ebba 100644 --- a/FBSDKLoginKit/FBSDKLoginKit/FBSDKLoginKit.h +++ b/FBSDKLoginKit/FBSDKLoginKit/FBSDKLoginKit.h @@ -22,11 +22,14 @@ #import "FBSDKDeviceLoginCodeInfo.h" #import "FBSDKDeviceLoginManager.h" #import "FBSDKDeviceLoginManagerResult.h" +#import "FBSDKLoginConfiguration.h" #import "FBSDKLoginConstants.h" #if !TARGET_OS_TV -#import "FBSDKLoginButton.h" -#import "FBSDKLoginManager.h" -#import "FBSDKLoginManagerLoginResult.h" -#import "FBSDKLoginTooltipView.h" + #import "FBSDKLoginButton.h" + #import "FBSDKLoginManager.h" + #import "FBSDKLoginManagerLoginResult.h" + #import "FBSDKLoginTooltipView.h" + #import "FBSDKReferralManager.h" + #import "FBSDKReferralManagerResult.h" #endif diff --git a/FBSDKLoginKit/FBSDKLoginKit/FBSDKLoginManager.h b/FBSDKLoginKit/FBSDKLoginKit/FBSDKLoginManager.h index 71997442ec..e6b5aafd54 100644 --- a/FBSDKLoginKit/FBSDKLoginKit/FBSDKLoginManager.h +++ b/FBSDKLoginKit/FBSDKLoginKit/FBSDKLoginManager.h @@ -18,6 +18,8 @@ #import +#import "FBSDKLoginConfiguration.h" + NS_ASSUME_NONNULL_BEGIN #if TARGET_OS_TV @@ -32,9 +34,11 @@ NS_ASSUME_NONNULL_BEGIN // The way to fix this is to remove extensions of ObjC types in Swift. @class LoginManagerLoginResult; +@class FBSDKLoginConfiguration; typedef NS_ENUM(NSUInteger, LoginBehavior) { LoginBehaviorBrowser }; typedef NS_ENUM(NSUInteger, DefaultAudience) { DefaultAudienceFriends }; + typedef void (^LoginManagerLoginResultBlock)(LoginManagerLoginResult *_Nullable result, NSError *_Nullable error); @@ -48,6 +52,11 @@ typedef void (^LoginManagerLoginResultBlock)(LoginManagerLoginResult *_Nullable handler:(nullable LoginManagerLoginResultBlock)handler NS_SWIFT_NAME(logIn(permissions:from:handler:)); +- (void)logInFromViewController:(nullable UIViewController *)viewController + configuration:(FBSDKLoginConfiguration *)configuration + completion:(LoginManagerLoginResultBlock)completion +NS_REFINED_FOR_SWIFT; + @end #else @@ -76,9 +85,7 @@ NS_SWIFT_NAME(LoginManagerLoginResultBlock); /** FBSDKDefaultAudience enum - Passed to open to indicate which default audience to use for sessions that post data to Facebook. - - + Passed to openURL to indicate which default audience to use for sessions that post data to Facebook. Certain operations such as publishing a status or publishing a photo require an audience. When the user grants an application permission to perform a publish operation, a default audience is selected as the @@ -98,14 +105,15 @@ typedef NS_ENUM(NSUInteger, FBSDKDefaultAudience) /** `FBSDKLoginManager` provides methods for logging the user in and out. - `FBSDKLoginManager` works directly with `[FBSDKAccessToken currentAccessToken]` and - sets the "currentAccessToken" upon successful authorizations (or sets `nil` in case of `logOut`). + `FBSDKLoginManager` serves to help manage sessions represented by tokens for authentication, + `AuthenticationToken`, and data access, `AccessToken`. - You should check `[FBSDKAccessToken currentAccessToken]` before calling logIn* to see if there is - a cached token available (typically in your viewDidLoad). + You should check if the type of token you expect is present as a singleton instance, either `AccessToken.current` + or `AuthenticationToken.current` before calling any of the login methods to see if there is a cached token + available. A standard place to do this is in `viewDidLoad`. - If you are managing your own token instances outside of "currentAccessToken", you will need to set - "currentAccessToken" before calling logIn* to authorize further permissions on your tokens. + @warning If you are managing your own token instances outside of `AccessToken.current`, you will need to set + `AccessToken.current` before calling any of the login methods to authorize further permissions on your tokens. */ NS_SWIFT_NAME(LoginManager) @interface FBSDKLoginManager : NSObject @@ -123,6 +131,7 @@ NS_SWIFT_NAME(LoginManager) /** Logs the user in or authorizes additional permissions. + @param permissions the optional array of permissions. Note this is converted to NSSet and is only an NSArray for the convenience of literal syntax. @param fromViewController the view controller to present from. If nil, the topmost view controller will be @@ -130,30 +139,73 @@ NS_SWIFT_NAME(LoginManager) @param handler the callback. Use this method when asking for read permissions. You should only ask for permissions when they - are needed and explain the value to the user. You can inspect the result.declinedPermissions to also - provide more information to the user if they decline permissions. - - This method will present UI the user. You typically should check if `[FBSDKAccessToken currentAccessToken]` - already contains the permissions you need before asking to reduce unnecessary app switching. For example, - you could make that check at viewDidLoad. - You can only do one login call at a time. Calling a login method before the completion handler is called - on a previous login will return an error. + are needed and explain the value to the user. You can inspect the `FBSDKLoginManagerLoginResultBlock`'s + `result.declinedPermissions` to provide more information to the user if they decline permissions. + You typically should check if `AccessToken.current` already contains the permissions you need before + asking to reduce unnecessary login attempts. For example, you could perform that check in `viewDidLoad`. + + @warning You can only perform one login call at a time. Calling a login method before the completion handler is called + on a previous login attempt will result in an error. + @warning This method will present a UI to the user and thus should be called on the main thread. */ - (void)logInWithPermissions:(NSArray *)permissions - fromViewController:(nullable UIViewController *)fromViewController - handler:(nullable FBSDKLoginManagerLoginResultBlock)handler + fromViewController:(nullable UIViewController *)fromViewController + handler:(nullable FBSDKLoginManagerLoginResultBlock)handler NS_SWIFT_NAME(logIn(permissions:from:handler:)); /** - Requests user's permission to reathorize application's data access, after it has expired due to inactivity. - @param fromViewController the view controller to present from. If nil, the topmost view controller will be - automatically determined as best as possible. + Logs the user in or authorizes additional permissions. + + @param viewController the view controller from which to present the login UI. If nil, the topmost view + controller will be automatically determined and used. + @param configuration the login configuration to use. + @param completion the login completion handler. + + Use this method when asking for permissions. You should only ask for permissions when they + are needed and the value should be explained to the user. You can inspect the + `FBSDKLoginManagerLoginResultBlock`'s `result.declinedPermissions` to provide more information + to the user if they decline permissions. + To reduce unnecessary login attempts, you should typically check if `AccessToken.current` + already contains the permissions you need. If it does, you probably do not need to call this method. + + @warning You can only perform one login call at a time. Calling a login method before the completion handler is called + on a previous login attempt will result in an error. + @warning This method will present a UI to the user and thus should be called on the main thread. + */ +- (void)logInFromViewController:(nullable UIViewController *)viewController + configuration:(FBSDKLoginConfiguration *)configuration + completion:(FBSDKLoginManagerLoginResultBlock)completion +NS_REFINED_FOR_SWIFT; + +/** + Logs the user in with the given deep link url. Will only log user in if the given url contains valid login data. + @param url the deep link url @param handler the callback. - Use this method when you need to reathorize your app's access to user data via Graph API, after such an access has expired. - You should provide as much context to the user as possible as to why you need to reauthorize the access, the scope of - access being reathorized, and what added value your app provides when the access is reathorized. - You can inspect the result.declinedPermissions to also provide more information to the user if they decline permissions. - This method will present UI the user. You typically should call this if `[FBSDKAccessToken isDataAccessExpired]` returns true. + +This method will present a UI to the user and thus should be called on the main thread. +This method should be called with the url from the openURL method. + + @warning This method will present a UI to the user and thus should be called on the main thread. + */ +- (void)logInWithURL:(NSURL *)url + handler:(nullable FBSDKLoginManagerLoginResultBlock)handler +NS_SWIFT_NAME(logIn(url:handler:)); + +/** + Requests user's permission to reathorize application's data access, after it has expired due to inactivity. + @param fromViewController the view controller from which to present the login UI. If nil, the topmost view + controller will be automatically determined and used. + @param handler the callback. + +Use this method when you need to reathorize your app's access to user data via the Graph API. +You should only call this after access has expired. +You should provide as much context to the user as possible as to why you need to reauthorize the access, the +scope of access being reathorized, and what added value your app provides when the access is reathorized. +You can inspect the `result.declinedPermissions` to determine if you should provide more information to the +user based on any declined permissions. + + @warning This method will reauthorize using a `LoginConfiguration` with `FBSDKLoginTracking` set to `.enabled`. + @warning This method will present UI the user. You typically should call this if `AccessToken.isDataAccessExpired` is true. */ - (void)reauthorizeDataAccess:(UIViewController *)fromViewController handler:(FBSDKLoginManagerLoginResultBlock)handler @@ -162,7 +214,9 @@ NS_SWIFT_NAME(reauthorizeDataAccess(from:handler:)); /** Logs the user out - This calls [FBSDKAccessToken setCurrentAccessToken:nil] and [FBSDKProfile setCurrentProfile:nil]. + This nils out the singleton instances of `AccessToken` `AuthenticationToken` and `Profle`. + + @note This is only a client side logout. It will not log the user out of their Facebook account. */ - (void)logOut; diff --git a/FBSDKLoginKit/FBSDKLoginKit/FBSDKLoginManager.m b/FBSDKLoginKit/FBSDKLoginKit/FBSDKLoginManager.m index a001ce857d..5c6447b0be 100644 --- a/FBSDKLoginKit/FBSDKLoginKit/FBSDKLoginManager.m +++ b/FBSDKLoginKit/FBSDKLoginKit/FBSDKLoginManager.m @@ -20,31 +20,34 @@ #if !TARGET_OS_TV -#import "FBSDKLoginManager+Internal.h" -#import "FBSDKLoginManagerLoginResult+Internal.h" - -#ifdef SWIFT_PACKAGE -#import "FBSDKAccessToken.h" -#import "FBSDKSettings.h" -#else -#import -#endif - -#ifdef FBSDKCOCOAPODS -#import -#else -#import "FBSDKCoreKit+Internal.h" -#endif - -#import "_FBSDKLoginRecoveryAttempter.h" -#import "FBSDKLoginCompletion.h" -#import "FBSDKLoginConstants.h" -#import "FBSDKLoginError.h" -#import "FBSDKLoginManagerLogger.h" -#import "FBSDKLoginUtility.h" + #import "FBSDKLoginManager+Internal.h" + + #import "FBSDKLoginManagerLoginResult+Internal.h" + + #ifdef SWIFT_PACKAGE + #import "FBSDKAccessToken.h" + #import "FBSDKSettings.h" + #else + #import + #endif + + #ifdef FBSDKCOCOAPODS + #import + #else + #import "FBSDKCoreKit+Internal.h" + #endif + + #import "FBSDKLoginCompletion.h" + #import "FBSDKLoginConstants.h" + #import "FBSDKLoginError.h" + #import "FBSDKLoginManagerLogger.h" + #import "FBSDKLoginUtility.h" + #import "FBSDKPermission.h" + #import "_FBSDKLoginRecoveryAttempter.h" static int const FBClientStateChallengeLength = 20; static NSString *const FBSDKExpectedChallengeKey = @"expected_login_challenge"; +static NSString *const FBSDKExpectedNonceKey = @"expected_login_nonce"; static NSString *const FBSDKOauthPath = @"/dialog/oauth"; static NSString *const SFVCCanceledLogin = @"com.apple.SafariServices.Authentication"; static NSString *const ASCanceledLogin = @"com.apple.AuthenticationServices.WebAuthenticationSession"; @@ -53,20 +56,13 @@ FBSDKLoginAuthType FBSDKLoginAuthTypeRerequest = @"rerequest"; FBSDKLoginAuthType FBSDKLoginAuthTypeReauthorize = @"reauthorize"; -typedef NS_ENUM(NSInteger, FBSDKLoginManagerState) { - FBSDKLoginManagerStateIdle, - // We received a call to start login. - FBSDKLoginManagerStateStart, - // We're calling out to the Facebook app or Safari to perform a log in - FBSDKLoginManagerStatePerformingLogin, -}; - @implementation FBSDKLoginManager { FBSDKLoginManagerLoginResultBlock _handler; FBSDKLoginManagerLogger *_logger; FBSDKLoginManagerState _state; FBSDKKeychainStore *_keychainStore; + FBSDKLoginConfiguration *_configuration; BOOL _usedSFAuthSession; } @@ -89,16 +85,40 @@ - (instancetype)init return self; } -- (void)logInWithPermissions:(NSArray *)permissions - fromViewController:(UIViewController *)fromViewController - handler:(FBSDKLoginManagerLoginResultBlock)handler +- (void)logInFromViewController:(UIViewController *)viewController + configuration:(FBSDKLoginConfiguration *)configuration + completion:(FBSDKLoginManagerLoginResultBlock)completion { if (![self validateLoginStartState]) { return; } - self.fromViewController = fromViewController; - NSSet *permissionSet = [NSSet setWithArray:permissions]; - [self logInWithPermissions:permissionSet handler:handler]; + if (!configuration) { + NSString *failureMessage = @"Cannot login without a valid login configuration. Please make sure the `LoginConfiguration` provided is non-nil"; + [FBSDKLogger singleShotLogEntry:FBSDKLoggingBehaviorDeveloperErrors + logEntry:failureMessage]; + NSError *error = [FBSDKError errorWithCode:FBSDKErrorInvalidArgument message:failureMessage]; + + _handler = [completion copy]; + [self invokeHandler:nil error:error]; + return; + } + + self.fromViewController = viewController; + _configuration = configuration; + _requestedPermissions = configuration.requestedPermissions; + + [self logInWithPermissions:configuration.requestedPermissions handler:completion]; +} + +- (void)logInWithPermissions:(NSArray *)permissions + fromViewController:(UIViewController *)viewController + handler:(FBSDKLoginManagerLoginResultBlock)handler +{ + FBSDKLoginConfiguration *config = [[FBSDKLoginConfiguration alloc] initWithPermissions:permissions + tracking:FBSDKLoginTrackingEnabled]; + [self logInFromViewController:viewController + configuration:config + completion:handler]; } - (void)reauthorizeDataAccess:(UIViewController *)fromViewController handler:(FBSDKLoginManagerLoginResultBlock)handler @@ -110,24 +130,38 @@ - (void)reauthorizeDataAccess:(UIViewController *)fromViewController handler:(FB [self reauthorizeDataAccess:handler]; } - - (void)logOut { [FBSDKAccessToken setCurrentAccessToken:nil]; + [FBSDKAuthenticationToken setCurrentAuthenticationToken:nil]; [FBSDKProfile setCurrentProfile:nil]; } -#pragma mark - Private - -- (void)raiseLoginException:(NSException *)exception +- (void)logInWithURL:(NSURL *)url + handler:(nullable FBSDKLoginManagerLoginResultBlock)handler { - _state = FBSDKLoginManagerStateIdle; - [exception raise]; + FBSDKServerConfiguration *serverConfiguration = [FBSDKServerConfigurationManager cachedServerConfiguration]; + _logger = [[FBSDKLoginManagerLogger alloc] initWithLoggingToken:serverConfiguration.loggingToken]; + _handler = [handler copy]; + + [_logger startSessionForLoginManager:self]; + [_logger startAuthMethod:FBSDKLoginManagerLoggerAuthMethod_Applink]; + + NSDictionary *params = [self logInParametersFromURL:url]; + if (params) { + id completer = [[FBSDKLoginURLCompleter alloc] initWithURLParameters:params appID:[FBSDKSettings appID]]; + [completer completeLoginWithHandler:^(FBSDKLoginCompletionParameters *parameters) { + [self completeAuthentication:parameters expectChallenge:NO]; + }]; + } } + #pragma mark - Private + - (void)handleImplicitCancelOfLogIn { FBSDKLoginManagerLoginResult *result = [[FBSDKLoginManagerLoginResult alloc] initWithToken:nil + authenticationToken:nil isCancelled:YES grantedPermissions:NSSet.set declinedPermissions:NSSet.set]; @@ -153,7 +187,7 @@ - (BOOL)validateLoginStartState formatString:@"%@", errorStr]; return NO; } - case FBSDKLoginManagerStatePerformingLogin:{ + case FBSDKLoginManagerStatePerformingLogin: { [self handleImplicitCancelOfLogIn]; return YES; } @@ -168,130 +202,72 @@ - (BOOL)isPerformingLogin return _state == FBSDKLoginManagerStatePerformingLogin; } -- (void)assertPermissions:(NSArray *)permissions -{ - for (NSString *permission in permissions) { - if (![permission isKindOfClass:[NSString class]]) { - [self raiseLoginException:[NSException exceptionWithName:NSInvalidArgumentException - reason:@"Permissions must be string values." - userInfo:nil]]; - } - if ([permission rangeOfString:@","].location != NSNotFound) { - [self raiseLoginException:[NSException exceptionWithName:NSInvalidArgumentException - reason:@"Permissions should each be specified in separate string values in the array." - userInfo:nil]]; - } - } -} - - (void)completeAuthentication:(FBSDKLoginCompletionParameters *)parameters expectChallenge:(BOOL)expectChallenge { - NSSet *recentlyGrantedPermissions = nil; - NSSet *recentlyDeclinedPermissions = nil; FBSDKLoginManagerLoginResult *result = nil; - NSError *error = parameters.error; - - NSString *tokenString = parameters.accessTokenString; - BOOL cancelled = (tokenString == nil); - BOOL challengePassed = YES; - if (expectChallenge) { - // Perform this check early so we be sure to clear expected challenge in all cases. - NSString *challengeReceived = parameters.challenge; - NSString *challengeExpected = [[self loadExpectedChallenge] stringByReplacingOccurrencesOfString:@"+" withString:@" "]; - if (![challengeExpected isEqualToString:challengeReceived]) { - challengePassed = NO; - } + NSError *error = parameters.error; + NSString *accessTokenString = parameters.accessTokenString; + BOOL cancelled = ((accessTokenString == nil) && (parameters.authenticationToken == nil)); - // Don't overwrite an existing error, if any. - if (!error && !cancelled && !challengePassed) { - error = [NSError fbErrorForFailedLoginWithCode:FBSDKLoginErrorBadChallengeString]; - } + if (expectChallenge && !cancelled && !error) { + error = [self _verifyChallengeWithCompletionParameters:parameters]; } - [self storeExpectedChallenge:nil]; if (!error) { if (!cancelled) { - NSSet *grantedPermissions = parameters.permissions; - NSSet *declinedPermissions = parameters.declinedPermissions; - - [self determineRecentlyGrantedPermissions:&recentlyGrantedPermissions - recentlyDeclinedPermissions:&recentlyDeclinedPermissions - forGrantedPermission:grantedPermissions - declinedPermissions:declinedPermissions]; - - if (recentlyGrantedPermissions.count > 0) { - FBSDKAccessToken *token = [[FBSDKAccessToken alloc] initWithTokenString:tokenString - permissions:grantedPermissions.allObjects - declinedPermissions:declinedPermissions.allObjects - expiredPermissions:@[] - appID:parameters.appID - userID:parameters.userID - expirationDate:parameters.expirationDate - refreshDate:[NSDate date] - dataAccessExpirationDate:parameters.dataAccessExpirationDate - graphDomain:parameters.graphDomain]; - result = [[FBSDKLoginManagerLoginResult alloc] initWithToken:token - isCancelled:NO - grantedPermissions:recentlyGrantedPermissions - declinedPermissions:recentlyDeclinedPermissions]; - - if ([FBSDKAccessToken currentAccessToken]) { - [self validateReauthentication:[FBSDKAccessToken currentAccessToken] withResult:result]; - // in a reauth, short circuit and let the login handler be called when the validation finishes. - return; - } - } - } + result = [self successResultFromParameters:parameters]; - if (cancelled || recentlyGrantedPermissions.count == 0) { - NSSet *declinedPermissions = nil; - if ([FBSDKAccessToken currentAccessToken] != nil) { - // Always include the list of declined permissions from this login request - // if an access token is already cached by the SDK - declinedPermissions = recentlyDeclinedPermissions; + if (result.token && FBSDKAccessToken.currentAccessToken) { + [self validateReauthentication:FBSDKAccessToken.currentAccessToken withResult:result]; + // in a reauth, short circuit and let the login handler be called when the validation finishes. + return; } - - result = [[FBSDKLoginManagerLoginResult alloc] initWithToken:nil - isCancelled:cancelled - grantedPermissions:NSSet.set - declinedPermissions:declinedPermissions]; + } else { + result = [self cancelledResultFromParameters:parameters]; } } - if (result.token) { - [FBSDKAccessToken setCurrentAccessToken:result.token]; - } + [self _setGlobalPropertiesWithParameters:parameters result:result]; [self invokeHandler:result error:error]; } -- (void)determineRecentlyGrantedPermissions:(NSSet **)recentlyGrantedPermissionsRef - recentlyDeclinedPermissions:(NSSet **)recentlyDeclinedPermissionsRef - forGrantedPermission:(NSSet *)grantedPermissions - declinedPermissions:(NSSet *)declinedPermissions -{ - NSMutableSet *recentlyGrantedPermissions = [grantedPermissions mutableCopy]; - NSSet *previouslyGrantedPermissions = ([FBSDKAccessToken currentAccessToken] ? - [FBSDKAccessToken currentAccessToken].permissions : - nil); - if (previouslyGrantedPermissions.count > 0) { - // If there were no requested permissions for this auth - treat all permissions as granted. - // Otherwise this is a reauth, so recentlyGranted should be a subset of what was requested. - if (_requestedPermissions.count != 0) { - [recentlyGrantedPermissions intersectSet:_requestedPermissions]; - } +- (void)_setGlobalPropertiesWithParameters:(FBSDKLoginCompletionParameters *)parameters + result:(FBSDKLoginManagerLoginResult *)result +{ + BOOL hasNewAuthenticationToken = (parameters.authenticationToken != nil); + BOOL hasNewOrUpdatedAccessToken = (result.token != nil); + + if (!hasNewAuthenticationToken && !hasNewOrUpdatedAccessToken) { + // Assume cancellation. Don't do anything + } else { + [self _setSharedAuthenticationToken:parameters.authenticationToken + accessToken:result.token + profile:parameters.profile]; } +} - NSMutableSet *recentlyDeclinedPermissions = [_requestedPermissions mutableCopy]; - [recentlyDeclinedPermissions intersectSet:declinedPermissions]; +/// Helper for setting global properties +- (void)_setSharedAuthenticationToken:(FBSDKAuthenticationToken *_Nullable)authToken + accessToken:(FBSDKAccessToken *_Nullable)accessToken + profile:(FBSDKProfile *_Nullable)profile +{ + FBSDKAuthenticationToken.currentAuthenticationToken = authToken; + FBSDKAccessToken.currentAccessToken = accessToken; + FBSDKProfile.currentProfile = profile; +} - if (recentlyGrantedPermissionsRef != NULL) { - *recentlyGrantedPermissionsRef = [recentlyGrantedPermissions copy]; - } - if (recentlyDeclinedPermissionsRef != NULL) { - *recentlyDeclinedPermissionsRef = [recentlyDeclinedPermissions copy]; +/// Returns an error if a stored challenge cannot be obtained from the completion parameters +- (NSError *)_verifyChallengeWithCompletionParameters:(FBSDKLoginCompletionParameters *)parameters +{ + NSString *challengeReceived = parameters.challenge; + NSString *challengeExpected = [[self loadExpectedChallenge] stringByReplacingOccurrencesOfString:@"+" withString:@" "]; + if (![challengeExpected isEqualToString:challengeReceived]) { + return [NSError fbErrorForFailedLoginWithCode:FBSDKLoginErrorBadChallengeString]; + } else { + return nil; } } @@ -299,6 +275,7 @@ - (void)invokeHandler:(FBSDKLoginManagerLoginResult *)result error:(NSError *)er { [_logger endLoginWithResult:result error:error]; [_logger endSession]; + [_logger postLoginHeartbeat]; _logger = nil; _state = FBSDKLoginManagerStateIdle; @@ -321,13 +298,27 @@ - (NSString *)loadExpectedChallenge return [_keychainStore stringForKey:FBSDKExpectedChallengeKey]; } -- (NSDictionary *)logInParametersWithPermissions:(NSSet *)permissions serverConfiguration:(FBSDKServerConfiguration *)serverConfiguration +- (NSString *)loadExpectedNonce +{ + return [_keychainStore stringForKey:FBSDKExpectedNonceKey]; +} + +- (NSDictionary *)logInParametersWithConfiguration:(FBSDKLoginConfiguration *)configuration + serverConfiguration:(FBSDKServerConfiguration *)serverConfiguration { + // Making sure configuration is not nil in case this method gets called + // internally without specifying a cofiguration. + if (!configuration) { + NSString *failureMessage = @"Unable to perform login."; + NSError *error = [FBSDKError errorWithCode:FBSDKErrorUnknown message:failureMessage]; + [self invokeHandler:nil error:error]; + return nil; + } + [FBSDKInternalUtility validateURLSchemes]; NSMutableDictionary *loginParams = [NSMutableDictionary dictionary]; [FBSDKTypeUtility dictionary:loginParams setObject:[FBSDKSettings appID] forKey:@"client_id"]; - [FBSDKTypeUtility dictionary:loginParams setObject:@"token_or_nonce,signed_request,graph_domain" forKey:@"response_type"]; [FBSDKTypeUtility dictionary:loginParams setObject:@"fbconnect://success" forKey:@"redirect_uri"]; [FBSDKTypeUtility dictionary:loginParams setObject:@"touch" forKey:@"display"]; [FBSDKTypeUtility dictionary:loginParams setObject:@"ios" forKey:@"sdk"]; @@ -339,17 +330,37 @@ - (NSDictionary *)logInParametersWithPermissions:(NSSet *)permissions serverConf long long cbtInMilliseconds = round(1000 * [NSDate date].timeIntervalSince1970); [FBSDKTypeUtility dictionary:loginParams setObject:@(cbtInMilliseconds) forKey:@"cbt"]; [FBSDKTypeUtility dictionary:loginParams setObject:[FBSDKSettings isAutoLogAppEventsEnabled] ? @1 : @0 forKey:@"ies"]; - [FBSDKTypeUtility dictionary:loginParams setObject:[FBSDKSettings appURLSchemeSuffix] forKey:@"local_client_id"]; [FBSDKTypeUtility dictionary:loginParams setObject:[FBSDKLoginUtility stringForAudience:self.defaultAudience] forKey:@"default_audience"]; + + NSSet *permissions = [configuration.requestedPermissions setByAddingObject:[[FBSDKPermission alloc]initWithString:@"openid"]]; [FBSDKTypeUtility dictionary:loginParams setObject:[permissions.allObjects componentsJoinedByString:@","] forKey:@"scope"]; NSString *expectedChallenge = [FBSDKLoginManager stringForChallenge]; - NSDictionary *state = @{@"challenge": [FBSDKUtility URLEncode:expectedChallenge]}; + NSDictionary *state = @{@"challenge" : [FBSDKUtility URLEncode:expectedChallenge]}; [FBSDKTypeUtility dictionary:loginParams setObject:[FBSDKBasicUtility JSONStringForObject:state error:NULL invalidObjectHandler:nil] forKey:@"state"]; - [self storeExpectedChallenge:expectedChallenge]; + NSString *responseType; + NSString *tp; + + switch (configuration.tracking) { + case FBSDKLoginTrackingLimited: + responseType = @"id_token"; + tp = @"ios_14_do_not_track"; + break; + case FBSDKLoginTrackingEnabled: + responseType = @"id_token,token_or_nonce,signed_request,graph_domain"; + tp = @"ios_14_can_track"; + break; + } + + [FBSDKTypeUtility dictionary:loginParams setObject:responseType forKey:@"response_type"]; + [FBSDKTypeUtility dictionary:loginParams setObject:tp forKey:@"tp"]; + + [FBSDKTypeUtility dictionary:loginParams setObject:configuration.nonce forKey:@"nonce"]; + [self storeExpectedNonce:configuration.nonce keychainStore:_keychainStore]; + return loginParams; } @@ -359,20 +370,47 @@ - (void)logInWithPermissions:(NSSet *)permissions handler:(FBSDKLoginManagerLogi _logger = [[FBSDKLoginManagerLogger alloc] initWithLoggingToken:serverConfiguration.loggingToken]; _handler = [handler copy]; - _requestedPermissions = permissions; [_logger startSessionForLoginManager:self]; [self logIn]; } +- (NSDictionary *)logInParametersFromURL:(NSURL *)url +{ + NSError *error = nil; + FBSDKURL *parsedUrl = [FBSDKURL URLWithURL:url]; + NSDictionary *extras = parsedUrl.appLinkExtras; + + if (extras) { + NSString *fbLoginDataString = extras[@"fb_login"]; + NSDictionary *fbLoginData = [FBSDKTypeUtility dictionaryValue:[FBSDKBasicUtility objectForJSONString:fbLoginDataString error:&error]]; + if (!error && fbLoginData) { + return fbLoginData; + } + } + error = error ?: [FBSDKError errorWithCode:FBSDKLoginErrorUnknown message:@"Failed to parse deep link url for login data"]; + [self invokeHandler:nil error:error]; + return nil; +} + - (void)reauthorizeDataAccess:(FBSDKLoginManagerLoginResultBlock)handler { + if (!FBSDKAccessToken.currentAccessToken) { + NSString *errorMessage = @"Must have an access token for which to reauthorize data access"; + NSError *error = [FBSDKError errorWithDomain:FBSDKLoginErrorDomain + code:FBSDKLoginErrorMissingAccessToken + message:errorMessage]; + [FBSDKLogger singleShotLogEntry:FBSDKLoggingBehaviorDeveloperErrors logEntry:errorMessage]; + handler(nil, error); + return; + } FBSDKServerConfiguration *serverConfiguration = [FBSDKServerConfigurationManager cachedServerConfiguration]; _logger = [[FBSDKLoginManagerLogger alloc] initWithLoggingToken:serverConfiguration.loggingToken]; _handler = [handler copy]; // Don't need to pass permissions for data reauthorization. _requestedPermissions = [NSSet set]; + _configuration = [[FBSDKLoginConfiguration alloc] initWithTracking:FBSDKLoginTrackingEnabled]; self.authType = FBSDKLoginAuthTypeReauthorize; [_logger startSessionForLoginManager:self]; [self logIn]; @@ -381,14 +419,14 @@ - (void)reauthorizeDataAccess:(FBSDKLoginManagerLoginResultBlock)handler - (void)logIn { FBSDKServerConfiguration *serverConfiguration = [FBSDKServerConfigurationManager cachedServerConfiguration]; - NSDictionary *loginParams = [self logInParametersWithPermissions:_requestedPermissions serverConfiguration:serverConfiguration]; + NSDictionary *loginParams = [self logInParametersWithConfiguration:_configuration serverConfiguration:serverConfiguration]; self->_usedSFAuthSession = NO; - void(^completion)(BOOL, NSError *) = ^void(BOOL didPerformLogIn, NSError *error) { + void (^completion)(BOOL, NSError *) = ^void (BOOL didPerformLogIn, NSError *error) { if (didPerformLogIn) { self->_state = FBSDKLoginManagerStatePerformingLogin; - } else if ([error.domain isEqualToString:SFVCCanceledLogin] || - [error.domain isEqualToString:ASCanceledLogin]) { + } else if ([error.domain isEqualToString:SFVCCanceledLogin] + || [error.domain isEqualToString:ASCanceledLogin]) { [self handleImplicitCancelOfLogIn]; } else { if (!error) { @@ -400,8 +438,8 @@ - (void)logIn [self performBrowserLogInWithParameters:loginParams handler:^(BOOL openedURL, NSError *openedURLError) { - completion(openedURL, openedURLError); - }]; + completion(openedURL, openedURLError); + }]; } - (void)storeExpectedChallenge:(NSString *)challengeExpected @@ -411,7 +449,15 @@ - (void)storeExpectedChallenge:(NSString *)challengeExpected accessibility:[FBSDKDynamicFrameworkLoader loadkSecAttrAccessibleAfterFirstUnlockThisDeviceOnly]]; } -+ (NSString *)stringForChallenge { +- (void)storeExpectedNonce:(NSString *)nonceExpected keychainStore:(FBSDKKeychainStore *)keychainStore +{ + [keychainStore setString:nonceExpected + forKey:FBSDKExpectedNonceKey + accessibility:[FBSDKDynamicFrameworkLoader loadkSecAttrAccessibleAfterFirstUnlockThisDeviceOnly]]; +} + ++ (NSString *)stringForChallenge +{ NSString *challenge = [FBSDKCrypto randomString:FBClientStateChallengeLength]; return [challenge stringByReplacingOccurrencesOfString:@"+" withString:@"="]; @@ -420,7 +466,7 @@ + (NSString *)stringForChallenge { - (void)validateReauthentication:(FBSDKAccessToken *)currentToken withResult:(FBSDKLoginManagerLoginResult *)loginResult { FBSDKGraphRequest *requestMe = [[FBSDKGraphRequest alloc] initWithGraphPath:@"me" - parameters:@{@"fields":@""} + parameters:@{@"fields" : @""} tokenString:loginResult.token.tokenString HTTPMethod:nil flags:FBSDKGraphRequestFlagDoNotInvalidateTokenOnError | FBSDKGraphRequestFlagDisableErrorRecovery]; @@ -435,23 +481,11 @@ - (void)validateReauthentication:(FBSDKAccessToken *)currentToken withResult:(FB NSError *resultError = [NSError errorWithDomain:FBSDKLoginErrorDomain code:FBSDKLoginErrorUserMismatch userInfo:userInfo]; - [self invokeHandler:nil error:resultError]; + [self invokeHandler:nil error:resultError]; } }]; } -#pragma mark - Test Methods - -- (void)setHandler:(FBSDKLoginManagerLoginResultBlock)handler -{ - _handler = [handler copy]; -} - -- (void)setRequestedPermissions:(NSSet *)requestedPermissions -{ - _requestedPermissions = [requestedPermissions copy]; -} - // change bool to auth method string. - (void)performBrowserLogInWithParameters:(NSDictionary *)loginParams handler:(FBSDKBrowserLoginSuccessBlock)handler @@ -470,8 +504,8 @@ - (void)performBrowserLogInWithParameters:(NSDictionary *)loginParams if (!error) { NSMutableDictionary *browserParams = [loginParams mutableCopy]; [FBSDKTypeUtility dictionary:browserParams - setObject:redirectURL - forKey:@"redirect_uri"]; + setObject:redirectURL + forKey:@"redirect_uri"]; authURL = [FBSDKInternalUtility facebookURLWithHostPrefix:@"m." path:FBSDKOauthPath queryParameters:browserParams @@ -481,7 +515,7 @@ - (void)performBrowserLogInWithParameters:(NSDictionary *)loginParams [_logger startAuthMethod:authMethod]; if (authURL) { - void(^handlerWrapper)(BOOL, NSError*) = ^(BOOL didOpen, NSError *anError) { + void (^handlerWrapper)(BOOL, NSError *) = ^(BOOL didOpen, NSError *anError) { if (handler) { handler(didOpen, anError); } @@ -491,9 +525,9 @@ - (void)performBrowserLogInWithParameters:(NSDictionary *)loginParams // Note based on above, authURL must be a http scheme. If that changes, add a guard, otherwise SFVC can throw self->_usedSFAuthSession = YES; [[FBSDKBridgeAPI sharedInstance] openURLWithSafariViewController:authURL - sender:self - fromViewController:self.fromViewController - handler:handlerWrapper]; + sender:self + fromViewController:self.fromViewController + handler:handlerWrapper]; } else { [[FBSDKBridgeAPI sharedInstance] openURL:authURL sender:self handler:handlerWrapper]; } @@ -505,7 +539,106 @@ - (void)performBrowserLogInWithParameters:(NSDictionary *)loginParams } } -#pragma mark - FBSDKURLOpening +- (FBSDKLoginManagerLoginResult *)cancelledResultFromParameters:(FBSDKLoginCompletionParameters *)parameters +{ + NSSet *declinedPermissions = nil; + if (FBSDKAccessToken.currentAccessToken != nil) { + // Always include the list of declined permissions from this login request + // if an access token is already cached by the SDK + declinedPermissions = [FBSDKPermission rawPermissionsFromPermissions:parameters.declinedPermissions]; + } + + return [[FBSDKLoginManagerLoginResult alloc] initWithToken:nil + authenticationToken:nil + isCancelled:YES + grantedPermissions:NSSet.set + declinedPermissions:declinedPermissions]; +} + +- (FBSDKLoginManagerLoginResult *)successResultFromParameters:(FBSDKLoginCompletionParameters *)parameters +{ + NSSet *grantedPermissions = parameters.permissions; + NSSet *declinedPermissions = parameters.declinedPermissions; + + // Recent permissions are largely based on the existence of an access token + // without an access token the 'recent' permissions will match the + // intersect of the granted permissions and the requested permissions. + // This is important because we want to create a 'result' that accurately reflects + // the currently granted permissions even when there is no access token. + NSSet *recentlyGrantedPermissions = [self recentlyGrantedPermissionsFromGrantedPermissions:grantedPermissions]; + NSSet *recentlyDeclinedPermissions = [self recentlyDeclinedPermissionsFromDeclinedPermissions:declinedPermissions]; + + if (recentlyGrantedPermissions.count > 0) { + NSSet *rawGrantedPermissions = [FBSDKPermission rawPermissionsFromPermissions:grantedPermissions]; + NSSet *rawDeclinedPermissions = [FBSDKPermission rawPermissionsFromPermissions:declinedPermissions]; + NSSet *rawRecentlyGrantedPermissions = [FBSDKPermission rawPermissionsFromPermissions:recentlyGrantedPermissions]; + NSSet *rawRecentlyDeclinedPermissions = [FBSDKPermission rawPermissionsFromPermissions:recentlyDeclinedPermissions]; + + FBSDKAccessToken *token; + if (parameters.accessTokenString) { + token = [[FBSDKAccessToken alloc] initWithTokenString:parameters.accessTokenString + permissions:rawGrantedPermissions.allObjects + declinedPermissions:rawDeclinedPermissions.allObjects + expiredPermissions:@[] + appID:parameters.appID + userID:parameters.userID + expirationDate:parameters.expirationDate + refreshDate:[NSDate date] + dataAccessExpirationDate:parameters.dataAccessExpirationDate + graphDomain:parameters.graphDomain]; + } + + return [[FBSDKLoginManagerLoginResult alloc] initWithToken:token + authenticationToken:parameters.authenticationToken + isCancelled:NO + grantedPermissions:rawRecentlyGrantedPermissions + declinedPermissions:rawRecentlyDeclinedPermissions]; + } else { + return [self cancelledResultFromParameters:parameters]; + } +} + + #pragma mark - Permissions Helpers + +- (NSSet *)recentlyGrantedPermissionsFromGrantedPermissions:(NSSet *)grantedPermissions +{ + NSMutableSet *recentlyGrantedPermissions = grantedPermissions.mutableCopy; + NSSet *previouslyGrantedPermissions = FBSDKAccessToken.currentAccessToken.permissions; + + // If there were no requested permissions for this auth, or no previously granted permissions - treat all permissions as recently granted. + // Otherwise this is a reauth, so recentlyGranted should be a subset of what was requested. + if (previouslyGrantedPermissions.count > 0 && _requestedPermissions.count != 0) { + [recentlyGrantedPermissions intersectSet:_requestedPermissions]; + } + + return recentlyGrantedPermissions; +} + +- (NSSet *)recentlyDeclinedPermissionsFromDeclinedPermissions:(NSSet *)declinedPermissions +{ + NSMutableSet *recentlyDeclinedPermissions = _requestedPermissions.mutableCopy; + [recentlyDeclinedPermissions intersectSet:declinedPermissions]; + return recentlyDeclinedPermissions; +} + + #pragma mark - Test Methods + +- (void)setHandler:(FBSDKLoginManagerLoginResultBlock)handler +{ + _handler = [handler copy]; +} + +- (void)setRequestedPermissions:(NSSet *)requestedPermissions +{ + _requestedPermissions = [FBSDKPermission permissionsFromRawPermissions:requestedPermissions]; +} + +- (FBSDKLoginConfiguration *)configuration +{ + return _configuration; +} + + #pragma mark - FBSDKURLOpening - (BOOL)application:(UIApplication *)application openURL:(NSURL *)url sourceApplication:(NSString *)sourceApplication annotation:(id)annotation { BOOL isFacebookURL = [self canOpenURL:url forApplication:application sourceApplication:sourceApplication annotation:annotation]; @@ -516,7 +649,8 @@ - (BOOL)application:(UIApplication *)application openURL:(NSURL *)url sourceAppl if (isFacebookURL) { NSDictionary *urlParameters = [FBSDKLoginUtility queryParamsFromLoginURL:url]; - id completer = [[FBSDKLoginURLCompleter alloc] initWithURLParameters:urlParameters appID:[FBSDKSettings appID]]; + id completer = [[FBSDKLoginURLCompleter alloc] initWithURLParameters:urlParameters + appID:[FBSDKSettings appID]]; if (_logger == nil) { _logger = [FBSDKLoginManagerLogger loggerFromParameters:urlParameters]; @@ -524,21 +658,22 @@ - (BOOL)application:(UIApplication *)application openURL:(NSURL *)url sourceAppl // any necessary strong reference is maintained by the FBSDKLoginURLCompleter handler [completer completeLoginWithHandler:^(FBSDKLoginCompletionParameters *parameters) { - [self completeAuthentication:parameters expectChallenge:YES]; - }]; + [self completeAuthentication:parameters expectChallenge:YES]; + } nonce:[self loadExpectedNonce]]; + [self storeExpectedNonce:nil keychainStore:_keychainStore]; } return isFacebookURL; } -- (BOOL)canOpenURL:(NSURL *)url - forApplication:(UIApplication *)application - sourceApplication:(NSString *)sourceApplication - annotation:(id)annotation +- (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 - return [url.scheme hasPrefix:[NSString stringWithFormat:@"fb%@", [FBSDKSettings appID]]] && - [url.host isEqualToString:@"authorize"]; + return [url.scheme hasPrefix:[NSString stringWithFormat:@"fb%@", [FBSDKSettings appID]]] + && [url.host isEqualToString:@"authorize"]; } - (void)applicationDidBecomeActive:(UIApplication *)application diff --git a/FBSDKLoginKit/FBSDKLoginKit/FBSDKLoginManagerLoginResult.h b/FBSDKLoginKit/FBSDKLoginKit/FBSDKLoginManagerLoginResult.h index d87d0f66f2..be4279098f 100644 --- a/FBSDKLoginKit/FBSDKLoginKit/FBSDKLoginManagerLoginResult.h +++ b/FBSDKLoginKit/FBSDKLoginKit/FBSDKLoginManagerLoginResult.h @@ -34,6 +34,7 @@ NS_ASSUME_NONNULL_BEGIN @interface LoginManagerLoginResult : NSObject @property (copy, nonatomic, nullable) FBSDKAccessToken *token; +@property (copy, nonatomic, nullable) FBSDKAuthenticationToken *authenticationToken; @property (readonly, nonatomic) BOOL isCancelled; @property (copy, nonatomic) NSSet *grantedPermissions; @property (copy, nonatomic) NSSet *declinedPermissions; @@ -43,6 +44,7 @@ NS_ASSUME_NONNULL_BEGIN #else @class FBSDKAccessToken; +@class FBSDKAuthenticationToken; /** Describes the result of a login attempt. @@ -58,6 +60,11 @@ NS_SWIFT_NAME(LoginManagerLoginResult) */ @property (copy, nonatomic, nullable) FBSDKAccessToken *token; +/** + the authentication token. + */ +@property (copy, nonatomic, nullable) FBSDKAuthenticationToken *authenticationToken; + /** whether the login was cancelled by the user. */ @@ -80,11 +87,13 @@ NS_SWIFT_NAME(LoginManagerLoginResult) /** Initializes a new instance. @param token the access token + @param authenticationToken the authentication token @param isCancelled whether the login was cancelled by the user @param grantedPermissions the set of granted permissions @param declinedPermissions the set of declined permissions */ - (instancetype)initWithToken:(nullable FBSDKAccessToken *)token + authenticationToken:(nullable FBSDKAuthenticationToken *)authenticationToken isCancelled:(BOOL)isCancelled grantedPermissions:(NSSet *)grantedPermissions declinedPermissions:(NSSet *)declinedPermissions diff --git a/FBSDKLoginKit/FBSDKLoginKit/FBSDKLoginManagerLoginResult.m b/FBSDKLoginKit/FBSDKLoginKit/FBSDKLoginManagerLoginResult.m index a3009e4d97..d1e22771eb 100644 --- a/FBSDKLoginKit/FBSDKLoginKit/FBSDKLoginManagerLoginResult.m +++ b/FBSDKLoginKit/FBSDKLoginKit/FBSDKLoginManagerLoginResult.m @@ -20,29 +20,34 @@ #if !TARGET_OS_TV -#import "FBSDKLoginManagerLoginResult+Internal.h" + #import "FBSDKLoginManagerLoginResult+Internal.h" -#ifdef FBSDKCOCOAPODS -#import -#else -#import "FBSDKCoreKit+Internal.h" -#endif + #ifdef FBSDKCOCOAPODS + #import + #else + #import "FBSDKCoreKit+Internal.h" + #endif -@implementation FBSDKLoginManagerLoginResult { +@implementation FBSDKLoginManagerLoginResult +{ NSMutableDictionary *_mutableLoggingExtras; } - (instancetype)initWithToken:(FBSDKAccessToken *)token + authenticationToken:(FBSDKAuthenticationToken *)authenticationToken isCancelled:(BOOL)isCancelled grantedPermissions:(NSSet *)grantedPermissions - declinedPermissions:(NSSet *)declinedPermissions { + declinedPermissions:(NSSet *)declinedPermissions +{ if ((self = [super init])) { _mutableLoggingExtras = [NSMutableDictionary dictionary]; _token = token ? [token copy] : nil; + _authenticationToken = authenticationToken; _isCancelled = isCancelled; _grantedPermissions = [grantedPermissions copy]; _declinedPermissions = [declinedPermissions copy]; - }; + } + ; return self; } diff --git a/FBSDKLoginKit/FBSDKLoginKit/FBSDKLoginTooltipView.m b/FBSDKLoginKit/FBSDKLoginKit/FBSDKLoginTooltipView.m index 7ebe1cb63a..dbbe49fa4d 100644 --- a/FBSDKLoginKit/FBSDKLoginKit/FBSDKLoginTooltipView.m +++ b/FBSDKLoginKit/FBSDKLoginKit/FBSDKLoginTooltipView.m @@ -20,13 +20,13 @@ #if !TARGET_OS_TV -#import "FBSDKLoginTooltipView.h" + #import "FBSDKLoginTooltipView.h" -#ifdef FBSDKCOCOAPODS -#import -#else -#import "FBSDKCoreKit+Internal.h" -#endif + #ifdef FBSDKCOCOAPODS + #import + #else + #import "FBSDKCoreKit+Internal.h" + #endif @interface FBSDKLoginTooltipView () @end @@ -36,9 +36,13 @@ @implementation FBSDKLoginTooltipView - (instancetype)init { NSString *tooltipMessage = - NSLocalizedStringWithDefaultValue(@"LoginTooltip.Message", @"FacebookSDK", [FBSDKInternalUtility bundleForStrings], - @"You're in control - choose what info you want to share with apps.", - @"The message of the FBSDKLoginTooltipView"); + NSLocalizedStringWithDefaultValue( + @"LoginTooltip.Message", + @"FacebookSDK", + [FBSDKInternalUtility bundleForStrings], + @"You're in control - choose what info you want to share with apps.", + @"The message of the FBSDKLoginTooltipView" + ); return [super initWithTagline:nil message:tooltipMessage colorStyle:FBSDKTooltipColorStyleFriendlyBlue]; } @@ -47,7 +51,6 @@ - (void)presentInView:(UIView *)view withArrowPosition:(CGPoint)arrowPosition di if (self.forceDisplay) { [super presentInView:view withArrowPosition:arrowPosition direction:arrowDirection]; } else { - [FBSDKServerConfigurationManager loadServerConfigurationWithCompletionBlock:^(FBSDKServerConfiguration *serverConfiguration, NSError *error) { self.message = serverConfiguration.loginTooltipText; BOOL shouldDisplay = serverConfiguration.loginTooltipEnabled; @@ -67,6 +70,7 @@ - (void)presentInView:(UIView *)view withArrowPosition:(CGPoint)arrowPosition di }]; } } + @end #endif diff --git a/FBSDKLoginKit/FBSDKLoginKit/FBSDKReferralCode.h b/FBSDKLoginKit/FBSDKLoginKit/FBSDKReferralCode.h new file mode 100644 index 0000000000..05ab81a451 --- /dev/null +++ b/FBSDKLoginKit/FBSDKLoginKit/FBSDKReferralCode.h @@ -0,0 +1,52 @@ +// 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 "TargetConditionals.h" + +#if !TARGET_OS_TV + +#import + +NS_ASSUME_NONNULL_BEGIN + +/** + Represent a referral code used in the referral process +*/ +NS_SWIFT_NAME(ReferralCode) +@interface FBSDKReferralCode : NSObject + +- (instancetype)init NS_UNAVAILABLE; ++ (instancetype)new NS_UNAVAILABLE; + +/** + The string value of the referral code +*/ +@property NSString *value; + +/** + Initializes a new instance if the referral code is valid. Otherwise returns nil. + A code is valid if it is non-empty and contains only alphanumeric characters. + @param string the raw string referral code +*/ ++ (nullable instancetype)initWithString:(NSString *)string; + +@end + +NS_ASSUME_NONNULL_END + +#endif diff --git a/FBSDKLoginKit/FBSDKLoginKit/FBSDKReferralCode.m b/FBSDKLoginKit/FBSDKLoginKit/FBSDKReferralCode.m new file mode 100644 index 0000000000..5375d5997d --- /dev/null +++ b/FBSDKLoginKit/FBSDKLoginKit/FBSDKReferralCode.m @@ -0,0 +1,64 @@ +// 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 "TargetConditionals.h" + +#if !TARGET_OS_TV + + #import "FBSDKReferralCode.h" + + #import + +@implementation FBSDKReferralCode + ++ (nullable instancetype)initWithString:(NSString *)string +{ + if (string.length == 0) { + return nil; + } + + NSCharacterSet *alphanumericSet = [NSCharacterSet alphanumericCharacterSet]; + if (![[string stringByTrimmingCharactersInSet:alphanumericSet] isEqualToString:@""]) { + return nil; + } + + FBSDKReferralCode *instance; + if ((instance = [super new])) { + instance.value = string; + } + return instance; +} + +- (BOOL)isEqual:(id)obj +{ + if (![obj isKindOfClass:[FBSDKReferralCode class]]) { + return NO; + } + + FBSDKReferralCode *other = (FBSDKReferralCode *)obj; + return [self.value isEqual:other.value]; +} + +- (NSString *)description +{ + return self.value; +} + +@end + +#endif diff --git a/FBSDKLoginKit/FBSDKLoginKit/FBSDKReferralManager.h b/FBSDKLoginKit/FBSDKLoginKit/FBSDKReferralManager.h new file mode 100644 index 0000000000..f923d54d79 --- /dev/null +++ b/FBSDKLoginKit/FBSDKLoginKit/FBSDKReferralManager.h @@ -0,0 +1,60 @@ +// 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 "TargetConditionals.h" + +#if !TARGET_OS_TV + +#import + +NS_ASSUME_NONNULL_BEGIN + +@class FBSDKReferralManagerResult; + +/** + Describes the call back to the FBSDKReferralManager + @param result the result of the referral + @param error the referral error, if any. + */ +typedef void (^FBSDKReferralManagerResultBlock)(FBSDKReferralManagerResult *_Nullable result, + NSError *_Nullable error) +NS_SWIFT_NAME(ReferralManagerResultBlock); + +/** + `FBSDKReferralManager` provides methods for starting the referral process. +*/ +NS_SWIFT_NAME(ReferralManager) +@interface FBSDKReferralManager : NSObject + +/** + Initialize a new instance with the provided view controller + @param viewController the view controller to present from. If nil, the topmost view controller will be automatically determined as best as possible. + */ +- (instancetype)initWithViewController:(nullable UIViewController *)viewController; + +/** + Open the referral dialog. + @param handler the callback. + */ +-(void)startReferralWithCompletionHandler:(nullable FBSDKReferralManagerResultBlock)handler; + +@end + +NS_ASSUME_NONNULL_END + +#endif diff --git a/FBSDKLoginKit/FBSDKLoginKit/FBSDKReferralManager.m b/FBSDKLoginKit/FBSDKLoginKit/FBSDKReferralManager.m new file mode 100644 index 0000000000..9ddbfecb7c --- /dev/null +++ b/FBSDKLoginKit/FBSDKLoginKit/FBSDKReferralManager.m @@ -0,0 +1,241 @@ +// 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 "TargetConditionals.h" + +#if !TARGET_OS_TV + + #import "FBSDKReferralManager+Internal.h" + + #ifdef FBSDKCOCOAPODS + #import + #else + #import "FBSDKCoreKit+Internal.h" + #endif + + #import "FBSDKLoginConstants.h" + #import "FBSDKReferralManagerLogger.h" + #import "FBSDKReferralManagerResult.h" + +static NSString *const FBSDKReferralPath = @"/dialog/share_referral"; +static NSString *const ReferralCodesKey = @"fb_referral_codes"; +static NSString *const ChalllengeKey = @"state"; +static NSString *const SFVCCanceledLogin = @"com.apple.SafariServices.Authentication"; +static NSString *const ASCanceledLogin = @"com.apple.AuthenticationServices.WebAuthenticationSession"; +static int const FBClientStateChallengeLength = 20; + +@implementation FBSDKReferralManager +{ + UIViewController *_viewController; + FBSDKReferralManagerResultBlock _handler; + FBSDKReferralManagerLogger *_logger; + BOOL _isPerformingReferral; + NSString *_expectedChallenge; +} + +- (instancetype)initWithViewController:(UIViewController *)viewController +{ + self = [super init]; + if (self) { + _viewController = viewController; + } + + return self; +} + +- (void)startReferralWithCompletionHandler:(FBSDKReferralManagerResultBlock)handler +{ + _handler = [handler copy]; + _logger = [FBSDKReferralManagerLogger new]; + + [_logger logReferralStart]; + + @try { + [FBSDKInternalUtility validateURLSchemes]; + } @catch (NSException *exception) { + NSError *error = [FBSDKError errorWithCode:FBSDKLoginErrorUnknown + message:[NSString stringWithFormat:@"%@: %@", exception.name, exception.reason]]; + [self handleReferralError:error]; + return; + } + + NSURL *referralURL = [self referralURL]; + if (referralURL) { + void (^completionHandler)(BOOL, NSError *) = ^(BOOL didOpen, NSError *error) { + [self handleOpenURLComplete:didOpen error:error]; + }; + + [[FBSDKBridgeAPI sharedInstance] openURLWithSafariViewController:referralURL + sender:self + fromViewController:_viewController + handler:completionHandler]; + } +} + +- (NSURL *)referralURL +{ + NSError *error; + NSURL *url; + NSMutableDictionary *params = [NSMutableDictionary dictionary]; + + [FBSDKTypeUtility dictionary:params setObject:FBSDKSettings.appID forKey:@"app_id"]; + + _expectedChallenge = [self stringForChallenge]; + [FBSDKTypeUtility dictionary:params setObject:_expectedChallenge forKey:@"state"]; + + NSURL *redirectURL = [FBSDKInternalUtility appURLWithHost:@"authorize" path:@"" queryParameters:@{} error:&error]; + if (!error) { + [FBSDKTypeUtility dictionary:params setObject:redirectURL forKey:@"redirect_uri"]; + + url = [FBSDKInternalUtility facebookURLWithHostPrefix:@"m." + path:FBSDKReferralPath + queryParameters:params + error:&error]; + } + + if (error || !url) { + error = error ?: [FBSDKError errorWithCode:FBSDKLoginErrorUnknown message:@"Failed to construct referral browser url"]; + [self handleReferralError:error]; + return nil; + } + + return url; +} + +- (NSString *)stringForChallenge +{ + NSString *challenge = [FBSDKCrypto randomString:FBClientStateChallengeLength]; + + return [challenge stringByReplacingOccurrencesOfString:@"+" withString:@"="]; +} + +- (void)invokeHandler:(FBSDKReferralManagerResult *)result error:(NSError *)error +{ + _isPerformingReferral = NO; + [_logger logReferralEnd:result error:error]; + _logger = nil; + + if (_handler) { + _handler(result, error); + _handler = nil; + } +} + +- (void)handleReferralCancel +{ + FBSDKReferralManagerResult *result = [[FBSDKReferralManagerResult alloc] initWithReferralCodes:nil isCancelled:YES]; + [self invokeHandler:result error:(NSError *)nil]; +} + +- (void)handleReferralError:(NSError *)error +{ + [self invokeHandler:nil error:error]; +} + +- (void)handleOpenURLComplete:(BOOL)didOpen error:(NSError *)error +{ + if (didOpen) { + self->_isPerformingReferral = YES; + } else if ([error.domain isEqualToString:SFVCCanceledLogin] + || [error.domain isEqualToString:ASCanceledLogin]) { + [self handleReferralCancel]; + } else { + if (!error) { + error = [FBSDKError errorWithCode:FBSDKLoginErrorUnknown message:@"Failed to open referral dialog"]; + } + [self handleReferralError:error]; + } +} + +- (BOOL)validateChallenge:(NSString *)challenge +{ + BOOL challengeValid = YES; + if (_expectedChallenge && ![_expectedChallenge isEqualToString:challenge]) { + challengeValid = NO; + } + + _expectedChallenge = nil; + return challengeValid; +} + + #pragma mark - FBSDKURLOpening + +- (BOOL)application:(UIApplication *)application openURL:(NSURL *)url sourceApplication:(NSString *)sourceApplication annotation:(id)annotation +{ + BOOL isFacebookURL = [self canOpenURL:url + forApplication:application + sourceApplication:sourceApplication + annotation:annotation]; + + if (isFacebookURL) { + NSError *error; + NSDictionary *params = [FBSDKInternalUtility parametersFromFBURL:url]; + + if (![self validateChallenge:params[ChalllengeKey]]) { + error = [FBSDKError errorWithCode:FBSDKLoginErrorBadChallengeString + message:@"The referral response was missing a valid challenge string"]; + [self handleReferralError:error]; + return YES; + } + + NSString *rawReferralCodes = params[ReferralCodesKey]; + NSArray *referralCodesJSON = [FBSDKCreateJSONFromString(rawReferralCodes, &error) matchArrayOrNil]; + if (!error) { + NSMutableArray *referralCodes = [NSMutableArray array]; + for (FBSDKJSONField *object in referralCodesJSON) { + FBSDKReferralCode *referralCode = [FBSDKReferralCode initWithString:[FBSDKTypeUtility stringValue:[object rawObject]]]; + if (referralCode) { + [FBSDKTypeUtility array:referralCodes addObject:referralCode]; + } + } + + FBSDKReferralManagerResult *result = [[FBSDKReferralManagerResult alloc]initWithReferralCodes:referralCodes isCancelled:NO]; + [self invokeHandler:result error:nil]; + } else { + [self handleReferralError:error]; + } + } else if (_isPerformingReferral) { + [self handleReferralCancel]; + } + + 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 referral request + return [url.scheme hasPrefix:[NSString stringWithFormat:@"fb%@", FBSDKSettings.appID]] + && [url.host isEqualToString:@"authorize"]; +} + +- (void)applicationDidBecomeActive:(UIApplication *)application +{ + // Do nothing since currently we don't switch to external app +} + +- (BOOL)isAuthenticationURL:(NSURL *)url +{ + return [url.path hasSuffix:FBSDKReferralPath]; +} + +@end + +#endif diff --git a/FBSDKLoginKit/FBSDKLoginKit/FBSDKReferralManagerResult.h b/FBSDKLoginKit/FBSDKLoginKit/FBSDKReferralManagerResult.h new file mode 100644 index 0000000000..8406c3036b --- /dev/null +++ b/FBSDKLoginKit/FBSDKLoginKit/FBSDKReferralManagerResult.h @@ -0,0 +1,59 @@ +// 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 "TargetConditionals.h" + +#if !TARGET_OS_TV + +#import + +#import "FBSDKReferralCode.h" + +NS_ASSUME_NONNULL_BEGIN + +/** + Describes the result of a referral request. + */ +NS_SWIFT_NAME(ReferralManagerResult) +@interface FBSDKReferralManagerResult : NSObject + +- (instancetype)init NS_UNAVAILABLE; ++ (instancetype)new NS_UNAVAILABLE; + +/** + whether the referral was cancelled by the user. + */ +@property (readonly, nonatomic) BOOL isCancelled; + +/** + An array of referral codes for each referral made by the user + */ +@property (copy, nonatomic) NSArray *referralCodes; + +/** Initializes a new instance. + @param referralCodes the referral codes + @param isCancelled whether the referral was cancelled by the user + */ +- (instancetype)initWithReferralCodes:(nullable NSArray *)referralCodes + isCancelled:(BOOL)isCancelled +NS_DESIGNATED_INITIALIZER; +@end + +NS_ASSUME_NONNULL_END + +#endif diff --git a/FBSDKLoginKit/FBSDKLoginKit/FBSDKReferralManagerResult.m b/FBSDKLoginKit/FBSDKLoginKit/FBSDKReferralManagerResult.m new file mode 100644 index 0000000000..50f1abc9fc --- /dev/null +++ b/FBSDKLoginKit/FBSDKLoginKit/FBSDKReferralManagerResult.m @@ -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 "TargetConditionals.h" + +#if !TARGET_OS_TV + + #import "FBSDKReferralManagerResult.h" + +@implementation FBSDKReferralManagerResult + +- (instancetype)initWithReferralCodes:(nullable NSArray *)referralCodes + isCancelled:(BOOL)isCancelled +{ + if (self = [super init]) { + _referralCodes = referralCodes; + _isCancelled = isCancelled; + } + ; + return self; +} + +@end + +#endif diff --git a/FBSDKLoginKit/FBSDKLoginKit/FBSDKTooltipView.m b/FBSDKLoginKit/FBSDKLoginKit/FBSDKTooltipView.m index 446ca035d7..8c5884f449 100644 --- a/FBSDKLoginKit/FBSDKLoginKit/FBSDKTooltipView.m +++ b/FBSDKLoginKit/FBSDKLoginKit/FBSDKTooltipView.m @@ -20,34 +20,34 @@ #if !TARGET_OS_TV -#import "FBSDKTooltipView.h" - -#import - -#ifdef FBSDKCOCOAPODS -#import -#else -#import "FBSDKCoreKit+Internal.h" -#endif - -static const CGFloat kTransitionDuration = 0.3; -static const CGFloat kZoomOutScale = 0.001f; -static const CGFloat kZoomInScale = 1.1f; -static const CGFloat kZoomBounceScale = 0.98f; - -static const CGFloat kNUXRectInset = 6; -static const CGFloat kNUXBubbleMargin = 17 - kNUXRectInset; -static const CGFloat kNUXPointMargin = -3; -static const CGFloat kNUXCornerRadius = 4; -static const CGFloat kNUXStrokeLineWidth = 0.5f; -static const CGFloat kNUXSideCap = 6; -static const CGFloat kNUXFontSize = 10; -static const CGFloat kNUXCrossGlyphSize = 11; + #import "FBSDKTooltipView.h" + + #import + + #ifdef FBSDKCOCOAPODS + #import + #else + #import "FBSDKCoreKit+Internal.h" + #endif + +static const CGFloat kTransitionDuration = 0.3; +static const CGFloat kZoomOutScale = 0.001f; +static const CGFloat kZoomInScale = 1.1f; +static const CGFloat kZoomBounceScale = 0.98f; + +static const CGFloat kNUXRectInset = 6; +static const CGFloat kNUXBubbleMargin = 17 - kNUXRectInset; +static const CGFloat kNUXPointMargin = -3; +static const CGFloat kNUXCornerRadius = 4; +static const CGFloat kNUXStrokeLineWidth = 0.5f; +static const CGFloat kNUXSideCap = 6; +static const CGFloat kNUXFontSize = 10; +static const CGFloat kNUXCrossGlyphSize = 11; static CGMutablePathRef _fbsdkCreateUpPointingBubbleWithRect(CGRect rect, CGFloat arrowMidpoint, CGFloat arrowHeight, CGFloat radius); static CGMutablePathRef _fbsdkCreateDownPointingBubbleWithRect(CGRect rect, CGFloat arrowMidpoint, CGFloat arrowHeight, CGFloat radius); -#pragma mark - + #pragma mark - @implementation FBSDKTooltipView { @@ -82,12 +82,12 @@ - (instancetype)initWithTagline:(NSString *)tagline message:(NSString *)message _textLabel.backgroundColor = [UIColor clearColor]; _textLabel.autoresizingMask = UIViewAutoresizingFlexibleRightMargin; _textLabel.numberOfLines = 0; - _textLabel.font = [UIFont boldSystemFontOfSize: kNUXFontSize]; + _textLabel.font = [UIFont boldSystemFontOfSize:kNUXFontSize]; _textLabel.textAlignment = NSTextAlignmentLeft; _arrowHeight = 7; _textPadding = 10; _maximumTextWidth = 185; - _verticalCrossOffset = - 2.5f; + _verticalCrossOffset = -2.5f; _verticalTextOffset = 0; _displayDuration = 6.0; self.colorStyle = colorStyle; @@ -117,7 +117,7 @@ - (void)dealloc [_insideTapGestureRecognizer removeTarget:self action:NULL]; } -#pragma mark - Public Methods + #pragma mark - Public Methods - (void)setMessage:(NSString *)message { @@ -135,7 +135,7 @@ - (void)setTagline:(NSString *)tagline } } -#pragma mark Presentation + #pragma mark Presentation - (void)presentFromView:(UIView *)anchorView { @@ -196,7 +196,7 @@ - (void)dismiss }]; } -#pragma mark Style + #pragma mark Style - (FBSDKTooltipColorStyle)colorStyle { @@ -209,29 +209,29 @@ - (void)setColorStyle:(FBSDKTooltipColorStyle)colorStyle switch (colorStyle) { case FBSDKTooltipColorStyleNeutralGray: _gradientColors = @[ - (id)(FBSDKUIColorWithRGB(0x51, 0x50, 0x4f).CGColor), - (id)(FBSDKUIColorWithRGB(0x2d, 0x2c, 0x2c).CGColor) - ]; - _innerStrokeColor = [UIColor colorWithWhite:0.13f alpha:1.0f]; - _crossCloseGlyphColor = [UIColor colorWithWhite:0.69f alpha:1.0f]; - break; + (id)(FBSDKUIColorWithRGB(0x51, 0x50, 0x4f).CGColor), + (id)(FBSDKUIColorWithRGB(0x2d, 0x2c, 0x2c).CGColor) + ]; + _innerStrokeColor = [UIColor colorWithWhite:0.13f alpha:1.0f]; + _crossCloseGlyphColor = [UIColor colorWithWhite:0.69f alpha:1.0f]; + break; case FBSDKTooltipColorStyleFriendlyBlue: default: _gradientColors = @[ - (id)(FBSDKUIColorWithRGB(0x6e, 0x9c, 0xf5).CGColor), - (id)(FBSDKUIColorWithRGB(0x49, 0x74, 0xc6).CGColor) - ]; - _innerStrokeColor = [UIColor colorWithRed:0.12f green:0.26f blue:0.55f alpha:1.0f]; - _crossCloseGlyphColor = [UIColor colorWithRed:0.60f green:0.73f blue:1.0f alpha:1.0f]; - break; + (id)(FBSDKUIColorWithRGB(0x6e, 0x9c, 0xf5).CGColor), + (id)(FBSDKUIColorWithRGB(0x49, 0x74, 0xc6).CGColor) + ]; + _innerStrokeColor = [UIColor colorWithRed:0.12f green:0.26f blue:0.55f alpha:1.0f]; + _crossCloseGlyphColor = [UIColor colorWithRed:0.60f green:0.73f blue:1.0f alpha:1.0f]; + break; } _textLabel.textColor = [UIColor whiteColor]; } -#pragma mark - Private Methods -#pragma mark Animation + #pragma mark - Private Methods + #pragma mark Animation - (void)animateFadeIn { @@ -243,8 +243,10 @@ - (void)animateFadeIn if (_pointingUp) { zoomOffsetY = -zoomOffsetY; } - self.layer.transform = fbsdkdfl_CATransform3DConcat(fbsdkdfl_CATransform3DMakeScale(kZoomOutScale, kZoomOutScale, kZoomOutScale), - fbsdkdfl_CATransform3DMakeTranslation(zoomOffsetX, zoomOffsetY, 0)); + self.layer.transform = fbsdkdfl_CATransform3DConcat( + fbsdkdfl_CATransform3DMakeScale(kZoomOutScale, kZoomOutScale, kZoomOutScale), + fbsdkdfl_CATransform3DMakeTranslation(zoomOffsetX, zoomOffsetY, 0) + ); self.hidden = NO; // Prepare animation steps @@ -259,7 +261,7 @@ - (void)animateFadeIn } CATransform3D scale = fbsdkdfl_CATransform3DMakeScale(kZoomInScale, kZoomInScale, kZoomInScale); - CATransform3D translate =fbsdkdfl_CATransform3DMakeTranslation(newZoomOffsetX, newZoomOffsetY, 0); + CATransform3D translate = fbsdkdfl_CATransform3DMakeTranslation(newZoomOffsetX, newZoomOffsetY, 0); self.layer.transform = fbsdkdfl_CATransform3DConcat(scale, translate); }; @@ -271,8 +273,10 @@ - (void)animateFadeIn if (self->_pointingUp) { zoomOffsetY2 = -zoomOffsetY2; } - self.layer.transform = fbsdkdfl_CATransform3DConcat(fbsdkdfl_CATransform3DMakeScale(kZoomBounceScale, kZoomBounceScale, kZoomBounceScale), - fbsdkdfl_CATransform3DMakeTranslation(zoomOffsetX2, zoomOffsetY2, 0)); + self.layer.transform = fbsdkdfl_CATransform3DConcat( + fbsdkdfl_CATransform3DMakeScale(kZoomBounceScale, kZoomBounceScale, kZoomBounceScale), + fbsdkdfl_CATransform3DMakeTranslation(zoomOffsetX2, zoomOffsetY2, 0) + ); }; // 3rd Step. @@ -281,21 +285,21 @@ - (void)animateFadeIn }; // Animate 3 steps sequentially - [UIView animateWithDuration:kTransitionDuration/1.5 + [UIView animateWithDuration:kTransitionDuration / 1.5 delay:0 options:UIViewAnimationOptionCurveEaseInOut animations:zoomIn completion:^(BOOL finished) { - [UIView animateWithDuration:kTransitionDuration/2.2 + [UIView animateWithDuration:kTransitionDuration / 2.2 animations:bounceZoom completion:^(BOOL innerFinished) { - [UIView animateWithDuration:kTransitionDuration/5 - animations:normalizeZoom]; - }]; + [UIView animateWithDuration:kTransitionDuration / 5 + animations:normalizeZoom]; + }]; }]; } -- (void) animateFadeOutWithCompletion: (void(^)(void)) completionHandler +- (void)animateFadeOutWithCompletion:(void (^)(void))completionHandler { [UIView animateWithDuration:0.3 delay:0 @@ -304,14 +308,15 @@ - (void) animateFadeOutWithCompletion: (void(^)(void)) completionHandler self.alpha = 0.0; } completion:^(BOOL complete) { - if(completionHandler) + if (completionHandler) { completionHandler(); + } }]; } -#pragma mark Gestures + #pragma mark Gestures -- (void)onTapInTooltip:(UIGestureRecognizer*)sender +- (void)onTapInTooltip:(UIGestureRecognizer *)sender { // ignore incomplete tap gestures if (sender.state != UIGestureRecognizerStateEnded) { @@ -322,7 +327,7 @@ - (void)onTapInTooltip:(UIGestureRecognizer*)sender [self dismiss]; } -#pragma mark Drawing + #pragma mark Drawing CGMutablePathRef _fbsdkCreateUpPointingBubbleWithRect(CGRect rect, CGFloat arrowMidpoint, CGFloat arrowHeight, CGFloat radius) { @@ -396,17 +401,25 @@ - (void)drawRect:(CGRect)rect CGFloat arrowSideMargin = 1 + 0.5f * MAX(kNUXRectInset, _arrowHeight); CGFloat arrowYMarginOffset = _pointingUp ? arrowSideMargin : kNUXRectInset; CGFloat halfStroke = kNUXStrokeLineWidth / 2.0; - CGRect outerRect = CGRectMake(kNUXRectInset + halfStroke, - arrowYMarginOffset + halfStroke, - self.bounds.size.width - 2 * kNUXRectInset - kNUXStrokeLineWidth, - self.bounds.size.height - kNUXRectInset - arrowSideMargin - kNUXStrokeLineWidth); + CGRect outerRect = CGRectMake( + kNUXRectInset + halfStroke, + arrowYMarginOffset + halfStroke, + self.bounds.size.width - 2 * kNUXRectInset - kNUXStrokeLineWidth, + self.bounds.size.height - kNUXRectInset - arrowSideMargin - kNUXStrokeLineWidth + ); outerRect = CGRectInset(outerRect, 5, 5); CGRect innerRect = CGRectInset(outerRect, kNUXStrokeLineWidth, kNUXStrokeLineWidth); - CGRect fillRect = CGRectInset(innerRect, kNUXStrokeLineWidth/2.0, kNUXStrokeLineWidth/2.0); - CGFloat closeCrossGlyphPositionY = MIN(CGRectGetMinY(fillRect) + _textPadding + _verticalCrossOffset, - CGRectGetMidY(fillRect) - 0.5f * kNUXCrossGlyphSize); - CGRect closeCrossGlyphRect = CGRectMake(CGRectGetMaxX(fillRect) - 2 * kNUXFontSize, closeCrossGlyphPositionY, - kNUXCrossGlyphSize, kNUXCrossGlyphSize); + CGRect fillRect = CGRectInset(innerRect, kNUXStrokeLineWidth / 2.0, kNUXStrokeLineWidth / 2.0); + CGFloat closeCrossGlyphPositionY = MIN( + CGRectGetMinY(fillRect) + _textPadding + _verticalCrossOffset, + CGRectGetMidY(fillRect) - 0.5f * kNUXCrossGlyphSize + ); + CGRect closeCrossGlyphRect = CGRectMake( + CGRectGetMaxX(fillRect) - 2 * kNUXFontSize, + closeCrossGlyphPositionY, + kNUXCrossGlyphSize, + kNUXCrossGlyphSize + ); // setup and get paths CGContextRef context = UIGraphicsGetCurrentContext(); @@ -416,27 +429,45 @@ - (void)drawRect:(CGRect)rect CGMutablePathRef crossCloseGlyphPath = _createCloseCrossGlyphWithRect(closeCrossGlyphRect); CGRect gradientRect = fillRect; if (_pointingUp) { - outerPath = _fbsdkCreateUpPointingBubbleWithRect(outerRect, - _arrowMidpoint, _arrowHeight, - kNUXCornerRadius + kNUXStrokeLineWidth); - innerPath = _fbsdkCreateUpPointingBubbleWithRect(innerRect, - _arrowMidpoint, _arrowHeight, - kNUXCornerRadius); - fillPath = _fbsdkCreateUpPointingBubbleWithRect(fillRect, - _arrowMidpoint, _arrowHeight, - kNUXCornerRadius - kNUXStrokeLineWidth); + outerPath = _fbsdkCreateUpPointingBubbleWithRect( + outerRect, + _arrowMidpoint, + _arrowHeight, + kNUXCornerRadius + kNUXStrokeLineWidth + ); + innerPath = _fbsdkCreateUpPointingBubbleWithRect( + innerRect, + _arrowMidpoint, + _arrowHeight, + kNUXCornerRadius + ); + fillPath = _fbsdkCreateUpPointingBubbleWithRect( + fillRect, + _arrowMidpoint, + _arrowHeight, + kNUXCornerRadius - kNUXStrokeLineWidth + ); gradientRect.origin.y -= _arrowHeight; gradientRect.size.height += _arrowHeight; } else { - outerPath = _fbsdkCreateDownPointingBubbleWithRect(outerRect, - _arrowMidpoint, _arrowHeight, - kNUXCornerRadius + kNUXStrokeLineWidth); - innerPath = _fbsdkCreateDownPointingBubbleWithRect(innerRect, - _arrowMidpoint, _arrowHeight, - kNUXCornerRadius); - fillPath = _fbsdkCreateDownPointingBubbleWithRect(fillRect, - _arrowMidpoint, _arrowHeight, - kNUXCornerRadius - kNUXStrokeLineWidth); + outerPath = _fbsdkCreateDownPointingBubbleWithRect( + outerRect, + _arrowMidpoint, + _arrowHeight, + kNUXCornerRadius + kNUXStrokeLineWidth + ); + innerPath = _fbsdkCreateDownPointingBubbleWithRect( + innerRect, + _arrowMidpoint, + _arrowHeight, + kNUXCornerRadius + ); + fillPath = _fbsdkCreateDownPointingBubbleWithRect( + fillRect, + _arrowMidpoint, + _arrowHeight, + kNUXCornerRadius - kNUXStrokeLineWidth + ); gradientRect.size.height += _arrowHeight; } self.layer.shadowPath = outerPath; @@ -467,7 +498,7 @@ - (void)drawRect:(CGRect)rect CFRelease(crossCloseGlyphPath); } -#pragma mark Layout + #pragma mark Layout - (void)layoutSubviews { @@ -478,8 +509,8 @@ - (void)layoutSubviews [self layoutSubviewsAndDetermineFrame]; } -#pragma clang diagnostic push -#pragma clang diagnostic ignored "-Wdeprecated-declarations" + #pragma clang diagnostic push + #pragma clang diagnostic ignored "-Wdeprecated-declarations" - (CGRect)layoutSubviewsAndDetermineFrame { // Compute the positioning of the arrow. @@ -501,10 +532,12 @@ - (CGRect)layoutSubviewsAndDetermineFrame CGFloat yPos = arrowYMarginOffset + kNUXStrokeLineWidth + _textPadding; // Set the text label frame. - _textLabel.frame = CGRectMake(xPos, - yPos + _verticalTextOffset, // sizing function may not return desired height exactly - CGRectGetWidth(_textLabel.bounds), - CGRectGetHeight(_textLabel.bounds)); + _textLabel.frame = CGRectMake( + xPos, + yPos + _verticalTextOffset, // sizing function may not return desired height exactly + CGRectGetWidth(_textLabel.bounds), + CGRectGetHeight(_textLabel.bounds) + ); // Determine the size of the nux bubble. CGFloat bubbleHeight = CGRectGetHeight(_textLabel.bounds) + _verticalTextOffset + _textPadding * 2; @@ -539,34 +572,38 @@ - (CGRect)layoutSubviewsAndDetermineFrame yOrigin = _positionInView.y - nuxHeight - kNUXPointMargin + MAX(0, kNUXRectInset - _arrowHeight); } - return CGRectMake(originX - kNUXRectInset, - yOrigin, - nuxWidth, - nuxHeight); + return CGRectMake( + originX - kNUXRectInset, + yOrigin, + nuxWidth, + nuxHeight + ); } -#pragma clang diagnostic pop -#pragma mark Message & Tagline + #pragma clang diagnostic pop + + #pragma mark Message & Tagline - (void)setMessage:(NSString *)message tagline:(NSString *)tagline { message = message ?: @""; // Ensure tagline is empty string or ends with space tagline = tagline ?: @""; - if (tagline.length && ![tagline hasSuffix:@" "]) + if (tagline.length && ![tagline hasSuffix:@" "]) { tagline = [tagline stringByAppendingString:@" "]; + } // Concatenate tagline & main message message = [tagline stringByAppendingString:message]; NSRange fullRange = NSMakeRange(0, message.length); - NSMutableAttributedString *attrString = [[NSMutableAttributedString alloc] initWithString: message]; + NSMutableAttributedString *attrString = [[NSMutableAttributedString alloc] initWithString:message]; - UIFont *font=[UIFont boldSystemFontOfSize:kNUXFontSize]; + UIFont *font = [UIFont boldSystemFontOfSize:kNUXFontSize]; [attrString addAttribute:NSFontAttributeName value:font range:fullRange]; [attrString addAttribute:NSForegroundColorAttributeName value:[UIColor whiteColor] range:fullRange]; if (tagline.length) { - [attrString addAttribute:NSForegroundColorAttributeName value: FBSDKUIColorWithRGB(0x6D, 0x87, 0xC7) range:NSMakeRange(0, tagline.length)]; + [attrString addAttribute:NSForegroundColorAttributeName value:FBSDKUIColorWithRGB(0x6D, 0x87, 0xC7) range:NSMakeRange(0, tagline.length)]; } _textLabel.attributedText = attrString; @@ -577,7 +614,7 @@ - (void)setMessage:(NSString *)message tagline:(NSString *)tagline [self setNeedsDisplay]; } -#pragma mark Auto Dismiss Timeout + #pragma mark Auto Dismiss Timeout - (void)scheduleAutomaticFadeout { diff --git a/FBSDKLoginKit/FBSDKLoginKit/Internal/FBSDKDeviceLoginManagerResult+Internal.h b/FBSDKLoginKit/FBSDKLoginKit/Internal/FBSDKDeviceLoginManagerResult+Internal.h index a3b683c191..986c07363b 100644 --- a/FBSDKLoginKit/FBSDKLoginKit/Internal/FBSDKDeviceLoginManagerResult+Internal.h +++ b/FBSDKLoginKit/FBSDKLoginKit/Internal/FBSDKDeviceLoginManagerResult+Internal.h @@ -19,7 +19,7 @@ #import #if defined BUCK || defined FBSDKCOCOAPODS -#import + #import #else @import FBSDKCoreKit; #endif diff --git a/FBSDKLoginKit/FBSDKLoginKit/Internal/FBSDKGraphRequestConnectionProviding.h b/FBSDKLoginKit/FBSDKLoginKit/Internal/FBSDKGraphRequestConnectionProviding.h new file mode 100644 index 0000000000..a2dbabea00 --- /dev/null +++ b/FBSDKLoginKit/FBSDKLoginKit/Internal/FBSDKGraphRequestConnectionProviding.h @@ -0,0 +1,44 @@ +// 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 + +#if SWIFT_PACKAGE +#import "FBSDKCoreKit.h" +#else +#import +#endif + +NS_ASSUME_NONNULL_BEGIN + +/// An internal protocol used to describe an object that can handle graph requests +NS_SWIFT_NAME(GraphRequestConnectionProviding) +@protocol FBSDKGraphRequestConnectionProviding + +- (void)addRequest:(FBSDKGraphRequest *)request + completionHandler:(FBSDKGraphRequestBlock)handler; +- (void)start; + +@end + +// MARK: Default Protocol Conformances + +@interface FBSDKGraphRequestConnection (GraphRequestConnection) +@end + +NS_ASSUME_NONNULL_END diff --git a/FBSDKLoginKit/FBSDKLoginKit/Internal/FBSDKLoginCompletion+Internal.h b/FBSDKLoginKit/FBSDKLoginKit/Internal/FBSDKLoginCompletion+Internal.h index 118407a21c..9a0138cc56 100644 --- a/FBSDKLoginKit/FBSDKLoginKit/Internal/FBSDKLoginCompletion+Internal.h +++ b/FBSDKLoginKit/FBSDKLoginKit/Internal/FBSDKLoginCompletion+Internal.h @@ -20,12 +20,16 @@ #if !TARGET_OS_TV -#import "FBSDKLoginCompletion.h" + #import "FBSDKLoginCompletion.h" @interface FBSDKLoginCompletionParameters () +@property (nonatomic) FBSDKAuthenticationToken *authenticationToken; +@property (nonatomic) FBSDKProfile *profile; + @property (nonatomic, copy) NSString *accessTokenString; @property (nonatomic, copy) NSString *nonceString; +@property (nonatomic, copy) NSString *authenticationTokenString; @property (nonatomic, copy) NSSet *permissions; @property (nonatomic, copy) NSSet *declinedPermissions; @@ -45,4 +49,12 @@ @end +@interface FBSDKLoginURLCompleter () + +@property (nonatomic, strong) FBSDKLoginCompletionParameters *parameters; + +- (void)exchangeNonceForTokenWithHandler:(FBSDKLoginCompletionParametersBlock)handler; + +@end + #endif diff --git a/FBSDKLoginKit/FBSDKLoginKit/Internal/FBSDKLoginCompletion.h b/FBSDKLoginKit/FBSDKLoginKit/Internal/FBSDKLoginCompletion.h index 0efe5136c2..c756478449 100644 --- a/FBSDKLoginKit/FBSDKLoginKit/Internal/FBSDKLoginCompletion.h +++ b/FBSDKLoginKit/FBSDKLoginKit/Internal/FBSDKLoginCompletion.h @@ -22,8 +22,14 @@ #import -@class FBSDKLoginManager; +@class FBSDKAuthenticationToken; +@class FBSDKAuthenticationTokenClaims; @class FBSDKLoginCompletionParameters; +@class FBSDKLoginManager; +@class FBSDKPermission; +@class FBSDKProfile; + +NS_ASSUME_NONNULL_BEGIN /** Success Block @@ -33,7 +39,7 @@ NS_SWIFT_NAME(LoginCompletionParametersBlock); /** Structured interface for accessing the parameters used to complete a log in request. - If \c accessTokenString is non-nil, the authentication succeeded. If \c error is + If \c authenticationTokenString is non-nil, the authentication succeeded. If \c error is non-nil the request failed. If both are \c nil, the request was cancelled. */ NS_SWIFT_NAME(LoginCompletionParameters) @@ -42,24 +48,29 @@ NS_SWIFT_NAME(LoginCompletionParameters) - (instancetype)init NS_DESIGNATED_INITIALIZER; - (instancetype)initWithError:(NSError *)error; -@property (nonatomic, copy, readonly) NSString *accessTokenString; -@property (nonatomic, copy, readonly) NSString *nonceString; +@property (nullable, nonatomic, readonly) FBSDKAuthenticationToken *authenticationToken; +@property (nullable, nonatomic, readonly) FBSDKProfile *profile; -@property (nonatomic, copy, readonly) NSSet *permissions; -@property (nonatomic, copy, readonly) NSSet *declinedPermissions; -@property (nonatomic, copy, readonly) NSSet *expiredPermissions; +@property (nullable, nonatomic, copy, readonly) NSString *accessTokenString; +@property (nullable, nonatomic, copy, readonly) NSString *nonceString; +@property (nullable, nonatomic, copy, readonly) NSString *authenticationTokenString; -@property (nonatomic, copy, readonly) NSString *appID; -@property (nonatomic, copy, readonly) NSString *userID; +@property (nullable, nonatomic, copy, readonly) NSSet *permissions; +@property (nullable, nonatomic, copy, readonly) NSSet *declinedPermissions; +@property (nullable, nonatomic, copy, readonly) NSSet *expiredPermissions; -@property (nonatomic, copy, readonly) NSError *error; +@property (nullable, nonatomic, copy, readonly) NSString *appID; +@property (nullable, nonatomic, copy, readonly) NSString *userID; -@property (nonatomic, copy, readonly) NSDate *expirationDate; -@property (nonatomic, copy, readonly) NSDate *dataAccessExpirationDate; +@property (nullable, nonatomic, copy, readonly) NSError *error; -@property (nonatomic, copy, readonly) NSString *challenge; +@property (nullable, nonatomic, copy, readonly) NSDate *expirationDate; +@property (nullable, nonatomic, copy, readonly) NSDate *dataAccessExpirationDate; + +@property (nullable, nonatomic, copy, readonly) NSString *challenge; + +@property (nullable, nonatomic, copy, readonly) NSString *graphDomain; -@property (nonatomic, copy, readonly) NSString *graphDomain; @end NS_SWIFT_NAME(LoginCompleting) @@ -71,6 +82,13 @@ NS_SWIFT_NAME(LoginCompleting) */ - (void)completeLoginWithHandler:(FBSDKLoginCompletionParametersBlock)handler; +/** + Invoke \p handler with the login parameters derived from the authentication result. + See the implementing class's documentation for whether it completes synchronously or asynchronously. + */ +- (void)completeLoginWithHandler:(FBSDKLoginCompletionParametersBlock)handler + nonce:(nullable NSString *)nonce; + @end #pragma mark - Completers @@ -89,8 +107,10 @@ NS_SWIFT_NAME(LoginURLCompleter) - (instancetype)init NS_UNAVAILABLE; + (instancetype)new NS_UNAVAILABLE; -- (instancetype)initWithURLParameters:(NSDictionary *)parameters appID:(NSString *)appID NS_DESIGNATED_INITIALIZER; +- (instancetype)initWithURLParameters:(NSDictionary *)parameters appID:(NSString *)appID; @end +NS_ASSUME_NONNULL_END + #endif diff --git a/FBSDKLoginKit/FBSDKLoginKit/Internal/FBSDKLoginCompletion.m b/FBSDKLoginKit/FBSDKLoginKit/Internal/FBSDKLoginCompletion.m index 7db05098b2..d2896dac90 100644 --- a/FBSDKLoginKit/FBSDKLoginKit/Internal/FBSDKLoginCompletion.m +++ b/FBSDKLoginKit/FBSDKLoginKit/Internal/FBSDKLoginCompletion.m @@ -20,79 +20,26 @@ #if !TARGET_OS_TV -#import "FBSDKLoginCompletion+Internal.h" + #import "FBSDKLoginCompletion+Internal.h" -#if SWIFT_PACKAGE + #if SWIFT_PACKAGE @import FBSDKCoreKit; -#else -#import -#endif + #else + #import + #endif -#import "FBSDKLoginConstants.h" -#import "FBSDKLoginError.h" -#import "FBSDKLoginManager+Internal.h" -#import "FBSDKLoginUtility.h" + #import "FBSDKGraphRequestConnectionProviding.h" + #import "FBSDKLoginConstants.h" + #import "FBSDKLoginError.h" + #import "FBSDKLoginManager+Internal.h" + #import "FBSDKLoginUtility.h" + #import "FBSDKPermission.h" -static void FBSDKLoginRequestMeAndPermissions(FBSDKLoginCompletionParameters *parameters, void(^completionBlock)(void)) -{ - __block NSUInteger pendingCount = 1; - void(^didCompleteBlock)(void) = ^{ - if (--pendingCount == 0) { - completionBlock(); - } - }; +@interface FBSDKAuthenticationToken (ClaimsProviding) - NSString *tokenString = parameters.accessTokenString; - FBSDKGraphRequestConnection *connection = [[FBSDKGraphRequestConnection alloc] init]; - - pendingCount++; - FBSDKGraphRequest *userIDRequest = [[FBSDKGraphRequest alloc] initWithGraphPath:@"me" - parameters:@{ @"fields" : @"id" } - tokenString:tokenString - HTTPMethod:nil - flags:FBSDKGraphRequestFlagDoNotInvalidateTokenOnError | FBSDKGraphRequestFlagDisableErrorRecovery]; - - [connection addRequest:userIDRequest completionHandler:^(FBSDKGraphRequestConnection *requestConnection, - id result, - NSError *error) { - parameters.userID = result[@"id"]; - if (error) { - parameters.error = error; - } - didCompleteBlock(); - }]; - - pendingCount++; - FBSDKGraphRequest *permissionsRequest = [[FBSDKGraphRequest alloc] initWithGraphPath:@"me/permissions" - parameters:@{@"fields":@""} - tokenString:tokenString - HTTPMethod:nil - flags:FBSDKGraphRequestFlagDoNotInvalidateTokenOnError | FBSDKGraphRequestFlagDisableErrorRecovery]; - - [connection addRequest:permissionsRequest completionHandler:^(FBSDKGraphRequestConnection *requestConnection, - id result, - NSError *error) { - NSMutableSet *grantedPermissions = [NSMutableSet set]; - NSMutableSet *declinedPermissions = [NSMutableSet set]; - NSMutableSet *expiredPermissions = [NSMutableSet set]; - - [FBSDKInternalUtility extractPermissionsFromResponse:result - grantedPermissions:grantedPermissions - declinedPermissions:declinedPermissions - expiredPermissions:expiredPermissions]; - - parameters.permissions = [grantedPermissions copy]; - parameters.declinedPermissions = [declinedPermissions copy]; - parameters.expiredPermissions = [expiredPermissions copy]; - if (error) { - parameters.error = error; - } - didCompleteBlock(); - }]; +- (FBSDKAuthenticationTokenClaims *)claims; - [connection start]; - didCompleteBlock(); -} +@end @implementation FBSDKLoginCompletionParameters @@ -111,7 +58,7 @@ - (instancetype)initWithError:(NSError *)error @end -#pragma mark - Completers + #pragma mark - Completers @implementation FBSDKLoginURLCompleter { @@ -120,19 +67,31 @@ @implementation FBSDKLoginURLCompleter BOOL _performExplicitFallback; } -- (instancetype)initWithURLParameters:(NSDictionary *)parameters appID:(NSString *)appID +- (instancetype)initWithURLParameters:(NSDictionary *)parameters + appID:(NSString *)appID { if ((self = [super init]) != nil) { - _parameters = [[FBSDKLoginCompletionParameters alloc] init]; + _parameters = [FBSDKLoginCompletionParameters new]; - _parameters.accessTokenString = parameters[@"access_token"]; - _parameters.nonceString = parameters[@"nonce"]; + BOOL hasNonEmptyNonceString = ((NSString *)[FBSDKTypeUtility dictionary:parameters objectForKey:@"nonce" ofType:NSString.class]).length > 0; + BOOL hasNonEmptyIdTokenString = ((NSString *)[FBSDKTypeUtility dictionary:parameters objectForKey:@"id_token" ofType:NSString.class]).length > 0; + BOOL hasNonEmptyAccessTokenString = ((NSString *)[FBSDKTypeUtility dictionary:parameters objectForKey:@"access_token" ofType:NSString.class]).length > 0; - if (_parameters.accessTokenString.length > 0 || _parameters.nonceString.length > 0) { + // Nonce and id token are mutually exclusive parameters + BOOL hasBothNonceAndIdToken = hasNonEmptyNonceString && hasNonEmptyIdTokenString; + BOOL hasEitherNonceOrIdToken = hasNonEmptyNonceString || hasNonEmptyIdTokenString; + + if (hasNonEmptyAccessTokenString || (hasEitherNonceOrIdToken && !hasBothNonceAndIdToken)) { [self setParametersWithDictionary:parameters appID:appID]; - } else { - _parameters.accessTokenString = nil; + } else if ([FBSDKTypeUtility dictionary:parameters objectForKey:@"error" ofType:NSString.class] || [FBSDKTypeUtility dictionary:parameters objectForKey:@"error_message" ofType:NSString.class]) { [self setErrorWithDictionary:parameters]; + } else if (hasBothNonceAndIdToken) { + // If a nonce is present in the parameter we assume that + // user logged in by app switching. + // Currently OIDC is not supported for app switching. We + // will treat the login attempt as invalid if an ID token + // if returned together with nonce. + _parameters.error = [FBSDKError errorWithCode:FBSDKLoginErrorUnknown message:@"Invalid server response. Please try to login again"]; } } return self; @@ -140,37 +99,67 @@ - (instancetype)initWithURLParameters:(NSDictionary *)parameters appID:(NSString - (void)completeLoginWithHandler:(FBSDKLoginCompletionParametersBlock)handler { + [self completeLoginWithHandler:handler nonce:nil]; +} + +/// Performs the work needed to populate the login completion parameters before they +/// are used to determine login success, failure or cancellation. +- (void)completeLoginWithHandler:(FBSDKLoginCompletionParametersBlock)handler + nonce:(nullable NSString *)nonce +{ + // If there is a nonceString then it means we logged in from the app. if (_parameters.nonceString) { - [self _exchangeNonceForTokenWithHandler:handler]; - return; - } else if (_parameters.accessTokenString && !_parameters.userID) { - void(^handlerCopy)(FBSDKLoginCompletionParameters *) = [handler copy]; - FBSDKLoginRequestMeAndPermissions(_parameters, ^{ - handlerCopy(self->_parameters); - }); - return; + [self exchangeNonceForTokenWithHandler:handler]; + } else if (_parameters.authenticationTokenString && !nonce) { + // If there is no nonce then somehow an auth token string was provided + // but the call did not originate from the sdk. This is not a valid state + _parameters.error = [FBSDKError errorWithCode:FBSDKLoginErrorUnknown message:@"Please try to login again"]; + handler(_parameters); + } else if (_parameters.authenticationTokenString && nonce) { + [self fetchAndSetPropertiesForParameters:_parameters nonce:nonce handler:handler]; + } else { + handler(_parameters); } +} - handler(_parameters); +/// Sets authenticationToken and profile onto the provided parameters and calls the provided completion handler +- (void)fetchAndSetPropertiesForParameters:(nonnull FBSDKLoginCompletionParameters *)parameters + nonce:(nonnull NSString *)nonce + handler:(FBSDKLoginCompletionParametersBlock)handler +{ + FBSDKAuthenticationTokenBlock completion = ^(FBSDKAuthenticationToken *token) { + if (token) { + parameters.authenticationToken = token; + parameters.profile = [FBSDKLoginURLCompleter profileWithClaims:token.claims]; + } else { + parameters.error = [FBSDKError errorWithCode:FBSDKLoginErrorInvalidIDToken message:@"Invalid ID token from login response."]; + } + handler(parameters); + }; + [[FBSDKAuthenticationTokenFactory new] createTokenFromTokenString:_parameters.authenticationTokenString nonce:nonce completion:completion]; } - (void)setParametersWithDictionary:(NSDictionary *)parameters appID:(NSString *)appID { - NSString *grantedPermissionsString = parameters[@"granted_scopes"]; - NSString *declinedPermissionsString = parameters[@"denied_scopes"]; + NSString *grantedPermissionsString = [FBSDKTypeUtility dictionary:parameters objectForKey:@"granted_scopes" ofType:NSString.class]; + NSString *declinedPermissionsString = [FBSDKTypeUtility dictionary:parameters objectForKey:@"denied_scopes" ofType:NSString.class]; + NSString *signedRequest = [FBSDKTypeUtility dictionary:parameters objectForKey:@"signed_request" ofType:NSString.class]; + NSString *userID = [FBSDKTypeUtility dictionary:parameters objectForKey:@"user_id" ofType:NSString.class]; + NSString *domain = [FBSDKTypeUtility dictionary:parameters objectForKey:@"graph_domain" ofType:NSString.class]; - NSString *signedRequest = parameters[@"signed_request"]; - NSString *userID = parameters[@"user_id"]; + _parameters.accessTokenString = [FBSDKTypeUtility dictionary:parameters objectForKey:@"access_token" ofType:NSString.class]; + _parameters.nonceString = [FBSDKTypeUtility dictionary:parameters objectForKey:@"nonce" ofType:NSString.class]; + _parameters.authenticationTokenString = [FBSDKTypeUtility dictionary:parameters objectForKey:@"id_token" ofType:NSString.class]; // check the string length so that we assign an empty set rather than a set with an empty string _parameters.permissions = (grantedPermissionsString.length > 0) - ? [NSSet setWithArray:[grantedPermissionsString componentsSeparatedByString:@","]] - : [NSSet set]; + ? [FBSDKPermission permissionsFromRawPermissions:[NSSet setWithArray:[grantedPermissionsString componentsSeparatedByString:@","]]] + : NSSet.set; _parameters.declinedPermissions = (declinedPermissionsString.length > 0) - ? [NSSet setWithArray:[declinedPermissionsString componentsSeparatedByString:@","]] - : [NSSet set]; + ? [FBSDKPermission permissionsFromRawPermissions:[NSSet setWithArray:[declinedPermissionsString componentsSeparatedByString:@","]]] + : NSSet.set; - _parameters.expiredPermissions = [NSSet set]; + _parameters.expiredPermissions = NSSet.set; _parameters.appID = appID; @@ -180,35 +169,21 @@ - (void)setParametersWithDictionary:(NSDictionary *)parameters appID:(NSString * _parameters.userID = userID; } - NSString *expirationDateString = parameters[@"expires"] ?: parameters[@"expires_at"]; - NSDate *expirationDate = [NSDate distantFuture]; - if (expirationDateString && expirationDateString.doubleValue > 0) { - expirationDate = [NSDate dateWithTimeIntervalSince1970:expirationDateString.doubleValue]; - } else if (parameters[@"expires_in"] && [parameters[@"expires_in"] integerValue] > 0) { - expirationDate = [NSDate dateWithTimeIntervalSinceNow:[parameters[@"expires_in"] integerValue]]; + if (domain.length > 0) { + _parameters.graphDomain = domain; } - _parameters.expirationDate = expirationDate; - NSDate *dataAccessExpirationDate = [NSDate distantFuture]; - if (parameters[@"data_access_expiration_time"] && [parameters[@"data_access_expiration_time"] integerValue] > 0) { - dataAccessExpirationDate = [NSDate dateWithTimeIntervalSince1970:[parameters[@"data_access_expiration_time"] integerValue]]; - } - _parameters.dataAccessExpirationDate = dataAccessExpirationDate; - - NSError *error = nil; - NSDictionary *state = [FBSDKBasicUtility objectForJSONString:parameters[@"state"] error:&error]; - _parameters.challenge = [FBSDKUtility URLDecode:state[@"challenge"]]; - - NSString *domain = parameters[@"graph_domain"]; - _parameters.graphDomain = [domain copy]; + _parameters.expirationDate = [FBSDKLoginURLCompleter expirationDateFromParameters:parameters]; + _parameters.dataAccessExpirationDate = [FBSDKLoginURLCompleter dataAccessExpirationDateFromParameters:parameters]; + _parameters.challenge = [FBSDKLoginURLCompleter challengeFromParameters:parameters]; } - (void)setErrorWithDictionary:(NSDictionary *)parameters { - NSString *legacyErrorReason = parameters[@"error"]; + NSString *legacyErrorReason = [FBSDKTypeUtility dictionary:parameters objectForKey:@"error" ofType:NSString.class]; - if ([legacyErrorReason isEqualToString:@"service_disabled_use_browser"] || - [legacyErrorReason isEqualToString:@"service_disabled"]) { + if ([legacyErrorReason isEqualToString:@"service_disabled_use_browser"] + || [legacyErrorReason isEqualToString:@"service_disabled"]) { _performExplicitFallback = YES; } @@ -217,73 +192,124 @@ - (void)setErrorWithDictionary:(NSDictionary *)parameters _parameters.error = [NSError fbErrorFromReturnURLParameters:parameters]; } -- (void)attemptBrowserLogIn:(FBSDKLoginManager *)loginManager { - if (_observer != nil) { - [[NSNotificationCenter defaultCenter] removeObserver:_observer]; - _observer = nil; - } - - if ([FBSDKBridgeAPI sharedInstance].isActive) { - [loginManager logIn]; - } else { - // The application is active but due to notification ordering the FBSDKApplicationDelegate - // doesn't know it yet. Wait one more turn of the run loop. - dispatch_async(dispatch_get_main_queue(), ^{ - [self attemptBrowserLogIn:loginManager]; - }); - } +- (void)exchangeNonceForTokenWithHandler:(FBSDKLoginCompletionParametersBlock)handler +{ + FBSDKGraphRequestConnection *connection = [FBSDKGraphRequestConnection new]; + [self exchangeNonceForTokenWithGraphRequestConnectionProvider:connection handler:handler]; } -- (void)_exchangeNonceForTokenWithHandler:(FBSDKLoginCompletionParametersBlock)handler +- (void)exchangeNonceForTokenWithGraphRequestConnectionProvider:(nonnull id)connection + handler:(nonnull FBSDKLoginCompletionParametersBlock)handler { if (!handler) { - return; + return; } NSString *nonce = _parameters.nonceString ?: @""; - NSString *appID = [FBSDKSettings appID] ?: @""; + NSString *appID = _parameters.appID ?: @""; if (nonce.length == 0 || appID.length == 0) { _parameters.error = [FBSDKError errorWithCode:FBSDKErrorInvalidArgument message:@"Missing required parameters to exchange nonce for access token."]; - handler(_parameters); return; } - FBSDKGraphRequestConnection *connection = [[FBSDKGraphRequestConnection alloc] init]; FBSDKGraphRequest *tokenRequest = [[FBSDKGraphRequest alloc] initWithGraphPath:@"oauth/access_token" parameters:@{ @"grant_type" : @"fb_exchange_nonce", @"fb_exchange_nonce" : nonce, @"client_id" : appID, @"fields" : @"" } - flags:FBSDKGraphRequestFlagDoNotInvalidateTokenOnError | - FBSDKGraphRequestFlagDisableErrorRecovery]; + flags:FBSDKGraphRequestFlagDoNotInvalidateTokenOnError + | FBSDKGraphRequestFlagDisableErrorRecovery]; __block FBSDKLoginCompletionParameters *parameters = _parameters; [connection addRequest:tokenRequest completionHandler:^(FBSDKGraphRequestConnection *requestConnection, id result, - NSError *error) { - if (!error) { - parameters.accessTokenString = result[@"access_token"]; - NSDate *expirationDate = [NSDate distantFuture]; - if (result[@"expires_in"] && [result[@"expires_in"] integerValue] > 0) { - expirationDate = [NSDate dateWithTimeIntervalSinceNow:[result[@"expires_in"] integerValue]]; - } - parameters.expirationDate = expirationDate; + NSError *graphRequestError) { + if (!graphRequestError) { + parameters.accessTokenString = [FBSDKTypeUtility dictionary:result objectForKey:@"access_token" ofType:NSString.class]; + parameters.expirationDate = [FBSDKLoginURLCompleter expirationDateFromParameters:result]; + parameters.dataAccessExpirationDate = [FBSDKLoginURLCompleter dataAccessExpirationDateFromParameters:result]; + } else { + parameters.error = graphRequestError; + } + + handler(parameters); + }]; - NSDate *dataAccessExpirationDate = [NSDate distantFuture]; - if (result[@"data_access_expiration_time"] && [result[@"data_access_expiration_time"] integerValue] > 0) { - dataAccessExpirationDate = [NSDate dateWithTimeIntervalSince1970:[result[@"data_access_expiration_time"] integerValue]]; + [connection start]; +} + ++ (nullable FBSDKProfile *)profileWithClaims:(FBSDKAuthenticationTokenClaims *)claims +{ + if (claims.sub.length == 0) { + return nil; + } + + NSURL *imageURL; + if (claims.picture) { + imageURL = [NSURL URLWithString:claims.picture]; + } + + return [[FBSDKProfile alloc] initWithUserID:claims.sub + firstName:nil + middleName:nil + lastName:nil + name:claims.name + linkURL:nil + refreshDate:nil + imageURL:imageURL + email:claims.email]; +} + ++ (NSDate *)expirationDateFromParameters:(NSDictionary *)parameters +{ + NSString *expiresString = [FBSDKTypeUtility dictionary:parameters objectForKey:@"expires" ofType:NSString.class]; + NSString *expiresAtString = [FBSDKTypeUtility dictionary:parameters objectForKey:@"expires_at" ofType:NSString.class]; + NSString *expiresInString = [FBSDKTypeUtility dictionary:parameters objectForKey:@"expires_in" ofType:NSString.class]; + NSString *expirationDateString = expiresString ?: expiresAtString; + + if (expirationDateString.doubleValue > 0) { + return [NSDate dateWithTimeIntervalSince1970:expirationDateString.doubleValue]; + } else if (expiresInString.integerValue > 0) { + return [NSDate dateWithTimeIntervalSinceNow:expiresInString.integerValue]; + } else { + return NSDate.distantFuture; + } +} + ++ (NSDate *)dataAccessExpirationDateFromParameters:(NSDictionary *)parameters +{ + NSString *dataAccessExpirationDateString = [FBSDKTypeUtility dictionary:parameters objectForKey:@"data_access_expiration_time" ofType:NSString.class]; + if (dataAccessExpirationDateString.integerValue > 0) { + return [NSDate dateWithTimeIntervalSince1970:dataAccessExpirationDateString.integerValue]; + } else { + return NSDate.distantFuture; + } +} + ++ (NSString *)challengeFromParameters:(NSDictionary *)parameters +{ + NSString *stateString = [FBSDKTypeUtility dictionary:parameters objectForKey:@"state" ofType:NSString.class]; + if (stateString.length > 0) { + NSError *error = nil; + NSDictionary *state = [FBSDKBasicUtility objectForJSONString:stateString error:&error]; + + if (!error) { + NSString *challenge = [FBSDKTypeUtility dictionary:state objectForKey:@"challenge" ofType:NSString.class]; + if (challenge.length > 0) { + return [FBSDKUtility URLDecode:challenge]; } - parameters.dataAccessExpirationDate = dataAccessExpirationDate; - } else { - parameters.error = error; } + } + return nil; +} - handler(parameters); - }]; +// MARK: Test Helpers - [connection start]; +- (FBSDKLoginCompletionParameters *)parameters +{ + return _parameters; } @end diff --git a/FBSDKLoginKit/FBSDKLoginKit/Internal/FBSDKLoginError.h b/FBSDKLoginKit/FBSDKLoginKit/Internal/FBSDKLoginError.h index af51ee7f9e..1659a17814 100644 --- a/FBSDKLoginKit/FBSDKLoginKit/Internal/FBSDKLoginError.h +++ b/FBSDKLoginKit/FBSDKLoginKit/Internal/FBSDKLoginError.h @@ -20,9 +20,13 @@ #if !TARGET_OS_TV -#import + #import -#import "FBSDKLoginConstants.h" + #if SWIFT_PACKAGE + #import "FBSDKLoginConstants.h" + #else + #import + #endif NS_ASSUME_NONNULL_BEGIN diff --git a/FBSDKLoginKit/FBSDKLoginKit/Internal/FBSDKLoginError.m b/FBSDKLoginKit/FBSDKLoginKit/Internal/FBSDKLoginError.m index 34c2f2e655..8d4ee00c0c 100644 --- a/FBSDKLoginKit/FBSDKLoginKit/Internal/FBSDKLoginError.m +++ b/FBSDKLoginKit/FBSDKLoginKit/Internal/FBSDKLoginError.m @@ -20,19 +20,19 @@ #if !TARGET_OS_TV -#import "FBSDKLoginError.h" + #import "FBSDKLoginError.h" -#ifdef FBSDKCOCOAPODS -#import -#else -#import "FBSDKCoreKit+Internal.h" -#endif + #ifdef FBSDKCOCOAPODS + #import + #else + #import "FBSDKCoreKit+Internal.h" + #endif -#ifndef NS_ERROR_ENUM -#define NS_ERROR_ENUM(_domain, _name) \ -enum _name: NSInteger _name; \ -enum __attribute__((ns_error_domain(_domain))) _name: NSInteger -#endif + #ifndef NS_ERROR_ENUM + #define NS_ERROR_ENUM(_domain, _name) \ + enum _name : NSInteger _name; \ + enum __attribute__((ns_error_domain(_domain))) _name: NSInteger + #endif typedef NS_ERROR_ENUM(FBSDKLoginErrorDomain, FBSDKLoginErrorSubcode) { @@ -62,33 +62,53 @@ + (NSError *)fbErrorForFailedLoginWithCode:(FBSDKLoginError)code case FBSDKErrorNetwork: errorDomain = FBSDKErrorDomain; localizedDescription = - NSLocalizedStringWithDefaultValue(@"LoginError.SystemAccount.Network", @"FacebookSDK", [FBSDKInternalUtility bundleForStrings], - @"Unable to connect to Facebook. Check your network connection and try again.", - @"The user facing error message when the Accounts framework encounters a network error."); + NSLocalizedStringWithDefaultValue( + @"LoginError.SystemAccount.Network", + @"FacebookSDK", + [FBSDKInternalUtility bundleForStrings], + @"Unable to connect to Facebook. Check your network connection and try again.", + @"The user facing error message when the Accounts framework encounters a network error." + ); break; case FBSDKLoginErrorUserCheckpointed: localizedDescription = - NSLocalizedStringWithDefaultValue(@"LoginError.SystemAccount.UserCheckpointed", @"FacebookSDK", [FBSDKInternalUtility bundleForStrings], - @"You cannot log in to apps at this time. Please log in to www.facebook.com and follow the instructions given.", - @"The user facing error message when the Facebook account signed in to the Accounts framework has been checkpointed."); + NSLocalizedStringWithDefaultValue( + @"LoginError.SystemAccount.UserCheckpointed", + @"FacebookSDK", + [FBSDKInternalUtility bundleForStrings], + @"You cannot log in to apps at this time. Please log in to www.facebook.com and follow the instructions given.", + @"The user facing error message when the Facebook account signed in to the Accounts framework has been checkpointed." + ); break; case FBSDKLoginErrorUnconfirmedUser: localizedDescription = - NSLocalizedStringWithDefaultValue(@"LoginError.SystemAccount.UnconfirmedUser", @"FacebookSDK", [FBSDKInternalUtility bundleForStrings], - @"Your account is not confirmed. Please log in to www.facebook.com and follow the instructions given.", - @"The user facing error message when the Facebook account signed in to the Accounts framework becomes unconfirmed."); + NSLocalizedStringWithDefaultValue( + @"LoginError.SystemAccount.UnconfirmedUser", + @"FacebookSDK", + [FBSDKInternalUtility bundleForStrings], + @"Your account is not confirmed. Please log in to www.facebook.com and follow the instructions given.", + @"The user facing error message when the Facebook account signed in to the Accounts framework becomes unconfirmed." + ); break; case FBSDKLoginErrorSystemAccountAppDisabled: localizedDescription = - NSLocalizedStringWithDefaultValue(@"LoginError.SystemAccount.Disabled", @"FacebookSDK", [FBSDKInternalUtility bundleForStrings], - @"Access has not been granted to the Facebook account. Verify device settings.", - @"The user facing error message when the app slider has been disabled and login fails."); + NSLocalizedStringWithDefaultValue( + @"LoginError.SystemAccount.Disabled", + @"FacebookSDK", + [FBSDKInternalUtility bundleForStrings], + @"Access has not been granted to the Facebook account. Verify device settings.", + @"The user facing error message when the app slider has been disabled and login fails." + ); break; case FBSDKLoginErrorSystemAccountUnavailable: localizedDescription = - NSLocalizedStringWithDefaultValue(@"LoginError.SystemAccount.Unavailable", @"FacebookSDK", [FBSDKInternalUtility bundleForStrings], - @"The Facebook account has not been configured on the device.", - @"The user facing error message when the device Facebook account is unavailable and login fails."); + NSLocalizedStringWithDefaultValue( + @"LoginError.SystemAccount.Unavailable", + @"FacebookSDK", + [FBSDKInternalUtility bundleForStrings], + @"The Facebook account has not been configured on the device.", + @"The user facing error message when the device Facebook account is unavailable and login fails." + ); break; default: break; @@ -105,9 +125,13 @@ + (NSError *)fbErrorForFailedLoginWithCode:(FBSDKLoginError)code + (NSError *)fbErrorForSystemPasswordChange:(NSError *)innerError { NSString *failureReasonAndDescription = - NSLocalizedStringWithDefaultValue(@"LoginError.SystemAccount.PasswordChange", @"FacebookSDK", [FBSDKInternalUtility bundleForStrings], - @"Your Facebook password has changed. To confirm your password, open Settings > Facebook and tap your name.", - @"The user facing error message when the device Facebook account password is incorrect and login fails."); + NSLocalizedStringWithDefaultValue( + @"LoginError.SystemAccount.PasswordChange", + @"FacebookSDK", + [FBSDKInternalUtility bundleForStrings], + @"Your Facebook password has changed. To confirm your password, open Settings > Facebook and tap your name.", + @"The user facing error message when the device Facebook account password is incorrect and login fails." + ); NSMutableDictionary *userInfo = [NSMutableDictionary dictionaryWithObjectsAndKeys: failureReasonAndDescription, FBSDKErrorLocalizedDescriptionKey, failureReasonAndDescription, NSLocalizedDescriptionKey, @@ -125,14 +149,14 @@ + (NSError *)fbErrorFromReturnURLParameters:(NSDictionary *)parameters NSError *error = nil; NSMutableDictionary *userInfo = [[NSMutableDictionary alloc] init]; - [FBSDKTypeUtility dictionary:userInfo setObject:parameters[@"error_message"] forKey:FBSDKErrorDeveloperMessageKey]; + [FBSDKTypeUtility dictionary:userInfo setObject:[FBSDKTypeUtility dictionary:parameters objectForKey:@"error_message" ofType:NSString.class] forKey:FBSDKErrorDeveloperMessageKey]; if (userInfo.count > 0) { - [FBSDKTypeUtility dictionary:userInfo setObject:parameters[@"error"] forKey:FBSDKErrorDeveloperMessageKey]; - [FBSDKTypeUtility dictionary:userInfo setObject:parameters[@"error_code"] forKey:FBSDKGraphRequestErrorGraphErrorCodeKey]; + [FBSDKTypeUtility dictionary:userInfo setObject:[FBSDKTypeUtility dictionary:parameters objectForKey:@"error" ofType:NSString.class] forKey:FBSDKErrorDeveloperMessageKey]; + [FBSDKTypeUtility dictionary:userInfo setObject:[FBSDKTypeUtility dictionary:parameters objectForKey:@"error_code" ofType:NSString.class] forKey:FBSDKGraphRequestErrorGraphErrorCodeKey]; if (!userInfo[FBSDKErrorDeveloperMessageKey]) { - [FBSDKTypeUtility dictionary:userInfo setObject:parameters[@"error_reason"] forKey:FBSDKErrorDeveloperMessageKey]; + [FBSDKTypeUtility dictionary:userInfo setObject:[FBSDKTypeUtility dictionary:parameters objectForKey:@"error_reason" ofType:NSString.class] forKey:FBSDKErrorDeveloperMessageKey]; } [FBSDKTypeUtility dictionary:userInfo setObject:@(FBSDKGraphRequestErrorOther) forKey:FBSDKGraphRequestErrorKey]; @@ -193,8 +217,8 @@ + (NSError *)fbErrorWithSystemAccountStoreDeniedError:(NSError *)accountStoreErr // The OAuth endpoint directs people to www.facebook.com when an account has been // checkpointed. If the web address is present, assume it's due to a checkpoint. errorCode = FBSDKLoginErrorUserCheckpointed; - } else if ([description rangeOfString:@"(452)"].location != NSNotFound || - [description rangeOfString:@"(460)"].location != NSNotFound) { + } else if ([description rangeOfString:@"(452)"].location != NSNotFound + || [description rangeOfString:@"(460)"].location != NSNotFound) { // The Facebook server could not fulfill this access request: Error validating access token: // Session does not match current stored session. This may be because the user changed the password since // the time the session was created or Facebook has changed the session for security reasons. (452)or(460) diff --git a/FBSDKLoginKit/FBSDKLoginKit/Internal/FBSDKLoginKit+Internal.h b/FBSDKLoginKit/FBSDKLoginKit/Internal/FBSDKLoginKit+Internal.h index a94b7cdc82..42edf3a45c 100644 --- a/FBSDKLoginKit/FBSDKLoginKit/Internal/FBSDKLoginKit+Internal.h +++ b/FBSDKLoginKit/FBSDKLoginKit/Internal/FBSDKLoginKit+Internal.h @@ -16,9 +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 "../FBSDKLoginKit.h" - #import "FBSDKLoginCompletion+Internal.h" #import "FBSDKLoginError.h" #import "FBSDKLoginManager+Internal.h" +#import "FBSDKLoginManagerLogger.h" #import "FBSDKLoginUtility.h" +#import "FBSDKPermission.h" diff --git a/FBSDKLoginKit/FBSDKLoginKit/Internal/FBSDKLoginManager+Internal.h b/FBSDKLoginKit/FBSDKLoginKit/Internal/FBSDKLoginManager+Internal.h index cc05176e03..d928a5833f 100644 --- a/FBSDKLoginKit/FBSDKLoginKit/Internal/FBSDKLoginManager+Internal.h +++ b/FBSDKLoginKit/FBSDKLoginKit/Internal/FBSDKLoginManager+Internal.h @@ -28,10 +28,16 @@ #import "FBSDKCoreKit+Internal.h" #endif +#if SWIFT_PACKAGE #import "FBSDKLoginManager.h" +#else +#import +#endif @class FBSDKAccessToken; @class FBSDKLoginCompletionParameters; +@class FBSDKLoginManagerLogger; +@class FBSDKPermission; /** Success Block @@ -39,21 +45,31 @@ typedef void (^FBSDKBrowserLoginSuccessBlock)(BOOL didOpen, NSError *error) NS_SWIFT_NAME(BrowserLoginSuccessBlock); +typedef NS_ENUM(NSInteger, FBSDKLoginManagerState) { + FBSDKLoginManagerStateIdle, + // We received a call to start login. + FBSDKLoginManagerStateStart, + // We're calling out to the Facebook app or Safari to perform a log in + FBSDKLoginManagerStatePerformingLogin, +}; + @interface FBSDKLoginManager () @property (nonatomic, weak) UIViewController *fromViewController; -@property (nonatomic, readonly) NSSet *requestedPermissions; +@property (nonatomic, readonly) NSSet *requestedPermissions; +@property (nonatomic, strong) FBSDKLoginManagerLogger *logger; +@property (nonatomic) FBSDKLoginManagerState state; +@property (nonatomic) BOOL usedSFAuthSession; // for testing only @property (nonatomic, readonly, copy) NSString *loadExpectedChallenge; - (void)completeAuthentication:(FBSDKLoginCompletionParameters *)parameters expectChallenge:(BOOL)expectChallenge; -// available to internal types to trigger login without checking read/publish mixtures. -- (void)logInWithPermissions:(NSSet *)permissions handler:(FBSDKLoginManagerLoginResultBlock)handler; - (void)logIn; // made available for testing only -- (NSDictionary *)logInParametersWithPermissions:(NSSet *)permissions serverConfiguration:(FBSDKServerConfiguration *)serverConfiguration; +- (NSDictionary *)logInParametersWithConfiguration:(FBSDKLoginConfiguration *)configuration + serverConfiguration:(FBSDKServerConfiguration *)serverConfiguration; // made available for testing only - (void)validateReauthentication:(FBSDKAccessToken *)currentToken withResult:(FBSDKLoginManagerLoginResult *)loginResult; @@ -61,8 +77,14 @@ NS_SWIFT_NAME(BrowserLoginSuccessBlock); - (void)setHandler:(FBSDKLoginManagerLoginResultBlock)handler; // for testing only - (void)setRequestedPermissions:(NSSet *)requestedPermissions; -// for testing only -- (void)performBrowserLogInWithParameters:(NSDictionary *)loginParams handler:(FBSDKBrowserLoginSuccessBlock)handler; + +// available to internal modules +- (void)handleImplicitCancelOfLogIn; +- (void)invokeHandler:(FBSDKLoginManagerLoginResult *)result error:(NSError *)error; +- (BOOL)validateLoginStartState; +- (BOOL)isPerformingLogin; ++ (NSString *)stringForChallenge; +- (void)storeExpectedChallenge:(NSString *)expectedChallenge; @end diff --git a/FBSDKLoginKit/FBSDKLoginKit/Internal/FBSDKLoginManagerLogger.h b/FBSDKLoginKit/FBSDKLoginKit/Internal/FBSDKLoginManagerLogger.h index 5c2ea0947e..eb133f696d 100644 --- a/FBSDKLoginKit/FBSDKLoginKit/Internal/FBSDKLoginManagerLogger.h +++ b/FBSDKLoginKit/FBSDKLoginKit/Internal/FBSDKLoginManagerLogger.h @@ -25,6 +25,7 @@ FOUNDATION_EXPORT NSString *const FBSDKLoginManagerLoggerAuthMethod_Native; FOUNDATION_EXPORT NSString *const FBSDKLoginManagerLoggerAuthMethod_Browser; FOUNDATION_EXPORT NSString *const FBSDKLoginManagerLoggerAuthMethod_SFVC; +FOUNDATION_EXPORT NSString *const FBSDKLoginManagerLoggerAuthMethod_Applink; NS_SWIFT_NAME(LoginManagerLogger) @@ -43,10 +44,14 @@ NS_SWIFT_NAME(LoginManagerLogger) - (void)startAuthMethod:(NSString *)authMethod; - (void)endLoginWithResult:(FBSDKLoginManagerLoginResult *)result error:(NSError *)error; +- (void)postLoginHeartbeat; + - (NSDictionary *)parametersWithTimeStampAndClientState:(NSDictionary *)loginParams forAuthMethod:(NSString *)authMethod; - (void)willAttemptAppSwitchingBehavior; - (void)logNativeAppDialogResult:(BOOL)result dialogDuration:(NSTimeInterval)dialogDuration; + +- (void)addSingleLoggingExtra:(id)extra forKey:(NSString *)key; @end #endif diff --git a/FBSDKLoginKit/FBSDKLoginKit/Internal/FBSDKLoginManagerLogger.m b/FBSDKLoginKit/FBSDKLoginKit/Internal/FBSDKLoginManagerLogger.m index e45f61771f..7792db82bc 100644 --- a/FBSDKLoginKit/FBSDKLoginKit/Internal/FBSDKLoginManagerLogger.m +++ b/FBSDKLoginKit/FBSDKLoginKit/Internal/FBSDKLoginManagerLogger.m @@ -20,20 +20,21 @@ #if !TARGET_OS_TV -#import "FBSDKLoginManagerLogger.h" + #import "FBSDKLoginManagerLogger.h" -#ifdef FBSDKCOCOAPODS -#import -#else -#import "FBSDKCoreKit+Internal.h" -#endif -#import "FBSDKLoginError.h" -#import "FBSDKLoginManagerLoginResult+Internal.h" -#import "FBSDKLoginUtility.h" + #ifdef FBSDKCOCOAPODS + #import + #else + #import "FBSDKCoreKit+Internal.h" + #endif + #import "FBSDKLoginError.h" + #import "FBSDKLoginManagerLoginResult+Internal.h" + #import "FBSDKLoginUtility.h" NSString *const FBSDKLoginManagerLoggerAuthMethod_Native = @"fb_application_web_auth"; NSString *const FBSDKLoginManagerLoggerAuthMethod_Browser = @"browser_auth"; NSString *const FBSDKLoginManagerLoggerAuthMethod_SFVC = @"sfvc_auth"; +NSString *const FBSDKLoginManagerLoggerAuthMethod_Applink = @"applink_auth"; static NSString *const FBSDKLoginManagerLoggingClientStateKey = @"state"; static NSString *const FBSDKLoginManagerLoggingClientStateIsClientState = @"com.facebook.sdk_client_state"; @@ -59,7 +60,7 @@ @implementation FBSDKLoginManagerLogger { -@private + @private NSString *_identifier; NSMutableDictionary *_extras; @@ -105,20 +106,21 @@ - (void)startSessionForLoginManager:(FBSDKLoginManager *)loginManager NSString *behaviorString = @"FBSDKLoginBehaviorBrowser"; [_extras addEntriesFromDictionary:@{ - FBSDKLoginManagerLoggerTryNative : @(willTryNative), - FBSDKLoginManagerLoggerTryBrowser : @(willTryBrowser), - @"isReauthorize" : @(isReauthorize), - @"login_behavior" : behaviorString, - @"default_audience" : [FBSDKLoginUtility stringForAudience:loginManager.defaultAudience], - @"permissions" : [loginManager.requestedPermissions.allObjects componentsJoinedByString:@","] ?: @"" - }]; + FBSDKLoginManagerLoggerTryNative : @(willTryNative), + FBSDKLoginManagerLoggerTryBrowser : @(willTryBrowser), + @"isReauthorize" : @(isReauthorize), + @"login_behavior" : behaviorString, + @"default_audience" : [FBSDKLoginUtility stringForAudience:loginManager.defaultAudience], + @"permissions" : [loginManager.requestedPermissions.allObjects componentsJoinedByString:@","] ?: @"" + }]; [self logEvent:FBSDKAppEventNameFBSessionAuthStart params:[self _parametersForNewEvent]]; } - (void)endSession { - [self logEvent:FBSDKAppEventNameFBSessionAuthEnd result:_lastResult error:_lastError]; + [self logEvent:FBSDKAppEventNameFBSessionAuthEnd result:_lastResult error:_lastError]; + [FBSDKAppEvents flush]; } - (void)startAuthMethod:(NSString *)authMethod @@ -151,14 +153,24 @@ - (void)endLoginWithResult:(FBSDKLoginManagerLoginResult *)result error:(NSError [self logEvent:FBSDKAppEventNameFBSessionAuthMethodEnd result:resultString error:error]; } +- (void)postLoginHeartbeat +{ + [NSTimer scheduledTimerWithTimeInterval:5.0 target:self selector:@selector(heartbestTimerDidFire) userInfo:nil repeats:NO]; +} + +- (void)heartbestTimerDidFire +{ + [self logEvent:FBSDKAppEventNameFBSessionAuthHeartbeat result:_lastResult error:_lastError]; +} + - (NSDictionary *)parametersWithTimeStampAndClientState:(NSDictionary *)loginParams forAuthMethod:(NSString *)authMethod { NSMutableDictionary *params = [loginParams mutableCopy]; NSTimeInterval timeValue = (NSTimeInterval)FBSDKMonotonicTimeGetCurrentSeconds(); NSString *e2eTimestampString = [FBSDKBasicUtility JSONStringForObject:@{ @"init" : @(timeValue) } - error:NULL - invalidObjectHandler:NULL]; + error:NULL + invalidObjectHandler:NULL]; [FBSDKTypeUtility dictionary:params setObject:e2eTimestampString forKey:@"e2e"]; NSDictionary *existingState = [FBSDKBasicUtility objectForJSONString:params[FBSDKLoginManagerLoggingClientStateKey] error:NULL]; @@ -176,30 +188,35 @@ - (void)willAttemptAppSwitchingBehavior BOOL isMessengerAppCanOpenURLSchemeRegistered = [FBSDKInternalUtility isRegisteredCanOpenURLScheme:FBSDK_CANOPENURL_MESSENGER]; [_extras addEntriesFromDictionary:@{ - @"isURLSchemeRegistered" : @(isURLSchemeRegistered), - @"isFacebookAppCanOpenURLSchemeRegistered" : @(isFacebookAppCanOpenURLSchemeRegistered), - @"isMessengerAppCanOpenURLSchemeRegistered" : @(isMessengerAppCanOpenURLSchemeRegistered), - }]; + @"isURLSchemeRegistered" : @(isURLSchemeRegistered), + @"isFacebookAppCanOpenURLSchemeRegistered" : @(isFacebookAppCanOpenURLSchemeRegistered), + @"isMessengerAppCanOpenURLSchemeRegistered" : @(isMessengerAppCanOpenURLSchemeRegistered), + }]; } - (void)logNativeAppDialogResult:(BOOL)result dialogDuration:(NSTimeInterval)dialogDuration { NSOperatingSystemVersion iOS10Version = { .majorVersion = 10, .minorVersion = 0, .patchVersion = 0 }; - if ([FBSDKInternalUtility isOSRunTimeVersionAtLeast:iOS10Version]) { + if ([NSProcessInfo.processInfo isOperatingSystemAtLeastVersion:iOS10Version]) { [FBSDKTypeUtility dictionary:_extras setObject:@(dialogDuration) forKey:@"native_app_login_dialog_duration"]; [FBSDKTypeUtility dictionary:_extras setObject:@(result) forKey:@"native_app_login_dialog_result"]; [self logEvent:FBSDKAppEventNameFBSessionFASLoginDialogResult params:[self _parametersForNewEvent]]; } } -#pragma mark - Private +- (void)addSingleLoggingExtra:(id)extra forKey:(NSString *)key +{ + [FBSDKTypeUtility dictionary:_extras setObject:extra forKey:key]; +} + + #pragma mark - Private - (NSString *)clientStateForAuthMethod:(NSString *)authMethod andExistingState:(NSDictionary *)existingState { NSDictionary *clientState = @{ - FBSDKLoginManagerLoggerParamAuthMethodKey: authMethod ?: @"", - FBSDKLoginManagerLoggerParamIdentifierKey: _identifier, - FBSDKLoginManagerLoggingClientStateIsClientState: @YES, + FBSDKLoginManagerLoggerParamAuthMethodKey : authMethod ?: @"", + FBSDKLoginManagerLoggerParamIdentifierKey : _identifier, + FBSDKLoginManagerLoggingClientStateIsClientState : @YES, }; if (existingState) { @@ -213,29 +230,29 @@ - (NSString *)clientStateForAuthMethod:(NSString *)authMethod andExistingState:( - (NSMutableDictionary *)_parametersForNewEvent { - NSMutableDictionary *eventParameters = [[NSMutableDictionary alloc] init]; - - // NOTE: We ALWAYS add all params to each event, to ensure predictable mapping on the backend. - [FBSDKTypeUtility dictionary:eventParameters setObject:_identifier ?: FBSDKLoginManagerLoggerValueEmpty forKey:FBSDKLoginManagerLoggerParamIdentifierKey]; - [FBSDKTypeUtility dictionary:eventParameters setObject:@(round(1000 * [NSDate date].timeIntervalSince1970)) forKey:FBSDKLoginManagerLoggerParamTimestampKey]; - [FBSDKTypeUtility dictionary:eventParameters setObject:FBSDKLoginManagerLoggerValueEmpty forKey:FBSDKLoginManagerLoggerParamResultKey]; - [FBSDKTypeUtility dictionary:eventParameters setObject:_authMethod forKey:FBSDKLoginManagerLoggerParamAuthMethodKey]; - [FBSDKTypeUtility dictionary:eventParameters setObject:FBSDKLoginManagerLoggerValueEmpty forKey:FBSDKLoginManagerLoggerParamErrorCodeKey]; - [FBSDKTypeUtility dictionary:eventParameters setObject:FBSDKLoginManagerLoggerValueEmpty forKey:FBSDKLoginManagerLoggerParamErrorMessageKey]; - [FBSDKTypeUtility dictionary:eventParameters setObject:FBSDKLoginManagerLoggerValueEmpty forKey:FBSDKLoginManagerLoggerParamExtrasKey]; - [FBSDKTypeUtility dictionary:eventParameters setObject:_loggingToken ?: FBSDKLoginManagerLoggerValueEmpty forKey:FBSDKLoginManagerLoggerParamLoggingTokenKey]; - - return eventParameters; + NSMutableDictionary *eventParameters = [[NSMutableDictionary alloc] init]; + + // NOTE: We ALWAYS add all params to each event, to ensure predictable mapping on the backend. + [FBSDKTypeUtility dictionary:eventParameters setObject:_identifier ?: FBSDKLoginManagerLoggerValueEmpty forKey:FBSDKLoginManagerLoggerParamIdentifierKey]; + [FBSDKTypeUtility dictionary:eventParameters setObject:@(round(1000 * [NSDate date].timeIntervalSince1970)) forKey:FBSDKLoginManagerLoggerParamTimestampKey]; + [FBSDKTypeUtility dictionary:eventParameters setObject:FBSDKLoginManagerLoggerValueEmpty forKey:FBSDKLoginManagerLoggerParamResultKey]; + [FBSDKTypeUtility dictionary:eventParameters setObject:_authMethod forKey:FBSDKLoginManagerLoggerParamAuthMethodKey]; + [FBSDKTypeUtility dictionary:eventParameters setObject:FBSDKLoginManagerLoggerValueEmpty forKey:FBSDKLoginManagerLoggerParamErrorCodeKey]; + [FBSDKTypeUtility dictionary:eventParameters setObject:FBSDKLoginManagerLoggerValueEmpty forKey:FBSDKLoginManagerLoggerParamErrorMessageKey]; + [FBSDKTypeUtility dictionary:eventParameters setObject:FBSDKLoginManagerLoggerValueEmpty forKey:FBSDKLoginManagerLoggerParamExtrasKey]; + [FBSDKTypeUtility dictionary:eventParameters setObject:_loggingToken ?: FBSDKLoginManagerLoggerValueEmpty forKey:FBSDKLoginManagerLoggerParamLoggingTokenKey]; + + return eventParameters; } - (void)logEvent:(NSString *)eventName params:(NSMutableDictionary *)params { if (_identifier) { NSString *extrasJSONString = [FBSDKBasicUtility JSONStringForObject:_extras - error:NULL - invalidObjectHandler:NULL]; + error:NULL + invalidObjectHandler:NULL]; if (extrasJSONString) { - [FBSDKTypeUtility dictionary:params setObject:extrasJSONString forKey:FBSDKLoginManagerLoggerParamExtrasKey]; + [FBSDKTypeUtility dictionary:params setObject:extrasJSONString forKey:FBSDKLoginManagerLoggerParamExtrasKey]; } [_extras removeAllObjects]; diff --git a/FBSDKLoginKit/FBSDKLoginKit/Internal/FBSDKLoginManagerLoginResult+Internal.h b/FBSDKLoginKit/FBSDKLoginKit/Internal/FBSDKLoginManagerLoginResult+Internal.h index 18dd7cef1b..1935c6707c 100644 --- a/FBSDKLoginKit/FBSDKLoginKit/Internal/FBSDKLoginManagerLoginResult+Internal.h +++ b/FBSDKLoginKit/FBSDKLoginKit/Internal/FBSDKLoginManagerLoginResult+Internal.h @@ -20,11 +20,11 @@ #if !TARGET_OS_TV -#import + #import -#import "FBSDKLoginManagerLoginResult.h" + #import "FBSDKLoginManagerLoginResult.h" -@interface FBSDKLoginManagerLoginResult() +@interface FBSDKLoginManagerLoginResult () @property (nonatomic, readonly) NSDictionary *loggingExtras; @@ -32,7 +32,7 @@ @property (nonatomic, assign) BOOL isSkipped; // adds additional logging entry to extras - only sent as part of `endLoginWithResult:` --(void)addLoggingExtra:(id)object forKey:(id)key; +- (void)addLoggingExtra:(id)object forKey:(id)key; @end #endif diff --git a/FBSDKLoginKit/FBSDKLoginKit/Internal/FBSDKLoginUtility.h b/FBSDKLoginKit/FBSDKLoginKit/Internal/FBSDKLoginUtility.h index 440f651efc..c397975c91 100644 --- a/FBSDKLoginKit/FBSDKLoginKit/Internal/FBSDKLoginUtility.h +++ b/FBSDKLoginKit/FBSDKLoginKit/Internal/FBSDKLoginUtility.h @@ -22,7 +22,11 @@ #import -#import "FBSDKLoginManager.h" +#if SWIFT_PACKAGE + #import "FBSDKLoginManager.h" +#else + #import +#endif NS_SWIFT_NAME(LoginUtility) @interface FBSDKLoginUtility : NSObject diff --git a/FBSDKLoginKit/FBSDKLoginKit/Internal/FBSDKLoginUtility.m b/FBSDKLoginKit/FBSDKLoginKit/Internal/FBSDKLoginUtility.m index f403b3336b..659c45b163 100644 --- a/FBSDKLoginKit/FBSDKLoginKit/Internal/FBSDKLoginUtility.m +++ b/FBSDKLoginKit/FBSDKLoginKit/Internal/FBSDKLoginUtility.m @@ -20,20 +20,20 @@ #if !TARGET_OS_TV -#import "FBSDKLoginUtility.h" + #import "FBSDKLoginUtility.h" -#if SWIFT_PACKAGE + #if SWIFT_PACKAGE @import FBSDKCoreKit; -#else -#import -#endif + #else + #import + #endif -#ifdef FBSDKCOCOAPODS -#import -#else -#import "FBSDKCoreKit+Internal.h" -#endif -#import "FBSDKLoginConstants.h" + #ifdef FBSDKCOCOAPODS + #import + #else + #import "FBSDKCoreKit+Internal.h" + #endif + #import "FBSDKLoginConstants.h" @implementation FBSDKLoginUtility @@ -63,7 +63,7 @@ + (NSDictionary *)queryParamsFromLoginURL:(NSURL *)url return nil; } } - NSMutableDictionary *params = [NSMutableDictionary dictionaryWithDictionary:[FBSDKInternalUtility dictionaryFromFBURL:url]]; + NSMutableDictionary *params = [NSMutableDictionary dictionaryWithDictionary:[FBSDKInternalUtility parametersFromFBURL:url]]; NSString *userID = [[self class] userIDFromSignedRequest:params[@"signed_request"]]; if (userID) { diff --git a/FBSDKLoginKit/FBSDKLoginKit/Internal/FBSDKNonceUtility.h b/FBSDKLoginKit/FBSDKLoginKit/Internal/FBSDKNonceUtility.h new file mode 100644 index 0000000000..02246bfa05 --- /dev/null +++ b/FBSDKLoginKit/FBSDKLoginKit/Internal/FBSDKNonceUtility.h @@ -0,0 +1,36 @@ +// 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 + +NS_ASSUME_NONNULL_BEGIN + +NS_SWIFT_NAME(Nonce) +@interface FBSDKNonceUtility: NSObject + +/** + Checks if a string represents a valid nonce. + */ ++ (BOOL)isValidNonce:(NSString *)nonce; + ++ (instancetype)new NS_UNAVAILABLE; +- (instancetype)init NS_UNAVAILABLE; + +@end + +NS_ASSUME_NONNULL_END diff --git a/FBSDKLoginKit/FBSDKLoginKit/Internal/FBSDKNonceUtility.m b/FBSDKLoginKit/FBSDKLoginKit/Internal/FBSDKNonceUtility.m new file mode 100644 index 0000000000..02b984b3fc --- /dev/null +++ b/FBSDKLoginKit/FBSDKLoginKit/Internal/FBSDKNonceUtility.m @@ -0,0 +1,38 @@ +// 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 "FBSDKNonceUtility.h" + +#ifdef FBSDKCOCOAPODS + #import +#else + #import "FBSDKCoreKit+Internal.h" +#endif + +@implementation FBSDKNonceUtility + ++ (BOOL)isValidNonce:(NSString *)nonce +{ + NSString *string = [FBSDKTypeUtility stringValue:nonce]; + NSRange whiteSpaceRange = [string rangeOfCharacterFromSet:[NSCharacterSet whitespaceCharacterSet]]; + BOOL containsWhitespace = (whiteSpaceRange.location != NSNotFound); + + return (([string length] > 0) && !containsWhitespace); +} + +@end diff --git a/FBSDKLoginKit/FBSDKLoginKit/Internal/FBSDKPermission.h b/FBSDKLoginKit/FBSDKLoginKit/Internal/FBSDKPermission.h new file mode 100644 index 0000000000..7a9fad4a0c --- /dev/null +++ b/FBSDKLoginKit/FBSDKLoginKit/Internal/FBSDKPermission.h @@ -0,0 +1,56 @@ +// 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 + +NS_ASSUME_NONNULL_BEGIN + +NS_SWIFT_NAME(FBPermission) +@interface FBSDKPermission : NSObject + ++ (instancetype)new NS_UNAVAILABLE; +- (instancetype)init NS_UNAVAILABLE; + +/** + The raw string representation of the permission +*/ +@property (nonatomic, readonly, copy) NSString *value; + +/** + Attempts to initialize a new permission with the given string. + Creation will fail and return nil if the string is invalid. + + @param string the raw permission string +*/ +- (nullable instancetype)initWithString:(NSString *)string; + +/** + Returns a set of FBSDKPermission from a set of raw permissions strings. + Will return nil if any of the input permissions is invalid. +*/ ++ (nullable NSSet *)permissionsFromRawPermissions:(NSSet *)rawPermissions; + +/** + Returns a set of string permissions from a set of FBSDKPermission by + extracting the "value" property for each element. +*/ ++ (NSSet *)rawPermissionsFromPermissions:(NSSet *)permissions; + +@end + +NS_ASSUME_NONNULL_END diff --git a/FBSDKLoginKit/FBSDKLoginKit/Internal/FBSDKPermission.m b/FBSDKLoginKit/FBSDKLoginKit/Internal/FBSDKPermission.m new file mode 100644 index 0000000000..162d1ad6a2 --- /dev/null +++ b/FBSDKLoginKit/FBSDKLoginKit/Internal/FBSDKPermission.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 "FBSDKPermission.h" + +#ifdef FBSDKCOCOAPODS + #import +#else + #import "FBSDKCoreKit+Internal.h" +#endif + +@implementation FBSDKPermission + +- (nullable instancetype)initWithString:(NSString *)string +{ + NSString *permission = [FBSDKTypeUtility stringValue:string]; + if (permission.length <= 0) { + return nil; + } + + NSCharacterSet *allowedSet = [NSCharacterSet characterSetWithCharactersInString:@"abcdefghijklmnopqrstuvwxyz_"]; + if (![[string stringByTrimmingCharactersInSet:allowedSet] isEqualToString:@""]) { + return nil; + } + + if ((self = [super init])) { + _value = permission; + } + return self; +} + ++ (NSSet *)permissionsFromRawPermissions:(NSSet *)rawPermissions +{ + NSMutableSet *permissions = NSMutableSet.new; + + for (NSString *rawPermission in rawPermissions) { + FBSDKPermission *permission = [[FBSDKPermission alloc] initWithString:rawPermission]; + if (!permission) { + return nil; + } + [permissions addObject:permission]; + } + + return permissions; +} + ++ (NSSet *)rawPermissionsFromPermissions:(NSSet *)permissions +{ + NSMutableSet *rawPermissions = NSMutableSet.new; + + for (FBSDKPermission *permission in permissions) { + [rawPermissions addObject:permission.value]; + } + + return rawPermissions; +} + +- (BOOL)isEqual:(id)obj +{ + if (![obj isKindOfClass:[FBSDKPermission class]]) { + return NO; + } + + FBSDKPermission *other = (FBSDKPermission *)obj; + return [self.value isEqualToString:other.value]; +} + +- (NSString *)description +{ + return self.value; +} + +- (NSUInteger)hash +{ + return self.value.hash; +} + +@end diff --git a/FBSDKLoginKit/FBSDKLoginKit/Internal/FBSDKReferralManager+Internal.h b/FBSDKLoginKit/FBSDKLoginKit/Internal/FBSDKReferralManager+Internal.h new file mode 100644 index 0000000000..c8d680c7d0 --- /dev/null +++ b/FBSDKLoginKit/FBSDKLoginKit/Internal/FBSDKReferralManager+Internal.h @@ -0,0 +1,39 @@ +// 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 "TargetConditionals.h" + +#if !TARGET_OS_TV + + #ifdef BUCK + #import + #else + #import "FBSDKReferralManager.h" + #endif + + #ifdef FBSDKCOCOAPODS + #import + #else + #import "FBSDKCoreKit+Internal.h" + #endif + +@interface FBSDKReferralManager () + +@end + +#endif diff --git a/FBSDKLoginKit/FBSDKLoginKit/Internal/FBSDKReferralManagerLogger.h b/FBSDKLoginKit/FBSDKLoginKit/Internal/FBSDKReferralManagerLogger.h new file mode 100644 index 0000000000..48fa93b26f --- /dev/null +++ b/FBSDKLoginKit/FBSDKLoginKit/Internal/FBSDKReferralManagerLogger.h @@ -0,0 +1,34 @@ +// 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 "TargetConditionals.h" + +#if !TARGET_OS_TV + +#import "FBSDKReferralManager+Internal.h" + +NS_SWIFT_NAME(ReferralManagerLogger) +@interface FBSDKReferralManagerLogger : NSObject + +- (void)logReferralStart; + +- (void)logReferralEnd:(FBSDKReferralManagerResult *)result error:(NSError *)error; + +@end + +#endif diff --git a/FBSDKLoginKit/FBSDKLoginKit/Internal/FBSDKReferralManagerLogger.m b/FBSDKLoginKit/FBSDKLoginKit/Internal/FBSDKReferralManagerLogger.m new file mode 100644 index 0000000000..2d084dc030 --- /dev/null +++ b/FBSDKLoginKit/FBSDKLoginKit/Internal/FBSDKReferralManagerLogger.m @@ -0,0 +1,174 @@ +// 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 "TargetConditionals.h" + +#if !TARGET_OS_TV + + #import "FBSDKReferralManagerLogger.h" + + #import "FBSDKLoginConstants.h" + #import "FBSDKReferralManagerResult.h" + + #ifdef FBSDKCOCOAPODS + #import + #else + #import "FBSDKCoreKit+Internal.h" + #endif + +static NSString *const FBSDKReferralManagerLoggerParamIdentifierKey = @"0_logger_id"; +static NSString *const FBSDKReferralManagerLoggerParamTimestampKey = @"1_timestamp_ms"; +static NSString *const FBSDKReferralManagerLoggerParamResultKey = @"2_result"; +static NSString *const FBSDKReferralManagerLoggerParamErrorCodeKey = @"3_error_code"; +static NSString *const FBSDKReferralManagerLoggerParamErrorMessageKey = @"4_error_message"; +static NSString *const FBSDKReferralManagerLoggerParamExtrasKey = @"5_extras"; +static NSString *const FBSDKReferralManagerLoggerParamLoggingTokenKey = @"6_logging_token"; + +static NSString *const FBSDKReferralManagerLoggerValueEmpty = @""; + +static NSString *const FBSDKReferralManagerLoggerResultSuccessString = @"success"; +static NSString *const FBSDKReferralManagerLoggerResultCancelString = @"cancelled"; +static NSString *const FBSDKReferralManagerLoggerResultErrorString = @"error"; + +@implementation FBSDKReferralManagerLogger +{ + @private + NSString *_identifier; + NSMutableDictionary *_extras; + NSString *_loggingToken; +} + +- (instancetype)init +{ + if (self = [super init]) { + FBSDKServerConfiguration *serverConfiguration = [FBSDKServerConfigurationManager cachedServerConfiguration]; + NSString *loggingToken = serverConfiguration.loggingToken; + _identifier = [NSUUID UUID].UUIDString; + _extras = [NSMutableDictionary dictionary]; + _loggingToken = [loggingToken copy]; + } + return self; +} + +- (void)logReferralStart +{ + [self logEvent:FBSDKAppEventNameFBReferralStart params:[self _parametersForNewEvent]]; +} + +- (void)logReferralEnd:(FBSDKReferralManagerResult *)result error:(NSError *)error +{ + NSString *resultString = FBSDKReferralManagerLoggerValueEmpty; + + if (error != nil) { + resultString = FBSDKReferralManagerLoggerResultErrorString; + } else if (result.isCancelled) { + resultString = FBSDKReferralManagerLoggerResultCancelString; + } else if (result.referralCodes) { + resultString = FBSDKReferralManagerLoggerResultSuccessString; + } + + NSMutableDictionary *params = [self _parametersForNewEvent]; + [FBSDKTypeUtility dictionary:params setObject:resultString forKey:FBSDKReferralManagerLoggerParamResultKey]; + + if ([error.domain isEqualToString:FBSDKErrorDomain] || [error.domain isEqualToString:FBSDKLoginErrorDomain]) { + NSString *errorMessage = error.userInfo[@"error_message"] ?: error.userInfo[FBSDKErrorLocalizedDescriptionKey]; + [FBSDKTypeUtility dictionary:params + setObject:errorMessage + forKey:FBSDKReferralManagerLoggerParamErrorMessageKey]; + + NSString *errorCode = error.userInfo[FBSDKGraphRequestErrorGraphErrorCodeKey] ?: [NSString stringWithFormat:@"%ld", (long)error.code]; + [FBSDKTypeUtility dictionary:params + setObject:errorCode + forKey:FBSDKReferralManagerLoggerParamErrorCodeKey]; + + NSError *innerError = error.userInfo[NSUnderlyingErrorKey]; + if (innerError != nil) { + NSString *innerErrorMessage = innerError.userInfo[@"error_message"] ?: innerError.userInfo[NSLocalizedDescriptionKey]; + [FBSDKTypeUtility dictionary:_extras + setObject:innerErrorMessage + forKey:@"inner_error_message"]; + + NSString *innerErrorCode = innerError.userInfo[FBSDKGraphRequestErrorGraphErrorCodeKey] ?: [NSString stringWithFormat:@"%ld", (long)innerError.code]; + [FBSDKTypeUtility dictionary:_extras + setObject:innerErrorCode + forKey:@"inner_error_code"]; + } + } else if (error) { + [FBSDKTypeUtility dictionary:params + setObject:@(error.code) + forKey:FBSDKReferralManagerLoggerParamErrorCodeKey]; + [FBSDKTypeUtility dictionary:params + setObject:error.localizedDescription + forKey:FBSDKReferralManagerLoggerParamErrorMessageKey]; + } + + [self logEvent:FBSDKAppEventNameFBReferralEnd params:params]; +} + +- (NSMutableDictionary *)_parametersForNewEvent +{ + NSMutableDictionary *eventParameters = [NSMutableDictionary new]; + + // NOTE: We ALWAYS add all params to each event, to ensure predictable mapping on the backend. + [FBSDKTypeUtility dictionary:eventParameters + setObject:_identifier ?: FBSDKReferralManagerLoggerValueEmpty + forKey:FBSDKReferralManagerLoggerParamIdentifierKey]; + [FBSDKTypeUtility dictionary:eventParameters + setObject:@(round(1000 * [NSDate date].timeIntervalSince1970)) + forKey:FBSDKReferralManagerLoggerParamTimestampKey]; + [FBSDKTypeUtility dictionary:eventParameters + setObject:FBSDKReferralManagerLoggerValueEmpty + forKey:FBSDKReferralManagerLoggerParamResultKey]; + [FBSDKTypeUtility dictionary:eventParameters + setObject:FBSDKReferralManagerLoggerValueEmpty + forKey:FBSDKReferralManagerLoggerParamErrorCodeKey]; + [FBSDKTypeUtility dictionary:eventParameters + setObject:FBSDKReferralManagerLoggerValueEmpty + forKey:FBSDKReferralManagerLoggerParamErrorMessageKey]; + [FBSDKTypeUtility dictionary:eventParameters + setObject:FBSDKReferralManagerLoggerValueEmpty + forKey:FBSDKReferralManagerLoggerParamExtrasKey]; + [FBSDKTypeUtility dictionary:eventParameters + setObject:_loggingToken ?: FBSDKReferralManagerLoggerValueEmpty + forKey:FBSDKReferralManagerLoggerParamLoggingTokenKey]; + + return eventParameters; +} + +- (void)logEvent:(NSString *)eventName params:(NSMutableDictionary *)params +{ + if (_identifier) { + NSString *extrasJSONString = [FBSDKBasicUtility JSONStringForObject:_extras + error:NULL + invalidObjectHandler:NULL]; + if (extrasJSONString) { + [FBSDKTypeUtility dictionary:params + setObject:extrasJSONString + forKey:FBSDKReferralManagerLoggerParamExtrasKey]; + } + [_extras removeAllObjects]; + + [FBSDKAppEvents logInternalEvent:eventName + parameters:params + isImplicitlyLogged:YES]; + } +} + +@end + +#endif diff --git a/FBSDKLoginKit/FBSDKLoginKit/Internal/_FBSDKLoginRecoveryAttempter.h b/FBSDKLoginKit/FBSDKLoginKit/Internal/_FBSDKLoginRecoveryAttempter.h index 63efb5e81f..a1643ba80b 100644 --- a/FBSDKLoginKit/FBSDKLoginKit/Internal/_FBSDKLoginRecoveryAttempter.h +++ b/FBSDKLoginKit/FBSDKLoginKit/Internal/_FBSDKLoginRecoveryAttempter.h @@ -20,11 +20,11 @@ #if !TARGET_OS_TV -#ifdef FBSDKCOCOAPODS -#import -#else -#import "FBSDKCoreKit+Internal.h" -#endif + #ifdef FBSDKCOCOAPODS + #import + #else + #import "FBSDKCoreKit+Internal.h" + #endif @interface _FBSDKLoginRecoveryAttempter : FBSDKErrorRecoveryAttempter diff --git a/FBSDKLoginKit/FBSDKLoginKit/Internal/_FBSDKLoginRecoveryAttempter.m b/FBSDKLoginKit/FBSDKLoginKit/Internal/_FBSDKLoginRecoveryAttempter.m index d344be4689..f884b4b973 100644 --- a/FBSDKLoginKit/FBSDKLoginKit/Internal/_FBSDKLoginRecoveryAttempter.m +++ b/FBSDKLoginKit/FBSDKLoginKit/Internal/_FBSDKLoginRecoveryAttempter.m @@ -20,9 +20,10 @@ #if !TARGET_OS_TV -#import "_FBSDKLoginRecoveryAttempter.h" + #import "_FBSDKLoginRecoveryAttempter.h" -#import "FBSDKLoginKit+Internal.h" + #import "FBSDKLoginKit+Internal.h" + #import "FBSDKLoginManagerLoginResult+Internal.h" @implementation _FBSDKLoginRecoveryAttempter @@ -30,15 +31,15 @@ - (void)attemptRecoveryFromError:(NSError *)error optionIndex:(NSUInteger)recoveryOptionIndex delegate:(id)delegate didRecoverSelector:(SEL)didRecoverSelector - contextInfo:(void *)contextInfo { - - void(^handler)(BOOL) = ^(BOOL didRecover) { + contextInfo:(void *)contextInfo +{ + void (^handler)(BOOL) = ^(BOOL didRecover) { [super completeRecovery:didRecover delegate:delegate didRecoverSelector:didRecoverSelector contextInfo:contextInfo]; }; NSSet *currentPermissions = [FBSDKAccessToken currentAccessToken].permissions; if (recoveryOptionIndex == 0 && currentPermissions.count > 0) { FBSDKLoginManager *login = [[FBSDKLoginManager alloc] init]; - [login logInWithPermissions:currentPermissions handler:^(FBSDKLoginManagerLoginResult *result, NSError *loginError) { + [login logInWithPermissions:currentPermissions.allObjects fromViewController:nil handler:^(FBSDKLoginManagerLoginResult *result, NSError *loginError) { // we can only consider a recovery successful if there are no declines // (note this could still set an updated currentAccessToken). handler(!loginError && !result.isCancelled && result.declinedPermissions.count == 0); diff --git a/FBSDKLoginKit/FBSDKLoginKit/Swift/LoginConfiguration.swift b/FBSDKLoginKit/FBSDKLoginKit/Swift/LoginConfiguration.swift new file mode 100644 index 0000000000..292fd05d29 --- /dev/null +++ b/FBSDKLoginKit/FBSDKLoginKit/Swift/LoginConfiguration.swift @@ -0,0 +1,45 @@ +// Copyright (c) 2016-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 FBSDKCoreKit + +public extension LoginConfiguration { + + /** + Attempts to allocate and initialize a new configuration with the expected parameters. + + - parameter permissions: The requested permissions for the login attempt. + The only permissions allowed when `tracking` is `.limited` are 'email' and 'public_profile'. + Defaults to an empty `Permission` array. + - parameter tracking: The tracking preference to use for a login attempt. Defaults to `.enabled` + - parameter nonce: An optional nonce to use for the login attempt. + A valid nonce must be an alphanumeric string without whitespace. + Creation of the configuration will fail if the nonce is invalid. Defaults to a `UUID` string. + */ + convenience init?( + permissions: Set = [], + tracking: LoginTracking = .enabled, + nonce: String = UUID().uuidString + ) { + self.init( + __permissions: permissions.map { $0.name }, + tracking: tracking, + nonce: nonce + ) + } +} diff --git a/FBSDKLoginKit/FBSDKLoginKit/Swift/LoginManager.swift b/FBSDKLoginKit/FBSDKLoginKit/Swift/LoginManager.swift index 5e8e8f0c7f..301e8e1892 100644 --- a/FBSDKLoginKit/FBSDKLoginKit/Swift/LoginManager.swift +++ b/FBSDKLoginKit/FBSDKLoginKit/Swift/LoginManager.swift @@ -30,7 +30,7 @@ public typealias LoginResultBlock = (LoginResult) -> Void @available(tvOS, unavailable) public enum LoginResult { /// User succesfully logged in. Contains granted, declined permissions and access token. - case success(granted: Set, declined: Set, token: FBSDKCoreKit.AccessToken) + case success(granted: Set, declined: Set, token: FBSDKCoreKit.AccessToken?) /// Login attempt was cancelled by the user. case cancelled /// Login attempt failed. @@ -42,14 +42,14 @@ public enum LoginResult { return } - guard !result.isCancelled, let token = result.token else { + guard !result.isCancelled else { self = .cancelled return } let granted: Set = Set(result.grantedPermissions.map { Permission(stringLiteral: $0) }) let declined: Set = Set(result.declinedPermissions.map { Permission(stringLiteral: $0) }) - self = .success(granted: granted, declined: declined, token: token) + self = .success(granted: granted, declined: declined, token: result.token) } } @@ -79,30 +79,98 @@ public extension LoginManager { /** Logs the user in or authorizes additional permissions. - Use this method when asking for read permissions. You should only ask for permissions when they - are needed and explain the value to the user. You can inspect the `declinedPermissions` in the result to also + Use this method when asking for permissions. You should only ask for permissions when they + are needed and the value should be explained to the user. You can inspect the result's `declinedPermissions` to also provide more information to the user if they decline permissions. - This method will present UI the user. You typically should check if `AccessToken.current` already - contains the permissions you need before asking to reduce unnecessary app switching. + This method will present a UI to the user. To reduce unnecessary app switching, you should typically check if + `AccessToken.current` already contains the permissions you need. If it does, you probably + do not need to call this method. + + You can only perform one login call at a time. Calling a login method before the completion handler is called + on a previous login will result in an error. - parameter permissions: Array of read permissions. Default: `[.PublicProfile]` - parameter viewController: Optional view controller to present from. Default: topmost view controller. - parameter completion: Optional callback. */ - func logIn(permissions: [Permission] = [.publicProfile], - viewController: UIViewController? = nil, - completion: LoginResultBlock? = nil) { + func logIn( + permissions: [Permission] = [.publicProfile], + viewController: UIViewController? = nil, + completion: LoginResultBlock? = nil + ) { self.logIn(permissions: permissions.map { $0.name }, from: viewController, handler: sdkCompletion(completion)) } + /** + Logs the user in or authorizes additional permissions. + + Use this method when asking for permissions. You should only ask for permissions when they + are needed and the value should be explained to the user. You can inspect the result's `declinedPermissions` to also + provide more information to the user if they decline permissions. + + This method will present a UI to the user. To reduce unnecessary app switching, you should typically check if + `AccessToken.current` already contains the permissions you need. If it does, you probably + do not need to call this method. + + You can only perform one login call at a time. Calling a login method before the completion handler is called + on a previous login will result in an error. + + - parameter viewController: Optional view controller to present from. Default: topmost view controller. + - parameter configuration the login configuration to use. + - parameter completion: Optional callback. + */ + func logIn( + viewController: UIViewController? = nil, + configuration: LoginConfiguration, + completion: @escaping LoginResultBlock + ) { + let legacyCompletion = { (result: LoginManagerLoginResult?, error: Error?) in + let result = LoginResult(result: result, error: error) + completion(result) + } + self.__logIn(from: viewController, configuration: configuration, completion: legacyCompletion) + } + + /** + Logs the user in or authorizes additional permissions. + + Use this method when asking for permissions. You should only ask for permissions when they + are needed and the value should be explained to the user. You can inspect the result's `declinedPermissions` to also + provide more information to the user if they decline permissions. + + This method will present a UI to the user. To reduce unnecessary app switching, you should typically check if + `AccessToken.current` already contains the permissions you need. If it does, you probably + do not need to call this method. + + You can only perform one login call at a time. Calling a login method before the completion handler is called + on a previous login will result in an error. + + - parameter configuration the login configuration to use. + - parameter completion: Optional callback. + */ + func logIn( + configuration: LoginConfiguration, + completion: @escaping LoginResultBlock + ) { + let legacyCompletion = { (result: LoginManagerLoginResult?, error: Error?) in + let result = LoginResult(result: result, error: error) + completion(result) + } + self.__logIn(from: nil, configuration: configuration, completion: legacyCompletion) + } + private func sdkCompletion(_ completion: LoginResultBlock?) -> LoginManagerLoginResultBlock? { - guard let completion = completion else { + guard let original = completion else { return nil } + return convertedResultHandler(original) + } + + private func convertedResultHandler(_ original: @escaping LoginResultBlock) -> LoginManagerLoginResultBlock { return { (result: LoginManagerLoginResult?, error: Error?) in let result = LoginResult(result: result, error: error) - completion(result) + original(result) } } } diff --git a/FBSDKLoginKit/FBSDKLoginKit/include/FBSDKLoginConfiguration.h b/FBSDKLoginKit/FBSDKLoginKit/include/FBSDKLoginConfiguration.h new file mode 120000 index 0000000000..57ba266e9d --- /dev/null +++ b/FBSDKLoginKit/FBSDKLoginKit/include/FBSDKLoginConfiguration.h @@ -0,0 +1 @@ +../FBSDKLoginConfiguration.h \ No newline at end of file diff --git a/FBSDKLoginKit/FBSDKLoginKit/include/FBSDKReferralCode.h b/FBSDKLoginKit/FBSDKLoginKit/include/FBSDKReferralCode.h new file mode 120000 index 0000000000..c35a2341f1 --- /dev/null +++ b/FBSDKLoginKit/FBSDKLoginKit/include/FBSDKReferralCode.h @@ -0,0 +1 @@ +../FBSDKReferralCode.h \ No newline at end of file diff --git a/FBSDKLoginKit/FBSDKLoginKit/include/FBSDKReferralManager.h b/FBSDKLoginKit/FBSDKLoginKit/include/FBSDKReferralManager.h new file mode 120000 index 0000000000..78b0848922 --- /dev/null +++ b/FBSDKLoginKit/FBSDKLoginKit/include/FBSDKReferralManager.h @@ -0,0 +1 @@ +../FBSDKReferralManager.h \ No newline at end of file diff --git a/FBSDKLoginKit/FBSDKLoginKit/include/FBSDKReferralManagerResult.h b/FBSDKLoginKit/FBSDKLoginKit/include/FBSDKReferralManagerResult.h new file mode 120000 index 0000000000..b142bb3321 --- /dev/null +++ b/FBSDKLoginKit/FBSDKLoginKit/include/FBSDKReferralManagerResult.h @@ -0,0 +1 @@ +../FBSDKReferralManagerResult.h \ No newline at end of file diff --git a/FBSDKLoginKit/FBSDKLoginKitTests/FBSDKLoginCompletionTests.m b/FBSDKLoginKit/FBSDKLoginKitTests/FBSDKLoginCompletionTests.m new file mode 100644 index 0000000000..1e6a4bee43 --- /dev/null +++ b/FBSDKLoginKit/FBSDKLoginKitTests/FBSDKLoginCompletionTests.m @@ -0,0 +1,552 @@ +// 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 + +#ifdef BUCK + #import + #import +#else + #import "FBSDKLoginCompletion+Internal.h" + #import "FBSDKPermission.h" +#endif +#import "FBSDKLoginKitTests-Swift.h" + +static NSString *const _fakeAppID = @"1234567"; +static NSString *const _fakeChallence = @"some_challenge"; + +@interface FBSDKLoginURLCompleter (Testing) + +- (FBSDKLoginCompletionParameters *)parameters; +- (void)exchangeNonceForTokenWithGraphRequestConnectionProvider:(id)connection + handler:(FBSDKLoginCompletionParametersBlock)handler; + +- (void)exchangeNonceForTokenWithHandler:(FBSDKLoginCompletionParametersBlock)handler; + +- (void)fetchAndSetPropertiesForParameters:(nonnull FBSDKLoginCompletionParameters *)parameters + nonce:(nonnull NSString *)nonce + handler:(FBSDKLoginCompletionParametersBlock)handler; + +@end + +@interface FBSDKLoginCompletionTests : XCTestCase +{ + NSDictionary *_parameters; +} + +@end + +@interface FBSDKTestLoginURLCompleter : FBSDKLoginURLCompleter + +@property int exchangeNonceCount; + +@property int fetchAndSetAuthTokenCount; + +@end + +@implementation FBSDKTestLoginURLCompleter + +- (void)exchangeNonceForTokenWithHandler:(FBSDKLoginCompletionParametersBlock)handler +{ + _exchangeNonceCount += 1; +} + +- (void)fetchAndSetPropertiesForParameters:(nonnull FBSDKLoginCompletionParameters *)parameters + nonce:(nonnull NSString *)nonce + handler:(FBSDKLoginCompletionParametersBlock)handler +{ + _fetchAndSetAuthTokenCount += 1; +} + +@end + +@implementation FBSDKLoginCompletionTests + +- (void)setUp +{ + [super setUp]; + + int secInDay = 60 * 60 * 24; + + _parameters = @{ + @"access_token" : @"some_access_token", + @"id_token" : @"some_id_token", + @"nonce" : @"some_nonce", + @"granted_scopes" : @"public_profile,openid", + @"denied_scopes" : @"email", + @"signed_request" : @"some_signed_request", + @"user_id" : @"123", + @"expires" : [@(NSDate.date.timeIntervalSince1970 + secInDay * 60) stringValue], + @"expires_at" : [@(NSDate.date.timeIntervalSince1970 + secInDay * 60) stringValue], + @"expires_in" : [@(secInDay * 60) stringValue], + @"data_access_expiration_time" : [@(NSDate.date.timeIntervalSince1970 + secInDay * 90) stringValue], + @"state" : [NSString stringWithFormat:@"{\"challenge\":\"%@\"}", _fakeChallence], + @"graph_domain" : @"facebook", + @"error" : @"some_error", + @"error_message" : @"some_error_message", + }; +} + +// MARK: Creation + +- (void)testInitWithAccessTokenWithIDToken +{ + NSMutableDictionary *parameters = self.parametersWithIDtoken.mutableCopy; + [parameters addEntriesFromDictionary:self.parametersWithAccessToken]; + + FBSDKLoginURLCompleter *completer = [[FBSDKLoginURLCompleter alloc] initWithURLParameters:parameters appID:_fakeAppID]; + + [self verifyParameters:completer.parameters urlParameter:parameters]; +} + +- (void)testInitWithAccessToken +{ + NSDictionary *parameters = self.parametersWithAccessToken; + + FBSDKLoginURLCompleter *completer = [[FBSDKLoginURLCompleter alloc] initWithURLParameters:parameters appID:_fakeAppID]; + + [self verifyParameters:completer.parameters urlParameter:parameters]; +} + +- (void)testInitWithNonce +{ + NSDictionary *parameters = self.parametersWithNonce; + + FBSDKLoginURLCompleter *completer = [[FBSDKLoginURLCompleter alloc] initWithURLParameters:parameters appID:_fakeAppID]; + + [self verifyParameters:completer.parameters urlParameter:parameters]; +} + +- (void)testInitWithIDToken +{ + NSDictionary *parameters = self.parametersWithIDtoken; + + FBSDKLoginURLCompleter *completer = [[FBSDKLoginURLCompleter alloc] initWithURLParameters:parameters appID:_fakeAppID]; + + [self verifyParameters:completer.parameters urlParameter:parameters]; +} + +- (void)testInitWithoutAccessTokenWithoutIDTokenWithoutNonce +{ + NSDictionary *parameters = self.parametersWithoutAccessTokenWithoutIDTokenWithoutNonce; + + FBSDKLoginURLCompleter *completer = [[FBSDKLoginURLCompleter alloc] initWithURLParameters:parameters appID:_fakeAppID]; + + [self verifyEmptyParameters:completer.parameters]; +} + +- (void)testInitWithEmptyAccessTokenWithEmptyIDTokenWithEmptyNonce +{ + NSDictionary *parameters = self.parametersWithEmptyAccessTokenWithEmptyIDTokenWithEmptyNonce; + + FBSDKLoginURLCompleter *completer = [[FBSDKLoginURLCompleter alloc] initWithURLParameters:parameters appID:_fakeAppID]; + + [self verifyEmptyParameters:completer.parameters]; +} + +- (void)testInitWithEmptyParameters +{ + FBSDKLoginURLCompleter *completer = [[FBSDKLoginURLCompleter alloc] initWithURLParameters:@{} appID:_fakeAppID]; + + [self verifyEmptyParameters:completer.parameters]; +} + +- (void)testInitWithIDTokenAndNonce +{ + NSMutableDictionary *parameters = self.parametersWithIDtoken.mutableCopy; + [parameters addEntriesFromDictionary:self.parametersWithNonce]; + + FBSDKLoginURLCompleter *completer = [[FBSDKLoginURLCompleter alloc] initWithURLParameters:parameters appID:_fakeAppID]; + + XCTAssertNotNil(completer.parameters.error); +} + +- (void)testInitWithError +{ + NSDictionary *parameters = self.parametersWithError; + + FBSDKLoginURLCompleter *completer = [[FBSDKLoginURLCompleter alloc] initWithURLParameters:parameters appID:_fakeAppID]; + + XCTAssertNotNil(completer.parameters.error); +} + +- (void)testInitWithFuzzyParameters +{ + for (int i = 0; i < 100; i++) { + NSDictionary *parameters = [Fuzzer randomizeWithJson:_parameters]; + FBSDKLoginURLCompleter *_completer __unused = [[FBSDKLoginURLCompleter alloc] initWithURLParameters:parameters appID:_fakeAppID]; + } +} + +// MARK: - Nonce Exchange + +- (void)testExchangeNonceForTokenWithMissingHandler +{ + FBSDKLoginURLCompleter *completer = [[FBSDKLoginURLCompleter alloc] initWithURLParameters:_parameters appID:_fakeAppID]; + FakeGraphRequestConnection *connection = [FakeGraphRequestConnection new]; + + [completer exchangeNonceForTokenWithGraphRequestConnectionProvider:connection + handler:nil]; + XCTAssertNil(connection.capturedGraphRequest, "Should not create a graph request if there's no handler to use the result"); +} + +- (void)testNonceExchangeWithoutNonce +{ + NSDictionary *parameters = self.rawParametersWithMissingNonce; + + FBSDKLoginURLCompleter *completer = [[FBSDKLoginURLCompleter alloc] initWithURLParameters:parameters appID:_fakeAppID]; + FakeGraphRequestConnection *connection = [FakeGraphRequestConnection new]; + + __block BOOL completionWasInvoked = NO; + FBSDKLoginCompletionParametersBlock handler = ^(FBSDKLoginCompletionParameters *_Nonnull completionParams) { + XCTAssertEqualObjects( + completer.parameters, + completionParams, + "Should call the completion with the provided parameters" + ); + XCTAssertEqual(completer.parameters.error.code, FBSDKErrorInvalidArgument, "Should provide an error with the expected code"); + completionWasInvoked = YES; + }; + + [completer exchangeNonceForTokenWithGraphRequestConnectionProvider:connection + handler:handler]; + XCTAssertTrue(completionWasInvoked); +} + +- (void)testNonceExchangeWithoutAppID +{ + NSString *appID = @"123"; + appID = nil; + FBSDKLoginURLCompleter *completer = [[FBSDKLoginURLCompleter alloc] initWithURLParameters:_parameters appID:appID]; + FakeGraphRequestConnection *connection = [FakeGraphRequestConnection new]; + + __block BOOL completionWasInvoked = NO; + FBSDKLoginCompletionParametersBlock handler = ^(FBSDKLoginCompletionParameters *_Nonnull completionParams) { + XCTAssertEqualObjects( + completer.parameters, + completionParams, + "Should call the completion with the provided parameters" + ); + XCTAssertEqual(completer.parameters.error.code, FBSDKErrorInvalidArgument, "Should provide an error with the expected code"); + completionWasInvoked = YES; + }; + + [completer exchangeNonceForTokenWithGraphRequestConnectionProvider:connection + handler:handler]; + XCTAssertTrue(completionWasInvoked); + XCTAssertNil(connection.capturedGraphRequest, "Should not create a graph request if there's no handler to use the result"); +} + +- (void)testNonceExchangeGraphRequestCreation +{ + FBSDKLoginURLCompleter *completer = [[FBSDKLoginURLCompleter alloc] initWithURLParameters:_parameters appID:_fakeAppID]; + FakeGraphRequestConnection *connection = [FakeGraphRequestConnection new]; + + [completer exchangeNonceForTokenWithGraphRequestConnectionProvider:connection + handler:^(FBSDKLoginCompletionParameters *_Nonnull parameters) { + // not important here + }]; + XCTAssertEqualObjects( + connection.capturedGraphRequest.graphPath, + @"oauth/access_token", + "Should create a graph request with the expected graph path" + ); + XCTAssertEqualObjects( + [connection.capturedGraphRequest.parameters objectForKey:@"grant_type"], + @"fb_exchange_nonce", + "Should create a graph request with the expected grant type parameter" + ); + XCTAssertEqualObjects( + [connection.capturedGraphRequest.parameters objectForKey:@"fb_exchange_nonce"], + completer.parameters.nonceString, + "Should create a graph request with the expected nonce parameter" + ); + XCTAssertEqualObjects( + [connection.capturedGraphRequest.parameters objectForKey:@"client_id"], + _fakeAppID, + "Should create a graph request with the expected app id parameter" + ); + XCTAssertEqualObjects( + [connection.capturedGraphRequest.parameters objectForKey:@"fields"], + @"", + "Should create a graph request with the expected fields parameter" + ); + XCTAssertEqual( + connection.capturedGraphRequest.flags, + FBSDKGraphRequestFlagDoNotInvalidateTokenOnError + | FBSDKGraphRequestFlagDisableErrorRecovery, + "The graph request should not invalidate the token on error or disable error recovery" + ); +} + +- (void)testNonceExchangeCompletionWithError +{ + FBSDKLoginURLCompleter *completer = [[FBSDKLoginURLCompleter alloc] initWithURLParameters:_parameters appID:_fakeAppID]; + FakeGraphRequestConnection *connection = [FakeGraphRequestConnection new]; + + __block BOOL completionWasInvoked = NO; + FBSDKLoginCompletionParametersBlock handler = ^(FBSDKLoginCompletionParameters *_Nonnull completionParams) { + XCTAssertEqualObjects( + completer.parameters, + completionParams, + "Should call the completion with the provided parameters" + ); + XCTAssertEqualObjects(completer.parameters.error, self.sampleError, "Should pass through the error from the graph request"); + completionWasInvoked = YES; + }; + + [completer exchangeNonceForTokenWithGraphRequestConnectionProvider:connection + handler:handler]; + connection.capturedCompletionHandler(nil, nil, self.sampleError); + XCTAssertTrue(completionWasInvoked); +} + +- (void)testNonceExchangeCompletionWithAccessTokenString +{ + FBSDKLoginURLCompleter *completer = [[FBSDKLoginURLCompleter alloc] initWithURLParameters:_parameters appID:_fakeAppID]; + FakeGraphRequestConnection *connection = [FakeGraphRequestConnection new]; + NSDictionary *stubbedResult = @{ @"access_token" : self.name }; + + __block BOOL completionWasInvoked = NO; + FBSDKLoginCompletionParametersBlock handler = ^(FBSDKLoginCompletionParameters *_Nonnull completionParams) { + XCTAssertEqualObjects( + completer.parameters, + completionParams, + "Should call the completion with the provided parameters" + ); + XCTAssertEqualObjects( + completer.parameters.accessTokenString, + self.name, + "Should set the access token string from the graph request's result" + ); + completionWasInvoked = YES; + }; + + [completer exchangeNonceForTokenWithGraphRequestConnectionProvider:connection + handler:handler]; + connection.capturedCompletionHandler(nil, stubbedResult, nil); + XCTAssertTrue(completionWasInvoked); +} + +- (void)testNonceExchangeWithRandomResults +{ + FBSDKLoginURLCompleter *completer = [[FBSDKLoginURLCompleter alloc] initWithURLParameters:_parameters appID:_fakeAppID]; + FakeGraphRequestConnection *connection = [FakeGraphRequestConnection new]; + NSDictionary *stubbedResult = @{ + @"access_token" : self.name, + @"expires_in" : @"10000", + @"data_access_expiration_time" : @1 + }; + + __block BOOL completionWasInvoked = NO; + FBSDKLoginCompletionParametersBlock handler = ^(FBSDKLoginCompletionParameters *_Nonnull completionParams) { + // Basically just making sure that nothing crashes here when we feed it garbage results + completionWasInvoked = YES; + }; + + [completer exchangeNonceForTokenWithGraphRequestConnectionProvider:connection + handler:handler]; + + for (int i = 0; i < 100; i++) { + NSDictionary *params = [stubbedResult copy]; + NSDictionary *parameters = [Fuzzer randomizeWithJson:params]; + connection.capturedCompletionHandler(nil, parameters, nil); + XCTAssertTrue(completionWasInvoked); + completionWasInvoked = NO; + } +} + +// MARK: Completion + +- (void)testCompleteWithNonce +{ + FBSDKTestLoginURLCompleter *completer = [[FBSDKTestLoginURLCompleter alloc] initWithURLParameters:self.parametersWithNonce appID:_fakeAppID]; + FBSDKLoginCompletionParametersBlock handler = ^(FBSDKLoginCompletionParameters *parameters) { + // do nothing + }; + + [completer completeLoginWithHandler:handler]; + + XCTAssertNil(completer.parameters.error); + XCTAssertEqual(completer.exchangeNonceCount, 1); + XCTAssertEqual(completer.fetchAndSetAuthTokenCount, 0); +} + +- (void)testCompleteWithAuthenticationTokenWithoutNonce +{ + FBSDKTestLoginURLCompleter *completer = [[FBSDKTestLoginURLCompleter alloc] initWithURLParameters:self.parametersWithIDtoken appID:_fakeAppID]; + FBSDKLoginCompletionParametersBlock handler = ^(FBSDKLoginCompletionParameters *parameters) { + // do nothing + }; + + [completer completeLoginWithHandler:handler]; + + XCTAssertNotNil(completer.parameters.error); + XCTAssertEqual(completer.exchangeNonceCount, 0); + XCTAssertEqual(completer.fetchAndSetAuthTokenCount, 0); +} + +- (void)testCompleteWithAuthenticationTokenWithNonce +{ + FBSDKTestLoginURLCompleter *completer = [[FBSDKTestLoginURLCompleter alloc] initWithURLParameters:self.parametersWithIDtoken appID:_fakeAppID]; + FBSDKLoginCompletionParametersBlock handler = ^(FBSDKLoginCompletionParameters *parameters) { + // do nothing + }; + + [completer completeLoginWithHandler:handler nonce:@"some_nonce"]; + + XCTAssertNil(completer.parameters.error); + XCTAssertEqual(completer.exchangeNonceCount, 0); + XCTAssertEqual(completer.fetchAndSetAuthTokenCount, 1); +} + +- (void)testCompleteWithAccessToken +{ + FBSDKTestLoginURLCompleter *completer = [[FBSDKTestLoginURLCompleter alloc] initWithURLParameters:self.parametersWithAccessToken appID:_fakeAppID]; + + __block BOOL wasCalled = NO; + FBSDKLoginCompletionParametersBlock handler = ^(FBSDKLoginCompletionParameters *parameters) { + wasCalled = YES; + }; + + [completer completeLoginWithHandler:handler nonce:@"some_nonce"]; + + XCTAssert(wasCalled, @"Handler should be invoked syncronously"); + XCTAssertNil(completer.parameters.error); + XCTAssertEqual(completer.exchangeNonceCount, 0); + XCTAssertEqual(completer.fetchAndSetAuthTokenCount, 0); +} + +- (void)testCompleteWithEmptyParameters +{ + FBSDKTestLoginURLCompleter *completer = [[FBSDKTestLoginURLCompleter alloc] initWithURLParameters:@{} appID:_fakeAppID]; + + __block BOOL wasCalled = NO; + FBSDKLoginCompletionParametersBlock handler = ^(FBSDKLoginCompletionParameters *parameters) { + wasCalled = YES; + }; + + [completer completeLoginWithHandler:handler nonce:@"some_nonce"]; + + XCTAssert(wasCalled, @"Handler should be invoked syncronously"); + XCTAssertNil(completer.parameters.error); + XCTAssertEqual(completer.exchangeNonceCount, 0); + XCTAssertEqual(completer.fetchAndSetAuthTokenCount, 0); +} + +// MARK: Helpers + +- (NSError *)sampleError +{ + return [NSError errorWithDomain:self.name code:0 userInfo:nil]; +} + +- (NSDictionary *)rawParametersWithMissingNonce +{ + NSMutableDictionary *parameters = _parameters.mutableCopy; + [parameters removeObjectsForKeys:@[@"nonce"]]; + return parameters; +} + +- (NSDictionary *)parametersWithNonce +{ + NSMutableDictionary *parameters = _parameters.mutableCopy; + [parameters removeObjectsForKeys:@[@"id_token", @"access_token", @"error", @"error_message"]]; + return parameters; +} + +- (NSDictionary *)parametersWithAccessToken +{ + NSMutableDictionary *parameters = _parameters.mutableCopy; + [parameters removeObjectsForKeys:@[@"id_token", @"nonce", @"error", @"error_message"]]; + return parameters; +} + +- (NSDictionary *)parametersWithIDtoken +{ + NSMutableDictionary *parameters = _parameters.mutableCopy; + [parameters removeObjectsForKeys:@[@"access_token", @"nonce", @"error", @"error_message"]]; + return parameters; +} + +- (NSDictionary *)parametersWithoutAccessTokenWithoutIDTokenWithoutNonce +{ + NSMutableDictionary *parameters = _parameters.mutableCopy; + [parameters removeObjectsForKeys:@[@"id_token", @"access_token", @"nonce", @"error", @"error_message"]]; + return parameters; +} + +- (NSDictionary *)parametersWithEmptyAccessTokenWithEmptyIDTokenWithEmptyNonce +{ + NSMutableDictionary *parameters = _parameters.mutableCopy; + [parameters removeObjectsForKeys:@[@"error", @"error_message"]]; + [parameters setValue:@"" forKey:@"access_token"]; + [parameters setValue:@"" forKey:@"id_token"]; + [parameters setValue:@"" forKey:@"nonce"]; + return parameters; +} + +- (NSDictionary *)parametersWithError +{ + NSMutableDictionary *parameters = _parameters.mutableCopy; + [parameters removeObjectsForKeys:@[@"id_token", @"access_token", @"nonce"]]; + return parameters; +} + +- (void)verifyParameters:(FBSDKLoginCompletionParameters *)parameters urlParameter:(NSDictionary *)urlParameters +{ + XCTAssertEqualObjects(parameters.accessTokenString, urlParameters[@"access_token"]); + XCTAssertEqualObjects(parameters.authenticationTokenString, urlParameters[@"id_token"]); + XCTAssertEqualObjects(parameters.appID, _fakeAppID); + XCTAssertEqualObjects(parameters.challenge, _fakeChallence); + NSSet *permissions = [FBSDKPermission permissionsFromRawPermissions:[NSSet setWithArray:[urlParameters[@"granted_scopes"] componentsSeparatedByString:@","]]]; + XCTAssertEqualObjects(parameters.permissions, permissions); + NSSet *declinedPermissions = [FBSDKPermission permissionsFromRawPermissions:[NSSet setWithArray:[urlParameters[@"denied_scopes"] componentsSeparatedByString:@","]]]; + XCTAssertEqualObjects(parameters.declinedPermissions, declinedPermissions); + XCTAssertEqualObjects(parameters.userID, urlParameters[@"user_id"]); + XCTAssertEqualObjects(parameters.graphDomain, urlParameters[@"graph_domain"]); + + if (urlParameters[@"expires"] || urlParameters[@"expires_at"] || urlParameters[@"expires_in"]) { + XCTAssertNotNil(parameters.expirationDate); + } + if (urlParameters[@"data_access_expiration_time"]) { + XCTAssertNotNil(parameters.dataAccessExpirationDate); + } + XCTAssertEqualObjects(parameters.nonceString, urlParameters[@"nonce"]); + XCTAssertNil(parameters.error); +} + +- (void)verifyEmptyParameters:(FBSDKLoginCompletionParameters *)parameters +{ + XCTAssertNil(parameters.accessTokenString); + XCTAssertNil(parameters.authenticationTokenString); + XCTAssertNil(parameters.appID); + XCTAssertNil(parameters.challenge); + XCTAssertNil(parameters.permissions); + XCTAssertNil(parameters.declinedPermissions); + XCTAssertNil(parameters.userID); + XCTAssertNil(parameters.graphDomain); + XCTAssertNil(parameters.expirationDate); + XCTAssertNil(parameters.dataAccessExpirationDate); + XCTAssertNil(parameters.nonceString); + XCTAssertNil(parameters.error); +} + +@end diff --git a/FBSDKLoginKit/FBSDKLoginKitTests/FBSDKLoginKitTests-Bridging-Header.h b/FBSDKLoginKit/FBSDKLoginKitTests/FBSDKLoginKitTests-Bridging-Header.h new file mode 100644 index 0000000000..a5223ad4fb --- /dev/null +++ b/FBSDKLoginKit/FBSDKLoginKitTests/FBSDKLoginKitTests-Bridging-Header.h @@ -0,0 +1,80 @@ +// 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 "FBSDKCoreKit+Internal.h" + +#ifdef BUCK + #import + #import + #import +#else + #import "FBSDKGraphRequestConnectionProviding.h" + #import "FBSDKNonceUtility.h" + #import "FBSDKPermission.h" +#endif + +@class FBSDKAuthenticationTokenClaims; + +NS_ASSUME_NONNULL_BEGIN + +// Categories needed to expose private methods to Swift + +@interface FBSDKLoginButton (Testing) + +- (FBSDKLoginConfiguration *)loginConfiguration; +- (BOOL)_isAuthenticated; +- (void)_fetchAndSetContent; +- (void)_initializeContent; +- (void)_updateContentForAccessToken; +- (void)_updateContentForUserProfile:(nullable FBSDKProfile *)profile; +- (void)_accessTokenDidChangeNotification:(NSNotification *)notification; +- (void)_profileDidChangeNotification:(NSNotification *)notification; +- (nullable NSString *)userName; +- (nullable NSString *)userID; + +@end + +@interface FBSDKAccessToken (Testing) + ++ (void)setCurrentAccessToken:(nullable FBSDKAccessToken *)token + shouldDispatchNotif:(BOOL)shouldDispatchNotif; + +@end + +@interface FBSDKProfile (Testing) + ++ (void)setCurrentProfile:(nullable FBSDKProfile *)profile + shouldPostNotification:(BOOL)shouldPostNotification; + +@end + +@interface FBSDKAuthenticationToken (Testing) + +- (instancetype)initWithTokenString:(NSString *)tokenString + nonce:(NSString *)nonce + claims:(nullable FBSDKAuthenticationTokenClaims *)claims + jti:(NSString *)jti; + ++ (void)setCurrentAuthenticationToken:(nullable FBSDKAuthenticationToken *)token + shouldPostNotification:(BOOL)shouldPostNotification; + +@end + +NS_ASSUME_NONNULL_END diff --git a/FBSDKLoginKit/FBSDKLoginKitTests/FBSDKLoginManagerLoggerTests.m b/FBSDKLoginKit/FBSDKLoginKitTests/FBSDKLoginManagerLoggerTests.m new file mode 100644 index 0000000000..34877e2e50 --- /dev/null +++ b/FBSDKLoginKit/FBSDKLoginKitTests/FBSDKLoginManagerLoggerTests.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 +#import + +#import + +#ifdef BUCK + #import + #import +#else + #import "FBSDKLoginManager.h" + #import "FBSDKLoginManagerLogger.h" +#endif + +@interface FBSDKAppEvents (Testing) + ++ (FBSDKAppEvents *)singleton; + +- (void)instanceLogEvent:(FBSDKAppEventName)eventName + valueToSum:(NSNumber *)valueToSum + parameters:(NSDictionary *)parameters + isImplicitlyLogged:(BOOL)isImplicitlyLogged + accessToken:(FBSDKAccessToken *)accessToken; + +@end + +@interface FBSDKLoginManagerLoggerTests : XCTestCase +@end + +@implementation FBSDKLoginManagerLoggerTests +{ + id _appEventsMock; + id _loginManagerMock; +} + +- (void)setUp +{ + [super setUp]; + _loginManagerMock = OCMClassMock(FBSDKLoginManager.class); + // Set up AppEvents singleton mock + _appEventsMock = OCMClassMock(FBSDKAppEvents.class); + OCMStub([_appEventsMock singleton]).andReturn(_appEventsMock); +} + +- (void)tearDown +{ + [super tearDown]; + [_loginManagerMock stopMocking]; + _loginManagerMock = nil; + [_appEventsMock stopMocking]; + _appEventsMock = nil; +} + +- (void)testExtrasForAddSingleLoggingExtra +{ + FBSDKLoginManagerLogger *logger = [[FBSDKLoginManagerLogger alloc] initWithLoggingToken:nil]; + BOOL (^verifyParameterContents)(NSDictionary *) = ^BOOL (NSDictionary *parameters) { + NSString *extras = [FBSDKTypeUtility dictionary:parameters objectForKey:@"6_extras" ofType:NSString.class]; + NSDictionary *extrasDictionary = [FBSDKBasicUtility objectForJSONString:extras error:nil]; + XCTAssertEqualObjects([FBSDKTypeUtility dictionary:extrasDictionary objectForKey:@"test_extra_key" ofType:NSString.class], @"test_extra_value"); + return YES; + }; + + [logger addSingleLoggingExtra:@"test_extra_value" forKey:@"test_extra_key"]; + [logger startSessionForLoginManager:_loginManagerMock]; + + OCMVerify( + [_appEventsMock instanceLogEvent:OCMArg.any + valueToSum:OCMArg.any + parameters:[OCMArg checkWithBlock:verifyParameterContents] + isImplicitlyLogged:OCMArg.any + accessToken:OCMArg.any] + ); +} + +@end diff --git a/FBSDKLoginKit/FBSDKLoginKitTests/FBSDKLoginManagerTests.m b/FBSDKLoginKit/FBSDKLoginKitTests/FBSDKLoginManagerTests.m index 198f8b742f..d6cb36f3b7 100644 --- a/FBSDKLoginKit/FBSDKLoginKitTests/FBSDKLoginManagerTests.m +++ b/FBSDKLoginKit/FBSDKLoginKitTests/FBSDKLoginManagerTests.m @@ -16,21 +16,54 @@ // 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 -#import +#ifdef BUCK + #import + #import + #import + #import +#else + #import "FBSDKLoginConstants.h" + #import "FBSDKLoginManager.h" + #import "FBSDKLoginManager+Internal.h" + #import "FBSDKLoginManagerLoginResult.h" +#endif -#import "FBSDKLoginManager+Internal.h" -#import "FBSDKLoginManagerLoginResult.h" #import "FBSDKLoginUtilityTests.h" static NSString *const kFakeAppID = @"7391628439"; - static NSString *const kFakeChallenge = @"a =bcdef"; +static NSString *const kFakeNonce = @"fedcb =a"; +static NSString *const kFakeJTI = @"a jti is just any string"; + +@interface FBSDKLoginManager (Testing) + +- (NSDictionary *)logInParametersFromURL:(NSURL *)url; + +- (NSString *)loadExpectedNonce; + +- (void)storeExpectedNonce:(NSString *)nonceExpected keychainStore:(FBSDKKeychainStore *)keychainStore; + +- (FBSDKLoginConfiguration *)configuration; + +@end + +@interface FBSDKAuthenticationTokenFactory (Testing) + ++ (void)setSkipSignatureVerification:(BOOL)value; + +@end + +@interface FBSDKAuthenticationToken (Testing) + +- (NSString *)jti; + +@end @interface FBSDKLoginManagerTests : XCTestCase @@ -39,12 +72,75 @@ @interface FBSDKLoginManagerTests : XCTestCase @implementation FBSDKLoginManagerTests { id _mockNSBundle; + id _mockInternalUtility; + id _mockLoginManager; + id _mockAccessTokenClass; + id _mockAuthenticationTokenClass; + id _mockProfileClass; + + NSDictionary *_claims; + NSDictionary *_header; } - (void)setUp { + [super setUp]; _mockNSBundle = [FBSDKLoginUtilityTests mainBundleMock]; [FBSDKSettings setAppID:kFakeAppID]; + [FBSDKAuthenticationToken setCurrentAuthenticationToken:nil]; + [FBSDKProfile setCurrentProfile:nil]; + [FBSDKAccessToken setCurrentAccessToken:nil]; + + _mockInternalUtility = OCMClassMock(FBSDKInternalUtility.class); + OCMStub(ClassMethod([_mockInternalUtility validateURLSchemes])); + + _mockLoginManager = OCMPartialMock([FBSDKLoginManager new]); + OCMStub([_mockLoginManager loadExpectedChallenge]).andReturn(kFakeChallenge); + OCMStub([_mockLoginManager loadExpectedNonce]).andReturn(kFakeNonce); + + _mockAccessTokenClass = OCMClassMock(FBSDKAccessToken.class); + _mockAuthenticationTokenClass = OCMClassMock(FBSDKAuthenticationToken.class); + _mockProfileClass = OCMClassMock(FBSDKProfile.class); + + long currentTime = [[NSNumber numberWithDouble:[[NSDate date] timeIntervalSince1970]] longValue]; + _claims = @{ + @"iss" : @"https://facebook.com/dialog/oauth", + @"aud" : kFakeAppID, + @"nonce" : kFakeNonce, + @"exp" : @(currentTime + 60 * 60 * 48), // 2 days later + @"iat" : @(currentTime - 60), // 1 min ago + @"jti" : kFakeJTI, + @"sub" : @"1234", + @"name" : @"Test User", + @"email" : @"email@email.com", + @"picture" : @"https://www.facebook.com/some_picture", + }; + + _header = @{ + @"alg" : @"RS256", + @"typ" : @"JWT", + @"kid" : @"abcd1234", + }; +} + +- (void)tearDown +{ + [super tearDown]; + + [_mockInternalUtility stopMocking]; + _mockInternalUtility = nil; + + [_mockLoginManager stopMocking]; + _mockLoginManager = nil; + + [_mockAccessTokenClass stopMocking]; + _mockAccessTokenClass = nil; + + [_mockAuthenticationTokenClass stopMocking]; + _mockAuthenticationTokenClass = nil; + + [_mockProfileClass stopMocking]; + _mockProfileClass = nil; } - (NSURL *)authorizeURLWithParameters:(NSString *)parameters joinedBy:(NSString *)joinChar @@ -59,9 +155,8 @@ - (NSURL *)authorizeURLWithFragment:(NSString *)fragment challenge:(NSString *)c fragment, fragment.length > 0 ? @"&" : @"", [FBSDKUtility URLEncode:[NSString stringWithFormat:@"{\"challenge\":\"%@\"}", challenge]] - ]; + ]; return [self authorizeURLWithParameters:fragment joinedBy:@"#"]; - } - (NSURL *)authorizeURLWithFragment:(NSString *)fragment @@ -69,23 +164,12 @@ - (NSURL *)authorizeURLWithFragment:(NSString *)fragment return [self authorizeURLWithFragment:fragment challenge:kFakeChallenge]; } -- (FBSDKLoginManager *)loginManagerExpectingChallenge -{ - FBSDKLoginManager *loginManager = [[FBSDKLoginManager alloc] init]; - id partialMock = (FBSDKLoginManager *)[OCMockObject partialMockForObject:loginManager]; - - [[[partialMock stub] andReturn:kFakeChallenge] loadExpectedChallenge]; - - return (FBSDKLoginManager *)partialMock; -} - // verify basic case of first login and getting granted and declined permissions (is not classified as cancelled) - (void)testOpenURLAuth { XCTestExpectation *expectation = [self expectationWithDescription:@"completed auth"]; - [FBSDKAccessToken setCurrentAccessToken:nil]; NSURL *url = [self authorizeURLWithFragment:@"granted_scopes=public_profile&denied_scopes=email%2Cuser_friends&signed_request=ggarbage.eyJhbGdvcml0aG0iOiJITUFDSEEyNTYiLCJjb2RlIjoid2h5bm90IiwiaXNzdWVkX2F0IjoxNDIyNTAyMDkyLCJ1c2VyX2lkIjoiMTIzIn0&access_token=sometoken&expires_in=5183949"]; - FBSDKLoginManager *target = [self loginManagerExpectingChallenge]; + FBSDKLoginManager *target = _mockLoginManager; [target setRequestedPermissions:[NSSet setWithObjects:@"email", @"user_friends", nil]]; __block FBSDKAccessToken *tokenAfterAuth; [target setHandler:^(FBSDKLoginManagerLoginResult *result, NSError *error) { @@ -117,9 +201,8 @@ - (void)testOpenURLAuth // verify basic case of first login and no declined permissions. - (void)testOpenURLAuthNoDeclines { - [FBSDKAccessToken setCurrentAccessToken:nil]; NSURL *url = [self authorizeURLWithFragment:@"granted_scopes=public_profile&denied_scopes=&signed_request=ggarbage.eyJhbGdvcml0aG0iOiJITUFDSEEyNTYiLCJjb2RlIjoid2h5bm90IiwiaXNzdWVkX2F0IjoxNDIyNTAyMDkyLCJ1c2VyX2lkIjoiMTIzIn0&access_token=sometoken&expires_in=5183949"]; - FBSDKLoginManager *target = [self loginManagerExpectingChallenge]; + FBSDKLoginManager *target = _mockLoginManager; XCTAssertTrue([target application:nil openURL:url sourceApplication:@"com.apple.mobilesafari" annotation:nil]); FBSDKAccessToken *actualToken = [FBSDKAccessToken currentAccessToken]; XCTAssertTrue([actualToken.userID isEqualToString:@"123"], @"failed to parse userID"); @@ -132,7 +215,6 @@ - (void)testOpenURLAuthNoDeclines - (void)testOpenURLRecentlyDeclined { XCTestExpectation *expectation = [self expectationWithDescription:@"completed auth"]; - [FBSDKAccessToken setCurrentAccessToken:nil]; // receive url with denied_scopes more than what was requested. NSURL *url = [self authorizeURLWithFragment:@"granted_scopes=public_profile&denied_scopes=user_friends,user_likes&signed_request=ggarbage.eyJhbGdvcml0aG0iOiJITUFDSEEyNTYiLCJjb2RlIjoid2h5bm90IiwiaXNzdWVkX2F0IjoxNDIyNTAyMDkyLCJ1c2VyX2lkIjoiMTIzIn0&access_token=sometoken&expires_in=5183949"]; @@ -144,7 +226,7 @@ - (void)testOpenURLRecentlyDeclined XCTAssertEqualObjects(result.grantedPermissions, [NSSet setWithObject:@"public_profile"]); [expectation fulfill]; }; - FBSDKLoginManager *target = [self loginManagerExpectingChallenge]; + FBSDKLoginManager *target = _mockLoginManager; [target setRequestedPermissions:[NSSet setWithObject:@"user_friends"]]; [target setHandler:handler]; XCTAssertTrue([target application:nil openURL:url sourceApplication:@"com.apple.mobilesafari" annotation:nil]); @@ -153,15 +235,15 @@ - (void)testOpenURLRecentlyDeclined }]; } -//verify that a reauth for already granted permissions is not treated as a cancellation. +// verify that a reauth for already granted permissions is not treated as a cancellation. - (void)testOpenURLReauthSamePermissionsIsNotCancelled { -// XCTestExpectation *expectation = [self expectationWithDescription:@"completed reauth"]; + // XCTestExpectation *expectation = [self expectationWithDescription:@"completed reauth"]; // set up a current token with public_profile FBSDKAccessToken *existingToken = [[FBSDKAccessToken alloc] initWithTokenString:@"token" permissions:@[@"public_profile", @"read_stream"] declinedPermissions:@[] - expiredPermissions:@[] + expiredPermissions:@[] appID:@"" userID:@"" expirationDate:nil @@ -170,7 +252,7 @@ - (void)testOpenURLReauthSamePermissionsIsNotCancelled [FBSDKAccessToken setCurrentAccessToken:existingToken]; NSURL *url = [self authorizeURLWithFragment:@"granted_scopes=public_profile,read_stream&denied_scopes=email%2Cuser_friends&signed_request=ggarbage.eyJhbGdvcml0aG0iOiJITUFDSEEyNTYiLCJjb2RlIjoid2h5bm90IiwiaXNzdWVkX2F0IjoxNDIyNTAyMDkyLCJ1c2VyX2lkIjoiMTIzIn0&access_token=sometoken&expires_in=5183949"]; // Use OCMock to verify the validateReauthentication: call and verify the result there. - id target = [OCMockObject partialMockForObject:[[FBSDKLoginManager alloc] init]]; + id target = _mockLoginManager; [[[target stub] andDo:^(NSInvocation *invocation) { __unsafe_unretained FBSDKLoginManagerLoginResult *result; [invocation getArgument:&result atIndex:3]; @@ -180,60 +262,59 @@ - (void)testOpenURLReauthSamePermissionsIsNotCancelled [target setRequestedPermissions:[NSSet setWithObjects:@"public_profile", @"read_stream", nil]]; -#pragma clang diagnostic push -#pragma clang diagnostic ignored "-Wnonnull" + #pragma clang diagnostic push + #pragma clang diagnostic ignored "-Wnonnull" XCTAssertTrue([target application:nil openURL:url sourceApplication:@"com.apple.mobilesafari" annotation:nil]); -#pragma clang diagnostic pop + #pragma clang diagnostic pop [target verify]; } -//verify that a reauth for already granted permissions is not treated as a cancellation. +// verify that a reauth for already granted permissions is not treated as a cancellation. - (void)testOpenURLReauthNoPermissionsIsNotCancelled { - // XCTestExpectation *expectation = [self expectationWithDescription:@"completed reauth"]; - // set up a current token with public_profile - FBSDKAccessToken *existingToken = [[FBSDKAccessToken alloc] initWithTokenString:@"token" - permissions:@[@"public_profile", @"read_stream"] - declinedPermissions:@[] - expiredPermissions:@[] - appID:@"" - userID:@"" - expirationDate:nil - refreshDate:nil - dataAccessExpirationDate:nil]; - [FBSDKAccessToken setCurrentAccessToken:existingToken]; - NSURL *url = [self authorizeURLWithFragment:@"granted_scopes=public_profile,read_stream&denied_scopes=email%2Cuser_friends&signed_request=ggarbage.eyJhbGdvcml0aG0iOiJITUFDSEEyNTYiLCJjb2RlIjoid2h5bm90IiwiaXNzdWVkX2F0IjoxNDIyNTAyMDkyLCJ1c2VyX2lkIjoiMTIzIn0&access_token=sometoken&expires_in=5183949"]; - // Use OCMock to verify the validateReauthentication: call and verify the result there. - id target = [OCMockObject partialMockForObject:[[FBSDKLoginManager alloc] init]]; - [[[target stub] andDo:^(NSInvocation *invocation) { - __unsafe_unretained FBSDKLoginManagerLoginResult *result; - [invocation getArgument:&result atIndex:3]; - XCTAssertFalse(result.isCancelled); - XCTAssertNotNil(result.token); - }] validateReauthentication:[OCMArg any] withResult:[OCMArg any]]; + // XCTestExpectation *expectation = [self expectationWithDescription:@"completed reauth"]; + // set up a current token with public_profile + FBSDKAccessToken *existingToken = [[FBSDKAccessToken alloc] initWithTokenString:@"token" + permissions:@[@"public_profile", @"read_stream"] + declinedPermissions:@[] + expiredPermissions:@[] + appID:@"" + userID:@"" + expirationDate:nil + refreshDate:nil + dataAccessExpirationDate:nil]; + [FBSDKAccessToken setCurrentAccessToken:existingToken]; + NSURL *url = [self authorizeURLWithFragment:@"granted_scopes=public_profile,read_stream&denied_scopes=email%2Cuser_friends&signed_request=ggarbage.eyJhbGdvcml0aG0iOiJITUFDSEEyNTYiLCJjb2RlIjoid2h5bm90IiwiaXNzdWVkX2F0IjoxNDIyNTAyMDkyLCJ1c2VyX2lkIjoiMTIzIn0&access_token=sometoken&expires_in=5183949"]; + // Use OCMock to verify the validateReauthentication: call and verify the result there. + id target = _mockLoginManager; + [[[target stub] andDo:^(NSInvocation *invocation) { + __unsafe_unretained FBSDKLoginManagerLoginResult *result; + [invocation getArgument:&result atIndex:3]; + XCTAssertFalse(result.isCancelled); + XCTAssertNotNil(result.token); + }] validateReauthentication:[OCMArg any] withResult:[OCMArg any]]; - [target setRequestedPermissions:nil]; + [(FBSDKLoginManager *)target setRequestedPermissions:nil]; -#pragma clang diagnostic push -#pragma clang diagnostic ignored "-Wnonnull" + #pragma clang diagnostic push + #pragma clang diagnostic ignored "-Wnonnull" - XCTAssertTrue([target application:nil openURL:url sourceApplication:@"com.apple.mobilesafari" annotation:nil]); + XCTAssertTrue([target application:nil openURL:url sourceApplication:@"com.apple.mobilesafari" annotation:nil]); -#pragma clang diagnostic pop + #pragma clang diagnostic pop - [target verify]; + [target verify]; } - (void)testOpenURLWithBadChallenge { XCTestExpectation *expectation = [self expectationWithDescription:@"completed auth"]; - [FBSDKAccessToken setCurrentAccessToken:nil]; NSURL *url = [self authorizeURLWithFragment:@"granted_scopes=public_profile&denied_scopes=email%2Cuser_friends&signed_request=ggarbage.eyJhbGdvcml0aG0iOiJITUFDSEEyNTYiLCJjb2RlIjoid2h5bm90IiwiaXNzdWVkX2F0IjoxNDIyNTAyMDkyLCJ1c2VyX2lkIjoiMTIzIn0&access_token=sometoken&expires_in=5183949" - challenge:@"someotherchallenge"]; - FBSDKLoginManager *target = [self loginManagerExpectingChallenge]; + challenge:@"someotherchallenge"]; + FBSDKLoginManager *target = _mockLoginManager; [target setRequestedPermissions:[NSSet setWithObjects:@"email", @"user_friends", nil]]; [target setHandler:^(FBSDKLoginManagerLoginResult *result, NSError *error) { XCTAssertNotNil(error); @@ -251,10 +332,9 @@ - (void)testOpenURLWithBadChallenge - (void)testOpenURLWithNoChallengeAndError { XCTestExpectation *expectation = [self expectationWithDescription:@"completed auth"]; - [FBSDKAccessToken setCurrentAccessToken:nil]; NSURL *url = [self authorizeURLWithParameters:@"error=some_error&error_code=999&error_message=Errorerror_reason=foo#_=_" joinedBy:@"?"]; - FBSDKLoginManager *target = [self loginManagerExpectingChallenge]; + FBSDKLoginManager *target = _mockLoginManager; [target setRequestedPermissions:[NSSet setWithObjects:@"email", @"user_friends", nil]]; [target setHandler:^(FBSDKLoginManagerLoginResult *result, NSError *error) { XCTAssertNotNil(error); @@ -268,20 +348,113 @@ - (void)testOpenURLWithNoChallengeAndError }]; } +- (void)testOpenURLAuthWithAuthenticationToken +{ + XCTestExpectation *expectation = [self expectationWithDescription:self.name]; + FBSDKLoginManager *target = _mockLoginManager; + [FBSDKAuthenticationTokenFactory setSkipSignatureVerification:YES]; + + NSData *claimsData = [FBSDKTypeUtility dataWithJSONObject:_claims options:0 error:nil]; + NSString *encodedClaims = [FBSDKBase64 encodeData:claimsData]; + NSData *headerData = [FBSDKTypeUtility dataWithJSONObject:_header options:0 error:nil]; + NSString *encodedHeader = [FBSDKBase64 encodeData:headerData]; + + NSString *tokenString = [NSString stringWithFormat:@"%@.%@.%@", encodedHeader, encodedClaims, @"signature"]; + NSURL *url = [self authorizeURLWithFragment:[NSString stringWithFormat:@"granted_scopes=public_profile,email&id_token=%@", tokenString] challenge:kFakeChallenge]; + + [target setHandler:^(FBSDKLoginManagerLoginResult *result, NSError *error) { + XCTAssertFalse(result.isCancelled); + + FBSDKAuthenticationToken *authToken = result.authenticationToken; + XCTAssertEqualObjects(authToken, FBSDKAuthenticationToken.currentAuthenticationToken); + [self validateAuthenticationToken:authToken expectedTokenString:tokenString]; + + [self validateProfile:FBSDKProfile.currentProfile]; + + [expectation fulfill]; + }]; + + XCTAssertTrue([target application:nil openURL:url sourceApplication:@"com.apple.mobilesafari" annotation:nil]); + + [self waitForExpectationsWithTimeout:3 handler:^(NSError *error) { + XCTAssertNil(error); + }]; + + [FBSDKAuthenticationTokenFactory setSkipSignatureVerification:NO]; +} + +- (void)testOpenURLAuthWithInvalidAuthenticationToken +{ + __block BOOL resultBlockInvoked = NO; + FBSDKLoginManager *target = _mockLoginManager; + NSURL *url = [self authorizeURLWithFragment:@"id_token=invalid_token" challenge:kFakeChallenge]; + + [target setHandler:^(FBSDKLoginManagerLoginResult *result, NSError *error) { + XCTAssertNotNil(error); + OCMReject([self->_mockAccessTokenClass setCurrentAccessToken:OCMOCK_ANY]); + OCMReject([self->_mockAuthenticationTokenClass setCurrentAuthenticationToken:OCMOCK_ANY]); + OCMReject([self->_mockProfileClass setCurrentProfile:OCMOCK_ANY]); + resultBlockInvoked = YES; + }]; + + XCTAssertTrue([target application:nil openURL:url sourceApplication:@"com.apple.mobilesafari" annotation:nil]); + XCTAssertTrue(resultBlockInvoked, "Should invoke completion synchronously"); +} + +- (void)testOpenURLAuthWithAuthenticationTokenWithAccessToken +{ + XCTestExpectation *expectation = [self expectationWithDescription:self.name]; + FBSDKLoginManager *target = _mockLoginManager; + [FBSDKAuthenticationTokenFactory setSkipSignatureVerification:YES]; + + NSData *claimsData = [FBSDKTypeUtility dataWithJSONObject:_claims options:0 error:nil]; + NSString *encodedClaims = [FBSDKBase64 encodeData:claimsData]; + NSData *headerData = [FBSDKTypeUtility dataWithJSONObject:_header options:0 error:nil]; + NSString *encodedHeader = [FBSDKBase64 encodeData:headerData]; + + NSString *tokenString = [NSString stringWithFormat:@"%@.%@.%@", encodedHeader, encodedClaims, @"signature"]; + NSString *fragment = [@"granted_scopes=public_profile%2Cemail%2Cuser_friends&signed_request=ggarbage.eyJhbGdvcml0aG0iOiJITUFDSEEyNTYiLCJjb2RlIjoid2h5bm90IiwiaXNzdWVkX2F0IjoxNDIyNTAyMDkyLCJ1c2VyX2lkIjoiMTIzIn0&access_token=sometoken&expires_in=5183949&id_token=" stringByAppendingString:tokenString]; + NSURL *url = [self authorizeURLWithFragment:fragment challenge:kFakeChallenge]; + + [target setHandler:^(FBSDKLoginManagerLoginResult *result, NSError *error) { + XCTAssertFalse(result.isCancelled); + + FBSDKAuthenticationToken *authToken = result.authenticationToken; + XCTAssertEqualObjects(authToken, FBSDKAuthenticationToken.currentAuthenticationToken); + [self validateAuthenticationToken:authToken expectedTokenString:tokenString]; + + [self validateProfile:FBSDKProfile.currentProfile]; + + FBSDKAccessToken *accessToken = [FBSDKAccessToken currentAccessToken]; + XCTAssertEqualObjects(accessToken, result.token); + XCTAssertEqualObjects(accessToken.userID, @"123", @"failed to parse userID"); + NSSet *permissions = [NSSet setWithObjects:@"public_profile", @"email", @"user_friends", nil]; + XCTAssertEqualObjects(accessToken.permissions, permissions, @"unexpected permissions"); + XCTAssertEqualObjects(result.grantedPermissions, permissions, @"unexpected permissions"); + XCTAssertFalse(accessToken.declinedPermissions.count, @"unexpected permissions"); + XCTAssertFalse(result.declinedPermissions.count, @"unexpected permissions"); + + [expectation fulfill]; + }]; + + XCTAssertTrue([target application:nil openURL:url sourceApplication:@"com.apple.mobilesafari" annotation:nil]); + + [self waitForExpectationsWithTimeout:3 handler:^(NSError *error) { + XCTAssertNil(error); + }]; + + [FBSDKAuthenticationTokenFactory setSkipSignatureVerification:NO]; +} - (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]; + [[[_mockInternalUtility stub] andReturnValue:@NO] isFacebookAppInstalled]; NSError *URLError = [[NSError alloc] initWithDomain:FBSDKErrorDomain code:0 userInfo:nil]; - [[FBSDKInternalUtilityMock stub] appURLWithHost:OCMOCK_ANY - path:OCMOCK_ANY - queryParameters:OCMOCK_ANY - error:((NSError __autoreleasing **)[OCMArg setTo:URLError])]; + [[_mockInternalUtility 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]; @@ -290,7 +463,7 @@ - (void)testLoginManagerRetainsItselfForLoginMethod }]; // This makes sure that FBSDKLoginManager is retaining itself for the duration of the call manager = nil; - [self waitForExpectationsWithTimeout:5 handler:^(NSError * _Nullable error) { + [self waitForExpectationsWithTimeout:5 handler:^(NSError *_Nullable error) { XCTAssertNil(error); }]; } @@ -298,14 +471,10 @@ - (void)testLoginManagerRetainsItselfForLoginMethod - (void)testCallingLoginWhileAnotherLoginHasNotFinishedNoOps { // Mock some methods to force a SafariVC load - id FBSDKInternalUtilityMock = [OCMockObject niceMockForClass:[FBSDKInternalUtility class]]; - [[[FBSDKInternalUtilityMock stub] andDo:^(NSInvocation *invocation) { - // Nothing - }] validateURLSchemes]; - [[[FBSDKInternalUtilityMock stub] andReturnValue:@NO] isFacebookAppInstalled]; + [[[_mockInternalUtility stub] andReturnValue:@NO] isFacebookAppInstalled]; __block int loginCount = 0; - FBSDKLoginManager *manager = [OCMockObject partialMockForObject:[FBSDKLoginManager new]]; + FBSDKLoginManager *manager = _mockLoginManager; [[[(id)manager stub] andDo:^(NSInvocation *invocation) { loginCount++; }] logIn]; @@ -322,26 +491,215 @@ - (void)testCallingLoginWhileAnotherLoginHasNotFinishedNoOps XCTAssertEqual(loginCount, 1); } -- (void)testLoginParams -{ - id FBSDKInternalUtilityMock = [OCMockObject niceMockForClass:[FBSDKInternalUtility class]]; - [[[FBSDKInternalUtilityMock stub] andDo:^(NSInvocation *invocation) { - // Nothing - }] validateURLSchemes]; - FBSDKLoginManager *loginManager = [[FBSDKLoginManager alloc] init]; - NSDictionary *params = [loginManager logInParametersWithPermissions:[NSSet setWithArray:@[@"public_profile", @"email"]] serverConfiguration:nil]; - long long cbt = [params[@"cbt"] longLongValue]; - long long currentMilliseconds = round(1000 * [NSDate date].timeIntervalSince1970); - XCTAssertEqualWithAccuracy(cbt, currentMilliseconds, 500); - XCTAssertEqualObjects(params[@"client_id"], @"7391628439"); - XCTAssertEqualObjects(params[@"response_type"], @"token_or_nonce,signed_request,graph_domain"); - XCTAssertEqualObjects(params[@"redirect_uri"], @"fbconnect://success"); - XCTAssertEqualObjects(params[@"display"], @"touch"); - XCTAssertEqualObjects(params[@"sdk"], @"ios"); - XCTAssertEqualObjects(params[@"return_scopes"], @"true"); - XCTAssertEqual(params[@"auth_type"], FBSDKLoginAuthTypeRerequest); - XCTAssertEqualObjects(params[@"fbapp_pres"], @0); - XCTAssertEqualObjects(params[@"ies"], [FBSDKSettings isAutoLogAppEventsEnabled] ? @1 : @0); +- (void)testCallingLoginWithNilConfigurationShouldFail +{ + __block BOOL resultBlockInvoked = NO; + FBSDKLoginManagerLoginResultBlock completion = ^(FBSDKLoginManagerLoginResult *result, NSError *error) { + XCTAssertNil(result); + XCTAssertNotNil(error); + resultBlockInvoked = YES; + }; + + FBSDKLoginConfiguration *invalidConfig = [[FBSDKLoginConfiguration alloc] initWithPermissions:@[] + tracking:FBSDKLoginTrackingLimited + nonce:@" "]; + XCTAssertNil(invalidConfig); + + [_mockLoginManager logInFromViewController:nil configuration:invalidConfig completion:completion]; + XCTAssertTrue(resultBlockInvoked, "Should invoke completion synchronously"); +} + +- (void)testLoginTrackingEnabledLoginParams +{ + FBSDKLoginConfiguration *config = [[FBSDKLoginConfiguration alloc] + initWithPermissions:@[@"public_profile", @"email"] + tracking:FBSDKLoginTrackingEnabled]; + + NSDictionary *params = [_mockLoginManager logInParametersWithConfiguration:config serverConfiguration:nil]; + [self validateCommonLoginParameters:params]; + XCTAssertEqualObjects(params[@"response_type"], @"id_token,token_or_nonce,signed_request,graph_domain"); + XCTAssertEqualObjects(params[@"scope"], @"public_profile,email,openid"); + XCTAssertNotNil(params[@"nonce"]); + XCTAssertEqualObjects(params[@"tp"], @"ios_14_can_track"); +} + +- (void)testLoginTrackingLimitedLoginParams +{ + FBSDKLoginConfiguration *config = [[FBSDKLoginConfiguration alloc] + initWithPermissions:@[@"public_profile", @"email"] + tracking:FBSDKLoginTrackingLimited + nonce:@"some_nonce"]; + + NSDictionary *params = [_mockLoginManager logInParametersWithConfiguration:config serverConfiguration:nil]; + [self validateCommonLoginParameters:params]; + XCTAssertEqualObjects(params[@"response_type"], @"id_token"); + XCTAssertEqualObjects(params[@"scope"], @"public_profile,email,openid"); + XCTAssertEqualObjects(params[@"nonce"], @"some_nonce"); + XCTAssertEqualObjects(params[@"tp"], @"ios_14_do_not_track"); +} + +- (void)testLoginParamsWithNilConfiguration +{ + __block BOOL wasCalled = NO; + FBSDKLoginManagerLoginResultBlock handler = ^(FBSDKLoginManagerLoginResult *result, NSError *error) { + XCTAssertNil(result); + XCTAssertNotNil(error); + wasCalled = YES; + }; + [_mockLoginManager setHandler:handler]; + + NSDictionary *params = [_mockLoginManager logInParametersWithConfiguration:nil serverConfiguration:nil]; + + XCTAssertNil(params); + XCTAssert(wasCalled); +} + +- (void)testlogInParametersFromURL +{ + NSURL *url = [NSURL URLWithString:@"myapp://somelink/?al_applink_data=%7B%22target_url%22%3Anull%2C%22extras%22%3A%7B%22fb_login%22%3A%22%7B%5C%22granted_scopes%5C%22%3A%5C%22public_profile%5C%22%2C%5C%22denied_scopes%5C%22%3A%5C%22%5C%22%2C%5C%22signed_request%5C%22%3A%5C%22ggarbage.eyJhbGdvcml0aG0iOiJITUFDSEEyNTYiLCJjb2RlIjoid2h5bm90IiwiaXNzdWVkX2F0IjoxNDIyNTAyMDkyLCJ1c2VyX2lkIjoiMTIzIn0%5C%22%2C%5C%22nonce%5C%22%3A%5C%22someNonce%5C%22%2C%5C%22data_access_expiration_time%5C%22%3A%5C%221607374566%5C%22%2C%5C%22expires_in%5C%22%3A%5C%225183401%5C%22%7D%22%7D%2C%22referer_app_link%22%3A%7B%22url%22%3A%22fb%3A%5C%2F%5C%2F%5C%2F%22%2C%22app_name%22%3A%22Facebook%22%7D%7D"]; + + NSDictionary *params = [_mockLoginManager logInParametersFromURL:url]; + + XCTAssertNotNil(params); + XCTAssertEqualObjects(params[@"nonce"], @"someNonce"); + XCTAssertEqualObjects(params[@"granted_scopes"], @"public_profile"); + XCTAssertEqualObjects(params[@"denied_scopes"], @""); +} + +- (void)testLogInWithURLFailWithInvalidLoginData +{ + XCTestExpectation *expectation = [self expectationWithDescription:self.name]; + NSURL *urlWithInvalidLoginData = [NSURL URLWithString:@"myapp://somelink/?al_applink_data=%7B%22target_url%22%3Anull%2C%22extras%22%3A%7B%22fb_login%22%3A%22invalid%22%7D%2C%22referer_app_link%22%3A%7B%22url%22%3A%22fb%3A%5C%2F%5C%2F%5C%2F%22%2C%22app_name%22%3A%22Facebook%22%7D%7D"]; + FBSDKLoginManagerLoginResultBlock handler = ^(FBSDKLoginManagerLoginResult *result, NSError *error) { + if (error) { + XCTAssertNil(result); + [expectation fulfill]; + } else { + XCTFail(@"Should have error"); + } + }; + + [_mockLoginManager logInWithURL:urlWithInvalidLoginData handler:handler]; + + [self waitForExpectationsWithTimeout:1 handler:^(NSError *_Nullable error) { + XCTAssertNil(error); + }]; +} + +- (void)testLogInWithURLFailWithNoLoginData +{ + XCTestExpectation *expectation = [self expectationWithDescription:self.name]; + NSURL *urlWithNoLoginData = [NSURL URLWithString:@"myapp://somelink/?al_applink_data=%7B%22target_url%22%3Anull%2C%22extras%22%3A%7B%22some_param%22%3A%22some_value%22%7D%2C%22referer_app_link%22%3A%7B%22url%22%3A%22fb%3A%5C%2F%5C%2F%5C%2F%22%2C%22app_name%22%3A%22Facebook%22%7D%7D"]; + FBSDKLoginManagerLoginResultBlock handler = ^(FBSDKLoginManagerLoginResult *result, NSError *error) { + if (error) { + XCTAssertNil(result); + [expectation fulfill]; + } else { + XCTFail(@"Should have error"); + } + }; + + [_mockLoginManager logInWithURL:urlWithNoLoginData handler:handler]; + + [self waitForExpectationsWithTimeout:1 handler:^(NSError *_Nullable error) { + XCTAssertNil(error); + }]; +} + +- (void)testLogout +{ + [_mockLoginManager logOut]; + + OCMVerify(ClassMethod([_mockAccessTokenClass setCurrentAccessToken:nil])); + OCMVerify(ClassMethod([_mockAuthenticationTokenClass setCurrentAuthenticationToken:nil])); + OCMVerify(ClassMethod([_mockProfileClass setCurrentProfile:nil])); +} + +- (void)testStoreExpectedNonce +{ + FBSDKKeychainStore *keychainStore = [[FBSDKKeychainStore alloc] initWithService:self.name accessGroup:nil]; + + [_mockLoginManager storeExpectedNonce:@"some_nonce" keychainStore:keychainStore]; + XCTAssertEqualObjects([keychainStore stringForKey:@"expected_login_nonce"], @"some_nonce"); + + [_mockLoginManager storeExpectedNonce:nil keychainStore:keychainStore]; + XCTAssertNil([keychainStore stringForKey:@"expected_login_nonce"]); +} + +- (void)testReauthorizingWithoutAccessToken +{ + [FBSDKAccessToken setCurrentAccessToken:nil shouldDispatchNotif:NO]; + + [_mockLoginManager reauthorizeDataAccess:[UIViewController new] + handler:^(FBSDKLoginManagerLoginResult *result, NSError *error) { + XCTAssertNil(result, "Should not have a result when reauthorizing without a current access token"); + XCTAssertEqual(error.domain, FBSDKLoginErrorDomain); + XCTAssertEqual(error.code, FBSDKLoginErrorMissingAccessToken); + }]; +} + +- (void)testReauthorizingWithAccessToken +{ + [FBSDKAccessToken setCurrentAccessToken:self.sampleAccessToken shouldDispatchNotif:NO]; + FBSDKLoginManager *manager = _mockLoginManager; + OCMStub([manager logIn]); + + [manager reauthorizeDataAccess:[UIViewController new] + handler:^(FBSDKLoginManagerLoginResult *result, NSError *error) { + XCTFail("Should not actually reauthorize and call the handler in this test"); + }]; + + XCTAssertEqual(manager.configuration.tracking, FBSDKLoginTrackingEnabled); + XCTAssertEqualObjects(manager.configuration.requestedPermissions, NSSet.new); + XCTAssertNotNil(manager.configuration.nonce); + OCMVerify([manager logIn]); +} + +- (FBSDKAccessToken *)sampleAccessToken +{ + return [[FBSDKAccessToken alloc] initWithTokenString:self.name + permissions:@[] + declinedPermissions:@[] + expiredPermissions:@[] + appID:@"abc123" + userID:@"userID" + expirationDate:nil + refreshDate:nil + dataAccessExpirationDate:nil]; +} + +- (void)validateCommonLoginParameters:(NSDictionary *)params +{ + XCTAssertEqualObjects(params[@"client_id"], kFakeAppID); + XCTAssertEqualObjects(params[@"redirect_uri"], @"fbconnect://success"); + XCTAssertEqualObjects(params[@"display"], @"touch"); + XCTAssertEqualObjects(params[@"sdk"], @"ios"); + XCTAssertEqualObjects(params[@"return_scopes"], @"true"); + XCTAssertEqual(params[@"auth_type"], FBSDKLoginAuthTypeRerequest); + XCTAssertEqualObjects(params[@"fbapp_pres"], @0); + XCTAssertEqualObjects(params[@"ies"], [FBSDKSettings isAutoLogAppEventsEnabled] ? @1 : @0); + + long long cbt = [params[@"cbt"] longLongValue]; + long long currentMilliseconds = round(1000 * [NSDate date].timeIntervalSince1970); + XCTAssertEqualWithAccuracy(cbt, currentMilliseconds, 500); +} + +- (void)validateAuthenticationToken:(FBSDKAuthenticationToken *)authToken + expectedTokenString:(NSString *)tokenString +{ + XCTAssertNotNil(authToken, @"An Authentication token should be created after successful login"); + XCTAssertEqualObjects(authToken.tokenString, tokenString, @"A raw authentication token string should be stored"); + XCTAssertEqualObjects(authToken.nonce, kFakeNonce, @"The nonce claims in the authentication token should be stored"); + XCTAssertEqualObjects(authToken.jti, kFakeJTI, @"The jit on the auth token should be derived from the claims"); +} + +- (void)validateProfile:(FBSDKProfile *)profile +{ + XCTAssertNotNil(profile, @"user profile should be updated"); + XCTAssertEqualObjects(profile.name, _claims[@"name"], @"failed to parse user name"); + XCTAssertEqualObjects(profile.userID, _claims[@"sub"], @"failed to parse userID"); + XCTAssertEqualObjects(profile.imageURL.absoluteString, _claims[@"picture"], @"failed to parse user profile picture"); + XCTAssertEqualObjects(profile.email, _claims[@"email"], @"failed to parse user email"); } @end diff --git a/FBSDKLoginKit/FBSDKLoginKitTests/FBSDKPermissionTests.m b/FBSDKLoginKit/FBSDKLoginKitTests/FBSDKPermissionTests.m new file mode 100644 index 0000000000..859a8131e4 --- /dev/null +++ b/FBSDKLoginKit/FBSDKLoginKitTests/FBSDKPermissionTests.m @@ -0,0 +1,96 @@ +// 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 + +#ifdef BUCK + #import +#else + #import "FBSDKPermission.h" +#endif + +@interface FBSDKPermissionTests : XCTestCase + +@end + +@implementation FBSDKPermissionTests + +- (void)testInvalidPermissions +{ + NSArray *permissions = @[ + @"", + @"foo bar", + @"PUBLIC_PROFILE", + @"public profile", + @"public-profile", + @"123_abc" + ]; + + for (NSString *rawPermission in permissions) { + FBSDKPermission *permission = [[FBSDKPermission alloc] initWithString:rawPermission]; + XCTAssertNil(permission); + } +} + +- (void)testValidPermissions +{ + NSArray *permissions = @[ + @"email", + @"public_profile", + @"pages_manage_ads" + ]; + + for (NSString *rawPermission in permissions) { + FBSDKPermission *permission = [[FBSDKPermission alloc] initWithString:rawPermission]; + XCTAssertEqualObjects(permission.value, rawPermission); + } +} + +- (void)testRawPermissionsFromPermissions +{ + NSSet *permissions = [NSSet setWithArray:@[ + [[FBSDKPermission alloc] initWithString:@"email"], + [[FBSDKPermission alloc] initWithString:@"public_profile"], + ]]; + + NSArray *rawPermissions = [FBSDKPermission rawPermissionsFromPermissions:permissions].allObjects; + NSArray *expectedRawPermissions = @[@"email", @"public_profile"]; + XCTAssertEqualObjects(rawPermissions, expectedRawPermissions); +} + +- (void)testPermissionsFromValidRawPermissions +{ + NSSet *rawPermissions = [NSSet setWithArray:@[@"email", @"user_friends"]]; + + NSArray *permissions = [FBSDKPermission permissionsFromRawPermissions:rawPermissions].allObjects; + NSArray *expectedPermissions = @[ + [[FBSDKPermission alloc] initWithString:@"email"], + [[FBSDKPermission alloc] initWithString:@"user_friends"], + ]; + XCTAssertEqualObjects(permissions, expectedPermissions); +} + +- (void)testPermissionsFromInvalidRawPermissions +{ + NSSet *rawPermissions = [NSSet setWithArray:@[@"email", @""]]; + + NSArray *permissions = [FBSDKPermission permissionsFromRawPermissions:rawPermissions].allObjects; + XCTAssertNil(permissions); +} + +@end diff --git a/FBSDKLoginKit/FBSDKLoginKitTests/FBSDKReferralCodeTests.m b/FBSDKLoginKit/FBSDKLoginKitTests/FBSDKReferralCodeTests.m new file mode 100644 index 0000000000..4c54131d6d --- /dev/null +++ b/FBSDKLoginKit/FBSDKLoginKitTests/FBSDKReferralCodeTests.m @@ -0,0 +1,76 @@ +// 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 + +#ifdef BUCK + #import +#else + #import "FBSDKReferralCode.h" +#endif + +static NSString *const _validReferralCode1 = @"abcd"; +static NSString *const _validReferralCode2 = @"123"; +static NSString *const _validReferralCode3 = @"123abc"; + +static NSString *const _invalidReferralCode1 = @"abd?"; +static NSString *const _invalidReferralCode2 = @"a b1"; +static NSString *const _invalidReferralCode3 = @" "; +static NSString *const _invalidReferralCode4 = @"\n\n"; + +static NSString *const _emptyReferralCode = @""; + +@interface FBSDKReferralCodeTests : XCTestCase + +@end + +@implementation FBSDKReferralCodeTests + +- (void)testCreateValidReferralCodeShouldSucceed +{ + [self assertCreationSucceedWithString:_validReferralCode1]; + [self assertCreationSucceedWithString:_validReferralCode2]; +} + +- (void)testCreateInvalidReferralCodeShoudFail +{ + [self assertCreationFailWithString:_invalidReferralCode1]; + [self assertCreationFailWithString:_invalidReferralCode2]; + [self assertCreationFailWithString:_invalidReferralCode3]; + [self assertCreationFailWithString:_invalidReferralCode4]; +} + +- (void)testCreateEmptyReferralCodeShoudFail +{ + [self assertCreationFailWithString:_emptyReferralCode]; +} + +- (void)assertCreationFailWithString:(NSString *)string +{ + FBSDKReferralCode *referralCode = [FBSDKReferralCode initWithString:string]; + XCTAssertNil(referralCode); +} + +- (void)assertCreationSucceedWithString:(NSString *)string +{ + FBSDKReferralCode *referralCode = [FBSDKReferralCode initWithString:string]; + XCTAssertNotNil(referralCode); + XCTAssertEqual(referralCode.value, string); +} + +@end diff --git a/FBSDKLoginKit/FBSDKLoginKitTests/FBSDKReferralManagerTests.m b/FBSDKLoginKit/FBSDKLoginKitTests/FBSDKReferralManagerTests.m new file mode 100644 index 0000000000..d8fc214ddb --- /dev/null +++ b/FBSDKLoginKit/FBSDKLoginKitTests/FBSDKReferralManagerTests.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 +#import + +#import + +#ifdef BUCK + #import + #import + #import +#else + #import "FBSDKLoginUtility.h" + #import "FBSDKReferralManager+Internal.h" + #import "FBSDKReferralManagerResult.h" +#endif + +static NSString *const _mockAppID = @"mockAppID"; +static NSString *const _mockChallenge = @"mockChallenge"; + +@interface FBSDKReferralManager (Testing) + +- (NSURL *)referralURL; + +- (void)handleOpenURLComplete:(BOOL)didOpen error:(NSError *)error; + +- (BOOL)validateChallenge:(NSString *)challenge; + +@end + +@interface FBSDKReferralManagerTests : XCTestCase +{ + FBSDKReferralManager *_manager; +} + +@end + +@implementation FBSDKReferralManagerTests + +- (void)setUp +{ + [super setUp]; + _manager = OCMPartialMock([FBSDKReferralManager new]); + [FBSDKSettings setAppID:_mockAppID]; +} + +- (void)mockURLScheme +{ + id FBSDKInternalUtilityMock = [OCMockObject niceMockForClass:[FBSDKInternalUtility class]]; + OCMStub(ClassMethod([FBSDKInternalUtilityMock validateURLSchemes])).andDo(^(NSInvocation *invocation) { + // Nothing + }); +} + +- (void)mockBridgeAPI +{ + id partialBridgeAPIMock = OCMPartialMock([FBSDKBridgeAPI sharedInstance]); + OCMStub([partialBridgeAPIMock openURLWithSafariViewController:OCMArg.any sender:OCMArg.any fromViewController:OCMArg.any handler:OCMArg.any]).andDo(^(NSInvocation *invocation) { + // Nothing + }); +} + +- (void)testReferralURL +{ + NSURL *url = [_manager referralURL]; + + XCTAssertTrue([url.path hasSuffix:@"dialog/share_referral"]); + + NSDictionary *params = [FBSDKInternalUtility parametersFromFBURL:url]; + NSString *appID = params[@"app_id"]; + NSString *redirectURI = params[@"redirect_uri"]; + NSString *challenge = params[@"state"]; + NSString *expectedUrlPrefix = [FBSDKInternalUtility + appURLWithHost:@"authorize" + path:@"" + queryParameters:@{} + error:NULL].absoluteString; + + XCTAssertEqualObjects(appID, _mockAppID); + XCTAssertTrue([redirectURI hasPrefix:expectedUrlPrefix]); + XCTAssert(challenge.length > 0); +} + +- (void)testStartReferralOpensSFVC +{ + [self mockURLScheme]; + + id partialBridgeAPIMock = OCMPartialMock([FBSDKBridgeAPI sharedInstance]); + + [_manager startReferralWithCompletionHandler:nil]; + OCMVerify([partialBridgeAPIMock openURLWithSafariViewController:OCMArg.any sender:OCMArg.any fromViewController:OCMArg.any handler:OCMArg.any]); +} + +- (void)testReferralSuccess +{ + [self mockURLScheme]; + [self mockBridgeAPI]; + OCMStub([_manager validateChallenge:_mockChallenge]).andReturn(YES); + + NSString *queryString = [@"?fb_referral_codes=%5B%22abc%22%2C%22def%22%5D&state=" stringByAppendingString:_mockChallenge]; + NSURL *url = [NSURL URLWithString:[NSString stringWithFormat:@"fb%@://authorize/%@", _mockAppID, queryString]]; + XCTestExpectation *expectation = [self expectationWithDescription:self.name]; + FBSDKReferralManagerResultBlock completionHandler = ^(FBSDKReferralManagerResult *result, NSError *referralError) { + if (referralError) { + XCTFail(@"Should not have error"); + } else { + NSArray *referralCodes = result.referralCodes; + NSArray *expectedReferralCodes = @[[FBSDKReferralCode initWithString:@"abc"], [FBSDKReferralCode initWithString:@"def"]]; + XCTAssertEqualObjects(referralCodes, expectedReferralCodes); + XCTAssertFalse(result.isCancelled); + [expectation fulfill]; + } + }; + + [_manager startReferralWithCompletionHandler:completionHandler]; + XCTAssertTrue([_manager application:nil openURL:url sourceApplication:@"com.apple.mobilesafari" annotation:nil]); + + [self waitForExpectationsWithTimeout:1 handler:^(NSError *_Nullable error) { + XCTAssertNil(error); + }]; +} + +- (void)testReferralCancelWithOpenURLCompletionHandler +{ + [self mockURLScheme]; + [self mockBridgeAPI]; + + XCTestExpectation *expectation = [self expectationWithDescription:self.name]; + NSError *cancelError = [[NSError alloc]initWithDomain:@"com.apple.SafariServices.Authentication" code:0 userInfo:nil]; + FBSDKReferralManagerResultBlock completionHandler = ^(FBSDKReferralManagerResult *result, NSError *referralError) { + if (referralError) { + XCTFail(@"Should not have error"); + } else { + XCTAssertTrue(result.isCancelled); + XCTAssertNil(result.referralCodes); + [expectation fulfill]; + } + }; + + [_manager startReferralWithCompletionHandler:completionHandler]; + [_manager handleOpenURLComplete:NO error:cancelError]; + + [self waitForExpectationsWithTimeout:1 handler:^(NSError *_Nullable error) { + XCTAssertNil(error); + }]; +} + +- (void)testReferralCancelWithAppDelegate +{ + [self mockURLScheme]; + [self mockBridgeAPI]; + + XCTestExpectation *expectation = [self expectationWithDescription:self.name]; + NSURL *fakeURL = [NSURL URLWithString:@"https://www.facebook.com"]; + FBSDKReferralManagerResultBlock completionHandler = ^(FBSDKReferralManagerResult *result, NSError *referralError) { + if (referralError) { + XCTFail(@"Should not have error"); + } else { + XCTAssertTrue(result.isCancelled); + XCTAssertNil(result.referralCodes); + [expectation fulfill]; + } + }; + + [_manager startReferralWithCompletionHandler:completionHandler]; + [_manager handleOpenURLComplete:YES error:nil]; + XCTAssertFalse([_manager application:nil openURL:fakeURL sourceApplication:@"com.apple.mobilesafari" annotation:nil]); + + [self waitForExpectationsWithTimeout:1 handler:^(NSError *_Nullable error) { + XCTAssertNil(error); + }]; +} + +- (void)testReferralErrorWithInvalidURLSchemes +{ + id FBSDKInternalUtilityMock = [OCMockObject niceMockForClass:[FBSDKInternalUtility class]]; + [OCMStub(ClassMethod([FBSDKInternalUtilityMock validateURLSchemes])) andThrow:[NSException exceptionWithName:@"InvalidOperationException" reason:nil userInfo:nil]]; + + XCTestExpectation *expectation = [self expectationWithDescription:self.name]; + FBSDKReferralManagerResultBlock completionHandler = ^(FBSDKReferralManagerResult *result, NSError *referralError) { + if (referralError) { + XCTAssertNil(result); + [expectation fulfill]; + } else { + XCTFail(@"Should have error"); + } + }; + + [_manager startReferralWithCompletionHandler:completionHandler]; + [self waitForExpectationsWithTimeout:1 handler:^(NSError *_Nullable error) { + XCTAssertNil(error); + }]; +} + +- (void)testReferralErrorWithOpenURLCompletionHandler +{ + [self mockURLScheme]; + [self mockBridgeAPI]; + + XCTestExpectation *expectation = [self expectationWithDescription:self.name]; + NSError *fakeError = [[NSError alloc]initWithDomain:FBSDKErrorDomain code:FBSDKErrorBridgeAPIInterruption userInfo:nil]; + FBSDKReferralManagerResultBlock completionHandler = ^(FBSDKReferralManagerResult *result, NSError *referralError) { + if (referralError) { + XCTAssertNil(result); + [expectation fulfill]; + } else { + XCTFail(@"Should have error"); + } + }; + + [_manager startReferralWithCompletionHandler:completionHandler]; + [_manager handleOpenURLComplete:NO error:fakeError]; + + [self waitForExpectationsWithTimeout:1 handler:^(NSError *_Nullable error) { + XCTAssertNil(error); + }]; +} + +- (void)testReferralErrorWithAppDelegate +{ + [self mockURLScheme]; + [self mockBridgeAPI]; + OCMStub([_manager validateChallenge:_mockChallenge]).andReturn(YES); + + XCTestExpectation *expectation = [self expectationWithDescription:self.name]; + NSString *invalidQueryString = @"?fb_referral_codes=%5B%22abc%2C%22def"; + NSURL *url = [NSURL URLWithString:[NSString stringWithFormat:@"fb%@://authorize/%@", _mockAppID, invalidQueryString]]; + FBSDKReferralManagerResultBlock completionHandler = ^(FBSDKReferralManagerResult *result, NSError *referralError) { + if (referralError) { + XCTAssertNil(result); + [expectation fulfill]; + } else { + XCTFail(@"Should have error"); + } + }; + + [_manager startReferralWithCompletionHandler:completionHandler]; + XCTAssertTrue([_manager application:nil openURL:url sourceApplication:@"com.apple.mobilesafari" annotation:nil]); + + [self waitForExpectationsWithTimeout:1 handler:^(NSError *_Nullable error) { + XCTAssertNil(error); + }]; +} + +- (void)testReferralErrorWithBadChallenge +{ + [self mockURLScheme]; + [self mockBridgeAPI]; + OCMStub([_manager validateChallenge:_mockChallenge]).andReturn(YES); + + NSString *badChallenge = @"badChallenge"; + NSString *queryString = [@"?fb_referral_codes=%5B%22abc%22%2C%22def%22%5D&state=" stringByAppendingString:badChallenge]; + NSURL *url = [NSURL URLWithString:[NSString stringWithFormat:@"fb%@://authorize/%@", _mockAppID, queryString]]; + XCTestExpectation *expectation = [self expectationWithDescription:self.name]; + FBSDKReferralManagerResultBlock completionHandler = ^(FBSDKReferralManagerResult *result, NSError *referralError) { + if (referralError) { + XCTAssertNil(result); + [expectation fulfill]; + } else { + XCTFail(@"Should have error"); + } + }; + + [_manager startReferralWithCompletionHandler:completionHandler]; + XCTAssertTrue([_manager application:nil openURL:url sourceApplication:@"com.apple.mobilesafari" annotation:nil]); + + [self waitForExpectationsWithTimeout:1 handler:^(NSError *_Nullable error) { + XCTAssertNil(error); + }]; +} + +- (void)testReferralSuccessWithInvalidReferralCode +{ + [self mockURLScheme]; + [self mockBridgeAPI]; + OCMStub([_manager validateChallenge:_mockChallenge]).andReturn(YES); + + NSString *queryStringWithInvalidCode = [@"?fb_referral_codes=%5B%22abc%22%2C%22def?%22%5D&state=" stringByAppendingString:_mockChallenge]; + NSURL *url = [NSURL URLWithString:[NSString stringWithFormat:@"fb%@://authorize/%@", _mockAppID, queryStringWithInvalidCode]]; + XCTestExpectation *expectation = [self expectationWithDescription:self.name]; + FBSDKReferralManagerResultBlock completionHandler = ^(FBSDKReferralManagerResult *result, NSError *referralError) { + if (referralError) { + XCTFail(@"Should not have error"); + } else { + NSArray *referralCodes = result.referralCodes; + NSArray *expectedReferralCodes = @[[FBSDKReferralCode initWithString:@"abc"]]; + XCTAssertEqualObjects(referralCodes, expectedReferralCodes, @"Only valid referral codes should be returned"); + XCTAssertFalse(result.isCancelled); + [expectation fulfill]; + } + }; + + [_manager startReferralWithCompletionHandler:completionHandler]; + XCTAssertTrue([_manager application:nil openURL:url sourceApplication:@"com.apple.mobilesafari" annotation:nil]); + + [self waitForExpectationsWithTimeout:1 handler:^(NSError *_Nullable error) { + XCTAssertNil(error); + }]; +} + +@end diff --git a/FBSDKLoginKit/FBSDKLoginKitTests/Helpers/FakeGraphRequestConnectionProvider.swift b/FBSDKLoginKit/FBSDKLoginKitTests/Helpers/FakeGraphRequestConnectionProvider.swift new file mode 100644 index 0000000000..8391fafbaf --- /dev/null +++ b/FBSDKLoginKit/FBSDKLoginKitTests/Helpers/FakeGraphRequestConnectionProvider.swift @@ -0,0 +1,17 @@ +import Foundation + +@objcMembers +class FakeGraphRequestConnection: NSObject, GraphRequestConnectionProviding { + var startCallCount = 0 + var capturedGraphRequest: GraphRequest? + var capturedCompletionHandler: GraphRequestBlock? + + func add(_ request: GraphRequest, completionHandler handler: @escaping GraphRequestBlock) { + capturedGraphRequest = request + capturedCompletionHandler = handler + } + + func start() { + startCallCount += 1 + } +} diff --git a/FBSDKLoginKit/FBSDKLoginKitTests/Helpers/Fuzzer.swift b/FBSDKLoginKit/FBSDKLoginKitTests/Helpers/Fuzzer.swift new file mode 100644 index 0000000000..68ab0b6c3a --- /dev/null +++ b/FBSDKLoginKit/FBSDKLoginKitTests/Helpers/Fuzzer.swift @@ -0,0 +1,132 @@ +// 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 Foundation + +@objc +public class Fuzzer: NSObject { + + private static let values: [Any] = [ + // Booleans + true, + false, + // Numbers + 1, + 0, + -1, + Int.max, + Double.greatestFiniteMagnitude, + Float.greatestFiniteMagnitude, + Double.leastNonzeroMagnitude, + Double.leastNormalMagnitude, + // Strings + "1", + "a", + " ", + "{}", + "[]", + "{", + "}", + "[ { \"something\": nonexistent } ]", + "\\{ \"foo\": \"bar\" \\}", + // Data + Data(), + "Foo".data(using: .utf8) ?? Data(), + Data(count: 100), + + //swiftlint:disable force_try + try! JSONSerialization.data(withJSONObject: ["foo": "bar"], options: .fragmentsAllowed), + //swiftlint:enable force_try + // Special Characters + "\\", + // Arrays + [], + [1, 2, 3], + [1, 2, 3, "a", "b", "c"], + (0 ... 100), + ("a" ..< "z"), + // Dictionaries + "[:]", + [:], + ["Foo": "Bar"], + ["": [1, 2, 3]], + ["Foo": true], + ["Foo": ["Bar": "Baz"]], + ["Foo": ["a", 1, [:]]] + ] + + @objc + public class var random: Any { + return values.randomElement() ?? values[0] + } + + /// Randomizes the values of a JSON object + /// Will not replace keys. Will either change their values or trim them entirely. + /// Will be called recursively on dictionaries and arrays + @objc + public class func randomize(json: Any) -> Any { + if var dictionary = json as? [String: Any] { + return randomizeInPlace(json: &dictionary) + } + else if var array = json as? [Any] { + return randomizeInPlace(array: &array) + } + else { + return json + } + } + + private class func randomizeInPlace(array: inout [Any]) -> [Any] { + var array = array + + array.enumerated().forEach { enumeration in + if var dictionary = enumeration.element as? [String: Any] { + array[enumeration.offset] = Bool.random() ? dictionary : randomizeInPlace(json: &dictionary) + } else if let subarray = enumeration.element as? [Any] { + array[enumeration.offset] = Bool.random() ? subarray : randomizeInPlace(array: &array) + } else { + array[enumeration.offset] = Bool.random() ? enumeration.element : random + } + } + + return array + } + + private class func randomizeInPlace(json: inout [String: Any]) -> [String: Any] { + json.keys.forEach { key in + if var value = json[key] as? [String: Any] { + json[key] = Bool.random() ? random : randomizeInPlace(json: &value) + } + // randomize array values if they are dictionaries + else if var values = json[key] as? [Any] { + json[key] = Bool.random() ? values : randomizeInPlace(array: &values) + } + else { + if Bool.random() { + json[key] = random + } + + else if Bool.random() { + json.removeValue(forKey: key) + } + } + } + + return json + } +} diff --git a/FBSDKLoginKit/FBSDKLoginKitTests/Helpers/SampleAccessToken.swift b/FBSDKLoginKit/FBSDKLoginKitTests/Helpers/SampleAccessToken.swift new file mode 100644 index 0000000000..f7a6b8e153 --- /dev/null +++ b/FBSDKLoginKit/FBSDKLoginKitTests/Helpers/SampleAccessToken.swift @@ -0,0 +1,50 @@ +// 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. + +@objcMembers +public class SampleAccessToken: NSObject { + + public static var validToken: AccessToken { + return AccessToken( + tokenString: "123", + permissions: [], + declinedPermissions: [], + expiredPermissions: [], + appID: "123", + userID: "user123", + expirationDate: nil, + refreshDate: nil, + dataAccessExpirationDate: nil + ) + } + + public static var expiredToken: AccessToken { + return AccessToken( + tokenString: "123", + permissions: [], + declinedPermissions: [], + expiredPermissions: [], + appID: "123", + userID: "user123", + expirationDate: .distantPast, + refreshDate: nil, + dataAccessExpirationDate: nil + ) + } + +} diff --git a/FBSDKLoginKit/FBSDKLoginKitTests/LoginButtonTests.swift b/FBSDKLoginKit/FBSDKLoginKitTests/LoginButtonTests.swift new file mode 100644 index 0000000000..81352151f2 --- /dev/null +++ b/FBSDKLoginKit/FBSDKLoginKitTests/LoginButtonTests.swift @@ -0,0 +1,396 @@ +// 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 FBSDKCoreKit +import XCTest + +class LoginButtonTests: XCTestCase { + + let validNonce: NSString = "abc123" + var button: FBLoginButton! // swiftlint:disable:this force_unwrapping + var sampleProfile: Profile { + return Profile( + userID: "Sample ID", + firstName: nil, + middleName: nil, + lastName: nil, + name: "Sample Name", + linkURL: nil, + refreshDate: nil + ) + } + + var sampleToken: AuthenticationToken { + return AuthenticationToken(tokenString: "abc", nonce: "123", claims: nil, jti: "jti") + } + + override func setUp() { + super.setUp() + + AccessToken.setCurrent(nil, shouldDispatchNotif: false) + AuthenticationToken.setCurrent(nil, shouldPostNotification: false) + Profile.current = nil + + button = FBLoginButton() + } + + func testDefaultNonce() { + XCTAssertNil(FBLoginButton().nonce, "Should not have a default nonce") + } + + func testSettingInvalidNonce() { + button.nonce = " " + + XCTAssertNil( + button.nonce, + "Should not set an invalid nonce" + ) + } + + func testSettingValidNonce() { + button.nonce = validNonce + + XCTAssertEqual( + button.nonce, + validNonce, + "Should set a valid nonce" + ) + } + + func testLoginConfigurationWithoutNonce() { + XCTAssertNotNil( + button.loginConfiguration(), + "Should be able to create a login configuration without a provided nonce" + ) + } + + func testLoginConfigurationWithInvalidNonce() { + button.nonce = " " + + XCTAssertNotNil( + button.loginConfiguration(), + "Should not create a login configuration with an invalid nonce" + ) + } + + // MARK: - Initial Content Update + + func testInitialContentUpdateWithInactiveAccessTokenWithProfile() { + let button = TestButton() + AccessToken.setCurrent(nil, shouldDispatchNotif: false) + Profile.setCurrent(sampleProfile, shouldPostNotification: false) + + button._initializeContent() + + XCTAssertEqual( + button.updateContentForProfileCallCount, + 1, + "Should use the profile when there is no access token" + ) + XCTAssertEqual( + button.updateContentForAccessTokenCallCount, + 0, + "Should not use the access token when there is no access token" + ) + } + + func testInitialContentUpdateWithActiveAccessTokenWithProfile() { + let button = TestButton() + AccessToken.setCurrent(SampleAccessToken.validToken, shouldDispatchNotif: false) + let profile = Profile( + userID: "Sample ID", + firstName: nil, + middleName: nil, + lastName: nil, + name: "Sample Name", + linkURL: nil, + refreshDate: nil + ) + Profile.setCurrent(profile, shouldPostNotification: false) + + button._initializeContent() + + XCTAssertEqual( + button.updateContentForProfileCallCount, + 0, + "Should not use the profile when there is an access token" + ) + XCTAssertEqual( + button.updateContentForAccessTokenCallCount, + 1, + "Should use the access token when there is one available" + ) + } + + func testInitialContentUpdateWithoutAccessTokenWithoutProfile() { + let button = TestButton() + AccessToken.setCurrent(nil, shouldDispatchNotif: false) + Profile.setCurrent(nil, shouldPostNotification: false); + + button._initializeContent() + + XCTAssertEqual( + button.updateContentForProfileCallCount, + 0, + "Should not use the profile when there is no access token or current profile" + ) + XCTAssertEqual( + button.updateContentForAccessTokenCallCount, + 0, + "Should not use the access token when there is no access token or current profile" + ) + XCTAssertFalse( + button.isSelected, + "Should not be selected when there is no access token or current profile" + ) + } + + // MARK: - Determining Authentication Status + + func testDeterminingAuthenticationWithAccessTokenWithoutAuthToken() { + AccessToken.setCurrent(SampleAccessToken.validToken, shouldDispatchNotif: false) + + XCTAssertTrue( + button._isAuthenticated(), + "Should consider a user authenticated if they have a current access token" + ) + } + + func testDeterminingAuthenticationWithoutAccessTokenWithAuthToken() { + AuthenticationToken.setCurrent(sampleToken, shouldPostNotification: false) + + XCTAssertTrue( + button._isAuthenticated(), + "Should consider a user authenticated if they have a current authentication token" + ) + } + + // MARK: - Handling Notifications + + func testReceivingAccessTokenNotificationWithDidChangeUserIdKey() { + let button = TestButton() + let notification = Notification( + name: .AccessTokenDidChange, + object: nil, + userInfo: [AccessTokenDidChangeUserIDKey: "foo"] + ) + + button._accessTokenDidChange(notification) + + XCTAssertEqual( + button.updateContentForAccessTokenCallCount, + 1, + "An access token notification with a changed user id key should trigger a content update" + ) + } + + func testReceivingAccessTokenNotificationWithTokenDidExpireKey() { + let button = TestButton() + let notification = Notification( + name: .AccessTokenDidChange, + object: nil, + userInfo: [AccessTokenDidExpireKey: "foo"] + ) + + button._accessTokenDidChange(notification) + + XCTAssertEqual( + button.updateContentForAccessTokenCallCount, + 1, + "An access token notification with an expired token key should trigger a content update" + ) + } + + func testReceivingAccessTokenNotificationWithoutRelevantUserInfo() { + let button = TestButton() + let notification = Notification( + name: .AccessTokenDidChange, + object: nil, + userInfo: nil + ) + + button._accessTokenDidChange(notification) + + XCTAssertEqual( + button.updateContentForAccessTokenCallCount, + 0, + "An access token notification without relevant user info should not trigger a content update" + ) + } + + func testReceivingProfileNotification() { + let button = TestButton() + let notification = Notification( + name: .ProfileDidChange, + object: nil, + userInfo: nil + ) + + button._profileDidChange(notification) + + XCTAssertEqual( + button.updateContentForProfileCallCount, + 1, + "An profile change should trigger a content update" + ) + } + + // MARK: - Updating Content + + func testUpdatingContentWithMissingProfile() { + button._updateContent(forUserProfile: nil) + + XCTAssertFalse( + button.isSelected, + "Should not be selected if there is not a profile" + ) + XCTAssertNil(button.userName()) + XCTAssertNil(button.userID()) + } + + func testUpdatingContentWithProfile() { + button._updateContent(forUserProfile: sampleProfile) + + XCTAssertTrue( + button.isSelected, + "Should be selected if there is a valid profile" + ) + XCTAssertEqual(button.userName(), sampleProfile.name) + XCTAssertEqual(button.userID(), sampleProfile.userID) + } + + func testUpdatingContentForProfileWithNewId() { + let button = TestButton() + let profile = sampleProfile(userID: name) + button._updateContent(forUserProfile: sampleProfile) + button._updateContent(forUserProfile: profile) + + XCTAssertEqual( + button.userName(), + profile.name, + "Should update the user information with the updated profile information" + ) + XCTAssertEqual( + button.userID(), + profile.userID, + "Should update the user information with the updated profile information" + ) + } + + func testUpdatingContentForProfileWithNewName() { + let button = TestButton() + let profile = sampleProfile(name: name) + button._updateContent(forUserProfile: sampleProfile) + button._updateContent(forUserProfile: profile) + + XCTAssertEqual( + button.userName(), + profile.name, + "Should update the user information with the updated profile information" + ) + XCTAssertEqual( + button.userID(), + profile.userID, + "Should update the user information with the updated profile information" + ) + } + + func testUpdatingContentWithValidAccessToken() { + let button = TestButton() + AccessToken.setCurrent(SampleAccessToken.validToken, shouldDispatchNotif: false) + + button._updateContentForAccessToken() + + XCTAssertEqual( + button.fetchAndSetContentCallCount, + 1, + "Should try to fetch content for a valid access token" + ) + } + + func testUpdatingContentWithInvalidAccessToken() { + let button = TestButton() + AccessToken.setCurrent(SampleAccessToken.expiredToken, shouldDispatchNotif: false) + + button._updateContentForAccessToken() + button._updateContentForAccessToken() + + XCTAssertEqual( + button.fetchAndSetContentCallCount, + 0, + "Should not try to fetch content for an invalid access token" + ) + } + + func testUpdatingContentWithIdenticalAccessToken() { + let button = TestButton() + + // Make sure the username and id properties on button are set to the same values + // as the access token. This is an easy way to do with without having to stub + // a network call + let profile = sampleProfile(userID: SampleAccessToken.validToken.userID) + button._updateContent(forUserProfile: profile) + + AccessToken.setCurrent(SampleAccessToken.validToken, shouldDispatchNotif: false) + + button._updateContentForAccessToken() + + XCTAssertEqual( + button.fetchAndSetContentCallCount, + 0, + "Should not try to fetch content for a token if the user identifier has not changed" + ) + } + + private func sampleProfile( + userID: String = "Sample ID", + name: String = "Sample Name" + ) -> Profile { + return Profile( + userID: userID, + firstName: nil, + middleName: nil, + lastName: nil, + name: name, + linkURL: nil, + refreshDate: nil + ) + } +} + +private class TestButton: FBLoginButton { + var fetchAndSetContentCallCount = 0 + var updateContentForAccessTokenCallCount = 0 + var updateContentForProfileCallCount = 0 + + override func _updateContentForAccessToken() { + updateContentForAccessTokenCallCount += 1 + + super._updateContentForAccessToken() + } + + override func _updateContent(forUserProfile profile: Profile?) { + updateContentForProfileCallCount += 1 + + super._updateContent(forUserProfile: profile) + } + + override func _fetchAndSetContent() { + fetchAndSetContentCallCount += 1 + } +} diff --git a/FBSDKLoginKit/FBSDKLoginKitTests/LoginConfigurationTests.swift b/FBSDKLoginKit/FBSDKLoginKitTests/LoginConfigurationTests.swift new file mode 100644 index 0000000000..7d7044f8ca --- /dev/null +++ b/FBSDKLoginKit/FBSDKLoginKitTests/LoginConfigurationTests.swift @@ -0,0 +1,109 @@ +// 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 XCTest + +class LoginConfigurationTests: XCTestCase { + + func testDefaults() { + guard let config = LoginConfiguration() else { + return XCTFail("Should be able to create a config with default arguments") + } + + XCTAssertEqual( + config.requestedPermissions, + [], + "A config should be created with default requested permissions" + ) + XCTAssertEqual( + config.tracking, + .enabled, + "Tracking should default to enabled when unspecified" + ) + XCTAssertNotNil( + config.nonce, + "A config should be created with a default nonce" + ) + } + + func testCreatingWithNonceString() { + let nonce = "12345" + let config = LoginConfiguration(nonce: nonce) + XCTAssertEqual( + config?.nonce, + nonce, + "Should create a configuration with the provided nonce string" + ) + } + + func testCreatingWithInvalidNonce() { + XCTAssertNil( + LoginConfiguration(nonce: " "), + "Should not create a login configuration with an invalid nonce" + ) + } + + func testCreatingWithTracking() { + let preferences = [ + LoginTracking.enabled, + .limited + ] + preferences.forEach { preference in + let config = LoginConfiguration(tracking: preference) + XCTAssertEqual( + config?.tracking, + preference, + "Should create a configuration with the provided tracking preference" + ) + } + } + + func testCreatingWithRequestedPermissions() { + let permissions = Set([Permission.email, .userLikes]) + let config = LoginConfiguration(permissions: permissions) + + XCTAssertEqual( + Set((config?.requestedPermissions.map { $0.value })!), + Set(permissions.map { $0.name }), + "Should create a configuration with the provided tracking preference" + ) + } + + func testCreatingWithPermissionsForLimitedTracking() { + let allowedPermissions = Set([Permission.email]) + let disallowedPermissions = Set([Permission.userBirthday, .userPosts]) + + var configuration = LoginConfiguration( + permissions: disallowedPermissions, + tracking: .limited + ) + XCTAssertNil( + configuration, + "Should not create a configuration with permissions that are disallowed based on the tracking preference" // swiftlint:disable:this line_length + ) + + configuration = LoginConfiguration( + permissions: allowedPermissions, + tracking: .limited + ) + XCTAssertNotNil( + configuration, + "Should create a configuration with permissions that are allowed based on the tracking preference" // swiftlint:disable:this line_length + ) + } +} diff --git a/FBSDKLoginKit/FBSDKLoginKitTests/NonceTests.swift b/FBSDKLoginKit/FBSDKLoginKitTests/NonceTests.swift new file mode 100644 index 0000000000..39fa27897e --- /dev/null +++ b/FBSDKLoginKit/FBSDKLoginKitTests/NonceTests.swift @@ -0,0 +1,48 @@ +// 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 XCTest + +class NonceTests: XCTestCase { + + func testInvalidNonces() { + [ + "", + "foo bar" + ].forEach { nonce in + XCTAssertFalse( + Nonce.isValidNonce(nonce), + "Should not consider: \(nonce) to be a valid nonce" + ) + } + } + + func testValidNonces() { + [ + "123", + "foo", + "asdfasdfasdfasdfasdfasdfasdf" + ].forEach { nonce in + XCTAssertTrue( + Nonce.isValidNonce(nonce), + "Should consider: \(nonce) to be a valid nonce" + ) + } + } + +} diff --git a/FBSDKMarketingKit.podspec b/FBSDKMarketingKit.podspec deleted file mode 100644 index 9ca9776e7b..0000000000 --- a/FBSDKMarketingKit.podspec +++ /dev/null @@ -1,45 +0,0 @@ -Pod::Spec.new do |s| - s.name = 'FBSDKMarketingKit' - s.version = '7.0.0' - s.deprecated = true - s.summary = 'Official Facebook SDK for iOS to set up Codeless Events' - - s.description = <<-DESC - The Facebook SDK for iOS Marketing framework provides: - * Set up codeless events. - DESC - - s.homepage = 'https://developers.facebook.com/docs/ios/' - - s.author = 'Facebook' - s.license = { :type => 'Facebook Platform License', :text => <<-LICENSE - 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. - LICENSE - } - s.platform = :ios - s.source = { :http => "https://github.com/facebook/facebook-ios-sdk/releases/download/v#{s.version}/FBSDKMarketingKit.zip", - :type => :zip } - s.source_files = 'FBSDKMarketingKit.framework/**/*.h' - s.public_header_files = 'FBSDKMarketingKit.framework/**/*.h' - s.ios.vendored_frameworks = 'FBSDKMarketingKit.framework' - - s.ios.deployment_target = '8.0' - - s.ios.dependency 'FBSDKCoreKit', "~> #{s.version}" -end diff --git a/FBSDKShareKit.podspec b/FBSDKShareKit.podspec index e8c268914c..f7d4c38691 100644 --- a/FBSDKShareKit.podspec +++ b/FBSDKShareKit.podspec @@ -3,7 +3,7 @@ Pod::Spec.new do |s| s.name = 'FBSDKShareKit' - s.version = '7.1.1' + s.version = '8.2.0' s.summary = 'Official Facebook SDK for iOS to access Facebook Platform Sharing Features' s.description = <<-DESC @@ -21,7 +21,7 @@ Pod::Spec.new do |s| s.author = 'Facebook' s.platform = :ios, :tvos - s.ios.deployment_target = '8.0' + s.ios.deployment_target = '9.0' s.tvos.deployment_target = '10.0' s.source = { @@ -45,7 +45,8 @@ Pod::Spec.new do |s| ss.exclude_files = 'FBSDKShareKit/FBSDKShareKit/include/**/*', 'FBSDKShareKit/FBSDKShareKit/Swift/Exports.swift' - ss.public_header_files = 'FBSDKShareKit/FBSDKShareKit/*.{h}' + ss.public_header_files = 'FBSDKShareKit/FBSDKShareKit/*.{h}', + 'FBSDKShareKit/FBSDKShareKit/Internal/FBSDKVideoUploader.h' ss.source_files = 'FBSDKShareKit/FBSDKShareKit/**/*.{h,m,swift}' end end diff --git a/FBSDKShareKit/Configurations/FBSDKShareKit-Dynamic.xcconfig b/FBSDKShareKit/Configurations/FBSDKShareKit-Dynamic.xcconfig index 6c278289d3..c7d7b14469 100644 --- a/FBSDKShareKit/Configurations/FBSDKShareKit-Dynamic.xcconfig +++ b/FBSDKShareKit/Configurations/FBSDKShareKit-Dynamic.xcconfig @@ -28,4 +28,4 @@ CURRENT_PROJECT_VERSION = $(FBSDK_PROJECT_VERSION) INFOPLIST_FILE = $(SRCROOT)/FBSDKShareKit/Info.plist MODULEMAP_FILE = $(SRCROOT)/FBSDKShareKit/FBSDKShareKit.modulemap -IPHONEOS_DEPLOYMENT_TARGET = 8.0 +IPHONEOS_DEPLOYMENT_TARGET = 9.0 diff --git a/FBSDKShareKit/Configurations/FBSDKShareKitTests.xcconfig b/FBSDKShareKit/Configurations/FBSDKShareKitTests.xcconfig index be3461ce7d..9cc9204d0e 100644 --- a/FBSDKShareKit/Configurations/FBSDKShareKitTests.xcconfig +++ b/FBSDKShareKit/Configurations/FBSDKShareKitTests.xcconfig @@ -24,7 +24,7 @@ PRODUCT_BUNDLE_IDENTIFIER = com.facebook.sdk.FBSDKShareKitTests INFOPLIST_FILE = $(SRCROOT)/FBSDKShareKitTests/Info.plist -IPHONEOS_DEPLOYMENT_TARGET = 8.0 +IPHONEOS_DEPLOYMENT_TARGET = 9.0 HEADER_SEARCH_PATHS = $(inherited) $(BUILT_PRODUCTS_DIR) LIBRARY_SEARCH_PATHS = $(inherited) $(BUILT_PRODUCTS_DIR) diff --git a/FBSDKShareKit/FBSDKShareKit.xcodeproj/project.pbxproj b/FBSDKShareKit/FBSDKShareKit.xcodeproj/project.pbxproj index d490e1456a..13f4a71adb 100644 --- a/FBSDKShareKit/FBSDKShareKit.xcodeproj/project.pbxproj +++ b/FBSDKShareKit/FBSDKShareKit.xcodeproj/project.pbxproj @@ -43,12 +43,9 @@ 8131FB4F1D261E5F000350FF /* UIKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 893774A71A3B807300BE2807 /* UIKit.framework */; }; 8144D8EC1D261D2900C8E4AC /* FBSDKHashtag.m in Sources */ = {isa = PBXBuildFile; fileRef = D5158B5B1C5FD492003E32EE /* FBSDKHashtag.m */; }; 8144D8ED1D261D2900C8E4AC /* FBSDKShareLinkContent.m in Sources */ = {isa = PBXBuildFile; fileRef = 89CE02E41A3F56470033C9CA /* FBSDKShareLinkContent.m */; }; - 8144D8EE1D261D2900C8E4AC /* FBSDKDeviceShareViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 9D10A64A1CB3806700F42AC1 /* FBSDKDeviceShareViewController.m */; }; 8144D8EF1D261D2900C8E4AC /* FBSDKShareConstants.m in Sources */ = {isa = PBXBuildFile; fileRef = 893774881A3B5B9A00BE2807 /* FBSDKShareConstants.m */; }; 8144D8F11D261D2900C8E4AC /* FBSDKSharePhotoContent.m in Sources */ = {isa = PBXBuildFile; fileRef = 89FC9C6E1A3FA1E00001910E /* FBSDKSharePhotoContent.m */; }; - 8144D8F21D261D2900C8E4AC /* FBSDKVideoUploader.m in Sources */ = {isa = PBXBuildFile; fileRef = 9382B78F1BB3A5AD00E4B24F /* FBSDKVideoUploader.m */; }; 8144D8F31D261D2900C8E4AC /* FBSDKShareVideo.m in Sources */ = {isa = PBXBuildFile; fileRef = 896DE2111A9E280900195731 /* FBSDKShareVideo.m */; }; - 8144D8F41D261D2900C8E4AC /* FBSDKDeviceShareButton.m in Sources */ = {isa = PBXBuildFile; fileRef = 9D9E16B31CB46E8F00C8B68F /* FBSDKDeviceShareButton.m */; }; 8144D8F61D261D2900C8E4AC /* FBSDKShareVideoContent.m in Sources */ = {isa = PBXBuildFile; fileRef = 896DE2191A9E282800195731 /* FBSDKShareVideoContent.m */; }; 8144D8F91D261D2900C8E4AC /* FBSDKShareMediaContent.m in Sources */ = {isa = PBXBuildFile; fileRef = 03A419D91C922A2A006E7767 /* FBSDKShareMediaContent.m */; }; 8144D8FC1D261D2900C8E4AC /* FBSDKSharePhoto.m in Sources */ = {isa = PBXBuildFile; fileRef = 892EBBF51A3A50D700721B03 /* FBSDKSharePhoto.m */; }; @@ -58,15 +55,12 @@ 8144D9051D261D2900C8E4AC /* FBSDKShareUtility.h in Headers */ = {isa = PBXBuildFile; fileRef = 891C54561A3BB23F00DF05AF /* FBSDKShareUtility.h */; }; 8144D9071D261D2900C8E4AC /* FBSDKShareConstants.h in Headers */ = {isa = PBXBuildFile; fileRef = 893774871A3B5B9A00BE2807 /* FBSDKShareConstants.h */; settings = {ATTRIBUTES = (Public, ); }; }; 8144D9081D261D2900C8E4AC /* FBSDKShareDefines.h in Headers */ = {isa = PBXBuildFile; fileRef = 8904148A1A648DAC00617215 /* FBSDKShareDefines.h */; }; - 8144D9091D261D2900C8E4AC /* FBSDKDeviceShareButton.h in Headers */ = {isa = PBXBuildFile; fileRef = 9D9E16B21CB46E8F00C8B68F /* FBSDKDeviceShareButton.h */; settings = {ATTRIBUTES = (Public, ); }; }; 8144D90B1D261D2900C8E4AC /* FBSDKSharingContent.h in Headers */ = {isa = PBXBuildFile; fileRef = 8973B37B1A40A93D0001EDC5 /* FBSDKSharingContent.h */; settings = {ATTRIBUTES = (Public, ); }; }; 8144D90C1D261D2900C8E4AC /* FBSDKSharing.h in Headers */ = {isa = PBXBuildFile; fileRef = 8904147F1A64849100617215 /* FBSDKSharing.h */; settings = {ATTRIBUTES = (Public, ); }; }; 8144D90D1D261D2900C8E4AC /* FBSDKShareKit.h in Headers */ = {isa = PBXBuildFile; fileRef = 9D46C5F51A11E5D800A0DDB6 /* FBSDKShareKit.h */; settings = {ATTRIBUTES = (Public, ); }; }; 8144D9101D261D2900C8E4AC /* FBSDKShareVideoContent.h in Headers */ = {isa = PBXBuildFile; fileRef = 896DE2181A9E282800195731 /* FBSDKShareVideoContent.h */; settings = {ATTRIBUTES = (Public, ); }; }; - 8144D9111D261D2900C8E4AC /* FBSDKDeviceShareViewController.h in Headers */ = {isa = PBXBuildFile; fileRef = 9D10A6491CB3806700F42AC1 /* FBSDKDeviceShareViewController.h */; settings = {ATTRIBUTES = (Public, ); }; }; 8144D9131D261D2900C8E4AC /* FBSDKShareVideo.h in Headers */ = {isa = PBXBuildFile; fileRef = 896DE2101A9E280900195731 /* FBSDKShareVideo.h */; settings = {ATTRIBUTES = (Public, ); }; }; 8144D9141D261D2900C8E4AC /* FBSDKShareLinkContent.h in Headers */ = {isa = PBXBuildFile; fileRef = 89CE02E31A3F56470033C9CA /* FBSDKShareLinkContent.h */; settings = {ATTRIBUTES = (Public, ); }; }; - 8144D9151D261D2900C8E4AC /* FBSDKVideoUploader.h in Headers */ = {isa = PBXBuildFile; fileRef = 9382B78D1BB3A54000E4B24F /* FBSDKVideoUploader.h */; }; 8144D9161D261D2900C8E4AC /* FBSDKHashtag.h in Headers */ = {isa = PBXBuildFile; fileRef = D5158B5A1C5FD492003E32EE /* FBSDKHashtag.h */; settings = {ATTRIBUTES = (Public, ); }; }; 8174916D1D1C6D79006E09DF /* FBSDKCoreKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 8118B63D1D0A49B500962084 /* FBSDKCoreKit.framework */; }; 817491741D1C6D84006E09DF /* FBSDKCoreKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 8118B63D1D0A49B500962084 /* FBSDKCoreKit.framework */; }; @@ -85,7 +79,6 @@ 81A07EF31D1A2E6A0041A29C /* FBSDKShareConstants.m in Sources */ = {isa = PBXBuildFile; fileRef = 893774881A3B5B9A00BE2807 /* FBSDKShareConstants.m */; }; 81A07EF61D1A2E6A0041A29C /* FBSDKShareUtility.m in Sources */ = {isa = PBXBuildFile; fileRef = 891C54571A3BB23F00DF05AF /* FBSDKShareUtility.m */; }; 81A07EF91D1A2E6A0041A29C /* FBSDKShareVideoContent.m in Sources */ = {isa = PBXBuildFile; fileRef = 896DE2191A9E282800195731 /* FBSDKShareVideoContent.m */; }; - 81A07EFB1D1A2E6A0041A29C /* FBSDKVideoUploader.m in Sources */ = {isa = PBXBuildFile; fileRef = 9382B78F1BB3A5AD00E4B24F /* FBSDKVideoUploader.m */; }; 81A07EFC1D1A2E6A0041A29C /* FBSDKShareDialogMode.m in Sources */ = {isa = PBXBuildFile; fileRef = DB38B6821AAF668100099656 /* FBSDKShareDialogMode.m */; }; 81A07EFD1D1A2E6A0041A29C /* FBSDKLikeActionController.m in Sources */ = {isa = PBXBuildFile; fileRef = 89D05A7E1AA0E0D300609300 /* FBSDKLikeActionController.m */; }; 81A07EFE1D1A2E6A0041A29C /* FBSDKShareMediaContent.m in Sources */ = {isa = PBXBuildFile; fileRef = 03A419D91C922A2A006E7767 /* FBSDKShareMediaContent.m */; }; @@ -110,7 +103,6 @@ 81A07F1E1D1A2E6A0041A29C /* FBSDKAppGroupContent.h in Headers */ = {isa = PBXBuildFile; fileRef = 89F203AD1AAA45BA0053499E /* FBSDKAppGroupContent.h */; settings = {ATTRIBUTES = (Public, ); }; }; 81A07F1F1D1A2E6A0041A29C /* FBSDKLikeBoxView.h in Headers */ = {isa = PBXBuildFile; fileRef = 89688B391AA62BD800A98519 /* FBSDKLikeBoxView.h */; }; 81A07F201D1A2E6A0041A29C /* FBSDKShareUtility.h in Headers */ = {isa = PBXBuildFile; fileRef = 891C54561A3BB23F00DF05AF /* FBSDKShareUtility.h */; }; - 81A07F221D1A2E6A0041A29C /* FBSDKVideoUploader.h in Headers */ = {isa = PBXBuildFile; fileRef = 9382B78D1BB3A54000E4B24F /* FBSDKVideoUploader.h */; }; 81A07F231D1A2E6A0041A29C /* FBSDKShareLinkContent.h in Headers */ = {isa = PBXBuildFile; fileRef = 89CE02E31A3F56470033C9CA /* FBSDKShareLinkContent.h */; settings = {ATTRIBUTES = (Public, ); }; }; 81A07F251D1A2E6A0041A29C /* FBSDKLikeBoxBorderView.h in Headers */ = {isa = PBXBuildFile; fileRef = 89688B401AA62C4100A98519 /* FBSDKLikeBoxBorderView.h */; }; 81A07F271D1A2E6A0041A29C /* FBSDKLikeActionControllerCache.h in Headers */ = {isa = PBXBuildFile; fileRef = 89CE85E31AA4D1940030C6FA /* FBSDKLikeActionControllerCache.h */; }; @@ -190,11 +182,7 @@ 89FC9C6F1A3FA1E00001910E /* FBSDKSharePhotoContent.h in Headers */ = {isa = PBXBuildFile; fileRef = 89FC9C6D1A3FA1E00001910E /* FBSDKSharePhotoContent.h */; settings = {ATTRIBUTES = (Public, ); }; }; 89FC9C701A3FA1E00001910E /* FBSDKSharePhotoContent.m in Sources */ = {isa = PBXBuildFile; fileRef = 89FC9C6E1A3FA1E00001910E /* FBSDKSharePhotoContent.m */; }; 89FC9C761A3FAB4B0001910E /* FBSDKSharePhotoContentTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 89FC9C751A3FAB4B0001910E /* FBSDKSharePhotoContentTests.m */; }; - 9382B7901BB3A5AD00E4B24F /* FBSDKVideoUploader.m in Sources */ = {isa = PBXBuildFile; fileRef = 9382B78F1BB3A5AD00E4B24F /* FBSDKVideoUploader.m */; }; 939000641EB80C1A00250D4C /* CoreGraphics.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 939000631EB80C1A00250D4C /* CoreGraphics.framework */; }; - 93AECB911BB4B93D001AC1C4 /* FBSDKVideoUploader.h in Headers */ = {isa = PBXBuildFile; fileRef = 9382B78D1BB3A54000E4B24F /* FBSDKVideoUploader.h */; }; - 9D10A64B1CB3806700F42AC1 /* FBSDKDeviceShareViewController.h in Headers */ = {isa = PBXBuildFile; fileRef = 9D10A6491CB3806700F42AC1 /* FBSDKDeviceShareViewController.h */; settings = {ATTRIBUTES = (Public, ); }; }; - 9D10A64C1CB3806700F42AC1 /* FBSDKDeviceShareViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 9D10A64A1CB3806700F42AC1 /* FBSDKDeviceShareViewController.m */; }; 9D2D999F1BC4538300929E76 /* FBSDKShareKit.h in Headers */ = {isa = PBXBuildFile; fileRef = 9D46C5F51A11E5D800A0DDB6 /* FBSDKShareKit.h */; settings = {ATTRIBUTES = (Public, ); }; }; 9D2D99A71BC453DB00929E76 /* FBSDKSharePhoto.h in Headers */ = {isa = PBXBuildFile; fileRef = 892EBBF41A3A50D700721B03 /* FBSDKSharePhoto.h */; settings = {ATTRIBUTES = (Public, ); }; }; 9D2D99A81BC453E100929E76 /* FBSDKSharePhoto.m in Sources */ = {isa = PBXBuildFile; fileRef = 892EBBF51A3A50D700721B03 /* FBSDKSharePhoto.m */; }; @@ -217,10 +205,6 @@ 9D2DE4271CA34ADC0072CF7E /* FBSDKHashtag.h in Headers */ = {isa = PBXBuildFile; fileRef = D5158B5A1C5FD492003E32EE /* FBSDKHashtag.h */; settings = {ATTRIBUTES = (Public, ); }; }; 9D46C5F61A11E5D800A0DDB6 /* FBSDKShareKit.h in Headers */ = {isa = PBXBuildFile; fileRef = 9D46C5F51A11E5D800A0DDB6 /* FBSDKShareKit.h */; settings = {ATTRIBUTES = (Public, ); }; }; 9D46C5FC1A11E5D800A0DDB6 /* FBSDKShareKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 9D46C5F01A11E5D800A0DDB6 /* FBSDKShareKit.framework */; }; - 9D9B1EB61BF1AF0000FD3175 /* FBSDKVideoUploader.h in Headers */ = {isa = PBXBuildFile; fileRef = 9382B78D1BB3A54000E4B24F /* FBSDKVideoUploader.h */; }; - 9D9B1EB71BF1AF0300FD3175 /* FBSDKVideoUploader.m in Sources */ = {isa = PBXBuildFile; fileRef = 9382B78F1BB3A5AD00E4B24F /* FBSDKVideoUploader.m */; }; - 9D9E16B41CB46E8F00C8B68F /* FBSDKDeviceShareButton.h in Headers */ = {isa = PBXBuildFile; fileRef = 9D9E16B21CB46E8F00C8B68F /* FBSDKDeviceShareButton.h */; settings = {ATTRIBUTES = (Public, ); }; }; - 9D9E16B51CB46E8F00C8B68F /* FBSDKDeviceShareButton.m in Sources */ = {isa = PBXBuildFile; fileRef = 9D9E16B31CB46E8F00C8B68F /* FBSDKDeviceShareButton.m */; }; 9DE155621C161B5F005FCF5C /* FBSDKShareDefines.h in Headers */ = {isa = PBXBuildFile; fileRef = 8904148A1A648DAC00617215 /* FBSDKShareDefines.h */; }; 9DEE5C901A671B2100D750E1 /* XCTest.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 9DEE5C8F1A671B2100D750E1 /* XCTest.framework */; }; A41549F71E6653AE001F7152 /* FBSDKCameraEffectTextures+Internal.h in Headers */ = {isa = PBXBuildFile; fileRef = A41549F61E6653AE001F7152 /* FBSDKCameraEffectTextures+Internal.h */; }; @@ -267,6 +251,10 @@ F46FA683245362880060C902 /* Enums+Extensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = F46FA682245362880060C902 /* Enums+Extensions.swift */; }; F46FA684245362880060C902 /* Enums+Extensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = F46FA682245362880060C902 /* Enums+Extensions.swift */; }; F4D7A84724520A6700A12EE5 /* FakeSharingDelegate.m in Sources */ = {isa = PBXBuildFile; fileRef = F4D7A84624520A6700A12EE5 /* FakeSharingDelegate.m */; }; + F4DC050F251947590073B380 /* FBSDKCoreKitInternalImport.h in Headers */ = {isa = PBXBuildFile; fileRef = F4DC05072519457C0073B380 /* FBSDKCoreKitInternalImport.h */; }; + F4DC05172519475A0073B380 /* FBSDKCoreKitInternalImport.h in Headers */ = {isa = PBXBuildFile; fileRef = F4DC05072519457C0073B380 /* FBSDKCoreKitInternalImport.h */; }; + F4DC05182519475C0073B380 /* FBSDKCoreKitInternalImport.h in Headers */ = {isa = PBXBuildFile; fileRef = F4DC05072519457C0073B380 /* FBSDKCoreKitInternalImport.h */; }; + F4DC05202519475C0073B380 /* FBSDKCoreKitInternalImport.h in Headers */ = {isa = PBXBuildFile; fileRef = F4DC05072519457C0073B380 /* FBSDKCoreKitInternalImport.h */; }; F4E7517223EA18C20061BBFC /* FBSDKMessageDialogTests.m in Sources */ = {isa = PBXBuildFile; fileRef = F4E7516823EA18C10061BBFC /* FBSDKMessageDialogTests.m */; }; F4E7517D23EA19010061BBFC /* FBSDKSendButton.m in Sources */ = {isa = PBXBuildFile; fileRef = F4E7517423EA19000061BBFC /* FBSDKSendButton.m */; }; F4E7517E23EA19010061BBFC /* FBSDKSendButton.m in Sources */ = {isa = PBXBuildFile; fileRef = F4E7517423EA19000061BBFC /* FBSDKSendButton.m */; }; @@ -397,7 +385,7 @@ remoteGlobalIDString = 81A07EDF1D1A2E6A0041A29C; remoteInfo = "FBSDKShareKit-Dynamic"; }; - F410D0582370CD66005B9318 /* PBXContainerItemProxy */ = { + F4DE319D24D9F9EC00297C18 /* PBXContainerItemProxy */ = { isa = PBXContainerItemProxy; containerPortal = 8118B6341D0A49B500962084 /* FBSDKCoreKit.xcodeproj */; proxyType = 1; @@ -502,8 +490,6 @@ 89FC9C6D1A3FA1E00001910E /* FBSDKSharePhotoContent.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = FBSDKSharePhotoContent.h; sourceTree = ""; }; 89FC9C6E1A3FA1E00001910E /* FBSDKSharePhotoContent.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = FBSDKSharePhotoContent.m; sourceTree = ""; }; 89FC9C751A3FAB4B0001910E /* FBSDKSharePhotoContentTests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = FBSDKSharePhotoContentTests.m; sourceTree = ""; }; - 9382B78D1BB3A54000E4B24F /* FBSDKVideoUploader.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = FBSDKVideoUploader.h; sourceTree = ""; }; - 9382B78F1BB3A5AD00E4B24F /* FBSDKVideoUploader.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = FBSDKVideoUploader.m; sourceTree = ""; }; 9382B7921BB47DC100E4B24F /* AssetsLibrary.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = AssetsLibrary.framework; path = System/Library/Frameworks/AssetsLibrary.framework; sourceTree = SDKROOT; }; 939000631EB80C1A00250D4C /* CoreGraphics.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = CoreGraphics.framework; path = Platforms/AppleTVOS.platform/Developer/SDKs/AppleTVOS10.2.sdk/System/Library/Frameworks/CoreGraphics.framework; sourceTree = DEVELOPER_DIR; }; 9D10A6491CB3806700F42AC1 /* FBSDKDeviceShareViewController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = FBSDKDeviceShareViewController.h; sourceTree = ""; }; @@ -543,9 +529,10 @@ EA3D469E1AA6835900D0C7D5 /* FBSDKAppInviteContent.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = FBSDKAppInviteContent.m; sourceTree = ""; }; EA6CCE931AA7855D00F1EC35 /* FBSDKAppInviteContentTests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = FBSDKAppInviteContentTests.m; sourceTree = ""; }; EAC370A21AAE3A9C000F0F04 /* FBSDKGameRequestContentTests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = FBSDKGameRequestContentTests.m; sourceTree = ""; }; - F46FA682245362880060C902 /* Enums+Extensions.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = "Enums+Extensions.swift"; path = "Swift/Enums+Extensions.swift"; sourceTree = ""; }; + F46FA682245362880060C902 /* Enums+Extensions.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "Enums+Extensions.swift"; sourceTree = ""; }; F4D7A84524520A6700A12EE5 /* FakeSharingDelegate.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = FakeSharingDelegate.h; sourceTree = ""; }; F4D7A84624520A6700A12EE5 /* FakeSharingDelegate.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = FakeSharingDelegate.m; sourceTree = ""; }; + F4DC05072519457C0073B380 /* FBSDKCoreKitInternalImport.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = FBSDKCoreKitInternalImport.h; sourceTree = ""; }; F4E7516823EA18C10061BBFC /* FBSDKMessageDialogTests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = FBSDKMessageDialogTests.m; sourceTree = ""; }; F4E7517323EA19000061BBFC /* FBSDKMessageDialog.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = FBSDKMessageDialog.h; sourceTree = ""; }; F4E7517423EA19000061BBFC /* FBSDKSendButton.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = FBSDKSendButton.m; sourceTree = ""; }; @@ -748,6 +735,7 @@ 8937747A1A3A5F5B00BE2807 /* Internal */ = { isa = PBXGroup; children = ( + F4DC05072519457C0073B380 /* FBSDKCoreKitInternalImport.h */, F4E7518F23EA1A130061BBFC /* FBSDKMessengerIcon.h */, F4E7519023EA1A130061BBFC /* FBSDKMessengerIcon.m */, A4F33E731E677A8600747AFD /* FBSDKCameraEffectArguments+Internal.h */, @@ -774,8 +762,6 @@ 890414791A6481AB00617215 /* FBSDKShareKit+Internal.h */, 891C54561A3BB23F00DF05AF /* FBSDKShareUtility.h */, 891C54571A3BB23F00DF05AF /* FBSDKShareUtility.m */, - 9382B78D1BB3A54000E4B24F /* FBSDKVideoUploader.h */, - 9382B78F1BB3A5AD00E4B24F /* FBSDKVideoUploader.m */, ); path = Internal; sourceTree = ""; @@ -913,7 +899,7 @@ children = ( F46FA682245362880060C902 /* Enums+Extensions.swift */, ); - name = Swift; + path = Swift; sourceTree = ""; }; /* End PBXGroup section */ @@ -924,12 +910,12 @@ buildActionMask = 2147483647; files = ( 8144D9031D261D2900C8E4AC /* FBSDKSharePhoto.h in Headers */, + F4DC05202519475C0073B380 /* FBSDKCoreKitInternalImport.h in Headers */, 8144D9041D261D2900C8E4AC /* FBSDKSharePhotoContent.h in Headers */, 8144D9051D261D2900C8E4AC /* FBSDKShareUtility.h in Headers */, F462DBFC23B9526300FFCECA /* FBSDKCoreKit+Internal.h in Headers */, 8144D9071D261D2900C8E4AC /* FBSDKShareConstants.h in Headers */, 8144D9081D261D2900C8E4AC /* FBSDKShareDefines.h in Headers */, - 8144D9091D261D2900C8E4AC /* FBSDKDeviceShareButton.h in Headers */, B3D17FF4212CD046000D28D6 /* FBSDKSharingValidation.h in Headers */, 8144D90B1D261D2900C8E4AC /* FBSDKSharingContent.h in Headers */, F4ECC66C23EE2065005DBF05 /* FBSDKCoreKitImport.h in Headers */, @@ -937,10 +923,8 @@ 883644BD1F969CA800124BCB /* FBSDKShareMediaContent.h in Headers */, 8144D90D1D261D2900C8E4AC /* FBSDKShareKit.h in Headers */, 8144D9101D261D2900C8E4AC /* FBSDKShareVideoContent.h in Headers */, - 8144D9111D261D2900C8E4AC /* FBSDKDeviceShareViewController.h in Headers */, 8144D9131D261D2900C8E4AC /* FBSDKShareVideo.h in Headers */, 8144D9141D261D2900C8E4AC /* FBSDKShareLinkContent.h in Headers */, - 8144D9151D261D2900C8E4AC /* FBSDKVideoUploader.h in Headers */, 8144D9161D261D2900C8E4AC /* FBSDKHashtag.h in Headers */, ); runOnlyForDeploymentPostprocessing = 0; @@ -966,7 +950,6 @@ 81A07F1E1D1A2E6A0041A29C /* FBSDKAppGroupContent.h in Headers */, 81A07F1F1D1A2E6A0041A29C /* FBSDKLikeBoxView.h in Headers */, 81A07F201D1A2E6A0041A29C /* FBSDKShareUtility.h in Headers */, - 81A07F221D1A2E6A0041A29C /* FBSDKVideoUploader.h in Headers */, 81A07F231D1A2E6A0041A29C /* FBSDKShareLinkContent.h in Headers */, A49C0F001E54E6E400157726 /* FBSDKShareCameraEffectContent.h in Headers */, 81A07F251D1A2E6A0041A29C /* FBSDKLikeBoxBorderView.h in Headers */, @@ -985,6 +968,7 @@ B3D17FEF212CD045000D28D6 /* FBSDKSharingValidation.h in Headers */, 81A07F331D1A2E6A0041A29C /* FBSDKShareKit.h in Headers */, 81A07F351D1A2E6A0041A29C /* FBSDKGameRequestFrictionlessRecipientCache.h in Headers */, + F4DC05172519475A0073B380 /* FBSDKCoreKitInternalImport.h in Headers */, 81A07F361D1A2E6A0041A29C /* FBSDKHashtag.h in Headers */, 81A07F371D1A2E6A0041A29C /* FBSDKSharePhotoContent.h in Headers */, B331E21B2208C94800F3E4EE /* FBSDKShareExtension.h in Headers */, @@ -1001,12 +985,12 @@ buildActionMask = 2147483647; files = ( 9D2D99A71BC453DB00929E76 /* FBSDKSharePhoto.h in Headers */, + F4DC05182519475C0073B380 /* FBSDKCoreKitInternalImport.h in Headers */, 9D2D99B51BC4544600929E76 /* FBSDKSharePhotoContent.h in Headers */, 9D2D99A91BC453E700929E76 /* FBSDKShareUtility.h in Headers */, F462DBF223B9526200FFCECA /* FBSDKCoreKit+Internal.h in Headers */, 9D2D99AB1BC453F700929E76 /* FBSDKShareConstants.h in Headers */, 9DE155621C161B5F005FCF5C /* FBSDKShareDefines.h in Headers */, - 9D9E16B41CB46E8F00C8B68F /* FBSDKDeviceShareButton.h in Headers */, B3D17FF3212CD046000D28D6 /* FBSDKSharingValidation.h in Headers */, 9D2D99B41BC4543300929E76 /* FBSDKSharingContent.h in Headers */, F4ECC66B23EE2065005DBF05 /* FBSDKCoreKitImport.h in Headers */, @@ -1014,10 +998,8 @@ 883644BC1F969CA700124BCB /* FBSDKShareMediaContent.h in Headers */, 9D2D999F1BC4538300929E76 /* FBSDKShareKit.h in Headers */, 9D2D99B91BC4545D00929E76 /* FBSDKShareVideoContent.h in Headers */, - 9D10A64B1CB3806700F42AC1 /* FBSDKDeviceShareViewController.h in Headers */, 9D2D99B71BC4545200929E76 /* FBSDKShareVideo.h in Headers */, 9D2D99B01BC4541200929E76 /* FBSDKShareLinkContent.h in Headers */, - 9D9B1EB61BF1AF0000FD3175 /* FBSDKVideoUploader.h in Headers */, 9D2DE4271CA34ADC0072CF7E /* FBSDKHashtag.h in Headers */, ); runOnlyForDeploymentPostprocessing = 0; @@ -1044,7 +1026,6 @@ 89F203AF1AAA45BA0053499E /* FBSDKAppGroupContent.h in Headers */, 89688B3B1AA62BD800A98519 /* FBSDKLikeBoxView.h in Headers */, 891C54581A3BB23F00DF05AF /* FBSDKShareUtility.h in Headers */, - 93AECB911BB4B93D001AC1C4 /* FBSDKVideoUploader.h in Headers */, 89CE02E51A3F56470033C9CA /* FBSDKShareLinkContent.h in Headers */, B38A44BA20BC71E800DDE80D /* FBSDKSharingScheme.h in Headers */, A49C0EFF1E54E6E400157726 /* FBSDKShareCameraEffectContent.h in Headers */, @@ -1062,6 +1043,7 @@ 890414A81A6EB7F900617215 /* FBSDKSharingButton.h in Headers */, 9D46C5F61A11E5D800A0DDB6 /* FBSDKShareKit.h in Headers */, 89F203A11AAA0DF80053499E /* FBSDKGameRequestFrictionlessRecipientCache.h in Headers */, + F4DC050F251947590073B380 /* FBSDKCoreKitInternalImport.h in Headers */, D5158B5C1C5FD492003E32EE /* FBSDKHashtag.h in Headers */, 89FC9C6F1A3FA1E00001910E /* FBSDKSharePhotoContent.h in Headers */, B331E21A2208C94800F3E4EE /* FBSDKShareExtension.h in Headers */, @@ -1164,7 +1146,7 @@ buildRules = ( ); dependencies = ( - F410D0592370CD66005B9318 /* PBXTargetDependency */, + F4DE319E24D9F9EC00297C18 /* PBXTargetDependency */, F410D0572370CD62005B9318 /* PBXTargetDependency */, ); name = FBSDKShareKitTests; @@ -1393,12 +1375,9 @@ files = ( 8144D8EC1D261D2900C8E4AC /* FBSDKHashtag.m in Sources */, 8144D8ED1D261D2900C8E4AC /* FBSDKShareLinkContent.m in Sources */, - 8144D8EE1D261D2900C8E4AC /* FBSDKDeviceShareViewController.m in Sources */, 8144D8EF1D261D2900C8E4AC /* FBSDKShareConstants.m in Sources */, 8144D8F11D261D2900C8E4AC /* FBSDKSharePhotoContent.m in Sources */, - 8144D8F21D261D2900C8E4AC /* FBSDKVideoUploader.m in Sources */, 8144D8F31D261D2900C8E4AC /* FBSDKShareVideo.m in Sources */, - 8144D8F41D261D2900C8E4AC /* FBSDKDeviceShareButton.m in Sources */, 8144D8F61D261D2900C8E4AC /* FBSDKShareVideoContent.m in Sources */, 8144D8F91D261D2900C8E4AC /* FBSDKShareMediaContent.m in Sources */, 8144D8FC1D261D2900C8E4AC /* FBSDKSharePhoto.m in Sources */, @@ -1433,7 +1412,6 @@ 81A07EF61D1A2E6A0041A29C /* FBSDKShareUtility.m in Sources */, A49C0F121E55098400157726 /* FBSDKCameraEffectArguments.m in Sources */, 81A07EF91D1A2E6A0041A29C /* FBSDKShareVideoContent.m in Sources */, - 81A07EFB1D1A2E6A0041A29C /* FBSDKVideoUploader.m in Sources */, 81A07EFC1D1A2E6A0041A29C /* FBSDKShareDialogMode.m in Sources */, 81A07EFD1D1A2E6A0041A29C /* FBSDKLikeActionController.m in Sources */, 81A07EFE1D1A2E6A0041A29C /* FBSDKShareMediaContent.m in Sources */, @@ -1452,12 +1430,9 @@ files = ( 9D2DE4251CA348AA0072CF7E /* FBSDKHashtag.m in Sources */, 9D2D99B11BC4541A00929E76 /* FBSDKShareLinkContent.m in Sources */, - 9D10A64C1CB3806700F42AC1 /* FBSDKDeviceShareViewController.m in Sources */, 9D2D99AC1BC453FC00929E76 /* FBSDKShareConstants.m in Sources */, 9D2D99B61BC4544C00929E76 /* FBSDKSharePhotoContent.m in Sources */, - 9D9B1EB71BF1AF0300FD3175 /* FBSDKVideoUploader.m in Sources */, 9D2D99B81BC4545600929E76 /* FBSDKShareVideo.m in Sources */, - 9D9E16B51CB46E8F00C8B68F /* FBSDKDeviceShareButton.m in Sources */, 9D2D99BA1BC4546100929E76 /* FBSDKShareVideoContent.m in Sources */, 9D2DE4261CA34ABA0072CF7E /* FBSDKShareMediaContent.m in Sources */, 9D2D99A81BC453E100929E76 /* FBSDKSharePhoto.m in Sources */, @@ -1492,7 +1467,6 @@ 891C54591A3BB23F00DF05AF /* FBSDKShareUtility.m in Sources */, A49C0F111E55098400157726 /* FBSDKCameraEffectArguments.m in Sources */, 896DE21B1A9E282800195731 /* FBSDKShareVideoContent.m in Sources */, - 9382B7901BB3A5AD00E4B24F /* FBSDKVideoUploader.m in Sources */, DB38B6831AAF668100099656 /* FBSDKShareDialogMode.m in Sources */, 89D05A801AA0E0D300609300 /* FBSDKLikeActionController.m in Sources */, 03A419DB1C922A2A006E7767 /* FBSDKShareMediaContent.m in Sources */, @@ -1566,10 +1540,10 @@ target = 81A07EDF1D1A2E6A0041A29C /* FBSDKShareKit-Dynamic */; targetProxy = F410D0562370CD62005B9318 /* PBXContainerItemProxy */; }; - F410D0592370CD66005B9318 /* PBXTargetDependency */ = { + F4DE319E24D9F9EC00297C18 /* PBXTargetDependency */ = { isa = PBXTargetDependency; name = "FBSDKCoreKit-Dynamic"; - targetProxy = F410D0582370CD66005B9318 /* PBXContainerItemProxy */; + targetProxy = F4DE319D24D9F9EC00297C18 /* PBXContainerItemProxy */; }; /* End PBXTargetDependency section */ diff --git a/FBSDKShareKit/FBSDKShareKit/FBSDKAppGroupContent.m b/FBSDKShareKit/FBSDKShareKit/FBSDKAppGroupContent.m index c8a64b43b7..117761eed4 100644 --- a/FBSDKShareKit/FBSDKShareKit/FBSDKAppGroupContent.m +++ b/FBSDKShareKit/FBSDKShareKit/FBSDKAppGroupContent.m @@ -29,24 +29,24 @@ #else -#ifdef FBSDKCOCOAPODS -#import -#else -#import "FBSDKCoreKit+Internal.h" -#endif -#import "FBSDKShareUtility.h" + #ifdef FBSDKCOCOAPODS + #import + #else + #import "FBSDKCoreKit+Internal.h" + #endif + #import "FBSDKShareUtility.h" -#define FBSDK_APP_GROUP_CONTENT_GROUP_DESCRIPTION_KEY @"groupDescription" -#define FBSDK_APP_GROUP_CONTENT_NAME_KEY @"name" -#define FBSDK_APP_GROUP_CONTENT_PRIVACY_KEY @"privacy" + #define FBSDK_APP_GROUP_CONTENT_GROUP_DESCRIPTION_KEY @"groupDescription" + #define FBSDK_APP_GROUP_CONTENT_NAME_KEY @"name" + #define FBSDK_APP_GROUP_CONTENT_PRIVACY_KEY @"privacy" NSString *NSStringFromFBSDKAppGroupPrivacy(FBSDKAppGroupPrivacy privacy) { switch (privacy) { - case FBSDKAppGroupPrivacyClosed:{ + case FBSDKAppGroupPrivacyClosed: { return @"closed"; } - case FBSDKAppGroupPrivacyOpen:{ + case FBSDKAppGroupPrivacyOpen: { return @"open"; } } @@ -54,7 +54,7 @@ @implementation FBSDKAppGroupContent -#pragma mark - Equality + #pragma mark - Equality - (NSUInteger)hash { @@ -79,13 +79,13 @@ - (BOOL)isEqual:(id)object - (BOOL)isEqualToAppGroupContent:(FBSDKAppGroupContent *)content { - return (content && - (_privacy == content.privacy) && - [FBSDKInternalUtility object:_name isEqualToObject:content.name] && - [FBSDKInternalUtility object:_groupDescription isEqualToObject:content.groupDescription]); + return (content + && (_privacy == content.privacy) + && [FBSDKInternalUtility object:_name isEqualToObject:content.name] + && [FBSDKInternalUtility object:_groupDescription isEqualToObject:content.groupDescription]); } -#pragma mark - NSCoding + #pragma mark - NSCoding + (BOOL)supportsSecureCoding { @@ -110,7 +110,7 @@ - (void)encodeWithCoder:(NSCoder *)encoder [encoder encodeInteger:_privacy forKey:FBSDK_APP_GROUP_CONTENT_PRIVACY_KEY]; } -#pragma mark - NSCopying + #pragma mark - NSCopying - (id)copyWithZone:(NSZone *)zone { diff --git a/FBSDKShareKit/FBSDKShareKit/FBSDKAppInviteContent.m b/FBSDKShareKit/FBSDKShareKit/FBSDKAppInviteContent.m index fbd1b3e497..9928131dee 100644 --- a/FBSDKShareKit/FBSDKShareKit/FBSDKAppInviteContent.m +++ b/FBSDKShareKit/FBSDKShareKit/FBSDKAppInviteContent.m @@ -20,21 +20,20 @@ #if !TARGET_OS_TV -#import "FBSDKAppInviteContent.h" + #import "FBSDKAppInviteContent.h" -#ifdef FBSDKCOCOAPODS -#import -#else -#import "FBSDKCoreKit+Internal.h" -#endif -#import "FBSDKShareUtility.h" - -#define FBSDK_APP_INVITE_CONTENT_APP_LINK_URL_KEY @"appLinkURL" -#define FBSDK_APP_INVITE_CONTENT_PREVIEW_IMAGE_KEY @"previewImage" -#define FBSDK_APP_INVITE_CONTENT_PROMO_CODE_KEY @"promoCode" -#define FBSDK_APP_INVITE_CONTENT_PROMO_TEXT_KEY @"promoText" -#define FBSDK_APP_INVITE_CONTENT_DESTINATION_KEY @"destination" + #ifdef FBSDKCOCOAPODS + #import + #else + #import "FBSDKCoreKit+Internal.h" + #endif + #import "FBSDKShareUtility.h" + #define FBSDK_APP_INVITE_CONTENT_APP_LINK_URL_KEY @"appLinkURL" + #define FBSDK_APP_INVITE_CONTENT_PREVIEW_IMAGE_KEY @"previewImage" + #define FBSDK_APP_INVITE_CONTENT_PROMO_CODE_KEY @"promoCode" + #define FBSDK_APP_INVITE_CONTENT_PROMO_TEXT_KEY @"promoText" + #define FBSDK_APP_INVITE_CONTENT_DESTINATION_KEY @"destination" @implementation FBSDKAppInviteContent @@ -48,14 +47,14 @@ - (void)setPreviewImageURL:(NSURL *)previewImageURL self.appInvitePreviewImageURL = previewImageURL; } -#pragma mark - FBSDKSharingValidation + #pragma mark - FBSDKSharingValidation - (BOOL)validateWithOptions:(FBSDKShareBridgeOptions)bridgeOptions error:(NSError *__autoreleasing *)errorRef { - return ([FBSDKShareUtility validateRequiredValue:_appLinkURL name:@"appLinkURL" error:errorRef] && - [FBSDKShareUtility validateNetworkURL:_appLinkURL name:@"appLinkURL" error:errorRef] && - [FBSDKShareUtility validateNetworkURL:_appInvitePreviewImageURL name:@"appInvitePreviewImageURL" error:errorRef] && - [self _validatePromoCodeWithError:errorRef]); + return ([FBSDKShareUtility validateRequiredValue:_appLinkURL name:@"appLinkURL" error:errorRef] + && [FBSDKShareUtility validateNetworkURL:_appLinkURL name:@"appLinkURL" error:errorRef] + && [FBSDKShareUtility validateNetworkURL:_appInvitePreviewImageURL name:@"appInvitePreviewImageURL" error:errorRef] + && [self _validatePromoCodeWithError:errorRef]); } - (BOOL)_validatePromoCodeWithError:(NSError *__autoreleasing *)errorRef @@ -92,7 +91,6 @@ - (BOOL)_validatePromoCodeWithError:(NSError *__autoreleasing *)errorRef } return NO; } - } if (errorRef != NULL) { @@ -102,7 +100,7 @@ - (BOOL)_validatePromoCodeWithError:(NSError *__autoreleasing *)errorRef return YES; } -#pragma mark - Equality + #pragma mark - Equality - (NSUInteger)hash { @@ -128,16 +126,16 @@ - (BOOL)isEqual:(id)object - (BOOL)isEqualToAppInviteContent:(FBSDKAppInviteContent *)content { - return (content && - [FBSDKInternalUtility object:_appLinkURL isEqualToObject:content.appLinkURL] && - [FBSDKInternalUtility object:_appInvitePreviewImageURL isEqualToObject:content.appInvitePreviewImageURL] && - [FBSDKInternalUtility object:_promotionText isEqualToObject:content.promotionText] && - [FBSDKInternalUtility object:_promotionCode isEqualToObject:content.promotionText] && - _destination == content.destination - ); + return (content + && [FBSDKInternalUtility object:_appLinkURL isEqualToObject:content.appLinkURL] + && [FBSDKInternalUtility object:_appInvitePreviewImageURL isEqualToObject:content.appInvitePreviewImageURL] + && [FBSDKInternalUtility object:_promotionText isEqualToObject:content.promotionText] + && [FBSDKInternalUtility object:_promotionCode isEqualToObject:content.promotionText] + && _destination == content.destination + ); } -#pragma mark - NSCoding + #pragma mark - NSCoding + (BOOL)supportsSecureCoding { @@ -150,12 +148,11 @@ - (instancetype)initWithCoder:(NSCoder *)decoder _appLinkURL = [decoder decodeObjectOfClass:[NSURL class] forKey:FBSDK_APP_INVITE_CONTENT_APP_LINK_URL_KEY]; _appInvitePreviewImageURL = [decoder decodeObjectOfClass:[NSURL class] forKey:FBSDK_APP_INVITE_CONTENT_PREVIEW_IMAGE_KEY]; _promotionCode = [decoder decodeObjectOfClass:[NSString class] forKey: - FBSDK_APP_INVITE_CONTENT_PROMO_CODE_KEY]; + FBSDK_APP_INVITE_CONTENT_PROMO_CODE_KEY]; _promotionText = [decoder decodeObjectOfClass:[NSString class] forKey: - FBSDK_APP_INVITE_CONTENT_PROMO_TEXT_KEY]; + FBSDK_APP_INVITE_CONTENT_PROMO_TEXT_KEY]; _destination = [decoder decodeIntegerForKey: - FBSDK_APP_INVITE_CONTENT_DESTINATION_KEY]; - + FBSDK_APP_INVITE_CONTENT_DESTINATION_KEY]; } return self; } @@ -169,7 +166,7 @@ - (void)encodeWithCoder:(NSCoder *)encoder [encoder encodeInt:(int)_destination forKey:FBSDK_APP_INVITE_CONTENT_DESTINATION_KEY]; } -#pragma mark - NSCopying + #pragma mark - NSCopying - (id)copyWithZone:(NSZone *)zone { diff --git a/FBSDKShareKit/FBSDKShareKit/FBSDKCameraEffectArguments.m b/FBSDKShareKit/FBSDKShareKit/FBSDKCameraEffectArguments.m index 1ab385bc0c..2030e6400e 100644 --- a/FBSDKShareKit/FBSDKShareKit/FBSDKCameraEffectArguments.m +++ b/FBSDKShareKit/FBSDKShareKit/FBSDKCameraEffectArguments.m @@ -20,14 +20,14 @@ #if !TARGET_OS_TV -#import "FBSDKCameraEffectArguments.h" + #import "FBSDKCameraEffectArguments.h" -#ifdef FBSDKCOCOAPODS -#import -#else -#import "FBSDKCoreKit+Internal.h" -#endif -#import "FBSDKShareUtility.h" + #ifdef FBSDKCOCOAPODS + #import + #else + #import "FBSDKCoreKit+Internal.h" + #endif + #import "FBSDKShareUtility.h" static NSString *const FBSDKCameraEffectArgumentsArgumentsKey = @"arguments"; @@ -36,7 +36,7 @@ @implementation FBSDKCameraEffectArguments NSMutableDictionary *_arguments; } -#pragma mark - Object Lifecycle + #pragma mark - Object Lifecycle - (instancetype)init { @@ -71,7 +71,7 @@ - (void)setArray:(NSArray *)array forKey:(NSString *)key return _arguments; } -#pragma mark - Equality + #pragma mark - Equality - (NSUInteger)hash { @@ -94,7 +94,7 @@ - (BOOL)isEqualToCameraEffectArguments:(FBSDKCameraEffectArguments *)object return [FBSDKInternalUtility object:_arguments isEqualToObject:[object allArguments]]; } -#pragma mark - NSCoding + #pragma mark - NSCoding + (BOOL)supportsSecureCoding { @@ -115,7 +115,7 @@ - (void)encodeWithCoder:(NSCoder *)encoder [encoder encodeObject:_arguments forKey:FBSDKCameraEffectArgumentsArgumentsKey]; } -#pragma mark - NSCopying + #pragma mark - NSCopying - (id)copyWithZone:(NSZone *)zone { @@ -124,8 +124,7 @@ - (id)copyWithZone:(NSZone *)zone return copy; } - -#pragma mark - Helper Methods + #pragma mark - Helper Methods - (void)_setValue:(id)value forKey:(NSString *)key { diff --git a/FBSDKShareKit/FBSDKShareKit/FBSDKCameraEffectTextures.m b/FBSDKShareKit/FBSDKShareKit/FBSDKCameraEffectTextures.m index 4bdac8c5e0..6bea464824 100644 --- a/FBSDKShareKit/FBSDKShareKit/FBSDKCameraEffectTextures.m +++ b/FBSDKShareKit/FBSDKShareKit/FBSDKCameraEffectTextures.m @@ -20,14 +20,14 @@ #if !TARGET_OS_TV -#import "FBSDKCameraEffectTextures.h" + #import "FBSDKCameraEffectTextures.h" -#ifdef FBSDKCOCOAPODS -#import -#else -#import "FBSDKCoreKit+Internal.h" -#endif -#import "FBSDKShareUtility.h" + #ifdef FBSDKCOCOAPODS + #import + #else + #import "FBSDKCoreKit+Internal.h" + #endif + #import "FBSDKShareUtility.h" static NSString *const FBSDKCameraEffectTexturesTexturesKey = @"textures"; @@ -36,7 +36,7 @@ @implementation FBSDKCameraEffectTextures NSMutableDictionary *_textures; } -#pragma mark - Object Lifecycle + #pragma mark - Object Lifecycle - (instancetype)init { @@ -61,7 +61,7 @@ - (UIImage *)imageForKey:(NSString *)key return _textures; } -#pragma mark - Equality + #pragma mark - Equality - (NSUInteger)hash { @@ -84,7 +84,7 @@ - (BOOL)isEqualToCameraEffectTextures:(FBSDKCameraEffectTextures *)object return [FBSDKInternalUtility object:_textures isEqualToObject:[object allTextures]]; } -#pragma mark - NSCoding + #pragma mark - NSCoding + (BOOL)supportsSecureCoding { @@ -105,7 +105,7 @@ - (void)encodeWithCoder:(NSCoder *)encoder [encoder encodeObject:_textures forKey:FBSDKCameraEffectTexturesTexturesKey]; } -#pragma mark - NSCopying + #pragma mark - NSCopying - (id)copyWithZone:(NSZone *)zone { @@ -114,7 +114,7 @@ - (id)copyWithZone:(NSZone *)zone return copy; } -#pragma mark - Helper Methods + #pragma mark - Helper Methods - (void)_setValue:(id)value forKey:(NSString *)key { diff --git a/FBSDKShareKit/FBSDKShareKit/FBSDKCoreKitImport.h b/FBSDKShareKit/FBSDKShareKit/FBSDKCoreKitImport.h index cb1975ff73..aa0979d4e6 100644 --- a/FBSDKShareKit/FBSDKShareKit/FBSDKCoreKitImport.h +++ b/FBSDKShareKit/FBSDKShareKit/FBSDKCoreKitImport.h @@ -24,7 +24,7 @@ // Even though this file is not available from projects using SPM, // it is available when building the packages themselves so we need to include this check. #if FBSDK_SWIFT_PACKAGE -#import + #import #else -#import + #import #endif diff --git a/FBSDKShareKit/FBSDKShareKit/FBSDKDeviceShareButton.m b/FBSDKShareKit/FBSDKShareKit/FBSDKDeviceShareButton.m deleted file mode 100644 index d88ab2e4f1..0000000000 --- a/FBSDKShareKit/FBSDKShareKit/FBSDKDeviceShareButton.m +++ /dev/null @@ -1,79 +0,0 @@ -// 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 "TargetConditionals.h" - -#if TARGET_OS_TV - -#import "FBSDKDeviceShareButton.h" -#import "FBSDKDeviceShareViewController.h" - -#if defined BUCK || defined FBSDKCOCOAPODS || defined __cplusplus -#import -#else -#import "FBSDKCoreKit+Internal.h" -#endif - -@implementation FBSDKDeviceShareButton - -- (void)configureButton -{ - [self configureWithIcon:nil - title:nil - backgroundColor:nil - highlightedColor:nil]; - - NSString *title = - NSLocalizedStringWithDefaultValue(@"ShareButton.Share", @"FacebookSDK", [FBSDKInternalUtility bundleForStrings], - @"Share", - @"The label for FBSDKShareButton"); - NSAttributedString *attributedTitle = [self attributedTitleStringFromString:title]; - [self setAttributedTitle:attributedTitle forState:UIControlStateNormal]; - [self setAttributedTitle:attributedTitle forState:UIControlStateFocused]; - [self setAttributedTitle:attributedTitle forState:UIControlStateSelected]; - [self setAttributedTitle:attributedTitle forState:UIControlStateSelected | UIControlStateHighlighted]; - [self setAttributedTitle:attributedTitle forState:UIControlStateSelected | UIControlStateFocused]; - - self.enabled = NO; - [self addTarget:self action:@selector(_buttonPressed:) forControlEvents:UIControlEventPrimaryActionTriggered]; -} - -#pragma mark - Properties - -- (void)setShareContent:(id)shareContent -{ - if (_shareContent != shareContent) { - _shareContent = shareContent; - self.enabled = (shareContent != nil); - } -} - -#pragma mark - Implementation - -- (void)_buttonPressed:(id)sender -{ - UIViewController *parentViewController = [FBSDKInternalUtility viewControllerForView:self]; - if (_shareContent) { - FBSDKDeviceShareViewController *vc = [[FBSDKDeviceShareViewController alloc] initWithShareContent:_shareContent]; - [parentViewController presentViewController:vc animated:YES completion:NULL]; - } -} - -@end - -#endif diff --git a/FBSDKShareKit/FBSDKShareKit/FBSDKDeviceShareViewController.h b/FBSDKShareKit/FBSDKShareKit/FBSDKDeviceShareViewController.h deleted file mode 100644 index 091b3c48d0..0000000000 --- a/FBSDKShareKit/FBSDKShareKit/FBSDKDeviceShareViewController.h +++ /dev/null @@ -1,102 +0,0 @@ -// 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 "TargetConditionals.h" - -#if TARGET_OS_TV - -#import - -#import "FBSDKCoreKitImport.h" - -#import "FBSDKSharingContent.h" - -NS_ASSUME_NONNULL_BEGIN - -@class FBSDKDeviceShareViewController; - -/** - A delegate for `FBSDKDeviceShareViewController` - */ -NS_SWIFT_NAME(DeviceShareViewControllerDelegate) -@protocol FBSDKDeviceShareViewControllerDelegate - -/** - Indicates the dialog was completed - - This can happen if the user tapped cancel, or menu on their Siri remote, or if the - device code has expired. You will not be informed if the user actually posted a share to Facebook. - */ -- (void)deviceShareViewControllerDidComplete:(FBSDKDeviceShareViewController *)viewController - error:(nullable NSError *)error; - -@end - -/** - Use this view controller to initiate Sharing for Devices, an easy way for people to share content - from your tvOS app without requiring Facebook Login. - - The `FBSDKDeviceShareViewController` can dismiss itself and notify its delegate - of completion. You should not re-use a `FBSDKDeviceShareViewController` instance again. - - See [Sharing for Devices](https://developers.facebook.com/docs/sharing/for-devices). - - @code - // from your view controller: - FBSDKDeviceShareViewController *vc = [[FBSDKDeviceShareViewController alloc] initWithShareContent:...]; - vc.delegate = self; - [self presentViewController:vc - animated:YES - completion:NULL]; - */ -NS_SWIFT_NAME(FBDeviceShareViewController) -@interface FBSDKDeviceShareViewController : FBSDKDeviceViewControllerBase - -/** - Initializes a new instance with share content. - @param shareContent The share content. Only `FBSDKShareLinkContent` is supported. - - Invalid content types will result in notifying the delegate with an error when the view controller is presented. - - For `FBSDKShareLinkContent`, only contentURL is used (e.g., properties are not supported) - */ -- (instancetype)initWithShareContent:(id)shareContent -NS_SWIFT_NAME(init(content:)) -NS_DESIGNATED_INITIALIZER; - -- (instancetype)init NS_UNAVAILABLE; -+ (instancetype)new NS_UNAVAILABLE; -- (nullable instancetype)initWithCoder:(NSCoder *)aDecoder NS_UNAVAILABLE; -- (instancetype)initWithNibName:(nullable NSString *)nibNameOrNil - bundle:(nullable NSBundle *)nibBundleOrNil NS_UNAVAILABLE; - -/** - The delegate. - */ -@property (nullable, nonatomic, weak) id delegate; - -/** - The share content. - */ -@property (nonatomic, readonly, strong) id shareContent; - -@end - -NS_ASSUME_NONNULL_END - -#endif diff --git a/FBSDKShareKit/FBSDKShareKit/FBSDKDeviceShareViewController.m b/FBSDKShareKit/FBSDKShareKit/FBSDKDeviceShareViewController.m deleted file mode 100644 index a779644ac2..0000000000 --- a/FBSDKShareKit/FBSDKShareKit/FBSDKDeviceShareViewController.m +++ /dev/null @@ -1,145 +0,0 @@ -// 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 "TargetConditionals.h" - -#if TARGET_OS_TV - -#import "FBSDKDeviceShareViewController.h" - -#ifdef FBSDKCOCOAPODS -#import -#else -#import "FBSDKCoreKit+Internal.h" -#endif -#import "FBSDKShareLinkContent.h" -#import "FBSDKShareUtility.h" - -@implementation FBSDKDeviceShareViewController - -- (instancetype)initWithShareContent:(id)shareContent -{ - if ((self = [super initWithNibName:nil bundle:nil])) - { - _shareContent = 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]; - [self.delegate deviceShareViewControllerDidComplete:self error:nil]; -} - -- (void)viewDidLoad -{ - [super viewDidLoad]; - [FBSDKInternalUtility validateRequiredClientAccessToken]; -} - -- (void)viewDidAppear:(BOOL)animated -{ - [super viewDidAppear:animated]; - - NSError *error; - NSDictionary *params = [self _graphRequestParametersForContent:_shareContent error:&error]; - if (!params) { - [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:[mutableParameters copy] - tokenString:[FBSDKInternalUtility validateRequiredClientAccessToken] - HTTPMethod:@"POST" - flags:FBSDKGraphRequestFlagNone]; - [request startWithCompletionHandler:^(FBSDKGraphRequestConnection *connection, id result, NSError *requestError) { - if (requestError) { - [self _dismissWithError:error]; - return; - } - NSString *code = result[@"user_code"]; - NSUInteger expires = [result[@"expires_in"] unsignedIntegerValue]; - if (!code || !expires) { - [self _dismissWithError:[FBSDKError unknownErrorWithMessage:@"Malformed response from server"]]; - return; - } - self.deviceDialogView.confirmationCode = code; - __weak typeof(self) weakSelf = self; - dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(expires * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{ - [weakSelf _dismissWithError:nil]; - }); - }]; -} - -#pragma mark - Private impl - -- (void)_dismissWithError:(NSError *)error -{ - id delegate = self.delegate; - // clear delegate to avoid double messaging after viewDidDisappear - self.delegate = nil; - [self dismissViewControllerAnimated:YES - completion:^{ - [delegate deviceShareViewControllerDidComplete:self - error:error]; - }]; -} - -- (NSDictionary *)_graphRequestParametersForContent:(id)shareContent error:(NSError **)error -{ - if (error != NULL) { - *error = nil; - } - if (!_shareContent) { - if (error != NULL) { - *error = [FBSDKError requiredArgumentErrorWithName:@"shareContent" message:nil]; - } - return nil; - } - if ([_shareContent isKindOfClass:[FBSDKShareLinkContent class]]) { - NSString *unused; - NSDictionary *params; - [FBSDKShareUtility buildWebShareContent:_shareContent - methodName:&unused - parameters:¶ms - error:error]; - return params; - } - if (error != NULL) { - *error = [FBSDKError - invalidArgumentErrorWithName:@"shareContent" - value:shareContent - message:[NSString stringWithFormat:@"%@ is not a supported content type", [shareContent class]]]; - } - return nil; -} -@end - -#endif diff --git a/FBSDKShareKit/FBSDKShareKit/FBSDKGameRequestContent.m b/FBSDKShareKit/FBSDKShareKit/FBSDKGameRequestContent.m index 8da067eab1..f5357a20b7 100644 --- a/FBSDKShareKit/FBSDKShareKit/FBSDKGameRequestContent.m +++ b/FBSDKShareKit/FBSDKShareKit/FBSDKGameRequestContent.m @@ -20,30 +20,30 @@ #if !TARGET_OS_TV -#import "FBSDKGameRequestContent.h" + #import "FBSDKGameRequestContent.h" -#ifdef FBSDKCOCOAPODS -#import -#else -#import "FBSDKCoreKit+Internal.h" -#endif -#import "FBSDKShareConstants.h" -#import "FBSDKShareUtility.h" + #ifdef FBSDKCOCOAPODS + #import + #else + #import "FBSDKCoreKit+Internal.h" + #endif + #import "FBSDKShareConstants.h" + #import "FBSDKShareUtility.h" -#define FBSDK_APP_REQUEST_CONTENT_TO_KEY @"to" -#define FBSDK_APP_REQUEST_CONTENT_MESSAGE_KEY @"message" -#define FBSDK_APP_REQUEST_CONTENT_ACTION_TYPE_KEY @"actionType" -#define FBSDK_APP_REQUEST_CONTENT_OBJECT_ID_KEY @"objectID" -#define FBSDK_APP_REQUEST_CONTENT_FILTERS_KEY @"filters" -#define FBSDK_APP_REQUEST_CONTENT_SUGGESTIONS_KEY @"suggestions" -#define FBSDK_APP_REQUEST_CONTENT_DATA_KEY @"data" -#define FBSDK_APP_REQUEST_CONTENT_TITLE_KEY @"title" + #define FBSDK_APP_REQUEST_CONTENT_TO_KEY @"to" + #define FBSDK_APP_REQUEST_CONTENT_MESSAGE_KEY @"message" + #define FBSDK_APP_REQUEST_CONTENT_ACTION_TYPE_KEY @"actionType" + #define FBSDK_APP_REQUEST_CONTENT_OBJECT_ID_KEY @"objectID" + #define FBSDK_APP_REQUEST_CONTENT_FILTERS_KEY @"filters" + #define FBSDK_APP_REQUEST_CONTENT_SUGGESTIONS_KEY @"suggestions" + #define FBSDK_APP_REQUEST_CONTENT_DATA_KEY @"data" + #define FBSDK_APP_REQUEST_CONTENT_TITLE_KEY @"title" @implementation FBSDKGameRequestContent -#pragma mark - Properties + #pragma mark - Properties --(void)setRecipients:(NSArray *)recipients +- (void)setRecipients:(NSArray *)recipients { [FBSDKShareUtility assertCollection:recipients ofClass:[NSString class] name:@"recipients"]; if (![_recipients isEqual:recipients]) { @@ -79,7 +79,7 @@ - (void)setTo:(NSArray *)to self.recipients = to; } -#pragma mark - FBSDKSharingValidation + #pragma mark - FBSDKSharingValidation - (BOOL)validateWithOptions:(FBSDKShareBridgeOptions)bridgeOptions error:(NSError *__autoreleasing *)errorRef { @@ -155,15 +155,15 @@ - (BOOL)validateWithOptions:(FBSDKShareBridgeOptions)bridgeOptions error:(NSErro @(FBSDKGameRequestActionTypeAskFor), @(FBSDKGameRequestActionTypeTurn)] error:errorRef] - && [FBSDKShareUtility validateArgumentWithName:@"filters" - value:_filters - isIn:@[@(FBSDKGameRequestFilterNone), - @(FBSDKGameRequestFilterAppUsers), - @(FBSDKGameRequestFilterAppNonUsers)] - error:errorRef]; + && [FBSDKShareUtility validateArgumentWithName:@"filters" + value:_filters + isIn:@[@(FBSDKGameRequestFilterNone), + @(FBSDKGameRequestFilterAppUsers), + @(FBSDKGameRequestFilterAppNonUsers)] + error:errorRef]; } -#pragma mark - Equality + #pragma mark - Equality - (NSUInteger)hash { @@ -193,18 +193,18 @@ - (BOOL)isEqual:(id)object - (BOOL)isEqualToGameRequestContent:(FBSDKGameRequestContent *)content { - return (content && - _actionType == content.actionType && - _filters == content.filters && - [FBSDKInternalUtility object:_data isEqualToObject:content.data] && - [FBSDKInternalUtility object:_message isEqualToObject:content.message] && - [FBSDKInternalUtility object:_objectID isEqualToObject:content.objectID] && - [FBSDKInternalUtility object:_recipientSuggestions isEqualToObject:content.recipientSuggestions] && - [FBSDKInternalUtility object:_title isEqualToObject:content.title] && - [FBSDKInternalUtility object:_recipients isEqualToObject:content.recipients]); + return (content + && _actionType == content.actionType + && _filters == content.filters + && [FBSDKInternalUtility object:_data isEqualToObject:content.data] + && [FBSDKInternalUtility object:_message isEqualToObject:content.message] + && [FBSDKInternalUtility object:_objectID isEqualToObject:content.objectID] + && [FBSDKInternalUtility object:_recipientSuggestions isEqualToObject:content.recipientSuggestions] + && [FBSDKInternalUtility object:_title isEqualToObject:content.title] + && [FBSDKInternalUtility object:_recipients isEqualToObject:content.recipients]); } -#pragma mark - NSCoding + #pragma mark - NSCoding + (BOOL)supportsSecureCoding { @@ -238,7 +238,7 @@ - (void)encodeWithCoder:(NSCoder *)encoder [encoder encodeObject:_recipients forKey:FBSDK_APP_REQUEST_CONTENT_TO_KEY]; } -#pragma mark - NSCopying + #pragma mark - NSCopying - (id)copyWithZone:(NSZone *)zone { diff --git a/FBSDKShareKit/FBSDKShareKit/FBSDKGameRequestDialog.m b/FBSDKShareKit/FBSDKShareKit/FBSDKGameRequestDialog.m index 0d099152f7..efb00bb6fe 100644 --- a/FBSDKShareKit/FBSDKShareKit/FBSDKGameRequestDialog.m +++ b/FBSDKShareKit/FBSDKShareKit/FBSDKGameRequestDialog.m @@ -20,16 +20,16 @@ #if !TARGET_OS_TV -#import "FBSDKGameRequestDialog.h" + #import "FBSDKGameRequestDialog.h" -#ifdef FBSDKCOCOAPODS -#import -#else -#import "FBSDKCoreKit+Internal.h" -#endif -#import "FBSDKGameRequestFrictionlessRecipientCache.h" -#import "FBSDKShareConstants.h" -#import "FBSDKShareUtility.h" + #ifdef FBSDKCOCOAPODS + #import + #else + #import "FBSDKCoreKit+Internal.h" + #endif + #import "FBSDKGameRequestFrictionlessRecipientCache.h" + #import "FBSDKShareConstants.h" + #import "FBSDKShareUtility.h" @interface FBSDKGameRequestDialog () @end @@ -40,9 +40,9 @@ @implementation FBSDKGameRequestDialog FBSDKWebDialog *_webDialog; } -#define FBSDK_APP_REQUEST_METHOD_NAME @"apprequests" + #define FBSDK_APP_REQUEST_METHOD_NAME @"apprequests" -#pragma mark - Class Methods + #pragma mark - Class Methods static FBSDKGameRequestFrictionlessRecipientCache *_recipientCache = nil; @@ -68,7 +68,7 @@ + (instancetype)showWithContent:(FBSDKGameRequestContent *)content delegate:(id< return dialog; } -#pragma mark - Object Lifecycle + #pragma mark - Object Lifecycle - (instancetype)init { @@ -85,7 +85,7 @@ - (void)dealloc _webDialog.delegate = nil; } -#pragma mark - Public Methods + #pragma mark - Public Methods - (BOOL)canShow { @@ -138,12 +138,7 @@ - (BOOL)show } } - if (@available(iOS 9, *)) { - [self _launchDialogViaBridgeAPIWithParameters:parameters]; - } else { - _webDialog.parameters = parameters; - [_webDialog show]; - } + [self _launchDialogViaBridgeAPIWithParameters:parameters]; [FBSDKInternalUtility registerTransientObject:self]; return YES; @@ -166,7 +161,7 @@ - (BOOL)validateWithError:(NSError *__autoreleasing *)errorRef return NO; } -#pragma mark - FBSDKWebDialogDelegate + #pragma mark - FBSDKWebDialogDelegate - (void)webDialog:(FBSDKWebDialog *)webDialog didCompleteWithResults:(NSDictionary *)results { @@ -195,7 +190,7 @@ - (void)webDialogDidCancel:(FBSDKWebDialog *)webDialog [self _didCancel]; } -#pragma mark - FBSDKBridgeAPI + #pragma mark - FBSDKBridgeAPI - (BOOL)_launchDialogViaBridgeAPIWithParameters:(NSDictionary *)parameters { @@ -224,8 +219,8 @@ - (BOOL)_launchDialogViaBridgeAPIWithParameters:(NSDictionary *)parameters useSafariViewController:false fromViewController:topMostViewController completionBlock:^(FBSDKBridgeAPIResponse *response) { - [weakSelf _handleBridgeAPIResponse:response]; - }]; + [weakSelf _handleBridgeAPIResponse:response]; + }]; return YES; } @@ -245,7 +240,7 @@ - (void)_handleBridgeAPIResponse:(FBSDKBridgeAPIResponse *)response [self _didCompleteWithResults:response.responseParameters]; } -#pragma mark - Response Handling + #pragma mark - Response Handling - (void)_didCompleteWithResults:(NSDictionary *)results { @@ -262,7 +257,7 @@ - (void)_didCompleteWithResults:(NSDictionary *)results [self _cleanUp]; NSError *error = [FBSDKError errorWithCode:[FBSDKTypeUtility unsignedIntegerValue:results[@"error_code"]] - message:[FBSDKTypeUtility stringValue:results[@"error_message"]]]; + message:[FBSDKTypeUtility stringValue:results[@"error_message"]]]; if (!error.code) { // reformat "to[x]" keys into an array. int counter = 0; @@ -299,7 +294,7 @@ - (void)_didCancel [FBSDKInternalUtility unregisterTransientObject:self]; } -#pragma mark - Helper Methods + #pragma mark - Helper Methods - (void)_cleanUp { @@ -312,41 +307,40 @@ - (void)_handleCompletionWithDialogResults:(NSDictionary *)results error:(NSErro return; } switch (error.code) { - case 0:{ + case 0: { [_delegate gameRequestDialog:self didCompleteWithResults:results]; break; } - case 4201:{ + case 4201: { [_delegate gameRequestDialogDidCancel:self]; break; } - default:{ + default: { [_delegate gameRequestDialog:self didFailWithError:error]; break; } } if (error) { return; - } else { - } + } else {} } - (NSString *)_actionTypeNameForActionType:(FBSDKGameRequestActionType)actionType { switch (actionType) { - case FBSDKGameRequestActionTypeNone:{ + case FBSDKGameRequestActionTypeNone: { return nil; } - case FBSDKGameRequestActionTypeSend:{ + case FBSDKGameRequestActionTypeSend: { return @"send"; } - case FBSDKGameRequestActionTypeAskFor:{ + case FBSDKGameRequestActionTypeAskFor: { return @"askfor"; } - case FBSDKGameRequestActionTypeTurn:{ + case FBSDKGameRequestActionTypeTurn: { return @"turn"; } - default:{ + default: { return nil; } } @@ -355,16 +349,16 @@ - (NSString *)_actionTypeNameForActionType:(FBSDKGameRequestActionType)actionTyp - (NSString *)_filtersNameForFilters:(FBSDKGameRequestFilter)filters { switch (filters) { - case FBSDKGameRequestFilterNone:{ + case FBSDKGameRequestFilterNone: { return nil; } - case FBSDKGameRequestFilterAppUsers:{ + case FBSDKGameRequestFilterAppUsers: { return @"app_users"; } - case FBSDKGameRequestFilterAppNonUsers:{ + case FBSDKGameRequestFilterAppNonUsers: { return @"app_non_users"; } - default:{ + default: { return nil; } } diff --git a/FBSDKShareKit/FBSDKShareKit/FBSDKHashtag.m b/FBSDKShareKit/FBSDKShareKit/FBSDKHashtag.m index a26aad4cd0..71752a5338 100644 --- a/FBSDKShareKit/FBSDKShareKit/FBSDKHashtag.m +++ b/FBSDKShareKit/FBSDKShareKit/FBSDKHashtag.m @@ -19,9 +19,9 @@ #import "FBSDKHashtag.h" #ifdef FBSDKCOCOAPODS -#import + #import #else -#import "FBSDKCoreKit+Internal.h" + #import "FBSDKCoreKit+Internal.h" #endif #define FBSDK_HASHTAG_STRING_KEY @"hashtag" @@ -66,8 +66,8 @@ - (BOOL)isValid NSRange fullString = NSMakeRange(0, _stringRepresentation.length); NSRegularExpression *hashtagRegularExpression = HashtagRegularExpression(); NSUInteger numberOfMatches = [hashtagRegularExpression numberOfMatchesInString:_stringRepresentation - options:0 - range:fullString]; + options:0 + range:fullString]; return numberOfMatches > 0; } @@ -91,8 +91,8 @@ - (BOOL)isEqual:(id)object - (BOOL)isEqualToHashtag:(FBSDKHashtag *)hashtag { - return (hashtag && - [FBSDKInternalUtility object:_stringRepresentation isEqualToObject:hashtag.stringRepresentation]); + return (hashtag + && [FBSDKInternalUtility object:_stringRepresentation isEqualToObject:hashtag.stringRepresentation]); } #pragma mark - NSCoding diff --git a/FBSDKShareKit/FBSDKShareKit/FBSDKMessageDialog.m b/FBSDKShareKit/FBSDKShareKit/FBSDKMessageDialog.m index a437d7c902..bed7b8df51 100644 --- a/FBSDKShareKit/FBSDKShareKit/FBSDKMessageDialog.m +++ b/FBSDKShareKit/FBSDKShareKit/FBSDKMessageDialog.m @@ -20,27 +20,27 @@ #if !TARGET_OS_TV -#import "FBSDKMessageDialog.h" - -#ifdef FBSDKCOCOAPODS -#import -#else -#import "FBSDKCoreKit+Internal.h" -#endif -#import "FBSDKShareCameraEffectContent.h" -#import "FBSDKShareConstants.h" -#import "FBSDKShareDefines.h" -#import "FBSDKShareUtility.h" -#import "FBSDKShareVideoContent.h" - -#define FBSDK_MESSAGE_DIALOG_APP_SCHEME @"fb-messenger-share-api" - -#pragma clang diagnostic push -#pragma clang diagnostic ignored "-Wdeprecated-implementations" + #import "FBSDKMessageDialog.h" + + #ifdef FBSDKCOCOAPODS + #import + #else + #import "FBSDKCoreKit+Internal.h" + #endif + #import "FBSDKShareCameraEffectContent.h" + #import "FBSDKShareConstants.h" + #import "FBSDKShareDefines.h" + #import "FBSDKShareUtility.h" + #import "FBSDKShareVideoContent.h" + + #define FBSDK_MESSAGE_DIALOG_APP_SCHEME @"fb-messenger-share-api" + + #pragma clang diagnostic push + #pragma clang diagnostic ignored "-Wdeprecated-implementations" @implementation FBSDKMessageDialog -#pragma clang diagnostic pop + #pragma clang diagnostic pop -#pragma mark - Class Methods + #pragma mark - Class Methods + (void)initialize { @@ -66,13 +66,13 @@ + (instancetype)showWithContent:(id)content delegate:(id -#else -#import "FBSDKCoreKit+Internal.h" -#endif -#import "FBSDKMessageDialog.h" -#import "FBSDKMessengerIcon.h" + #ifdef FBSDKCOCOAPODS + #import + #else + #import "FBSDKCoreKit+Internal.h" + #endif + #import "FBSDKMessageDialog.h" + #import "FBSDKMessengerIcon.h" @interface FBSDKSendButton () @end -#pragma clang diagnostic push -#pragma clang diagnostic ignored "-Wdeprecated-implementations" + #pragma clang diagnostic push + #pragma clang diagnostic ignored "-Wdeprecated-implementations" @implementation FBSDKSendButton -#pragma clang diagnostic pop + #pragma clang diagnostic pop { FBSDKMessageDialog *_dialog; } -#pragma mark - Properties + #pragma mark - Properties - (id)shareContent { @@ -54,7 +54,7 @@ - (void)setShareContent:(id)shareContent [self checkImplicitlyDisabled]; } -#pragma mark - FBSDKButtonImpressionTracking + #pragma mark - FBSDKButtonImpressionTracking - (NSDictionary *)analyticsParameters { @@ -71,17 +71,21 @@ - (NSString *)impressionTrackingIdentifier return @"send"; } -#pragma mark - FBSDKButton + #pragma mark - FBSDKButton - (void)configureButton { NSString *title = - NSLocalizedStringWithDefaultValue(@"SendButton.Send", @"FacebookSDK", [FBSDKInternalUtility bundleForStrings], - @"Send", - @"The label for FBSDKSendButton"); + NSLocalizedStringWithDefaultValue( + @"SendButton.Send", + @"FacebookSDK", + [FBSDKInternalUtility bundleForStrings], + @"Send", + @"The label for FBSDKSendButton" + ); - UIColor *backgroundColor = [UIColor colorWithRed:0.0 green:132.0/255.0 blue:1.0 alpha:1.0]; - UIColor *highlightedColor = [UIColor colorWithRed:0.0 green:111.0/255.0 blue:1.0 alpha:1.0]; + UIColor *backgroundColor = [UIColor colorWithRed:0.0 green:132.0 / 255.0 blue:1.0 alpha:1.0]; + UIColor *highlightedColor = [UIColor colorWithRed:0.0 green:111.0 / 255.0 blue:1.0 alpha:1.0]; [self configureWithIcon:[[FBSDKMessengerIcon alloc] init] title:title @@ -97,7 +101,7 @@ - (BOOL)isImplicitlyDisabled return !_dialog.canShow || ![_dialog validateWithError:NULL]; } -#pragma mark - Helper Methods + #pragma mark - Helper Methods - (void)_share:(id)sender { diff --git a/FBSDKShareKit/FBSDKShareKit/FBSDKShareButton.m b/FBSDKShareKit/FBSDKShareKit/FBSDKShareButton.m index 0a9856631f..2cdae35535 100644 --- a/FBSDKShareKit/FBSDKShareKit/FBSDKShareButton.m +++ b/FBSDKShareKit/FBSDKShareKit/FBSDKShareButton.m @@ -20,21 +20,21 @@ #if !TARGET_OS_TV -#import "FBSDKShareButton.h" + #import "FBSDKShareButton.h" -#ifdef FBSDKCOCOAPODS -#import -#else -#import "FBSDKCoreKit+Internal.h" -#endif -#import "FBSDKShareDialog.h" + #ifdef FBSDKCOCOAPODS + #import + #else + #import "FBSDKCoreKit+Internal.h" + #endif + #import "FBSDKShareDialog.h" @implementation FBSDKShareButton { FBSDKShareDialog *_dialog; } -#pragma mark - Properties + #pragma mark - Properties - (id)shareContent { @@ -47,7 +47,7 @@ - (void)setShareContent:(id)shareContent [self checkImplicitlyDisabled]; } -#pragma mark - FBSDKButtonImpressionTracking + #pragma mark - FBSDKButtonImpressionTracking - (NSDictionary *)analyticsParameters { @@ -64,14 +64,18 @@ - (NSString *)impressionTrackingIdentifier return @"share"; } -#pragma mark - FBSDKButton + #pragma mark - FBSDKButton - (void)configureButton { NSString *title = - NSLocalizedStringWithDefaultValue(@"ShareButton.Share", @"FacebookSDK", [FBSDKInternalUtility bundleForStrings], - @"Share", - @"The label for FBSDKShareButton"); + NSLocalizedStringWithDefaultValue( + @"ShareButton.Share", + @"FacebookSDK", + [FBSDKInternalUtility bundleForStrings], + @"Share", + @"The label for FBSDKShareButton" + ); [self configureWithIcon:nil title:title @@ -87,7 +91,7 @@ - (BOOL)isImplicitlyDisabled return ![_dialog canShow] || ![_dialog validateWithError:NULL]; } -#pragma mark - Helper Methods + #pragma mark - Helper Methods - (void)_share:(id)sender { diff --git a/FBSDKShareKit/FBSDKShareKit/FBSDKShareCameraEffectContent.m b/FBSDKShareKit/FBSDKShareKit/FBSDKShareCameraEffectContent.m index f7c34ba67e..a13a7c554f 100644 --- a/FBSDKShareKit/FBSDKShareKit/FBSDKShareCameraEffectContent.m +++ b/FBSDKShareKit/FBSDKShareKit/FBSDKShareCameraEffectContent.m @@ -20,17 +20,17 @@ #if !TARGET_OS_TV -#import "FBSDKShareCameraEffectContent.h" - -#import "FBSDKCameraEffectArguments+Internal.h" -#import "FBSDKCameraEffectTextures+Internal.h" -#ifdef FBSDKCOCOAPODS -#import -#else -#import "FBSDKCoreKit+Internal.h" -#endif -#import "FBSDKHashtag.h" -#import "FBSDKShareUtility.h" + #import "FBSDKShareCameraEffectContent.h" + + #import "FBSDKCameraEffectArguments+Internal.h" + #import "FBSDKCameraEffectTextures+Internal.h" + #ifdef FBSDKCOCOAPODS + #import + #else + #import "FBSDKCoreKit+Internal.h" + #endif + #import "FBSDKHashtag.h" + #import "FBSDKShareUtility.h" static NSString *const kFBSDKShareCameraEffectContentEffectIDKey = @"effectID"; static NSString *const kFBSDKShareCameraEffectContentEffectArgumentsKey = @"effectArguments"; @@ -45,7 +45,7 @@ @implementation FBSDKShareCameraEffectContent -#pragma mark - Properties + #pragma mark - Properties @synthesize effectID = _effectID; @synthesize effectArguments = _effectArguments; @@ -58,7 +58,7 @@ @implementation FBSDKShareCameraEffectContent @synthesize pageID = _pageID; @synthesize shareUUID = _shareUUID; -#pragma mark - Initializer + #pragma mark - Initializer - (instancetype)init { @@ -69,15 +69,15 @@ - (instancetype)init return self; } -#pragma mark - FBSDKSharingContent + #pragma mark - FBSDKSharingContent - (NSDictionary *)addParameters:(NSDictionary *)existingParameters bridgeOptions:(FBSDKShareBridgeOptions)bridgeOptions { NSMutableDictionary *updatedParameters = [NSMutableDictionary dictionaryWithDictionary:existingParameters]; [FBSDKTypeUtility dictionary:updatedParameters - setObject:_effectID - forKey:@"effect_id"]; + setObject:_effectID + forKey:@"effect_id"]; NSString *effectArgumentsJSON; if (_effectArguments) { @@ -86,8 +86,8 @@ - (instancetype)init invalidObjectHandler:NULL]; } [FBSDKTypeUtility dictionary:updatedParameters - setObject:effectArgumentsJSON - forKey:@"effect_arguments"]; + setObject:effectArgumentsJSON + forKey:@"effect_arguments"]; NSData *effectTexturesData; if (_effectTextures) { @@ -102,20 +102,20 @@ - (instancetype)init texturesDataDict[key] = imageData; } }]; - #if __IPHONE_OS_VERSION_MIN_REQUIRED >= __IPHONE_11_0 - effectTexturesData = [NSKeyedArchiver archivedDataWithRootObject:texturesDataDict requiringSecureCoding:YES error:NULL]; - #else - effectTexturesData = [NSKeyedArchiver archivedDataWithRootObject:texturesDataDict]; - #endif + #if __IPHONE_OS_VERSION_MIN_REQUIRED >= __IPHONE_11_0 + effectTexturesData = [NSKeyedArchiver archivedDataWithRootObject:texturesDataDict requiringSecureCoding:YES error:NULL]; + #else + effectTexturesData = [NSKeyedArchiver archivedDataWithRootObject:texturesDataDict]; + #endif } [FBSDKTypeUtility dictionary:updatedParameters - setObject:effectTexturesData - forKey:@"effect_textures"]; + setObject:effectTexturesData + forKey:@"effect_textures"]; return updatedParameters; } -#pragma mark - FBSDKSharingScheme + #pragma mark - FBSDKSharingScheme - (NSString *)schemeForMode:(FBSDKShareDialogMode)mode { @@ -128,12 +128,12 @@ - (NSString *)schemeForMode:(FBSDKShareDialogMode)mode return nil; } -#pragma mark - FBSDKSharingValidation + #pragma mark - FBSDKSharingValidation - (BOOL)validateWithOptions:(FBSDKShareBridgeOptions)bridgeOptions error:(NSError *__autoreleasing *)errorRef { if (_effectID.length > 0) { - NSCharacterSet* nonDigitCharacters = [NSCharacterSet decimalDigitCharacterSet].invertedSet; + NSCharacterSet *nonDigitCharacters = [NSCharacterSet decimalDigitCharacterSet].invertedSet; if ([_effectID rangeOfCharacterFromSet:nonDigitCharacters].location != NSNotFound) { if (errorRef != NULL) { *errorRef = [FBSDKError invalidArgumentErrorWithName:@"effectID" @@ -147,7 +147,7 @@ - (BOOL)validateWithOptions:(FBSDKShareBridgeOptions)bridgeOptions error:(NSErro return YES; } -#pragma mark - Equality + #pragma mark - Equality - (NSUInteger)hash { @@ -179,20 +179,20 @@ - (BOOL)isEqual:(id)object - (BOOL)isEqualToShareCameraEffectContent:(FBSDKShareCameraEffectContent *)content { - return (content && - [FBSDKInternalUtility object:_effectID isEqualToObject:content.effectID] && - [FBSDKInternalUtility object:_effectArguments isEqualToObject:content.effectArguments] && - [FBSDKInternalUtility object:_effectTextures isEqualToObject:content.effectTextures] && - [FBSDKInternalUtility object:_contentURL isEqualToObject:content.contentURL] && - [FBSDKInternalUtility object:_hashtag isEqualToObject:content.hashtag] && - [FBSDKInternalUtility object:_peopleIDs isEqualToObject:content.peopleIDs] && - [FBSDKInternalUtility object:_placeID isEqualToObject:content.placeID] && - [FBSDKInternalUtility object:_ref isEqualToObject:content.ref] && - [FBSDKInternalUtility object:_shareUUID isEqualToObject:content.shareUUID] && - [FBSDKInternalUtility object:_pageID isEqualToObject:content.pageID]); + return (content + && [FBSDKInternalUtility object:_effectID isEqualToObject:content.effectID] + && [FBSDKInternalUtility object:_effectArguments isEqualToObject:content.effectArguments] + && [FBSDKInternalUtility object:_effectTextures isEqualToObject:content.effectTextures] + && [FBSDKInternalUtility object:_contentURL isEqualToObject:content.contentURL] + && [FBSDKInternalUtility object:_hashtag isEqualToObject:content.hashtag] + && [FBSDKInternalUtility object:_peopleIDs isEqualToObject:content.peopleIDs] + && [FBSDKInternalUtility object:_placeID isEqualToObject:content.placeID] + && [FBSDKInternalUtility object:_ref isEqualToObject:content.ref] + && [FBSDKInternalUtility object:_shareUUID isEqualToObject:content.shareUUID] + && [FBSDKInternalUtility object:_pageID isEqualToObject:content.pageID]); } -#pragma mark - NSCoding + #pragma mark - NSCoding + (BOOL)supportsSecureCoding { @@ -230,7 +230,7 @@ - (void)encodeWithCoder:(NSCoder *)encoder [encoder encodeObject:_shareUUID forKey:kFBSDKShareCameraEffectContentUUIDKey]; } -#pragma mark - NSCopying + #pragma mark - NSCopying - (id)copyWithZone:(NSZone *)zone { diff --git a/FBSDKShareKit/FBSDKShareKit/FBSDKShareDialog.m b/FBSDKShareKit/FBSDKShareKit/FBSDKShareDialog.m index 9431d09434..881cac690e 100644 --- a/FBSDKShareKit/FBSDKShareKit/FBSDKShareDialog.m +++ b/FBSDKShareKit/FBSDKShareKit/FBSDKShareDialog.m @@ -20,35 +20,35 @@ #if !TARGET_OS_TV -#import "FBSDKShareDialog.h" - -#import - -#ifdef FBSDKCOCOAPODS -#import -#else -#import "FBSDKCoreKit+Internal.h" -#endif -#import "FBSDKShareCameraEffectContent.h" -#import "FBSDKShareConstants.h" -#import "FBSDKShareDefines.h" -#import "FBSDKShareExtension.h" -#import "FBSDKShareLinkContent.h" -#import "FBSDKShareMediaContent.h" -#import "FBSDKSharePhoto.h" -#import "FBSDKSharePhotoContent.h" -#import "FBSDKShareUtility.h" -#import "FBSDKShareVideo.h" -#import "FBSDKShareVideoContent.h" - -#define FBSDK_SHARE_FEED_METHOD_NAME @"feed" -#define FBSDK_SHARE_METHOD_CAMERA_MIN_VERSION @"20170417" -#define FBSDK_SHARE_METHOD_MIN_VERSION @"20130410" -#define FBSDK_SHARE_METHOD_PHOTOS_MIN_VERSION @"20140116" -#define FBSDK_SHARE_METHOD_VIDEO_MIN_VERSION @"20150313" -#define FBSDK_SHARE_METHOD_ATTRIBUTED_SHARE_SHEET_MIN_VERSION @"20150629" -#define FBSDK_SHARE_METHOD_QUOTE_MIN_VERSION @"20160328" -#define FBSDK_SHARE_METHOD_MMP_MIN_VERSION @"20160328" + #import "FBSDKShareDialog.h" + + #import + + #ifdef FBSDKCOCOAPODS + #import + #else + #import "FBSDKCoreKit+Internal.h" + #endif + #import "FBSDKShareCameraEffectContent.h" + #import "FBSDKShareConstants.h" + #import "FBSDKShareDefines.h" + #import "FBSDKShareExtension.h" + #import "FBSDKShareLinkContent.h" + #import "FBSDKShareMediaContent.h" + #import "FBSDKSharePhoto.h" + #import "FBSDKSharePhotoContent.h" + #import "FBSDKShareUtility.h" + #import "FBSDKShareVideo.h" + #import "FBSDKShareVideoContent.h" + + #define FBSDK_SHARE_FEED_METHOD_NAME @"feed" + #define FBSDK_SHARE_METHOD_CAMERA_MIN_VERSION @"20170417" + #define FBSDK_SHARE_METHOD_MIN_VERSION @"20130410" + #define FBSDK_SHARE_METHOD_PHOTOS_MIN_VERSION @"20140116" + #define FBSDK_SHARE_METHOD_VIDEO_MIN_VERSION @"20150313" + #define FBSDK_SHARE_METHOD_ATTRIBUTED_SHARE_SHEET_MIN_VERSION @"20150629" + #define FBSDK_SHARE_METHOD_QUOTE_MIN_VERSION @"20160328" + #define FBSDK_SHARE_METHOD_MMP_MIN_VERSION @"20160328" static inline void FBSDKShareDialogValidateAPISchemeRegisteredForCanOpenUrl() { @@ -66,7 +66,6 @@ static inline void FBSDKShareDialogValidateShareExtensionSchemeRegisteredForCanO }); } - @interface FBSDKShareDialog () @end @@ -76,7 +75,7 @@ @implementation FBSDKShareDialog NSMutableArray *_temporaryFiles; } -#pragma mark - Class Methods + #pragma mark - Class Methods + (void)initialize { @@ -108,7 +107,7 @@ + (instancetype)showFromViewController:(UIViewController *)viewController return dialog; } -#pragma mark - Object Lifecycle + #pragma mark - Object Lifecycle - (void)dealloc { @@ -122,13 +121,13 @@ - (void)dealloc } } -#pragma mark - Properties + #pragma mark - Properties @synthesize delegate = _delegate; @synthesize shareContent = _shareContent; @synthesize shouldFailOnDataError = _shouldFailOnDataError; -#pragma mark - Public Methods + #pragma mark - Public Methods - (BOOL)canShow { @@ -143,13 +142,13 @@ - (BOOL)canShow case FBSDKShareDialogModeBrowser: case FBSDKShareDialogModeFeedBrowser: case FBSDKShareDialogModeFeedWeb: - case FBSDKShareDialogModeWeb:{ + case FBSDKShareDialogModeWeb: { return YES; } - case FBSDKShareDialogModeNative:{ + case FBSDKShareDialogModeNative: { return [self _canShowNative]; } - case FBSDKShareDialogModeShareSheet:{ + case FBSDKShareDialogModeShareSheet: { return [self _canShowShareSheet]; } } @@ -164,31 +163,31 @@ - (BOOL)show if ([self _validateWithError:&error]) { switch (self.mode) { - case FBSDKShareDialogModeAutomatic:{ + case FBSDKShareDialogModeAutomatic: { didShow = [self _showAutomatic:&error]; break; } - case FBSDKShareDialogModeBrowser:{ + case FBSDKShareDialogModeBrowser: { didShow = [self _showBrowser:&error]; break; } - case FBSDKShareDialogModeFeedBrowser:{ + case FBSDKShareDialogModeFeedBrowser: { didShow = [self _showFeedBrowser:&error]; break; } - case FBSDKShareDialogModeFeedWeb:{ + case FBSDKShareDialogModeFeedWeb: { didShow = [self _showFeedWeb:&error]; break; } - case FBSDKShareDialogModeNative:{ + case FBSDKShareDialogModeNative: { didShow = [self _showNativeWithCanShowError:&error validationError:&validationError]; break; } - case FBSDKShareDialogModeShareSheet:{ + case FBSDKShareDialogModeShareSheet: { didShow = [self _showShareSheetWithCanShowError:&error validationError:&validationError]; break; } - case FBSDKShareDialogModeWeb:{ + case FBSDKShareDialogModeWeb: { didShow = [self _showWeb:&error]; break; } @@ -208,7 +207,7 @@ - (BOOL)validateWithError:(NSError **)errorRef return [self _validateWithError:errorRef] && [self _validateFullyCompatibleWithError:errorRef]; } -#pragma mark - FBSDKWebDialogDelegate + #pragma mark - FBSDKWebDialogDelegate - (void)webDialog:(FBSDKWebDialog *)webDialog didCompleteWithResults:(NSDictionary *)results { @@ -223,14 +222,14 @@ - (void)webDialog:(FBSDKWebDialog *)webDialog didCompleteWithResults:(NSDictiona NSError *error = [FBSDKError errorWithDomain:FBSDKShareErrorDomain code:FBSDKShareErrorUnknown userInfo:@{ - FBSDKGraphRequestErrorGraphErrorCodeKey : @(errorCode) - } + FBSDKGraphRequestErrorGraphErrorCodeKey : @(errorCode) + } message:results[@"error_message"] underlyingError:nil]; - [self _handleWebResponseParameters:nil error:error cancelled: NO]; + [self _handleWebResponseParameters:nil error:error cancelled:NO]; } else { // not all web dialogs report cancellation, so assume that the share has completed with no additional information - [self _handleWebResponseParameters:results error:nil cancelled: NO]; + [self _handleWebResponseParameters:results error:nil cancelled:NO]; } [FBSDKInternalUtility unregisterTransientObject:self]; } @@ -255,9 +254,9 @@ - (void)webDialogDidCancel:(FBSDKWebDialog *)webDialog [FBSDKInternalUtility unregisterTransientObject:self]; } -#pragma mark - Helper Methods + #pragma mark - Helper Methods --(BOOL)_isDefaultToShareSheet +- (BOOL)_isDefaultToShareSheet { if ([self.shareContent isKindOfClass:[FBSDKShareCameraEffectContent class]]) { return NO; @@ -266,18 +265,18 @@ -(BOOL)_isDefaultToShareSheet return [configuration.defaultShareMode isEqualToString:@"share_sheet"]; } --(BOOL)_showAutomatic:(NSError **)errorRef +- (BOOL)_showAutomatic:(NSError **)errorRef { BOOL isDefaultToShareSheet = [self _isDefaultToShareSheet]; BOOL useNativeDialog = [self _useNativeDialog]; - return ((isDefaultToShareSheet && [self _showShareSheetWithCanShowError:NULL validationError:errorRef]) || - (useNativeDialog && [self _showNativeWithCanShowError:NULL validationError:errorRef]) || - (!isDefaultToShareSheet && [self _showShareSheetWithCanShowError:NULL validationError:errorRef]) || - [self _showFeedBrowser:errorRef] || - [self _showFeedWeb:errorRef] || - [self _showBrowser:errorRef] || - [self _showWeb:errorRef] || - (!useNativeDialog && [self _showNativeWithCanShowError:NULL validationError:errorRef])); + return ((isDefaultToShareSheet && [self _showShareSheetWithCanShowError:NULL validationError:errorRef]) + || (useNativeDialog && [self _showNativeWithCanShowError:NULL validationError:errorRef]) + || (!isDefaultToShareSheet && [self _showShareSheetWithCanShowError:NULL validationError:errorRef]) + || [self _showFeedBrowser:errorRef] + || [self _showFeedWeb:errorRef] + || [self _showBrowser:errorRef] + || [self _showWeb:errorRef] + || (!useNativeDialog && [self _showNativeWithCanShowError:NULL validationError:errorRef])); } - (void)_loadNativeMethodName:(NSString **)methodNameRef methodVersion:(NSString **)methodVersionRef @@ -337,7 +336,7 @@ - (BOOL)_canShowShareSheet // iOS 11 returns NO for `isAvailableForServiceType` but it will still work NSString *facebookServiceType = fbsdkdfl_SLServiceTypeFacebook(); NSOperatingSystemVersion iOS11Version = { .majorVersion = 11, .minorVersion = 0, .patchVersion = 0 }; - if (![FBSDKInternalUtility isOSRunTimeVersionAtLeast:iOS11Version] && ![composeViewControllerClass isAvailableForServiceType:facebookServiceType]) { + if (![NSProcessInfo.processInfo isOperatingSystemAtLeastVersion:iOS11Version] && ![composeViewControllerClass isAvailableForServiceType:facebookServiceType]) { return NO; } return YES; @@ -345,26 +344,18 @@ - (BOOL)_canShowShareSheet - (BOOL)_canAttributeThroughShareSheet { - NSOperatingSystemVersion iOS8Version = { .majorVersion = 8, .minorVersion = 0, .patchVersion = 0 }; - if (![FBSDKInternalUtility isOSRunTimeVersionAtLeast:iOS8Version]) { - return NO; - } FBSDKShareDialogValidateAPISchemeRegisteredForCanOpenUrl(); NSString *scheme = FBSDK_CANOPENURL_FBAPI; NSString *minimumVersion = FBSDK_SHARE_METHOD_ATTRIBUTED_SHARE_SHEET_MIN_VERSION; NSURLComponents *components = [[NSURLComponents alloc] init]; components.scheme = [scheme stringByAppendingString:minimumVersion]; components.path = @"/"; - return ([[UIApplication sharedApplication] canOpenURL:components.URL] || - [self _canUseFBShareSheet]); + return ([[UIApplication sharedApplication] canOpenURL:components.URL] + || [self _canUseFBShareSheet]); } - (BOOL)_canUseFBShareSheet { - NSOperatingSystemVersion iOS8Version = { .majorVersion = 8, .minorVersion = 0, .patchVersion = 0 }; - if (![FBSDKInternalUtility isOSRunTimeVersionAtLeast:iOS8Version]) { - return NO; - } FBSDKShareDialogValidateShareExtensionSchemeRegisteredForCanOpenUrl(); NSURLComponents *components = [[NSURLComponents alloc] init]; components.scheme = FBSDK_CANOPENURL_SHARE_EXTENSION; @@ -384,10 +375,6 @@ - (BOOL)_canUseMMPInShareSheet - (BOOL)_supportsShareSheetMinimumVersion:(NSString *)minimumVersion { - NSOperatingSystemVersion iOS8Version = { .majorVersion = 8, .minorVersion = 0, .patchVersion = 0 }; - if (![FBSDKInternalUtility isOSRunTimeVersionAtLeast:iOS8Version]) { - return NO; - } FBSDKShareDialogValidateAPISchemeRegisteredForCanOpenUrl(); NSString *scheme = FBSDK_CANOPENURL_FBAPI; NSURLComponents *components = [[NSURLComponents alloc] init]; @@ -497,8 +484,8 @@ - (void)_handleWebResponseParameters:(NSDictionary *)webResponseParameters NSMutableDictionary *results = [[NSMutableDictionary alloc] init]; // the web response comes back with a different payload, so we need to translate it [FBSDKTypeUtility dictionary:results - setObject:webResponseParameters[FBSDK_SHARE_WEB_PARAM_POST_ID_KEY] - forKey:FBSDK_SHARE_RESULT_POST_ID_KEY]; + setObject:webResponseParameters[FBSDK_SHARE_WEB_PARAM_POST_ID_KEY] + forKey:FBSDK_SHARE_RESULT_POST_ID_KEY]; [self _invokeDelegateDidCompleteWithResults:results]; } } @@ -521,10 +508,10 @@ - (BOOL)_showBrowser:(NSError **)errorRef } id shareContent = self.shareContent; if ([shareContent isKindOfClass:[FBSDKSharePhotoContent class]] && [self _photoContentHasAtLeastOneImage:(FBSDKSharePhotoContent *)shareContent]) { - void(^completion)(BOOL, NSString *, NSDictionary *) = ^(BOOL successfullyBuilt, NSString *cMethodName, NSDictionary *cParameters) { + void (^completion)(BOOL, NSString *, NSDictionary *) = ^(BOOL successfullyBuilt, NSString *cMethodName, NSDictionary *cParameters) { if (successfullyBuilt) { FBSDKBridgeAPIResponseBlock completionBlock = ^(FBSDKBridgeAPIResponse *response) { - [self _handleWebResponseParameters:response.responseParameters error:response.error cancelled: response.isCancelled]; + [self _handleWebResponseParameters:response.responseParameters error:response.error cancelled:response.isCancelled]; [FBSDKInternalUtility unregisterTransientObject:self]; }; FBSDKBridgeAPIRequest *request; @@ -535,9 +522,9 @@ - (BOOL)_showBrowser:(NSError **)errorRef parameters:cParameters userInfo:nil]; [[FBSDKBridgeAPI sharedInstance] openBridgeAPIRequest:request - useSafariViewController:[self _useSafariViewController] - fromViewController:self.fromViewController - completionBlock:completionBlock]; + useSafariViewController:[self _useSafariViewController] + fromViewController:self.fromViewController + completionBlock:completionBlock]; } }; @@ -553,7 +540,7 @@ - (BOOL)_showBrowser:(NSError **)errorRef return NO; } FBSDKBridgeAPIResponseBlock completionBlock = ^(FBSDKBridgeAPIResponse *response) { - [self _handleWebResponseParameters:response.responseParameters error:response.error cancelled: response.isCancelled]; + [self _handleWebResponseParameters:response.responseParameters error:response.error cancelled:response.isCancelled]; [FBSDKInternalUtility unregisterTransientObject:self]; }; FBSDKBridgeAPIRequest *request; @@ -564,9 +551,9 @@ - (BOOL)_showBrowser:(NSError **)errorRef parameters:parameters userInfo:nil]; [[FBSDKBridgeAPI sharedInstance] openBridgeAPIRequest:request - useSafariViewController:[self _useSafariViewController] - fromViewController:self.fromViewController - completionBlock:completionBlock]; + useSafariViewController:[self _useSafariViewController] + fromViewController:self.fromViewController + completionBlock:completionBlock]; } return YES; } @@ -590,9 +577,9 @@ - (BOOL)_showFeedBrowser:(NSError **)errorRef parameters:parameters userInfo:nil]; [[FBSDKBridgeAPI sharedInstance] openBridgeAPIRequest:request - useSafariViewController:[self _useSafariViewController] - fromViewController:self.fromViewController - completionBlock:completionBlock]; + useSafariViewController:[self _useSafariViewController] + fromViewController:self.fromViewController + completionBlock:completionBlock]; return YES; } @@ -645,31 +632,31 @@ - (BOOL)_showNativeWithCanShowError:(NSError **)canShowErrorRef validationError: FBSDKBridgeAPIResponseBlock completionBlock = ^(FBSDKBridgeAPIResponse *response) { if (response.error.code == FBSDKErrorAppVersionUnsupported) { NSError *fallbackError; - if ([self _showShareSheetWithCanShowError:NULL validationError:&fallbackError] || - [self _showFeedBrowser:&fallbackError]) { + if ([self _showShareSheetWithCanShowError:NULL validationError:&fallbackError] + || [self _showFeedBrowser:&fallbackError]) { return; } } NSDictionary *responseParameters = response.responseParameters; NSString *completionGesture = responseParameters[FBSDK_SHARE_RESULT_COMPLETION_GESTURE_KEY]; - if ([completionGesture isEqualToString:FBSDK_SHARE_RESULT_COMPLETION_GESTURE_VALUE_CANCEL] || - response.isCancelled) { + if ([completionGesture isEqualToString:FBSDK_SHARE_RESULT_COMPLETION_GESTURE_VALUE_CANCEL] + || response.isCancelled) { [self _invokeDelegateDidCancel]; } else if (response.error) { [self _invokeDelegateDidFailWithError:response.error]; } else { NSMutableDictionary *results = [[NSMutableDictionary alloc] init]; [FBSDKTypeUtility dictionary:results - setObject:responseParameters[FBSDK_SHARE_RESULT_POST_ID_KEY] - forKey:FBSDK_SHARE_RESULT_POST_ID_KEY]; + setObject:responseParameters[FBSDK_SHARE_RESULT_POST_ID_KEY] + forKey:FBSDK_SHARE_RESULT_POST_ID_KEY]; [self _invokeDelegateDidCompleteWithResults:results]; } [FBSDKInternalUtility unregisterTransientObject:self]; }; [[FBSDKBridgeAPI sharedInstance] openBridgeAPIRequest:request - useSafariViewController:[self _useSafariViewController] - fromViewController:self.fromViewController - completionBlock:completionBlock]; + useSafariViewController:[self _useSafariViewController] + fromViewController:self.fromViewController + completionBlock:completionBlock]; return YES; } @@ -729,11 +716,11 @@ - (BOOL)_showShareSheetWithCanShowError:(NSError **)canShowErrorRef validationEr } composeViewController.completionHandler = ^(SLComposeViewControllerResult result) { switch (result) { - case SLComposeViewControllerResultCancelled:{ + case SLComposeViewControllerResultCancelled: { [self _invokeDelegateDidCancel]; break; } - case SLComposeViewControllerResultDone:{ + case SLComposeViewControllerResultDone: { [self _invokeDelegateDidCompleteWithResults:@{}]; break; } @@ -791,12 +778,11 @@ - (BOOL)_validateWithError:(NSError **)errorRef } if (self.shareContent) { - if ([self.shareContent isKindOfClass:[FBSDKShareCameraEffectContent class]] || - [self.shareContent isKindOfClass:[FBSDKShareLinkContent class]] || - [self.shareContent isKindOfClass:[FBSDKShareMediaContent class]] || - [self.shareContent isKindOfClass:[FBSDKSharePhotoContent class]] || - [self.shareContent isKindOfClass:[FBSDKShareVideoContent class]]) { - } else { + if ([self.shareContent isKindOfClass:[FBSDKShareCameraEffectContent class]] + || [self.shareContent isKindOfClass:[FBSDKShareLinkContent class]] + || [self.shareContent isKindOfClass:[FBSDKShareMediaContent class]] + || [self.shareContent isKindOfClass:[FBSDKSharePhotoContent class]] + || [self.shareContent isKindOfClass:[FBSDKShareVideoContent class]]) {} else { if (errorRef != NULL) { NSString *message = [NSString stringWithFormat:@"Share dialog does not support %@.", NSStringFromClass(self.shareContent.class)]; @@ -815,27 +801,27 @@ - (BOOL)_validateWithError:(NSError **)errorRef } switch (self.mode) { - case FBSDKShareDialogModeAutomatic:{ + case FBSDKShareDialogModeAutomatic: { return ( - ([self _canShowNative] && [self _validateShareContentForNative:errorRef]) || - ([self _canShowShareSheet] && [self _validateShareContentForShareSheet:errorRef]) || - [self _validateShareContentForFeed:errorRef] || - [self _validateShareContentForBrowserWithOptions:FBSDKShareBridgeOptionsDefault error:errorRef]); + ([self _canShowNative] && [self _validateShareContentForNative:errorRef]) + || ([self _canShowShareSheet] && [self _validateShareContentForShareSheet:errorRef]) + || [self _validateShareContentForFeed:errorRef] + || [self _validateShareContentForBrowserWithOptions:FBSDKShareBridgeOptionsDefault error:errorRef]); } - case FBSDKShareDialogModeNative:{ + case FBSDKShareDialogModeNative: { return [self _validateShareContentForNative:errorRef]; } - case FBSDKShareDialogModeShareSheet:{ + case FBSDKShareDialogModeShareSheet: { return [self _validateShareContentForShareSheet:errorRef]; } - case FBSDKShareDialogModeBrowser:{ + case FBSDKShareDialogModeBrowser: { return [self _validateShareContentForBrowserWithOptions:FBSDKShareBridgeOptionsDefault error:errorRef]; } - case FBSDKShareDialogModeWeb:{ + case FBSDKShareDialogModeWeb: { return [self _validateShareContentForBrowserWithOptions:FBSDKShareBridgeOptionsPhotoImageURL error:errorRef]; } case FBSDKShareDialogModeFeedBrowser: - case FBSDKShareDialogModeFeedWeb:{ + case FBSDKShareDialogModeFeedWeb: { return [self _validateShareContentForFeed:errorRef]; } } @@ -865,9 +851,9 @@ - (BOOL)_validateFullyCompatibleWithError:(NSError **)errorRef id shareContent = self.shareContent; if ([shareContent isKindOfClass:[FBSDKShareLinkContent class]]) { FBSDKShareLinkContent *shareLinkContent = (FBSDKShareLinkContent *)shareContent; - if (shareLinkContent.quote.length > 0 && - self.mode == FBSDKShareDialogModeShareSheet && - ![self _canUseQuoteInShareSheet]) { + if (shareLinkContent.quote.length > 0 + && self.mode == FBSDKShareDialogModeShareSheet + && ![self _canUseQuoteInShareSheet]) { if ((errorRef != NULL) && !*errorRef) { *errorRef = [FBSDKError invalidArgumentErrorWithDomain:FBSDKShareErrorDomain name:@"shareContent" @@ -1029,12 +1015,12 @@ - (BOOL)_validateShareContentForShareSheet:(NSError **)errorRef return NO; } } else if ([shareContent isKindOfClass:[FBSDKShareVideoContent class]]) { - return ([self _canUseFBShareSheet] && - [(FBSDKShareVideoContent *)shareContent validateWithOptions:FBSDKShareBridgeOptionsDefault error:errorRef]); + return ([self _canUseFBShareSheet] + && [(FBSDKShareVideoContent *)shareContent validateWithOptions:FBSDKShareBridgeOptionsDefault error:errorRef]); } else if ([shareContent isKindOfClass:[FBSDKShareMediaContent class]]) { - return ([self _canUseFBShareSheet] && - [self _validateShareMediaContentAvailability:shareContent error:errorRef] && - [(FBSDKShareMediaContent *)shareContent validateWithOptions:FBSDKShareBridgeOptionsDefault error:errorRef]); + return ([self _canUseFBShareSheet] + && [self _validateShareMediaContentAvailability:shareContent error:errorRef] + && [(FBSDKShareMediaContent *)shareContent validateWithOptions:FBSDKShareBridgeOptionsDefault error:errorRef]); } else if ([shareContent isKindOfClass:[FBSDKShareLinkContent class]]) { return YES; } else { @@ -1054,9 +1040,9 @@ - (BOOL)_validateShareContentForShareSheet:(NSError **)errorRef - (BOOL)_validateShareMediaContentAvailability:(FBSDKShareMediaContent *)shareContent error:(NSError **)errorRef { - if ([FBSDKShareUtility shareMediaContentContainsPhotosAndVideos:shareContent] && - self.mode == FBSDKShareDialogModeShareSheet && - ![self _canUseMMPInShareSheet]) { + if ([FBSDKShareUtility shareMediaContentContainsPhotosAndVideos:shareContent] + && self.mode == FBSDKShareDialogModeShareSheet + && ![self _canUseMMPInShareSheet]) { if ((errorRef != NULL) && !*errorRef) { *errorRef = [FBSDKError invalidArgumentErrorWithDomain:FBSDKShareErrorDomain name:@"shareContent" @@ -1070,9 +1056,9 @@ - (BOOL)_validateShareMediaContentAvailability:(FBSDKShareMediaContent *)shareCo - (void)_invokeDelegateDidCancel { - NSDictionary * parameters = @{ - FBSDKAppEventParameterDialogOutcome : FBSDKAppEventsDialogOutcomeValue_Cancelled, - }; + NSDictionary *parameters = @{ + FBSDKAppEventParameterDialogOutcome : FBSDKAppEventsDialogOutcomeValue_Cancelled, + }; [FBSDKAppEvents logInternalEvent:FBSDKAppEventNameFBSDKEventShareDialogResult parameters:parameters @@ -1084,9 +1070,9 @@ - (void)_invokeDelegateDidCancel - (void)_invokeDelegateDidCompleteWithResults:(NSDictionary *)results { - NSDictionary * parameters = @{ - FBSDKAppEventParameterDialogOutcome : FBSDKAppEventsDialogOutcomeValue_Completed - }; + NSDictionary *parameters = @{ + FBSDKAppEventParameterDialogOutcome : FBSDKAppEventsDialogOutcomeValue_Completed + }; [FBSDKAppEvents logInternalEvent:FBSDKAppEventNameFBSDKEventShareDialogResult parameters:parameters @@ -1098,10 +1084,10 @@ - (void)_invokeDelegateDidCompleteWithResults:(NSDictionary *)results - (void)_invokeDelegateDidFailWithError:(NSError *)error { - NSDictionary * parameters = @{ - FBSDKAppEventParameterDialogOutcome : FBSDKAppEventsDialogOutcomeValue_Failed, - FBSDKAppEventParameterDialogErrorMessage : [NSString stringWithFormat:@"%@", error] - }; + NSDictionary *parameters = @{ + FBSDKAppEventParameterDialogOutcome : FBSDKAppEventsDialogOutcomeValue_Failed, + FBSDKAppEventParameterDialogErrorMessage : [NSString stringWithFormat:@"%@", error] + }; [FBSDKAppEvents logInternalEvent:FBSDKAppEventNameFBSDKEventShareDialogResult parameters:parameters @@ -1129,10 +1115,9 @@ - (void)_logDialogShow } NSDictionary *parameters = @{ - FBSDKAppEventParameterDialogMode : shareMode, - FBSDKAppEventParameterDialogShareContentType : contentType, - - }; + FBSDKAppEventParameterDialogMode : shareMode, + FBSDKAppEventParameterDialogShareContentType : contentType, + }; [FBSDKAppEvents logInternalEvent:FBSDKAppEventNameFBSDKEventShareDialogShow parameters:parameters diff --git a/FBSDKShareKit/FBSDKShareKit/FBSDKShareDialogMode.m b/FBSDKShareKit/FBSDKShareKit/FBSDKShareDialogMode.m index 0e0abb0cab..4603e70786 100644 --- a/FBSDKShareKit/FBSDKShareKit/FBSDKShareDialogMode.m +++ b/FBSDKShareKit/FBSDKShareKit/FBSDKShareDialogMode.m @@ -19,36 +19,36 @@ #import "FBSDKShareDialogMode.h" #ifdef FBSDKCOCOAPODS -#import + #import #else -#import "FBSDKCoreKit+Internal.h" + #import "FBSDKCoreKit+Internal.h" #endif NSString *NSStringFromFBSDKShareDialogMode(FBSDKShareDialogMode dialogMode) { switch (dialogMode) { - case FBSDKShareDialogModeAutomatic:{ + case FBSDKShareDialogModeAutomatic: { return FBSDKAppEventsDialogShareModeAutomatic; } - case FBSDKShareDialogModeBrowser:{ + case FBSDKShareDialogModeBrowser: { return FBSDKAppEventsDialogShareModeBrowser; } - case FBSDKShareDialogModeNative:{ + case FBSDKShareDialogModeNative: { return FBSDKAppEventsDialogShareModeNative; } - case FBSDKShareDialogModeShareSheet:{ + case FBSDKShareDialogModeShareSheet: { return FBSDKAppEventsDialogShareModeShareSheet; } - case FBSDKShareDialogModeWeb:{ + case FBSDKShareDialogModeWeb: { return FBSDKAppEventsDialogShareModeWeb; } case FBSDKShareDialogModeFeedBrowser: { return FBSDKAppEventsDialogShareModeFeedBrowser; } - case FBSDKShareDialogModeFeedWeb:{ + case FBSDKShareDialogModeFeedWeb: { return FBSDKAppEventsDialogShareModeFeedWeb; } - default:{ + default: { return FBSDKAppEventsDialogShareModeUnknown; } } diff --git a/FBSDKShareKit/FBSDKShareKit/FBSDKShareKit.h b/FBSDKShareKit/FBSDKShareKit/FBSDKShareKit.h index 61ebccdc5d..cca91f95e5 100644 --- a/FBSDKShareKit/FBSDKShareKit/FBSDKShareKit.h +++ b/FBSDKShareKit/FBSDKShareKit/FBSDKShareKit.h @@ -16,8 +16,6 @@ // 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 "TargetConditionals.h" - #import "FBSDKHashtag.h" #import "FBSDKShareConstants.h" #import "FBSDKShareLinkContent.h" @@ -28,21 +26,19 @@ #import "FBSDKShareVideoContent.h" #import "FBSDKSharing.h" #import "FBSDKSharingContent.h" +#import "TargetConditionals.h" #if !TARGET_OS_TV -#import "FBSDKAppGroupContent.h" -#import "FBSDKAppInviteContent.h" -#import "FBSDKGameRequestContent.h" -#import "FBSDKGameRequestDialog.h" -#import "FBSDKLiking.h" -#import "FBSDKLikeObjectType.h" -#import "FBSDKMessageDialog.h" -#import "FBSDKShareButton.h" -#import "FBSDKShareCameraEffectContent.h" -#import "FBSDKShareDialog.h" -#import "FBSDKShareDialogMode.h" -#import "FBSDKSendButton.h" -#else -#import "FBSDKDeviceShareViewController.h" -#import "FBSDKDeviceShareButton.h" + #import "FBSDKAppGroupContent.h" + #import "FBSDKAppInviteContent.h" + #import "FBSDKGameRequestContent.h" + #import "FBSDKGameRequestDialog.h" + #import "FBSDKLikeObjectType.h" + #import "FBSDKLiking.h" + #import "FBSDKMessageDialog.h" + #import "FBSDKSendButton.h" + #import "FBSDKShareButton.h" + #import "FBSDKShareCameraEffectContent.h" + #import "FBSDKShareDialog.h" + #import "FBSDKShareDialogMode.h" #endif diff --git a/FBSDKShareKit/FBSDKShareKit/FBSDKShareLinkContent.m b/FBSDKShareKit/FBSDKShareKit/FBSDKShareLinkContent.m index 45ebe8337b..2753ec03cf 100644 --- a/FBSDKShareKit/FBSDKShareKit/FBSDKShareLinkContent.m +++ b/FBSDKShareKit/FBSDKShareKit/FBSDKShareLinkContent.m @@ -19,9 +19,9 @@ #import "FBSDKShareLinkContent.h" #ifdef FBSDKCOCOAPODS -#import + #import #else -#import "FBSDKCoreKit+Internal.h" + #import "FBSDKCoreKit+Internal.h" #endif #import "FBSDKHashtag.h" #import "FBSDKShareUtility.h" @@ -125,15 +125,15 @@ - (BOOL)isEqual:(id)object - (BOOL)isEqualToShareLinkContent:(FBSDKShareLinkContent *)content { - return (content && - [FBSDKInternalUtility object:_contentURL isEqualToObject:content.contentURL] && - [FBSDKInternalUtility object:_hashtag isEqualToObject:content.hashtag] && - [FBSDKInternalUtility object:_peopleIDs isEqualToObject:content.peopleIDs] && - [FBSDKInternalUtility object:_placeID isEqualToObject:content.placeID] && - [FBSDKInternalUtility object:_ref isEqualToObject:content.ref] && - [FBSDKInternalUtility object:_pageID isEqualToObject:content.pageID] && - [FBSDKInternalUtility object:_shareUUID isEqualToObject:content.shareUUID]) && - [FBSDKInternalUtility object:_quote isEqualToObject:content.quote]; + return (content + && [FBSDKInternalUtility object:_contentURL isEqualToObject:content.contentURL] + && [FBSDKInternalUtility object:_hashtag isEqualToObject:content.hashtag] + && [FBSDKInternalUtility object:_peopleIDs isEqualToObject:content.peopleIDs] + && [FBSDKInternalUtility object:_placeID isEqualToObject:content.placeID] + && [FBSDKInternalUtility object:_ref isEqualToObject:content.ref] + && [FBSDKInternalUtility object:_pageID isEqualToObject:content.pageID] + && [FBSDKInternalUtility object:_shareUUID isEqualToObject:content.shareUUID]) + && [FBSDKInternalUtility object:_quote isEqualToObject:content.quote]; } #pragma mark - NSCoding diff --git a/FBSDKShareKit/FBSDKShareKit/FBSDKShareMediaContent.m b/FBSDKShareKit/FBSDKShareKit/FBSDKShareMediaContent.m index e48dcd088f..59c4d9cab3 100644 --- a/FBSDKShareKit/FBSDKShareKit/FBSDKShareMediaContent.m +++ b/FBSDKShareKit/FBSDKShareKit/FBSDKShareMediaContent.m @@ -19,9 +19,9 @@ #import "FBSDKShareMediaContent.h" #ifdef FBSDKCOCOAPODS -#import + #import #else -#import "FBSDKCoreKit+Internal.h" + #import "FBSDKCoreKit+Internal.h" #endif #import "FBSDKHashtag.h" #import "FBSDKShareConstants.h" @@ -126,7 +126,6 @@ - (BOOL)validateWithOptions:(FBSDKShareBridgeOptions)bridgeOptions error:(NSErro if (![video validateWithOptions:bridgeOptions error:errorRef]) { return NO; } - } else { if (errorRef != NULL) { *errorRef = [FBSDKError invalidArgumentErrorWithDomain:FBSDKShareErrorDomain @@ -170,15 +169,15 @@ - (BOOL)isEqual:(id)object - (BOOL)isEqualToShareMediaContent:(FBSDKShareMediaContent *)content { - return (content && - [FBSDKInternalUtility object:_contentURL isEqualToObject:content.contentURL] && - [FBSDKInternalUtility object:_hashtag isEqualToObject:content.hashtag] && - [FBSDKInternalUtility object:_peopleIDs isEqualToObject:content.peopleIDs] && - [FBSDKInternalUtility object:_media isEqualToObject:content.media] && - [FBSDKInternalUtility object:_placeID isEqualToObject:content.placeID] && - [FBSDKInternalUtility object:_ref isEqualToObject:content.ref] && - [FBSDKInternalUtility object:_shareUUID isEqualToObject:content.shareUUID] && - [FBSDKInternalUtility object:_pageID isEqualToObject:content.pageID]); + return (content + && [FBSDKInternalUtility object:_contentURL isEqualToObject:content.contentURL] + && [FBSDKInternalUtility object:_hashtag isEqualToObject:content.hashtag] + && [FBSDKInternalUtility object:_peopleIDs isEqualToObject:content.peopleIDs] + && [FBSDKInternalUtility object:_media isEqualToObject:content.media] + && [FBSDKInternalUtility object:_placeID isEqualToObject:content.placeID] + && [FBSDKInternalUtility object:_ref isEqualToObject:content.ref] + && [FBSDKInternalUtility object:_shareUUID isEqualToObject:content.shareUUID] + && [FBSDKInternalUtility object:_pageID isEqualToObject:content.pageID]); } #pragma mark - NSCoding diff --git a/FBSDKShareKit/FBSDKShareKit/FBSDKSharePhoto.m b/FBSDKShareKit/FBSDKShareKit/FBSDKSharePhoto.m index bb853bf894..a3a61aada4 100644 --- a/FBSDKShareKit/FBSDKShareKit/FBSDKSharePhoto.m +++ b/FBSDKShareKit/FBSDKShareKit/FBSDKSharePhoto.m @@ -21,9 +21,9 @@ #import #ifdef FBSDKCOCOAPODS -#import + #import #else -#import "FBSDKCoreKit+Internal.h" + #import "FBSDKCoreKit+Internal.h" #endif #import "FBSDKShareConstants.h" @@ -111,12 +111,12 @@ - (BOOL)isEqual:(id)object - (BOOL)isEqualToSharePhoto:(FBSDKSharePhoto *)photo { - return (photo && - (_userGenerated == photo.userGenerated) && - [FBSDKInternalUtility object:_image isEqualToObject:photo.image] && - [FBSDKInternalUtility object:_imageURL isEqualToObject:photo.imageURL] && - [FBSDKInternalUtility object:_photoAsset isEqualToObject:photo.photoAsset] && - [FBSDKInternalUtility object:_caption isEqualToObject:photo.caption]); + return (photo + && (_userGenerated == photo.userGenerated) + && [FBSDKInternalUtility object:_image isEqualToObject:photo.image] + && [FBSDKInternalUtility object:_imageURL isEqualToObject:photo.imageURL] + && [FBSDKInternalUtility object:_photoAsset isEqualToObject:photo.photoAsset] + && [FBSDKInternalUtility object:_caption isEqualToObject:photo.caption]); } #pragma mark - FBSDKSharingValidation diff --git a/FBSDKShareKit/FBSDKShareKit/FBSDKSharePhotoContent.m b/FBSDKShareKit/FBSDKShareKit/FBSDKSharePhotoContent.m index 4869bb091c..e66fce116a 100644 --- a/FBSDKShareKit/FBSDKShareKit/FBSDKSharePhotoContent.m +++ b/FBSDKShareKit/FBSDKShareKit/FBSDKSharePhotoContent.m @@ -21,9 +21,9 @@ #import #ifdef FBSDKCOCOAPODS -#import + #import #else -#import "FBSDKCoreKit+Internal.h" + #import "FBSDKCoreKit+Internal.h" #endif #import "FBSDKHashtag.h" #import "FBSDKSharePhoto.h" @@ -119,8 +119,8 @@ - (void)setPhotos:(NSArray *)photos } if (images.count > 0) { [FBSDKTypeUtility dictionary:updatedParameters - setObject:images - forKey:@"photos"]; + setObject:images + forKey:@"photos"]; } return updatedParameters; @@ -171,15 +171,15 @@ - (BOOL)isEqual:(id)object - (BOOL)isEqualToSharePhotoContent:(FBSDKSharePhotoContent *)content { - return (content && - [FBSDKInternalUtility object:_contentURL isEqualToObject:content.contentURL] && - [FBSDKInternalUtility object:_hashtag isEqualToObject:content.hashtag] && - [FBSDKInternalUtility object:_peopleIDs isEqualToObject:content.peopleIDs] && - [FBSDKInternalUtility object:_photos isEqualToObject:content.photos] && - [FBSDKInternalUtility object:_placeID isEqualToObject:content.placeID] && - [FBSDKInternalUtility object:_ref isEqualToObject:content.ref] && - [FBSDKInternalUtility object:_shareUUID isEqualToObject:content.shareUUID] && - [FBSDKInternalUtility object:_pageID isEqualToObject:content.pageID]); + return (content + && [FBSDKInternalUtility object:_contentURL isEqualToObject:content.contentURL] + && [FBSDKInternalUtility object:_hashtag isEqualToObject:content.hashtag] + && [FBSDKInternalUtility object:_peopleIDs isEqualToObject:content.peopleIDs] + && [FBSDKInternalUtility object:_photos isEqualToObject:content.photos] + && [FBSDKInternalUtility object:_placeID isEqualToObject:content.placeID] + && [FBSDKInternalUtility object:_ref isEqualToObject:content.ref] + && [FBSDKInternalUtility object:_shareUUID isEqualToObject:content.shareUUID] + && [FBSDKInternalUtility object:_pageID isEqualToObject:content.pageID]); } #pragma mark - NSCoding diff --git a/FBSDKShareKit/FBSDKShareKit/FBSDKShareVideo.m b/FBSDKShareKit/FBSDKShareKit/FBSDKShareVideo.m index 72aeae494b..c5e9153bec 100644 --- a/FBSDKShareKit/FBSDKShareKit/FBSDKShareVideo.m +++ b/FBSDKShareKit/FBSDKShareKit/FBSDKShareVideo.m @@ -19,9 +19,9 @@ #import "FBSDKShareVideo.h" #ifdef FBSDKCOCOAPODS -#import + #import #else -#import "FBSDKCoreKit+Internal.h" + #import "FBSDKCoreKit+Internal.h" #endif #import "FBSDKShareConstants.h" #import "FBSDKSharePhoto.h" @@ -133,11 +133,11 @@ - (BOOL)isEqual:(id)object - (BOOL)isEqualToShareVideo:(FBSDKShareVideo *)video { - return (video && - [FBSDKInternalUtility object:_data isEqualToObject:video.data] && - [FBSDKInternalUtility object:_videoAsset isEqualToObject:video.videoAsset] && - [FBSDKInternalUtility object:_videoURL isEqualToObject:video.videoURL] && - [FBSDKInternalUtility object:_previewPhoto isEqualToObject:video.previewPhoto]); + return (video + && [FBSDKInternalUtility object:_data isEqualToObject:video.data] + && [FBSDKInternalUtility object:_videoAsset isEqualToObject:video.videoAsset] + && [FBSDKInternalUtility object:_videoURL isEqualToObject:video.videoURL] + && [FBSDKInternalUtility object:_previewPhoto isEqualToObject:video.previewPhoto]); } #pragma mark - FBSDKSharingValidation diff --git a/FBSDKShareKit/FBSDKShareKit/FBSDKShareVideoContent.m b/FBSDKShareKit/FBSDKShareKit/FBSDKShareVideoContent.m index 66afc3810b..7521820928 100644 --- a/FBSDKShareKit/FBSDKShareKit/FBSDKShareVideoContent.m +++ b/FBSDKShareKit/FBSDKShareKit/FBSDKShareVideoContent.m @@ -21,9 +21,9 @@ #import #ifdef FBSDKCOCOAPODS -#import + #import #else -#import "FBSDKCoreKit+Internal.h" + #import "FBSDKCoreKit+Internal.h" #endif #import "FBSDKHashtag.h" #import "FBSDKShareUtility.h" @@ -83,47 +83,47 @@ - (void)setPeopleIDs:(NSArray *)peopleIDs if (bridgeOptions & FBSDKShareBridgeOptionsVideoAsset) { // bridge the PHAsset.localIdentifier [FBSDKTypeUtility dictionary:videoParameters - setObject:_video.videoAsset.localIdentifier - forKey:@"assetIdentifier"]; + setObject:_video.videoAsset.localIdentifier + forKey:@"assetIdentifier"]; } else { // bridge the legacy "assets-library" URL from AVAsset [FBSDKTypeUtility dictionary:videoParameters - setObject:_video.videoAsset.videoURL - forKey:@"assetURL"]; + setObject:_video.videoAsset.videoURL + forKey:@"assetURL"]; } } else if (_video.data) { if (bridgeOptions & FBSDKShareBridgeOptionsVideoData) { // bridge the data [FBSDKTypeUtility dictionary:videoParameters - setObject:_video.data - forKey:@"data"]; + setObject:_video.data + forKey:@"data"]; } } else if (_video.videoURL) { if ([_video.videoURL.scheme.lowercaseString isEqualToString:@"assets-library"]) { // bridge the legacy "assets-library" URL [FBSDKTypeUtility dictionary:videoParameters - setObject:_video.videoURL - forKey:@"assetURL"]; + setObject:_video.videoURL + forKey:@"assetURL"]; } else if (_video.videoURL.isFileURL) { if (bridgeOptions & FBSDKShareBridgeOptionsVideoData) { // load the contents of the file and bridge the data NSData *data = [NSData dataWithContentsOfURL:_video.videoURL options:NSDataReadingMappedIfSafe error:NULL]; [FBSDKTypeUtility dictionary:videoParameters - setObject:data - forKey:@"data"]; + setObject:data + forKey:@"data"]; } } } if (_video.previewPhoto) { [FBSDKTypeUtility dictionary:videoParameters - setObject:[FBSDKShareUtility convertPhoto:_video.previewPhoto] - forKey:@"previewPhoto"]; + setObject:[FBSDKShareUtility convertPhoto:_video.previewPhoto] + forKey:@"previewPhoto"]; } [FBSDKTypeUtility dictionary:updatedParameters - setObject:videoParameters - forKey:@"video"]; + setObject:videoParameters + forKey:@"video"]; return updatedParameters; } @@ -168,15 +168,15 @@ - (BOOL)isEqual:(id)object - (BOOL)isEqualToShareVideoContent:(FBSDKShareVideoContent *)content { - return (content && - [FBSDKInternalUtility object:_contentURL isEqualToObject:content.contentURL] && - [FBSDKInternalUtility object:_hashtag isEqualToObject:content.hashtag] && - [FBSDKInternalUtility object:_peopleIDs isEqualToObject:content.peopleIDs] && - [FBSDKInternalUtility object:_placeID isEqualToObject:content.placeID] && - [FBSDKInternalUtility object:_ref isEqualToObject:content.ref] && - [FBSDKInternalUtility object:_pageID isEqualToObject:content.pageID] && - [FBSDKInternalUtility object:_shareUUID isEqualToObject:content.shareUUID] && - [FBSDKInternalUtility object:_video isEqualToObject:content.video]); + return (content + && [FBSDKInternalUtility object:_contentURL isEqualToObject:content.contentURL] + && [FBSDKInternalUtility object:_hashtag isEqualToObject:content.hashtag] + && [FBSDKInternalUtility object:_peopleIDs isEqualToObject:content.peopleIDs] + && [FBSDKInternalUtility object:_placeID isEqualToObject:content.placeID] + && [FBSDKInternalUtility object:_ref isEqualToObject:content.ref] + && [FBSDKInternalUtility object:_pageID isEqualToObject:content.pageID] + && [FBSDKInternalUtility object:_shareUUID isEqualToObject:content.shareUUID] + && [FBSDKInternalUtility object:_video isEqualToObject:content.video]); } #pragma mark - NSCoding diff --git a/FBSDKShareKit/FBSDKShareKit/Internal/FBSDKCameraEffectArguments+Internal.h b/FBSDKShareKit/FBSDKShareKit/Internal/FBSDKCameraEffectArguments+Internal.h index dd93c7861f..2419720b89 100644 --- a/FBSDKShareKit/FBSDKShareKit/Internal/FBSDKCameraEffectArguments+Internal.h +++ b/FBSDKShareKit/FBSDKShareKit/Internal/FBSDKCameraEffectArguments+Internal.h @@ -20,7 +20,7 @@ #if !TARGET_OS_TV -#import "FBSDKCameraEffectArguments.h" + #import "FBSDKCameraEffectArguments.h" @interface FBSDKCameraEffectArguments () diff --git a/FBSDKShareKit/FBSDKShareKit/Internal/FBSDKCameraEffectTextures+Internal.h b/FBSDKShareKit/FBSDKShareKit/Internal/FBSDKCameraEffectTextures+Internal.h index 9abab1d7c2..e5e1b90f66 100644 --- a/FBSDKShareKit/FBSDKShareKit/Internal/FBSDKCameraEffectTextures+Internal.h +++ b/FBSDKShareKit/FBSDKShareKit/Internal/FBSDKCameraEffectTextures+Internal.h @@ -20,7 +20,7 @@ #if !TARGET_OS_TV -#import "FBSDKCameraEffectTextures.h" + #import "FBSDKCameraEffectTextures.h" @interface FBSDKCameraEffectTextures () diff --git a/FBSDKShareKit/FBSDKShareKit/Internal/FBSDKCheckmarkIcon.m b/FBSDKShareKit/FBSDKShareKit/Internal/FBSDKCheckmarkIcon.m index 2b1d9027d6..0bef89d2f6 100644 --- a/FBSDKShareKit/FBSDKShareKit/Internal/FBSDKCheckmarkIcon.m +++ b/FBSDKShareKit/FBSDKShareKit/Internal/FBSDKCheckmarkIcon.m @@ -20,7 +20,7 @@ #if !TARGET_OS_TV -#import "FBSDKCheckmarkIcon.h" + #import "FBSDKCheckmarkIcon.h" @implementation FBSDKCheckmarkIcon diff --git a/FBSDKShareKit/FBSDKShareKit/Internal/FBSDKCoreKitInternalImport.h b/FBSDKShareKit/FBSDKShareKit/Internal/FBSDKCoreKitInternalImport.h new file mode 100644 index 0000000000..2e3713615b --- /dev/null +++ b/FBSDKShareKit/FBSDKShareKit/Internal/FBSDKCoreKitInternalImport.h @@ -0,0 +1,26 @@ +// 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. + +// Importing FBSDKCoreKit+Internal is tricky due to build variants. +// SPM, xcodebuild, and BUCK require that it is imported as "FBSDKCoreKit+Internal.h" +// CocoaPods requires that is is imported as +#if defined FBSDKCOCOAPODS + #import +#else + #import "FBSDKCoreKit+Internal.h" +#endif diff --git a/FBSDKShareKit/FBSDKShareKit/Internal/FBSDKGameRequestFrictionlessRecipientCache.m b/FBSDKShareKit/FBSDKShareKit/Internal/FBSDKGameRequestFrictionlessRecipientCache.m index 4776c79c7c..b42939200d 100644 --- a/FBSDKShareKit/FBSDKShareKit/Internal/FBSDKGameRequestFrictionlessRecipientCache.m +++ b/FBSDKShareKit/FBSDKShareKit/Internal/FBSDKGameRequestFrictionlessRecipientCache.m @@ -20,26 +20,26 @@ #if !TARGET_OS_TV -#import "FBSDKGameRequestFrictionlessRecipientCache.h" + #import "FBSDKGameRequestFrictionlessRecipientCache.h" -#if defined BUCK || defined FBSDKCOCOAPODS -#import -#else + #if defined BUCK || defined FBSDKCOCOAPODS + #import + #else @import FBSDKCoreKit; -#endif + #endif -#ifdef FBSDKCOCOAPODS -#import -#else -#import "FBSDKCoreKit+Internal.h" -#endif + #ifdef FBSDKCOCOAPODS + #import + #else + #import "FBSDKCoreKit+Internal.h" + #endif @implementation FBSDKGameRequestFrictionlessRecipientCache { NSSet *_recipientIDs; } -#pragma mark - Object Lifecycle + #pragma mark - Object Lifecycle - (instancetype)init { @@ -58,7 +58,7 @@ - (void)dealloc [[NSNotificationCenter defaultCenter] removeObserver:self]; } -#pragma mark - Public API + #pragma mark - Public API - (BOOL)recipientsAreFrictionless:(id)recipients { @@ -85,7 +85,7 @@ - (void)updateWithResults:(NSDictionary *)results } } -#pragma mark - Helper Methods + #pragma mark - Helper Methods - (void)_accessTokenDidChangeNotification:(NSNotification *)notification { @@ -103,9 +103,9 @@ - (void)_updateCache return; } FBSDKGraphRequest *request = [[FBSDKGraphRequest alloc] initWithGraphPath:@"me/apprequestformerrecipients" - parameters:@{@"fields":@""} - flags:(FBSDKGraphRequestFlagDoNotInvalidateTokenOnError | - FBSDKGraphRequestFlagDisableErrorRecovery)]; + parameters:@{@"fields" : @""} + flags:(FBSDKGraphRequestFlagDoNotInvalidateTokenOnError + | FBSDKGraphRequestFlagDisableErrorRecovery)]; [request startWithCompletionHandler:^(FBSDKGraphRequestConnection *connection, id result, NSError *error) { if (!error) { NSArray *items = [FBSDKTypeUtility arrayValue:result[@"data"]]; diff --git a/FBSDKShareKit/FBSDKShareKit/Internal/FBSDKLikeActionController.m b/FBSDKShareKit/FBSDKShareKit/Internal/FBSDKLikeActionController.m index 0628bd3198..2e560b0b4c 100644 --- a/FBSDKShareKit/FBSDKShareKit/Internal/FBSDKLikeActionController.m +++ b/FBSDKShareKit/FBSDKShareKit/Internal/FBSDKLikeActionController.m @@ -20,83 +20,115 @@ #if !TARGET_OS_TV -#import "FBSDKLikeActionController.h" + #import "FBSDKLikeActionController.h" -#import + #import -#if defined BUCK || defined FBSDKCOCOAPODS -#import -#else + #if defined BUCK || defined FBSDKCOCOAPODS + #import + #else @import FBSDKCoreKit; -#endif + #endif + + #ifdef FBSDKCOCOAPODS + #import + #else + #import "FBSDKCoreKit+Internal.h" + #endif + #import "FBSDKLikeActionControllerCache.h" + #import "FBSDKLikeDialog.h" + +typedef NS_ENUM(NSInteger, FBSDKTriStateBOOL) { + FBSDKTriStateBOOLValueUnknown = -1, + FBSDKTriStateBOOLValueNO = 0, + FBSDKTriStateBOOLValueYES = 1, +}; -#ifdef FBSDKCOCOAPODS -#import -#else -#import "FBSDKCoreKit+Internal.h" -#endif -#import "FBSDKLikeActionControllerCache.h" -#import "FBSDKLikeDialog.h" +FOUNDATION_EXPORT FBSDKTriStateBOOL FBSDKTriStateBOOLFromBOOL(BOOL value); +FOUNDATION_EXPORT FBSDKTriStateBOOL FBSDKTriStateBOOLFromNSNumber(NSNumber *value); +FOUNDATION_EXPORT BOOL BOOLFromFBSDKTriStateBOOL(FBSDKTriStateBOOL value, BOOL defaultValue); + +FBSDKTriStateBOOL FBSDKTriStateBOOLFromBOOL(BOOL value) +{ + return value ? FBSDKTriStateBOOLValueYES : FBSDKTriStateBOOLValueNO; +} -#if __IPHONE_OS_VERSION_MAX_ALLOWED >= __IPHONE_10_0 +FBSDKTriStateBOOL FBSDKTriStateBOOLFromNSNumber(NSNumber *value) +{ + return ([value isKindOfClass:[NSNumber class]] + ? FBSDKTriStateBOOLFromBOOL(value.boolValue) + : FBSDKTriStateBOOLValueUnknown); +} + +BOOL BOOLFromFBSDKTriStateBOOL(FBSDKTriStateBOOL value, BOOL defaultValue) +{ + switch (value) { + case FBSDKTriStateBOOLValueYES: + return YES; + case FBSDKTriStateBOOLValueNO: + return NO; + case FBSDKTriStateBOOLValueUnknown: + return defaultValue; + } +} + + #if __IPHONE_OS_VERSION_MAX_ALLOWED >= __IPHONE_10_0 NSNotificationName const FBSDKLikeActionControllerDidDisableNotification = @"FBSDKLikeActionControllerDidDisableNotification"; NSNotificationName const FBSDKLikeActionControllerDidResetNotification = @"FBSDKLikeActionControllerDidResetNotification"; NSNotificationName const FBSDKLikeActionControllerDidUpdateNotification = @"FBSDKLikeActionControllerDidUpdateNotification"; -#else + #else NSString *const FBSDKLikeActionControllerDidDisableNotification = @"FBSDKLikeActionControllerDidDisableNotification"; NSString *const FBSDKLikeActionControllerDidResetNotification = @"FBSDKLikeActionControllerDidResetNotification"; NSString *const FBSDKLikeActionControllerDidUpdateNotification = @"FBSDKLikeActionControllerDidUpdateNotification"; -#endif + #endif NSString *const FBSDKLikeActionControllerAnimatedKey = @"animated"; -#define FBSDK_LIKE_ACTION_CONTROLLER_ANIMATION_DELAY 0.5 -#define FBSDK_LIKE_ACTION_CONTROLLER_SOUND_DELAY 0.15 -#define FBSDK_LIKE_ACTION_CONTROLLER_API_VERSION @"v2.1" + #define FBSDK_LIKE_ACTION_CONTROLLER_ANIMATION_DELAY 0.5 + #define FBSDK_LIKE_ACTION_CONTROLLER_SOUND_DELAY 0.15 + #define FBSDK_LIKE_ACTION_CONTROLLER_API_VERSION @"v2.1" -#define FBSDK_LIKE_ACTION_CONTROLLER_LIKE_PROPERTY_KEY @"like" -#define FBSDK_LIKE_ACTION_CONTROLLER_REFRESH_PROPERTY_KEY @"refresh" + #define FBSDK_LIKE_ACTION_CONTROLLER_LIKE_PROPERTY_KEY @"like" + #define FBSDK_LIKE_ACTION_CONTROLLER_REFRESH_PROPERTY_KEY @"refresh" -#define FBSDK_LIKE_ACTION_CONTROLLER_LAST_UPDATE_TIME_KEY @"lastUpdateTime" -#define FBSDK_LIKE_ACTION_CONTROLLER_LIKE_COUNT_STRING_WITH_LIKE_KEY @"likeCountStringWithLike" -#define FBSDK_LIKE_ACTION_CONTROLLER_LIKE_COUNT_STRING_WITHOUT_LIKE_KEY @"likeCountStringWithoutLike" -#define FBSDK_LIKE_ACTION_CONTROLLER_OBJECT_ID_KEY @"objectID" -#define FBSDK_LIKE_ACTION_CONTROLLER_OBJECT_IS_LIKED_KEY @"objectIsLiked" -#define FBSDK_LIKE_ACTION_CONTROLLER_OBJECT_TYPE_KEY @"objectType" -#define FBSDK_LIKE_ACTION_CONTROLLER_SOCIAL_SENTENCE_WITH_LIKE_KEY @"socialSentenceWithLike" -#define FBSDK_LIKE_ACTION_CONTROLLER_SOCIAL_SENTENCE_WITHOUT_LIKE_KEY @"socialSentenceWithoutLike" -#define FBSDK_LIKE_ACTION_CONTROLLER_UNLIKE_TOKEN_KEY @"unlikeToken" -#define FBSDK_LIKE_ACTION_CONTROLLER_VERSION_KEY @"version" + #define FBSDK_LIKE_ACTION_CONTROLLER_LAST_UPDATE_TIME_KEY @"lastUpdateTime" + #define FBSDK_LIKE_ACTION_CONTROLLER_LIKE_COUNT_STRING_WITH_LIKE_KEY @"likeCountStringWithLike" + #define FBSDK_LIKE_ACTION_CONTROLLER_LIKE_COUNT_STRING_WITHOUT_LIKE_KEY @"likeCountStringWithoutLike" + #define FBSDK_LIKE_ACTION_CONTROLLER_OBJECT_ID_KEY @"objectID" + #define FBSDK_LIKE_ACTION_CONTROLLER_OBJECT_IS_LIKED_KEY @"objectIsLiked" + #define FBSDK_LIKE_ACTION_CONTROLLER_OBJECT_TYPE_KEY @"objectType" + #define FBSDK_LIKE_ACTION_CONTROLLER_SOCIAL_SENTENCE_WITH_LIKE_KEY @"socialSentenceWithLike" + #define FBSDK_LIKE_ACTION_CONTROLLER_SOCIAL_SENTENCE_WITHOUT_LIKE_KEY @"socialSentenceWithoutLike" + #define FBSDK_LIKE_ACTION_CONTROLLER_UNLIKE_TOKEN_KEY @"unlikeToken" + #define FBSDK_LIKE_ACTION_CONTROLLER_VERSION_KEY @"version" -#define FBSDK_LIKE_ACTION_CONTROLLER_VERSION 4 + #define FBSDK_LIKE_ACTION_CONTROLLER_VERSION 4 -typedef NS_ENUM(NSUInteger, FBSDKLikeActionControllerRefreshMode) -{ +typedef NS_ENUM(NSUInteger, FBSDKLikeActionControllerRefreshMode) { FBSDKLikeActionControllerRefreshModeInitial, FBSDKLikeActionControllerRefreshModeForce, }; -typedef NS_ENUM(NSUInteger, FBSDKLikeActionControllerRefreshState) -{ +typedef NS_ENUM(NSUInteger, FBSDKLikeActionControllerRefreshState) { FBSDKLikeActionControllerRefreshStateNone, FBSDKLikeActionControllerRefreshStateActive, FBSDKLikeActionControllerRefreshStateComplete, }; -typedef void(^fbsdk_like_action_block)(FBSDKTriStateBOOL objectIsLiked, - NSString *likeCountStringWithLike, - NSString *likeCountStringWithoutLike, - NSString *socialSentenceWithLike, - NSString *socialSentenceWithoutLike, - NSString *unlikeToken, - BOOL likeStateChanged, - BOOL animated); +typedef void (^fbsdk_like_action_block)(FBSDKTriStateBOOL objectIsLiked, + NSString *likeCountStringWithLike, + NSString *likeCountStringWithoutLike, + NSString *socialSentenceWithLike, + NSString *socialSentenceWithoutLike, + NSString *unlikeToken, + BOOL likeStateChanged, + BOOL animated); -typedef void(^fbsdk_like_action_controller_ensure_verified_object_id_completion_block)(NSString *verifiedObjectID); +typedef void (^fbsdk_like_action_controller_ensure_verified_object_id_completion_block)(NSString *verifiedObjectID); @interface FBSDKLikeActionController () @end @@ -120,7 +152,7 @@ @implementation FBSDKLikeActionController NSString *_verifiedObjectID; } -#pragma mark - Class Methods + #pragma mark - Class Methods static BOOL _fbsdkLikeActionControllerDisabled = YES; @@ -139,17 +171,16 @@ + (void)initialize NSURL *fileURL = [self _cacheFileURL]; NSData *data = [[NSData alloc] initWithContentsOfURL:fileURL]; if (data) { - #if __IPHONE_OS_VERSION_MIN_REQUIRED >= __IPHONE_11_0 - NSKeyedUnarchiver *unarchiver = [[NSKeyedUnarchiver alloc] initForReadingFromData:data error:NULL]; - #else - NSKeyedUnarchiver *unarchiver = [[NSKeyedUnarchiver alloc] initForReadingWithData:data]; - #endif + #if __IPHONE_OS_VERSION_MIN_REQUIRED >= __IPHONE_11_0 + NSKeyedUnarchiver *unarchiver = [[NSKeyedUnarchiver alloc] initForReadingFromData:data error:NULL]; + #else + NSKeyedUnarchiver *unarchiver = [[NSKeyedUnarchiver alloc] initForReadingWithData:data]; + #endif unarchiver.requiresSecureCoding = YES; @try { _cache = [unarchiver decodeObjectOfClass:[FBSDKLikeActionControllerCache class] forKey:NSKeyedArchiveRootObjectKey]; - } - @catch (NSException *ex) { + } @catch (NSException *ex) { // ignore decoding exceptions from previous versions of the archive, etc } if (![_cache.accessTokenString isEqualToString:accessTokenString]) { @@ -210,7 +241,6 @@ + (NSURL *)_cacheFileURL return [directoryURL URLByAppendingPathComponent:@"com-facebook-sdk-like-data"]; } - + (instancetype)likeActionControllerForObjectID:(NSString *)objectID objectType:(FBSDKLikeObjectType)objectType { if (!objectID) { @@ -230,7 +260,7 @@ + (instancetype)likeActionControllerForObjectID:(NSString *)objectID objectType: } } -#pragma mark - Object Lifecycle + #pragma mark - Object Lifecycle - (instancetype)initWithObjectID:(NSString *)objectID objectType:(FBSDKLikeObjectType)objectType @@ -251,7 +281,7 @@ - (instancetype)init return [self initWithObjectID:nil objectType:FBSDKLikeObjectTypeUnknown accessToken:nil]; } -#pragma mark - NSCoding + #pragma mark - NSCoding + (BOOL)supportsSecureCoding { @@ -306,7 +336,7 @@ - (void)encodeWithCoder:(NSCoder *)coder [coder encodeInteger:FBSDK_LIKE_ACTION_CONTROLLER_VERSION forKey:FBSDK_LIKE_ACTION_CONTROLLER_VERSION_KEY]; } -#pragma mark - Properties + #pragma mark - Properties - (NSString *)likeCountString { @@ -318,14 +348,14 @@ - (NSString *)socialSentence return (_objectIsLiked ? _socialSentenceWithLike : _socialSentenceWithoutLike); } -#pragma mark - Public API + #pragma mark - Public API - (void)refresh { [self _refreshWithMode:FBSDKLikeActionControllerRefreshModeForce]; } -#pragma mark - NSDiscardableContent + #pragma mark - NSDiscardableContent - (BOOL)beginContentAccess { @@ -351,7 +381,7 @@ - (BOOL)isContentDiscarded return _contentDiscarded; } -#pragma mark - FBSDKLikeDialogDelegate + #pragma mark - FBSDKLikeDialogDelegate - (void)likeDialog:(FBSDKLikeDialog *)likeDialog didCompleteWithResults:(NSDictionary *)results { @@ -365,14 +395,16 @@ - (void)likeDialog:(FBSDKLikeDialog *)likeDialog didCompleteWithResults:(NSDicti if (updateBlock != NULL) { // we do not need to specify values for with/without like, since we will fast-app-switch to change // the value - updateBlock(objectIsLiked, - likeCountString, - likeCountString, - socialSentence, - socialSentence, - unlikeToken, - likeStateChanged, - YES); + updateBlock( + objectIsLiked, + likeCountString, + likeCountString, + socialSentence, + socialSentence, + unlikeToken, + likeStateChanged, + YES + ); } [self _setExecuting:NO forKey:FBSDK_LIKE_ACTION_CONTROLLER_LIKE_PROPERTY_KEY]; } @@ -399,14 +431,14 @@ - (void)likeDialog:(FBSDKLikeDialog *)likeDialog didFailWithError:(NSError *)err [self _setExecuting:NO forKey:FBSDK_LIKE_ACTION_CONTROLLER_LIKE_PROPERTY_KEY]; } -#pragma mark - Helper Methods + #pragma mark - Helper Methods - (void)_configure { NSPointerFunctionsOptions keyOptions = (NSPointerFunctionsStrongMemory | NSPointerFunctionsObjectPersonality); - NSPointerFunctionsOptions valueOptions = (NSPointerFunctionsStrongMemory | - NSPointerFunctionsObjectPersonality | - NSPointerFunctionsCopyIn); + NSPointerFunctionsOptions valueOptions = (NSPointerFunctionsStrongMemory + | NSPointerFunctionsObjectPersonality + | NSPointerFunctionsCopyIn); _dialogToAnalyticsParametersMap = [[NSMapTable alloc] initWithKeyOptions:keyOptions valueOptions:valueOptions capacity:0]; _dialogToUpdateBlockMap = [[NSMapTable alloc] initWithKeyOptions:keyOptions valueOptions:valueOptions capacity:0]; @@ -420,25 +452,25 @@ static void FBSDKLikeActionControllerLogError(NSString *currentAction, NSError *error) { NSDictionary *parameters = @{ - @"object_id": objectID, - @"object_type": NSStringFromFBSDKLikeObjectType(objectType), - @"current_action": currentAction, - @"error": error.description ?: @"", - }; - NSString *eventName = ([FBSDKError isNetworkError:error] ? - FBSDKAppEventNameFBSDKLikeControlNetworkUnavailable : - FBSDKAppEventNameFBSDKLikeControlError); + @"object_id" : objectID, + @"object_type" : NSStringFromFBSDKLikeObjectType(objectType), + @"current_action" : currentAction, + @"error" : error.description ?: @"", + }; + NSString *eventName = ([FBSDKError isNetworkError:error] + ? FBSDKAppEventNameFBSDKLikeControlNetworkUnavailable + : FBSDKAppEventNameFBSDKLikeControlError); [FBSDKAppEvents logInternalEvent:eventName parameters:parameters isImplicitlyLogged:YES accessToken:accessToken]; } -typedef void(^fbsdk_like_action_controller_get_engagement_completion_block)(BOOL success, - NSString *likeCountStringWithLike, - NSString *likeCountStringWithoutLike, - NSString *socialSentenceWithLike, - NSString *socialSentenceWithoutLike); +typedef void (^fbsdk_like_action_controller_get_engagement_completion_block)(BOOL success, + NSString *likeCountStringWithLike, + NSString *likeCountStringWithoutLike, + NSString *socialSentenceWithLike, + NSString *socialSentenceWithoutLike); static void FBSDKLikeActionControllerAddGetEngagementRequest(FBSDKAccessToken *accessToken, FBSDKGraphRequestConnection *connection, NSString *objectID, @@ -451,9 +483,8 @@ static void FBSDKLikeActionControllerAddGetEngagementRequest(FBSDKAccessToken *a NSString *fields = @"engagement.fields(count_string_with_like,count_string_without_like,social_sentence_with_like," @"social_sentence_without_like)"; FBSDKGraphRequest *request = [[FBSDKGraphRequest alloc] initWithGraphPath:objectID - parameters:@{ @"fields": fields, - @"locale": [NSLocale currentLocale].localeIdentifier - } + parameters:@{ @"fields" : fields, + @"locale" : [NSLocale currentLocale].localeIdentifier} tokenString:accessToken.tokenString HTTPMethod:@"GET" flags:FBSDKGraphRequestFlagDoNotInvalidateTokenOnError | FBSDKGraphRequestFlagDisableErrorRecovery]; @@ -478,17 +509,19 @@ static void FBSDKLikeActionControllerAddGetEngagementRequest(FBSDKAccessToken *a socialSentenceWithLike = [FBSDKTypeUtility stringValue:[result valueForKeyPath:@"engagement.social_sentence_with_like"]]; socialSentenceWithoutLike = [FBSDKTypeUtility stringValue:[result valueForKeyPath:@"engagement.social_sentence_without_like"]]; } - completionHandler(success, - likeCountStringWithLike, - likeCountStringWithoutLike, - socialSentenceWithLike, - socialSentenceWithoutLike); + completionHandler( + success, + likeCountStringWithLike, + likeCountStringWithoutLike, + socialSentenceWithLike, + socialSentenceWithoutLike + ); }]; } -typedef void(^fbsdk_like_action_controller_get_object_id_completion_block)(BOOL success, - NSString *verifiedObjectID, - BOOL objectIsPage); +typedef void (^fbsdk_like_action_controller_get_object_id_completion_block)(BOOL success, + NSString *verifiedObjectID, + BOOL objectIsPage); static void FBSDKLikeActionControllerAddGetObjectIDRequest(FBSDKAccessToken *accessToken, FBSDKGraphRequestConnection *connection, NSString *objectID, @@ -499,12 +532,12 @@ static void FBSDKLikeActionControllerAddGetObjectIDRequest(FBSDKAccessToken *acc } FBSDKGraphRequest *request = [[FBSDKGraphRequest alloc] initWithGraphPath:@"" parameters:@{ - @"fields": @"id", - @"id": objectID, - @"metadata": @"1", - @"type": @"og", - @"locale": [NSLocale currentLocale].localeIdentifier - } + @"fields" : @"id", + @"id" : objectID, + @"metadata" : @"1", + @"type" : @"og", + @"locale" : [NSLocale currentLocale].localeIdentifier + } tokenString:accessToken.tokenString HTTPMethod:@"GET" flags:FBSDKGraphRequestFlagDoNotInvalidateTokenOnError | FBSDKGraphRequestFlagDisableErrorRecovery]; @@ -527,10 +560,10 @@ static void FBSDKLikeActionControllerAddGetObjectIDWithObjectURLRequest(FBSDKAcc } FBSDKGraphRequest *request = [[FBSDKGraphRequest alloc] initWithGraphPath:@"" parameters:@{ - @"fields": @"og_object.fields(id)", - @"id": objectID, - @"locale": [NSLocale currentLocale].localeIdentifier - } + @"fields" : @"og_object.fields(id)", + @"id" : objectID, + @"locale" : [NSLocale currentLocale].localeIdentifier + } tokenString:accessToken.tokenString HTTPMethod:@"GET" flags:FBSDKGraphRequestFlagDoNotInvalidateTokenOnError | FBSDKGraphRequestFlagDisableErrorRecovery]; @@ -541,9 +574,9 @@ static void FBSDKLikeActionControllerAddGetObjectIDWithObjectURLRequest(FBSDKAcc }]; } -typedef void(^fbsdk_like_action_controller_get_og_object_like_completion_block)(BOOL success, - FBSDKTriStateBOOL objectIsLiked, - NSString *unlikeToken); +typedef void (^fbsdk_like_action_controller_get_og_object_like_completion_block)(BOOL success, + FBSDKTriStateBOOL objectIsLiked, + NSString *unlikeToken); static void FBSDKLikeActionControllerAddGetOGObjectLikeRequest(FBSDKAccessToken *accessToken, FBSDKGraphRequestConnection *connection, NSString *objectID, @@ -555,10 +588,10 @@ static void FBSDKLikeActionControllerAddGetOGObjectLikeRequest(FBSDKAccessToken } FBSDKGraphRequest *request = [[FBSDKGraphRequest alloc] initWithGraphPath:@"me/og.likes" parameters:@{ - @"fields": @"id,application", - @"object": objectID, - @"locale": [NSLocale currentLocale].localeIdentifier - } + @"fields" : @"id,application", + @"object" : objectID, + @"locale" : [NSLocale currentLocale].localeIdentifier + } tokenString:accessToken.tokenString HTTPMethod:@"GET" flags:FBSDKGraphRequestFlagDoNotInvalidateTokenOnError | FBSDKGraphRequestFlagDisableErrorRecovery]; @@ -588,7 +621,7 @@ static void FBSDKLikeActionControllerAddGetOGObjectLikeRequest(FBSDKAccessToken }]; } -typedef void(^fbsdk_like_action_controller_publish_like_completion_block)(BOOL success, NSString *unlikeToken); +typedef void (^fbsdk_like_action_controller_publish_like_completion_block)(BOOL success, NSString *unlikeToken); static void FBSDKLikeActionControllerAddPublishLikeRequest(FBSDKAccessToken *accessToken, FBSDKGraphRequestConnection *connection, NSString *objectID, @@ -596,9 +629,8 @@ static void FBSDKLikeActionControllerAddPublishLikeRequest(FBSDKAccessToken *acc fbsdk_like_action_controller_publish_like_completion_block completionHandler) { FBSDKGraphRequest *request = [[FBSDKGraphRequest alloc] initWithGraphPath:@"me/og.likes" - parameters:@{ @"object": objectID, - @"locale": [NSLocale currentLocale].localeIdentifier - } + parameters:@{ @"object" : objectID, + @"locale" : [NSLocale currentLocale].localeIdentifier} tokenString:accessToken.tokenString version:nil HTTPMethod:@"POST"]; @@ -620,7 +652,7 @@ static void FBSDKLikeActionControllerAddPublishLikeRequest(FBSDKAccessToken *acc }]; } -typedef void(^fbsdk_like_action_controller_publish_unlike_completion_block)(BOOL success); +typedef void (^fbsdk_like_action_controller_publish_unlike_completion_block)(BOOL success); static void FBSDKLikeActionControllerAddPublishUnlikeRequest(FBSDKAccessToken *accessToken, FBSDKGraphRequestConnection *connection, NSString *unlikeToken, @@ -663,15 +695,17 @@ static void FBSDKLikeActionControllerAddRefreshRequests(FBSDKAccessToken *access __block NSString *socialSentenceWithoutLike = nil; __block NSString *unlikeToken = nil; - void(^handleResults)(void) = ^{ - completionHandler(objectIsLiked, - likeCountStringWithLike, - likeCountStringWithoutLike, - socialSentenceWithLike, - socialSentenceWithoutLike, - unlikeToken, - NO, - NO); + void (^handleResults)(void) = ^{ + completionHandler( + objectIsLiked, + likeCountStringWithLike, + likeCountStringWithoutLike, + socialSentenceWithLike, + socialSentenceWithoutLike, + unlikeToken, + NO, + NO + ); }; fbsdk_like_action_controller_get_og_object_like_completion_block getLikeStateCompletionBlock = ^(BOOL success, @@ -684,11 +718,13 @@ static void FBSDKLikeActionControllerAddRefreshRequests(FBSDKAccessToken *access } } }; - FBSDKLikeActionControllerAddGetOGObjectLikeRequest(accessToken, - connection, - objectID, - objectType, - getLikeStateCompletionBlock); + FBSDKLikeActionControllerAddGetOGObjectLikeRequest( + accessToken, + connection, + objectID, + objectType, + getLikeStateCompletionBlock + ); fbsdk_like_action_controller_get_engagement_completion_block engagementCompletionBlock = ^(BOOL success, NSString *innerLikeCountStringWithLike, @@ -705,14 +741,15 @@ static void FBSDKLikeActionControllerAddRefreshRequests(FBSDKAccessToken *access handleResults(); } }; - FBSDKLikeActionControllerAddGetEngagementRequest(accessToken, - connection, - objectID, - objectType, - engagementCompletionBlock); + FBSDKLikeActionControllerAddGetEngagementRequest( + accessToken, + connection, + objectID, + objectType, + engagementCompletionBlock + ); } - - (void)_ensureVerifiedObjectID:(fbsdk_like_action_controller_ensure_verified_object_id_completion_block)completion { if (completion == NULL) { @@ -721,31 +758,35 @@ - (void)_ensureVerifiedObjectID:(fbsdk_like_action_controller_ensure_verified_ob FBSDKGraphRequestConnection *connection = [[FBSDKGraphRequestConnection alloc] init]; [connection overrideGraphAPIVersion:FBSDK_LIKE_ACTION_CONTROLLER_API_VERSION]; if ([_objectID rangeOfString:@"://"].location != NSNotFound) { - FBSDKLikeActionControllerAddGetObjectIDWithObjectURLRequest(_accessToken, connection, _objectID, ^(BOOL success, - NSString *innerVerifiedObjectID, - BOOL innerObjectIsPage) { - if (success) { - self->_verifiedObjectID = [innerVerifiedObjectID copy]; - self->_objectIsPage = innerObjectIsPage; - } - }); + FBSDKLikeActionControllerAddGetObjectIDWithObjectURLRequest(_accessToken, + connection, + _objectID, ^(BOOL success, + NSString *innerVerifiedObjectID, + BOOL innerObjectIsPage) { + if (success) { + self->_verifiedObjectID = [innerVerifiedObjectID copy]; + self->_objectIsPage = innerObjectIsPage; + } + }); } - FBSDKLikeActionControllerAddGetObjectIDRequest(_accessToken, connection, _objectID, ^(BOOL success, - NSString *innerVerifiedObjectID, - BOOL innerObjectIsPage) { - if (success) { - // if this was an URL based request, then we want to use the objectID from that request - this value will just - // be an echo of the URL - if (!self->_verifiedObjectID) { - self->_verifiedObjectID = [innerVerifiedObjectID copy]; - } - self->_objectIsPage = innerObjectIsPage; - } - if (self->_verifiedObjectID) { - completion(self->_verifiedObjectID); - } - }); + FBSDKLikeActionControllerAddGetObjectIDRequest(_accessToken, + connection, + _objectID, ^(BOOL success, + NSString *innerVerifiedObjectID, + BOOL innerObjectIsPage) { + if (success) { + // if this was an URL based request, then we want to use the objectID from that request - this value will just + // be an echo of the URL + if (!self->_verifiedObjectID) { + self->_verifiedObjectID = [innerVerifiedObjectID copy]; + } + self->_objectIsPage = innerObjectIsPage; + } + if (self->_verifiedObjectID) { + completion(self->_verifiedObjectID); + } + }); [connection start]; } @@ -793,34 +834,38 @@ - (void)_publishLikeWithUpdateBlock:(fbsdk_like_action_block)updateBlock [connection overrideGraphAPIVersion:FBSDK_LIKE_ACTION_CONTROLLER_API_VERSION]; fbsdk_like_action_controller_publish_like_completion_block completionHandler = ^(BOOL success, NSString *unlikeToken) { - self->_objectIsLikedIsPending = NO; - if (success) { - [FBSDKAppEvents logInternalEvent:FBSDKAppEventNameFBSDKLikeControlDidLike - parameters:analyticsParameters - isImplicitlyLogged:YES - accessToken:self->_accessToken]; - self->_objectIsLikedOnServer = YES; - self->_unlikeToken = [unlikeToken copy]; - if (updateBlock != NULL) { - updateBlock(FBSDKTriStateBOOLFromBOOL(self.objectIsLiked), - self->_likeCountStringWithLike, - self->_likeCountStringWithoutLike, - self->_socialSentenceWithLike, - self->_socialSentenceWithoutLike, - self->_unlikeToken, - NO, - NO); - } - [self _publishIfNeededWithUpdateBlock:updateBlock analyticsParameters:analyticsParameters fromViewController:fromViewController]; - } else { - [self _presentLikeDialogWithUpdateBlock:updateBlock analyticsParameters:analyticsParameters fromViewController:fromViewController]; - } - }; - FBSDKLikeActionControllerAddPublishLikeRequest(self->_accessToken, - connection, - verifiedObjectID, - self->_objectType, - completionHandler); + self->_objectIsLikedIsPending = NO; + if (success) { + [FBSDKAppEvents logInternalEvent:FBSDKAppEventNameFBSDKLikeControlDidLike + parameters:analyticsParameters + isImplicitlyLogged:YES + accessToken:self->_accessToken]; + self->_objectIsLikedOnServer = YES; + self->_unlikeToken = [unlikeToken copy]; + if (updateBlock != NULL) { + updateBlock( + FBSDKTriStateBOOLFromBOOL(self.objectIsLiked), + self->_likeCountStringWithLike, + self->_likeCountStringWithoutLike, + self->_socialSentenceWithLike, + self->_socialSentenceWithoutLike, + self->_unlikeToken, + NO, + NO + ); + } + [self _publishIfNeededWithUpdateBlock:updateBlock analyticsParameters:analyticsParameters fromViewController:fromViewController]; + } else { + [self _presentLikeDialogWithUpdateBlock:updateBlock analyticsParameters:analyticsParameters fromViewController:fromViewController]; + } + }; + FBSDKLikeActionControllerAddPublishLikeRequest( + self->_accessToken, + connection, + verifiedObjectID, + self->_objectType, + completionHandler + ); [connection start]; }]; } @@ -842,39 +887,43 @@ - (void)_publishUnlikeWithUpdateBlock:(fbsdk_like_action_block)updateBlock self->_objectIsLikedOnServer = NO; self->_unlikeToken = nil; if (updateBlock != NULL) { - updateBlock(FBSDKTriStateBOOLFromBOOL(self.objectIsLiked), - self->_likeCountStringWithLike, - self->_likeCountStringWithoutLike, - self->_socialSentenceWithLike, - self->_socialSentenceWithoutLike, - self->_unlikeToken, - NO, - NO); + updateBlock( + FBSDKTriStateBOOLFromBOOL(self.objectIsLiked), + self->_likeCountStringWithLike, + self->_likeCountStringWithoutLike, + self->_socialSentenceWithLike, + self->_socialSentenceWithoutLike, + self->_unlikeToken, + NO, + NO + ); } [self _publishIfNeededWithUpdateBlock:updateBlock analyticsParameters:analyticsParameters fromViewController:fromViewController]; } else { [self _presentLikeDialogWithUpdateBlock:updateBlock analyticsParameters:analyticsParameters fromViewController:fromViewController]; } }; - FBSDKLikeActionControllerAddPublishUnlikeRequest(_accessToken, - connection, - _unlikeToken, - _objectType, - completionHandler); + FBSDKLikeActionControllerAddPublishUnlikeRequest( + _accessToken, + connection, + _unlikeToken, + _objectType, + completionHandler + ); [connection start]; } - (void)_refreshWithMode:(FBSDKLikeActionControllerRefreshMode)mode { switch (mode) { - case FBSDKLikeActionControllerRefreshModeForce:{ + case FBSDKLikeActionControllerRefreshModeForce: { // if we're already refreshing, skip if (_refreshState == FBSDKLikeActionControllerRefreshStateActive) { return; } break; } - case FBSDKLikeActionControllerRefreshModeInitial:{ + case FBSDKLikeActionControllerRefreshModeInitial: { // if we've already started any refresh, skip this if (_refreshState != FBSDKLikeActionControllerRefreshStateNone) { return; @@ -895,28 +944,28 @@ - (void)_refreshWithMode:(FBSDKLikeActionControllerRefreshMode)mode FBSDKGraphRequestConnection *connection = [[FBSDKGraphRequestConnection alloc] init]; [connection overrideGraphAPIVersion:FBSDK_LIKE_ACTION_CONTROLLER_API_VERSION]; FBSDKLikeActionControllerAddRefreshRequests(self->_accessToken, - connection, - verifiedObjectID, - self->_objectType, - ^(FBSDKTriStateBOOL objectIsLiked, - NSString *likeCountStringWithLike, - NSString *likeCountStringWithoutLike, - NSString *socialSentenceWithLike, - NSString *socialSentenceWithoutLike, - NSString *unlikeToken, - BOOL likeStateChanged, - BOOL animated) { - [self _updateWithObjectIsLiked:objectIsLiked - likeCountStringWithLike:likeCountStringWithLike - likeCountStringWithoutLike:likeCountStringWithoutLike - socialSentenceWithLike:socialSentenceWithLike - socialSentenceWithoutLike:socialSentenceWithoutLike - unlikeToken:unlikeToken - animated:NO - deferred:NO]; - [self _setExecuting:NO forKey:FBSDK_LIKE_ACTION_CONTROLLER_REFRESH_PROPERTY_KEY]; - self->_refreshState = FBSDKLikeActionControllerRefreshStateComplete; - }); + connection, + verifiedObjectID, + self->_objectType, + ^(FBSDKTriStateBOOL objectIsLiked, + NSString *likeCountStringWithLike, + NSString *likeCountStringWithoutLike, + NSString *socialSentenceWithLike, + NSString *socialSentenceWithoutLike, + NSString *unlikeToken, + BOOL likeStateChanged, + BOOL animated) { + [self _updateWithObjectIsLiked:objectIsLiked + likeCountStringWithLike:likeCountStringWithLike + likeCountStringWithoutLike:likeCountStringWithoutLike + socialSentenceWithLike:socialSentenceWithLike + socialSentenceWithoutLike:socialSentenceWithoutLike + unlikeToken:unlikeToken + animated:NO + deferred:NO]; + [self _setExecuting:NO forKey:FBSDK_LIKE_ACTION_CONTROLLER_REFRESH_PROPERTY_KEY]; + self->_refreshState = FBSDKLikeActionControllerRefreshStateComplete; + }); [connection start]; }]; } @@ -963,17 +1012,17 @@ - (void)_updateWithObjectIsLiked:(FBSDKTriStateBOOL)objectIsLikedTriState // If the new like state is unknown, we don't consider the state to have changed. BOOL objectIsLikedChanged = (objectIsLikedTriState != FBSDKTriStateBOOLValueUnknown) && (self.objectIsLiked != objectIsLiked); - if (!objectIsLikedChanged && - [FBSDKInternalUtility object:_likeCountStringWithLike isEqualToObject:likeCountStringWithLike] && - [FBSDKInternalUtility object:_likeCountStringWithoutLike isEqualToObject:likeCountStringWithoutLike] && - [FBSDKInternalUtility object:_socialSentenceWithLike isEqualToObject:socialSentenceWithLike] && - [FBSDKInternalUtility object:_socialSentenceWithoutLike isEqualToObject:socialSentenceWithoutLike] && - [FBSDKInternalUtility object:_unlikeToken isEqualToObject:unlikeToken]) { + if (!objectIsLikedChanged + && [FBSDKInternalUtility object:_likeCountStringWithLike isEqualToObject:likeCountStringWithLike] + && [FBSDKInternalUtility object:_likeCountStringWithoutLike isEqualToObject:likeCountStringWithoutLike] + && [FBSDKInternalUtility object:_socialSentenceWithLike isEqualToObject:socialSentenceWithLike] + && [FBSDKInternalUtility object:_socialSentenceWithoutLike isEqualToObject:socialSentenceWithoutLike] + && [FBSDKInternalUtility object:_unlikeToken isEqualToObject:unlikeToken]) { // check if the like state changed and only animate if it did return; } - void(^updateBlock)(void) = ^{ + void (^updateBlock)(void) = ^{ if (objectIsLikedChanged) { self->_objectIsLiked = objectIsLiked; } @@ -994,8 +1043,8 @@ - (void)_updateWithObjectIsLiked:(FBSDKTriStateBOOL)objectIsLikedTriState self->_unlikeToken = [unlikeToken copy]; } - void(^notificationBlock)(void) = ^{ - NSDictionary *userInfo = @{FBSDKLikeActionControllerAnimatedKey: @(animated)}; + void (^notificationBlock)(void) = ^{ + NSDictionary *userInfo = @{FBSDKLikeActionControllerAnimatedKey : @(animated)}; [[NSNotificationCenter defaultCenter] postNotificationName:FBSDKLikeActionControllerDidUpdateNotification object:self userInfo:userInfo]; @@ -1016,10 +1065,10 @@ - (void)_updateWithObjectIsLiked:(FBSDKTriStateBOOL)objectIsLikedTriState - (BOOL)_useOGLike { - return (_accessToken && - !_objectIsPage && - _verifiedObjectID && - [_accessToken.permissions containsObject:@"publish_actions"]); + return (_accessToken + && !_objectIsPage + && _verifiedObjectID + && [_accessToken.permissions containsObject:@"publish_actions"]); } @end diff --git a/FBSDKShareKit/FBSDKShareKit/Internal/FBSDKLikeActionControllerCache.m b/FBSDKShareKit/FBSDKShareKit/Internal/FBSDKLikeActionControllerCache.m index d5d0c26eef..5e08aba4a3 100644 --- a/FBSDKShareKit/FBSDKShareKit/Internal/FBSDKLikeActionControllerCache.m +++ b/FBSDKShareKit/FBSDKShareKit/Internal/FBSDKLikeActionControllerCache.m @@ -20,22 +20,23 @@ #if !TARGET_OS_TV -#import "FBSDKLikeActionControllerCache.h" + #import "FBSDKLikeActionControllerCache.h" -#import + #import -#import "FBSDKLikeActionController.h" #ifdef FBSDKCOCOAPODS -#import +#import #else -#import "FBSDKCoreKit+Internal.h" +#import "FBSDKCoreKitInternalImport.h" #endif + #import "FBSDKLikeActionController.h" + // after 1 day, expire the cached states -#define FBSDK_LIKE_ACTION_CONTROLLER_CACHE_TIMEOUT 60 * 24 + #define FBSDK_LIKE_ACTION_CONTROLLER_CACHE_TIMEOUT 60 * 24 -#define FBSDK_LIKE_ACTION_CONTROLLER_CACHE_ACCESS_TOKEN_KEY @"accessTokenString" -#define FBSDK_LIKE_ACTION_CONTROLLER_CACHE_ITEMS_KEY @"items" + #define FBSDK_LIKE_ACTION_CONTROLLER_CACHE_ACCESS_TOKEN_KEY @"accessTokenString" + #define FBSDK_LIKE_ACTION_CONTROLLER_CACHE_ITEMS_KEY @"items" @implementation FBSDKLikeActionControllerCache { @@ -43,7 +44,7 @@ @implementation FBSDKLikeActionControllerCache NSMutableDictionary *_items; } -#pragma mark - Object Lifecycle + #pragma mark - Object Lifecycle - (instancetype)initWithAccessTokenString:(NSString *)accessTokenString { @@ -54,7 +55,7 @@ - (instancetype)initWithAccessTokenString:(NSString *)accessTokenString return self; } -#pragma mark - NSCoding + #pragma mark - NSCoding + (BOOL)supportsSecureCoding { @@ -81,7 +82,7 @@ - (void)encodeWithCoder:(NSCoder *)encoder [encoder encodeObject:_items forKey:FBSDK_LIKE_ACTION_CONTROLLER_CACHE_ITEMS_KEY]; } -#pragma mark - Public Methods + #pragma mark - Public Methods - (id)objectForKeyedSubscript:(id)key { @@ -99,20 +100,20 @@ - (void)setObject:(id)object forKeyedSubscript:(id)key [FBSDKTypeUtility dictionary:_items setObject:object forKey:key]; } -#pragma mark - Helper Methods + #pragma mark - Helper Methods - (void)_prune { NSMutableArray *keysToRemove = [[NSMutableArray alloc] init]; [FBSDKTypeUtility dictionary:_items enumerateKeysAndObjectsUsingBlock:^(NSString *objectID, - FBSDKLikeActionController *likeActionController, - BOOL *stop) { - NSDate *lastUpdateTime = likeActionController.lastUpdateTime; - if (!lastUpdateTime || - ([[NSDate date] timeIntervalSinceDate:lastUpdateTime] > FBSDK_LIKE_ACTION_CONTROLLER_CACHE_TIMEOUT)) { - [keysToRemove addObject:objectID]; - } - }]; + FBSDKLikeActionController *likeActionController, + BOOL *stop) { + NSDate *lastUpdateTime = likeActionController.lastUpdateTime; + if (!lastUpdateTime + || ([[NSDate date] timeIntervalSinceDate:lastUpdateTime] > FBSDK_LIKE_ACTION_CONTROLLER_CACHE_TIMEOUT)) { + [keysToRemove addObject:objectID]; + } + }]; [_items removeObjectsForKeys:keysToRemove]; } diff --git a/FBSDKShareKit/FBSDKShareKit/Internal/FBSDKLikeBoxBorderView.m b/FBSDKShareKit/FBSDKShareKit/Internal/FBSDKLikeBoxBorderView.m index 937a98a7ea..ec63e96d03 100644 --- a/FBSDKShareKit/FBSDKShareKit/Internal/FBSDKLikeBoxBorderView.m +++ b/FBSDKShareKit/FBSDKShareKit/Internal/FBSDKLikeBoxBorderView.m @@ -20,22 +20,22 @@ #if !TARGET_OS_TV -#import "FBSDKLikeBoxBorderView.h" + #import "FBSDKLikeBoxBorderView.h" -#ifdef FBSDKCOCOAPODS -#import -#else -#import "FBSDKCoreKit+Internal.h" -#endif + #ifdef FBSDKCOCOAPODS + #import + #else + #import "FBSDKCoreKit+Internal.h" + #endif -#define FBSDK_LIKE_BOX_BORDER_CARET_WIDTH 6.0 -#define FBSDK_LIKE_BOX_BORDER_CARET_HEIGHT 3.0 -#define FBSDK_LIKE_BOX_BORDER_CARET_PADDING 3.0 -#define FBSDK_LIKE_BOX_BORDER_CONTENT_PADDING 4.0 + #define FBSDK_LIKE_BOX_BORDER_CARET_WIDTH 6.0 + #define FBSDK_LIKE_BOX_BORDER_CARET_HEIGHT 3.0 + #define FBSDK_LIKE_BOX_BORDER_CARET_PADDING 3.0 + #define FBSDK_LIKE_BOX_BORDER_CONTENT_PADDING 4.0 @implementation FBSDKLikeBoxBorderView -#pragma mark - Object Lifecycle + #pragma mark - Object Lifecycle - (instancetype)initWithFrame:(CGRect)frame { @@ -53,7 +53,7 @@ - (id)initWithCoder:(NSCoder *)decoder return self; } -#pragma mark - Properties + #pragma mark - Properties - (void)setBackgroundColor:(UIColor *)backgroundColor { @@ -93,10 +93,12 @@ - (void)setCaretPosition:(FBSDKLikeBoxCaretPosition)caretPosition - (UIEdgeInsets)contentInsets { UIEdgeInsets borderInsets = [self _borderInsets]; - return UIEdgeInsetsMake(borderInsets.top + FBSDK_LIKE_BOX_BORDER_CONTENT_PADDING, - borderInsets.left + FBSDK_LIKE_BOX_BORDER_CONTENT_PADDING, - borderInsets.bottom + FBSDK_LIKE_BOX_BORDER_CONTENT_PADDING, - borderInsets.right + FBSDK_LIKE_BOX_BORDER_CONTENT_PADDING); + return UIEdgeInsetsMake( + borderInsets.top + FBSDK_LIKE_BOX_BORDER_CONTENT_PADDING, + borderInsets.left + FBSDK_LIKE_BOX_BORDER_CONTENT_PADDING, + borderInsets.bottom + FBSDK_LIKE_BOX_BORDER_CONTENT_PADDING, + borderInsets.right + FBSDK_LIKE_BOX_BORDER_CONTENT_PADDING + ); } - (void)setContentView:(UIView *)contentView @@ -126,7 +128,7 @@ - (void)setForegroundColor:(UIColor *)foregroundColor } } -#pragma mark - Layout + #pragma mark - Layout - (CGSize)intrinsicContentSize { @@ -149,7 +151,7 @@ - (CGSize)sizeThatFits:(CGSize)size return size; } -#pragma mark - Drawing + #pragma mark - Drawing - (void)drawRect:(CGRect)rect { @@ -210,12 +212,18 @@ - (void)drawRect:(CGRect)rect switch (self.caretPosition) { case FBSDKLikeBoxCaretPositionTop: CGContextMoveToPoint(context, topRightArc[end].x, topRightArc[end].y); - caretPoints[0] = CGPointMake(FBSDKPointsForScreenPixels(floorf, contentScaleFactor, CGRectGetMidX(borderFrame) + (FBSDK_LIKE_BOX_BORDER_CARET_WIDTH / 2)), - CGRectGetMinY(borderFrame)); - caretPoints[1] = CGPointMake(FBSDKPointsForScreenPixels(floorf, contentScaleFactor, CGRectGetMidX(borderFrame)), - CGRectGetMinY(borderFrame) - FBSDK_LIKE_BOX_BORDER_CARET_HEIGHT); - caretPoints[2] = CGPointMake(FBSDKPointsForScreenPixels(floorf, contentScaleFactor, CGRectGetMidX(borderFrame) - (FBSDK_LIKE_BOX_BORDER_CARET_WIDTH / 2)), - CGRectGetMinY(borderFrame)); + caretPoints[0] = CGPointMake( + FBSDKPointsForScreenPixels(floorf, contentScaleFactor, CGRectGetMidX(borderFrame) + (FBSDK_LIKE_BOX_BORDER_CARET_WIDTH / 2)), + CGRectGetMinY(borderFrame) + ); + caretPoints[1] = CGPointMake( + FBSDKPointsForScreenPixels(floorf, contentScaleFactor, CGRectGetMidX(borderFrame)), + CGRectGetMinY(borderFrame) - FBSDK_LIKE_BOX_BORDER_CARET_HEIGHT + ); + caretPoints[2] = CGPointMake( + FBSDKPointsForScreenPixels(floorf, contentScaleFactor, CGRectGetMidX(borderFrame) - (FBSDK_LIKE_BOX_BORDER_CARET_WIDTH / 2)), + CGRectGetMinY(borderFrame) + ); CGContextAddLines(context, caretPoints, sizeof(caretPoints) / sizeof(caretPoints[0])); CGContextAddArcToPoint(context, topLeftArc[tangent].x, topLeftArc[tangent].y, topLeftArc[end].x, topLeftArc[end].y, borderCornerRadius); CGContextAddLineToPoint(context, bottomLeftArc[start].x, bottomLeftArc[start].y); @@ -227,12 +235,18 @@ - (void)drawRect:(CGRect)rect break; case FBSDKLikeBoxCaretPositionLeft: CGContextMoveToPoint(context, topLeftArc[end].x, topLeftArc[end].y); - caretPoints[0] = CGPointMake(CGRectGetMinX(borderFrame), - FBSDKPointsForScreenPixels(floorf, contentScaleFactor, CGRectGetMidY(borderFrame) - (FBSDK_LIKE_BOX_BORDER_CARET_WIDTH / 2))); - caretPoints[1] = CGPointMake(CGRectGetMinX(borderFrame) - FBSDK_LIKE_BOX_BORDER_CARET_HEIGHT, - FBSDKPointsForScreenPixels(floorf, contentScaleFactor, CGRectGetMidY(borderFrame))); - caretPoints[2] = CGPointMake(CGRectGetMinX(borderFrame), - FBSDKPointsForScreenPixels(floorf, contentScaleFactor, CGRectGetMidY(borderFrame) + (FBSDK_LIKE_BOX_BORDER_CARET_WIDTH / 2))); + caretPoints[0] = CGPointMake( + CGRectGetMinX(borderFrame), + FBSDKPointsForScreenPixels(floorf, contentScaleFactor, CGRectGetMidY(borderFrame) - (FBSDK_LIKE_BOX_BORDER_CARET_WIDTH / 2)) + ); + caretPoints[1] = CGPointMake( + CGRectGetMinX(borderFrame) - FBSDK_LIKE_BOX_BORDER_CARET_HEIGHT, + FBSDKPointsForScreenPixels(floorf, contentScaleFactor, CGRectGetMidY(borderFrame)) + ); + caretPoints[2] = CGPointMake( + CGRectGetMinX(borderFrame), + FBSDKPointsForScreenPixels(floorf, contentScaleFactor, CGRectGetMidY(borderFrame) + (FBSDK_LIKE_BOX_BORDER_CARET_WIDTH / 2)) + ); CGContextAddLines(context, caretPoints, sizeof(caretPoints) / sizeof(caretPoints[0])); CGContextAddArcToPoint(context, bottomLeftArc[tangent].x, bottomLeftArc[tangent].y, bottomLeftArc[end].x, bottomLeftArc[end].y, borderCornerRadius); CGContextAddLineToPoint(context, bottomRightArc[start].x, bottomRightArc[start].y); @@ -244,12 +258,18 @@ - (void)drawRect:(CGRect)rect break; case FBSDKLikeBoxCaretPositionBottom: CGContextMoveToPoint(context, bottomLeftArc[end].x, bottomLeftArc[end].y); - caretPoints[0] = CGPointMake(FBSDKPointsForScreenPixels(floorf, contentScaleFactor, CGRectGetMidX(borderFrame) - (FBSDK_LIKE_BOX_BORDER_CARET_WIDTH / 2)), - CGRectGetMaxY(borderFrame)); - caretPoints[1] = CGPointMake(FBSDKPointsForScreenPixels(floorf, contentScaleFactor, CGRectGetMidX(borderFrame)), - CGRectGetMaxY(borderFrame) + FBSDK_LIKE_BOX_BORDER_CARET_HEIGHT); - caretPoints[2] = CGPointMake(FBSDKPointsForScreenPixels(floorf, contentScaleFactor, CGRectGetMidX(borderFrame) + (FBSDK_LIKE_BOX_BORDER_CARET_WIDTH / 2)), - CGRectGetMaxY(borderFrame)); + caretPoints[0] = CGPointMake( + FBSDKPointsForScreenPixels(floorf, contentScaleFactor, CGRectGetMidX(borderFrame) - (FBSDK_LIKE_BOX_BORDER_CARET_WIDTH / 2)), + CGRectGetMaxY(borderFrame) + ); + caretPoints[1] = CGPointMake( + FBSDKPointsForScreenPixels(floorf, contentScaleFactor, CGRectGetMidX(borderFrame)), + CGRectGetMaxY(borderFrame) + FBSDK_LIKE_BOX_BORDER_CARET_HEIGHT + ); + caretPoints[2] = CGPointMake( + FBSDKPointsForScreenPixels(floorf, contentScaleFactor, CGRectGetMidX(borderFrame) + (FBSDK_LIKE_BOX_BORDER_CARET_WIDTH / 2)), + CGRectGetMaxY(borderFrame) + ); CGContextAddLines(context, caretPoints, sizeof(caretPoints) / sizeof(caretPoints[0])); CGContextAddArcToPoint(context, bottomRightArc[tangent].x, bottomRightArc[tangent].y, bottomRightArc[end].x, bottomRightArc[end].y, borderCornerRadius); CGContextAddLineToPoint(context, topRightArc[start].x, topRightArc[start].y); @@ -261,12 +281,18 @@ - (void)drawRect:(CGRect)rect break; case FBSDKLikeBoxCaretPositionRight: CGContextMoveToPoint(context, bottomRightArc[end].x, bottomRightArc[end].y); - caretPoints[0] = CGPointMake(CGRectGetMaxX(borderFrame), - FBSDKPointsForScreenPixels(floorf, contentScaleFactor, CGRectGetMidY(borderFrame) + (FBSDK_LIKE_BOX_BORDER_CARET_WIDTH / 2))); - caretPoints[1] = CGPointMake(CGRectGetMaxX(borderFrame) + FBSDK_LIKE_BOX_BORDER_CARET_HEIGHT, - FBSDKPointsForScreenPixels(floorf, contentScaleFactor, CGRectGetMidY(borderFrame))); - caretPoints[2] = CGPointMake(CGRectGetMaxX(borderFrame), - FBSDKPointsForScreenPixels(floorf, contentScaleFactor, CGRectGetMidY(borderFrame) - (FBSDK_LIKE_BOX_BORDER_CARET_WIDTH / 2))); + caretPoints[0] = CGPointMake( + CGRectGetMaxX(borderFrame), + FBSDKPointsForScreenPixels(floorf, contentScaleFactor, CGRectGetMidY(borderFrame) + (FBSDK_LIKE_BOX_BORDER_CARET_WIDTH / 2)) + ); + caretPoints[1] = CGPointMake( + CGRectGetMaxX(borderFrame) + FBSDK_LIKE_BOX_BORDER_CARET_HEIGHT, + FBSDKPointsForScreenPixels(floorf, contentScaleFactor, CGRectGetMidY(borderFrame)) + ); + caretPoints[2] = CGPointMake( + CGRectGetMaxX(borderFrame), + FBSDKPointsForScreenPixels(floorf, contentScaleFactor, CGRectGetMidY(borderFrame) - (FBSDK_LIKE_BOX_BORDER_CARET_WIDTH / 2)) + ); CGContextAddLines(context, caretPoints, sizeof(caretPoints) / sizeof(caretPoints[0])); CGContextAddArcToPoint(context, topRightArc[tangent].x, topRightArc[tangent].y, topRightArc[end].x, topRightArc[end].y, borderCornerRadius); CGContextAddLineToPoint(context, topLeftArc[start].x, topLeftArc[start].y); @@ -285,7 +311,7 @@ - (void)drawRect:(CGRect)rect CGContextRestoreGState(context); } -#pragma mark - Helper Methods + #pragma mark - Helper Methods - (UIEdgeInsets)_borderInsets { @@ -296,19 +322,19 @@ - (UIEdgeInsets)_borderInsets // adjust the insets for the caret position switch (self.caretPosition) { - case FBSDKLikeBoxCaretPositionTop:{ + case FBSDKLikeBoxCaretPositionTop: { borderInsets.top += FBSDK_LIKE_BOX_BORDER_CARET_HEIGHT + FBSDK_LIKE_BOX_BORDER_CARET_PADDING; break; } - case FBSDKLikeBoxCaretPositionLeft:{ + case FBSDKLikeBoxCaretPositionLeft: { borderInsets.left += FBSDK_LIKE_BOX_BORDER_CARET_HEIGHT + FBSDK_LIKE_BOX_BORDER_CARET_PADDING; break; } - case FBSDKLikeBoxCaretPositionBottom:{ + case FBSDKLikeBoxCaretPositionBottom: { borderInsets.bottom += FBSDK_LIKE_BOX_BORDER_CARET_HEIGHT + FBSDK_LIKE_BOX_BORDER_CARET_PADDING; break; } - case FBSDKLikeBoxCaretPositionRight:{ + case FBSDKLikeBoxCaretPositionRight: { borderInsets.right += FBSDK_LIKE_BOX_BORDER_CARET_HEIGHT + FBSDK_LIKE_BOX_BORDER_CARET_PADDING; break; } diff --git a/FBSDKShareKit/FBSDKShareKit/Internal/FBSDKLikeBoxView.m b/FBSDKShareKit/FBSDKShareKit/Internal/FBSDKLikeBoxView.m index 9610449059..39f7d7f0ce 100644 --- a/FBSDKShareKit/FBSDKShareKit/Internal/FBSDKLikeBoxView.m +++ b/FBSDKShareKit/FBSDKShareKit/Internal/FBSDKLikeBoxView.m @@ -20,14 +20,14 @@ #if !TARGET_OS_TV -#import "FBSDKLikeBoxView.h" + #import "FBSDKLikeBoxView.h" -#ifdef FBSDKCOCOAPODS -#import -#else -#import "FBSDKCoreKit+Internal.h" -#endif -#import "FBSDKLikeBoxBorderView.h" + #ifdef FBSDKCOCOAPODS + #import + #else + #import "FBSDKCoreKit+Internal.h" + #endif + #import "FBSDKLikeBoxBorderView.h" @implementation FBSDKLikeBoxView { @@ -35,7 +35,7 @@ @implementation FBSDKLikeBoxView UILabel *_likeCountLabel; } -#pragma mark - Object Lifecycle + #pragma mark - Object Lifecycle - (instancetype)initWithFrame:(CGRect)frame { @@ -53,7 +53,7 @@ - (id)initWithCoder:(NSCoder *)decoder return self; } -#pragma mark - Properties + #pragma mark - Properties - (void)setCaretPosition:(FBSDKLikeBoxCaretPosition)caretPosition { @@ -79,7 +79,7 @@ - (void)setText:(NSString *)text } } -#pragma mark - Layout + #pragma mark - Layout - (CGSize)intrinsicContentSize { @@ -99,7 +99,7 @@ - (CGSize)sizeThatFits:(CGSize)size return [_borderView sizeThatFits:size]; } -#pragma mark - Helper Methods + #pragma mark - Helper Methods - (void)_initializeContent { diff --git a/FBSDKShareKit/FBSDKShareKit/Internal/FBSDKLikeButton+Internal.h b/FBSDKShareKit/FBSDKShareKit/Internal/FBSDKLikeButton+Internal.h index 6646d9277d..472a72b39e 100644 --- a/FBSDKShareKit/FBSDKShareKit/Internal/FBSDKLikeButton+Internal.h +++ b/FBSDKShareKit/FBSDKShareKit/Internal/FBSDKLikeButton+Internal.h @@ -20,12 +20,12 @@ #if !TARGET_OS_TV -#import + #import -#import + #import -#import "FBSDKLikeActionController.h" -#import "FBSDKLikeButton.h" + #import "FBSDKLikeActionController.h" + #import "FBSDKLikeButton.h" @interface FBSDKLikeButton () diff --git a/FBSDKShareKit/FBSDKShareKit/Internal/FBSDKLikeDialog.m b/FBSDKShareKit/FBSDKShareKit/Internal/FBSDKLikeDialog.m index 14ce20f498..7eee7f2b77 100644 --- a/FBSDKShareKit/FBSDKShareKit/Internal/FBSDKLikeDialog.m +++ b/FBSDKShareKit/FBSDKShareKit/Internal/FBSDKLikeDialog.m @@ -20,24 +20,24 @@ #if !TARGET_OS_TV -#import "FBSDKLikeDialog.h" + #import "FBSDKLikeDialog.h" -#ifdef FBSDKCOCOAPODS -#import -#else -#import "FBSDKCoreKit+Internal.h" -#endif -#import "FBSDKShareConstants.h" -#import "FBSDKShareDefines.h" + #ifdef FBSDKCOCOAPODS + #import + #else + #import "FBSDKCoreKit+Internal.h" + #endif + #import "FBSDKShareConstants.h" + #import "FBSDKShareDefines.h" @implementation FBSDKLikeDialog -#define FBSDK_LIKE_METHOD_MIN_VERSION @"20140410" -#define FBSDK_LIKE_METHOD_NAME @"like" -#define FBSDK_SHARE_RESULT_COMPLETION_GESTURE_VALUE_LIKE @"like" -#define FBSDK_SHARE_RESULT_COMPLETION_GESTURE_VALUE_UNLIKE @"unlike" + #define FBSDK_LIKE_METHOD_MIN_VERSION @"20140410" + #define FBSDK_LIKE_METHOD_NAME @"like" + #define FBSDK_SHARE_RESULT_COMPLETION_GESTURE_VALUE_LIKE @"like" + #define FBSDK_SHARE_RESULT_COMPLETION_GESTURE_VALUE_UNLIKE @"unlike" -#pragma mark - Class Methods + #pragma mark - Class Methods + (void)initialize { @@ -58,7 +58,7 @@ + (instancetype)likeWithObjectID:(NSString *)objectID return dialog; } -#pragma mark - Public Methods + #pragma mark - Public Methods - (BOOL)canLike { @@ -83,14 +83,14 @@ - (BOOL)like NSMutableDictionary *parameters = [[NSMutableDictionary alloc] init]; [FBSDKTypeUtility dictionary:parameters setObject:self.objectID forKey:@"object_id"]; [FBSDKTypeUtility dictionary:parameters - setObject:NSStringFromFBSDKLikeObjectType(self.objectType) - forKey:@"object_type"]; - FBSDKBridgeAPIRequest * webRequest = [FBSDKBridgeAPIRequest bridgeAPIRequestWithProtocolType:FBSDKBridgeAPIProtocolTypeWeb - scheme:FBSDK_SHARE_JS_DIALOG_SCHEME - methodName:FBSDK_LIKE_METHOD_NAME - methodVersion:nil - parameters:parameters - userInfo:nil]; + setObject:NSStringFromFBSDKLikeObjectType(self.objectType) + forKey:@"object_type"]; + FBSDKBridgeAPIRequest *webRequest = [FBSDKBridgeAPIRequest bridgeAPIRequestWithProtocolType:FBSDKBridgeAPIProtocolTypeWeb + scheme:FBSDK_SHARE_JS_DIALOG_SCHEME + methodName:FBSDK_LIKE_METHOD_NAME + methodVersion:nil + parameters:parameters + userInfo:nil]; FBSDKBridgeAPIResponseBlock completionBlock = ^(FBSDKBridgeAPIResponse *response) { [self _handleCompletionWithDialogResults:response.responseParameters error:response.error]; }; @@ -107,22 +107,22 @@ - (BOOL)like void (^networkCompletionBlock)(FBSDKBridgeAPIResponse *) = ^(FBSDKBridgeAPIResponse *response) { if (response.error.code == FBSDKErrorAppVersionUnsupported) { [[FBSDKBridgeAPI sharedInstance] openBridgeAPIRequest:webRequest - useSafariViewController:useSafariViewController - fromViewController:self.fromViewController - completionBlock:completionBlock]; + useSafariViewController:useSafariViewController + fromViewController:self.fromViewController + completionBlock:completionBlock]; } else { completionBlock(response); } }; [[FBSDKBridgeAPI sharedInstance] openBridgeAPIRequest:nativeRequest - useSafariViewController:useSafariViewController - fromViewController:self.fromViewController - completionBlock:networkCompletionBlock]; + useSafariViewController:useSafariViewController + fromViewController:self.fromViewController + completionBlock:networkCompletionBlock]; } else { [[FBSDKBridgeAPI sharedInstance] openBridgeAPIRequest:webRequest - useSafariViewController:useSafariViewController - fromViewController:self.fromViewController - completionBlock:completionBlock]; + useSafariViewController:useSafariViewController + fromViewController:self.fromViewController + completionBlock:completionBlock]; } return YES; @@ -144,7 +144,7 @@ - (BOOL)validateWithError:(NSError *__autoreleasing *)errorRef return YES; } -#pragma mark - Helper Methods + #pragma mark - Helper Methods - (BOOL)_canLikeNative { diff --git a/FBSDKShareKit/FBSDKShareKit/Internal/FBSDKMessengerIcon.m b/FBSDKShareKit/FBSDKShareKit/Internal/FBSDKMessengerIcon.m index 42bb55fddd..a3fc2fb2aa 100644 --- a/FBSDKShareKit/FBSDKShareKit/Internal/FBSDKMessengerIcon.m +++ b/FBSDKShareKit/FBSDKShareKit/Internal/FBSDKMessengerIcon.m @@ -20,7 +20,7 @@ #if !TARGET_OS_TV -#import "FBSDKMessengerIcon.h" + #import "FBSDKMessengerIcon.h" @implementation FBSDKMessengerIcon diff --git a/FBSDKShareKit/FBSDKShareKit/Internal/FBSDKShareDefines.h b/FBSDKShareKit/FBSDKShareKit/Internal/FBSDKShareDefines.h index d3d8565e4a..3c4eda4d0b 100644 --- a/FBSDKShareKit/FBSDKShareKit/Internal/FBSDKShareDefines.h +++ b/FBSDKShareKit/FBSDKShareKit/Internal/FBSDKShareDefines.h @@ -28,16 +28,5 @@ #define FBSDK_SHARE_RESULT_DID_COMPLETE_KEY @"didComplete" #define FBSDK_SHARE_RESULT_PHOTO_IDS_KEY @"photo_ids" #define FBSDK_SHARE_RESULT_POST_ID_KEY @"postId" -#define FBSDK_SHARE_VIDEO_END_OFFSET @"end_offset" -#define FBSDK_SHARE_VIDEO_FILE_CHUNK @"video_file_chunk" -#define FBSDK_SHARE_VIDEO_ID @"video_id" -#define FBSDK_SHARE_VIDEO_SIZE @"file_size" -#define FBSDK_SHARE_VIDEO_START_OFFSET @"start_offset" -#define FBSDK_SHARE_VIDEO_UPLOAD_PHASE @"upload_phase" -#define FBSDK_SHARE_VIDEO_UPLOAD_PHASE_FINISH @"finish" -#define FBSDK_SHARE_VIDEO_UPLOAD_PHASE_START @"start" -#define FBSDK_SHARE_VIDEO_UPLOAD_PHASE_TRANSFER @"transfer" -#define FBSDK_SHARE_VIDEO_UPLOAD_SESSION_ID @"upload_session_id" -#define FBSDK_SHARE_VIDEO_UPLOAD_SUCCESS @"success" #define FBSDK_SHARE_WEB_PARAM_POST_ID_KEY @"post_id" #define FBSDK_SHARE_WEB_SCHEME @"https" diff --git a/FBSDKShareKit/FBSDKShareKit/Internal/FBSDKShareExtension.h b/FBSDKShareKit/FBSDKShareKit/Internal/FBSDKShareExtension.h index 96523d3e4e..05a26a8a84 100644 --- a/FBSDKShareKit/FBSDKShareKit/Internal/FBSDKShareExtension.h +++ b/FBSDKShareKit/FBSDKShareKit/Internal/FBSDKShareExtension.h @@ -20,7 +20,7 @@ #if !TARGET_OS_TV -#import + #import NS_ASSUME_NONNULL_BEGIN diff --git a/FBSDKShareKit/FBSDKShareKit/Internal/FBSDKShareExtension.m b/FBSDKShareKit/FBSDKShareKit/Internal/FBSDKShareExtension.m index b6403d3959..b2713ebca7 100644 --- a/FBSDKShareKit/FBSDKShareKit/Internal/FBSDKShareExtension.m +++ b/FBSDKShareKit/FBSDKShareKit/Internal/FBSDKShareExtension.m @@ -20,7 +20,7 @@ #if !TARGET_OS_TV -#import "FBSDKShareExtension.h" + #import "FBSDKShareExtension.h" NSString *const FBSDKShareExtensionParamAppID = @"app_id"; // application identifier string NSString *const FBSDKShareExtensionParamHashtags = @"hashtags"; // array of hashtag strings (max 1) diff --git a/FBSDKShareKit/FBSDKShareKit/Internal/FBSDKShareKit+Internal.h b/FBSDKShareKit/FBSDKShareKit/Internal/FBSDKShareKit+Internal.h index 31dad70250..76eba84815 100644 --- a/FBSDKShareKit/FBSDKShareKit/Internal/FBSDKShareKit+Internal.h +++ b/FBSDKShareKit/FBSDKShareKit/Internal/FBSDKShareKit+Internal.h @@ -18,8 +18,6 @@ #import -#import "FBSDKShareKit.h" - #import "FBSDKShareDefines.h" +#import "FBSDKShareKit.h" #import "FBSDKShareUtility.h" -#import "FBSDKVideoUploader.h" diff --git a/FBSDKShareKit/FBSDKShareKit/Internal/FBSDKShareUtility.m b/FBSDKShareKit/FBSDKShareKit/Internal/FBSDKShareUtility.m index 631fdfbaab..c88d0fdfe6 100644 --- a/FBSDKShareKit/FBSDKShareKit/Internal/FBSDKShareUtility.m +++ b/FBSDKShareKit/FBSDKShareKit/Internal/FBSDKShareUtility.m @@ -21,9 +21,9 @@ #import "FBSDKHashtag.h" #ifdef FBSDKCOCOAPODS -#import + #import #else -#import "FBSDKCoreKit+Internal.h" + #import "FBSDKCoreKit+Internal.h" #endif #import "FBSDKShareConstants.h" #import "FBSDKShareLinkContent.h" @@ -118,19 +118,19 @@ + (NSString *)buildWebShareTags:(NSArray *)peopleIDs + (void)buildAsyncWebPhotoContent:(FBSDKSharePhotoContent *)content completionHandler:(FBSDKWebPhotoContentBlock)completion { - void(^stageImageCompletion)(NSArray *) = ^(NSArray *stagedURIs) { + void (^stageImageCompletion)(NSArray *) = ^(NSArray *stagedURIs) { NSString *const methodName = @"share"; NSMutableDictionary *const parameters = - [[FBSDKShareUtility parametersForShareContent:content - bridgeOptions:FBSDKShareBridgeOptionsWebHashtag - shouldFailOnDataError:NO] mutableCopy]; + [[FBSDKShareUtility parametersForShareContent:content + bridgeOptions:FBSDKShareBridgeOptionsWebHashtag + shouldFailOnDataError:NO] mutableCopy]; [parameters removeObjectForKey:@"photos"]; NSString *const stagedURIJSONString = [FBSDKBasicUtility JSONStringForObject:stagedURIs error:nil invalidObjectHandler:NULL]; [FBSDKTypeUtility dictionary:parameters - setObject:stagedURIJSONString - forKey:@"media"]; + setObject:stagedURIJSONString + forKey:@"media"]; [FBSDKTypeUtility dictionary:parameters setObject:[FBSDKShareUtility buildWebShareTags:content.peopleIDs] forKey:@"tags"]; if (completion != NULL) { completion(YES, methodName, [parameters copy]); @@ -175,10 +175,12 @@ + (UIImage *)imageWithCircleColor:(UIColor *)color canvasSize:(CGSize)canvasSize circleSize:(CGSize)circleSize { - CGRect circleFrame = CGRectMake((canvasSize.width - circleSize.width) / 2.0, - (canvasSize.height - circleSize.height) / 2.0, - circleSize.width, - circleSize.height); + CGRect circleFrame = CGRectMake( + (canvasSize.width - circleSize.width) / 2.0, + (canvasSize.height - circleSize.height) / 2.0, + circleSize.width, + circleSize.height + ); UIGraphicsBeginImageContextWithOptions(canvasSize, NO, 0); CGContextRef context = UIGraphicsGetCurrentContext(); [color setFill]; @@ -280,8 +282,7 @@ + (BOOL)validateShareContent:(id)shareContent { if (![self validateRequiredValue:shareContent name:@"shareContent" error:errorRef]) { return NO; - } - else if ([shareContent respondsToSelector:@selector(validateWithOptions:error:)]) { + } else if ([shareContent respondsToSelector:@selector(validateWithOptions:error:)]) { return [shareContent validateWithOptions:bridgeOptions error:errorRef]; } else { if (errorRef != NULL) { @@ -332,7 +333,7 @@ + (id)_convertObject:(id)object } + (void)_stageImagesForPhotoContent:(FBSDKSharePhotoContent *)content - withCompletionHandler:(void(^)(NSArray *))completion + withCompletionHandler:(void (^)(NSArray *))completion { __block NSMutableArray *stagedURIs = [NSMutableArray array]; dispatch_group_t group = dispatch_group_create(); @@ -340,8 +341,8 @@ + (void)_stageImagesForPhotoContent:(FBSDKSharePhotoContent *)content if (photo.image != nil) { dispatch_group_enter(group); NSDictionary *stagingParameters = @{ - @"file" : photo.image, - }; + @"file" : photo.image, + }; FBSDKGraphRequest *request = [[FBSDKGraphRequest alloc] initWithGraphPath:@"me/staging_resources" parameters:stagingParameters HTTPMethod:@"POST"]; @@ -355,11 +356,12 @@ + (void)_stageImagesForPhotoContent:(FBSDKSharePhotoContent *)content } } - dispatch_group_notify(group, dispatch_get_main_queue(), ^{ - if (completion != NULL) { - completion([stagedURIs copy]); - } - }); + dispatch_group_notify(group, + dispatch_get_main_queue(), ^{ + if (completion != NULL) { + completion([stagedURIs copy]); + } + }); } + (void)_testObject:(id)object containsMedia:(BOOL *)containsMediaRef containsPhotos:(BOOL *)containsPhotosRef containsVideos:(BOOL *)containsVideosRef @@ -483,10 +485,10 @@ + (BOOL)validateNetworkURL:(NSURL *)URL name:(NSString *)name error:(NSError *__ + (BOOL)validateRequiredValue:(id)value name:(NSString *)name error:(NSError *__autoreleasing *)errorRef { - if (!value || - ([value isKindOfClass:[NSString class]] && !((NSString *)value).length) || - ([value isKindOfClass:[NSArray class]] && !((NSArray *)value).count) || - ([value isKindOfClass:[NSDictionary class]] && !((NSDictionary *)value).count)) { + if (!value + || ([value isKindOfClass:[NSString class]] && !((NSString *)value).length) + || ([value isKindOfClass:[NSArray class]] && !((NSArray *)value).count) + || ([value isKindOfClass:[NSDictionary class]] && !((NSDictionary *)value).count)) { if (errorRef != NULL) { *errorRef = [FBSDKError requiredArgumentErrorWithDomain:FBSDKShareErrorDomain name:name diff --git a/FBSDKShareKit/FBSDKShareKit/include/FBSDKDeviceShareButton.h b/FBSDKShareKit/FBSDKShareKit/include/FBSDKDeviceShareButton.h deleted file mode 120000 index 7d3f136e32..0000000000 --- a/FBSDKShareKit/FBSDKShareKit/include/FBSDKDeviceShareButton.h +++ /dev/null @@ -1 +0,0 @@ -../FBSDKDeviceShareButton.h \ No newline at end of file diff --git a/FBSDKShareKit/FBSDKShareKit/include/FBSDKDeviceShareViewController.h b/FBSDKShareKit/FBSDKShareKit/include/FBSDKDeviceShareViewController.h deleted file mode 120000 index b92b5af1cb..0000000000 --- a/FBSDKShareKit/FBSDKShareKit/include/FBSDKDeviceShareViewController.h +++ /dev/null @@ -1 +0,0 @@ -../FBSDKDeviceShareViewController.h \ No newline at end of file diff --git a/FBSDKShareKit/FBSDKShareKitTests/FBSDKMessageDialogTests.m b/FBSDKShareKit/FBSDKShareKitTests/FBSDKMessageDialogTests.m index 576564bb04..afb150373d 100644 --- a/FBSDKShareKit/FBSDKShareKitTests/FBSDKMessageDialogTests.m +++ b/FBSDKShareKit/FBSDKShareKitTests/FBSDKMessageDialogTests.m @@ -16,31 +16,29 @@ // 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 #ifdef BUCK -#import + #import #else @import FBSDKCoreKit; #endif -#import "FBSDKMessageDialog.h" -#import "FakeSharingDelegate.h" - #import #import "FBSDKCoreKit+Internal.h" +#import "FBSDKMessageDialog.h" #import "FBSDKShareKitTestUtility.h" #import "FBSDKShareModelTestUtility.h" +#import "FakeSharingDelegate.h" @interface FBSDKMessageDialogTests : XCTestCase @end @implementation FBSDKMessageDialogTests -- (void)_mockApplicationForURL:(NSURL *)URL canOpen:(BOOL)canOpen usingBlock:(void(^)(void))block +- (void)_mockApplicationForURL:(NSURL *)URL canOpen:(BOOL)canOpen usingBlock:(void (^)(void))block { if (block != NULL) { id applicationMock = OCMClassMock(UIApplication.class); @@ -85,31 +83,47 @@ - (void)testValidate FBSDKMessageDialog *dialog = [[FBSDKMessageDialog alloc] init]; NSError *error; dialog.shareContent = [FBSDKShareModelTestUtility linkContent]; - XCTAssertTrue([dialog validateWithError:&error], - @"Known valid content should pass validation without issue if this test fails then the criteria for the fixture may no longer be valid"); - XCTAssertNil(error, - @"A successful validation should not populate the error reference that was passed to it"); + XCTAssertTrue( + [dialog validateWithError:&error], + @"Known valid content should pass validation without issue if this test fails then the criteria for the fixture may no longer be valid" + ); + XCTAssertNil( + error, + @"A successful validation should not populate the error reference that was passed to it" + ); dialog.shareContent = [FBSDKShareModelTestUtility photoContentWithImages]; error = nil; - XCTAssertTrue([dialog validateWithError:&error], - @"Known valid content should pass validation without issue if this test fails then the criteria for the fixture may no longer be valid"); - XCTAssertNil(error, - @"A successful validation should not populate the error reference that was passed to it"); + XCTAssertTrue( + [dialog validateWithError:&error], + @"Known valid content should pass validation without issue if this test fails then the criteria for the fixture may no longer be valid" + ); + XCTAssertNil( + error, + @"A successful validation should not populate the error reference that was passed to it" + ); dialog.shareContent = [FBSDKShareModelTestUtility videoContentWithoutPreviewPhoto]; error = nil; - XCTAssertTrue([dialog validateWithError:&error], - @"Known valid content should pass validation without issue if this test fails then the criteria for the fixture may no longer be valid"); - XCTAssertNil(error, - @"A successful validation should not populate the error reference that was passed to it"); + XCTAssertTrue( + [dialog validateWithError:&error], + @"Known valid content should pass validation without issue if this test fails then the criteria for the fixture may no longer be valid" + ); + XCTAssertNil( + error, + @"A successful validation should not populate the error reference that was passed to it" + ); dialog.shareContent = [FBSDKShareModelTestUtility cameraEffectContent]; error = nil; - XCTAssertFalse([dialog validateWithError:&error], - @"Should not successfully validate share content that is known to be missing content"); - XCTAssertNotNil(error, - @"A failed validation should populate the error reference that was passed to it"); + XCTAssertFalse( + [dialog validateWithError:&error], + @"Should not successfully validate share content that is known to be missing content" + ); + XCTAssertNotNil( + error, + @"A failed validation should populate the error reference that was passed to it" + ); } - (void)testShowInvokesDelegateWhenCannotShow @@ -121,13 +135,21 @@ - (void)testShowInvokesDelegateWhenCannotShow [dialog show]; - XCTAssertEqualObjects(delegate.capturedError.domain, FBSDKShareErrorDomain, - "Failure to show a message dialog should present an error with the expected domain."); - XCTAssertEqual(delegate.capturedError.code, FBSDKShareErrorDialogNotAvailable, - "Failure to show a message dialog should present an error with the expected code."); - XCTAssertEqualObjects(delegate.capturedError.userInfo[FBSDKErrorDeveloperMessageKey], - @"Message dialog is not available.", - "Failure to show a message dialog should present an error with the expected message."); + XCTAssertEqualObjects( + delegate.capturedError.domain, + FBSDKShareErrorDomain, + "Failure to show a message dialog should present an error with the expected domain." + ); + XCTAssertEqual( + delegate.capturedError.code, + FBSDKShareErrorDialogNotAvailable, + "Failure to show a message dialog should present an error with the expected code." + ); + XCTAssertEqualObjects( + delegate.capturedError.userInfo[FBSDKErrorDeveloperMessageKey], + @"Message dialog is not available.", + "Failure to show a message dialog should present an error with the expected message." + ); }]; } @@ -140,13 +162,21 @@ - (void)testShowInvokesDelegateWhenMissingContent [dialog show]; - XCTAssertEqualObjects(delegate.capturedError.domain, FBSDKShareErrorDomain, - "Failure to show a message dialog should present an error with the expected domain."); - XCTAssertEqual(delegate.capturedError.code, FBSDKErrorInvalidArgument, - "Failure to show a message dialog should present an error with the expected code."); - XCTAssertEqualObjects(delegate.capturedError.userInfo[FBSDKErrorDeveloperMessageKey], - @"Value for shareContent is required.", - "Failure to show a message dialog should present an error with the expected message."); + XCTAssertEqualObjects( + delegate.capturedError.domain, + FBSDKShareErrorDomain, + "Failure to show a message dialog should present an error with the expected domain." + ); + XCTAssertEqual( + delegate.capturedError.code, + FBSDKErrorInvalidArgument, + "Failure to show a message dialog should present an error with the expected code." + ); + XCTAssertEqualObjects( + delegate.capturedError.userInfo[FBSDKErrorDeveloperMessageKey], + @"Value for shareContent is required.", + "Failure to show a message dialog should present an error with the expected message." + ); }]; } @@ -161,13 +191,21 @@ - (void)testShowInvokesDelegateWhenCannotValidate [dialog show]; - XCTAssertEqualObjects(delegate.capturedError.domain, FBSDKShareErrorDomain, - "Failure to show a message dialog should present an error with the expected domain."); - XCTAssertEqual(delegate.capturedError.code, FBSDKErrorInvalidArgument, - "Failure to show a message dialog should present an error with the expected code."); - XCTAssertEqualObjects(delegate.capturedError.userInfo[FBSDKErrorDeveloperMessageKey], - @"Message dialog does not support FBSDKShareCameraEffectContent.", - "Failure to show a message dialog should present an error with the expected message."); + XCTAssertEqualObjects( + delegate.capturedError.domain, + FBSDKShareErrorDomain, + "Failure to show a message dialog should present an error with the expected domain." + ); + XCTAssertEqual( + delegate.capturedError.code, + FBSDKErrorInvalidArgument, + "Failure to show a message dialog should present an error with the expected code." + ); + XCTAssertEqualObjects( + delegate.capturedError.userInfo[FBSDKErrorDeveloperMessageKey], + @"Message dialog does not support FBSDKShareCameraEffectContent.", + "Failure to show a message dialog should present an error with the expected message." + ); }]; } diff --git a/FBSDKShareKit/FBSDKShareKitTests/FBSDKShareDialogTests.m b/FBSDKShareKit/FBSDKShareKitTests/FBSDKShareDialogTests.m index f5079892e3..85902a3caa 100644 --- a/FBSDKShareKit/FBSDKShareKitTests/FBSDKShareDialogTests.m +++ b/FBSDKShareKit/FBSDKShareKitTests/FBSDKShareDialogTests.m @@ -16,23 +16,21 @@ // 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 - #ifdef BUCK -#import + #import #else @import FBSDKCoreKit; #endif -#import "FBSDKHashtag.h" -#import "FBSDKShareDialog.h" - #import "FBSDKCoreKit+Internal.h" +#import "FBSDKHashtag.h" #import "FBSDKShareDefines.h" +#import "FBSDKShareDialog.h" #import "FBSDKShareKitTestUtility.h" #import "FBSDKShareModelTestUtility.h" @@ -41,7 +39,7 @@ @interface FBSDKShareDialogTests : XCTestCase @implementation FBSDKShareDialogTests -- (void)_mockApplicationForURL:(NSURL *)URL canOpen:(BOOL)canOpen usingBlock:(void(^)(void))block +- (void)_mockApplicationForURL:(NSURL *)URL canOpen:(BOOL)canOpen usingBlock:(void (^)(void))block { if (block != NULL) { id applicationMock = [OCMockObject mockForClass:[UIApplication class]]; @@ -56,7 +54,7 @@ - (void)_mockApplicationForURL:(NSURL *)URL canOpen:(BOOL)canOpen usingBlock:(vo } } -- (void)_mockUseNativeDialogUsingBlock:(void(^)(void))block +- (void)_mockUseNativeDialogUsingBlock:(void (^)(void))block { if (block != NULL) { id configurationMock = [OCMockObject mockForClass:[FBSDKServerConfiguration class]]; @@ -77,56 +75,70 @@ - (void)setUp #pragma mark - Native -- (void)testCanShowNativeDialogWithoutShareContent { +- (void)testCanShowNativeDialogWithoutShareContent +{ FBSDKShareDialog *const dialog = [[FBSDKShareDialog alloc] init]; dialog.mode = FBSDKShareDialogModeNative; [self _mockApplicationForURL:OCMOCK_ANY canOpen:YES usingBlock:^{ [self _mockUseNativeDialogUsingBlock:^{ - XCTAssertTrue([dialog canShow], - @"A dialog without share content should be showable on a native dialog"); + XCTAssertTrue( + [dialog canShow], + @"A dialog without share content should be showable on a native dialog" + ); }]; }]; } -- (void)testCanShowNativeLinkContent { +- (void)testCanShowNativeLinkContent +{ FBSDKShareDialog *const dialog = [[FBSDKShareDialog alloc] init]; dialog.mode = FBSDKShareDialogModeNative; [self _mockUseNativeDialogUsingBlock:^{ - dialog.shareContent = [FBSDKShareModelTestUtility linkContent]; - XCTAssertTrue([dialog canShow], - @"A dialog with valid link content should be showable on a native dialog"); + XCTAssertTrue( + [dialog canShow], + @"A dialog with valid link content should be showable on a native dialog" + ); }]; } -- (void)testCanShowNativePhotoContent { +- (void)testCanShowNativePhotoContent +{ FBSDKShareDialog *const dialog = [[FBSDKShareDialog alloc] init]; dialog.mode = FBSDKShareDialogModeNative; [self _mockUseNativeDialogUsingBlock:^{ dialog.shareContent = [FBSDKShareModelTestUtility photoContent]; - XCTAssertFalse([dialog canShow], - @"Photo content with photos that have web urls should not be showable on a native dialog"); + XCTAssertFalse( + [dialog canShow], + @"Photo content with photos that have web urls should not be showable on a native dialog" + ); }]; } -- (void)testCanShowNativePhotoContentWithFileURL { +- (void)testCanShowNativePhotoContentWithFileURL +{ FBSDKShareDialog *const dialog = [[FBSDKShareDialog alloc] init]; dialog.mode = FBSDKShareDialogModeNative; [self _mockUseNativeDialogUsingBlock:^{ dialog.shareContent = [FBSDKShareModelTestUtility photoContentWithFileURLs]; - XCTAssertTrue([dialog canShow], - @"Photo content with photos that have file urls should be showable on a native dialog"); + XCTAssertTrue( + [dialog canShow], + @"Photo content with photos that have file urls should be showable on a native dialog" + ); }]; } -- (void)testCanShowNativeVideoContentWithoutPreviewPhoto { +- (void)testCanShowNativeVideoContentWithoutPreviewPhoto +{ FBSDKShareDialog *const dialog = [[FBSDKShareDialog alloc] init]; dialog.mode = FBSDKShareDialogModeNative; [self _mockApplicationForURL:OCMOCK_ANY canOpen:YES usingBlock:^{ [self _mockUseNativeDialogUsingBlock:^{ dialog.shareContent = [FBSDKShareModelTestUtility videoContentWithoutPreviewPhoto]; - XCTAssertTrue([dialog canShow], - @"Video content without a preview photo should be showable on a native dialog"); + XCTAssertTrue( + [dialog canShow], + @"Video content without a preview photo should be showable on a native dialog" + ); }]; }]; } @@ -137,8 +149,10 @@ - (void)testCanShowNative dialog.mode = FBSDKShareDialogModeNative; [self _mockApplicationForURL:OCMOCK_ANY canOpen:NO usingBlock:^{ [self _mockUseNativeDialogUsingBlock:^{ - XCTAssertFalse([dialog canShow], - @"A native dialog should not be showable if the application is unable to open a url, this can also occur if the api scheme is not whitelisted in the third party app or if the application cannot handle the share API scheme"); + XCTAssertFalse( + [dialog canShow], + @"A native dialog should not be showable if the application is unable to open a url, this can also occur if the api scheme is not whitelisted in the third party app or if the application cannot handle the share API scheme" + ); }]; }]; } @@ -180,19 +194,27 @@ - (void)testCanShowBrowser { FBSDKShareDialog *const dialog = [[FBSDKShareDialog alloc] init]; dialog.mode = FBSDKShareDialogModeBrowser; - XCTAssertTrue([dialog canShow], - @"A dialog without share content should be showable in a browser"); + XCTAssertTrue( + [dialog canShow], + @"A dialog without share content should be showable in a browser" + ); dialog.shareContent = [FBSDKShareModelTestUtility linkContent]; - XCTAssertTrue([dialog canShow], - @"A dialog with link content should be showable in a browser"); + XCTAssertTrue( + [dialog canShow], + @"A dialog with link content should be showable in a browser" + ); [self _performBlockWithAccessToken:^{ dialog.shareContent = [FBSDKShareModelTestUtility photoContentWithFileURLs]; - XCTAssertTrue([dialog canShow], - @"A dialog with photo content with file urls should be showable in a browser when there is a current access token"); + XCTAssertTrue( + [dialog canShow], + @"A dialog with photo content with file urls should be showable in a browser when there is a current access token" + ); dialog.shareContent = [FBSDKShareModelTestUtility videoContentWithoutPreviewPhoto]; - XCTAssertTrue([dialog canShow], - @"A dialog with video content without a preview photo should be showable in a browser when there is a current access token"); + XCTAssertTrue( + [dialog canShow], + @"A dialog with video content without a preview photo should be showable in a browser when there is a current access token" + ); }]; } @@ -224,18 +246,26 @@ - (void)testCanShowWeb { FBSDKShareDialog *const dialog = [[FBSDKShareDialog alloc] init]; dialog.mode = FBSDKShareDialogModeWeb; - XCTAssertTrue([dialog canShow], - @"A dialog without share content should be showable on web"); + XCTAssertTrue( + [dialog canShow], + @"A dialog without share content should be showable on web" + ); dialog.shareContent = [FBSDKShareModelTestUtility linkContent]; - XCTAssertTrue([dialog canShow], - @"A dialog with link content should be showable on web"); + XCTAssertTrue( + [dialog canShow], + @"A dialog with link content should be showable on web" + ); [self _performBlockWithAccessToken:^{ dialog.shareContent = [FBSDKShareModelTestUtility photoContent]; - XCTAssertFalse([dialog canShow], - @"A dialog with photos should not be showable on web"); + XCTAssertFalse( + [dialog canShow], + @"A dialog with photos should not be showable on web" + ); dialog.shareContent = [FBSDKShareModelTestUtility videoContentWithoutPreviewPhoto]; - XCTAssertFalse([dialog canShow], - @"A dialog with content that contains local media should not be showable on web"); + XCTAssertFalse( + [dialog canShow], + @"A dialog with content that contains local media should not be showable on web" + ); }]; } @@ -250,34 +280,54 @@ - (void)testValidateWeb [self _performBlockWithAccessToken:^{ dialog.shareContent = [FBSDKShareModelTestUtility photoContent]; - XCTAssertFalse([dialog validateWithError:&error], - @"A dialog with photo content that points to remote urls should not be considered valid on web"); - XCTAssertNotNil(error, - @"Validating a dialog with photo content on web should provide a meaningful error"); + XCTAssertFalse( + [dialog validateWithError:&error], + @"A dialog with photo content that points to remote urls should not be considered valid on web" + ); + XCTAssertNotNil( + error, + @"Validating a dialog with photo content on web should provide a meaningful error" + ); dialog.shareContent = [FBSDKShareModelTestUtility photoContentWithImages]; - XCTAssertFalse([dialog validateWithError:&error], - @"A dialog with photo content that is already loaded should not be considered valid on web"); - XCTAssertNotNil(error, - @"Validating a dialog with photo content that is already loaded on web should provide a meaningful error"); + XCTAssertFalse( + [dialog validateWithError:&error], + @"A dialog with photo content that is already loaded should not be considered valid on web" + ); + XCTAssertNotNil( + error, + @"Validating a dialog with photo content that is already loaded on web should provide a meaningful error" + ); dialog.shareContent = [FBSDKShareModelTestUtility photoContentWithFileURLs]; - XCTAssertFalse([dialog validateWithError:&error], - @"A dialog with photo content that points to file urls should not be considered valid on web"); - XCTAssertNotNil(error, - @"Validating a dialog with photo content that points to file urls on web should provide a meaningful error"); + XCTAssertFalse( + [dialog validateWithError:&error], + @"A dialog with photo content that points to file urls should not be considered valid on web" + ); + XCTAssertNotNil( + error, + @"Validating a dialog with photo content that points to file urls on web should provide a meaningful error" + ); dialog.shareContent = [FBSDKShareModelTestUtility videoContentWithoutPreviewPhoto]; - XCTAssertFalse([dialog validateWithError:&error], - @"A dialog that includes local media should not be considered valid on web"); - XCTAssertNotNil(error, - @"Validating a dialog that includes local media should provide a meaningful error"); + XCTAssertFalse( + [dialog validateWithError:&error], + @"A dialog that includes local media should not be considered valid on web" + ); + XCTAssertNotNil( + error, + @"Validating a dialog that includes local media should provide a meaningful error" + ); }]; [self _performBlockWithNilAccessToken:^{ - XCTAssertFalse([dialog validateWithError:&error], - @"A dialog with content but no access token should not be considered valid on web"); - XCTAssertNotNil(error, - @"Validating a dialog with content but no access token should provide a meaningful error"); + XCTAssertFalse( + [dialog validateWithError:&error], + @"A dialog with content but no access token should not be considered valid on web" + ); + XCTAssertNotNil( + error, + @"Validating a dialog with content but no access token should provide a meaningful error" + ); }]; } @@ -287,17 +337,25 @@ - (void)testCanShowFeedBrowser { FBSDKShareDialog *const dialog = [[FBSDKShareDialog alloc] init]; dialog.mode = FBSDKShareDialogModeFeedBrowser; - XCTAssertTrue([dialog canShow], - @"A dialog without content should be showable in a browser feed"); + XCTAssertTrue( + [dialog canShow], + @"A dialog without content should be showable in a browser feed" + ); dialog.shareContent = [FBSDKShareModelTestUtility linkContent]; - XCTAssertTrue([dialog canShow], - @"A dialog with link content should be showable in a browser feed"); + XCTAssertTrue( + [dialog canShow], + @"A dialog with link content should be showable in a browser feed" + ); dialog.shareContent = [FBSDKShareModelTestUtility photoContent]; - XCTAssertFalse([dialog canShow], - @"A dialog with photo content should not be showable in a browser feed"); + XCTAssertFalse( + [dialog canShow], + @"A dialog with photo content should not be showable in a browser feed" + ); dialog.shareContent = [FBSDKShareModelTestUtility videoContentWithoutPreviewPhoto]; - XCTAssertFalse([dialog canShow], - @"A dialog with video content that has no preview photo should not be showable in a browser feed"); + XCTAssertFalse( + [dialog canShow], + @"A dialog with video content that has no preview photo should not be showable in a browser feed" + ); } - (void)testValidateFeedBrowser @@ -322,17 +380,25 @@ - (void)testCanShowFeedWeb { FBSDKShareDialog *const dialog = [[FBSDKShareDialog alloc] init]; dialog.mode = FBSDKShareDialogModeFeedWeb; - XCTAssertTrue([dialog canShow], - @"A dialog without content should be showable in a web feed"); + XCTAssertTrue( + [dialog canShow], + @"A dialog without content should be showable in a web feed" + ); dialog.shareContent = [FBSDKShareModelTestUtility linkContent]; - XCTAssertTrue([dialog canShow], - @"A dialog with link content should be showable in a web feed"); + XCTAssertTrue( + [dialog canShow], + @"A dialog with link content should be showable in a web feed" + ); dialog.shareContent = [FBSDKShareModelTestUtility photoContent]; - XCTAssertFalse([dialog canShow], - @"A dialog with photo content should not be showable in a web feed"); + XCTAssertFalse( + [dialog canShow], + @"A dialog with photo content should not be showable in a web feed" + ); dialog.shareContent = [FBSDKShareModelTestUtility videoContentWithoutPreviewPhoto]; - XCTAssertFalse([dialog canShow], - @"A dialog with video content and no preview photo should not be showable in a web feed"); + XCTAssertFalse( + [dialog canShow], + @"A dialog with video content and no preview photo should not be showable in a web feed" + ); } - (void)testValidateFeedWeb @@ -359,12 +425,11 @@ - (void)testThatInitialTextIsSetCorrectlyWhenShareExtensionIsAvailable content.quote = @"a quote"; dialog.shareContent = content; - NSDictionary *expectedJSON = @{@"app_id":@"appID", @"hashtags":@[@"#hashtag"], @"quotes":@[@"a quote"]}; + NSDictionary *expectedJSON = @{@"app_id" : @"appID", @"hashtags" : @[@"#hashtag"], @"quotes" : @[@"a quote"]}; [self _showDialog:dialog - appID:@"appID" -shareSheetAvailable:YES -expectedPreJSONtext:@"fb-app-id:appID #hashtag" - expectedJSON:expectedJSON]; + appID:@"appID" + expectedPreJSONtext:@"fb-app-id:appID #hashtag" + expectedJSON:expectedJSON]; } #pragma mark - Camera Share @@ -444,18 +509,6 @@ - (void)testShowCameraShareToFBWhenPlayerNotInstalled #pragma mark - FullyCompatible Validation -- (void)testThatInitialTextIsSetCorrectlyWhenShareExtensionIsNOTAvailable -{ - FBSDKShareDialog *const dialog = [[FBSDKShareDialog alloc] init]; - FBSDKShareLinkContent *content = [FBSDKShareModelTestUtility linkContentWithoutQuote]; - content.hashtag = [FBSDKHashtag hashtagWithString:@"#hashtag"]; - dialog.shareContent = content; - [self _showDialog:dialog - appID:@"appID" -shareSheetAvailable:NO -expectedPreJSONtext:@"#hashtag" expectedJSON:nil]; -} - - (void)testThatValidateWithErrorReturnsNOForLinkQuoteIfAValidShareExtensionVersionIsNotAvailable { [self _testValidateShareContent:[FBSDKShareModelTestUtility linkContent] @@ -472,7 +525,6 @@ - (void)testThatValidateWithErrorReturnsYESForLinkQuoteIfAValidShareExtensionVer expectShow:YES mode:FBSDKShareDialogModeShareSheet nonSupportedScheme:nil]; - } - (void)testThatValidateWithErrorReturnsNOForMMPIfAValidShareExtensionVersionIsNotAvailable @@ -512,12 +564,10 @@ - (void)_testValidateShareContent:(id)shareContent { id mockApplication = [OCMockObject niceMockForClass:[UIApplication class]]; [[[mockApplication stub] andReturn:mockApplication] sharedApplication]; - [[[mockApplication stub] andReturnValue:@YES] canOpenURL:[OCMArg checkWithBlock:^BOOL(NSURL *url) { + [[[mockApplication stub] andReturnValue:@YES] canOpenURL:[OCMArg checkWithBlock:^BOOL (NSURL *url) { return ![url.absoluteString isEqualToString:nonSupportedScheme]; }]]; - NSOperatingSystemVersion iOS8Version = { .majorVersion = 8, .minorVersion = 0, .patchVersion = 0 }; id mockInternalUtility = [OCMockObject niceMockForClass:[FBSDKInternalUtility class]]; - [[[mockInternalUtility stub] andReturnValue:@YES] isOSRunTimeVersionAtLeast:iOS8Version]; id mockSLController = [OCMockObject niceMockForClass:[fbsdkdfl_SLComposeViewControllerClass() class]]; [[[mockSLController stub] andReturn:mockSLController] composeViewControllerForServiceType:OCMOCK_ANY]; [[[mockSLController stub] andReturnValue:@YES] isAvailableForServiceType:OCMOCK_ANY]; @@ -542,24 +592,21 @@ - (void)_testValidateShareContent:(id)shareContent [mockSLController stopMocking]; } -- (void)_showDialog:(FBSDKShareDialog *)dialog - appID:(NSString *)appID -shareSheetAvailable:(BOOL)shareSheetAvailable -expectedPreJSONtext:(NSString *)expectedPreJSONText - expectedJSON:(NSDictionary *)expectedJSON +- (void) _showDialog:(FBSDKShareDialog *)dialog + appID:(NSString *)appID + expectedPreJSONtext:(NSString *)expectedPreJSONText + expectedJSON:(NSDictionary *)expectedJSON { id mockApplication = [OCMockObject niceMockForClass:[UIApplication class]]; [[[mockApplication stub] andReturn:mockApplication] sharedApplication]; [[[mockApplication stub] andReturnValue:@YES] canOpenURL:OCMOCK_ANY]; - NSOperatingSystemVersion iOS8Version = { .majorVersion = 8, .minorVersion = 0, .patchVersion = 0 }; id mockInternalUtility = [OCMockObject niceMockForClass:[FBSDKInternalUtility class]]; - [[[mockInternalUtility stub] andReturnValue:@(shareSheetAvailable)] isOSRunTimeVersionAtLeast:iOS8Version]; id settingsClassMock = [OCMockObject niceMockForClass:[FBSDKSettings class]]; [[[settingsClassMock stub] andReturn:appID] appID]; id mockSLController = [OCMockObject niceMockForClass:[fbsdkdfl_SLComposeViewControllerClass() class]]; [[[mockSLController stub] andReturn:mockSLController] composeViewControllerForServiceType:OCMOCK_ANY]; [[[mockSLController stub] andReturnValue:@YES] isAvailableForServiceType:OCMOCK_ANY]; - [[mockSLController expect] setInitialText:[OCMArg checkWithBlock:^BOOL(NSString *text) { + [[mockSLController expect] setInitialText:[OCMArg checkWithBlock:^BOOL (NSString *text) { NSRange JSONDelimiterRange = [text rangeOfString:@"|"]; NSString *preJSONText; NSDictionary *json; @@ -567,11 +614,11 @@ - (void)_showDialog:(FBSDKShareDialog *)dialog preJSONText = text; } else { preJSONText = [text substringToIndex:JSONDelimiterRange.location]; - NSString *jsonText = [text substringFromIndex:JSONDelimiterRange.location+1]; + NSString *jsonText = [text substringFromIndex:JSONDelimiterRange.location + 1]; json = [FBSDKTypeUtility JSONObjectWithData:[jsonText dataUsingEncoding:NSUTF8StringEncoding] options:0 error:nil]; } - return ((expectedPreJSONText == nil && preJSONText == nil) || [expectedPreJSONText isEqualToString:preJSONText]) && - ((expectedJSON == nil && json == nil) || [expectedJSON isEqual:json]); + return ((expectedPreJSONText == nil && preJSONText == nil) || [expectedPreJSONText isEqualToString:preJSONText]) + && ((expectedJSON == nil && json == nil) || [expectedJSON isEqual:json]); }]]; UIViewController *vc = [UIViewController new]; @@ -593,7 +640,7 @@ - (void)_showNativeDialog:(FBSDKShareDialog *)dialog { id mockApplication = [OCMockObject niceMockForClass:[UIApplication class]]; [[[mockApplication stub] andReturn:mockApplication] sharedApplication]; - [[[mockApplication stub] andReturnValue:@YES] canOpenURL:[OCMArg checkWithBlock:^BOOL(NSURL *url) { + [[[mockApplication stub] andReturnValue:@YES] canOpenURL:[OCMArg checkWithBlock:^BOOL (NSURL *url) { return ![url.absoluteString isEqualToString:nonSupportedScheme]; }]]; id settingsClassMock = [OCMockObject niceMockForClass:[FBSDKSettings class]]; @@ -604,14 +651,14 @@ - (void)_showNativeDialog:(FBSDKShareDialog *)dialog id mockSDKBridgeAPI = [OCMockObject niceMockForClass:[FBSDKBridgeAPI class]]; [[[mockSDKBridgeAPI stub] andReturn:mockSDKBridgeAPI] sharedInstance]; // Check API bridge request - [[mockSDKBridgeAPI expect] openBridgeAPIRequest:[OCMArg checkWithBlock:^BOOL(FBSDKBridgeAPIRequest *request) { + [[mockSDKBridgeAPI expect] openBridgeAPIRequest:[OCMArg checkWithBlock:^BOOL (FBSDKBridgeAPIRequest *request) { XCTAssertEqualObjects(request.scheme, scheme); XCTAssertEqualObjects(request.methodName, methodName); return YES; }] - useSafariViewController:(BOOL)OCMOCK_ANY - fromViewController:OCMOCK_ANY - completionBlock:OCMOCK_ANY]; + useSafariViewController:(BOOL)OCMOCK_ANY + fromViewController:OCMOCK_ANY + completionBlock:OCMOCK_ANY]; UIViewController *vc = [UIViewController new]; dialog.fromViewController = vc; @@ -622,6 +669,7 @@ - (void)_showNativeDialog:(FBSDKShareDialog *)dialog [settingsClassMock stopMocking]; [mockApplication stopMocking]; } + - (void)_performBlockWithAccessToken:(dispatch_block_t)block { FBSDKAccessToken *accessToken = [[FBSDKAccessToken alloc] initWithTokenString:@"FBSDKShareDialogTests" diff --git a/FBSDKShareKit/FBSDKShareKitTests/FBSDKShareKitTestUtility.m b/FBSDKShareKit/FBSDKShareKitTests/FBSDKShareKitTestUtility.m index 02663359e7..b00dce863e 100644 --- a/FBSDKShareKit/FBSDKShareKitTests/FBSDKShareKitTestUtility.m +++ b/FBSDKShareKit/FBSDKShareKitTests/FBSDKShareKitTestUtility.m @@ -18,12 +18,11 @@ #import "FBSDKShareKitTestUtility.h" -#import - #import +#import #import -#import +#import #import "FBSDKCoreKit+Internal.h" #import "FBSDKShareDialog.h" diff --git a/FBSDKShareKit/FBSDKShareKitTests/FakeSharingDelegate.h b/FBSDKShareKit/FBSDKShareKitTests/FakeSharingDelegate.h index 040d51fee1..ba3c50d5ed 100644 --- a/FBSDKShareKit/FBSDKShareKitTests/FakeSharingDelegate.h +++ b/FBSDKShareKit/FBSDKShareKitTests/FakeSharingDelegate.h @@ -22,7 +22,7 @@ NS_ASSUME_NONNULL_BEGIN -@interface FakeSharingDelegate : NSObject +@interface FakeSharingDelegate : NSObject @property (nonatomic) NSDictionary *capturedResults; @property (nonatomic) NSError *capturedError; diff --git a/FBSDKShareKit/FBSDKShareKitTests/FakeSharingDelegate.m b/FBSDKShareKit/FBSDKShareKitTests/FakeSharingDelegate.m index 6cc669fdde..cf8ae8ada1 100644 --- a/FBSDKShareKit/FBSDKShareKitTests/FakeSharingDelegate.m +++ b/FBSDKShareKit/FBSDKShareKitTests/FakeSharingDelegate.m @@ -20,15 +20,18 @@ @implementation FakeSharingDelegate -- (void)sharer:(nonnull id)sharer didCompleteWithResults:(nonnull NSDictionary *)results { +- (void)sharer:(nonnull id)sharer didCompleteWithResults:(nonnull NSDictionary *)results +{ self.capturedResults = results; } -- (void)sharer:(nonnull id)sharer didFailWithError:(nonnull NSError *)error { +- (void)sharer:(nonnull id)sharer didFailWithError:(nonnull NSError *)error +{ self.capturedError = error; } -- (void)sharerDidCancel:(nonnull id)sharer { +- (void)sharerDidCancel:(nonnull id)sharer +{ self.didCancel = true; } diff --git a/FBSDKShareKit/FBSDKShareKitTests/Internal/FBSDKShareUtilityTests.m b/FBSDKShareKit/FBSDKShareKitTests/Internal/FBSDKShareUtilityTests.m index d580793cd6..2c69401cc2 100644 --- a/FBSDKShareKit/FBSDKShareKitTests/Internal/FBSDKShareUtilityTests.m +++ b/FBSDKShareKit/FBSDKShareKitTests/Internal/FBSDKShareUtilityTests.m @@ -20,7 +20,7 @@ #import #ifdef BUCK -#import + #import #else @import FBSDKCoreKit; #endif diff --git a/FBSDKShareKit/FBSDKShareKitTests/Models/FBSDKAppInviteContentTests.m b/FBSDKShareKit/FBSDKShareKitTests/Models/FBSDKAppInviteContentTests.m index e5140586c2..48067c8925 100644 --- a/FBSDKShareKit/FBSDKShareKitTests/Models/FBSDKAppInviteContentTests.m +++ b/FBSDKShareKit/FBSDKShareKitTests/Models/FBSDKAppInviteContentTests.m @@ -19,7 +19,7 @@ #import #ifdef BUCK -#import + #import #else @import FBSDKCoreKit; #endif diff --git a/FBSDKShareKit/FBSDKShareKitTests/Models/FBSDKCameraEffectArgumentsTests.m b/FBSDKShareKit/FBSDKShareKitTests/Models/FBSDKCameraEffectArgumentsTests.m index 1f8df9abb4..d1a58f09ac 100644 --- a/FBSDKShareKit/FBSDKShareKitTests/Models/FBSDKCameraEffectArgumentsTests.m +++ b/FBSDKShareKit/FBSDKShareKitTests/Models/FBSDKCameraEffectArgumentsTests.m @@ -20,7 +20,7 @@ #import #ifdef BUCK -#import + #import #else @import FBSDKCoreKit; #endif @@ -45,9 +45,9 @@ - (void)testCoding FBSDKCameraEffectArguments *arguments = [FBSDKShareModelTestUtility cameraEffectArguments]; NSData *data = [NSKeyedArchiver archivedDataWithRootObject:arguments]; #if __IPHONE_OS_VERSION_MIN_REQUIRED >= __IPHONE_11_0 - FBSDKCameraEffectArguments *unarchivedArguments = [NSKeyedUnarchiver unarchivedObjectOfClass:[FBSDKCameraEffectArguments class] fromData:data error:nil]; + FBSDKCameraEffectArguments *unarchivedArguments = [NSKeyedUnarchiver unarchivedObjectOfClass:[FBSDKCameraEffectArguments class] fromData:data error:nil]; #else - FBSDKCameraEffectArguments *unarchivedArguments = [NSKeyedUnarchiver unarchiveObjectWithData:data]; + FBSDKCameraEffectArguments *unarchivedArguments = [NSKeyedUnarchiver unarchiveObjectWithData:data]; #endif XCTAssertEqualObjects(unarchivedArguments, arguments); } @@ -59,8 +59,8 @@ - (void)testTypes // Supported types [arguments setString:@"1234" forKey:@"string"]; XCTAssertEqualObjects([arguments stringForKey:@"string"], @"1234"); - [arguments setArray:@[@"a",@"b",@"c"] forKey:@"string_array"]; - XCTAssertEqualObjects([arguments arrayForKey:@"string_array"], (@[@"a",@"b",@"c"])); + [arguments setArray:@[@"a", @"b", @"c"] forKey:@"string_array"]; + XCTAssertEqualObjects([arguments arrayForKey:@"string_array"], (@[@"a", @"b", @"c"])); [arguments setArray:@[] forKey:@"empty_array"]; XCTAssertEqualObjects([arguments arrayForKey:@"empty_array"], @[]); [arguments setString:nil forKey:@"nil_string"]; diff --git a/FBSDKShareKit/FBSDKShareKitTests/Models/FBSDKGameRequestContentTests.m b/FBSDKShareKit/FBSDKShareKitTests/Models/FBSDKGameRequestContentTests.m index 49e70311d2..48852e7049 100644 --- a/FBSDKShareKit/FBSDKShareKitTests/Models/FBSDKGameRequestContentTests.m +++ b/FBSDKShareKit/FBSDKShareKitTests/Models/FBSDKGameRequestContentTests.m @@ -19,7 +19,7 @@ #import #ifdef BUCK -#import + #import #else @import FBSDKCoreKit; #endif @@ -58,7 +58,7 @@ - (void)testCoding NSKeyedUnarchiver *unarchiver = [[NSKeyedUnarchiver alloc] initForReadingWithData:data]; [unarchiver setRequiresSecureCoding:YES]; FBSDKGameRequestContent *unarchivedObject = [unarchiver decodeObjectOfClass:[FBSDKGameRequestContent class] - forKey:NSKeyedArchiveRootObjectKey]; + forKey:NSKeyedArchiveRootObjectKey]; XCTAssertEqualObjects(unarchivedObject, content); } diff --git a/FBSDKShareKit/FBSDKShareKitTests/Models/FBSDKShareLinkContentTests.m b/FBSDKShareKit/FBSDKShareKitTests/Models/FBSDKShareLinkContentTests.m index 660b38f815..e8821731ad 100644 --- a/FBSDKShareKit/FBSDKShareKitTests/Models/FBSDKShareLinkContentTests.m +++ b/FBSDKShareKit/FBSDKShareKitTests/Models/FBSDKShareLinkContentTests.m @@ -19,15 +19,14 @@ #import #ifdef BUCK -#import + #import #else @import FBSDKCoreKit; #endif -#import "FBSDKShareLinkContent.h" - #import +#import "FBSDKShareLinkContent.h" #import "FBSDKShareModelTestUtility.h" #import "FBSDKShareUtility.h" @@ -68,19 +67,21 @@ - (void)testWithInvalidPeopleIDs { FBSDKShareLinkContent *content = [[FBSDKShareLinkContent alloc] init]; NSArray *array = @[ - @"one", - @2, - @"three", - ]; + @"one", + @2, + @"three", + ]; XCTAssertThrowsSpecificNamed([content setPeopleIDs:array], NSException, NSInvalidArgumentException); } - (void)testValidationWithValidContent { NSError *error; - XCTAssertTrue([FBSDKShareUtility validateShareContent:[FBSDKShareModelTestUtility linkContent] - bridgeOptions:FBSDKShareBridgeOptionsDefault - error:&error]); + XCTAssertTrue( + [FBSDKShareUtility validateShareContent:[FBSDKShareModelTestUtility linkContent] + bridgeOptions:FBSDKShareBridgeOptionsDefault + error:&error] + ); XCTAssertNil(error); } diff --git a/FBSDKShareKit/FBSDKShareKitTests/Models/FBSDKShareModelTestUtility.m b/FBSDKShareKit/FBSDKShareKitTests/Models/FBSDKShareModelTestUtility.m index ea11ed3875..02c0bf5fe1 100644 --- a/FBSDKShareKit/FBSDKShareKitTests/Models/FBSDKShareModelTestUtility.m +++ b/FBSDKShareKit/FBSDKShareKitTests/Models/FBSDKShareModelTestUtility.m @@ -150,33 +150,33 @@ + (FBSDKSharePhoto *)photoWithImageURL + (NSArray *)photos { return @[ - [FBSDKSharePhoto photoWithImageURL:[NSURL URLWithString:@"https://fbstatic-a.akamaihd.net/rsrc.php/v2/yC/r/YRwxe7CPWSs.png"] - userGenerated:NO], - [FBSDKSharePhoto photoWithImageURL:[NSURL URLWithString:@"https://fbstatic-a.akamaihd.net/rsrc.php/v2/yS/r/9f82O0jy9RH.png"] - userGenerated:NO], - [FBSDKSharePhoto photoWithImageURL:[NSURL URLWithString:@"https://fbcdn-dragon-a.akamaihd.net/hphotos-ak-xaf1/t39.2178-6/10173500_1398474223767412_616498772_n.png"] - userGenerated:YES], - ]; + [FBSDKSharePhoto photoWithImageURL:[NSURL URLWithString:@"https://fbstatic-a.akamaihd.net/rsrc.php/v2/yC/r/YRwxe7CPWSs.png"] + userGenerated:NO], + [FBSDKSharePhoto photoWithImageURL:[NSURL URLWithString:@"https://fbstatic-a.akamaihd.net/rsrc.php/v2/yS/r/9f82O0jy9RH.png"] + userGenerated:NO], + [FBSDKSharePhoto photoWithImageURL:[NSURL URLWithString:@"https://fbcdn-dragon-a.akamaihd.net/hphotos-ak-xaf1/t39.2178-6/10173500_1398474223767412_616498772_n.png"] + userGenerated:YES], + ]; } + (NSArray *)photosWithFileUrls { return @[ - [FBSDKShareModelTestUtility photoWithFileURL], - ]; + [FBSDKShareModelTestUtility photoWithFileURL], + ]; } + (NSArray *)photosWithImages { - // equality checks are pointer equality for UIImage, so just return the same instance each time + // equality checks are pointer equality for UIImage, so just return the same instance each time static NSArray *_photos = nil; static dispatch_once_t onceToken; dispatch_once(&onceToken, ^{ _photos = @[ - [FBSDKSharePhoto photoWithImage:[self _generateImage] userGenerated:YES], - [FBSDKSharePhoto photoWithImage:[self _generateImage] userGenerated:YES], - [FBSDKSharePhoto photoWithImage:[self _generateImage] userGenerated:YES], - ]; + [FBSDKSharePhoto photoWithImage:[self _generateImage] userGenerated:YES], + [FBSDKSharePhoto photoWithImage:[self _generateImage] userGenerated:YES], + [FBSDKSharePhoto photoWithImage:[self _generateImage] userGenerated:YES], + ]; }); return _photos; } diff --git a/FBSDKShareKit/FBSDKShareKitTests/Models/FBSDKSharePhotoContentTests.m b/FBSDKShareKit/FBSDKShareKitTests/Models/FBSDKSharePhotoContentTests.m index ea0d461d0f..6298962579 100644 --- a/FBSDKShareKit/FBSDKShareKitTests/Models/FBSDKSharePhotoContentTests.m +++ b/FBSDKShareKit/FBSDKShareKitTests/Models/FBSDKSharePhotoContentTests.m @@ -20,7 +20,7 @@ #import #ifdef BUCK -#import + #import #else @import FBSDKCoreKit; #endif @@ -65,9 +65,9 @@ - (void)testWithInvalidPhotos { FBSDKSharePhotoContent *content = [[FBSDKSharePhotoContent alloc] init]; NSArray *photos = @[ - [FBSDKShareModelTestUtility photoWithImageURL], - [FBSDKShareModelTestUtility photoImageURL], - ]; + [FBSDKShareModelTestUtility photoWithImageURL], + [FBSDKShareModelTestUtility photoImageURL], + ]; XCTAssertThrowsSpecificNamed([content setPhotos:photos], NSException, NSInvalidArgumentException); } diff --git a/FBSDKShareKit/FBSDKShareKitTests/Models/FBSDKSharePhotoTests.m b/FBSDKShareKit/FBSDKShareKitTests/Models/FBSDKSharePhotoTests.m index 17c29d0694..b8f8ddc2bd 100644 --- a/FBSDKShareKit/FBSDKShareKitTests/Models/FBSDKSharePhotoTests.m +++ b/FBSDKShareKit/FBSDKShareKitTests/Models/FBSDKSharePhotoTests.m @@ -20,7 +20,7 @@ #import #ifdef BUCK -#import + #import #else @import FBSDKCoreKit; #endif @@ -92,10 +92,10 @@ - (void)testWithInvalidPhotos { FBSDKSharePhotoContent *content = [[FBSDKSharePhotoContent alloc] init]; NSArray *photos = @[ - [FBSDKShareModelTestUtility photoWithImageURL], - [FBSDKShareModelTestUtility photoWithImage], - @"my photo", - ]; + [FBSDKShareModelTestUtility photoWithImageURL], + [FBSDKShareModelTestUtility photoWithImage], + @"my photo", + ]; XCTAssertThrowsSpecificNamed([content setPhotos:photos], NSException, NSInvalidArgumentException); } diff --git a/FBSDKShareKit/FBSDKShareKitTests/Models/FBSDKShareVideoContentTests.m b/FBSDKShareKit/FBSDKShareKitTests/Models/FBSDKShareVideoContentTests.m index b99065a568..108699a1cb 100644 --- a/FBSDKShareKit/FBSDKShareKitTests/Models/FBSDKShareVideoContentTests.m +++ b/FBSDKShareKit/FBSDKShareKitTests/Models/FBSDKShareVideoContentTests.m @@ -20,7 +20,7 @@ #import #ifdef BUCK -#import + #import #else @import FBSDKCoreKit; #endif @@ -96,8 +96,11 @@ - (void)testValidationWithNilVideoURL XCTAssertFalse([FBSDKShareUtility validateShareContent:content bridgeOptions:FBSDKShareBridgeOptionsDefault error:&error]); XCTAssertNotNil(error); XCTAssertEqual(error.code, FBSDKErrorInvalidArgument); - XCTAssertEqualObjects(error.userInfo[FBSDKErrorArgumentNameKey], @"video", - @"Attempting to validate video share content with a missing url should return a general video error"); + XCTAssertEqualObjects( + error.userInfo[FBSDKErrorArgumentNameKey], + @"video", + @"Attempting to validate video share content with a missing url should return a general video error" + ); } - (void)testValidationWithInvalidVideoURL @@ -110,8 +113,11 @@ - (void)testValidationWithInvalidVideoURL XCTAssertFalse([FBSDKShareUtility validateShareContent:content bridgeOptions:FBSDKShareBridgeOptionsDefault error:&error]); XCTAssertNotNil(error); XCTAssertEqual(error.code, FBSDKErrorInvalidArgument); - XCTAssertEqualObjects(error.userInfo[FBSDKErrorArgumentNameKey], @"videoURL", - @"Attempting to validate video share content with an empty url should return a video url specific error"); + XCTAssertEqualObjects( + error.userInfo[FBSDKErrorArgumentNameKey], + @"videoURL", + @"Attempting to validate video share content with an empty url should return a video url specific error" + ); } - (void)testValidationWithNonVideoURL @@ -124,8 +130,11 @@ - (void)testValidationWithNonVideoURL XCTAssertFalse([FBSDKShareUtility validateShareContent:content bridgeOptions:FBSDKShareBridgeOptionsDefault error:&error]); XCTAssertNotNil(error); XCTAssertEqual(error.code, FBSDKErrorInvalidArgument); - XCTAssertEqualObjects(error.userInfo[FBSDKErrorArgumentNameKey], @"videoURL", - @"Attempting to validate video share content with a non-video url should return a video url specific error"); + XCTAssertEqualObjects( + error.userInfo[FBSDKErrorArgumentNameKey], + @"videoURL", + @"Attempting to validate video share content with a non-video url should return a video url specific error" + ); } - (void)testValidationWithNetworkVideoURL @@ -149,10 +158,13 @@ - (void)testValidationWithValidFileVideoURLWhenBridgeOptionIsDefault content.video = video; XCTAssertNotNil(content); NSError *error; - XCTAssertFalse([FBSDKShareUtility validateShareContent:content bridgeOptions: FBSDKShareBridgeOptionsDefault error:&error]); + XCTAssertFalse([FBSDKShareUtility validateShareContent:content bridgeOptions:FBSDKShareBridgeOptionsDefault error:&error]); XCTAssertEqual(error.code, FBSDKErrorInvalidArgument); - XCTAssertEqualObjects(error.userInfo[FBSDKErrorArgumentNameKey], @"videoURL", - @"Attempting to validate video share content with a valid file url should return a video url specific error when there is no specified bridge option to handle video data"); + XCTAssertEqualObjects( + error.userInfo[FBSDKErrorArgumentNameKey], + @"videoURL", + @"Attempting to validate video share content with a valid file url should return a video url specific error when there is no specified bridge option to handle video data" + ); } - (void)testValidationWithValidFileVideoURLWhenBridgeOptionIsVideoData @@ -164,7 +176,7 @@ - (void)testValidationWithValidFileVideoURLWhenBridgeOptionIsVideoData content.video = video; XCTAssertNotNil(content); NSError *error; - XCTAssertTrue([FBSDKShareUtility validateShareContent:content bridgeOptions: FBSDKShareBridgeOptionsVideoData error:&error]); + XCTAssertTrue([FBSDKShareUtility validateShareContent:content bridgeOptions:FBSDKShareBridgeOptionsVideoData error:&error]); XCTAssertNil(error); } diff --git a/FBSDKShareKit/FBSDKShareKitTests/Models/FBSDKShareVideoTests.m b/FBSDKShareKit/FBSDKShareKitTests/Models/FBSDKShareVideoTests.m index 1a693b88f6..8d862f874a 100644 --- a/FBSDKShareKit/FBSDKShareKitTests/Models/FBSDKShareVideoTests.m +++ b/FBSDKShareKit/FBSDKShareKitTests/Models/FBSDKShareVideoTests.m @@ -20,7 +20,7 @@ #import #ifdef BUCK -#import + #import #else @import FBSDKCoreKit; #endif diff --git a/FBSDKTVOSKit.podspec b/FBSDKTVOSKit.podspec index 25b5d521a4..feec9370b9 100644 --- a/FBSDKTVOSKit.podspec +++ b/FBSDKTVOSKit.podspec @@ -3,7 +3,7 @@ Pod::Spec.new do |s| s.name = 'FBSDKTVOSKit' - s.version = '7.1.1' + s.version = '8.2.0' s.summary = 'Official Facebook SDK for tvOS to access Facebook Platform with features like Login and Graph API.' s.description = <<-DESC diff --git a/FBSDKTVOSKit/Configurations/FBSDKTVOSKitTests.xcconfig b/FBSDKTVOSKit/Configurations/FBSDKTVOSKitTests.xcconfig index c91be838eb..a937279c08 100644 --- a/FBSDKTVOSKit/Configurations/FBSDKTVOSKitTests.xcconfig +++ b/FBSDKTVOSKit/Configurations/FBSDKTVOSKitTests.xcconfig @@ -24,7 +24,7 @@ PRODUCT_BUNDLE_IDENTIFIER = com.facebook.sdk.FBSDKTVOSKitTests INFOPLIST_FILE = $(SRCROOT)/FBSDKTVOSKitTests/Info.plist -IPHONEOS_DEPLOYMENT_TARGET = 8.0 +IPHONEOS_DEPLOYMENT_TARGET = 9.0 HEADER_SEARCH_PATHS = $(inherited) $(BUILT_PRODUCTS_DIR) LIBRARY_SEARCH_PATHS = $(inherited) $(BUILT_PRODUCTS_DIR) diff --git a/FBSDKTVOSKit/FBSDKTVOSKit.xcodeproj/project.pbxproj b/FBSDKTVOSKit/FBSDKTVOSKit.xcodeproj/project.pbxproj index 0b974ae1ae..677934d9d5 100644 --- a/FBSDKTVOSKit/FBSDKTVOSKit.xcodeproj/project.pbxproj +++ b/FBSDKTVOSKit/FBSDKTVOSKit.xcodeproj/project.pbxproj @@ -30,7 +30,6 @@ 8117C27E1D30303D00784D79 /* FBSDKTVOSConstants.m in Sources */ = {isa = PBXBuildFile; fileRef = 9D8E94771BC332900012023B /* FBSDKTVOSConstants.m */; }; 8117C2801D30303D00784D79 /* FBSDKTVLoginButtonElement.m in Sources */ = {isa = PBXBuildFile; fileRef = 9D81FDA21C50433D00AF5F8D /* FBSDKTVLoginButtonElement.m */; }; 8117C2811D30303D00784D79 /* FBSDKJS.m in Sources */ = {isa = PBXBuildFile; fileRef = 9D82CF1C1C56EB5300CB7AD7 /* FBSDKJS.m */; }; - 8117C2821D30303D00784D79 /* FBSDKTVShareButtonElement.m in Sources */ = {isa = PBXBuildFile; fileRef = 9D084B141CB57FF000A51DA0 /* FBSDKTVShareButtonElement.m */; }; 8117C2851D30303D00784D79 /* FBSDKTVLoginButtonElement.h in Headers */ = {isa = PBXBuildFile; fileRef = 9D81FDA11C50433D00AF5F8D /* FBSDKTVLoginButtonElement.h */; settings = {ATTRIBUTES = (Public, ); }; }; 8117C2861D30303D00784D79 /* FBSDKCoreKit+Internal.h in Headers */ = {isa = PBXBuildFile; fileRef = 9D8E94741BC32F920012023B /* FBSDKCoreKit+Internal.h */; }; 8117C2871D30303D00784D79 /* FBSDKTVOSKit.h in Headers */ = {isa = PBXBuildFile; fileRef = 9DEC55861BBA636A00C7DB4A /* FBSDKTVOSKit.h */; settings = {ATTRIBUTES = (Public, ); }; }; @@ -40,17 +39,12 @@ 8117C2901D30303D00784D79 /* FBSDKTVInterfaceFactory.h in Headers */ = {isa = PBXBuildFile; fileRef = 9D81FD9D1C50407000AF5F8D /* FBSDKTVInterfaceFactory.h */; settings = {ATTRIBUTES = (Public, ); }; }; 8117C2911D30303D00784D79 /* FBSDKTVOSConstants.h in Headers */ = {isa = PBXBuildFile; fileRef = 9D8E94761BC332900012023B /* FBSDKTVOSConstants.h */; settings = {ATTRIBUTES = (Public, ); }; }; 8117C2921D30303D00784D79 /* FBSDKTVLoginViewControllerElement.h in Headers */ = {isa = PBXBuildFile; fileRef = 9D82CF171C56C3EE00CB7AD7 /* FBSDKTVLoginViewControllerElement.h */; settings = {ATTRIBUTES = (Public, ); }; }; - 8117C2931D30303D00784D79 /* FBSDKTVShareButtonElement.h in Headers */ = {isa = PBXBuildFile; fileRef = 9D084B131CB57FF000A51DA0 /* FBSDKTVShareButtonElement.h */; settings = {ATTRIBUTES = (Public, ); }; }; 8117C2BC1D30308700784D79 /* FBSDKCoreKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 8118B6151D0A45F000962084 /* FBSDKCoreKit.framework */; }; - 8117C2BD1D30308700784D79 /* FBSDKShareKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 8118B62D1D0A462B00962084 /* FBSDKShareKit.framework */; }; 8117C2C21D30309600784D79 /* UIKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 8117C2C11D30309600784D79 /* UIKit.framework */; }; 8117C2C51D30309C00784D79 /* TVMLKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 8117C2C41D30309C00784D79 /* TVMLKit.framework */; }; 8117C2C81D3030A000784D79 /* CoreGraphics.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 8117C2C71D3030A000784D79 /* CoreGraphics.framework */; }; 8118B6941D0A5F9800962084 /* FBSDKTVOSKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 9DEC55831BBA636A00C7DB4A /* FBSDKTVOSKit.framework */; }; 8118B6951D0A5F9800962084 /* FBSDKCoreKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 8118B6151D0A45F000962084 /* FBSDKCoreKit.framework */; }; - 8118B6961D0A5F9800962084 /* FBSDKShareKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 8118B62D1D0A462B00962084 /* FBSDKShareKit.framework */; }; - 9D084B151CB57FF000A51DA0 /* FBSDKTVShareButtonElement.h in Headers */ = {isa = PBXBuildFile; fileRef = 9D084B131CB57FF000A51DA0 /* FBSDKTVShareButtonElement.h */; settings = {ATTRIBUTES = (Public, ); }; }; - 9D084B161CB57FF000A51DA0 /* FBSDKTVShareButtonElement.m in Sources */ = {isa = PBXBuildFile; fileRef = 9D084B141CB57FF000A51DA0 /* FBSDKTVShareButtonElement.m */; }; 9D6538261BF40AB3008A08E9 /* FBSDKDeviceLoginButton.h in Headers */ = {isa = PBXBuildFile; fileRef = 9D6538241BF40AB3008A08E9 /* FBSDKDeviceLoginButton.h */; settings = {ATTRIBUTES = (Public, ); }; }; 9D6538271BF40AB3008A08E9 /* FBSDKDeviceLoginButton.m in Sources */ = {isa = PBXBuildFile; fileRef = 9D6538251BF40AB3008A08E9 /* FBSDKDeviceLoginButton.m */; }; 9D81FD9F1C50407000AF5F8D /* FBSDKTVInterfaceFactory.h in Headers */ = {isa = PBXBuildFile; fileRef = 9D81FD9D1C50407000AF5F8D /* FBSDKTVInterfaceFactory.h */; settings = {ATTRIBUTES = (Public, ); }; }; @@ -155,13 +149,6 @@ remoteGlobalIDString = 814AC7E11D1B528900D61E6C; remoteInfo = "FBSDKCoreKit_TV-Dynamic"; }; - 8117C2BA1D30307C00784D79 /* PBXContainerItemProxy */ = { - isa = PBXContainerItemProxy; - containerPortal = 9D084B1A1CB5887000A51DA0 /* FBSDKShareKit.xcodeproj */; - proxyType = 1; - remoteGlobalIDString = 8144D8E81D261D2900C8E4AC; - remoteInfo = "FBSDKShareKit_TV-Dynamic"; - }; 8118B6101D0A45F000962084 /* PBXContainerItemProxy */ = { isa = PBXContainerItemProxy; containerPortal = 8118B6081D0A45F000962084 /* FBSDKCoreKit.xcodeproj */; @@ -211,13 +198,6 @@ remoteGlobalIDString = 9D2D99941BC452DE00929E76; remoteInfo = FBSDKShareKit_TV; }; - 8118B62F1D0A463000962084 /* PBXContainerItemProxy */ = { - isa = PBXContainerItemProxy; - containerPortal = 9D084B1A1CB5887000A51DA0 /* FBSDKShareKit.xcodeproj */; - proxyType = 1; - remoteGlobalIDString = 9D2D99931BC452DE00929E76; - remoteInfo = FBSDKShareKit_TV; - }; 8118B6971D0A5FA300962084 /* PBXContainerItemProxy */ = { isa = PBXContainerItemProxy; containerPortal = 8118B6081D0A45F000962084 /* FBSDKCoreKit.xcodeproj */; @@ -225,13 +205,6 @@ remoteGlobalIDString = 9DB0FA721BC1CA71005EB8B1; remoteInfo = FBSDKCoreKit_TV; }; - 8118B6991D0A5FA300962084 /* PBXContainerItemProxy */ = { - isa = PBXContainerItemProxy; - containerPortal = 9D084B1A1CB5887000A51DA0 /* FBSDKShareKit.xcodeproj */; - proxyType = 1; - remoteGlobalIDString = 9D2D99931BC452DE00929E76; - remoteInfo = FBSDKShareKit_TV; - }; 9DEC558F1BBA636A00C7DB4A /* PBXContainerItemProxy */ = { isa = PBXContainerItemProxy; containerPortal = 9DEC557A1BBA636A00C7DB4A /* Project object */; @@ -274,48 +247,6 @@ remoteGlobalIDString = C5C4B4EA2276BA8D00CA3706; remoteInfo = "FBSDKCoreKit_Basics_TV-Dynamic"; }; - F46A2C8224464D30000EFBAC /* PBXContainerItemProxy */ = { - isa = PBXContainerItemProxy; - containerPortal = 8118B6081D0A45F000962084 /* FBSDKCoreKit.xcodeproj */; - proxyType = 2; - remoteGlobalIDString = F487DBB9231EBCD2008416A9; - remoteInfo = FBSDKCoreKitSwift; - }; - F46A2C8424464D30000EFBAC /* PBXContainerItemProxy */ = { - isa = PBXContainerItemProxy; - containerPortal = 8118B6081D0A45F000962084 /* FBSDKCoreKit.xcodeproj */; - proxyType = 2; - remoteGlobalIDString = F483F414233AC13000703DE3; - remoteInfo = "FBSDKCoreKitSwift-Dynamic"; - }; - F46A2C8D24464D30000EFBAC /* PBXContainerItemProxy */ = { - isa = PBXContainerItemProxy; - containerPortal = 0B9DBF54207C080600662776 /* FBSDKLoginKit.xcodeproj */; - proxyType = 2; - remoteGlobalIDString = F487DC03231EC34B008416A9; - remoteInfo = FBSDKLoginKitSwift; - }; - F46A2C8F24464D30000EFBAC /* PBXContainerItemProxy */ = { - isa = PBXContainerItemProxy; - containerPortal = 0B9DBF54207C080600662776 /* FBSDKLoginKit.xcodeproj */; - proxyType = 2; - remoteGlobalIDString = F483F44D233AC85F00703DE3; - remoteInfo = "FBSDKLoginKitSwift-Dynamic"; - }; - F46A2C9824464D30000EFBAC /* PBXContainerItemProxy */ = { - isa = PBXContainerItemProxy; - containerPortal = 9D084B1A1CB5887000A51DA0 /* FBSDKShareKit.xcodeproj */; - proxyType = 2; - remoteGlobalIDString = F487DC86231EC4CC008416A9; - remoteInfo = FBSDKShareKitSwift; - }; - F46A2C9A24464D30000EFBAC /* PBXContainerItemProxy */ = { - isa = PBXContainerItemProxy; - containerPortal = 9D084B1A1CB5887000A51DA0 /* FBSDKShareKit.xcodeproj */; - proxyType = 2; - remoteGlobalIDString = F483F4CA233AC91700703DE3; - remoteInfo = "FBSDKShareKitSwift-Dynamic"; - }; /* End PBXContainerItemProxy section */ /* Begin PBXFileReference section */ @@ -350,8 +281,6 @@ 8117C2C41D30309C00784D79 /* TVMLKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = TVMLKit.framework; path = Platforms/AppleTVOS.platform/Developer/SDKs/AppleTVOS.sdk/System/Library/Frameworks/TVMLKit.framework; sourceTree = DEVELOPER_DIR; }; 8117C2C71D3030A000784D79 /* CoreGraphics.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = CoreGraphics.framework; path = Platforms/AppleTVOS.platform/Developer/SDKs/AppleTVOS.sdk/System/Library/Frameworks/CoreGraphics.framework; sourceTree = DEVELOPER_DIR; }; 8118B6081D0A45F000962084 /* FBSDKCoreKit.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = FBSDKCoreKit.xcodeproj; path = ../../FBSDKCoreKit/FBSDKCoreKit.xcodeproj; sourceTree = ""; }; - 9D084B131CB57FF000A51DA0 /* FBSDKTVShareButtonElement.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = FBSDKTVShareButtonElement.h; sourceTree = ""; }; - 9D084B141CB57FF000A51DA0 /* FBSDKTVShareButtonElement.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = FBSDKTVShareButtonElement.m; sourceTree = ""; }; 9D084B1A1CB5887000A51DA0 /* FBSDKShareKit.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = FBSDKShareKit.xcodeproj; path = ../../FBSDKShareKit/FBSDKShareKit.xcodeproj; sourceTree = ""; }; 9D6538241BF40AB3008A08E9 /* FBSDKDeviceLoginButton.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = FBSDKDeviceLoginButton.h; sourceTree = ""; }; 9D6538251BF40AB3008A08E9 /* FBSDKDeviceLoginButton.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = FBSDKDeviceLoginButton.m; sourceTree = ""; }; @@ -397,7 +326,6 @@ 8117C2C51D30309C00784D79 /* TVMLKit.framework in Frameworks */, 8117C2C21D30309600784D79 /* UIKit.framework in Frameworks */, 8117C2BC1D30308700784D79 /* FBSDKCoreKit.framework in Frameworks */, - 8117C2BD1D30308700784D79 /* FBSDKShareKit.framework in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -414,7 +342,6 @@ files = ( 8118B6941D0A5F9800962084 /* FBSDKTVOSKit.framework in Frameworks */, 8118B6951D0A5F9800962084 /* FBSDKCoreKit.framework in Frameworks */, - 8118B6961D0A5F9800962084 /* FBSDKShareKit.framework in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -429,8 +356,6 @@ 0B9DBF62207C080600662776 /* FBSDKLoginKitTests.xctest */, 0B9DBF64207C080600662776 /* FBSDKLoginKit.framework */, 0B9DBF66207C080600662776 /* FBSDKLoginKit.framework */, - F46A2C8E24464D30000EFBAC /* FBSDKLoginKit.framework */, - F46A2C9024464D30000EFBAC /* FBSDKLoginKit.framework */, ); name = Products; sourceTree = ""; @@ -519,8 +444,6 @@ C57538DA2292B7DD007C2EFF /* FBSDKCoreKit.framework */, C57538DC2292B7DD007C2EFF /* FBSDKCoreKit.framework */, C57538DE2292B7DD007C2EFF /* FBSDKCoreKit.framework */, - F46A2C8324464D30000EFBAC /* FBSDKCoreKit.framework */, - F46A2C8524464D30000EFBAC /* FBSDKCoreKit.framework */, ); name = Products; sourceTree = ""; @@ -533,8 +456,6 @@ 8118B62B1D0A462B00962084 /* FBSDKShareKitTests.xctest */, 8118B62D1D0A462B00962084 /* FBSDKShareKit.framework */, 8117C2461D302F6900784D79 /* FBSDKShareKit.framework */, - F46A2C9924464D30000EFBAC /* FBSDKShareKit.framework */, - F46A2C9B24464D30000EFBAC /* FBSDKShareKit.framework */, ); name = Products; sourceTree = ""; @@ -581,8 +502,6 @@ 9D8E94761BC332900012023B /* FBSDKTVOSConstants.h */, 9D8E94771BC332900012023B /* FBSDKTVOSConstants.m */, 9DEC55861BBA636A00C7DB4A /* FBSDKTVOSKit.h */, - 9D084B131CB57FF000A51DA0 /* FBSDKTVShareButtonElement.h */, - 9D084B141CB57FF000A51DA0 /* FBSDKTVShareButtonElement.m */, 526CE1CD21768C9100F69C7C /* Internal */, 9DEC55BD1BBA684500C7DB4A /* Supporting Files */, ); @@ -655,7 +574,6 @@ 8117C2901D30303D00784D79 /* FBSDKTVInterfaceFactory.h in Headers */, 8117C2911D30303D00784D79 /* FBSDKTVOSConstants.h in Headers */, 8117C2921D30303D00784D79 /* FBSDKTVLoginViewControllerElement.h in Headers */, - 8117C2931D30303D00784D79 /* FBSDKTVShareButtonElement.h in Headers */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -672,7 +590,6 @@ 9D81FD9F1C50407000AF5F8D /* FBSDKTVInterfaceFactory.h in Headers */, 9D8E94781BC332900012023B /* FBSDKTVOSConstants.h in Headers */, 9D82CF191C56C3EE00CB7AD7 /* FBSDKTVLoginViewControllerElement.h in Headers */, - 9D084B151CB57FF000A51DA0 /* FBSDKTVShareButtonElement.h in Headers */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -693,7 +610,6 @@ dependencies = ( 0B3229382093848300F011F9 /* PBXTargetDependency */, 8117C2B91D30307C00784D79 /* PBXTargetDependency */, - 8117C2BB1D30307C00784D79 /* PBXTargetDependency */, ); name = "FBSDKTVOSKit_TV-Dynamic"; productName = FBSDKTVOSKit; @@ -713,7 +629,6 @@ ); dependencies = ( 0B9DBF68207C080E00662776 /* PBXTargetDependency */, - 8118B6301D0A463000962084 /* PBXTargetDependency */, 8118B6171D0A45F600962084 /* PBXTargetDependency */, ); name = FBSDKTVOSKit_TV; @@ -733,7 +648,6 @@ ); dependencies = ( 8118B6981D0A5FA300962084 /* PBXTargetDependency */, - 8118B69A1D0A5FA300962084 /* PBXTargetDependency */, 9DEC55901BBA636A00C7DB4A /* PBXTargetDependency */, ); name = FBSDKTVOSKitTests; @@ -930,48 +844,6 @@ remoteRef = C57538DD2292B7DD007C2EFF /* PBXContainerItemProxy */; sourceTree = BUILT_PRODUCTS_DIR; }; - F46A2C8324464D30000EFBAC /* FBSDKCoreKit.framework */ = { - isa = PBXReferenceProxy; - fileType = wrapper.framework; - path = FBSDKCoreKit.framework; - remoteRef = F46A2C8224464D30000EFBAC /* PBXContainerItemProxy */; - sourceTree = BUILT_PRODUCTS_DIR; - }; - F46A2C8524464D30000EFBAC /* FBSDKCoreKit.framework */ = { - isa = PBXReferenceProxy; - fileType = wrapper.framework; - path = FBSDKCoreKit.framework; - remoteRef = F46A2C8424464D30000EFBAC /* PBXContainerItemProxy */; - sourceTree = BUILT_PRODUCTS_DIR; - }; - F46A2C8E24464D30000EFBAC /* FBSDKLoginKit.framework */ = { - isa = PBXReferenceProxy; - fileType = wrapper.framework; - path = FBSDKLoginKit.framework; - remoteRef = F46A2C8D24464D30000EFBAC /* PBXContainerItemProxy */; - sourceTree = BUILT_PRODUCTS_DIR; - }; - F46A2C9024464D30000EFBAC /* FBSDKLoginKit.framework */ = { - isa = PBXReferenceProxy; - fileType = wrapper.framework; - path = FBSDKLoginKit.framework; - remoteRef = F46A2C8F24464D30000EFBAC /* PBXContainerItemProxy */; - sourceTree = BUILT_PRODUCTS_DIR; - }; - F46A2C9924464D30000EFBAC /* FBSDKShareKit.framework */ = { - isa = PBXReferenceProxy; - fileType = wrapper.framework; - path = FBSDKShareKit.framework; - remoteRef = F46A2C9824464D30000EFBAC /* PBXContainerItemProxy */; - sourceTree = BUILT_PRODUCTS_DIR; - }; - F46A2C9B24464D30000EFBAC /* FBSDKShareKit.framework */ = { - isa = PBXReferenceProxy; - fileType = wrapper.framework; - path = FBSDKShareKit.framework; - remoteRef = F46A2C9A24464D30000EFBAC /* PBXContainerItemProxy */; - sourceTree = BUILT_PRODUCTS_DIR; - }; /* End PBXReferenceProxy section */ /* Begin PBXResourcesBuildPhase section */ @@ -1026,7 +898,6 @@ 8117C27E1D30303D00784D79 /* FBSDKTVOSConstants.m in Sources */, 8117C2801D30303D00784D79 /* FBSDKTVLoginButtonElement.m in Sources */, 8117C2811D30303D00784D79 /* FBSDKJS.m in Sources */, - 8117C2821D30303D00784D79 /* FBSDKTVShareButtonElement.m in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -1041,7 +912,6 @@ 9D8E94791BC332900012023B /* FBSDKTVOSConstants.m in Sources */, 9D81FDA41C50433D00AF5F8D /* FBSDKTVLoginButtonElement.m in Sources */, 9D82CF1E1C56EB5300CB7AD7 /* FBSDKJS.m in Sources */, - 9D084B161CB57FF000A51DA0 /* FBSDKTVShareButtonElement.m in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -1071,31 +941,16 @@ name = "FBSDKCoreKit_TV-Dynamic"; targetProxy = 8117C2B81D30307C00784D79 /* PBXContainerItemProxy */; }; - 8117C2BB1D30307C00784D79 /* PBXTargetDependency */ = { - isa = PBXTargetDependency; - name = "FBSDKShareKit_TV-Dynamic"; - targetProxy = 8117C2BA1D30307C00784D79 /* PBXContainerItemProxy */; - }; 8118B6171D0A45F600962084 /* PBXTargetDependency */ = { isa = PBXTargetDependency; name = FBSDKCoreKit_TV; targetProxy = 8118B6161D0A45F600962084 /* PBXContainerItemProxy */; }; - 8118B6301D0A463000962084 /* PBXTargetDependency */ = { - isa = PBXTargetDependency; - name = FBSDKShareKit_TV; - targetProxy = 8118B62F1D0A463000962084 /* PBXContainerItemProxy */; - }; 8118B6981D0A5FA300962084 /* PBXTargetDependency */ = { isa = PBXTargetDependency; name = FBSDKCoreKit_TV; targetProxy = 8118B6971D0A5FA300962084 /* PBXContainerItemProxy */; }; - 8118B69A1D0A5FA300962084 /* PBXTargetDependency */ = { - isa = PBXTargetDependency; - name = FBSDKShareKit_TV; - targetProxy = 8118B6991D0A5FA300962084 /* PBXContainerItemProxy */; - }; 9DEC55901BBA636A00C7DB4A /* PBXTargetDependency */ = { isa = PBXTargetDependency; target = 9DEC55821BBA636A00C7DB4A /* FBSDKTVOSKit_TV */; diff --git a/FBSDKTVOSKit/FBSDKTVOSKit/FBSDKDeviceLoginButton.m b/FBSDKTVOSKit/FBSDKTVOSKit/FBSDKDeviceLoginButton.m index 31ea758ad4..8bbc618640 100644 --- a/FBSDKTVOSKit/FBSDKTVOSKit/FBSDKDeviceLoginButton.m +++ b/FBSDKTVOSKit/FBSDKTVOSKit/FBSDKDeviceLoginButton.m @@ -19,13 +19,13 @@ #import "FBSDKDeviceLoginButton.h" #ifdef FBSDKCOCOAPODS -#import + #import #else -#import "FBSDKCoreKit+Internal.h" + #import "FBSDKCoreKit+Internal.h" #endif #import "FBSDKDeviceLoginViewController.h" -@interface FBSDKDeviceLoginButton() +@interface FBSDKDeviceLoginButton () @end @@ -60,10 +60,13 @@ - (CGSize)sizeThatFits:(CGSize)size CGSize selectedSize = [self sizeThatFits:size attributedTitle:[self _logOutTitle]]; CGSize normalSize = [self sizeThatFits:CGSizeMake(CGFLOAT_MAX, size.height) attributedTitle:[self _longLogInTitle]]; if (normalSize.width > size.width) { - return normalSize = [self sizeThatFits:size attributedTitle:[self _shortLogInTitle]]; + normalSize = [self sizeThatFits:size attributedTitle:[self _shortLogInTitle]]; + return normalSize; } - CGSize maxSize = CGSizeMake(MAX(normalSize.width, selectedSize.width), - MAX(normalSize.height, selectedSize.height)); + CGSize maxSize = CGSizeMake( + MAX(normalSize.width, selectedSize.width), + MAX(normalSize.height, selectedSize.height) + ); return CGSizeMake(maxSize.width, maxSize.height); } @@ -84,13 +87,13 @@ - (void)configureButton NSAttributedString *logOutTitle = [self _logOutTitle]; [self configureWithIcon:nil - title:nil - backgroundColor:[super defaultBackgroundColor] - highlightedColor:nil - selectedTitle:nil - selectedIcon:nil - selectedColor:[super defaultBackgroundColor] - selectedHighlightedColor:nil]; + title:nil + backgroundColor:[super defaultBackgroundColor] + highlightedColor:nil + selectedTitle:nil + selectedIcon:nil + selectedColor:[super defaultBackgroundColor] + selectedHighlightedColor:nil]; [self setAttributedTitle:logInTitle forState:UIControlStateNormal]; [self setAttributedTitle:logInTitle forState:UIControlStateFocused]; [self setAttributedTitle:logOutTitle forState:UIControlStateSelected]; @@ -123,33 +126,49 @@ - (void)_buttonPressed:(id)sender if (_userName) { NSString *localizedFormatString = - NSLocalizedStringWithDefaultValue(@"LoginButton.LoggedInAs", @"FacebookSDK", [FBSDKInternalUtility bundleForStrings], - @"Logged in as %@", - @"The format string for the FBSDKLoginButton label when the user is logged in"); + NSLocalizedStringWithDefaultValue( + @"LoginButton.LoggedInAs", + @"FacebookSDK", + [FBSDKInternalUtility bundleForStrings], + @"Logged in as %@", + @"The format string for the FBSDKLoginButton label when the user is logged in" + ); title = [NSString localizedStringWithFormat:localizedFormatString, _userName]; } else { NSString *localizedLoggedIn = - NSLocalizedStringWithDefaultValue(@"LoginButton.LoggedIn", @"FacebookSDK", [FBSDKInternalUtility bundleForStrings], - @"Logged in using Facebook", - @"The fallback string for the FBSDKLoginButton label when the user name is not available yet"); + NSLocalizedStringWithDefaultValue( + @"LoginButton.LoggedIn", + @"FacebookSDK", + [FBSDKInternalUtility bundleForStrings], + @"Logged in using Facebook", + @"The fallback string for the FBSDKLoginButton label when the user name is not available yet" + ); title = localizedLoggedIn; } NSString *cancelTitle = - NSLocalizedStringWithDefaultValue(@"LoginButton.CancelLogout", @"FacebookSDK", [FBSDKInternalUtility bundleForStrings], - @"Cancel", - @"The label for the FBSDKLoginButton action sheet to cancel logging out"); + NSLocalizedStringWithDefaultValue( + @"LoginButton.CancelLogout", + @"FacebookSDK", + [FBSDKInternalUtility bundleForStrings], + @"Cancel", + @"The label for the FBSDKLoginButton action sheet to cancel logging out" + ); NSString *logOutTitle = - NSLocalizedStringWithDefaultValue(@"LoginButton.ConfirmLogOut", @"FacebookSDK", [FBSDKInternalUtility bundleForStrings], - @"Log Out", - @"The label for the FBSDKLoginButton action sheet to confirm logging out"); + NSLocalizedStringWithDefaultValue( + @"LoginButton.ConfirmLogOut", + @"FacebookSDK", + [FBSDKInternalUtility bundleForStrings], + @"Log Out", + @"The label for the FBSDKLoginButton action sheet to confirm logging out" + ); UIAlertController *alertController = [UIAlertController alertControllerWithTitle:nil message:title preferredStyle:UIAlertControllerStyleActionSheet]; [alertController addAction:[UIAlertAction actionWithTitle:logOutTitle - style:UIAlertActionStyleDestructive - handler:^(UIAlertAction * _Nonnull action) { - [FBSDKAccessToken setCurrentAccessToken:nil]; - [self.delegate deviceLoginButtonDidLogOut:self]; - }]]; + style:UIAlertActionStyleDestructive + handler:^(UIAlertAction *_Nonnull action) { + [FBSDKAccessToken setCurrentAccessToken:nil]; + [self.delegate deviceLoginButtonDidLogOut:self]; + }]]; [alertController addAction:[UIAlertAction actionWithTitle:cancelTitle style:UIAlertActionStyleCancel handler:NULL]]; [parentViewController presentViewController:alertController animated:YES completion:NULL]; } else { @@ -165,33 +184,45 @@ - (NSAttributedString *)_loginTitle { CGSize size = self.bounds.size; CGSize longTitleSize = [super sizeThatFits:size attributedTitle:[self _longLogInTitle]]; - NSAttributedString *title = (longTitleSize.width <= size.width ? - [self _longLogInTitle] : - [self _shortLogInTitle]); + NSAttributedString *title = (longTitleSize.width <= size.width + ? [self _longLogInTitle] + : [self _shortLogInTitle]); return title; } - (NSAttributedString *)_logOutTitle { - NSString *string = NSLocalizedStringWithDefaultValue(@"LoginButton.LogOut", @"FacebookSDK", [FBSDKInternalUtility bundleForStrings], - @"Log out", - @"The label for the FBSDKLoginButton when the user is currently logged in"); + NSString *string = NSLocalizedStringWithDefaultValue( + @"LoginButton.LogOut", + @"FacebookSDK", + [FBSDKInternalUtility bundleForStrings], + @"Log out", + @"The label for the FBSDKLoginButton when the user is currently logged in" + ); return [self attributedTitleStringFromString:string]; } - (NSAttributedString *)_longLogInTitle { - NSString *string = NSLocalizedStringWithDefaultValue(@"LoginButton.LogInLong", @"FacebookSDK", [FBSDKInternalUtility bundleForStrings], - @"Log in with Facebook", - @"The long label for the FBSDKLoginButton when the user is currently logged out"); + NSString *string = NSLocalizedStringWithDefaultValue( + @"LoginButton.LogInLong", + @"FacebookSDK", + [FBSDKInternalUtility bundleForStrings], + @"Log in with Facebook", + @"The long label for the FBSDKLoginButton when the user is currently logged out" + ); return [self attributedTitleStringFromString:string]; } - (NSAttributedString *)_shortLogInTitle { - NSString *string = NSLocalizedStringWithDefaultValue(@"LoginButton.LogIn", @"FacebookSDK", [FBSDKInternalUtility bundleForStrings], - @"Log in", - @"The short label for the FBSDKLoginButton when the user is currently logged out"); + NSString *string = NSLocalizedStringWithDefaultValue( + @"LoginButton.LogIn", + @"FacebookSDK", + [FBSDKInternalUtility bundleForStrings], + @"Log in", + @"The short label for the FBSDKLoginButton when the user is currently logged out" + ); return [self attributedTitleStringFromString:string]; } diff --git a/FBSDKTVOSKit/FBSDKTVOSKit/FBSDKDeviceLoginViewController.m b/FBSDKTVOSKit/FBSDKTVOSKit/FBSDKDeviceLoginViewController.m index 9004685137..717cc2c4ef 100644 --- a/FBSDKTVOSKit/FBSDKTVOSKit/FBSDKDeviceLoginViewController.m +++ b/FBSDKTVOSKit/FBSDKTVOSKit/FBSDKDeviceLoginViewController.m @@ -21,17 +21,18 @@ #import #ifdef FBSDKCOCOAPODS -#import + #import #else -#import "FBSDKCoreKit+Internal.h" + #import "FBSDKCoreKit+Internal.h" #endif -@interface FBSDKDeviceLoginViewController() < +@interface FBSDKDeviceLoginViewController () < FBSDKDeviceLoginManagerDelegate > @end -@implementation FBSDKDeviceLoginViewController { +@implementation FBSDKDeviceLoginViewController +{ FBSDKDeviceLoginManager *_loginManager; BOOL _isRetry; } @@ -70,9 +71,9 @@ - (void)deviceLoginManager:(FBSDKDeviceLoginManager *)loginManager completedWith self.delegate = nil; FBSDKAccessToken *token = result.accessToken; - BOOL requireConfirm = (([FBSDKServerConfigurationManager cachedServerConfiguration].smartLoginOptions & FBSDKServerConfigurationSmartLoginOptionsRequireConfirmation) && - (token != nil) && - !_isRetry); + BOOL requireConfirm = (([FBSDKServerConfigurationManager cachedServerConfiguration].smartLoginOptions & FBSDKServerConfigurationSmartLoginOptionsRequireConfirmation) + && (token != nil) + && !_isRetry); if (requireConfirm) { FBSDKGraphRequest *graphRequest = [[FBSDKGraphRequest alloc] initWithGraphPath:@"me" parameters:@{ @"fields" : @"name" } @@ -87,16 +88,24 @@ - (void)deviceLoginManager:(FBSDKDeviceLoginManager *)loginManager completedWith }); }]; } else if ([self isNetworkError:error]) { - NSString *networkErrorMessage = NSLocalizedStringWithDefaultValue(@"LoginError.SystemAccount.Network", @"FacebookSDK", [FBSDKInternalUtility bundleForStrings], - @"Unable to connect to Facebook. Check your network connection and try again.", - @"The user facing error message when the Accounts framework encounters a network error."); + NSString *networkErrorMessage = NSLocalizedStringWithDefaultValue( + @"LoginError.SystemAccount.Network", + @"FacebookSDK", + [FBSDKInternalUtility bundleForStrings], + @"Unable to connect to Facebook. Check your network connection and try again.", + @"The user facing error message when the Accounts framework encounters a network error." + ); UIAlertController *alertController = [UIAlertController alertControllerWithTitle:nil message:networkErrorMessage preferredStyle:UIAlertControllerStyleAlert]; - NSString *localizedOK = NSLocalizedStringWithDefaultValue(@"ErrorRecovery.Alert.OK", @"FacebookSDK", [FBSDKInternalUtility bundleForStrings], - @"OK", - @"The title of the label to dismiss the alert when presenting user facing error messages"); + NSString *localizedOK = NSLocalizedStringWithDefaultValue( + @"ErrorRecovery.Alert.OK", + @"FacebookSDK", + [FBSDKInternalUtility bundleForStrings], + @"OK", + @"The title of the label to dismiss the alert when presenting user facing error messages" + ); UIAlertAction *okAction = [UIAlertAction actionWithTitle:localizedOK style:UIAlertActionStyleCancel - handler:^(UIAlertAction * _Nonnull action) { + handler:^(UIAlertAction *_Nonnull action) { [self dismissViewControllerAnimated:YES completion:^{ [delegate deviceLoginViewController:self didFailWithError:error]; }]; @@ -141,7 +150,7 @@ - (BOOL)isNetworkError:(NSError *)error #pragma mark - Private impl - (void)_notifySuccessForDelegate:(id)delegate - token:(FBSDKAccessToken *)token + token:(FBSDKAccessToken *)token { [FBSDKAccessToken setCurrentAccessToken:token]; [delegate deviceLoginViewControllerDidFinish:self]; @@ -152,30 +161,42 @@ - (void)_presentConfirmationForDelegate:(id for the alert when smart login requires confirmation"); + 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) { + handler:^(UIAlertAction *_Nonnull action) { [self dismissViewControllerAnimated:YES completion:^{ [self _notifySuccessForDelegate:delegate token:token]; }]; }]]; [alertController addAction:[UIAlertAction actionWithTitle:cancelTitle style:UIAlertActionStyleCancel - handler:^(UIAlertAction * _Nonnull action) { + handler:^(UIAlertAction *_Nonnull action) { self->_isRetry = YES; FBSDKDeviceDialogView *view = [[FBSDKDeviceDialogView alloc] initWithFrame:self.view.frame]; view.delegate = self; @@ -185,20 +206,19 @@ - (void)_presentConfirmationForDelegate:(id` (see FBSDKTVLoginButtonElement.h for details) * `` (see FBSDKTVLoginViewControllerElement.h for details) - * `` (see FBSDKTVShareButtonElement.h for details) */ NS_SWIFT_NAME(TVInterfaceFactory) diff --git a/FBSDKTVOSKit/FBSDKTVOSKit/FBSDKTVInterfaceFactory.m b/FBSDKTVOSKit/FBSDKTVOSKit/FBSDKTVInterfaceFactory.m index 686e46fed6..48261d50aa 100644 --- a/FBSDKTVOSKit/FBSDKTVOSKit/FBSDKTVInterfaceFactory.m +++ b/FBSDKTVOSKit/FBSDKTVOSKit/FBSDKTVInterfaceFactory.m @@ -18,45 +18,22 @@ #import "FBSDKTVInterfaceFactory.h" -#import - #import #ifdef FBSDKCOCOAPODS -#import + #import #else -#import "FBSDKCoreKit+Internal.h" + #import "FBSDKCoreKit+Internal.h" #endif #import "FBSDKDeviceLoginButton.h" #import "FBSDKTVLoginButtonElement.h" #import "FBSDKTVLoginViewControllerElement.h" -#import "FBSDKTVShareButtonElement.h" static NSString *const FBSDKLoginButtonTag = @"FBSDKLoginButton"; -static NSString *const FBSDKShareButtonTag = @"FBSDKShareButton"; static NSString *const FBSDKLoginViewControllerTag = @"FBSDKLoginViewController"; -static Class FBSDKDynamicallyLoadShareKitClassFromString(NSString *className) +@implementation FBSDKTVInterfaceFactory { - static NSMutableDictionary *classes; - static dispatch_once_t onceToken; - dispatch_once(&onceToken, ^{ - classes = [NSMutableDictionary dictionary]; - }); - if (!classes[className]) { - classes[className] = NSClassFromString(className); - } - Class clazz = classes[className]; - if (clazz == nil) { - NSString *message = [NSString stringWithFormat:@"Unable to load class %1$@. Did you link FBSDKShareKit.framework or add [%1$@ class] in your application delegate?", className]; - @throw [NSException exceptionWithName:NSInternalInconsistencyException - reason:message - userInfo:nil]; - } - return clazz; -} - -@implementation FBSDKTVInterfaceFactory { id _interfaceCreator; } @@ -68,8 +45,6 @@ - (instancetype)initWithInterfaceCreator:(id)interfaceCreat [TVElementFactory registerViewElementClass:[FBSDKTVLoginButtonElement class] forElementName:FBSDKLoginButtonTag]; [TVElementFactory registerViewElementClass:[FBSDKTVLoginViewControllerElement class] forElementName:FBSDKLoginViewControllerTag]; - [TVElementFactory registerViewElementClass:[FBSDKTVShareButtonElement class] forElementName:FBSDKShareButtonTag]; - return self; } @@ -84,24 +59,6 @@ - (UIView *)viewForElement:(TVViewElement *)element button.permissions = [self permissionsFromElement:element]; button.redirectURL = [NSURL URLWithString:element.attributes[@"redirectURL"]]; return button; - } else if ([element isKindOfClass:[FBSDKTVShareButtonElement class]]) { - FBSDKDeviceShareButton *button = [[FBSDKDynamicallyLoadShareKitClassFromString(@"FBSDKDeviceShareButton") alloc] initWithFrame:CGRectZero]; - id content = nil; - if (element.attributes[@"href"]) { - content = [[FBSDKDynamicallyLoadShareKitClassFromString(@"FBSDKShareLinkContent") alloc] init]; - content.contentURL = [NSURL URLWithString:element.attributes[@"href"]]; - } - - if (content) { - button.shareContent = content; - return button; - } else { - NSString *message = [NSString stringWithFormat:@"Invalid parameters for %@ (%@)", element.elementIdentifier, element.elementName]; - @throw [NSException exceptionWithName:NSInternalInconsistencyException - reason:message - userInfo:nil]; - } - } if ([_interfaceCreator respondsToSelector:@selector(viewForElement:existingView:)]) { return [_interfaceCreator viewForElement:element existingView:existingView]; diff --git a/FBSDKTVOSKit/FBSDKTVOSKit/FBSDKTVLoginButtonElement.m b/FBSDKTVOSKit/FBSDKTVOSKit/FBSDKTVLoginButtonElement.m index 54d3dd43c8..66c941fd95 100644 --- a/FBSDKTVOSKit/FBSDKTVOSKit/FBSDKTVLoginButtonElement.m +++ b/FBSDKTVOSKit/FBSDKTVOSKit/FBSDKTVLoginButtonElement.m @@ -30,14 +30,16 @@ - (void)deviceLoginButtonDidCancel:(FBSDKDeviceLoginButton *)button extraInfo:nil completion:NULL]; } + - (void)deviceLoginButton:(FBSDKDeviceLoginButton *)button didFailWithError:(NSError *)error { [self dispatchEventWithName:@"onFacebookLoginError" canBubble:YES cancellable:YES - extraInfo: @{ @"error": error } + extraInfo:@{ @"error" : error } completion:NULL]; } + - (void)deviceLoginButtonDidLogIn:(FBSDKDeviceLoginButton *)button { [self dispatchEventWithName:@"onFacebookLogin" @@ -46,6 +48,7 @@ - (void)deviceLoginButtonDidLogIn:(FBSDKDeviceLoginButton *)button extraInfo:nil completion:NULL]; } + - (void)deviceLoginButtonDidLogOut:(FBSDKDeviceLoginButton *)button { [self dispatchEventWithName:@"onFacebookLogout" @@ -54,4 +57,5 @@ - (void)deviceLoginButtonDidLogOut:(FBSDKDeviceLoginButton *)button extraInfo:nil completion:NULL]; } + @end diff --git a/FBSDKTVOSKit/FBSDKTVOSKit/FBSDKTVLoginViewControllerElement.m b/FBSDKTVOSKit/FBSDKTVOSKit/FBSDKTVLoginViewControllerElement.m index 16881231e0..bc58ac74d6 100644 --- a/FBSDKTVOSKit/FBSDKTVOSKit/FBSDKTVLoginViewControllerElement.m +++ b/FBSDKTVOSKit/FBSDKTVOSKit/FBSDKTVLoginViewControllerElement.m @@ -43,7 +43,7 @@ - (void)deviceLoginViewController:(FBSDKDeviceLoginViewController *)viewControll [self dispatchEventWithName:@"onFacebookLoginViewControllerError" canBubble:YES cancellable:YES - extraInfo: @{ @"error": error } + extraInfo:@{ @"error" : error } completion:NULL]; } diff --git a/FBSDKTVOSKit/FBSDKTVOSKit/FBSDKTVOSKit.h b/FBSDKTVOSKit/FBSDKTVOSKit/FBSDKTVOSKit.h index fd848a0749..c484a3ff9b 100644 --- a/FBSDKTVOSKit/FBSDKTVOSKit/FBSDKTVOSKit.h +++ b/FBSDKTVOSKit/FBSDKTVOSKit/FBSDKTVOSKit.h @@ -16,8 +16,6 @@ // 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 @@ -25,4 +23,5 @@ #import #import #import -#import + +#import diff --git a/FBSDKTVOSKit/FBSDKTVOSKitTests/FBSDKTVOSKitTests.m b/FBSDKTVOSKit/FBSDKTVOSKitTests/FBSDKTVOSKitTests.m index e9c7f7517c..95a2f4713d 100644 --- a/FBSDKTVOSKit/FBSDKTVOSKitTests/FBSDKTVOSKitTests.m +++ b/FBSDKTVOSKit/FBSDKTVOSKitTests/FBSDKTVOSKitTests.m @@ -24,9 +24,10 @@ @interface FBSDKTVOSKitTests : XCTestCase @implementation FBSDKTVOSKitTests -- (void)testExample { - // This is an example of a functional test case. - // Use XCTAssert and related functions to verify your tests produce the correct results. +- (void)testExample +{ + // This is an example of a functional test case. + // Use XCTAssert and related functions to verify your tests produce the correct results. } @end diff --git a/FacebookSDK.podspec b/FacebookSDK.podspec index 72e36372b2..bc7384d6db 100644 --- a/FacebookSDK.podspec +++ b/FacebookSDK.podspec @@ -3,7 +3,7 @@ Pod::Spec.new do |s| s.name = 'FacebookSDK' - s.version = '7.1.1' + s.version = '8.2.0' s.summary = 'Official Facebook SDK for iOS to access Facebook Platform' s.description = <<-DESC @@ -18,7 +18,7 @@ Pod::Spec.new do |s| s.author = 'Facebook' s.platform = :ios, :tvos - s.ios.deployment_target = '8.0' + s.ios.deployment_target = '9.0' s.tvos.deployment_target = '10.0' s.source = { :git => 'https://github.com/facebook/facebook-ios-sdk.git', diff --git a/FacebookSDK.xcworkspace/xcshareddata/xcschemes/BuildAllKits.xcscheme b/FacebookSDK.xcworkspace/xcshareddata/xcschemes/BuildAllKits.xcscheme index da0b807829..3befffa24d 100644 --- a/FacebookSDK.xcworkspace/xcshareddata/xcschemes/BuildAllKits.xcscheme +++ b/FacebookSDK.xcworkspace/xcshareddata/xcschemes/BuildAllKits.xcscheme @@ -110,16 +110,8 @@ buildConfiguration = "Debug" selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB" selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB" - shouldUseLaunchSchemeArgsEnv = "YES"> - - - - + shouldUseLaunchSchemeArgsEnv = "YES" + codeCoverageEnabled = "YES"> @@ -151,6 +143,16 @@ ReferencedContainer = "container:FBSDKShareKit/FBSDKShareKit.xcodeproj"> + + + + For projects that include Swift, use `FacebookCore`, `FacebookLogin`, and `FacebookShare` ### CocoaPods @@ -41,6 +40,16 @@ For projects that include Swift, use `FacebookCore`, `FacebookLogin`, and `Faceb 3. Check-out the tutorials available online at: 4. Start coding! Visit for tutorials and reference documentation. +## iOS 14 CHANGES + +### Data Disclosure + +Due to the release of iOS 14, tracking events that your app collects and sends to Facebook may require you to disclosed these data types in the App Store Connect questionnaire. It is your responsibility to ensure this is reflected in your application’s privacy policy. Visit our blogpost for information on affected Facebook SDKs, APIs, and products and the Apple App Store Privacy Details article to learn more about the data types you will need to disclose. + +link to FB blogpost https://developers.facebook.com/blog/post/2020/10/22/preparing-for-apple-app-store-data-disclosure-requirements/ + +apple store details https://developer.apple.com/app-store/app-privacy-details/ + ## FEATURES - Login - diff --git a/FBSDKCoreKit/FBSDKCoreKit/Basics/Internal/FBSDKBasicUtility.m b/Sources/FBSDKCoreKit_Basics/FBSDKBasicUtility.m similarity index 82% rename from FBSDKCoreKit/FBSDKCoreKit/Basics/Internal/FBSDKBasicUtility.m rename to Sources/FBSDKCoreKit_Basics/FBSDKBasicUtility.m index 7d18965daa..c27d8d81ab 100644 --- a/FBSDKCoreKit/FBSDKCoreKit/Basics/Internal/FBSDKBasicUtility.m +++ b/Sources/FBSDKCoreKit_Basics/FBSDKBasicUtility.m @@ -16,9 +16,11 @@ // 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 "FBSDKBasicUtility.h" + +#import #import -#import "FBSDKBasicUtility.h" #import "FBSDKTypeUtility.h" #define kChunkSize 1024 @@ -26,6 +28,24 @@ static NSString *const FBSDK_BASICUTILITY_ANONYMOUSIDFILENAME = @"com-facebook-sdk-PersistedAnonymousID.json"; static NSString *const FBSDK_BASICUTILITY_ANONYMOUSID_KEY = @"anon_id"; +void fb_dispatch_on_main_thread(dispatch_block_t block) +{ + if (block != nil) { + if ([NSThread isMainThread]) { + block(); + } else { + dispatch_async(dispatch_get_main_queue(), block); + } + } +} + +void fb_dispatch_on_default_thread(dispatch_block_t block) +{ + if (block != nil) { + dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), block); + } +} + @protocol BASIC_FBSDKError + (NSError *)invalidArgumentErrorWithName:(NSString *)name value:(id)value message:(NSString *)message; @@ -59,10 +79,10 @@ + (NSString *)JSONStringForObject:(id)object return [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding]; } -+ (BOOL)dictionary:(NSMutableDictionary *)dictionary -setJSONStringForObject:(id)object - forKey:(id)key - error:(NSError *__autoreleasing *)errorRef ++ (BOOL) dictionary:(NSMutableDictionary *)dictionary + setJSONStringForObject:(id)object + forKey:(id)key + error:(NSError *__autoreleasing *)errorRef { if (!object || !key) { return YES; @@ -86,10 +106,10 @@ + (id)_convertObjectToJSONObject:(id)object object = ((NSURL *)object).absoluteString; } else if ([object isKindOfClass:[NSDictionary class]]) { NSMutableDictionary *dictionary = [[NSMutableDictionary alloc] init]; - [FBSDKTypeUtility dictionary:(NSDictionary *)object enumerateKeysAndObjectsUsingBlock:^(id key, id obj, BOOL *dictionaryStop) { + [FBSDKTypeUtility dictionary:(NSDictionary *) object enumerateKeysAndObjectsUsingBlock:^(id key, id obj, BOOL *dictionaryStop) { [FBSDKTypeUtility dictionary:dictionary - setObject:[self _convertObjectToJSONObject:obj invalidObjectHandler:invalidObjectHandler stop:&stop] - forKey:[FBSDKTypeUtility stringValue:key]]; + setObject:[self _convertObjectToJSONObject:obj invalidObjectHandler:invalidObjectHandler stop:&stop] + forKey:[FBSDKTypeUtility stringValue:key]]; if (stop) { *dictionaryStop = YES; } @@ -126,16 +146,16 @@ + (id)objectForJSONString:(NSString *)string error:(NSError *__autoreleasing *)e return [FBSDKTypeUtility JSONObjectWithData:data options:NSJSONReadingAllowFragments error:errorRef]; } -+ (NSString *)queryStringWithDictionary:(NSDictionary *)dictionary - error:(NSError *__autoreleasing *)errorRef - invalidObjectHandler:(FBSDKInvalidObjectHandler)invalidObjectHandler ++ (nullable NSString *)queryStringWithDictionary:(NSDictionary *)dictionary + error:(NSError *__autoreleasing *)errorRef + invalidObjectHandler:(FBSDKInvalidObjectHandler)invalidObjectHandler { NSMutableString *queryString = [[NSMutableString alloc] init]; __block BOOL hasParameters = NO; if (dictionary) { NSMutableArray *keys = [dictionary.allKeys mutableCopy]; // remove non-string keys, as they are not valid - [keys filterUsingPredicate:[NSPredicate predicateWithBlock:^BOOL(id evaluatedObject, NSDictionary *bindings) { + [keys filterUsingPredicate:[NSPredicate predicateWithBlock:^BOOL (id evaluatedObject, NSDictionary *bindings) { return [evaluatedObject isKindOfClass:[NSString class]]; }]]; // sort the keys so that the query string order is deterministic @@ -181,11 +201,13 @@ + (id)convertRequestValue:(id)value #pragma clang diagnostic ignored "-Wdeprecated-declarations" + (NSString *)URLEncode:(NSString *)value { - return (__bridge_transfer NSString *)CFURLCreateStringByAddingPercentEscapes(NULL, - (CFStringRef)value, - NULL, // characters to leave unescaped - CFSTR(":!*();@/&?+$,='"), - kCFStringEncodingUTF8); + return (__bridge_transfer NSString *)CFURLCreateStringByAddingPercentEscapes( + NULL, + (CFStringRef)value, + NULL, // characters to leave unescaped + CFSTR(":!*();@/&?+$,='"), + kCFStringEncodingUTF8 + ); } #pragma clang diagnostic pop @@ -224,10 +246,10 @@ + (NSString *)URLEncode:(NSString *)value + (NSString *)URLDecode:(NSString *)value { value = [value stringByReplacingOccurrencesOfString:@"+" withString:@" "]; -#pragma clang diagnostic push -#pragma clang diagnostic ignored "-Wdeprecated-declarations" + #pragma clang diagnostic push + #pragma clang diagnostic ignored "-Wdeprecated-declarations" value = [value stringByReplacingPercentEscapesUsingEncoding:NSUTF8StringEncoding]; -#pragma clang diagnostic pop + #pragma clang diagnostic pop return value; } @@ -323,4 +345,28 @@ + (void)persistAnonymousID:(NSString *)anonymousID error:nil]; } ++ (NSString *)SHA256Hash:(NSObject *)input +{ + NSData *data = nil; + + if ([input isKindOfClass:[NSData class]]) { + data = (NSData *)input; + } else if ([input isKindOfClass:[NSString class]]) { + data = [(NSString *)input dataUsingEncoding:NSUTF8StringEncoding]; + } + + if (!data) { + return nil; + } + + uint8_t digest[CC_SHA256_DIGEST_LENGTH]; + CC_SHA256(data.bytes, (CC_LONG)data.length, digest); + NSMutableString *hashed = [NSMutableString stringWithCapacity:CC_SHA256_DIGEST_LENGTH * 2]; + for (int i = 0; i < CC_SHA256_DIGEST_LENGTH; i++) { + [hashed appendFormat:@"%02x", digest[i]]; + } + + return [hashed copy]; +} + @end diff --git a/FBSDKCoreKit/FBSDKCoreKit/Basics/Instrument/FBSDKCrashHandler.m b/Sources/FBSDKCoreKit_Basics/FBSDKCrashHandler.m similarity index 67% rename from FBSDKCoreKit/FBSDKCoreKit/Basics/Instrument/FBSDKCrashHandler.m rename to Sources/FBSDKCoreKit_Basics/FBSDKCrashHandler.m index 33c5b8c9f1..ccae730c05 100644 --- a/FBSDKCoreKit/FBSDKCoreKit/Basics/Instrument/FBSDKCrashHandler.m +++ b/Sources/FBSDKCoreKit_Basics/FBSDKCrashHandler.m @@ -18,20 +18,17 @@ #import "FBSDKCrashHandler.h" -#import - #import -#include +#import -#import "FBSDKBasicUtility.h" #import "FBSDKLibAnalyzer.h" #import "FBSDKTypeUtility.h" #define FBSDK_MAX_CRASH_LOGS 5 #define FBSDK_CRASH_PATH_NAME @"instrument" #ifndef FBSDK_VERSION_STRING -#define FBSDK_VERSION_STRING @"7.1.1" + #define FBSDK_VERSION_STRING @"8.2.0" #endif static NSUncaughtExceptionHandler *previousExceptionHandler = NULL; @@ -54,10 +51,6 @@ @implementation FBSDKCrashHandler static NSArray *> *_processedCrashLogs; static BOOL _isTurnedOff; -void FBSDKSignalHandler(int signal); - -# pragma mark - Class Methods - + (void)initialize { NSString *dirPath = [NSTemporaryDirectory() stringByAppendingPathComponent:FBSDK_CRASH_PATH_NAME]; @@ -70,80 +63,68 @@ + (void)initialize _observers = [[NSHashTable alloc] init]; } -+ (void)sendCrashLogs -{ - NSArray> *observers = [_observers copy]; - for (id observer in observers) { - if (observer && [observer respondsToSelector:@selector(didReceiveCrashLogs:)]) { - NSArray *> *filteredCrashLogs = [self filterCrashLogs:observer.prefixes processedCrashLogs:_processedCrashLogs]; - [observer didReceiveCrashLogs:filteredCrashLogs]; - } - } -} - -+ (NSArray *> *)filterCrashLogs:(NSArray *)prefixList - processedCrashLogs:(NSArray *> *)processedCrashLogs -{ - NSMutableArray *> *crashLogs = [NSMutableArray array]; - for (NSDictionary *crashLog in processedCrashLogs) { - NSArray *callstack = crashLog[kFBSDKCallstack]; - if ([self callstack:callstack containsPrefix:prefixList]) { - [FBSDKTypeUtility array:crashLogs addObject:crashLog]; - } - } - return crashLogs; -} +#pragma mark - Public API -+ (BOOL)callstack:(NSArray *)callstack - containsPrefix:(NSArray *)prefixList ++ (NSString *)getFBSDKVersion { - NSString *callStackString = [callstack componentsJoinedByString:@""]; - for (NSString *prefix in prefixList) { - if ([callStackString containsString:prefix]) { - return YES; - } - } - return NO; + return FBSDK_VERSION_STRING; } + (void)disable { _isTurnedOff = YES; - [FBSDKCrashHandler uninstallExceptionsHandler]; + [FBSDKCrashHandler _uninstallExceptionsHandler]; _observers = nil; } + (void)addObserver:(id)observer { - if (_isTurnedOff || ![self isSafeToGenerateMapping]) { + if (_isTurnedOff || ![self _isSafeToGenerateMapping]) { return; } static dispatch_once_t onceToken = 0; dispatch_once(&onceToken, ^{ - [FBSDKCrashHandler installExceptionsHandler]; - [FBSDKCrashHandler installSignalHandler]; - _processedCrashLogs = [self getProcessedCrashLogs]; + [FBSDKCrashHandler _installExceptionsHandler]; + _processedCrashLogs = [self _getProcessedCrashLogs]; }); - if (![_observers containsObject:observer]) { - [_observers addObject:observer]; - [self generateMethodMapping:observer]; - [self sendCrashLogs]; + @synchronized(_observers) { + if (![_observers containsObject:observer]) { + [_observers addObject:observer]; + dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_BACKGROUND, 0), ^(void) { + [self _generateMethodMapping:observer]; + }); + [self _sendCrashLogs]; + } } } + (void)removeObserver:(id)observer { - if ([_observers containsObject:observer]) { - [_observers removeObject:observer]; - if (_observers.count == 0) { - [FBSDKCrashHandler uninstallExceptionsHandler]; + @synchronized(_observers) { + if ([_observers containsObject:observer]) { + [_observers removeObject:observer]; + if (_observers.count == 0) { + [FBSDKCrashHandler _uninstallExceptionsHandler]; + } } } } -# pragma mark handler function ++ (void)clearCrashReportFiles +{ + NSArray *files = [[NSFileManager defaultManager] contentsOfDirectoryAtPath:directoryPath error:nil]; -+ (void)installExceptionsHandler + for (NSUInteger i = 0; i < files.count; i++) { + // remove all crash related files except for the current mapping table + if ([[FBSDKTypeUtility array:files objectAtIndex:i] hasPrefix:@"crash_"] && ![[FBSDKTypeUtility array:files objectAtIndex:i] containsString:mappingTableIdentifier]) { + [[NSFileManager defaultManager] removeItemAtPath:[directoryPath stringByAppendingPathComponent:[FBSDKTypeUtility array:files objectAtIndex:i]] error:nil]; + } + } +} + +# pragma mark - Handler + ++ (void)_installExceptionsHandler { NSUncaughtExceptionHandler *currentHandler = NSGetUncaughtExceptionHandler(); @@ -153,7 +134,7 @@ + (void)installExceptionsHandler } } -+ (void)uninstallExceptionsHandler ++ (void)_uninstallExceptionsHandler { NSSetUncaughtExceptionHandler(previousExceptionHandler); previousExceptionHandler = nil; @@ -161,66 +142,28 @@ + (void)uninstallExceptionsHandler static void FBSDKExceptionHandler(NSException *exception) { - [FBSDKCrashHandler saveException:exception]; + [FBSDKCrashHandler _saveException:exception]; if (previousExceptionHandler) { previousExceptionHandler(exception); } } -+ (void)installSignalHandler -{ - signal(SIGBUS, FBSDKSignalHandler); - signal(SIGFPE, FBSDKSignalHandler); - signal(SIGILL, FBSDKSignalHandler); - signal(SIGPIPE, FBSDKSignalHandler); - signal(SIGSEGV, FBSDKSignalHandler); - signal(SIGSYS, FBSDKSignalHandler); -} - -void FBSDKSignalHandler(int sig) -{ - NSMutableArray *callStack = [[NSThread callStackSymbols] mutableCopy]; - if (callStack) { - NSIndexSet *indexSet = [NSIndexSet indexSetWithIndexesInRange:NSMakeRange(0, 2)]; - if (callStack.count > 2 && [callStack objectsAtIndexes:indexSet]) { - [callStack removeObjectsAtIndexes:indexSet]; - } - } - [FBSDKCrashHandler saveSignal:sig withCallStack:callStack]; - - // reset to default handler - signal(sig, SIG_DFL); - // re-signal to default handler - raise(sig); -} - -#pragma mark - Storage +#pragma mark - Storage & Process -+ (void)saveException:(NSException *)exception ++ (void)_saveException:(NSException *)exception { if (exception.callStackSymbols && exception.name) { NSArray *stackSymbols = [NSArray arrayWithArray:exception.callStackSymbols]; - [self saveCrashLog:@{ - kFBSDKCallstack : stackSymbols, - kFBSDKCrashReason : exception.name, - }]; + [self _saveCrashLog:@{ + kFBSDKCallstack : stackSymbols, + kFBSDKCrashReason : exception.name, + }]; } } -+ (void)saveSignal:(int)signal withCallStack:(NSArray *)callStack ++ (NSArray *> *)_getProcessedCrashLogs { - if (callStack) { - NSString *signalDescription = [NSString stringWithCString:strsignal(signal) encoding:NSUTF8StringEncoding] ?: [NSString stringWithFormat:@"SIGNUM(%i)", signal]; - [self saveCrashLog:@{ - kFBSDKCallstack : callStack, - kFBSDKCrashReason : signalDescription, - }]; - } -} - -+ (NSArray *> *)getProcessedCrashLogs -{ - NSArray *> *crashLogs = [self loadCrashLogs]; + NSArray *> *crashLogs = [self _loadCrashLogs]; if (0 == crashLogs.count) { [self clearCrashReportFiles]; return nil; @@ -229,13 +172,13 @@ + (void)saveSignal:(int)signal withCallStack:(NSArray *)callStack for (NSDictionary *crashLog in crashLogs) { NSArray *callstack = crashLog[kFBSDKCallstack]; - NSData *data = [self loadLibData:crashLog]; + NSData *data = [self _loadLibData:crashLog]; if (!data) { continue; } - NSDictionary *methodMapping = [FBSDKTypeUtility JSONObjectWithData:data - options:kNilOptions - error:nil]; + NSDictionary *methodMapping = [FBSDKTypeUtility JSONObjectWithData:data + options:kNilOptions + error:nil]; NSArray *symbolicatedCallstack = [FBSDKLibAnalyzer symbolicateCallstack:callstack methodMapping:methodMapping]; NSMutableDictionary *symbolicatedCrashLog = [NSMutableDictionary dictionaryWithDictionary:crashLog]; if (symbolicatedCallstack) { @@ -247,22 +190,22 @@ + (void)saveSignal:(int)signal withCallStack:(NSArray *)callStack return processedCrashLogs; } -+ (NSArray *> *)loadCrashLogs ++ (NSArray *> *)_loadCrashLogs { NSArray *files = [[NSFileManager defaultManager] contentsOfDirectoryAtPath:directoryPath error:NULL]; - NSArray *fileNames = [[self getCrashLogFileNames:files] sortedArrayUsingComparator:^NSComparisonResult(id _Nonnull obj1, id _Nonnull obj2){ + NSArray *fileNames = [[self _getCrashLogFileNames:files] sortedArrayUsingComparator:^NSComparisonResult (id _Nonnull obj1, id _Nonnull obj2) { return [obj2 compare:obj1]; }]; NSMutableArray *> *crashLogArray = [NSMutableArray array]; for (NSUInteger i = 0; i < MIN(fileNames.count, FBSDK_MAX_CRASH_LOGS); i++) { - NSData *data = [self loadCrashLog:[FBSDKTypeUtility array:fileNames objectAtIndex:i]]; + NSData *data = [self _loadCrashLog:[FBSDKTypeUtility array:fileNames objectAtIndex:i]]; if (!data) { continue; } - NSDictionary* crashLog = [FBSDKTypeUtility JSONObjectWithData:data - options:kNilOptions - error:nil]; + NSDictionary *crashLog = [FBSDKTypeUtility JSONObjectWithData:data + options:kNilOptions + error:nil]; if (crashLog) { [FBSDKTypeUtility array:crashLogArray addObject:crashLog]; } @@ -270,24 +213,12 @@ + (void)saveSignal:(int)signal withCallStack:(NSArray *)callStack return [crashLogArray copy]; } -+ (nullable NSData *)loadCrashLog:(NSString *)fileName ++ (nullable NSData *)_loadCrashLog:(NSString *)fileName { return [NSData dataWithContentsOfFile:[directoryPath stringByAppendingPathComponent:fileName] options:NSDataReadingMappedIfSafe error:nil]; } -+ (void)clearCrashReportFiles -{ - NSArray *files = [[NSFileManager defaultManager] contentsOfDirectoryAtPath:directoryPath error:nil]; - - for (NSUInteger i = 0; i < files.count; i++) { - // remove all crash related files except for the current mapping table - if ([[FBSDKTypeUtility array:files objectAtIndex:i] hasPrefix:@"crash_"] && ![[FBSDKTypeUtility array:files objectAtIndex:i] containsString:mappingTableIdentifier]) { - [[NSFileManager defaultManager] removeItemAtPath:[directoryPath stringByAppendingPathComponent:[FBSDKTypeUtility array:files objectAtIndex:i]] error:nil]; - } - } -} - -+ (NSArray *)getCrashLogFileNames:(NSArray *)files ++ (NSArray *)_getCrashLogFileNames:(NSArray *)files { NSMutableArray *fileNames = [NSMutableArray array]; @@ -300,7 +231,7 @@ + (void)clearCrashReportFiles return fileNames; } -+ (void)saveCrashLog:(NSDictionary *)crashLog ++ (void)_saveCrashLog:(NSDictionary *)crashLog { NSMutableDictionary *completeCrashLog = [NSMutableDictionary dictionaryWithDictionary:crashLog]; NSString *currentTimestamp = [NSString stringWithFormat:@"%.0lf", [[NSDate date] timeIntervalSince1970]]; @@ -321,11 +252,48 @@ + (void)saveCrashLog:(NSDictionary *)crashLog NSData *data = [FBSDKTypeUtility dataWithJSONObject:completeCrashLog options:0 error:nil]; - [data writeToFile:[self getPathToCrashFile:currentTimestamp] + [data writeToFile:[self _getPathToCrashFile:currentTimestamp] atomically:YES]; } -+ (void)generateMethodMapping:(id)observer ++ (void)_sendCrashLogs +{ + for (id observer in _observers) { + if (observer && [observer respondsToSelector:@selector(didReceiveCrashLogs:)]) { + NSArray *> *filteredCrashLogs = [self _filterCrashLogs:observer.prefixes processedCrashLogs:_processedCrashLogs]; + [observer didReceiveCrashLogs:filteredCrashLogs]; + } + } +} + ++ (NSArray *> *)_filterCrashLogs:(NSArray *)prefixList + processedCrashLogs:(NSArray *> *)processedCrashLogs +{ + NSMutableArray *> *crashLogs = [NSMutableArray array]; + for (NSDictionary *crashLog in processedCrashLogs) { + NSArray *callstack = crashLog[kFBSDKCallstack]; + if ([self _callstack:callstack containsPrefix:prefixList]) { + [FBSDKTypeUtility array:crashLogs addObject:crashLog]; + } + } + return crashLogs; +} + ++ (BOOL)_callstack:(NSArray *)callstack + containsPrefix:(NSArray *)prefixList +{ + NSString *callStackString = [callstack componentsJoinedByString:@""]; + for (NSString *prefix in prefixList) { + if ([callStackString containsString:prefix]) { + return YES; + } + } + return NO; +} + +#pragma mark - Method Mapping + ++ (void)_generateMethodMapping:(id)observer { if (observer.prefixes.count == 0) { return; @@ -333,50 +301,44 @@ + (void)generateMethodMapping:(id)observer [[NSUserDefaults standardUserDefaults] setObject:mappingTableIdentifier forKey:kFBSDKMappingTableIdentifier]; NSDictionary *methodMapping = [FBSDKLibAnalyzer getMethodsTable:observer.prefixes frameworks:observer.frameworks]; - if (methodMapping.count > 0){ + if (methodMapping.count > 0) { NSData *data = [FBSDKTypeUtility dataWithJSONObject:methodMapping options:0 error:nil]; - [data writeToFile:[self getPathToLibDataFile:mappingTableIdentifier] - atomically:YES]; + [data writeToFile:[self _getPathToLibDataFile:mappingTableIdentifier] + atomically:YES]; } } -+ (nullable NSData *)loadLibData:(NSDictionary *)crashLog ++ (nullable NSData *)_loadLibData:(NSDictionary *)crashLog { NSString *identifier = [FBSDKTypeUtility dictionary:crashLog objectForKey:kFBSDKMappingTableIdentifier ofType:NSObject.class]; - return [NSData dataWithContentsOfFile:[self getPathToLibDataFile:identifier] options:NSDataReadingMappedIfSafe error:nil]; + return [NSData dataWithContentsOfFile:[self _getPathToLibDataFile:identifier] options:NSDataReadingMappedIfSafe error:nil]; } -+ (NSString *)getPathToCrashFile:(NSString *)timestamp ++ (NSString *)_getPathToCrashFile:(NSString *)timestamp { return [directoryPath stringByAppendingPathComponent: [NSString stringWithFormat:@"crash_log_%@.json", timestamp]]; } -+ (NSString *)getPathToLibDataFile:(NSString *)identifier ++ (NSString *)_getPathToLibDataFile:(NSString *)identifier { return [directoryPath stringByAppendingPathComponent: [NSString stringWithFormat:@"crash_lib_data_%@.json", identifier]]; - } -+ (BOOL)isSafeToGenerateMapping ++ (BOOL)_isSafeToGenerateMapping { #if TARGET_OS_SIMULATOR return YES; #else NSString *identifier = [[NSUserDefaults standardUserDefaults] objectForKey:kFBSDKMappingTableIdentifier]; - //first app start + // first app start if (!identifier) { return YES; } - return [[NSFileManager defaultManager] fileExistsAtPath:[self getPathToLibDataFile:identifier]]; + return [[NSFileManager defaultManager] fileExistsAtPath:[self _getPathToLibDataFile:identifier]]; #endif } -+ (NSString *)getFBSDKVersion -{ - return FBSDK_VERSION_STRING; -} - @end diff --git a/Sources/FBSDKCoreKit_Basics/FBSDKJSONValue.m b/Sources/FBSDKCoreKit_Basics/FBSDKJSONValue.m new file mode 100644 index 0000000000..f73f2fd4ea --- /dev/null +++ b/Sources/FBSDKCoreKit_Basics/FBSDKJSONValue.m @@ -0,0 +1,232 @@ +// 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 "FBSDKJSONValue.h" + +#import + +#import "FBSDKBasicUtility.h" +#import "FBSDKSafeCast.h" +#import "FBSDKTypeUtility.h" + +@interface FBSDKJSONField () +- (instancetype)initWithPotentialJSONField:(id)obj; +@end + +static NSArray *createArray(id obj) +{ + NSArray *const original = FBSDK_CAST_TO_CLASS_OR_NIL(obj, NSArray); + if (!original) { + return @[]; + } + + NSMutableArray *const fields = + [[NSMutableArray alloc] initWithCapacity:original.count]; + + for (id field in original) { + FBSDKJSONField *const f = [[FBSDKJSONField alloc] initWithPotentialJSONField:field]; + if (f) { + [fields addObject:f]; + } + } + + return fields; +} + +static NSDictionary *createDictionary(id obj) +{ + NSDictionary *const original = FBSDK_CAST_TO_CLASS_OR_NIL(obj, NSDictionary); + if (!original) { + return @{}; + } + + NSMutableDictionary *const fields = + [[NSMutableDictionary alloc] initWithCapacity:original.count]; + + for (id key in original) { + // This is just a sanity check. Apple should only give us string keys + // anyway. + if (![key respondsToSelector:@selector(isKindOfClass:)] || ![key isKindOfClass:NSString.class]) { + continue; + } + NSString *const stringKey = (NSString *)key; + + FBSDKJSONField *const typedField = [[FBSDKJSONField alloc] initWithPotentialJSONField:original[key]]; + if (typedField) { + fields[stringKey] = typedField; + } + } + + return fields; +} + +@implementation FBSDKJSONValue + +- (instancetype)initWithPotentialJSONObject:(id)obj +{ + // If this isn't a real JSON object, dump it. + if (![FBSDKTypeUtility isValidJSONObject:obj]) { + return nil; + } + _rawObject = obj; + + return self; +} + +- (void)matchArray:(void (^)(NSArray *))arrayMatcher + dictionary:(void (^)(NSDictionary *))dictMatcher +{ + if (arrayMatcher && [_rawObject isKindOfClass:[NSArray class]]) { + arrayMatcher(createArray(_rawObject)); + } else if (dictMatcher && [_rawObject isKindOfClass:[NSDictionary class]]) { + dictMatcher(createDictionary(_rawObject)); + } +} + +- (NSDictionary *_Nullable)matchDictionaryOrNil +{ + __block NSDictionary *result = nil; + [self matchArray:nil dictionary:^(NSDictionary *_Nonnull value) { + result = value; + }]; + return result; +} + +- (NSDictionary *_Nullable)unsafe_matchDictionaryOrNil +{ + return [_rawObject isKindOfClass:NSDictionary.class] ? _rawObject : nil; +} + +- (NSArray *_Nullable)matchArrayOrNil +{ + __block NSArray *result = nil; + [self matchArray:^(NSArray *_Nonnull value) { + result = value; + } dictionary:nil]; + return result; +} + +- (NSArray *_Nullable)unsafe_matchArrayOrNil +{ + __block BOOL isArray = NO; + [self matchArray:^(NSArray *_Nonnull _) { + isArray = YES; + } dictionary:nil]; + + return [_rawObject isKindOfClass:NSArray.class] ? _rawObject : nil; +} + +@end + +@implementation FBSDKJSONField + +- (instancetype)initWithPotentialJSONField:(id)obj +{ + // If this is nil, don't wrap it. + if (obj == nil) { + return nil; + } + + // Per Apple's Docs, these are the only types FBSDKTypeUtility can return. + if ( + ![obj isKindOfClass:NSString.class] + && ![obj isKindOfClass:NSNumber.class] + && ![obj isKindOfClass:NSNull.class] + && ![obj isKindOfClass:NSDictionary.class] + && ![obj isKindOfClass:NSArray.class]) { + return nil; + } + + if (self = [super init]) { + _rawObject = obj; + } + + return self; +} + +- (void)matchArray:(void (^)(NSArray *))arrayMatcher + dictionary:(void (^)(NSDictionary *_Nonnull))dictionaryMatcher + string:(void (^)(NSString *_Nonnull))stringMatcher + number:(void (^)(NSNumber *_Nonnull))numberMatcher + null:(void (^)(void))nullMatcher +{ + if (nullMatcher && [_rawObject isKindOfClass:NSNull.class]) { + nullMatcher(); + } else if (numberMatcher && [_rawObject isKindOfClass:NSNumber.class]) { + numberMatcher(_rawObject); + } else if (stringMatcher && [_rawObject isKindOfClass:NSString.class]) { + stringMatcher(_rawObject); + } else if (arrayMatcher && [_rawObject isKindOfClass:NSArray.class]) { + arrayMatcher(createArray(_rawObject)); + } else if (dictionaryMatcher && [_rawObject isKindOfClass:NSDictionary.class]) { + dictionaryMatcher(createDictionary(_rawObject)); + } +} + +- (NSArray *_Nullable)arrayOrNil +{ + __block NSArray *result = nil; + [self matchArray:^(NSArray *_Nonnull a) { + result = [a copy]; + } dictionary:nil string:nil number:nil null:nil]; + return result; +} + +- (NSDictionary *_Nullable)dictionaryOrNil +{ + __block NSDictionary *result = nil; + [self matchArray:nil dictionary:^(NSDictionary *_Nonnull d) { + result = [d copy]; + } string:nil number:nil null:nil]; + return result; +} + +- (NSString *_Nullable)stringOrNil +{ + __block NSString *result = nil; + [self matchArray:nil dictionary:nil string:^(NSString *_Nonnull s) { + result = [s copy]; + } number:nil null:nil]; + return result; +} + +- (NSNumber *_Nullable)numberOrNil +{ + __block NSNumber *result = nil; + [self matchArray:nil dictionary:nil string:nil number:^(NSNumber *_Nonnull n) { + result = n; + } null:nil]; + return result; +} + +- (NSNull *_Nullable)nullOrNil +{ + __block NSNull *result = nil; + [self matchArray:nil dictionary:nil string:nil number:nil null:^{ + result = [NSNull null]; + }]; + return result; +} + +@end + +FBSDKJSONValue *_Nullable FBSDKCreateJSONFromString(NSString *_Nullable string, NSError *__autoreleasing *errorRef) +{ + return + [[FBSDKJSONValue alloc] initWithPotentialJSONObject:[FBSDKBasicUtility objectForJSONString:string error:errorRef]]; +} diff --git a/FBSDKCoreKit/FBSDKCoreKit/Basics/Instrument/FBSDKLibAnalyzer.m b/Sources/FBSDKCoreKit_Basics/FBSDKLibAnalyzer.m similarity index 72% rename from FBSDKCoreKit/FBSDKCoreKit/Basics/Instrument/FBSDKLibAnalyzer.m rename to Sources/FBSDKCoreKit_Basics/FBSDKLibAnalyzer.m index 2d0567063c..76312f2422 100644 --- a/FBSDKCoreKit/FBSDKCoreKit/Basics/Instrument/FBSDKLibAnalyzer.m +++ b/Sources/FBSDKCoreKit_Basics/FBSDKLibAnalyzer.m @@ -17,10 +17,11 @@ // CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. #import "FBSDKLibAnalyzer.h" -#import "FBSDKTypeUtility.h" #import +#import "FBSDKTypeUtility.h" + @implementation FBSDKLibAnalyzer static NSMutableDictionary *_methodMapping; @@ -33,28 +34,70 @@ + (void)initialize + (NSDictionary *)getMethodsTable:(NSArray *)prefixes frameworks:(NSArray *)frameworks { - NSArray *allClasses = [self getClassNames:prefixes frameworks:frameworks]; + NSArray *allClasses = [self _getClassNames:prefixes frameworks:frameworks]; for (NSString *className in allClasses) { Class class = NSClassFromString(className); if (class) { - [self addClass:class isClassMethod:NO]; - [self addClass:object_getClass(class) isClassMethod:YES]; + [self _addClass:class isClassMethod:NO]; + [self _addClass:object_getClass(class) isClassMethod:YES]; } } - @synchronized (_methodMapping) { + @synchronized(_methodMapping) { return [_methodMapping copy]; } } -#pragma mark - private methods ++ (nullable NSArray *)symbolicateCallstack:(NSArray *)callstack + methodMapping:(NSDictionary *)methodMapping +{ + if (!callstack || !methodMapping) { + return nil; + } + NSArray *sortedAllAddress = [methodMapping.allKeys sortedArrayUsingComparator:^NSComparisonResult (id _Nonnull obj1, id _Nonnull obj2) { + return [obj1 compare:obj2]; + }]; + + BOOL containsFBSDKFunction = NO; + NSInteger nonSDKMethodCount = 0; + NSMutableArray *symbolicatedCallstack = [NSMutableArray array]; + + for (NSUInteger i = 0; i < callstack.count; i++) { + NSString *rawAddress = [self _getAddress:[FBSDKTypeUtility array:callstack objectAtIndex:i]]; + if (rawAddress.length < 10) { + continue; + } + NSString *addressString = [NSString stringWithFormat:@"0x%@", [rawAddress substringWithRange:NSMakeRange(rawAddress.length - 10, 10)]]; + NSString *methodAddress = [self _searchMethod:addressString sortedAllAddress:sortedAllAddress]; + + if (methodAddress) { + containsFBSDKFunction = YES; + nonSDKMethodCount == 0 ?: [FBSDKTypeUtility array:symbolicatedCallstack addObject:[NSString stringWithFormat:@"(%ld DEV METHODS)", (long)nonSDKMethodCount]]; + nonSDKMethodCount = 0; + NSString *methodName = [FBSDKTypeUtility dictionary:methodMapping objectForKey:methodAddress ofType:NSObject.class]; + + // filter out cxx_destruct + if ([methodName containsString:@".cxx_destruct"]) { + return nil; + } + [FBSDKTypeUtility array:symbolicatedCallstack addObject:[NSString stringWithFormat:@"%@%@", methodName, [self _getOffset:addressString secondString:methodAddress]]]; + } else { + nonSDKMethodCount++; + } + } + nonSDKMethodCount == 0 ?: [FBSDKTypeUtility array:symbolicatedCallstack addObject:[NSString stringWithFormat:@"(%ld DEV METHODS)", (long)nonSDKMethodCount]]; + + return containsFBSDKFunction ? symbolicatedCallstack : nil; +} -+ (NSArray *)getClassNames:(NSArray *)prefixes - frameworks:(NSArray *)frameworks +#pragma mark - Private Methods + ++ (NSArray *)_getClassNames:(NSArray *)prefixes + frameworks:(NSArray *)frameworks { NSMutableArray *classNames = [NSMutableArray new]; // from main bundle - [classNames addObjectsFromArray:[self getClassesFrom:[[NSBundle mainBundle] executablePath] - prefixes:prefixes]]; + [classNames addObjectsFromArray:[self _getClassesFrom:[[NSBundle mainBundle] executablePath] + prefixes:prefixes]]; // from dynamic libraries if (frameworks.count > 0) { unsigned int count = 0; @@ -63,8 +106,8 @@ + (void)initialize NSString *image = [NSString stringWithUTF8String:images[i]]; for (NSString *framework in frameworks) { if ([image containsString:framework]) { - [classNames addObjectsFromArray:[self getClassesFrom:image - prefixes:nil]]; + [classNames addObjectsFromArray:[self _getClassesFrom:image + prefixes:nil]]; } } } @@ -74,13 +117,13 @@ + (void)initialize return [classNames copy]; } -+ (NSArray *)getClassesFrom:(NSString *)image - prefixes:(NSArray *)prefixes ++ (NSArray *)_getClassesFrom:(NSString *)image + prefixes:(NSArray *)prefixes { NSMutableArray *classNames = [NSMutableArray array]; unsigned int count = 0; const char **classes = objc_copyClassNamesForImage([image UTF8String], &count); - for (unsigned int i = 0; i < count; i++){ + for (unsigned int i = 0; i < count; i++) { NSString *className = [NSString stringWithUTF8String:classes[i]]; if (prefixes.count > 0) { for (NSString *prefix in prefixes) { @@ -97,8 +140,8 @@ + (void)initialize return [classNames copy]; } -+ (void)addClass:(Class)class - isClassMethod:(BOOL)isClassMethod ++ (void)_addClass:(Class)class + isClassMethod:(BOOL)isClassMethod { unsigned int methodsCount = 0; Method *methods = class_copyMethodList(class, &methodsCount); @@ -119,7 +162,7 @@ + (void)addClass:(Class)class NSStringFromSelector(selector)]; if (methodAddress && methodName) { - @synchronized (_methodMapping) { + @synchronized(_methodMapping) { [FBSDKTypeUtility dictionary:_methodMapping setObject:methodName forKey:methodAddress]; } } @@ -128,62 +171,22 @@ + (void)addClass:(Class)class free(methods); } -+ (NSArray *)symbolicateCallstack:(NSArray *)callstack - methodMapping:(NSDictionary *)methodMapping ++ (nullable NSString *)_getAddress:(nullable NSString *)callstackEntry { - if (!callstack || !methodMapping) { - return nil; - } - NSArray *sortedAllAddress = [methodMapping.allKeys sortedArrayUsingComparator:^NSComparisonResult(id _Nonnull obj1, id _Nonnull obj2) { - return [obj1 compare:obj2]; - }]; - - BOOL containsFBSDKFunction = NO; - NSInteger nonSDKMethodCount = 0; - NSMutableArray *symbolicatedCallstack = [NSMutableArray array]; - - for (NSUInteger i = 0; i < callstack.count; i++){ - NSString *rawAddress = [self getAddress:[FBSDKTypeUtility array:callstack objectAtIndex:i]]; - NSString *addressString = [NSString stringWithFormat:@"0x%@",[rawAddress substringWithRange:NSMakeRange(rawAddress.length - 10, 10)]]; - NSString *methodAddress = [self searchMethod:addressString sortedAllAddress:sortedAllAddress]; - - if (methodAddress) { - containsFBSDKFunction = YES; - nonSDKMethodCount == 0 ?: [FBSDKTypeUtility array:symbolicatedCallstack addObject:[NSString stringWithFormat:@"(%ld DEV METHODS)", (long)nonSDKMethodCount]]; - nonSDKMethodCount = 0; - NSString *methodName = [FBSDKTypeUtility dictionary:methodMapping objectForKey:methodAddress ofType:NSObject.class]; - - // filter out cxx_destruct - if ([methodName containsString:@".cxx_destruct"]) { - return nil; + if ([callstackEntry isKindOfClass:[NSString class]]) { + NSArray *components = [callstackEntry componentsSeparatedByString:@" "]; + for (NSString *component in components) { + if ([component containsString:@"0x"]) { + return component; } - [FBSDKTypeUtility array:symbolicatedCallstack addObject:[NSString stringWithFormat:@"%@%@", methodName, [self getOffset:addressString secondString:methodAddress]]]; - } else { - nonSDKMethodCount++; - } - } - nonSDKMethodCount == 0 ?: [FBSDKTypeUtility array:symbolicatedCallstack addObject:[NSString stringWithFormat:@"(%ld DEV METHODS)", (long)nonSDKMethodCount]]; - - return containsFBSDKFunction ? symbolicatedCallstack : nil; -} - -+ (NSString *)getAddress:(NSString *)callstackEntry -{ - NSArray *components = [callstackEntry componentsSeparatedByString:@" "]; - for (NSString *component in components) { - if ([component containsString:@"0x"]) { - return component; } } return nil; } -+ (NSString *)getOffset:(NSString *)firstString - secondString:(NSString *)secondString ++ (NSString *)_getOffset:(NSString *)firstString + secondString:(NSString *)secondString { - if (!firstString || !secondString) { - return nil; - } unsigned long long first = 0, second = 0; NSScanner *scanner = [NSScanner scannerWithString:firstString]; [scanner scanHexLongLong:&first]; @@ -195,8 +198,8 @@ + (NSString *)getOffset:(NSString *)firstString return [NSString stringWithFormat:@"+%llu", difference]; } -+ (NSString *)searchMethod:(NSString *)address - sortedAllAddress:(NSArray *)sortedAllAddress ++ (nullable NSString *)_searchMethod:(NSString *)address + sortedAllAddress:(NSArray *)sortedAllAddress { if (0 == sortedAllAddress.count) { return nil; @@ -219,7 +222,7 @@ + (NSString *)searchMethod:(NSString *)address NSUInteger index = [sortedAllAddress indexOfObject:address inSortedRange:NSMakeRange(0, sortedAllAddress.count - 1) options:NSBinarySearchingInsertionIndex - usingComparator:^NSComparisonResult(id _Nonnull obj1, id _Nonnull obj2) { + usingComparator:^NSComparisonResult (id _Nonnull obj1, id _Nonnull obj2) { return [obj1 compare:obj2]; }]; return [FBSDKTypeUtility array:sortedAllAddress objectAtIndex:index - 1]; diff --git a/FBSDKCoreKit/FBSDKCoreKit/Basics/Internal/FBSDKSafeCast.m b/Sources/FBSDKCoreKit_Basics/FBSDKSafeCast.m similarity index 100% rename from FBSDKCoreKit/FBSDKCoreKit/Basics/Internal/FBSDKSafeCast.m rename to Sources/FBSDKCoreKit_Basics/FBSDKSafeCast.m diff --git a/FBSDKCoreKit/FBSDKCoreKit/Basics/Internal/FBSDKTypeUtility.m b/Sources/FBSDKCoreKit_Basics/FBSDKTypeUtility.m similarity index 90% rename from FBSDKCoreKit/FBSDKCoreKit/Basics/Internal/FBSDKTypeUtility.m rename to Sources/FBSDKCoreKit_Basics/FBSDKTypeUtility.m index 80e3b18032..572ab02ae8 100644 --- a/FBSDKCoreKit/FBSDKCoreKit/Basics/Internal/FBSDKTypeUtility.m +++ b/Sources/FBSDKCoreKit_Basics/FBSDKTypeUtility.m @@ -43,7 +43,8 @@ + (void)array:(NSMutableArray *)array addObject:(id)object } } -+ (void)array:(NSMutableArray *)array addObject:(nullable id)object atIndex:(NSUInteger)index { ++ (void)array:(NSMutableArray *)array addObject:(nullable id)object atIndex:(NSUInteger)index +{ if (object && [array isKindOfClass:NSMutableArray.class]) { if (index < array.count) { [array insertObject:object atIndex:index]; @@ -89,7 +90,7 @@ + (void)dictionary:(NSMutableDictionary *)dictionary setObject:(id)object forKey } } -+ (void)dictionary:(NSDictionary *)dictionary enumerateKeysAndObjectsUsingBlock:(void (NS_NOESCAPE ^)(id key, id obj, BOOL *stop))block ++ (void)dictionary:(NSDictionary *)dictionary enumerateKeysAndObjectsUsingBlock:(void(NS_NOESCAPE ^)(id key, id obj, BOOL *stop))block { NSDictionary *validDictionary = [self dictionaryValue:dictionary]; if (validDictionary) { @@ -173,20 +174,19 @@ + (BOOL)isValidJSONObject:(id)obj return [NSJSONSerialization isValidJSONObject:obj]; } -+ (NSData *)dataWithJSONObject:(id)obj options:(NSJSONWritingOptions)opt error:(NSError *__autoreleasing _Nullable *)error ++ (NSData *)dataWithJSONObject:(id)obj options:(NSJSONWritingOptions)opt error:(NSError *__autoreleasing _Nullable *)error { NSData *data; @try { data = [NSJSONSerialization dataWithJSONObject:obj options:opt error:error]; - } - @catch (NSException *exception) { - NSLog(@"FBSDKJSONSerialization - dataWithJSONObject:options:error failed: %@", exception.reason); + } @catch (NSException *exception) { + NSLog(@"FBSDKJSONSerialization - dataWithJSONObject:options:error failed: %@", exception.reason); } return data; } -+ (id)JSONObjectWithData:(NSData *)data options:(NSJSONReadingOptions)opt error:(NSError *__autoreleasing _Nullable *)error ++ (id)JSONObjectWithData:(NSData *)data options:(NSJSONReadingOptions)opt error:(NSError *__autoreleasing _Nullable *)error { if (![data isKindOfClass:NSData.class]) { return nil; @@ -194,10 +194,9 @@ + (id)JSONObjectWithData:(NSData *)data options:(NSJSONReadingOptions)opt error: id object; @try { - object = [NSJSONSerialization JSONObjectWithData:data options:opt error:error]; - } - @catch (NSException *exception) { - NSLog(@"FBSDKJSONSerialization - JSONObjectWithData:options:error failed: %@", exception.reason); + object = [NSJSONSerialization JSONObjectWithData:data options:opt error:error]; + } @catch (NSException *exception) { + NSLog(@"FBSDKJSONSerialization - JSONObjectWithData:options:error failed: %@", exception.reason); } return object; } diff --git a/FBSDKCoreKit/FBSDKCoreKit/Basics/Internal/FBSDKURLSession.m b/Sources/FBSDKCoreKit_Basics/FBSDKURLSession.m similarity index 100% rename from FBSDKCoreKit/FBSDKCoreKit/Basics/Internal/FBSDKURLSession.m rename to Sources/FBSDKCoreKit_Basics/FBSDKURLSession.m diff --git a/FBSDKCoreKit/FBSDKCoreKit/Basics/Internal/FBSDKURLSessionTask.m b/Sources/FBSDKCoreKit_Basics/FBSDKURLSessionTask.m similarity index 100% rename from FBSDKCoreKit/FBSDKCoreKit/Basics/Internal/FBSDKURLSessionTask.m rename to Sources/FBSDKCoreKit_Basics/FBSDKURLSessionTask.m diff --git a/FBSDKCoreKit/FBSDKCoreKit/AppEvents/Internal/FBSDKUserDataStore.m b/Sources/FBSDKCoreKit_Basics/FBSDKUserDataStore.m similarity index 77% rename from FBSDKCoreKit/FBSDKCoreKit/AppEvents/Internal/FBSDKUserDataStore.m rename to Sources/FBSDKCoreKit_Basics/FBSDKUserDataStore.m index e4428309a8..f2384c4540 100644 --- a/FBSDKCoreKit/FBSDKCoreKit/AppEvents/Internal/FBSDKUserDataStore.m +++ b/Sources/FBSDKCoreKit_Basics/FBSDKUserDataStore.m @@ -17,15 +17,13 @@ // CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. #import "FBSDKUserDataStore.h" +#import "FBSDKUserDataStore+Internal.h" -#import "FBSDKAppEventsUtility.h" -#import "FBSDKLogger.h" -#import "FBSDKSettings.h" -#import "FBSDKUtility.h" +#import "FBSDKBasicUtility.h" #import "FBSDKTypeUtility.h" -static NSString *const FBSDKUserDataKey = @"com.facebook.appevents.UserDataStore.userData"; -static NSString *const FBSDKInternalUserDataKey = @"com.facebook.appevents.UserDataStore.internalUserData"; +static NSString *const FBSDKUserDataKey = @"com.facebook.appevents.UserDataStore.userData"; +static NSString *const FBSDKInternalUserDataKey = @"com.facebook.appevents.UserDataStore.internalUserData"; static NSMutableDictionary *hashedUserData; static NSMutableDictionary *internalHashedUserData; @@ -33,6 +31,21 @@ static dispatch_queue_t serialQueue; +// +// Public event user data types +// + +FBSDKAppEventUserDataType FBSDKAppEventEmail = @"em"; +FBSDKAppEventUserDataType FBSDKAppEventFirstName = @"fn"; +FBSDKAppEventUserDataType FBSDKAppEventLastName = @"ln"; +FBSDKAppEventUserDataType FBSDKAppEventPhone = @"ph"; +FBSDKAppEventUserDataType FBSDKAppEventDateOfBirth = @"dob"; +FBSDKAppEventUserDataType FBSDKAppEventGender = @"ge"; +FBSDKAppEventUserDataType FBSDKAppEventCity = @"ct"; +FBSDKAppEventUserDataType FBSDKAppEventState = @"st"; +FBSDKAppEventUserDataType FBSDKAppEventZip = @"zp"; +FBSDKAppEventUserDataType FBSDKAppEventCountry = @"country"; + @implementation FBSDKUserDataStore + (void)initialize @@ -43,16 +56,16 @@ + (void)initialize enabledRules = [[NSMutableSet alloc] init]; } -+ (void)setAndHashUserEmail:(nullable NSString *)email - firstName:(nullable NSString *)firstName - lastName:(nullable NSString *)lastName - phone:(nullable NSString *)phone - dateOfBirth:(nullable NSString *)dateOfBirth - gender:(nullable NSString *)gender - city:(nullable NSString *)city - state:(nullable NSString *)state - zip:(nullable NSString *)zip - country:(nullable NSString *)country ++ (void)setUserEmail:(nullable NSString *)email + firstName:(nullable NSString *)firstName + lastName:(nullable NSString *)lastName + phone:(nullable NSString *)phone + dateOfBirth:(nullable NSString *)dateOfBirth + gender:(nullable NSString *)gender + city:(nullable NSString *)city + state:(nullable NSString *)state + zip:(nullable NSString *)zip + country:(nullable NSString *)country { NSMutableDictionary *ud = [[NSMutableDictionary alloc] init]; if (email) { @@ -93,8 +106,8 @@ + (void)setAndHashUserEmail:(nullable NSString *)email }); } -+ (void)setAndHashData:(nullable NSString *)data - forType:(FBSDKAppEventUserDataType)type ++ (void)setUserData:(nullable NSString *)data + forType:(FBSDKAppEventUserDataType)type { [FBSDKUserDataStore setHashData:[FBSDKUserDataStore encryptData:data type:type] forType:type]; @@ -135,9 +148,14 @@ + (void)setEnabledRules:(NSArray *)rules } } -+ (void)clearDataForType:(FBSDKAppEventUserDataType)type ++ (void)clearUserDataForType:(FBSDKAppEventUserDataType)type +{ + [FBSDKUserDataStore setUserData:nil forType:type]; +} + ++ (NSString *)getUserData { - [FBSDKUserDataStore setAndHashData:nil forType:type]; + return [FBSDKUserDataStore getHashedData]; } + (NSString *)getHashedData @@ -156,6 +174,20 @@ + (NSString *)getHashedData return hashedUserDataString; } ++ (void)clearUserData +{ + [FBSDKUserDataStore setUserEmail:nil + firstName:nil + lastName:nil + phone:nil + dateOfBirth:nil + gender:nil + city:nil + state:nil + zip:nil + country:nil]; +} + + (NSString *)getInternalHashedDataForType:(FBSDKAppEventUserDataType)type { __block NSString *hashedData; @@ -173,8 +205,8 @@ + (NSString *)getInternalHashedDataForType:(FBSDKAppEventUserDataType)type NSMutableDictionary *hashedUD = nil; if (userData) { hashedUD = (NSMutableDictionary *)[FBSDKTypeUtility JSONObjectWithData:[userData dataUsingEncoding:NSUTF8StringEncoding] - options:NSJSONReadingMutableContainers - error:nil]; + options: NSJSONReadingMutableContainers + error: nil]; } if (!hashedUD) { hashedUD = [[NSMutableDictionary alloc] init]; @@ -186,13 +218,12 @@ + (NSString *)stringByHashedData:(id)hashedData { NSError *error; NSData *jsonData = [FBSDKTypeUtility dataWithJSONObject:hashedData - options:0 - error:&error]; + options:0 + error:&error]; if (jsonData) { return [[NSString alloc] initWithData:jsonData encoding:NSUTF8StringEncoding]; } else { - [FBSDKAppEventsUtility logAndNotify:[NSString stringWithFormat:@"Invalid json object: %@", error]]; return @""; } } @@ -203,7 +234,7 @@ + (NSString *)encryptData:(NSString *)data if (data.length == 0 || [FBSDKUserDataStore maybeSHA256Hashed:data]) { return data; } - return [FBSDKUtility SHA256Hash:[FBSDKUserDataStore normalizeData:data type:type]]; + return [FBSDKBasicUtility SHA256Hash:[FBSDKUserDataStore normalizeData:data type:type]]; } + (NSString *)normalizeData:(NSString *)data @@ -220,16 +251,16 @@ + (NSString *)normalizeData:(NSString *)data NSRegularExpression *regex = [NSRegularExpression regularExpressionWithPattern:@"[^0-9]" options:NSRegularExpressionCaseInsensitive error:&error - ]; + ]; normalizedData = [regex stringByReplacingMatchesInString:data options:0 range:NSMakeRange(0, data.length) withTemplate:@"" - ]; + ]; } else if ([type isEqualToString:FBSDKAppEventGender]) { NSString *temp = [data stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceCharacterSet]]; temp = temp.lowercaseString; - normalizedData = temp.length > 0 ? [temp substringToIndex:1]: @""; + normalizedData = temp.length > 0 ? [temp substringToIndex:1] : @""; } return normalizedData; } diff --git a/FBSDKCoreKit/FBSDKCoreKit/Basics/Internal/FBSDKBasicUtility.h b/Sources/FBSDKCoreKit_Basics/include/FBSDKBasicUtility.h similarity index 90% rename from FBSDKCoreKit/FBSDKCoreKit/Basics/Internal/FBSDKBasicUtility.h rename to Sources/FBSDKCoreKit_Basics/include/FBSDKBasicUtility.h index cebaffb0d9..37629ca23f 100644 --- a/FBSDKCoreKit/FBSDKCoreKit/Basics/Internal/FBSDKBasicUtility.h +++ b/Sources/FBSDKCoreKit_Basics/include/FBSDKBasicUtility.h @@ -20,6 +20,18 @@ NS_ASSUME_NONNULL_BEGIN +/** + Dispatches the specified block on the main thread. + @param block the block to dispatch + */ +extern void fb_dispatch_on_main_thread(dispatch_block_t block); + +/** + Dispatches the specified block on the default thread. + @param block the block to dispatch + */ +extern void fb_dispatch_on_default_thread(dispatch_block_t block); + /** Describes the callback for appLinkFromURLInBackground. @param object the FBSDKAppLink representing the deferred App Link @@ -71,7 +83,7 @@ setJSONStringForObject:(id)object @param invalidObjectHandler Handles objects that are invalid, returning a replacement value or nil to ignore. @return Query string representation of the parameters. */ -+ (NSString *)queryStringWithDictionary:(NSDictionary *)dictionary ++ (nullable NSString *)queryStringWithDictionary:(NSDictionary *)dictionary error:(NSError *__autoreleasing *)errorRef invalidObjectHandler:(nullable FBSDKInvalidObjectHandler)invalidObjectHandler; @@ -112,6 +124,7 @@ setJSONStringForObject:(id)object + (NSString *)anonymousID; + (NSString *)persistenceFilePath:(NSString *)filename; ++ (nullable NSString *)SHA256Hash:(nullable NSObject *)input; @end diff --git a/Sources/FBSDKCoreKit_Basics/include/FBSDKCoreKit_Basics.h b/Sources/FBSDKCoreKit_Basics/include/FBSDKCoreKit_Basics.h new file mode 100644 index 0000000000..e645f91073 --- /dev/null +++ b/Sources/FBSDKCoreKit_Basics/include/FBSDKCoreKit_Basics.h @@ -0,0 +1,41 @@ +// 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. + +#ifdef BUCK + #import + #import + #import + #import + #import + #import + #import + #import + #import + #import +#else + #import "FBSDKBasicUtility.h" + #import "FBSDKCrashHandler.h" + #import "FBSDKCrashObserving.h" + #import "FBSDKJSONValue.h" + #import "FBSDKLibAnalyzer.h" + #import "FBSDKSafeCast.h" + #import "FBSDKTypeUtility.h" + #import "FBSDKURLSession.h" + #import "FBSDKURLSessionTask.h" + #import "FBSDKUserDataStore.h" +#endif diff --git a/FBSDKCoreKit/FBSDKCoreKit/Basics/Instrument/FBSDKCrashHandler.h b/Sources/FBSDKCoreKit_Basics/include/FBSDKCrashHandler.h similarity index 100% rename from FBSDKCoreKit/FBSDKCoreKit/Basics/Instrument/FBSDKCrashHandler.h rename to Sources/FBSDKCoreKit_Basics/include/FBSDKCrashHandler.h diff --git a/FBSDKCoreKit/FBSDKCoreKit/Basics/Instrument/FBSDKCrashObserving.h b/Sources/FBSDKCoreKit_Basics/include/FBSDKCrashObserving.h similarity index 95% rename from FBSDKCoreKit/FBSDKCoreKit/Basics/Instrument/FBSDKCrashObserving.h rename to Sources/FBSDKCoreKit_Basics/include/FBSDKCrashObserving.h index 070416cabf..a210191a37 100644 --- a/FBSDKCoreKit/FBSDKCoreKit/Basics/Instrument/FBSDKCrashObserving.h +++ b/Sources/FBSDKCoreKit_Basics/include/FBSDKCrashObserving.h @@ -23,7 +23,7 @@ NS_ASSUME_NONNULL_BEGIN @protocol FBSDKCrashObserving @property (nonatomic, copy) NSArray *prefixes; -@property (nonatomic, copy, nullable) NSArray *frameworks; +@property (nullable, nonatomic, copy) NSArray *frameworks; @optional diff --git a/Sources/FBSDKCoreKit_Basics/include/FBSDKJSONValue.h b/Sources/FBSDKCoreKit_Basics/include/FBSDKJSONValue.h new file mode 100644 index 0000000000..5d953da45d --- /dev/null +++ b/Sources/FBSDKCoreKit_Basics/include/FBSDKJSONValue.h @@ -0,0 +1,112 @@ +// 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 + +NS_ASSUME_NONNULL_BEGIN +/** + The purpose of this class is to serve as thin, type-safe wrapper + around FBSDKTypeUtility + */ +@interface FBSDKJSONField : NSObject + +/** + This can only be created by FBSDKJSONValue. + */ +- (instancetype)init NS_UNAVAILABLE; ++ (instancetype)new NS_UNAVAILABLE; + +/** +A safe method to unpack the values in the top-level JSON object. + https://developer.apple.com/documentation/foundation/nsjsonserialization +*/ +- (void)matchArray:(void (^_Nullable)(NSArray *_Nonnull))arrayMatcher + dictionary:(void (^_Nullable)(NSDictionary *_Nonnull))dictionaryMatcher + string:(void (^_Nullable)(NSString *_Nonnull))stringMatcher + number:(void (^_Nullable)(NSNumber *_Nonnull))numberMatcher + null:(void (^_Nullable)(void))nullMatcher; + +/** + The underlying JSON object. The only guarantee we provide with this + is that it passes [FBSDKTypeUtility isValidJSONObject:] + */ +@property (nonnull, nonatomic, readonly, strong) id rawObject; + +- (NSArray *_Nullable)arrayOrNil; +- (NSDictionary *_Nullable)dictionaryOrNil; +- (NSString *_Nullable)stringOrNil; +- (NSNumber *_Nullable)numberOrNil; +- (NSNull *_Nullable)nullOrNil; + +@end + +/** + Represents Top-level JSON objects. + */ +@interface FBSDKJSONValue : NSObject + +/** + If the object does not pass [FBSDKTypeUtility isValidJSONObject:] + this will return nil. + */ +- (_Nullable instancetype)initWithPotentialJSONObject:(id)obj; + +- (instancetype)init NS_UNAVAILABLE; ++ (instancetype)new NS_UNAVAILABLE; + +/** + The underlying JSON object. The only guarantee we provide with this + is that it passes [FBSDKTypeUtility isValidJSONObject:] + */ +@property (nonatomic, readonly, strong) id rawObject; + +/** + A safe method to unpack the values in the top-level JSON object. + + The specs are per Apple's documentation: https://developer.apple.com/documentation/foundation/nsjsonserialization + */ +- (void)matchArray:(void (^_Nullable)(NSArray *))arrayMatcher + dictionary:(void (^_Nullable)(NSDictionary *))dictMatcher; + +/** + Returns the dictionary if that's truly what it is, otherwise, nil. + */ +- (NSDictionary *_Nullable)matchDictionaryOrNil; + +/** + The unsafe variant which drops all the type-safety for this class. + If this object is nonnull, you at least have guarantees from Apple that this is NSNull, NSString, NSNumber, NSArray, or NSDictionary. + */ +- (NSDictionary *_Nullable)unsafe_matchDictionaryOrNil; + +- (NSArray *_Nullable)matchArrayOrNil; +- (NSArray *_Nullable)unsafe_matchArrayOrNil; + +@end + +/** +FBSDKTypeUtility returns id, which is problematic in our codebase. + +You can wrap resulting objects in this to force users of your JSON to use +type-safe bindings. + +If this is not a valid JSON object...this will return nil. +*/ +FBSDKJSONValue *_Nullable FBSDKCreateJSONFromString(NSString *_Nullable string, NSError *__autoreleasing *errorRef); + +NS_ASSUME_NONNULL_END diff --git a/FBSDKCoreKit/FBSDKCoreKit/Basics/Instrument/FBSDKLibAnalyzer.h b/Sources/FBSDKCoreKit_Basics/include/FBSDKLibAnalyzer.h similarity index 95% rename from FBSDKCoreKit/FBSDKCoreKit/Basics/Instrument/FBSDKLibAnalyzer.h rename to Sources/FBSDKCoreKit_Basics/include/FBSDKLibAnalyzer.h index e6873adef1..5303875be2 100644 --- a/FBSDKCoreKit/FBSDKCoreKit/Basics/Instrument/FBSDKLibAnalyzer.h +++ b/Sources/FBSDKCoreKit_Basics/include/FBSDKLibAnalyzer.h @@ -23,9 +23,9 @@ NS_ASSUME_NONNULL_BEGIN @interface FBSDKLibAnalyzer : NSObject + (NSDictionary *)getMethodsTable:(NSArray *)prefixes - frameworks:(NSArray * _Nullable)frameworks; + frameworks:(NSArray *_Nullable)frameworks; + (nullable NSArray *)symbolicateCallstack:(NSArray *)callstack - methodMapping:(NSDictionary *)methodMapping; + methodMapping:(NSDictionary *)methodMapping; @end diff --git a/FBSDKCoreKit/FBSDKCoreKit/Basics/Internal/FBSDKSafeCast.h b/Sources/FBSDKCoreKit_Basics/include/FBSDKSafeCast.h similarity index 100% rename from FBSDKCoreKit/FBSDKCoreKit/Basics/Internal/FBSDKSafeCast.h rename to Sources/FBSDKCoreKit_Basics/include/FBSDKSafeCast.h diff --git a/FBSDKCoreKit/FBSDKCoreKit/Basics/Internal/FBSDKTypeUtility.h b/Sources/FBSDKCoreKit_Basics/include/FBSDKTypeUtility.h similarity index 100% rename from FBSDKCoreKit/FBSDKCoreKit/Basics/Internal/FBSDKTypeUtility.h rename to Sources/FBSDKCoreKit_Basics/include/FBSDKTypeUtility.h diff --git a/FBSDKCoreKit/FBSDKCoreKit/Basics/Internal/FBSDKURLSession.h b/Sources/FBSDKCoreKit_Basics/include/FBSDKURLSession.h similarity index 89% rename from FBSDKCoreKit/FBSDKCoreKit/Basics/Internal/FBSDKURLSession.h rename to Sources/FBSDKCoreKit_Basics/include/FBSDKURLSession.h index 34707dc4e1..e19287aadc 100644 --- a/FBSDKCoreKit/FBSDKCoreKit/Basics/Internal/FBSDKURLSession.h +++ b/Sources/FBSDKCoreKit_Basics/include/FBSDKURLSession.h @@ -24,9 +24,9 @@ NS_ASSUME_NONNULL_BEGIN @interface FBSDKURLSession : NSObject -@property (atomic, strong, nullable) NSURLSession *session; -@property (nonatomic, weak, nullable) id delegate; -@property (nonatomic, retain, nullable) NSOperationQueue *delegateQueue; +@property (nullable, atomic, strong) NSURLSession *session; +@property (nullable, nonatomic, weak) id delegate; +@property (nullable, nonatomic, retain) NSOperationQueue *delegateQueue; - (instancetype)initWithDelegate:(id)delegate delegateQueue:(NSOperationQueue *)delegateQueue; diff --git a/FBSDKCoreKit/FBSDKCoreKit/Basics/Internal/FBSDKURLSessionTask.h b/Sources/FBSDKCoreKit_Basics/include/FBSDKURLSessionTask.h similarity index 100% rename from FBSDKCoreKit/FBSDKCoreKit/Basics/Internal/FBSDKURLSessionTask.h rename to Sources/FBSDKCoreKit_Basics/include/FBSDKURLSessionTask.h diff --git a/Sources/FBSDKCoreKit_Basics/include/FBSDKUserDataStore+Internal.h b/Sources/FBSDKCoreKit_Basics/include/FBSDKUserDataStore+Internal.h new file mode 100644 index 0000000000..2804ed010a --- /dev/null +++ b/Sources/FBSDKCoreKit_Basics/include/FBSDKUserDataStore+Internal.h @@ -0,0 +1,35 @@ +// 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 "FBSDKUserDataStore.h" + +NS_ASSUME_NONNULL_BEGIN + +@interface FBSDKUserDataStore (Internal) + ++ (void)setInternalHashData:(nullable NSString *)hashData + forType:(FBSDKAppEventUserDataType)type; ++ (void)setEnabledRules:(NSArray *)rules; + ++ (nullable NSString *)getInternalHashedDataForType:(FBSDKAppEventUserDataType)type; + +@end + +NS_ASSUME_NONNULL_END diff --git a/Sources/FBSDKCoreKit_Basics/include/FBSDKUserDataStore.h b/Sources/FBSDKCoreKit_Basics/include/FBSDKUserDataStore.h new file mode 100644 index 0000000000..0539599351 --- /dev/null +++ b/Sources/FBSDKCoreKit_Basics/include/FBSDKUserDataStore.h @@ -0,0 +1,118 @@ +// 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 + +NS_ASSUME_NONNULL_BEGIN + +/// typedef for FBSDKAppEventUserDataType +typedef NSString *const FBSDKAppEventUserDataType NS_TYPED_EXTENSIBLE_ENUM NS_SWIFT_NAME(AppEvents.UserDataType); + +/** Parameter key used to specify user's email. */ +FOUNDATION_EXPORT FBSDKAppEventUserDataType FBSDKAppEventEmail; +FOUNDATION_EXPORT FBSDKAppEventUserDataType FBSDKAppEventEmail; + +/** Parameter key used to specify user's first name. */ +FOUNDATION_EXPORT FBSDKAppEventUserDataType FBSDKAppEventFirstName; + +/** Parameter key used to specify user's last name. */ +FOUNDATION_EXPORT FBSDKAppEventUserDataType FBSDKAppEventLastName; + +/** Parameter key used to specify user's phone. */ +FOUNDATION_EXPORT FBSDKAppEventUserDataType FBSDKAppEventPhone; + +/** Parameter key used to specify user's date of birth. */ +FOUNDATION_EXPORT FBSDKAppEventUserDataType FBSDKAppEventDateOfBirth; + +/** Parameter key used to specify user's gender. */ +FOUNDATION_EXPORT FBSDKAppEventUserDataType FBSDKAppEventGender; + +/** Parameter key used to specify user's city. */ +FOUNDATION_EXPORT FBSDKAppEventUserDataType FBSDKAppEventCity; + +/** Parameter key used to specify user's state. */ +FOUNDATION_EXPORT FBSDKAppEventUserDataType FBSDKAppEventState; + +/** Parameter key used to specify user's zip. */ +FOUNDATION_EXPORT FBSDKAppEventUserDataType FBSDKAppEventZip; + +/** Parameter key used to specify user's country. */ +FOUNDATION_EXPORT FBSDKAppEventUserDataType FBSDKAppEventCountry; + +NS_SWIFT_NAME(UserDataStore) +@interface FBSDKUserDataStore : NSObject + +/* + Sets custom user data to associate with all app events. All user data are hashed + and used to match Facebook user from this instance of an application. + + The user data will be persisted between application instances. + + @param email user's email + @param firstName user's first name + @param lastName user's last name + @param phone user's phone + @param dateOfBirth user's date of birth + @param gender user's gender + @param city user's city + @param state user's state + @param zip user's zip + @param country user's country + */ ++ (void)setUserEmail:(nullable NSString *)email + firstName:(nullable NSString *)firstName + lastName:(nullable NSString *)lastName + phone:(nullable NSString *)phone + dateOfBirth:(nullable NSString *)dateOfBirth + gender:(nullable NSString *)gender + city:(nullable NSString *)city + state:(nullable NSString *)state + zip:(nullable NSString *)zip + country:(nullable NSString *)country +NS_SWIFT_NAME(setUser(email:firstName:lastName:phone:dateOfBirth:gender:city:state:zip:country:)); + +/* + Returns the set user data else nil +*/ ++ (nullable NSString *)getUserData; + +/* + Clears the current user data +*/ ++ (void)clearUserData; + +/* + Sets custom user data to associate with all app events. All user data are hashed + and used to match Facebook user from this instance of an application. + + The user data will be persisted between application instances. + + @param data data + @param type data type, e.g. FBSDKAppEventEmail, FBSDKAppEventPhone + */ ++ (void)setUserData:(nullable NSString *)data + forType:(FBSDKAppEventUserDataType)type; + +/* + Clears the current user data of certain type + */ ++ (void)clearUserDataForType:(FBSDKAppEventUserDataType)type; + +@end + +NS_ASSUME_NONNULL_END diff --git a/samples/Configurations/Project.xcconfig b/samples/Configurations/Project.xcconfig index 2174945e7e..aae9796b88 100644 --- a/samples/Configurations/Project.xcconfig +++ b/samples/Configurations/Project.xcconfig @@ -18,7 +18,7 @@ // Architectures ARCHS = i386 armv7 x86_64 arm64 -IPHONEOS_DEPLOYMENT_TARGET = 8.0 +IPHONEOS_DEPLOYMENT_TARGET = 9.0 SDKROOT = iphoneos // Build Options diff --git a/samples/FacebookLoginSample/FacebookLoginSample.xcodeproj/project.pbxproj b/samples/FacebookLoginSample/FacebookLoginSample.xcodeproj/project.pbxproj index 35134084b5..478506522f 100644 --- a/samples/FacebookLoginSample/FacebookLoginSample.xcodeproj/project.pbxproj +++ b/samples/FacebookLoginSample/FacebookLoginSample.xcodeproj/project.pbxproj @@ -13,7 +13,7 @@ F431CBE023DB5AA700A243D4 /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = F431CBDE23DB5AA700A243D4 /* Main.storyboard */; }; F431CBE223DB5AAA00A243D4 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = F431CBE123DB5AAA00A243D4 /* Assets.xcassets */; }; F431CBE523DB5AAA00A243D4 /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = F431CBE323DB5AAA00A243D4 /* LaunchScreen.storyboard */; }; - F431CBFC23DB5CD200A243D4 /* FacebookLogin in Frameworks */ = {isa = PBXBuildFile; productRef = F431CBFB23DB5CD200A243D4 /* FacebookLogin */; }; + F45CD1D524C7552D00C84BF7 /* FacebookLogin in Frameworks */ = {isa = PBXBuildFile; productRef = F45CD1D424C7552D00C84BF7 /* FacebookLogin */; }; F4D3AE8623DB60F7001BAF89 /* LoginViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = F4D3AE8523DB60F7001BAF89 /* LoginViewController.swift */; }; F4F742FD23DB7A3500A823A9 /* LoginManagerViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = F4F742FC23DB7A3500A823A9 /* LoginManagerViewController.swift */; }; F4F742FF23DB832F00A823A9 /* LoginDetailsViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = F4F742FE23DB832F00A823A9 /* LoginDetailsViewController.swift */; }; @@ -28,6 +28,7 @@ F431CBE123DB5AAA00A243D4 /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; }; F431CBE423DB5AAA00A243D4 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = ""; }; F431CBE623DB5AAA00A243D4 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; + F45CD1D224C7552300C84BF7 /* facebook-ios-sdk */ = {isa = PBXFileReference; lastKnownFileType = folder; name = "facebook-ios-sdk"; path = ../..; sourceTree = ""; }; F4D3AE8523DB60F7001BAF89 /* LoginViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LoginViewController.swift; sourceTree = ""; }; F4F742FC23DB7A3500A823A9 /* LoginManagerViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LoginManagerViewController.swift; sourceTree = ""; }; F4F742FE23DB832F00A823A9 /* LoginDetailsViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LoginDetailsViewController.swift; sourceTree = ""; }; @@ -38,7 +39,7 @@ isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; files = ( - F431CBFC23DB5CD200A243D4 /* FacebookLogin in Frameworks */, + F45CD1D524C7552D00C84BF7 /* FacebookLogin in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -48,8 +49,10 @@ F431CBCC23DB5AA700A243D4 = { isa = PBXGroup; children = ( + F45CD1D224C7552300C84BF7 /* facebook-ios-sdk */, F431CBD723DB5AA700A243D4 /* FacebookLoginSample */, F431CBD623DB5AA700A243D4 /* Products */, + F45CD1D324C7552D00C84BF7 /* Frameworks */, ); sourceTree = ""; }; @@ -78,6 +81,13 @@ path = FacebookLoginSample; sourceTree = ""; }; + F45CD1D324C7552D00C84BF7 /* Frameworks */ = { + isa = PBXGroup; + children = ( + ); + name = Frameworks; + sourceTree = ""; + }; /* End PBXGroup section */ /* Begin PBXNativeTarget section */ @@ -95,7 +105,7 @@ ); name = FacebookLoginSample; packageProductDependencies = ( - F431CBFB23DB5CD200A243D4 /* FacebookLogin */, + F45CD1D424C7552D00C84BF7 /* FacebookLogin */, ); productName = FacebookLoginSample; productReference = F431CBD523DB5AA700A243D4 /* FacebookLoginSample.app */; @@ -126,7 +136,6 @@ ); mainGroup = F431CBCC23DB5AA700A243D4; packageReferences = ( - F431CBFA23DB5CD200A243D4 /* XCRemoteSwiftPackageReference "facebook-ios-sdk" */, ); productRefGroup = F431CBD623DB5AA700A243D4 /* Products */; projectDirPath = ""; @@ -357,21 +366,9 @@ }; /* End XCConfigurationList section */ -/* Begin XCRemoteSwiftPackageReference section */ - F431CBFA23DB5CD200A243D4 /* XCRemoteSwiftPackageReference "facebook-ios-sdk" */ = { - isa = XCRemoteSwiftPackageReference; - repositoryURL = "https://github.com/facebook/facebook-ios-sdk"; - requirement = { - kind = upToNextMajorVersion; - minimumVersion = 6.0.0; - }; - }; -/* End XCRemoteSwiftPackageReference section */ - /* Begin XCSwiftPackageProductDependency section */ - F431CBFB23DB5CD200A243D4 /* FacebookLogin */ = { + F45CD1D424C7552D00C84BF7 /* FacebookLogin */ = { isa = XCSwiftPackageProductDependency; - package = F431CBFA23DB5CD200A243D4 /* XCRemoteSwiftPackageReference "facebook-ios-sdk" */; productName = FacebookLogin; }; /* End XCSwiftPackageProductDependency section */ diff --git a/samples/FacebookShareSample/FacebookShareSample.xcodeproj/project.pbxproj b/samples/FacebookShareSample/FacebookShareSample.xcodeproj/project.pbxproj index a7296d922c..a36f2e3971 100644 --- a/samples/FacebookShareSample/FacebookShareSample.xcodeproj/project.pbxproj +++ b/samples/FacebookShareSample/FacebookShareSample.xcodeproj/project.pbxproj @@ -7,13 +7,13 @@ objects = { /* Begin PBXBuildFile section */ + F45CD1D124C754C100C84BF7 /* FacebookShare in Frameworks */ = {isa = PBXBuildFile; productRef = F45CD1D024C754C100C84BF7 /* FacebookShare */; }; F46A5FA523E1ED3200948850 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = F46A5FA423E1ED3200948850 /* AppDelegate.swift */; }; F46A5FA723E1ED3200948850 /* SceneDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = F46A5FA623E1ED3200948850 /* SceneDelegate.swift */; }; F46A5FA923E1ED3200948850 /* ShareViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = F46A5FA823E1ED3200948850 /* ShareViewController.swift */; }; F46A5FAC23E1ED3200948850 /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = F46A5FAA23E1ED3200948850 /* Main.storyboard */; }; F46A5FAE23E1ED3300948850 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = F46A5FAD23E1ED3300948850 /* Assets.xcassets */; }; F46A5FB123E1ED3300948850 /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = F46A5FAF23E1ED3300948850 /* LaunchScreen.storyboard */; }; - F46A5FC823E1F3B800948850 /* FacebookShare in Frameworks */ = {isa = PBXBuildFile; productRef = F46A5FC723E1F3B800948850 /* FacebookShare */; }; F46A5FCA23E20C5300948850 /* ShareViewControllerExtensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = F46A5FC923E20C5300948850 /* ShareViewControllerExtensions.swift */; }; /* End PBXBuildFile section */ @@ -28,6 +28,7 @@ /* End PBXContainerItemProxy section */ /* Begin PBXFileReference section */ + F45CD1CE24C754AB00C84BF7 /* facebook-ios-sdk */ = {isa = PBXFileReference; lastKnownFileType = folder; name = "facebook-ios-sdk"; path = ../..; sourceTree = ""; }; F46A5FA123E1ED3200948850 /* FacebookShareSample.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = FacebookShareSample.app; sourceTree = BUILT_PRODUCTS_DIR; }; F46A5FA423E1ED3200948850 /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = ""; }; F46A5FA623E1ED3200948850 /* SceneDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SceneDelegate.swift; sourceTree = ""; }; @@ -45,7 +46,7 @@ isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; files = ( - F46A5FC823E1F3B800948850 /* FacebookShare in Frameworks */, + F45CD1D124C754C100C84BF7 /* FacebookShare in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -59,11 +60,20 @@ /* End PBXFrameworksBuildPhase section */ /* Begin PBXGroup section */ + F45CD1CF24C754C100C84BF7 /* Frameworks */ = { + isa = PBXGroup; + children = ( + ); + name = Frameworks; + sourceTree = ""; + }; F46A5F9823E1ED3200948850 = { isa = PBXGroup; children = ( + F45CD1CE24C754AB00C84BF7 /* facebook-ios-sdk */, F46A5FA323E1ED3200948850 /* FacebookShareSample */, F46A5FA223E1ED3200948850 /* Products */, + F45CD1CF24C754C100C84BF7 /* Frameworks */, ); sourceTree = ""; }; @@ -108,7 +118,7 @@ ); name = FacebookShareSample; packageProductDependencies = ( - F46A5FC723E1F3B800948850 /* FacebookShare */, + F45CD1D024C754C100C84BF7 /* FacebookShare */, ); productName = FacebookShareSample; productReference = F46A5FA123E1ED3200948850 /* FacebookShareSample.app */; @@ -161,7 +171,6 @@ ); mainGroup = F46A5F9823E1ED3200948850; packageReferences = ( - F46A5FC623E1F3B800948850 /* XCRemoteSwiftPackageReference "facebook-ios-sdk" */, ); productRefGroup = F46A5FA223E1ED3200948850 /* Products */; projectDirPath = ""; @@ -466,21 +475,9 @@ }; /* End XCConfigurationList section */ -/* Begin XCRemoteSwiftPackageReference section */ - F46A5FC623E1F3B800948850 /* XCRemoteSwiftPackageReference "facebook-ios-sdk" */ = { - isa = XCRemoteSwiftPackageReference; - repositoryURL = "https://github.com/facebook/facebook-ios-sdk"; - requirement = { - kind = upToNextMajorVersion; - minimumVersion = 6.0.0; - }; - }; -/* End XCRemoteSwiftPackageReference section */ - /* Begin XCSwiftPackageProductDependency section */ - F46A5FC723E1F3B800948850 /* FacebookShare */ = { + F45CD1D024C754C100C84BF7 /* FacebookShare */ = { isa = XCSwiftPackageProductDependency; - package = F46A5FC623E1F3B800948850 /* XCRemoteSwiftPackageReference "facebook-ios-sdk" */; productName = FacebookShare; }; /* End XCSwiftPackageProductDependency section */ diff --git a/samples/RPSSample/RPSSample/RPSAppDelegate.h b/samples/RPSSample/RPSSample/RPSAppDelegate.h index 9dd9c9a586..d2ed8cebf2 100644 --- a/samples/RPSSample/RPSSample/RPSAppDelegate.h +++ b/samples/RPSSample/RPSSample/RPSAppDelegate.h @@ -28,8 +28,8 @@ @interface RPSAppDelegate : UIResponder -@property (strong, nonatomic) UIWindow *window; -//@property (strong, nonatomic) UITabBarController *tabBarController; -@property (strong, nonatomic) UINavigationController *navigationController; +@property (nonatomic, strong) UIWindow *window; +// @property (strong, nonatomic) UITabBarController *tabBarController; +@property (nonatomic, strong) UINavigationController *navigationController; @end diff --git a/samples/RPSSample/RPSSample/RPSAppDelegate.m b/samples/RPSSample/RPSSample/RPSAppDelegate.m index 88a193a93f..8499639168 100644 --- a/samples/RPSSample/RPSSample/RPSAppDelegate.m +++ b/samples/RPSSample/RPSSample/RPSAppDelegate.m @@ -32,41 +32,42 @@ @implementation RPSAppDelegate #pragma mark - Class methods -+ (RPSCall)callFromAppLinkURL:(NSURL *)url sourceApplication:(NSString *)sourceApplication { - FBSDKURL *appLinkURL = [FBSDKURL URLWithInboundURL:url sourceApplication:sourceApplication]; - NSURL *appLinkTargetURL = [appLinkURL targetURL]; - if (!appLinkTargetURL) { - return RPSCallNone; - } - NSString *queryString = [appLinkTargetURL query]; - for(NSString *component in [queryString componentsSeparatedByString:@"&"]) { - NSArray *pair = [component componentsSeparatedByString:@"="]; - NSString *param = pair[0]; - NSString *val = pair[1]; - if ([param isEqualToString:@"gesture"]) { - if ([val isEqualToString:@"rock"]) { - return RPSCallRock; - } else if ([val isEqualToString:@"paper"]) { - return RPSCallPaper; - } else if ([val isEqualToString:@"scissors"]) { - return RPSCallScissors; - } - } ++ (RPSCall)callFromAppLinkURL:(NSURL *)url sourceApplication:(NSString *)sourceApplication +{ + FBSDKURL *appLinkURL = [FBSDKURL URLWithInboundURL:url sourceApplication:sourceApplication]; + NSURL *appLinkTargetURL = [appLinkURL targetURL]; + if (!appLinkTargetURL) { + return RPSCallNone; + } + NSString *queryString = [appLinkTargetURL query]; + for (NSString *component in [queryString componentsSeparatedByString:@"&"]) { + NSArray *pair = [component componentsSeparatedByString:@"="]; + NSString *param = pair[0]; + NSString *val = pair[1]; + if ([param isEqualToString:@"gesture"]) { + if ([val isEqualToString:@"rock"]) { + return RPSCallRock; + } else if ([val isEqualToString:@"paper"]) { + return RPSCallPaper; + } else if ([val isEqualToString:@"scissors"]) { + return RPSCallScissors; + } } + } - return RPSCallNone; + return RPSCallNone; } #pragma mark - UIApplicationDelegate - (BOOL)application:(UIApplication *)application openURL:(NSURL *)url - options:(nonnull NSDictionary *)options + options:(nonnull NSDictionary *)options { - return [self application:application - openURL:url - sourceApplication:options[UIApplicationOpenURLOptionsSourceApplicationKey] - annotation:options[UIApplicationOpenURLOptionsAnnotationKey]]; + return [self application:application + openURL:url + sourceApplication:options[UIApplicationOpenURLOptionsSourceApplicationKey] + annotation:options[UIApplicationOpenURLOptionsAnnotationKey]]; } // Still need this for iOS8 @@ -75,40 +76,41 @@ - (BOOL)application:(UIApplication *)application sourceApplication:(nullable NSString *)sourceApplication annotation:(nonnull id)annotation { - FBSDKURL *appLink = [FBSDKURL URLWithInboundURL:url sourceApplication:sourceApplication]; - if (appLink.isAutoAppLink) { - [[[UIAlertView alloc] initWithTitle:@"Received Auto App Link:" - message:[NSString stringWithFormat:@"product id: %@", appLink.appLinkData[@"product_id"]] - delegate:nil - cancelButtonTitle:@"OK" - otherButtonTitles:nil] show]; - } - - BOOL result = [[FBSDKApplicationDelegate sharedInstance] application:application - openURL:url - sourceApplication:sourceApplication - annotation:annotation]; - return result; + FBSDKURL *appLink = [FBSDKURL URLWithInboundURL:url sourceApplication:sourceApplication]; + if (appLink.isAutoAppLink) { + [[[UIAlertView alloc] initWithTitle:@"Received Auto App Link:" + message:[NSString stringWithFormat:@"product id: %@", appLink.appLinkData[@"product_id"]] + delegate:nil + cancelButtonTitle:@"OK" + otherButtonTitles:nil] show]; + } + + BOOL result = [[FBSDKApplicationDelegate sharedInstance] application:application + openURL:url + sourceApplication:sourceApplication + annotation:annotation]; + return result; } -- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions { - self.window = [[UIWindow alloc] initWithFrame:[[UIScreen mainScreen] bounds]]; - // Override point for customization after application launch. +- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions +{ + self.window = [[UIWindow alloc] initWithFrame:[[UIScreen mainScreen] bounds]]; + // Override point for customization after application launch. - RPSRootViewController *rootViewController = [[RPSRootViewController alloc] init]; - self.window.rootViewController = rootViewController; - [self.window makeKeyAndVisible]; + RPSRootViewController *rootViewController = [[RPSRootViewController alloc] init]; + self.window.rootViewController = rootViewController; + [self.window makeKeyAndVisible]; - [FBSDKAppLinkUtility fetchDeferredAppLink:^(NSURL *url, NSError *error) { - if (error) { - NSLog(@"Received error while fetching deferred app link %@", error); - } - if (url) { - [[UIApplication sharedApplication] openURL:url]; - } - }]; + [FBSDKAppLinkUtility fetchDeferredAppLink:^(NSURL *url, NSError *error) { + if (error) { + NSLog(@"Received error while fetching deferred app link %@", error); + } + if (url) { + [[UIApplication sharedApplication] openURL:url]; + } + }]; - return YES; + return YES; } @end diff --git a/samples/RPSSample/RPSSample/RPSAppLinkedViewController.m b/samples/RPSSample/RPSSample/RPSAppLinkedViewController.m index 0afd2409b4..44060efafc 100644 --- a/samples/RPSSample/RPSSample/RPSAppLinkedViewController.m +++ b/samples/RPSSample/RPSSample/RPSAppLinkedViewController.m @@ -21,60 +21,63 @@ #import "RPSCommonObjects.h" @interface RPSAppLinkedViewController () -@property (nonatomic, assign) RPSCall call; +@property (nonatomic, assign) RPSCall call; @property (nonatomic, weak) IBOutlet UIImageView *callImageView; -@property (nonatomic, weak) IBOutlet UIButton *playButton; +@property (nonatomic, weak) IBOutlet UIButton *playButton; @end @implementation RPSAppLinkedViewController #pragma mark - Lifecycle -- (instancetype)initWithCall:(RPSCall)call { - NSParameterAssert(call != RPSCallNone); +- (instancetype)initWithCall:(RPSCall)call +{ + NSParameterAssert(call != RPSCallNone); - self = [super init]; + self = [super init]; - if (self) { - self.call = call; - self.modalTransitionStyle = UIModalTransitionStyleCrossDissolve; - } + if (self) { + self.call = call; + self.modalTransitionStyle = UIModalTransitionStyleCrossDissolve; + } - return self; + return self; } #pragma mark - Methods -- (IBAction)play:(id)sender { - [self dismissViewControllerAnimated:YES completion:nil]; +- (IBAction)play:(id)sender +{ + [self dismissViewControllerAnimated:YES completion:nil]; } #pragma mark - UIViewController -- (void)viewDidLoad { - [super viewDidLoad]; - - self.playButton.layer.cornerRadius = 8.0; - self.playButton.layer.borderWidth = 4.0; - self.playButton.layer.borderColor = self.playButton.titleLabel.textColor.CGColor; - - UIImage *callImage = nil; - switch (self.call) { - case RPSCallPaper: - callImage = [UIImage imageNamed:@"right-paper-128.png"]; - break; - case RPSCallRock: - callImage = [UIImage imageNamed:@"right-rock-128.png"]; - break; - case RPSCallScissors: - callImage = [UIImage imageNamed:@"right-scissors-128.png"]; - break; - - default: - break; - } - - [self.callImageView setImage:callImage]; +- (void)viewDidLoad +{ + [super viewDidLoad]; + + self.playButton.layer.cornerRadius = 8.0; + self.playButton.layer.borderWidth = 4.0; + self.playButton.layer.borderColor = self.playButton.titleLabel.textColor.CGColor; + + UIImage *callImage = nil; + switch (self.call) { + case RPSCallPaper: + callImage = [UIImage imageNamed:@"right-paper-128.png"]; + break; + case RPSCallRock: + callImage = [UIImage imageNamed:@"right-rock-128.png"]; + break; + case RPSCallScissors: + callImage = [UIImage imageNamed:@"right-scissors-128.png"]; + break; + + default: + break; + } + + [self.callImageView setImage:callImage]; } @end diff --git a/samples/RPSSample/RPSSample/RPSAutoAppLinkBasicViewController.m b/samples/RPSSample/RPSSample/RPSAutoAppLinkBasicViewController.m index bc8f470454..eabc9e84c2 100644 --- a/samples/RPSSample/RPSSample/RPSAutoAppLinkBasicViewController.m +++ b/samples/RPSSample/RPSSample/RPSAutoAppLinkBasicViewController.m @@ -20,9 +20,9 @@ static const int paddingLen = 10; -@interface RPSAutoAppLinkBasicViewController() +@interface RPSAutoAppLinkBasicViewController () -@property (strong, nonatomic) Coffee* product; +@property (nonatomic, strong) Coffee *product; @property (nonatomic, copy) NSDictionary *data; @end @@ -31,49 +31,49 @@ @implementation RPSAutoAppLinkBasicViewController - (void)viewDidLoad { - [super viewDidLoad]; + [super viewDidLoad]; - self.view.backgroundColor = [UIColor whiteColor]; - UIScrollView *scrollView = [[UIScrollView alloc] initWithFrame:CGRectMake(0, 0, self.view.frame.size.width, self.view.frame.size.height)]; - int stdWidth = scrollView.frame.size.width - paddingLen*2; + self.view.backgroundColor = [UIColor whiteColor]; + UIScrollView *scrollView = [[UIScrollView alloc] initWithFrame:CGRectMake(0, 0, self.view.frame.size.width, self.view.frame.size.height)]; + int stdWidth = scrollView.frame.size.width - paddingLen * 2; - UILabel *nameLabel = [[UILabel alloc] initWithFrame:CGRectMake(paddingLen, 50, stdWidth, 30)]; - nameLabel.font = [UIFont boldSystemFontOfSize:24]; - nameLabel.textColor = [UIColor grayColor]; + UILabel *nameLabel = [[UILabel alloc] initWithFrame:CGRectMake(paddingLen, 50, stdWidth, 30)]; + nameLabel.font = [UIFont boldSystemFontOfSize:24]; + nameLabel.textColor = [UIColor grayColor]; - UILabel *descLabel = [[UILabel alloc] initWithFrame:CGRectMake(paddingLen, 90, stdWidth, 20)]; - descLabel.font = [UIFont systemFontOfSize:14]; - descLabel.textColor = [UIColor lightGrayColor]; + UILabel *descLabel = [[UILabel alloc] initWithFrame:CGRectMake(paddingLen, 90, stdWidth, 20)]; + descLabel.font = [UIFont systemFontOfSize:14]; + descLabel.textColor = [UIColor lightGrayColor]; - UILabel *priceLabel = [[UILabel alloc] initWithFrame:CGRectMake(paddingLen, 130, stdWidth, 20)]; - priceLabel.font = [UIFont systemFontOfSize:20]; - priceLabel.textColor = [UIColor blackColor]; + UILabel *priceLabel = [[UILabel alloc] initWithFrame:CGRectMake(paddingLen, 130, stdWidth, 20)]; + priceLabel.font = [UIFont systemFontOfSize:20]; + priceLabel.textColor = [UIColor blackColor]; - if (self.product == nil) { - self.product = [[Coffee alloc] initWithName:@"Coffee" desc:@"I am just a coffee" price:1]; - } - nameLabel.text = self.product.name; - descLabel.text = [@"Description: " stringByAppendingString:self.product.desc]; - priceLabel.text = [@"Price: $" stringByAppendingString:[@(self.product.price) stringValue]]; + if (self.product == nil) { + self.product = [[Coffee alloc] initWithName:@"Coffee" desc:@"I am just a coffee" price:1]; + } + nameLabel.text = self.product.name; + descLabel.text = [@"Description: " stringByAppendingString:self.product.desc]; + priceLabel.text = [@"Price: $" stringByAppendingString:[@(self.product.price) stringValue]]; - [scrollView addSubview: nameLabel]; - [scrollView addSubview: descLabel]; - [scrollView addSubview: priceLabel]; + [scrollView addSubview:nameLabel]; + [scrollView addSubview:descLabel]; + [scrollView addSubview:priceLabel]; - if (self.data != nil) { - UILabel *dataLabel = [[UILabel alloc] init]; - dataLabel.font = [UIFont systemFontOfSize:20]; - dataLabel.textColor = [UIColor blueColor]; - dataLabel.text = [NSString stringWithFormat:@"data is: %@", self.data]; - dataLabel.numberOfLines = 0; - CGSize size = [dataLabel.text boundingRectWithSize:CGSizeMake(stdWidth, 1000) - options:NSStringDrawingUsesLineFragmentOrigin - attributes:@{NSFontAttributeName:dataLabel.font} - context:nil].size; - dataLabel.frame = CGRectMake(paddingLen, 180, size.width, size.height); - [scrollView addSubview:dataLabel]; - } - [self.view addSubview: scrollView]; + if (self.data != nil) { + UILabel *dataLabel = [[UILabel alloc] init]; + dataLabel.font = [UIFont systemFontOfSize:20]; + dataLabel.textColor = [UIColor blueColor]; + dataLabel.text = [NSString stringWithFormat:@"data is: %@", self.data]; + dataLabel.numberOfLines = 0; + CGSize size = [dataLabel.text boundingRectWithSize:CGSizeMake(stdWidth, 1000) + options:NSStringDrawingUsesLineFragmentOrigin + attributes:@{NSFontAttributeName : dataLabel.font} + context:nil].size; + dataLabel.frame = CGRectMake(paddingLen, 180, size.width, size.height); + [scrollView addSubview:dataLabel]; + } + [self.view addSubview:scrollView]; } @end diff --git a/samples/RPSSample/RPSSample/RPSAutoAppLinkDebugTool.m b/samples/RPSSample/RPSSample/RPSAutoAppLinkDebugTool.m index 8bc4aaf285..ae22911949 100644 --- a/samples/RPSSample/RPSSample/RPSAutoAppLinkDebugTool.m +++ b/samples/RPSSample/RPSSample/RPSAutoAppLinkDebugTool.m @@ -29,94 +29,90 @@ @implementation RPSAutoAppLinkDebugTool - (void)viewDidLoad { - [super viewDidLoad]; - - self.view.backgroundColor = [UIColor whiteColor]; - UIScrollView *scrollView = [[UIScrollView alloc] initWithFrame:self.view.frame]; - int frameWidth = scrollView.frame.size.width - paddingLen*2; - - UILabel *labelName = [[UILabel alloc] initWithFrame:CGRectMake(paddingLen, 50, frameWidth, frameHeight)]; - labelName.font = [UIFont boldSystemFontOfSize:24]; - labelName.textColor = [UIColor grayColor]; - labelName.text = @"Auto Applink Debug Tool"; - - UILabel *labelDesc = [[UILabel alloc] initWithFrame:CGRectMake(paddingLen, 100, frameWidth, frameHeight + 10)]; - labelDesc.font = [UIFont systemFontOfSize:14]; - labelDesc.textColor = [UIColor lightGrayColor]; - labelDesc.text = @"Enter your FB App ID and product ID to get your auto applink"; - labelDesc.numberOfLines = 0; - - self.appIDView = [self textFieldWithText: @"FB App ID (ex. 111222333)" keyBoardType:UIKeyboardTypeNumberPad]; - self.appIDView.frame = CGRectMake(paddingLen, 150, frameWidth, frameHeight); - - self.productIDView = [self textFieldWithText: @"Product ID (ex. 123)" keyBoardType:UIKeyboardTypeDefault]; - self.productIDView.frame = CGRectMake(paddingLen, 200, frameWidth, frameHeight); - - UIButton *sendButton = [[UIButton alloc] initWithFrame:CGRectMake(paddingLen, 250, frameWidth, frameHeight + 10)]; - [sendButton setBackgroundColor:[[UIColor blueColor] colorWithAlphaComponent:0.4]]; - [sendButton setTitle:@"Send" forState:UIControlStateNormal]; - [sendButton setTitleColor:UIColor.whiteColor forState:UIControlStateNormal]; - [sendButton addTarget:self action:@selector(sendAutoAppLink:) forControlEvents:UIControlEventTouchUpInside]; - - [scrollView addSubview:labelName]; - [scrollView addSubview:labelDesc]; - [scrollView addSubview:self.appIDView]; - [scrollView addSubview:self.productIDView]; - [scrollView addSubview:sendButton]; - [self.view addSubview:scrollView]; + [super viewDidLoad]; + + self.view.backgroundColor = [UIColor whiteColor]; + UIScrollView *scrollView = [[UIScrollView alloc] initWithFrame:self.view.frame]; + int frameWidth = scrollView.frame.size.width - paddingLen * 2; + + UILabel *labelName = [[UILabel alloc] initWithFrame:CGRectMake(paddingLen, 50, frameWidth, frameHeight)]; + labelName.font = [UIFont boldSystemFontOfSize:24]; + labelName.textColor = [UIColor grayColor]; + labelName.text = @"Auto Applink Debug Tool"; + + UILabel *labelDesc = [[UILabel alloc] initWithFrame:CGRectMake(paddingLen, 100, frameWidth, frameHeight + 10)]; + labelDesc.font = [UIFont systemFontOfSize:14]; + labelDesc.textColor = [UIColor lightGrayColor]; + labelDesc.text = @"Enter your FB App ID and product ID to get your auto applink"; + labelDesc.numberOfLines = 0; + + self.appIDView = [self textFieldWithText:@"FB App ID (ex. 111222333)" keyBoardType:UIKeyboardTypeNumberPad]; + self.appIDView.frame = CGRectMake(paddingLen, 150, frameWidth, frameHeight); + + self.productIDView = [self textFieldWithText:@"Product ID (ex. 123)" keyBoardType:UIKeyboardTypeDefault]; + self.productIDView.frame = CGRectMake(paddingLen, 200, frameWidth, frameHeight); + + UIButton *sendButton = [[UIButton alloc] initWithFrame:CGRectMake(paddingLen, 250, frameWidth, frameHeight + 10)]; + [sendButton setBackgroundColor:[[UIColor blueColor] colorWithAlphaComponent:0.4]]; + [sendButton setTitle:@"Send" forState:UIControlStateNormal]; + [sendButton setTitleColor:UIColor.whiteColor forState:UIControlStateNormal]; + [sendButton addTarget:self action:@selector(sendAutoAppLink:) forControlEvents:UIControlEventTouchUpInside]; + + [scrollView addSubview:labelName]; + [scrollView addSubview:labelDesc]; + [scrollView addSubview:self.appIDView]; + [scrollView addSubview:self.productIDView]; + [scrollView addSubview:sendButton]; + [self.view addSubview:scrollView]; } - (UITextField *)textFieldWithText:(NSString *)text keyBoardType:(UIKeyboardType)type { - UITextField *textField; - textField = [[UITextField alloc] init]; - textField.layer.borderColor = [UIColor lightGrayColor].CGColor; - textField.layer.borderWidth = 1; - [textField setKeyboardType:type]; - textField.placeholder = text; - UIView *paddingView = [[UIView alloc] initWithFrame:CGRectMake(0, 0, 5, 20)]; - textField.leftView = paddingView; - textField.leftViewMode = UITextFieldViewModeAlways; - return textField; + UITextField *textField; + textField = [[UITextField alloc] init]; + textField.layer.borderColor = [UIColor lightGrayColor].CGColor; + textField.layer.borderWidth = 1; + [textField setKeyboardType:type]; + textField.placeholder = text; + UIView *paddingView = [[UIView alloc] initWithFrame:CGRectMake(0, 0, 5, 20)]; + textField.leftView = paddingView; + textField.leftViewMode = UITextFieldViewModeAlways; + return textField; } - (void)sendAutoAppLink:(UIButton *)button { - NSString *autoAppLink = [NSString stringWithFormat:@"fb%@://applinks?al_applink_data=", self.appIDView.text]; - NSDictionary *data = @{@"product_id" : self.productIDView.text, - @"is_auto_applink" : @"true" - }; - NSData *jsonData = [NSJSONSerialization dataWithJSONObject:data options:0 error:nil]; - if (self.appIDView.text.length>0 && self.productIDView.text.length>0 && jsonData) { - NSString *encodeData = [FBSDKUtility URLEncode:[[NSString alloc] initWithData:jsonData encoding:NSUTF8StringEncoding]]; - NSString *encodeURL = [autoAppLink stringByAppendingString:encodeData]; - NSURL *url = [NSURL URLWithString:encodeURL]; - if (![[UIApplication sharedApplication] openURL:url]) { - [self showAlert: @"Cannot open the URL!"]; - } + NSString *autoAppLink = [NSString stringWithFormat:@"fb%@://applinks?al_applink_data=", self.appIDView.text]; + NSDictionary *data = @{@"product_id" : self.productIDView.text, + @"is_auto_applink" : @"true"}; + NSData *jsonData = [NSJSONSerialization dataWithJSONObject:data options:0 error:nil]; + if (self.appIDView.text.length > 0 && self.productIDView.text.length > 0 && jsonData) { + NSString *encodeData = [FBSDKUtility URLEncode:[[NSString alloc] initWithData:jsonData encoding:NSUTF8StringEncoding]]; + NSString *encodeURL = [autoAppLink stringByAppendingString:encodeData]; + NSURL *url = [NSURL URLWithString:encodeURL]; + if (![[UIApplication sharedApplication] openURL:url]) { + [self showAlert:@"Cannot open the URL!"]; } - else { - if (self.appIDView.text.length == 0) { - [self showAlert: @"Invalid App ID!"]; - } - else if (self.productIDView.text.length == 0) { - [self showAlert: @"Invalid Product ID!"]; - } - else { - [self showAlert: @"Cannot generate url from input!"]; - } + } else { + if (self.appIDView.text.length == 0) { + [self showAlert:@"Invalid App ID!"]; + } else if (self.productIDView.text.length == 0) { + [self showAlert:@"Invalid Product ID!"]; + } else { + [self showAlert:@"Cannot generate url from input!"]; } + } } -- (void)showAlert:(NSString *) message +- (void)showAlert:(NSString *)message { - UIAlertView *alertView = [[UIAlertView alloc] initWithTitle:@"Error" - message:message - delegate:self - cancelButtonTitle:@"Close" - otherButtonTitles:nil]; - [alertView show]; + UIAlertView *alertView = [[UIAlertView alloc] initWithTitle:@"Error" + message:message + delegate:self + cancelButtonTitle:@"Close" + otherButtonTitles:nil]; + [alertView show]; } @end diff --git a/samples/RPSSample/RPSSample/RPSAutoAppLinkStoryboardViewController.h b/samples/RPSSample/RPSSample/RPSAutoAppLinkStoryboardViewController.h index e89e326d08..94aee14018 100644 --- a/samples/RPSSample/RPSSample/RPSAutoAppLinkStoryboardViewController.h +++ b/samples/RPSSample/RPSSample/RPSAutoAppLinkStoryboardViewController.h @@ -16,7 +16,6 @@ // 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 "RPSCoffee.h" diff --git a/samples/RPSSample/RPSSample/RPSAutoAppLinkStoryboardViewController.m b/samples/RPSSample/RPSSample/RPSAutoAppLinkStoryboardViewController.m index 564183aee6..81c791d74d 100644 --- a/samples/RPSSample/RPSSample/RPSAutoAppLinkStoryboardViewController.m +++ b/samples/RPSSample/RPSSample/RPSAutoAppLinkStoryboardViewController.m @@ -18,15 +18,15 @@ #import "RPSAutoAppLinkStoryboardViewController.h" -@interface RPSAutoAppLinkStoryboardViewController() +@interface RPSAutoAppLinkStoryboardViewController () -@property (strong, nonatomic) Coffee* product; +@property (nonatomic, strong) Coffee *product; @property (nonatomic, copy) NSDictionary *data; -@property (weak, nonatomic) IBOutlet UILabel* nameLabel; -@property (weak, nonatomic) IBOutlet UILabel* descLabel; -@property (weak, nonatomic) IBOutlet UILabel* priceLabel; -@property (weak, nonatomic) IBOutlet UILabel* dataLabel; +@property (nonatomic, weak) IBOutlet UILabel *nameLabel; +@property (nonatomic, weak) IBOutlet UILabel *descLabel; +@property (nonatomic, weak) IBOutlet UILabel *priceLabel; +@property (nonatomic, weak) IBOutlet UILabel *dataLabel; @end @@ -34,19 +34,19 @@ @implementation RPSAutoAppLinkStoryboardViewController - (void)viewDidLoad { - [super viewDidLoad]; + [super viewDidLoad]; - if (self.product == nil) { - self.product = [[Coffee alloc] initWithName:@"Coffee" desc:@"I am just a STORYBOARD coffee" price:1]; - } + if (self.product == nil) { + self.product = [[Coffee alloc] initWithName:@"Coffee" desc:@"I am just a STORYBOARD coffee" price:1]; + } - self.nameLabel.text = self.product.name; - self.descLabel.text = [@"Description: " stringByAppendingString:self.product.desc]; - self.priceLabel.text = [@"Price: $" stringByAppendingString:[@(self.product.price) stringValue]]; + self.nameLabel.text = self.product.name; + self.descLabel.text = [@"Description: " stringByAppendingString:self.product.desc]; + self.priceLabel.text = [@"Price: $" stringByAppendingString:[@(self.product.price) stringValue]]; - if (self.data != nil) { - self.dataLabel.text = [NSString stringWithFormat:@"data is: %@", self.data]; - } + if (self.data != nil) { + self.dataLabel.text = [NSString stringWithFormat:@"data is: %@", self.data]; + } } @end diff --git a/samples/RPSSample/RPSSample/RPSCoffee.h b/samples/RPSSample/RPSSample/RPSCoffee.h index 9229854a8b..d368b80e33 100644 --- a/samples/RPSSample/RPSSample/RPSCoffee.h +++ b/samples/RPSSample/RPSSample/RPSCoffee.h @@ -24,6 +24,6 @@ @property (nonatomic, copy) NSString *desc; @property (nonatomic, assign) float price; -- (instancetype)initWithName:(NSString*)name desc:(NSString*)desc price:(float)price; +- (instancetype)initWithName:(NSString *)name desc:(NSString *)desc price:(float)price; @end diff --git a/samples/RPSSample/RPSSample/RPSCoffee.m b/samples/RPSSample/RPSSample/RPSCoffee.m index 88229f2421..faacf99a44 100644 --- a/samples/RPSSample/RPSSample/RPSCoffee.m +++ b/samples/RPSSample/RPSSample/RPSCoffee.m @@ -20,14 +20,14 @@ @implementation Coffee -- (instancetype)initWithName:(NSString*)name desc:(NSString*)desc price:(float)price +- (instancetype)initWithName:(NSString *)name desc:(NSString *)desc price:(float)price { - if ((self = [super init])) { - _name = [name copy]; - _desc = [desc copy]; - _price = price; - } - return self; + if ((self = [super init])) { + _name = [name copy]; + _desc = [desc copy]; + _price = price; + } + return self; } @end diff --git a/samples/RPSSample/RPSSample/RPSCommonObjects.h b/samples/RPSSample/RPSSample/RPSCommonObjects.h index afa6410d45..af8a911dfb 100644 --- a/samples/RPSSample/RPSSample/RPSCommonObjects.h +++ b/samples/RPSSample/RPSSample/RPSCommonObjects.h @@ -17,11 +17,16 @@ // CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. typedef enum { - RPSCallNone = -1, RPSCallRock = 0, RPSCallPaper = 1, RPSCallScissors = 2 // enum is also used to index arrays + RPSCallNone = -1, + RPSCallRock = 0, + RPSCallPaper = 1, + RPSCallScissors = 2, // enum is also used to index arrays } RPSCall; typedef enum { - RPSResultWin = 0, RPSResultLoss = 1, RPSResultTie = 2 + RPSResultWin = 0, + RPSResultLoss = 1, + RPSResultTie = 2, } RPSResult; extern NSString *builtInOpenGraphObjects[3]; diff --git a/samples/RPSSample/RPSSample/RPSCommonObjects.m b/samples/RPSSample/RPSSample/RPSCommonObjects.m index 509173fa86..b4bd530219 100644 --- a/samples/RPSSample/RPSSample/RPSCommonObjects.m +++ b/samples/RPSSample/RPSSample/RPSCommonObjects.m @@ -17,6 +17,7 @@ // CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. NSString *builtInOpenGraphObjects[3] = { - @"672839339475385", // rock - @"296430467206197", // paper - @"524651207660361"}; // scissors + @"672839339475385", // rock + @"296430467206197", // paper + @"524651207660361" +}; // scissors diff --git a/samples/RPSSample/RPSSample/RPSFriendsViewController.h b/samples/RPSSample/RPSSample/RPSFriendsViewController.h index e325126e89..d192b4f330 100644 --- a/samples/RPSSample/RPSSample/RPSFriendsViewController.h +++ b/samples/RPSSample/RPSSample/RPSFriendsViewController.h @@ -20,11 +20,11 @@ @interface RPSFriendsViewController : UIViewController -@property (weak, nonatomic) IBOutlet UITextView *activityTextView; +@property (nonatomic, weak) IBOutlet UITextView *activityTextView; -@property (weak, nonatomic) IBOutlet UITableView *tableView; +@property (nonatomic, weak) IBOutlet UITableView *tableView; -@property (weak, nonatomic) IBOutlet UIButton *challengeButton; +@property (nonatomic, weak) IBOutlet UIButton *challengeButton; - (IBAction)tapChallengeFriends:(id)sender; diff --git a/samples/RPSSample/RPSSample/RPSFriendsViewController.m b/samples/RPSSample/RPSSample/RPSFriendsViewController.m index e07a403b89..0672e4bf1f 100644 --- a/samples/RPSSample/RPSSample/RPSFriendsViewController.m +++ b/samples/RPSSample/RPSSample/RPSFriendsViewController.m @@ -21,277 +21,282 @@ #import #import - #import - #import @interface RPSFriendsViewController () @end - -@implementation RPSFriendsViewController { - NSMutableArray *_tableData; - BOOL _isPerformingLogin; +@implementation RPSFriendsViewController +{ + NSMutableArray *_tableData; + BOOL _isPerformingLogin; } -- (instancetype)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil { - self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil]; - if (self) { - _isPerformingLogin = NO; - self.title = NSLocalizedString(@"Rock w/Friends", @"Rock w/Friends"); - } +- (instancetype)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil +{ + self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil]; + if (self) { + _isPerformingLogin = NO; + self.title = NSLocalizedString(@"Rock w/Friends", @"Rock w/Friends"); + } - return self; + return self; } #pragma mark - View lifecycle -- (void)viewDidAppear:(BOOL)animated { - [super viewDidAppear:animated]; - - if (!_isPerformingLogin) { - // Login with read permssions - FBSDKAccessToken *accessToken = [FBSDKAccessToken currentAccessToken]; - if (![accessToken.permissions containsObject:@"user_friends"]) { - FBSDKLoginManager *loginManager = [[FBSDKLoginManager alloc] init]; - _isPerformingLogin = YES; - [loginManager logInWithPermissions:@[@"user_friends"] - fromViewController:self - handler:^(FBSDKLoginManagerLoginResult *result, NSError *error) { - _isPerformingLogin = NO; - if (error) { - NSLog(@"Failed to login:%@", error); - return; - } - - FBSDKAccessToken *newToken = [FBSDKAccessToken currentAccessToken]; - if (![newToken.permissions containsObject:@"user_friends"]) { - // Show alert - UIAlertView *alertView = [[UIAlertView alloc] initWithTitle:@"Login Failed" - message:@"You must login and grant access to your friends list to use this feature" - delegate:self - cancelButtonTitle:@"OK" - otherButtonTitles:nil]; - [alertView show]; - [self.navigationController popToRootViewControllerAnimated:YES]; - return; - } - [self updateFriendsTable]; - }]; - } else { - [self updateFriendsTable]; - } +- (void)viewDidAppear:(BOOL)animated +{ + [super viewDidAppear:animated]; + + if (!_isPerformingLogin) { + // Login with read permssions + FBSDKAccessToken *accessToken = [FBSDKAccessToken currentAccessToken]; + if (![accessToken.permissions containsObject:@"user_friends"]) { + FBSDKLoginManager *loginManager = [[FBSDKLoginManager alloc] init]; + _isPerformingLogin = YES; + [loginManager logInWithPermissions:@[@"user_friends"] + fromViewController:self + handler:^(FBSDKLoginManagerLoginResult *result, NSError *error) { + _isPerformingLogin = NO; + if (error) { + NSLog(@"Failed to login:%@", error); + return; + } + + FBSDKAccessToken *newToken = [FBSDKAccessToken currentAccessToken]; + if (![newToken.permissions containsObject:@"user_friends"]) { + // Show alert + UIAlertView *alertView = [[UIAlertView alloc] initWithTitle:@"Login Failed" + message:@"You must login and grant access to your friends list to use this feature" + delegate:self + cancelButtonTitle:@"OK" + otherButtonTitles:nil]; + [alertView show]; + [self.navigationController popToRootViewControllerAnimated:YES]; + return; + } + [self updateFriendsTable]; + }]; + } else { + [self updateFriendsTable]; } + } } #pragma makr - InvitFriends Button -- (IBAction)tapChallengeFriends:(id)sender { - FBSDKGameRequestDialog *gameRequestDialog = [[FBSDKGameRequestDialog alloc] init]; - FBSDKGameRequestContent *content = [[FBSDKGameRequestContent alloc] init]; - content.title = @"Challenge a Friend"; - content.message = @"Please come play RPS with me!"; - gameRequestDialog.content = content; - [gameRequestDialog show]; +- (IBAction)tapChallengeFriends:(id)sender +{ + FBSDKGameRequestDialog *gameRequestDialog = [[FBSDKGameRequestDialog alloc] init]; + FBSDKGameRequestContent *content = [[FBSDKGameRequestContent alloc] init]; + content.title = @"Challenge a Friend"; + content.message = @"Please come play RPS with me!"; + gameRequestDialog.content = content; + [gameRequestDialog show]; } #pragma mark - UITableViewDataSource - (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section { - return [_tableData count]; + return [_tableData count]; } - (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath { - static NSString *const simpleTableIdentifier = @"SimpleTableItem"; + static NSString *const simpleTableIdentifier = @"SimpleTableItem"; - UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:simpleTableIdentifier]; + UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:simpleTableIdentifier]; - if (cell == nil) { - cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:simpleTableIdentifier]; - } + if (cell == nil) { + cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:simpleTableIdentifier]; + } - // Don't have the cell highlighted since we use the checkmark instead - cell.selectionStyle = UITableViewCellSelectionStyleNone; + // Don't have the cell highlighted since we use the checkmark instead + cell.selectionStyle = UITableViewCellSelectionStyleNone; - NSDictionary *data = [_tableData objectAtIndex:indexPath.row]; - cell.textLabel.text = data[@"name"]; - return cell; + NSDictionary *data = [_tableData objectAtIndex:indexPath.row]; + cell.textLabel.text = data[@"name"]; + return cell; } -- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath { - UITableViewCell *cell = [tableView cellForRowAtIndexPath:indexPath]; - cell.accessoryType = UITableViewCellAccessoryCheckmark; +- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath +{ + UITableViewCell *cell = [tableView cellForRowAtIndexPath:indexPath]; + cell.accessoryType = UITableViewCellAccessoryCheckmark; - self.activityTextView.text = @"Loading..."; - NSDictionary *user = [_tableData objectAtIndex:[indexPath row]]; - [self updateActivityForID:user[@"id"]]; + self.activityTextView.text = @"Loading..."; + NSDictionary *user = [_tableData objectAtIndex:[indexPath row]]; + [self updateActivityForID:user[@"id"]]; } --(void)tableView:(UITableView *)tableView didDeselectRowAtIndexPath:(NSIndexPath *)indexPath +- (void)tableView:(UITableView *)tableView didDeselectRowAtIndexPath:(NSIndexPath *)indexPath { - [tableView cellForRowAtIndexPath:indexPath].accessoryType = UITableViewCellAccessoryNone; + [tableView cellForRowAtIndexPath:indexPath].accessoryType = UITableViewCellAccessoryNone; } #pragma mark - private methods -- (void)updateFriendsTable { - // We limit the friends list to only 50 results for this sample. In production you should - // use paging to dynamically grab more users. - NSDictionary *parameters = @{ - @"fields": @"name", - @"limit" : @"50" - }; - // This will only return the list of friends who have this app installed - FBSDKGraphRequest *friendsRequest = [[FBSDKGraphRequest alloc] initWithGraphPath:@"me/friends" - parameters:parameters]; - FBSDKGraphRequestConnection *connection = [[FBSDKGraphRequestConnection alloc] init]; - [connection addRequest:friendsRequest - completionHandler:^(FBSDKGraphRequestConnection *innerConnection, NSDictionary *result, NSError *error) { - if (error) { - NSLog(@"%@", error); - return; - } - - if (result) { - NSArray *data = result[@"data"]; - _tableData = [data copy]; - [_tableView reloadData]; - } - }]; - // start the actual request - [connection start]; +- (void)updateFriendsTable +{ + // We limit the friends list to only 50 results for this sample. In production you should + // use paging to dynamically grab more users. + NSDictionary *parameters = @{ + @"fields" : @"name", + @"limit" : @"50" + }; + // This will only return the list of friends who have this app installed + FBSDKGraphRequest *friendsRequest = [[FBSDKGraphRequest alloc] initWithGraphPath:@"me/friends" + parameters:parameters]; + FBSDKGraphRequestConnection *connection = [[FBSDKGraphRequestConnection alloc] init]; + [connection addRequest:friendsRequest + completionHandler:^(FBSDKGraphRequestConnection *innerConnection, NSDictionary *result, NSError *error) { + if (error) { + NSLog(@"%@", error); + return; + } + + if (result) { + NSArray *data = result[@"data"]; + _tableData = [data copy]; + [_tableView reloadData]; + } + }]; + // start the actual request + [connection start]; } // This is the workhorse method of this view. It updates the textView with the activity of a given user. It // accomplishes this by fetching the "throw" actions for the selected user. -- (void)getActivityForID:(NSString *)fbid callback:(void (^)(NSMutableArray *))callback{ - NSInteger __block pendingRequestCount = 0; - NSMutableArray *selectedUserActiviy = [NSMutableArray array]; - FBSDKGraphRequestConnection *connection = [[FBSDKGraphRequestConnection alloc] init]; - - // Get the results for plays posted to Facebook explicitly. - FBSDKGraphRequest *playActivityRequest = [[FBSDKGraphRequest alloc] initWithGraphPath:[NSString stringWithFormat:@"%@/fb_sample_rps:throw", fbid] - parameters:@{ - @"fields" : @"data,publish_time", - @"limit" : @"10", - @"date_format" : @"U" - }]; - ++pendingRequestCount; - [connection addRequest:playActivityRequest - completionHandler:^(FBSDKGraphRequestConnection *innerConnection, id playActivity, NSError *error) { - if (error) { - NSLog(@"Failed get fb_sample_rps:throw activities for user '%@': %@",fbid, error); - } else if (playActivity) { - for (id entry in playActivity[@"data"]) { - NSString *gesture = entry[@"data"][@"gesture"][@"title"]; - NSString *opposing_gesture = entry[@"data"][@"opposing_gesture"][@"title"]; - [selectedUserActiviy addObject:@{ - @"publish_date" : [self getDateFromEpochTime:entry[@"publish_time"]], - @"player_gesture" : gesture, - @"opponent_gesture" : opposing_gesture - }]; - } - } - if (--pendingRequestCount == 0) { - callback(selectedUserActiviy); - } +- (void)getActivityForID:(NSString *)fbid callback:(void (^)(NSMutableArray *))callback +{ + NSInteger __block pendingRequestCount = 0; + NSMutableArray *selectedUserActiviy = [NSMutableArray array]; + FBSDKGraphRequestConnection *connection = [[FBSDKGraphRequestConnection alloc] init]; + + // Get the results for plays posted to Facebook explicitly. + FBSDKGraphRequest *playActivityRequest = [[FBSDKGraphRequest alloc] initWithGraphPath:[NSString stringWithFormat:@"%@/fb_sample_rps:throw", fbid] + parameters:@{ + @"fields" : @"data,publish_time", + @"limit" : @"10", + @"date_format" : @"U" + }]; + ++pendingRequestCount; + [connection addRequest:playActivityRequest + completionHandler:^(FBSDKGraphRequestConnection *innerConnection, id playActivity, NSError *error) { + if (error) { + NSLog(@"Failed get fb_sample_rps:throw activities for user '%@': %@", fbid, error); + } else if (playActivity) { + for (id entry in playActivity[@"data"]) { + NSString *gesture = entry[@"data"][@"gesture"][@"title"]; + NSString *opposing_gesture = entry[@"data"][@"opposing_gesture"][@"title"]; + [selectedUserActiviy addObject:@{ + @"publish_date" : [self getDateFromEpochTime:entry[@"publish_time"]], + @"player_gesture" : gesture, + @"opponent_gesture" : opposing_gesture + }]; + } + } + if (--pendingRequestCount == 0) { + callback(selectedUserActiviy); + } + }]; + + FBSDKGraphRequest *gameActivityRequest = [[FBSDKGraphRequest alloc] initWithGraphPath:[NSString stringWithFormat:@"%@/fb_sample_rps:play", fbid] + parameters:@{ + @"fields" : @"data,publish_time", + @"limit" : @"10", + @"date_format" : @"U", + }]; + ++pendingRequestCount; + [connection addRequest:gameActivityRequest + batchEntryName:@"games-post" + completionHandler:^(FBSDKGraphRequestConnection *innerConnection, id result, NSError *error) { + if (error) { + NSLog(@"Failed to get game activity %@:", error); + } + if (--pendingRequestCount == 0) { + callback(selectedUserActiviy); + } + } + ]; + // A batch request that id dependent on the previous result + FBSDKGraphRequest *gameData = [[FBSDKGraphRequest alloc] initWithGraphPath:@"?ids={result=games-post:$.data.*.data.game.id}" + parameters:@{ + @"fields" : @"data,created_time", + @"date_format" : @"U", + }]; + ++pendingRequestCount; + [connection addRequest:gameData completionHandler:^(FBSDKGraphRequestConnection *innerConnection, id games, NSError *innerError) { + if (innerError) { + // ignore code 2500 errors since that indicates the parent games-post error was empty. + if ([innerError.userInfo[FBSDKGraphRequestErrorGraphErrorCodeKey] integerValue] != 2500) { + NSLog(@"Failed to get detailed game data for 'play' objects: %@", innerError); + } + } else if (games) { + for (id gameKey in games) { + NSDictionary *game = games[gameKey]; + NSString *player_gesture = game[@"data"][@"player_gesture"][@"title"]; + NSString *opponent_gesture = game[@"data"][@"opponent_gesture"][@"title"]; + [selectedUserActiviy addObject:@{ + @"publish_date" : [self getDateFromEpochTime:game[@"created_time"]], + @"player_gesture" : player_gesture, + @"opponent_gesture" : opponent_gesture }]; + } + } + if (--pendingRequestCount == 0) { + callback(selectedUserActiviy); + } + }]; - FBSDKGraphRequest *gameActivityRequest = [[FBSDKGraphRequest alloc] initWithGraphPath:[NSString stringWithFormat:@"%@/fb_sample_rps:play", fbid] - parameters:@{ - @"fields" : @"data,publish_time", - @"limit" : @"10", - @"date_format" : @"U", - }]; - ++pendingRequestCount; - [connection addRequest:gameActivityRequest - batchEntryName:@"games-post" - completionHandler:^(FBSDKGraphRequestConnection *innerConnection, id result, NSError *error) { - if (error) { - NSLog(@"Failed to get game activity %@:", error); - } - if (--pendingRequestCount == 0) { - callback(selectedUserActiviy); - } - } - ]; - // A batch request that id dependent on the previous result - FBSDKGraphRequest *gameData = [[FBSDKGraphRequest alloc] initWithGraphPath:@"?ids={result=games-post:$.data.*.data.game.id}" - parameters:@{ - @"fields" : @"data,created_time", - @"date_format" : @"U", - }]; - ++pendingRequestCount; - [connection addRequest:gameData completionHandler:^(FBSDKGraphRequestConnection *innerConnection, id games, NSError *innerError) { - if (innerError) { - // ignore code 2500 errors since that indicates the parent games-post error was empty. - if ([innerError.userInfo[FBSDKGraphRequestErrorGraphErrorCodeKey] integerValue] != 2500) { - NSLog(@"Failed to get detailed game data for 'play' objects: %@", innerError); - } - } else if (games) { - for (id gameKey in games) { - NSDictionary *game = games[gameKey]; - NSString *player_gesture = game[@"data"][@"player_gesture"][@"title"]; - NSString *opponent_gesture = game[@"data"][@"opponent_gesture"][@"title"]; - [selectedUserActiviy addObject:@{ - @"publish_date" : [self getDateFromEpochTime:game[@"created_time"]], - @"player_gesture" : player_gesture, - @"opponent_gesture" : opponent_gesture - }]; - } - } - if (--pendingRequestCount == 0) { - callback(selectedUserActiviy); - } - }]; - - - [connection start]; + [connection start]; } -- (NSDate*)getDateFromEpochTime:(NSString *)time { - NSInteger publishTime = [time integerValue]; - return [NSDate dateWithTimeIntervalSince1970:publishTime]; +- (NSDate *)getDateFromEpochTime:(NSString *)time +{ + NSInteger publishTime = [time integerValue]; + return [NSDate dateWithTimeIntervalSince1970:publishTime]; } -- (void)updateActivityForID:(NSString *)fbid { - if (!fbid) { - self.activityTextView.text = @"No User Selected"; - return; +- (void)updateActivityForID:(NSString *)fbid +{ + if (!fbid) { + self.activityTextView.text = @"No User Selected"; + return; + } + + // keep track of the selction + [self getActivityForID:fbid callback:^(NSMutableArray *activity) { + // sort the array by date + [activity sortUsingComparator:^NSComparisonResult (id obj1, + id obj2) { + NSDate *obj1Date = obj1[@"publish_date"]; + NSDate *obj2Date = obj2[@"publish_date"]; + if (obj1Date && obj2Date) { + return [obj2Date compare:obj1Date]; + } + return NSOrderedSame; + }]; + + NSMutableString *output = [NSMutableString string]; + for (id entry in activity) { + NSDateComponents *c = [[NSCalendar currentCalendar] + components:NSCalendarUnitDay | NSCalendarUnitMonth | NSCalendarUnitYear + fromDate:entry[@"publish_date"]]; + NSString *gesture = entry[@"player_gesture"]; + NSString *opposing_gesture = entry[@"opponent_gesture"]; + [output appendFormat:@"%02li/%02li/%02li - %@ %@ %@\n", + (long)c.month, + (long)c.day, + (long)c.year, + gesture, + @"vs", + opposing_gesture]; } - - // keep track of the selction - [self getActivityForID:fbid callback:^(NSMutableArray *activity) { - // sort the array by date - [activity sortUsingComparator:^NSComparisonResult(id obj1, - id obj2) { - NSDate *obj1Date = obj1[@"publish_date"]; - NSDate *obj2Date = obj2[@"publish_date"]; - if (obj1Date && obj2Date) { - return [obj2Date compare:obj1Date]; - } - return NSOrderedSame; - }]; - - NSMutableString *output = [NSMutableString string]; - for (id entry in activity) { - NSDateComponents *c = [[NSCalendar currentCalendar] - components:NSCalendarUnitDay | NSCalendarUnitMonth | NSCalendarUnitYear - fromDate:entry[@"publish_date"]]; - NSString *gesture = entry[@"player_gesture"]; - NSString *opposing_gesture = entry[@"opponent_gesture"]; - [output appendFormat:@"%02li/%02li/%02li - %@ %@ %@\n", - (long)c.month, - (long)c.day, - (long)c.year, - gesture, - @"vs", - opposing_gesture]; - } - self.activityTextView.text = output; - }]; + self.activityTextView.text = output; + }]; } @end diff --git a/samples/RPSSample/RPSSample/RPSGameViewController.h b/samples/RPSSample/RPSSample/RPSGameViewController.h index 3ce533172f..23f03048d3 100644 --- a/samples/RPSSample/RPSSample/RPSGameViewController.h +++ b/samples/RPSSample/RPSSample/RPSGameViewController.h @@ -20,24 +20,24 @@ @interface RPSGameViewController : UIViewController -@property (unsafe_unretained, nonatomic) IBOutlet UILabel *rockLabel; -@property (unsafe_unretained, nonatomic) IBOutlet UILabel *paperLabel; -@property (unsafe_unretained, nonatomic) IBOutlet UILabel *scissorsLabel; +@property (nonatomic, unsafe_unretained) IBOutlet UILabel *rockLabel; +@property (nonatomic, unsafe_unretained) IBOutlet UILabel *paperLabel; +@property (nonatomic, unsafe_unretained) IBOutlet UILabel *scissorsLabel; -@property (unsafe_unretained, nonatomic) IBOutlet UILabel *shootLabel; +@property (nonatomic, unsafe_unretained) IBOutlet UILabel *shootLabel; -@property (unsafe_unretained, nonatomic) IBOutlet UIImageView *playerHand; -@property (unsafe_unretained, nonatomic) IBOutlet UIImageView *computerHand; +@property (nonatomic, unsafe_unretained) IBOutlet UIImageView *playerHand; +@property (nonatomic, unsafe_unretained) IBOutlet UIImageView *computerHand; -@property (unsafe_unretained, nonatomic) IBOutlet UIButton *rockButton; -@property (unsafe_unretained, nonatomic) IBOutlet UIButton *paperButton; -@property (unsafe_unretained, nonatomic) IBOutlet UIButton *scissorsButton; +@property (nonatomic, unsafe_unretained) IBOutlet UIButton *rockButton; +@property (nonatomic, unsafe_unretained) IBOutlet UIButton *paperButton; +@property (nonatomic, unsafe_unretained) IBOutlet UIButton *scissorsButton; -@property (unsafe_unretained, nonatomic) IBOutlet UIButton *againButton; -@property (unsafe_unretained, nonatomic) IBOutlet UIButton *facebookButton; +@property (nonatomic, unsafe_unretained) IBOutlet UIButton *againButton; +@property (nonatomic, unsafe_unretained) IBOutlet UIButton *facebookButton; -@property (unsafe_unretained, nonatomic) IBOutlet UILabel *resultLabel; -@property (unsafe_unretained, nonatomic) IBOutlet UILabel *scoreLabel; +@property (nonatomic, unsafe_unretained) IBOutlet UILabel *resultLabel; +@property (nonatomic, unsafe_unretained) IBOutlet UILabel *scoreLabel; - (IBAction)clickRPSButton:(id)sender; - (IBAction)clickAgainButton:(id)sender; diff --git a/samples/RPSSample/RPSSample/RPSGameViewController.m b/samples/RPSSample/RPSSample/RPSGameViewController.m index c25e736d9d..758bfeb631 100644 --- a/samples/RPSSample/RPSSample/RPSGameViewController.m +++ b/samples/RPSSample/RPSSample/RPSGameViewController.m @@ -22,9 +22,7 @@ #import #import - #import - #import #import "RPSAppDelegate.h" @@ -32,24 +30,24 @@ #import "RPSFriendsViewController.h" static NSString *callType[] = { - @"unknown", - @"rock", - @"paper", - @"scissors" + @"unknown", + @"rock", + @"paper", + @"scissors" }; // Some constants for creating Open Graph objects. static NSString *kResults[] = { - @"won", - @"lost", - @"tied" + @"won", + @"lost", + @"tied" }; // We upload photos for games, but we'd like to reuse the same objects during a session. static NSString *photoURLs[] = { - nil, - nil, - nil + nil, + nil, + nil }; typedef void (^RPSBlock)(void); @@ -57,82 +55,80 @@ @interface RPSGameViewController () @end -@implementation RPSGameViewController { - BOOL _needsInitialAnimation; - BOOL _interestedInImplicitShare; - RPSCall _lastPlayerCall, _lastComputerCall; - UIImage *_rightImages[3]; - UIImage *_leftImages[3]; - UIImage *_imagesToPublish[3]; - RPSBlock _alertOkHandler; - int _wins, _losses, _ties; - NSDate *_lastAnimationStartTime; - NSMutableSet *_activeConnections; +@implementation RPSGameViewController +{ + BOOL _needsInitialAnimation; + BOOL _interestedInImplicitShare; + RPSCall _lastPlayerCall, _lastComputerCall; + UIImage *_rightImages[3]; + UIImage *_leftImages[3]; + UIImage *_imagesToPublish[3]; + RPSBlock _alertOkHandler; + int _wins, _losses, _ties; + NSDate *_lastAnimationStartTime; + NSMutableSet *_activeConnections; } -- (instancetype)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil { - self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil]; - if (self) { - self.title = NSLocalizedString(@"You Rock!", @"You Rock!"); - self.tabBarItem.image = [UIImage imageNamed:@"first"]; +- (instancetype)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil +{ + self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil]; + if (self) { + self.title = NSLocalizedString(@"You Rock!", @"You Rock!"); + self.tabBarItem.image = [UIImage imageNamed:@"first"]; + + BOOL ipad = ([[UIDevice currentDevice] userInterfaceIdiom] == UIUserInterfaceIdiomPad); - BOOL ipad = ([[UIDevice currentDevice] userInterfaceIdiom] == UIUserInterfaceIdiomPad); + NSString *rockRight = ipad ? @"right-rock-128.png" : @"right-rock-88.png"; + NSString *paperRight = ipad ? @"right-paper-128.png" : @"right-paper-88.png"; + NSString *scissorsRight = ipad ? @"right-scissors-128.png" : @"right-scissors-88.png"; - NSString *rockRight = ipad ? @"right-rock-128.png" : @"right-rock-88.png"; - NSString *paperRight = ipad ? @"right-paper-128.png" : @"right-paper-88.png"; - NSString *scissorsRight = ipad ? @"right-scissors-128.png" : @"right-scissors-88.png"; + NSString *rockLeft = ipad ? @"left-rock-128.png" : @"left-rock-88.png"; + NSString *paperLeft = ipad ? @"left-paper-128.png" : @"left-paper-88.png"; + NSString *scissorsLeft = ipad ? @"left-scissors-128.png" : @"left-scissors-88.png"; - NSString *rockLeft = ipad ? @"left-rock-128.png" : @"left-rock-88.png"; - NSString *paperLeft = ipad ? @"left-paper-128.png" : @"left-paper-88.png"; - NSString *scissorsLeft = ipad ? @"left-scissors-128.png" : @"left-scissors-88.png"; + _rightImages[RPSCallRock] = [UIImage imageNamed:rockRight]; + _rightImages[RPSCallPaper] = [UIImage imageNamed:paperRight]; + _rightImages[RPSCallScissors] = [UIImage imageNamed:scissorsRight]; - _rightImages[RPSCallRock] = [UIImage imageNamed:rockRight]; - _rightImages[RPSCallPaper] = [UIImage imageNamed:paperRight]; - _rightImages[RPSCallScissors] = [UIImage imageNamed:scissorsRight]; + _leftImages[RPSCallRock] = [UIImage imageNamed:rockLeft]; + _leftImages[RPSCallPaper] = [UIImage imageNamed:paperLeft]; + _leftImages[RPSCallScissors] = [UIImage imageNamed:scissorsLeft]; - _leftImages[RPSCallRock] = [UIImage imageNamed:rockLeft]; - _leftImages[RPSCallPaper] = [UIImage imageNamed:paperLeft]; - _leftImages[RPSCallScissors] = [UIImage imageNamed:scissorsLeft]; + _imagesToPublish[RPSCallRock] = [UIImage imageNamed:@"left-rock-128.png"]; + _imagesToPublish[RPSCallPaper] = [UIImage imageNamed:@"left-paper-128.png"]; + _imagesToPublish[RPSCallScissors] = [UIImage imageNamed:@"left-scissors-128.png"]; - _imagesToPublish[RPSCallRock] = [UIImage imageNamed:@"left-rock-128.png"]; - _imagesToPublish[RPSCallPaper] = [UIImage imageNamed:@"left-paper-128.png"]; - _imagesToPublish[RPSCallScissors] = [UIImage imageNamed:@"left-scissors-128.png"]; + _lastPlayerCall = _lastComputerCall = RPSCallNone; + _wins = _losses = _ties = 0; + _alertOkHandler = nil; + _needsInitialAnimation = YES; + _interestedInImplicitShare = YES; - _lastPlayerCall = _lastComputerCall = RPSCallNone; - _wins = _losses = _ties = 0; - _alertOkHandler = nil; - _needsInitialAnimation = YES; - _interestedInImplicitShare = YES; + _activeConnections = [[NSMutableSet alloc] init]; + } + return self; +} - _activeConnections = [[NSMutableSet alloc] init]; +- (void)viewDidLoad +{ + [super viewDidLoad]; - } - return self; -} - -- (void)viewDidLoad { - [super viewDidLoad]; - - // Check for a 15 digit FB App ID. If the FB App ID is less than 15 digits, display an alert. - - NSString *strFbAppId = [FBSDKSettings appID]; - NSString *strEmptyFBId = @"{your-facebook-app-id}"; - - if ([strFbAppId isEqualToString:strEmptyFBId]){ - - UIAlertController *alert = [UIAlertController alertControllerWithTitle:@"Missing the Facebook App ID" message:@"The RPSSample-info.plist is missing the Facebook App ID in the FacebookAppID key.\r\n\nPlease close the app, add your Facebook App ID to FacebookAppID key in RPSSample-info.plist, and then restart the app.\r\n\nFor more information, see ReadMe.txt." preferredStyle:UIAlertControllerStyleAlert]; - - UIAlertAction *cancel = [UIAlertAction actionWithTitle:@"Dismiss" style:UIAlertActionStyleCancel handler:^(UIAlertAction * _Nonnull action) { - [alert dismissViewControllerAnimated:YES completion:nil]; - }]; - - [alert addAction:cancel]; - - [self presentViewController:alert animated:YES completion:nil]; - } - - else { - + // Check for a 15 digit FB App ID. If the FB App ID is less than 15 digits, display an alert. + + NSString *strFbAppId = [FBSDKSettings appID]; + NSString *strEmptyFBId = @"{your-facebook-app-id}"; + + if ([strFbAppId isEqualToString:strEmptyFBId]) { + UIAlertController *alert = [UIAlertController alertControllerWithTitle:@"Missing the Facebook App ID" message:@"The RPSSample-info.plist is missing the Facebook App ID in the FacebookAppID key.\r\n\nPlease close the app, add your Facebook App ID to FacebookAppID key in RPSSample-info.plist, and then restart the app.\r\n\nFor more information, see ReadMe.txt." preferredStyle:UIAlertControllerStyleAlert]; + + UIAlertAction *cancel = [UIAlertAction actionWithTitle:@"Dismiss" style:UIAlertActionStyleCancel handler:^(UIAlertAction *_Nonnull action) { + [alert dismissViewControllerAnimated:YES completion:nil]; + }]; + + [alert addAction:cancel]; + + [self presentViewController:alert animated:YES completion:nil]; + } else { UIColor *fontColor = self.rockLabel.textColor; [self.rockButton.layer setCornerRadius:8.0]; [self.rockButton.layer setBorderWidth:4.0]; @@ -177,494 +173,534 @@ - (void)viewDidLoad { [self updateScoreLabel]; [self resetField]; - } -} - -- (void)viewWillAppear:(BOOL)animated { - [super viewWillAppear:animated]; - self.navigationController.navigationBarHidden = YES; -} - -- (void)viewDidAppear:(BOOL)animated { - [super viewDidAppear:animated]; - if (_needsInitialAnimation) { - // get things rolling - _needsInitialAnimation = NO; - dispatch_after(dispatch_time(DISPATCH_TIME_NOW, .5 * NSEC_PER_SEC), dispatch_get_main_queue(), ^{ - [self animateField]; - }); - } -} - -- (void)viewWillDisappear:(BOOL)animated { - [super viewWillDisappear:animated]; - self.navigationController.navigationBarHidden = NO; -} - -- (void)viewDidUnload { - [self setRockLabel:nil]; - [self setPaperLabel:nil]; - [self setScissorsLabel:nil]; - [self setRockButton:nil]; - [self setRockButton:nil]; - [self setPaperButton:nil]; - [self setScissorsButton:nil]; - [self setShootLabel:nil]; - [self setComputerHand:nil]; - [self setAgainButton:nil]; - [self setPlayerHand:nil]; - [super viewDidUnload]; -} - -- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation { - // Return YES for supported orientations - if ([[UIDevice currentDevice] userInterfaceIdiom] == UIUserInterfaceIdiomPhone) { - return (interfaceOrientation != UIInterfaceOrientationPortraitUpsideDown); - } else { - return YES; - } -} - -- (void)resetField { - self.rockButton.hidden = - self.paperButton.hidden = - self.scissorsButton.hidden = - self.rockLabel.hidden = - self.paperLabel.hidden = - self.scissorsLabel.hidden = - self.shootLabel.hidden = - self.computerHand.hidden = - self.playerHand.hidden = - self.againButton.hidden = YES; - - self.rockButton.enabled = - self.paperButton.enabled = - self.scissorsButton.enabled = NO; - - self.resultLabel.text = @""; -} - -- (void)setFieldForPlayAgain { - self.shootLabel.hidden = - self.rockButton.hidden = - self.paperButton.hidden = - self.scissorsButton.hidden = YES; - - self.playerHand.hidden = - self.againButton.hidden = NO; -} - -- (void)animateField { - // rock - dispatch_after(dispatch_time(DISPATCH_TIME_NOW, .5 * NSEC_PER_SEC), dispatch_get_main_queue(), ^{ - self.rockLabel.hidden = NO; - self.rockButton.hidden = NO; - - // paper - dispatch_after(dispatch_time(DISPATCH_TIME_NOW, 1 * NSEC_PER_SEC), dispatch_get_main_queue(), ^{ - self.paperLabel.hidden = NO; - self.paperButton.hidden = NO; - - // scissors - dispatch_after(dispatch_time(DISPATCH_TIME_NOW, 1 * NSEC_PER_SEC), dispatch_get_main_queue(), ^{ - self.scissorsLabel.hidden = NO; - self.scissorsButton.hidden = NO; - - // shoot! - dispatch_after(dispatch_time(DISPATCH_TIME_NOW, 1 * NSEC_PER_SEC), dispatch_get_main_queue(), ^{ - self.shootLabel.hidden = - self.computerHand.hidden = NO; - self.rockButton.enabled = - self.paperButton.enabled = - self.scissorsButton.enabled = YES; - - self.computerHand.animationImages = @[ _rightImages[RPSCallRock], _rightImages[RPSCallPaper], _rightImages[RPSCallScissors]]; - self.computerHand.animationDuration = .4; - self.computerHand.animationRepeatCount = 0; - [self.computerHand startAnimating]; - _lastAnimationStartTime = [NSDate date]; + } +} + +- (void)viewWillAppear:(BOOL)animated +{ + [super viewWillAppear:animated]; + self.navigationController.navigationBarHidden = YES; +} + +- (void)viewDidAppear:(BOOL)animated +{ + [super viewDidAppear:animated]; + if (_needsInitialAnimation) { + // get things rolling + _needsInitialAnimation = NO; + dispatch_after(dispatch_time(DISPATCH_TIME_NOW, .5 * NSEC_PER_SEC), + dispatch_get_main_queue(), ^{ + [self animateField]; + }); + } +} + +- (void)viewWillDisappear:(BOOL)animated +{ + [super viewWillDisappear:animated]; + self.navigationController.navigationBarHidden = NO; +} + +- (void)viewDidUnload +{ + [self setRockLabel:nil]; + [self setPaperLabel:nil]; + [self setScissorsLabel:nil]; + [self setRockButton:nil]; + [self setRockButton:nil]; + [self setPaperButton:nil]; + [self setScissorsButton:nil]; + [self setShootLabel:nil]; + [self setComputerHand:nil]; + [self setAgainButton:nil]; + [self setPlayerHand:nil]; + [super viewDidUnload]; +} + +- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation +{ + // Return YES for supported orientations + if ([[UIDevice currentDevice] userInterfaceIdiom] == UIUserInterfaceIdiomPhone) { + return (interfaceOrientation != UIInterfaceOrientationPortraitUpsideDown); + } else { + return YES; + } +} + +- (void)resetField +{ + self.rockButton.hidden = + self.paperButton.hidden = + self.scissorsButton.hidden = + self.rockLabel.hidden = + self.paperLabel.hidden = + self.scissorsLabel.hidden = + self.shootLabel.hidden = + self.computerHand.hidden = + self.playerHand.hidden = + self.againButton.hidden = YES; + + self.rockButton.enabled = + self.paperButton.enabled = + self.scissorsButton.enabled = NO; + + self.resultLabel.text = @""; +} + +- (void)setFieldForPlayAgain +{ + self.shootLabel.hidden = + self.rockButton.hidden = + self.paperButton.hidden = + self.scissorsButton.hidden = YES; + + self.playerHand.hidden = + self.againButton.hidden = NO; +} + +- (void)animateField +{ + // rock + dispatch_after(dispatch_time(DISPATCH_TIME_NOW, .5 * NSEC_PER_SEC), + dispatch_get_main_queue(), ^{ + self.rockLabel.hidden = NO; + self.rockButton.hidden = NO; + + // paper + dispatch_after(dispatch_time(DISPATCH_TIME_NOW, 1 * NSEC_PER_SEC), + dispatch_get_main_queue(), ^{ + self.paperLabel.hidden = NO; + self.paperButton.hidden = NO; + + // scissors + dispatch_after(dispatch_time(DISPATCH_TIME_NOW, 1 * NSEC_PER_SEC), + dispatch_get_main_queue(), ^{ + self.scissorsLabel.hidden = NO; + self.scissorsButton.hidden = NO; + + // shoot! + dispatch_after(dispatch_time(DISPATCH_TIME_NOW, 1 * NSEC_PER_SEC), + dispatch_get_main_queue(), ^{ + self.shootLabel.hidden = + self.computerHand.hidden = NO; + self.rockButton.enabled = + self.paperButton.enabled = + self.scissorsButton.enabled = YES; + + self.computerHand.animationImages = @[_rightImages[RPSCallRock], _rightImages[RPSCallPaper], _rightImages[RPSCallScissors]]; + self.computerHand.animationDuration = .4; + self.computerHand.animationRepeatCount = 0; + [self.computerHand startAnimating]; + _lastAnimationStartTime = [NSDate date]; }); }); }); }); } -- (RPSCall)callViaRandom { - return (RPSCall)(arc4random() % 3); +- (RPSCall)callViaRandom +{ + return (RPSCall)(arc4random() % 3); } - (RPSResult)resultForPlayerCall:(RPSCall)playerCall - computerCall:(RPSCall)computerCall { - static RPSResult results[3][3] = { - {RPSResultTie, RPSResultLoss, RPSResultWin}, - {RPSResultWin, RPSResultTie, RPSResultLoss}, - {RPSResultLoss, RPSResultWin, RPSResultTie} - }; - return results[playerCall][computerCall]; -} - -- (void)callGame:(RPSCall)playerCall { - NSTimeInterval timeTaken = fabs([_lastAnimationStartTime timeIntervalSinceNow]); - [self logTimeTaken:timeTaken]; - [self logCurrentPlayerCall:playerCall lastPlayerCall:_lastPlayerCall lastComputerCall:_lastComputerCall]; - - // stop animating and identify each opponent's call - [self.computerHand stopAnimating]; - _lastPlayerCall = playerCall; - _lastComputerCall = [self callViaRandom]; - self.computerHand.image = _rightImages[_lastComputerCall]; - - // update UI and counts based on result - RPSResult result = [self resultForPlayerCall:_lastPlayerCall - computerCall:_lastComputerCall]; - - switch (result) { - case RPSResultWin: - _wins++; - self.resultLabel.text = @"Win!"; - [self logPlayerCall:playerCall result:RPSResultWin timeTaken:timeTaken]; - break; - case RPSResultLoss: - _losses++; - self.resultLabel.text = @"Loss."; - [self logPlayerCall:playerCall result:RPSResultLoss timeTaken:timeTaken]; - break; - case RPSResultTie: - _ties++; - self.resultLabel.text = @"Tie..."; - [self logPlayerCall:playerCall result:RPSResultTie timeTaken:timeTaken]; - break; + computerCall:(RPSCall)computerCall +{ + static RPSResult results[3][3] = { + {RPSResultTie, RPSResultLoss, RPSResultWin}, + {RPSResultWin, RPSResultTie, RPSResultLoss}, + {RPSResultLoss, RPSResultWin, RPSResultTie} + }; + return results[playerCall][computerCall]; +} + +- (void)callGame:(RPSCall)playerCall +{ + NSTimeInterval timeTaken = fabs([_lastAnimationStartTime timeIntervalSinceNow]); + [self logTimeTaken:timeTaken]; + [self logCurrentPlayerCall:playerCall lastPlayerCall:_lastPlayerCall lastComputerCall:_lastComputerCall]; + + // stop animating and identify each opponent's call + [self.computerHand stopAnimating]; + _lastPlayerCall = playerCall; + _lastComputerCall = [self callViaRandom]; + self.computerHand.image = _rightImages[_lastComputerCall]; + + // update UI and counts based on result + RPSResult result = [self resultForPlayerCall:_lastPlayerCall + computerCall:_lastComputerCall]; + + switch (result) { + case RPSResultWin: + _wins++; + self.resultLabel.text = @"Win!"; + [self logPlayerCall:playerCall result:RPSResultWin timeTaken:timeTaken]; + break; + case RPSResultLoss: + _losses++; + self.resultLabel.text = @"Loss."; + [self logPlayerCall:playerCall result:RPSResultLoss timeTaken:timeTaken]; + break; + case RPSResultTie: + _ties++; + self.resultLabel.text = @"Tie..."; + [self logPlayerCall:playerCall result:RPSResultTie timeTaken:timeTaken]; + break; + } + [self updateScoreLabel]; + + if (_interestedInImplicitShare) { + [self publishResult]; + } +} + +- (void)updateScoreLabel +{ + self.scoreLabel.text = [NSString stringWithFormat:@"W = %d L = %d T = %d", _wins, _losses, _ties]; +} + +- (IBAction)clickRPSButton:(id)sender +{ + UIButton *button = sender; + RPSCall choice = (RPSCall)button.tag; + self.playerHand.image = _leftImages[choice]; + [self callGame:choice]; + [self setFieldForPlayAgain]; +} + +- (IBAction)clickAgainButton:(id)sender +{ + [self resetField]; + [self animateField]; +} + +- (IBAction)clickFacebookButton:(id)sender +{ + UIActionSheet *sheet = [[UIActionSheet alloc] initWithTitle:nil + delegate:self + cancelButtonTitle:@"Cancel" + destructiveButtonTitle:nil + otherButtonTitles:@"Share on Facebook", + @"Share on Messenger", + @"Friends' Activity", + [FBSDKAccessToken currentAccessToken] ? @"Log out" : @"Log in", + nil]; + // Show the sheet + [sheet showInView:sender]; +} + +- (void)alertView:(UIAlertView *)alertView clickedButtonAtIndex:(NSInteger)buttonIndex +{ + if (buttonIndex != 0) { // ok + if (_alertOkHandler) { + _alertOkHandler(); + _alertOkHandler = nil; } - [self updateScoreLabel]; - - if (_interestedInImplicitShare) { - [self publishResult]; + } +} + +- (void)actionSheet:(UIActionSheet *)actionSheet didDismissWithButtonIndex:(NSInteger)buttonIndex +{ + switch (buttonIndex) { + case 0: { // Share on Facebook + FBSDKShareDialog *shareDialog = [[FBSDKShareDialog alloc] init]; + shareDialog.fromViewController = self; + if (![self shareWith:shareDialog content:[self getGameShareContent:NO]]) { + [self displayInstallAppWithAppName:@"Facebook"]; + } + break; } - -} - -- (void)updateScoreLabel { - self.scoreLabel.text = [NSString stringWithFormat:@"W = %d L = %d T = %d", _wins, _losses, _ties]; -} - -- (IBAction)clickRPSButton:(id)sender { - UIButton *button = sender; - RPSCall choice = (RPSCall)button.tag; - self.playerHand.image = _leftImages[choice]; - [self callGame:choice]; - [self setFieldForPlayAgain]; -} - -- (IBAction)clickAgainButton:(id)sender { - [self resetField]; - [self animateField]; -} - -- (IBAction)clickFacebookButton:(id)sender { - UIActionSheet *sheet = [[UIActionSheet alloc] initWithTitle:nil - delegate:self - cancelButtonTitle:@"Cancel" - destructiveButtonTitle:nil - otherButtonTitles:@"Share on Facebook", - @"Share on Messenger", - @"Friends' Activity", - [FBSDKAccessToken currentAccessToken] ? @"Log out" : @"Log in", - nil]; - // Show the sheet - [sheet showInView:sender]; -} - -- (void)alertView:(UIAlertView *)alertView clickedButtonAtIndex:(NSInteger)buttonIndex { - if (buttonIndex != 0) { // ok - if (_alertOkHandler) { - _alertOkHandler(); - _alertOkHandler = nil; - } + case 1: { // Share on Messenger + if (![self shareWith:[[FBSDKMessageDialog alloc] init] content:[self getGameShareContent:YES]]) { + [self displayInstallAppWithAppName:@"Messenger"]; + } + break; } -} - -- (void)actionSheet:(UIActionSheet *)actionSheet didDismissWithButtonIndex:(NSInteger)buttonIndex { - switch (buttonIndex) { - case 0: { // Share on Facebook - FBSDKShareDialog *shareDialog = [[FBSDKShareDialog alloc] init]; - shareDialog.fromViewController = self; - if (![self shareWith:shareDialog content:[self getGameShareContent:NO]]) { - [self displayInstallAppWithAppName:@"Facebook"]; - } - break; - } - case 1: { // Share on Messenger - if (![self shareWith:[[FBSDKMessageDialog alloc] init] content:[self getGameShareContent:YES]]) { - [self displayInstallAppWithAppName:@"Messenger"]; - } - break; - } - case 2: { // See Friends - UIViewController *friends; - if ([[UIDevice currentDevice] userInterfaceIdiom] == UIUserInterfaceIdiomPhone) { - friends = [[RPSFriendsViewController alloc] initWithNibName:@"RPSFriendsViewController_iPhone" bundle:nil]; - } else { - friends = [[RPSFriendsViewController alloc] initWithNibName:@"RPSFriendsViewController_iPad" bundle:nil]; - } - [self.navigationController pushViewController:friends - animated:YES]; - break; - } - case 3: { // Login and logout - if ([FBSDKAccessToken currentAccessToken]) { - FBSDKLoginManager *login = [[FBSDKLoginManager alloc] init]; - [login logOut]; - } else { - // Try to login with permissions - [self loginAndRequestPermissionsWithSuccessHandler:nil - declinedOrCanceledHandler:^{ - // If the user declined permissions tell them why we need permissions - // and ask for permissions again if they want to grant permissions. - [self alertDeclinedPublishActionsWithCompletion:^{ - [self loginAndRequestPermissionsWithSuccessHandler:nil - declinedOrCanceledHandler:nil - errorHandler:^(NSError * error) { - NSLog(@"Error: %@", error.description); - }]; - }]; - } - errorHandler:^(NSError * error) { - NSLog(@"Error: %@", error.description); - }]; - } - } + case 2: { // See Friends + UIViewController *friends; + if ([[UIDevice currentDevice] userInterfaceIdiom] == UIUserInterfaceIdiomPhone) { + friends = [[RPSFriendsViewController alloc] initWithNibName:@"RPSFriendsViewController_iPhone" bundle:nil]; + } else { + friends = [[RPSFriendsViewController alloc] initWithNibName:@"RPSFriendsViewController_iPad" bundle:nil]; + } + [self.navigationController pushViewController:friends + animated:YES]; + break; } -} - -- (BOOL)hasPlayedAtLeastOnce { - return _lastPlayerCall != RPSCallNone && _lastComputerCall != RPSCallNone; -} - -- (id) getGameShareContent:(BOOL)isShareForMessenger { - return (self.hasPlayedAtLeastOnce && !isShareForMessenger) ? [self getGameActivityShareContent] : [self getGameLinkShareContent]; -} - -- (FBSDKShareOpenGraphContent *) getGameActivityShareContent { - // set action's gesture property - FBSDKShareOpenGraphAction *action = [FBSDKShareOpenGraphAction actionWithType:@"fb_sample_rps:throw" - objectID:builtInOpenGraphObjects[_lastPlayerCall] - key:@"fb_sample_rps:gesture"]; - // set action's opposing_gesture property - [action setString:builtInOpenGraphObjects[_lastComputerCall] forKey:@"fb_sample_rps:opposing_gesture"]; - - FBSDKShareOpenGraphContent *content = [[FBSDKShareOpenGraphContent alloc] init]; - content.action = action; - content.previewPropertyName = @"fb_sample_rps:gesture"; - return content; -} - -- (BOOL)shareWith:(id)dialog content:(id)content{ - dialog.shareContent = content; - dialog.delegate = self; - return [dialog show]; -} - -- (void) displayInstallAppWithAppName:(NSString *)appName { - NSString *message = [NSString stringWithFormat: - @"Install or upgrade the %@ application on your device and " - @"get cool new sharing features for this application. " - @"What do you want to do?" , appName]; - [self alertWithMessage:message - ok:@"Install or Upgrade Now" - cancel:@"Decide Later" - completion:^{ - [[UIApplication sharedApplication] - openURL:[NSURL URLWithString:[NSString stringWithFormat:@"itms-apps://itunes.com/apps/%@", appName]]]; - }]; -} - -- (FBSDKShareLinkContent *)getGameLinkShareContent { - FBSDKShareLinkContent *content = [[FBSDKShareLinkContent alloc] init]; - content.contentURL = [NSURL URLWithString:@"https://developers.facebook.com/"]; - return content; -} - -- (FBSDKShareOpenGraphObject *)createGameObject { - RPSResult result = [self resultForPlayerCall:_lastPlayerCall - computerCall:_lastComputerCall]; - - NSString *resultName = kResults[result]; - - FBSDKShareOpenGraphObject *object = [[FBSDKShareOpenGraphObject alloc] init]; - [object setString:@"fb_sample_rps:game" forKey:@"og:type"]; - [object setString:@"an awesome game of Rock, Paper, Scissors" forKey:@"og:title"]; - [object setString:builtInOpenGraphObjects[_lastPlayerCall] forKey:@"fb_sample_rps:player_gesture"]; - [object setString:builtInOpenGraphObjects[_lastComputerCall] forKey:@"fb_sample_rps:opponent_gesture"]; - [object setString:resultName forKey:@"fb_sample_rps:result"]; - [object setString:photoURLs[_lastPlayerCall] forKey:@"og:image"]; - return object; -} - -- (FBSDKShareOpenGraphAction *)createPlayActionWithGame:(FBSDKShareOpenGraphObject *)game { - return [FBSDKShareOpenGraphAction actionWithType:@"fb_sample_rps:play" object:game key:@"fb_sample_rps:game"]; -} - -- (void)loginAndRequestPermissionsWithSuccessHandler:(RPSBlock) successHandler - declinedOrCanceledHandler:(RPSBlock) declinedOrCanceledHandler - errorHandler:(void (^)(NSError *)) errorHandler{ - FBSDKLoginManager *login = [[FBSDKLoginManager alloc] init]; - [login logInWithPermissions:@[@"publish_actions"] - fromViewController:self - handler:^(FBSDKLoginManagerLoginResult *result, NSError *error) { - if (error) { - if (errorHandler) { - errorHandler(error); - } - return; - } + case 3: { // Login and logout + if ([FBSDKAccessToken currentAccessToken]) { + FBSDKLoginManager *login = [[FBSDKLoginManager alloc] init]; + [login logOut]; + } else { + // Try to login with permissions + [self loginAndRequestPermissionsWithSuccessHandler:nil + declinedOrCanceledHandler:^{ + // If the user declined permissions tell them why we need permissions + // and ask for permissions again if they want to grant permissions. + [self alertDeclinedPublishActionsWithCompletion:^{ + [self loginAndRequestPermissionsWithSuccessHandler:nil + declinedOrCanceledHandler:nil + errorHandler:^(NSError *error) { + NSLog(@"Error: %@", error.description); + }]; + }]; + } + errorHandler:^(NSError *error) { + NSLog(@"Error: %@", error.description); + }]; + } + } + } +} + +- (BOOL)hasPlayedAtLeastOnce +{ + return _lastPlayerCall != RPSCallNone && _lastComputerCall != RPSCallNone; +} + +- (id)getGameShareContent:(BOOL)isShareForMessenger +{ + return (self.hasPlayedAtLeastOnce && !isShareForMessenger) ? [self getGameActivityShareContent] : [self getGameLinkShareContent]; +} + +- (FBSDKShareOpenGraphContent *)getGameActivityShareContent +{ + // set action's gesture property + FBSDKShareOpenGraphAction *action = [FBSDKShareOpenGraphAction actionWithType:@"fb_sample_rps:throw" + objectID:builtInOpenGraphObjects[_lastPlayerCall] + key:@"fb_sample_rps:gesture"]; + // set action's opposing_gesture property + [action setString:builtInOpenGraphObjects[_lastComputerCall] forKey:@"fb_sample_rps:opposing_gesture"]; + + FBSDKShareOpenGraphContent *content = [[FBSDKShareOpenGraphContent alloc] init]; + content.action = action; + content.previewPropertyName = @"fb_sample_rps:gesture"; + return content; +} + +- (BOOL)shareWith:(id)dialog content:(id)content +{ + dialog.shareContent = content; + dialog.delegate = self; + return [dialog show]; +} + +- (void)displayInstallAppWithAppName:(NSString *)appName +{ + NSString *message = [NSString stringWithFormat: + @"Install or upgrade the %@ application on your device and " + @"get cool new sharing features for this application. " + @"What do you want to do?", appName]; + [self alertWithMessage:message + ok:@"Install or Upgrade Now" + cancel:@"Decide Later" + completion:^{ + [[UIApplication sharedApplication] + openURL:[NSURL URLWithString:[NSString stringWithFormat:@"itms-apps://itunes.com/apps/%@", appName]]]; + }]; +} + +- (FBSDKShareLinkContent *)getGameLinkShareContent +{ + FBSDKShareLinkContent *content = [[FBSDKShareLinkContent alloc] init]; + content.contentURL = [NSURL URLWithString:@"https://developers.facebook.com/"]; + return content; +} + +- (FBSDKShareOpenGraphObject *)createGameObject +{ + RPSResult result = [self resultForPlayerCall:_lastPlayerCall + computerCall:_lastComputerCall]; + + NSString *resultName = kResults[result]; + + FBSDKShareOpenGraphObject *object = [[FBSDKShareOpenGraphObject alloc] init]; + [object setString:@"fb_sample_rps:game" forKey:@"og:type"]; + [object setString:@"an awesome game of Rock, Paper, Scissors" forKey:@"og:title"]; + [object setString:builtInOpenGraphObjects[_lastPlayerCall] forKey:@"fb_sample_rps:player_gesture"]; + [object setString:builtInOpenGraphObjects[_lastComputerCall] forKey:@"fb_sample_rps:opponent_gesture"]; + [object setString:resultName forKey:@"fb_sample_rps:result"]; + [object setString:photoURLs[_lastPlayerCall] forKey:@"og:image"]; + return object; +} + +- (FBSDKShareOpenGraphAction *)createPlayActionWithGame:(FBSDKShareOpenGraphObject *)game +{ + return [FBSDKShareOpenGraphAction actionWithType:@"fb_sample_rps:play" object:game key:@"fb_sample_rps:game"]; +} + +- (void)loginAndRequestPermissionsWithSuccessHandler:(RPSBlock)successHandler + declinedOrCanceledHandler:(RPSBlock)declinedOrCanceledHandler + errorHandler:(void (^)(NSError *))errorHandler +{ + FBSDKLoginManager *login = [[FBSDKLoginManager alloc] init]; + [login logInWithPermissions:@[@"publish_actions"] + fromViewController:self + handler:^(FBSDKLoginManagerLoginResult *result, NSError *error) { + if (error) { + if (errorHandler) { + errorHandler(error); + } + return; + } - if ([FBSDKAccessToken currentAccessToken] && - [[FBSDKAccessToken currentAccessToken].permissions containsObject:@"publish_actions"]) { - if (successHandler) { - successHandler(); - } - return; - } + if ([FBSDKAccessToken currentAccessToken] + && [[FBSDKAccessToken currentAccessToken].permissions containsObject:@"publish_actions"]) { + if (successHandler) { + successHandler(); + } + return; + } - if (declinedOrCanceledHandler) { - declinedOrCanceledHandler(); - } - }]; + if (declinedOrCanceledHandler) { + declinedOrCanceledHandler(); + } + }]; } -- (void)alertDeclinedPublishActionsWithCompletion:(RPSBlock)completion { - UIAlertView *alertView = [[UIAlertView alloc] initWithTitle:@"Publish Permissions" - message:@"Publish permissions are needed to share game content automatically. Do you want to enable publish permissions?" - delegate:self - cancelButtonTitle:@"No" - otherButtonTitles:@"Ok", nil]; - _alertOkHandler = [completion copy]; - [alertView show]; +- (void)alertDeclinedPublishActionsWithCompletion:(RPSBlock)completion +{ + UIAlertView *alertView = [[UIAlertView alloc] initWithTitle:@"Publish Permissions" + message:@"Publish permissions are needed to share game content automatically. Do you want to enable publish permissions?" + delegate:self + cancelButtonTitle:@"No" + otherButtonTitles:@"Ok", nil]; + _alertOkHandler = [completion copy]; + [alertView show]; } - (void)alertWithMessage:(NSString *)message ok:(NSString *)ok cancel:(NSString *)cancel - completion:(RPSBlock)completion { - UIAlertView *alertView = [[UIAlertView alloc] initWithTitle:@"Share with Facebook" - message:message - delegate:self - cancelButtonTitle:cancel - otherButtonTitles:ok, nil]; - _alertOkHandler = [completion copy]; - [alertView show]; -} - -- (void)publishPhotoForGesture:(RPSCall)gesture { - FBSDKGraphRequestConnection *conn = [[FBSDKGraphRequestConnection alloc] init]; - FBSDKGraphRequest *request = [[FBSDKGraphRequest alloc] initWithGraphPath:@"me/staging_resources" - parameters:@{@"file":_imagesToPublish[gesture]} - tokenString:[FBSDKAccessToken currentAccessToken].tokenString - version:nil - HTTPMethod:@"POST"]; - [conn addRequest:request completionHandler:^(FBSDKGraphRequestConnection *connection, id result, NSError *error) { - if (error) { - NSLog(@"%@", error); - } else { - photoURLs[gesture] = result[@"uri"]; - [self publishResult]; - } - }]; - [conn start]; -} - -- (void)publishResult { - // Check if we have publish permissions and ask for them if we don't - if (![FBSDKAccessToken currentAccessToken] || - ![[FBSDKAccessToken currentAccessToken].permissions containsObject:@"publish_actions"]) - { - NSLog(@"Re-requesting permissions"); - _interestedInImplicitShare = NO; - [self alertWithMessage:@"Share game activity with your friends?" - ok:@"Yes" - cancel:@"Maybe Later" - completion:^{ - _interestedInImplicitShare = YES; - [self loginAndRequestPermissionsWithSuccessHandler:^{ - [self publishResult]; - } - declinedOrCanceledHandler:nil - errorHandler:^(NSError * error) { - NSLog(@"Error: %@", error.description); - }]; - }]; - return; - } - - // We want to upload a photo representing the gesture the player threw, and use it as the - // image for our game OG object. But we optimize this and only upload one instance per session. - // So if we already have the image URL, we use it, otherwise we'll initiate an upload and - // publish the result once it finishes. - if (!photoURLs[_lastPlayerCall]) { - [self publishPhotoForGesture:_lastPlayerCall]; - return; + completion:(RPSBlock)completion +{ + UIAlertView *alertView = [[UIAlertView alloc] initWithTitle:@"Share with Facebook" + message:message + delegate:self + cancelButtonTitle:cancel + otherButtonTitles:ok, nil]; + _alertOkHandler = [completion copy]; + [alertView show]; +} + +- (void)publishPhotoForGesture:(RPSCall)gesture +{ + FBSDKGraphRequestConnection *conn = [[FBSDKGraphRequestConnection alloc] init]; + FBSDKGraphRequest *request = [[FBSDKGraphRequest alloc] initWithGraphPath:@"me/staging_resources" + parameters:@{@"file" : _imagesToPublish[gesture]} + tokenString:[FBSDKAccessToken currentAccessToken].tokenString + version:nil + HTTPMethod:@"POST"]; + [conn addRequest:request completionHandler:^(FBSDKGraphRequestConnection *connection, id result, NSError *error) { + if (error) { + NSLog(@"%@", error); + } else { + photoURLs[gesture] = result[@"uri"]; + [self publishResult]; } - - FBSDKShareOpenGraphObject *game = [self createGameObject]; - FBSDKShareOpenGraphAction *action = [self createPlayActionWithGame:game]; - FBSDKShareOpenGraphContent *content = [[FBSDKShareOpenGraphContent alloc] init]; - content.action = action; - content.previewPropertyName = @"fb_sample_rps:game"; - [FBSDKShareAPI shareWithContent:content delegate:self]; + }]; + [conn start]; +} + +- (void)publishResult +{ + // Check if we have publish permissions and ask for them if we don't + if (![FBSDKAccessToken currentAccessToken] + || ![[FBSDKAccessToken currentAccessToken].permissions containsObject:@"publish_actions"]) { + NSLog(@"Re-requesting permissions"); + _interestedInImplicitShare = NO; + [self alertWithMessage:@"Share game activity with your friends?" + ok:@"Yes" + cancel:@"Maybe Later" + completion:^{ + _interestedInImplicitShare = YES; + [self loginAndRequestPermissionsWithSuccessHandler:^{ + [self publishResult]; + } + declinedOrCanceledHandler:nil + errorHandler:^(NSError *error) { + NSLog(@"Error: %@", error.description); + }]; + }]; + return; + } + + // We want to upload a photo representing the gesture the player threw, and use it as the + // image for our game OG object. But we optimize this and only upload one instance per session. + // So if we already have the image URL, we use it, otherwise we'll initiate an upload and + // publish the result once it finishes. + if (!photoURLs[_lastPlayerCall]) { + [self publishPhotoForGesture:_lastPlayerCall]; + return; + } + + FBSDKShareOpenGraphObject *game = [self createGameObject]; + FBSDKShareOpenGraphAction *action = [self createPlayActionWithGame:game]; + FBSDKShareOpenGraphContent *content = [[FBSDKShareOpenGraphContent alloc] init]; + content.action = action; + content.previewPropertyName = @"fb_sample_rps:game"; + [FBSDKShareAPI shareWithContent:content delegate:self]; } #pragma mark - FBSDKSharingDelegate -- (void)sharer:(id)sharer didCompleteWithResults:(NSDictionary *)results { - NSLog(@"Posted OG action with id: %@", results[@"postId"]); +- (void)sharer:(id)sharer didCompleteWithResults:(NSDictionary *)results +{ + NSLog(@"Posted OG action with id: %@", results[@"postId"]); } -- (void)sharer:(id)sharer didFailWithError:(NSError *)error { - NSLog(@"Error: %@", error.description); +- (void)sharer:(id)sharer didFailWithError:(NSError *)error +{ + NSLog(@"Error: %@", error.description); } -- (void)sharerDidCancel:(id)sharer { - NSLog(@"Canceled share"); +- (void)sharerDidCancel:(id)sharer +{ + NSLog(@"Canceled share"); } #pragma mark - Logging App Event - (void)logCurrentPlayerCall:(RPSCall)playerCall lastPlayerCall:(RPSCall)lastPlayerCall - lastComputerCall:(RPSCall)lastComputerCall { - // log the user's choice while comparing it against the result of their last throw - if (lastComputerCall != RPSCallNone && lastComputerCall != RPSCallNone) { - RPSResult lastResult = [self resultForPlayerCall:lastPlayerCall - computerCall:lastComputerCall]; - - NSString *transitionalWord = (lastResult == RPSResultWin? @"against" : - lastResult == RPSResultTie? @"with" : @"to"); - NSString *previousResult = [NSString stringWithFormat:@"%@ %@ %@", - kResults[lastResult], - transitionalWord, - callType[lastPlayerCall + 1]]; - [FBSDKAppEvents logEvent:@"Throw Based on Last Result" - parameters:@{callType[playerCall + 1] : previousResult}]; - } + lastComputerCall:(RPSCall)lastComputerCall +{ + // log the user's choice while comparing it against the result of their last throw + if (lastComputerCall != RPSCallNone && lastComputerCall != RPSCallNone) { + RPSResult lastResult = [self resultForPlayerCall:lastPlayerCall + computerCall:lastComputerCall]; + + NSString *transitionalWord = (lastResult == RPSResultWin ? @"against" + : lastResult == RPSResultTie ? @"with" : @"to"); + NSString *previousResult = [NSString stringWithFormat:@"%@ %@ %@", + kResults[lastResult], + transitionalWord, + callType[lastPlayerCall + 1]]; + [FBSDKAppEvents logEvent:@"Throw Based on Last Result" + parameters:@{callType[playerCall + 1] : previousResult}]; + } +} + +- (void)logPlayerCall:(RPSCall)playerCall result:(RPSResult)result timeTaken:(NSTimeInterval)timeTaken +{ + // log the user's choice and the respective result + NSString *playerChoice = callType[playerCall + 1]; + [FBSDKAppEvents logEvent:@"Round End" + valueToSum:timeTaken + parameters:@{@"roundResult" : kResults[result], @"playerChoice" : playerChoice}]; +} + +- (void)logTimeTaken:(NSTimeInterval)timeTaken +{ + // logs the time a user takes to make a choice in a round + NSString *timeTakenStr = (timeTaken < 0.5f ? @"< 0.5s" + : timeTaken < 1.0f ? @"0.5s <= t < 1.0s" + : timeTaken < 1.5f ? @"1.0s <= t < 1.5s" + : timeTaken < 2.0f ? @"1.5s <= t < 2.0s" + : timeTaken < 2.5f ? @"2.0s <= t < 2.5s" : @" >= 2.5s"); + [FBSDKAppEvents logEvent:@"Time Taken" + valueToSum:timeTaken + parameters:@{@"Time Taken" : timeTakenStr}]; } -- (void)logPlayerCall:(RPSCall)playerCall result:(RPSResult)result timeTaken:(NSTimeInterval)timeTaken { - // log the user's choice and the respective result - NSString *playerChoice = callType[playerCall + 1]; - [FBSDKAppEvents logEvent:@"Round End" - valueToSum:timeTaken - parameters:@{@"roundResult": kResults[result], @"playerChoice" : playerChoice}]; -} - -- (void)logTimeTaken:(NSTimeInterval)timeTaken { - // logs the time a user takes to make a choice in a round - NSString *timeTakenStr = (timeTaken < 0.5f? @"< 0.5s" : - timeTaken < 1.0f? @"0.5s <= t < 1.0s" : - timeTaken < 1.5f? @"1.0s <= t < 1.5s" : - timeTaken < 2.0f? @"1.5s <= t < 2.0s" : - timeTaken < 2.5f? @"2.0s <= t < 2.5s" : @" >= 2.5s"); - [FBSDKAppEvents logEvent:@"Time Taken" - valueToSum:timeTaken - parameters:@{@"Time Taken" : timeTakenStr}]; -} @end diff --git a/samples/RPSSample/RPSSample/RPSRootViewController.h b/samples/RPSSample/RPSSample/RPSRootViewController.h index c44eeca179..3fb5f3e341 100644 --- a/samples/RPSSample/RPSSample/RPSRootViewController.h +++ b/samples/RPSSample/RPSSample/RPSRootViewController.h @@ -20,6 +20,6 @@ @interface RPSRootViewController : UIViewController -@property (nonatomic, strong)UITabBarController *tabBarController; +@property (nonatomic, strong) UITabBarController *tabBarController; @end diff --git a/samples/RPSSample/RPSSample/RPSRootViewController.m b/samples/RPSSample/RPSSample/RPSRootViewController.m index bcb17c5dce..d0ddc42b95 100644 --- a/samples/RPSSample/RPSSample/RPSRootViewController.m +++ b/samples/RPSSample/RPSSample/RPSRootViewController.m @@ -25,26 +25,26 @@ @implementation RPSRootViewController - (void)viewDidLoad { - [super viewDidLoad]; - self.tabBarController = [[UITabBarController alloc] init]; - - UIViewController *gameViewController; - if ([[UIDevice currentDevice] userInterfaceIdiom] == UIUserInterfaceIdiomPhone) { - gameViewController = [[RPSGameViewController alloc] initWithNibName:@"RPSGameViewController_iPhone" bundle:nil]; - } else { - gameViewController = [[RPSGameViewController alloc] initWithNibName:@"RPSGameViewController_iPad" bundle:nil]; - } - - UINavigationController *gameNavigationController = [[UINavigationController alloc] initWithRootViewController:gameViewController]; - gameViewController.tabBarItem = [[UITabBarItem alloc] initWithTitle:@"Game" image:[UIImage imageNamed:@"game.png"] tag:0]; - - UIViewController *toolViewController = [[RPSAutoAppLinkDebugTool alloc] init]; - UINavigationController *toolNavigationController = [[UINavigationController alloc] initWithRootViewController:toolViewController]; - toolViewController.tabBarItem = [[UITabBarItem alloc] initWithTitle:@"Auto Applink Debug Tool" image:[UIImage imageNamed:@"tool.png"] tag:1]; - toolNavigationController.navigationBar.topItem.title = @"Debug Tool"; - - self.tabBarController.viewControllers = @[gameNavigationController, toolNavigationController]; - [self.view addSubview:self.tabBarController.view]; + [super viewDidLoad]; + self.tabBarController = [[UITabBarController alloc] init]; + + UIViewController *gameViewController; + if ([[UIDevice currentDevice] userInterfaceIdiom] == UIUserInterfaceIdiomPhone) { + gameViewController = [[RPSGameViewController alloc] initWithNibName:@"RPSGameViewController_iPhone" bundle:nil]; + } else { + gameViewController = [[RPSGameViewController alloc] initWithNibName:@"RPSGameViewController_iPad" bundle:nil]; + } + + UINavigationController *gameNavigationController = [[UINavigationController alloc] initWithRootViewController:gameViewController]; + gameViewController.tabBarItem = [[UITabBarItem alloc] initWithTitle:@"Game" image:[UIImage imageNamed:@"game.png"] tag:0]; + + UIViewController *toolViewController = [[RPSAutoAppLinkDebugTool alloc] init]; + UINavigationController *toolNavigationController = [[UINavigationController alloc] initWithRootViewController:toolViewController]; + toolViewController.tabBarItem = [[UITabBarItem alloc] initWithTitle:@"Auto Applink Debug Tool" image:[UIImage imageNamed:@"tool.png"] tag:1]; + toolNavigationController.navigationBar.topItem.title = @"Debug Tool"; + + self.tabBarController.viewControllers = @[gameNavigationController, toolNavigationController]; + [self.view addSubview:self.tabBarController.view]; } @end diff --git a/samples/RPSSample/RPSSample/main.m b/samples/RPSSample/RPSSample/main.m index 0cbabe53ba..4c15bcbc14 100644 --- a/samples/RPSSample/RPSSample/main.m +++ b/samples/RPSSample/RPSSample/main.m @@ -22,7 +22,7 @@ int main(int argc, char *argv[]) { - @autoreleasepool { - return UIApplicationMain(argc, argv, nil, NSStringFromClass([RPSAppDelegate class])); - } + @autoreleasepool { + return UIApplicationMain(argc, argv, nil, NSStringFromClass([RPSAppDelegate class])); + } } diff --git a/scripts/carthage.sh b/scripts/carthage.sh new file mode 100755 index 0000000000..8cf6495c77 --- /dev/null +++ b/scripts/carthage.sh @@ -0,0 +1,8 @@ +#!/bin/bash +# carthage.sh + +# shellcheck disable=SC1091 +# shellcheck source=exclude-architectures.sh +. "$(dirname "$0")/exclude-architectures.sh" + +"$CARTHAGE_BIN_PATH" "$@" diff --git a/scripts/exclude-architectures.sh b/scripts/exclude-architectures.sh new file mode 100755 index 0000000000..6b150aa3b1 --- /dev/null +++ b/scripts/exclude-architectures.sh @@ -0,0 +1,26 @@ +#!/bin/bash +# carthage.sh +# Xcode 12 workaround. +# See: https://github.com/Carthage/Carthage/blob/master/Documentation/Xcode12Workaround.md +# +# Usage example: ./carthage.sh build --platform iOS + +set -euo pipefail + +xcconfig=$(mktemp /tmp/static.xcconfig.XXXXXX) +trap 'rm -f "$xcconfig"' INT TERM HUP EXIT + +# For Xcode 12 make sure EXCLUDED_ARCHS is set to arm architectures otherwise +# the build will fail on lipo due to duplicate architectures. + +CURRENT_XCODE_VERSION=$(xcodebuild -version | grep "Build version" | cut -d' ' -f3) + +# Disabling check since Xcode will be expanding vars +# shellcheck disable=SC2016 +{ + echo "EXCLUDED_ARCHS__EFFECTIVE_PLATFORM_SUFFIX_simulator__NATIVE_ARCH_64_BIT_x86_64__XCODE_1200__BUILD_$CURRENT_XCODE_VERSION = arm64 arm64e armv7 armv7s armv6 armv8 i386" + echo 'EXCLUDED_ARCHS__EFFECTIVE_PLATFORM_SUFFIX_simulator__NATIVE_ARCH_64_BIT_x86_64__XCODE_1200 = $(EXCLUDED_ARCHS__EFFECTIVE_PLATFORM_SUFFIX_simulator__NATIVE_ARCH_64_BIT_x86_64__XCODE_1200__BUILD_$(XCODE_PRODUCT_BUILD_VERSION))' + echo 'EXCLUDED_ARCHS = $(inherited) $(EXCLUDED_ARCHS__EFFECTIVE_PLATFORM_SUFFIX_$(EFFECTIVE_PLATFORM_SUFFIX)__NATIVE_ARCH_64_BIT_$(NATIVE_ARCH_64_BIT)__XCODE_$(XCODE_VERSION_MAJOR))' +} >> "$xcconfig" + +export XCODE_XCCONFIG_FILE="$xcconfig" diff --git a/scripts/run.sh b/scripts/run.sh index 21ea3cebda..38f544c206 100755 --- a/scripts/run.sh +++ b/scripts/run.sh @@ -74,7 +74,7 @@ main() { SDK_VERSION_FILES=( "Configurations/Version.xcconfig" "FBSDKCoreKit/FBSDKCoreKit/FBSDKCoreKit.h" - "FBSDKCoreKit/FBSDKCoreKit/Basics/Instrument/FBSDKCrashHandler.m" + "Sources/FBSDKCoreKit_Basics/FBSDKCrashHandler.m" ) SDK_GRAPH_API_VERSION_FILES=( @@ -287,10 +287,10 @@ build_sdk() { } build_carthage() { - carthage build --no-skip-current + CARTHAGE_BIN_PATH=$( which carthage ) sh scripts/carthage.sh build --no-skip-current if [ "${1:-}" == "--archive" ]; then - carthage archive --output Carthage/Release/ + CARTHAGE_BIN_PATH=$( which carthage ) sh scripts/carthage.sh archive --output Carthage/Release/ fi } @@ -434,15 +434,15 @@ release_sdk() { # Release frameworks in dynamic (mostly for Carthage) release_dynamic() { - carthage build --no-skip-current - carthage archive --output build/Release/ + CARTHAGE_BIN_PATH=$( which carthage ) sh scripts/carthage.sh build --no-skip-current + CARTHAGE_BIN_PATH=$( which carthage ) sh scripts/carthage.sh archive --output build/Release/ mv build/Release/FBSDKCoreKit.framework.zip build/Release/FacebookSDK_Dynamic.framework.zip } # Release frameworks in static release_static() { release_basics() { - xcodebuild build \ + xcodebuild clean build \ -workspace FacebookSDK.xcworkspace \ -scheme BuildCoreKitBasics \ -configuration Release | xcpretty @@ -461,12 +461,12 @@ release_sdk() { cd .. } - xcodebuild build \ + xcodebuild clean build \ -workspace FacebookSDK.xcworkspace \ -scheme BuildAllKits \ -configuration Release | xcpretty - xcodebuild build \ + xcodebuild clean build \ -workspace FacebookSDK.xcworkspace \ -scheme BuildAllKits_TV \ -configuration Release | xcpretty @@ -522,6 +522,8 @@ release_sdk() { release_docs() { for kit in "${SDK_KITS[@]}"; do + rm -rf "$kit/build" || true + ruby "$SDK_SCRIPTS_DIR"/genDocs.rb "$kit" # Zip the result so it can be uploaded easily diff --git a/scripts/xcode/build-universal-framework.sh b/scripts/xcode/build-universal-framework.sh index 648bc3175c..c3c774ef3c 100755 --- a/scripts/xcode/build-universal-framework.sh +++ b/scripts/xcode/build-universal-framework.sh @@ -21,6 +21,10 @@ # Main Script # -------------- +# shellcheck disable=SC1091 +# shellcheck source=exclude-architectures.sh +. "${SOURCE_ROOT}/../scripts/exclude-architectures.sh" + UNIVERSAL_BUILD_FOLDER=../build/ # make the output directory and delete the framework directory @@ -52,7 +56,7 @@ cp -R "${BUILD_DIR}/${CONFIGURATION}-iphoneos/${PROJECT_NAME}.framework" "${UNIV # Step 3. Copy the swiftmodule files created during the simulator build rsync -a "${BUILD_DIR}/${CONFIGURATION}-iphonesimulator/${PROJECT_NAME}.framework/Modules/${PROJECT_NAME}.swiftmodule/" \ - "${UNIVERSAL_BUILD_FOLDER}/${PROJECT_NAME}.framework/Modules/${PROJECT_NAME}.swiftmodule" + "${UNIVERSAL_BUILD_FOLDER}/${PROJECT_NAME}.framework/Modules/${PROJECT_NAME}.swiftmodule" || true # Step 4. Create universal binary file using lipo and place the combined executable in the copied framework directory lipo -create -output "${UNIVERSAL_BUILD_FOLDER}/${PROJECT_NAME}.framework/${PROJECT_NAME}" "${BUILD_DIR}/${CONFIGURATION}-iphonesimulator/${PROJECT_NAME}.framework/${PROJECT_NAME}" "${BUILD_DIR}/${CONFIGURATION}-iphoneos/${PROJECT_NAME}.framework/${PROJECT_NAME}" diff --git a/scripts/xcode/build-universal-tvos-framework.sh b/scripts/xcode/build-universal-tvos-framework.sh index d4768edd9f..4f43bdff1d 100755 --- a/scripts/xcode/build-universal-tvos-framework.sh +++ b/scripts/xcode/build-universal-tvos-framework.sh @@ -21,6 +21,10 @@ # Main Script # -------------- +# shellcheck disable=SC1091 +# shellcheck source=exclude-architectures.sh +. "${SOURCE_ROOT}/../scripts/exclude-architectures.sh" + UNIVERSAL_TV_BUILD_FOLDER=../build/tv/ # make the output directory and delete the framework directory diff --git a/testing/TestXcodeIntegration/TestXcodeIntegration/AppDelegate.swift b/testing/TestXcodeIntegration/TestXcodeIntegration/AppDelegate.swift index 01c7535b5d..8d4867f2f9 100644 --- a/testing/TestXcodeIntegration/TestXcodeIntegration/AppDelegate.swift +++ b/testing/TestXcodeIntegration/TestXcodeIntegration/AppDelegate.swift @@ -19,15 +19,13 @@ import UIKit import FBSDKCoreKit +import FBSDKGamingServicesKit import FBSDKLoginKit import FBSDKShareKit -import FBSDKGamingServicesKit @UIApplicationMain class AppDelegate: UIResponder, UIApplicationDelegate { - - func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool { // Override point for customization after application launch. return true @@ -46,6 +44,4 @@ class AppDelegate: UIResponder, UIApplicationDelegate { // If any sessions were discarded while the application was not running, this will be called shortly after application:didFinishLaunchingWithOptions. // Use this method to release any resources that were specific to the discarded scenes, as they will not return. } - - } diff --git a/testing/TestXcodeIntegration/TestXcodeIntegration/SceneDelegate.swift b/testing/TestXcodeIntegration/TestXcodeIntegration/SceneDelegate.swift index 5fd28c2a78..ab03ec700d 100644 --- a/testing/TestXcodeIntegration/TestXcodeIntegration/SceneDelegate.swift +++ b/testing/TestXcodeIntegration/TestXcodeIntegration/SceneDelegate.swift @@ -22,7 +22,6 @@ class SceneDelegate: UIResponder, UIWindowSceneDelegate { var window: UIWindow? - func scene(_ scene: UIScene, willConnectTo session: UISceneSession, options connectionOptions: UIScene.ConnectionOptions) { // Use this method to optionally configure and attach the UIWindow `window` to the provided UIWindowScene `scene`. // If using a storyboard, the `window` property will automatically be initialized and attached to the scene. @@ -57,6 +56,4 @@ class SceneDelegate: UIResponder, UIWindowSceneDelegate { // Use this method to save data, release shared resources, and store enough scene-specific state information // to restore the scene back to its current state. } - - }