Can't make multiple FBSDKGraphRequests - ios

I'm trying to read the posts from a Facebook page using the following code:
class FacebookGraphAPI {
class func getPosts(fromPageWithID pageId: String, parameters: [String: AnyObject]?, completion: #escaping ([FacebookPost]?, Error?) -> Void) {
self.getAccessToken { (accessToken, error) in
if error != nil {
completion(nil, error)
return
}
let params = ["access_token": accessToken, "fields": "created_time,message,story"]
if let request = FBSDKGraphRequest(graphPath: "\(pageId)/posts", parameters: params) {
request.start { (connection, result, error) in
if error != nil {
completion(nil, error)
return
}
guard let resultDict = result as? [String: AnyObject],
let data = resultDict["data"] as? NSArray
else {
completion(nil, nil)
return
}
var posts = [FacebookPost]()
for item in data {
posts.append(FacebookPost(dict: item as! NSDictionary))
}
completion(posts, nil)
}
}
completion(nil, nil)
}
}
class func getAccessToken(completion: #escaping (String?, Error?) -> Void) {
let clientId = Bundle.main.object(forInfoDictionaryKey: "FacebookAppID") as! String
let clientSecret = Bundle.main.object(forInfoDictionaryKey: "FacebookAppSecret") as! String
let params = ["client_id": clientId, "client_secret": clientSecret, "grant_type": "client_credentials", "fields": "access_token"]
if let request = FBSDKGraphRequest(graphPath: "oauth/access_token", parameters: params) {
request.start(completionHandler: { (connection, result, error) in
if error != nil {
completion(nil, error)
return
}
guard let resultDict = result as? [String: AnyObject] else {
completion(nil, nil)
return
}
let accessToken = resultDict["access_token"] as! String
completion(accessToken, nil)
})
}
}
}
Which I then call using e.g. the following:
FacebookGraphAPI.getPosts(fromPageWithID: "{page-id}", parameters: ["limit": 5 as AnyObject]) { (posts, error) in
guard error == nil else { return }
...
}
The error I'm getting is: -[_SwiftValue length]: unrecognized selector sent to instance on the second FBSDKGraphRequest start.
I tried removing the first FBSDKGraphRequest and then I at least get a response in the completionHandler. It almost seams as if I can't make more than one FBSDKGraphRequest.
Any help is greatly appreciated.
Thanks in advance!

I finally managed to find the issue, when thinking about the error and remembering that the FBSDKCoreKit framework was written in Objective-C.
All I needed to do was cast accessToken inside the parameters array to an NSString.
I changed the following:
let params = ["access_token": accessToken, "fields": "created_time,message,story"]
To:
let params = ["access_token": accessToken! as NSString, "fields": "created_time,message,story"]

Related

Converting Graph API from Swift 3 to Swift 5 for Facebook SDK

I am developing an app that allows the user to login using Facebook. The code snippet I have is using Swift 3 though and I can't find a converter online. The Swift 3 code is as follows:
In the example which is in Swift 3, Xcode suggests:
request.start(completion: ((HTTPURLResponse?, GraphRequestResult<GraphRequest>) -> Void)?)
And the programmer then enters (this is the entire function):
func getUserInfo(completion: #escaping (_ : [String: Any]?, _ : Error?) -> Void) {
let request = GraphRequest(graphPath: "me", parameters: ["fields" : "id,email,picture"])
request.start { response, result in
switch result {
case .failed(let error):
completion(nil, error)
case .success (let graphResponse):
completion(graphResponse.dictionaryValue, nil)
}
}
When I start to type:
request.start
Which gives me this line of code:
request.start(completionHandler: GraphRequestBlock?)
How can I convert this from Swift 3 to Swift 5?
Update after comment
My "HomeAfterLogInViewController.swift" file is as follows:
import Foundation
import FacebookCore
import FacebookLogin
class HomeAfterLogInViewController: UIViewController
{
override func viewDidLoad()
{
super.viewDidLoad()
getFacebookProfileInfo()
}
}
func getFacebookProfileInfo()
{
let requestMe = GraphRequest.init(graphPath: "me", parameters: ["fields" : "id,name,email,picture.type(large)"])
let connection = GraphRequestConnection()
connection.add(requestMe, completionHandler:{ (connectn, userresult, error) in
if let dictData: [String : Any] = userresult as? [String : Any]
{
DispatchQueue.main.async
{
if let pictureData: [String : Any] = dictData["picture"] as? [String : Any]
{
if let data : [String: Any] = pictureData["data"] as? [String: Any]
{
print(data)
print(dictData["email"]!)
}
}
}
}
})
connection.start()
}
And this code works but there is one more step I need - explained in the screenshot:
func getFacebookProfileInfo() {
let requestMe = GraphRequest.init(graphPath: "me", parameters: ["fields" : "id,name,email,picture.type(large)"])
let connection = GraphRequestConnection()
connection.add(requestMe, completionHandler: { (connectn, userresult, error) in
if let dictData: [String : Any] = userresult as? [String : Any] {
DispatchQueue.main.async {
if let pictureData: [String : Any] = dictData["picture"] as? [String : Any] {
if let data : [String: Any] = pictureData["data"] as? [String: Any] {
print(data)
print(dictData["email"]!)
}
}
}
}
})
connection.start()
}
Try the below code to get the information of the user.
let params = ["fields":"email, id, name, first_name, last_name,gender"]//, user_gender, user_birthday"]
let request = GraphRequest(graphPath: "me", parameters: params, accessToken: AccessToken.current, httpMethod: .GET, apiVersion: FacebookCore.GraphAPIVersion.defaultVersion)
request.start { (response, result) in
switch result {
case .success(let value):
print(value.dictionaryValue!)
var parsedData = value.dictionaryValue as Dictionary<String, AnyObject>?
if let firstName = parsedData?["first_name"] {
print("First Name: \(firstName)")
}
if let lastName = parsedData?["last_name"] {
print("Last Name: \(lastName)")
}
if let email = parsedData?["email"] {
print("Email: \(email as! String)")
}
if let id = parsedData?["id"] {
let faceBookId = id as! String
print("Facebook Id: \(faceBookId)")
//you can get profile picture URL here.
let pictureURL = "https://graph.facebook.com/" + "\(faceBookId)/" + "picture?type=large&return_ssl_resources=1"
print("Profile Picture URL: \(pictureURL)")
}
case .failed(let error):
print(error.localizedDescription)
}
}
GraphRequest(graphPath: "me", parameters: ["fields": "id, name, first_name, last_name, picture.type(large), email"]).start(completionHandler: { (connection, result, error) -> Void in
if error == nil{
//everything works print the user data
if let userInfo = result as? [String: Any] {
if let email = userInfo["email"] as? String {
let firstName = userInfo["first_name"] as? String
let lastName = userInfo["last_name"] as? String
var profilePicUrl: String? = nil
if let fbUserId = userInfo["id"] as? String {
profilePicUrl = "http://graph.facebook.com/\(fbUserId)/picture?type=large"
}
//Do your operations here.
}
}
}
})
Hope that will help!

Use of undeclared type 'GraphRequestResult' after update of pods

I'm using the new facebook graph request and after updating pods I get an error
< Use of undeclared type 'GraphRequestResult'>
let graphRequest = GraphRequest(graphPath: kGraphPathMe, parameters: ["fields":"id,email,last_name,first_name,picture"], tokenString: accessToken.tokenString, version: .init(), httpMethod: .get)
graphRequest.start {(response: HTTPURLResponse?, result: GraphRequestResult<GraphRequest>) in
switch result {
case .success(let graphResponse):
if let dictionary = graphResponse.dictionaryValue {
completion(FacebookUser(jsonDict: dictionary))
}
break
default:
print("Facebook request user error")
}
}
check this code
let parameters = ["fields": "email, id, name"]
let graphRequest = FBSDKGraphRequest(graphPath: "me", parameters: parameters)
_ = graphRequest?.start { [weak self] connection, result, error in
// If something went wrong, we're logged out
if (error != nil) {
// Clear email, but ignore error for now
return
}
// Transform to dictionary first
if let result = result as? [String: Any] {
// Got the email; send it to Lucid's server
guard let email = result["email"] as? String else {
// No email? Fail the login
return
}
guard let username = result["name"] as? String else {
// No username? Fail the login
return
}
guard let userId = result["id"] as? String else {
// No userId? Fail the login
return
}
}
} // End of graph request

From FBSDKGraphRequest to Storyboard

i'm total beginner in coding, but i have a big question :)
I dont know how to get information to display on Main Storyboard form FBSDKGraphRequest.
What i should do next to get picture to Storyboard? I hope someone can help me :)
Using Swift 3, Xcode 8
Facebook login is working and code is:
func getFBUserData(){
if((FBSDKAccessToken.current()) != nil){
FBSDKGraphRequest(graphPath: "me", parameters: ["fields": "id, name, first_name, last_name, picture.type(large), email"]).start(completionHandler: { (connection, result, error) -> Void in
if (error == nil){
self.dict = result as? [String : AnyObject]
print(result!)
print(self.dict)
}
})
}
I see it as four steps -- my apologies to you if you already know some of this.
Drag an UIImageView from the object library in interface builder to a view in your storyboard.
Connect the UIImageView to your code as an IBOutlet and name it. Do this by control dragging from your new UIImageView to your code, and then in the popup specify an outlet and name the UIImageView. In my case I named it 'facebookPicture'
Get the URL for the image from the FaceBook result. The following sample code drills down into the result dictionary step by step. There are many ways to shorten this.
#IBOutlet var facebookPicture: UIImageView!
func getFBUserID(){
if((FBSDKAccessToken.current()) != nil) {
FBSDKGraphRequest(graphPath: "me", parameters: ["fields": "id, name, first_name, last_name, picture.type(large), email"]).start(
completionHandler: {
[weak self] (connection, result, error) -> Void in
guard let strongSelf = self else { return }
if let error = error {
print("Failed to download FB user with error:. \(error)")
}
if (error == nil) {
let resultDictSwift = result as! Dictionary<String, Any>
if let picture = resultDictSwift["picture"] as? Dictionary<String, Any> {
if let pictureData = picture["data"] as? Dictionary<String, Any> {
if let pictureURL = NSURL(string: (pictureData["url"] as? String)! ) {
strongSelf.downloadImage(url: pictureURL as URL)
}
}
}
print(result!)
let FBID = resultDictSwift["id"]
strongSelf.facebookID = FBID as! String?
print("User FB id: \(strongSelf.facebookID!)")
}
})
}
}
Download the image from the URL location. The following does this asynchronously and uses a previous stack overflow answer here When the download is complete, it sets the result of the download to be the image in your UIImageView
func getDataFromUrl(url: URL, completion: #escaping (Data?, URLResponse?, Error?) -> ()) {
URLSession.shared.dataTask(with: url) { data, response, error in
completion(data, response, error)
}.resume()
}
func downloadImage(url: URL) {
print("Download Started")
getDataFromUrl(url: url) { data, response, error in
guard let data = data, error == nil else { return }
print(response?.suggestedFilename ?? url.lastPathComponent)
print("Download Finished")
DispatchQueue.main.async() {
self.facebookPicture.image = UIImage(data: data)
}
}
}
I hope this helps!
In Swift 3
#IBAction func btnFacebookTapped(_ sender: UIButton)
{
let loginManager = FBSDKLoginManager()
loginManager.logIn(withReadPermissions: ["user_about_me", "email" , "user_birthday","user_hometown"], from: self) { (loginResult, error) in
if error != nil
{
self.showalert(strMessage: (error?.localizedDescription)!)
}
else
{
if loginResult?.grantedPermissions == nil
{
self.showalert(strMessage: "Login Permissions not granted")
return
}
if (loginResult?.grantedPermissions.contains("email"))!
{
self.getFBUserData()
}
}
}
}
func getFBUserData()
{
FBSDKGraphRequest.init(graphPath: "me?fields=id,name,email,first_name,last_name,cover,picture.type(large),gender,birthday,hometown", parameters: nil).start(completionHandler: { (connection , result , error ) in
if(error == nil){
DispatchQueue.main.async {
let dictionary = result as! NSDictionary
print(dictionary)
print("Name : \(dictionary.value(forKey: "name")!)")
print("FB ID : \(dictionary.value(forKey: "id")!)")
}
}else{
self.showalert(strMessage: "Somthig Went Wrong..!")
}
})
}

how to go on next viewController

In my app i have tried to make different class for api calling. like click on login button and its call the method of different class. but when i want to go to another viewcontroller from that different class its getting crash.
here is my code in loginViewController
let mydata = DataControllerLogin()
mydata.login(txtemail.text!,password: txtPassword.text!)
class DataControllerLogin: UIViewController {
func login(username:String,password:String)
{
if Reachability.isConnectedToNetwork() == true
{
let url = "\(basicURL)login"
let param : [String : AnyObject] = [
"email" : username,
"password" : password
]
Alamofire.request(.POST, url, parameters: param, encoding: .JSON).responseObject(completionHandler: { (response:Response<LoginCode, NSError>) in
if (response.result.value != nil)
{
let LoginCode = response.result.value
let message = LoginCode?.Message
let detail = LoginCode?.result
if (LoginCode?.Status == 1)
{
let controller : LoginViewController = self.storyboard?.instantiateViewControllerWithIdentifier("LoginViewController") as! LoginViewController
self.navigationController?.pushViewController(controller, animated: true)
SVProgressHUD.dismiss()
}
else
{
alertViewShow(self, title: "Sorry", message: message!)
SVProgressHUD.dismiss()
}
if let threedayForecast = LoginCode?.result {
print(threedayForecast.FirstName)
}
}
else
{
}
})
}
else {
alertViewShow(self, title: "No Internet Connection", message: "Make sure your device is connected to the internet.")
}
}
}
but its getting crash on line wherever i have define viewController.
let controller : LoginViewController = self.storyboard?.instantiateViewControllerWithIdentifier("LoginViewController") as! LoginViewController
self.navigationController?.pushViewController(controller, animated: true)
its showing error like
exc_bad_instruction (code=exc_i386_invop subcode=0x0)
so if you know please let me know what is the issue?
Write this at the top of the file.
import UIKit
import Alamofire
import SwiftyJSON
typealias SOAPICompletionHandler = (code:Int, error:NSError?, response:NSDictionary?) -> Void
Add below method in your file:-
func callApi(strApiName:String, param : [String : AnyObject]?, type:String, header:[String : String]?, completionHandler:SOAPICompletionHandler) {
//let strURL : String = BASEURL+"/"+strApiName
let strURL = strApiName;
if type == POSTREQ {
Alamofire.request(.POST, strURL, parameters: param, encoding: .JSON, headers: header).responseJSON(completionHandler: { (responseData) -> Void in
let isSuccess = JSON(responseData.result.isSuccess)
if isSuccess {
// let swiftyJson = JSON(responseData.result.value! as! NSDictionary) as! AnyObject
completionHandler(code: 1, error: nil, response: responseData.result.value! as? NSDictionary)
} else {
let error = responseData.result.error! as NSError
completionHandler(code: 0, error: error, response: nil)
}
})
} else if type == GETREQ {
Alamofire.request(.GET, strURL, parameters: param, encoding: .JSON, headers: header).responseJSON(completionHandler: { (responseData) -> Void in
let isSuccess = JSON(responseData.result.isSuccess)
if isSuccess {
// let swiftyJson = JSON(responseData.result.value! as! NSDictionary)
completionHandler(code: 1, error: nil, response: responseData.result.value! as? NSDictionary)
} else {
let error = responseData.result.error! as NSError
completionHandler(code: 0, error: error, response: nil)
}
})
} else if type == PUTREQ{
Alamofire.request(.PUT, strURL, parameters: param, encoding: .JSON, headers: header).responseJSON(completionHandler: { (responseData) -> Void in
let isSuccess = JSON(responseData.result.isSuccess)
if isSuccess {
// let swiftyJson = JSON(responseData.result.value! as! NSDictionary)
completionHandler(code: 1, error: nil, response: responseData.result.value! as? NSDictionary)
} else {
let error = responseData.result.error! as NSError
completionHandler(code: 0, error: error, response: nil)
}
})
} else if type == DELETEREQ{
Alamofire.request(.DELETE, strURL, parameters: param, encoding: .JSON, headers: header).responseJSON(completionHandler: { (responseData) -> Void in
let isSuccess = JSON(responseData.result.isSuccess)
if isSuccess {
// let swiftyJson = JSON(responseData.result.value! as! NSDictionary)
completionHandler(code: 1, error: nil, response: responseData.result.value! as? NSDictionary)
} else {
let error = responseData.result.error! as NSError
completionHandler(code: 0, error: error, response: nil)
}
})
}
else if type == PATCHREQ{
Alamofire.request(.PATCH, strURL, parameters: param, encoding: .JSON, headers: header).responseJSON(completionHandler: { (responseData) -> Void in
let isSuccess = JSON(responseData.result.isSuccess)
if isSuccess {
// let swiftyJson = JSON(responseData.result.value! as! NSDictionary)
completionHandler(code: 1, error: nil, response: responseData.result.value! as? NSDictionary)
} else {
let error = responseData.result.error! as NSError
completionHandler(code: 0, error: error, response: nil)
}
})
}
}
you need to add Alamofire and SwiftyJSON.
Hope for best.

Can't return output of closure using graph api

If I call this method to obtain a Facebook profile picture it does not work, response gets printed with the data but when I return it its nil, can anyone help?
func getProfilePicture() -> NSData?{
var response:NSData?
let graphRequest : FBSDKGraphRequest = FBSDKGraphRequest(graphPath: "me", parameters: ["fields":"picture.type(large)"])
graphRequest.startWithCompletionHandler({ (connection, result, error) -> Void in
if error != nil {
print("login error: \(error!.localizedDescription)")
return
}
let json = JSON(result)
let profilePicture = json["picture"]["data"]["url"].stringValue
if let url = NSURL(string: profilePicture) {
if let pictureData = NSData(contentsOfURL: url){
response = pictureData
}
}
})
print(response)
return response
}
The Facebook call is using an async completionblock and your return statement will return nil before the call is done loading. That is why you also should use a completion block in your method.
Here is an example how to do this:
func getProfilePicture(completion: (pictureData: NSData?, error: NSError?) -> Void) {
let graphRequest : FBSDKGraphRequest = FBSDKGraphRequest(graphPath: "me", parameters: ["fields":"picture.type(large)"])
graphRequest.startWithCompletionHandler({ (connection, result, error) -> Void in
var pictureData: NSData?
let json = JSON(result)
let profilePicture = json["picture"]["data"]["url"].stringValue
if let url = NSURL(string: profilePicture) {
pictureData = NSData(contentsOfURL: url)
}
completion(pictureData: pictureData, error: error)
})
}
You can call this code like this:
self.getProfilePicture {(pictureData, error) -> Void in
if error != nil {
print("login error: \(error!.localizedDescription)")
}
print(pictureData)
}

Resources