1

I'm trying to use a Struct to pass some variables that I get from a Facebook Graph Request such as email, name, gender, etc. I've created the Struct ('fbDemographics') and the variables in 'ViewController' but I get an error when I try to call the struct and one of the variables in 'SecondViewController' (Type 'ViewController' has no member 'fbDemographics'). I've never used struct before so a bit baffled why I am getting this error. Thanks for any thoughts. The code for both view controllers is below:

class ViewController: UIViewController, FBSDKLoginButtonDelegate {

override func viewDidLoad() {
    super.viewDidLoad()

    struct fbDemographics {

        static var relationship_status: String?
        static var gender: String?
        static var user_education_history: String?
        static var user_location: String?
        static var email: String?
        static var name: String?

    }     

    FBSDKGraphRequest(graphPath: "me", parameters: ["fields": "id, name, relationship_status, gender, user_location, user_education_history, email"]).start(completionHandler: { (connection, result, error) -> Void in
        if (error == nil){
            //let fbDetails = result as! NSDictionary
            //print(fbDetails)

            if let userDataDict = result as? NSDictionary {
                fbDemographics.gender = userDataDict["gender"] as? String
                fbDemographics.email = userDataDict["email"] as? String
                fbDemographics.name = userDataDict["name"] as? String
                fbDemographics.user_location = userDataDict["user_location"] as? String
                fbDemographics.user_education_history = userDataDict["user_education_history"] as? String
                fbDemographics.relationship_status = userDataDict["relationship_status"] as? String


                let myEducation = fbDemographics.user_education_history
                let myEmail = fbDemographics.email
                let myGender = fbDemographics.gender
                let myName = fbDemographics.name
                let myStatus = fbDemographics.relationship_status
                let myLocation = fbDemographics.user_location                   

                self.performSegue(withIdentifier: "LoginToHome", sender: (Any).self)


            }


        }

SECOND VIEW CONTROLLER

class SecondViewController: UIViewController {

@IBAction func verticalSliderChanged(_ sender: UISlider) {

        let currentValue = String(sender.value);

        sliderLabel.text = "\(currentValue)"

    func viewDidLoad() {
        super.viewDidLoad()

        ***ViewController.fbDemographics.myEmail***

    }
}
3
  • There are a lot of things wrong with your current solution. I would search for some tutorials on passing data between view controllers. Commented May 6, 2017 at 17:39
  • I've been going through a lot of tutorials. Just not getting passing variables but I guess ill keep plugging away. Thanks for taking a look. Commented May 6, 2017 at 17:43
  • 1
    Your question have a lot of unnecessary code. Please, delete and keep only the important. Commented May 6, 2017 at 17:48

1 Answer 1

3

The problem is that you've defined your struct inside viewDidLoad. So the scope is limited to that method. Move it out of viewDidLoad, but still in ViewController, and you should be able to access it from the SecondViewController. Obviously, you'll have to fix that reference to myEmail, too, because it's called email. Also, in your SecondViewController you should pull viewDidLoad implementation out of the verticalSliderChanged method; the viewDidLoad should be a top-level instance method of SecondViewController, not defined inside another method.

There are deeper problems here, though. Rather than using struct with static variables, you really should make those simple instance variables, create an instance of your FbDemographics type (note, start struct types with uppercase letter), and then pass this instance in prepare(for:sender:).


For example, the right way to pass data would be to:

  • eliminate static variables;
  • give your struct a name that starts with uppercase letter;
  • create instance of your struct; and
  • pass this instance to the destination view controller in prepare(for:sender:).

E.g.

struct FbDemographics {
    var relationshipStatus: String?
    var gender: String?
    var userEducationHistory: String?
    var userLocation: String?
    var email: String?
    var name: String?
}

class ViewController: UIViewController {

    var demographics: FbDemographics?

    override func viewDidLoad() {
        super.viewDidLoad()

        performRequest()   // I actually don't think you should be initiating this in `viewDidLoad` ... perhaps in `viewDidAppear`
    }

    override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
        if let destination = segue.destination as? SecondViewController {
            destination.demographics = demographics
        }
    }

    func performRequest() {
        FBSDKGraphRequest(graphPath: "me", parameters: ["fields": "id, name, relationship_status, gender, user_location, user_education_history, email"]).start { connection, result, error in
            guard let userDataDict = result as? NSDictionary, error == nil else {
                print("\(error)")
                return
            }

            self.demographics = FbDemographics(
                relationshipStatus: userDataDict["relationship_status"] as? String,
                gender: userDataDict["gender"] as? String,
                userEducationHistory: userDataDict["user_education_history"] as? String,
                userLocation: userDataDict["user_location"] as? String,
                email: userDataDict["email"] as? String,
                name: userDataDict["name"] as? String
            )

            self.performSegue(withIdentifier: "LoginToHome", sender: self)
        }
    }

}

And then SecondViewController could:

class SecondViewController: UIViewController {

    var demographics: FbDemographics!

    override func viewDidLoad() {
        super.viewDidLoad()

        let value = demographics.email  // this should work fine here
    }

}
Sign up to request clarification or add additional context in comments.

2 Comments

Thanks so much for taking the time to put up such a complete answer. I went through this and followed what you said. I don't have any errors which is a good first start. One question though; I wasn't sure what you meant when you said pass the instance (prepareforsegue). Thanks again.
For myriad reasons, it's inadvisable to declare static properties (in which every instance of FbDemographics will share the same value). You instead want to create an "instance" with the let foo = FbDemographics(...) syntax. Then, the question becomes how does SecondViewController gets a copy of this instance. So you have the prepare(for:sender:) confirm that the destination is a SecondViewController, and if so, assign the corresponding demographics property to be a copy of that original FbDemographics instance that we created above.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.