From 704d9dc8d1fcbb8bc1d5227bddd1ecaf6587471d Mon Sep 17 00:00:00 2001 From: Cosmo Wolfe Date: Wed, 16 Jan 2019 16:33:23 -0800 Subject: [PATCH] V2.0.0 (#25) * Adds the new SDK * Update README --- .circleci/config.yml | 2 +- CleverSDK.podspec | 11 +- CleverSDK.xcodeproj/project.pbxproj | 159 +++------ CleverSDK/CLVApiRequest.h | 23 -- CleverSDK/CLVApiRequest.m | 37 -- CleverSDK/CLVCleverSDK.h | 18 - CleverSDK/CLVLoginButton.h | 29 -- CleverSDK/CLVOAuthManager.h | 125 ------- CleverSDK/CLVOAuthManager.m | 337 ------------------ CleverSDK/CleverLoginButton.h | 11 + .../{CLVLoginButton.m => CleverLoginButton.m} | 56 ++- CleverSDK/CleverSDK.h | 18 + CleverSDK/CleverSDK.m | 141 ++++++++ CleverSDKTests/CleverSDKTests.m | 88 +---- Example/AppDelegate.h | 7 + Example/AppDelegate.m | 45 +++ Example/CLVAppDelegate.h | 18 - Example/CLVAppDelegate.m | 98 ----- Example/CLVLoginViewController.h | 13 - Example/CLVLoginViewController.m | 43 --- Example/CLVSuccessViewController.h | 15 - Example/CLVSuccessViewController.m | 50 --- Example/Example.entitlements | 10 + Example/LoginViewController.h | 5 + Example/LoginViewController.m | 26 ++ ...Controller.xib => LoginViewController.xib} | 2 +- Example/SuccessViewController.h | 7 + Example/SuccessViewController.m | 25 ++ ...ntroller.xib => SuccessViewController.xib} | 57 +-- Example/main.m | 12 +- Podfile | 2 +- README.md | 157 ++++---- VERSION | 2 +- 33 files changed, 509 insertions(+), 1140 deletions(-) delete mode 100755 CleverSDK/CLVApiRequest.h delete mode 100755 CleverSDK/CLVApiRequest.m delete mode 100755 CleverSDK/CLVCleverSDK.h delete mode 100755 CleverSDK/CLVLoginButton.h delete mode 100755 CleverSDK/CLVOAuthManager.h delete mode 100755 CleverSDK/CLVOAuthManager.m create mode 100755 CleverSDK/CleverLoginButton.h rename CleverSDK/{CLVLoginButton.m => CleverLoginButton.m} (79%) create mode 100755 CleverSDK/CleverSDK.h create mode 100755 CleverSDK/CleverSDK.m create mode 100755 Example/AppDelegate.h create mode 100755 Example/AppDelegate.m delete mode 100755 Example/CLVAppDelegate.h delete mode 100755 Example/CLVAppDelegate.m delete mode 100755 Example/CLVLoginViewController.h delete mode 100755 Example/CLVLoginViewController.m delete mode 100755 Example/CLVSuccessViewController.h delete mode 100755 Example/CLVSuccessViewController.m create mode 100644 Example/Example.entitlements create mode 100755 Example/LoginViewController.h create mode 100755 Example/LoginViewController.m rename Example/{CLVLoginViewController.xib => LoginViewController.xib} (98%) create mode 100755 Example/SuccessViewController.h create mode 100755 Example/SuccessViewController.m rename Example/{CLVSuccessViewController.xib => SuccessViewController.xib} (72%) diff --git a/.circleci/config.yml b/.circleci/config.yml index dc1aa1d..7fd1c32 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -28,4 +28,4 @@ jobs: command: if [ "${CIRCLE_BRANCH}" == "master" ] && ! pod trunk info CleverSDK | grep -q "$(cat VERSION)"; then $HOME/ci-scripts/circleci/github-release $GH_RELEASE_TOKEN; fi; - run: name: Publish CocoaPods release - command: if [ "${CIRCLE_BRANCH}" == "master" ] && ! pod trunk info CleverSDK | grep -q "$(cat VERSION)"; then pod trunk push --allow-warnings; fi; + command: if [ "${CIRCLE_BRANCH}" == "master" ] && ! pod trunk info CleverSDK | grep -q "$(cat VERSION)"; then pod trunk push; fi; diff --git a/CleverSDK.podspec b/CleverSDK.podspec index b592b5a..13aeaee 100644 --- a/CleverSDK.podspec +++ b/CleverSDK.podspec @@ -9,14 +9,9 @@ Pod::Spec.new do |s| s.name = "CleverSDK" - # Version is also set in CLVCleverSDK.h, please keep them in sync s.version = File.read("VERSION") s.summary = "A simple iOS library to access Clever Instant Login" - s.description = <<-DESC - CleverSDK provides developers with a simple library to access Clever Instant Login. - The SDK includes a Login handler (the `CLVOAuthManager`) and a Login Button (the "CLVLoginButton") that can be added to any `UIView`. - The SDK returns an `access_token` to the user that can be used to make calls to the Clever API. - DESC + s.description = "CleverSDK provides developers with a simple library to access Clever Instant Login." s.homepage = "https://github.com/Clever/ios-sdk" s.license = "Apache 2.0" s.authors = { "Clever" => "tech-notify@clever.com" } @@ -24,14 +19,12 @@ Pod::Spec.new do |s| s.social_media_url = "https://twitter.com/clever" s.documentation_url = "https://dev.clever.com/" - s.platform = :ios, "8.0" + s.platform = :ios, "9.0" s.requires_arc = true s.public_header_files = "CleverSDK/**/*.h" s.source_files = "CleverSDK/**/*" s.exclude_files = "CleverSDK/**/*.plist" - s.dependency "AFNetworking", "~> 3.1" s.dependency "PocketSVG", "~> 0.7" - s.dependency "SAMKeychain", "~> 1.5" end diff --git a/CleverSDK.xcodeproj/project.pbxproj b/CleverSDK.xcodeproj/project.pbxproj index e603e99..5c7a3d9 100644 --- a/CleverSDK.xcodeproj/project.pbxproj +++ b/CleverSDK.xcodeproj/project.pbxproj @@ -8,20 +8,17 @@ /* Begin PBXBuildFile section */ 262DC45821ED8B0200FE5B20 /* LaunchScreen.xib in Resources */ = {isa = PBXBuildFile; fileRef = 262DC45721ED8B0200FE5B20 /* LaunchScreen.xib */; }; - 262DC46121EDAEE700FE5B20 /* CLVAppDelegate.m in Sources */ = {isa = PBXBuildFile; fileRef = 262DC45921EDAEE600FE5B20 /* CLVAppDelegate.m */; }; - 262DC46221EDAEE700FE5B20 /* CLVLoginViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 262DC45A21EDAEE600FE5B20 /* CLVLoginViewController.m */; }; - 262DC46321EDAEE700FE5B20 /* CLVSuccessViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 262DC45D21EDAEE600FE5B20 /* CLVSuccessViewController.m */; }; - 262DC46421EDAEE700FE5B20 /* CLVLoginViewController.xib in Resources */ = {isa = PBXBuildFile; fileRef = 262DC45E21EDAEE600FE5B20 /* CLVLoginViewController.xib */; }; - 262DC46521EDAEE700FE5B20 /* CLVSuccessViewController.xib in Resources */ = {isa = PBXBuildFile; fileRef = 262DC46021EDAEE700FE5B20 /* CLVSuccessViewController.xib */; }; + 262DC46121EDAEE700FE5B20 /* AppDelegate.m in Sources */ = {isa = PBXBuildFile; fileRef = 262DC45921EDAEE600FE5B20 /* AppDelegate.m */; }; + 262DC46221EDAEE700FE5B20 /* LoginViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 262DC45A21EDAEE600FE5B20 /* LoginViewController.m */; }; + 262DC46321EDAEE700FE5B20 /* SuccessViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 262DC45D21EDAEE600FE5B20 /* SuccessViewController.m */; }; + 262DC46421EDAEE700FE5B20 /* LoginViewController.xib in Resources */ = {isa = PBXBuildFile; fileRef = 262DC45E21EDAEE600FE5B20 /* LoginViewController.xib */; }; + 262DC46521EDAEE700FE5B20 /* SuccessViewController.xib in Resources */ = {isa = PBXBuildFile; fileRef = 262DC46021EDAEE700FE5B20 /* SuccessViewController.xib */; }; 2663BEB221ED880D00ED2BC1 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 2663BEB121ED880D00ED2BC1 /* Assets.xcassets */; }; 2663BEB821ED880D00ED2BC1 /* main.m in Sources */ = {isa = PBXBuildFile; fileRef = 2663BEB721ED880D00ED2BC1 /* main.m */; }; - 268D08DA21ED781700DE2088 /* CLVApiRequest.h in Headers */ = {isa = PBXBuildFile; fileRef = 268D08D321ED781700DE2088 /* CLVApiRequest.h */; }; - 268D08DB21ED781700DE2088 /* CLVOAuthManager.h in Headers */ = {isa = PBXBuildFile; fileRef = 268D08D421ED781700DE2088 /* CLVOAuthManager.h */; }; - 268D08DC21ED781700DE2088 /* CLVApiRequest.m in Sources */ = {isa = PBXBuildFile; fileRef = 268D08D521ED781700DE2088 /* CLVApiRequest.m */; }; - 268D08DD21ED781700DE2088 /* CLVLoginButton.m in Sources */ = {isa = PBXBuildFile; fileRef = 268D08D621ED781700DE2088 /* CLVLoginButton.m */; }; - 268D08DE21ED781700DE2088 /* CLVOAuthManager.m in Sources */ = {isa = PBXBuildFile; fileRef = 268D08D721ED781700DE2088 /* CLVOAuthManager.m */; }; - 268D08DF21ED781700DE2088 /* CLVLoginButton.h in Headers */ = {isa = PBXBuildFile; fileRef = 268D08D821ED781700DE2088 /* CLVLoginButton.h */; }; - 268D08E021ED781700DE2088 /* CLVCleverSDK.h in Headers */ = {isa = PBXBuildFile; fileRef = 268D08D921ED781700DE2088 /* CLVCleverSDK.h */; }; + 268D08DB21ED781700DE2088 /* CleverSDK.h in Headers */ = {isa = PBXBuildFile; fileRef = 268D08D421ED781700DE2088 /* CleverSDK.h */; }; + 268D08DD21ED781700DE2088 /* CleverLoginButton.m in Sources */ = {isa = PBXBuildFile; fileRef = 268D08D621ED781700DE2088 /* CleverLoginButton.m */; }; + 268D08DE21ED781700DE2088 /* CleverSDK.m in Sources */ = {isa = PBXBuildFile; fileRef = 268D08D721ED781700DE2088 /* CleverSDK.m */; }; + 268D08DF21ED781700DE2088 /* CleverLoginButton.h in Headers */ = {isa = PBXBuildFile; fileRef = 268D08D821ED781700DE2088 /* CleverLoginButton.h */; }; 26AF353121ED59B800263628 /* CleverSDKTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 26AF353021ED59B800263628 /* CleverSDKTests.m */; }; 26AF353321ED59B800263628 /* CleverSDK.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 26BB6E7621ED23F50005D165 /* CleverSDK.framework */; }; 26F13BCA21EDB9C600852ED1 /* CleverSDK.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 26BB6E7621ED23F50005D165 /* CleverSDK.framework */; }; @@ -45,26 +42,24 @@ 054C6BA1F3D9B75317F5E19D /* libPods-Example.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = "libPods-Example.a"; sourceTree = BUILT_PRODUCTS_DIR; }; 234F52C9664ABC5B6AD3DE57 /* Pods-CleverSDK.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-CleverSDK.debug.xcconfig"; path = "Pods/Target Support Files/Pods-CleverSDK/Pods-CleverSDK.debug.xcconfig"; sourceTree = ""; }; 262DC45721ED8B0200FE5B20 /* LaunchScreen.xib */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.xib; path = LaunchScreen.xib; sourceTree = ""; }; - 262DC45921EDAEE600FE5B20 /* CLVAppDelegate.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = CLVAppDelegate.m; sourceTree = ""; }; - 262DC45A21EDAEE600FE5B20 /* CLVLoginViewController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = CLVLoginViewController.m; sourceTree = ""; }; - 262DC45B21EDAEE600FE5B20 /* CLVSuccessViewController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = CLVSuccessViewController.h; sourceTree = ""; }; - 262DC45C21EDAEE600FE5B20 /* CLVAppDelegate.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = CLVAppDelegate.h; sourceTree = ""; }; - 262DC45D21EDAEE600FE5B20 /* CLVSuccessViewController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = CLVSuccessViewController.m; sourceTree = ""; }; - 262DC45E21EDAEE600FE5B20 /* CLVLoginViewController.xib */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.xib; path = CLVLoginViewController.xib; sourceTree = ""; }; - 262DC45F21EDAEE600FE5B20 /* CLVLoginViewController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = CLVLoginViewController.h; sourceTree = ""; }; - 262DC46021EDAEE700FE5B20 /* CLVSuccessViewController.xib */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.xib; path = CLVSuccessViewController.xib; sourceTree = ""; }; + 262DC45921EDAEE600FE5B20 /* AppDelegate.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = AppDelegate.m; sourceTree = ""; }; + 262DC45A21EDAEE600FE5B20 /* LoginViewController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = LoginViewController.m; sourceTree = ""; }; + 262DC45B21EDAEE600FE5B20 /* SuccessViewController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SuccessViewController.h; sourceTree = ""; }; + 262DC45C21EDAEE600FE5B20 /* AppDelegate.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = AppDelegate.h; sourceTree = ""; }; + 262DC45D21EDAEE600FE5B20 /* SuccessViewController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = SuccessViewController.m; sourceTree = ""; }; + 262DC45E21EDAEE600FE5B20 /* LoginViewController.xib */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.xib; path = LoginViewController.xib; sourceTree = ""; }; + 262DC45F21EDAEE600FE5B20 /* LoginViewController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = LoginViewController.h; sourceTree = ""; }; + 262DC46021EDAEE700FE5B20 /* SuccessViewController.xib */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.xib; path = SuccessViewController.xib; sourceTree = ""; }; 2663BEA621ED880900ED2BC1 /* Example.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = Example.app; sourceTree = BUILT_PRODUCTS_DIR; }; 2663BEB121ED880D00ED2BC1 /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; }; 2663BEB621ED880D00ED2BC1 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 2663BEB721ED880D00ED2BC1 /* main.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = main.m; sourceTree = ""; }; + 267B719B21EFF9ED003A32A0 /* Example.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = Example.entitlements; sourceTree = ""; }; 268D08D221ED780300DE2088 /* README.md */ = {isa = PBXFileReference; lastKnownFileType = net.daringfireball.markdown; path = README.md; sourceTree = ""; }; - 268D08D321ED781700DE2088 /* CLVApiRequest.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = CLVApiRequest.h; sourceTree = ""; }; - 268D08D421ED781700DE2088 /* CLVOAuthManager.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = CLVOAuthManager.h; sourceTree = ""; }; - 268D08D521ED781700DE2088 /* CLVApiRequest.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = CLVApiRequest.m; sourceTree = ""; }; - 268D08D621ED781700DE2088 /* CLVLoginButton.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = CLVLoginButton.m; sourceTree = ""; }; - 268D08D721ED781700DE2088 /* CLVOAuthManager.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = CLVOAuthManager.m; sourceTree = ""; }; - 268D08D821ED781700DE2088 /* CLVLoginButton.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = CLVLoginButton.h; sourceTree = ""; }; - 268D08D921ED781700DE2088 /* CLVCleverSDK.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = CLVCleverSDK.h; sourceTree = ""; }; + 268D08D421ED781700DE2088 /* CleverSDK.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = CleverSDK.h; sourceTree = ""; }; + 268D08D621ED781700DE2088 /* CleverLoginButton.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = CleverLoginButton.m; sourceTree = ""; }; + 268D08D721ED781700DE2088 /* CleverSDK.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = CleverSDK.m; sourceTree = ""; }; + 268D08D821ED781700DE2088 /* CleverLoginButton.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = CleverLoginButton.h; sourceTree = ""; }; 26AF352E21ED59B800263628 /* CleverSDKTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = CleverSDKTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; 26AF353021ED59B800263628 /* CleverSDKTests.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = CleverSDKTests.m; sourceTree = ""; }; 26AF353221ED59B800263628 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; @@ -114,14 +109,15 @@ 2663BEA721ED880900ED2BC1 /* Example */ = { isa = PBXGroup; children = ( - 262DC45C21EDAEE600FE5B20 /* CLVAppDelegate.h */, - 262DC45921EDAEE600FE5B20 /* CLVAppDelegate.m */, - 262DC45F21EDAEE600FE5B20 /* CLVLoginViewController.h */, - 262DC45A21EDAEE600FE5B20 /* CLVLoginViewController.m */, - 262DC45E21EDAEE600FE5B20 /* CLVLoginViewController.xib */, - 262DC45B21EDAEE600FE5B20 /* CLVSuccessViewController.h */, - 262DC45D21EDAEE600FE5B20 /* CLVSuccessViewController.m */, - 262DC46021EDAEE700FE5B20 /* CLVSuccessViewController.xib */, + 267B719B21EFF9ED003A32A0 /* Example.entitlements */, + 262DC45C21EDAEE600FE5B20 /* AppDelegate.h */, + 262DC45921EDAEE600FE5B20 /* AppDelegate.m */, + 262DC45F21EDAEE600FE5B20 /* LoginViewController.h */, + 262DC45A21EDAEE600FE5B20 /* LoginViewController.m */, + 262DC45E21EDAEE600FE5B20 /* LoginViewController.xib */, + 262DC45B21EDAEE600FE5B20 /* SuccessViewController.h */, + 262DC45D21EDAEE600FE5B20 /* SuccessViewController.m */, + 262DC46021EDAEE700FE5B20 /* SuccessViewController.xib */, 2663BEB121ED880D00ED2BC1 /* Assets.xcassets */, 262DC45721ED8B0200FE5B20 /* LaunchScreen.xib */, 2663BEB621ED880D00ED2BC1 /* Info.plist */, @@ -169,13 +165,10 @@ isa = PBXGroup; children = ( 26F9A8F421ED52C500ED1B6F /* Info.plist */, - 268D08D321ED781700DE2088 /* CLVApiRequest.h */, - 268D08D521ED781700DE2088 /* CLVApiRequest.m */, - 268D08D921ED781700DE2088 /* CLVCleverSDK.h */, - 268D08D821ED781700DE2088 /* CLVLoginButton.h */, - 268D08D621ED781700DE2088 /* CLVLoginButton.m */, - 268D08D421ED781700DE2088 /* CLVOAuthManager.h */, - 268D08D721ED781700DE2088 /* CLVOAuthManager.m */, + 268D08D821ED781700DE2088 /* CleverLoginButton.h */, + 268D08D621ED781700DE2088 /* CleverLoginButton.m */, + 268D08D421ED781700DE2088 /* CleverSDK.h */, + 268D08D721ED781700DE2088 /* CleverSDK.m */, ); path = CleverSDK; sourceTree = ""; @@ -210,10 +203,8 @@ isa = PBXHeadersBuildPhase; buildActionMask = 2147483647; files = ( - 268D08E021ED781700DE2088 /* CLVCleverSDK.h in Headers */, - 268D08DF21ED781700DE2088 /* CLVLoginButton.h in Headers */, - 268D08DB21ED781700DE2088 /* CLVOAuthManager.h in Headers */, - 268D08DA21ED781700DE2088 /* CLVApiRequest.h in Headers */, + 268D08DF21ED781700DE2088 /* CleverLoginButton.h in Headers */, + 268D08DB21ED781700DE2088 /* CleverSDK.h in Headers */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -246,7 +237,6 @@ 26AF352A21ED59B800263628 /* Sources */, 26AF352B21ED59B800263628 /* Frameworks */, 26AF352C21ED59B800263628 /* Resources */, - 581604DF43A195E827F8E111 /* [CP] Copy Pods Resources */, ); buildRules = ( ); @@ -267,7 +257,6 @@ 26BB6E7221ED23F50005D165 /* Sources */, 26BB6E7321ED23F50005D165 /* Frameworks */, 26BB6E7421ED23F50005D165 /* Resources */, - 8B0741FC2ACA48151CFBF364 /* [CP] Copy Pods Resources */, ); buildRules = ( ); @@ -289,6 +278,11 @@ TargetAttributes = { 2663BEA521ED880900ED2BC1 = { CreatedOnToolsVersion = 10.1; + SystemCapabilities = { + com.apple.SafariKeychain = { + enabled = 1; + }; + }; }; 26AF352D21ED59B800263628 = { CreatedOnToolsVersion = 10.1; @@ -324,8 +318,8 @@ buildActionMask = 2147483647; files = ( 262DC45821ED8B0200FE5B20 /* LaunchScreen.xib in Resources */, - 262DC46421EDAEE700FE5B20 /* CLVLoginViewController.xib in Resources */, - 262DC46521EDAEE700FE5B20 /* CLVSuccessViewController.xib in Resources */, + 262DC46421EDAEE700FE5B20 /* LoginViewController.xib in Resources */, + 262DC46521EDAEE700FE5B20 /* SuccessViewController.xib in Resources */, 2663BEB221ED880D00ED2BC1 /* Assets.xcassets in Resources */, ); runOnlyForDeploymentPostprocessing = 0; @@ -369,28 +363,6 @@ shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n"; showEnvVarsInLog = 0; }; - 581604DF43A195E827F8E111 /* [CP] Copy Pods Resources */ = { - isa = PBXShellScriptBuildPhase; - buildActionMask = 2147483647; - files = ( - ); - inputFileListPaths = ( - ); - inputPaths = ( - "${SRCROOT}/Pods/Target Support Files/Pods-CleverSDKTests/Pods-CleverSDKTests-resources.sh", - "${PODS_ROOT}/SAMKeychain/Support/SAMKeychain.bundle", - ); - name = "[CP] Copy Pods Resources"; - outputFileListPaths = ( - ); - outputPaths = ( - "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/SAMKeychain.bundle", - ); - runOnlyForDeploymentPostprocessing = 0; - shellPath = /bin/sh; - shellScript = "\"${SRCROOT}/Pods/Target Support Files/Pods-CleverSDKTests/Pods-CleverSDKTests-resources.sh\"\n"; - showEnvVarsInLog = 0; - }; 7855BD6DE5CC38056255DC39 /* [CP] Check Pods Manifest.lock */ = { isa = PBXShellScriptBuildPhase; buildActionMask = 2147483647; @@ -413,28 +385,6 @@ shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n"; showEnvVarsInLog = 0; }; - 8B0741FC2ACA48151CFBF364 /* [CP] Copy Pods Resources */ = { - isa = PBXShellScriptBuildPhase; - buildActionMask = 2147483647; - files = ( - ); - inputFileListPaths = ( - ); - inputPaths = ( - "${SRCROOT}/Pods/Target Support Files/Pods-CleverSDK/Pods-CleverSDK-resources.sh", - "${PODS_ROOT}/SAMKeychain/Support/SAMKeychain.bundle", - ); - name = "[CP] Copy Pods Resources"; - outputFileListPaths = ( - ); - outputPaths = ( - "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/SAMKeychain.bundle", - ); - runOnlyForDeploymentPostprocessing = 0; - shellPath = /bin/sh; - shellScript = "\"${SRCROOT}/Pods/Target Support Files/Pods-CleverSDK/Pods-CleverSDK-resources.sh\"\n"; - showEnvVarsInLog = 0; - }; F342E30368C017BF2A2695A6 /* [CP] Check Pods Manifest.lock */ = { isa = PBXShellScriptBuildPhase; buildActionMask = 2147483647; @@ -464,9 +414,9 @@ isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; files = ( - 262DC46321EDAEE700FE5B20 /* CLVSuccessViewController.m in Sources */, - 262DC46121EDAEE700FE5B20 /* CLVAppDelegate.m in Sources */, - 262DC46221EDAEE700FE5B20 /* CLVLoginViewController.m in Sources */, + 262DC46321EDAEE700FE5B20 /* SuccessViewController.m in Sources */, + 262DC46121EDAEE700FE5B20 /* AppDelegate.m in Sources */, + 262DC46221EDAEE700FE5B20 /* LoginViewController.m in Sources */, 2663BEB821ED880D00ED2BC1 /* main.m in Sources */, ); runOnlyForDeploymentPostprocessing = 0; @@ -483,9 +433,8 @@ isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; files = ( - 268D08DC21ED781700DE2088 /* CLVApiRequest.m in Sources */, - 268D08DE21ED781700DE2088 /* CLVOAuthManager.m in Sources */, - 268D08DD21ED781700DE2088 /* CLVLoginButton.m in Sources */, + 268D08DE21ED781700DE2088 /* CleverSDK.m in Sources */, + 268D08DD21ED781700DE2088 /* CleverLoginButton.m in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -505,11 +454,12 @@ baseConfigurationReference = 64E8BA1FCB6DF2477D8A9182 /* Pods-Example.debug.xcconfig */; buildSettings = { ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; + CODE_SIGN_ENTITLEMENTS = Example/Example.entitlements; CODE_SIGN_IDENTITY = "iPhone Developer"; CODE_SIGN_STYLE = Automatic; DEVELOPMENT_TEAM = 6D5FQ2E2ZU; INFOPLIST_FILE = Example/Info.plist; - IPHONEOS_DEPLOYMENT_TARGET = 8.0; + IPHONEOS_DEPLOYMENT_TARGET = 9.0; LD_RUNPATH_SEARCH_PATHS = ( "$(inherited)", "@executable_path/Frameworks", @@ -526,11 +476,12 @@ baseConfigurationReference = 2E76C922290D30329BE308FD /* Pods-Example.release.xcconfig */; buildSettings = { ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; + CODE_SIGN_ENTITLEMENTS = Example/Example.entitlements; CODE_SIGN_IDENTITY = "iPhone Developer"; CODE_SIGN_STYLE = Automatic; DEVELOPMENT_TEAM = 6D5FQ2E2ZU; INFOPLIST_FILE = Example/Info.plist; - IPHONEOS_DEPLOYMENT_TARGET = 8.0; + IPHONEOS_DEPLOYMENT_TARGET = 9.0; LD_RUNPATH_SEARCH_PATHS = ( "$(inherited)", "@executable_path/Frameworks", @@ -720,7 +671,7 @@ FRAMEWORK_VERSION = A; INFOPLIST_FILE = CleverSDK/Info.plist; INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; - IPHONEOS_DEPLOYMENT_TARGET = 8.0; + IPHONEOS_DEPLOYMENT_TARGET = 9.0; LD_RUNPATH_SEARCH_PATHS = ( "$(inherited)", "@executable_path/../Frameworks", @@ -748,7 +699,7 @@ FRAMEWORK_VERSION = A; INFOPLIST_FILE = CleverSDK/Info.plist; INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; - IPHONEOS_DEPLOYMENT_TARGET = 8.0; + IPHONEOS_DEPLOYMENT_TARGET = 9.0; LD_RUNPATH_SEARCH_PATHS = ( "$(inherited)", "@executable_path/../Frameworks", diff --git a/CleverSDK/CLVApiRequest.h b/CleverSDK/CLVApiRequest.h deleted file mode 100755 index bbb7758..0000000 --- a/CleverSDK/CLVApiRequest.h +++ /dev/null @@ -1,23 +0,0 @@ -// -// CLVApiRequest.h -// CleverSDK -// -// Created by Nikhil Pandit on 4/7/15. -// -// - -#import - -NS_ASSUME_NONNULL_BEGIN - -@interface CLVApiRequest : AFHTTPSessionManager - -+ (instancetype)sharedManager; - -+ (void)endpoint:(NSString *)endpoint params:(NSDictionary *)params - success:(void (^)(NSURLSessionDataTask *task, id responseObject))successHandler - failure:(void (^)(NSURLSessionDataTask *task, NSError *error))failureHandler; - -@end - -NS_ASSUME_NONNULL_END diff --git a/CleverSDK/CLVApiRequest.m b/CleverSDK/CLVApiRequest.m deleted file mode 100755 index aa3ad0a..0000000 --- a/CleverSDK/CLVApiRequest.m +++ /dev/null @@ -1,37 +0,0 @@ -// -// CLVApiRequest.m -// CleverSDK -// -// Created by Nikhil Pandit on 4/7/15. -// -// - -#import "CLVCleverSDK.h" -#import "CLVApiRequest.h" -#import "AFNetworkActivityIndicatorManager.h" -#import "CLVOAuthManager.h" - -@implementation CLVApiRequest - -+ (instancetype)sharedManager { - static CLVApiRequest *_sharedManager = nil; - static dispatch_once_t onceToken; - dispatch_once(&onceToken, ^{ - [AFNetworkActivityIndicatorManager sharedManager].enabled = YES; - _sharedManager = [[self alloc] initWithBaseURL:[NSURL URLWithString:@"https://api.clever.com/"]]; - _sharedManager.requestSerializer = [AFJSONRequestSerializer serializer]; - _sharedManager.responseSerializer = [AFJSONResponseSerializer serializer]; - }); - [_sharedManager.requestSerializer setValue:[NSString stringWithFormat:@"Bearer %@", [CLVOAuthManager accessToken]] forHTTPHeaderField:@"Authorization"]; - [_sharedManager.requestSerializer setValue:[NSString stringWithFormat:SDK_VERSION] forHTTPHeaderField:@"X-Clever-SDK-Version"]; - return _sharedManager; -} - -+ (void)endpoint:(NSString *)endpoint params:(NSDictionary *)params - success:(void (^)(NSURLSessionDataTask *task, id responseObject))successHandler - failure:(void (^)(NSURLSessionDataTask *task, NSError *error))failureHandler { - - [[self sharedManager] GET:endpoint parameters:params progress:nil success:successHandler failure:failureHandler]; -} - -@end diff --git a/CleverSDK/CLVCleverSDK.h b/CleverSDK/CLVCleverSDK.h deleted file mode 100755 index ec8b259..0000000 --- a/CleverSDK/CLVCleverSDK.h +++ /dev/null @@ -1,18 +0,0 @@ -// -// CLVCleverSDK.h -// CleverSDK -// -// Created by Nikhil Pandit on 4/3/15. -// Copyright (c) 2015 Clever, Inc. All rights reserved. -// - -#ifndef SimpleLogin_CLVCleverSDK_h -#define SimpleLogin_CLVCleverSDK_h - -#define SDK_VERSION @"iOS-1.0.0" - -#import "CLVLoginButton.h" -#import "CLVOAuthManager.h" -#import "CLVApiRequest.h" - -#endif diff --git a/CleverSDK/CLVLoginButton.h b/CleverSDK/CLVLoginButton.h deleted file mode 100755 index 642842d..0000000 --- a/CleverSDK/CLVLoginButton.h +++ /dev/null @@ -1,29 +0,0 @@ -// -// CLVLoginButton.h -// CleverSDK -// -// Created by Nikhil Pandit on 4/3/15. -// Copyright (c) 2015 Clever, Inc. All rights reserved. -// - -#import - -@interface CLVLoginButton : UIButton - -/** - Create a `CLVLoginButton` - */ -+ (CLVLoginButton *)createLoginButton; - -/** - Set the origin of the button. This is the preferred method of manipulating the button frame. - */ -- (void)setOrigin:(CGPoint)origin; - -/** - Set the button width. The button height is always. - The text will be always centered in the button, and the Clever C will be left aligned. - */ -- (void)setWidth:(CGFloat)width; - -@end diff --git a/CleverSDK/CLVOAuthManager.h b/CleverSDK/CLVOAuthManager.h deleted file mode 100755 index f87245c..0000000 --- a/CleverSDK/CLVOAuthManager.h +++ /dev/null @@ -1,125 +0,0 @@ -// -// CLVOAuthManager.h -// CleverSDK -// -// Created by Nikhil Pandit on 4/3/15. -// Copyright (c) 2015 Clever, Inc. All rights reserved. -// - -#import -#import - -@protocol CLVOauthDelegate -- (void) signInToClever:( NSString* _Nullable )accessToken withError:( NSString* _Nullable )error; -@end -NS_ASSUME_NONNULL_BEGIN - -/** - CLVOAuthManager - */ -@interface CLVOAuthManager : NSObject - -@property (weak) id delegate; // receiver of successful signInToClever completions -@property (weak) UIViewController *uiDelegate; // on iOS 9/10, receiver of calls to present/dismiss SFSafariViewController for sign in - -/** - * Initializes the CLVOauthManager. Sets the clientId which is used for constructing the OAuth URL and logging in. - */ -+ (void)startWithClientId:(NSString *)clientId; - -/** - * Initializes the CLVOAuthManager with successHandler and failureHandler. If this method is used, the delegate will not called. - * uiDelegate still needs to be set on iOS even if you use this method. - */ -+ (void)startWithClientId:(NSString *)clientId successHandler:(void (^)(NSString *accessToken))successHandler failureHandler:(void (^)(NSString *errorMessage))failureHandler; - -/** - * setDelegate sets the CLVOAuthDelegate implementer to be called upon completion - */ -+ (void)setDelegate:(id) delegate; - -/** - * setUIDelegate sets the UIViewController to be called to present/dismiss the SFSafariViewController. - * This is only used on iOS 9/10 - */ -+ (void)setUIDelegate:(UIViewController *) uiDelegate; - -/** - * This method should be called under `application:openURL:sourceApplication:annotation:` method in the AppDelegate - * This will also retrieve the `access_token` and attach it automatically for requests made using `CLVApiRequest` - */ -+ (BOOL)handleURL:(NSURL *)url sourceApplication:(NSString *)sourceApplication annotation:(id)annotation; - -/** - * Start the login flow - */ -+ (void)login; - -/** - * It is important to call this method when the user tries to logout. This clears the `access_token` locally, and invalidates any stored state on the device. - * It also makes a call to the server to logout the user from the app. - */ -+ (void)logout; - -/** - * Return the access token. - * Use this as the bearer token when constructing network requests to the Clever API. - * See https://dev.clever.com/ for more information on using the accessToken. - * Alternatively, use the CLVApiRequest class to make requests. - */ -+ (NSString *)accessToken; - -///---------------------------------------- -/// Methods used by other classes of the SDK or internally -///---------------------------------------- - -/** - Simple helper method to determine whether or not the clientId was set - */ -+ (BOOL)clientIdIsNotSet; - -/** - Return the saved clientId to the caller - */ -+ (NSString *)clientId; - -/** - Return the redirectURI to the caller - */ -+ (NSString *)redirectUri; - -/** - Return the uiDelegate - */ -+ (UIViewController *)uiDelegate; - -/** - Call the success handler - */ -+ (void)callSucessHandler; - -/** - Call the failure handler - */ -+ (void)callFailureHandler; - -/** - Allows setting the access token. Users of the SDK should not need to call this method directly. - */ -+ (void)setAccessToken:(NSString *)accessToken; - -/** - This method sets the state value used in the OAuth fow. - It should be called before every login attempt to ensure a unique, random state value is used. - */ -+ (void)setState:(NSString *)state; - -/** - Return the state - */ -+ (NSString *)state; - - -@end - -NS_ASSUME_NONNULL_END diff --git a/CleverSDK/CLVOAuthManager.m b/CleverSDK/CLVOAuthManager.m deleted file mode 100755 index 3e83981..0000000 --- a/CleverSDK/CLVOAuthManager.m +++ /dev/null @@ -1,337 +0,0 @@ -// -// CLVOAuthManager.m -// CleverSDK -// -// Created by Nikhil Pandit on 4/3/15. -// Copyright (c) 2015 Clever, Inc. All rights reserved. -// - -#import "CLVOAuthManager.h" -#import -#import -#import "AFHTTPSessionManager.h" -#import "AFNetworkActivityIndicatorManager.h" -#import "CLVCleverSDK.h" - -#define SYSTEM_VERSION_EQUAL_TO(v) ([[[UIDevice currentDevice] systemVersion] compare:v options:NSNumericSearch] == NSOrderedSame) -#define SYSTEM_VERSION_GREATER_THAN(v) ([[[UIDevice currentDevice] systemVersion] compare:v options:NSNumericSearch] == NSOrderedDescending) -#define SYSTEM_VERSION_GREATER_THAN_OR_EQUAL_TO(v) ([[[UIDevice currentDevice] systemVersion] compare:v options:NSNumericSearch] != NSOrderedAscending) -#define SYSTEM_VERSION_LESS_THAN(v) ([[[UIDevice currentDevice] systemVersion] compare:v options:NSNumericSearch] == NSOrderedAscending) -#define SYSTEM_VERSION_LESS_THAN_OR_EQUAL_TO(v) ([[[UIDevice currentDevice] systemVersion] compare:v options:NSNumericSearch] != NSOrderedDescending) - -static NSString *const CLVServiceName = @"com.clever.CleverSDK"; - -@interface CLVOAuthManager () - -@property (nonatomic, strong) NSString *clientId; // The partner app's clientID -@property (nonatomic, strong) NSString *districtId; // optional districtID -@property (nonatomic, strong) NSString *state; // used for oauthflow -@property (nonatomic, strong) NSString *accessToken; -@property (nonatomic, strong) NSString *errorMessage; -@property (atomic, assign) BOOL alreadyMissedCode; // used to track if a flow has already been called without a code - -@property (nonatomic, copy) void (^successHandler)(NSString *); -@property (nonatomic, copy) void (^failureHandler)(NSString *); - -+ (instancetype)sharedManager; - -@end - -@implementation CLVOAuthManager - -+ (instancetype)sharedManager { - static CLVOAuthManager *_sharedManager = nil; - static dispatch_once_t onceToken; - dispatch_once(&onceToken, ^{ - _sharedManager = [[self alloc] init]; - }); - return _sharedManager; -} - -+ (void)startWithClientId:(NSString *)clientId { - CLVOAuthManager *manager = [self sharedManager]; - manager.clientId = clientId; - manager.alreadyMissedCode = NO; -} - -+ (void)startWithClientId:(NSString *)clientId successHandler:(void (^)(NSString *accessToken))successHandler failureHandler:(void (^)(NSString *errorMessage))failureHandler { - [self startWithClientId:clientId]; - CLVOAuthManager *manager = [self sharedManager]; - manager.successHandler = successHandler; - manager.failureHandler = failureHandler; -} - -+ (void)setDelegate:(id) delegate { - CLVOAuthManager *manager = [self sharedManager]; - manager.delegate = delegate; -} - -+ (UIViewController *)uiDelegate { - return [[self sharedManager] uiDelegate]; -} - -+ (void)setUIDelegate:(UIViewController *) uiDelegate { - CLVOAuthManager *manager = [self sharedManager]; - manager.uiDelegate = uiDelegate; -} - -+ (NSString *)generateRandomString:(int)length { - NSAssert(length % 2 == 0, @"Must generate random string with even length"); - NSMutableData *data = [NSMutableData dataWithLength:length / 2]; - NSAssert(SecRandomCopyBytes(kSecRandomDefault, length, [data mutableBytes]) == 0, @"Failure in SecRandomCopyBytes: %d", errno); - NSMutableString *hexString = [NSMutableString stringWithCapacity:(length)]; - const unsigned char *dataBytes = [data bytes]; - for (int i = 0; i < length / 2; ++i) - { - [hexString appendFormat:@"%02x", (unsigned int)dataBytes[i]]; - } - return [NSString stringWithString:hexString]; -} - -+ (void)setState:(NSString *)state { - CLVOAuthManager *manager = [self sharedManager]; - manager.state = state; -} - -+ (BOOL)clientIdIsNotSet { - NSString *clientId = [self clientId]; - if (!clientId || [clientId isEqualToString:@""] || [clientId isEqualToString:@"CLIENT_ID"]) { - // checking to see it's not empty or the default value of "CLIENT_ID" - return YES; - } else { - return NO; - } -} - -+ (NSString *)clientId { - return [[self sharedManager] clientId]; -} - -+ (NSString *)redirectUri { - return [NSString stringWithFormat:@"clever-%@://oauth", [self clientId]]; -} - -+ (NSString *)state { - CLVOAuthManager *manager = [self sharedManager]; - return [manager state]; -} - -+ (void)login { - NSString *state = [self generateRandomString:32]; - [self setState:state]; - - NSString *safariURLString = [NSString stringWithFormat:@"https://clever.com/oauth/authorize?response_type=code&client_id=%@&redirect_uri=%@&state=%@", - [self clientId], [self redirectUri], [self state]]; - - NSString *cleverAppURLString = [NSString stringWithFormat:@"com.clever://oauth/authorize?response_type=code&client_id=%@&redirect_uri=%@&state=%@&sdk_version=%@", [self clientId], [self redirectUri], [self state], SDK_VERSION]; - - CLVOAuthManager *manager = [self sharedManager]; - if (manager.districtId) { - NSString *districtId = manager.districtId; - if (districtId == nil) { - districtId = @""; - } - safariURLString = [NSString stringWithFormat:@"%@&district_id=%@", safariURLString, districtId]; - cleverAppURLString = [NSString stringWithFormat:@"%@&district_id=%@", safariURLString, districtId]; - } - - // iOS 8 - always use Safari. Clever App not supported - if (SYSTEM_VERSION_LESS_THAN(@"9.0")) { - [[UIApplication sharedApplication] openURL: [NSURL URLWithString:safariURLString]]; - return; - } - - // Switch to native Clever app if possible - if ([[UIApplication sharedApplication] canOpenURL:[NSURL URLWithString:cleverAppURLString]]) { - // Use old openURL method if on iOS 9- - if (SYSTEM_VERSION_LESS_THAN(@"10.0")) { - [[UIApplication sharedApplication] openURL:[NSURL URLWithString:cleverAppURLString]]; - return; - } - [[UIApplication sharedApplication] openURL:[NSURL URLWithString:cleverAppURLString] options:@{} completionHandler:nil]; - return; - } - - // Fallbacks: - // iOS 9/10 - use SFSafariViewController - // iOS 11+ - use Safari - if (SYSTEM_VERSION_LESS_THAN(@"11.0") && manager.uiDelegate) { - SFSafariViewController *svc = [[SFSafariViewController alloc] initWithURL:[NSURL URLWithString:safariURLString] entersReaderIfAvailable:NO]; - if ([manager.uiDelegate presentedViewController]) { - [manager.uiDelegate dismissViewControllerAnimated:YES completion:^{ - [manager.uiDelegate presentViewController:svc animated:YES completion:nil]; - }]; - } else { - [manager.uiDelegate presentViewController:svc animated:YES completion:nil]; - } - return; - } - - // Use old openURL method if on iOS 9- - if (SYSTEM_VERSION_LESS_THAN(@"10.0")) { - [[UIApplication sharedApplication] openURL:[NSURL URLWithString:safariURLString]]; - return; - } - [[UIApplication sharedApplication] openURL:[NSURL URLWithString:safariURLString] options:@{} completionHandler:nil]; -} - -+ (BOOL)handleURL:(NSURL *)url sourceApplication:(NSString *)sourceApplication annotation:(id)annotation { - // check if it's the Clever redirect URI first - if (![url.scheme isEqualToString:[NSString stringWithFormat:@"clever-%@", [self clientId]]]) { - // not a Clever redirect URL, so exit - return NO; - } - NSString *query = url.query; - NSMutableDictionary *kvpairs = [NSMutableDictionary dictionaryWithCapacity:1]; - NSArray *components = [query componentsSeparatedByString:@"&"]; - for (NSString *component in components) { - NSArray *kv = [component componentsSeparatedByString:@"="]; - kvpairs[kv[0]] = kv[1]; - } - - // if code is missing, then this is a Clever Portal initiated login, and we should kick off the Oauth flow - NSString *code = kvpairs[@"code"]; - if (!code) { - CLVOAuthManager* manager = [self sharedManager]; - if (manager.alreadyMissedCode) { - manager.alreadyMissedCode = NO; - manager.errorMessage = [NSString localizedStringWithFormat:@"Authorization failed. Please try logging in again."]; - [self callFailureHandler]; - return YES; - } - manager.alreadyMissedCode = YES; - [self login]; - return YES; - } - - NSString *state = kvpairs[@"state"]; - if (![state isEqualToString:[self state]]) { - // If state doesn't match, return failure - CLVOAuthManager *manager = [self sharedManager]; - manager.errorMessage = @"Authorization failed. Please try logging in again."; - [self callFailureHandler]; - return YES; - } - - AFHTTPSessionManager *tokens = [[AFHTTPSessionManager alloc] initWithBaseURL:[NSURL URLWithString:@"https://clever.com"]]; - [AFNetworkActivityIndicatorManager sharedManager].enabled = YES; - tokens.requestSerializer = [AFJSONRequestSerializer serializer]; - tokens.responseSerializer = [AFJSONResponseSerializer serializer]; - NSString *encodedClientID = [[[NSString stringWithFormat:@"%@:", [self clientId]] dataUsingEncoding:NSUTF8StringEncoding] base64EncodedStringWithOptions:0]; - - [tokens.requestSerializer setValue:[NSString stringWithFormat:@"Basic %@", encodedClientID] forHTTPHeaderField:@"Authorization"]; - NSDictionary *parameters = @{@"code": code, @"grant_type": @"authorization_code", @"redirect_uri": [self redirectUri]}; - - [tokens POST:@"oauth/tokens" parameters:parameters progress:nil success:^(NSURLSessionDataTask *task, id responseObject) { - CLVOAuthManager *manager = [self sharedManager]; - manager.alreadyMissedCode = NO; - // verify that the client id is what we expect - if ([responseObject objectForKey:@"access_token"]) { - [self setAccessToken:responseObject[@"access_token"]]; - [self callSucessHandler]; - } else { - // if no access token was received, consider this a failure - [self callFailureHandler]; - } - } failure:^(NSURLSessionDataTask *task, NSError *error) { - CLVOAuthManager *manager = [self sharedManager]; - manager.alreadyMissedCode = NO; - manager.errorMessage = [NSString stringWithFormat:@"%@", [error localizedDescription]]; - [self callFailureHandler]; - }]; - return YES; -} - -+ (void)callSucessHandler { - CLVOAuthManager *manager = [self sharedManager]; - // iOS 8 - call success handler - // iOS 9/10 w/o native app - dismiss SFSafariViewController before calling success handler - // iOS 9/10 w/ native app - call success handler - // iOS 11+ - call success handler - if (SYSTEM_VERSION_LESS_THAN(@"11.0") - && SYSTEM_VERSION_GREATER_THAN_OR_EQUAL_TO(@"9.0") - && manager.uiDelegate - && [manager.uiDelegate presentedViewController]) { - // Must dismiss SFSafariViewController - [manager.uiDelegate dismissViewControllerAnimated:NO completion:^{ - if (manager.successHandler) { - manager.successHandler(self.accessToken); - } else if (manager.delegate) { - [manager.delegate signInToClever:[self accessToken] withError:nil]; - } - }]; - return; - } - if (manager.successHandler) { - manager.successHandler(self.accessToken); - } else if (manager.delegate) { - [manager.delegate signInToClever:[self accessToken] withError:nil]; - } -} - -+ (void)callFailureHandler { - [self logout]; - CLVOAuthManager *manager = [self sharedManager]; - // iOS 8 - call success handler - // iOS 9/10 w/o native app - dismiss SFSafariViewController before calling success handler - // iOS 9/10 w/ native app - call success handler - // iOS 11+ - call success handler - if (SYSTEM_VERSION_LESS_THAN(@"11.0") - && SYSTEM_VERSION_GREATER_THAN_OR_EQUAL_TO(@"9.0") - && manager.uiDelegate - && [manager.uiDelegate presentedViewController]) { - // Must dismiss SFSafariViewController - [manager.uiDelegate dismissViewControllerAnimated:NO completion:^{ - if (manager.failureHandler) { - manager.failureHandler(manager.errorMessage); - } else if (manager.delegate) { - [manager.delegate signInToClever:nil withError:[manager errorMessage]]; - } - }]; - return; - } - if (manager.failureHandler) { - manager.failureHandler(manager.errorMessage); - } else if (manager.delegate) { - [manager.delegate signInToClever:nil withError:[manager errorMessage]]; - } -} - -+ (NSString *)accessToken { - CLVOAuthManager *manager = [self sharedManager]; - if (!manager.accessToken) { - // accessToken property is not set, so get it from Keychain - NSString *appIdentifier = [[NSBundle mainBundle] bundleIdentifier]; - manager.accessToken = [SAMKeychain passwordForService:CLVServiceName account:appIdentifier]; - } - return manager.accessToken; -} - -+ (void)setAccessToken:(NSString *)accessToken { - // any time accessToken property is changed, the value in the Keychain should also be updated - CLVOAuthManager *manager = [self sharedManager]; - manager.accessToken = accessToken; - NSString *appIdentifer = [[NSBundle mainBundle] bundleIdentifier]; - [SAMKeychain setPassword:accessToken forService:CLVServiceName account:appIdentifer]; -} - -+ (void)clearAccessToken { - CLVOAuthManager *manager = [self sharedManager]; - manager.accessToken = nil; - [SAMKeychain deletePasswordForService:CLVServiceName account:[[NSBundle mainBundle] bundleIdentifier]]; -} - -+ (void)clearBrowserCookies { - NSHTTPCookieStorage *storage = [NSHTTPCookieStorage sharedHTTPCookieStorage]; - for (NSHTTPCookie *cookie in [storage cookies]) { - [storage deleteCookie:cookie]; - } - [[NSUserDefaults standardUserDefaults] synchronize]; -} - -+ (void)logout { - [self clearBrowserCookies]; - [self clearAccessToken]; -} - -@end diff --git a/CleverSDK/CleverLoginButton.h b/CleverSDK/CleverLoginButton.h new file mode 100755 index 0000000..7f0fc63 --- /dev/null +++ b/CleverSDK/CleverLoginButton.h @@ -0,0 +1,11 @@ +#import + +@interface CleverLoginButton : UIButton + ++ (CleverLoginButton *)createLoginButton; + +- (void)setOrigin:(CGPoint)origin; + +- (void)setWidth:(CGFloat)width; + +@end diff --git a/CleverSDK/CLVLoginButton.m b/CleverSDK/CleverLoginButton.m similarity index 79% rename from CleverSDK/CLVLoginButton.m rename to CleverSDK/CleverLoginButton.m index 38b6b24..b50853f 100755 --- a/CleverSDK/CLVLoginButton.m +++ b/CleverSDK/CleverLoginButton.m @@ -1,19 +1,10 @@ -// -// CLVLoginButton.m -// CleverSDK -// -// Created by Nikhil Pandit on 4/3/15. -// Copyright (c) 2015 Clever, Inc. All rights reserved. -// - -#import "CLVLoginButton.h" -#import "CLVOAuthManager.h" +#import "CleverSDK.h" #import -const CGFloat CLVLoginButtonBaseWidth = 248.0; -const CGFloat CLVLoginButtonBaseHeight = 44.0; +const CGFloat CleverLoginButtonBaseWidth = 240.0; +const CGFloat CleverLoginButtonBaseHeight = 52.0; -@interface CLVLoginButton () +@interface CleverLoginButton () @property (nonatomic, strong) UIImageView *textImage; @@ -21,23 +12,27 @@ @interface CLVLoginButton () @end -@implementation CLVLoginButton +@implementation CleverLoginButton -+ (CLVLoginButton *)createLoginButton { - CLVLoginButton *button = [CLVLoginButton buttonWithType:UIButtonTypeCustom]; - button.frame = CGRectMake(0, 0, CLVLoginButtonBaseWidth, CLVLoginButtonBaseHeight); ++ (CleverLoginButton *)createLoginButton { + CleverLoginButton *button = [CleverLoginButton buttonWithType:UIButtonTypeCustom]; + button.frame = CGRectMake(0, 0, CleverLoginButtonBaseWidth, CleverLoginButtonBaseHeight); - UIImage *bgImage = [CLVLoginButton backgroundImageForButton]; + UIImage *bgImage = [CleverLoginButton backgroundImageForButton]; [button setBackgroundImage:bgImage forState:UIControlStateNormal]; - [button setImage:[CLVLoginButton cleverIconWithSize:button.bounds.size] forState:UIControlStateNormal]; + [button setImage:[CleverLoginButton cleverIconWithSize:button.bounds.size] forState:UIControlStateNormal]; [button setContentHorizontalAlignment:UIControlContentHorizontalAlignmentLeft]; + [button.layer setShadowColor:UIColor.blackColor.CGColor]; + [button.layer setShadowOpacity:0.25]; + [button.layer setShadowRadius:4.0]; + [button.layer setShadowOffset:CGSizeMake(-1.0, 1.0)]; - button.textImage = [[UIImageView alloc] initWithImage:[CLVLoginButton textForButton:button.frame.size]]; + button.textImage = [[UIImageView alloc] initWithImage:[CleverLoginButton textForButton:button.frame.size]]; button.textImage.frame = button.bounds; [button addSubview:button.textImage]; [button addTarget:button action:@selector(loginButtonPressed:) forControlEvents:UIControlEventTouchUpInside]; - + return button; } @@ -53,17 +48,17 @@ - (void)setWidth:(CGFloat)width { self.frame = frame; // need to adjust the text too - self.textImage.image = [CLVLoginButton textForButton:self.frame.size]; + self.textImage.image = [CleverLoginButton textForButton:self.frame.size]; self.textImage.frame = self.bounds; } - (void)loginButtonPressed:(id)loginButton { - [CLVOAuthManager login]; + [CleverSDK login]; } + (UIImage *)backgroundImageForButton { - UIColor *color = [UIColor colorWithRed:0.18 green:0.40 blue:0.66 alpha:1.0]; - CGFloat cornerRadius = 3.0; + UIColor *color = [UIColor colorWithRed:(100.0/255.0) green:(134.0/255.0) blue:(248.0/255.0) alpha:1.0]; + CGFloat cornerRadius = 4.0; CGFloat scale = [UIScreen mainScreen].scale; CGFloat size = 1.0 + 2 * cornerRadius; @@ -89,18 +84,21 @@ + (UIImage *)backgroundImageForButton { return [image stretchableImageWithLeftCapWidth:cornerRadius topCapHeight:cornerRadius]; } + + (UIImage *)cleverIconWithSize:(CGSize)size { CGFloat scale = [UIScreen mainScreen].scale; UIGraphicsBeginImageContextWithOptions(size, NO, scale); CGContextRef context = UIGraphicsGetCurrentContext(); // the Clever C - CGPathRef cleverC = [PocketSVG pathFromSVGString:@"d=\"M24,0H3C1.343,0,0,1.343,0,3v21c0,1.656,1.343,3,3,3h21c1.656,0,3-1.344,3-3V3C27,1.343,25.656,0,24,0z M14.559,23.7C8.5,23.7,4,19.307,4,13.755V13.7c0-5.497,4.412-10,10.735-10c3.882,0,6.206,1.216,8.118,2.983l-2.883,3.122c-1.588-1.354-3.206-2.182-5.265-2.182c-3.471,0-5.971,2.707-5.971,6.022V13.7c0,3.314,2.441,6.078,5.971,6.078c2.353,0,3.794-0.885,5.411-2.266L23,20.247C20.883,22.374,18.529,23.7,14.559,23.7zz\""]; + CGPathRef cleverC = [PocketSVG pathFromSVGString:@"d=\"M10.559,20.7 C4.5,20.7 0,16.307 0,10.755 L0,10.7 C0,5.203 4.412,0.7 10.735,0.7 C14.617,0.7 16.941,1.916 18.853,3.683 L15.97,6.805 C14.382,5.451 12.764,4.623 10.705,4.623 C7.234,4.623 4.734,7.33 4.734,10.645 L4.734,10.7 C4.734,14.014 7.175,16.778 10.705,16.778 C13.058,16.778 14.499,15.893 16.116,14.512 L19,17.247 C16.883,19.374 14.529,20.7 10.559,20.7 Z\""]; - CGAffineTransform moveTransform = CGAffineTransformMakeTranslation(10, 8); + CGAffineTransform moveTransform = CGAffineTransformMakeTranslation(14, 16); cleverC = CGPathCreateCopyByTransformingPath(cleverC, &moveTransform); + CGContextAddRect(context, CGRectMake(46.0, 8.0, 0.5, size.height - 16.0)); CGContextAddPath(context, cleverC); + CGContextSetFillColorWithColor(context, [UIColor whiteColor].CGColor); CGContextFillPath(context); CGPathRelease(cleverC); @@ -118,8 +116,8 @@ + (UIImage *)textForButton:(CGSize)size { // "Login with Clever" CGPathRef cleverText = [PocketSVG pathFromSVGString:@"d=\"M43.923,7.019h-2.86v13.343h8.502v-2.501h-5.642V7.019z M55.684,10.459c-3.181,0-5.101,2.32-5.101,5.061s1.92,5.082,5.101,5.082c3.201,0,5.121-2.341,5.121-5.082S58.885,10.459,55.684,10.459z M55.684,18.34c-1.58,0-2.46-1.299-2.46-2.82c0-1.5,0.88-2.801,2.46-2.801c1.601,0,2.48,1.3,2.48,2.801C58.165,17.042,57.285,18.34,55.684,18.34z M69.345,11.94c-0.78-1-1.84-1.48-3.001-1.48c-2.44,0-4.261,1.76-4.261,4.921c0,3.221,1.86,4.901,4.261,4.901c1.201,0,2.241-0.541,3.001-1.5v0.939c0,1.961-1.46,2.48-2.681,2.48c-1.2,0-2.241-0.34-3.021-1.18l-1.141,1.82c1.221,1.061,2.521,1.439,4.161,1.439c2.38,0,5.221-0.899,5.221-4.561v-9.022h-2.541V11.94z M69.345,16.941c-0.44,0.62-1.36,1.101-2.181,1.101c-1.46,0-2.46-1-2.46-2.661c0-1.66,1-2.661,2.46-2.661c0.82,0,1.741,0.46,2.181,1.081V16.941zM80.803,6.639c-0.82,0-1.5,0.66-1.5,1.5c0,0.84,0.68,1.52,1.5,1.52c0.84,0,1.52-0.68,1.52-1.52C82.323,7.299,81.643,6.639,80.803,6.639z M79.543,20.362h2.541v-9.663h-2.541V20.362z M90.524,10.459c-1.561,0-2.761,0.76-3.381,1.48v-1.241h-2.541v9.663h2.541V13.84c0.44-0.561,1.2-1.121,2.201-1.121c1.08,0,1.78,0.46,1.78,1.8v5.842h2.561V13.54C93.685,11.66,92.665,10.459,90.524,10.459z M111.004,17.201l-2.12-6.501h-2.261l-2.121,6.501l-1.8-6.501h-2.641l2.94,9.663h2.721l2.04-6.582l2.041,6.582h2.721l2.94-9.663h-2.66L111.004,17.201z M116.722,20.362h2.54v-9.663h-2.54V20.362z M117.982,6.639c-0.819,0-1.5,0.66-1.5,1.5c0,0.84,0.681,1.52,1.5,1.52c0.841,0,1.521-0.68,1.521-1.52C119.502,7.299,118.823,6.639,117.982,6.639z M125.723,18.34c-0.561,0-0.881-0.459-0.881-1.08v-4.34h1.961v-2.221h-1.961V8.059h-2.54v2.641h-1.601v2.221h1.601v5.021c0,1.74,0.939,2.661,2.721,2.661c1.06,0,1.74-0.28,2.12-0.621l-0.54-1.939C126.462,18.202,126.103,18.34,125.723,18.34z M134.483,10.459c-1.58,0-2.761,0.76-3.381,1.48V7.019h-2.561v13.343h2.561V13.84c0.42-0.561,1.2-1.121,2.201-1.121c1.08,0,1.78,0.42,1.78,1.76v5.882h2.54V13.5C137.624,11.62,136.604,10.459,134.483,10.459zM151.823,9.319c1.36,0,2.561,0.86,3.12,1.94l2.441-1.2c-0.94-1.68-2.641-3.26-5.562-3.26c-4.021,0-7.122,2.78-7.122,6.901c0,4.101,3.102,6.902,7.122,6.902c2.921,0,4.621-1.621,5.562-3.281l-2.441-1.18c-0.56,1.081-1.76,1.94-3.12,1.94c-2.44,0-4.201-1.86-4.201-4.38S149.382,9.319,151.823,9.319z M159.021,20.362h2.541V7.019h-2.541V20.362z M168.462,10.459c-2.921,0-5.001,2.26-5.001,5.061c0,3.101,2.22,5.082,5.16,5.082c1.501,0,3.021-0.461,3.981-1.341l-1.141-1.681c-0.62,0.601-1.74,0.94-2.561,0.94c-1.64,0-2.601-0.979-2.78-2.16h7.182v-0.6C173.303,12.62,171.363,10.459,168.462,10.459zM166.101,14.6c0.101-0.96,0.78-2.06,2.361-2.06c1.68,0,2.32,1.14,2.4,2.06H166.101z M179.001,17.42l-2.521-6.721h-2.721l3.881,9.663h2.741l3.881-9.663h-2.721L179.001,17.42z M189.782,10.459c-2.921,0-5.001,2.26-5.001,5.061c0,3.101,2.22,5.082,5.161,5.082c1.5,0,3.021-0.461,3.98-1.341l-1.141-1.681c-0.62,0.601-1.74,0.94-2.561,0.94c-1.64,0-2.6-0.979-2.78-2.16h7.182v-0.6C194.623,12.62,192.682,10.459,189.782,10.459z M187.421,14.6c0.1-0.96,0.78-2.06,2.36-2.06c1.681,0,2.32,1.14,2.4,2.06H187.421z M199.001,12v-1.3h-2.541v9.663h2.541V13.98c0.42-0.62,1.54-1.1,2.38-1.1c0.301,0,0.521,0.02,0.7,0.06v-2.48C200.881,10.459,199.702,11.16,199.001,12z\""]; - CGFloat moveX = ((size.width - CLVLoginButtonBaseWidth) / 2) + 15; - CGFloat moveY = 8; + CGFloat moveX = ((size.width - CleverLoginButtonBaseWidth) / 2) + 20; + CGFloat moveY = 12; CGAffineTransform moveTransform = CGAffineTransformMakeTranslation(moveX, moveY); cleverText = CGPathCreateCopyByTransformingPath(cleverText, &moveTransform); diff --git a/CleverSDK/CleverSDK.h b/CleverSDK/CleverSDK.h new file mode 100755 index 0000000..7b16cb7 --- /dev/null +++ b/CleverSDK/CleverSDK.h @@ -0,0 +1,18 @@ +#import +#import "CleverLoginButton.h" + +#define SDK_VERSION @"iOS-2.0.0" + +@interface CleverSDK : NSObject + ++ (void)startWithClientId:(NSString *)clientId LegacyIosClientId:(NSString *)legacyIosClientId RedirectURI:(NSString *)redirectUri successHandler:(void (^)(NSString *code, BOOL validState))successHandler failureHandler:(void (^)(NSString *errorMessage))failureHandler; + ++ (void)startWithClientId:(NSString *)clientId RedirectURI:(NSString *)redirectUri successHandler:(void (^)(NSString *code, BOOL validState))successHandler failureHandler:(void (^)(NSString *errorMessage))failureHandler; + ++ (BOOL)handleURL:(NSURL *)url; + ++ (void)login; + ++ (void)loginWithDistrictId:(NSString *)districtId; + +@end diff --git a/CleverSDK/CleverSDK.m b/CleverSDK/CleverSDK.m new file mode 100755 index 0000000..3f5015e --- /dev/null +++ b/CleverSDK/CleverSDK.m @@ -0,0 +1,141 @@ +#import "CleverSDK.h" + +@interface CleverSDK () + +@property (nonatomic, strong) NSString *clientId; +@property (nonatomic, strong) NSString *legacyIosClientId; +@property (nonatomic, strong) NSString *redirectUri; + +@property (nonatomic, strong) NSString *state; +@property (atomic, assign) BOOL alreadyMissedCode; + +@property (nonatomic, copy) void (^successHandler)(NSString *, BOOL); +@property (nonatomic, copy) void (^failureHandler)(NSString *); + ++ (instancetype)sharedManager; + +@end + +@implementation CleverSDK + ++ (instancetype)sharedManager { + static CleverSDK *_sharedManager = nil; + static dispatch_once_t onceToken; + dispatch_once(&onceToken, ^{ + _sharedManager = [[self alloc] init]; + }); + return _sharedManager; +} + ++ (void) startWithClientId:(NSString *)clientId LegacyIosClientId:(NSString *)legacyIosClientId RedirectURI:(NSString *)redirectUri successHandler:(void (^)(NSString *code, BOOL validState))successHandler failureHandler:(void (^)(NSString *errorMessage))failureHandler { + CleverSDK *manager = [self sharedManager]; + manager.clientId = clientId; + manager.alreadyMissedCode = NO; + manager.legacyIosClientId = legacyIosClientId; + manager.redirectUri = redirectUri; + manager.successHandler = successHandler; + manager.failureHandler = failureHandler; +} ++ (void)startWithClientId:(NSString *)clientId RedirectURI:(NSString *)redirectUri successHandler:(void (^)(NSString *code, BOOL validState))successHandler failureHandler:(void (^)(NSString *errorMessage))failureHandler { + [self startWithClientId:clientId LegacyIosClientId:nil RedirectURI:redirectUri successHandler:successHandler failureHandler:failureHandler]; +} + ++ (NSString *)generateRandomString:(int)length { + NSAssert(length % 2 == 0, @"Must generate random string with even length"); + NSMutableData *data = [NSMutableData dataWithLength:length / 2]; + NSAssert(SecRandomCopyBytes(kSecRandomDefault, length, [data mutableBytes]) == 0, @"Failure in SecRandomCopyBytes: %d", errno); + NSMutableString *hexString = [NSMutableString stringWithCapacity:(length)]; + const unsigned char *dataBytes = [data bytes]; + for (int i = 0; i < length / 2; ++i) + { + [hexString appendFormat:@"%02x", (unsigned int)dataBytes[i]]; + } + return [NSString stringWithString:hexString]; +} + ++ (void)login { + [self loginWithDistrictId:nil]; +} + ++ (void)loginWithDistrictId:(NSString *)districtId { + CleverSDK *manager = [self sharedManager]; + manager.state = [self generateRandomString:32]; + + NSString *legacyIosRedirectURI = nil; + if (manager.legacyIosClientId != nil) { + legacyIosRedirectURI = [NSString stringWithFormat:@"clever-%@://oauth", manager.legacyIosClientId]; + } + + NSString *webURLString = [NSString stringWithFormat:@"https://clever.com/oauth/authorize?response_type=code&client_id=%@&redirect_uri=%@&state=%@", manager.clientId, manager.redirectUri, manager.state]; + NSString *cleverAppURLString = [NSString stringWithFormat:@"com.clever://oauth/authorize?response_type=code&client_id=%@&redirect_uri=%@&state=%@&sdk_version=%@", manager.legacyIosClientId, legacyIosRedirectURI, manager.state, SDK_VERSION]; + + if (districtId != nil) { + webURLString = [NSString stringWithFormat:@"%@&district_id=%@", webURLString, districtId]; + cleverAppURLString = [NSString stringWithFormat:@"%@&district_id=%@", cleverAppURLString, districtId]; + } + + // Switch to native Clever app if possible + if ([[UIApplication sharedApplication] canOpenURL:[NSURL URLWithString:cleverAppURLString]] && manager.legacyIosClientId != nil) { + if (@available(iOS 10, *)) { + [[UIApplication sharedApplication] openURL:[NSURL URLWithString:cleverAppURLString] options:@{} completionHandler:nil]; + } else { + [[UIApplication sharedApplication] openURL:[NSURL URLWithString:cleverAppURLString]]; + } + return; + } + + if (@available(iOS 10, *)) { + [[UIApplication sharedApplication] openURL:[NSURL URLWithString:webURLString] options:@{} completionHandler:nil]; + } else { + [[UIApplication sharedApplication] openURL:[NSURL URLWithString:webURLString]]; + } +} + ++ (BOOL)handleURL:(NSURL *)url { + CleverSDK *manager = [self sharedManager]; + + NSURL *redirectURL = [NSURL URLWithString:manager.redirectUri]; + + if (!( + [url.scheme isEqualToString:[NSString stringWithFormat:@"clever-%@", manager.legacyIosClientId]] || ( + [url.scheme isEqualToString:redirectURL.scheme] && + [url.host isEqualToString:redirectURL.host] && + [url.path isEqualToString:redirectURL.path] + ))) { + return NO; + } + + NSString *query = url.query; + NSMutableDictionary *kvpairs = [NSMutableDictionary dictionaryWithCapacity:1]; + NSArray *components = [query componentsSeparatedByString:@"&"]; + for (NSString *component in components) { + NSArray *kv = [component componentsSeparatedByString:@"="]; + kvpairs[kv[0]] = kv[1]; + } + + // if code is missing, then this is a Clever Portal initiated login, and we should kick off the Oauth flow + NSString *code = kvpairs[@"code"]; + if (!code) { + CleverSDK* manager = [self sharedManager]; + if (manager.alreadyMissedCode) { + manager.alreadyMissedCode = NO; + manager.failureHandler([NSString localizedStringWithFormat:@"Authorization failed. Please try logging in again."]); + return YES; + } + manager.alreadyMissedCode = YES; + [self login]; + return YES; + } + + BOOL validState = NO; + + NSString *state = kvpairs[@"state"]; + if ([state isEqualToString:manager.state]) { + validState = YES; + } + + manager.successHandler(code, validState); + return YES; +} + +@end diff --git a/CleverSDKTests/CleverSDKTests.m b/CleverSDKTests/CleverSDKTests.m index be5722f..ae0732b 100644 --- a/CleverSDKTests/CleverSDKTests.m +++ b/CleverSDKTests/CleverSDKTests.m @@ -1,21 +1,13 @@ -// -// SimpleLoginTests.m -// SimpleLoginTests -// -// Created by Nikhil Pandit on 04/09/2015. -// Copyright (c) 2014 Nikhil Pandit. All rights reserved. -// - -#import "CLVCleverSDK.h" +#import "CleverSDK.h" SpecBegin(InitialSpecs) -describe(@"CLVLoginButton", ^{ +describe(@"CleverLoginButton", ^{ - __block CLVLoginButton *button; + __block CleverLoginButton *button; before(^{ - button = [CLVLoginButton createLoginButton]; + button = [CleverLoginButton createLoginButton]; }); it(@"can set origin", ^{ @@ -27,79 +19,13 @@ }); it(@"can set width", ^{ - expect(button.frame.size.width).to.beCloseTo(248); - expect(button.frame.size.height).to.beCloseTo(44); + expect(button.frame.size.width).to.beCloseTo(240); + expect(button.frame.size.height).to.beCloseTo(52); [button setWidth:300]; expect(button.frame.size.width).to.beCloseTo(300); - expect(button.frame.size.height).to.beCloseTo(44); - }); - -}); - -describe(@"CLVOAuthManager", ^{ - - before(^{ - [CLVOAuthManager startWithClientId:@"1234"]; - }); - - it(@"sets clientId on start", ^{ - // start is called in the before, so no need to call it here again - expect([CLVOAuthManager clientId]).to.equal(@"1234"); - }); - - // We don't set accessToken until after we post - it(@"generates a state of length 32 if no code passed in", ^{ - [CLVOAuthManager setState:@"abcd"]; - expect([CLVOAuthManager state]).to.equal(@"abcd"); - [CLVOAuthManager handleURL:[NSURL URLWithString:@"clever-1234://oauth"] sourceApplication:@"" annotation:@{}]; - expect([CLVOAuthManager state]).notTo.equal(@"abcd"); - expect([CLVOAuthManager state]).to.haveCountOf(32); + expect(button.frame.size.height).to.beCloseTo(52); }); - it(@"does not generate a state if code is passed in", ^{ - [CLVOAuthManager setState:@"abcd"]; - expect([CLVOAuthManager state]).to.equal(@"abcd"); - [CLVOAuthManager handleURL:[NSURL URLWithString:@"clever-1234://oauth?code=somecode&state=abcd"] sourceApplication:@"" annotation:@{}]; - expect([CLVOAuthManager state]).to.equal(@"abcd"); - }); - - it(@"errors if passed in state does not match stored state", ^{ - [CLVOAuthManager startWithClientId:@"1234" successHandler:^(NSString * _Nonnull accessToken) { - } failureHandler:^(NSString * _Nonnull errorMessage) { - expect(errorMessage).to.equal(@"Authorization failed. Please try logging in again."); - }]; - [CLVOAuthManager setState:@"abcd"]; - expect([CLVOAuthManager state]).to.equal(@"abcd"); - [CLVOAuthManager handleURL:[NSURL URLWithString:@"clever-1234://oauth?code=somecode&state=abcD"] sourceApplication:@"" annotation:@{}]; - [CLVOAuthManager callFailureHandler]; - }); - - it(@"clears accessToken on logout", ^{ - [CLVOAuthManager setAccessToken:@"abcd"]; - expect([CLVOAuthManager accessToken]).to.equal(@"abcd"); - [CLVOAuthManager logout]; - expect([CLVOAuthManager accessToken]).to.beNil; - }); - - // Update this test when we can mock out AFHTTPSessionManager - // We want to check the access token in success handler after token - it(@"returns access token in success handler", ^{ - [CLVOAuthManager startWithClientId:@"1234" successHandler:^(NSString * _Nonnull accessToken) { - expect(accessToken).notTo.equal(nil); - expect(accessToken).to.equal(@"access_token"); - } failureHandler:^(NSString * _Nonnull errorMessage) { - }]; - [CLVOAuthManager startWithClientId:@"1234"]; - [CLVOAuthManager handleURL:[NSURL URLWithString:@"clever-1234://oauth?code=somecode&state=abcd"] sourceApplication:@"" annotation:@{}]; - }); -}); - -describe(@"CLVApiRequest", ^{ - it(@"sets the bearer token", ^{ - [CLVOAuthManager setAccessToken:@"abcd"]; - CLVApiRequest *request = [CLVApiRequest sharedManager]; - expect(request.requestSerializer.HTTPRequestHeaders[@"Authorization"]).to.equal(@"Bearer abcd"); - }); }); SpecEnd diff --git a/Example/AppDelegate.h b/Example/AppDelegate.h new file mode 100755 index 0000000..a5a8b38 --- /dev/null +++ b/Example/AppDelegate.h @@ -0,0 +1,7 @@ +#import + +@interface AppDelegate : UIResponder + +@property (strong, nonatomic) UIWindow *window; + +@end diff --git a/Example/AppDelegate.m b/Example/AppDelegate.m new file mode 100755 index 0000000..b7baf8c --- /dev/null +++ b/Example/AppDelegate.m @@ -0,0 +1,45 @@ +#import "AppDelegate.h" +#import "LoginViewController.h" +#import "SuccessViewController.h" +#import "CleverSDK.h" + +@interface AppDelegate () + +@property (nonatomic, strong) UINavigationController* navigationController; + +@end + +@implementation AppDelegate + +- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions { + // Override point for customization after application launch. + self.window = [[UIWindow alloc] initWithFrame:[[UIScreen mainScreen] bounds]]; + LoginViewController *vc = [[LoginViewController alloc] initWithNibName:nil bundle:nil]; + self.navigationController = [[UINavigationController alloc] initWithRootViewController:vc]; + self.window.rootViewController = self.navigationController; + + // Start the CleverSDK with your client + // Do not forget to replace CLIENT_ID with your client_id + [CleverSDK startWithClientId:@"CLIENT_ID" RedirectURI:@"http://example.com" successHandler:^(NSString *code, BOOL validState) { + SuccessViewController *vc = [[SuccessViewController alloc] initWithCode:code]; + [self.navigationController popToRootViewControllerAnimated:NO]; + [self.navigationController pushViewController:vc animated:YES]; + } failureHandler:^(NSString *errorMessage) { + UIAlertController * alert = [UIAlertController + alertControllerWithTitle:@"Error" + message: errorMessage + preferredStyle:UIAlertControllerStyleAlert]; + [alert addAction:[UIAlertAction actionWithTitle:@"OK" style:UIAlertActionStyleDefault handler:nil]]; + [vc presentViewController:alert animated:YES completion:nil]; + [self.navigationController popToRootViewControllerAnimated:YES]; + return; + }]; + + [self.window makeKeyAndVisible]; + return YES; +} + +- (BOOL)application:(UIApplication *)application continueUserActivity:(nonnull NSUserActivity *)userActivity restorationHandler:(nonnull void (^)(NSArray> * _Nullable))restorationHandler { + return [CleverSDK handleURL:[userActivity webpageURL]]; +} +@end diff --git a/Example/CLVAppDelegate.h b/Example/CLVAppDelegate.h deleted file mode 100755 index 4d43c84..0000000 --- a/Example/CLVAppDelegate.h +++ /dev/null @@ -1,18 +0,0 @@ -// -// CLVAppDelegate.h -// SimpleLogin -// -// Created by Nikhil Pandit on 4/2/15. -// Copyright (c) 2015 Clever, Inc. All rights reserved. -// - -#import -#import "CLVCleverSDK.h" - -@interface CLVAppDelegate : UIResponder - -@property (strong, nonatomic) UIWindow *window; - - -@end - diff --git a/Example/CLVAppDelegate.m b/Example/CLVAppDelegate.m deleted file mode 100755 index c768aff..0000000 --- a/Example/CLVAppDelegate.m +++ /dev/null @@ -1,98 +0,0 @@ -// -// CLVAppDelegate.m -// SimpleLogin -// -// Created by Nikhil Pandit on 4/2/15. -// Copyright (c) 2015 Clever, Inc. All rights reserved. -// - -#import "CLVAppDelegate.h" -#import "CLVLoginViewController.h" -#import "CLVSuccessViewController.h" -#import "CLVCleverSDK.h" - -@interface CLVAppDelegate () - -@property (nonatomic, strong) UINavigationController* navigationController; - -@end - -@implementation CLVAppDelegate - - -- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions { - // Override point for customization after application launch. - self.window = [[UIWindow alloc] initWithFrame:[[UIScreen mainScreen] bounds]]; - CLVLoginViewController *vc = [[CLVLoginViewController alloc] initWithNibName:nil bundle:nil]; - self.navigationController = [[UINavigationController alloc] initWithRootViewController:vc]; - self.window.rootViewController = self.navigationController; - - // Start the CleverSDK with your client - // Do not forget to replace CLIENT_ID with your client_id - [CLVOAuthManager startWithClientId:@"CLIENT_ID"]; - [CLVOAuthManager setDelegate:self]; - - // Alternatively, you can initialize the CLVOAuthManager with success and failure blocks. - // If non-null blocks are used, signInToClever:withError: will not be called -// [CLVOAuthManager startWithClientId:@"CLIENT_ID" successHandler:^(NSString * _Nonnull accessToken) { -// NSLog(@"success"); -// } failureHandler:^(NSString * _Nonnull errorMessage) { -// NSLog(@"failure"); -// }]; - - // If on iOS 8, you must always set the UIDelegate, regardless of whether you use - // blocks or a delegate for completion. - [CLVOAuthManager setUIDelegate:vc]; - - [self.window makeKeyAndVisible]; - return YES; -} - -- (BOOL)application:(UIApplication *)application openURL:(NSURL *)url sourceApplication:(NSString *)sourceApplication annotation:(id)annotation { - // Clever's URL handler - return [CLVOAuthManager handleURL:url sourceApplication:sourceApplication annotation:annotation]; -} - -- (void)signInToClever:(NSString *)accessToken withError:(NSString *)error { - UINavigationController *navigationController = self.navigationController; - if (error) { - // handle failure - UIAlertView *alertView = [[UIAlertView alloc] initWithTitle:@"Error" - message:error - delegate:nil - cancelButtonTitle:@"OK" - otherButtonTitles:nil - ]; - [navigationController popToRootViewControllerAnimated:YES]; - [alertView show]; - return; - } - // success - CLVSuccessViewController *vc = [[CLVSuccessViewController alloc] initWithAccessToken:accessToken]; - [navigationController popToRootViewControllerAnimated:NO]; - [navigationController pushViewController:vc animated:YES]; -} - -- (void)applicationWillResignActive:(UIApplication *)application { - // Sent when the application is about to move from active to inactive state. This can occur for certain types of temporary interruptions (such as an incoming phone call or SMS message) or when the user quits the application and it begins the transition to the background state. - // Use this method to pause ongoing tasks, disable timers, and throttle down OpenGL ES frame rates. Games should use this method to pause the game. -} - -- (void)applicationDidEnterBackground:(UIApplication *)application { - // Use this method to release shared resources, save user data, invalidate timers, and store enough application state information to restore your application to its current state in case it is terminated later. - // If your application supports background execution, this method is called instead of applicationWillTerminate: when the user quits. -} - -- (void)applicationWillEnterForeground:(UIApplication *)application { - // Called as part of the transition from the background to the inactive state; here you can undo many of the changes made on entering the background. -} - -- (void)applicationDidBecomeActive:(UIApplication *)application { - // Restart any tasks that were paused (or not yet started) while the application was inactive. If the application was previously in the background, optionally refresh the user interface. -} - -- (void)applicationWillTerminate:(UIApplication *)application { - // Called when the application is about to terminate. Save data if appropriate. See also applicationDidEnterBackground:. -} - -@end diff --git a/Example/CLVLoginViewController.h b/Example/CLVLoginViewController.h deleted file mode 100755 index 0449b8e..0000000 --- a/Example/CLVLoginViewController.h +++ /dev/null @@ -1,13 +0,0 @@ -// -// CLVLoginViewController.h -// SimpleLogin -// -// Created by Nikhil Pandit on 4/9/15. -// Copyright (c) 2015 Clever, Inc. All rights reserved. -// - -#import - -@interface CLVLoginViewController : UIViewController - -@end diff --git a/Example/CLVLoginViewController.m b/Example/CLVLoginViewController.m deleted file mode 100755 index 7998a65..0000000 --- a/Example/CLVLoginViewController.m +++ /dev/null @@ -1,43 +0,0 @@ -// -// CLVLoginViewController.m -// SimpleLogin -// -// Created by Nikhil Pandit on 4/9/15. -// Copyright (c) 2015 Clever, Inc. All rights reserved. -// - -#import "CLVLoginViewController.h" -#import "CLVCleverSDK.h" -#import "CLVSuccessViewController.h" - -@interface CLVLoginViewController () - -@property (nonatomic, weak) IBOutlet UILabel *titleLabel; -@property (nonatomic, weak) IBOutlet UILabel *detailLabel; - -@end - -@implementation CLVLoginViewController - -- (void)viewDidLoad { - [super viewDidLoad]; - // Do any additional setup after loading the view from its nib. - - self.navigationController.navigationBar.translucent = NO; - - // Create a "Log in with Clever" button (optional) - CLVLoginButton *loginButton = [CLVLoginButton createLoginButton]; - - CGRect frame = loginButton.frame; - CGSize size = [UIScreen mainScreen].bounds.size; - [loginButton setOrigin:CGPointMake((size.width - frame.size.width) / 2, - self.detailLabel.frame.origin.y + self.detailLabel.frame.size.height + 50)]; - [self.view addSubview:loginButton]; -} - -- (void)didReceiveMemoryWarning { - [super didReceiveMemoryWarning]; - // Dispose of any resources that can be recreated. -} - -@end diff --git a/Example/CLVSuccessViewController.h b/Example/CLVSuccessViewController.h deleted file mode 100755 index 20efb2e..0000000 --- a/Example/CLVSuccessViewController.h +++ /dev/null @@ -1,15 +0,0 @@ -// -// CLVSuccessViewController.h -// SimpleLogin -// -// Created by Nikhil Pandit on 4/9/15. -// Copyright (c) 2015 Clever, Inc. All rights reserved. -// - -#import - -@interface CLVSuccessViewController : UIViewController - -- (id)initWithAccessToken:(NSString *)accessToken; - -@end diff --git a/Example/CLVSuccessViewController.m b/Example/CLVSuccessViewController.m deleted file mode 100755 index 4455fb5..0000000 --- a/Example/CLVSuccessViewController.m +++ /dev/null @@ -1,50 +0,0 @@ -// -// CLVSuccessViewController.m -// SimpleLogin -// -// Created by Nikhil Pandit on 4/9/15. -// Copyright (c) 2015 Clever, Inc. All rights reserved. -// - -#import "CLVSuccessViewController.h" -#import "CLVCleverSDK.h" - -@interface CLVSuccessViewController () - -@property (nonatomic, weak) IBOutlet UILabel *titleLabel; -@property (nonatomic, weak) IBOutlet UILabel *detailLabel; -@property (nonatomic, weak) IBOutlet UILabel *accessTokenLabel; -@property (nonatomic, weak) IBOutlet UILabel *readMoreLabel; - -@property (nonatomic, strong) NSString *accessToken; - -@end - -@implementation CLVSuccessViewController - -- (id)initWithAccessToken:(NSString *)accessToken { - self = [self initWithNibName:nil bundle:nil]; - if (self) { - self.accessToken = accessToken; - } - return self; -} - -- (void)viewDidLoad { - [super viewDidLoad]; - // Do any additional setup after loading the view from its nib. - - self.accessTokenLabel.text = self.accessToken; -} - -- (void)didReceiveMemoryWarning { - [super didReceiveMemoryWarning]; - // Dispose of any resources that can be recreated. -} - - -#pragma mark - Navigation -- (void)viewWillDisappear:(BOOL)animated { - [CLVOAuthManager logout]; -} -@end diff --git a/Example/Example.entitlements b/Example/Example.entitlements new file mode 100644 index 0000000..4a572e8 --- /dev/null +++ b/Example/Example.entitlements @@ -0,0 +1,10 @@ + + + + + com.apple.developer.associated-domains + + applinks:example.com + + + diff --git a/Example/LoginViewController.h b/Example/LoginViewController.h new file mode 100755 index 0000000..baf6c5c --- /dev/null +++ b/Example/LoginViewController.h @@ -0,0 +1,5 @@ +#import + +@interface LoginViewController : UIViewController + +@end diff --git a/Example/LoginViewController.m b/Example/LoginViewController.m new file mode 100755 index 0000000..05e8229 --- /dev/null +++ b/Example/LoginViewController.m @@ -0,0 +1,26 @@ +#import "LoginViewController.h" +#import "CleverSDK.h" + +@interface LoginViewController () + +@property (nonatomic, weak) IBOutlet UILabel *detailLabel; + +@end + +@implementation LoginViewController + +- (void)viewDidLoad { + [super viewDidLoad]; + + self.navigationController.navigationBar.translucent = NO; + + // Creates the "Log in with Clever" button + CleverLoginButton *loginButton = [CleverLoginButton createLoginButton]; + + CGRect frame = loginButton.frame; + CGSize size = [UIScreen mainScreen].bounds.size; + [loginButton setOrigin:CGPointMake((size.width - frame.size.width) / 2, self.detailLabel.frame.origin.y + self.detailLabel.frame.size.height + 50)]; + [self.view addSubview:loginButton]; +} + +@end diff --git a/Example/CLVLoginViewController.xib b/Example/LoginViewController.xib similarity index 98% rename from Example/CLVLoginViewController.xib rename to Example/LoginViewController.xib index 584af08..466f5d3 100755 --- a/Example/CLVLoginViewController.xib +++ b/Example/LoginViewController.xib @@ -9,7 +9,7 @@ - + diff --git a/Example/SuccessViewController.h b/Example/SuccessViewController.h new file mode 100755 index 0000000..1f12f70 --- /dev/null +++ b/Example/SuccessViewController.h @@ -0,0 +1,7 @@ +#import + +@interface SuccessViewController : UIViewController + +- (id)initWithCode:(NSString *)code; + +@end diff --git a/Example/SuccessViewController.m b/Example/SuccessViewController.m new file mode 100755 index 0000000..fd0c598 --- /dev/null +++ b/Example/SuccessViewController.m @@ -0,0 +1,25 @@ +#import "SuccessViewController.h" + +@interface SuccessViewController () + +@property (nonatomic, weak) IBOutlet UILabel *codeLabel; +@property (nonatomic, strong) NSString *code; + +@end + +@implementation SuccessViewController + +- (id)initWithCode:(NSString *)code { + self = [self initWithNibName:nil bundle:nil]; + if (self) { + self.code = code; + } + return self; +} + +- (void)viewDidLoad { + [super viewDidLoad]; + self.codeLabel.text = self.code; +} + +@end diff --git a/Example/CLVSuccessViewController.xib b/Example/SuccessViewController.xib similarity index 72% rename from Example/CLVSuccessViewController.xib rename to Example/SuccessViewController.xib index bd2d9c4..239d96f 100755 --- a/Example/CLVSuccessViewController.xib +++ b/Example/SuccessViewController.xib @@ -1,12 +1,17 @@ - - + + + + + - + + + - + - + @@ -15,44 +20,32 @@ - + - - + @@ -73,6 +75,7 @@ + diff --git a/Example/main.m b/Example/main.m index 7950c79..81e84cb 100644 --- a/Example/main.m +++ b/Example/main.m @@ -1,16 +1,8 @@ -// -// main.m -// Example -// -// Created by Cosmo Wolfe on 1/14/19. -// Copyright © 2019 Clever. All rights reserved. -// - #import -#import "CLVAppDelegate.h" +#import "AppDelegate.h" int main(int argc, char * argv[]) { @autoreleasepool { - return UIApplicationMain(argc, argv, nil, NSStringFromClass([CLVAppDelegate class])); + return UIApplicationMain(argc, argv, nil, NSStringFromClass([AppDelegate class])); } } diff --git a/Podfile b/Podfile index 866de03..6e5d46b 100644 --- a/Podfile +++ b/Podfile @@ -1,5 +1,5 @@ target "CleverSDK" do - platform :ios, "8.0" + platform :ios, "9.0" inhibit_all_warnings! podspec diff --git a/README.md b/README.md index bb39dcf..9f480a0 100755 --- a/README.md +++ b/README.md @@ -1,107 +1,124 @@ -# Clever iOS SDK +# CleverSDK CleverSDK is a simple iOS library that makes it easy for iOS developers to integrate Clever Instant Login into their application. -You can read more about integrating Clever Instant Login in your app [here](https://dev.clever.com/). +You can read more about integrating Clever Instant Login in your app [here](https://dev.clever.com/docs/il-native-ios). -## Usage - -### Configure your Clever application to support the iOS redirect URL. +## Installation -You can create an iOS redirect URL by going to https://apps.clever.com/partner/applications and clicking View / Edit on your application. +CleverSDK is available through [CocoaPods](https://cocoapods.org/pods/CleverSDK). +To install it, simply add the following line to your Podfile: -Click on the "Enable iOS Platform" button (contact Clever Support if option is not available). +``` +pod "CleverSDK" +``` -You will then get access to a client ID and redirect URI you can use for your iOS app. +to import the SDK into your codebase simply add the following header -You can also set a "fallback URL" where users will be redirected if they don't have your app installed. +```obj-c +#import +``` -### Configure your iOS app -Once you have the custom redirect URL, add it to your application as a custom URL scheme. -If you are not sure how to do so, follow the steps below: -1. Open your app's `Info.plist` file. -2. Look for a key named "URL types". If you don't see one, then add the key to `Info.plist`. -3. Expand "URL types" and add a row called "URL Schemes" under "URL types". -4. Expand "URL Schemes" and you will see a key called "Item 0" (if it doesn't exist, add a key named "Item 0"). Put the custom redirect URL as the value to this Key. +## Usage -For example, if your custom redirect URL is `clever-1234`, then the structure should look something like this: - +The `CleverSDK` utilizes [universal links](https://developer.apple.com/documentation/uikit/core_app/allowing_apps_and_websites_to_link_to_your_content) to open your application during the login flow. +In order to use `CleverSDK` you will first have to configure your application to support universal links. +Specifically you'll need to configure your application to handle your primary Clever redirect URI via universal links. +This means that if your users are directed to your redirect URI during the login flow (either from the Clever Portal or a [Log in with Clever button](https://dev.clever.com/docs/identity-api#section-log-in-with-clever)) your application will open and can complete the login. -Finally, add `com.clever` to your LSApplicationQueriesSchemes in your Info.plist, so you can redirect directly to the Clever app. -More information on LSApplicationQueriesSchemes can be found [here](https://developer.apple.com/library/content/documentation/General/Reference/InfoPlistKeyReference/Articles/LaunchServicesKeys.html#//apple_ref/doc/uid/TP40009250-SW14). +Once your application is configured to handle your primary redirect URI you can instantiate the `CleverSDK`. -### Sign in with Clever -Once the app configuration has been updated, add the following code to the `application:didFinishLaunchingWithOptions:` method in AppDelegate.m: ```obj-C -- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions { - // Override point for customization after application launch. - self.window = [[UIWindow alloc] initWithFrame:[[UIScreen mainScreen] bounds]]; - - // Start the CleverSDK with your client - // Do not forget to replace CLIENT_ID with your client_id - [CLVOAuthManager startWithClientId:@"CLIENT_ID" successHandler:^(NSString * _Nonnull accessToken) { - NSLog(@"success"); - } failureHandler:^(NSString * _Nonnull errorMessage) { - NSLog(@"failure"); - }]; - - // To support iOS 9/10, you must set the UIDelegate to the UIViewController - // that will be displayed when the user is logging in. - MyLoginViewController *vc = [[MyLoginViewController alloc] initWithNibName:nil bundle:nil]; - self.window.rootViewController = vc; - [CLVOAuthManager setUIDelegate:vc] - - // Alternatively, you can initialize CLVOAuthManager without success/failure blocks and instead use the delegate pattern. - // See "Delegate Pattern" below for handling completion when using the delegate pattern - // [CLVOAuthManager startWithClientId:@"CLIENT_ID"]; - // [CLVOAuthManager setDelegate:self]; -}] +[CleverSDK startWithClientId:@"YOUR_CLIENT_ID" // Your Clever client ID + RedirectURI:@"http://example.com" // A valid Clever redirect URI (that your app is configured to open with universal links) + successHandler:^(NSString *code, BOOL validState) { + // At this point your application has a code, which it should send to your backend to exchange for whatever information + // is needed to complete the login into your application. + // Additionally you're given the "validState" param which indicates that the CleverSDK initiated the login and that the + // state param was validated. The Clever SDK generates and validates the state param when it initiates a login. + // However if the login comes from the Clever Portal it will not have a state param. If your application needs extra + // guarantees that the user who is logging in is who they say they are you can start another login in the case that + // validState is false. This will result in a slower and more disruptive login experience (since users will be redirected + // back to Clever), but will provide an extra layer of security during the login flow. You can learn more about this here + // https://dev.clever.com/docs/il-design#section-protecting-against-cross-site-request-forgery-csrf + } + failureHandler:^(NSString *errorMessage) { + // If an unexpected error happened during the login you'll receive it here. + } +]; ``` -Besides the above change, you also need to add some code to handle the iOS redirect URI. -This is done by implementing the `application:openURL:sourceApplication:annotation:` method of the AppDelegate: +You'll also need to configure your application to call the `CleverSDK` when it receives a universal link. +This is done by implementing the `application:continueUserActivity:restorationHandler:` method of the AppDelegate: + ```obj-C -- (BOOL)application:(UIApplication *)application openURL:(NSURL *)url sourceApplication:(NSString *)sourceApplication annotation:(id)annotation { - // Clever's URL handler - return [CLVOAuthManager handleURL:url sourceApplication:sourceApplication annotation:annotation]; +- (BOOL)application:(UIApplication *)application continueUserActivity:(nonnull NSUserActivity *)userActivity restorationHandler:(nonnull void (^)(NSArray> * _Nullable))restorationHandler { + // handleURL returns a boolean indicating if the url was handled by Clever. If your application has other universal links you + // can check to see if Clever handled the url with this boolean, and if it's false continue to handle the url in your application. + return [CleverSDK handleURL:[userActivity webpageURL]]; } ``` +Once the `CleverSDK` is instantiated you can start a login by calling the `login` method. + +```obj-C +[CleverSDK login]; +``` + +Alternatively if you know the Clever district ID of the user before the log in you can simplify their login experience by providing it when beginning the login. + +```obj-C +[CleverSDK loginWithDistrictId:@"CLEVER_DISTRICT_ID"]; +``` + ### Log in with Clever Button -You can also set up a Log in with Clever Button. In the `UIViewController` set as the UIDelegate, add the following code to the `viewDidLoad` method: +To render a [Log in with Clever Button](https://dev.clever.com/docs/identity-api#section-log-in-with-clever) you can use the provided `CleverLoginButton` class. +In a `UIViewController` simply add the following code to the `viewDidLoad` method: + ```obj-C -// Create a "Log in with Clever" button -loginButton = [CLVLoginButton createLoginButton]; +loginButton = [CleverLoginButton createLoginButton]; [self.view addSubview:loginButton]; ``` The button is instantiated with a particular width and height. -You can update the width of the button by calling `setWidth:` method on the button: +You can update the width of the button by calling `setWidth:` method on the button and the height will be adjusted automatically to preserve the design. ```obj-C [self.loginButton setWidth:300.0]; ``` -#### Delegate Pattern -If you are using the delegate pattern instead of completion blocks, add the following method to your AppDelegate.m: +### Supporting Legacy iOS Instant Login + +Before Clever released v2.0.0 of the `CleverSDK` Instant Login on iOS was powered using custom protocol urls (such as `com.clever://oauth/authorize`), not universal links. +If your application made use of these custom urls (or the old version of the `CleverSDK`) v2.0.0 of the SDK has additional features you can use to stay backwards compatible. + +When you instantiate the SDK you should also provide the `LegacyIosClientId` client ID (this is the client ID you used specifically in your iOS app). ```obj-C -// If non-null blocks are provided, signInToClever:withError: will not be called -- (void)signInToClever:(NSString *)accessToken withError:(NSString *)error { - if (error) { - // error - } - // success -} +[CleverSDK startWithClientId:@"YOUR_CLIENT_ID" // Your Clever client ID + LegacyIosClientId:@"YOUR_IOS_SPECIFIC_LEGACY_CLIENT_ID" + RedirectURI:@"http://example.com" // A valid Clever redirect URI (that your app is configured to open with universal links) + successHandler:^(NSString *code, BOOL validState) { + // ... + } + failureHandler:^(NSString *errorMessage) { + // ... + } +]; ``` -To run the example project, clone the repo, and run `pod install` from the Example/SimpleLogin directory first. +Besides the above change, you also need to add some code to handle the iOS redirect URI. +This is done by implementing the `application:openURL:sourceApplication:annotation:` method of the AppDelegate: +```obj-C +- (BOOL)application:(UIApplication *)application openURL:(NSURL *)url options:(NSDictionary *)options { + return [CleverSDK handleURL:url]; +} +``` -## Installation +You'll also need to add some information to your `Info.plist` to support the custom URI schemes. +1. Add `com.clever` to your [LSApplicationQueriesSchemes](https://developer.apple.com/library/content/documentation/General/Reference/InfoPlistKeyReference/Articles/LaunchServicesKeys.html#//apple_ref/doc/uid/TP40009250-SW14) so you can redirect directly to the Clever app. +2. Add your custom clever redirect URI (should look like `clever-YOUR_CLIENT_ID`) to [URL types](https://developer.apple.com/documentation/uikit/core_app/allowing_apps_and_websites_to_link_to_your_content/defining_a_custom_url_scheme_for_your_app?language=objc), so the Clever app can open your application. -CleverSDK is available through [CocoaPods](http://cocoapods.org). To install -it, simply add the following line to your Podfile: +## Example -``` -pod "CleverSDK" -``` +The `CleverSDK` project comes with a simple example application to show usage of the SDK. You can view the code for this example in the [/Example](./Example) directory, or you can open the project in Xcode and run the `Example` target. ## License diff --git a/VERSION b/VERSION index 6d7de6e..227cea2 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -1.0.2 +2.0.0