diff --git a/.gitignore b/.gitignore new file mode 100644 index 00000000..56880a0f --- /dev/null +++ b/.gitignore @@ -0,0 +1,37 @@ +## OS X +.DS_Store + +## Build generated +build/ +DerivedData + +## Various settings +*.pbxuser +!default.pbxuser +*.mode1v3 +!default.mode1v3 +*.mode2v3 +!default.mode2v3 +*.perspectivev3 +!default.perspectivev3 +xcuserdata + +## Other +*.xccheckout +*.moved-aside +*.xcuserstate +*.xcscmblueprint + +## Obj-C/Swift specific +*.hmap +*.ipa + +## Dependency Managers +Pods/ +Carthage/Build + +## AppCode +.idea/ + +## Ignore Docs Folder +.docs diff --git a/.gitmodules b/.gitmodules new file mode 100644 index 00000000..47b61e54 --- /dev/null +++ b/.gitmodules @@ -0,0 +1,9 @@ +[submodule "Carthage/Checkouts/Bolts-ObjC"] + path = Carthage/Checkouts/Bolts-ObjC + url = https://github.com/BoltsFramework/Bolts-ObjC.git +[submodule "Carthage/Checkouts/facebook-ios-sdk"] + path = Carthage/Checkouts/facebook-ios-sdk + url = https://github.com/facebook/facebook-ios-sdk.git +[submodule "Vendor/xctoolchain"] + path = Vendor/xctoolchain + url = https://github.com/ParsePlatform/xctoolchain.git diff --git a/.jazzy.json b/.jazzy.json new file mode 100644 index 00000000..61ca639f --- /dev/null +++ b/.jazzy.json @@ -0,0 +1,137 @@ +{ + "author": "Facebook", + "author_url": "https://github.com/facebook/facebook-sdk-swift", + "custom_categories": [ + { + "children": [ + "AccessToken", + "UserProfile", + "UserProfilePictureView", + "ApplicationDelegate", + "SDKSettings", + "SDKLoggingBehavior", + "SDKVersion" + ], + "name": "Common" + }, + { + "children": [ + "AppEvent", + "AppEventName", + "AppEventParameterName", + "AppEventsLogger", + "AppEventLoggable", + "AppEventParameterValueType" + ], + "name": "App Events" + }, + { + "children": [ + "LoginManager", + "LoginButton", + "LoginButtonDelegate", + "LoginBehavior", + "LoginDefaultAudience", + "LoginResult" + ], + "name": "Facebook Login" + }, + { + "children": [ + "Permission", + "ReadPermission", + "PublishPermission" + ], + "name": "Facebook Permissions" + }, + { + "children": [ + "GraphRequest", + "GraphRequestProtocol", + "GraphRequestResult", + "GraphRequestHTTPMethod", + "GraphResponse", + "GraphResponseProtocol", + "GraphRequestConnection", + "GraphRequestDataAttachment" + ], + "name": "Graph API" + }, + { + "children": [ + "ShareButton", + "SendButton", + "LikeButton", + "LikeControl", + "LikableObject" + ], + "name": "Sharing Buttons" + }, + { + "children": [ + "ShareDialog", + "ShareDialogMode", + "MessageDialog", + "ContentSharingDialogProtocol" + ], + "name": "Sharing Dialogs" + }, + { + "children": [ + "LinkShareContent", + "Photo", + "PhotoShareContent", + "Video", + "VideoShareContent", + "Hashtag", + "ContentProtocol", + "ContentSharingResultProtocol", + "PostSharingResult" + ], + "name": "Shareable Content" + }, + { + "children": [ + "OpenGraphObject", + "OpenGraphAction", + "OpenGraphShareContent", + "OpenGraphPropertyName", + "OpenGraphPropertyValue", + "OpenGraphPropertyContaining" + ], + "name": "Share Custom Open Graph Stories" + }, + { + "children": [ + "GraphSharer", + "ShareError", + "ContentSharerResult", + "ContentSharingProtocol" + ], + "name": "Sharing via Graph API" + }, + { + "children": [ + "AppInvite" + ], + "name": "App Invites" + }, + { + "children": [ + "GameRequest" + ], + "name": "Game Requests" + }, + { + "children": [ + "==(_:_:)" + ], + "name": "Equality Helpers" + } + ], + "hide_documentation_coverage": true, + "module": "Facebook Swift SDK", + "readme": "README.md", + "skip_undocumented": true, + "theme": "fullwidth" +} diff --git a/.ruby-version b/.ruby-version new file mode 100644 index 00000000..2bf1c1cc --- /dev/null +++ b/.ruby-version @@ -0,0 +1 @@ +2.3.1 diff --git a/.swiftlint.yml b/.swiftlint.yml new file mode 100644 index 00000000..2fe6603c --- /dev/null +++ b/.swiftlint.yml @@ -0,0 +1,11 @@ +line_length: 140 +file_length: 1000 +type_body_length: 500 +opt_in_rules: + - empty_count + - missing_docs +disabled_rules: + - cyclomatic_complexity + - conditional_binding_cascade +excluded: + - Pods diff --git a/.travis.yml b/.travis.yml new file mode 100644 index 00000000..81b6252c --- /dev/null +++ b/.travis.yml @@ -0,0 +1,46 @@ +branches: + only: + - master +language: objective-c +os: osx +osx_image: xcode7.3 +cache: + - cocoapods +env: + matrix: + - TEST_TYPE=iOS + - TEST_TYPE=Lint + - TEST_TYPE=Samples + - TEST_TYPE=CocoaPods +before_install: +- | + if [ "$TEST_TYPE" = Lint ] || [ "$TEST_TYPE" = Carthage ]; then + brew update + fi +install: +- | + gem update --system --no-document + bundle install + if [ "$TEST_TYPE" = Lint ]; then + brew install swiftlint || brew upgrade swiftlint + elif [ "$TEST_TYPE" = CocoaPods ]; then + pod repo update --silent + fi +script: +- | + if [ "$TEST_TYPE" = iOS ]; then + set -o pipefail + xcodebuild build -workspace FacebookSwift.xcworkspace -sdk iphonesimulator -scheme FacebookCore -configuration Debug -destination "platform=iOS Simulator,name=iPhone 4s" -destination "platform=iOS Simulator,name=iPhone 6 Plus" | xcpretty -c + xcodebuild build -workspace FacebookSwift.xcworkspace -sdk iphonesimulator -scheme FacebookLogin -configuration Debug -destination "platform=iOS Simulator,name=iPhone 4s" -destination "platform=iOS Simulator,name=iPhone 6 Plus" | xcpretty -c + xcodebuild build -workspace FacebookSwift.xcworkspace -sdk iphonesimulator -scheme FacebookShare -configuration Debug -destination "platform=iOS Simulator,name=iPhone 4s" -destination "platform=iOS Simulator,name=iPhone 6 Plus" | xcpretty -c + elif [ "$TEST_TYPE" = Lint ]; then + swiftlint lint --path Sources --quiet + swiftlint lint --path Samples --quiet + elif [ "$TEST_TYPE" = Samples ]; then + set -o pipefail + xcodebuild build -workspace FacebookSwift.xcworkspace -sdk iphonesimulator -scheme SwiftCatalog -configuration Debug -destination "platform=iOS Simulator,name=iPhone 4s" -destination "platform=iOS Simulator,name=iPhone 6 Plus" | xcpretty -c + elif [ "$TEST_TYPE" = CocoaPods ]; then + #pod lib lint FacebookCore.podspec + #pod lib lint FacebookLogin.podspec + #pod lib lint FacebookShare.podspec + fi diff --git a/CHANGELOG.md b/CHANGELOG.md new file mode 100644 index 00000000..f216efca --- /dev/null +++ b/CHANGELOG.md @@ -0,0 +1,5 @@ +# Change Log + +## [0.1.0](https://github.com/facebook/Facebook-SDK-Swift/tree/0.1.0) + +\* *This Change Log was automatically generated by [github_changelog_generator](https://github.com/skywinder/Github-Changelog-Generator) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md new file mode 100644 index 00000000..bcd69c05 --- /dev/null +++ b/CONTRIBUTING.md @@ -0,0 +1,33 @@ +# Contributing to Facebook Swift SDK +We want to make contributing to this project as easy and transparent as possible. + +## Pull Requests +We actively welcome your pull requests. + +1. Fork the repo and create your branch from `master`. +2. If you've added code that should be tested, add tests. +3. If you've changed APIs, update the documentation. +4. Ensure the test suite passes. +5. Make sure your code lints. +6. If you haven't already, complete the Contributor License Agreement ("CLA"). + +## Contributor License Agreement ("CLA") +In order to accept your pull request, we need you to submit a CLA. You only need +to do this once to work on any of Facebook's open source projects. + +Complete your CLA here: + +## Issues +We use GitHub issues to track public bugs. Please ensure your description is +clear and has sufficient instructions to be able to reproduce the issue. + +Facebook has a [bounty program](https://www.facebook.com/whitehat/) for the safe +disclosure of security bugs. In those cases, please go through the process +outlined on that page and do not file a public issue. + +## Coding Style +* Most importantly, match the existing code style as much as possible. +* Try to keep lines under 140 characters, if possible. + +## License +See the `LICENSE` file. diff --git a/Cartfile b/Cartfile new file mode 100644 index 00000000..bd89cb0a --- /dev/null +++ b/Cartfile @@ -0,0 +1 @@ +github "facebook/facebook-ios-sdk" ~> 4.14 diff --git a/Cartfile.resolved b/Cartfile.resolved new file mode 100644 index 00000000..4c098a37 --- /dev/null +++ b/Cartfile.resolved @@ -0,0 +1,2 @@ +github "BoltsFramework/Bolts-ObjC" "1.8.4" +github "facebook/facebook-ios-sdk" "sdk-version-4.14.0" diff --git a/Carthage/Checkouts/Bolts-ObjC b/Carthage/Checkouts/Bolts-ObjC new file mode 160000 index 00000000..e64deecb --- /dev/null +++ b/Carthage/Checkouts/Bolts-ObjC @@ -0,0 +1 @@ +Subproject commit e64deecb2f0e10ac0dbb71f522c7a5b9cafb0b4d diff --git a/Carthage/Checkouts/facebook-ios-sdk b/Carthage/Checkouts/facebook-ios-sdk new file mode 160000 index 00000000..bc053d6b --- /dev/null +++ b/Carthage/Checkouts/facebook-ios-sdk @@ -0,0 +1 @@ +Subproject commit bc053d6b234f1f79ba8ba592d6bbd9bf68e46b1f diff --git a/Configurations/FacebookCore.xcconfig b/Configurations/FacebookCore.xcconfig new file mode 100644 index 00000000..26d4b7cd --- /dev/null +++ b/Configurations/FacebookCore.xcconfig @@ -0,0 +1,33 @@ +// 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. + +#include "Shared/Platform/iOS.xcconfig" +#include "Shared/Product/DynamicFramework.xcconfig" +#include "Version.xcconfig" + +PRODUCT_NAME = FacebookCore +PRODUCT_BUNDLE_IDENTIFIER = com.facebook.swift.core + +SWIFT_VERSION = 2.3 + +IPHONEOS_DEPLOYMENT_TARGET = 8.0 + +INFOPLIST_FILE = $(SRCROOT)/Resources/Info.plist + +// Enable testability to make internal things visible with @testable import +ENABLE_TESTABILITY = YES diff --git a/Configurations/FacebookLogin.xcconfig b/Configurations/FacebookLogin.xcconfig new file mode 100644 index 00000000..08616564 --- /dev/null +++ b/Configurations/FacebookLogin.xcconfig @@ -0,0 +1,30 @@ +// 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. + +#include "Shared/Platform/iOS.xcconfig" +#include "Shared/Product/DynamicFramework.xcconfig" +#include "Version.xcconfig" + +PRODUCT_NAME = FacebookLogin +PRODUCT_BUNDLE_IDENTIFIER = com.facebook.swift.login + +SWIFT_VERSION = 2.3 + +IPHONEOS_DEPLOYMENT_TARGET = 8.0 + +INFOPLIST_FILE = $(SRCROOT)/Resources/Info.plist diff --git a/Configurations/FacebookShare.xcconfig b/Configurations/FacebookShare.xcconfig new file mode 100644 index 00000000..4e78a8e0 --- /dev/null +++ b/Configurations/FacebookShare.xcconfig @@ -0,0 +1,30 @@ +// 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. + +#include "Shared/Platform/iOS.xcconfig" +#include "Shared/Product/DynamicFramework.xcconfig" +#include "Version.xcconfig" + +PRODUCT_NAME = FacebookShare +PRODUCT_BUNDLE_IDENTIFIER = com.facebook.swift.share + +SWIFT_VERSION = 2.3 + +IPHONEOS_DEPLOYMENT_TARGET = 8.0 + +INFOPLIST_FILE = $(SRCROOT)/Resources/Info.plist diff --git a/Configurations/Shared b/Configurations/Shared new file mode 120000 index 00000000..177da2bb --- /dev/null +++ b/Configurations/Shared @@ -0,0 +1 @@ +../Vendor/xctoolchain/Configurations \ No newline at end of file diff --git a/Configurations/Version.xcconfig b/Configurations/Version.xcconfig new file mode 100644 index 00000000..08c848d1 --- /dev/null +++ b/Configurations/Version.xcconfig @@ -0,0 +1,19 @@ +// 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. + +FBSDK_SWIFT_VERSION = 0.1.0 diff --git a/FacebookCore.podspec b/FacebookCore.podspec new file mode 100644 index 00000000..634d8d88 --- /dev/null +++ b/FacebookCore.podspec @@ -0,0 +1,28 @@ +Pod::Spec.new do |s| + s.name = 'FacebookCore' + s.version = '0.1.0' + s.author = 'Facebook' + s.homepage = 'https://developers.facebook.com/docs/swift' + s.license = { :type => 'Facebook Platform License', :file => 'LICENSE' } + + s.summary = "Official Facebook SDK in Swift to access Facebook Platform's core features." + s.description = <<-DESC + The Facebook SDK in Swift Core framework provides: + * App Events (for App Analytics) + * Graph API Access and Error Recovery + * Working with Access Tokens and User Profiles + DESC + + s.source = { :git => 'https://github.com/facebook/facebook-sdk-swift.git', :tag => s.version.to_s } + + s.requires_arc = true + s.platform = :ios + + s.ios.deployment_target = '8.0' + + s.source_files = 'Sources/Core/**/*.swift' + s.module_name = 'FacebookCore' + + s.ios.dependency 'Bolts', '~> 1.8' + s.ios.dependency 'FBSDKCoreKit', '~> 4.14' +end diff --git a/FacebookLogin.podspec b/FacebookLogin.podspec new file mode 100644 index 00000000..3d5c0935 --- /dev/null +++ b/FacebookLogin.podspec @@ -0,0 +1,23 @@ +Pod::Spec.new do |s| + s.name = 'FacebookLogin' + s.version = '0.1.0' + s.author = 'Facebook' + s.homepage = 'https://developers.facebook.com/docs/swift' + s.license = { :type => 'Facebook Platform License', :file => 'LICENSE' } + + s.summary = "Official Facebook SDK in Swift to integrate with Facebook Login." + + s.source = { :git => 'https://github.com/facebook/facebook-sdk-swift.git', :tag => s.version.to_s } + + s.requires_arc = true + s.platform = :ios + + s.ios.deployment_target = '8.0' + + s.source_files = 'Sources/Login/**/*.swift' + s.module_name = 'FacebookLogin' + + s.ios.dependency 'Bolts', '~> 1.8' + s.ios.dependency 'FBSDKCoreKit', '~> 4.14' + s.ios.dependency 'FBSDKLoginKit', '~> 4.14' +end diff --git a/FacebookShare.podspec b/FacebookShare.podspec new file mode 100644 index 00000000..8365836e --- /dev/null +++ b/FacebookShare.podspec @@ -0,0 +1,29 @@ +Pod::Spec.new do |s| + s.name = 'FacebookShare' + s.version = '0.1.0' + s.author = 'Facebook' + s.homepage = 'https://developers.facebook.com/docs/swift' + s.license = { :type => 'Facebook Platform License', :file => 'LICENSE' } + + s.summary = "Official Facebook SDK in Swift to access Facebook Platform's Sharing Features." + s.description = <<-DESC + The Facebook SDK for iOS ShareKit framework provides: + * Share content with Share Dialog and Message Dialog. + * Send Game Requests or App Invites to grow your app. + * Publish content and open graph stories with the Graph API. + DESC + + s.source = { :git => 'https://github.com/facebook/facebook-sdk-swift.git', :tag => s.version.to_s } + + s.requires_arc = true + s.platform = :ios + + s.ios.deployment_target = '8.0' + + s.source_files = 'Sources/Share/**/*.swift' + s.module_name = 'FacebookShare' + + s.ios.dependency 'Bolts', '~> 1.8' + s.ios.dependency 'FBSDKCoreKit', '~> 4.14' + s.ios.dependency 'FBSDKShareKit', '~> 4.14' +end diff --git a/FacebookSwift.xcodeproj/project.pbxproj b/FacebookSwift.xcodeproj/project.pbxproj new file mode 100644 index 00000000..6a158529 --- /dev/null +++ b/FacebookSwift.xcodeproj/project.pbxproj @@ -0,0 +1,1522 @@ +// !$*UTF8*$! +{ + archiveVersion = 1; + classes = { + }; + objectVersion = 46; + objects = { + +/* Begin PBXBuildFile section */ + 810192AE1D01305400B9E881 /* AppEvent.Builtin.swift in Sources */ = {isa = PBXBuildFile; fileRef = 810192A31D01305400B9E881 /* AppEvent.Builtin.swift */; }; + 810192AF1D01305400B9E881 /* AppEvent.swift in Sources */ = {isa = PBXBuildFile; fileRef = 810192A41D01305400B9E881 /* AppEvent.swift */; }; + 810192B01D01305400B9E881 /* AppEventName.swift in Sources */ = {isa = PBXBuildFile; fileRef = 810192A51D01305400B9E881 /* AppEventName.swift */; }; + 810192B11D01305400B9E881 /* AppEventParameterName.swift in Sources */ = {isa = PBXBuildFile; fileRef = 810192A61D01305400B9E881 /* AppEventParameterName.swift */; }; + 810192B21D01305400B9E881 /* AppEventsLogger.FlushBehavior.swift in Sources */ = {isa = PBXBuildFile; fileRef = 810192A71D01305400B9E881 /* AppEventsLogger.FlushBehavior.swift */; }; + 810192B31D01305400B9E881 /* AppEventsLogger.swift in Sources */ = {isa = PBXBuildFile; fileRef = 810192A81D01305400B9E881 /* AppEventsLogger.swift */; }; + 810192B41D01305400B9E881 /* AccessToken.swift in Sources */ = {isa = PBXBuildFile; fileRef = 810192AA1D01305400B9E881 /* AccessToken.swift */; }; + 810192B51D01305400B9E881 /* ApplicationDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 810192AB1D01305400B9E881 /* ApplicationDelegate.swift */; }; + 810192B61D01305400B9E881 /* SDKLoggingBehavior.swift in Sources */ = {isa = PBXBuildFile; fileRef = 810192AC1D01305400B9E881 /* SDKLoggingBehavior.swift */; }; + 810192B71D01305400B9E881 /* SDKSettings.swift in Sources */ = {isa = PBXBuildFile; fileRef = 810192AD1D01305400B9E881 /* SDKSettings.swift */; }; + 810192C41D0130C500B9E881 /* GraphRequest.swift in Sources */ = {isa = PBXBuildFile; fileRef = 810192C31D0130C500B9E881 /* GraphRequest.swift */; }; + 810192C81D01380600B9E881 /* GraphRequestConnection.swift in Sources */ = {isa = PBXBuildFile; fileRef = 810192C71D01380600B9E881 /* GraphRequestConnection.swift */; }; + 810192CA1D0139CF00B9E881 /* GraphRequestConnection.Delegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 810192C91D0139CF00B9E881 /* GraphRequestConnection.Delegate.swift */; }; + 810192CC1D013FC100B9E881 /* GraphRequestProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = 810192CB1D013FC100B9E881 /* GraphRequestProtocol.swift */; }; + 810192CE1D01409400B9E881 /* GraphResponse.swift in Sources */ = {isa = PBXBuildFile; fileRef = 810192CD1D01409400B9E881 /* GraphResponse.swift */; }; + 810192D01D0145AE00B9E881 /* GraphResponseProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = 810192CF1D0145AE00B9E881 /* GraphResponseProtocol.swift */; }; + 811C5E4D1CFFD4D100E4A925 /* FacebookCore.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 8115C0D81CFE540D001FF33B /* FacebookCore.framework */; }; + 811C5E561CFFD4F800E4A925 /* FacebookCore.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 8115C0D81CFE540D001FF33B /* FacebookCore.framework */; }; + 811C5E771CFFD5DD00E4A925 /* Dictionary+KeyValueMap.swift in Sources */ = {isa = PBXBuildFile; fileRef = 811C5E751CFFD5DD00E4A925 /* Dictionary+KeyValueMap.swift */; }; + 811C5E781CFFD5DD00E4A925 /* Optional+OnSome.swift in Sources */ = {isa = PBXBuildFile; fileRef = 811C5E761CFFD5DD00E4A925 /* Optional+OnSome.swift */; }; + 813C9B561D1DB16F00288BFA /* LoginButton.swift in Sources */ = {isa = PBXBuildFile; fileRef = 813C9B551D1DB16F00288BFA /* LoginButton.swift */; }; + 813C9BB31D1DB48100288BFA /* LoginButtonDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 813C9BB21D1DB48100288BFA /* LoginButtonDelegate.swift */; }; + 813C9CFA1D1DBFA000288BFA /* LoginButton.Tooltip.swift in Sources */ = {isa = PBXBuildFile; fileRef = 813C9CF91D1DBFA000288BFA /* LoginButton.Tooltip.swift */; }; + 815DEBCC1D024ADD000370CA /* GraphRequestProtocol.Bridge.swift in Sources */ = {isa = PBXBuildFile; fileRef = 815DEBCB1D024ADD000370CA /* GraphRequestProtocol.Bridge.swift */; }; + 816B75A71D07AEB70012AC43 /* GraphRequestDataAttachment.swift in Sources */ = {isa = PBXBuildFile; fileRef = 816B75A61D07AEB70012AC43 /* GraphRequestDataAttachment.swift */; }; + 817021AE1D18DE1500ECE7AC /* UserProfilePictureView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 817021AD1D18DE1500ECE7AC /* UserProfilePictureView.swift */; }; + 817A0A0E1D07CE8A00FD423A /* GraphRequestResult.swift in Sources */ = {isa = PBXBuildFile; fileRef = 817A0A0D1D07CE8A00FD423A /* GraphRequestResult.swift */; }; + 81AB8A871D36D43100066F63 /* FBSDKCoreKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 81AB8A3D1D36D41700066F63 /* FBSDKCoreKit.framework */; }; + 81AB8A941D36D44C00066F63 /* FBSDKCoreKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 81AB8A3D1D36D41700066F63 /* FBSDKCoreKit.framework */; }; + 81AB8A951D36D44C00066F63 /* FBSDKLoginKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 81AB8A4B1D36D41700066F63 /* FBSDKLoginKit.framework */; }; + 81AB8A9F1D36D46100066F63 /* FBSDKCoreKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 81AB8A3D1D36D41700066F63 /* FBSDKCoreKit.framework */; }; + 81AB8AA01D36D46100066F63 /* FBSDKShareKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 81AB8A581D36D41700066F63 /* FBSDKShareKit.framework */; }; + 81AB8B351D36D51B00066F63 /* Bolts.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 81AB8AFF1D36D4AA00066F63 /* Bolts.framework */; }; + 81AB8B3A1D36D56600066F63 /* Bolts.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 81AB8AFD1D36D4AA00066F63 /* Bolts.framework */; }; + 81AB8B3F1D36D57300066F63 /* Bolts.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 81AB8AFD1D36D4AA00066F63 /* Bolts.framework */; }; + 81AC801D1D348A1800EA6664 /* LoginButtonDelegateBridge.swift in Sources */ = {isa = PBXBuildFile; fileRef = 81AC801C1D348A1800EA6664 /* LoginButtonDelegateBridge.swift */; }; + 81D6468A1D2C8D7800690609 /* ContentProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = 81D646521D2C8D7800690609 /* ContentProtocol.swift */; }; + 81D6468B1D2C8D7800690609 /* ContentSharingResultProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = 81D646531D2C8D7800690609 /* ContentSharingResultProtocol.swift */; }; + 81D6468C1D2C8D7800690609 /* Hashtag.swift in Sources */ = {isa = PBXBuildFile; fileRef = 81D646541D2C8D7800690609 /* Hashtag.swift */; }; + 81D6468D1D2C8D7800690609 /* PostSharingResult.swift in Sources */ = {isa = PBXBuildFile; fileRef = 81D646551D2C8D7800690609 /* PostSharingResult.swift */; }; + 81D6468E1D2C8D7800690609 /* LinkShareContent.swift in Sources */ = {isa = PBXBuildFile; fileRef = 81D646571D2C8D7800690609 /* LinkShareContent.swift */; }; + 81D6468F1D2C8D7800690609 /* OpenGraphAction.swift in Sources */ = {isa = PBXBuildFile; fileRef = 81D646591D2C8D7800690609 /* OpenGraphAction.swift */; }; + 81D646901D2C8D7800690609 /* OpenGraphObject.swift in Sources */ = {isa = PBXBuildFile; fileRef = 81D6465A1D2C8D7800690609 /* OpenGraphObject.swift */; }; + 81D646911D2C8D7800690609 /* OpenGraphPropertyContaining.swift in Sources */ = {isa = PBXBuildFile; fileRef = 81D6465B1D2C8D7800690609 /* OpenGraphPropertyContaining.swift */; }; + 81D646921D2C8D7800690609 /* OpenGraphPropertyName.swift in Sources */ = {isa = PBXBuildFile; fileRef = 81D6465C1D2C8D7800690609 /* OpenGraphPropertyName.swift */; }; + 81D646931D2C8D7800690609 /* OpenGraphPropertyValue.swift in Sources */ = {isa = PBXBuildFile; fileRef = 81D6465D1D2C8D7800690609 /* OpenGraphPropertyValue.swift */; }; + 81D646941D2C8D7800690609 /* OpenGraphShareContent.swift in Sources */ = {isa = PBXBuildFile; fileRef = 81D6465E1D2C8D7800690609 /* OpenGraphShareContent.swift */; }; + 81D646951D2C8D7800690609 /* Photo.swift in Sources */ = {isa = PBXBuildFile; fileRef = 81D646601D2C8D7800690609 /* Photo.swift */; }; + 81D646961D2C8D7800690609 /* PhotoShareContent.swift in Sources */ = {isa = PBXBuildFile; fileRef = 81D646611D2C8D7800690609 /* PhotoShareContent.swift */; }; + 81D646971D2C8D7800690609 /* Video.swift in Sources */ = {isa = PBXBuildFile; fileRef = 81D646631D2C8D7800690609 /* Video.swift */; }; + 81D646981D2C8D7800690609 /* VideoShareContent.swift in Sources */ = {isa = PBXBuildFile; fileRef = 81D646641D2C8D7800690609 /* VideoShareContent.swift */; }; + 81D646991D2C8D7800690609 /* ContentSharingProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = 81D646661D2C8D7800690609 /* ContentSharingProtocol.swift */; }; + 81D6469A1D2C8D7800690609 /* GraphSharer.swift in Sources */ = {isa = PBXBuildFile; fileRef = 81D646671D2C8D7800690609 /* GraphSharer.swift */; }; + 81D646A11D2C8D7800690609 /* ContentSharingDialogProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = 81D646701D2C8D7800690609 /* ContentSharingDialogProtocol.swift */; }; + 81D646A21D2C8D7800690609 /* GameRequest.ActionType.swift in Sources */ = {isa = PBXBuildFile; fileRef = 81D646721D2C8D7800690609 /* GameRequest.ActionType.swift */; }; + 81D646A31D2C8D7800690609 /* GameRequest.Dialog.swift in Sources */ = {isa = PBXBuildFile; fileRef = 81D646731D2C8D7800690609 /* GameRequest.Dialog.swift */; }; + 81D646A41D2C8D7800690609 /* GameRequest.Recipient.swift in Sources */ = {isa = PBXBuildFile; fileRef = 81D646741D2C8D7800690609 /* GameRequest.Recipient.swift */; }; + 81D646A51D2C8D7800690609 /* GameRequest.RecipientsFilter.swift in Sources */ = {isa = PBXBuildFile; fileRef = 81D646751D2C8D7800690609 /* GameRequest.RecipientsFilter.swift */; }; + 81D646A61D2C8D7800690609 /* GameRequest.Result.swift in Sources */ = {isa = PBXBuildFile; fileRef = 81D646761D2C8D7800690609 /* GameRequest.Result.swift */; }; + 81D646A71D2C8D7800690609 /* GameRequest.SDKDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 81D646771D2C8D7800690609 /* GameRequest.SDKDelegate.swift */; }; + 81D646A81D2C8D7800690609 /* GameRequest.swift in Sources */ = {isa = PBXBuildFile; fileRef = 81D646781D2C8D7800690609 /* GameRequest.swift */; }; + 81D646A91D2C8D7800690609 /* MessageDialog.swift in Sources */ = {isa = PBXBuildFile; fileRef = 81D6467A1D2C8D7800690609 /* MessageDialog.swift */; }; + 81D646AA1D2C8D7800690609 /* ShareDialog.swift in Sources */ = {isa = PBXBuildFile; fileRef = 81D6467C1D2C8D7800690609 /* ShareDialog.swift */; }; + 81D646AB1D2C8D7800690609 /* ShareDialogMode.swift in Sources */ = {isa = PBXBuildFile; fileRef = 81D6467D1D2C8D7800690609 /* ShareDialogMode.swift */; }; + 81D646AC1D2C8D7800690609 /* Constants.Internal.swift in Sources */ = {isa = PBXBuildFile; fileRef = 81D6467F1D2C8D7800690609 /* Constants.Internal.swift */; }; + 81D646AD1D2C8D7800690609 /* ContentBridger.swift in Sources */ = {isa = PBXBuildFile; fileRef = 81D646801D2C8D7800690609 /* ContentBridger.swift */; }; + 81D646AE1D2C8D7800690609 /* SDKSharingDelegateBridge.swift in Sources */ = {isa = PBXBuildFile; fileRef = 81D646811D2C8D7800690609 /* SDKSharingDelegateBridge.swift */; }; + 81D646AF1D2C8D7800690609 /* ShareError.swift in Sources */ = {isa = PBXBuildFile; fileRef = 81D646821D2C8D7800690609 /* ShareError.swift */; }; + 81D646B01D2C8D7800690609 /* LikableObject.swift in Sources */ = {isa = PBXBuildFile; fileRef = 81D646841D2C8D7800690609 /* LikableObject.swift */; }; + 81D646B11D2C8D7800690609 /* LikeButton.swift in Sources */ = {isa = PBXBuildFile; fileRef = 81D646851D2C8D7800690609 /* LikeButton.swift */; }; + 81D646B21D2C8D7800690609 /* LikeControl.AuxilaryStyle.swift in Sources */ = {isa = PBXBuildFile; fileRef = 81D646861D2C8D7800690609 /* LikeControl.AuxilaryStyle.swift */; }; + 81D646B31D2C8D7800690609 /* LikeControl.swift in Sources */ = {isa = PBXBuildFile; fileRef = 81D646871D2C8D7800690609 /* LikeControl.swift */; }; + 81D646B41D2C8D7800690609 /* SendButton.swift in Sources */ = {isa = PBXBuildFile; fileRef = 81D646881D2C8D7800690609 /* SendButton.swift */; }; + 81D646B51D2C8D7800690609 /* ShareButton.swift in Sources */ = {isa = PBXBuildFile; fileRef = 81D646891D2C8D7800690609 /* ShareButton.swift */; }; + 81FC4B601D064F68003F3A46 /* LoginManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = 81FC4B5F1D064F68003F3A46 /* LoginManager.swift */; }; + 81FC4B731D064F8B003F3A46 /* LoginDefaultAudience.swift in Sources */ = {isa = PBXBuildFile; fileRef = 81FC4B721D064F8B003F3A46 /* LoginDefaultAudience.swift */; }; + 81FC4B7B1D065059003F3A46 /* LoginBehavior.swift in Sources */ = {isa = PBXBuildFile; fileRef = 81FC4B7A1D065059003F3A46 /* LoginBehavior.swift */; }; + 81FC4BB61D0653E4003F3A46 /* LoginResult.swift in Sources */ = {isa = PBXBuildFile; fileRef = 81FC4BB51D0653E4003F3A46 /* LoginResult.swift */; }; + 81FC4C981D067448003F3A46 /* Permission.swift in Sources */ = {isa = PBXBuildFile; fileRef = 81FC4C971D067448003F3A46 /* Permission.swift */; }; + 81FC4CAB1D067490003F3A46 /* ReadPermission.swift in Sources */ = {isa = PBXBuildFile; fileRef = 81FC4CAA1D067490003F3A46 /* ReadPermission.swift */; }; + 81FC4CBB1D0674F4003F3A46 /* PublishPermission.swift in Sources */ = {isa = PBXBuildFile; fileRef = 81FC4CBA1D0674F4003F3A46 /* PublishPermission.swift */; }; + 81FC4EF21D068564003F3A46 /* UserProfile.swift in Sources */ = {isa = PBXBuildFile; fileRef = 81FC4EF11D068564003F3A46 /* UserProfile.swift */; }; + F5FE8BFC1D2DDB1100A0E4CF /* AppInvite.DeliveryMethod.swift in Sources */ = {isa = PBXBuildFile; fileRef = F5FE8BF61D2DDB1100A0E4CF /* AppInvite.DeliveryMethod.swift */; }; + F5FE8BFD1D2DDB1100A0E4CF /* AppInvite.Dialog.swift in Sources */ = {isa = PBXBuildFile; fileRef = F5FE8BF71D2DDB1100A0E4CF /* AppInvite.Dialog.swift */; }; + F5FE8BFE1D2DDB1100A0E4CF /* AppInvite.PromoCode.swift in Sources */ = {isa = PBXBuildFile; fileRef = F5FE8BF81D2DDB1100A0E4CF /* AppInvite.PromoCode.swift */; }; + F5FE8BFF1D2DDB1100A0E4CF /* AppInvite.Result.swift in Sources */ = {isa = PBXBuildFile; fileRef = F5FE8BF91D2DDB1100A0E4CF /* AppInvite.Result.swift */; }; + F5FE8C001D2DDB1100A0E4CF /* AppInvite.SDKDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = F5FE8BFA1D2DDB1100A0E4CF /* AppInvite.SDKDelegate.swift */; }; + F5FE8C011D2DDB1100A0E4CF /* AppInvite.swift in Sources */ = {isa = PBXBuildFile; fileRef = F5FE8BFB1D2DDB1100A0E4CF /* AppInvite.swift */; }; +/* End PBXBuildFile section */ + +/* Begin PBXContainerItemProxy section */ + 811C5E471CFFD4C800E4A925 /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = 8115C0CF1CFE540D001FF33B /* Project object */; + proxyType = 1; + remoteGlobalIDString = 8115C0D71CFE540D001FF33B; + remoteInfo = FacebookCore; + }; + 811C5E541CFFD4F100E4A925 /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = 8115C0CF1CFE540D001FF33B /* Project object */; + proxyType = 1; + remoteGlobalIDString = 8115C0D71CFE540D001FF33B; + remoteInfo = FacebookCore; + }; + 81AB8A3A1D36D41700066F63 /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = 81AB8A091D36D41700066F63 /* FBSDKCoreKit.xcodeproj */; + proxyType = 2; + remoteGlobalIDString = 9D2697471A5DF40700143BFC; + remoteInfo = FBSDKCoreKit; + }; + 81AB8A3C1D36D41700066F63 /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = 81AB8A091D36D41700066F63 /* FBSDKCoreKit.xcodeproj */; + proxyType = 2; + remoteGlobalIDString = 81B71DA31D19C87400933E93; + remoteInfo = "FBSDKCoreKit-Dynamic"; + }; + 81AB8A3E1D36D41700066F63 /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = 81AB8A091D36D41700066F63 /* FBSDKCoreKit.xcodeproj */; + proxyType = 2; + remoteGlobalIDString = 9D2697521A5DF40700143BFC; + remoteInfo = FBSDKCoreKitTests; + }; + 81AB8A401D36D41700066F63 /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = 81AB8A091D36D41700066F63 /* FBSDKCoreKit.xcodeproj */; + proxyType = 2; + remoteGlobalIDString = 9DB0FA731BC1CA71005EB8B1; + remoteInfo = FBSDKCoreKit_TV; + }; + 81AB8A421D36D41700066F63 /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = 81AB8A091D36D41700066F63 /* FBSDKCoreKit.xcodeproj */; + proxyType = 2; + remoteGlobalIDString = 814AC8571D1B528900D61E6C; + remoteInfo = "FBSDKCoreKit_TV-Dynamic"; + }; + 81AB8A481D36D41700066F63 /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = 81AB8A0C1D36D41700066F63 /* FBSDKLoginKit.xcodeproj */; + proxyType = 2; + remoteGlobalIDString = 9D9DB8D91A114E500086167B; + remoteInfo = FBSDKLoginKit; + }; + 81AB8A4A1D36D41700066F63 /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = 81AB8A0C1D36D41700066F63 /* FBSDKLoginKit.xcodeproj */; + proxyType = 2; + remoteGlobalIDString = 818EB4411D1A283100252851; + remoteInfo = "FBSDKLoginKit-Dynamic"; + }; + 81AB8A4C1D36D41700066F63 /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = 81AB8A0C1D36D41700066F63 /* FBSDKLoginKit.xcodeproj */; + proxyType = 2; + remoteGlobalIDString = 9D9DB8E41A114E500086167B; + remoteInfo = FBSDKLoginKitTests; + }; + 81AB8A551D36D41700066F63 /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = 81AB8A0F1D36D41700066F63 /* FBSDKShareKit.xcodeproj */; + proxyType = 2; + remoteGlobalIDString = 9D46C5F01A11E5D800A0DDB6; + remoteInfo = FBSDKShareKit; + }; + 81AB8A571D36D41700066F63 /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = 81AB8A0F1D36D41700066F63 /* FBSDKShareKit.xcodeproj */; + proxyType = 2; + remoteGlobalIDString = 81A07F441D1A2E6A0041A29C; + remoteInfo = "FBSDKShareKit-Dynamic"; + }; + 81AB8A591D36D41700066F63 /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = 81AB8A0F1D36D41700066F63 /* FBSDKShareKit.xcodeproj */; + proxyType = 2; + remoteGlobalIDString = 9D46C5FB1A11E5D800A0DDB6; + remoteInfo = FBSDKShareKitTests; + }; + 81AB8A5B1D36D41700066F63 /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = 81AB8A0F1D36D41700066F63 /* FBSDKShareKit.xcodeproj */; + proxyType = 2; + remoteGlobalIDString = 9D2D99941BC452DE00929E76; + remoteInfo = FBSDKShareKit_TV; + }; + 81AB8A5D1D36D41700066F63 /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = 81AB8A0F1D36D41700066F63 /* FBSDKShareKit.xcodeproj */; + proxyType = 2; + remoteGlobalIDString = 8144D91B1D261D2900C8E4AC; + remoteInfo = "FBSDKShareKit_TV-Dynamic"; + }; + 81AB8A831D36D42500066F63 /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = 81AB8A091D36D41700066F63 /* FBSDKCoreKit.xcodeproj */; + proxyType = 1; + remoteGlobalIDString = 81B71CFC1D19C87400933E93; + remoteInfo = "FBSDKCoreKit-Dynamic"; + }; + 81AB8A8F1D36D43D00066F63 /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = 81AB8A091D36D41700066F63 /* FBSDKCoreKit.xcodeproj */; + proxyType = 1; + remoteGlobalIDString = 81B71CFC1D19C87400933E93; + remoteInfo = "FBSDKCoreKit-Dynamic"; + }; + 81AB8A911D36D43D00066F63 /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = 81AB8A0C1D36D41700066F63 /* FBSDKLoginKit.xcodeproj */; + proxyType = 1; + remoteGlobalIDString = 818EB4211D1A283100252851; + remoteInfo = "FBSDKLoginKit-Dynamic"; + }; + 81AB8A9A1D36D45700066F63 /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = 81AB8A091D36D41700066F63 /* FBSDKCoreKit.xcodeproj */; + proxyType = 1; + remoteGlobalIDString = 81B71CFC1D19C87400933E93; + remoteInfo = "FBSDKCoreKit-Dynamic"; + }; + 81AB8A9C1D36D45700066F63 /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = 81AB8A0F1D36D41700066F63 /* FBSDKShareKit.xcodeproj */; + proxyType = 1; + remoteGlobalIDString = 81A07EDF1D1A2E6A0041A29C; + remoteInfo = "FBSDKShareKit-Dynamic"; + }; + 81AB8AFC1D36D4AA00066F63 /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = 81AB8AEE1D36D4AA00066F63 /* Bolts.xcodeproj */; + proxyType = 2; + remoteGlobalIDString = 81ED94291BE147CF00795F05; + remoteInfo = "Bolts-iOS"; + }; + 81AB8AFE1D36D4AA00066F63 /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = 81AB8AEE1D36D4AA00066F63 /* Bolts.xcodeproj */; + proxyType = 2; + remoteGlobalIDString = 1D5D7DD31BE3CE8200FD67C7; + remoteInfo = "Bolts-iOS-Dynamic"; + }; + 81AB8B001D36D4AA00066F63 /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = 81AB8AEE1D36D4AA00066F63 /* Bolts.xcodeproj */; + proxyType = 2; + remoteGlobalIDString = 81ED946E1BE14B5200795F05; + remoteInfo = "Bolts-OSX"; + }; + 81AB8B021D36D4AA00066F63 /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = 81AB8AEE1D36D4AA00066F63 /* Bolts.xcodeproj */; + proxyType = 2; + remoteGlobalIDString = F5AFCA021BA752750076E927; + remoteInfo = "Bolts-tvOS"; + }; + 81AB8B041D36D4AA00066F63 /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = 81AB8AEE1D36D4AA00066F63 /* Bolts.xcodeproj */; + proxyType = 2; + remoteGlobalIDString = 81E94D6A1C2B8BF200A6291E; + remoteInfo = "Bolts-tvOS-Dynamic"; + }; + 81AB8B061D36D4AA00066F63 /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = 81AB8AEE1D36D4AA00066F63 /* Bolts.xcodeproj */; + proxyType = 2; + remoteGlobalIDString = 8178F99C1BB0F87700AD289D; + remoteInfo = "Bolts-watchOS"; + }; + 81AB8B081D36D4AA00066F63 /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = 81AB8AEE1D36D4AA00066F63 /* Bolts.xcodeproj */; + proxyType = 2; + remoteGlobalIDString = 819573F11C2B8ECB00BFCA39; + remoteInfo = "Bolts-watchOS-Dynamic"; + }; + 81AB8B0A1D36D4AA00066F63 /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = 81AB8AEE1D36D4AA00066F63 /* Bolts.xcodeproj */; + proxyType = 2; + remoteGlobalIDString = 8E8C8EE917F23D1D00E3F1C7; + remoteInfo = "BoltsTests-iOS"; + }; + 81AB8B0C1D36D4AA00066F63 /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = 81AB8AEE1D36D4AA00066F63 /* Bolts.xcodeproj */; + proxyType = 2; + remoteGlobalIDString = 8E8C8F1917F241DA00E3F1C7; + remoteInfo = "BoltsTests-OSX"; + }; + 81AB8B0E1D36D4AA00066F63 /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = 81AB8AEE1D36D4AA00066F63 /* Bolts.xcodeproj */; + proxyType = 2; + remoteGlobalIDString = F5AFCA131BA752770076E927; + remoteInfo = "BoltsTests-tvOS"; + }; + 81AB8B101D36D4AA00066F63 /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = 81AB8AEE1D36D4AA00066F63 /* Bolts.xcodeproj */; + proxyType = 2; + remoteGlobalIDString = 1EC3016018CDAA8400D06D07; + remoteInfo = BoltsTestUI; + }; + 81AB8B331D36D51700066F63 /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = 81AB8AEE1D36D4AA00066F63 /* Bolts.xcodeproj */; + proxyType = 1; + remoteGlobalIDString = 1D5D7DA61BE3CE8200FD67C7; + remoteInfo = "Bolts-iOS-Dynamic"; + }; + 81AB8B381D36D56200066F63 /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = 81AB8AEE1D36D4AA00066F63 /* Bolts.xcodeproj */; + proxyType = 1; + remoteGlobalIDString = 1D5D7DA61BE3CE8200FD67C7; + remoteInfo = "Bolts-iOS-Dynamic"; + }; + 81AB8B3D1D36D56E00066F63 /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = 81AB8AEE1D36D4AA00066F63 /* Bolts.xcodeproj */; + proxyType = 1; + remoteGlobalIDString = 1D5D7DA61BE3CE8200FD67C7; + remoteInfo = "Bolts-iOS-Dynamic"; + }; +/* End PBXContainerItemProxy section */ + +/* Begin PBXFileReference section */ + 810192A31D01305400B9E881 /* AppEvent.Builtin.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AppEvent.Builtin.swift; sourceTree = ""; }; + 810192A41D01305400B9E881 /* AppEvent.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AppEvent.swift; sourceTree = ""; }; + 810192A51D01305400B9E881 /* AppEventName.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AppEventName.swift; sourceTree = ""; }; + 810192A61D01305400B9E881 /* AppEventParameterName.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AppEventParameterName.swift; sourceTree = ""; }; + 810192A71D01305400B9E881 /* AppEventsLogger.FlushBehavior.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AppEventsLogger.FlushBehavior.swift; sourceTree = ""; }; + 810192A81D01305400B9E881 /* AppEventsLogger.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AppEventsLogger.swift; sourceTree = ""; }; + 810192AA1D01305400B9E881 /* AccessToken.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AccessToken.swift; sourceTree = ""; }; + 810192AB1D01305400B9E881 /* ApplicationDelegate.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ApplicationDelegate.swift; sourceTree = ""; }; + 810192AC1D01305400B9E881 /* SDKLoggingBehavior.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SDKLoggingBehavior.swift; sourceTree = ""; }; + 810192AD1D01305400B9E881 /* SDKSettings.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SDKSettings.swift; sourceTree = ""; }; + 810192C31D0130C500B9E881 /* GraphRequest.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = GraphRequest.swift; sourceTree = ""; }; + 810192C71D01380600B9E881 /* GraphRequestConnection.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = GraphRequestConnection.swift; sourceTree = ""; }; + 810192C91D0139CF00B9E881 /* GraphRequestConnection.Delegate.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = GraphRequestConnection.Delegate.swift; sourceTree = ""; }; + 810192CB1D013FC100B9E881 /* GraphRequestProtocol.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = GraphRequestProtocol.swift; sourceTree = ""; }; + 810192CD1D01409400B9E881 /* GraphResponse.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = GraphResponse.swift; sourceTree = ""; }; + 810192CF1D0145AE00B9E881 /* GraphResponseProtocol.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = GraphResponseProtocol.swift; sourceTree = ""; }; + 8115C0D81CFE540D001FF33B /* FacebookCore.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = FacebookCore.framework; sourceTree = BUILT_PRODUCTS_DIR; }; + 8115C0E41CFE543A001FF33B /* Info.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; + 811AD1B51D35D9C8007689DC /* Version.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = Version.xcconfig; sourceTree = ""; }; + 811C5DFD1CFFD36600E4A925 /* FacebookLogin.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = FacebookLogin.framework; sourceTree = BUILT_PRODUCTS_DIR; }; + 811C5E0A1CFFD36D00E4A925 /* FacebookShare.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = FacebookShare.framework; sourceTree = BUILT_PRODUCTS_DIR; }; + 811C5E751CFFD5DD00E4A925 /* Dictionary+KeyValueMap.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "Dictionary+KeyValueMap.swift"; sourceTree = ""; }; + 811C5E761CFFD5DD00E4A925 /* Optional+OnSome.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "Optional+OnSome.swift"; sourceTree = ""; }; + 813C9B551D1DB16F00288BFA /* LoginButton.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = LoginButton.swift; sourceTree = ""; }; + 813C9BB21D1DB48100288BFA /* LoginButtonDelegate.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = LoginButtonDelegate.swift; sourceTree = ""; }; + 813C9CF91D1DBFA000288BFA /* LoginButton.Tooltip.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = LoginButton.Tooltip.swift; sourceTree = ""; }; + 815DEBCB1D024ADD000370CA /* GraphRequestProtocol.Bridge.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = GraphRequestProtocol.Bridge.swift; sourceTree = ""; }; + 81654DC41D356E9A006401F1 /* FacebookCore.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = FacebookCore.xcconfig; sourceTree = ""; }; + 81654DC51D356E9A006401F1 /* FacebookLogin.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = FacebookLogin.xcconfig; sourceTree = ""; }; + 81654DC61D356E9A006401F1 /* FacebookShare.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = FacebookShare.xcconfig; sourceTree = ""; }; + 81654DC81D356E9A006401F1 /* Common.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = Common.xcconfig; sourceTree = ""; }; + 81654DCA1D356E9A006401F1 /* iOS.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = iOS.xcconfig; sourceTree = ""; }; + 81654DCB1D356E9A006401F1 /* macOS.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = macOS.xcconfig; sourceTree = ""; }; + 81654DCC1D356E9A006401F1 /* tvOS.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = tvOS.xcconfig; sourceTree = ""; }; + 81654DCD1D356E9A006401F1 /* watchOS.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = watchOS.xcconfig; sourceTree = ""; }; + 81654DCF1D356E9A006401F1 /* Application.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = Application.xcconfig; sourceTree = ""; }; + 81654DD01D356E9A006401F1 /* DynamicFramework.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = DynamicFramework.xcconfig; sourceTree = ""; }; + 81654DD11D356E9A006401F1 /* LogicTests.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = LogicTests.xcconfig; sourceTree = ""; }; + 81654DD21D356E9A006401F1 /* StaticFramework.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = StaticFramework.xcconfig; sourceTree = ""; }; + 81654DD41D356E9A006401F1 /* Debug.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = Debug.xcconfig; sourceTree = ""; }; + 81654DD51D356E9A006401F1 /* Release.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = Release.xcconfig; sourceTree = ""; }; + 81654DD61D356E9A006401F1 /* Warnings.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = Warnings.xcconfig; sourceTree = ""; }; + 816B75A61D07AEB70012AC43 /* GraphRequestDataAttachment.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = GraphRequestDataAttachment.swift; sourceTree = ""; }; + 817021AD1D18DE1500ECE7AC /* UserProfilePictureView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = UserProfilePictureView.swift; sourceTree = ""; }; + 817A0A0D1D07CE8A00FD423A /* GraphRequestResult.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = GraphRequestResult.swift; sourceTree = ""; }; + 81AB8A091D36D41700066F63 /* FBSDKCoreKit.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = FBSDKCoreKit.xcodeproj; path = "../Carthage/Checkouts/facebook-ios-sdk/FBSDKCoreKit/FBSDKCoreKit.xcodeproj"; sourceTree = ""; }; + 81AB8A0C1D36D41700066F63 /* FBSDKLoginKit.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = FBSDKLoginKit.xcodeproj; path = "../Carthage/Checkouts/facebook-ios-sdk/FBSDKLoginKit/FBSDKLoginKit.xcodeproj"; sourceTree = ""; }; + 81AB8A0F1D36D41700066F63 /* FBSDKShareKit.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = FBSDKShareKit.xcodeproj; path = "../Carthage/Checkouts/facebook-ios-sdk/FBSDKShareKit/FBSDKShareKit.xcodeproj"; sourceTree = ""; }; + 81AB8AEE1D36D4AA00066F63 /* Bolts.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = Bolts.xcodeproj; path = "../Carthage/Checkouts/facebook-ios-sdk/Bolts-IOS/Bolts.xcodeproj"; sourceTree = ""; }; + 81AC801C1D348A1800EA6664 /* LoginButtonDelegateBridge.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = LoginButtonDelegateBridge.swift; sourceTree = ""; }; + 81D646521D2C8D7800690609 /* ContentProtocol.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ContentProtocol.swift; sourceTree = ""; }; + 81D646531D2C8D7800690609 /* ContentSharingResultProtocol.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ContentSharingResultProtocol.swift; sourceTree = ""; }; + 81D646541D2C8D7800690609 /* Hashtag.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Hashtag.swift; sourceTree = ""; }; + 81D646551D2C8D7800690609 /* PostSharingResult.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = PostSharingResult.swift; sourceTree = ""; }; + 81D646571D2C8D7800690609 /* LinkShareContent.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = LinkShareContent.swift; sourceTree = ""; }; + 81D646591D2C8D7800690609 /* OpenGraphAction.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = OpenGraphAction.swift; sourceTree = ""; }; + 81D6465A1D2C8D7800690609 /* OpenGraphObject.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = OpenGraphObject.swift; sourceTree = ""; }; + 81D6465B1D2C8D7800690609 /* OpenGraphPropertyContaining.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = OpenGraphPropertyContaining.swift; sourceTree = ""; }; + 81D6465C1D2C8D7800690609 /* OpenGraphPropertyName.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = OpenGraphPropertyName.swift; sourceTree = ""; }; + 81D6465D1D2C8D7800690609 /* OpenGraphPropertyValue.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = OpenGraphPropertyValue.swift; sourceTree = ""; }; + 81D6465E1D2C8D7800690609 /* OpenGraphShareContent.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = OpenGraphShareContent.swift; sourceTree = ""; }; + 81D646601D2C8D7800690609 /* Photo.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Photo.swift; sourceTree = ""; }; + 81D646611D2C8D7800690609 /* PhotoShareContent.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = PhotoShareContent.swift; sourceTree = ""; }; + 81D646631D2C8D7800690609 /* Video.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Video.swift; sourceTree = ""; }; + 81D646641D2C8D7800690609 /* VideoShareContent.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = VideoShareContent.swift; sourceTree = ""; }; + 81D646661D2C8D7800690609 /* ContentSharingProtocol.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ContentSharingProtocol.swift; sourceTree = ""; }; + 81D646671D2C8D7800690609 /* GraphSharer.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = GraphSharer.swift; sourceTree = ""; }; + 81D646701D2C8D7800690609 /* ContentSharingDialogProtocol.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ContentSharingDialogProtocol.swift; sourceTree = ""; }; + 81D646721D2C8D7800690609 /* GameRequest.ActionType.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = GameRequest.ActionType.swift; sourceTree = ""; }; + 81D646731D2C8D7800690609 /* GameRequest.Dialog.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = GameRequest.Dialog.swift; sourceTree = ""; }; + 81D646741D2C8D7800690609 /* GameRequest.Recipient.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = GameRequest.Recipient.swift; sourceTree = ""; }; + 81D646751D2C8D7800690609 /* GameRequest.RecipientsFilter.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = GameRequest.RecipientsFilter.swift; sourceTree = ""; }; + 81D646761D2C8D7800690609 /* GameRequest.Result.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = GameRequest.Result.swift; sourceTree = ""; }; + 81D646771D2C8D7800690609 /* GameRequest.SDKDelegate.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = GameRequest.SDKDelegate.swift; sourceTree = ""; }; + 81D646781D2C8D7800690609 /* GameRequest.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = GameRequest.swift; sourceTree = ""; }; + 81D6467A1D2C8D7800690609 /* MessageDialog.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = MessageDialog.swift; sourceTree = ""; }; + 81D6467C1D2C8D7800690609 /* ShareDialog.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ShareDialog.swift; sourceTree = ""; }; + 81D6467D1D2C8D7800690609 /* ShareDialogMode.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ShareDialogMode.swift; sourceTree = ""; }; + 81D6467F1D2C8D7800690609 /* Constants.Internal.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Constants.Internal.swift; sourceTree = ""; }; + 81D646801D2C8D7800690609 /* ContentBridger.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ContentBridger.swift; sourceTree = ""; }; + 81D646811D2C8D7800690609 /* SDKSharingDelegateBridge.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SDKSharingDelegateBridge.swift; sourceTree = ""; }; + 81D646821D2C8D7800690609 /* ShareError.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ShareError.swift; sourceTree = ""; }; + 81D646841D2C8D7800690609 /* LikableObject.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = LikableObject.swift; sourceTree = ""; }; + 81D646851D2C8D7800690609 /* LikeButton.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = LikeButton.swift; sourceTree = ""; }; + 81D646861D2C8D7800690609 /* LikeControl.AuxilaryStyle.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = LikeControl.AuxilaryStyle.swift; sourceTree = ""; }; + 81D646871D2C8D7800690609 /* LikeControl.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = LikeControl.swift; sourceTree = ""; }; + 81D646881D2C8D7800690609 /* SendButton.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SendButton.swift; sourceTree = ""; }; + 81D646891D2C8D7800690609 /* ShareButton.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ShareButton.swift; sourceTree = ""; }; + 81FC4B5F1D064F68003F3A46 /* LoginManager.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = LoginManager.swift; sourceTree = ""; }; + 81FC4B721D064F8B003F3A46 /* LoginDefaultAudience.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = LoginDefaultAudience.swift; sourceTree = ""; }; + 81FC4B7A1D065059003F3A46 /* LoginBehavior.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = LoginBehavior.swift; sourceTree = ""; }; + 81FC4BB51D0653E4003F3A46 /* LoginResult.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = LoginResult.swift; sourceTree = ""; }; + 81FC4C971D067448003F3A46 /* Permission.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Permission.swift; sourceTree = ""; }; + 81FC4CAA1D067490003F3A46 /* ReadPermission.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ReadPermission.swift; sourceTree = ""; }; + 81FC4CBA1D0674F4003F3A46 /* PublishPermission.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = PublishPermission.swift; sourceTree = ""; }; + 81FC4EF11D068564003F3A46 /* UserProfile.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = UserProfile.swift; sourceTree = ""; }; + F5FE8BF61D2DDB1100A0E4CF /* AppInvite.DeliveryMethod.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AppInvite.DeliveryMethod.swift; sourceTree = ""; }; + F5FE8BF71D2DDB1100A0E4CF /* AppInvite.Dialog.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AppInvite.Dialog.swift; sourceTree = ""; }; + F5FE8BF81D2DDB1100A0E4CF /* AppInvite.PromoCode.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AppInvite.PromoCode.swift; sourceTree = ""; }; + F5FE8BF91D2DDB1100A0E4CF /* AppInvite.Result.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AppInvite.Result.swift; sourceTree = ""; }; + F5FE8BFA1D2DDB1100A0E4CF /* AppInvite.SDKDelegate.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AppInvite.SDKDelegate.swift; sourceTree = ""; }; + F5FE8BFB1D2DDB1100A0E4CF /* AppInvite.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AppInvite.swift; sourceTree = ""; }; +/* End PBXFileReference section */ + +/* Begin PBXFrameworksBuildPhase section */ + 8115C0D41CFE540D001FF33B /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + 81AB8B351D36D51B00066F63 /* Bolts.framework in Frameworks */, + 81AB8A871D36D43100066F63 /* FBSDKCoreKit.framework in Frameworks */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + 811C5DF61CFFD36600E4A925 /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + 81AB8B3A1D36D56600066F63 /* Bolts.framework in Frameworks */, + 81AB8A941D36D44C00066F63 /* FBSDKCoreKit.framework in Frameworks */, + 81AB8A951D36D44C00066F63 /* FBSDKLoginKit.framework in Frameworks */, + 811C5E4D1CFFD4D100E4A925 /* FacebookCore.framework in Frameworks */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + 811C5E031CFFD36D00E4A925 /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + 81AB8B3F1D36D57300066F63 /* Bolts.framework in Frameworks */, + 81AB8A9F1D36D46100066F63 /* FBSDKCoreKit.framework in Frameworks */, + 81AB8AA01D36D46100066F63 /* FBSDKShareKit.framework in Frameworks */, + 811C5E561CFFD4F800E4A925 /* FacebookCore.framework in Frameworks */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXFrameworksBuildPhase section */ + +/* Begin PBXGroup section */ + 810192A21D01305400B9E881 /* AppEvents */ = { + isa = PBXGroup; + children = ( + 810192A31D01305400B9E881 /* AppEvent.Builtin.swift */, + 810192A41D01305400B9E881 /* AppEvent.swift */, + 810192A51D01305400B9E881 /* AppEventName.swift */, + 810192A61D01305400B9E881 /* AppEventParameterName.swift */, + 810192A71D01305400B9E881 /* AppEventsLogger.FlushBehavior.swift */, + 810192A81D01305400B9E881 /* AppEventsLogger.swift */, + ); + path = AppEvents; + sourceTree = ""; + }; + 810192A91D01305400B9E881 /* Common */ = { + isa = PBXGroup; + children = ( + 810192AA1D01305400B9E881 /* AccessToken.swift */, + 810192AB1D01305400B9E881 /* ApplicationDelegate.swift */, + 810192AC1D01305400B9E881 /* SDKLoggingBehavior.swift */, + 810192AD1D01305400B9E881 /* SDKSettings.swift */, + ); + path = Common; + sourceTree = ""; + }; + 810192C21D0130C000B9E881 /* GraphRequest */ = { + isa = PBXGroup; + children = ( + 810192C31D0130C500B9E881 /* GraphRequest.swift */, + 810192CB1D013FC100B9E881 /* GraphRequestProtocol.swift */, + 815DEBCB1D024ADD000370CA /* GraphRequestProtocol.Bridge.swift */, + 817A0A0D1D07CE8A00FD423A /* GraphRequestResult.swift */, + 816B75A61D07AEB70012AC43 /* GraphRequestDataAttachment.swift */, + 810192CD1D01409400B9E881 /* GraphResponse.swift */, + 810192CF1D0145AE00B9E881 /* GraphResponseProtocol.swift */, + 810192C71D01380600B9E881 /* GraphRequestConnection.swift */, + 810192C91D0139CF00B9E881 /* GraphRequestConnection.Delegate.swift */, + ); + path = GraphRequest; + sourceTree = ""; + }; + 8115C0CE1CFE540D001FF33B = { + isa = PBXGroup; + children = ( + 81654DC31D356E9A006401F1 /* Configurations */, + 811C5E5D1CFFD52B00E4A925 /* Sources */, + 81E94E461CFFD0FA009844F2 /* Dependencies */, + 8115C0E31CFE543A001FF33B /* Resources */, + 8115C0D91CFE540D001FF33B /* Products */, + ); + indentWidth = 2; + sourceTree = ""; + tabWidth = 2; + }; + 8115C0D91CFE540D001FF33B /* Products */ = { + isa = PBXGroup; + children = ( + 8115C0D81CFE540D001FF33B /* FacebookCore.framework */, + 811C5DFD1CFFD36600E4A925 /* FacebookLogin.framework */, + 811C5E0A1CFFD36D00E4A925 /* FacebookShare.framework */, + ); + name = Products; + sourceTree = ""; + }; + 8115C0E31CFE543A001FF33B /* Resources */ = { + isa = PBXGroup; + children = ( + 8115C0E41CFE543A001FF33B /* Info.plist */, + ); + path = Resources; + sourceTree = ""; + }; + 811C5E5D1CFFD52B00E4A925 /* Sources */ = { + isa = PBXGroup; + children = ( + 811C5E5E1CFFD52B00E4A925 /* Core */, + 811C5E601CFFD52B00E4A925 /* Login */, + 811C5E621CFFD52B00E4A925 /* Share */, + ); + path = Sources; + sourceTree = ""; + }; + 811C5E5E1CFFD52B00E4A925 /* Core */ = { + isa = PBXGroup; + children = ( + 810192A21D01305400B9E881 /* AppEvents */, + 810192A91D01305400B9E881 /* Common */, + 81FC4C961D067448003F3A46 /* Permissions */, + 810192C21D0130C000B9E881 /* GraphRequest */, + 81FC4EF01D068564003F3A46 /* UserProfile */, + 811C5E5F1CFFD52B00E4A925 /* Internal */, + ); + path = Core; + sourceTree = ""; + }; + 811C5E5F1CFFD52B00E4A925 /* Internal */ = { + isa = PBXGroup; + children = ( + 811C5E741CFFD5DD00E4A925 /* Extensions */, + ); + path = Internal; + sourceTree = ""; + }; + 811C5E601CFFD52B00E4A925 /* Login */ = { + isa = PBXGroup; + children = ( + 81FC4B5F1D064F68003F3A46 /* LoginManager.swift */, + 81FC4B7A1D065059003F3A46 /* LoginBehavior.swift */, + 81FC4B721D064F8B003F3A46 /* LoginDefaultAudience.swift */, + 81FC4BB51D0653E4003F3A46 /* LoginResult.swift */, + 813C9D111D1DC12600288BFA /* LoginButton */, + ); + path = Login; + sourceTree = ""; + }; + 811C5E621CFFD52B00E4A925 /* Share */ = { + isa = PBXGroup; + children = ( + 81D646501D2C8D7800690609 /* Content */, + 81D646651D2C8D7800690609 /* Content Sharing */, + 81D646681D2C8D7800690609 /* Dialogs */, + 81D646831D2C8D7800690609 /* Views */, + 81D646821D2C8D7800690609 /* ShareError.swift */, + 81D6467E1D2C8D7800690609 /* Internal */, + ); + path = Share; + sourceTree = ""; + }; + 811C5E741CFFD5DD00E4A925 /* Extensions */ = { + isa = PBXGroup; + children = ( + 811C5E751CFFD5DD00E4A925 /* Dictionary+KeyValueMap.swift */, + 811C5E761CFFD5DD00E4A925 /* Optional+OnSome.swift */, + ); + path = Extensions; + sourceTree = ""; + }; + 813C9D111D1DC12600288BFA /* LoginButton */ = { + isa = PBXGroup; + children = ( + 813C9B551D1DB16F00288BFA /* LoginButton.swift */, + 813C9CF91D1DBFA000288BFA /* LoginButton.Tooltip.swift */, + 813C9BB21D1DB48100288BFA /* LoginButtonDelegate.swift */, + 81AC801C1D348A1800EA6664 /* LoginButtonDelegateBridge.swift */, + ); + name = LoginButton; + sourceTree = ""; + }; + 81654DC31D356E9A006401F1 /* Configurations */ = { + isa = PBXGroup; + children = ( + 81654DC41D356E9A006401F1 /* FacebookCore.xcconfig */, + 81654DC51D356E9A006401F1 /* FacebookLogin.xcconfig */, + 81654DC61D356E9A006401F1 /* FacebookShare.xcconfig */, + 811AD1B51D35D9C8007689DC /* Version.xcconfig */, + 81654DC71D356E9A006401F1 /* Shared */, + ); + path = Configurations; + sourceTree = ""; + }; + 81654DC71D356E9A006401F1 /* Shared */ = { + isa = PBXGroup; + children = ( + 81654DC81D356E9A006401F1 /* Common.xcconfig */, + 81654DC91D356E9A006401F1 /* Platform */, + 81654DCE1D356E9A006401F1 /* Product */, + 81654DD31D356E9A006401F1 /* Project */, + 81654DD61D356E9A006401F1 /* Warnings.xcconfig */, + ); + path = Shared; + sourceTree = ""; + }; + 81654DC91D356E9A006401F1 /* Platform */ = { + isa = PBXGroup; + children = ( + 81654DCA1D356E9A006401F1 /* iOS.xcconfig */, + 81654DCB1D356E9A006401F1 /* macOS.xcconfig */, + 81654DCC1D356E9A006401F1 /* tvOS.xcconfig */, + 81654DCD1D356E9A006401F1 /* watchOS.xcconfig */, + ); + path = Platform; + sourceTree = ""; + }; + 81654DCE1D356E9A006401F1 /* Product */ = { + isa = PBXGroup; + children = ( + 81654DCF1D356E9A006401F1 /* Application.xcconfig */, + 81654DD01D356E9A006401F1 /* DynamicFramework.xcconfig */, + 81654DD11D356E9A006401F1 /* LogicTests.xcconfig */, + 81654DD21D356E9A006401F1 /* StaticFramework.xcconfig */, + ); + path = Product; + sourceTree = ""; + }; + 81654DD31D356E9A006401F1 /* Project */ = { + isa = PBXGroup; + children = ( + 81654DD41D356E9A006401F1 /* Debug.xcconfig */, + 81654DD51D356E9A006401F1 /* Release.xcconfig */, + ); + path = Project; + sourceTree = ""; + }; + 81AB8A0A1D36D41700066F63 /* Products */ = { + isa = PBXGroup; + children = ( + 81AB8A3B1D36D41700066F63 /* FBSDKCoreKit.framework */, + 81AB8A3D1D36D41700066F63 /* FBSDKCoreKit.framework */, + 81AB8A3F1D36D41700066F63 /* FBSDKCoreKitTests.xctest */, + 81AB8A411D36D41700066F63 /* FBSDKCoreKit.framework */, + 81AB8A431D36D41700066F63 /* FBSDKCoreKit.framework */, + ); + name = Products; + sourceTree = ""; + }; + 81AB8A0D1D36D41700066F63 /* Products */ = { + isa = PBXGroup; + children = ( + 81AB8A491D36D41700066F63 /* FBSDKLoginKit.framework */, + 81AB8A4B1D36D41700066F63 /* FBSDKLoginKit.framework */, + 81AB8A4D1D36D41700066F63 /* FBSDKLoginKitTests.xctest */, + ); + name = Products; + sourceTree = ""; + }; + 81AB8A101D36D41700066F63 /* Products */ = { + isa = PBXGroup; + children = ( + 81AB8A561D36D41700066F63 /* FBSDKShareKit.framework */, + 81AB8A581D36D41700066F63 /* FBSDKShareKit.framework */, + 81AB8A5A1D36D41700066F63 /* FBSDKShareKitTests.xctest */, + 81AB8A5C1D36D41700066F63 /* FBSDKShareKit.framework */, + 81AB8A5E1D36D41700066F63 /* FBSDKShareKit.framework */, + ); + name = Products; + sourceTree = ""; + }; + 81AB8AEF1D36D4AA00066F63 /* Products */ = { + isa = PBXGroup; + children = ( + 81AB8AFD1D36D4AA00066F63 /* Bolts.framework */, + 81AB8AFF1D36D4AA00066F63 /* Bolts.framework */, + 81AB8B011D36D4AA00066F63 /* Bolts.framework */, + 81AB8B031D36D4AA00066F63 /* Bolts.framework */, + 81AB8B051D36D4AA00066F63 /* Bolts.framework */, + 81AB8B071D36D4AA00066F63 /* Bolts.framework */, + 81AB8B091D36D4AA00066F63 /* Bolts.framework */, + 81AB8B0B1D36D4AA00066F63 /* BoltsTests-iOS.xctest */, + 81AB8B0D1D36D4AA00066F63 /* BoltsTests-OSX.xctest */, + 81AB8B0F1D36D4AA00066F63 /* BoltsTests-tvOS.xctest */, + 81AB8B111D36D4AA00066F63 /* BoltsTestUI.app */, + ); + name = Products; + sourceTree = ""; + }; + 81D646501D2C8D7800690609 /* Content */ = { + isa = PBXGroup; + children = ( + 81D646511D2C8D7800690609 /* Common */, + 81D646561D2C8D7800690609 /* Link */, + 81D646581D2C8D7800690609 /* OpenGraph */, + 81D6465F1D2C8D7800690609 /* Photo */, + 81D646621D2C8D7800690609 /* Video */, + ); + path = Content; + sourceTree = ""; + }; + 81D646511D2C8D7800690609 /* Common */ = { + isa = PBXGroup; + children = ( + 81D646521D2C8D7800690609 /* ContentProtocol.swift */, + 81D646531D2C8D7800690609 /* ContentSharingResultProtocol.swift */, + 81D646541D2C8D7800690609 /* Hashtag.swift */, + 81D646551D2C8D7800690609 /* PostSharingResult.swift */, + ); + path = Common; + sourceTree = ""; + }; + 81D646561D2C8D7800690609 /* Link */ = { + isa = PBXGroup; + children = ( + 81D646571D2C8D7800690609 /* LinkShareContent.swift */, + ); + path = Link; + sourceTree = ""; + }; + 81D646581D2C8D7800690609 /* OpenGraph */ = { + isa = PBXGroup; + children = ( + 81D646591D2C8D7800690609 /* OpenGraphAction.swift */, + 81D6465A1D2C8D7800690609 /* OpenGraphObject.swift */, + 81D6465B1D2C8D7800690609 /* OpenGraphPropertyContaining.swift */, + 81D6465C1D2C8D7800690609 /* OpenGraphPropertyName.swift */, + 81D6465D1D2C8D7800690609 /* OpenGraphPropertyValue.swift */, + 81D6465E1D2C8D7800690609 /* OpenGraphShareContent.swift */, + ); + path = OpenGraph; + sourceTree = ""; + }; + 81D6465F1D2C8D7800690609 /* Photo */ = { + isa = PBXGroup; + children = ( + 81D646601D2C8D7800690609 /* Photo.swift */, + 81D646611D2C8D7800690609 /* PhotoShareContent.swift */, + ); + path = Photo; + sourceTree = ""; + }; + 81D646621D2C8D7800690609 /* Video */ = { + isa = PBXGroup; + children = ( + 81D646631D2C8D7800690609 /* Video.swift */, + 81D646641D2C8D7800690609 /* VideoShareContent.swift */, + ); + path = Video; + sourceTree = ""; + }; + 81D646651D2C8D7800690609 /* Content Sharing */ = { + isa = PBXGroup; + children = ( + 81D646661D2C8D7800690609 /* ContentSharingProtocol.swift */, + 81D646671D2C8D7800690609 /* GraphSharer.swift */, + ); + path = "Content Sharing"; + sourceTree = ""; + }; + 81D646681D2C8D7800690609 /* Dialogs */ = { + isa = PBXGroup; + children = ( + F5FE8BF51D2DDB1100A0E4CF /* AppInvite */, + 81D646701D2C8D7800690609 /* ContentSharingDialogProtocol.swift */, + 81D646711D2C8D7800690609 /* GameRequest */, + 81D646791D2C8D7800690609 /* MessageDialog */, + 81D6467B1D2C8D7800690609 /* ShareDialog */, + ); + path = Dialogs; + sourceTree = ""; + }; + 81D646711D2C8D7800690609 /* GameRequest */ = { + isa = PBXGroup; + children = ( + 81D646721D2C8D7800690609 /* GameRequest.ActionType.swift */, + 81D646731D2C8D7800690609 /* GameRequest.Dialog.swift */, + 81D646741D2C8D7800690609 /* GameRequest.Recipient.swift */, + 81D646751D2C8D7800690609 /* GameRequest.RecipientsFilter.swift */, + 81D646761D2C8D7800690609 /* GameRequest.Result.swift */, + 81D646771D2C8D7800690609 /* GameRequest.SDKDelegate.swift */, + 81D646781D2C8D7800690609 /* GameRequest.swift */, + ); + path = GameRequest; + sourceTree = ""; + }; + 81D646791D2C8D7800690609 /* MessageDialog */ = { + isa = PBXGroup; + children = ( + 81D6467A1D2C8D7800690609 /* MessageDialog.swift */, + ); + path = MessageDialog; + sourceTree = ""; + }; + 81D6467B1D2C8D7800690609 /* ShareDialog */ = { + isa = PBXGroup; + children = ( + 81D6467C1D2C8D7800690609 /* ShareDialog.swift */, + 81D6467D1D2C8D7800690609 /* ShareDialogMode.swift */, + ); + path = ShareDialog; + sourceTree = ""; + }; + 81D6467E1D2C8D7800690609 /* Internal */ = { + isa = PBXGroup; + children = ( + 81D6467F1D2C8D7800690609 /* Constants.Internal.swift */, + 81D646801D2C8D7800690609 /* ContentBridger.swift */, + 81D646811D2C8D7800690609 /* SDKSharingDelegateBridge.swift */, + ); + path = Internal; + sourceTree = ""; + }; + 81D646831D2C8D7800690609 /* Views */ = { + isa = PBXGroup; + children = ( + 81D646841D2C8D7800690609 /* LikableObject.swift */, + 81D646851D2C8D7800690609 /* LikeButton.swift */, + 81D646861D2C8D7800690609 /* LikeControl.AuxilaryStyle.swift */, + 81D646871D2C8D7800690609 /* LikeControl.swift */, + 81D646881D2C8D7800690609 /* SendButton.swift */, + 81D646891D2C8D7800690609 /* ShareButton.swift */, + ); + path = Views; + sourceTree = ""; + }; + 81E94E461CFFD0FA009844F2 /* Dependencies */ = { + isa = PBXGroup; + children = ( + 81E94E471CFFD101009844F2 /* ObjC */, + ); + name = Dependencies; + path = Resources; + sourceTree = ""; + }; + 81E94E471CFFD101009844F2 /* ObjC */ = { + isa = PBXGroup; + children = ( + 81AB8AEE1D36D4AA00066F63 /* Bolts.xcodeproj */, + 81AB8A091D36D41700066F63 /* FBSDKCoreKit.xcodeproj */, + 81AB8A0C1D36D41700066F63 /* FBSDKLoginKit.xcodeproj */, + 81AB8A0F1D36D41700066F63 /* FBSDKShareKit.xcodeproj */, + ); + name = ObjC; + sourceTree = ""; + }; + 81FC4C961D067448003F3A46 /* Permissions */ = { + isa = PBXGroup; + children = ( + 81FC4C971D067448003F3A46 /* Permission.swift */, + 81FC4CAA1D067490003F3A46 /* ReadPermission.swift */, + 81FC4CBA1D0674F4003F3A46 /* PublishPermission.swift */, + ); + path = Permissions; + sourceTree = ""; + }; + 81FC4EF01D068564003F3A46 /* UserProfile */ = { + isa = PBXGroup; + children = ( + 81FC4EF11D068564003F3A46 /* UserProfile.swift */, + 817021AD1D18DE1500ECE7AC /* UserProfilePictureView.swift */, + ); + path = UserProfile; + sourceTree = ""; + }; + F5FE8BF51D2DDB1100A0E4CF /* AppInvite */ = { + isa = PBXGroup; + children = ( + F5FE8BF61D2DDB1100A0E4CF /* AppInvite.DeliveryMethod.swift */, + F5FE8BF71D2DDB1100A0E4CF /* AppInvite.Dialog.swift */, + F5FE8BF81D2DDB1100A0E4CF /* AppInvite.PromoCode.swift */, + F5FE8BF91D2DDB1100A0E4CF /* AppInvite.Result.swift */, + F5FE8BFA1D2DDB1100A0E4CF /* AppInvite.SDKDelegate.swift */, + F5FE8BFB1D2DDB1100A0E4CF /* AppInvite.swift */, + ); + path = AppInvite; + sourceTree = ""; + }; +/* End PBXGroup section */ + +/* Begin PBXHeadersBuildPhase section */ + 8115C0D51CFE540D001FF33B /* Headers */ = { + isa = PBXHeadersBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; + 811C5DF81CFFD36600E4A925 /* Headers */ = { + isa = PBXHeadersBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; + 811C5E051CFFD36D00E4A925 /* Headers */ = { + isa = PBXHeadersBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXHeadersBuildPhase section */ + +/* Begin PBXNativeTarget section */ + 8115C0D71CFE540D001FF33B /* FacebookCore */ = { + isa = PBXNativeTarget; + buildConfigurationList = 8115C0E01CFE540D001FF33B /* Build configuration list for PBXNativeTarget "FacebookCore" */; + buildPhases = ( + 8115C0D31CFE540D001FF33B /* Sources */, + 8115C0D41CFE540D001FF33B /* Frameworks */, + 8115C0D51CFE540D001FF33B /* Headers */, + 8115C0D61CFE540D001FF33B /* Resources */, + ); + buildRules = ( + ); + dependencies = ( + 81AB8B341D36D51700066F63 /* PBXTargetDependency */, + 81AB8A841D36D42500066F63 /* PBXTargetDependency */, + ); + name = FacebookCore; + productName = FBSDKCoreKit; + productReference = 8115C0D81CFE540D001FF33B /* FacebookCore.framework */; + productType = "com.apple.product-type.framework"; + }; + 811C5DF21CFFD36600E4A925 /* FacebookLogin */ = { + isa = PBXNativeTarget; + buildConfigurationList = 811C5DFA1CFFD36600E4A925 /* Build configuration list for PBXNativeTarget "FacebookLogin" */; + buildPhases = ( + 811C5DF51CFFD36600E4A925 /* Sources */, + 811C5DF61CFFD36600E4A925 /* Frameworks */, + 811C5DF81CFFD36600E4A925 /* Headers */, + 811C5DF91CFFD36600E4A925 /* Resources */, + ); + buildRules = ( + ); + dependencies = ( + 81AB8B391D36D56200066F63 /* PBXTargetDependency */, + 81AB8A901D36D43D00066F63 /* PBXTargetDependency */, + 81AB8A921D36D43D00066F63 /* PBXTargetDependency */, + 811C5E481CFFD4C800E4A925 /* PBXTargetDependency */, + ); + name = FacebookLogin; + productName = FBSDKCoreKit; + productReference = 811C5DFD1CFFD36600E4A925 /* FacebookLogin.framework */; + productType = "com.apple.product-type.framework"; + }; + 811C5DFF1CFFD36D00E4A925 /* FacebookShare */ = { + isa = PBXNativeTarget; + buildConfigurationList = 811C5E071CFFD36D00E4A925 /* Build configuration list for PBXNativeTarget "FacebookShare" */; + buildPhases = ( + 811C5E021CFFD36D00E4A925 /* Sources */, + 811C5E031CFFD36D00E4A925 /* Frameworks */, + 811C5E051CFFD36D00E4A925 /* Headers */, + 811C5E061CFFD36D00E4A925 /* Resources */, + ); + buildRules = ( + ); + dependencies = ( + 81AB8B3E1D36D56E00066F63 /* PBXTargetDependency */, + 81AB8A9B1D36D45700066F63 /* PBXTargetDependency */, + 81AB8A9D1D36D45700066F63 /* PBXTargetDependency */, + 811C5E551CFFD4F100E4A925 /* PBXTargetDependency */, + ); + name = FacebookShare; + productName = FBSDKCoreKit; + productReference = 811C5E0A1CFFD36D00E4A925 /* FacebookShare.framework */; + productType = "com.apple.product-type.framework"; + }; +/* End PBXNativeTarget section */ + +/* Begin PBXProject section */ + 8115C0CF1CFE540D001FF33B /* Project object */ = { + isa = PBXProject; + attributes = { + LastUpgradeCheck = 0800; + ORGANIZATIONNAME = "Facebook Inc"; + TargetAttributes = { + 8115C0D71CFE540D001FF33B = { + CreatedOnToolsVersion = 7.3.1; + LastSwiftMigration = 0800; + }; + 811C5DF21CFFD36600E4A925 = { + LastSwiftMigration = 0800; + }; + 811C5DFF1CFFD36D00E4A925 = { + LastSwiftMigration = 0800; + }; + }; + }; + buildConfigurationList = 8115C0D21CFE540D001FF33B /* Build configuration list for PBXProject "FacebookSwift" */; + compatibilityVersion = "Xcode 3.2"; + developmentRegion = English; + hasScannedForEncodings = 0; + knownRegions = ( + en, + ); + mainGroup = 8115C0CE1CFE540D001FF33B; + productRefGroup = 8115C0D91CFE540D001FF33B /* Products */; + projectDirPath = ""; + projectReferences = ( + { + ProductGroup = 81AB8AEF1D36D4AA00066F63 /* Products */; + ProjectRef = 81AB8AEE1D36D4AA00066F63 /* Bolts.xcodeproj */; + }, + { + ProductGroup = 81AB8A0A1D36D41700066F63 /* Products */; + ProjectRef = 81AB8A091D36D41700066F63 /* FBSDKCoreKit.xcodeproj */; + }, + { + ProductGroup = 81AB8A0D1D36D41700066F63 /* Products */; + ProjectRef = 81AB8A0C1D36D41700066F63 /* FBSDKLoginKit.xcodeproj */; + }, + { + ProductGroup = 81AB8A101D36D41700066F63 /* Products */; + ProjectRef = 81AB8A0F1D36D41700066F63 /* FBSDKShareKit.xcodeproj */; + }, + ); + projectRoot = ""; + targets = ( + 8115C0D71CFE540D001FF33B /* FacebookCore */, + 811C5DF21CFFD36600E4A925 /* FacebookLogin */, + 811C5DFF1CFFD36D00E4A925 /* FacebookShare */, + ); + }; +/* End PBXProject section */ + +/* Begin PBXReferenceProxy section */ + 81AB8A3B1D36D41700066F63 /* FBSDKCoreKit.framework */ = { + isa = PBXReferenceProxy; + fileType = wrapper.framework; + path = FBSDKCoreKit.framework; + remoteRef = 81AB8A3A1D36D41700066F63 /* PBXContainerItemProxy */; + sourceTree = BUILT_PRODUCTS_DIR; + }; + 81AB8A3D1D36D41700066F63 /* FBSDKCoreKit.framework */ = { + isa = PBXReferenceProxy; + fileType = wrapper.framework; + path = FBSDKCoreKit.framework; + remoteRef = 81AB8A3C1D36D41700066F63 /* PBXContainerItemProxy */; + sourceTree = BUILT_PRODUCTS_DIR; + }; + 81AB8A3F1D36D41700066F63 /* FBSDKCoreKitTests.xctest */ = { + isa = PBXReferenceProxy; + fileType = wrapper.cfbundle; + path = FBSDKCoreKitTests.xctest; + remoteRef = 81AB8A3E1D36D41700066F63 /* PBXContainerItemProxy */; + sourceTree = BUILT_PRODUCTS_DIR; + }; + 81AB8A411D36D41700066F63 /* FBSDKCoreKit.framework */ = { + isa = PBXReferenceProxy; + fileType = wrapper.framework; + path = FBSDKCoreKit.framework; + remoteRef = 81AB8A401D36D41700066F63 /* PBXContainerItemProxy */; + sourceTree = BUILT_PRODUCTS_DIR; + }; + 81AB8A431D36D41700066F63 /* FBSDKCoreKit.framework */ = { + isa = PBXReferenceProxy; + fileType = wrapper.framework; + path = FBSDKCoreKit.framework; + remoteRef = 81AB8A421D36D41700066F63 /* PBXContainerItemProxy */; + sourceTree = BUILT_PRODUCTS_DIR; + }; + 81AB8A491D36D41700066F63 /* FBSDKLoginKit.framework */ = { + isa = PBXReferenceProxy; + fileType = wrapper.framework; + path = FBSDKLoginKit.framework; + remoteRef = 81AB8A481D36D41700066F63 /* PBXContainerItemProxy */; + sourceTree = BUILT_PRODUCTS_DIR; + }; + 81AB8A4B1D36D41700066F63 /* FBSDKLoginKit.framework */ = { + isa = PBXReferenceProxy; + fileType = wrapper.framework; + path = FBSDKLoginKit.framework; + remoteRef = 81AB8A4A1D36D41700066F63 /* PBXContainerItemProxy */; + sourceTree = BUILT_PRODUCTS_DIR; + }; + 81AB8A4D1D36D41700066F63 /* FBSDKLoginKitTests.xctest */ = { + isa = PBXReferenceProxy; + fileType = wrapper.cfbundle; + path = FBSDKLoginKitTests.xctest; + remoteRef = 81AB8A4C1D36D41700066F63 /* PBXContainerItemProxy */; + sourceTree = BUILT_PRODUCTS_DIR; + }; + 81AB8A561D36D41700066F63 /* FBSDKShareKit.framework */ = { + isa = PBXReferenceProxy; + fileType = wrapper.framework; + path = FBSDKShareKit.framework; + remoteRef = 81AB8A551D36D41700066F63 /* PBXContainerItemProxy */; + sourceTree = BUILT_PRODUCTS_DIR; + }; + 81AB8A581D36D41700066F63 /* FBSDKShareKit.framework */ = { + isa = PBXReferenceProxy; + fileType = wrapper.framework; + path = FBSDKShareKit.framework; + remoteRef = 81AB8A571D36D41700066F63 /* PBXContainerItemProxy */; + sourceTree = BUILT_PRODUCTS_DIR; + }; + 81AB8A5A1D36D41700066F63 /* FBSDKShareKitTests.xctest */ = { + isa = PBXReferenceProxy; + fileType = wrapper.cfbundle; + path = FBSDKShareKitTests.xctest; + remoteRef = 81AB8A591D36D41700066F63 /* PBXContainerItemProxy */; + sourceTree = BUILT_PRODUCTS_DIR; + }; + 81AB8A5C1D36D41700066F63 /* FBSDKShareKit.framework */ = { + isa = PBXReferenceProxy; + fileType = wrapper.framework; + path = FBSDKShareKit.framework; + remoteRef = 81AB8A5B1D36D41700066F63 /* PBXContainerItemProxy */; + sourceTree = BUILT_PRODUCTS_DIR; + }; + 81AB8A5E1D36D41700066F63 /* FBSDKShareKit.framework */ = { + isa = PBXReferenceProxy; + fileType = wrapper.framework; + path = FBSDKShareKit.framework; + remoteRef = 81AB8A5D1D36D41700066F63 /* PBXContainerItemProxy */; + sourceTree = BUILT_PRODUCTS_DIR; + }; + 81AB8AFD1D36D4AA00066F63 /* Bolts.framework */ = { + isa = PBXReferenceProxy; + fileType = wrapper.framework; + path = Bolts.framework; + remoteRef = 81AB8AFC1D36D4AA00066F63 /* PBXContainerItemProxy */; + sourceTree = BUILT_PRODUCTS_DIR; + }; + 81AB8AFF1D36D4AA00066F63 /* Bolts.framework */ = { + isa = PBXReferenceProxy; + fileType = wrapper.framework; + path = Bolts.framework; + remoteRef = 81AB8AFE1D36D4AA00066F63 /* PBXContainerItemProxy */; + sourceTree = BUILT_PRODUCTS_DIR; + }; + 81AB8B011D36D4AA00066F63 /* Bolts.framework */ = { + isa = PBXReferenceProxy; + fileType = wrapper.framework; + path = Bolts.framework; + remoteRef = 81AB8B001D36D4AA00066F63 /* PBXContainerItemProxy */; + sourceTree = BUILT_PRODUCTS_DIR; + }; + 81AB8B031D36D4AA00066F63 /* Bolts.framework */ = { + isa = PBXReferenceProxy; + fileType = wrapper.framework; + path = Bolts.framework; + remoteRef = 81AB8B021D36D4AA00066F63 /* PBXContainerItemProxy */; + sourceTree = BUILT_PRODUCTS_DIR; + }; + 81AB8B051D36D4AA00066F63 /* Bolts.framework */ = { + isa = PBXReferenceProxy; + fileType = wrapper.framework; + path = Bolts.framework; + remoteRef = 81AB8B041D36D4AA00066F63 /* PBXContainerItemProxy */; + sourceTree = BUILT_PRODUCTS_DIR; + }; + 81AB8B071D36D4AA00066F63 /* Bolts.framework */ = { + isa = PBXReferenceProxy; + fileType = wrapper.framework; + path = Bolts.framework; + remoteRef = 81AB8B061D36D4AA00066F63 /* PBXContainerItemProxy */; + sourceTree = BUILT_PRODUCTS_DIR; + }; + 81AB8B091D36D4AA00066F63 /* Bolts.framework */ = { + isa = PBXReferenceProxy; + fileType = wrapper.framework; + path = Bolts.framework; + remoteRef = 81AB8B081D36D4AA00066F63 /* PBXContainerItemProxy */; + sourceTree = BUILT_PRODUCTS_DIR; + }; + 81AB8B0B1D36D4AA00066F63 /* BoltsTests-iOS.xctest */ = { + isa = PBXReferenceProxy; + fileType = wrapper.cfbundle; + path = "BoltsTests-iOS.xctest"; + remoteRef = 81AB8B0A1D36D4AA00066F63 /* PBXContainerItemProxy */; + sourceTree = BUILT_PRODUCTS_DIR; + }; + 81AB8B0D1D36D4AA00066F63 /* BoltsTests-OSX.xctest */ = { + isa = PBXReferenceProxy; + fileType = wrapper.cfbundle; + path = "BoltsTests-OSX.xctest"; + remoteRef = 81AB8B0C1D36D4AA00066F63 /* PBXContainerItemProxy */; + sourceTree = BUILT_PRODUCTS_DIR; + }; + 81AB8B0F1D36D4AA00066F63 /* BoltsTests-tvOS.xctest */ = { + isa = PBXReferenceProxy; + fileType = wrapper.cfbundle; + path = "BoltsTests-tvOS.xctest"; + remoteRef = 81AB8B0E1D36D4AA00066F63 /* PBXContainerItemProxy */; + sourceTree = BUILT_PRODUCTS_DIR; + }; + 81AB8B111D36D4AA00066F63 /* BoltsTestUI.app */ = { + isa = PBXReferenceProxy; + fileType = wrapper.application; + path = BoltsTestUI.app; + remoteRef = 81AB8B101D36D4AA00066F63 /* PBXContainerItemProxy */; + sourceTree = BUILT_PRODUCTS_DIR; + }; +/* End PBXReferenceProxy section */ + +/* Begin PBXResourcesBuildPhase section */ + 8115C0D61CFE540D001FF33B /* Resources */ = { + isa = PBXResourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; + 811C5DF91CFFD36600E4A925 /* Resources */ = { + isa = PBXResourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; + 811C5E061CFFD36D00E4A925 /* Resources */ = { + isa = PBXResourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXResourcesBuildPhase section */ + +/* Begin PBXSourcesBuildPhase section */ + 8115C0D31CFE540D001FF33B /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 817021AE1D18DE1500ECE7AC /* UserProfilePictureView.swift in Sources */, + 810192B61D01305400B9E881 /* SDKLoggingBehavior.swift in Sources */, + 81FC4CBB1D0674F4003F3A46 /* PublishPermission.swift in Sources */, + 810192B51D01305400B9E881 /* ApplicationDelegate.swift in Sources */, + 810192C81D01380600B9E881 /* GraphRequestConnection.swift in Sources */, + 810192C41D0130C500B9E881 /* GraphRequest.swift in Sources */, + 810192B01D01305400B9E881 /* AppEventName.swift in Sources */, + 810192B71D01305400B9E881 /* SDKSettings.swift in Sources */, + 811C5E781CFFD5DD00E4A925 /* Optional+OnSome.swift in Sources */, + 815DEBCC1D024ADD000370CA /* GraphRequestProtocol.Bridge.swift in Sources */, + 810192B31D01305400B9E881 /* AppEventsLogger.swift in Sources */, + 810192B11D01305400B9E881 /* AppEventParameterName.swift in Sources */, + 811C5E771CFFD5DD00E4A925 /* Dictionary+KeyValueMap.swift in Sources */, + 810192B21D01305400B9E881 /* AppEventsLogger.FlushBehavior.swift in Sources */, + 817A0A0E1D07CE8A00FD423A /* GraphRequestResult.swift in Sources */, + 810192AE1D01305400B9E881 /* AppEvent.Builtin.swift in Sources */, + 810192AF1D01305400B9E881 /* AppEvent.swift in Sources */, + 810192B41D01305400B9E881 /* AccessToken.swift in Sources */, + 81FC4CAB1D067490003F3A46 /* ReadPermission.swift in Sources */, + 810192CA1D0139CF00B9E881 /* GraphRequestConnection.Delegate.swift in Sources */, + 816B75A71D07AEB70012AC43 /* GraphRequestDataAttachment.swift in Sources */, + 810192CC1D013FC100B9E881 /* GraphRequestProtocol.swift in Sources */, + 81FC4EF21D068564003F3A46 /* UserProfile.swift in Sources */, + 810192CE1D01409400B9E881 /* GraphResponse.swift in Sources */, + 81FC4C981D067448003F3A46 /* Permission.swift in Sources */, + 810192D01D0145AE00B9E881 /* GraphResponseProtocol.swift in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + 811C5DF51CFFD36600E4A925 /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 813C9B561D1DB16F00288BFA /* LoginButton.swift in Sources */, + 813C9BB31D1DB48100288BFA /* LoginButtonDelegate.swift in Sources */, + 81FC4BB61D0653E4003F3A46 /* LoginResult.swift in Sources */, + 81FC4B7B1D065059003F3A46 /* LoginBehavior.swift in Sources */, + 813C9CFA1D1DBFA000288BFA /* LoginButton.Tooltip.swift in Sources */, + 81AC801D1D348A1800EA6664 /* LoginButtonDelegateBridge.swift in Sources */, + 81FC4B601D064F68003F3A46 /* LoginManager.swift in Sources */, + 81FC4B731D064F8B003F3A46 /* LoginDefaultAudience.swift in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + 811C5E021CFFD36D00E4A925 /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 81D646991D2C8D7800690609 /* ContentSharingProtocol.swift in Sources */, + 81D646B21D2C8D7800690609 /* LikeControl.AuxilaryStyle.swift in Sources */, + F5FE8BFC1D2DDB1100A0E4CF /* AppInvite.DeliveryMethod.swift in Sources */, + 81D6469A1D2C8D7800690609 /* GraphSharer.swift in Sources */, + 81D646B51D2C8D7800690609 /* ShareButton.swift in Sources */, + 81D646B01D2C8D7800690609 /* LikableObject.swift in Sources */, + 81D646A51D2C8D7800690609 /* GameRequest.RecipientsFilter.swift in Sources */, + 81D646A91D2C8D7800690609 /* MessageDialog.swift in Sources */, + 81D646A31D2C8D7800690609 /* GameRequest.Dialog.swift in Sources */, + 81D646951D2C8D7800690609 /* Photo.swift in Sources */, + 81D646AB1D2C8D7800690609 /* ShareDialogMode.swift in Sources */, + 81D646A11D2C8D7800690609 /* ContentSharingDialogProtocol.swift in Sources */, + 81D6468F1D2C8D7800690609 /* OpenGraphAction.swift in Sources */, + 81D646B31D2C8D7800690609 /* LikeControl.swift in Sources */, + 81D646A61D2C8D7800690609 /* GameRequest.Result.swift in Sources */, + 81D646901D2C8D7800690609 /* OpenGraphObject.swift in Sources */, + 81D6468A1D2C8D7800690609 /* ContentProtocol.swift in Sources */, + 81D646AD1D2C8D7800690609 /* ContentBridger.swift in Sources */, + 81D646AE1D2C8D7800690609 /* SDKSharingDelegateBridge.swift in Sources */, + 81D646A21D2C8D7800690609 /* GameRequest.ActionType.swift in Sources */, + 81D6468C1D2C8D7800690609 /* Hashtag.swift in Sources */, + 81D646A81D2C8D7800690609 /* GameRequest.swift in Sources */, + F5FE8BFD1D2DDB1100A0E4CF /* AppInvite.Dialog.swift in Sources */, + 81D646AF1D2C8D7800690609 /* ShareError.swift in Sources */, + 81D646971D2C8D7800690609 /* Video.swift in Sources */, + 81D646921D2C8D7800690609 /* OpenGraphPropertyName.swift in Sources */, + 81D6468B1D2C8D7800690609 /* ContentSharingResultProtocol.swift in Sources */, + F5FE8C011D2DDB1100A0E4CF /* AppInvite.swift in Sources */, + 81D646981D2C8D7800690609 /* VideoShareContent.swift in Sources */, + 81D646B11D2C8D7800690609 /* LikeButton.swift in Sources */, + 81D646A41D2C8D7800690609 /* GameRequest.Recipient.swift in Sources */, + 81D646911D2C8D7800690609 /* OpenGraphPropertyContaining.swift in Sources */, + 81D646941D2C8D7800690609 /* OpenGraphShareContent.swift in Sources */, + 81D646931D2C8D7800690609 /* OpenGraphPropertyValue.swift in Sources */, + 81D646A71D2C8D7800690609 /* GameRequest.SDKDelegate.swift in Sources */, + 81D646961D2C8D7800690609 /* PhotoShareContent.swift in Sources */, + F5FE8BFE1D2DDB1100A0E4CF /* AppInvite.PromoCode.swift in Sources */, + F5FE8C001D2DDB1100A0E4CF /* AppInvite.SDKDelegate.swift in Sources */, + 81D6468D1D2C8D7800690609 /* PostSharingResult.swift in Sources */, + 81D646AC1D2C8D7800690609 /* Constants.Internal.swift in Sources */, + 81D646AA1D2C8D7800690609 /* ShareDialog.swift in Sources */, + 81D646B41D2C8D7800690609 /* SendButton.swift in Sources */, + F5FE8BFF1D2DDB1100A0E4CF /* AppInvite.Result.swift in Sources */, + 81D6468E1D2C8D7800690609 /* LinkShareContent.swift in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXSourcesBuildPhase section */ + +/* Begin PBXTargetDependency section */ + 811C5E481CFFD4C800E4A925 /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + target = 8115C0D71CFE540D001FF33B /* FacebookCore */; + targetProxy = 811C5E471CFFD4C800E4A925 /* PBXContainerItemProxy */; + }; + 811C5E551CFFD4F100E4A925 /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + target = 8115C0D71CFE540D001FF33B /* FacebookCore */; + targetProxy = 811C5E541CFFD4F100E4A925 /* PBXContainerItemProxy */; + }; + 81AB8A841D36D42500066F63 /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + name = "FBSDKCoreKit-Dynamic"; + targetProxy = 81AB8A831D36D42500066F63 /* PBXContainerItemProxy */; + }; + 81AB8A901D36D43D00066F63 /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + name = "FBSDKCoreKit-Dynamic"; + targetProxy = 81AB8A8F1D36D43D00066F63 /* PBXContainerItemProxy */; + }; + 81AB8A921D36D43D00066F63 /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + name = "FBSDKLoginKit-Dynamic"; + targetProxy = 81AB8A911D36D43D00066F63 /* PBXContainerItemProxy */; + }; + 81AB8A9B1D36D45700066F63 /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + name = "FBSDKCoreKit-Dynamic"; + targetProxy = 81AB8A9A1D36D45700066F63 /* PBXContainerItemProxy */; + }; + 81AB8A9D1D36D45700066F63 /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + name = "FBSDKShareKit-Dynamic"; + targetProxy = 81AB8A9C1D36D45700066F63 /* PBXContainerItemProxy */; + }; + 81AB8B341D36D51700066F63 /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + name = "Bolts-iOS-Dynamic"; + targetProxy = 81AB8B331D36D51700066F63 /* PBXContainerItemProxy */; + }; + 81AB8B391D36D56200066F63 /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + name = "Bolts-iOS-Dynamic"; + targetProxy = 81AB8B381D36D56200066F63 /* PBXContainerItemProxy */; + }; + 81AB8B3E1D36D56E00066F63 /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + name = "Bolts-iOS-Dynamic"; + targetProxy = 81AB8B3D1D36D56E00066F63 /* PBXContainerItemProxy */; + }; +/* End PBXTargetDependency section */ + +/* Begin XCBuildConfiguration section */ + 8115C0DE1CFE540D001FF33B /* Debug */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = 81654DD41D356E9A006401F1 /* Debug.xcconfig */; + buildSettings = { + }; + name = Debug; + }; + 8115C0DF1CFE540D001FF33B /* Release */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = 81654DD51D356E9A006401F1 /* Release.xcconfig */; + buildSettings = { + }; + name = Release; + }; + 8115C0E11CFE540D001FF33B /* Debug */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = 81654DC41D356E9A006401F1 /* FacebookCore.xcconfig */; + buildSettings = { + }; + name = Debug; + }; + 8115C0E21CFE540D001FF33B /* Release */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = 81654DC41D356E9A006401F1 /* FacebookCore.xcconfig */; + buildSettings = { + }; + name = Release; + }; + 811C5DFB1CFFD36600E4A925 /* Debug */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = 81654DC51D356E9A006401F1 /* FacebookLogin.xcconfig */; + buildSettings = { + }; + name = Debug; + }; + 811C5DFC1CFFD36600E4A925 /* Release */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = 81654DC51D356E9A006401F1 /* FacebookLogin.xcconfig */; + buildSettings = { + }; + name = Release; + }; + 811C5E081CFFD36D00E4A925 /* Debug */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = 81654DC61D356E9A006401F1 /* FacebookShare.xcconfig */; + buildSettings = { + }; + name = Debug; + }; + 811C5E091CFFD36D00E4A925 /* Release */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = 81654DC61D356E9A006401F1 /* FacebookShare.xcconfig */; + buildSettings = { + }; + name = Release; + }; +/* End XCBuildConfiguration section */ + +/* Begin XCConfigurationList section */ + 8115C0D21CFE540D001FF33B /* Build configuration list for PBXProject "FacebookSwift" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 8115C0DE1CFE540D001FF33B /* Debug */, + 8115C0DF1CFE540D001FF33B /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + 8115C0E01CFE540D001FF33B /* Build configuration list for PBXNativeTarget "FacebookCore" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 8115C0E11CFE540D001FF33B /* Debug */, + 8115C0E21CFE540D001FF33B /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + 811C5DFA1CFFD36600E4A925 /* Build configuration list for PBXNativeTarget "FacebookLogin" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 811C5DFB1CFFD36600E4A925 /* Debug */, + 811C5DFC1CFFD36600E4A925 /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + 811C5E071CFFD36D00E4A925 /* Build configuration list for PBXNativeTarget "FacebookShare" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 811C5E081CFFD36D00E4A925 /* Debug */, + 811C5E091CFFD36D00E4A925 /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; +/* End XCConfigurationList section */ + }; + rootObject = 8115C0CF1CFE540D001FF33B /* Project object */; +} diff --git a/FacebookSwift.xcodeproj/project.xcworkspace/contents.xcworkspacedata b/FacebookSwift.xcodeproj/project.xcworkspace/contents.xcworkspacedata new file mode 100644 index 00000000..919434a6 --- /dev/null +++ b/FacebookSwift.xcodeproj/project.xcworkspace/contents.xcworkspacedata @@ -0,0 +1,7 @@ + + + + + diff --git a/FacebookSwift.xcodeproj/xcshareddata/xcschemes/FacebookCore.xcscheme b/FacebookSwift.xcodeproj/xcshareddata/xcschemes/FacebookCore.xcscheme new file mode 100644 index 00000000..3919bfc9 --- /dev/null +++ b/FacebookSwift.xcodeproj/xcshareddata/xcschemes/FacebookCore.xcscheme @@ -0,0 +1,80 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/FacebookSwift.xcodeproj/xcshareddata/xcschemes/FacebookLogin.xcscheme b/FacebookSwift.xcodeproj/xcshareddata/xcschemes/FacebookLogin.xcscheme new file mode 100644 index 00000000..25b4ce95 --- /dev/null +++ b/FacebookSwift.xcodeproj/xcshareddata/xcschemes/FacebookLogin.xcscheme @@ -0,0 +1,80 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/FacebookSwift.xcodeproj/xcshareddata/xcschemes/FacebookShare.xcscheme b/FacebookSwift.xcodeproj/xcshareddata/xcschemes/FacebookShare.xcscheme new file mode 100644 index 00000000..4b968040 --- /dev/null +++ b/FacebookSwift.xcodeproj/xcshareddata/xcschemes/FacebookShare.xcscheme @@ -0,0 +1,80 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/FacebookSwift.xcworkspace/contents.xcworkspacedata b/FacebookSwift.xcworkspace/contents.xcworkspacedata new file mode 100644 index 00000000..e6560d30 --- /dev/null +++ b/FacebookSwift.xcworkspace/contents.xcworkspacedata @@ -0,0 +1,30 @@ + + + + + + + + + + + + + + + + + + + diff --git a/FacebookSwift.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings b/FacebookSwift.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings new file mode 100644 index 00000000..08de0be8 --- /dev/null +++ b/FacebookSwift.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings @@ -0,0 +1,8 @@ + + + + + IDEWorkspaceSharedSettings_AutocreateContextsIfNeeded + + + diff --git a/Gemfile b/Gemfile new file mode 100644 index 00000000..d1759f12 --- /dev/null +++ b/Gemfile @@ -0,0 +1,5 @@ +source 'https://rubygems.org' + +gem 'rake' +gem 'jazzy' +gem 'cocoapods' diff --git a/Gemfile.lock b/Gemfile.lock new file mode 100644 index 00000000..6c01fe17 --- /dev/null +++ b/Gemfile.lock @@ -0,0 +1,87 @@ +GEM + remote: https://rubygems.org/ + specs: + activesupport (5.0.0) + concurrent-ruby (~> 1.0, >= 1.0.2) + i18n (~> 0.7) + minitest (~> 5.1) + tzinfo (~> 1.1) + claide (1.0.0) + cocoapods (1.0.1) + activesupport (>= 4.0.2) + claide (>= 1.0.0, < 2.0) + cocoapods-core (= 1.0.1) + cocoapods-deintegrate (>= 1.0.0, < 2.0) + cocoapods-downloader (>= 1.0.0, < 2.0) + cocoapods-plugins (>= 1.0.0, < 2.0) + cocoapods-search (>= 1.0.0, < 2.0) + cocoapods-stats (>= 1.0.0, < 2.0) + cocoapods-trunk (>= 1.0.0, < 2.0) + cocoapods-try (>= 1.0.0, < 2.0) + colored (~> 1.2) + escape (~> 0.0.4) + fourflusher (~> 0.3.0) + molinillo (~> 0.4.5) + nap (~> 1.0) + xcodeproj (>= 1.1.0, < 2.0) + cocoapods-core (1.0.1) + activesupport (>= 4.0.2) + fuzzy_match (~> 2.0.4) + nap (~> 1.0) + cocoapods-deintegrate (1.0.0) + cocoapods-downloader (1.1.0) + cocoapods-plugins (1.0.0) + nap + cocoapods-search (1.0.0) + cocoapods-stats (1.0.0) + cocoapods-trunk (1.0.0) + nap (>= 0.8, < 2.0) + netrc (= 0.7.8) + cocoapods-try (1.1.0) + colored (1.2) + concurrent-ruby (1.0.2) + escape (0.0.4) + fourflusher (0.3.2) + fuzzy_match (2.0.4) + i18n (0.7.0) + jazzy (0.7.0) + cocoapods (~> 1.0) + mustache (~> 0.99) + open4 + redcarpet (~> 3.2) + rouge (~> 1.5) + sass (~> 3.4) + sqlite3 (~> 1.3) + xcinvoke (~> 0.2.1) + liferaft (0.0.4) + minitest (5.9.0) + molinillo (0.4.5) + mustache (0.99.8) + nap (1.1.0) + netrc (0.7.8) + open4 (1.3.4) + rake (11.2.2) + redcarpet (3.3.4) + rouge (1.11.1) + sass (3.4.22) + sqlite3 (1.3.11) + thread_safe (0.3.5) + tzinfo (1.2.2) + thread_safe (~> 0.1) + xcinvoke (0.2.1) + liferaft (~> 0.0.4) + xcodeproj (1.2.0) + activesupport (>= 3) + claide (>= 1.0.0, < 2.0) + colored (~> 1.2) + +PLATFORMS + ruby + +DEPENDENCIES + cocoapods + jazzy + rake + +BUNDLED WITH + 1.12.5 diff --git a/LICENSE b/LICENSE new file mode 100644 index 00000000..d3185752 --- /dev/null +++ b/LICENSE @@ -0,0 +1,19 @@ +Copyright (c) 2016-present, Facebook, Inc. All rights reserved. + +For Facebook Swift SDK software + +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. diff --git a/README.md b/README.md new file mode 100644 index 00000000..bc3be218 --- /dev/null +++ b/README.md @@ -0,0 +1,155 @@ +# Facebook SDK in Swift (Beta) + +![Platforms](https://img.shields.io/cocoapods/v/FacebookCore.svg) +[![Carthage compatible](https://img.shields.io/badge/Carthage-compatible-4BC51D.svg?style=flat)](carthage-link) + +[![Build Status](https://img.shields.io/travis/facebook/facebook-sdk-swift/master.svg?style=flat)](https://travis-ci.org/facebook/facebook-sdk-swift) + +Swift-taylored experience to integrate your app with Facebook. Including: + +- [Facebook Login](https://developers.facebook.com/docs/swift/login) - Authenticate people with their Facebook credentials. +- [Share and Send Dialogs](https://developers.facebook.com/docs/swift/sharing) - Enable sharing content from your app to Facebook. +- [App Events](https://developers.facebook.com/docs/swift/appevents) - Understand your audience and the performance of your app. +- [Graph API](https://developers.facebook.com/docs/swift/graph) - Read and write directly to Facebook social graph. + +## Getting Started + +- **[CocoaPods](https://cocoapods.org)** + + Add the following line to your Podfile: + ```ruby + pod 'FacebookCore' + pod 'FacebookLogin' + pod 'FacebookShare' + ``` + Run `pod install`, and you are all set. + You may also exclude any of these dependencies, if you not need the features of those parts of the SDK. + +- **[Carthage](https://github.com/carthage/carthage)** + + Add the following line to your Cartfile: + ``` + github "facebook/Facebook-SDK-Swift" + ``` + Run `carthage update`, you should be able to add `FacebookCore.framework`, `FacebookLogin.framework`, and `FacebookShare.framework` to your project, along with their Facebook SDK for iOS dependencies (`FBSDKCoreKit.framework`, `FBSDKLoginKit.framework`, `FBSDKShareKit.framework` and `Bolts.framework`). + +- **Using Facebook SDK as a sub-project** + + While not recommended, it is entirely possible for you to build the Facebook SDK for Swift outside of any dependency management system. Note that you will have to manage updating this solution (as well as the dependencies on the Facebook SDK for iOS) on your own. + + After you've initialized the repository, you should add the FacebookSwift.xcodeproj as a sub-project to your application's project, and then add the `FacebookCore.framework`, `FacebookLogin.framework`, and `FacebookShare.framework` build products from that subproject to your applications 'Link Frameworks and Libraries' section, as well as your 'Embedded Binaries' section. Don't forget to also embed/link `FBSDKCoreKit.framework`, `FBSDKLoginKit.framework`, and `FBSDKShareKit.framework`, too! + +## Modules + +The frameworks for the Facebook SDK in Swift are organized in the same way that the Facebook SDK for iOS is. + +They also currently depend upon the Facebook SDK for iOS, although this may change at some point in the future. + +### FacebookCore + +[![FacebookCore on CocoaPods](https://img.shields.io/cocoapods/v/FacebookCore.svg)](https://cocoapods.org/pods/FacebookCore) + +Depends on `FBSDKCoreKit.framework` and `Bolts.framework`. + +The following types are included, with enhancements for Swift: + +- `AccessToken` +- `ApplicationDelegate` +- `AppEvents` + + A myriad of improvements, including type-safe built-in `AppEvent`s, an `AppEvent` struct, and more. + +- `GraphRequest` + + You can now implement your own type-safe `GraphRequest`s, including native-typed results. + +- `SDKSettings` + + Logging behaviors are now implemented as a type-safe set, based on Swift enums. + +- `Permission`s + + Are no longer stringly-typed (string-based), but separate types for read and write permissions (also includes a built-in permission list, which includes most common permissions by default). + +### FacebookLogin + +[![FacebookCore on CocoaPods](https://img.shields.io/cocoapods/v/FacebookLogin.svg)](https://cocoapods.org/pods/FacebookLogin) + +Depends on `FacebookCore.framework` and `FBSDKLoginKit.framework`. + +The following types are included, with enhancements for Swift: + +- `LoginManager` + + Now uses the type-safe permissions from `FacebookCore`, and has constructors with `LoginBehavior` and `DefaultAudience`, instead of requiring manual setting of properties. + +- `LoginButton` + + Can no longer change permissions after creation, helping to enforce using a single login button for a given set of permissions. + Note that `LoginButton` is not intended to work with interface builder or storyboards at this time. We may re-address this in the future. + +### FacebookShare + +[![FacebookCore on CocoaPods](https://img.shields.io/cocoapods/v/FacebookShare.svg)](https://cocoapods.org/pods/FacebookShare) + +Depends on `FacebookCore.framework` and `FBSDKShareKit.framework`. + +The following types are included, with enhancements for Swift: + +- `LinkShareContent` + + Now a struct, and has a proper initializer enforcing required properties. + +- `OpenGraphShareContent` + + Now a struct, uses type-safe `OpenGraphPropertyName` and `OpenGraphPropertyValue`, as well as structs for `OpenGraphObject` and `OpenGraphAction`. + +- `PhotoShareContent` + + Now a struct, and better type-safety for properties on it. + +- `VideoShareContent` + + Now a struct, and better type-safety for properties on it. + +- `GraphSharer` + + Now a generic type, that can handle any type of content. + +- `ShareDialog` + + Now a generic type, that can handle any type of content. + +- `MessageDialog` + + Now a generic type, that can handle any type of content. + +- `GameRequest` + + Now a struct, contains proper type-safe enum for `Recipient`, `Result`. + +- `GameRequest.Dialog` +- `AppGroupRequest.Dialog` +- `AppInvite` + + Now a struct, use a type-safe `Promotion` property, instead of separate `promotionCode` and `promotionText`. + +- `AppInvite.Dialog` + +## Give Feedback + +Facebook SDK in Swift is still in beta, and we would love to hear your thoughts and feedback on it. + +- **Have an idea or feature request?** [Open an issue](https://github.com/facebook/facebook-sdk-swift/issues/new). Tell us more about the feature or an idea and why you think it's relevant. +- **Have a bug to report?** [Open an issue](https://github.com/facebook/facebook-sdk-swift/issues/new). If possible, include the version of the SDK you are using, and any technical details. +- **Need help with your code?** Join [Facebook Developers Group](https://www.facebook.com/groups/fbdevelopers) on Facebook or ask questions on [Stack Overflow](https://facebook.stackoverflow.com). + +## Contribute + +All of Facebook SDK for Swift development happens on GitHub. +Contributions make for good karma and we welcome new contributors with tremendous joy. +We encourage you also to read our [contributing guidelines](https://github.com/facebook/facebook-sdk-swift/blob/master/CONTRIBUTING.md) before submitting a Pull Request. + +## LICENSE + +See the [`LICENSE`](https://github.com/facebook/facebook-sdk-swift/blob/master/LICENSE) file. diff --git a/Rakefile b/Rakefile new file mode 100644 index 00000000..d913e42d --- /dev/null +++ b/Rakefile @@ -0,0 +1,78 @@ +# 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. + +require 'mkmf' +require 'json' + +# Make FileUtils less verbose aka not log anything when running `mkdir_p` +Rake::FileUtilsExt.verbose(false) + +WORKSPACE = 'FacebookSwift.xcworkspace' +SCHEMES = [ + 'FacebookCore', + 'FacebookLogin', + 'FacebookShare', +] + +DOCS_FOLDER = '.docs' +JAZZY_CONFIG = '.jazzy.json' + +def which(cmd) + exts = ENV['PATHEXT'] ? ENV['PATHEXT'].split(';') : [''] + ENV['PATH'].split(File::PATH_SEPARATOR).each do |path| + exts.each { |ext| + exe = File.join(path, "#{cmd}#{ext}") + return exe if File.executable?(exe) && !File.directory?(exe) + } + end + return nil +end + +desc 'Generate API Reference' +task :docs do + unless which('sourcekitten') && which('jazzy') + puts 'Can\'t find sourcekitten and jazzy in $PATH.' + puts 'Install them with \'brew install sourcekitten\' and \'gem install jazzy\'.' + exit 1 + end + + declarations = [] + SCHEMES.each do |scheme| + json_string = `sourcekitten doc -- -scheme #{scheme} -workspace #{WORKSPACE}` + json = JSON.parse(json_string) + + json = json.map do |element| + element.map do |key,value| + value["key.substructure"] = value["key.substructure"].select do |element| + element["key.name"] != "==(_:_:)" + end + [key, value] + end.to_h + end + declarations.concat json + end + + mkdir_p '.docs' + + sourcekit_file = File.join(DOCS_FOLDER, 'source.json') + File.open(sourcekit_file, 'w') do |f| + f.write(declarations.to_json) + end + + `jazzy --config #{JAZZY_CONFIG} --sourcekitten-sourcefile #{sourcekit_file} --output #{DOCS_FOLDER}` +end diff --git a/Resources/Info.plist b/Resources/Info.plist new file mode 100644 index 00000000..e01fb9a1 --- /dev/null +++ b/Resources/Info.plist @@ -0,0 +1,26 @@ + + + + + CFBundleDevelopmentRegion + en + CFBundleExecutable + $(EXECUTABLE_NAME) + CFBundleIdentifier + $(PRODUCT_BUNDLE_IDENTIFIER) + CFBundleInfoDictionaryVersion + 6.0 + CFBundleName + $(PRODUCT_NAME) + CFBundlePackageType + FMWK + CFBundleShortVersionString + $(FBSDK_SWIFT_VERSION) + CFBundleSignature + ???? + CFBundleVersion + $(FBSDK_SWIFT_VERSION) + NSPrincipalClass + + + diff --git a/Samples/Catalog/Configurations/Shared b/Samples/Catalog/Configurations/Shared new file mode 120000 index 00000000..cbb7fe7d --- /dev/null +++ b/Samples/Catalog/Configurations/Shared @@ -0,0 +1 @@ +../../../Configurations/Shared \ No newline at end of file diff --git a/Samples/Catalog/Configurations/SwiftCatalog.xcconfig b/Samples/Catalog/Configurations/SwiftCatalog.xcconfig new file mode 100644 index 00000000..416b6567 --- /dev/null +++ b/Samples/Catalog/Configurations/SwiftCatalog.xcconfig @@ -0,0 +1,31 @@ +// 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. + +#include "Shared/Platform/iOS.xcconfig" +#include "Shared/Product/Application.xcconfig" + +PRODUCT_NAME = SwiftCatalog +PRODUCT_BUNDLE_IDENTIFIER = com.facebook.SwiftCatalog + +CODE_SIGN_IDENTITY = iPhone Developer + +SWIFT_VERSION = 2.3 + +IPHONEOS_DEPLOYMENT_TARGET = 8.0 + +INFOPLIST_FILE = $(SRCROOT)/Resources/Info.plist diff --git a/Samples/Catalog/Resources/Assets.xcassets/AppEventsIcon.imageset/AppEventsIcon.png b/Samples/Catalog/Resources/Assets.xcassets/AppEventsIcon.imageset/AppEventsIcon.png new file mode 100644 index 00000000..a3fbed34 Binary files /dev/null and b/Samples/Catalog/Resources/Assets.xcassets/AppEventsIcon.imageset/AppEventsIcon.png differ diff --git a/Samples/Catalog/Resources/Assets.xcassets/AppEventsIcon.imageset/Contents.json b/Samples/Catalog/Resources/Assets.xcassets/AppEventsIcon.imageset/Contents.json new file mode 100644 index 00000000..519aef9e --- /dev/null +++ b/Samples/Catalog/Resources/Assets.xcassets/AppEventsIcon.imageset/Contents.json @@ -0,0 +1,21 @@ +{ + "images" : [ + { + "idiom" : "universal", + "filename" : "AppEventsIcon.png", + "scale" : "1x" + }, + { + "idiom" : "universal", + "scale" : "2x" + }, + { + "idiom" : "universal", + "scale" : "3x" + } + ], + "info" : { + "version" : 1, + "author" : "xcode" + } +} \ No newline at end of file diff --git a/Samples/Catalog/Resources/Assets.xcassets/AppIcon.appiconset/Contents.json b/Samples/Catalog/Resources/Assets.xcassets/AppIcon.appiconset/Contents.json new file mode 100644 index 00000000..a4b3297e --- /dev/null +++ b/Samples/Catalog/Resources/Assets.xcassets/AppIcon.appiconset/Contents.json @@ -0,0 +1,128 @@ +{ + "images" : [ + { + "size" : "29x29", + "idiom" : "iphone", + "filename" : "Icon-Small.png", + "scale" : "1x" + }, + { + "size" : "29x29", + "idiom" : "iphone", + "filename" : "Icon-Small@2x.png", + "scale" : "2x" + }, + { + "size" : "29x29", + "idiom" : "iphone", + "filename" : "Icon-Small@3x.png", + "scale" : "3x" + }, + { + "size" : "40x40", + "idiom" : "iphone", + "filename" : "Icon-40@2x.png", + "scale" : "2x" + }, + { + "size" : "40x40", + "idiom" : "iphone", + "filename" : "Icon-40@3x.png", + "scale" : "3x" + }, + { + "size" : "57x57", + "idiom" : "iphone", + "filename" : "Icon.png", + "scale" : "1x" + }, + { + "size" : "57x57", + "idiom" : "iphone", + "filename" : "Icon@2x.png", + "scale" : "2x" + }, + { + "size" : "60x60", + "idiom" : "iphone", + "filename" : "Icon-60@2x.png", + "scale" : "2x" + }, + { + "size" : "60x60", + "idiom" : "iphone", + "filename" : "Icon-60@3x.png", + "scale" : "3x" + }, + { + "size" : "29x29", + "idiom" : "ipad", + "filename" : "Icon-Small.png", + "scale" : "1x" + }, + { + "size" : "29x29", + "idiom" : "ipad", + "filename" : "Icon-Small@2x.png", + "scale" : "2x" + }, + { + "size" : "40x40", + "idiom" : "ipad", + "filename" : "Icon-40.png", + "scale" : "1x" + }, + { + "size" : "40x40", + "idiom" : "ipad", + "filename" : "Icon-40@2x.png", + "scale" : "2x" + }, + { + "size" : "50x50", + "idiom" : "ipad", + "filename" : "Icon-Small-50.png", + "scale" : "1x" + }, + { + "size" : "50x50", + "idiom" : "ipad", + "filename" : "Icon-Small-50@2x.png", + "scale" : "2x" + }, + { + "size" : "72x72", + "idiom" : "ipad", + "filename" : "Icon-72.png", + "scale" : "1x" + }, + { + "size" : "72x72", + "idiom" : "ipad", + "filename" : "Icon-72@2x.png", + "scale" : "2x" + }, + { + "size" : "76x76", + "idiom" : "ipad", + "filename" : "Icon-76.png", + "scale" : "1x" + }, + { + "size" : "76x76", + "idiom" : "ipad", + "filename" : "Icon-76@2x.png", + "scale" : "2x" + }, + { + "size" : "83.5x83.5", + "idiom" : "ipad", + "filename" : "Icon-83.5@2x.png", + "scale" : "2x" + } + ], + "info" : { + "version" : 1, + "author" : "xcode" + } +} \ No newline at end of file diff --git a/Samples/Catalog/Resources/Assets.xcassets/AppIcon.appiconset/Icon-40.png b/Samples/Catalog/Resources/Assets.xcassets/AppIcon.appiconset/Icon-40.png new file mode 100644 index 00000000..4c7cf09f Binary files /dev/null and b/Samples/Catalog/Resources/Assets.xcassets/AppIcon.appiconset/Icon-40.png differ diff --git a/Samples/Catalog/Resources/Assets.xcassets/AppIcon.appiconset/Icon-40@2x.png b/Samples/Catalog/Resources/Assets.xcassets/AppIcon.appiconset/Icon-40@2x.png new file mode 100644 index 00000000..8fb0159e Binary files /dev/null and b/Samples/Catalog/Resources/Assets.xcassets/AppIcon.appiconset/Icon-40@2x.png differ diff --git a/Samples/Catalog/Resources/Assets.xcassets/AppIcon.appiconset/Icon-40@3x.png b/Samples/Catalog/Resources/Assets.xcassets/AppIcon.appiconset/Icon-40@3x.png new file mode 100644 index 00000000..6ada6299 Binary files /dev/null and b/Samples/Catalog/Resources/Assets.xcassets/AppIcon.appiconset/Icon-40@3x.png differ diff --git a/Samples/Catalog/Resources/Assets.xcassets/AppIcon.appiconset/Icon-60@2x.png b/Samples/Catalog/Resources/Assets.xcassets/AppIcon.appiconset/Icon-60@2x.png new file mode 100644 index 00000000..6ada6299 Binary files /dev/null and b/Samples/Catalog/Resources/Assets.xcassets/AppIcon.appiconset/Icon-60@2x.png differ diff --git a/Samples/Catalog/Resources/Assets.xcassets/AppIcon.appiconset/Icon-60@3x.png b/Samples/Catalog/Resources/Assets.xcassets/AppIcon.appiconset/Icon-60@3x.png new file mode 100644 index 00000000..417a29df Binary files /dev/null and b/Samples/Catalog/Resources/Assets.xcassets/AppIcon.appiconset/Icon-60@3x.png differ diff --git a/Samples/Catalog/Resources/Assets.xcassets/AppIcon.appiconset/Icon-72.png b/Samples/Catalog/Resources/Assets.xcassets/AppIcon.appiconset/Icon-72.png new file mode 100644 index 00000000..d4a29d72 Binary files /dev/null and b/Samples/Catalog/Resources/Assets.xcassets/AppIcon.appiconset/Icon-72.png differ diff --git a/Samples/Catalog/Resources/Assets.xcassets/AppIcon.appiconset/Icon-72@2x.png b/Samples/Catalog/Resources/Assets.xcassets/AppIcon.appiconset/Icon-72@2x.png new file mode 100644 index 00000000..0082bb99 Binary files /dev/null and b/Samples/Catalog/Resources/Assets.xcassets/AppIcon.appiconset/Icon-72@2x.png differ diff --git a/Samples/Catalog/Resources/Assets.xcassets/AppIcon.appiconset/Icon-76.png b/Samples/Catalog/Resources/Assets.xcassets/AppIcon.appiconset/Icon-76.png new file mode 100644 index 00000000..00454f1a Binary files /dev/null and b/Samples/Catalog/Resources/Assets.xcassets/AppIcon.appiconset/Icon-76.png differ diff --git a/Samples/Catalog/Resources/Assets.xcassets/AppIcon.appiconset/Icon-76@2x.png b/Samples/Catalog/Resources/Assets.xcassets/AppIcon.appiconset/Icon-76@2x.png new file mode 100644 index 00000000..834a1cbe Binary files /dev/null and b/Samples/Catalog/Resources/Assets.xcassets/AppIcon.appiconset/Icon-76@2x.png differ diff --git a/Samples/Catalog/Resources/Assets.xcassets/AppIcon.appiconset/Icon-83.5@2x.png b/Samples/Catalog/Resources/Assets.xcassets/AppIcon.appiconset/Icon-83.5@2x.png new file mode 100644 index 00000000..3487928e Binary files /dev/null and b/Samples/Catalog/Resources/Assets.xcassets/AppIcon.appiconset/Icon-83.5@2x.png differ diff --git a/Samples/Catalog/Resources/Assets.xcassets/AppIcon.appiconset/Icon-Small-50.png b/Samples/Catalog/Resources/Assets.xcassets/AppIcon.appiconset/Icon-Small-50.png new file mode 100644 index 00000000..c5ae8070 Binary files /dev/null and b/Samples/Catalog/Resources/Assets.xcassets/AppIcon.appiconset/Icon-Small-50.png differ diff --git a/Samples/Catalog/Resources/Assets.xcassets/AppIcon.appiconset/Icon-Small-50@2x.png b/Samples/Catalog/Resources/Assets.xcassets/AppIcon.appiconset/Icon-Small-50@2x.png new file mode 100644 index 00000000..eddb07a1 Binary files /dev/null and b/Samples/Catalog/Resources/Assets.xcassets/AppIcon.appiconset/Icon-Small-50@2x.png differ diff --git a/Samples/Catalog/Resources/Assets.xcassets/AppIcon.appiconset/Icon-Small.png b/Samples/Catalog/Resources/Assets.xcassets/AppIcon.appiconset/Icon-Small.png new file mode 100644 index 00000000..5ea1874d Binary files /dev/null and b/Samples/Catalog/Resources/Assets.xcassets/AppIcon.appiconset/Icon-Small.png differ diff --git a/Samples/Catalog/Resources/Assets.xcassets/AppIcon.appiconset/Icon-Small@2x.png b/Samples/Catalog/Resources/Assets.xcassets/AppIcon.appiconset/Icon-Small@2x.png new file mode 100644 index 00000000..3b5206d9 Binary files /dev/null and b/Samples/Catalog/Resources/Assets.xcassets/AppIcon.appiconset/Icon-Small@2x.png differ diff --git a/Samples/Catalog/Resources/Assets.xcassets/AppIcon.appiconset/Icon-Small@3x.png b/Samples/Catalog/Resources/Assets.xcassets/AppIcon.appiconset/Icon-Small@3x.png new file mode 100644 index 00000000..2e2ceeb6 Binary files /dev/null and b/Samples/Catalog/Resources/Assets.xcassets/AppIcon.appiconset/Icon-Small@3x.png differ diff --git a/Samples/Catalog/Resources/Assets.xcassets/AppIcon.appiconset/Icon.png b/Samples/Catalog/Resources/Assets.xcassets/AppIcon.appiconset/Icon.png new file mode 100644 index 00000000..bfc9bda6 Binary files /dev/null and b/Samples/Catalog/Resources/Assets.xcassets/AppIcon.appiconset/Icon.png differ diff --git a/Samples/Catalog/Resources/Assets.xcassets/AppIcon.appiconset/Icon@2x.png b/Samples/Catalog/Resources/Assets.xcassets/AppIcon.appiconset/Icon@2x.png new file mode 100644 index 00000000..c2a4cdba Binary files /dev/null and b/Samples/Catalog/Resources/Assets.xcassets/AppIcon.appiconset/Icon@2x.png differ diff --git a/Samples/Catalog/Resources/Assets.xcassets/AppInvitesIcon.imageset/AppInvitesIcon.png b/Samples/Catalog/Resources/Assets.xcassets/AppInvitesIcon.imageset/AppInvitesIcon.png new file mode 100644 index 00000000..e33f27c5 Binary files /dev/null and b/Samples/Catalog/Resources/Assets.xcassets/AppInvitesIcon.imageset/AppInvitesIcon.png differ diff --git a/Samples/Catalog/Resources/Assets.xcassets/AppInvitesIcon.imageset/Contents.json b/Samples/Catalog/Resources/Assets.xcassets/AppInvitesIcon.imageset/Contents.json new file mode 100644 index 00000000..580b7b46 --- /dev/null +++ b/Samples/Catalog/Resources/Assets.xcassets/AppInvitesIcon.imageset/Contents.json @@ -0,0 +1,21 @@ +{ + "images" : [ + { + "idiom" : "universal", + "filename" : "AppInvitesIcon.png", + "scale" : "1x" + }, + { + "idiom" : "universal", + "scale" : "2x" + }, + { + "idiom" : "universal", + "scale" : "3x" + } + ], + "info" : { + "version" : 1, + "author" : "xcode" + } +} \ No newline at end of file diff --git a/Samples/Catalog/Resources/Assets.xcassets/AppLinksIcon.imageset/AppLinksIcon.png b/Samples/Catalog/Resources/Assets.xcassets/AppLinksIcon.imageset/AppLinksIcon.png new file mode 100644 index 00000000..ade0e439 Binary files /dev/null and b/Samples/Catalog/Resources/Assets.xcassets/AppLinksIcon.imageset/AppLinksIcon.png differ diff --git a/Samples/Catalog/Resources/Assets.xcassets/AppLinksIcon.imageset/Contents.json b/Samples/Catalog/Resources/Assets.xcassets/AppLinksIcon.imageset/Contents.json new file mode 100644 index 00000000..5a16b22a --- /dev/null +++ b/Samples/Catalog/Resources/Assets.xcassets/AppLinksIcon.imageset/Contents.json @@ -0,0 +1,21 @@ +{ + "images" : [ + { + "idiom" : "universal", + "filename" : "AppLinksIcon.png", + "scale" : "1x" + }, + { + "idiom" : "universal", + "scale" : "2x" + }, + { + "idiom" : "universal", + "scale" : "3x" + } + ], + "info" : { + "version" : 1, + "author" : "xcode" + } +} \ No newline at end of file diff --git a/Samples/Catalog/Resources/Assets.xcassets/Contents.json b/Samples/Catalog/Resources/Assets.xcassets/Contents.json new file mode 100644 index 00000000..da4a164c --- /dev/null +++ b/Samples/Catalog/Resources/Assets.xcassets/Contents.json @@ -0,0 +1,6 @@ +{ + "info" : { + "version" : 1, + "author" : "xcode" + } +} \ No newline at end of file diff --git a/Samples/Catalog/Resources/Assets.xcassets/GraphAPIIcon.imageset/Contents.json b/Samples/Catalog/Resources/Assets.xcassets/GraphAPIIcon.imageset/Contents.json new file mode 100644 index 00000000..266ad2f5 --- /dev/null +++ b/Samples/Catalog/Resources/Assets.xcassets/GraphAPIIcon.imageset/Contents.json @@ -0,0 +1,21 @@ +{ + "images" : [ + { + "idiom" : "universal", + "filename" : "GraphAPIIcon.png", + "scale" : "1x" + }, + { + "idiom" : "universal", + "scale" : "2x" + }, + { + "idiom" : "universal", + "scale" : "3x" + } + ], + "info" : { + "version" : 1, + "author" : "xcode" + } +} \ No newline at end of file diff --git a/Samples/Catalog/Resources/Assets.xcassets/GraphAPIIcon.imageset/GraphAPIIcon.png b/Samples/Catalog/Resources/Assets.xcassets/GraphAPIIcon.imageset/GraphAPIIcon.png new file mode 100644 index 00000000..2d3604e4 Binary files /dev/null and b/Samples/Catalog/Resources/Assets.xcassets/GraphAPIIcon.imageset/GraphAPIIcon.png differ diff --git a/Samples/Catalog/Resources/Assets.xcassets/LaunchImage.launchimage/1024x768.png b/Samples/Catalog/Resources/Assets.xcassets/LaunchImage.launchimage/1024x768.png new file mode 100644 index 00000000..917157c5 Binary files /dev/null and b/Samples/Catalog/Resources/Assets.xcassets/LaunchImage.launchimage/1024x768.png differ diff --git a/Samples/Catalog/Resources/Assets.xcassets/LaunchImage.launchimage/1242x2208.png b/Samples/Catalog/Resources/Assets.xcassets/LaunchImage.launchimage/1242x2208.png new file mode 100644 index 00000000..2c35d363 Binary files /dev/null and b/Samples/Catalog/Resources/Assets.xcassets/LaunchImage.launchimage/1242x2208.png differ diff --git a/Samples/Catalog/Resources/Assets.xcassets/LaunchImage.launchimage/1536x2048.png b/Samples/Catalog/Resources/Assets.xcassets/LaunchImage.launchimage/1536x2048.png new file mode 100644 index 00000000..ee565b6f Binary files /dev/null and b/Samples/Catalog/Resources/Assets.xcassets/LaunchImage.launchimage/1536x2048.png differ diff --git a/Samples/Catalog/Resources/Assets.xcassets/LaunchImage.launchimage/2048x1536.png b/Samples/Catalog/Resources/Assets.xcassets/LaunchImage.launchimage/2048x1536.png new file mode 100644 index 00000000..4d97ff2c Binary files /dev/null and b/Samples/Catalog/Resources/Assets.xcassets/LaunchImage.launchimage/2048x1536.png differ diff --git a/Samples/Catalog/Resources/Assets.xcassets/LaunchImage.launchimage/2208x1242.png b/Samples/Catalog/Resources/Assets.xcassets/LaunchImage.launchimage/2208x1242.png new file mode 100644 index 00000000..9d7135d8 Binary files /dev/null and b/Samples/Catalog/Resources/Assets.xcassets/LaunchImage.launchimage/2208x1242.png differ diff --git a/Samples/Catalog/Resources/Assets.xcassets/LaunchImage.launchimage/640x1136.png b/Samples/Catalog/Resources/Assets.xcassets/LaunchImage.launchimage/640x1136.png new file mode 100644 index 00000000..dc550adf Binary files /dev/null and b/Samples/Catalog/Resources/Assets.xcassets/LaunchImage.launchimage/640x1136.png differ diff --git a/Samples/Catalog/Resources/Assets.xcassets/LaunchImage.launchimage/640x960.png b/Samples/Catalog/Resources/Assets.xcassets/LaunchImage.launchimage/640x960.png new file mode 100644 index 00000000..50fea23b Binary files /dev/null and b/Samples/Catalog/Resources/Assets.xcassets/LaunchImage.launchimage/640x960.png differ diff --git a/Samples/Catalog/Resources/Assets.xcassets/LaunchImage.launchimage/750x1334.png b/Samples/Catalog/Resources/Assets.xcassets/LaunchImage.launchimage/750x1334.png new file mode 100644 index 00000000..894a71bf Binary files /dev/null and b/Samples/Catalog/Resources/Assets.xcassets/LaunchImage.launchimage/750x1334.png differ diff --git a/Samples/Catalog/Resources/Assets.xcassets/LaunchImage.launchimage/768x1024.png b/Samples/Catalog/Resources/Assets.xcassets/LaunchImage.launchimage/768x1024.png new file mode 100644 index 00000000..8b8470a1 Binary files /dev/null and b/Samples/Catalog/Resources/Assets.xcassets/LaunchImage.launchimage/768x1024.png differ diff --git a/Samples/Catalog/Resources/Assets.xcassets/LaunchImage.launchimage/Contents.json b/Samples/Catalog/Resources/Assets.xcassets/LaunchImage.launchimage/Contents.json new file mode 100644 index 00000000..4499d2b1 --- /dev/null +++ b/Samples/Catalog/Resources/Assets.xcassets/LaunchImage.launchimage/Contents.json @@ -0,0 +1,84 @@ +{ + "images" : [ + { + "extent" : "full-screen", + "idiom" : "iphone", + "subtype" : "736h", + "filename" : "1242x2208.png", + "minimum-system-version" : "8.0", + "orientation" : "portrait", + "scale" : "3x" + }, + { + "extent" : "full-screen", + "idiom" : "iphone", + "subtype" : "736h", + "filename" : "2208x1242.png", + "minimum-system-version" : "8.0", + "orientation" : "landscape", + "scale" : "3x" + }, + { + "extent" : "full-screen", + "idiom" : "iphone", + "subtype" : "667h", + "filename" : "750x1334.png", + "minimum-system-version" : "8.0", + "orientation" : "portrait", + "scale" : "2x" + }, + { + "orientation" : "portrait", + "idiom" : "iphone", + "filename" : "640x960.png", + "extent" : "full-screen", + "minimum-system-version" : "7.0", + "scale" : "2x" + }, + { + "extent" : "full-screen", + "idiom" : "iphone", + "subtype" : "retina4", + "filename" : "640x1136.png", + "minimum-system-version" : "7.0", + "orientation" : "portrait", + "scale" : "2x" + }, + { + "orientation" : "portrait", + "idiom" : "ipad", + "filename" : "768x1024.png", + "extent" : "full-screen", + "minimum-system-version" : "7.0", + "scale" : "1x" + }, + { + "orientation" : "landscape", + "idiom" : "ipad", + "filename" : "1024x768.png", + "extent" : "full-screen", + "minimum-system-version" : "7.0", + "scale" : "1x" + }, + { + "orientation" : "portrait", + "idiom" : "ipad", + "filename" : "1536x2048.png", + "extent" : "full-screen", + "minimum-system-version" : "7.0", + "scale" : "2x" + }, + { + "orientation" : "landscape", + "idiom" : "ipad", + "filename" : "2048x1536.png", + "extent" : "full-screen", + "minimum-system-version" : "7.0", + "scale" : "2x" + } + ], + "info" : { + "version" : 1, + "author" : "xcode" + } +} \ No newline at end of file diff --git a/Samples/Catalog/Resources/Assets.xcassets/LoginIcon.imageset/Contents.json b/Samples/Catalog/Resources/Assets.xcassets/LoginIcon.imageset/Contents.json new file mode 100644 index 00000000..b343ea24 --- /dev/null +++ b/Samples/Catalog/Resources/Assets.xcassets/LoginIcon.imageset/Contents.json @@ -0,0 +1,21 @@ +{ + "images" : [ + { + "idiom" : "universal", + "filename" : "LoginIcon.png", + "scale" : "1x" + }, + { + "idiom" : "universal", + "scale" : "2x" + }, + { + "idiom" : "universal", + "scale" : "3x" + } + ], + "info" : { + "version" : 1, + "author" : "xcode" + } +} \ No newline at end of file diff --git a/Samples/Catalog/Resources/Assets.xcassets/LoginIcon.imageset/LoginIcon.png b/Samples/Catalog/Resources/Assets.xcassets/LoginIcon.imageset/LoginIcon.png new file mode 100644 index 00000000..cce9403a Binary files /dev/null and b/Samples/Catalog/Resources/Assets.xcassets/LoginIcon.imageset/LoginIcon.png differ diff --git a/Samples/Catalog/Resources/Assets.xcassets/ShareIcon.imageset/Contents.json b/Samples/Catalog/Resources/Assets.xcassets/ShareIcon.imageset/Contents.json new file mode 100644 index 00000000..987e4dd0 --- /dev/null +++ b/Samples/Catalog/Resources/Assets.xcassets/ShareIcon.imageset/Contents.json @@ -0,0 +1,21 @@ +{ + "images" : [ + { + "idiom" : "universal", + "filename" : "ShareIcon.png", + "scale" : "1x" + }, + { + "idiom" : "universal", + "scale" : "2x" + }, + { + "idiom" : "universal", + "scale" : "3x" + } + ], + "info" : { + "version" : 1, + "author" : "xcode" + } +} \ No newline at end of file diff --git a/Samples/Catalog/Resources/Assets.xcassets/ShareIcon.imageset/ShareIcon.png b/Samples/Catalog/Resources/Assets.xcassets/ShareIcon.imageset/ShareIcon.png new file mode 100644 index 00000000..339438c7 Binary files /dev/null and b/Samples/Catalog/Resources/Assets.xcassets/ShareIcon.imageset/ShareIcon.png differ diff --git a/Samples/Catalog/Resources/Assets.xcassets/sky.dataset/Contents.json b/Samples/Catalog/Resources/Assets.xcassets/sky.dataset/Contents.json new file mode 100644 index 00000000..5a762606 --- /dev/null +++ b/Samples/Catalog/Resources/Assets.xcassets/sky.dataset/Contents.json @@ -0,0 +1,12 @@ +{ + "info" : { + "version" : 1, + "author" : "xcode" + }, + "data" : [ + { + "idiom" : "universal", + "filename" : "sky.mp4" + } + ] +} \ No newline at end of file diff --git a/Samples/Catalog/Resources/Assets.xcassets/sky.dataset/sky.mp4 b/Samples/Catalog/Resources/Assets.xcassets/sky.dataset/sky.mp4 new file mode 100644 index 00000000..fc723aeb Binary files /dev/null and b/Samples/Catalog/Resources/Assets.xcassets/sky.dataset/sky.mp4 differ diff --git a/Samples/Catalog/Resources/Assets.xcassets/sky.imageset/Contents.json b/Samples/Catalog/Resources/Assets.xcassets/sky.imageset/Contents.json new file mode 100644 index 00000000..f4cf6359 --- /dev/null +++ b/Samples/Catalog/Resources/Assets.xcassets/sky.imageset/Contents.json @@ -0,0 +1,21 @@ +{ + "images" : [ + { + "idiom" : "universal", + "filename" : "sky.jpg", + "scale" : "1x" + }, + { + "idiom" : "universal", + "scale" : "2x" + }, + { + "idiom" : "universal", + "scale" : "3x" + } + ], + "info" : { + "version" : 1, + "author" : "xcode" + } +} \ No newline at end of file diff --git a/Samples/Catalog/Resources/Assets.xcassets/sky.imageset/sky.jpg b/Samples/Catalog/Resources/Assets.xcassets/sky.imageset/sky.jpg new file mode 100644 index 00000000..0c9a51d9 Binary files /dev/null and b/Samples/Catalog/Resources/Assets.xcassets/sky.imageset/sky.jpg differ diff --git a/Samples/Catalog/Resources/Base.lproj/LaunchScreen.storyboard b/Samples/Catalog/Resources/Base.lproj/LaunchScreen.storyboard new file mode 100644 index 00000000..bb0fc345 --- /dev/null +++ b/Samples/Catalog/Resources/Base.lproj/LaunchScreen.storyboard @@ -0,0 +1,44 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Samples/Catalog/Resources/Base.lproj/Main.storyboard b/Samples/Catalog/Resources/Base.lproj/Main.storyboard new file mode 100644 index 00000000..f3ee1872 --- /dev/null +++ b/Samples/Catalog/Resources/Base.lproj/Main.storyboard @@ -0,0 +1,1391 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Samples/Catalog/Resources/Info.plist b/Samples/Catalog/Resources/Info.plist new file mode 100644 index 00000000..f961f61a --- /dev/null +++ b/Samples/Catalog/Resources/Info.plist @@ -0,0 +1,70 @@ + + + + + CFBundleDevelopmentRegion + en + CFBundleDisplayName + FBSDKCatalog + CFBundleExecutable + $(EXECUTABLE_NAME) + CFBundleIdentifier + $(PRODUCT_BUNDLE_IDENTIFIER) + CFBundleInfoDictionaryVersion + 6.0 + CFBundleName + $(PRODUCT_NAME) + CFBundlePackageType + APPL + CFBundleShortVersionString + 1.0 + CFBundleSignature + ???? + CFBundleURLTypes + + + CFBundleURLSchemes + + catalogApp + + + + CFBundleURLSchemes + + fb1532460837044551 + + + + CFBundleVersion + 1 + FacebookAppID + 1532460837044551 + FacebookDisplayName + Catalog + LSApplicationQueriesSchemes + + fbapi + fb-messenger-api + fbauth2 + fbshareextension + + LSRequiresIPhoneOS + + NSMainStoryboardFile + Main + UILaunchStoryboardName + LaunchScreen + UIMainStoryboardFile + Main + UIRequiredDeviceCapabilities + + armv7 + + UISupportedInterfaceOrientations + + UIInterfaceOrientationPortrait + UIInterfaceOrientationLandscapeLeft + UIInterfaceOrientationLandscapeRight + + + diff --git a/Samples/Catalog/Sources/AlertController.swift b/Samples/Catalog/Sources/AlertController.swift new file mode 100644 index 00000000..a402d932 --- /dev/null +++ b/Samples/Catalog/Sources/AlertController.swift @@ -0,0 +1,30 @@ +// 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 Foundation +import UIKit + +extension UIAlertController { + convenience init(title: String, message: String) { + self.init(title: title, message: message, preferredStyle: .Alert) + let action = UIAlertAction(title: NSLocalizedString("OK", comment: "OK action"), + style: .Default, + handler: nil) + addAction(action) + } +} diff --git a/Samples/Catalog/Sources/AppDelegate.swift b/Samples/Catalog/Sources/AppDelegate.swift new file mode 100644 index 00000000..2f9d1518 --- /dev/null +++ b/Samples/Catalog/Sources/AppDelegate.swift @@ -0,0 +1,46 @@ +// 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 Foundation +import UIKit + +import FacebookCore + +@UIApplicationMain +final class AppDelegate: UIResponder { + var window: UIWindow? +} + +extension AppDelegate: UIApplicationDelegate { + func application(application: UIApplication, didFinishLaunchingWithOptions launchOptions: [NSObject: AnyObject]?) -> Bool { + ApplicationDelegate.shared.application(application, didFinishLaunchingWithOptions: launchOptions) + + return true + } + + func application(application: UIApplication, openURL url: NSURL, sourceApplication: String?, annotation: AnyObject) -> Bool { + return ApplicationDelegate.shared.application(application, + openURL: url, + sourceApplication: sourceApplication, + annotation: annotation) + } + + func applicationDidBecomeActive(application: UIApplication) { + AppEventsLogger.activate(application) + } +} diff --git a/Samples/Catalog/Sources/AppInviteViewController.swift b/Samples/Catalog/Sources/AppInviteViewController.swift new file mode 100644 index 00000000..b6ee3cc2 --- /dev/null +++ b/Samples/Catalog/Sources/AppInviteViewController.swift @@ -0,0 +1,54 @@ +// 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 UIKit + +import FacebookShare + +final class AppInviteViewController: UITableViewController { + func showAppInviteDialog(for appInvite: AppInvite) { + do { + try AppInvite.Dialog.show(from: self, invite: appInvite) { result in + switch result { + case .Success(let result): + print("App Invite Sent with result \(result)") + case .Failed(let error): + print("Failed to send app invite with error \(error)") + } + } + } catch let error { + print("Failed to show app invite dialog with error \(error)") + } + } +} + +extension AppInviteViewController { + @IBAction func appInviteWithDefaultImage() { + // Facebook hosted App Link is used here. See https://developers.facebook.com/docs/applinks for details. + let appInvite = AppInvite(appLink: NSURL(string: "https://fb.me/1539184863038815")!, deliveryMethod: .Facebook) + showAppInviteDialog(for: appInvite) + } + + @IBAction func appInviteWithCustomImage() { + // Facebook hosted App Link is used here. See https://developers.facebook.com/docs/applinks for details. + let appInvite = AppInvite(appLink: NSURL(string: "https://fb.me/1539184863038815")!, + deliveryMethod: .Facebook, + previewImageURL: NSURL(string: "http://catalogapp.parseapp.com/FacebookDeveloper.jpg")) + showAppInviteDialog(for: appInvite) + } +} diff --git a/Samples/Catalog/Sources/CustomAppEventViewController.swift b/Samples/Catalog/Sources/CustomAppEventViewController.swift new file mode 100644 index 00000000..5c460ab9 --- /dev/null +++ b/Samples/Catalog/Sources/CustomAppEventViewController.swift @@ -0,0 +1,46 @@ +// 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 Foundation +import UIKit +import FacebookCore + +final class CustomAppEventViewController: UIViewController { + @IBOutlet var eventNameField: UITextField? +} + +//-------------------------------------- +// MARK: - Log Custom Event +//-------------------------------------- + +extension CustomAppEventViewController { + @IBAction func logCustomEvent() { + guard let eventName = eventNameField?.text else { + let alertController = UIAlertController(title: "Invalid Event", message: "Event name can't be empty.") + presentViewController(alertController, animated: true, completion: nil) + return + } + + AppEventsLogger.log(eventName) + // View your event at https://developers.facebook.com/analytics/. + // See https://developers.facebook.com/docs/analytics for details. + + let alertController = UIAlertController(title: "Log Event", message: "Log Event Success") + presentViewController(alertController, animated: true, completion: nil) + } +} diff --git a/Samples/Catalog/Sources/GraphAPIPostViewController.swift b/Samples/Catalog/Sources/GraphAPIPostViewController.swift new file mode 100644 index 00000000..85d7a344 --- /dev/null +++ b/Samples/Catalog/Sources/GraphAPIPostViewController.swift @@ -0,0 +1,64 @@ +// 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 Foundation +import UIKit +import FacebookCore + +class GraphAPIPostViewController: UITableViewController { + func presentAlertControllerFor(result: GraphRequestResult) { + let alertController: UIAlertController + switch result { + case .Success(let response): + alertController = UIAlertController(title: "Graph Request Success", + message: "Graph Request Succeeded with response: \(response)") + case .Failed(let error): + alertController = UIAlertController(title: "Graph Request Failed", + message: "Graph Request Failed with error: \(error)") + } + presentViewController(alertController, animated: true, completion: nil) + } +} + +//-------------------------------------- +// MARK: - Post Checkin +//-------------------------------------- + +extension GraphAPIPostViewController { + /** + Posts a check-in to your feed. + + See https://developers.facebook.com/docs/graph-api/reference/user/feed for details. + Place is hardcoded here, see https://developers.facebook.com/docs/graph-api/using-graph-api/#search for building a place picker. + */ + @IBAction func postCheckin() { + let request = GraphRequest(graphPath: "/me/feed", + parameters: [ "message" : "Here I am!", "place" : "141887372509674" ], + httpMethod: .POST) + request.start { httpResponse, result in + switch result { + case .Success(let response): + print("Graph Request Succeeded: \(response)") + case .Failed(let error): + print("Graph Request Failed: \(error)") + } + + self.presentAlertControllerFor(result) + } + } +} diff --git a/Samples/Catalog/Sources/GraphAPIReadViewController.swift b/Samples/Catalog/Sources/GraphAPIReadViewController.swift new file mode 100644 index 00000000..e99c464a --- /dev/null +++ b/Samples/Catalog/Sources/GraphAPIReadViewController.swift @@ -0,0 +1,114 @@ +// 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 Foundation +import UIKit + +import FacebookCore + +class GraphAPIReadViewController: UITableViewController { + func presentAlertControllerFor(result: GraphRequestResult) { + let alertController: UIAlertController + switch result { + case .Success(let response): + alertController = UIAlertController(title: "Graph Request Success", + message: "Graph Request Succeeded with response: \(response)") + case .Failed(let error): + alertController = UIAlertController(title: "Graph Request Failed", + message: "Graph Request Failed with error: \(error)") + } + presentViewController(alertController, animated: true, completion: nil) + } +} + +//-------------------------------------- +// MARK: - Read Profile +//-------------------------------------- + +extension GraphAPIReadViewController { + /** + Fetches the currently logged in user's public profile. + + See https://developers.facebook.com/docs/graph-api/reference/user/ for details. + */ + @IBAction func readProfile() { + let request = GraphRequest(graphPath: "/me", + parameters: [ "fields" : "id, name" ], + httpMethod: .GET) + request.start { httpResponse, result in + switch result { + case .Success(let response): + print("Graph Request Succeeded: \(response)") + case .Failed(let error): + print("Graph Request Failed: \(error)") + } + + self.presentAlertControllerFor(result) + } + } +} + +//-------------------------------------- +// MARK: - Read User Events +//-------------------------------------- + +extension GraphAPIReadViewController { + /** + Fetches the currently logged in user's list of events. + */ + @IBAction func readUserEvents() { + let request = GraphRequest(graphPath: "/me/events", + parameters: [ "fields" : "data, description" ], + httpMethod: .GET) + request.start { _, result in + switch result { + case .Success(let response): + print("Graph Request Succeeded: \(response)") + case .Failed(let error): + print("Graph Request Failed: \(error)") + } + + self.presentAlertControllerFor(result) + } + } +} + +//-------------------------------------- +// MARK: - Read User Friend List +//-------------------------------------- + +extension GraphAPIReadViewController { + /** + Fetches the currently logged in user's list of facebook friends. + */ + @IBAction func readUserFriendList() { + let request = GraphRequest(graphPath: "/me/friends", + parameters: [ "fields" : "data" ], + httpMethod: .GET) + request.start { httpResponse, result in + switch result { + case .Success(let response): + print("Graph Request Succeeded: \(response)") + case .Failed(let error): + print("Graph Request Failed: \(error)") + } + + self.presentAlertControllerFor(result) + } + } +} diff --git a/Samples/Catalog/Sources/LoginButtonViewController.swift b/Samples/Catalog/Sources/LoginButtonViewController.swift new file mode 100644 index 00000000..e096bed9 --- /dev/null +++ b/Samples/Catalog/Sources/LoginButtonViewController.swift @@ -0,0 +1,40 @@ +// 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 Foundation +import UIKit +import FacebookLogin + +class LoginButtonViewController: UIViewController { + override func viewDidLoad() { + let button = LoginButton(publishPermissions: [.PublishActions]) + button.delegate = self + button.center = view.center + view.addSubview(button) + } +} + +extension LoginButtonViewController: LoginButtonDelegate { + func loginButtonDidCompleteLogin(loginButton: LoginButton, result: LoginResult) { + print("Did complete login via LoginButton with result \(result)") + } + + func loginButtonDidLogOut(loginButton: LoginButton) { + print("Did logout via LoginButton") + } +} diff --git a/Samples/Catalog/Sources/LoginManagerViewController.swift b/Samples/Catalog/Sources/LoginManagerViewController.swift new file mode 100644 index 00000000..62db060f --- /dev/null +++ b/Samples/Catalog/Sources/LoginManagerViewController.swift @@ -0,0 +1,49 @@ +// 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 Foundation +import UIKit + +import FacebookCore +import FacebookLogin + +class LoginManagerViewController: UIViewController { + @IBAction func customLogin() { + let loginManager = LoginManager() + + if AccessToken.current == nil { + loginManager.logIn([.PublicProfile, .UserFriends], viewController: self) { result in + let alertController: UIAlertController + switch result { + case .Cancelled: + alertController = UIAlertController(title: "Login Cancelled", message: "User cancelled login.") + case .Failed(let error): + alertController = UIAlertController(title: "Login Fail", message: "Login failed with error \(error)") + case .Success(let grantedPermissions, _, _): + alertController = UIAlertController(title: "Login Success", + message: "Login succeeded with granted permissions: \(grantedPermissions)") + } + self.presentViewController(alertController, animated: true, completion: nil) + } + } else { + loginManager.logOut() + let alertController = UIAlertController(title: "Logout", message: "Logged out.") + presentViewController(alertController, animated: true, completion: nil) + } + } +} diff --git a/Samples/Catalog/Sources/MainCollectionViewCell.swift b/Samples/Catalog/Sources/MainCollectionViewCell.swift new file mode 100644 index 00000000..ec44ca3d --- /dev/null +++ b/Samples/Catalog/Sources/MainCollectionViewCell.swift @@ -0,0 +1,29 @@ +// 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 UIKit + +final class MainCollectionViewCell: UICollectionViewCell { + @IBOutlet var textLabel: UILabel? + @IBOutlet var iconImageView: UIImageView? + + func updateFrom(item: MainCollectionViewController.Item) { + textLabel?.text = item.text + iconImageView?.image = item.image + } +} diff --git a/Samples/Catalog/Sources/MainCollectionViewController.swift b/Samples/Catalog/Sources/MainCollectionViewController.swift new file mode 100644 index 00000000..be498661 --- /dev/null +++ b/Samples/Catalog/Sources/MainCollectionViewController.swift @@ -0,0 +1,89 @@ +// 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 Foundation +import UIKit + +final class MainCollectionViewController: UICollectionViewController { + private let iconArray = [ + Item(text: "Login", iconImageName: "LoginIcon"), + Item(text: "Share", iconImageName: "ShareIcon"), + Item(text: "App Events", iconImageName: "AppEventsIcon"), + Item(text: "App Invites", iconImageName: "AppInvitesIcon"), + Item(text: "Graph API", iconImageName: "GraphAPIIcon") + ] + + override func viewWillAppear(animated: Bool) { + super.viewWillAppear(animated) + navigationController?.navigationBar.hidden = true + } + + override func viewWillDisappear(animated: Bool) { + super.viewWillDisappear(animated) + navigationController?.navigationBar.hidden = false + } +} + +//-------------------------------------- +// MARK: - UICollectionViewDataSource +//-------------------------------------- + +extension MainCollectionViewController { + override func numberOfSectionsInCollectionView(collectionView: UICollectionView) -> Int { + return 1 + } + + override func collectionView(collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int { + return iconArray.count + } + + override func collectionView(collectionView: UICollectionView, cellForItemAtIndexPath indexPath: NSIndexPath) -> UICollectionViewCell { + let cell = collectionView.dequeueReusableCellWithReuseIdentifier("cell", forIndexPath: indexPath) + if let menuCell = cell as? MainCollectionViewCell { + menuCell.updateFrom(iconArray[indexPath.row]) + } else { + fatalError("CollectionView provided wrong cell for indexPath \(indexPath)") + } + return cell + } +} + +//-------------------------------------- +// MARK: - UICollectionViewDelegate +//-------------------------------------- + +extension MainCollectionViewController { + override func collectionView(collectionView: UICollectionView, didSelectItemAtIndexPath indexPath: NSIndexPath) { + performSegueWithIdentifier(iconArray[indexPath.row].text, sender: self) + } +} + +//-------------------------------------- +// MARK: - Types +//-------------------------------------- + +extension MainCollectionViewController { + struct Item { + var text: String + var iconImageName: String + + var image: UIImage? { + return UIImage(named: iconImageName) + } + } +} diff --git a/Samples/Catalog/Sources/ManualAppEventViewController.swift b/Samples/Catalog/Sources/ManualAppEventViewController.swift new file mode 100644 index 00000000..fec8df24 --- /dev/null +++ b/Samples/Catalog/Sources/ManualAppEventViewController.swift @@ -0,0 +1,83 @@ +// 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 UIKit +import FacebookCore + +final class ManualAppEventViewController: UITableViewController { + @IBOutlet var purchasePriceField: UITextField? + @IBOutlet var purchaseCurrencyField: UITextField? + @IBOutlet var itemPriceField: UITextField? + @IBOutlet var itemCurrencyField: UITextField? +} + +//-------------------------------------- +// MARK: - Log Purchase +//-------------------------------------- + +extension ManualAppEventViewController { + @IBAction func logPurchase() { + guard + let priceString = purchasePriceField?.text, + let price = Double(priceString) else { + let alertController = UIAlertController(title: "Invalid Purchase Price", message: "Purchase price must be a valid number.") + presentViewController(alertController, animated: true, completion: nil) + return + } + guard let currency = purchaseCurrencyField?.text else { + let alertController = UIAlertController(title: "Invalid currency", message: "Currency cannot be empty.") + presentViewController(alertController, animated: true, completion: nil) + return + } + + let event = AppEvent.Purchased(amount: price, currency: currency) + AppEventsLogger.log(event) + // View your event at https://developers.facebook.com/analytics/. + // See https://developers.facebook.com/docs/analytics for details. + + let alertController = UIAlertController(title: "Log Event", message: "Log Event Success") + presentViewController(alertController, animated: true, completion: nil) + } +} + +//-------------------------------------- +// MARK: - Log Add To Cart +//-------------------------------------- + +extension ManualAppEventViewController { + @IBAction func logAddToCart() { + guard + let priceString = itemPriceField?.text, + let price = Double(priceString) else { + let alertController = UIAlertController(title: "Invalid Item Price", message: "Item price must be a valid number.") + presentViewController(alertController, animated: true, completion: nil) + return + } + guard let currency = itemCurrencyField?.text else { + let alertController = UIAlertController(title: "Invalid currency", message: "Currency cannot be empty.") + presentViewController(alertController, animated: true, completion: nil) + return + } + + let event = AppEvent.AddedToCart(currency: currency, valueToSum: price) + AppEventsLogger.log(event) + + let alertController = UIAlertController(title: "Log Event", message: "Log Event Success") + presentViewController(alertController, animated: true, completion: nil) + } +} diff --git a/Samples/Catalog/Sources/ShareAPIViewController.swift b/Samples/Catalog/Sources/ShareAPIViewController.swift new file mode 100644 index 00000000..1f509215 --- /dev/null +++ b/Samples/Catalog/Sources/ShareAPIViewController.swift @@ -0,0 +1,90 @@ +// 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 UIKit + +import FacebookShare + +final class ShareAPIViewController: UITableViewController { + + func share(content: C) { + var title: String = "" + var message: String = "" + + do { + try GraphSharer.share(content) { result in + switch result { + case .Success(let contentResult): + title = "Share Success" + message = "Succesfully shared: \(contentResult)" + case .Cancelled: + title = "Share Cancelled" + message = "Sharing was cancelled by user." + case .Failed(let error): + title = "Share Failed" + message = "Sharing failed with error \(error)" + } + let alertController = UIAlertController(title: title, message: message) + self.presentViewController(alertController, animated: true, completion: nil) + } + } catch (let error) { + title = "Share API Fail" + message = "Failed to invoke share API with error: \(error)" + let alertController = UIAlertController(title: title, message: message) + presentViewController(alertController, animated: true, completion: nil) + } + } +} + +//-------------------------------------- +// MARK: - Link Content +//-------------------------------------- + +extension ShareAPIViewController { + @IBAction func shareLink() { + let content = LinkShareContent(url: NSURL(string: "https://newsroom.fb.com/")!, + title: "Name: Facebook News Room", + description: "Description: The Facebook Swift SDK helps you develop Facebook integrated iOS apps.", + imageURL: NSURL(string: "https://raw.github.com/fbsamples/ios-3.x-howtos/master/Images/iossdk_logo.png")) + share(content) + } +} + +//-------------------------------------- +// MARK: - Photo Content +//-------------------------------------- + +extension ShareAPIViewController { + @IBAction func sharePhoto() { + let photo = Photo(image: UIImage(named: "sky.jpg")!, userGenerated: true) + let content = PhotoShareContent(photos: [photo]) + share(content) + } +} + +//-------------------------------------- +// MARK: - Video Content +//-------------------------------------- + +extension ShareAPIViewController { + @IBAction func shareVideo() { + let video = Video(url: NSBundle.mainBundle().URLForResource("sky", withExtension: "mp4")!) + let content = VideoShareContent(video: video) + share(content) + } +} diff --git a/Samples/Catalog/Sources/ShareDialogViewController.swift b/Samples/Catalog/Sources/ShareDialogViewController.swift new file mode 100644 index 00000000..a4ff4121 --- /dev/null +++ b/Samples/Catalog/Sources/ShareDialogViewController.swift @@ -0,0 +1,109 @@ +// 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 UIKit +import MobileCoreServices + +import FacebookShare + +final class ShareDialogViewController: UITableViewController { + + func showShareDialog(content: C, mode: ShareDialogMode = .Automatic) { + let dialog = ShareDialog(content: content) + dialog.presentingViewController = self + dialog.mode = mode + do { + try dialog.show() + } catch (let error) { + let alertController = UIAlertController(title: "Invalid share content", message: "Failed to present share dialog with error \(error)") + presentViewController(alertController, animated: true, completion: nil) + } + } +} + +//-------------------------------------- +// MARK: - Link Content +//-------------------------------------- + +extension ShareDialogViewController { + @IBAction func showLinkShareDialogModeAutomatic() { + var content = LinkShareContent(url: NSURL(string: "https://newsroom.fb.com/")!, + title: "Name: Facebook News Room", + description: "Description: The Facebook Swift SDK helps you develop Facebook integrated iOS apps.", + imageURL: NSURL(string: "https://raw.github.com/fbsamples/ios-3.x-howtos/master/Images/iossdk_logo.png")) + + // placeId is hardcoded here, see https://developers.facebook.com/docs/graph-api/using-graph-api/#search for building a place picker. + content.placeId = "166793820034304" + + showShareDialog(content, mode: .Automatic) + } + + @IBAction func showLinkShareDialogModeWeb() { + var content = LinkShareContent(url: NSURL(string: "https://newsroom.fb.com/")!, + title: "Name: Facebook News Room", + description: "Description: The Facebook Swift SDK helps you develop Facebook integrated iOS apps.", + imageURL: NSURL(string: "https://raw.github.com/fbsamples/ios-3.x-howtos/master/Images/iossdk_logo.png")) + + // placeId is hardcoded here, see https://developers.facebook.com/docs/graph-api/using-graph-api/#search for building a place picker. + content.placeId = "166793820034304" + + showShareDialog(content, mode: .Web) + } +} + +//-------------------------------------- +// MARK: - Photo Content +//-------------------------------------- + +extension ShareDialogViewController { + + @IBAction func showShareDialogPhotoContent() { + let photo = Photo(image: UIImage(named: "sky.jpg")!, userGenerated: true) + let content = PhotoShareContent(photos: [photo]) + showShareDialog(content) + } +} + +//-------------------------------------- +// MARK: - Video Content +//-------------------------------------- + +extension ShareDialogViewController { + + @IBAction func showShareDialogVideoContent() { + let imagePickerController = UIImagePickerController() + imagePickerController.delegate = self + imagePickerController.sourceType = .PhotoLibrary + imagePickerController.mediaTypes = [kUTTypeMovie as String] + presentViewController(imagePickerController, animated: true, completion: nil) + } +} + +extension ShareDialogViewController: UIImagePickerControllerDelegate, UINavigationControllerDelegate { + func imagePickerController(picker: UIImagePickerController, didFinishPickingMediaWithInfo info: [String : AnyObject]) { + picker.dismissViewControllerAnimated(true, completion: nil) + + guard let videoURL = info[UIImagePickerControllerReferenceURL] as? NSURL else { + return + } + + let video = Video(url: videoURL) + let content = VideoShareContent(video: video) + showShareDialog(content) + } +} diff --git a/Samples/Catalog/SwiftCatalog.xcodeproj/project.pbxproj b/Samples/Catalog/SwiftCatalog.xcodeproj/project.pbxproj new file mode 100644 index 00000000..6cf45586 --- /dev/null +++ b/Samples/Catalog/SwiftCatalog.xcodeproj/project.pbxproj @@ -0,0 +1,1012 @@ +// !$*UTF8*$! +{ + archiveVersion = 1; + classes = { + }; + objectVersion = 46; + objects = { + +/* Begin PBXBuildFile section */ + 813C9C241D1DB8E500288BFA /* LoginButtonViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 813C9C231D1DB8E500288BFA /* LoginButtonViewController.swift */; }; + 816B75F01D07B7030012AC43 /* GraphAPIPostViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 816B75EF1D07B7030012AC43 /* GraphAPIPostViewController.swift */; }; + 816B75FC1D07B70B0012AC43 /* GraphAPIReadViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 816B75FB1D07B70B0012AC43 /* GraphAPIReadViewController.swift */; }; + 819700A01D07921100286023 /* LoginManagerViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8197009F1D07921100286023 /* LoginManagerViewController.swift */; }; + 81AB8BA31D36D63C00066F63 /* Bolts.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 81AB8B5A1D36D60100066F63 /* Bolts.framework */; }; + 81AB8BA41D36D63C00066F63 /* FBSDKCoreKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 81AB8B771D36D60100066F63 /* FBSDKCoreKit.framework */; }; + 81AB8BA51D36D63C00066F63 /* FBSDKLoginKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 81AB8B851D36D60100066F63 /* FBSDKLoginKit.framework */; }; + 81AB8BA61D36D63C00066F63 /* FBSDKShareKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 81AB8B921D36D60100066F63 /* FBSDKShareKit.framework */; }; + 81D645311D2C590C00690609 /* AppInviteViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 81D645301D2C590C00690609 /* AppInviteViewController.swift */; }; + 81D645361D2C59AA00690609 /* ShareDialogViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 81D645351D2C59AA00690609 /* ShareDialogViewController.swift */; }; + 81D645491D2C686500690609 /* ShareAPIViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 81D645481D2C686500690609 /* ShareAPIViewController.swift */; }; + 81EDA3371D025F9B00E742F4 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 81EDA3361D025F9B00E742F4 /* AppDelegate.swift */; }; + 81F43A161D060C6E00569A1A /* FacebookCore.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 81F43A111D060C6800569A1A /* FacebookCore.framework */; }; + 81F43A171D060C6E00569A1A /* FacebookCore.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = 81F43A111D060C6800569A1A /* FacebookCore.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; + 81F43A1A1D060C6E00569A1A /* FacebookLogin.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 81F43A131D060C6800569A1A /* FacebookLogin.framework */; }; + 81F43A1B1D060C6E00569A1A /* FacebookLogin.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = 81F43A131D060C6800569A1A /* FacebookLogin.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; + 81F43A1E1D060C6F00569A1A /* FacebookShare.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 81F43A151D060C6800569A1A /* FacebookShare.framework */; }; + 81F43A1F1D060C6F00569A1A /* FacebookShare.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = 81F43A151D060C6800569A1A /* FacebookShare.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; + 81F43A7B1D060D6400569A1A /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 81EDA2D51D025EA600E742F4 /* Main.storyboard */; }; + 81F43A981D060DB300569A1A /* MainCollectionViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 81F43A971D060DB300569A1A /* MainCollectionViewController.swift */; }; + 81F43B461D0611B700569A1A /* MainCollectionViewCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 81F43B451D0611B700569A1A /* MainCollectionViewCell.swift */; }; + 81F43B8A1D06139900569A1A /* ManualAppEventViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 81F43B891D06139900569A1A /* ManualAppEventViewController.swift */; }; + 81F43B941D0613A100569A1A /* CustomAppEventViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 81F43B931D0613A100569A1A /* CustomAppEventViewController.swift */; }; + 81FC4A511D064717003F3A46 /* AlertController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 81FC4A501D064717003F3A46 /* AlertController.swift */; }; + 81FC4B061D064A39003F3A46 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 81EDA2D21D025EA600E742F4 /* Assets.xcassets */; }; +/* End PBXBuildFile section */ + +/* Begin PBXContainerItemProxy section */ + 81AB8B591D36D60100066F63 /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = 81AB8B421D36D60100066F63 /* Bolts.xcodeproj */; + proxyType = 2; + remoteGlobalIDString = 81ED94291BE147CF00795F05; + remoteInfo = "Bolts-iOS"; + }; + 81AB8B5B1D36D60100066F63 /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = 81AB8B421D36D60100066F63 /* Bolts.xcodeproj */; + proxyType = 2; + remoteGlobalIDString = 1D5D7DD31BE3CE8200FD67C7; + remoteInfo = "Bolts-iOS-Dynamic"; + }; + 81AB8B5D1D36D60100066F63 /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = 81AB8B421D36D60100066F63 /* Bolts.xcodeproj */; + proxyType = 2; + remoteGlobalIDString = 81ED946E1BE14B5200795F05; + remoteInfo = "Bolts-OSX"; + }; + 81AB8B5F1D36D60100066F63 /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = 81AB8B421D36D60100066F63 /* Bolts.xcodeproj */; + proxyType = 2; + remoteGlobalIDString = F5AFCA021BA752750076E927; + remoteInfo = "Bolts-tvOS"; + }; + 81AB8B611D36D60100066F63 /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = 81AB8B421D36D60100066F63 /* Bolts.xcodeproj */; + proxyType = 2; + remoteGlobalIDString = 81E94D6A1C2B8BF200A6291E; + remoteInfo = "Bolts-tvOS-Dynamic"; + }; + 81AB8B631D36D60100066F63 /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = 81AB8B421D36D60100066F63 /* Bolts.xcodeproj */; + proxyType = 2; + remoteGlobalIDString = 8178F99C1BB0F87700AD289D; + remoteInfo = "Bolts-watchOS"; + }; + 81AB8B651D36D60100066F63 /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = 81AB8B421D36D60100066F63 /* Bolts.xcodeproj */; + proxyType = 2; + remoteGlobalIDString = 819573F11C2B8ECB00BFCA39; + remoteInfo = "Bolts-watchOS-Dynamic"; + }; + 81AB8B671D36D60100066F63 /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = 81AB8B421D36D60100066F63 /* Bolts.xcodeproj */; + proxyType = 2; + remoteGlobalIDString = 8E8C8EE917F23D1D00E3F1C7; + remoteInfo = "BoltsTests-iOS"; + }; + 81AB8B691D36D60100066F63 /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = 81AB8B421D36D60100066F63 /* Bolts.xcodeproj */; + proxyType = 2; + remoteGlobalIDString = 8E8C8F1917F241DA00E3F1C7; + remoteInfo = "BoltsTests-OSX"; + }; + 81AB8B6B1D36D60100066F63 /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = 81AB8B421D36D60100066F63 /* Bolts.xcodeproj */; + proxyType = 2; + remoteGlobalIDString = F5AFCA131BA752770076E927; + remoteInfo = "BoltsTests-tvOS"; + }; + 81AB8B6D1D36D60100066F63 /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = 81AB8B421D36D60100066F63 /* Bolts.xcodeproj */; + proxyType = 2; + remoteGlobalIDString = 1EC3016018CDAA8400D06D07; + remoteInfo = BoltsTestUI; + }; + 81AB8B761D36D60100066F63 /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = 81AB8B451D36D60100066F63 /* FBSDKCoreKit.xcodeproj */; + proxyType = 2; + remoteGlobalIDString = 9D2697471A5DF40700143BFC; + remoteInfo = FBSDKCoreKit; + }; + 81AB8B781D36D60100066F63 /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = 81AB8B451D36D60100066F63 /* FBSDKCoreKit.xcodeproj */; + proxyType = 2; + remoteGlobalIDString = 81B71DA31D19C87400933E93; + remoteInfo = "FBSDKCoreKit-Dynamic"; + }; + 81AB8B7A1D36D60100066F63 /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = 81AB8B451D36D60100066F63 /* FBSDKCoreKit.xcodeproj */; + proxyType = 2; + remoteGlobalIDString = 9D2697521A5DF40700143BFC; + remoteInfo = FBSDKCoreKitTests; + }; + 81AB8B7C1D36D60100066F63 /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = 81AB8B451D36D60100066F63 /* FBSDKCoreKit.xcodeproj */; + proxyType = 2; + remoteGlobalIDString = 9DB0FA731BC1CA71005EB8B1; + remoteInfo = FBSDKCoreKit_TV; + }; + 81AB8B7E1D36D60100066F63 /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = 81AB8B451D36D60100066F63 /* FBSDKCoreKit.xcodeproj */; + proxyType = 2; + remoteGlobalIDString = 814AC8571D1B528900D61E6C; + remoteInfo = "FBSDKCoreKit_TV-Dynamic"; + }; + 81AB8B841D36D60100066F63 /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = 81AB8B481D36D60100066F63 /* FBSDKLoginKit.xcodeproj */; + proxyType = 2; + remoteGlobalIDString = 9D9DB8D91A114E500086167B; + remoteInfo = FBSDKLoginKit; + }; + 81AB8B861D36D60100066F63 /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = 81AB8B481D36D60100066F63 /* FBSDKLoginKit.xcodeproj */; + proxyType = 2; + remoteGlobalIDString = 818EB4411D1A283100252851; + remoteInfo = "FBSDKLoginKit-Dynamic"; + }; + 81AB8B881D36D60100066F63 /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = 81AB8B481D36D60100066F63 /* FBSDKLoginKit.xcodeproj */; + proxyType = 2; + remoteGlobalIDString = 9D9DB8E41A114E500086167B; + remoteInfo = FBSDKLoginKitTests; + }; + 81AB8B911D36D60100066F63 /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = 81AB8B4B1D36D60100066F63 /* FBSDKShareKit.xcodeproj */; + proxyType = 2; + remoteGlobalIDString = 9D46C5F01A11E5D800A0DDB6; + remoteInfo = FBSDKShareKit; + }; + 81AB8B931D36D60100066F63 /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = 81AB8B4B1D36D60100066F63 /* FBSDKShareKit.xcodeproj */; + proxyType = 2; + remoteGlobalIDString = 81A07F441D1A2E6A0041A29C; + remoteInfo = "FBSDKShareKit-Dynamic"; + }; + 81AB8B951D36D60100066F63 /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = 81AB8B4B1D36D60100066F63 /* FBSDKShareKit.xcodeproj */; + proxyType = 2; + remoteGlobalIDString = 9D46C5FB1A11E5D800A0DDB6; + remoteInfo = FBSDKShareKitTests; + }; + 81AB8B971D36D60100066F63 /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = 81AB8B4B1D36D60100066F63 /* FBSDKShareKit.xcodeproj */; + proxyType = 2; + remoteGlobalIDString = 9D2D99941BC452DE00929E76; + remoteInfo = FBSDKShareKit_TV; + }; + 81AB8B991D36D60100066F63 /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = 81AB8B4B1D36D60100066F63 /* FBSDKShareKit.xcodeproj */; + proxyType = 2; + remoteGlobalIDString = 8144D91B1D261D2900C8E4AC; + remoteInfo = "FBSDKShareKit_TV-Dynamic"; + }; + 81AB8B9B1D36D63000066F63 /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = 81AB8B421D36D60100066F63 /* Bolts.xcodeproj */; + proxyType = 1; + remoteGlobalIDString = 1D5D7DA61BE3CE8200FD67C7; + remoteInfo = "Bolts-iOS-Dynamic"; + }; + 81AB8B9D1D36D63000066F63 /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = 81AB8B451D36D60100066F63 /* FBSDKCoreKit.xcodeproj */; + proxyType = 1; + remoteGlobalIDString = 81B71CFC1D19C87400933E93; + remoteInfo = "FBSDKCoreKit-Dynamic"; + }; + 81AB8B9F1D36D63000066F63 /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = 81AB8B481D36D60100066F63 /* FBSDKLoginKit.xcodeproj */; + proxyType = 1; + remoteGlobalIDString = 818EB4211D1A283100252851; + remoteInfo = "FBSDKLoginKit-Dynamic"; + }; + 81AB8BA11D36D63000066F63 /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = 81AB8B4B1D36D60100066F63 /* FBSDKShareKit.xcodeproj */; + proxyType = 1; + remoteGlobalIDString = 81A07EDF1D1A2E6A0041A29C; + remoteInfo = "FBSDKShareKit-Dynamic"; + }; + 81F43A101D060C6800569A1A /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = 81F43A0A1D060C6700569A1A /* FacebookSwift.xcodeproj */; + proxyType = 2; + remoteGlobalIDString = 8115C0D81CFE540D001FF33B; + remoteInfo = FacebookCore; + }; + 81F43A121D060C6800569A1A /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = 81F43A0A1D060C6700569A1A /* FacebookSwift.xcodeproj */; + proxyType = 2; + remoteGlobalIDString = 811C5DFD1CFFD36600E4A925; + remoteInfo = FacebookLogin; + }; + 81F43A141D060C6800569A1A /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = 81F43A0A1D060C6700569A1A /* FacebookSwift.xcodeproj */; + proxyType = 2; + remoteGlobalIDString = 811C5E0A1CFFD36D00E4A925; + remoteInfo = FacebookShare; + }; + 81F43A181D060C6E00569A1A /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = 81F43A0A1D060C6700569A1A /* FacebookSwift.xcodeproj */; + proxyType = 1; + remoteGlobalIDString = 8115C0D71CFE540D001FF33B; + remoteInfo = FacebookCore; + }; + 81F43A1C1D060C6F00569A1A /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = 81F43A0A1D060C6700569A1A /* FacebookSwift.xcodeproj */; + proxyType = 1; + remoteGlobalIDString = 811C5DF21CFFD36600E4A925; + remoteInfo = FacebookLogin; + }; + 81F43A201D060C6F00569A1A /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = 81F43A0A1D060C6700569A1A /* FacebookSwift.xcodeproj */; + proxyType = 1; + remoteGlobalIDString = 811C5DFF1CFFD36D00E4A925; + remoteInfo = FacebookShare; + }; +/* End PBXContainerItemProxy section */ + +/* Begin PBXCopyFilesBuildPhase section */ + 81F43A221D060C6F00569A1A /* Embed Frameworks */ = { + isa = PBXCopyFilesBuildPhase; + buildActionMask = 2147483647; + dstPath = ""; + dstSubfolderSpec = 10; + files = ( + 81F43A1F1D060C6F00569A1A /* FacebookShare.framework in Embed Frameworks */, + 81F43A1B1D060C6E00569A1A /* FacebookLogin.framework in Embed Frameworks */, + 81F43A171D060C6E00569A1A /* FacebookCore.framework in Embed Frameworks */, + ); + name = "Embed Frameworks"; + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXCopyFilesBuildPhase section */ + +/* Begin PBXFileReference section */ + 813C9C231D1DB8E500288BFA /* LoginButtonViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = LoginButtonViewController.swift; sourceTree = ""; }; + 816B75EF1D07B7030012AC43 /* GraphAPIPostViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = GraphAPIPostViewController.swift; sourceTree = ""; }; + 816B75FB1D07B70B0012AC43 /* GraphAPIReadViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = GraphAPIReadViewController.swift; sourceTree = ""; }; + 8197009F1D07921100286023 /* LoginManagerViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = LoginManagerViewController.swift; sourceTree = ""; }; + 81AB8B421D36D60100066F63 /* Bolts.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = Bolts.xcodeproj; path = "../../Carthage/Checkouts/facebook-ios-sdk/Bolts-IOS/Bolts.xcodeproj"; sourceTree = ""; }; + 81AB8B451D36D60100066F63 /* FBSDKCoreKit.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = FBSDKCoreKit.xcodeproj; path = "../../Carthage/Checkouts/facebook-ios-sdk/FBSDKCoreKit/FBSDKCoreKit.xcodeproj"; sourceTree = ""; }; + 81AB8B481D36D60100066F63 /* FBSDKLoginKit.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = FBSDKLoginKit.xcodeproj; path = "../../Carthage/Checkouts/facebook-ios-sdk/FBSDKLoginKit/FBSDKLoginKit.xcodeproj"; sourceTree = ""; }; + 81AB8B4B1D36D60100066F63 /* FBSDKShareKit.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = FBSDKShareKit.xcodeproj; path = "../../Carthage/Checkouts/facebook-ios-sdk/FBSDKShareKit/FBSDKShareKit.xcodeproj"; sourceTree = ""; }; + 81BA3CB01D35949E00017B14 /* Common.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = Common.xcconfig; sourceTree = ""; }; + 81BA3CB21D35949E00017B14 /* iOS.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = iOS.xcconfig; sourceTree = ""; }; + 81BA3CB31D35949E00017B14 /* macOS.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = macOS.xcconfig; sourceTree = ""; }; + 81BA3CB41D35949E00017B14 /* tvOS.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = tvOS.xcconfig; sourceTree = ""; }; + 81BA3CB51D35949E00017B14 /* watchOS.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = watchOS.xcconfig; sourceTree = ""; }; + 81BA3CB71D35949E00017B14 /* Application.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = Application.xcconfig; sourceTree = ""; }; + 81BA3CB81D35949E00017B14 /* DynamicFramework.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = DynamicFramework.xcconfig; sourceTree = ""; }; + 81BA3CB91D35949E00017B14 /* LogicTests.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = LogicTests.xcconfig; sourceTree = ""; }; + 81BA3CBA1D35949E00017B14 /* StaticFramework.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = StaticFramework.xcconfig; sourceTree = ""; }; + 81BA3CBC1D35949E00017B14 /* Debug.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = Debug.xcconfig; sourceTree = ""; }; + 81BA3CBD1D35949E00017B14 /* Release.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = Release.xcconfig; sourceTree = ""; }; + 81BA3CBE1D35949E00017B14 /* Warnings.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = Warnings.xcconfig; sourceTree = ""; }; + 81BA3CBF1D35949E00017B14 /* SwiftCatalog.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = SwiftCatalog.xcconfig; sourceTree = ""; }; + 81D645301D2C590C00690609 /* AppInviteViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AppInviteViewController.swift; sourceTree = ""; }; + 81D645351D2C59AA00690609 /* ShareDialogViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ShareDialogViewController.swift; sourceTree = ""; }; + 81D645481D2C686500690609 /* ShareAPIViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ShareAPIViewController.swift; sourceTree = ""; }; + 81EDA2D21D025EA600E742F4 /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; }; + 81EDA2D41D025EA600E742F4 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = ""; }; + 81EDA2D61D025EA600E742F4 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/Main.storyboard; sourceTree = ""; }; + 81EDA3161D025F2D00E742F4 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; + 81EDA31E1D025F4600E742F4 /* SwiftCatalog.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = SwiftCatalog.app; sourceTree = BUILT_PRODUCTS_DIR; }; + 81EDA3361D025F9B00E742F4 /* AppDelegate.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = ""; }; + 81F43A0A1D060C6700569A1A /* FacebookSwift.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = FacebookSwift.xcodeproj; path = ../../FacebookSwift.xcodeproj; sourceTree = ""; }; + 81F43A971D060DB300569A1A /* MainCollectionViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = MainCollectionViewController.swift; sourceTree = ""; }; + 81F43B451D0611B700569A1A /* MainCollectionViewCell.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = MainCollectionViewCell.swift; sourceTree = ""; }; + 81F43B891D06139900569A1A /* ManualAppEventViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ManualAppEventViewController.swift; sourceTree = ""; }; + 81F43B931D0613A100569A1A /* CustomAppEventViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = CustomAppEventViewController.swift; sourceTree = ""; }; + 81FC4A501D064717003F3A46 /* AlertController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AlertController.swift; sourceTree = ""; }; +/* End PBXFileReference section */ + +/* Begin PBXFrameworksBuildPhase section */ + 81EDA31B1D025F4600E742F4 /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + 81AB8BA31D36D63C00066F63 /* Bolts.framework in Frameworks */, + 81AB8BA41D36D63C00066F63 /* FBSDKCoreKit.framework in Frameworks */, + 81AB8BA51D36D63C00066F63 /* FBSDKLoginKit.framework in Frameworks */, + 81AB8BA61D36D63C00066F63 /* FBSDKShareKit.framework in Frameworks */, + 81F43A1E1D060C6F00569A1A /* FacebookShare.framework in Frameworks */, + 81F43A1A1D060C6E00569A1A /* FacebookLogin.framework in Frameworks */, + 81F43A161D060C6E00569A1A /* FacebookCore.framework in Frameworks */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXFrameworksBuildPhase section */ + +/* Begin PBXGroup section */ + 816B75EA1D07B6F80012AC43 /* GraphAPI */ = { + isa = PBXGroup; + children = ( + 816B75FB1D07B70B0012AC43 /* GraphAPIReadViewController.swift */, + 816B75EF1D07B7030012AC43 /* GraphAPIPostViewController.swift */, + ); + name = GraphAPI; + sourceTree = ""; + }; + 819700A91D07921500286023 /* Login */ = { + isa = PBXGroup; + children = ( + 8197009F1D07921100286023 /* LoginManagerViewController.swift */, + 813C9C231D1DB8E500288BFA /* LoginButtonViewController.swift */, + ); + name = Login; + sourceTree = ""; + }; + 81AB8B431D36D60100066F63 /* Products */ = { + isa = PBXGroup; + children = ( + 81AB8B5A1D36D60100066F63 /* Bolts.framework */, + 81AB8B5C1D36D60100066F63 /* Bolts.framework */, + 81AB8B5E1D36D60100066F63 /* Bolts.framework */, + 81AB8B601D36D60100066F63 /* Bolts.framework */, + 81AB8B621D36D60100066F63 /* Bolts.framework */, + 81AB8B641D36D60100066F63 /* Bolts.framework */, + 81AB8B661D36D60100066F63 /* Bolts.framework */, + 81AB8B681D36D60100066F63 /* BoltsTests-iOS.xctest */, + 81AB8B6A1D36D60100066F63 /* BoltsTests-OSX.xctest */, + 81AB8B6C1D36D60100066F63 /* BoltsTests-tvOS.xctest */, + 81AB8B6E1D36D60100066F63 /* BoltsTestUI.app */, + ); + name = Products; + sourceTree = ""; + }; + 81AB8B461D36D60100066F63 /* Products */ = { + isa = PBXGroup; + children = ( + 81AB8B771D36D60100066F63 /* FBSDKCoreKit.framework */, + 81AB8B791D36D60100066F63 /* FBSDKCoreKit.framework */, + 81AB8B7B1D36D60100066F63 /* FBSDKCoreKitTests.xctest */, + 81AB8B7D1D36D60100066F63 /* FBSDKCoreKit.framework */, + 81AB8B7F1D36D60100066F63 /* FBSDKCoreKit.framework */, + ); + name = Products; + sourceTree = ""; + }; + 81AB8B491D36D60100066F63 /* Products */ = { + isa = PBXGroup; + children = ( + 81AB8B851D36D60100066F63 /* FBSDKLoginKit.framework */, + 81AB8B871D36D60100066F63 /* FBSDKLoginKit.framework */, + 81AB8B891D36D60100066F63 /* FBSDKLoginKitTests.xctest */, + ); + name = Products; + sourceTree = ""; + }; + 81AB8B4C1D36D60100066F63 /* Products */ = { + isa = PBXGroup; + children = ( + 81AB8B921D36D60100066F63 /* FBSDKShareKit.framework */, + 81AB8B941D36D60100066F63 /* FBSDKShareKit.framework */, + 81AB8B961D36D60100066F63 /* FBSDKShareKitTests.xctest */, + 81AB8B981D36D60100066F63 /* FBSDKShareKit.framework */, + 81AB8B9A1D36D60100066F63 /* FBSDKShareKit.framework */, + ); + name = Products; + sourceTree = ""; + }; + 81BA3CAE1D35949E00017B14 /* Configurations */ = { + isa = PBXGroup; + children = ( + 81BA3CAF1D35949E00017B14 /* Shared */, + 81BA3CBF1D35949E00017B14 /* SwiftCatalog.xcconfig */, + ); + path = Configurations; + sourceTree = ""; + }; + 81BA3CAF1D35949E00017B14 /* Shared */ = { + isa = PBXGroup; + children = ( + 81BA3CB01D35949E00017B14 /* Common.xcconfig */, + 81BA3CB11D35949E00017B14 /* Platform */, + 81BA3CB61D35949E00017B14 /* Product */, + 81BA3CBB1D35949E00017B14 /* Project */, + 81BA3CBE1D35949E00017B14 /* Warnings.xcconfig */, + ); + path = Shared; + sourceTree = ""; + }; + 81BA3CB11D35949E00017B14 /* Platform */ = { + isa = PBXGroup; + children = ( + 81BA3CB21D35949E00017B14 /* iOS.xcconfig */, + 81BA3CB31D35949E00017B14 /* macOS.xcconfig */, + 81BA3CB41D35949E00017B14 /* tvOS.xcconfig */, + 81BA3CB51D35949E00017B14 /* watchOS.xcconfig */, + ); + path = Platform; + sourceTree = ""; + }; + 81BA3CB61D35949E00017B14 /* Product */ = { + isa = PBXGroup; + children = ( + 81BA3CB71D35949E00017B14 /* Application.xcconfig */, + 81BA3CB81D35949E00017B14 /* DynamicFramework.xcconfig */, + 81BA3CB91D35949E00017B14 /* LogicTests.xcconfig */, + 81BA3CBA1D35949E00017B14 /* StaticFramework.xcconfig */, + ); + path = Product; + sourceTree = ""; + }; + 81BA3CBB1D35949E00017B14 /* Project */ = { + isa = PBXGroup; + children = ( + 81BA3CBC1D35949E00017B14 /* Debug.xcconfig */, + 81BA3CBD1D35949E00017B14 /* Release.xcconfig */, + ); + path = Project; + sourceTree = ""; + }; + 81D6452F1D2C58FA00690609 /* App Invite */ = { + isa = PBXGroup; + children = ( + 81D645301D2C590C00690609 /* AppInviteViewController.swift */, + ); + name = "App Invite"; + sourceTree = ""; + }; + 81D645341D2C599800690609 /* Share Dialog */ = { + isa = PBXGroup; + children = ( + 81D645351D2C59AA00690609 /* ShareDialogViewController.swift */, + ); + name = "Share Dialog"; + sourceTree = ""; + }; + 81D645471D2C684E00690609 /* Share API */ = { + isa = PBXGroup; + children = ( + 81D645481D2C686500690609 /* ShareAPIViewController.swift */, + ); + name = "Share API"; + sourceTree = ""; + }; + 81EDA2D11D025EA600E742F4 /* Resources */ = { + isa = PBXGroup; + children = ( + 81EDA2D21D025EA600E742F4 /* Assets.xcassets */, + 81EDA2D31D025EA600E742F4 /* LaunchScreen.storyboard */, + 81EDA2D51D025EA600E742F4 /* Main.storyboard */, + 81EDA3161D025F2D00E742F4 /* Info.plist */, + ); + path = Resources; + sourceTree = ""; + }; + 81EDA2D81D025EA600E742F4 /* Sources */ = { + isa = PBXGroup; + children = ( + 81EDA3361D025F9B00E742F4 /* AppDelegate.swift */, + 81D6452F1D2C58FA00690609 /* App Invite */, + 816B75EA1D07B6F80012AC43 /* GraphAPI */, + 819700A91D07921500286023 /* Login */, + 81F43B871D06137300569A1A /* AppEvents */, + 81D645471D2C684E00690609 /* Share API */, + 81D645341D2C599800690609 /* Share Dialog */, + 81F43B861D06136A00569A1A /* MainMenu */, + 81FC4A4F1D06470C003F3A46 /* Utilities */, + ); + path = Sources; + sourceTree = ""; + }; + 81F43A091D060C5C00569A1A /* Frameworks */ = { + isa = PBXGroup; + children = ( + 81AB8B421D36D60100066F63 /* Bolts.xcodeproj */, + 81AB8B451D36D60100066F63 /* FBSDKCoreKit.xcodeproj */, + 81AB8B481D36D60100066F63 /* FBSDKLoginKit.xcodeproj */, + 81AB8B4B1D36D60100066F63 /* FBSDKShareKit.xcodeproj */, + 81F43A0A1D060C6700569A1A /* FacebookSwift.xcodeproj */, + 93570B511BD0B71100D6BBF4 /* Products */, + ); + name = Frameworks; + sourceTree = ""; + }; + 81F43A0B1D060C6700569A1A /* Products */ = { + isa = PBXGroup; + children = ( + 81F43A111D060C6800569A1A /* FacebookCore.framework */, + 81F43A131D060C6800569A1A /* FacebookLogin.framework */, + 81F43A151D060C6800569A1A /* FacebookShare.framework */, + ); + name = Products; + sourceTree = ""; + }; + 81F43B861D06136A00569A1A /* MainMenu */ = { + isa = PBXGroup; + children = ( + 81F43A971D060DB300569A1A /* MainCollectionViewController.swift */, + 81F43B451D0611B700569A1A /* MainCollectionViewCell.swift */, + ); + name = MainMenu; + sourceTree = ""; + }; + 81F43B871D06137300569A1A /* AppEvents */ = { + isa = PBXGroup; + children = ( + 81F43B891D06139900569A1A /* ManualAppEventViewController.swift */, + 81F43B931D0613A100569A1A /* CustomAppEventViewController.swift */, + ); + name = AppEvents; + sourceTree = ""; + }; + 81FC4A4F1D06470C003F3A46 /* Utilities */ = { + isa = PBXGroup; + children = ( + 81FC4A501D064717003F3A46 /* AlertController.swift */, + ); + name = Utilities; + sourceTree = ""; + }; + 93570B471BD0B71100D6BBF4 = { + isa = PBXGroup; + children = ( + 81BA3CAE1D35949E00017B14 /* Configurations */, + 81EDA2D81D025EA600E742F4 /* Sources */, + 81EDA2D11D025EA600E742F4 /* Resources */, + 81F43A091D060C5C00569A1A /* Frameworks */, + ); + sourceTree = ""; + }; + 93570B511BD0B71100D6BBF4 /* Products */ = { + isa = PBXGroup; + children = ( + 81EDA31E1D025F4600E742F4 /* SwiftCatalog.app */, + ); + name = Products; + sourceTree = ""; + }; +/* End PBXGroup section */ + +/* Begin PBXNativeTarget section */ + 81EDA31D1D025F4600E742F4 /* SwiftCatalog */ = { + isa = PBXNativeTarget; + buildConfigurationList = 81EDA32D1D025F4600E742F4 /* Build configuration list for PBXNativeTarget "SwiftCatalog" */; + buildPhases = ( + 81EDA31A1D025F4600E742F4 /* Sources */, + 81EDA31B1D025F4600E742F4 /* Frameworks */, + 81EDA31C1D025F4600E742F4 /* Resources */, + 81F43A221D060C6F00569A1A /* Embed Frameworks */, + ); + buildRules = ( + ); + dependencies = ( + 81AB8B9C1D36D63000066F63 /* PBXTargetDependency */, + 81AB8B9E1D36D63000066F63 /* PBXTargetDependency */, + 81AB8BA01D36D63000066F63 /* PBXTargetDependency */, + 81AB8BA21D36D63000066F63 /* PBXTargetDependency */, + 81F43A191D060C6E00569A1A /* PBXTargetDependency */, + 81F43A1D1D060C6F00569A1A /* PBXTargetDependency */, + 81F43A211D060C6F00569A1A /* PBXTargetDependency */, + ); + name = SwiftCatalog; + productName = Catalog; + productReference = 81EDA31E1D025F4600E742F4 /* SwiftCatalog.app */; + productType = "com.apple.product-type.application"; + }; +/* End PBXNativeTarget section */ + +/* Begin PBXProject section */ + 93570B481BD0B71100D6BBF4 /* Project object */ = { + isa = PBXProject; + attributes = { + LastSwiftUpdateCheck = 0730; + LastUpgradeCheck = 0800; + ORGANIZATIONNAME = "Facebook, Inc"; + TargetAttributes = { + 81EDA31D1D025F4600E742F4 = { + CreatedOnToolsVersion = 7.3.1; + LastSwiftMigration = 0800; + }; + }; + }; + buildConfigurationList = 93570B4B1BD0B71100D6BBF4 /* Build configuration list for PBXProject "SwiftCatalog" */; + compatibilityVersion = "Xcode 3.2"; + developmentRegion = English; + hasScannedForEncodings = 0; + knownRegions = ( + en, + Base, + ); + mainGroup = 93570B471BD0B71100D6BBF4; + productRefGroup = 93570B511BD0B71100D6BBF4 /* Products */; + projectDirPath = ""; + projectReferences = ( + { + ProductGroup = 81AB8B431D36D60100066F63 /* Products */; + ProjectRef = 81AB8B421D36D60100066F63 /* Bolts.xcodeproj */; + }, + { + ProductGroup = 81F43A0B1D060C6700569A1A /* Products */; + ProjectRef = 81F43A0A1D060C6700569A1A /* FacebookSwift.xcodeproj */; + }, + { + ProductGroup = 81AB8B461D36D60100066F63 /* Products */; + ProjectRef = 81AB8B451D36D60100066F63 /* FBSDKCoreKit.xcodeproj */; + }, + { + ProductGroup = 81AB8B491D36D60100066F63 /* Products */; + ProjectRef = 81AB8B481D36D60100066F63 /* FBSDKLoginKit.xcodeproj */; + }, + { + ProductGroup = 81AB8B4C1D36D60100066F63 /* Products */; + ProjectRef = 81AB8B4B1D36D60100066F63 /* FBSDKShareKit.xcodeproj */; + }, + ); + projectRoot = ""; + targets = ( + 81EDA31D1D025F4600E742F4 /* SwiftCatalog */, + ); + }; +/* End PBXProject section */ + +/* Begin PBXReferenceProxy section */ + 81AB8B5A1D36D60100066F63 /* Bolts.framework */ = { + isa = PBXReferenceProxy; + fileType = wrapper.framework; + path = Bolts.framework; + remoteRef = 81AB8B591D36D60100066F63 /* PBXContainerItemProxy */; + sourceTree = BUILT_PRODUCTS_DIR; + }; + 81AB8B5C1D36D60100066F63 /* Bolts.framework */ = { + isa = PBXReferenceProxy; + fileType = wrapper.framework; + path = Bolts.framework; + remoteRef = 81AB8B5B1D36D60100066F63 /* PBXContainerItemProxy */; + sourceTree = BUILT_PRODUCTS_DIR; + }; + 81AB8B5E1D36D60100066F63 /* Bolts.framework */ = { + isa = PBXReferenceProxy; + fileType = wrapper.framework; + path = Bolts.framework; + remoteRef = 81AB8B5D1D36D60100066F63 /* PBXContainerItemProxy */; + sourceTree = BUILT_PRODUCTS_DIR; + }; + 81AB8B601D36D60100066F63 /* Bolts.framework */ = { + isa = PBXReferenceProxy; + fileType = wrapper.framework; + path = Bolts.framework; + remoteRef = 81AB8B5F1D36D60100066F63 /* PBXContainerItemProxy */; + sourceTree = BUILT_PRODUCTS_DIR; + }; + 81AB8B621D36D60100066F63 /* Bolts.framework */ = { + isa = PBXReferenceProxy; + fileType = wrapper.framework; + path = Bolts.framework; + remoteRef = 81AB8B611D36D60100066F63 /* PBXContainerItemProxy */; + sourceTree = BUILT_PRODUCTS_DIR; + }; + 81AB8B641D36D60100066F63 /* Bolts.framework */ = { + isa = PBXReferenceProxy; + fileType = wrapper.framework; + path = Bolts.framework; + remoteRef = 81AB8B631D36D60100066F63 /* PBXContainerItemProxy */; + sourceTree = BUILT_PRODUCTS_DIR; + }; + 81AB8B661D36D60100066F63 /* Bolts.framework */ = { + isa = PBXReferenceProxy; + fileType = wrapper.framework; + path = Bolts.framework; + remoteRef = 81AB8B651D36D60100066F63 /* PBXContainerItemProxy */; + sourceTree = BUILT_PRODUCTS_DIR; + }; + 81AB8B681D36D60100066F63 /* BoltsTests-iOS.xctest */ = { + isa = PBXReferenceProxy; + fileType = wrapper.cfbundle; + path = "BoltsTests-iOS.xctest"; + remoteRef = 81AB8B671D36D60100066F63 /* PBXContainerItemProxy */; + sourceTree = BUILT_PRODUCTS_DIR; + }; + 81AB8B6A1D36D60100066F63 /* BoltsTests-OSX.xctest */ = { + isa = PBXReferenceProxy; + fileType = wrapper.cfbundle; + path = "BoltsTests-OSX.xctest"; + remoteRef = 81AB8B691D36D60100066F63 /* PBXContainerItemProxy */; + sourceTree = BUILT_PRODUCTS_DIR; + }; + 81AB8B6C1D36D60100066F63 /* BoltsTests-tvOS.xctest */ = { + isa = PBXReferenceProxy; + fileType = wrapper.cfbundle; + path = "BoltsTests-tvOS.xctest"; + remoteRef = 81AB8B6B1D36D60100066F63 /* PBXContainerItemProxy */; + sourceTree = BUILT_PRODUCTS_DIR; + }; + 81AB8B6E1D36D60100066F63 /* BoltsTestUI.app */ = { + isa = PBXReferenceProxy; + fileType = wrapper.application; + path = BoltsTestUI.app; + remoteRef = 81AB8B6D1D36D60100066F63 /* PBXContainerItemProxy */; + sourceTree = BUILT_PRODUCTS_DIR; + }; + 81AB8B771D36D60100066F63 /* FBSDKCoreKit.framework */ = { + isa = PBXReferenceProxy; + fileType = wrapper.framework; + path = FBSDKCoreKit.framework; + remoteRef = 81AB8B761D36D60100066F63 /* PBXContainerItemProxy */; + sourceTree = BUILT_PRODUCTS_DIR; + }; + 81AB8B791D36D60100066F63 /* FBSDKCoreKit.framework */ = { + isa = PBXReferenceProxy; + fileType = wrapper.framework; + path = FBSDKCoreKit.framework; + remoteRef = 81AB8B781D36D60100066F63 /* PBXContainerItemProxy */; + sourceTree = BUILT_PRODUCTS_DIR; + }; + 81AB8B7B1D36D60100066F63 /* FBSDKCoreKitTests.xctest */ = { + isa = PBXReferenceProxy; + fileType = wrapper.cfbundle; + path = FBSDKCoreKitTests.xctest; + remoteRef = 81AB8B7A1D36D60100066F63 /* PBXContainerItemProxy */; + sourceTree = BUILT_PRODUCTS_DIR; + }; + 81AB8B7D1D36D60100066F63 /* FBSDKCoreKit.framework */ = { + isa = PBXReferenceProxy; + fileType = wrapper.framework; + path = FBSDKCoreKit.framework; + remoteRef = 81AB8B7C1D36D60100066F63 /* PBXContainerItemProxy */; + sourceTree = BUILT_PRODUCTS_DIR; + }; + 81AB8B7F1D36D60100066F63 /* FBSDKCoreKit.framework */ = { + isa = PBXReferenceProxy; + fileType = wrapper.framework; + path = FBSDKCoreKit.framework; + remoteRef = 81AB8B7E1D36D60100066F63 /* PBXContainerItemProxy */; + sourceTree = BUILT_PRODUCTS_DIR; + }; + 81AB8B851D36D60100066F63 /* FBSDKLoginKit.framework */ = { + isa = PBXReferenceProxy; + fileType = wrapper.framework; + path = FBSDKLoginKit.framework; + remoteRef = 81AB8B841D36D60100066F63 /* PBXContainerItemProxy */; + sourceTree = BUILT_PRODUCTS_DIR; + }; + 81AB8B871D36D60100066F63 /* FBSDKLoginKit.framework */ = { + isa = PBXReferenceProxy; + fileType = wrapper.framework; + path = FBSDKLoginKit.framework; + remoteRef = 81AB8B861D36D60100066F63 /* PBXContainerItemProxy */; + sourceTree = BUILT_PRODUCTS_DIR; + }; + 81AB8B891D36D60100066F63 /* FBSDKLoginKitTests.xctest */ = { + isa = PBXReferenceProxy; + fileType = wrapper.cfbundle; + path = FBSDKLoginKitTests.xctest; + remoteRef = 81AB8B881D36D60100066F63 /* PBXContainerItemProxy */; + sourceTree = BUILT_PRODUCTS_DIR; + }; + 81AB8B921D36D60100066F63 /* FBSDKShareKit.framework */ = { + isa = PBXReferenceProxy; + fileType = wrapper.framework; + path = FBSDKShareKit.framework; + remoteRef = 81AB8B911D36D60100066F63 /* PBXContainerItemProxy */; + sourceTree = BUILT_PRODUCTS_DIR; + }; + 81AB8B941D36D60100066F63 /* FBSDKShareKit.framework */ = { + isa = PBXReferenceProxy; + fileType = wrapper.framework; + path = FBSDKShareKit.framework; + remoteRef = 81AB8B931D36D60100066F63 /* PBXContainerItemProxy */; + sourceTree = BUILT_PRODUCTS_DIR; + }; + 81AB8B961D36D60100066F63 /* FBSDKShareKitTests.xctest */ = { + isa = PBXReferenceProxy; + fileType = wrapper.cfbundle; + path = FBSDKShareKitTests.xctest; + remoteRef = 81AB8B951D36D60100066F63 /* PBXContainerItemProxy */; + sourceTree = BUILT_PRODUCTS_DIR; + }; + 81AB8B981D36D60100066F63 /* FBSDKShareKit.framework */ = { + isa = PBXReferenceProxy; + fileType = wrapper.framework; + path = FBSDKShareKit.framework; + remoteRef = 81AB8B971D36D60100066F63 /* PBXContainerItemProxy */; + sourceTree = BUILT_PRODUCTS_DIR; + }; + 81AB8B9A1D36D60100066F63 /* FBSDKShareKit.framework */ = { + isa = PBXReferenceProxy; + fileType = wrapper.framework; + path = FBSDKShareKit.framework; + remoteRef = 81AB8B991D36D60100066F63 /* PBXContainerItemProxy */; + sourceTree = BUILT_PRODUCTS_DIR; + }; + 81F43A111D060C6800569A1A /* FacebookCore.framework */ = { + isa = PBXReferenceProxy; + fileType = wrapper.framework; + path = FacebookCore.framework; + remoteRef = 81F43A101D060C6800569A1A /* PBXContainerItemProxy */; + sourceTree = BUILT_PRODUCTS_DIR; + }; + 81F43A131D060C6800569A1A /* FacebookLogin.framework */ = { + isa = PBXReferenceProxy; + fileType = wrapper.framework; + path = FacebookLogin.framework; + remoteRef = 81F43A121D060C6800569A1A /* PBXContainerItemProxy */; + sourceTree = BUILT_PRODUCTS_DIR; + }; + 81F43A151D060C6800569A1A /* FacebookShare.framework */ = { + isa = PBXReferenceProxy; + fileType = wrapper.framework; + path = FacebookShare.framework; + remoteRef = 81F43A141D060C6800569A1A /* PBXContainerItemProxy */; + sourceTree = BUILT_PRODUCTS_DIR; + }; +/* End PBXReferenceProxy section */ + +/* Begin PBXResourcesBuildPhase section */ + 81EDA31C1D025F4600E742F4 /* Resources */ = { + isa = PBXResourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 81FC4B061D064A39003F3A46 /* Assets.xcassets in Resources */, + 81F43A7B1D060D6400569A1A /* Main.storyboard in Resources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXResourcesBuildPhase section */ + +/* Begin PBXSourcesBuildPhase section */ + 81EDA31A1D025F4600E742F4 /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 816B75F01D07B7030012AC43 /* GraphAPIPostViewController.swift in Sources */, + 81F43B8A1D06139900569A1A /* ManualAppEventViewController.swift in Sources */, + 81F43B941D0613A100569A1A /* CustomAppEventViewController.swift in Sources */, + 816B75FC1D07B70B0012AC43 /* GraphAPIReadViewController.swift in Sources */, + 81EDA3371D025F9B00E742F4 /* AppDelegate.swift in Sources */, + 81F43B461D0611B700569A1A /* MainCollectionViewCell.swift in Sources */, + 819700A01D07921100286023 /* LoginManagerViewController.swift in Sources */, + 81D645311D2C590C00690609 /* AppInviteViewController.swift in Sources */, + 81D645491D2C686500690609 /* ShareAPIViewController.swift in Sources */, + 81D645361D2C59AA00690609 /* ShareDialogViewController.swift in Sources */, + 81FC4A511D064717003F3A46 /* AlertController.swift in Sources */, + 813C9C241D1DB8E500288BFA /* LoginButtonViewController.swift in Sources */, + 81F43A981D060DB300569A1A /* MainCollectionViewController.swift in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXSourcesBuildPhase section */ + +/* Begin PBXTargetDependency section */ + 81AB8B9C1D36D63000066F63 /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + name = "Bolts-iOS-Dynamic"; + targetProxy = 81AB8B9B1D36D63000066F63 /* PBXContainerItemProxy */; + }; + 81AB8B9E1D36D63000066F63 /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + name = "FBSDKCoreKit-Dynamic"; + targetProxy = 81AB8B9D1D36D63000066F63 /* PBXContainerItemProxy */; + }; + 81AB8BA01D36D63000066F63 /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + name = "FBSDKLoginKit-Dynamic"; + targetProxy = 81AB8B9F1D36D63000066F63 /* PBXContainerItemProxy */; + }; + 81AB8BA21D36D63000066F63 /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + name = "FBSDKShareKit-Dynamic"; + targetProxy = 81AB8BA11D36D63000066F63 /* PBXContainerItemProxy */; + }; + 81F43A191D060C6E00569A1A /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + name = FacebookCore; + targetProxy = 81F43A181D060C6E00569A1A /* PBXContainerItemProxy */; + }; + 81F43A1D1D060C6F00569A1A /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + name = FacebookLogin; + targetProxy = 81F43A1C1D060C6F00569A1A /* PBXContainerItemProxy */; + }; + 81F43A211D060C6F00569A1A /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + name = FacebookShare; + targetProxy = 81F43A201D060C6F00569A1A /* PBXContainerItemProxy */; + }; +/* End PBXTargetDependency section */ + +/* Begin PBXVariantGroup section */ + 81EDA2D31D025EA600E742F4 /* LaunchScreen.storyboard */ = { + isa = PBXVariantGroup; + children = ( + 81EDA2D41D025EA600E742F4 /* Base */, + ); + name = LaunchScreen.storyboard; + sourceTree = ""; + }; + 81EDA2D51D025EA600E742F4 /* Main.storyboard */ = { + isa = PBXVariantGroup; + children = ( + 81EDA2D61D025EA600E742F4 /* Base */, + ); + name = Main.storyboard; + sourceTree = ""; + }; +/* End PBXVariantGroup section */ + +/* Begin XCBuildConfiguration section */ + 81EDA32E1D025F4600E742F4 /* Debug */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = 81BA3CBF1D35949E00017B14 /* SwiftCatalog.xcconfig */; + buildSettings = { + }; + name = Debug; + }; + 81EDA32F1D025F4600E742F4 /* Release */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = 81BA3CBF1D35949E00017B14 /* SwiftCatalog.xcconfig */; + buildSettings = { + }; + name = Release; + }; + 93570B7B1BD0B71100D6BBF4 /* Debug */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = 81BA3CBC1D35949E00017B14 /* Debug.xcconfig */; + buildSettings = { + }; + name = Debug; + }; + 93570B7C1BD0B71100D6BBF4 /* Release */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = 81BA3CBD1D35949E00017B14 /* Release.xcconfig */; + buildSettings = { + }; + name = Release; + }; +/* End XCBuildConfiguration section */ + +/* Begin XCConfigurationList section */ + 81EDA32D1D025F4600E742F4 /* Build configuration list for PBXNativeTarget "SwiftCatalog" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 81EDA32E1D025F4600E742F4 /* Debug */, + 81EDA32F1D025F4600E742F4 /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + 93570B4B1BD0B71100D6BBF4 /* Build configuration list for PBXProject "SwiftCatalog" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 93570B7B1BD0B71100D6BBF4 /* Debug */, + 93570B7C1BD0B71100D6BBF4 /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; +/* End XCConfigurationList section */ + }; + rootObject = 93570B481BD0B71100D6BBF4 /* Project object */; +} diff --git a/Samples/Catalog/SwiftCatalog.xcodeproj/xcshareddata/xcschemes/SwiftCatalog.xcscheme b/Samples/Catalog/SwiftCatalog.xcodeproj/xcshareddata/xcschemes/SwiftCatalog.xcscheme new file mode 100644 index 00000000..fee5e494 --- /dev/null +++ b/Samples/Catalog/SwiftCatalog.xcodeproj/xcshareddata/xcschemes/SwiftCatalog.xcscheme @@ -0,0 +1,91 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Sources/Core/AppEvents/AppEvent.Builtin.swift b/Sources/Core/AppEvents/AppEvent.Builtin.swift new file mode 100644 index 00000000..05c341cf --- /dev/null +++ b/Sources/Core/AppEvents/AppEvent.Builtin.swift @@ -0,0 +1,310 @@ +// 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.FBSDKAppEvents + +//-------------------------------------- +// MARK: - General +//-------------------------------------- + +extension AppEvent { + /** + Create an event that indicates that the user has completed registration. + + - parameter registrationMethod: Optional registration method used. + - parameter valueToSum: Optional value to sum. + - parameter extraParameters: Optional dictionary of extra parameters. + + - returns: An app event that can be logged via `AppEventsLogger`. + */ + public static func CompletedRegistration(registrationMethod registrationMethod: String? = nil, + valueToSum: Double? = nil, + extraParameters: ParametersDictionary = [:]) -> AppEvent { + var parameters = extraParameters + registrationMethod.onSome({ parameters[.RegistrationMethod] = $0 }) + return AppEvent(name: .CompletedRegistration, parameters: parameters, valueToSum: valueToSum) + } + + /** + Create an event that indicates that the user has completed tutorial. + + - parameter successful: Optional boolean value that indicates whether operation was succesful. + - parameter valueToSum: Optional value to sum. + - parameter extraParameters: Optional dictionary of extra parameters. + + - returns: An app event that can be logged via `AppEventsLogger`. + */ + public static func CompletedTutorial(successful successful: BooleanType? = nil, + valueToSum: Double? = nil, + extraParameters: ParametersDictionary = [:]) -> AppEvent { + var parameters = extraParameters + successful.onSome({ parameters[.Successful] = $0.boolValue ? FBSDKAppEventParameterValueYes : FBSDKAppEventParameterValueNo }) + return AppEvent(name: .CompletedTutorial, parameters: parameters, valueToSum: valueToSum) + } + + /** + Create an event that indicates that the user viewed specific content. + + - parameter contentType: Optional content type. + - parameter contentId: Optional content identifier. + - parameter currency: Optional string representation of currency. + - parameter valueToSum: Optional value to sum. + - parameter extraParameters: Optional dictionary of extra parameters. + + - returns: An app event that can be logged via `AppEventsLogger`. + */ + public static func ViewedContent(contentType contentType: String? = nil, + contentId: String? = nil, + currency: String? = nil, + valueToSum: Double? = nil, + extraParameters: ParametersDictionary = [:]) -> AppEvent { + var parameters = extraParameters + contentType.onSome({ parameters[.ContentType] = $0 }) + contentId.onSome({ parameters[.ContentId] = $0 }) + currency.onSome({ parameters[.Currency] = $0 }) + return AppEvent(name: .ViewedContent, parameters: parameters, valueToSum: valueToSum) + } + + /** + Create an event that indicatest that the user has performed a search within the app. + + - parameter contentId: Optional content identifer. + - parameter searchedString: Optional searched string. + - parameter successful: Optional boolean value that indicatest whether the operation was succesful. + - parameter valueToSum: Optional value to sum. + - parameter extraParameters: Optional dictionary of extra parameters. + + - returns: An app event that can be logged via `AppEventsLogger`. + */ + public static func Searched(contentId contentId: String? = nil, + searchedString: String? = nil, + successful: BooleanType? = nil, + valueToSum: Double? = nil, + extraParameters: ParametersDictionary = [:]) -> AppEvent { + var parameters = extraParameters + contentId.onSome({ parameters[.ContentId] = $0 }) + searchedString.onSome({ parameters[.SearchedString] = $0 }) + successful.onSome({ parameters[.Successful] = $0.boolValue ? FBSDKAppEventParameterValueYes : FBSDKAppEventParameterValueNo }) + return AppEvent(name: .Searched, parameters: parameters, valueToSum: valueToSum) + } + + /** + Create an event that indicatest the user has rated an item in the app. + + - parameter contentType: Optional type of the content. + - parameter contentId: Optional content identifier. + - parameter maxRatingValue: Optional max rating value. + - parameter valueToSum: Optional value to sum. + - parameter extraParameters: Optional dictionary of extra parameters. + + - returns: An app event that can be logged via `AppEventsLogger`. + */ + public static func Rated( + contentType contentType: String? = nil, + contentId: String? = nil, + maxRatingValue: T? = nil, + valueToSum: Double? = nil, + extraParameters: ParametersDictionary = [:]) -> AppEvent { + var parameters = extraParameters + contentType.onSome({ parameters[.ContentType] = $0 }) + contentId.onSome({ parameters[.ContentId] = $0 }) + maxRatingValue.onSome({ parameters[.MaxRatingValue] = NSNumber(unsignedLongLong: $0.toUIntMax()) }) + return AppEvent(name: .Rated, parameters: parameters, valueToSum: valueToSum) + } +} + +//-------------------------------------- +// MARK: - Commerce +//-------------------------------------- + +extension AppEvent { + + /** + Create an app event that a user has purchased something in the application. + + - parameter amount: An amount of purchase. + - parameter currency: Optional string representation of currency. + - parameter extraParameters: Optional dictionary of extra parameters. + + - returns: An app event that can be logged via `AppEventsLogger`. + */ + public static func Purchased(amount amount: Double, + currency: String? = nil, + extraParameters: ParametersDictionary = [:]) -> AppEvent { + var parameters = extraParameters + currency.onSome({ parameters[.Currency] = $0 }) + return AppEvent(name: .Purchased, parameters: parameters, valueToSum: amount) + } + + /** + Create an app event that indicatest that user has added an item to the cart. + + - parameter contentType: Optional content type. + - parameter contentId: Optional content identifier. + - parameter currency: Optional string representation of currency. + - parameter valueToSum: Optional value to sum. + - parameter extraParameters: Optional dictionary of extra parameters. + + - returns: An app event that can be logged via `AppEventsLogger`. + */ + public static func AddedToCart(contentType contentType: String? = nil, + contentId: String? = nil, + currency: String? = nil, + valueToSum: Double? = nil, + extraParameters: ParametersDictionary = [:]) -> AppEvent { + var parameters = extraParameters + contentType.onSome({ parameters[.ContentType] = $0 }) + contentId.onSome({ parameters[.ContentId] = $0 }) + currency.onSome({ parameters[.Currency] = $0 }) + return AppEvent(name: .AddedToCart, parameters: parameters, valueToSum: valueToSum) + } + + /** + Create an app event that indicates that user added an item to the wishlist. + + - parameter contentType: Optional content type. + - parameter contentId: Optional content identifier. + - parameter currency: Optional string representation of currency. + - parameter valueToSum: Optional value to sum. + - parameter extraParameters: Optional dictionary of extra parameters. + + - returns: An app event that can be logged via `AppEventsLogger`. + */ + public static func AddedToWishlist(contentType contentType: String? = nil, + contentId: String? = nil, + currency: String? = nil, + valueToSum: Double? = nil, + extraParameters: ParametersDictionary = [:]) -> AppEvent { + var parameters = extraParameters + contentType.onSome({ parameters[.ContentType] = $0 }) + contentId.onSome({ parameters[.ContentId] = $0 }) + currency.onSome({ parameters[.Currency] = $0 }) + return AppEvent(name: .AddedToWishlist, parameters: parameters, valueToSum: valueToSum) + } + + /** + Create an event that indicatest that a user added payment information. + + - parameter successful: Optional boolean value that indicates whether operation was succesful. + - parameter valueToSum: Optional value to sum. + - parameter extraParameters: Optional dictionary of extra parameters. + + - returns: An app event that can be logged via `AppEventsLogger`. + */ + public static func AddedPaymentInfo(successful successful: BooleanType? = nil, + valueToSum: Double? = nil, + extraParameters: ParametersDictionary = [:]) -> AppEvent { + var parameters = extraParameters + successful.onSome({ parameters[.Successful] = $0.boolValue ? FBSDKAppEventParameterValueYes : FBSDKAppEventParameterValueNo }) + return AppEvent(name: .AddedPaymentInfo, parameters: parameters, valueToSum: valueToSum) + } + + /** + Create an event that indicatest that a user has initiated a checkout. + + - parameter contentType: Optional content type. + - parameter contentId: Optional content identifier. + - parameter itemCount: Optional count of items. + - parameter paymentInfoAvailable: Optional boolean value that indicatest whether payment info is available. + - parameter currency: Optional string representation of currency. + - parameter valueToSum: Optional value to sum. + - parameter extraParameters: Optional dictionary of extra parameters. + + - returns: An app event that can be logged via `AppEventsLogger`. + */ + public static func InitiatedCheckout( + contentType contentType: String? = nil, + contentId: String? = nil, + itemCount: T? = nil, + paymentInfoAvailable: BooleanType? = nil, + currency: String? = nil, + valueToSum: Double? = nil, + extraParameters: ParametersDictionary = [:]) -> AppEvent { + var parameters = extraParameters + contentType.onSome({ parameters[.ContentType] = $0 }) + contentId.onSome({ parameters[.ContentId] = $0 }) + itemCount.onSome({ parameters[.ItemCount] = NSNumber(unsignedLongLong: $0.toUIntMax()) }) + paymentInfoAvailable.onSome({ + parameters[.PaymentInfoAvailable] = $0.boolValue ? FBSDKAppEventParameterValueYes : FBSDKAppEventParameterValueNo + }) + currency.onSome({ parameters[.Currency] = $0 }) + return AppEvent(name: .InitiatedCheckout, parameters: parameters, valueToSum: valueToSum) + } +} + +//-------------------------------------- +// MARK: - Gaming +//-------------------------------------- + +extension AppEvent { + + /** + Create an app event that indicates that a user has achieved a level in the application. + + - parameter level: Optional level achieved. Can be either a `String` or `NSNumber`. + - parameter valueToSum: Optional value to sum. + - parameter extraParameters: Optional dictionary of extra parameters. + + - returns: An app event that can be logged via `AppEventsLogger`. + */ + public static func AchievedLevel(level level: AppEventParameterValueType? = nil, + valueToSum: Double? = nil, + extraParameters: ParametersDictionary = [:]) -> AppEvent { + var parameters = extraParameters + level.onSome({ parameters[.Level] = $0 }) + return AppEvent(name: .AchievedLevel, parameters: parameters, valueToSum: valueToSum) + } + + /** + Create an app event that indicatest that a user has unlocked an achievement. + + - parameter description: Optional achievement description. + - parameter valueToSum: Optional value to sum. + - parameter extraParameters: Optional dictionary of extra parameters. + + - returns: An app event that can be logged via `AppEventsLogger`. + */ + public static func UnlockedAchievement(description description: String? = nil, + valueToSum: Double? = nil, + extraParameters: ParametersDictionary = [:]) -> AppEvent { + var parameters = extraParameters + description.onSome({ parameters[.Description] = $0 }) + return AppEvent(name: .UnlockedAchievement, parameters: parameters, valueToSum: valueToSum) + } + + /** + Create an event that indicatest that a user spent in-app credits. + + - parameter contentType: Optional content type. + - parameter contentId: Optional content identifier. + - parameter valueToSum: Optional value to sum. + - parameter extraParameters: Optional dictionary of extra parameters. + + - returns: An app event that can be logged via `AppEventsLogger`. + */ + public static func SpentCredits( + contentType contentType: String? = nil, + contentId: String? = nil, + valueToSum: Double? = nil, + extraParameters: ParametersDictionary = [:]) -> AppEvent { + var parameters = extraParameters + contentType.onSome({ parameters[.ContentType] = $0 }) + contentId.onSome({ parameters[.ContentId] = $0 }) + return AppEvent(name: .SpentCredits, parameters: parameters, valueToSum: valueToSum) + } +} diff --git a/Sources/Core/AppEvents/AppEvent.swift b/Sources/Core/AppEvents/AppEvent.swift new file mode 100644 index 00000000..693dc2b5 --- /dev/null +++ b/Sources/Core/AppEvents/AppEvent.swift @@ -0,0 +1,114 @@ +// 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 Foundation + +/** + Represents a single application event that can be logged to Facebook Analytics. + */ +public struct AppEvent: AppEventLoggable { + public typealias ParametersDictionary = [AppEventParameterName : AppEventParameterValueType] + + /// Name of the application event. + public let name: AppEventName + + /// Arbitrary parameter dictionary of characteristics of an event. + public var parameters: ParametersDictionary + + /** + Amount to be aggregated into all events of this eventName. + App Insights will report the cumulative and average value of this amount. + */ + public var valueToSum: Double? + + /** + Creates an app event. + + - parameter name: App event name. + - parameter parameters: Parameters dictionary. Default: empty. + - parameter valueToSum: Optional value to sum. Default: `nil`. + */ + public init(name: AppEventName, parameters: ParametersDictionary = [:], valueToSum: Double? = nil) { + self.name = name + self.parameters = parameters + self.valueToSum = valueToSum + } +} + +extension AppEvent { + /** + Creates an app event. + + - parameter name: String representation of app event name. + - parameter parameters: Parameters dictionary. Default: empty. + - parameter valueToSum: Optional value to sum. Default: `nil`. + */ + public init(name: String, parameters: ParametersDictionary = [:], valueToSum: Double? = nil) { + self.init(name: AppEventName(name), parameters: parameters, valueToSum: valueToSum) + } +} + +/** + Protocol that describes a single application event that can be logged to Facebook Analytics. + */ +public protocol AppEventLoggable { + /// Name of the application event. + var name: AppEventName { get } + /// Arbitrary parameter dictionary of characteristics of an event. + var parameters: AppEvent.ParametersDictionary { get } + /// Amount to be aggregated into all events of this eventName. + var valueToSum: Double? { get } +} + +/** + Conforming types that can be logged as a parameter value of `AppEventLoggable`. + By default implemented for `NSNumber`, `String`, `IntegerLiteralType` and `FloatLiteralType`. + Should only return either a `String` or `NSNumber`. + */ +public protocol AppEventParameterValueType { + /// Object value. Can be either `NSNumber` or `String`. + var appEventParameterValue: AnyObject { get } +} + +extension NSNumber: AppEventParameterValueType { + /// An object representation of `self`, suitable for parameter value of `AppEventLoggable`. + public var appEventParameterValue: AnyObject { + return self + } +} + +extension IntegerLiteralType: AppEventParameterValueType { + /// An object representation of `self`, suitable for parameter value of `AppEventLoggable`. + public var appEventParameterValue: AnyObject { + return self as NSNumber + } +} + +extension FloatLiteralType: AppEventParameterValueType { + /// An object representation of `self`, suitable for parameter value of `AppEventLoggable`. + public var appEventParameterValue: AnyObject { + return self as NSNumber + } +} + +extension String: AppEventParameterValueType { + /// An object representation of `self`, suitable for parameter value of `AppEventLoggable`. + public var appEventParameterValue: AnyObject { + return self + } +} diff --git a/Sources/Core/AppEvents/AppEventName.swift b/Sources/Core/AppEvents/AppEventName.swift new file mode 100644 index 00000000..f263026f --- /dev/null +++ b/Sources/Core/AppEvents/AppEventName.swift @@ -0,0 +1,167 @@ +// 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.FBSDKAppEvents + +/** + Represents a name of the Facebook Analytics application event. + Could either be one of built-in names or a custom `String`. + + - seealso: AppEvent + - seealso: AppEventLoggable + */ +public enum AppEventName { + + // MARK: General + + /// Name of the event that indicates that the user has completed a registration. + case CompletedRegistration + /// Name of the event that indicates that the user has completed a tutorial. + case CompletedTutorial + /// Name of the event that indicates that the user has viewed a content. + case ViewedContent + /// Name of the event that indicates that the user has performed search within the application. + case Searched + /// Name of the event that indicates that the user has has rated an item in the app. + case Rated + + // MARK: Commerce + + /// Name of the event that indicates that the user has purchased something in the application. + case Purchased + /// Name of the event that indicates that the user has added an item to the cart. + case AddedToCart + /// Name of the event that indicates that the user has added an item to the wishlist. + case AddedToWishlist + /// Name of the event that indicates that the user has added payment information. + case AddedPaymentInfo + /// Name of the event that indicates that the user has initiated a checkout. + case InitiatedCheckout + + // MARK: Gaming + + /// Name of the event that indicates that the user has achieved a level. + case AchievedLevel + /// Name of the event that indicates that the user has unlocked an achievement. + case UnlockedAchievement + /// Name of the event that indicates that the user has spent in-app credits. + case SpentCredits + + // MARK: Custom + + /// Custom name of the event that is represented by a string. + case Custom(String) + + /** + Create an `AppEventName` from `String`. + + - parameter string: String to create an app event name from. + */ + public init(_ string: String) { + self = .Custom(string) + } +} + +extension AppEventName: RawRepresentable { + /** + Create an `AppEventName` from `String`. + + - parameter rawValue: String to create an app event name from. + */ + public init?(rawValue: String) { + self = .Custom(rawValue) + } + + /// The corresponding `String` value. + public var rawValue: String { + switch self { + case .CompletedRegistration: return FBSDKAppEventNameCompletedRegistration + case .CompletedTutorial: return FBSDKAppEventNameCompletedTutorial + case .ViewedContent: return FBSDKAppEventNameViewedContent + case .Searched: return FBSDKAppEventNameSearched + case .Rated: return FBSDKAppEventNameRated + + case .Purchased: return "fb_mobile_purchase" // Hard-coded as a string, since it's internal API of FBSDKCoreKit. + case .AddedToCart: return FBSDKAppEventNameAddedToCart + case .AddedToWishlist: return FBSDKAppEventNameAddedToWishlist + case .AddedPaymentInfo: return FBSDKAppEventNameAddedPaymentInfo + case .InitiatedCheckout: return FBSDKAppEventNameInitiatedCheckout + + case .AchievedLevel: return FBSDKAppEventNameAchievedLevel + case .UnlockedAchievement: return FBSDKAppEventNameUnlockedAchievement + case .SpentCredits: return FBSDKAppEventNameSpentCredits + + case .Custom(let string): return string + } + } +} + +extension AppEventName: StringLiteralConvertible { + /** + Create an `AppEventName` from a string literal. + + - parameter value: The string literal to create from. + */ + public init(stringLiteral value: StringLiteralType) { + self = .Custom(value) + } + + /** + Create an `AppEventName` from a unicode scalar literal. + + - parameter value: The string literal to create from. + */ + public init(unicodeScalarLiteral value: String) { + self.init(stringLiteral: value) + } + + /** + Create an `AppEventName` from an extended grapheme cluster. + + - parameter value: The string literal to create from. + */ + public init(extendedGraphemeClusterLiteral value: String) { + self.init(stringLiteral: value) + } +} + +extension AppEventName: Hashable { + /// The hash value. + public var hashValue: Int { + return self.rawValue.hashValue + } +} + +extension AppEventName: CustomStringConvertible { + /// Textual representation of an app event name. + public var description: String { + return rawValue + } +} + +/** + Compare two `AppEventName`s for equality. + + - parameter lhs: The first app event name to compare. + - parameter rhs: The second app event name to compare. + + - returns: Whether or not the app event names are equal. + */ +public func == (lhs: AppEventName, rhs: AppEventName) -> Bool { + return lhs.rawValue == rhs.rawValue +} diff --git a/Sources/Core/AppEvents/AppEventParameterName.swift b/Sources/Core/AppEvents/AppEventParameterName.swift new file mode 100644 index 00000000..dd22d683 --- /dev/null +++ b/Sources/Core/AppEvents/AppEventParameterName.swift @@ -0,0 +1,142 @@ +// 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.FBSDKAppEvents + +/** + Represents a parameter name of the Facebook Analytics application event. + */ +public enum AppEventParameterName { + /// Identifier for the specific piece of content. + case ContentId + /// Type of the content, e.g. "music"/"photo"/"video". + case ContentType + /// Currency. E.g. "USD"/"EUR"/"GBP". See ISO-4217 for specific values. + case Currency + /// Appropriate description for the event. + case Description + /// Current level or level achieved. + case Level + /// Maximum rating available. E.g. "5"/"10". + case MaxRatingValue + /// Count of items being proccessed. + case ItemCount + /// Boolean value indicating whether payment information is available. + case PaymentInfoAvailable + /// Registration method used. E.g. "Facebook"/"email"/"sms". + case RegistrationMethod + /// String provided by the user for a search operation. + case SearchedString + /// Boolean value indicating wehtehr an activity being logged was succesful. + case Successful + /// Custom name of the parameter that is represented by a string. + case Custom(String) + + /** + Create an `AppEventParameterName` from `String`. + + - parameter string: String to create an app event name from. + */ + public init(_ string: String) { + self = .Custom(string) + } +} + +extension AppEventParameterName: RawRepresentable { + /** + Create an `AppEventParameterName` from `String`. + + - parameter rawValue: String to create an app event name from. + */ + public init?(rawValue: String) { + self = .Custom(rawValue) + } + + /// The corresponding `String` value. + public var rawValue: String { + switch self { + case .ContentId: return FBSDKAppEventParameterNameContentID + case .ContentType: return FBSDKAppEventParameterNameContentType + case .Currency: return FBSDKAppEventParameterNameCurrency + case .Description: return FBSDKAppEventParameterNameDescription + case .Level: return FBSDKAppEventNameAchievedLevel + case .MaxRatingValue: return FBSDKAppEventParameterNameMaxRatingValue + case .ItemCount: return FBSDKAppEventParameterNameNumItems + case .PaymentInfoAvailable: return FBSDKAppEventParameterNamePaymentInfoAvailable + case .RegistrationMethod: return FBSDKAppEventParameterNameRegistrationMethod + case .SearchedString: return FBSDKAppEventParameterNameSearchString + case .Successful: return FBSDKAppEventParameterNameSuccess + case .Custom(let string): return string + } + } +} + +extension AppEventParameterName: StringLiteralConvertible { + /** + Create an `AppEventParameterName` from a string literal. + + - parameter value: The string literal to create from. + */ + public init(stringLiteral value: StringLiteralType) { + self = .Custom(value) + } + + /** + Create an `AppEventParameterName` from a unicode scalar literal. + + - parameter value: The string literal to create from. + */ + public init(unicodeScalarLiteral value: String) { + self.init(stringLiteral: value) + } + + /** + Create an `AppEventParameterName` from an extended grapheme cluster. + + - parameter value: The string literal to create from. + */ + public init(extendedGraphemeClusterLiteral value: String) { + self.init(stringLiteral: value) + } +} + +extension AppEventParameterName: Hashable { + /// The hash value. + public var hashValue: Int { + return self.rawValue.hashValue + } +} + +extension AppEventParameterName: CustomStringConvertible { + /// Textual representation of an app event parameter name. + public var description: String { + return rawValue + } +} + +/** + Compare two `AppEventParameterName`s for equality. + + - parameter lhs: The first parameter name to compare. + - parameter rhs: The second parameter name to compare. + + - returns: Whether or not the parameter names are equal. + */ +public func == (lhs: AppEventParameterName, rhs: AppEventParameterName) -> Bool { + return lhs.rawValue == rhs.rawValue +} diff --git a/Sources/Core/AppEvents/AppEventsLogger.FlushBehavior.swift b/Sources/Core/AppEvents/AppEventsLogger.FlushBehavior.swift new file mode 100644 index 00000000..6b010774 --- /dev/null +++ b/Sources/Core/AppEvents/AppEventsLogger.FlushBehavior.swift @@ -0,0 +1,54 @@ +// 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.FBSDKAppEvents + +extension AppEventsLogger { + /** + Specifies when `AppEventsLogger` sends log events to the server. + */ + public enum FlushBehavior { + /** + Flush automatically: periodically (once a minute or every 100 logged events) and always at app reactivation. + */ + case Auto + + /** + Only flush when the `flush` method is called. + When an app is moved to background/terminated, the events are persisted and re-established at activation, + but they will only be written with an explicit call to `flush`. + */ + case ExplicitOnly + } +} + +extension AppEventsLogger.FlushBehavior { + internal init(sdkFlushBehavior: FBSDKAppEventsFlushBehavior) { + switch sdkFlushBehavior { + case .Auto: self = .Auto + case .ExplicitOnly: self = .ExplicitOnly + } + } + + internal var sdkFlushBehavior: FBSDKAppEventsFlushBehavior { + switch self { + case .Auto: return .Auto + case .ExplicitOnly: return .ExplicitOnly + } + } +} diff --git a/Sources/Core/AppEvents/AppEventsLogger.swift b/Sources/Core/AppEvents/AppEventsLogger.swift new file mode 100644 index 00000000..07cc1533 --- /dev/null +++ b/Sources/Core/AppEvents/AppEventsLogger.swift @@ -0,0 +1,191 @@ +// 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 Foundation +import FBSDKCoreKit.FBSDKAppEvents + +/** + Client-side event logging for specialized application analytics available through Facebook App Insights + and for use with Facebook Ads conversion tracking and optimization. + + The `AppEventsLogger` static class has a few related roles: + + + Logging predefined and application-defined events to Facebook App Insights with a + numeric value to sum across a large number of events, and an optional set of key/value + parameters that define "segments" for this event (e.g., 'purchaserStatus' : 'frequent', or + 'gamerLevel' : 'intermediate') + + + Logging events to later be used for ads optimization around lifetime value. + + + Methods that control the way in which events are flushed out to the Facebook servers. + + Here are some important characteristics of the logging mechanism provided by `AppEventsLogger`: + + + Events are not sent immediately when logged. They're cached and flushed out to the Facebook servers + in a number of situations: + - when an event count threshold is passed (currently 100 logged events). + - when a time threshold is passed (currently 15 seconds). + - when an app has gone to background and is then brought back to the foreground. + + + Events will be accumulated when the app is in a disconnected state, and sent when the connection is + restored and one of the above 'flush' conditions are met. + + + The `AppEventsLogger` class is thread-safe in that events may be logged from any of the app's threads. + + + The developer can set the `flushBehavior` to force the flushing of events to only + occur on an explicit call to the `flush` method. + + + The developer can turn on console debug output for event logging and flushing to the server by using + the `FBSDKLoggingBehaviorAppEvents` value in `[FBSettings setLoggingBehavior:]`. + + Some things to note when logging events: + + + There is a limit on the number of unique event names an app can use, on the order of 1000. + + There is a limit to the number of unique parameter names in the provided parameters that can + be used per event, on the order of 25. This is not just for an individual call, but for all invocations for that eventName. + + Event names and parameter names (the keys in the Dictionary) must be between 2 and 40 characters, + and must consist of alphanumeric characters, _, -, or spaces. + + The length of each parameter value can be no more than on the order of 100 characters. + */ +public class AppEventsLogger { + + //-------------------------------------- + // MARK: - Activate + //-------------------------------------- + + /** + Notifies the events system that the app has launched and, when appropriate, logs an "activated app" event. + Should typically be placed in the app delegates' `applicationDidBecomeActive()` function. + + This method also takes care of logging the event indicating the first time this app has been launched, + which, among other things, is used to track user acquisition and app install ads conversions. + + `activate()` will not log an event on every app launch, since launches happen every time the app is backgrounded and then foregrounded. + "activated app" events will be logged when the app has not been active for more than 60 seconds. + This method also causes a "deactivated app" event to be logged when sessions are "completed", + and these events are logged with the session length, with an indication of how much time has elapsed between sessions, + and with the number of background/foreground interruptions that session had. This data is all visible in your app's App Events Insights. + + - parameter application: Optional instance of UIApplication. Default: `UIApplication.sharedApplication()`. + */ + public static func activate(application: UIApplication = UIApplication.sharedApplication()) { + FBSDKAppEvents.activateApp() + } + + //-------------------------------------- + // MARK: - Log Events + //-------------------------------------- + + /** + Log an app event. + + - parameter event: The application event to log. + - parameter accessToken: Optional access token to use to log the event. Default: `AccessToken.current`. + */ + public static func log(event: AppEventLoggable, accessToken: AccessToken? = AccessToken.current) { + let valueToSum = event.valueToSum.map({ NSNumber(double:$0 ) }) + let parameters = event.parameters.keyValueMap { + ($0.0.rawValue as NSString, $0.1.appEventParameterValue) + } + FBSDKAppEvents.logEvent(event.name.rawValue, + valueToSum: valueToSum, + parameters: parameters, + accessToken: accessToken?.sdkAccessTokenRepresentation) + } + + /** + Log an app event. + + This overload is required, so dot-syntax works in this example: + ``` + AppEventsLogger().log(.Searched()) + ``` + + - parameter event: The application event to log. + - parameter accessToken: Optional access token to use to log the event. Default: `AccessToken.current`. + */ + public static func log(event: AppEvent, accessToken: AccessToken? = AccessToken.current) { + log(event as AppEventLoggable, accessToken: accessToken) + } + + /** + Log an app event. + + - parameter eventName: The name of the event to record. + - parameter parameters: Arbitrary parameter dictionary of characteristics. + - parameter valueToSum: Amount to be aggregated into all events of this eventName, and App Insights will report + the cumulative and average value of this amount. + - parameter accessToken: The optional access token to log the event as. Default: `AccessToken.current`. + */ + public static func log(eventName: String, + parameters: AppEvent.ParametersDictionary = [:], + valueToSum: Double? = nil, + accessToken: AccessToken? = AccessToken.current) { + let event = AppEvent(name: AppEventName(eventName), parameters: parameters, valueToSum: valueToSum) + log(event, accessToken: accessToken) + } + + //-------------------------------------- + // MARK: - Flush + //-------------------------------------- + + /** + The current event flushing behavior specifying when events are sent to Facebook. + */ + public static var flushBehavior: FlushBehavior { + get { + return FlushBehavior(sdkFlushBehavior: FBSDKAppEvents.flushBehavior()) + } + set { + FBSDKAppEvents.setFlushBehavior(newValue.sdkFlushBehavior) + } + } + + /** + Explicitly kick off flushing of events to Facebook. + This is an asynchronous method, but it does initiate an immediate kick off. + Server failures will be reported through the NotificationCenter with notification ID `FBSDKAppEventsLoggingResultNotification`. + */ + public static func flush() { + FBSDKAppEvents.flush() + } + + //-------------------------------------- + // MARK: - Override App Id + //-------------------------------------- + + /** + Facebook application id that is going to be used for logging all app events. + + In some cases, you might want to use one Facebook App ID for login and social presence and another for App Event logging. + (An example is if multiple apps from the same company share an app ID for login, but want distinct logging.) + By default, this value defers to the `FBSDKAppEventsOverrideAppIDBundleKey` plist value. + If that's not set, it defaults to `FBSDKSettings.appId`. + */ + public static var loggingAppId: String? { + get { + if let appId = FBSDKAppEvents.loggingOverrideAppID() { + return appId + } + return FBSDKSettings.appID() + } + set { + return FBSDKAppEvents.setLoggingOverrideAppID(newValue) + } + } +} diff --git a/Sources/Core/Common/AccessToken.swift b/Sources/Core/Common/AccessToken.swift new file mode 100644 index 00000000..d5154fc1 --- /dev/null +++ b/Sources/Core/Common/AccessToken.swift @@ -0,0 +1,148 @@ +// 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 Foundation +import FBSDKCoreKit + +//-------------------------------------- +// MARK: - Access Token +//-------------------------------------- + +/** + Represents an immutable access token used to authenticate with Facebook services. + */ +public struct AccessToken { + + /// The application id for this token. + public let appId: String + + /// An opaque authentication token. + public let authenticationToken: String + + /// The logged in user identifier. + public let userId: String? + + /// The date the token was last refreshed. + public let refreshDate: NSDate + + /// The expirate date for the token. + public let expirationDate: NSDate + + /// Known granted permissions. + public let grantedPermissions: Set? + + /// Known declined permissions. + public let declinedPermissions: Set? + + /** + Creates a new access token instance. + + - parameter appId: Optional application id for this token. Default: `SDKSettings.appId`. + - parameter authenticationToken: An opaque authentication token. + - parameter userId: Optional logged in user identifier. + - parameter refreshDate: Optional date the token was last refreshed (defaults to current date). + - parameter expirationDate: Optional expiration date (defaults to `NSDate.distantFuture()`). + - parameter grantedPermissions: Set of known granted permissions. + - parameter declinedPermissions: Set of known declined permissions. + */ + public init(appId: String = SDKSettings.appId, + authenticationToken: String, + userId: String? = nil, + refreshDate: NSDate = NSDate(), + expirationDate: NSDate = NSDate.distantFuture(), + grantedPermissions: Set? = nil, + declinedPermissions: Set? = nil) { + self.appId = appId + self.authenticationToken = authenticationToken + self.userId = userId + self.refreshDate = refreshDate + self.expirationDate = expirationDate + self.grantedPermissions = grantedPermissions + self.declinedPermissions = declinedPermissions + } +} + +//-------------------------------------- +// MARK: - Current Token +//-------------------------------------- + +extension AccessToken { + /** + A convenient representation of the authentication token of the current user + that is used by other SDK components (like `LoginManager` or `AppEventsLogger`). + */ + public static var current: AccessToken? { + get { + let token = FBSDKAccessToken.currentAccessToken() as FBSDKAccessToken? + return token.map(AccessToken.init) + } + set { + FBSDKAccessToken.setCurrentAccessToken(newValue?.sdkAccessTokenRepresentation) + } + } + + /** + Refresh the current access token's permission state and extend the token's expiration date, if possible. + + On a successful refresh, the `current` access token will be updated automatically, so you don't need to set it again. + + - note: If a token is already expired, it can't be refreshed. + - parameter completion: Optional completion to call when the token was refreshed or failed. + */ + public static func refreshCurrentToken(completion: ((AccessToken?, ErrorType?) -> Void)? = nil) { + FBSDKAccessToken.refreshCurrentAccessToken { (_, _, error: NSError?) in + completion?(self.current, error) + } + } +} + +//-------------------------------------- +// MARK: - Internal +//-------------------------------------- + +extension AccessToken { + internal init(sdkAccessToken: FBSDKAccessToken) { + self.init(appId: sdkAccessToken.appID, + authenticationToken: sdkAccessToken.tokenString, + userId: sdkAccessToken.userID, + refreshDate: sdkAccessToken.refreshDate, + expirationDate: sdkAccessToken.expirationDate, + grantedPermissions: sdkAccessToken.grantedSwiftPermissions, + declinedPermissions: sdkAccessToken.declinedSwiftPermissions) + } + + internal var sdkAccessTokenRepresentation: FBSDKAccessToken { + return FBSDKAccessToken(tokenString: authenticationToken, + permissions: grantedPermissions?.map({ $0.name }), + declinedPermissions: declinedPermissions?.map({ $0.name }), + appID: appId, + userID: userId, + expirationDate: expirationDate, + refreshDate: refreshDate) + } +} + +private extension FBSDKAccessToken { + var grantedSwiftPermissions: Set? { + return (permissions?.flatMap({ $0 as? String }).map({ Permission(name: $0) })).map(Set.init) + } + + var declinedSwiftPermissions: Set? { + return (declinedPermissions?.flatMap({ $0 as? String }).map({ Permission(name: $0) })).map(Set.init) + } +} diff --git a/Sources/Core/Common/ApplicationDelegate.swift b/Sources/Core/Common/ApplicationDelegate.swift new file mode 100644 index 00000000..ee3a9c0d --- /dev/null +++ b/Sources/Core/Common/ApplicationDelegate.swift @@ -0,0 +1,68 @@ +// 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 Foundation +import UIKit +import FBSDKCoreKit + +/** + The ApplicationDelegate is designed to post process the results from Facebook Login or Facebook Dialogs + (or any action that requires switching over to the native Facebook app or Safari). + + The methods in this class are designed to mirror those in UIApplicationDelegate, and you + should call them in the respective methods in your AppDelegate implementation. + */ +public final class ApplicationDelegate { + private let delegate: FBSDKApplicationDelegate = FBSDKApplicationDelegate.sharedInstance() + + /// Returns the singleton instance of an application delegate. + public static let shared = ApplicationDelegate() + + private init() { } +} + +extension ApplicationDelegate { + /** + Call this function from the `UIApplicationDelegate.application(application:didFinishLaunchingWithOptions:)` function + of the AppDelegate of your app It should be invoked for the proper initialization of the Facebook SDK. + + - parameter application: The application as passed to `UIApplicationDelegate`. + - parameter launchOptions: The launchOptions as passed to `UIApplicationDelegate`. + + - returns: `true` if the url contained in the `launchOptions` was intended for the Facebook SDK, otherwise - `false`. + */ + public func application(application: UIApplication, didFinishLaunchingWithOptions launchOptions: [NSObject : AnyObject]?) -> Bool { + return delegate.application(application, didFinishLaunchingWithOptions: launchOptions) + } + + /** + Call this function from the `UIApplicationDelegate.application(application:openURL:sourceApplication:annotation:)` function + of the AppDelegate for your app. It should be invoked for the proper processing of responses during interaction + with the native Facebook app or Safari as part of SSO authorization flow or Facebook dialogs. + + - parameter application: The application as passed to `UIApplicationDelegate`. + - parameter url: The URL as passed to `UIApplicationDelegate`. + - parameter sourceApplication: The sourceApplication as passed to `UIApplicationDelegate`. + - parameter annotation: The annotation as passed to `UIApplicationDelegate`. + + - returns: `true` if the url was intended for the Facebook SDK, otherwise - `false`. + */ + public func application(application: UIApplication, openURL url: NSURL, sourceApplication: String?, annotation: AnyObject) -> Bool { + return delegate.application(application, openURL:url, sourceApplication:sourceApplication, annotation:annotation) + } +} diff --git a/Sources/Core/Common/SDKLoggingBehavior.swift b/Sources/Core/Common/SDKLoggingBehavior.swift new file mode 100644 index 00000000..edc8ef0b --- /dev/null +++ b/Sources/Core/Common/SDKLoggingBehavior.swift @@ -0,0 +1,91 @@ +// 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.FBSDKSettings + +/** + Defines logging behavior used by the SDK. + To set/enable/disable specific behaviors use `Settings.loggingBehaviors`. + */ +public enum SDKLoggingBehavior { + /// Include access token in logging. + case AccessTokens + + /// Log performance characteristics + case PerformanceCharacteristics + + /// Log `AppEventsLogger` interactions. + case AppEvents + + /// Log informational occurences. + case Informational + + /// Log cache errors. + case CacheErrors + + /// Log errors from SDK UI controls. + case UIControlErrors + + /// Log debug warnings from API responses, e.g. when friends fields were requested, but `user_friends` permissions isn't granted. + case GraphAPIDebugWarning + + /** + Log warnings from API responses, e.g. when requested feature will be deprecated in next version of API. + Info is the lowest level of severity, using it will result in logging all previously mentioned levels. + */ + case GraphAPIDebugInfo + + /// Log errors from SDK network requests. + case NetworkRequests + + /// Log errors likely to be preventable by the developer. This behavior is enabled by default. + case DeveloperErrors +} + +extension SDKLoggingBehavior { + internal init?(sdkStringValue: String) { + switch sdkStringValue { + case FBSDKLoggingBehaviorAccessTokens: self = .AccessTokens + case FBSDKLoggingBehaviorPerformanceCharacteristics: self = .PerformanceCharacteristics + case FBSDKLoggingBehaviorAppEvents: self = .AppEvents + case FBSDKLoggingBehaviorInformational: self = .Informational + case FBSDKLoggingBehaviorCacheErrors: self = .CacheErrors + case FBSDKLoggingBehaviorUIControlErrors: self = .UIControlErrors + case FBSDKLoggingBehaviorGraphAPIDebugWarning: self = .GraphAPIDebugWarning + case FBSDKLoggingBehaviorGraphAPIDebugInfo: self = .GraphAPIDebugInfo + case FBSDKLoggingBehaviorNetworkRequests: self = .NetworkRequests + case FBSDKLoggingBehaviorDeveloperErrors: self = .DeveloperErrors + default: return nil + } + } + + internal var sdkStringValue: String { + switch self { + case .AccessTokens: return FBSDKLoggingBehaviorAccessTokens + case .PerformanceCharacteristics: return FBSDKLoggingBehaviorPerformanceCharacteristics + case .AppEvents: return FBSDKLoggingBehaviorAppEvents + case .Informational: return FBSDKLoggingBehaviorInformational + case .CacheErrors: return FBSDKLoggingBehaviorCacheErrors + case .UIControlErrors: return FBSDKLoggingBehaviorUIControlErrors + case .GraphAPIDebugWarning: return FBSDKLoggingBehaviorGraphAPIDebugWarning + case .GraphAPIDebugInfo: return FBSDKLoggingBehaviorGraphAPIDebugInfo + case .NetworkRequests: return FBSDKLoggingBehaviorNetworkRequests + case .DeveloperErrors: return FBSDKLoggingBehaviorDeveloperErrors + } + } +} diff --git a/Sources/Core/Common/SDKSettings.swift b/Sources/Core/Common/SDKSettings.swift new file mode 100644 index 00000000..989ce95b --- /dev/null +++ b/Sources/Core/Common/SDKSettings.swift @@ -0,0 +1,176 @@ +// 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 UIKit +import FBSDKCoreKit.FBSDKSettings + +//-------------------------------------- +// MARK: - SDK Version +//-------------------------------------- + +/// Current SDK version. +public let SDKVersion = FBSDKSettings.sdkVersion() + +//-------------------------------------- +// MARK: - SDKSettings +//-------------------------------------- + +/** + Provides access to settings and configuration used by the entire SDK. + */ +public struct SDKSettings { + /** + Facebook App ID used by the SDK. + Default value is read from the application's Info.plist under `FacebookAppId` key. + */ + public static var appId: String { + get { + return FBSDKSettings.appID() + } + set { + FBSDKSettings.setAppID(newValue) + } + } + + /** + The default url scheme suffix used for sessions. + Default value is read from the applicaiton's Info.plist under `FacebookUrlSchemeSuffix` key. + */ + public static var appURLSchemeSuffix: String { + get { + return FBSDKSettings.appURLSchemeSuffix() + } + set { + FBSDKSettings.setAppURLSchemeSuffix(newValue) + } + } + + /** + The client token used by the SDK. + It's is required for certain API calls when made anonymously aka without a user-based access token. + + Default value is read from the application's Info.plist under `FacebookClientToken` key. + */ + public static var clientToken: String? { + get { + return FBSDKSettings.clientToken() + } + set { + FBSDKSettings.setClientToken(clientToken) + } + } + + /** + Facebook Display Name used by the SDK. + Default value is read from the application's Info.plist under `FacebookDisplayName` key. + */ + public static var displayName: String? { + get { + return FBSDKSettings.displayName() + } + set { + FBSDKSettings.setDisplayName(newValue) + } + } + + /** + Facebook sudomain name used by the SDK. + This can be used to add the subdomain for all network requests that SDK is sending, + e.g. setting this to `"beta"` will make all graph requests to be sent to `graph.beta.facebook.com`. + Default value is read from the application's Info.plist under `FacebookDomainPart` key. + */ + public static var facebookSubdomain: String? { + get { + return FBSDKSettings.facebookDomainPart() + } + set { + FBSDKSettings.setFacebookDomainPart(newValue) + } + } + + /** + The quality of JPEG images sent to Facebook from the SDK. + Default value is `0.9`. + */ + public static var JPEGCompressionQuality: Double { + get { + return Double(FBSDKSettings.JPEGCompressionQuality()) + } + set { + FBSDKSettings.setJPEGCompressionQuality(CGFloat(newValue)) + } + } + + /** + If enabled, data such as that generated through `AppEventsLogger` and sent to Facebook + should be restricted from being used for other than analytics and conversions. + Defaults to `false`. This value is stored on the device and persists across app launches. + */ + public static var limitedEventAndDataUsage: Bool { + get { + return FBSDKSettings.limitEventAndDataUsage() + } + set { + FBSDKSettings.setLimitEventAndDataUsage(newValue) + } + } +} + +//-------------------------------------- +// MARK: - SDKSettings + Logging Behavior +//-------------------------------------- + +extension SDKSettings { + /** + Current logging behaviors of Facebook SDK. + The default enabled behavior is `.DeveloperErrors` only. + */ + public static var enabledLoggingBehaviors: Set { + get { + let behaviors = FBSDKSettings.loggingBehavior().flatMap { object -> SDKLoggingBehavior? in + if let value = object as? String { + return SDKLoggingBehavior(sdkStringValue: value) + } + return nil + } + return Set(behaviors) + } + set { + let behaviors = newValue.map({ $0.sdkStringValue }) + FBSDKSettings.setLoggingBehavior(Set(behaviors)) + } + } + + /** + Enable a particular Facebook SDK logging behavior. + + - parameter behavior: The behavior to enable + */ + public static func enableLoggingBehavior(behavior: SDKLoggingBehavior) { + FBSDKSettings.enableLoggingBehavior(behavior.sdkStringValue) + } + + /** + Disable a particular Facebook SDK logging behavior. + + - parameter behavior: The behavior to disable. + */ + public static func disableLoggingBehavior(behavior: SDKLoggingBehavior) { + FBSDKSettings.disableLoggingBehavior(behavior.sdkStringValue) + } +} diff --git a/Sources/Core/GraphRequest/GraphRequest.swift b/Sources/Core/GraphRequest/GraphRequest.swift new file mode 100644 index 00000000..18eeea30 --- /dev/null +++ b/Sources/Core/GraphRequest/GraphRequest.swift @@ -0,0 +1,71 @@ +// 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 Foundation + +import FBSDKCoreKit + +/** + Represents a request to the Facebook Graph API. + + `GraphRequest` encapsulates the components of a request (the Graph API path, the parameters, error recovery behavior) + and should be used in conjunction with `GraphRequestConnection` to issue the request. + + Nearly all Graph APIs require an access token. + Unless specified, the `AccessToken.current` is used. Therefore, most requests + will require login first (see `LoginManager` in `FacebookLogin.framework`). + */ +public struct GraphRequest: GraphRequestProtocol { + public typealias Response = GraphResponse + + /// The Graph API endpoint to use for the request, e.g. `"me"`. + public let graphPath: String + + /// The request parameters. + public var parameters: [String : AnyObject]? + + /// The `AccessToken` used by the request to authenticate. + public let accessToken: AccessToken? + + /// The `HTTPMethod` to use for the request, e.g. `.GET`/`.POST`/`.DELETE`. + public let httpMethod: GraphRequestHTTPMethod + + /// Graph API Version to use, e.g. `"2.7"`. + public let apiVersion: String + + /** + Initializes a new instance of graph request. + + - parameter graphPath: The Graph API endpoint to use for the request. + - parameter parameters: Optional parameters dictionary. + - parameter accessToken: Optional authentication token to use. Defaults to `AccessToken.current`. + - parameter httpMethod: Optional `GraphRequestHTTPMethod` to use for the request. Defaults to `.GET`. + - parameter apiVersion: Optional Graph API version to use. Defaults to `FBSDK_TARGET_PLATFORM_VERSION`. + */ + public init(graphPath: String, + parameters: [String : AnyObject] = [:], + accessToken: AccessToken? = AccessToken.current, + httpMethod: GraphRequestHTTPMethod = .GET, + apiVersion: String = FBSDK_TARGET_PLATFORM_VERSION) { + self.graphPath = graphPath + self.parameters = parameters + self.accessToken = accessToken + self.httpMethod = httpMethod + self.apiVersion = apiVersion + } +} diff --git a/Sources/Core/GraphRequest/GraphRequestConnection.Delegate.swift b/Sources/Core/GraphRequest/GraphRequestConnection.Delegate.swift new file mode 100644 index 00000000..47a79da0 --- /dev/null +++ b/Sources/Core/GraphRequest/GraphRequestConnection.Delegate.swift @@ -0,0 +1,50 @@ +// 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 Foundation +import FBSDKCoreKit.FBSDKGraphRequestConnection + +internal class GraphRequestConnectionDelegateBridge: NSObject { + var networkFailureHandler: GraphRequestConnection.NetworkFailureHandler? = nil + var networkProgressHandler: GraphRequestConnection.NetworkProgressHandler? = nil + + func setupAsDelegateFor(connection: FBSDKGraphRequestConnection) { + // We need for the connection to retain us, + // so we can stick around and keep calling into handlers, + // as long as the connection is alive/sending messages. + objc_setAssociatedObject(connection, unsafeAddressOf(self), self, .OBJC_ASSOCIATION_RETAIN) + connection.delegate = self + } +} + +extension GraphRequestConnectionDelegateBridge: FBSDKGraphRequestConnectionDelegate { + func requestConnection(connection: FBSDKGraphRequestConnection!, + didSendBodyData bytesWritten: Int, totalBytesWritten: Int, totalBytesExpectedToWrite: Int) { + if let handler = networkProgressHandler { + handler(bytesSent: Int64(bytesWritten), + totalBytesSent: Int64(totalBytesWritten), + totalExpectedBytes: Int64(totalBytesExpectedToWrite)) + } + } + + func requestConnection(connection: FBSDKGraphRequestConnection!, didFailWithError error: NSError!) { + if let handler = networkFailureHandler { + handler(error) + } + } +} diff --git a/Sources/Core/GraphRequest/GraphRequestConnection.swift b/Sources/Core/GraphRequest/GraphRequestConnection.swift new file mode 100644 index 00000000..fba907ee --- /dev/null +++ b/Sources/Core/GraphRequest/GraphRequestConnection.swift @@ -0,0 +1,157 @@ +// 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 Foundation +import FBSDKCoreKit.FBSDKGraphRequestConnection + +//-------------------------------------- +// MARK: - GraphRequestConnection +//-------------------------------------- +/** + Represents a single connection to Facebook to service a single or multiple requests. + + The request settings and properties are encapsulated in a reusable `GraphRequest` or a custom `GraphRequestProtocol`. + This object encapsulates the concerns of a single communication e.g. starting a connection, canceling a connection, or batching requests. + */ +public class GraphRequestConnection { + /// A type of the closure that could be used to track network progress of a specific connection. + public typealias NetworkProgressHandler = (bytesSent: Int64, totalBytesSent: Int64, totalExpectedBytes: Int64) -> Void + + /// A type of the closure that could be used to track network errors of a specific connection. + public typealias NetworkFailureHandler = (ErrorType) -> Void + + /// Network progress closure that is going to be called every time data is sent to the server. + public var networkProgressHandler: NetworkProgressHandler? = nil { + didSet { + sdkDelegateBridge.networkProgressHandler = networkProgressHandler + } + } + + /** + Network failure handler that is going to be called when a connection fails with a network error. + Use completion on per request basis to get additional information, that is not related to network errors. + */ + public var networkFailureHandler: NetworkFailureHandler? = nil { + didSet { + sdkDelegateBridge.networkFailureHandler = networkFailureHandler + } + } + + /// The operation queue that is used to call all network handlers. + public var networkHandlerQueue: NSOperationQueue = NSOperationQueue.mainQueue() { + didSet { + sdkConnection.setDelegateQueue(networkHandlerQueue) + } + } + + private var sdkConnection = FBSDKGraphRequestConnection() + private var sdkDelegateBridge = GraphRequestConnectionDelegateBridge() + + /** + Initializes a connection. + */ + public init() { + sdkDelegateBridge.setupAsDelegateFor(sdkConnection) + } +} + +//-------------------------------------- +// MARK: - Requests +//-------------------------------------- + +extension GraphRequestConnection { + /** + Adds a request object to this connection. + + - parameter request: Request to be included in this connection. + - parameter batchEntryName: Optional name for this request. + This can be used to feed the results of one request to the input of another, as long as they are in the same `GraphRequestConnection` + As described in [Graph API Batch Requests](https://developers.facebook.com/docs/reference/api/batch/). + - parameter completion: Optional completion closure that is going to be called when the connection finishes or fails. + */ + public func add( + request: T, + batchEntryName: String? = nil, + completion: ((connectionHTTPResponse: NSHTTPURLResponse?, result: GraphRequestResult) -> Void)? = nil) { + let batchParameters = batchEntryName.map({ ["name" : $0] }) + add(request, batchParameters: batchParameters, completion: completion) + } + + /** + Adds a request object to this connection. + + - parameter request: Request to be included in this connection. + - parameter batchParameters: Optional dictionary of parameters to include for this request + as described in [Graph API Batch Requests](https://developers.facebook.com/docs/reference/api/batch/). + Examples include "depends_on", "name", or "omit_response_on_success". + - parameter completion: Optional completion closure that is going to be called when the connection finishes or fails. + */ + public func add( + request: T, + batchParameters: [String : AnyObject]?, + completion: ((connectionHTTPResponse: NSHTTPURLResponse?, result: GraphRequestResult) -> Void)? = nil) { + sdkConnection.addRequest(request.sdkRequest, + completionHandler: completion.map(sdkRequestCompletion), + batchParameters: batchParameters) + } + + /** + Starts a connection with the server and sends all the requests in this connection. + - warning: This method can't be called twice per a single `GraphRequestConnection` instance. + */ + public func start() { + sdkConnection.start() + } + + /** + Signals that a connect should be logically terminated as per application is no longer interested in a response. + + Synchronouslly calls any handlers indicating the request was cancelled. + This doesn't guarantee that the request-related processing will cease. + It does promise that all handlers will complete before the cancel returns. + A call to `cancel` prior to a start implies a cancellation of all requests associated with the connection. + */ + public func cancel() { + sdkConnection.cancel() + } +} + +//-------------------------------------- +// MARK: - Private +//-------------------------------------- + +extension GraphRequestConnection { + /// Custom typealias that is the same as FBSDKGraphRequestHandler, but without implicitly unwrapped optionals. + private typealias SDKRequestCompletion = (connection: FBSDKGraphRequestConnection?, rawResponse: AnyObject?, error: NSError?) -> Void + + private func sdkRequestCompletion( + from completion: ((httpResponse: NSHTTPURLResponse?, result: GraphRequestResult) -> Void) + ) -> SDKRequestCompletion { + return { connection, rawResponse, error in + let result: GraphRequestResult = { + switch error { + case let error?: + return .Failed(error) + default: + return .Success(response: T.Response(rawResponse: rawResponse)) + } + }() + completion(httpResponse: connection?.URLResponse, result: result) + } + } +} diff --git a/Sources/Core/GraphRequest/GraphRequestDataAttachment.swift b/Sources/Core/GraphRequest/GraphRequestDataAttachment.swift new file mode 100644 index 00000000..ffc0af9d --- /dev/null +++ b/Sources/Core/GraphRequest/GraphRequestDataAttachment.swift @@ -0,0 +1,58 @@ +// 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 Foundation +import FBSDKCoreKit + +/** + A container for data attachments so that additional metadata can be provided about the attachment (like content type or filename). + */ +public class GraphRequestDataAttachment { + + /// The attachment data. + public let data: NSData + + /// The file name of the attachment. + public let filename: String? + + /// The content type of the attachment. + public let contentType: String? + + /** + Initializes a data attachment + + - parameter data: The attachment data (retained, not copied). + - parameter filename: Optional filename for the attachment. Default: `nil`. + - parameter contentType: Optional content type for the attachment. Default: `nil`. + */ + public init(data: NSData, filename: String? = nil, contentType: String? = nil) { + self.data = data + self.filename = filename + self.contentType = contentType + } +} + +//-------------------------------------- +// MARK: - Bridging +//-------------------------------------- + +extension GraphRequestDataAttachment { + internal var sdkDataAttachment: FBSDKGraphRequestDataAttachment { + return FBSDKGraphRequestDataAttachment(data: data, filename: filename, contentType: contentType) + } +} diff --git a/Sources/Core/GraphRequest/GraphRequestProtocol.Bridge.swift b/Sources/Core/GraphRequest/GraphRequestProtocol.Bridge.swift new file mode 100644 index 00000000..77c072cd --- /dev/null +++ b/Sources/Core/GraphRequest/GraphRequestProtocol.Bridge.swift @@ -0,0 +1,37 @@ +// 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 Foundation +import FBSDKCoreKit.FBSDKGraphRequest + +extension GraphRequestProtocol { + internal var sdkRequest: FBSDKGraphRequest { + // TODO: (nlutsenko) Consider constraining `parameters` for specific types aka create `GraphRequestParameterValue` protocol. + let sdkParameters: [String : AnyObject]? = parameters?.keyValueMap({ key, value in + if let value = value as? GraphRequestDataAttachment { + return (key, value.sdkDataAttachment) + } + return (key, value) + }) + return FBSDKGraphRequest(graphPath: graphPath, + parameters: sdkParameters, + tokenString: accessToken?.authenticationToken, + version: apiVersion, + HTTPMethod: httpMethod.rawValue) + } +} diff --git a/Sources/Core/GraphRequest/GraphRequestProtocol.swift b/Sources/Core/GraphRequest/GraphRequestProtocol.swift new file mode 100644 index 00000000..347547a5 --- /dev/null +++ b/Sources/Core/GraphRequest/GraphRequestProtocol.swift @@ -0,0 +1,77 @@ +// 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 Foundation + +/** + Protocol that represents a request to the Facebook Graph API. + + An implementation of this protocol is intended to be either generic and be used for a lot of separate endpoints, + or encapsulate a request + response type for a single endpoint, for example `Profile`. + + To send a request and receive a response - see `GraphRequestConnection`. + + Nearly all Graph APIs require an access token. + Unless specified, the `AccessToken.current` is used. Therefore, most requests + will require login first (see `LoginManager` in `FacebookLogin.framework`). + + A `start` function is provided for convenience for single requests. + */ +public protocol GraphRequestProtocol { + associatedtype Response: GraphResponseProtocol + + /// The Graph API endpoint to use for the request, e.g. `"me"`. + var graphPath: String { get } + + /// The request parameters. + var parameters: [String : AnyObject]? { get } + + /// The `AccessToken` used by the request to authenticate. + var accessToken: AccessToken? { get } + + /// The `HTTPMethod` to use for the request, e.g. `.GET`/`.POST`/`.DELETE`. + var httpMethod: GraphRequestHTTPMethod { get } + + /// Graph API Version to use, e.g. `"2.7"`. + var apiVersion: String { get } +} + +extension GraphRequestProtocol { + /** + A convenience method that creates and starts a connection to the Graph API. + + - parameter completion: Optional completion closure that is going to be called when the connection finishes or fails. + */ + public func start(completion: ((httpResponse: NSHTTPURLResponse?, result: GraphRequestResult) -> Void)? = nil) { + let connection = GraphRequestConnection() + connection.add(self, completion: completion) + connection.start() + } +} + +/** + Represents HTTP methods that could be used to issue `GraphRequestProtocol`. + */ +public enum GraphRequestHTTPMethod: String { + /// `GET` graph request HTTP method. + case GET = "GET" + /// `POST` graph request HTTP method. + case POST = "POST" + /// `DELETE` graph request HTTP method. + case DELETE = "DELETE" +} diff --git a/Sources/Core/GraphRequest/GraphRequestResult.swift b/Sources/Core/GraphRequest/GraphRequestResult.swift new file mode 100644 index 00000000..bdc2a1b5 --- /dev/null +++ b/Sources/Core/GraphRequest/GraphRequestResult.swift @@ -0,0 +1,34 @@ +// 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. + +/** + Represents result of a single request. + Can either be `.Success` or `.Failed` + */ +public enum GraphRequestResult { + /** + Represents succesful result of a `GraphRequestProtocol`. + Encapsulates response from the server. + */ + case Success(response: T.Response) + /** + Represents errored result of a `GraphRequestProtocol`. + Encapsulates error that was encountered. + */ + case Failed(ErrorType) +} diff --git a/Sources/Core/GraphRequest/GraphResponse.swift b/Sources/Core/GraphRequest/GraphResponse.swift new file mode 100644 index 00000000..75a0a7ec --- /dev/null +++ b/Sources/Core/GraphRequest/GraphResponse.swift @@ -0,0 +1,62 @@ +// 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 Foundation + +/** + Represents a generic response that was received when `GraphRequest` succeeded. + */ +public struct GraphResponse: GraphResponseProtocol { + private let rawResponse: AnyObject? + + /** + Initializes a `GraphResponse`. + + - parameter rawResponse: Raw response received from a server. + Usually is represented by either a `Dictionary` or `Array`. + */ + public init(rawResponse: AnyObject?) { + self.rawResponse = rawResponse + } +} + +extension GraphResponse { + /** + Converts and returns a response in a form of `Dictionary`. + If the conversion fails or there is was response - returns `nil`. + */ + public var dictionaryValue: [String : AnyObject]? { + return rawResponse as? [String : AnyObject] + } + + /** + Converts and returns a response in a form of `Array` + If the conversion fails or there is was response - returns `nil`. + */ + public var arrayValue: [AnyObject]? { + return rawResponse as? [AnyObject] + } + + /** + Converts and returns a response in a form of `String`. + If the conversion fails or there is was response - returns `nil`. + */ + public var stringValue: String? { + return rawResponse as? String + } +} diff --git a/Sources/Core/GraphRequest/GraphResponseProtocol.swift b/Sources/Core/GraphRequest/GraphResponseProtocol.swift new file mode 100644 index 00000000..35190414 --- /dev/null +++ b/Sources/Core/GraphRequest/GraphResponseProtocol.swift @@ -0,0 +1,32 @@ +// 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 Foundation + +/** + Protocol that represents a response for a given `GraphRequestProtocol`. + */ +public protocol GraphResponseProtocol { + /** + Initializes a response. + + - parameter rawResponse: Raw response received from a server. + Usually is represented by either a `Dictionary` or `Array`. + */ + init(rawResponse: AnyObject?) +} diff --git a/Sources/Core/Internal/Extensions/Dictionary+KeyValueMap.swift b/Sources/Core/Internal/Extensions/Dictionary+KeyValueMap.swift new file mode 100644 index 00000000..88f14648 --- /dev/null +++ b/Sources/Core/Internal/Extensions/Dictionary+KeyValueMap.swift @@ -0,0 +1,40 @@ +// 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. + +extension Dictionary { + func keyValueMap(@noescape transform: Element throws -> (K, V)) rethrows -> [K:V] { + var dictionary: [K:V] = [:] + try forEach { + let transformed = try transform($0.0, $0.1) + dictionary[transformed.0] = transformed.1 + } + return dictionary + } + + func keyValueFlatMap(@noescape transform: Element throws -> (K?, V?)) rethrows -> [K:V] { + var dictionary: [K:V] = [:] + try forEach { + let transformed = try transform($0.0, $0.1) + if let key = transformed.0, + let value = transformed.1 { + dictionary[key] = value + } + } + return dictionary + } +} diff --git a/Sources/Core/Internal/Extensions/Optional+OnSome.swift b/Sources/Core/Internal/Extensions/Optional+OnSome.swift new file mode 100644 index 00000000..9535b1b4 --- /dev/null +++ b/Sources/Core/Internal/Extensions/Optional+OnSome.swift @@ -0,0 +1,23 @@ +// 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. + +extension Optional { + internal func onSome(@noescape closure: (Wrapped) throws -> Void) rethrows { + _ = try map(closure) + } +} diff --git a/Sources/Core/Permissions/Permission.swift b/Sources/Core/Permissions/Permission.swift new file mode 100644 index 00000000..373106c4 --- /dev/null +++ b/Sources/Core/Permissions/Permission.swift @@ -0,0 +1,89 @@ +// 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 Foundation + +/** + Represents a Graph API permission. + Each permission has its own set of requirements and suggested use cases. + See a full list at https://developers.facebook.com/docs/facebook-login/permissions + */ +public struct Permission { + internal let name: String + + /** + Create a permission with a string value. + + - parameter name: Name of the permission. + */ + public init(name: String) { + self.name = name + } +} + +extension Permission: StringLiteralConvertible { + /** + Create a permission with a string value. + + - parameter value: String literal representation of the permission. + */ + public init(stringLiteral value: StringLiteralType) { + self.init(name: value) + } + + /** + Create a permission with a string value. + + - parameter value: String literal representation of the permission. + */ + public init(unicodeScalarLiteral value: String) { + self.init(name: value) + } + + /** + Create a permission with a string value. + + - parameter value: String literal representation of the permission. + */ + public init(extendedGraphemeClusterLiteral value: String) { + self.init(name: value) + } +} + +extension Permission: Equatable, Hashable { + /// The hash value. + public var hashValue: Int { + return name.hashValue + } +} + +/** + Compare two `Permission`s for equality. + + - parameter lhs: The first permission to compare. + - parameter rhs: The second permission to compare. + + - returns: Whether or not the permissions are equal. + */ +public func == (lhs: Permission, rhs: Permission) -> Bool { + return lhs.name == rhs.name +} + +internal protocol PermissionRepresentable { + var permissionValue: Permission { get } +} diff --git a/Sources/Core/Permissions/PublishPermission.swift b/Sources/Core/Permissions/PublishPermission.swift new file mode 100644 index 00000000..929e3c1a --- /dev/null +++ b/Sources/Core/Permissions/PublishPermission.swift @@ -0,0 +1,47 @@ +// 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 Foundation + +/** + Enum that represents a Graph API publish permission. + Each permission has its own set of requirements and suggested use cases. + See a full list at https://developers.facebook.com/docs/facebook-login/permissions + */ +public enum PublishPermission { + /** + Provides access to publish Posts, Open Graph actions, achievements, + scores and other activity on behalf of a person using your app. + */ + case PublishActions + + /** + Permission with a custom string value. + See https://developers.facebook.com/docs/facebook-login/permissions for full list of available permissions. + */ + case Custom(String) +} + +extension PublishPermission: PermissionRepresentable { + internal var permissionValue: Permission { + switch self { + case .PublishActions: return "publish_actions" + case .Custom(let string): return Permission(name: string) + } + } +} diff --git a/Sources/Core/Permissions/ReadPermission.swift b/Sources/Core/Permissions/ReadPermission.swift new file mode 100644 index 00000000..d1b2e54d --- /dev/null +++ b/Sources/Core/Permissions/ReadPermission.swift @@ -0,0 +1,49 @@ +// 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 Foundation + +/** + Enum that represents a Graph API read permission. + Each permission has its own set of requirements and suggested use cases. + See a full list at https://developers.facebook.com/docs/facebook-login/permissions + */ +public enum ReadPermission { + /// Provides access to a subset of items that are part of a person's public profile. + case PublicProfile + /// Provides access the list of friends that also use your app. + case UserFriends + /// Provides access to the person's primary email address. + case Email + /** + Permission with a custom string value. + See https://developers.facebook.com/docs/facebook-login/permissions for full list of available permissions. + */ + case Custom(String) +} + +extension ReadPermission: PermissionRepresentable { + internal var permissionValue: Permission { + switch self { + case .PublicProfile: return "public_profile" + case .UserFriends: return "user_friends" + case .Email: return "email" + case .Custom(let string): return Permission(name: string) + } + } +} diff --git a/Sources/Core/UserProfile/UserProfile.swift b/Sources/Core/UserProfile/UserProfile.swift new file mode 100644 index 00000000..b2f50129 --- /dev/null +++ b/Sources/Core/UserProfile/UserProfile.swift @@ -0,0 +1,195 @@ +// 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 Foundation +import UIKit +import FBSDKCoreKit.FBSDKProfile + +//-------------------------------------- +// MARK: - UserProfile +//-------------------------------------- + +/** + Represents an immutable Facebook profile. + + This class provides a global `current` instance to more easily add social context to your application. + When the profile changes, a notification is posted so that you can update relevant parts of your UI and is persisted to `NSUserDefaults`. + + Typically, you will want to call `Profile.updatesOnAccessTokenChange = true`, + so that it automatically observes changes to the `AccessToken.current`. + + You can use this class to build your own `ProfilePictureView` or in place of typical requests to "/me". + */ +public struct UserProfile { + /// The user id. + public let userId: String + + /// The user's first name. + public let firstName: String? + + /// The user's middle name. + public let middleName: String? + + /// The user's last name. + public let lastName: String? + + /// The user's complete name. + public let fullName: String? + + /// A URL to the user's profile. + public let profileURL: NSURL? + + /// The last time the profile was refreshed. + public let refreshDate: NSDate + + /** + Creates a new instance of `Profile`. + + - parameter userId: The user id. + - parameter firstName: Optional user's first name. + - parameter middleName: Optional user's middle name. + - parameter lastName: Optional user's last name. + - parameter fullName: Optional user's full name. + - parameter profileURL: Optional user's profile URL. + - parameter refreshDate: Optional user's last refresh date. Default: `NSDate()` aka current date/time. + */ + public init(userId: String, + firstName: String? = nil, + middleName: String? = nil, + lastName: String? = nil, + fullName: String? = nil, + profileURL: NSURL? = nil, + refreshDate: NSDate = NSDate()) { + self.userId = userId + self.firstName = firstName + self.middleName = middleName + self.lastName = lastName + self.fullName = fullName + self.profileURL = profileURL + self.refreshDate = refreshDate + } +} + +//-------------------------------------- +// MARK: - Current Profile +//-------------------------------------- + +extension UserProfile { + /** + Current instance of `Profile` that represents the currently logged in user's profile. + */ + public static var current: UserProfile? { + get { + let sdkProfile = FBSDKProfile.currentProfile() as FBSDKProfile? + return sdkProfile.map(UserProfile.init) + } + set { + FBSDKProfile.setCurrentProfile(newValue?.sdkProfileRepresentation) + } + } + + /** + Loads the current profile and passes it to the completion closure. + + If the `current` profile is already loaded, this method will call the completion block synchronously, + otherwise it will begin a graph request to update `current` profile and the call the completion closure when finished. + + - parameter completion: The closure to be executed once the profile is loaded. + */ + public static func loadCurrent(completion: ((UserProfile?, ErrorType?) -> Void)?) { + FBSDKProfile.loadCurrentProfileWithCompletion { (profile: FBSDKProfile?, error: NSError?) in + completion?(profile.map(UserProfile.init), error) + } + } + + private static var _updatesOnAccessTokenChange: Bool = false + /** + Allows controlling whether `current` profile should automatically update when `AccessToken.current` changes. + + - note: If `AccessToken.current` is unset (changes to `nil`), the `current` profile instance remains. + It's also possible for the `current` to return `nil` until the data is fetched. + */ + public static var updatesOnAccessTokenChange: Bool { + get { + return _updatesOnAccessTokenChange + } + set { + _updatesOnAccessTokenChange = newValue + FBSDKProfile.enableUpdatesOnAccessTokenChange(newValue) + } + } +} + +//-------------------------------------- +// MARK: - Profile Picture +//-------------------------------------- + +extension UserProfile { + /** + Defines the aspect ratio for the source image of the profile picture. + */ + public enum PictureAspectRatio { + /// A square cropped version of the profile picture. + case Square + /// The original picture's aspect ratio. + case Normal + + internal var sdkPictureMode: FBSDKProfilePictureMode { + switch self { + case Square: return .Square + case Normal: return .Normal + } + } + } + + /** + Returns a complete `NSURL` for retrieving the user's profile image. + + - parameter aspectRatio: Apsect ratio of the source image to use. + - parameter size: Requested height and width of the image. Will be rounded to integer precision. + */ + public func imageURLWith(aspectRatio: PictureAspectRatio, size: CGSize) -> NSURL { + return sdkProfileRepresentation.imageURLForPictureMode(aspectRatio.sdkPictureMode, size: size) + } +} + +//-------------------------------------- +// MARK: - Internal +//-------------------------------------- + +extension UserProfile { + internal init(sdkProfile: FBSDKProfile) { + self.init(userId: sdkProfile.userID, + firstName: sdkProfile.firstName, + middleName: sdkProfile.middleName, + lastName: sdkProfile.lastName, + fullName: sdkProfile.name, + profileURL: sdkProfile.linkURL, + refreshDate: sdkProfile.refreshDate) + } + + internal var sdkProfileRepresentation: FBSDKProfile { + return FBSDKProfile(userID: userId, + firstName: firstName, + middleName: middleName, + lastName: lastName, + name: fullName, + linkURL: profileURL, + refreshDate: refreshDate) + } +} diff --git a/Sources/Core/UserProfile/UserProfilePictureView.swift b/Sources/Core/UserProfile/UserProfilePictureView.swift new file mode 100644 index 00000000..0d74c073 --- /dev/null +++ b/Sources/Core/UserProfile/UserProfilePictureView.swift @@ -0,0 +1,86 @@ +// 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 Foundation +import UIKit + +import FBSDKCoreKit.FBSDKProfilePictureView + +/// A view to display a profile picture. +public class UserProfilePictureView: UIView { + + private let sdkProfilePictureView = FBSDKProfilePictureView(frame: .zero) + + /// The aspect ratio of the source image of the profile picture. + public var pictureAspectRatio = UserProfile.PictureAspectRatio.Square { + didSet { + sdkProfilePictureView.pictureMode = pictureAspectRatio.sdkPictureMode + } + } + + /// The user id to show the picture for. + public var userId = "me" { + didSet { + sdkProfilePictureView.profileID = userId + } + } + + /** + Create a new instance of `UserProfilePictureView`. + + - parameter frame: Optional frame rectangle for the view, measured in points. + - parameter profile: Optional profile to display a picture for. Default: `UserProfile.current`. + */ + public init(frame: CGRect = .zero, profile: UserProfile? = nil) { + super.init(frame: frame) + + if let profile = profile { + userId = profile.userId + } + setupSDKProfilePictureView() + } + + /** + Create a new instance of `UserProfilePictureView` from an encoded interface file. + + - parameter decoder: The coder to initialize from. + */ + public required init?(coder decoder: NSCoder) { + super.init(coder: decoder) + setupSDKProfilePictureView() + } + + /** + Explicitly marks the receiver as needing to update the image. + + This method is called whenever any properties that affect the source image are modified, + but this can also be used to trigger a manual update of the image if it needs to be re-downloaded. + */ + public func setNeedsImageUpdate() { + sdkProfilePictureView.setNeedsImageUpdate() + } +} + +extension UserProfilePictureView { + private func setupSDKProfilePictureView() { + sdkProfilePictureView.frame = bounds + sdkProfilePictureView.autoresizingMask = [.FlexibleWidth, .FlexibleHeight] + addSubview(sdkProfilePictureView) + setNeedsImageUpdate() // Trigger the update to refresh the image, just in case. + } +} diff --git a/Sources/Login/LoginBehavior.swift b/Sources/Login/LoginBehavior.swift new file mode 100644 index 00000000..0ffbcd7a --- /dev/null +++ b/Sources/Login/LoginBehavior.swift @@ -0,0 +1,70 @@ +// 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 Foundation + +import FBSDKLoginKit + +/** + Indicates how Facebook Login should be attempted. + + Facebook Login authorizes the application to act on behalf of the user, using the user's Facebook account. + Usually a Facebook Login will rely on an account maintained outside of the application, + by the native Facebook application, the browser, or perhaps the device itself. + This avoids the need for a user to enter their username and password directly, + and provides the most secure and lowest friction way for a user to authorize the application to interact with Facebook. + + This enum specifies which log-in methods may be used. + The SDK will determine the best behavior based on the current device (such as OS version). + */ +public enum LoginBehavior { + /** + This is the default behavior, and indicates logging in through the native Facebook app may be used. + The SDK may still use Safari.app or `SFSafariViewController` instead. + */ + case Native + /** + Attempts log in through the Safari.app or `SFSafariViewController`, if available. + */ + case Browser + /** + Attempts log in through the Facebook account currently signed in through the the device Settings. + + - note: If the account is not available to the app (either not configured by user or + as determined by the SDK) this behavior falls back to `.Native`. + */ + case SystemAccount + /** + Attempts log in through a modal `WebView` pop up. + + - note: This behavior is only available to certain types of apps. + Please check the Facebook Platform Policy to verify your app meets the restrictions. + */ + case Web +} + +extension LoginBehavior { + var sdkBehavior: FBSDKLoginBehavior { + switch self { + case .Native: return .Native + case .Browser: return .Browser + case .SystemAccount: return .SystemAccount + case .Web: return .Web + } + } +} diff --git a/Sources/Login/LoginButton.Tooltip.swift b/Sources/Login/LoginButton.Tooltip.swift new file mode 100644 index 00000000..09bf9414 --- /dev/null +++ b/Sources/Login/LoginButton.Tooltip.swift @@ -0,0 +1,61 @@ +// 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 Foundation +import FBSDKLoginKit + +extension LoginButton { + /** + Indicates the desired login tooltip behavior. + */ + public enum TooltipBehavior { + /// Tooltip will only be displayed if the app is eligible (determined by possible server round trip). + case Automatic + /// Force display of the tooltip (typically for UI testing). + case ForceDisplay + /// Force disable the tooltip. + case Disable + + internal var sdkBehavior: FBSDKLoginButtonTooltipBehavior { + switch self { + case .Automatic: return .Automatic + case .ForceDisplay: return .ForceDisplay + case .Disable: return .Disable + } + } + } +} + +extension LoginButton { + /** + Indicates the desired login tooltip color style. + */ + public enum TooltipColorStyle { + /// Light blue background, white text, faded blue close button. + case FriendlyBlue + /// Dark gray background, white text, light gray close button. + case NeutralGray + + internal var sdkColorStyle: FBSDKTooltipColorStyle { + switch self { + case .FriendlyBlue: return .FriendlyBlue + case .NeutralGray: return .NeutralGray + } + } + } +} diff --git a/Sources/Login/LoginButton.swift b/Sources/Login/LoginButton.swift new file mode 100644 index 00000000..f2e8b82d --- /dev/null +++ b/Sources/Login/LoginButton.swift @@ -0,0 +1,147 @@ +// 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 UIKit + +@testable import FacebookCore +import FBSDKLoginKit + +/** + A button that initiates a log in or log out flow upon tapping. + + `LoginButton` works with `AccessToken.current` to determine what to display, + and automatically starts authentication when tapped (i.e., you do not need to manually subscribe action targets). + + Like `LoginManager`, you should make sure your app delegate is connected to `ApplicationDelegate` + in order for the button's delegate to receive messages. + + `LoginButton` has a fixed height of @c 30 pixels, but you may change the width. + Initializing the button with `nil` frame will size the button to its minimum frame. + */ +public class LoginButton: UIView { + + private var sdkLoginButton: FBSDKLoginButton + + /// Delegate of the login button that can handle the result, logout events. + public var delegate: LoginButtonDelegate? + private var delegateBridge: LoginButtonDelegateBridge + + /// The login behavior that is going to be used. Default: `.Native`. + public var loginBehavior = LoginBehavior.Native { + didSet { + sdkLoginButton.loginBehavior = loginBehavior.sdkBehavior + } + } + + /// The default audience. Default: `.Friends`. + public var defaultAudience = LoginDefaultAudience.Friends { + didSet { + sdkLoginButton.defaultAudience = defaultAudience.sdkAudience + } + } + + /// The desired tooltip behavior. Default: `.Automatic`. + public var tooltipBehavior = TooltipBehavior.Automatic { + didSet { + sdkLoginButton.tooltipBehavior = tooltipBehavior.sdkBehavior + } + } + + /// The desired tooltip color style. Default: `.FriendlyBlue`. + public var tooltipColorStyle = TooltipColorStyle.FriendlyBlue { + didSet { + sdkLoginButton.tooltipColorStyle = tooltipColorStyle.sdkColorStyle + } + } + + /** + Create a new `LoginButton` with a given optional frame and read permissions. + + - parameter frame: Optional frame to initialize with. Default: `nil`, which uses a default size for the button. + - parameter readPermissions: Array of read permissions to request when logging in. + */ + public init(frame: CGRect? = nil, readPermissions: [ReadPermission]) { + let sdkLoginButton = FBSDKLoginButton() + sdkLoginButton.readPermissions = readPermissions.map({ $0.permissionValue.name }) + + self.sdkLoginButton = sdkLoginButton + delegateBridge = LoginButtonDelegateBridge() + + let frame = frame ?? CGRect(origin: .zero, size: sdkLoginButton.bounds.size) + super.init(frame: frame) + + delegateBridge.setupAsDelegateFor(sdkLoginButton, loginButton: self) + addSubview(sdkLoginButton) + } + + /** + Create a new `LoginButton` with a given optional frame and publish permissions. + + - parameter frame: Optional frame to initialize with. Default: `nil`, which uses a default size for the button. + - parameter publishPermissions: Array of publish permissions to request when logging in. + */ + public init(frame: CGRect? = nil, publishPermissions: [PublishPermission]) { + let sdkLoginButton = FBSDKLoginButton() + sdkLoginButton.publishPermissions = publishPermissions.map({ $0.permissionValue.name }) + + self.sdkLoginButton = sdkLoginButton + delegateBridge = LoginButtonDelegateBridge() + + let frame = frame ?? sdkLoginButton.bounds + super.init(frame: frame) + + delegateBridge.setupAsDelegateFor(sdkLoginButton, loginButton: self) + addSubview(sdkLoginButton) + } + + /** + Create a new `LoginButton` from an encoded interface file. + + - parameter decoder: The coder to initialize from. + */ + public required init?(coder decoder: NSCoder) { + sdkLoginButton = FBSDKLoginButton(coder: decoder) ?? FBSDKLoginButton() + delegateBridge = LoginButtonDelegateBridge() + + super.init(coder: decoder) + + delegateBridge.setupAsDelegateFor(sdkLoginButton, loginButton: self) + addSubview(sdkLoginButton) + } +} + +extension LoginButton { + + public override func layoutSubviews() { + super.layoutSubviews() + + sdkLoginButton.frame = CGRect(origin: .zero, size: bounds.size) + } + + public override func sizeToFit() { + bounds.size = sizeThatFits(CGSize(width: CGFloat.max, height: CGFloat.max)) + } + + public override func sizeThatFits(size: CGSize) -> CGSize { + return sdkLoginButton.sizeThatFits(size) + } + + public override func intrinsicContentSize() -> CGSize { + return sdkLoginButton.intrinsicContentSize() + } +} diff --git a/Sources/Login/LoginButtonDelegate.swift b/Sources/Login/LoginButtonDelegate.swift new file mode 100644 index 00000000..f24aafa6 --- /dev/null +++ b/Sources/Login/LoginButtonDelegate.swift @@ -0,0 +1,39 @@ +// 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 Foundation + +/** + A delegate for `LoginButton`. + */ +public protocol LoginButtonDelegate { + /** + Called when the button was used to login and the process finished. + + - parameter loginButton: Button that was used to login. + - parameter result: The result of the login. + */ + func loginButtonDidCompleteLogin(loginButton: LoginButton, result: LoginResult) + + /** + Called when the button was used to logout. + + - parameter loginButton: Button that was used to logout. + */ + func loginButtonDidLogOut(loginButton: LoginButton) +} diff --git a/Sources/Login/LoginButtonDelegateBridge.swift b/Sources/Login/LoginButtonDelegateBridge.swift new file mode 100644 index 00000000..88ab6887 --- /dev/null +++ b/Sources/Login/LoginButtonDelegateBridge.swift @@ -0,0 +1,53 @@ +// 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 Foundation + +import FBSDKLoginKit + +internal class LoginButtonDelegateBridge: NSObject { + internal weak var loginButton: LoginButton? + + func setupAsDelegateFor(sdkLoginButton: FBSDKLoginButton, loginButton: LoginButton) { + self.loginButton = loginButton + sdkLoginButton.delegate = self + } +} + +extension LoginButtonDelegateBridge: FBSDKLoginButtonDelegate { + func loginButton(sdkButton: FBSDKLoginButton!, didCompleteWithResult sdkResult: FBSDKLoginManagerLoginResult?, error: NSError?) { + guard + let loginButton = loginButton, + let delegate = loginButton.delegate else { + return + } + + let result = LoginResult(sdkResult: sdkResult, error: error) + delegate.loginButtonDidCompleteLogin(loginButton, result: result) + } + + func loginButtonDidLogOut(sdkButton: FBSDKLoginButton!) { + guard + let loginButton = loginButton, + let delegate = loginButton.delegate else { + return + } + + delegate.loginButtonDidLogOut(loginButton) + } +} diff --git a/Sources/Login/LoginDefaultAudience.swift b/Sources/Login/LoginDefaultAudience.swift new file mode 100644 index 00000000..f5192c1f --- /dev/null +++ b/Sources/Login/LoginDefaultAudience.swift @@ -0,0 +1,48 @@ +// 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 Foundation + +import FBSDKLoginKit + +/** + Indicates 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 publication ceiling for the application. + This enumerated value allows the application to select which audience to ask the user to grant publish permission for. + */ +public enum LoginDefaultAudience { + /// Indicates that the user's friends are able to see posts made by the application. + case Friends + /// Indicates that only the user is able to see posts made by the application. + case OnlyMe + /// Indicates that all Facebook users are able to see posts made by the application. + case Everyone +} + +extension LoginDefaultAudience { + internal var sdkAudience: FBSDKDefaultAudience { + switch self { + case .Friends: return .Friends + case .OnlyMe: return .OnlyMe + case .Everyone: return .Everyone + } + } +} diff --git a/Sources/Login/LoginManager.DefaultAudience.swift b/Sources/Login/LoginManager.DefaultAudience.swift new file mode 100644 index 00000000..78d26b95 --- /dev/null +++ b/Sources/Login/LoginManager.DefaultAudience.swift @@ -0,0 +1,36 @@ +// 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 Foundation + +/** + Indicates 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 publication ceiling for the application. + This enumerated value allows the application to select which audience to ask the user to grant publish permission for. + */ +public enum LoginDefaultAudience { + /// Indicates that the user's friends are able to see posts made by the application. + case Friends + /// Indicates that only the user is able to see posts made by the application. + case OnlyMe + /// Indicates that all Facebook users are able to see posts made by the application. + case Everyone +} diff --git a/Sources/Login/LoginManager.swift b/Sources/Login/LoginManager.swift new file mode 100644 index 00000000..1d203ba7 --- /dev/null +++ b/Sources/Login/LoginManager.swift @@ -0,0 +1,130 @@ +// 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 Foundation +import UIKit +import FBSDKLoginKit +@testable import FacebookCore + +/** + This class provides methods for logging the user in and out. + It works directly with `AccessToken.current` and + sets the "current" token upon successful authorizations (or sets `nil` in case of `logOut`). + + You should check `AccessToken.current` before calling `logIn()` to see if there is + a cached token available (typically in your `viewDidLoad`). + + If you are managing your own token instances outside of `AccessToken.current`, you will need to set + `current` before calling `logIn()` to authorize further permissions on your tokens. + */ +public class LoginManager { + private let sdkManager = FBSDKLoginManager() + + /// The login behavior that is going to be used. Default: `.Native`. + public var loginBehavior: LoginBehavior { + didSet { + sdkManager.loginBehavior = loginBehavior.sdkBehavior + } + } + + /// The default audience. Default: `.Friends`. + public var defaultAudience: LoginDefaultAudience { + didSet { + sdkManager.defaultAudience = defaultAudience.sdkAudience + } + } + + /** + Initialize an instance of `LoginManager.` + + - parameter loginBehavior: Optional login behavior to use. Default: `.Native`. + - parameter defaultAudience: Optional default audience to use. Default: `.Friends`. + */ + public init(loginBehavior: LoginBehavior = .Native, + defaultAudience: LoginDefaultAudience = .Friends) { + self.loginBehavior = loginBehavior + self.defaultAudience = defaultAudience + } + + /** + 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 + 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. + + - parameter permissions: Array of read permissions. Default: `[.PublicProfile]` + - parameter viewController: Optional view controller to present from. Default: topmost view controller. + - parameter completion: Optional callback. + */ + public func logIn(permissions: [ReadPermission] = [.PublicProfile], + viewController: UIViewController? = nil, + completion: ((LoginResult) -> Void)? = nil) { + let sdkPermissions = permissions.map({ $0.permissionValue.name }) + sdkManager.logInWithReadPermissions(sdkPermissions, + fromViewController: viewController, + handler: LoginManager.sdkCompletionFor(completion)) + } + + /** + Logs the user in or authorizes additional permissions. + + Use this method when asking for publish 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 + 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. + + - parameter permissions: Array of publish permissions. Default: `[.PublishActions]` + - parameter viewController: Optional view controller to present from. Default: topmost view controller. + - parameter completion: Optional callback. + */ + public func logIn(permissions: [PublishPermission] = [.PublishActions], + viewController: UIViewController? = nil, + completion: ((LoginResult) -> Void)? = nil) { + let sdkPermissions = permissions.map({ $0.permissionValue.name }) + sdkManager.logInWithReadPermissions(sdkPermissions, + fromViewController: viewController, + handler: LoginManager.sdkCompletionFor(completion)) + } + + /** + Logs the user out. + This calls `AccessToken.current = nil` and `Profile.current = nil`. + */ + public func logOut() { + AccessToken.current = nil + UserProfile.current = nil + } +} + +private extension LoginManager { + private class func sdkCompletionFor(completion: ((LoginResult) -> Void)?) -> FBSDKLoginManagerRequestTokenHandler? { + guard let completion = completion else { + return nil + } + return { (sdkResult: FBSDKLoginManagerLoginResult?, error: NSError?) -> Void in + let result = LoginResult(sdkResult: sdkResult, error: error) + completion(result) + } + } +} diff --git a/Sources/Login/LoginResult.swift b/Sources/Login/LoginResult.swift new file mode 100644 index 00000000..7506451d --- /dev/null +++ b/Sources/Login/LoginResult.swift @@ -0,0 +1,58 @@ +// 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 Foundation +@testable import FacebookCore +import FBSDKLoginKit + +/** + Describes the result of a login attempt. + */ +public enum LoginResult { + /// User succesfully logged in. Contains granted, declined permissions and access token. + case Success(grantedPermissions: Set, declinedPermissions: Set, token: AccessToken) + /// Login attempt was cancelled by the user. + case Cancelled + /// Login attempt failed. + case Failed(ErrorType) +} + +extension LoginResult { + internal init(sdkResult: FBSDKLoginManagerLoginResult?, error: NSError?) { + if let error = error { + self = .Failed(error) + return + } + guard let sdkResult = sdkResult else { + //FIXME: (nlutsenko) Use a good error type here. + let error = NSError(domain: "", code: 42, userInfo: nil) + self = .Failed(error) + return + } + if sdkResult.isCancelled { + self = .Cancelled + } else { + let grantedPermissions = (sdkResult.grantedPermissions?.flatMap({ $0 as? String }).map({ Permission(name: $0) })).map(Set.init) + let declinedPermissions = (sdkResult.declinedPermissions?.flatMap({ $0 as? String }).map({ Permission(name: $0) })).map(Set.init) + self = .Success(grantedPermissions: grantedPermissions ?? [], + declinedPermissions: declinedPermissions ?? [], + token: AccessToken(sdkAccessToken: sdkResult.token)) + + } + } +} diff --git a/Sources/Share/Content Sharing/ContentSharingProtocol.swift b/Sources/Share/Content Sharing/ContentSharingProtocol.swift new file mode 100644 index 00000000..88c6e6d1 --- /dev/null +++ b/Sources/Share/Content Sharing/ContentSharingProtocol.swift @@ -0,0 +1,57 @@ +// 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. + +/** + The common interface for components that initiate sharing. + */ +public protocol ContentSharingProtocol { + associatedtype Content: ContentProtocol + + /// The content that is being shared. + var content: Content { get } + + /// The completion handler to call when sharing is complete. + var completion: ((ContentSharerResult) -> Void)? { get set } + + /** + A Boolean value that indicates whether the receiver should fail if it finds an error with the share content. + + If `false`, the sharer will still be displayed without the data that was mis-configured. + For example, an invalid `placeId` specified on the `content` would produce a data error. + */ + var failsOnInvalidData: Bool { get } + + /** + Validates the content on the receiver. + */ + func validate() throws +} + +/** + The results of an attempted operation by a `ContentSharer`. + */ +public enum ContentSharerResult { + /// The operation was successful. + case Success(Content.Result) + + /// The operation failed. + case Failed(ErrorType) + + /// The operation was cancelled by the user. + case Cancelled +} diff --git a/Sources/Share/Content Sharing/GraphSharer.swift b/Sources/Share/Content Sharing/GraphSharer.swift new file mode 100644 index 00000000..730c09be --- /dev/null +++ b/Sources/Share/Content Sharing/GraphSharer.swift @@ -0,0 +1,174 @@ +// 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 FBSDKShareKit +@testable import FacebookCore + +/** + A utility class for sharing through the graph API. Using this class requires an access token that + has been granted the "publish_actions" permission. + + GraphSharer network requests are scheduled on the current run loop in the default run loop mode + (like NSURLConnection). If you want to use GraphSharer in a background thread, you must manage the run loop + yourself. + */ +public final class GraphSharer { + + private let sdkSharer: FBSDKShareAPI + private let sdkShareDelegate: SDKSharingDelegateBridge + + /// The message the person has provided through the custom dialog that will accompany the share content. + public var message: String? { + get { + return sdkSharer.message + } + set { + sdkSharer.message = newValue + } + } + + /// The graph node to which content should be shared. + public var graphNode: String? { + get { + return sdkSharer.graphNode + } + set { + sdkSharer.graphNode = newValue + } + } + + /// The access token used when performing a share. The access token must have the "publish_actions" permission granted. + public var accessToken: AccessToken? { + get { + let accessToken: FBSDKAccessToken? = sdkSharer.accessToken + return accessToken.flatMap(AccessToken.init) + } + set { + sdkSharer.accessToken = newValue.map { $0.sdkAccessTokenRepresentation } + } + } + + /** + Create a new Graph API sharer. + + - parameter content: The content to share. + */ + public init(content: Content) { + sdkSharer = FBSDKShareAPI() + sdkShareDelegate = SDKSharingDelegateBridge() + + sdkShareDelegate.setupAsDelegateFor(sdkSharer) + sdkSharer.shareContent = ContentBridger.bridgeToObjC(content) + } +} + +//-------------------------------------- +// MARK: - Share +//-------------------------------------- + +extension GraphSharer { + /** + Attempt to share `content` with the graph API. + + - throws: If the content fails to share. + */ + public func share() throws { + var error: ErrorType? + let completionHandler = sdkShareDelegate.completion + sdkShareDelegate.completion = { + if case .Failed(let resultError) = $0 { + error = resultError + } + } + + sdkSharer.share() + sdkShareDelegate.completion = completionHandler + + if let error = error { + throw error + } + } +} + + +//-------------------------------------- +// MARK: - ContentSharingProtocol +//-------------------------------------- + +extension GraphSharer: ContentSharingProtocol { + + /// The content that is being shared. + public var content: Content { + get { + guard let swiftContent: Content = ContentBridger.bridgeToSwift(sdkSharer.shareContent) else { + fatalError("Content of our private sharer has changed type. Something horrible has happened.") + } + return swiftContent + } + } + + /// The completion handler to be invoked upon the share performing. + public var completion: ((ContentSharerResult) -> Void)? { + get { + return sdkShareDelegate.completion + } + set { + sdkShareDelegate.completion = newValue + } + } + + /// Whether or not this sharer fails on invalid data. + public var failsOnInvalidData: Bool { + get { + return sdkSharer.shouldFailOnDataError + } + set { + sdkSharer.shouldFailOnDataError = newValue + } + } + + /** + Validates the content on the receiver. + - throws: If The content could not be validated. + */ + public func validate() throws { + try sdkSharer.validate() + } +} + +//-------------------------------------- +// MARK: - Convenience +//-------------------------------------- + +extension GraphSharer { + /** + Share a given `content` to the Graph API, with a completion handler. + + - parameter content: The content to share. + - parameter completion: The completion handler to invoke. + + - returns: Whether or not the operation was successfully started. + - throws: If the share fails. + */ + public static func share(content: Content, completion: (ContentSharerResult -> Void)? = nil) throws -> GraphSharer { + let sharer = self.init(content: content) + sharer.completion = completion + try sharer.share() + return sharer + } +} diff --git a/Sources/Share/Content/Common/ContentProtocol.swift b/Sources/Share/Content/Common/ContentProtocol.swift new file mode 100644 index 00000000..a0a40b69 --- /dev/null +++ b/Sources/Share/Content/Common/ContentProtocol.swift @@ -0,0 +1,50 @@ +// 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 Foundation + +/** + A base interface for content to be shared. + */ +public protocol ContentProtocol { + associatedtype Result: ContentSharingResultProtocol + + /** + URL for the content being shared. + + This URL will be checked for all link meta tags for linking in platform specific ways. + See documentation for [App Links](https://developers.facebook.com/docs/applinks/) + */ + var url: NSURL? { get } + + /// Hashtag for the content being shared. + var hashtag: Hashtag? { get } + + /** + List of IDs for taggable people to tag with this content. + + See documentation for [Taggable Friends](https://developers.facebook.com/docs/graph-api/reference/user/taggable_friends) + */ + var taggedPeopleIds: [String]? { get } + + /// The ID for a place to tag with this content. + var placeId: String? { get } + + /// A value to be added to the referrer URL when a person follows a link from this shared content on feed. + var referer: String? { get } +} diff --git a/Sources/Share/Content/Common/ContentSharingResultProtocol.swift b/Sources/Share/Content/Common/ContentSharingResultProtocol.swift new file mode 100644 index 00000000..edf11759 --- /dev/null +++ b/Sources/Share/Content/Common/ContentSharingResultProtocol.swift @@ -0,0 +1,29 @@ +// 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. + +/** + Represents the results of a content sharing operation. + */ +public protocol ContentSharingResultProtocol { + /** + Create the sharing results from a dictionary provided from the content sharer. + + - parameter dictionary: The dictionary representation of the sharing results + */ + init(dictionary: [String : String]) +} diff --git a/Sources/Share/Content/Common/Hashtag.swift b/Sources/Share/Content/Common/Hashtag.swift new file mode 100644 index 00000000..e7253456 --- /dev/null +++ b/Sources/Share/Content/Common/Hashtag.swift @@ -0,0 +1,72 @@ +// 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 FBSDKShareKit + +/** + Represents a valid hashtag. + */ +public struct Hashtag { + /// The hashtag string. + public let stringValue: String + + /** + Attempt to create a hashtag for a given string. + + - parameter string: The string to create from. + If this is not a valid hashtag (matching the regular expression `#\w+`), the initializer returns `nil`. + */ + public init?(_ string: String) { + let sdkHashtag = FBSDKHashtag(string: string) + self.init(sdkHashtag: sdkHashtag) + } +} + +extension Hashtag { + internal init?(sdkHashtag: FBSDKHashtag) { + if !sdkHashtag.valid { + return nil + } + self.stringValue = sdkHashtag.stringRepresentation + } + + internal var sdkHashtagRepresentation: FBSDKHashtag { + get { + return FBSDKHashtag(string: stringValue) + } + } +} + +extension Hashtag: Hashable { + /// Calculates the hash value of this Hashtag (yo dawg). + public var hashValue: Int { + return stringValue.hashValue + } +} + +/** + Check if two hashtags are equal. + + - parameter lhs: The first hashtag to compare. + - parameter rhs: The second hashtag to compare. + + - returns: `true` if the hashtags are equal, `false` otherwise. + */ +public func == (lhs: Hashtag, rhs: Hashtag) -> Bool { + return lhs.stringValue == rhs.stringValue +} diff --git a/Sources/Share/Content/Common/PostSharingResult.swift b/Sources/Share/Content/Common/PostSharingResult.swift new file mode 100644 index 00000000..2905cd61 --- /dev/null +++ b/Sources/Share/Content/Common/PostSharingResult.swift @@ -0,0 +1,53 @@ +// 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 Foundation + +/** + Represents the results of a successful Facebook post share. + */ +public struct PostSharingResult: ContentSharingResultProtocol { + private let dictionary: [String : String] + + /** + Initialize this result from the contents of a dictionary + + - parameter dictionary: The dictionary to initialize from + */ + public init(dictionary: [String : String]) { + self.dictionary = dictionary + } + + /// The post ID of the shared content + public var postId: String? { + get { + return dictionary[ShareResultKeys.PostId.rawValue] + } + } + + /** + Get the value of a given key from this result. + + - parameter key: A key to search for. + + - returns: The value associated with that key. + */ + public subscript(key: String) -> String? { + return dictionary[key] + } +} diff --git a/Sources/Share/Content/Link/LinkShareContent.swift b/Sources/Share/Content/Link/LinkShareContent.swift new file mode 100644 index 00000000..39bd62df --- /dev/null +++ b/Sources/Share/Content/Link/LinkShareContent.swift @@ -0,0 +1,131 @@ +// 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 Foundation +import FBSDKShareKit + +/** + A model for status and link content to be shared + */ +public struct LinkShareContent: ContentProtocol { + public typealias Result = PostSharingResult + + /** + The title to display for this link. + + This value may be discarded for specially handled links (ex: iTunes URLs). + */ + public var title: String? + + /** + The description of the link. + + If not specified, this field is automatically populated by information scraped from the contentURL, + typically the title of the page. This value may be discarded for specially handled links (ex: iTunes URLs). + */ + public var description: String? + + /** + Some quote text of the link. + + If specified, the quote text will render with custom styling on top of the link. + */ + public var quote: String? + + /// The URL of a picture to attach to this content. + public var imageURL: NSURL? + + /** + Create link share content. + + - parameter url: The URL being shared. + - parameter title: Optional title to display for this link. + - parameter description: Optional description of the link. + - parameter quote: Optional quote text of the link. + - parameter imageURL: OPtional image URL of a picture to attach. + */ + public init(url: NSURL, + title: String? = nil, + description: String? = nil, + quote: String? = nil, + imageURL: NSURL? = nil) { + self.url = url + self.title = title + self.description = description + self.quote = quote + self.imageURL = imageURL + } + + //-------------------------------------- + // MARK - Content + //-------------------------------------- + + /** + URL for the content being shared. + + This URL will be checked for all link meta tags for linking in platform specific ways. + See documentation for App Links (https://developers.facebook.com/docs/applinks/). + */ + public var url: NSURL? + + /// Hashtag for the content being shared. + public var hashtag: Hashtag? + + /** + List of IDs for taggable people to tag with this content. + + See documentation for Taggable Friends (https://developers.facebook.com/docs/graph-api/reference/user/taggable_friends) + */ + public var taggedPeopleIds: [String]? + + /// The ID for a place to tag with this content. + public var placeId: String? + + /// A value to be added to the referrer URL when a person follows a link from this shared content on feed. + public var referer: String? +} + +extension LinkShareContent: Equatable { } + +/** + Compares two `LinkContent`s for equality. + + - parameter lhs: One content to compare. + - parameter rhs: The other content to compare to. + + - returns: Whether or not the content are equivalent. + */ +public func == (lhs: LinkShareContent, rhs: LinkShareContent) -> Bool { + return lhs.sdkSharingContentRepresentation.isEqual(rhs.sdkSharingContentRepresentation) +} + +extension LinkShareContent: SDKBridgedContent { + internal var sdkSharingContentRepresentation: FBSDKSharingContent { + let content = FBSDKShareLinkContent() + content.contentDescription = self.description + content.contentTitle = self.title + content.imageURL = self.imageURL + content.quote = self.quote + content.contentURL = self.url + content.hashtag = self.hashtag?.sdkHashtagRepresentation + content.peopleIDs = self.taggedPeopleIds + content.placeID = self.placeId + content.ref = self.referer + return content + } +} diff --git a/Sources/Share/Content/OpenGraph/OpenGraphAction.swift b/Sources/Share/Content/OpenGraph/OpenGraphAction.swift new file mode 100644 index 00000000..57b5d2a2 --- /dev/null +++ b/Sources/Share/Content/OpenGraph/OpenGraphAction.swift @@ -0,0 +1,95 @@ +// 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 Foundation +import FBSDKShareKit +@testable import FacebookCore + +/** + An Open Graph action for sharing. + */ +public struct OpenGraphAction: Equatable { + // TODO (richardross): Make ActionType an enum with common action types. + /// The action type. + public var type: String + + private var properties: [OpenGraphPropertyName : OpenGraphPropertyValue] + + /** + Create an `OpenGraphAction` with a specific action type. + + - parameter type: The type of the action. + */ + public init(type: String) { + self.type = type + self.properties = [:] + } +} + +extension OpenGraphAction: OpenGraphPropertyContaining { + /// Get the property names contained in this container. + public var propertyNames: Set { + return Set(properties.keys) + } + + public subscript(key: OpenGraphPropertyName) -> OpenGraphPropertyValue? { + get { + return properties[key] + } + set { + properties[key] = newValue + } + } +} + +extension OpenGraphAction { + internal var sdkActionRepresentation: FBSDKShareOpenGraphAction { + let sdkAction = FBSDKShareOpenGraphAction() + sdkAction.actionType = type + sdkAction.parseProperties(properties.keyValueMap { key, value in + (key.rawValue, value.openGraphPropertyValue) + }) + + return sdkAction + } + + internal init(sdkAction: FBSDKShareOpenGraphAction) { + self.type = sdkAction.actionType + self.properties = [:] + + sdkAction.enumerateKeysAndObjectsUsingBlock { (key: String?, value: AnyObject?, stop) in + guard let key = key.map(OpenGraphPropertyName.init(rawValue:)), + let value = value.map(OpenGraphPropertyValueConverter.valueFrom) else { + return + } + self.properties[key] = value + } + } +} + +/** + Compare two `OpenGraphAction`s for equality. + + - parameter lhs: The first action to compare. + - parameter rhs: The second action to compare. + + - returns: Whether or not the actions are equal. + */ +public func == (lhs: OpenGraphAction, rhs: OpenGraphAction) -> Bool { + return lhs.sdkActionRepresentation == rhs.sdkActionRepresentation +} diff --git a/Sources/Share/Content/OpenGraph/OpenGraphObject.swift b/Sources/Share/Content/OpenGraph/OpenGraphObject.swift new file mode 100644 index 00000000..208ee199 --- /dev/null +++ b/Sources/Share/Content/OpenGraph/OpenGraphObject.swift @@ -0,0 +1,122 @@ +// 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 FBSDKShareKit +@testable import FacebookCore + +/** + An Open Graph Object for sharing. + + The property keys MUST have namespaces specified on them, such as `og:image`, and `og:type` is required. + + See https://developers.facebook.com/docs/sharing/opengraph/object-properties for other properties. + + You can specify nested namespaces inline to define complex properties. For example, the following code will generate a + fitness.course object with a location: + + ``` + let course: OpenGraphObject = [ + "og:type": "fitness.course", + "og:title": "Sample course", + "fitness:metrics:location:latitude": "41.40338", + "fitness:metrics:location:longitude": "2.17403", + ] + ``` + */ +public struct OpenGraphObject: Equatable { + private var properties: [OpenGraphPropertyName : OpenGraphPropertyValue] + + /** + Create a new `OpenGraphObject`. + */ + public init() { + properties = [:] + } +} + +extension OpenGraphObject: OpenGraphPropertyContaining { + /// Get the property names contained in this container. + public var propertyNames: Set { + return Set(properties.keys) + } + + public subscript(key: OpenGraphPropertyName) -> OpenGraphPropertyValue? { + get { + return properties[key] + } set { + properties[key] = newValue + } + } +} + +extension OpenGraphObject: DictionaryLiteralConvertible { + /** + Convenience method to build a new object from a dictinary literal. + + - parameter elements: The elements of the dictionary literal to initialize from. + + - example: + ``` + let object: OpenGraphObject = [ + "og:type": "foo", + "og:title": "bar", + .... + ] + ``` + */ + public init(dictionaryLiteral elements: (OpenGraphPropertyName, OpenGraphPropertyValue)...) { + properties = [:] + for (key, value) in elements { + properties[key] = value + } + } +} + +extension OpenGraphObject { + internal var sdkGraphObjectRepresentation: FBSDKShareOpenGraphObject { + let sdkObject = FBSDKShareOpenGraphObject() + sdkObject.parseProperties(properties.keyValueMap { key, value in + (key.rawValue, value.openGraphPropertyValue) + }) + return sdkObject + } + + internal init(sdkGraphObject: FBSDKShareOpenGraphObject) { + self.properties = [:] + + sdkGraphObject.enumerateKeysAndObjectsUsingBlock { (key: String?, value: AnyObject?, stop) in + guard let key = key.map(OpenGraphPropertyName.init(rawValue:)), + let value = value.map(OpenGraphPropertyValueConverter.valueFrom) else { + return + } + self.properties[key] = value + } + } +} + +/** + Compare two `OpenGraphObject`s for equality. + + - parameter lhs: The first `OpenGraphObject` to compare. + - parameter rhs: The second `OpenGraphObject` to compare. + + - returns: Whether or not the objects are equal. + */ +public func == (lhs: OpenGraphObject, rhs: OpenGraphObject) -> Bool { + return false +} diff --git a/Sources/Share/Content/OpenGraph/OpenGraphPropertyContaining.swift b/Sources/Share/Content/OpenGraph/OpenGraphPropertyContaining.swift new file mode 100644 index 00000000..0dc7f3ca --- /dev/null +++ b/Sources/Share/Content/OpenGraph/OpenGraphPropertyContaining.swift @@ -0,0 +1,73 @@ +// 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 Foundation + +/** + Protocol defining operations on open graph actions and objects. + */ +public protocol OpenGraphPropertyContaining { + /// Get the property names contained in this container. + var propertyNames: Set { get } + + /** + Get the value corresponding to a given property name. + + - parameter name: The property name to retrieve. + + - returns: The value for the given property. + */ + subscript(name: OpenGraphPropertyName) -> OpenGraphPropertyValue? { get set } +} + +extension OpenGraphPropertyContaining { + public subscript(key: OpenGraphPropertyName) -> String? { + get { + let graphValue: OpenGraphPropertyValue? = self[key] + return graphValue as? String + } + } + + public subscript(key: OpenGraphPropertyName) -> NSNumber? { + get { + let graphValue: OpenGraphPropertyValue? = self[key] + return graphValue as? NSNumber + } + } + + public subscript(key: OpenGraphPropertyName) -> [OpenGraphPropertyValue]? { + get { + let graphValue: OpenGraphPropertyValue? = self[key] + return graphValue as? [OpenGraphPropertyValue] + } + } + + public subscript(key: OpenGraphPropertyName) -> Photo? { + get { + let graphValue: OpenGraphPropertyValue? = self[key] + return graphValue as? Photo + } + } + + public subscript(key: OpenGraphPropertyName) -> NSURL? { + get { + let graphValue: OpenGraphPropertyValue? = self[key] + return graphValue as? NSURL + } + } +} diff --git a/Sources/Share/Content/OpenGraph/OpenGraphPropertyName.swift b/Sources/Share/Content/OpenGraph/OpenGraphPropertyName.swift new file mode 100644 index 00000000..2dae1610 --- /dev/null +++ b/Sources/Share/Content/OpenGraph/OpenGraphPropertyName.swift @@ -0,0 +1,134 @@ +// 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 Foundation + +/** + Represents a type-safe OpenGraph property name. + */ +public struct OpenGraphPropertyName { + /// The namespace of this open graph property + public var namespace: String + + /// The name of this open graph property + public var name: String + + /** + Attempt to parse an `OpenGraphPropertyName` from a raw OpenGraph formatted property name. + + - parameter string: The string to create from. + */ + public init?(_ string: String) { + guard let components: [String.CharacterView] = string.characters.split(":") where components.count >= 2 else { + return nil + } + + self.namespace = String(components[0]) + + let subcharacters = components[1 ... components.count] + self.name = subcharacters.reduce("", combine: { $0 + ":" + String($1) }) + } + + /** + Create an `OpenGraphPropertyName` with a specific namespace and name. + + - parameter namespace: The namespace to use. + - parameter name: The name to use. + */ + public init(namespace: String, name: String) { + self.namespace = namespace + self.name = name + } +} + +extension OpenGraphPropertyName: RawRepresentable { + public typealias RawValue = String + + /// The raw OpenGraph formatted property name. + public var rawValue: String { + return namespace.isEmpty + ? name + : namespace + ":" + name + } + + /** + Attempt to parse an `OpenGraphPropertyName` from a raw OpenGraph formatted proeprty name. + + - parameter rawValue: The string to create from. + */ + public init(rawValue: String) { + self.init(stringLiteral: rawValue) + } +} + +extension OpenGraphPropertyName: StringLiteralConvertible { + /** + Create an `OpenGraphPropertyName` from a string literal. + + - parameter value: The string literal to create from. + */ + public init(stringLiteral value: String) { + guard let propertyName = OpenGraphPropertyName(value) else { + print("Warning: Attempting to create OpenGraphPropertyName for string \"\(value)\", which has no namespace!") + + self.namespace = "" + self.name = value + return + } + + self.namespace = propertyName.namespace + self.name = propertyName.name + } + + /** + Create an `OpenGraphPropertyName` from a unicode scalar literal. + + - parameter value: The scalar literal to create from. + */ + public init(unicodeScalarLiteral value: UnicodeScalarType) { + self.init(stringLiteral: value) + } + + /** + Create an `OpenGraphPropertyName` from an grapheme cluster literal. + + - parameter value: The grapheme cluster to create from. + */ + public init(extendedGraphemeClusterLiteral value: ExtendedGraphemeClusterType) { + self.init(stringLiteral: value) + } +} + +extension OpenGraphPropertyName: Hashable { + /// Calculates the hash value of this `OpenGraphPropertyName`. + public var hashValue: Int { + return rawValue.hashValue + } +} + +/** + Compares two `OpenGraphPropertyName`s for equality. + + - parameter lhs: The first property name to compare. + - parameter rhs: The second property name to compare. + + - returns: Whether or not these names are equal. + */ +public func == (lhs: OpenGraphPropertyName, rhs: OpenGraphPropertyName) -> Bool { + return lhs.rawValue == rhs.rawValue +} diff --git a/Sources/Share/Content/OpenGraph/OpenGraphPropertyValue.swift b/Sources/Share/Content/OpenGraph/OpenGraphPropertyValue.swift new file mode 100644 index 00000000..5e4d4787 --- /dev/null +++ b/Sources/Share/Content/OpenGraph/OpenGraphPropertyValue.swift @@ -0,0 +1,88 @@ +// 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 Foundation +import FBSDKShareKit + +/** + A generic protocol for holding any value that can be represented as a property value in OpenGraph. + */ +public protocol OpenGraphPropertyValue { + /// The bridged OpenGraph raw value. + var openGraphPropertyValue: AnyObject { get } +} + +internal struct OpenGraphPropertyValueConverter { + internal static func valueFrom(openGraphObjectValue value: AnyObject) -> OpenGraphPropertyValue? { + switch value { + case let value as String: return value + case let value as NSNumber: return value + case let value as NSArray: return value.flatMap { valueFrom(openGraphObjectValue: $0) } + case let value as NSURL: return value + case let value as FBSDKSharePhoto: return Photo(sdkPhoto: value) + case let value as FBSDKShareOpenGraphObject: return OpenGraphObject(sdkGraphObject: value) + default: + print("Recieved unknown OpenGraph value \(value)") + return nil + } + } +} + +extension NSNumber: OpenGraphPropertyValue { + /// The bridged OpenGraph raw value. + public var openGraphPropertyValue: AnyObject { + return self + } +} + +extension String: OpenGraphPropertyValue { + /// The bridged OpenGraph raw value. + public var openGraphPropertyValue: AnyObject { + return self + } +} + +extension Array: OpenGraphPropertyValue { + /// The bridged OpenGraph raw value. + public var openGraphPropertyValue: AnyObject { + return self + .flatMap { $0 as? OpenGraphPropertyValue } + .map { $0.openGraphPropertyValue } + } +} + +extension NSURL: OpenGraphPropertyValue { + /// The bridged OpenGraph raw value. + public var openGraphPropertyValue: AnyObject { + return self + } +} + +extension Photo: OpenGraphPropertyValue { + /// The bridged OpenGraph raw value. + public var openGraphPropertyValue: AnyObject { + return sdkPhotoRepresentation + } +} + +extension OpenGraphObject: OpenGraphPropertyValue { + /// The bridged OpenGraph raw value. + public var openGraphPropertyValue: AnyObject { + return sdkGraphObjectRepresentation + } +} diff --git a/Sources/Share/Content/OpenGraph/OpenGraphShareContent.swift b/Sources/Share/Content/OpenGraph/OpenGraphShareContent.swift new file mode 100644 index 00000000..fb9e9c79 --- /dev/null +++ b/Sources/Share/Content/OpenGraph/OpenGraphShareContent.swift @@ -0,0 +1,89 @@ +// 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 Foundation +import FBSDKShareKit + +/** + A model for Open Graph content to be shared. + */ +public struct OpenGraphShareContent { + public typealias Result = PostSharingResult + + /// The Open Graph action to be shared. + public var action: OpenGraphAction? + + /// Property name that points to the primary Open Graph Object in the action. This is used for rendering the preview of the share. + public var previewPropertyName: OpenGraphPropertyName? + + //-------------------------------------- + // MARK: - ContentProtocol + //-------------------------------------- + + /** + URL for the content being shared. + + This URL will be checked for all link meta tags for linking in platform specific ways. + See documentation for [App Links](https://developers.facebook.com/docs/applinks/) + */ + public var url: NSURL? + + /// Hashtag for the content being shared. + public var hashtag: Hashtag? + + /** + List of IDs for taggable people to tag with this content. + + See documentation for [Taggable Friends](https://developers.facebook.com/docs/graph-api/reference/user/taggable_friends) + */ + public var taggedPeopleIds: [String]? + + /// The ID for a place to tag with this content. + public var placeId: String? + + /// A value to be added to the referrer URL when a person follows a link from this shared content on feed. + public var referer: String? +} + +extension OpenGraphShareContent: Equatable { } + +extension OpenGraphShareContent: SDKBridgedContent { + internal var sdkSharingContentRepresentation: FBSDKSharingContent { + let sdkContent = FBSDKShareOpenGraphContent() + sdkContent.action = action?.sdkActionRepresentation + sdkContent.previewPropertyName = previewPropertyName?.rawValue + sdkContent.contentURL = url + sdkContent.hashtag = hashtag?.sdkHashtagRepresentation + sdkContent.peopleIDs = taggedPeopleIds + sdkContent.placeID = placeId + sdkContent.ref = referer + return sdkContent + } +} + +/** + Compares two `OpenGraphContent`s for equality. + + - parameter lhs: The first content to compare. + - parameter rhs: The second content to comare. + + - returns: Whether or not the content are equal. + */ +public func == (lhs: OpenGraphShareContent, rhs: OpenGraphShareContent) -> Bool { + return lhs.sdkSharingContentRepresentation.isEqual(rhs.sdkSharingContentRepresentation) +} diff --git a/Sources/Share/Content/Photo/Photo.swift b/Sources/Share/Content/Photo/Photo.swift new file mode 100644 index 00000000..b4bb7e31 --- /dev/null +++ b/Sources/Share/Content/Photo/Photo.swift @@ -0,0 +1,94 @@ +// 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 Foundation +import UIKit +import FBSDKShareKit + +/** + A photo for sharing. + */ +public struct Photo: Equatable { + /// If the photo is resident in memory, this method supplies the data. + public var image: UIImage? + + /// The URL to the photo. + public var url: NSURL? + + /// Specifies whether the photo represented by the receiver was generated by the user or by the application. + public var isUserGenerated: Bool + + /** + The user generated caption for the photo. Note that the 'caption' must come from the user, as pre-filled content + is forbidden by the Platform Policies (2.3). + */ + public var caption: String? + + /** + Conveniece method to Create a `Photo` with an image. + + - parameter image: The image to create with. + - parameter userGenerated: Whether or not this image was user generated. + */ + public init(image: UIImage, userGenerated: Bool) { + self.image = image + self.isUserGenerated = userGenerated + } + + /** + Conveniece method to Create a `Photo` with an image. + + - parameter url: The image URL to create with. + - parameter userGenerated: Whether or not this image was user generated. + */ + public init(url: NSURL, userGenerated: Bool) { + self.url = url + self.isUserGenerated = userGenerated + } +} + +extension Photo { + internal var sdkPhotoRepresentation: FBSDKSharePhoto { + let photo = FBSDKSharePhoto() + photo.image = image + photo.imageURL = url + photo.userGenerated = isUserGenerated + photo.caption = caption + + return photo + } + + internal init(sdkPhoto: FBSDKSharePhoto) { + self.image = sdkPhoto.image + self.url = sdkPhoto.imageURL + self.isUserGenerated = sdkPhoto.userGenerated + self.caption = sdkPhoto.caption + } +} + +/** + Compare to photos for equality. + + - parameter lhs: The first photo to compare. + - parameter rhs: The second photo to compare. + + - returns: Whether or not the photos are equal. + */ +public func == (lhs: Photo, rhs: Photo) -> Bool { + return lhs.sdkPhotoRepresentation == rhs.sdkPhotoRepresentation +} diff --git a/Sources/Share/Content/Photo/PhotoShareContent.swift b/Sources/Share/Content/Photo/PhotoShareContent.swift new file mode 100644 index 00000000..b8336267 --- /dev/null +++ b/Sources/Share/Content/Photo/PhotoShareContent.swift @@ -0,0 +1,94 @@ +// 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 Foundation +import FBSDKShareKit + +/** + A model for photo content to be shared. + */ +public struct PhotoShareContent: ContentProtocol { + public typealias Result = PostSharingResult + + /// Photos to be shared. + public var photos: [Photo] + + /** + Create a `PhotoShareContent` with a list of of photos to share. + + - parameter photos: The list of photos to share. + */ + public init(photos: [Photo] = []) { + self.photos = photos + } + + //-------------------------------------- + // MARK - ContentProtocol + //-------------------------------------- + + /** + URL for the content being shared. + + This URL will be checked for all link meta tags for linking in platform specific ways. + See documentation for [App Links](https://developers.facebook.com/docs/applinks/) + */ + public var url: NSURL? + + /// Hashtag for the content being shared. + public var hashtag: Hashtag? + + /** + List of IDs for taggable people to tag with this content. + + See documentation for [Taggable Friends](https://developers.facebook.com/docs/graph-api/reference/user/taggable_friends) + */ + public var taggedPeopleIds: [String]? + + /// The ID for a place to tag with this content. + public var placeId: String? + + /// A value to be added to the referrer URL when a person follows a link from this shared content on feed. + public var referer: String? +} + +extension PhotoShareContent: Equatable { } + +extension PhotoShareContent: SDKBridgedContent { + var sdkSharingContentRepresentation: FBSDKSharingContent { + let sdkPhotoContent = FBSDKSharePhotoContent() + sdkPhotoContent.photos = photos.map { $0.sdkPhotoRepresentation } + sdkPhotoContent.contentURL = url + sdkPhotoContent.hashtag = hashtag?.sdkHashtagRepresentation + sdkPhotoContent.peopleIDs = taggedPeopleIds + sdkPhotoContent.placeID = placeId + sdkPhotoContent.ref = referer + return sdkPhotoContent + } +} + +/** + Compare two `PhotoShareContent`s for equality. + + - parameter lhs: The first `PhotoShareContent` to compare. + - parameter rhs: The second `PhotoShareContent` to compare. + + - returns: Whether or not the content are equal. + */ +public func == (lhs: PhotoShareContent, rhs: PhotoShareContent) -> Bool { + return lhs.sdkSharingContentRepresentation.isEqual(rhs.sdkSharingContentRepresentation) +} diff --git a/Sources/Share/Content/Video/Video.swift b/Sources/Share/Content/Video/Video.swift new file mode 100644 index 00000000..be1b2ac9 --- /dev/null +++ b/Sources/Share/Content/Video/Video.swift @@ -0,0 +1,66 @@ +// 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 Foundation +import FBSDKShareKit + +/** + A video for sharing. + */ +public struct Video: Equatable { + + /// The file URL to the video. + public var url: NSURL + + /** + Build a new video with a video URL and preivew photo. + + - parameter url: The file URL to the video. + */ + public init(url: NSURL) { + self.url = url + } +} + +extension Video { + internal var sdkVideoRepresentation: FBSDKShareVideo { + let sdkVideo = FBSDKShareVideo() + sdkVideo.videoURL = url + + return sdkVideo + } + + internal init?(sdkVideoRepresentation: FBSDKShareVideo) { + guard let url = sdkVideoRepresentation.videoURL else { + return nil + } + self.url = url + } +} + +/** + Compare two `Video`s for equality. + + - parameter lhs: The first `Video` to compare. + - parameter rhs: The second `Video` to compare. + + - returns: Whether or not the videos are equal. + */ +public func == (lhs: Video, rhs: Video) -> Bool { + return lhs.sdkVideoRepresentation == rhs.sdkVideoRepresentation +} diff --git a/Sources/Share/Content/Video/VideoShareContent.swift b/Sources/Share/Content/Video/VideoShareContent.swift new file mode 100644 index 00000000..91729883 --- /dev/null +++ b/Sources/Share/Content/Video/VideoShareContent.swift @@ -0,0 +1,100 @@ +// 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 Foundation +import FBSDKShareKit + +/** + A model for video content to be shared. + */ +public struct VideoShareContent: ContentProtocol { + public typealias Result = PostSharingResult + + /// Video to be shared. + public var video: Video + + /// The photo that represents the video. + public var previewPhoto: Photo? + + /** + Create a `VideoShareContent` with a list of of photos to share. + + - parameter video: The video to share. + - parameter previewPhoto: The photo that represents the video. + */ + public init(video: Video, previewPhoto: Photo? = nil) { + self.video = video + self.previewPhoto = previewPhoto + } + + //-------------------------------------- + // MARK - ContentProtocol + //-------------------------------------- + + /** + URL for the content being shared. + + This URL will be checked for all link meta tags for linking in platform specific ways. + See documentation for [App Links](https://developers.facebook.com/docs/applinks/) + */ + public var url: NSURL? + + /// Hashtag for the content being shared. + public var hashtag: Hashtag? + + /** + List of IDs for taggable people to tag with this content. + + See documentation for [Taggable Friends](https://developers.facebook.com/docs/graph-api/reference/user/taggable_friends) + */ + public var taggedPeopleIds: [String]? + + /// The ID for a place to tag with this content. + public var placeId: String? + + /// A value to be added to the referrer URL when a person follows a link from this shared content on feed. + public var referer: String? +} + +extension VideoShareContent: Equatable { } + +extension VideoShareContent: SDKBridgedContent { + var sdkSharingContentRepresentation: FBSDKSharingContent { + let sdkVideoContent = FBSDKShareVideoContent() + sdkVideoContent.video = video.sdkVideoRepresentation + sdkVideoContent.previewPhoto = previewPhoto?.sdkPhotoRepresentation + sdkVideoContent.contentURL = url + sdkVideoContent.hashtag = hashtag?.sdkHashtagRepresentation + sdkVideoContent.peopleIDs = taggedPeopleIds + sdkVideoContent.placeID = placeId + sdkVideoContent.ref = referer + return sdkVideoContent + } +} + +/** + Compare two `VideoShareContent`s for equality. + + - parameter lhs: The first `VideoShareContent` to compare. + - parameter rhs: The second `VideoShareContent` to compare. + + - returns: Whether or not the content are equal. + */ +public func == (lhs: VideoShareContent, rhs: VideoShareContent) -> Bool { + return lhs.sdkSharingContentRepresentation.isEqual(rhs.sdkSharingContentRepresentation) +} diff --git a/Sources/Share/Dialogs/AppInvite/AppInvite.DeliveryMethod.swift b/Sources/Share/Dialogs/AppInvite/AppInvite.DeliveryMethod.swift new file mode 100644 index 00000000..173d73df --- /dev/null +++ b/Sources/Share/Dialogs/AppInvite/AppInvite.DeliveryMethod.swift @@ -0,0 +1,49 @@ +// 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 Foundation +import FBSDKShareKit + +extension AppInvite { + /** + Specifies the delivery method for an app invite. + */ + public enum DeliveryMethod { + /// Deliver via Facebook + case Facebook + + /// Deliver via Messenger + case Messenger + } +} + +extension AppInvite.DeliveryMethod { + internal init(sdkDestinationRepresentation: FBSDKAppInviteDestination) { + switch sdkDestinationRepresentation { + case .Facebook: self = .Facebook + case .Messenger: self = .Messenger + } + } + + internal var sdkDestinationRepresentation: FBSDKAppInviteDestination { + switch self { + case .Facebook: return .Facebook + case .Messenger: return .Messenger + } + } +} diff --git a/Sources/Share/Dialogs/AppInvite/AppInvite.Dialog.swift b/Sources/Share/Dialogs/AppInvite/AppInvite.Dialog.swift new file mode 100644 index 00000000..b18d6a3e --- /dev/null +++ b/Sources/Share/Dialogs/AppInvite/AppInvite.Dialog.swift @@ -0,0 +1,125 @@ +// 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 Foundation +import UIKit +import FBSDKShareKit + +extension AppInvite { + /// A dialog to send app invites. + public final class Dialog { + private let sdkDialog: FBSDKAppInviteDialog + private let sdkDelegate: SDKDelegate + + /// The invite to send. + public let invite: AppInvite + + /** + A UIViewController to present the dialog from. + + If not specified, the top most view controller will be automatically determined as best as possible. + */ + public var presentingViewController: UIViewController? { + get { + return sdkDialog.fromViewController + } + set { + sdkDialog.fromViewController = newValue + } + } + + /// The completion handler to be invoked upon showing the dialog. + public var completion: (Result -> Void)? { + get { + return sdkDelegate.completion + } + set { + sdkDelegate.completion = newValue + } + } + + /** + Create a dialog with an invite. + + - parameter invite: The invite to send. + */ + public init(invite: AppInvite) { + sdkDialog = FBSDKAppInviteDialog() + sdkDialog.content = invite.sdkInviteRepresentation + + sdkDelegate = SDKDelegate() + sdkDelegate.setupAsDelegateFor(sdkDialog) + + self.invite = invite + } + + /** + Attempt to show the dialog modally. + + - throws: If the dialog fails to present. + */ + public func show() throws { + var error: ErrorType? + let completionHandler = sdkDelegate.completion + sdkDelegate.completion = { + if case .Failed(let resultError) = $0 { + error = resultError + } + } + + sdkDialog.show() + sdkDelegate.completion = completionHandler + + if let error = error { + throw error + } + } + + /** + Validates the contents of the reciever as valid. + + - throws: If the content is invalid. + */ + public func validate() throws { + try sdkDialog.validate() + } + } +} + +extension AppInvite.Dialog { + /** + Convenience method to show a `Dialog` with a `presentingViewController`, `invite`, and `completion`. + + - parameter viewController: The view controller to present from. + - parameter invite: The invite to send. + - parameter completion: The completion handler to invoke upon success. + + - throws: If the dialog fails to present. + + - returns: The dialog that has been presented. + */ + public static func show(from viewController: UIViewController, + invite: AppInvite, + completion: (AppInvite.Result -> Void)? = nil) throws -> Self { + let dialog = self.init(invite: invite) + dialog.presentingViewController = viewController + dialog.completion = completion + try dialog.show() + return dialog + } +} diff --git a/Sources/Share/Dialogs/AppInvite/AppInvite.PromoCode.swift b/Sources/Share/Dialogs/AppInvite/AppInvite.PromoCode.swift new file mode 100644 index 00000000..c8a29b6d --- /dev/null +++ b/Sources/Share/Dialogs/AppInvite/AppInvite.PromoCode.swift @@ -0,0 +1,111 @@ +// 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 Foundation + +extension AppInvite { + /** + A promo code for an App Invite promotion. This can be between 0 and 10 characters long, and can contain alphanumeric + and spaces only. + + If you attempt to create a Promo Code with an invalid string literal, you will receive a runtime warning, and your + code will be truncated. + */ + public struct PromoCode: Equatable, Hashable { + internal let rawValue: String + + /** + Attempt to create a promo code from a string. + + - parameter string: The string to initialize from. + */ + public init?(string: String) { + let truncated = PromoCode.truncateString(string) + if string != truncated { + return nil + } + + rawValue = string + } + + /// The hash of this promo code. + public var hashValue: Int { + return rawValue.hashValue + } + } +} + +extension AppInvite.PromoCode { + private static func truncateString(string: String) -> String { + let validCharacters = NSCharacterSet.alphanumericCharacterSet() + let cleaned = string.unicodeScalars.filter { + validCharacters.characterIsMember(UInt16($0.value)) + } + + let range = 0 ..< min(10, cleaned.count) + let characters = cleaned[range].map(Character.init) + + return String(characters) + } +} + +extension AppInvite.PromoCode: StringLiteralConvertible { + /** + Create a PromoCode from a string literal. + + - parameter value: The string literal to intiialize from. + */ + public init(stringLiteral value: String) { + let truncated = AppInvite.PromoCode.truncateString(value) + if truncated != value { + print("Warning: Attempted to create a PromoCode from \"\(value)\" which contained invalid characters, or was too long.") + } + + rawValue = truncated + } + + /** + Create a PromoCode from a unicode scalar literal. + + - parameter value: The string literal to intiialize from. + */ + public init(unicodeScalarLiteral value: String) { + self.init(stringLiteral: value) + } + + /** + Create a PromoCode from an extended grapheme cluster literal. + + - parameter value: The string literal to initialize from. + */ + public init(extendedGraphemeClusterLiteral value: String) { + self.init(stringLiteral: value) + } +} + +/** + Compare two `PromoCode`s for equality. + + - parameter lhs: The first promo code to compare. + - parameter rhs: The second promo code to compare. + + - returns: Whether or not the promo codes are equal. + */ +public func == (lhs: AppInvite.PromoCode, rhs: AppInvite.PromoCode) -> Bool { + return lhs.rawValue == rhs.rawValue +} diff --git a/Sources/Share/Dialogs/AppInvite/AppInvite.Result.swift b/Sources/Share/Dialogs/AppInvite/AppInvite.Result.swift new file mode 100644 index 00000000..8d094812 --- /dev/null +++ b/Sources/Share/Dialogs/AppInvite/AppInvite.Result.swift @@ -0,0 +1,33 @@ +// 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 Foundation + +extension AppInvite { + /** + Represents the results of an AppGroupDialog action. + */ + public enum Result { + // TODO: Better concrete type for results here. + /// The App Invite was sent succesfully. + case Success([String:String]) + + /// The App Invite failed. + case Failed(ErrorType) + } +} diff --git a/Sources/Share/Dialogs/AppInvite/AppInvite.SDKDelegate.swift b/Sources/Share/Dialogs/AppInvite/AppInvite.SDKDelegate.swift new file mode 100644 index 00000000..ffbe3cab --- /dev/null +++ b/Sources/Share/Dialogs/AppInvite/AppInvite.SDKDelegate.swift @@ -0,0 +1,43 @@ +// 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. + +@testable import FacebookCore + +import FBSDKShareKit + +extension AppInvite { + internal class SDKDelegate: NSObject, FBSDKAppInviteDialogDelegate { + internal var completion: (Result -> Void)? + + func setupAsDelegateFor(dialog: FBSDKAppInviteDialog) { + // We need for the connection to retain us, + // so we can stick around and keep calling into handlers, + // as long as the connection is alive/sending messages. + objc_setAssociatedObject(dialog, unsafeAddressOf(self), self, .OBJC_ASSOCIATION_RETAIN) + dialog.delegate = self + } + + func appInviteDialog(appInviteDialog: FBSDKAppInviteDialog?, didCompleteWithResults results: [NSObject : AnyObject]?) { + completion?(.Success(results?.keyValueFlatMap { ($0 as? String, $1 as? String) } ?? [:])) + } + + func appInviteDialog(appInviteDialog: FBSDKAppInviteDialog?, didFailWithError error: NSError) { + completion?(.Failed(error)) + } + } +} diff --git a/Sources/Share/Dialogs/AppInvite/AppInvite.swift b/Sources/Share/Dialogs/AppInvite/AppInvite.swift new file mode 100644 index 00000000..4700f1d8 --- /dev/null +++ b/Sources/Share/Dialogs/AppInvite/AppInvite.swift @@ -0,0 +1,81 @@ +// 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 Foundation +import FBSDKShareKit + +/** + A model for an app invite. + */ +public struct AppInvite: Equatable { + + /// An app link target that will be used as a target when the user accepts the invite. + public var appLink: NSURL + + /// The delivery method for this app invite. + public var deliveryMethod: DeliveryMethod + + /// The URL to a preview image that will be displayed with the app invite. + public var previewImageURL: NSURL? + + /// The promotional code and text to be displayed while sending and recieving the invite. + public var promotion: (code: PromoCode, text: String)? + + /** + Create an `AppInvite` with a link, delivery method, preview image, and promotion. + + - parameter appLink: The app link target. + - parameter deliveryMethod: Optonal delivery method to use. Default: `.Facebook`. + - parameter previewImageURL: Optional preview image to use. Default: `nil`. + - parameter promotion: Optional promotion to be displayed. Default: `nil`. + */ + public init(appLink: NSURL, + deliveryMethod: DeliveryMethod = .Facebook, + previewImageURL: NSURL? = nil, + promotion: (code: PromoCode, text: String)? = nil) { + self.appLink = appLink + self.deliveryMethod = deliveryMethod + self.previewImageURL = previewImageURL + self.promotion = promotion + } +} + +extension AppInvite { + internal var sdkInviteRepresentation: FBSDKAppInviteContent { + let sdkContent = FBSDKAppInviteContent() + sdkContent.appLinkURL = appLink + sdkContent.appInvitePreviewImageURL = previewImageURL + sdkContent.promotionCode = promotion?.code.rawValue + sdkContent.promotionText = promotion?.text + sdkContent.destination = deliveryMethod.sdkDestinationRepresentation + + return sdkContent + } +} + +/** + Compare two `AppInvite`s for equality. + + - parameter lhs: The first invite to compare. + - parameter rhs: The second invite to compare. + + - returns: Whether or not the invites are equal. + */ +public func == (lhs: AppInvite, rhs: AppInvite) -> Bool { + return lhs.sdkInviteRepresentation == rhs.sdkInviteRepresentation +} diff --git a/Sources/Share/Dialogs/ContentSharingDialogProtocol.swift b/Sources/Share/Dialogs/ContentSharingDialogProtocol.swift new file mode 100644 index 00000000..9010e0f8 --- /dev/null +++ b/Sources/Share/Dialogs/ContentSharingDialogProtocol.swift @@ -0,0 +1,31 @@ +// 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 Foundation + +/** + Represents the base protocol for all Share dialogs. + */ +public protocol ContentSharingDialogProtocol: ContentSharingProtocol { + /** + Shows the dialog. + + - throws: If the dialog was unable to present. + */ + func show() throws +} diff --git a/Sources/Share/Dialogs/GameRequest/GameRequest.ActionType.swift b/Sources/Share/Dialogs/GameRequest/GameRequest.ActionType.swift new file mode 100644 index 00000000..58c08ae2 --- /dev/null +++ b/Sources/Share/Dialogs/GameRequest/GameRequest.ActionType.swift @@ -0,0 +1,51 @@ +// 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 FBSDKShareKit + +extension GameRequest { + /** + Additional context about the nature of the request. + */ + public enum ActionType { + /** + Send action type: The user is sending an object to the friends. + + - parameter objectId: The Open Graph object ID of the object being sent. + */ + case Send(objectId: String) + + /** + Ask for action type: The user is asking for an object from friends. + + - parameter objectId: The Open Graph object ID of the object being sent. + */ + case AskFor(objectId: String) + + /// Turn action type: It is the turn of the friends to play against the user in a match. (no object) + case Turn + + internal var sdkActionRepresentation: (FBSDKGameRequestActionType, String!) { + switch self { + case .Send(let objectId): return (FBSDKGameRequestActionType.Send, objectId) + case .AskFor(let objectId): return (FBSDKGameRequestActionType.AskFor, objectId) + case .Turn: return (FBSDKGameRequestActionType.Turn, nil) + } + } + } +} diff --git a/Sources/Share/Dialogs/GameRequest/GameRequest.Dialog.swift b/Sources/Share/Dialogs/GameRequest/GameRequest.Dialog.swift new file mode 100644 index 00000000..7e4458c7 --- /dev/null +++ b/Sources/Share/Dialogs/GameRequest/GameRequest.Dialog.swift @@ -0,0 +1,111 @@ +// 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 FBSDKShareKit + +extension GameRequest { + /// A dialog for sending game requests. + public final class Dialog { + private let sdkDialog: FBSDKGameRequestDialog + private let sdkDelegate: SDKDelegate + + /// The content for the game request. + public let request: GameRequest + + /// The completion handler to be invoked upon completion of the request. + public var completion: (Result -> Void)? { + didSet { + sdkDelegate.completion = completion + } + } + + /// Specifies whether frictionless requests are enabled. + public var frictionlessRequestsEnabled: Bool { + get { + return sdkDialog.frictionlessRequestsEnabled + } + set { + sdkDialog.frictionlessRequestsEnabled = false + } + } + + /** + Create a game request dialog with a given request. + + - parameter request: The game request to send. + */ + public init(request: GameRequest) { + self.request = request + + sdkDialog = FBSDKGameRequestDialog() + sdkDelegate = SDKDelegate() + + sdkDelegate.setupAsDelegateFor(sdkDialog) + sdkDialog.content = request.sdkContentRepresentation + } + + /** + Begins the game request from the receiver. + + - throws: If the dialog fails to be presented. + */ + public func show() throws { + var error: ErrorType? + let completionHandler = sdkDelegate.completion + sdkDelegate.completion = { + if case .Failed(let resultError) = $0 { + error = resultError + } + } + + sdkDialog.show() + sdkDelegate.completion = completionHandler + + if let error = error { + throw error + } + } + + /** + Validates the content on the receiver. + + - throws: If an error occurs during validation. + */ + public func validate() throws { + return try sdkDialog.validate() + } + } +} + +extension GameRequest.Dialog { + /** + Convenience method to build and show a game request dialog. + + - parameter request: The request to send. + - parameter completion: The completion handler to be invoked upon completion of the request. + + - returns: The dialog instance that has been shown. + - throws: If the dialog fails to be presented. + */ + public static func show(request: GameRequest, completion: (GameRequest.Result -> Void)?) throws -> Self { + let dialog = self.init(request: request) + dialog.completion = completion + try dialog.show() + return dialog + } +} diff --git a/Sources/Share/Dialogs/GameRequest/GameRequest.Recipient.swift b/Sources/Share/Dialogs/GameRequest/GameRequest.Recipient.swift new file mode 100644 index 00000000..2b7eb729 --- /dev/null +++ b/Sources/Share/Dialogs/GameRequest/GameRequest.Recipient.swift @@ -0,0 +1,52 @@ +// 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. + +extension GameRequest { + /** + Represents a recipient for a game request. + */ + public enum Recipient: Hashable { + /** + The Facebook user ID of the recipient. + */ + case UserId(String) + + /// The username of the recipient. + case Username(String) + + /// An invite token describing the recipient. + case InviteToken(String) + + /// Calculate the hash of this `Recipient.` + public var hashValue: Int { + switch self { + case .UserId(let userId): return userId.hashValue + case .Username(let username): return username.hashValue + case .InviteToken(let inviteToken): return inviteToken.hashValue + } + } + + internal var rawValue: String { + switch self { + case .UserId(let userId): return userId + case .Username(let username): return username + case .InviteToken(let inviteToken): return inviteToken + } + } + } +} diff --git a/Sources/Share/Dialogs/GameRequest/GameRequest.RecipientsFilter.swift b/Sources/Share/Dialogs/GameRequest/GameRequest.RecipientsFilter.swift new file mode 100644 index 00000000..ffcfab26 --- /dev/null +++ b/Sources/Share/Dialogs/GameRequest/GameRequest.RecipientsFilter.swift @@ -0,0 +1,57 @@ +// 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 FBSDKShareKit + +extension GameRequest { + /** + Filter for who can be displayed in the multi-friend selector. + */ + public struct RecipientsFilter: OptionSetType { + /// The raw value of the filter. + public let rawValue: Int + + /** + Initialize with a raw value for filter. + + - parameter rawValue: The raw value for the filter. + */ + public init(rawValue: Int) { + self.rawValue = rawValue + } + + /// Friends using the app cannot be displayed. + public static let HideUsers = RecipientsFilter(rawValue: 1 << 0) + + /// Friends not using the app cannot be displayed. + public static let HideNonUsers = RecipientsFilter(rawValue: 1 << 1) + + /// The default filter. Includes users and non-users. + public static let Default: RecipientsFilter = [ ] + + internal var sdkFilterRepresentation: FBSDKGameRequestFilter { + if contains(.HideUsers) { + return .AppNonUsers + } + if contains(.HideNonUsers) { + return .AppUsers + } + return .None + } + } +} diff --git a/Sources/Share/Dialogs/GameRequest/GameRequest.Result.swift b/Sources/Share/Dialogs/GameRequest/GameRequest.Result.swift new file mode 100644 index 00000000..4c45aae3 --- /dev/null +++ b/Sources/Share/Dialogs/GameRequest/GameRequest.Result.swift @@ -0,0 +1,34 @@ +// 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. + +extension GameRequest { + /** + Represents the results of an attempted game request. + */ + public enum Result { + // TODO: Better concrete type for results here. + /// The Game Request was succesfully sent. + case Success([String:String]) + + /// The Game Request failed. + case Failed(ErrorType) + + /// The Game Request was cancelled by the user. + case Cancelled + } +} diff --git a/Sources/Share/Dialogs/GameRequest/GameRequest.SDKDelegate.swift b/Sources/Share/Dialogs/GameRequest/GameRequest.SDKDelegate.swift new file mode 100644 index 00000000..4d2e5d32 --- /dev/null +++ b/Sources/Share/Dialogs/GameRequest/GameRequest.SDKDelegate.swift @@ -0,0 +1,49 @@ +// 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 Foundation +import FBSDKShareKit + +@testable import FacebookCore + +extension GameRequest { + internal class SDKDelegate: NSObject, FBSDKGameRequestDialogDelegate { + var completion: (Result -> Void)? + + func gameRequestDialog(gameRequestDialog: FBSDKGameRequestDialog?, didCompleteWithResults results: [NSObject : AnyObject]?) { + let result: Result = .Success(results?.keyValueFlatMap { ($0 as? String, $1 as? String) } ?? [:]) + completion?(result) + } + + func gameRequestDialog(gameRequestDialog: FBSDKGameRequestDialog?, didFailWithError error: NSError) { + completion?(.Failed(error)) + } + + func gameRequestDialogDidCancel(gameRequestDialog: FBSDKGameRequestDialog?) { + completion?(.Cancelled) + } + + func setupAsDelegateFor(dialog: FBSDKGameRequestDialog) { + // We need for the connection to retain us, + // so we can stick around and keep calling into handlers, + // as long as the connection is alive/sending messages. + objc_setAssociatedObject(dialog, unsafeAddressOf(self), self, .OBJC_ASSOCIATION_RETAIN) + dialog.delegate = self + } + } +} diff --git a/Sources/Share/Dialogs/GameRequest/GameRequest.swift b/Sources/Share/Dialogs/GameRequest/GameRequest.swift new file mode 100644 index 00000000..ae75f4d6 --- /dev/null +++ b/Sources/Share/Dialogs/GameRequest/GameRequest.swift @@ -0,0 +1,122 @@ +// 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 FBSDKShareKit + +/** + A model for a game request. + */ +public struct GameRequest: Equatable { + /** + Used when defining additional context about the nature of the request. + */ + public var actionType: ActionType? + + /** + Additional freeform data you may pass for tracking. This will be stored as part of the request objects created. + Maximum length is 255 characters. + */ + public var data: String? + + /** + This controls the set of friends someone sees if a multi-friend selector is shown. + It is `.Default` by default, meaning that all friends can be shown. + If specified as `HideUsers`, only friends who don't use the app will be shown. + If specified as `HideNonUsers`, only friends who do use the app will be shown. + */ + public var recipientsFilter: RecipientsFilter + + /** + A plain-text message to be sent as part of the request. + + This text will surface in the App Center view of the request, but not on the notification jewel. + */ + public var message: String + + /// The title for the dialog. + public var title: String + + /** + A set of user IDs, usernames or invite tokens of people to send the request to. + + These may or may not be a friend of the sender. If this is specified by the app, the sender will not have a choice + of recipients. If not, the sender will see a multi-friend selector. + */ + public var recipients: Set? + + /** + An array of user IDs that will be included in the dialog as the first suggested friends. + */ + public var recipientSuggestions: Set? + + /** + Create a new game request content with a title and a message. + + - parameter title: The title for the dialog. + - parameter message: The message to be sent as part of the request. + */ + public init(title: String, message: String) { + self.title = title + self.message = message + self.recipientsFilter = .Default + } +} + +extension GameRequest { + internal var sdkContentRepresentation: FBSDKGameRequestContent { + let sdkContent = FBSDKGameRequestContent() + (sdkContent.actionType, sdkContent.objectID) = actionType?.sdkActionRepresentation ?? (.None, nil) + sdkContent.data = data + sdkContent.filters = recipientsFilter.sdkFilterRepresentation + sdkContent.title = title + sdkContent.message = message + sdkContent.recipients = recipients?.map { $0.rawValue } + sdkContent.recipientSuggestions = recipientSuggestions?.map { $0.rawValue } + + return sdkContent + } +} + +/** + Compare two `GameRequest`s for equality. + + - parameter lhs: The first request to compare. + - parameter rhs: The second request to compare. + + - returns: Whether or not the requests are equal. + */ +public func == (lhs: GameRequest, rhs: GameRequest) -> Bool { + return lhs.sdkContentRepresentation == rhs.sdkContentRepresentation +} + +/** + Compare two `Recipient`s for equality. + + - parameter lhs: The first recipient to compare. + - parameter rhs: The second recipient to compare. + + - returns: Whether or not the recipients are equal. + */ +public func == (lhs: GameRequest.Recipient, rhs: GameRequest.Recipient) -> Bool { + switch (lhs, rhs) { + case (.UserId(let lhs), .UserId(let rhs)): return lhs == rhs + case (.Username(let lhs), .Username(let rhs)): return lhs == rhs + case (.InviteToken(let lhs), .InviteToken(let rhs)): return lhs == rhs + default: return false + } +} diff --git a/Sources/Share/Dialogs/MessageDialog/MessageDialog.swift b/Sources/Share/Dialogs/MessageDialog/MessageDialog.swift new file mode 100644 index 00000000..14287c83 --- /dev/null +++ b/Sources/Share/Dialogs/MessageDialog/MessageDialog.swift @@ -0,0 +1,122 @@ +// 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 FBSDKShareKit + +/// A dialog for sharing content through Messenger. +public final class MessageDialog { + + private let sdkSharer: FBSDKMessageDialog + private let sdkShareDelegate: SDKSharingDelegateBridge + + /** + Create a `MessageDialog` with a given content. + + - parameter content: The content to share. + */ + public init(content: Content) { + sdkSharer = FBSDKMessageDialog() + sdkShareDelegate = SDKSharingDelegateBridge() + + sdkShareDelegate.setupAsDelegateFor(sdkSharer) + sdkSharer.shareContent = ContentBridger.bridgeToObjC(content) + } +} + +extension MessageDialog: ContentSharingProtocol { + + /// The content that is being shared. + public var content: Content { + get { + guard let swiftContent: Content = ContentBridger.bridgeToSwift(sdkSharer.shareContent) else { + fatalError("Content of our private share dialog has changed type. Something horrible has happened.") + } + return swiftContent + } + } + + /// The completion handler to be invoked upon the share performing. + public var completion: (ContentSharerResult -> Void)? { + get { + return sdkShareDelegate.completion + } + set { + sdkShareDelegate.completion = newValue + } + } + + /// Whether or not this sharer fails on invalid data. + public var failsOnInvalidData: Bool { + get { + return sdkSharer.shouldFailOnDataError + } + set { + sdkSharer.shouldFailOnDataError = newValue + } + } + + /** + Validates the content on the receiver. + - throws: If The content could not be validated. + */ + public func validate() throws { + try sdkSharer.validate() + } +} + +extension MessageDialog: ContentSharingDialogProtocol { + /** + Shows the dialog. + + - throws: If the dialog cannot be presented. + */ + public func show() throws { + var error: ErrorType? + let completionHandler = sdkShareDelegate.completion + sdkShareDelegate.completion = { + if case .Failed(let resultError) = $0 { + error = resultError + } + } + + sdkSharer.show() + sdkShareDelegate.completion = completionHandler + + if let error = error { + throw error + } + } +} + +extension MessageDialog { + /** + Convenience method to show a Message Share Dialog with content and a completion handler. + + - parameter content: The content to share. + - parameter completion: The completion handler to invoke. + + - returns: The dialog that has been presented. + - throws: If the dialog fails to validate. + */ + public static func show(content: Content, completion: (ContentSharerResult -> Void)? = nil) throws -> Self { + let dialog = self.init(content: content) + dialog.completion = completion + try dialog.show() + return dialog + } +} diff --git a/Sources/Share/Dialogs/ShareDialog/ShareDialog.swift b/Sources/Share/Dialogs/ShareDialog/ShareDialog.swift new file mode 100644 index 00000000..d0f8e5b2 --- /dev/null +++ b/Sources/Share/Dialogs/ShareDialog/ShareDialog.swift @@ -0,0 +1,154 @@ +// 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 FBSDKShareKit + +/// A dialog for sharing content on Facebook. +public final class ShareDialog { + private let sdkSharer: FBSDKShareDialog + private let sdkShareDelegate: SDKSharingDelegateBridge + + /** + A `UIViewController` to present the dialog from. + + If not specified, the top most view controller will be automatically determined as best as possible. + */ + public var presentingViewController: UIViewController? { + get { + return sdkSharer.fromViewController + } + set { + sdkSharer.fromViewController = newValue + } + } + + /** + The mode with which to display the dialog. + + Defaults to `.Automatic`, which will automatically choose the best available mode. + */ + public var mode: ShareDialogMode { + get { + return ShareDialogMode(sdkShareMode: sdkSharer.mode) + } + set { + sdkSharer.mode = newValue.sdkShareMode + } + } + + /** + Create a `ShareDialog` with a given content. + + - parameter content: The content to share. + */ + public init(content: Content) { + sdkSharer = FBSDKShareDialog() + sdkShareDelegate = SDKSharingDelegateBridge() + + sdkShareDelegate.setupAsDelegateFor(sdkSharer) + sdkSharer.shareContent = ContentBridger.bridgeToObjC(content) + } +} + +extension ShareDialog: ContentSharingProtocol { + + /// The content that is being shared. + public var content: Content { + get { + guard let swiftContent: Content = ContentBridger.bridgeToSwift(sdkSharer.shareContent) else { + fatalError("Content of our private share dialog has changed type. Something horrible has happened.") + } + return swiftContent + } + } + + /// The completion handler to be invoked upon the share performing. + public var completion: (ContentSharerResult -> Void)? { + get { + return sdkShareDelegate.completion + } + set { + sdkShareDelegate.completion = newValue + } + } + + /// Whether or not this sharer fails on invalid data. + public var failsOnInvalidData: Bool { + get { + return sdkSharer.shouldFailOnDataError + } + set { + sdkSharer.shouldFailOnDataError = newValue + } + } + + /** + Validates the content on the receiver. + - throws: If The content could not be validated. + */ + public func validate() throws { + try sdkSharer.validate() + } +} + + +extension ShareDialog: ContentSharingDialogProtocol { + /** + Shows the dialog. + + - throws: If the dialog cannot be presented. + */ + public func show() throws { + var error: ErrorType? + let completionHandler = sdkShareDelegate.completion + sdkShareDelegate.completion = { + if case .Failed(let resultError) = $0 { + error = resultError + } + } + + sdkSharer.show() + sdkShareDelegate.completion = completionHandler + + if let error = error { + throw error + } + } +} + +extension ShareDialog { + /** + Convenience method to create and show a `ShareDialog` with a `fromViewController`, `content`, and `completion`. + + - parameter viewController: The viewController to present the dialog from. + - parameter content: The content to share. + - parameter completion: The completion handler to invoke. + + - returns: The `ShareDialog` that has been presented. + - throws: If the dialog fails to validate. + */ + public static func show(from viewController: UIViewController, + content: Content, + completion: (ContentSharerResult -> Void)? = nil) throws -> Self { + let shareDialog = self.init(content: content) + shareDialog.presentingViewController = viewController + shareDialog.completion = completion + try shareDialog.show() + return shareDialog + } +} diff --git a/Sources/Share/Dialogs/ShareDialog/ShareDialogMode.swift b/Sources/Share/Dialogs/ShareDialog/ShareDialogMode.swift new file mode 100644 index 00000000..6745ce2e --- /dev/null +++ b/Sources/Share/Dialogs/ShareDialog/ShareDialogMode.swift @@ -0,0 +1,74 @@ +// 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 FBSDKShareKit + +/** + Modes for the `ShareDialog`. + + The automatic mode will progressively check the availability of different modes and open the most appropriate mode + for the dialog that is available. + */ +public enum ShareDialogMode { + /// Acts with the most appropriate mode that is available. + case Automatic + + /// Displays the dialog in the main native Facebook app. + case Native + + /// Displays the dialog in the iOS integrated share sheet. + case ShareSheet + + /// Displays the dialog in Safari. + case Browser + + /// Displays the dialog in a UIWebView within the app. + case Web + + /// Displays the feed dialog in Safari. + case FeedBrowser + + /// Displays the feed dialog in a UIWebView within the app. + case FeedWeb +} + +extension ShareDialogMode { + internal init(sdkShareMode: FBSDKShareDialogMode) { + switch sdkShareMode { + case .Automatic: self = .Automatic + case .Native: self = .Native + case .ShareSheet: self = .ShareSheet + case .Browser: self = .Browser + case .Web: self = .Web + case .FeedBrowser: self = .FeedBrowser + case .FeedWeb: self = .FeedWeb + } + } + + internal var sdkShareMode: FBSDKShareDialogMode { + switch self { + case .Automatic: return .Automatic + case .Native: return .Native + case .ShareSheet: return .ShareSheet + case .Browser: return .Browser + case .Web: return .Web + case .FeedBrowser: return .FeedBrowser + case .FeedWeb: return .FeedWeb + } + } +} diff --git a/Sources/Share/Internal/Constants.Internal.swift b/Sources/Share/Internal/Constants.Internal.swift new file mode 100644 index 00000000..f14948b1 --- /dev/null +++ b/Sources/Share/Internal/Constants.Internal.swift @@ -0,0 +1,27 @@ +// 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 Foundation + +// Taken from FBSDKShareDefines.h + +enum ShareResultKeys: String { + case CompletionGesture = "completionGesture" + case DidComplete = "didComplete" + case PostId = "postId" +} diff --git a/Sources/Share/Internal/ContentBridger.swift b/Sources/Share/Internal/ContentBridger.swift new file mode 100644 index 00000000..effed10d --- /dev/null +++ b/Sources/Share/Internal/ContentBridger.swift @@ -0,0 +1,67 @@ +// 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 FBSDKShareKit +import ObjectiveC.runtime + +/** + Represents content that is sharable with the Graph API. This protocol is internal only, as it is only needed for + internal sharable types. If we ever have custom types which implement `Content`, then we need to re-investigate + this architecture. + */ +internal protocol SDKBridgedContent { + var sdkSharingContentRepresentation: FBSDKSharingContent { get } +} + +internal struct ContentBridger { + // The only way for swift to guarantee a stable pointer is by using UnsafeMutablePointer.alloc. Using the `&` + // operator, or using withUnsafePointer is liable to have a stack-copied pointer, not a static pointer, which is + // what we need. + private static let contentHolderKey = UnsafeMutablePointer.alloc(1) + + internal static func bridgeToObjC(content: C) -> FBSDKSharingContent? { + guard let nativeContent = content as? SDKBridgedContent else { + return nil + } + + let sdkRepresentation = nativeContent.sdkSharingContentRepresentation + let contentHolder = SwiftContentHolder(swiftContent: content) + objc_setAssociatedObject(sdkRepresentation, contentHolderKey, contentHolder, .OBJC_ASSOCIATION_RETAIN) + + return sdkRepresentation + } + + internal static func bridgeToSwift(content: FBSDKSharingContent) -> C? { + let object = objc_getAssociatedObject(content, contentHolderKey) + guard let contentHolder = object as? SwiftContentHolder else { + return nil + } + + return contentHolder.swiftContent + } +} + +// Basic class wrapper for holding `Content`. This gets set as an associated object on the Objective-C +// FBSDKSharingContent, so we can extract the swift content (which is probably a struct) back as its proper type. +private class SwiftContentHolder: NSObject { + private let swiftContent: C + + private init(swiftContent: C) { + self.swiftContent = swiftContent + } +} diff --git a/Sources/Share/Internal/SDKSharingDelegateBridge.swift b/Sources/Share/Internal/SDKSharingDelegateBridge.swift new file mode 100644 index 00000000..f8663083 --- /dev/null +++ b/Sources/Share/Internal/SDKSharingDelegateBridge.swift @@ -0,0 +1,59 @@ +// 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 FBSDKShareKit + +@testable import FacebookCore + +private struct BridgingFailedError: ErrorType { + let nativeResults: [NSObject: AnyObject]? +} + +internal class SDKSharingDelegateBridge: NSObject, FBSDKSharingDelegate { + internal var completion: (ContentSharerResult -> Void)? = nil + + func setupAsDelegateFor(sharer: FBSDKSharing) { + // We need for the connection to retain us, + // so we can stick around and keep calling into handlers, + // as long as the connection is alive/sending messages. + objc_setAssociatedObject(sharer, unsafeAddressOf(self), self, .OBJC_ASSOCIATION_RETAIN) + sharer.delegate = self + } + + func sharer(sharer: FBSDKSharing, didCompleteWithResults results: [NSObject : AnyObject]?) { + let dictionary = results.map { + $0.keyValueFlatMap { key, value in + (key as? String, value as? String) + } + } + let sharingResult = dictionary.map(Content.Result.init) + let result: ContentSharerResult = sharingResult.map(ContentSharerResult.Success) ?? + .Failed(BridgingFailedError(nativeResults: results)) + + completion?(result) + } + + func sharer(sharer: FBSDKSharing, didFailWithError error: NSError) { + let error: ErrorType = ShareError(error: error) ?? error + completion?(.Failed(error)) + } + + func sharerDidCancel(sharer: FBSDKSharing) { + completion?(.Cancelled) + } +} diff --git a/Sources/Share/ShareError.swift b/Sources/Share/ShareError.swift new file mode 100644 index 00000000..76b13d09 --- /dev/null +++ b/Sources/Share/ShareError.swift @@ -0,0 +1,59 @@ +// 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 FBSDKShareKit + +/** + Represents an error returned by the FacebookShare framework. + */ +public enum ShareError: ErrorType { + + /// Reserved. + case Reserved + + /// The error code for errors from uploading open graph objects. + case OpenGraph + + /** + The error code for when a sharing dialog is not available. + Use the `canShare` property to check for this case before calling show. + */ + case DialogNotAvailable + + /// The error code for unknown errors. + case Unknown + + /** + Attempt to create a share error from a NSError returned by the Facebook SDK. + + - parameter error: The error to attempt to convert. + */ + internal init?(error: NSError) { + if error.domain != FBSDKShareErrorDomain { + return nil + } + + switch FBSDKShareErrorCode(rawValue: error.code) { + case .ReservedErrorCode?: self = .Reserved + case .OpenGraphErrorCode?: self = .OpenGraph + case .DialogNotAvailableErrorCode?: self = .DialogNotAvailable + case .UnknownErrorCode?: self = .Unknown + default: return nil + } + } +} diff --git a/Sources/Share/Views/LikableObject.swift b/Sources/Share/Views/LikableObject.swift new file mode 100644 index 00000000..a9bb7047 --- /dev/null +++ b/Sources/Share/Views/LikableObject.swift @@ -0,0 +1,67 @@ +// 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 FBSDKShareKit + +/** + Specifies the type of object referenced by the objectID for likes. + */ +public enum LikableObject: Equatable { + /// The objectId refers to an OpenGraph object. + case OpenGraph(objectId: String) + + /// The objectId refers to a Page object. + case Page(objectId: String) + + /** + The objectId refers to an unknown object. + + The control will determine the object type by querying the server with the objectID. + */ + case Unknown(objectId: String) +} + +extension LikableObject { + internal init(sdkObjectType: FBSDKLikeObjectType, sdkObjectId: String) { + switch sdkObjectType { + case .OpenGraph: self = .OpenGraph(objectId: sdkObjectId) + case .Page: self = .Page(objectId: sdkObjectId) + case .Unknown: self = .Unknown(objectId: sdkObjectId) + } + } + + internal var sdkObjectRepresntation: (objectType: FBSDKLikeObjectType, objectId: String) { + switch self { + case .OpenGraph(let objectId): return (.OpenGraph, objectId) + case .Page(let objectId): return (.Page, objectId) + case .Unknown(let objectId): return (.Unknown, objectId) + } + } +} + +/** + Compare two `LikableObject`s for equality. + + - parameter lhs: The first object to compare. + - parameter rhs: The second object to compare. + + - returns: Whether or not the objects are equal. + */ +public func == (lhs: LikableObject, rhs: LikableObject) -> Bool { + return lhs.sdkObjectRepresntation.objectId == rhs.sdkObjectRepresntation.objectId +} diff --git a/Sources/Share/Views/LikeButton.swift b/Sources/Share/Views/LikeButton.swift new file mode 100644 index 00000000..201c0602 --- /dev/null +++ b/Sources/Share/Views/LikeButton.swift @@ -0,0 +1,121 @@ +// 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 Foundation +import FBSDKShareKit + +/** + A button to like an object. + + Tapping the receiver will invoke an API call to the Facebook app through a fast-app-switch that allows + the object to be liked. Upon return to the calling app, the view will update with the new state. If the + currentAccessToken has "publish_actions" permission and the object is an Open Graph object, then the like can happen + seamlessly without the fast-app-switch. + */ +public class LikeButton: UIView { + + private var sdkLikeButton: FBSDKLikeButton + + /// If `true`, a sound is played when the reciever is toggled. + public var isSoundEnabled: Bool { + get { + return sdkLikeButton.soundEnabled + } + set { + sdkLikeButton.soundEnabled = newValue + } + } + + /// The object to like + public var object: LikableObject { + get { + return LikableObject(sdkObjectType: sdkLikeButton.objectType, sdkObjectId: sdkLikeButton.objectID) + } + set { + let sdkRepresentation = newValue.sdkObjectRepresntation + sdkLikeButton.objectType = sdkRepresentation.objectType + sdkLikeButton.objectID = sdkRepresentation.objectId + } + } + + /** + Create a new LikeButton with a given frame and object. + + - parameter frame: The frame to initialize with. + - parameter object: The object to like. + */ + public init(frame: CGRect? = nil, object: LikableObject) { + let sdkLikeButton = FBSDKLikeButton() + let frame = frame ?? sdkLikeButton.bounds + + self.sdkLikeButton = sdkLikeButton + + super.init(frame: frame) + + self.object = object + self.addSubview(sdkLikeButton) + } + + /** + Create a new LikeButton from an encoded interface file. + + - parameter aDecoder: The coder to initialize from. + */ + public required init?(coder aDecoder: NSCoder) { + sdkLikeButton = FBSDKLikeButton() + + super.init(coder: aDecoder) + addSubview(sdkLikeButton) + } + + /** + Performs logic for laying out subviews. + */ + public override func layoutSubviews() { + super.layoutSubviews() + + sdkLikeButton.frame = CGRect(origin: .zero, size: bounds.size) + } + + /** + Resizes and moves the receiver view so it just encloses its subviews. + */ + public override func sizeToFit() { + bounds.size = sizeThatFits(CGSize(width: CGFloat.max, height: CGFloat.max)) + } + + /** + Asks the view to calculate and return the size that best fits the specified size. + + - parameter size: A new size that fits the receiver’s subviews. + + - returns: A new size that fits the receiver’s subviews. + */ + public override func sizeThatFits(size: CGSize) -> CGSize { + return sdkLikeButton.sizeThatFits(size) + } + + /** + Returns the natural size for the receiving view, considering only properties of the view itself. + + - returns: A size indicating the natural size for the receiving view based on its intrinsic properties. + */ + public override func intrinsicContentSize() -> CGSize { + return sdkLikeButton.intrinsicContentSize() + } +} diff --git a/Sources/Share/Views/LikeControl.AuxilaryStyle.swift b/Sources/Share/Views/LikeControl.AuxilaryStyle.swift new file mode 100644 index 00000000..0463fed7 --- /dev/null +++ b/Sources/Share/Views/LikeControl.AuxilaryStyle.swift @@ -0,0 +1,174 @@ +// 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 FBSDKShareKit + +extension LikeControl { + /** + Specifies the style of the auxilary view in the like control. + */ + public enum AuxilaryStyle: Equatable { + /// Use the standard social share message. + case Standard(horizontalAlignment: HorizontalAlignment, verticalAlignment: VerticalAlignment) + + /// Use a more compact box count auxilary view. + case BoxCount(horizontalAlignment: HorizontalAlignment, verticalAlignment: VerticalAlignment) + + /// The horizontal alignment of this style. + public var horizontalAlignment: HorizontalAlignment { + get { + switch self { + case .Standard(let alignment): return alignment.horizontalAlignment + case .BoxCount(let alignment): return alignment.horizontalAlignment + } + } + set { + switch self { + case .Standard(let alignment): + self = .Standard(horizontalAlignment: newValue, verticalAlignment: alignment.verticalAlignment) + + case .BoxCount(let alignment): + self = .BoxCount(horizontalAlignment: newValue, verticalAlignment: alignment.verticalAlignment) + } + } + } + + /// The vertical alignment of this style. + public var verticalAlignment: VerticalAlignment { + get { + switch self { + case .Standard(let alignment): return alignment.verticalAlignment + case .BoxCount(let alignment): return alignment.verticalAlignment + } + } + set { + switch self { + case .Standard(let alignment): + self = .Standard(horizontalAlignment: alignment.horizontalAlignment, verticalAlignment: newValue) + + case .BoxCount(let alignment): + self = .BoxCount(horizontalAlignment: alignment.horizontalAlignment, verticalAlignment: newValue) + } + } + } + } +} + +extension LikeControl.AuxilaryStyle { + internal init( + sdkStyle: FBSDKLikeControlStyle, + sdkHorizontalAlignment: FBSDKLikeControlHorizontalAlignment, + sdkAuxilaryPosition: FBSDKLikeControlAuxiliaryPosition + ) { + let horizontalAlignment = HorizontalAlignment(sdkHorizontalAlignment: sdkHorizontalAlignment) + let verticalAlignment = VerticalAlignment(sdkAuxilaryPosition: sdkAuxilaryPosition) + + switch sdkStyle { + case .Standard: self = .Standard(horizontalAlignment: horizontalAlignment, verticalAlignment: verticalAlignment) + case .BoxCount: self = .BoxCount(horizontalAlignment: horizontalAlignment, verticalAlignment: verticalAlignment) + } + } + + internal var sdkStyleRepresentation: (FBSDKLikeControlStyle, FBSDKLikeControlHorizontalAlignment, FBSDKLikeControlAuxiliaryPosition) { + switch self { + case .Standard(let horizontal, let vertical): + return (.Standard, horizontal.sdkHorizontalAlignment, vertical.sdkAuxilaryPosition) + + case .BoxCount(let horizontal, let vertical): + return (.BoxCount, horizontal.sdkHorizontalAlignment, vertical.sdkAuxilaryPosition) + } + } +} + +extension LikeControl.AuxilaryStyle { + /** + Control the horizontal alignment of the auxilary view. + */ + public enum HorizontalAlignment { + /// The auxilary view should be placed to the left of the like button. + case Left + + /// The auxilary view should be placed centered to the like button. + case Center + + /// The auxilary view should be placed to the right of the like button. + case Right + + internal init(sdkHorizontalAlignment: FBSDKLikeControlHorizontalAlignment) { + switch sdkHorizontalAlignment { + case .Left: self = .Left + case .Center: self = .Center + case .Right: self = .Right + } + } + + internal var sdkHorizontalAlignment: FBSDKLikeControlHorizontalAlignment { + switch self { + case .Left: return .Left + case .Center: return .Center + case .Right: return .Right + } + } + } + + /** + Controls vertical alignment of the auxilary view. + */ + public enum VerticalAlignment { + /// The auxilary view should be placed above the like button. + case Top + + /// The auxilary view should be placed inline with the like button. + case Inline + + /// The auxilary view should be placed below the like button. + case Bottom + + internal init(sdkAuxilaryPosition: FBSDKLikeControlAuxiliaryPosition) { + switch sdkAuxilaryPosition { + case .Top: self = .Top + case .Inline: self = .Inline + case .Bottom: self = .Bottom + } + } + + internal var sdkAuxilaryPosition: FBSDKLikeControlAuxiliaryPosition { + switch self { + case .Top: return .Top + case .Inline: return .Inline + case .Bottom: return .Bottom + } + } + } +} + +/** + Compare two `AuxilaryStyle`'s for equality. + + - parameter lhs: The first style to compare. + - parameter rhs: The second style to compare. + + - returns: Whether or not the styles are equal. + */ +public func == (lhs: LikeControl.AuxilaryStyle, rhs: LikeControl.AuxilaryStyle) -> Bool { + switch (lhs, rhs) { + case (.Standard(let lhs), .Standard(let rhs)): return lhs == rhs + case (.BoxCount(let lhs), .BoxCount(let rhs)): return lhs == rhs + default: return false + } +} diff --git a/Sources/Share/Views/LikeControl.swift b/Sources/Share/Views/LikeControl.swift new file mode 100644 index 00000000..37770ae0 --- /dev/null +++ b/Sources/Share/Views/LikeControl.swift @@ -0,0 +1,165 @@ +// 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 Foundation +import UIKit +import FBSDKShareKit + +/** + UI control to like an object in the Facebook graph. + + Taps on the like button within this control will invoke an API call to the Facebook app through a fast-app-switch + that allows the user to like the object. Upon return to the calling app, the view will update with the new state. + */ +public class LikeControl: UIView { + private let sdkLikeControl: FBSDKLikeControl + + /** + Create a new LikeControl with an optional frame and object. + + - parameter frame: The frame to use for this control. If `nil`, defaults to a default size. + - parameter object: The object to like. + */ + public init(frame: CGRect? = nil, object: LikableObject) { + let sdkLikeControl = FBSDKLikeControl() + let frame = frame ?? sdkLikeControl.bounds + + self.sdkLikeControl = sdkLikeControl + + super.init(frame: frame) + + self.object = object + addSubview(sdkLikeControl) + } + + /** + Create a new LikeControl from an encoded interface file. + + - parameter coder: The coder to initialize from. + */ + public required init?(coder: NSCoder) { + sdkLikeControl = FBSDKLikeControl() + + super.init(coder: coder) + self.addSubview(sdkLikeControl) + } + + /// The foreground color to use for the content of the control. + public var foregroundColor: UIColor { + get { + return sdkLikeControl.foregroundColor + } + set { + sdkLikeControl.foregroundColor = newValue + } + } + + /// The object to like. + public var object: LikableObject { + get { + return LikableObject(sdkObjectType: sdkLikeControl.objectType, sdkObjectId: sdkLikeControl.objectID) + } + set { + let sdkRepresentation = newValue.sdkObjectRepresntation + sdkLikeControl.objectType = sdkRepresentation.objectType + sdkLikeControl.objectID = sdkRepresentation.objectId + } + } + + /// The style to use for this control. + public var auxilaryStyle: AuxilaryStyle { + get { + return AuxilaryStyle( + sdkStyle: sdkLikeControl.likeControlStyle, + sdkHorizontalAlignment: sdkLikeControl.likeControlHorizontalAlignment, + sdkAuxilaryPosition: sdkLikeControl.likeControlAuxiliaryPosition + ) + } + set { + ( + sdkLikeControl.likeControlStyle, + sdkLikeControl.likeControlHorizontalAlignment, + sdkLikeControl.likeControlAuxiliaryPosition + ) = newValue.sdkStyleRepresentation + } + } + + /** + The preferred maximum width (in points) for autolayout. + + This property affects the size of the receiver when layout constraints are applied to it. During layout, + if the text extends beyond the width specified by this property, the additional text is flowed to one or more new + lines, thereby increasing the height of the receiver. + */ + public var preferredMaxLayoutWidth: CGFloat { + get { + return sdkLikeControl.preferredMaxLayoutWidth + } + set { + sdkLikeControl.preferredMaxLayoutWidth = newValue + } + } + + /// If `true`, a sound is played when the control is toggled. + public var isSoundEnabled: Bool { + get { + return sdkLikeControl.soundEnabled + } + set { + sdkLikeControl.soundEnabled = newValue + } + } +} + +extension LikeControl { + /** + Performs logic for laying out subviews. + */ + public override func layoutSubviews() { + super.layoutSubviews() + + sdkLikeControl.frame = CGRect(origin: .zero, size: bounds.size) + } + + /** + Resizes and moves the receiver view so it just encloses its subviews. + */ + public override func sizeToFit() { + bounds.size = sizeThatFits(CGSize(width: CGFloat.max, height: CGFloat.max)) + } + + /** + Asks the view to calculate and return the size that best fits the specified size. + + - parameter size: A new size that fits the receiver’s subviews. + + - returns: A new size that fits the receiver’s subviews. + */ + public override func sizeThatFits(size: CGSize) -> CGSize { + return sdkLikeControl.sizeThatFits(size) + } + + /** + Returns the natural size for the receiving view, considering only properties of the view itself. + + - returns: A size indicating the natural size for the receiving view based on its intrinsic properties. + */ + public override func intrinsicContentSize() -> CGSize { + return sdkLikeControl.intrinsicContentSize() + } +} diff --git a/Sources/Share/Views/SendButton.swift b/Sources/Share/Views/SendButton.swift new file mode 100644 index 00000000..713bf404 --- /dev/null +++ b/Sources/Share/Views/SendButton.swift @@ -0,0 +1,87 @@ +// 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 Foundation +import FBSDKShareKit + +/** + A button for sending content with messenger. + */ +public class SendButton: UIView { + private var sdkSendButton: FBSDKSendButton + + /// The content to share. + public var content: C? = nil { + didSet { + sdkSendButton.shareContent = content.flatMap(ContentBridger.bridgeToObjC) + } + } + + /** + Create a new SendButton with a given frame and content. + + - parameter frame: The frame to initialize with. + - parameter content: The content to share. + */ + public init(frame: CGRect? = nil, content: C? = nil) { + let sdkSendButton = FBSDKSendButton() + let frame = frame ?? sdkSendButton.bounds + + self.sdkSendButton = sdkSendButton + self.content = content + + super.init(frame: frame ?? sdkSendButton.bounds) + addSubview(sdkSendButton) + } + + /** + Performs logic for laying out subviews. + */ + public override func layoutSubviews() { + super.layoutSubviews() + + sdkSendButton.frame = CGRect(origin: .zero, size: bounds.size) + } + + /** + Resizes and moves the receiver view so it just encloses its subviews. + */ + public override func sizeToFit() { + bounds.size = sizeThatFits(CGSize(width: CGFloat.max, height: CGFloat.max)) + } + + /** + Asks the view to calculate and return the size that best fits the specified size. + + - parameter size: A new size that fits the receiver’s subviews. + + - returns: A new size that fits the receiver’s subviews. + */ + public override func sizeThatFits(size: CGSize) -> CGSize { + return sdkSendButton.sizeThatFits(size) + } + + /** + Returns the natural size for the receiving view, considering only properties of the view itself. + + - returns: A size indicating the natural size for the receiving view based on its intrinsic properties. + */ + public override func intrinsicContentSize() -> CGSize { + return sdkSendButton.intrinsicContentSize() + } +} diff --git a/Sources/Share/Views/ShareButton.swift b/Sources/Share/Views/ShareButton.swift new file mode 100644 index 00000000..16cbf931 --- /dev/null +++ b/Sources/Share/Views/ShareButton.swift @@ -0,0 +1,87 @@ +// 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 Foundation +import FBSDKShareKit + +/** + A button for sharing content. + */ +public class ShareButton: UIView { + private var sdkShareButton: FBSDKShareButton + + /// The content to share. + public var content: C? = nil { + didSet { + sdkShareButton.shareContent = content.flatMap(ContentBridger.bridgeToObjC) + } + } + + /** + Create a new ShareButton with a given frame and content. + + - parameter frame: The frame to initialize with. + - parameter content: The content to share. + */ + public init(frame: CGRect? = nil, content: C? = nil) { + let sdkShareButton = FBSDKShareButton() + let frame = frame ?? sdkShareButton.bounds + + self.sdkShareButton = sdkShareButton + self.content = content + + super.init(frame: frame) + addSubview(sdkShareButton) + } + + /** + Performs logic for laying out subviews. + */ + public override func layoutSubviews() { + super.layoutSubviews() + + sdkShareButton.frame = CGRect(origin: .zero, size: bounds.size) + } + + /** + Resizes and moves the receiver view so it just encloses its subviews. + */ + public override func sizeToFit() { + bounds.size = sizeThatFits(CGSize(width: CGFloat.max, height: CGFloat.max)) + } + + /** + Asks the view to calculate and return the size that best fits the specified size. + + - parameter size: A new size that fits the receiver’s subviews. + + - returns: A new size that fits the receiver’s subviews. + */ + public override func sizeThatFits(size: CGSize) -> CGSize { + return sdkShareButton.sizeThatFits(size) + } + + /** + Returns the natural size for the receiving view, considering only properties of the view itself. + + - returns: A size indicating the natural size for the receiving view based on its intrinsic properties. + */ + public override func intrinsicContentSize() -> CGSize { + return sdkShareButton.intrinsicContentSize() + } +} diff --git a/Vendor/xctoolchain b/Vendor/xctoolchain new file mode 160000 index 00000000..af8d7f69 --- /dev/null +++ b/Vendor/xctoolchain @@ -0,0 +1 @@ +Subproject commit af8d7f69bf7a5b8e8edd94a139f83fc465b7b64a