0

So im a bit new to swift and object-c as well and was wondering if someone could help me out a bit.

I'm used to creating usually a utils file where I have functions I use often in programming.

In this case im trying to call a function from another swift file and return an array of data.

For example in my mainViewController.swift im calling the function:

var Data = fbGraphCall()

In the Utils.swift file I have a function that Im trying to get it to return an array of data collected.

func fbGraphCall() -> Array<String>{

var fbData: [String] = [""]

if (FBSDKAccessToken.currentAccessToken() != nil){

    // get fb info
    var userProfileRequestParams = [ "fields" : "id, name, email, about, age_range, address, gender, timezone"]

    let userProfileRequest = FBSDKGraphRequest(graphPath: "me", parameters: userProfileRequestParams)

    let graphConnection = FBSDKGraphRequestConnection()

    graphConnection.addRequest(userProfileRequest, completionHandler: { (connection: FBSDKGraphRequestConnection!, result: AnyObject!, error: NSError!) -> Void in
        if(error != nil) {

            println(error)

        } else {

            // DEBUG
            println(result)


            let fbEmail = result.objectForKey("email") as! String

            // DEBUG
            println(fbEmail)

            fbData.append("\(fbEmail)")

            let fbID = result.objectForKey("id") as! String


            if(fbEmail != "") {
                PFUser.currentUser()?.username = fbEmail
                PFUser.currentUser()?.saveEventually(nil)
            }

            println("Email: \(fbEmail)")

            println("FBUserId: \(fbID)")


        }


    })

    graphConnection.start()
}

println(fbData)
return fbData
}

I can confirm that im getting the fbEmail and fbID back from facebook with my debug statements but as I said im still new on how to return data back.

Ideally I usually want an array back if its more than one value or the ability to get back data like Data.fbEmail, Data.fbID or an array maybe like ["email" : "[email protected]", "id" : "1324134124zadfa"]

When I hit the return statement its blank.. so not sure why the constants are not keeping values or passing values into my fbData array.. I'm trying fbData.append(fbEmail) for example ..

any thoughts on what might be wrong?

1
  • do you want to return an array of string? Commented Jul 29, 2015 at 4:57

1 Answer 1

2

The graphConnection.addRequest is an asynchronous function and you are trying to synchronously return the array of strings back. This won't work because the graphConnection.addRequest is done in the background to avoid blocking the main thread. So instead of returning the data directly make a completion handler. Your function would then become this:

func fbGraphCall(completion: ([String]) -> Void, errorHandler errorHandler: ((NSError) -> Void)?) {
    if (FBSDKAccessToken.currentAccessToken() != nil) {
        // get fb info
        var userProfileRequestParams = [ "fields" : "id, name, email, about, age_range, address, gender, timezone"]

        let userProfileRequest = FBSDKGraphRequest(graphPath: "me", parameters: userProfileRequestParams)

        let graphConnection = FBSDKGraphRequestConnection()

        graphConnection.addRequest(userProfileRequest, completionHandler: { (connection: FBSDKGraphRequestConnection!, result: AnyObject!, error: NSError!) -> Void in
            if(error != nil) {
                println(error)
                errorHandler?(error!)
            } else {
                var fbData = [String]() // Notice how I removed the empty string you were putting in here.
                // DEBUG
                println(result)


                let fbEmail = result.objectForKey("email") as! String

                // DEBUG
                println(fbEmail)

                fbData.append("\(fbEmail)")

                let fbID = result.objectForKey("id") as! String


                if(fbEmail != "") {
                    PFUser.currentUser()?.username = fbEmail
                    PFUser.currentUser()?.saveEventually(nil)
                }

                println("Email: \(fbEmail)")

                println("FBUserId: \(fbID)")

                completion(fbData)
            }


        })

        graphConnection.start()
    }
}

I added the completion handler and the error handler blocks that get executed according to what's needed.

Now at the call site you can do something like this:

fbGraphCall( { println($0) // $0 refers to the array of Strings retrieved }, errorHandler:  { println($0) // TODO: Error handling  }) // Optionally you can pass `nil` for the error block too incase you don't want to do any error handling but this is not recommended.

Edit:

In order to use the variables you would do something like this at the call site

 fbGraphCall( { array in
      dispatch_async(dispatch_get_main_queue(), {  // Get the main queue because UI updates must always happen on the main queue.
             self.fbIDLabel.text = array.first // array is the array we received from the function so make sure you check the bounds and use the right index to get the right values.
             self.fbEmailLabel.text = array.last 
      })
 }, errorHandler:  { 
        println($0) 
        // TODO: Error handling  
  })
Sign up to request clarification or add additional context in comments.

5 Comments

Thanks, im still trying to digest it, but the fact that the was asynchronous makes sense to me now and is non blocking. One thing that xcode 6.4 pointed out that it wanted to change the function line to func fbGraphCall(completion: ([String]) -> Void, #errorHandler: ((NSError) -> Void)?) {
Oh yes, I forgot you are still using Swift 1.2, I've gotten so used to Swift 2 where the # syntax no longer works. In fact in Swift 2, you wouldn't need to put the # or the extra errorHandler because global functions get similar naming conventions like methods on classes. But for now you should change it to what Xcode 6.4 suggested.
sorry to be a total noob on this.. but not sure how I can assign the data returned to variables I can use for UILables.. something like var data = fbGraphCall( { $0 }, errorHandler: {$0}) .. right now 2 things are returned fbemail and fbid.. some how I need to get those so I can then add them to fbIDLabel.text = (data returned) , fbEmailLable.text = (data.returned)
getting an error.. see link
The error is probably occurring because we are using a closure to get the main queue so we cannot use an unnamed parameter for the top level closure but we should name it. See the edit.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.