diff --git a/.swift-version b/.swift-version new file mode 100644 index 0000000..9f55b2c --- /dev/null +++ b/.swift-version @@ -0,0 +1 @@ +3.0 diff --git a/README.md b/README.md index 7328b38..18efee7 100644 --- a/README.md +++ b/README.md @@ -29,7 +29,7 @@ Simplicity is maintained by [Stormpath](https://stormpath.com), an API service f ## Installation -Requires XCode 7.3+ / Swift 2.2+ +Requires XCode 8+ / Swift 3+ To install Simplicity, we use [CocoaPods](http://cocoapods.org). To install it, simply add the following line to your Podfile: @@ -45,6 +45,13 @@ To use Simplicity with [Carthage](https://github.com/Carthage/Carthage), specify github "SimplicityMobile/Simplicity" ``` +**Swift 2** + +Older versions of Simplicity support Swift 2.3 (Xcode 8) or Swift 2.2 (Xcode 7). + +* Swift 2.3 support is on branch [`swift2.3`](https://github.com/SimplicityMobile/Simplicity/tree/swift2.3) +* Swift 2.2 support is on version [`1.x`](https://github.com/SimplicityMobile/Simplicity/tree/1.0.2) + ### Add the link handlers to the AppDelegate When a user finishes their log in flow, Facebook or Google will redirect back into the app. Simplicity will listen for the access token or error. You need to add the following lines of code to `AppDelegate.swift`: @@ -52,12 +59,12 @@ When a user finishes their log in flow, Facebook or Google will redirect back in ```Swift import Simplicity -func application(app: UIApplication, openURL url: NSURL, options: [String : AnyObject]) -> Bool { - return Simplicity.application(app, openURL: url, options: options) +func application(_ app: UIApplication, open url: URL, options: [UIApplicationOpenURLOptionsKey: Any]) -> Bool { + return Simplicity.application(app, open: url, options: options) } -func application(application: UIApplication, openURL url: NSURL, sourceApplication: String?, annotation: AnyObject) -> Bool { - return Simplicity.application(application, openURL: url, sourceApplication: sourceApplication, annotation: annotation) +func application(_ application: UIApplication, open url: URL, sourceApplication: String?, annotation: Any) -> Bool { + return Simplicity.application(application, open: url, sourceApplication: sourceApplication, annotation: annotation) } ``` diff --git a/Simplicity.podspec b/Simplicity.podspec index f1767e3..1d4e934 100644 --- a/Simplicity.podspec +++ b/Simplicity.podspec @@ -5,7 +5,7 @@ Pod::Spec.new do |s| s.name = "Simplicity" -s.version = "1.0.2" +s.version = "2.0" s.summary = "A simple way to login with Facebook or Google on iOS" s.description = <<-DESC @@ -25,4 +25,4 @@ s.ios.deployment_target = '8.0' s.source_files = 'Simplicity/**/*.swift' -end \ No newline at end of file +end diff --git a/Simplicity.xcodeproj/project.pbxproj b/Simplicity.xcodeproj/project.pbxproj index 60846f8..14421cd 100644 --- a/Simplicity.xcodeproj/project.pbxproj +++ b/Simplicity.xcodeproj/project.pbxproj @@ -151,11 +151,12 @@ DF74EC271CE2A8BB008F16BF /* Project object */ = { isa = PBXProject; attributes = { - LastUpgradeCheck = 0730; + LastUpgradeCheck = 0800; ORGANIZATIONNAME = Stormpath; TargetAttributes = { DF74EC2F1CE2A8BB008F16BF = { CreatedOnToolsVersion = 7.3.1; + LastSwiftMigration = 0800; }; }; }; @@ -222,8 +223,10 @@ CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; CLANG_WARN_EMPTY_BODY = YES; CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INFINITE_RECURSION = YES; CLANG_WARN_INT_CONVERSION = YES; CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_SUSPICIOUS_MOVE = YES; CLANG_WARN_UNREACHABLE_CODE = YES; CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; @@ -251,6 +254,7 @@ ONLY_ACTIVE_ARCH = YES; SDKROOT = iphoneos; SWIFT_OPTIMIZATION_LEVEL = "-Onone"; + SWIFT_VERSION = 3.0; TARGETED_DEVICE_FAMILY = "1,2"; VERSIONING_SYSTEM = "apple-generic"; VERSION_INFO_PREFIX = ""; @@ -271,8 +275,10 @@ CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; CLANG_WARN_EMPTY_BODY = YES; CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INFINITE_RECURSION = YES; CLANG_WARN_INT_CONVERSION = YES; CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_SUSPICIOUS_MOVE = YES; CLANG_WARN_UNREACHABLE_CODE = YES; CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; @@ -292,6 +298,8 @@ IPHONEOS_DEPLOYMENT_TARGET = 9.3; MTL_ENABLE_DEBUG_INFO = NO; SDKROOT = iphoneos; + SWIFT_OPTIMIZATION_LEVEL = "-Owholemodule"; + SWIFT_VERSION = 3.0; TARGETED_DEVICE_FAMILY = "1,2"; VALIDATE_PRODUCT = YES; VERSIONING_SYSTEM = "apple-generic"; @@ -303,6 +311,7 @@ isa = XCBuildConfiguration; buildSettings = { CLANG_ENABLE_MODULES = YES; + "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = ""; DEFINES_MODULE = YES; DYLIB_COMPATIBILITY_VERSION = 1; DYLIB_CURRENT_VERSION = 1; @@ -315,6 +324,7 @@ PRODUCT_NAME = "$(TARGET_NAME)"; SKIP_INSTALL = YES; SWIFT_OPTIMIZATION_LEVEL = "-Onone"; + SWIFT_VERSION = 3.0; }; name = Debug; }; @@ -322,6 +332,7 @@ isa = XCBuildConfiguration; buildSettings = { CLANG_ENABLE_MODULES = YES; + "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = ""; DEFINES_MODULE = YES; DYLIB_COMPATIBILITY_VERSION = 1; DYLIB_CURRENT_VERSION = 1; @@ -333,6 +344,7 @@ PRODUCT_BUNDLE_IDENTIFIER = com.stormpath.Simplicity; PRODUCT_NAME = "$(TARGET_NAME)"; SKIP_INSTALL = YES; + SWIFT_VERSION = 3.0; }; name = Release; }; diff --git a/Simplicity.xcodeproj/xcshareddata/xcschemes/Simplicity.xcscheme b/Simplicity.xcodeproj/xcshareddata/xcschemes/Simplicity.xcscheme index 338c1c8..6d0e049 100644 --- a/Simplicity.xcodeproj/xcshareddata/xcschemes/Simplicity.xcscheme +++ b/Simplicity.xcodeproj/xcshareddata/xcschemes/Simplicity.xcscheme @@ -1,6 +1,6 @@ Bool) -> [String] { - guard let urlTypes = NSBundle.mainBundle().infoDictionary?["CFBundleURLTypes"] as? [[String: AnyObject]] else { + static func registeredURLSchemes(filter closure: (String) -> Bool) -> [String] { + guard let urlTypes = Bundle.main.infoDictionary?["CFBundleURLTypes"] as? [[String: AnyObject]] else { return [String]() } @@ -35,18 +35,18 @@ class Helpers { - parts: A dictionary of parameters to put in a query string. - returns: A query string */ - static func queryString(parts: [String: String?]) -> String? { + static func queryString(_ parts: [String: String?]) -> String? { return parts.flatMap { key, value -> String? in if let value = value { return key + "=" + value } else { return nil } - }.joinWithSeparator("&").stringByAddingPercentEncodingWithAllowedCharacters(NSCharacterSet.URLQueryAllowedCharacterSet()) + }.joined(separator: "&").addingPercentEncoding(withAllowedCharacters: CharacterSet.urlQueryAllowed) } } -extension NSURL { +extension URL { /// Dictionary with key/value pairs from the URL fragment var fragmentDictionary: [String: String] { return dictionaryFromFormEncodedString(fragment) @@ -65,22 +65,22 @@ extension NSURL { return result } - private func dictionaryFromFormEncodedString(input: String?) -> [String: String] { + private func dictionaryFromFormEncodedString(_ input: String?) -> [String: String] { var result = [String: String]() guard let input = input else { return result } - let inputPairs = input.componentsSeparatedByString("&") + let inputPairs = input.components(separatedBy: "&") for pair in inputPairs { - let split = pair.componentsSeparatedByString("=") + let split = pair.components(separatedBy: "=") if split.count == 2 { - if let key = split[0].stringByRemovingPercentEncoding, value = split[1].stringByRemovingPercentEncoding { + if let key = split[0].removingPercentEncoding, let value = split[1].removingPercentEncoding { result[key] = value } } } return result } -} \ No newline at end of file +} diff --git a/Simplicity/LoginError.swift b/Simplicity/LoginError.swift index e7c8110..c8cf29a 100644 --- a/Simplicity/LoginError.swift +++ b/Simplicity/LoginError.swift @@ -24,7 +24,7 @@ public class LoginError: NSError { - description: Localized description of the error. */ public init(code: Int, description: String) { - var userInfo = [String: AnyObject]() + var userInfo = [String: Any]() userInfo[NSLocalizedDescriptionKey] = description super.init(domain: "Simplicity", code: code, userInfo: userInfo) diff --git a/Simplicity/LoginProvider.swift b/Simplicity/LoginProvider.swift index dbffb67..4d2f2dc 100644 --- a/Simplicity/LoginProvider.swift +++ b/Simplicity/LoginProvider.swift @@ -13,7 +13,7 @@ import Foundation */ public protocol LoginProvider { /// The URL to redirect to when beginning the login process - var authorizationURL: NSURL { get } + var authorizationURL: URL { get } /// The URL Scheme that this LoginProvider is bound to. var urlScheme: String { get } @@ -26,11 +26,11 @@ public protocol LoginProvider { - url: The URL that triggered that AppDelegate's link handler - callback: A callback that returns with an access token or NSError. */ - func linkHandler(url: NSURL, callback: ExternalLoginCallback) + func linkHandler(_ url: URL, callback: @escaping ExternalLoginCallback) } public extension LoginProvider { - func login(callback: ExternalLoginCallback) { + func login(_ callback: @escaping ExternalLoginCallback) { Simplicity.login(self, callback: callback) } -} \ No newline at end of file +} diff --git a/Simplicity/LoginProviders/Facebook.swift b/Simplicity/LoginProviders/Facebook.swift index c4a6877..1bea523 100644 --- a/Simplicity/LoginProviders/Facebook.swift +++ b/Simplicity/LoginProviders/Facebook.swift @@ -50,12 +50,12 @@ public class Facebook: OAuth2 { // Search for URL Scheme, error if not there guard let urlScheme = Helpers.registeredURLSchemes(filter: {$0.hasPrefix("fb")}).first, - range = urlScheme.rangeOfString("\\d+", options: .RegularExpressionSearch) else { + let range = urlScheme.range(of: "\\d+", options: .regularExpression) else { preconditionFailure("You must configure your Facebook URL Scheme to use Facebook login.") } - let clientId = urlScheme.substringWithRange(range) - let authorizationEndpoint = NSURL(string: "https://www.facebook.com/dialog/oauth")! - let redirectEndpoint = NSURL(string: urlScheme + "://authorize")! + let clientId = urlScheme.substring(with: range) + let authorizationEndpoint = URL(string: "https://www.facebook.com/dialog/oauth")! + let redirectEndpoint = URL(string: urlScheme + "://authorize")! super.init(clientId: clientId, authorizationEndpoint: authorizationEndpoint, redirectEndpoint: redirectEndpoint, grantType: .Implicit) } @@ -77,4 +77,4 @@ public enum FacebookAuthType: String { /// None None = "" -} \ No newline at end of file +} diff --git a/Simplicity/LoginProviders/Google.swift b/Simplicity/LoginProviders/Google.swift index 1760401..96b36bd 100644 --- a/Simplicity/LoginProviders/Google.swift +++ b/Simplicity/LoginProviders/Google.swift @@ -42,9 +42,9 @@ public class Google: OAuth2 { preconditionFailure("You must configure your Google URL Scheme to use Google login.") } - let appId = urlScheme.componentsSeparatedByString(".").reverse().joinWithSeparator(".") - let authorizationEndpoint = NSURL(string: "https://accounts.google.com/o/oauth2/auth")! - let redirectionEndpoint = NSURL(string: "\(urlScheme):/oauth2callback")! + let appId = urlScheme.components(separatedBy: ".").reversed().joined(separator: ".") + let authorizationEndpoint = URL(string: "https://accounts.google.com/o/oauth2/auth")! + let redirectionEndpoint = URL(string: "\(urlScheme):/oauth2callback")! super.init(clientId: appId, authorizationEndpoint: authorizationEndpoint, redirectEndpoint: redirectionEndpoint, grantType: .AuthorizationCode) self.scopes = ["email", "profile"] @@ -57,37 +57,37 @@ public class Google: OAuth2 { - url: The OAuth redirect URL - callback: A callback that returns with an access token or NSError. */ - override public func linkHandler(url: NSURL, callback: ExternalLoginCallback) { - guard let authorizationCode = url.queryDictionary["code"] where url.queryDictionary["state"] == state else { + override public func linkHandler(_ url: URL, callback: @escaping ExternalLoginCallback) { + guard let authorizationCode = url.queryDictionary["code"], url.queryDictionary["state"] == state else { if let error = OAuth2Error.error(url.queryDictionary) ?? OAuth2Error.error(url.queryDictionary) { - callback(accessToken: nil, error: error) + callback(nil, error) } else { - callback(accessToken: nil, error: LoginError.InternalSDKError) + callback(nil, LoginError.InternalSDKError) } return } exchangeCodeForAccessToken(authorizationCode, callback: callback) } - private func exchangeCodeForAccessToken(authorizationCode: String, callback: ExternalLoginCallback) { - let session = NSURLSession(configuration: NSURLSessionConfiguration.ephemeralSessionConfiguration()) - let url = NSURL(string: "https://www.googleapis.com/oauth2/v4/token")! + private func exchangeCodeForAccessToken(_ authorizationCode: String, callback: @escaping ExternalLoginCallback) { + let session = URLSession(configuration: URLSessionConfiguration.ephemeral) + let url = URL(string: "https://www.googleapis.com/oauth2/v4/token")! let requestParams: [String: String?] = ["client_id": clientId, "code": authorizationCode, "grant_type": "authorization_code", "redirect_uri": authorizationURLParameters["redirect_uri"] ?? nil] - let request = NSMutableURLRequest(URL: url) - request.HTTPMethod = "POST" - request.HTTPBody = Helpers.queryString(requestParams)?.dataUsingEncoding(NSUTF8StringEncoding) + var request = URLRequest(url: url) + request.httpMethod = "POST" + request.httpBody = Helpers.queryString(requestParams)?.data(using: String.Encoding.utf8) - let task = session.dataTaskWithRequest(request) { (data, response, error) -> Void in - guard let data = data, json = try? NSJSONSerialization.JSONObjectWithData(data, options: []), accessToken = json["access_token"] as? String else { - callback(accessToken: nil, error: LoginError.InternalSDKError) // This request should not fail. + let task = session.dataTask(with: request) { (data, response, error) -> Void in + guard let data = data, let json = (try? JSONSerialization.jsonObject(with: data, options: [])) as? [String: Any], let accessToken = json["access_token"] as? String else { + callback(nil, LoginError.InternalSDKError) // This request should not fail. return } - callback(accessToken: accessToken, error: nil) + callback(accessToken, nil) } task.resume() } diff --git a/Simplicity/LoginProviders/VKontakte.swift b/Simplicity/LoginProviders/VKontakte.swift index 12a95e4..997c65b 100644 --- a/Simplicity/LoginProviders/VKontakte.swift +++ b/Simplicity/LoginProviders/VKontakte.swift @@ -30,12 +30,12 @@ public class VKontakte: OAuth2 { public init() { guard let urlScheme = Helpers.registeredURLSchemes(filter: {$0.hasPrefix("vk")}).first, - range = urlScheme.rangeOfString("\\d+", options: .RegularExpressionSearch) else { + let range = urlScheme.range(of: "\\d+", options: .regularExpression) else { preconditionFailure("You must configure your VK URL Scheme to use VK login.") } - let clientId = urlScheme.substringWithRange(range) - let authorizationEndpoint = NSURL(string: "https://oauth.vk.com/authorize")! - let redirectEndpoint = NSURL(string: urlScheme + "://authorize")! + let clientId = urlScheme.substring(with: range) + let authorizationEndpoint = URL(string: "https://oauth.vk.com/authorize")! + let redirectEndpoint = URL(string: urlScheme + "://authorize")! super.init(clientId: clientId, authorizationEndpoint: authorizationEndpoint, redirectEndpoint: redirectEndpoint, grantType: .Implicit) } diff --git a/Simplicity/OAuth2.swift b/Simplicity/OAuth2.swift index 54a043e..f6d3e47 100644 --- a/Simplicity/OAuth2.swift +++ b/Simplicity/OAuth2.swift @@ -23,7 +23,7 @@ public class OAuth2: LoginProvider { /// The URL Scheme registered by the app. public final var urlScheme: String { - return redirectEndpoint.scheme + return redirectEndpoint.scheme! } /// The state used to prevent CSRF attacks with bad access tokens. @@ -36,10 +36,10 @@ public class OAuth2: LoginProvider { public final let grantType: OAuth2GrantType /// The OAuth 2 authorization endpoint - public final let authorizationEndpoint: NSURL + public final let authorizationEndpoint: URL /// The OAuth 2 redirection endpoint - public final let redirectEndpoint: NSURL + public final let redirectEndpoint: URL /** An array with query string parameters for the authorization URL. @@ -52,23 +52,23 @@ public class OAuth2: LoginProvider { return ["client_id": clientId, "redirect_uri": redirectEndpoint.absoluteString, "response_type": grantType.rawValue, - "scope": scopes.joinWithSeparator(" "), + "scope": scopes.joined(separator: " "), "state": state] } /// The authorization URL to start the OAuth flow. - public var authorizationURL: NSURL { + public var authorizationURL: URL { guard grantType != .Custom else { preconditionFailure("Custom Grant Type Not Supported") } - let url = NSURLComponents(URL: authorizationEndpoint, resolvingAgainstBaseURL: false)! + var url = URLComponents(url: authorizationEndpoint, resolvingAgainstBaseURL: false)! - url.queryItems = authorizationURLParameters.flatMap({key, value -> NSURLQueryItem? in - return value != nil ? NSURLQueryItem(name: key, value: value) : nil + url.queryItems = authorizationURLParameters.flatMap({key, value -> URLQueryItem? in + return value != nil ? URLQueryItem(name: key, value: value) : nil }) - return url.URL! + return url.url! } /** @@ -78,27 +78,27 @@ public class OAuth2: LoginProvider { - url: The OAuth redirect URL - callback: A callback that returns with an access token or NSError. */ - public func linkHandler(url: NSURL, callback: ExternalLoginCallback) { + public func linkHandler(_ url: URL, callback: @escaping ExternalLoginCallback) { switch grantType { case .AuthorizationCode: preconditionFailure("Authorization Code Grant Type Not Supported") case .Implicit: // Get the access token, and check that the state is the same - guard let accessToken = url.fragmentDictionary["access_token"] where url.fragmentAndQueryDictionary["state"] == state else { + guard let accessToken = url.fragmentDictionary["access_token"], url.fragmentAndQueryDictionary["state"] == state else { /** Facebook's mobile implicit grant type returns errors as query. Don't think it's a huge issue to be liberal in looking for errors, so will check both. */ if let error = OAuth2Error.error(url.fragmentAndQueryDictionary) { - callback(accessToken: nil, error: error) + callback(nil, error) } else { - callback(accessToken: nil, error: LoginError.InternalSDKError) + callback(nil, LoginError.InternalSDKError) } return } - callback(accessToken: accessToken, error: nil) + callback(accessToken, nil) case .Custom: preconditionFailure("Custom Grant Type Not Supported") } @@ -114,7 +114,7 @@ public class OAuth2: LoginProvider { - redirectEndpoint: The redirect URI passed to the provider. - grantType: The OAuth 2 Grant Type */ - public init(clientId: String, authorizationEndpoint: NSURL, redirectEndpoint: NSURL, grantType: OAuth2GrantType) { + public init(clientId: String, authorizationEndpoint: URL, redirectEndpoint: URL, grantType: OAuth2GrantType) { self.grantType = grantType self.clientId = clientId self.authorizationEndpoint = authorizationEndpoint @@ -136,4 +136,4 @@ public enum OAuth2GrantType: String { /// Custom Grant Type Custom = "" -} \ No newline at end of file +} diff --git a/Simplicity/OAuth2Error.swift b/Simplicity/OAuth2Error.swift index fe7397a..a6d6eef 100644 --- a/Simplicity/OAuth2Error.swift +++ b/Simplicity/OAuth2Error.swift @@ -15,13 +15,13 @@ import Foundation */ public class OAuth2Error: LoginError { /// A mapping of OAuth 2 Error strings to OAuth2ErrorCode enum. - public static let mapping: [String: OAuth2ErrorCode] = [ "invalid_request": .InvalidRequest, - "unauthorized_client": .UnauthorizedClient, - "access_denied": .AccessDenied, - "unsupported_response_type": .UnsupportedResponseType, - "invalid_scope": .InvalidScope, - "server_error": .ServerError, - "temporarily_unavailable": .TemporarilyUnavailable ] + public static let mapping: [String: OAuth2ErrorCode] = [ "invalid_request": .invalidRequest, + "unauthorized_client": .unauthorizedClient, + "access_denied": .accessDenied, + "unsupported_response_type": .unsupportedResponseType, + "invalid_scope": .invalidScope, + "server_error": .serverError, + "temporarily_unavailable": .temporarilyUnavailable ] /** Constructs a OAuth 2 error object from an OAuth response. @@ -30,11 +30,11 @@ public class OAuth2Error: LoginError { - callbackParameters: A dictionary of OAuth 2 Error response parameters. - returns: OAuth2Error object. */ - public class func error(callbackParameters: [String: String]) -> LoginError? { + public class func error(_ callbackParameters: [String: String]) -> LoginError? { let errorCode = mapping[callbackParameters["error"] ?? ""] if let errorCode = errorCode { - let errorDescription = callbackParameters["error_description"]?.stringByRemovingPercentEncoding?.stringByReplacingOccurrencesOfString("+", withString: " ") ?? errorCode.description + let errorDescription = callbackParameters["error_description"]?.removingPercentEncoding?.replacingOccurrences(of: "+", with: " ") ?? errorCode.description return OAuth2Error(code: errorCode.rawValue, description: errorDescription) } else { @@ -49,43 +49,43 @@ public enum OAuth2ErrorCode: Int, CustomStringConvertible { The request is missing a required parameter. This is usually programmer error, and should be filed as a GitHub issue. */ - case InvalidRequest = 100, + case invalidRequest = 100, /// The client ID is not authorized to make this request. - UnauthorizedClient, + unauthorizedClient, /// The user or OAuth server denied this request. - AccessDenied, + accessDenied, /// The grant type requested is not supported. This is programmer error. - UnsupportedResponseType, + unsupportedResponseType, /// A scope requested is invalid. - InvalidScope, + invalidScope, /// The authorization server is currently experiencing an error. - ServerError, + serverError, /// The authorization server is currently unavailable. - TemporarilyUnavailable + temporarilyUnavailable /// User readable default error message public var description: String { switch self { - case .InvalidRequest: + case .invalidRequest: return "The OAuth request is missing a required parameter" - case .UnauthorizedClient: + case .unauthorizedClient: return "The client ID is not authorized to make this request" - case .AccessDenied: + case .accessDenied: return "You denied the login request" - case .UnsupportedResponseType: + case .unsupportedResponseType: return "The grant type requested is not supported" - case .InvalidScope: + case .invalidScope: return "A scope requested is invalid" - case .ServerError: + case .serverError: return "The login server experienced an internal error" - case .TemporarilyUnavailable: + case .temporarilyUnavailable: return "The login server is temporarily unavailable. Please try again later. " } } -} \ No newline at end of file +} diff --git a/Simplicity/Simplicity.swift b/Simplicity/Simplicity.swift index 07c5531..5aa6313 100644 --- a/Simplicity/Simplicity.swift +++ b/Simplicity/Simplicity.swift @@ -10,7 +10,7 @@ import UIKit import SafariServices /// Callback handler after an external login completes. -public typealias ExternalLoginCallback = (accessToken: String?, error: NSError?) -> Void +public typealias ExternalLoginCallback = (String?, NSError?) -> Void /** Simplicity is a framework for authenticating with external providers on iOS. @@ -27,7 +27,7 @@ public final class Simplicity { - loginProvider: The login provider object configured to be used. - callback: A callback with the access token, or a SimplicityError. */ - public static func login(loginProvider: LoginProvider, callback: ExternalLoginCallback) { + public static func login(_ loginProvider: LoginProvider, callback: @escaping ExternalLoginCallback) { self.currentLoginProvider = loginProvider self.callback = callback @@ -35,9 +35,9 @@ public final class Simplicity { } /// Deep link handler (iOS9) - public static func application(app: UIApplication, openURL url: NSURL, options: [String : AnyObject]) -> Bool { - safari?.dismissViewControllerAnimated(true, completion: nil) - guard let callback = callback where url.scheme == currentLoginProvider?.urlScheme else { + public static func application(_ app: UIApplication, open url: URL, options: [UIApplicationOpenURLOptionsKey: Any]) -> Bool { + safari?.dismiss(animated: true, completion: nil) + guard let callback = callback, url.scheme == currentLoginProvider?.urlScheme else { return false } currentLoginProvider?.linkHandler(url, callback: callback) @@ -47,16 +47,16 @@ public final class Simplicity { } /// Deep link handler ( Bool { - return self.application(application, openURL: url, options: [String: AnyObject]()) + public static func application(_ application: UIApplication, open url: URL, sourceApplication: String?, annotation: Any) -> Bool { + return self.application(application, open: url, options: [UIApplicationOpenURLOptionsKey: Any]()) } - private static func presentSafariView(url: NSURL) { + private static func presentSafariView(_ url: URL) { if #available(iOS 9, *) { - safari = SFSafariViewController(URL: url) - UIApplication.sharedApplication().delegate?.window??.rootViewController?.presentViewController(safari!, animated: true, completion: nil) + safari = SFSafariViewController(url: url) + UIApplication.shared.delegate?.window??.rootViewController?.present(safari!, animated: true, completion: nil) } else { - UIApplication.sharedApplication().openURL(url) + UIApplication.shared.openURL(url) } } -} \ No newline at end of file +}