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
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