Skip to content

Commit

Permalink
Merge pull request #19 from SimplicityMobile/swift3
Browse files Browse the repository at this point in the history
Swift 3 Support
  • Loading branch information
edjiang authored Sep 13, 2016
2 parents 29969c6 + e66a1d4 commit a85b51b
Show file tree
Hide file tree
Showing 14 changed files with 122 additions and 102 deletions.
1 change: 1 addition & 0 deletions .swift-version
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
3.0
17 changes: 12 additions & 5 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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:

Expand All @@ -45,19 +45,26 @@ 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`:

```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)
}
```

Expand Down
4 changes: 2 additions & 2 deletions Simplicity.podspec
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -25,4 +25,4 @@ s.ios.deployment_target = '8.0'

s.source_files = 'Simplicity/**/*.swift'

end
end
14 changes: 13 additions & 1 deletion Simplicity.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -151,11 +151,12 @@
DF74EC271CE2A8BB008F16BF /* Project object */ = {
isa = PBXProject;
attributes = {
LastUpgradeCheck = 0730;
LastUpgradeCheck = 0800;
ORGANIZATIONNAME = Stormpath;
TargetAttributes = {
DF74EC2F1CE2A8BB008F16BF = {
CreatedOnToolsVersion = 7.3.1;
LastSwiftMigration = 0800;
};
};
};
Expand Down Expand Up @@ -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";
Expand Down Expand Up @@ -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 = "";
Expand All @@ -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";
Expand All @@ -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";
Expand All @@ -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;
Expand All @@ -315,13 +324,15 @@
PRODUCT_NAME = "$(TARGET_NAME)";
SKIP_INSTALL = YES;
SWIFT_OPTIMIZATION_LEVEL = "-Onone";
SWIFT_VERSION = 3.0;
};
name = Debug;
};
DF74EC3A1CE2A8BB008F16BF /* Release */ = {
isa = XCBuildConfiguration;
buildSettings = {
CLANG_ENABLE_MODULES = YES;
"CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "";
DEFINES_MODULE = YES;
DYLIB_COMPATIBILITY_VERSION = 1;
DYLIB_CURRENT_VERSION = 1;
Expand All @@ -333,6 +344,7 @@
PRODUCT_BUNDLE_IDENTIFIER = com.stormpath.Simplicity;
PRODUCT_NAME = "$(TARGET_NAME)";
SKIP_INSTALL = YES;
SWIFT_VERSION = 3.0;
};
name = Release;
};
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<Scheme
LastUpgradeVersion = "0730"
LastUpgradeVersion = "0800"
version = "1.3">
<BuildAction
parallelizeBuildables = "YES"
Expand Down
20 changes: 10 additions & 10 deletions Simplicity/Helpers.swift
Original file line number Diff line number Diff line change
Expand Up @@ -17,8 +17,8 @@ class Helpers {
Scheme matches the filter
- returns: A list of URL Schemes that match the filter closure.
*/
static func registeredURLSchemes(filter closure: String -> 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]()
}

Expand All @@ -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)
Expand All @@ -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
}
}
}
2 changes: 1 addition & 1 deletion Simplicity/LoginError.swift
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down
8 changes: 4 additions & 4 deletions Simplicity/LoginProvider.swift
Original file line number Diff line number Diff line change
Expand Up @@ -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 }
Expand All @@ -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)
}
}
}
10 changes: 5 additions & 5 deletions Simplicity/LoginProviders/Facebook.swift
Original file line number Diff line number Diff line change
Expand Up @@ -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)
}
Expand All @@ -77,4 +77,4 @@ public enum FacebookAuthType: String {

/// None
None = ""
}
}
34 changes: 17 additions & 17 deletions Simplicity/LoginProviders/Google.swift
Original file line number Diff line number Diff line change
Expand Up @@ -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"]
Expand All @@ -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()
}
Expand Down
8 changes: 4 additions & 4 deletions Simplicity/LoginProviders/VKontakte.swift
Original file line number Diff line number Diff line change
Expand Up @@ -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)
}
Expand Down
Loading

0 comments on commit a85b51b

Please sign in to comment.