prepare segue unexpectedly found nil while unwrapping an Optional value - ios

Yes, another found nil while unwrapping an Optional value error. I have read tons of other stack overflow posts with similar errors such as this one and many others. I still do not fully understand how to properly deal with unwrapping a variable.
I have a class that is similar to the following:
#IBOutlet weak var nameTextField: UITextField?
#IBOutlet weak var valueInput: UITextField?
var checkbox : CheckBox?
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
let name = nameTextField.text ?? ""
let state = buttonState.getIsChecked()
let value : Int? = Int(valueInput.text!)
let isMoveable = true
checkbox = CheckBox(name: name, value: value, state: state, isMoveable: isMoveable)
}
I get the error on the line the "let value : Int? = Int(valueInput.text!) line.

You can safely unwrap the value using if let construct
var value : Int? = nil
if let val = valueInput.text {
value = Int(val) // it would either nil or optional value
}
and also you can do it by nil coalescing operator ?? in a single line
let value : Int? = Int(valueInput.text ?? "")
UPDATE
First check if textfields disconnected from the Interface Builder , if not connect, connect them. and if you become your textfields optionals you also have to safely unwrap the textfields ( you forgot to add it from interface builder and it will not crash if you make them optionals).
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
var name: String? = nil
var value : Int? = nil
let state = buttonState.getIsChecked()
let isMoveable = true
if let nameTextField = self.nameTextField where nameTextField.text != nil {
name = nameTextField.text!
}
if let valueTextField = self.valueInput where valueTextField.text != nil {
value = Int(valueTextField.text!)
}
checkbox = CheckBox(name: name, value: value, state: state, isMoveable: isMoveable)
}

Sahil's answer is on track, but doesn't address that valueInput is also an optional. Use this code:
if let valueInput = valueInput,
let val = valueInput.text {
value = Int(val)
}
In addition regarding properly unwrapping the optional valueInput I wanted to add that chances are that if valueInput is an IBOutlet it's defined as:
#IBOutlet weak var valueInput: UITextField!
That's called an implicitly unwrapped optional. The annoying thing is that since it is also an IBOutlet, if it ever becomes disconnected from the UITextField in Interface Builder, the variable will become nil, and accessing it from anywhere in the code will cause a crash.
Change it to this:
#IBOutlet weak var valueInput: UITextField?
I've also written about this on my blog: http://cleanswifter.com/implicitly-unwrapped-iboutlets/
Note: you didn't show the definition of valueInput so I assumed it to be a UITextField

Related

How do I find the Max from a group of user inputed Ints using Xcode 11 and swift

Here's what I have so far:
import UIKit
class ViewController: UIViewController {
#IBOutlet weak var game1: UITextField!
#IBOutlet weak var game2: UITextField!
#IBOutlet weak var game3: UITextField!
#IBOutlet weak var series: UILabel!
#IBOutlet weak var average: UILabel!
#IBOutlet weak var high: UILabel!
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view.
}
#IBAction func calculate(_ sender: Any) {
let game1Results = Int(game1.text!)
let game2Results = Int(game2.text!)
let game3Results = Int(game3.text!)
let gameResultsArray = [game1Results, game2Results, game3Results]
high.text = "\(gameResultsArray.max())"
}
}
I've been trying to use the .max function but I'm getting errors stating I need to refer to conform to "Comparable". I'm pretty new to swift, any help will be much appreciated.
It happens because you try to compare values of optional Int (Int?)
First of all, you should know that not each String can be converted to an Int
E.g. '42' will be converted correctly, but 'ie42zx' can't be converted to and Int - that's why Int(String) function returns optional Int (Int?)
'Optional' property says like 'i can have a value, but I can also be nil'. That's why you should unwrap optionals in your code
First, I suggest to avoid force unwraping here.
let game1Results = Int(game1.text!)
It can be done like that:
guard
let text1 = game1.text,
let text2 = game2.text,
let text3 = game3.text
else { return }
You should do that because not every text field contains text, so textField.text property returns an optional String?
Then you can convert your String results to an Int and unwrap these results before compare:
guard
let game1Results = Int(text1),
let game2Results = Int(text2),
let game3Results = Int(text3)
else { return }
I would suggest to read more about optionals in swift and unwrapping (see Optionals paragraph)
I believe your issue is that the Int constructor with a string returns an optional integer value. You're trying to take the max of a bunch of optionals, so it cant figure it out.
You can use the compactMap function on your array before calling max() to filter out the nil values.
gameResultsArray.compactMap{ $0 }.max()

Cannot assign value of type 'Int' to type 'Int?.Type' Swift 5

I am trying to pass a value from one view controller to another of type Int.
Here is how I am invoking my sugue:
if questionNumber + 1 == quizBrain.quiz.count{
self.performSegue(withIdentifier: "goToScore", sender: self)
}
My prepare function is:
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if segue.identifier == "goToScore" {
let destinationVC = segue.destination as! ResultViewController
destinationVC.finalScore = quizBrain.getScore()
}
}
Here is my destination view class:
import UIKit
class ResultViewController: UIViewController {
var finalScore = Int?
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view.
}
quizBrain.getScore() fetches a value of type Int. I am trying to pass this value and catch it in the other view in var finalScore.
The error I am getting is:
Cannot assign value of type 'Int' to type 'Int?.Type'
I am not sure what this particularly means, I am new to Swift and was unable to find something similar with a type Int?.Type. I am also running into a similar issue if I try passing a String in a different project.
Changing var finalScore = Int? to var finalScore:Int = 0 fixed it! Not sure if this is a Swift version issue, would be helpful if somebody could confirm why this worked.
An optional in Swift is a type that can hold either a value or no value. Optionals are written by appending a ? to any type:
You should define an optional integer like:
var finalScore: Int? //now the value of finalScore can be nil or any integer value etc.
While using the option values, there are couple of ways:
if finalScore != nil {
print(finalScore!)
}
or
guard let score = finalScore else {
return
}
print(score)
or
print(finalScore ?? 0)
For more info, you can refer Apple Doc: https://developer.apple.com/documentation/swift/optional
Swift basics: https://docs.swift.org/swift-book/LanguageGuide/TheBasics.html#//apple_ref/doc/uid/TP40014097-CH5-XID_399

Add value dictionary to a label after getting data from prepare segue

In the prepare segue, I add data to a dictionary in the other controller, here is that dictionary:
var data = [String:String]()
and in prepare segue I add value and key to the dictionary
movieDetail.data.updateValue("title", forKey: self.animationItems[index!].snippet.title)
movieDetail.data.updateValue("description", forKey: self.animationItems[index!].snippet.description)
Is it right?
Then, there is an outlet in the new controller:
#IBOutlet weak var movieTitle: UILabel!
this title should get the value(title) of the dictionary
I did that:
override func viewDidLoad() {
super.viewDidLoad()
movieTitle.text = data[title]?
}
but it shows an error:
Cannot subscript a value of type '[String : String]' with an index of type 'String?'
Could you help me on that?
It should have been:
override func viewDidLoad() {
super.viewDidLoad()
movieTitle.text = data["title"]
}
You should have added the "" to denote a string literal.
Your prepareForSegue is also wrong. The two arguments for updateValue are the other way around. To avoid confusion, you should just always use the dict[key] = value syntax:
movieDetail.data["title"] = self.animationItems[index!].snippet.title
movieDetail.data["description"] = self.animationItems[index!].snippet.description
Anyway, you should use a class/struct to represent your movies, then these problems will go away.
struct Movie {
var title: String
var description: String
}
// in MovieDetailController
var movie: Movie!
// in prepareForSegue
movieDetail.movie = Movie(title: self.animationItems[index!].snippet.title, description: self.animationItems[index!].snippet.description)
// in viewDidLoad
movieTitle.text = movie.title

Cannot invoke initializer for type 'Double' with an argument list of type '(String?)' - Swift Storyboard

Getting this error trying to convert to a double. Any ideas why?
class ViewController : UIViewController {
#IBOutlet var textField : UITextField!
#IBOutlet var answerButton : UIButton!
#IBOutlet var fahrenheitLabel : UILabel!
#IBAction func tempFieldEditingChange(_ textField: UITextField) {
fahrenheitLabel.text = textField.text
}
#IBAction func showAnswer (_ sender : UIButton) {
let temperatures = ["hot","warm","cool","cold"]
let thresholds : [Double] = [80,60,40,0]
let temperature = Double(textField.text) //<-- (ERROR)
for (i,threshold) in thresholds.enumerated() {
if temperature >= threshold {
fahrenheitLabel.text = temperatures[i]
break
}
}
}
}
Trying to take user input of a temperature and spit out a label with hot warm cool or cold. I know theres something I'm missing please help! Thank you.
textField.text is an optional String, so you need to unwrap it before passing the value to a function that doesn’t accept an optional, such as the Double(_ String:) initialiser.
I would use a guard statement to do so. The string may not be able to be parsed as a Double, so that initialiser also returns an optional, which needs to be unwrapped.
#IBAction func showAnswer (_ sender : UIButton) {
guard let text = textField.text else {
return
}
let temperatures = ["hot","warm","cool","cold"]
let thresholds : [Double] = [80,60,40,0]
if let temperature = Double(text) {
for (i,threshold) in thresholds.enumerated() {
if temperature >= threshold {
fahrenheitLabel.text = temperatures[i]
break
}
}
}
}
}
The UITextField.text property returns an optional String? type. The Double's initializer requires a regular String.
To use the text property, you must first "unwrap" it, i.e. transform it from an optional value into a non-optional. There are several ways to do it:
Forced unwrapping
If you are certain, that the text property is not nil, you may forcibly unwrap it. Be mindful though, as when you try to forcibly unwrap a nil value, your app will crash.
if textField.text != nil {
let temperature = Double(textField.text!)
}
In this case, the text property should never be nil. However, if there were some code changing the property inside the if statement and before the line where the property is forcibly unwrapped, the forced uwrapping might crash.
Optional binding (the preferred way)
This method lets you unwrap the property safely by binding its value to another constant/variable, and once the value is bound, it can be freely used without the possibility of it becoming nil.
if let temperatureValue = textField.text {
let temperature = Double(temperatureValue)
}
The unwrapped temperatureValue constant will remain available and non-optional throughout the whole if-let scope, meaning that up to the closing brace of the if-let statement you can use it freely and will be gone outside the statement's braces. If the textField.text is nil, the inside of the statement's braces will never be executed.
Instead of if-let, you might use the guard-let statement:
guard let temperatureValue = textField.text else {
return
}
let temperature = Double(temperatureValue)
Notice however that any guard statement requires the function to return if the statement fails, but the unwrapped value can be accessed normally in the rest of the function, not only in a closing braces of a statement, like with if-let.
Last thing: the Double's initializer that takes a String also returns an optional value - so in order to use it (e.g. compare to other Double values), you must unwrap it as well:
if let temperature = Double(temperatureValue) {
// compare "temperature" to other Double values
}
This should work with if let way
let atextField = UITextField(frame: .zero)
atextField.text = "55.9"
if let d = Double(atextField.text!) {
print(d)
} else {
print("no")
}
You need to unwrap .text. Here is how I would do it:
class ViewController : UIViewController {
#IBOutlet var textField : UITextField!
#IBOutlet var answerButton : UIButton!
#IBOutlet var fahrenheitLabel : UILabel!
#IBAction func tempFieldEditingChange(_ textField: UITextField) {
fahrenheitLabel.text = textField.text
}
#IBAction func showAnswer (_ sender : UIButton) {
guard let text = textField else {
fatalError("Handle case when textField.text is nil") // TODO
}
guard let temperature = Double(textField) {
fatalError("Handle case when textField.text is nonnil but not a double") // TODO
}
switch temperature {
case ..<40: textField.text = "cold"
case 40..<60: textField.text = "cool"
case 60..<80: textField.text = "warm"
case 80...: textField.text = "hot"
default: fatalError("Non-exhaustive temperature ranges!")
}
}
}

Passing Int property from textfield back to controller

I am trying to pass data from textfield, back to previous controller using delegation. I am stuck when I try to assign Int value in this call.
This problem is fairly easy, however I cannot find a simple solution. I have been trying different approaches with additional properties that should hold this value, but with no succeed. What do I have to do with this budgetAmount.text to be properly converted?
protocol BudgetDelegate: class {
func enteredBudgetData(info: String, info2: Int)
}
class AddBudgetViewController: UIViewController {
var budget: Budget?
weak var delegate: BudgetDelegate? = nil
#IBOutlet weak var budgetName: UITextField!
#IBOutlet weak var budgetAmount: UITextField!
//
#IBAction func saveContent(_ sender: UIButton) {
if ((budgetName.text?.isEmpty)! && (budgetAmount.text?.isEmpty)!) {
navigationController?.pop(animated: true)
} else {
-> delegate?.enteredBudgetData(info: budgetName.text!, info2: budgetAmount.text!)
navigationController?.pop(animated: true)
}
}
}
Error Cannot convert value of type 'String' to expected argument type 'Int'
The info2 parameter of your protocol method is of type Int but you are passing budgetAmount.text! which is of course a String. You need to pass an Int.
Perhaps you need to convert the text in the textfield to an Int.
delegate?.enteredBudgetData(info: budgetName.text!, info2: Int(budgetAmount.text!) ?? 0)
BTW - you are making several terrible uses of the ! operator. You should spend time learning about optionals and how to safely work with then.
So based on your question, you just want to pass an Int data coming from a UITextField. And based on your description, you do not have any problem with delegation.
Converting a String into an Int is easy:
Example:
let num = "1"
if let intNum = Int(num) {
// There you have your Integer.
}

Resources