Creating a calculator app - swift

So I'm very new to Swift and have been following this tutorial to make this app https://www.youtube.com/watch?v=NJHsdjH2HdY
This was the first problem: currentNumber = currentNumber * 10 + Float(sender.titleLabel!.text!.toInt()!)
In the comments section the guy said to change that line to:
currentNumber = currentNumber * 10 + Float(Int(sender.titleLabel!.text!)!)
I did this and I get the error: Thread 1: EXC_BAD_INSTRUCTION (code=EXC_I386_INVOP, subcode=0x0)

As said in the comments, you should always avoid using the 'crash operator' (!) – and learn to safely deal with optional values instead.
Your program is crashing because either the titleLabel, text or Int(...) are nil – and you are then trying to force unwrap them. This could mean that your button doesn't have a titleLabel or that titleLabel's text isn't convertible to an Int.
The solution is to safely deal with the optionals as you encounter them. There are many ways of doing this, but I usually like to use one or multiple guard statements. This allows you to safely unwrap an optional if it has a value, otherwise it will execute the code within the brackets. This is useful for when future code depends on the optional not being nil. For example:
guard let buttonText = sender.titleLabel?.text else {
print("Sender didn't have either a titleLabel or text!")
return
}
guard let textAsInt = Int(buttonText) else {
print("Text wasn't convertible to an Int!")
return
}
currentNumber = currentNumber*10 + Float(textAsInt)
Now you get helpful print messages instead of crashes! Therefore you know exactly what went wrong, and what you can do to fix it (if it needs fixing).
You could also consolidate both of these checks into a single guard if you want more concise code, but less precise errors:
guard let buttonText = sender.titleLabel?.text, textAsInt = Int(buttonText) else {
print("Something went wrong when converting the button title to an Int!")
return
}
currentNumber = currentNumber*10 + Float(textAsInt)
Or you can use flatMap if you like closures:
guard let i = sender.titleLabel?.text.flatMap({Int($0)}) else {
print("Something went wrong when converting the button title to an Int!")
return
}
currentNumber = currentNumber*10 + Float(i)
The flatMap option can look a bit weird at first, but all it's doing is attempting to convert the button's titleLabel's text to an Int. If it fails it will return nil (which the guard will pick up), else it will return the numerical value of the text.
As #vacawama said in the comments, you could also use the nil coalescing operator in order to use 0 in the event that the titleLabel, text or Int(...) are nil:
currentNumber = currentNumber * 10 + Float(Int(sender.titleLabel?.text ?? "") ?? 0)
However bear in mind that this could lead to unexpected behaviour. I suspect that your program is crashing because your logic is getting run for non-numerical buttons, for example the "+" button. If this is the case, you'll be multiplying your number by 10 every time you press a non-numerical button. You'd have to first ensure that your logic only gets called on numerical buttons.
Although without seeing your full code, it's hard to say for sure.
For more info about how to safely deal with optionals, see this extensive Q&A on the subject.

Related

Swift 3 capitalize string

let first = postalText.text?[(postalText.text?.startIndex)!]
let second = postalText.text?[(postalText.text?.index((postalText.text?.startIndex)!, offsetBy: 1))!]
let third = postalText.text?[(postalText.text?.index((postalText.text?.startIndex)!, offsetBy: 2))!]
I'm trying to capitalize the FIRST and THIRD character and then merge all 3 into a new string
but the .uppercase and .capitalized doesn't work .
Also how do i check that the SECOND character is a number ?
.uppercased and .capitalized only work for strings, what you show there are Characters. You can cast a Character as a String and make it capitalized.
let firstCapitalized = String(first!).capitalized
If you want to check if a Character is an int, you can also make it a String, and then check if casting the String as an Int is non-nil:
if Int("\(second!)") != nil {
print("Is Integer")
}
These cases all assume your first, second, and third are all non-nil, and force-unwraps them.
EDIT
I had some free time and was overlooking some old posts on SO, and I realized this answer I posted isn't using the best coding form. First off, force unwrapping anything is always a bad idea (it's a recipe for a crash in the future), so for the first part. Do something like this:
let firstCapitalized = String(first ?? "").capitalized
This at least gives you a back-out in case first == nil then you'll just be stuck with an empty string.
For the second part, I would use optional unwrapping instead of if Int("\(second!)") != nil. I would say the more proper method would be something like this:
if let second = second, let stringConvertedToInteger = Int("\(String(second))") {
print("\(stringConvertedToInteger) is an integer")
} else {
print("Either second is nil, or it cannot be converted to an integer")
}
This will optionally unwrap the character second, and if it has a value, convert it to an integer (should it be one, checked by optional unwrapping). This is the safest way to do it, and will keep you from experiencing any runtime errors.

Using guard keyword

I have encountered numerous situations where a coder have used the guard keyword. And then later, in a seemingly almost identical situation the same coder in the same code does not use the guard keyword. I am aware that this may be a stupid question, so please don't bash it. When should I use the guard keyword and where shouldn't I?
Here is an example (there are many more). This is part of a script that is requesting data form an API.
//Here I am using guard
guard let json = json else {
//Now I am not using guard
if let error = error {
completion(.Failure(error))
} else {
//Error handling
}
return
}
Why not use the:
if let var1 = var1 {
//Keep on going
} else {
//Don't crash
}
syntax all the time instead of the guard syntax? At first glance it even seems to have more functionality, but I am certain that does not have to be the case.
One great benefit of the guard statement is that you know that if the condition is not satisfied then the execution flow gets stopped.
This is important for several reasons
Unwrapping
You can define unwrapped values which don't need a new scope { ... } to be available
func next(num:Int?) -> Int? {
guard let num = num else { return nil }
return num + 1
}
Readability
When you read the code you know that if the guard condition is not satisfied then the following lines won't be executed.
Semantics
You know a guard statement is there to check conditions required for the following block of code.
But I can replace every guard with an if
Sure. We could also replace every while and for with a goto in some languages. And we could always replace recursion with iteration (and viceversa).
But this doesn't necessarily means it is always a good idea.
Despite we can implement some behaviours with more then one programming "tool", we should still use the one that better fits that specific scenario.

Best way to pull non-optional values from optional containers

This is a best practices question, and I have a feeling I'm missing an obvious solution...
I was stumped why this bit of code made it past the if when wantsGroundAlways was false and I1 was undefined:
let i = c.data["I1"]?.integerValue
if !wantsGroundAlways && i == 0 { return }
The problem was that data["I1"] is, by definition, optional, so Swift inferred i as int?, and nil != 0. Subtle, but obvious in retrospect.
But what is the best way to deal with this common case? One solution is:
let i = (c.data["I1"] ?? 0).integerValue
But personally I think that looks terrible and hides the intent. Something along the lines of:
guard let i = c.data["I1"]?.integerValue else { i = 0 }
would make it obvious what you're trying to do, but it doesn't work because the i cannot be accessed in the else clause and { let i = 0 } is not the same i (try it, you'll see what I mean).
Now there is this:
guard let arcradius = c.data["F1"]?.doubleValue else { return }
which seems really close to what I want to do, but I'm not sure this is really what I think it means - will the return really fire if F1 is not in the dict? And what is the difference between that version and this:
guard case let arcradius = c.data["F1"]?.doubleValue else { return }
Which tells me it's always true?
I think I am missing something key here... so what do you all do in these situations?
I think this is the most straightforward and expressive way of solving your first question:
let i = c.data["I1"]?.integerValue ?? 0
It doesn't require brackets, and shows intent.
?? is the nil coalescence operator. It's a binary operator. If the left operand is not nil, it's returned, otherwise the right operand is returned.
c.data["I1"] can be nil (because there might be no value for the "I1" key). In such a case, c.data["I1"]?.integerValue will be nil. The ?? will then return the 0. If all goes well, and the left side isn't nil, it'll be returned, ignoring the 0.
If you want only to check if a key exist or not then a "type cast" to Int or Double is irrelevant.
Why not simply
guard c.data["I1"] == nil && !wantsGroundAlways else { return }
It passes the test if I1 is not in the dictionary and wantsGroundAlways is false.
if let or guard let is not needed either because according the condition the value for key is never used.

Deletion of text leads to "Unexpectedly found nil while unwrapping an optional value"

I am writing an app for a fencing company where the user can specify how many meters of fence they require and it will calculate the number of panels, feet and clamps needed for that meterage, I also have 2 fields where they can add additional clamps and additional feet to their order when they do this it automatically updates the values it calculated before to include the extras.
This works fine so long as the text field always has a number in it, for example, if you enter 30m into the first box the app would work out that you need 31 feet, if I put 3 additional feet on the order it says 34 feet, if I then change my mind and delete the contents of the extra feet field the app crashes due to finding nil while unwrapping an optional value, is there a way I can get around this?
I have the following code:
#IBAction func AdditionalFeetField(sender: AnyObject) {
if AdditionalFeetField.text != nil {
let AdditionalFeetValue1 = Double(PanelQtyField.text!);
let AdditionsFeetValue2 = Double(AdditionalFeetField.text!);
let AdditionalFeetResult = (AdditionalFeetValue1! + AdditionsFeetValue2!) + 1
self.FeetQtyLabel.text = "\(AdditionalFeetResult)"
}
}
`
When you're using ! you are force unwrapping the values of the text fields in this case, meaning that you say to the compiler "Just give me the value of the text field, I don't care if it is nil or not". This is almost always a bad idea...as you've found out :-)
A better way is to use if let or guard to unwrap the values.
I've tried to update your function to use if let
#IBAction func AdditionalFeetField(sender: AnyObject) {
if let additionalFeetStringValue = AdditionalFeetField.text,
let panelQtyFieldStringValue = PanelQtyField.text {
let additionalFeetValue1 = Double(panelQtyFieldStringValue) ?? 0
let additionsFeetValue2 = Double(additionalFeetStringValue) ?? 0
let additionalFeetResult = (additionalFeetValue1 + additionsFeetValue2) + 1
FeetQtyLabel.text = "\(AdditionalFeetResult)"
}
}
(I took the liberty of deleting your semicolons and self references too :-))
Hope that helps you. Maybe you can combine it with #mattias suggestion about checking if the value can be cast to a double.

Swift basic expression

I'm very new to swift, but proficient in other languages like Java, JavaScript, C, ... I'm lost with Swift syntax when it comes to create expressions. Look at this basic example where I just try to find out if one string is contained into another by calling String.rangeOfString that returns an Optional Range (Range?)
This works as expected:
let LEXEMA:String="http://"
let longUrl:String="http://badgirls.many/picture.png"
let range=longUrl.rangeOfString(LEXEMA);
if (range? != nil) {
// blah
}
Now I'm trying to combine the expression inside the if, something like:
if (longUrl.rangeOfString(LEXEMA)? !=nil) {
// blah
}
But I always get syntax errors, the above yields a "Expected Separator" and can't understand why. Done some more tests:
if (absolutePath.rangeOfString(URL_LEXEMA) !=nil) { }
Expected Separator before "!"
if absolutePath.rangeOfString(URL_LEXEMA) !=nil { }
Braced block of statements is an unused closure
What am I doing wrong?
If you’re coming from other like Java, you might be thinking of optionals like pointers/references, and so used to equating them to nil and if non-nil, using them. But this is probably going to lead to more confusion. Instead, think of them like a container for a possible result, that you need to unwrap to use. if let combines the test and unwrapping operation.
With this in mind, here’s how you could adapt your code:
let LEXEMA: String="http://"
let longUrl: String="http://badgirls.many/picture.png"
if let range = longUrl.rangeOfString(LEXEMA) {
// use range, which will be the unwrapped non-optional range
}
else {
// no such range, perhaps log an error if this shouldn’t happen
}
Note, that ? suffixing behaviour you were using changes in Swift 1.2 so even the code in your question that compiles in 1.1 won’t in 1.2.
It’s possible that sometimes you are whether there was a value returned, but you don’t actually need that value, just to know it wasn’t nil. In that case, you can compare the value to nil without the let:
if longUrl.rangeOfString(LEXEMA) != nil {
// there was a value, but you don't care what that value was
}
That said, the above is probably better expressed as:
if longUrl.hasPrefix(LEXEMA) { }
For starters:
You don't need parenthesis with if statements unless you have nested parenthetical subexpressions that require it.
You don't need to specify the type on the left side of the = of a let or var declaration if Swift can figure it out from the right side of the =. Very often Swift can figure it out, and you can tell that Swift can figure it out, so you can avoid that redundant clutter.
You do need to specify the type if Swift cannot figure out the type from
the right side. Example:
For example, consider the following lines:
let LEXEMA = "http://"
let longUrl = "http://badgirls.many/picture.png"
Swift can figure out that they're strings.
Similarly for this function or class that returns a UIView:
var myView = ViewReturningClassOrFunc()
Consider this:
#IBOutlet var myView : UIView!
In the above line, Swift cannot figure out ahead of time it will be assigned a UIView, so you have to provide the type. By providing a ! at the end you've made it an implicitly unwrapped optional. That means, like ?, you're indicating that it can be nil, but that you are confident it will never be nil at the time you access it, so Swift won't require you to put a ! after it when you reference it. That trick is a time saver and big convenience.
You should NOT add the ? to the line:
if (longUrl.rangeOfString(URL_LEXEMA) !=nil) {
As another answer pointed out, you're missing the let.
if let longUrl.rangeOfString(URL_LEXEMA) {
println("What do I win? :-)")
}
swift is case sensitive language. you need to check about whitespaces as well
if longUrl.rangeOfString(LEXEMA) != nil {
//your condition
}
there should be space between statement != nil
Just add a space between != and nil like:
if longUrl.rangeOfString(LEXEMA) != nil {
// blah
}
I tested your code in playground, an error of Expected ',' separator reported.
And do not forget the rules that 1s and 0s and Airspeed Velocity said.