Swift - Why is my application finding nil in this scenario - swift

I have an tableView which loads cells from a xib file. The cells contain 2 labels, 1 shows the quantity and the second shows the price. The quantity is pulled from a static model and works just fine, and the price is pulled from a model which is populated from the result of a URLSession. Below is my cellForRowAt function which works fine in this scenario:
if indexPath.section == 0 {
let item: BasketModel = cellItems[indexPath.row] as! BasketModel
let ordersCell = Bundle.main.loadNibNamed("OrderTableViewCell", owner: self, options: nil)?.first as! OrderTableViewCell
ordersCell.priceLabel.text = item.price
ordersCell.quantityLabel.text = String(basketStruct.getQty(id: item.id!))
return ordersCell
}
My cell shows as follows Qty: 3 Price: 0.70
Now, the problem I have is, when i alter the line above where I set the price label to the following:
ordersCell.priceLabel.text = String(basketStruct.getQty(id: item.id!) * Int(item.price!)!)
I get the error:
Thread 1: Fatal error: Unexpectedly found nil while unwrapping an Optional value
What I want, is the qty to be multiplied by the price. However, when I try to use item.price for anything I have to force unwrap it and then I get my error.
How would I go about this ?

It is not that item.price is nil, but Int(item.price!) is nil.
From your question, you said that the price displayed is 0.70, so I suppose item.price must contain the string "0.70".
Well, 0.70 is not a valid Int, so when you try to parse it as an Int, it evaluates to nil.
I think you meant Double(item.price!)!.
As a good practise, you should always check for invalid number strings:
if let price = Double(item.price!) {
// ...
} else {
print("Invalid price: \(item.price!)")
}

Related

Updating CoreData value with UIStepper effect per cell

Hopefully someone can help me... I am passing an item from the web into coredata, I am passing the values with a click of a button.. as soon as the button gets pressed then number of items and cost are immediately passed on to Coredata attributes. e.i price: 30.00, itemQuantity 1 (all well and good here per cell)
these items are then displayed on a cellForRowAt along side with a UIStepper to change the values of the item per cell. take a look:
var itemsArray: [ItemModel] = []
let currentPrice = itemsArray[indexPath.row].price
let currentQty = itemsArray[indexPath.row].itemQuantity
let totalQty = currentQty + Int16(sender.value)
let totalPrice = Double(currentPrice) * sender.value
itemsArray[indexPath.row].setValue(totalPrice, forKey: "price")
itemsArray[indexPath.row].setValue(totalQty, forKey: "itemQuantity")
let context = CoreDataManager.shared.persistentContainer.viewContext
do {
try context.save()
} catch {
print("Failed to save main context: \(error)")
}
Theoretically this would work just find.. and it does! if I don't use Coredata and just plain UI...
But since I am using CoreData... here is what's actually happening (I bet you can already tell): I am re-using the itemsArray which initial value per item is already 30.00, so when I multiply the price with a the new sender.value now equals to 2 i'll get 60.00 which is great but NOW the price in coredata is 60 and new itemQuantity is 2 so when i click the + on the Stepper it all gets multiplied now three times the updated value, so it all gets messed up... because Im updating the values with the same previously-updated-values.. so.. yea.. I'm stuck : / How can I make this right?

Swift - Changing constant doesn't show error

I'm reading a tutorial from a book. I can't attach the book here. In a chapter an UIImage constant is declared and its value is assigned in next lines. It is not a var neither optional. It runs successfully. How does it work?
extension ViewController: MKMapViewDelegate {
private func addAnnotations() {
for business in businesses {
guard let yelpCoordinate = business.location.coordinate else {
continue
}
let coordinate = CLLocationCoordinate2D(latitude: yelpCoordinate.latitude,
longitude: yelpCoordinate.longitude)
let name = business.name
let rating = business.rating
let image:UIImage //Constant non-optional
switch rating {
case 0.0..<3.5:
image = UIImage(named: "bad")!
case 3.5..<4.0:
image = UIImage(named: "meh")!
case 4.0..<4.75:
image = UIImage(named: "good")!
case 4.75..<5.0:
image = UIImage(named: "great")!
default:
image = UIImage(named: "bad")!
}
let annotation = BusinessMapViewModel(coordinate: coordinate,
name: name,
rating: rating, image: image)
mapView.addAnnotation(annotation)
}
}
}
First, you should know that it is perfectly fine in Swift to declare a variable and assign it a value on the next line, as long as you don't refer to that variable before it is assigned.
let a: Int
... // you can't use "a" here
a = 10 // OK!
Look at the switch statement after the variable declaration. A switch statement must be exhaustive, meaning that at least one case of it will be run. In this switch statement, every case has a single statement that assigns to image, and there are no fallthroughs. From these observations, both us and the compiler can conclude that image will be assigned (and assigned only once) after the switch statement, hence you can use it in the line:
let annotation = BusinessMapViewModel(coordinate: coordinate,
name: name,
rating: rating, image: image)
From the Swift 5.1 reference, here
When a constant declaration occurs in the context of a function or method, it can be initialized later, as long as it is guaranteed to have a value set before the first time its value is read.

Accessing Firebase Data inside unique AutoID

This is my first question and I'm still learning Swift/Xcode/Firebase, so I appreciate your patience. I've been stalking StackOverflow and have found a lot of answers to help with various things, but nothing that makes sense for the problem I've been struggling with for 2 days.
I am writing a program that will save a date picked on a previous viewcontroller and a set of user-entered floats from text fields to a Firebase database, and append each data set as a separate entry instead of overwriting the previous data. Using the first block of code below, I've got this problem solved except I can't find a way to do it without using AutoID. This leaves me with a setup like this in Firebase, but with multiple categories and "optionSelected" sections in each category:
program-name
Category 1
optionSelected
L1cggMnqFqaJf1a7UOv
Date: "21-12-2017"
Variable 1 Float: "12345"
Variable 2 Float: "26.51"
L1ciVpLq1yXm5khimQC
Date: "30-12-2017"
Variable 1 Float: "23456"
Variable 2 Float: "35.88"
Code used to save:
func newWithNewVars() {
let myDatabase = Database.database().reference().child("Category 1").child(optionSelected)
let variable1 = textField1.text
let variable2 = textField2.text
let variable1Float = (textField1.text! as NSString).floatValue
let variable2Float = (textField2.text! as NSString).floatValue
let writeArray = ["Date": textPassedOverDate, "Variable 1 Float": variable1Float, "Variable 2 Float": variable2Float]
myDatabase.childByAutoId().setValue(gasArray) {
(error, reference) in
if error != nil {
print(error!)
}
else {
print("Message saved successfully!")
}
}
}
The problem comes with recalling data. Since the AutoID is unique, I can't figure out how to access the data deeper inside for calculations. Specifically, I want to be able to make a new entry, press the save data button, and have it find the most recent entry in the "optionSelected" section so it can do calculations like subtract the older variable 1 from the new variable 1 and such.
Given the above description, layout, and code used above, what code structure would allow me to find the most recent date and access the data inside the AutoID sections for a specific category and "optionSelected"?
Thank you for your help.
The issue you're having is that you're trying to dig deeper but can't as you don't have a hold of that id. You'll want to use the .childAdded in your reference observation when you want to get inside of a list in your JSON tree when you don't have a hold of that id to get inside - this will be called as many times as there are values inside of Category 1 tree:
let reference = Database.database().reference()
reference.child("Category 1").child("optionSelected").observe(.childAdded, with: { (snapshot) in
let uniqueKey = snapshot.key // IF YOU WANT ACCESS TO THAT UNIQUE ID
print(uniqueKey)
guard let dictionary = snapshot.value as? [String: AnyObject] else { return }
let date = dictionary["date"] as? String
let variableOne = dictionary["Variable 1 Float"] as? Float
let variableOne = dictionary["Variable 2 Float"] as? Float
}, withCancel: nil)
You may also want to avoid using spaces in your database keys to avoid any problems in the near future. I'd stick with the common lowercased underscore practice e.g. "category_1" or "variable_2_float"

Performing functions with valueForKey

I am trying to get my app to perform functions. I have two attributes per item (quantity and price) that I want to multiply together and then total for all the didSelectRow items on the list. There is two sections on my tableView. Section 0 is regular and moved to section 1 with didSelectRow. (I only explain this because it comes into play further down)
My code so far is...
`func cartTotalFunc() {
itemFetchRequest().returnsObjectsAsFaults = false
do {
let results = try moc.executeFetchRequest(itemFetchRequest())
print("===\(results)")
// Calculate the grand total.
var grandTotal = 0
for order in results {
let SLP = order.valueForKey("slprice") as! Int
let SLQ = order.valueForKey("slqty") as! Int
grandTotal += SLP * SLQ
}
print("\(grandTotal)")
cartTotal.text = "$\(grandTotal)" as String
} catch let error as NSError {
print(error)
}
}
`
slprice and slqty are strings in Core Data. I am trying to cast them as Int so they will do the arithmetic. I had this working but it totaled every item instead of only the crossed off ones (section 1). I gave it a rest for a while and now when I come back to try to work on it again Xcode is giving me an error of, "can not Could not cast value of type 'NSTaggedPointerString' (0x104592ae8) to 'NSNumber' (0x1051642a0)."
Can anyone help with this, please?

Why in swift are variables option in a function but not in playground

I am puzzled. I need to compare product date codes. they look like 12-34-56. I wrote some code to break the parts up and compare them. this code works fin in the play ground. But when i make it a function in a view controller values come up NIL and i get a lot of "Optional("12-34-56")" values when printed to the log or viewed in a break. I tried unwrapping in many locations but nothing takes.? don't be confused by the variables date and month because they are not product codes can have 90 days and 90 months depending on the production machine used.
func compaireSerial(oldNumIn: NSString, newNumIn: String) -> Bool {
// take the parts of the number and compare the pics on at a time.
// Set up the old Num in chunks
let oldNum = NSString(string: oldNumIn)
let oldMonth = Int(oldNum.substringToIndex(2))
let oldDay = Int(oldNum.substringWithRange(NSRange(location: 3, length: 2)))
let oldYear = Int(oldNum.substringFromIndex(6))
print(oldMonth,oldDay, oldYear)
// Set up the new Num in chunks
let newNum = NSString(string: newNumIn)
let newMonth = Int(newNum.substringToIndex(2))
let newDay = Int(newNum.substringWithRange(NSRange(location: 3, length: 2)))
let newYear = Int(newNum.substringFromIndex(6))
print(newMonth, newDay, newYear)
// LETS Do the IF comparison steps.
if oldYear < newYear {
return true
} else if oldMonth < newMonth {
return true
} else if oldDay < newDay {
return true
} else {
return false
}
}
May thanks to any one. Im totally stumped
All Int() initializers with String parameters return always an optional Int.
The realtime result column in a Playground doesn't indicate the optional but printing it does.
let twentyTwo = Int("22") | 22
print(twentyTwo) | "Optional(22)\n"
I don't see how i can delete my question so ill post this to let others know it is fixed. Turns out the auction works okay but the NSUserDefaults value coming in was optional. So i was feeding the optional in. After unwrapping the NSUser value all works.