diff --git a/AsakusaSatellite.podspec b/AsakusaSatellite.podspec index 5487dfb..dcd3311 100644 --- a/AsakusaSatellite.podspec +++ b/AsakusaSatellite.podspec @@ -9,9 +9,11 @@ Pod::Spec.new do |s| s.license = "MIT" s.author = { "banjun" => "banjun@gmail.com" } s.ios.deployment_target = "8.0" - # s.osx.deployment_target = "10.10" + s.osx.deployment_target = "10.10" s.source = { :git => "https://github.com/codefirst/AsakusaSatelliteSwiftClient.git", :tag => "0.0.1" } - s.source_files = "Classes", "Classes/**/*.{h,m,swift}" + s.source_files = 'Classes/*.swift' + s.ios.source_files = 'Classes/ios/*.swift' + s.osx.source_files = '' s.requires_arc = true s.dependency "Alamofire", "~> 1.1" s.dependency "SwiftyJSON", "~> 2.1" diff --git a/Classes/Client.swift b/Classes/Client.swift index a8ebca5..1de5a0a 100644 --- a/Classes/Client.swift +++ b/Classes/Client.swift @@ -12,15 +12,16 @@ import SwiftyJSON public class Client { - let baseURL: String + public let rootURL: String + var apiBaseURL: String { return "\(rootURL)/api/v1" } let apiKey: String? public convenience init(apiKey: String?) { - self.init(baseURL: "https://asakusa-satellite.herokuapp.com/api/v1", apiKey: apiKey) + self.init(rootURL: "https://asakusa-satellite.herokuapp.com", apiKey: apiKey) } - public init(baseURL: String, apiKey: String?) { - self.baseURL = baseURL + public init(rootURL: String, apiKey: String?) { + self.rootURL = rootURL self.apiKey = apiKey // remove all AasakusaSatellite cookies @@ -30,7 +31,7 @@ public class Client { private func removeCookies() { let cs = NSHTTPCookieStorage.sharedHTTPCookieStorage() - for cookie in (cs.cookiesForURL(NSURL(string: baseURL)!) as? [NSHTTPCookie]) ?? [] { + for cookie in (cs.cookiesForURL(NSURL(string: rootURL)!) as? [NSHTTPCookie]) ?? [] { cs.deleteCookie(cookie) } } @@ -74,7 +75,7 @@ public class Client { // MARK: - private func request(endpoint: Endpoint, completion: Response -> Void) { - Alamofire.request(endpoint.URLRequest(baseURL, apiKey: apiKey)).responseJSON { (request, response, object, error) -> Void in + Alamofire.request(endpoint.URLRequest(apiBaseURL, apiKey: apiKey)).responseJSON { (request, response, object, error) -> Void in if object == nil || error != nil { NSLog("failure in Client.request(\(endpoint)): \(error)") completion(.Failure(error)) diff --git a/Classes/ios/TwitterAuthViewController.swift b/Classes/ios/TwitterAuthViewController.swift new file mode 100644 index 0000000..17a23b3 --- /dev/null +++ b/Classes/ios/TwitterAuthViewController.swift @@ -0,0 +1,114 @@ +// +// TwitterAuthViewController.swift +// AsakusaSatelliteSwiftClient +// +// Created by BAN Jun on 2015/03/15. +// Copyright (c) 2015年 codefirst. All rights reserved. +// + +import Foundation +import UIKit + + +private let kAuthTwitterPath = "/auth/twitter" +private let kAccountPath = "/account" + + +public class TwitterAuthViewController: UIViewController, UIWebViewDelegate { + let webview = UIWebView(frame: CGRectZero) + let rootURL: NSURL + let completion: (String? -> Void) + + // MARK: init + + public init(rootURL: NSURL, completion: (String? -> Void)) { + self.rootURL = rootURL + self.completion = completion + super.init(nibName: nil, bundle: nil) + } + + required public init(coder aDecoder: NSCoder) { + fatalError("init(coder:) has not been implemented") + } + + // MARK: - + + public override func viewDidLoad() { + title = NSLocalizedString("Sign in with Twitter", comment: "") + + webview.autoresizingMask = .FlexibleWidth | .FlexibleHeight + webview.frame = view.bounds + webview.delegate = self + view.addSubview(webview) + + if let url = NSURL(string: kAuthTwitterPath, relativeToURL: rootURL) { + // load /auth/twitter with referer /account + // oauth callback redirects to referer + let request = NSMutableURLRequest(URL: url) + request.setValue(kAccountPath, forHTTPHeaderField: "Referer") + webview.loadRequest(request) + } else { + let ac = UIAlertController( + title: NSLocalizedString("Cannot Load", comment: ""), + message: NSLocalizedString("Invalid URL: ", comment: "") + "\(rootURL)", + preferredStyle: .Alert) + ac.addAction(UIAlertAction(title: "OK", style: .Default, handler: { _ in + ac.dismissViewControllerAnimated(true, completion: nil) + })) + self.presentViewController(ac, animated: true, completion: nil) + } + } + + // MARK: UIWebViewDelegate + + private func isRedirectedBackToAsakusaSatellite(request: NSURLRequest) -> Bool { + let reqURLString = request.URL.absoluteString + let rootURLString = rootURL.absoluteString! + + return reqURLString?.hasPrefix(rootURLString) == true && request.URL.path == kAccountPath + } + + public func webView(webView: UIWebView, shouldStartLoadWithRequest request: NSURLRequest, navigationType: UIWebViewNavigationType) -> Bool { + if isRedirectedBackToAsakusaSatellite(request) { + // TODO: display HUD + NSLog("Getting API Key...") + } + return true + } + + public func webViewDidStartLoad(webView: UIWebView) { + UIApplication.sharedApplication().networkActivityIndicatorVisible = true + } + + public func webViewDidFinishLoad(webView: UIWebView) { + UIApplication.sharedApplication().networkActivityIndicatorVisible = false + + if isRedirectedBackToAsakusaSatellite(webview.request!) { + // did load /account on AsakusaSatellite + // TODO: display HUD + NSLog("Completed") + + // get apiKey from text field + let js = "$('#account_secret_key').attr('value')" + let apiKey = webview.stringByEvaluatingJavaScriptFromString(js) + + webView.delegate = nil // unlink delegate before removing self + navigationController?.popViewControllerAnimated(true) + completion((apiKey?.isEmpty ?? true) ? nil : apiKey) + } + } + + public func webView(webView: UIWebView, didFailLoadWithError error: NSError) { + UIApplication.sharedApplication().networkActivityIndicatorVisible = false + + let ac = UIAlertController( + title: NSLocalizedString("Cannot Load", comment: ""), + message: error.localizedDescription, + preferredStyle: .Alert) + ac.addAction(UIAlertAction(title: "OK", style: .Default, handler: { _ in + ac.dismissViewControllerAnimated(true, completion: nil) + })) + self.presentViewController(ac, animated: true, completion: nil) + } +} + diff --git a/Example/AsakusaSatelliteSwiftClientExample/ViewController.swift b/Example/AsakusaSatelliteSwiftClientExample/ViewController.swift index 3fb95d4..7dbcf9d 100644 --- a/Example/AsakusaSatelliteSwiftClientExample/ViewController.swift +++ b/Example/AsakusaSatelliteSwiftClientExample/ViewController.swift @@ -46,6 +46,8 @@ class ViewController: UIViewController, UITextFieldDelegate, UITextViewDelegate listButton.addTarget(self, action: "list:", forControlEvents: .TouchUpInside) messagesTextView.delegate = self + navigationItem.rightBarButtonItem = UIBarButtonItem(title: "Sign in", style: .Plain, target: self, action: "signin:") + let views = [ "apiKey": apiKeyField, "name": usernameLabel, @@ -82,7 +84,7 @@ class ViewController: UIViewController, UITextFieldDelegate, UITextViewDelegate let apiKey = NSUserDefaults.standardUserDefaults().objectForKey(kDefaultsKeyApiKey) as? String apiKeyField.text = apiKey client = AsakusaSatellite.Client(apiKey: apiKey) - // client = AsakusaSatellite.Client(baseURL: "http://localhost:3000/api/v1", apiKey: apiKey) + // client = AsakusaSatellite.Client(rootURL: "http://localhost:3000", apiKey: apiKey) NSLog("initialized client with apiKey = \(apiKey)") usernameLabel.text = "(initialized)" @@ -144,6 +146,21 @@ class ViewController: UIViewController, UITextFieldDelegate, UITextViewDelegate } } + func signin(sender: AnyObject?) { + let vc = TwitterAuthViewController(rootURL: NSURL(string: client.rootURL)!) { [weak self] apiKey in + let defaults = NSUserDefaults.standardUserDefaults() + if let apiKey = apiKey { + defaults.setObject(apiKey, forKey: kDefaultsKeyApiKey) + } else { + NSLog("cannot sign in") + defaults.removeObjectForKey(kDefaultsKeyApiKey) + } + defaults.synchronize() + self?.reloadClient() + } + navigationController?.pushViewController(vc, animated: true) + } + // MARK: - TextField func textFieldShouldReturn(textField: UITextField) -> Bool { diff --git a/Example/Podfile.lock b/Example/Podfile.lock index e6b62ca..05dc2ad 100644 --- a/Example/Podfile.lock +++ b/Example/Podfile.lock @@ -16,7 +16,7 @@ EXTERNAL SOURCES: SPEC CHECKSUMS: Alamofire: 524225da382071ee3e6d0badd0ee4b4dc36740de - AsakusaSatellite: e7082b74b819a4436ccb0efa522ed93e94406b74 + AsakusaSatellite: 499c3117a42e276856fbd04608e38d40718b1c71 Socket.IO-Client-Swift: 23d9f0db0cdcb98623486ddf0ee811b158e9cd02 SwiftyJSON: 48be7490a3989a58a3f511cd54167f0a2b466e76