lifewithelliott lifewithelliott - 6 months ago 11
Swift Question

Property value prints but when assigned to label it shows nil

Currently working with two view controllers and a swift file dealing with the details of a store such as the phone number. There is a main ViewController and a DetailsViewController.

I currently acquire data from google places api and am able to successfully store the values in a PlaceDetails Class. Testing out the data - I am able to print to the console. However, when I try to assign a value taken from the API to a UILabel the application crashes and shows that the value of the property is nil. I am not sure why this is the case. I feel in the two view controllers I am instantiating the PlaceDetails class correctly and accessing it's properties correctly but there is something I am not aware of that is going wrong.

class ViewController: UIViewController
{
let placeDetails = PlaceDetails()
let detailsVC = DetailsViewController()

func tapLabel( sender: UITapGestureRecognizer )
{

// print statement successfully prints out the stored value as - Optional("1 888-555-5555")
print(placeDetails.phoneNumber)

// assigning value to label causes a crash stating value is nil
detailsVC.phoneNumberLabel.text = placeDetails.phoneNumber!

self.performSegueWithIdentifier("showDetailsVC", sender: self)

}
}

class DetailsViewController: UIViewController
{
@IBOutlet weak var phoneNumberLabel : UILabel!

let placeDetails = PlaceDetails()

override func viewDidLoad()
{
super.viewDidLoad()
//This approach does not cause a crash but outputs nil to the console for both the print statement and the assignment statement
print(placeDetails.phoneNumber)
phoneNumberLabel.text = placeDetails.phoneNumber!
}
}

class PlaceDetails
{
override init()
{
super.init()
}
var phoneNumber : String? //viewcontroller actions give this class property its value
}

Answer

You need to assign placeDetails to your destination view controller in prepareForSegue. I know you aren't doing this as you have created placeDetails as a let constant rather than a variable so it can't ever change from the empty PlaceDetails you originally assign.

You should declare it as an optional variable and then unwrap it properly when you use it;

class ViewController: UIViewController
{
    let placeDetails = PlaceDetails()

    func tapLabel( sender: UITapGestureRecognizer )
    {
        self.performSegueWithIdentifier("showDetailsVC", sender: self)
    }

    override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject!) {
        if (segue.identifier == "showDetailsVC") {
            let destVC = segue.destinationViewController as! DetailsViewController
            destVC.placeDetails = self.placeDetails
        }
    }
}

class DetailsViewController: UIViewController
{
    @IBOutlet weak var phoneNumberLabel  : UILabel!

    var placeDetails: PlaceDetails?

    override func viewWillAppear(animated: Bool)
    {
        super.viewWillAppear(animated)
        if let placeDetails = self.placeDetails {
            if let phoneNumber = placeDetails.phoneNumber {
                self.phoneNumberLabel.text = phoneNumber
            }
        }
    }
}

You can't use the value in viewDidLoad as this method will execute before the property has been set; the view controller is loaded before prepareForSegue is called, so viewWillAppear is a better place.