Can't remove optional("String"). states its non-optional - swift

I'm working on a spendings tracker app. All the logic is now working, but when I want to display transaction data in a UILable, it displays it as optional("String")
I have looked around the Internet and have tried unwrapping the string in 2 different ways, but I'm not able to fix it.
Adding an ! to the end of the string gives an error Cannot force unwrap value of non-optional type "String"
Here is the code I'm using now that displays optional("String")
Here I set up my struct and array
struct Transaction {
var discr = ""
var amount = 0
}
var transactions = [Transaction]()
This is how I add data to the array
transactions.append(Transaction( discr: String(describing: transDescrInput.text), amount: Int(tempAmount)))
This is how I display the data in a tableview
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = transTable.dequeueReusableCell(withIdentifier: "sCell")
let discrText = transactions[indexPath.row].discr.uppercased()
cell?.textLabel?.text = "€\(transactions[indexPath.row].amount)"
cell?.detailTextLabel?.text = "\(discrText)"
return cell!
}
This is how it shows up in the app
Iphone simulator screenshot

The problem is already where you add data to the array.
Assuming that transDescrInput.text is an optional string,
String(describing: transDescrInput.text)
returns a non-optional string "Optional(text...)" and there is
no sensible way to revert that. You should use optional binding
or other unwrapping mechanisms instead, for example
if let text = transDescrInput.text {
transactions.append(Transaction(discr: text, amount: Int(tempAmount)))
}
or with nil-coalescing:
transactions.append(Transaction(discr: transDescrInput.text ?? "", amount: Int(tempAmount)))
As a rule of thumb, String(describing:) almost never the correct
solution (even if the compiler suggest it as a Fix-it), it only hides
the actual problem.

Right after posting this post I realised I have to unwrap the text before I add it to my array. So I changed the way I save the string:
transactions.append(Transaction( discr: String(describing: transDescrInput.text!), amount: Int(tempAmount)))
I added an ! behind the transDescrInput.text to unwrap it before I save it to my array.

May I suggest do something like this?
let discrText = transactions[indexPath.row].discr.uppercased()
cell?.detailTextLabel?.text = "\(discrText!)"

Related

Nill value when passing data beetween related entities in Core Data

I'm trying for some days now (without success) saving on Core Data some properties of an entity ("Alumno") just after saving on its "children" viewController ("Especialidad") its related value.
Mine is a music teaching app, just as a contacts one. I pass the selected "Especialidad" (the played instrument in this case), after saving it to Core Data, for the correspondent "segue" to be performed, from its table view
override func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
especialidadSeleccionada = especialidades[indexPath.row]
performSegue(withIdentifier: "GuardarEspecialidad", sender: self)
}
to its related "parent", the complete pupil's data, containing this "Especialidad", among others properties.
Next, I try to save every pupil's data on Core Data:
if nombreField.text != "" && especialidadField.text != "" {
let context = (UIApplication.shared.delegate as!
AppDelegate).persistentContainer.viewContext
let nuevoAlumno = Alumno(context: context)
nuevoAlumno.nombre = nombreField.text!
print("Data before: \(especialidadSeleccionada)")
nuevoAlumno.especialidadRelacionada?.nombre = especialidadSeleccionada?.nombre
print("Data after: \(nuevoAlumno.especialidadRelacionada?.nombre)")
//... and so on with the rest of properties
do {
try context.save()
} catch {
print("error")
}
But it saves every property EXCEPT "nuevoAlumno.especialidadRelacionada?.nombre", marked as nill. As you can see, for debugging purposes, I print its value BEFORE the equality, obtaining a non-optional value, and AFTER it, getting... an optional value!!
I'm almost sure that it has to be something about their relationship. I've double-checked that "Especialidades" exact data is correct, but I really can't find the way to save "especialidad.nombre" in this "Alumno" entity...
An idea: maybe using "didSet" for that property of "Alumnos" could help? I've tried, but don't know how.
Any help? Thanks in advance!
I have reached to a solution. I was just using an incorrect property syntax:
nuevoAlumno.especialidadRelacionada = especialidadSeleccionada
Now it works.
Looks to me like nuevoAlumno.especialidadRelacionada might be nil. Note that you're printing two different things in your before/after. I'd say set a breakpoint on this line:
nuevoAlumno.especialidadRelacionada?.nombre = especialidadSeleccionada?.nombre
and inspect all the values there.

Getting current NSPopUpButton's selection with titleOfSelectedItem returns "optional value"

Edit: I figured out how to prevent this, but I still have a question as to why it returns an optional value. You may skip to the end.
I'm quite new to Swift, and I'm getting behavior I can't understand. Let's say I drag a popup button called myButton into the ViewController. I want to print the currently selected item to the console. Here's how I would do it:
#IBOutlet weak var myButton: NSPopUpButton!
override func viewDidLoad() {
super.viewDidLoad()
let myVariable = myButton.titleOfSelectedItem
print(myVariable)
// Do any additional setup after loading the view.
}
I expect Item1 to be printed, as that's the option selected by default when the view loads. However, I actually get Optional("Item 1").
This has been causing a few problems for me. For example, the print(myVariable) line gives me this cryptic error:
Expression explicitly coerced from 'String?' to Any
Also, I can't do something like this:
if myButton.titleOfSelectedItem == "Item1" || "Item3" {
let currentSelection = "odd"
} else {
let currentSelection = "even"
}
as I get a bunch of errors – because of the || and the else as far as I can tell, even though I think it should work fine.
I've tried searching for why this occurs, but couldn't find any explanations. From the warning, it seems like when I get the selection with titleOfSelectedItem, it gives me an optional value. This doesn't make sense, as a menu item has to be selected. Its value can't be nil.
I looked up a bunch of tutorials to see how they implement popup button functionality. The only thing I could see was when someone
Made an array
Removed all items from the popup button with func removeAllItems()
Added items to the popup button from the array with func addItems(withTitles: [String])
Obtained the index of the selected item with var indexOfSelectedItem: Int
Retrieved the respective value from the array
and then used that. This, however, seems unnecessarily complicated and I can't understand why I wouldn't be able to get just the title of the selected popup item simply with myButton.titleOfSelectedItem. Can anyone suggest to me what to do?
Edit:
So I realized you need to "unwrap" the optional value to make it a normal value, which is as simple as adding a ! to the end of the variable, as follows:
#IBOutlet weak var myButton: NSPopUpButton!
override func viewDidLoad() {
super.viewDidLoad()
let myVariable = myButton.titleOfSelectedItem!
print(myVariable)
// Do any additional setup after loading the view.
}
Now there's no error, and Item1 is printed.
What I can't yet understand is, why was an optional value printed in the first place? There are three items in the NSPopUpButton. Any one of the three has to be selected. There's no opportunity for myButton.titleOfSelectedButton to be nil. Why then do I need to unwrap it to use it as a string with myButton.titleOfSelectedButton! if it's not optional?
titleOfSelectedItem returns an optional because no item could be selected.
You need optional bindings to unwrap the optional safely and you have to evaluate both "Item1" and "Item3" against the title string:
if let title = myButton.titleOfSelectedItem {
print(title)
let currentSelection : String
if title == "Item1" || title == "Item3" {
currentSelection = "odd"
} else {
currentSelection = "even"
}
}
In Objective C you can do it in awakeFromNib:
[[your_NSView your_NSPopUpButton] selectItemAtIndex:your_Int];
... of course, you must have declared 'your_NSView' and 'your_NSPopUpButton' as properties.

How to fetch core data objects into Dictionary in Swift?

I've saved objects in core data, and I am looking how to fetch those objects as a Dictionary
Here is an example of my code where sections is keys for the dictionary and Company as an array of core data objects.
private var companies = Dictionary<String, Array<Company>>()
private var sections: [String] = ["Pending", "Active", "Pending"]
override func viewWillAppear(_ animated: Bool) {
let fetchRequest : NSFetchRequest<Company> = Company.fetchRequest()
let moc = DatabaseController.getContext()
do {
let request = try moc.fetch(fetchRequest)
for case let (index, object) in request.enumerated() {
companies[sections[index]]!.append(object)
}
} catch let error as NSError {
print("Could not fetch. \(error), \(error.userInfo)")
}}
When I am trying to execute my code, I have an error:
fatal error: unexpectedly found nil while unwrapping an Optional value
Could anyone help me with that issue?
That error message means that you're force unwrapping an optional that doesn't have a value. In other words you're using ! where you shouldn't. (You should basically never use the force unwrap operator (!).)
Let's look at the line where you do that:
companies[sections[index]]!.append(object)
If we break this down and add the inferred types we have:
let section: String? = sections[index]
let companyArray: Array<Company> = companies[section]!
You're crashing because companies starts off empty, so asking for any of the arrays will return nil. (Actually, I'm not sure how your code is compiling, since you can't subscript into the dictionary with an optional.)
However, if you fix that, we still have a problem because you're using the index of the fetched array to look up the section. If you have more than three companies, that will start to fail.
I suspect you want something like:
for let company in result {
if var companyArray = companies[company.status] {
companyArray.append(company)
} else {
companies[company.status] = [company]
}
}
Where status is an invented property on Company that returns a String like "Pending" or "Active".
I've found the solution, just need to use NSFetchResultController in order to display data in TableView by different sections.

Swift String Interpolation displaying optional?

When i use the following code and have nameTextField be "Jeffrey" (or any other name)
#IBAction func helloWorldAction(nameTextField: UITextField) {
nameLabel.text = "Hello, \(nameTextField.text)"
}
nameLabel displays... Hello, Optional("Jeffrey")
But, when I change the previous code to include a "!" like this:
#IBAction func helloWorldAction(nameTextField: UITextField) {
nameLabel.text = "Hello, \(nameTextField.text!)"
}
The code works as expected and nameLabel displays.... Hello, Jeffrey
Why is the "!" required, in the video tutorial I used to create this simple program he did not use the "!" and the program worked as expected.
Another alternative is to use the null coalescing operator within the interpolated string for prettier text without the need for if let.
nameLabel.text = "Hello, \(nameTextField.text ?? "")"
It's less readable in this case, but if there were a lot of strings it might be preferable.
Optionals must be unwrapped. You must check for it or force unwrap as you do. Imagine the optional as a box where you put a value. Before you can access it, you need to put it out.
if let name = nameTextField.text {
nameLabel.text = "Hello, \(name)"
}
Here's a handy extension to unwrap Any? to String.
Set a default value for nil values.
extension String {
init(_ any: Any?) {
self = any == nil ? "My Default Value" : "\(any!)"
}
}
// Example
let count: Int? = 3
let index: Int? = nil
String(count)
String(index)
// Output
// 3
// My Default Value
You can also use optional map.
This is where I learned of how to use it.
Basically, map will take an optional and return a value if there's a value and return nil if there's no value.
It think this makes more sense in code, so here's the code I found useful:
func getUserAge() -> Int? {
return 38
}
let age = getUserAge()
let ageString = age.map { "Your age is \($0)" }
print(ageString ?? "We don't know your age.")
I guess this may not be super helpful in the case where you're passing in an optional string, (nil coalescing works just fine in that case), but this is super helpful for when you need to use some value that isn't a string.
It's even more useful when you want to run logic on the given value since map is a closure and you can use $0 multiple times.

Swift: strange behavior about unwrapping

In my Child model, I have declared the following properties:
var name:String?
var year:String?
var make:String?
var model:String?
and the init:
init(name:String, ... ,year:String, make:String, model:String, ...){
self.name = name
...
self.year = year
self.make = make
self.model = model
}
Here I construct a child:
Child(name:cName,...,year:cYear,make:cMake, model:cModel,...)
In
func tableView(tableView: UITableView!, cellForRowAtIndexPath indexPath: NSIndexPath!) -> UITableViewCell!{
For the child's name I could use it directly without unwrapping:
cell!.textLabel.text = child.name
But I have to unwrap child.year, child.make, child.model if I want to use them. Why is that?
cell!.detailTextLabel.text = child.year! + " " + child.make! + " " + child.model!
I followed this tutorial and build the project successfully. In that project, in a similar situation, I don't need to do unwrapping. Code
cell.text = album.title
cell.image = UIImage(named: "Blank52")
cell.detailTextLabel.text = album.price
You need an explicit unwrap because you are using concatenation operator. Concatenation is not defined for optional strings, so you need to add exclamation point to unwrap the values. You should be able to avoid it if you use string interpolation, like this:
cell!.detailTextLabel.text = "\(child.year) \(child.make) \(child.model)"
Note: if the initializer that you show is the only one available, you should be able to make your strings non-optional.