Passing data between controllers - unexpectedly found nil while unwrapping an Optional - ios

I am attempting a very simple task of passing data between two view controllers. I have control-dragged the label into the proper class and allowed the segues to populate with the correct data. Upon clicking the row, the prepareForSegue action is as:
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
if segue.identifier == "individualChat" {
if let indexPath = tableView.indexPathForSelectedRow {
let friend: Friend
friend = mappedFriends[indexPath.row]
let controller = segue.destinationViewController as! IndividualChatController
controller.friendChat = friend
}
}
}
and into the residing view controller:
import UIKit
class IndividualChatController: UIViewController {
#IBOutlet weak var anotherTestLabel: UILabel!
var friendChat: Friend! {
didSet {
configureView()
}
}
func configureView() {
if let friendChat = friendChat {
anotherTestLabel.text = friendChat.name as? String
}
}
}
Yet, upon clicking the row, the error appears:
fatal error: unexpectedly found nil while unwrapping an Optional value
on line:
anotherTestLabel.text = friendChat.name as? String
How can this be fixed?
Additional Screen Capture:

When you execute some code in prepareForSegue the next view is not loaded yet so all your outlets are still set to nil; because of this when you try to implicitly unwrap the content of anotherTestLabel at
anotherTestLabel.text = friendChat.name as? String
you get that error.
You can check if your label has been loaded and only if that's the case set its text using
anotherTestLabel?.text = friendChat.name as? String
and then manually calling configureView() in viewDidLoad() of IndividualChatController to proper load the label.
The added ? will check if your label is nil, if so nothing will be done, else it will proceed unwrapping the label and setting the text.

Related

String not being passed to next view controller from prepareForSegue

I have a push segue on my StoryBoard which is named toGuestVC.
I use that to segue to the next ViewController in my didSelectRowAtIndexPath method like so:
override func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
let username = followUsernameArray[indexPath.row]
performSegue(withIdentifier: SG_TO_GUEST_VIEW_CONTROLLER, sender: username)
}
Then in my prepareForSegue:
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if segue.identifier == SG_TO_GUEST_VIEW_CONTROLLER {
if let nextVC = segue.destination as? GuestCollectionVC, let sender = sender as? String {
print("PRINTING NEXT VC: \(nextVC)") //This prints out the memory address. Not sure if this is what you meant by print nextVC.
nextVC.guestUser = sender
}
}
}
For some reason this line in my prepareForSegue is not running:
nextVC.guestUser = sender.username
When I try to print out the value guestUser in my nextViewController the value of guestUser is nil. But when I print out the value of sender in my prepareForSegue method it is not nil.
So is my sender value not being passed to the next ViewController? I can't find a solution to this problem any ideas?
GuestCollectionVC Implementation:
import UIKit
import Parse
private let reuseIdentifier = "Cell"
class GuestCollectionVC: UICollectionViewController {
var guestUser: String!
override func viewDidLoad() {
super.viewDidLoad()
print("PRINTING SELF IN GuestCollectionVC: \(self)")
loadPosts()
}
func loadPosts() {
//Load posts query
let query = PFQuery(className: PF_POSTS_CLASS)
query.limit = postCount
//Getting error here below this comment when I use guestUser since it is nil
query.whereKey(PF_POSTS_USERNAME_COLUMN, equalTo: guestUser)
query.findObjectsInBackground { (result: [PFObject]?, error: Error?) -> Void in
if error == nil {
if let result = result {
self.uuidOfPosts.removeAll(keepingCapacity: false)
self.imageArrayOfPFFIle.removeAll(keepingCapacity: false)
for postObject in result {
if let uuid = postObject[PF_POSTS_UUID_COLUMN] as? String, let pic = postObject[PF_POSTS_PIC_COLUMN] as? PFFile {
self.uuidOfPosts.append(uuid)
self.imageArrayOfPFFIle.append(pic)
}
}
self.collectionView?.reloadData()
}
}else if error != nil {
print("ERROR FROM GUEST COLLECTION VC FROM loadPosts FUNCTION: \(error?.localizedDescription)")
}
}
}
}
So this is my implementation in the GuestViewController. In my loadPosts method where I used the variable guestUser I am getting the error:
fatal error: unexpectedly found nil while unwrapping an Optional value
From console printing
PRINTING NEXT VC: "InstagramClone.GuestCollectionVC: 0x7a6c5cc0"
PRINTING SELF IN GuestCollectionVC: "InstagramClone.GuestCollectionVC: 0x7a6c5100"
it's now obvious that hidden unexpected instance of GuestCollectionVC was created. So, there are different errors occurs depending on order of this two objects invoke their viewDidLoad method (can be any order). Also there are can be other errors in nextVC viewDidLoad method, but this is other story for other question.
You got this problems because you created action segue, that works automatically on cell click (hidden view controller created), and at the same time you are perform this segue in code, creating second controller nextVC.
To solve issue, you should find and remove that segue and add new one, not action segue from some element of your view controller, but "empty" segue between view controllers. To create segue of this type you should select first view controller, hold "control" key and start dragging to next view controller from yellow square on top of first view controller (that symbol controller itself), choose "show", and set identifier.
Since I don't have the full context of the values within your tableView method I can only speculate. That said, the sender you're passing in should be the view controller:
performSegue(withIdentifier: "toGuestVC", sender: username)
You're passing in a value called username which looks to be a string value? It should be something like:
performSegue(withIdentifier: "toGuestVC", sender: self)
where self is your view controller. If you're passing in a string value to sender then in your prepareForSegue method then sender does not have a property called username sender actually is username. Therefore you should pass the value elsewhere:
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
let indexPath = self.tableView.indexPathForSelectedRow
if let nextVC = segue.destination as? GuestCollectionVC {
nextVC.guestUser = followUsernameArray[indexPath.row]
}
}

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
}
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.
Try to cast your phoneNumber in a string.
detailsVC.phoneNumberLabel.text = String(placeDetails.phoneNumber!)
Also, the nil value could come from the encoding method of the response of the API.
EDIT
I think you have to set the text of your UILabel in the viewDidLoad() method of your showDetailsVC. Declare a string variable in showDetailVC, and then pass your placeDetails.phoneNumber variable to the string you just declare. (Instead of directly set the text in the tapLabel() method). Then in your
showDetailVC set the text to your UILabel in the viewDidLoad()

Swift: unexpectedly found nil while unwrapping an Optional value While updating a Label

I have a UILabel inside a modelviewcontroller.
What i want to do is change its text on a certain point of my code. The problem is it works in the first point and stops working on the second one, throwing the following error:
unexpectedly found nil while unwrapping an Optional value
I found other questions on the same error but those solutions are not working for me. Maybe I am doing something wrong with the optionals.
Here is the code of my model view controller:
import UIKit
class CheckInViewController: UIViewController {
#IBOutlet weak var test: UILabel!
override func viewDidLoad() {
super.viewDidLoad()
}
override func viewDidAppear(animated: Bool) {
print("Appeared")
let storyboard = UIStoryboard(name: "Main", bundle: nil)
let vc = storyboard.instantiateViewControllerWithIdentifier("scanViewController") as! ScanViewController
test.text = "Here it works" // FIRST POINT WORKS AND CHANGES THE LABEL
if let qrCode = vc.qrCode{ // vc.qrCode IS AN OPTIONAL
qrRead(qrCode)
vc.qrCode = nil
}
}
func qrRead(qrCode: String) {
print(qrCode)
test.text = "Here it doesnt work" // HERE IT STOPS WORKING
}
You are setting the vc.qrCode to nil directly after passing it to the func qrRead(qrCode: String) function.
Remove this line: vc.qrCode = nil and check if the error persists.
If it does not, as I suspect, nil the var out elsewhere.
Remember using if let, you actually access the underlying instance, not a copy.
As such, when you set vc.qrCode to nil, you are ALSO setting qrCode to nil which causes issues when you try to print it in the qrRead function
Example:
class Person {
}
var a:Person? = Person()
if let some = a
{
print(unsafeAddressOf(some))
print(unsafeAddressOf(a!))
}
Prints:
0x0000000005011ed0
0x0000000005011ed0
Which shows they are the same instance in memory.

Passing NSManagedObject from one view controller to another

I have a uitableview which is filled with core data objects. I need to pass object for selected row to detail view controller. Following is my code for it:
Alert Screen:
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
if segue.identifier == "ShowAlertDetails" {
if let destination = segue.destinationViewController as? AlertDetailsViewController {
if let blogIndex = tblvwAlerts!.indexPathForSelectedRow()?.row {
let objAlert:Alert = arrReferrals[blogIndex] as! Alert
destination.objAlert = objAlert
}
}
}
}
Detail View Controller:
class AlertDetailsViewController: UIViewController {
#IBOutlet weak var tblvwHitDetail: UITableView?
var objAlert:Alert = Alert()
I am getting following error when I am trying to copy object from first page to detail page:
CoreData: error: Failed to call designated initializer on NSManagedObject class 'Alert'
The error is caused by calling Alert() method in var objAlert:Alert = Alert(). You can set Alert as an Implicitly unwrapped value var objAlert:Alert!. It will be initially nil and then it will hold the reference of the managed objected which is passed from the previous controller.

Can't UnWrap Optional - None Swift

I assure you that i have checked all the answers prior posting this question on Unwrapping an object, but this thing simply does not seem to work for me. I am simply trying to pass on a text value from my cell tapped to the Label on the next screen.
override func prepareForSegue(segue: UIStoryboardSegue!, sender: AnyObject!) {
if(segue.identifier == "detailViewSegue")
{
var DVC:DetailedViewController = segue.destinationViewController as DetailedViewController
let path:NSIndexPath = self.tableView.indexPathForSelectedRow()
var segueRecipeName:String = recipeMgr.RecipeName[path.row] as String
DVC.detailedRecipeLabel.text = segueRecipeName
}
}
The problem occurs at this line -
DVC.detailedRecipeLabel.text = segueRecipeName //Can't UnWrap Optional - None
I know I'm supposed to check the nil value of segueRecipeName before assigning. I do a print check on the console and it clearly is not null. The error occurs only when I'm assigning the value to the 2nd view controller class object. I'm sure this will help others learning Swift :)
DVC.detailedRecipeLabel is nil
Probably it's a IBOutlet that hasn't been loaded yet.
You should add a custom property to your DetailedViewController that will accept your text, save it and then you can assign it to the label in viewDidLoad.
Example:
class DetailedViewController ... {
...
var recipeName: NSString? = nil
...
override func viewDidLoad() -> () {
...
self.detailedRecipeLabel.text = self.recipeName
}
}
DVC.recipeName = segueRecipeName

Resources