How do I make all of the functions wait for a little before they access the facebookData variable? Since the network call is asynchronous, facebookData is being accessed before it is getting the values from the Facebook.
Please find the below code from information.
func graphRequestToReturnUserData(graphParameters: Dictionary<String, String>) {
let graphRequest : FBSDKGraphRequest = FBSDKGraphRequest(graphPath: "me", parameters: graphParameters)
graphRequest.startWithCompletionHandler({ (connection, result, error) -> Void in
if ((error) != nil) {
print("Error: \(error.localizedDescription)") /*Error Handling*/
}else {
self.facebookData = result as! NSDictionary
}
})
}
I don't want to invoke the function like this :-
if ((error) != nil) {
print("Error: \(error.localizedDescription)") /*Error Handling*/
}else {
self.facebookData = result as! NSDictionary
self.printFacebookData()
}
})
}
func printFacebookData() {
print(self.facebookData)
}
Since I have found a solution that is working good in my situation, I am going to post it here for more to read, and possibly comment on it to improve further.
The simple solution here would be to use the property observer and call a function soon the property is changed. No need to mess up with the threads, or use of semaphores.
The solution code now looks like this: -
class facebookGraphRequest {
static var facebookData = NSDictionary()
//Property Observer, willSet-didSet
var facebookD = NSDictionary() {
didSet {
facebookGraphRequest.facebookData = facebookD //Assigning values to the static class variable
facebookUserSignUpLogIn().checkUserExistsOrNot() //Calling a function from the other class
}
}
//Mark: GraphRequestForFetchingUserData
func graphRequestToReturnUserData(graphParameters: Dictionary<String, String>) {
let graphRequest : FBSDKGraphRequest = FBSDKGraphRequest(graphPath: "me", parameters: graphParameters)
graphRequest.startWithCompletionHandler({ (connection, result, error) -> Void in
if ((error) != nil) {
print("Error: \(error.localizedDescription)") //Error Handling
} else {
self.facebookD = result as! NSDictionary
}
})
}
}
Here, the first part is the property observer (willSet-didSet), and then the GraphAPI call for getting the user data. Soon the facebookD variable is set to a value; the function checkUserExistsOrNot() is called from the class facebookUserSignUpLogIn for further actions.
Related
I keep getting back an empty array from the FBSDK even though my user account in Facebook is marked as attending 4 upcoming events.
Here is my code
func loadEvents() {
let parameters = ["fields": "id, name, place"]
FBSDKGraphRequest(graphPath: "me/events", parameters: parameters).start { (connection, result, error) -> Void in
if ((error) != nil) {
print("Error")
print("Error getting events \(String(describing: error))");
}
print(result)
if let result = result as? [String: Any] {
if let data = result["data"] as? NSArray {
//print(data)
}
}
}
And the result im getting in data is the following:
["data": <__NSArray0 0x60c00000b410>(
) ]
Any help appreciated. Is this a permissions issue? or am i passing in the wrong parameters object?
OK it was a permissions issue. I needed to add the user_events permission when login.
loginButton.readPermissions = ["public_profile", "user_events"]
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..!")
}
})
}
I am creating an app in which I am posting my post to Facebook from my app in which they provide me a postID now this postID is a constant and its in closure now I had to convert this postID in variable of type String and store that postID in my database.
And when I print my result it shows like the below image I think this is a dictionary and I don't know how to convert it into string. If anyone can help.Below code used for posting:
if (FBSDKAccessToken.currentAccessToken() != nil){
if FBSDKAccessToken.currentAccessToken().hasGranted("publish_actions") {
FBSDKGraphRequest(graphPath: "me/feed", parameters: ["message": "Task: \(self.textField.text!)\n Task Description: \(self.textAreaDescription.text!)\n Time: \(self.dateTime)\n Location: \(self.placemark!.locality!) \(self.placemark!.administrativeArea!)"], HTTPMethod: "POST").startWithCompletionHandler({
(connection, result, error) -> Void in
if (error != nil) {
// Do nothing
} else {
print("postID: \(result)")
}
})
}
}
try
if let dic = result as? Dictionary<String, AnyObject> {
let id = dic["id"]
}
I want to check, if my current Facebook token (it´s present) still has all needed permissions granted by user.
So I call the graph to retrieve all granted permissions associated with my token:
FBSDKGraphRequest(graphPath: "me", parameters: ["fields": "permissions"]).startWithCompletionHandler({ (connection, result, error) -> Void in
var resultDictionary:NSDictionary!
if (error != nil) {
println("ERROR = \(error)")
}
else {
resultDictionary = result as! NSDictionary
println("1: ---- SUCCESS (result) ----")
println("\(result)")
// this doesn´t work:
// var test = resultDictionary.objectForKey("permissions")?.objectForKey("data")! ?? nil
// var count = test.count
// println("---- test (\(count))")
// println(test)
}
})
The result from Facebook is:
{
id = 646571222102633;
permissions = {
data = (
{
permission = "user_friends";
status = granted;
},
{
permission = "publish_actions";
status = granted;
},
{
permission = "public_profile";
status = granted;
}
);
};
}
Because there might be no/one/a lot of permissions, I want to iterate through the part of the result under permissions / data.
But I´m not familiar with the conversion of this NSDictionary into something where I can count the elements down under and iterate through.
Any help?
Got it!
FBSDKGraphRequest(graphPath: "me", parameters: ["fields": "permissions"]).startWithCompletionHandler({ (connection, result, error) -> Void in
var resultDictionary:NSDictionary!
if (error != nil) {
DebugOutput("ERROR = \(error)")
}
else {
resultDictionary = result as! [String: AnyObject]
println("---- SUCCESS (result) ----")
println("\(result)")
var test = resultDictionary.objectForKey("permissions")?.objectForKey("data")! as! [[String:AnyObject]]
var count = test.count
println("---- test (\(count))")
println(test)
}
})
Thanks a lot to: http://ustwo.github.io/tech-blog/2014/08/01/ios-swift-dictionaries/
I am developing an iPad application using Swift. For http requests I use Alamofire library. So far I have managed to pull some data from an API. But the problem is since it is an asynchronous call I don't know how to check whether the request has completed. Any help would be appreciated.
This is the code I have implemented so far
Client class
func getUsers(completionHandler: ([User]?, NSError?) -> ()) -> (){
var users: [User] = []
let parameters = [
"ID": "123",
"apikey": "1234",
]
Alamofire.request(.GET, "API_URL", parameters: parameters).responseJSON() {
(_, _, JSON, error) in
let items = (JSON!.valueForKey("Users") as! [NSDictionary])
for item in items {
var user: User = User()
user.userId = item.valueForKey("ID")! as? String
user.userName = item.valueForKey("Name")! as? String
user.group = item.valueForKey("Group")! as? String
users.append(user)
}
completionHandler(users, error)
}
}
Main class
func getUsers(){
FacilityClient().getUsers() { (users, error) -> Void in
if users != nil {
self.users = users!
} else{
println("error - \(error)")
}
}
tableUsers.reloadData()
}
Thank you.
The closure part of the Alamofire request is called, when the request has completed. I've used your code and commented the line where the request hast finished:
Alamofire.request(.GET, "API_URL", parameters: parameters).responseJSON() {
(_, _, JSON, error) in
//
// The request has finished here. Check if error != nil before doing anything else here.
//
let items = (JSON!.valueForKey("Users") as! [NSDictionary])
for item in items {
var user: User = User()
user.userId = item.valueForKey("ID")! as? String
user.userName = item.valueForKey("Name")! as? String
user.group = item.valueForKey("Group")! as? String
users.append(user)
}
//
// After creating the user array we will call the completionHandler, which probably reports back to a ViewController instance
//
completionHandler(users, error)
}
If you want the tableView to reload data after successfully fetching data over network, you will need to call tableUsers.reloadData() inside that completion handler, like this:
func getUsers(){
FacilityClient().getUsers() { (users, error) -> Void in
if users != nil {
self.users = users!
tableUsers.reloadData() // SHOULD be here
} else{
println("error - \(error)")
}
}
// tableUsers.reloadData() // WAS here
}
And Alamofire make sure the completion handler is called on main queue if you don't specify a queue. You don't need to add code to make it be called on mainThread again.
And to your originally question: The only way Alamofire let you know a request has completed is by calling that completion handler which you give to the Alamofire.request method.