OAuth2,Swift 3,Instagram

OAuth2, Swift 3, Instagram

IG好像变了很多。许多OAuth2 repos,似乎都有错误,或者真的不容易转换为Swift3。想知道是否有人有迁移到 Swift3 并使用 Instagram 最新更改的解决方案?

最受欢迎的解决方案。 OAuth2 的实现似乎是其中一个更复杂的事情。令人惊讶的是 IG 没有提供他们自己的示例文档来说明如何使用 iOS 执行此操作。他们只有基于网络的解决方案的文档。

也许那里正在酝酿什么?他们有数以百万计的编码员。但是现在,正在寻找一个(我敢说?)简单的解决方案。

感谢一百万。 :-)

我将下面的代码用于 facebook 和 google+,我认为它也适用于 Instagram,可能会进行一些调整。

import UIKit

class Signup: UIViewController, UIWebViewDelegate {
    let GOOGLE_ID = "xxxxxx.apps.googleusercontent.com"
    let GOOGLE_SECRET = "xxxxxxx";
    let GOOGLE_REDIRECT_URI="http://yourdomain.com/api/account/googlecallback"
    let GOOGLE_TOKEN_URL = "https://accounts.google.com/o/oauth2/token";
    let GOOGLE_OAUTH_URL = "https://accounts.google.com/o/oauth2/auth";
    let GOOGLE_OAUTH_SCOPE = "profile email";
    let GOOGLE_GET_PROFILE = "https://www.googleapis.com/userinfo/v2/me";
    let FACEBOOK_ID = "xxxxx";
    let FACEBOOK_REDIRECT_URI = "http://yourdomain.com/api/account/facebookcallback";
    let FACEBOOK_OAUTH_URL = "https://www.facebook.com/dialog/oauth?client_id=";
    let FACEBOOK_OAUTH_SCOPE = "public_profile,email"
    let FACEBOOK_GET_PROFILE = "https://graph.facebook.com/me?access_token="
    var currentURL: String = ""
    var queryString: String = ""
    var receivedToken: String = ""
    var authCode: String = ""
    var authComplete = false
    var webV:UIWebView = UIWebView(frame: CGRect(x: 0, y: 0, width: UIScreen.main.bounds.width, height: UIScreen.main.bounds.height))
    @IBAction func google(_ sender: AnyObject) {
        AppVars.Provider = "Google"
        webV.delegate = self
        let url = GOOGLE_OAUTH_URL + "?redirect_uri=" + GOOGLE_REDIRECT_URI + "&response_type=code&client_id=" + GOOGLE_ID + "&scope=" + GOOGLE_OAUTH_SCOPE
        let urlString :String =  url.addingPercentEncoding(withAllowedCharacters: CharacterSet.urlQueryAllowed)!
        webV.loadRequest(URLRequest(url: URL(string:urlString)!))
        self.view.addSubview(webV)
    }

    @IBAction func facebook(_ sender: AnyObject) {
        AppVars.Provider = "Facebook"
        webV.delegate = self
        let url = FACEBOOK_OAUTH_URL + FACEBOOK_ID + "&redirect_uri=" + FACEBOOK_REDIRECT_URI + "&scope=" + FACEBOOK_OAUTH_SCOPE + "&display=popup&response_type=token"
        let urlString :String =  url.addingPercentEncoding(withAllowedCharacters: CharacterSet.urlQueryAllowed)!
        webV.loadRequest(URLRequest(url: URL(string:urlString)!))
        self.view.addSubview(webV)
    }
    func webView(_ webView: UIWebView, didFailLoadWithError error: Error) {
         self.showAlert(self, message: "Internet is not working")
    }

    func webView(_ webView: UIWebView, shouldStartLoadWith request: URLRequest, navigationType: UIWebViewNavigationType) -> Bool {
        return true;
    }

    func webViewDidStartLoad(_ webView: UIWebView) {
    }

    func webViewDidFinishLoad(_ webView: UIWebView) {
        currentURL = (webView.request?.url!.absoluteString)!
        if AppVars.Provider == "Google" {
           googleSignup((webView.request?.url!)!)
        } else {
           facebookSignup((webView.request?.url!)!)
        }
    }

    func googleSignup (_ returnCode: URL) {
            let url = String(currentURL)
            if (url?.range(of: "?code=") != nil && authComplete != true) {
                authCode =  getQueryItemValueForKey("code", url: returnCode)!
                authComplete = true
                let paramString = "code=" + authCode + "&client_id=" + GOOGLE_ID + "&client_secret=" +
                    GOOGLE_SECRET + "&redirect_uri=" + GOOGLE_REDIRECT_URI + "&grant_type=authorization_code"
                self.requestServer(urlSource: GOOGLE_TOKEN_URL, params: paramString, requestType: "POST") { (dataResult, errorResult) -> () in
                    if errorResult != nil {
                         self.showAlert(self, message: "Internet is not working")
                    } else {
                        let dataString:NSString = NSString(data: dataResult as! Data, encoding: String.Encoding.utf8.rawValue)!
                        let dataResult2 = dataString.data(using: String.Encoding.utf8.rawValue, allowLossyConversion: false)!
                        do {
                            let jsonDict = try JSONSerialization.jsonObject(with: dataResult2, options: .allowFragments)  as! [String:Any]
                            if let token = jsonDict["access_token"] as? String {
                                self.requestServerSignup(self.GOOGLE_GET_PROFILE, param: token, requestType: "GET") { (dataResult, errorResult) -> () in
                                    if errorResult != nil {
                                         self.showAlert(self, message: "Internet is not working")
                                    } else {
                                        let dataString:NSString = NSString(data: dataResult as! Data, encoding: String.Encoding.utf8.rawValue)!
                                        let dataResult2 = dataString.data(using: String.Encoding.utf8.rawValue, allowLossyConversion: false)!
                                        do {
                                            let jsonDict = try JSONSerialization.jsonObject(with: dataResult2, options: .allowFragments) as! [String:Any]
                                            if let name = jsonDict["name"] as? String {
                                                AppVars.NameLogin = name
                                                AppVars.PictureLogin = jsonDict["picture"] as! String
                                                AppVars.EmailLogin = jsonDict["email"] as! String
                                                self.performSegue(withIdentifier: "externalLoginSegue", sender: self)
                                                // show picture, email and name for checking profile
                                            }
                                        } catch  {
                                             self.showAlert(self, message: "Internet is not working")
                                        }
                                    }
                                }
                            }

                        } catch  {
                             self.showAlert(self, message: "Internet is not working")
                        }
                    }
                }
                self.webV.removeFromSuperview()
            }

    }

    func facebookSignup(_ returnCode: URL) {
            let url = String(currentURL)
            if (url?.range(of: "access_token=") != nil && authComplete != true) {
                let url2: String = returnCode.absoluteString.replacingOccurrences(of: "#access_token", with: "access_token")
                let url3: URL = URL(string: url2)!
                authCode =  getQueryItemValueForKey("access_token", url: (url3))!
                authComplete = true
                let paramString = "code=" + authCode + "&client_id=" + GOOGLE_ID + "&client_secret=" +
                    GOOGLE_SECRET + "&redirect_uri=" + GOOGLE_REDIRECT_URI + "&grant_type=authorization_code"
                self.requestServer(urlSource: self.FACEBOOK_GET_PROFILE + authCode + "&fields=name,picture,email", params: paramString, requestType: "GET") { (dataResult, errorResult) -> () in
                    if errorResult != nil {
                         self.showAlert(self, message: "Internet is not working")
                    } else {
                        let dataString:NSString = NSString(data: dataResult as! Data, encoding: String.Encoding.utf8.rawValue)!
                        let dataResult2 = dataString.data(using: String.Encoding.utf8.rawValue, allowLossyConversion: false)!
                        do {
                            let jsonDict = try JSONSerialization.jsonObject(with: dataResult2, options: .allowFragments) as! [String:Any]
                            if let name = jsonDict["name"] as? String {
                                AppVars.NameLogin = name
                                if let picture = jsonDict["picture"] as? [String:Any] {
                                    if let dataPicture = picture["data"] as? [String:Any] {
                                        if let url = dataPicture["url"] as? String {
                                            AppVars.PictureLogin = url
                                        }
                                    }
                                }
                                AppVars.EmailLogin = jsonDict["email"] as! String
                                self.performSegue(withIdentifier: "externalLoginSegue", sender: self)
                                // show picture, email and name for checking profile
                            }

                        } catch  {
                             self.showAlert(self, message: "Internet is not working")
                        }
                    }
                }
                self.webV.removeFromSuperview()
            }

    }

    override func viewDidLoad() {
        super.viewDidLoad()
    }


    override func didReceiveMemoryWarning() {
        super.didReceiveMemoryWarning()
    }


    func getQueryItemValueForKey(_ key: String, url: URL) -> String? {
        guard let components = URLComponents(url: url, resolvingAgainstBaseURL: false) else {
            return nil
        }
        guard let queryItems = components.queryItems else { return nil }
        return queryItems.filter {
            [=10=].name == key
            }.first?.value
    }

   func requestServer(urlSource:String, params:String, requestType:String, result:@escaping (_ dataResult:NSData?, _ errorResult:NSError?) -> ()) {
        let url: URL = URL(string: urlSource)!
        var request = URLRequest(url:url)
        request.httpMethod = requestType
        request.setValue("application/x-www-form-urlencoded; charset=utf-8", forHTTPHeaderField: "Content-Type")
        if params.characters.count > 0 {
            request.httpBody = params.data(using: String.Encoding.utf8)
        }
        let session = URLSession.shared
        session.dataTask(with: request) { (data, response, error) -> Void in
            DispatchQueue.main.async(execute: { () -> Void in
                if error == nil {
                    result(data as NSData?, nil)
                } else {
                    result(nil, error as NSError?)
                }
            })
        }.resume()
    }

}

对于Swift 3:

更新:2017 年 4 月 17 日:由于依赖关系损坏,Pods 的安装不再有效。因此,我已经删除了所需的内容并使用桥接 Header 创建了一个 new Github project 并将所有需要的文件存储在项目中。如果您克隆或下载 github 项目,您将立即能够登录 Instagram。

要在您的项目中使用这些文件,只需将所有文件从 SimpleAuth 文件夹拖放到您的项目中,确保标记 copy item if needed

您还需要在 Instagram 开发者控制台中禁用 Disable implicit oAuth

然后你要么 copy/paste 我的代码从桥接 Header 到你的,要么你使用我的。在目标的构建设置中设置桥接 Header。

其他一切正常:

我有一个 Instagram 帐户结构:

struct InstagramUser {

    var token: String = ""
    var uid: String = ""
    var bio: String = ""
    var followed_by: String = ""
    var follows: String = ""
    var media: String = ""
    var username: String = ""
    var image: String = ""
}

接收token的函数:

typealias JSONDictionary = [String:Any]
var user: InstagramUser?
let INSTAGRAM_CLIENT_ID = "16ee14XXXXXXXXXXXXXXXXXXXXXXXXX"
let INSTAGRAM_REDIRECT_URI = "http://www.davidseek.com/just_a_made_up_dummy_url" //just important, that it matches your developer account uri at Instagram

extension ViewController {

    func connectInstagram() {

        let auth: NSMutableDictionary = ["client_id": INSTAGRAM_CLIENT_ID,
                                         SimpleAuthRedirectURIKey: INSTAGRAM_REDIRECT_URI]

        SimpleAuth.configuration()["instagram"] = auth            
        SimpleAuth.authorize("instagram", options: [:]) { (result: Any?, error: Error?) -> Void in

            if let result = result as? JSONDictionary  {

                var token = ""
                var uid = ""
                var bio = "" 
                var followed_by = ""
                var follows = ""
                var media = ""
                var username = ""
                var image = ""

                token = (result["credentials"] as! JSONDictionary)["token"] as! String
                uid = result["uid"] as! String

                if let extra = result["extra"] as? JSONDictionary,
                    let rawInfo = extra ["raw_info"] as? JSONDictionary,
                    let data = rawInfo["data"] as? JSONDictionary {

                    bio = data["bio"] as! String

                    if let counts = data["counts"] as? JSONDictionary {
                        followed_by = String(describing: counts["followed_by"]!)
                        follows = String(describing: counts["follows"]!)
                        media = String(describing: counts["media"]!)
                    }
                }

                if let userInfo = result["user_info"] as? JSONDictionary {
                    username = userInfo["username"] as! String
                    image = userInfo["image"] as! String
                }

                self.user = InstagramUser(token: token, uid: uid, bio: bio, followed_by: followed_by, follows: follows, media: media, username: username, image: image)


            } else {
                // this handles if user aborts or the API has a problem like server issue
                let alert = UIAlertController(title: "Error!", message: nil, preferredStyle: UIAlertControllerStyle.alert)
                alert.addAction(UIAlertAction(title: "OK", style: UIAlertActionStyle.default, handler: nil))
                self.present(alert, animated: true, completion: nil)
            }

            if error != nil {
                print("Error during SimpleAuth.authorize: \(error)")
            }
        }
    }
}

Instagram 还说:

Important

Even though our access tokens do not specify an expiration time, your app should handle the case that either the user revokes access, or Instagram expires the token after some period of time. If the token is no longer valid, API responses will contain an “error_type=OAuthAccessTokenException”. In this case you will need to re-authenticate the user to obtain a new valid token. In other words: do not assume your access_token is valid forever.

所以处理收到的情况OAuthAccessTokenException