Tackling OAuth 2.0 in iOS 9 with Swift 2


In an earlier post I discussed how to tackle OAuth 2.0 in an Android app. This post discusses how to do that in iOS 9 with Swift 2.

Step 1, in the post referenced above, is performed in a UIWebView. The view containing the UIWebView implements UIWebViewDelegate, intercepts all accessed URLs in webView:shouldStartLoadWithRequest:navigationType: function, and acquires the authorization code using regular expressions.

class ViewController: UIViewController, UIWebViewDelegate {
    @IBOutlet weak var webView: UIWebView!

    override func viewDidLoad() {
        super.viewDidLoad()
        // Do any additional setup after loading the view, typically from a nib.
        webView.delegate = self;
        webView.loadRequest(loginURL) // initialize loginURL
    }

    func webView(webView: UIWebView, shouldStartLoadWithRequest: NSURLRequest, navigationType: UIWebViewNavigationType) -> Bool {
        var continueToUrl = true
        do {
            let regex = try NSRegularExpression(pattern: "code=(.+?(?=&|$))", options: NSRegularExpressionOptions())
            let url: String? = shouldStartLoadWithRequest.URL?.absoluteString;
            let result: NSTextCheckingResult? = regex.firstMatchInString(url!, options: NSMatchingOptions(), range: NSMakeRange(0, url!.characters.count))
            let codeNSRange = result?.rangeAtIndex(1)
            if (codeNSRange != nil) {
                let startIndex = (url?.startIndex)!.advancedBy((codeNSRange?.location)!)
                let endIndex = (url?.startIndex)!.advancedBy((codeNSRange?.location)! + (codeNSRange?.length)!)
                let code = url?.substringWithRange(Range<String.Index>(startIndex..<endIndex))
                debugPrint(code)
                acquireAccessToken(code!);
                continueToUrl = false
            }

        } catch {
        }
        return continueToUrl;
    }

I found it challenging to use the interfaces for regular expression matching, and extracting substrings. They are quite different from those found in other popular languages, but should be familiar to Objective C developers.

Step 2, to acquire the access token, can be performed thus

    func acquireAccessToken(code: String) {
        manager!.request(.POST, tokenURL, parameters: ["code": code], encoding: .URLEncodedInURL)
            .responseJSON { (response) -> Void in
                switch response.result {
                case .Success:
                    if let value = response.result.value {
                        let json = JSON(value)
                        self.accessToken = json["access_token"].stringValue
                        debugPrint("Access Token: \(self.accessToken)")
                    }
                    
                case .Failure(let error):
                    debugPrint(error)
                }
        }
    }

The code above utilizes Alamofire for performing REST calls, and SwiftyJSON to handle JSON. Obtain those two frameworks using Carthage. Create a Cartfile with

github "Alamofire/Alamofire" ~> 3.0
github "SwiftyJSON/SwiftyJSON"

Then, invoke carthage to build the frameworks

carthage update

Drag the .framework files under Carthage/Build/ into your Xcode project. The files are added by reference. Adjust project target settings to ensure that the frameworks are embedded, otherwise you’ll get an error such as

dyld: Library not loaded: @rpath/SwiftyJSON.framework/SwiftyJSON
  Referenced from: ...

If you use git for version control, remember to adjust .gitignore to exclude Carthage/ folder.

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s