I'm trying to create a var without assigning a value when it's initialized. I'm new to swift, coming from Objective-c, and I'm having trouble using the wrapper/unwrapper. I have the following in my code:
var elapsedTime : NSTimeInterval!
self.elapsedTime -= (NSTimeInterval(hours) * 3600)
I get the following error at the second line:
Binary operator '-=' cannot be applied to operands of type
'NSTimeInterval!' and '(Double)'
What should I do to fix that, and why am I getting an error?
It's a behavior specific to the implicitly unwrapped optional. The ! after the name of the type makes it an implictly unwrapped optional, for instance with NSTimeInterval!. The compiler will let you try and access the value inside the optional without checking for nil. If there is a nil there at runtime, your program will explode.
For some reason, -= can't be applied with the implicitly unwrapped optional on the left. NSTimeInterval is a typealias for Double, so I'm going to use a simple example.
var time = 0.0
var dangerousTime: Double! = 15.0
time -= dangerousTime // works fine
dangerousTime = dangerousTime - time // works fine
dangerousTime -= time // errors out
Implicitly unwrapped optionals are dangerous and you should avoid using them. This will probably fix your problem anyway. A few possible ways to go from here:
In Swift, you don't need an Optional to declare a variable without initialization. The compiler just won't let you use it until you've assigned it an initial value.
var time: Double
var foo = time + 5 // error
time = 4.0
foo = time + 5 // success
What you may want is a regular old Optional and a safe syntax for using the Optional.
var maybeTime: Double?
if let time = maybeTime {
maybeTime = time - 42
}
You may have run into a compiler bug.
elapsedTime is declared as an Optional, which means that it may have a value, like zero, or it may have no value.
The -= operator only has meaning when elapsedTime has a value.
If you want to handle the case where elapsedTime has no value, you can do something like this:
if let elapsedTime = self.elapsedTime {
self.elapsedTime = elapsedTime - (NSTimeInterval(hours) * 3600)
}
else {
// self.elapsedTime has no value. What do you want to do?
}
If you just want to assume that self.elapsedTime always has a value, you can do this:
let elapsedTime : NSTimeInterval = self.elapsedTime
self.elapsedTime = elapsedTime - (NSTimeInterval(hours) * 3600)
That will compile, but it will crash at runtime if self.elapsedTime has no value.
It might be a compiler bug because -= should perhaps be equivalent to the last example, but instead produces a compiler error.
Related
I tried for a long time to turn the text into an Int but it did not work. I tried it like this:
(AnzahlString is a textfield)
var AnzahlAInt = 0
if let AnzahlAString = AnzahlString.text {
let AnzahlAInt = Int(AnzahlAString)
}
But then I always get the error:
Value of optional type 'Int?' must be unwrapped to a value of type 'Int'
Then I added a ! at the end of Int(AnzahlAString)! so I don't get a error, but now when I press on the button, the app crashes. It was predictable, but how can I change this now to an Int without the !?
At first glance, it looks like you have two things to check for:
is AnzahlString.text present, and
does it represent an Int
The first check is in fact not necessary, since .text will never return nil, even though it's marked as Optional. This means you can safely force-unwrap it.
The second check is easily done by using the ?? operator:
let AnzahlAInt = Int(AnzahlString.text!) ?? 0
PS, just as a stylistic hint: variable names in Swift ususally start with a lowercase letter, names starting with capital letters are used for types.
PPS: your code as written shadows AnzahlAInt - the value of your var is never changed.
The reason why the resulting Int is optional, is that parsing might or might not succeed. For example, if you try to parse the string "Fluffy Bunnies" into an Int, there is no reasonable Int that can be returned, therefore the result of parsing that string will be nil.
Furthermore, if you force the parser by using !, you're telling Swift that you know for sure that the string you pass will always result in a valid Int, and when it doesn't, the app crashes.
You need to handle the situation in which the parse result is nil. For example:
if let AnzahlAIntResult = Int(AnzahlAString) {
// We only get here if the parse was successful and we have an Int.
// AnzahlAIntResult is now an Int, so it can be assigned to AnzahlAInt.
AnzahlAInt = AnzahlAIntResult
}
You did a good job so far but missed out one thing.
This line tries to convert the String into an Int. However this can fail, since your String can be something like this "dfhuse".
let AnzahlAInt = Int(AnzahlAString)
This is why the result of Int(AnzahlAString) is an Optional (Int?). To use it as an real Int, you have to unwrap it.
First solution is the !, however, every time this does fail your app crashes. Not a good Idea to use so.
The best solution would be Optional Binding, as you already used to get the text of your text field.
if let AnzahlAString = AnzahlString.text {
if let safeInt = Int(AnzahlAString) {
// You can use safeInt as a real Int
} else {
print("Converting your String to an Int failed badly!")
}
}
Hope this helps you. Feel free to ask again if something is unclear.
For unwrapping you can also use guard like this
Simple and easy
guard let AnzahlAInt = Int(AnzahlString.text!) else {
return
}
print(AnzahlAInt)
First post here so please be gentle. Am fairly new to coding and am trying to get my head around SWIFT and its optionals. Would really appreciate some advice from the pros!
I am writing a simple app whereby textfields are entered by the user and then some multiplication occurs in app before spitting out an answer into another textfield on the press of a button: "calculateTM".
I am having some trouble with the calculation itself and perhaps it's because I am trying to do too much on one line - take the textfield entry, convert to integer, multiply with another textfield entry converted to an integer, essentially what I wrote in the title:
var someVariable: Int = textfield.text.toInt() * textfield2.text.toInt()
The problem is, Xcode is wanting my to force unwrap and add an ! to the end of both toInt(). This is fine, except of course when the user doesn't enter anything into the boxes and presses calculate, at which point the nil value causes the program to crash, e.g.:
var someVariable: Int = textfield.text.toInt() x textfield2.text.toInt()
var someVariable2: Int = textfield3.text.toInt() x textfield4.text.toInt()
where the user doesn't enter anything into textfield3 or 4
Following this simple arithmetic, the code updates the labels (which are textfields) as such:
label1.text = String(someVariable)
label2.text = String(someVariable2)
So this final conversion back to a string might also create some issues as to how the optionals are treated in the first part of the code.
Apologies for the long-winded explanation, and I hope I've been clear enough, but I imagine I am missing something really basic with the first part of the code. I have tried using the optional ? and also the nil-coalescing operator (to set to 0 in case of nil) but can't get it to work. Please help?
Many thanks in advance!
Use if let for optional binding:
if let var1 = textField.text?.toInt(),
let var2 = textField2.text?.toInt() {
someVariable = var1 * var2 // or directly label1.text = String(var1 * var2)
}
The method toInt() actually returns an optional type 'Int?' that can be nil or integer value, so you need to check if the String->Int cast successful returns an Int or a nil.
For the most basic way:
var intValue: Int? = text.toInt()
if intValue != nil {
// operations using intValue!
}
In swift, you can try:
if let intValue = text.toInt() {
// operations
}
There is a impulse applied to a ball. but this ball keeps moving after that impulse, but I want it to move just a bit. So here is what I am trying:
var impulse:CGFloat = 0.22 // bal impulse
applying impulse:
bal.physicsBody?.applyImpulse(CGVectorMake(50 * impulse, 50 * impulse))
trying to limit the movement:
if bal.physicsBody?.velocity.dx > impulse{
bal.physicsBody?.velocity.dx = bal.physicsBody?.velocity.dx - impulse
}
But this last piece of code is getting this error:
Value of optional type CGFloat? not unwrapped, did you mean to use ! or ?
How to fix?
When workking with optionals, you always should unwrap them using if let, because if you use the ? and the value is nil, the code will not execute, and you will not know that there was a nil value.
Use it like this:
var impulse:CGFloat = 0.22 // bal impulse
if let body = bal.physicsBody {
body.applyImpulse(CGVectorMake(50 * impulse, 50 * impulse))
if body.velocity.dx > impulse {
body.velocity.dx = body.velocity.dx - impulse
}
}
else
{
// bal.physicsBody is nil !
}
Given you’ve asked a similar question recently, you really need to spend some time understanding optionals, as you’ll keep hitting these kind of problems. You need to get a feel for what optionals are, and what the different techniques are for handling them. Try reading this and this to start.
In this particular example, you have an optional velocity, because you are accessing it via another optional, physicsBody, via “optional chaining” (?.). So to perform an operation like - on it, you need to use an optional technique. This looks like a good case for a if let with an extra where clause:
if let v = bal.physicsBody?.velocity where v.dx > impulse {
bal.physicsBody?.velocity = CGVector(dx: v.dx - impulse, dy: v.dy)
}
Note, optional chaining can be “assigned through” which is why bal.physicsBody?.velocity = works.
Also, there’s no need to use CGVectorMake. Unlike in Objective-C, CGVector has an initializer that takes the same parameters (as shown above).
I have this code in Swift and it works, but I would think there is a better way to get my object from NSNumber and convert it to a Double:
var rating: NSNumber
var ratingDouble: Double
rating = self.prodResult?.prodsInfo.prodList[indexPath.row].avgRating as NSNumber!!
ratingDouble = Double(rating.doubleValue)
Update
Swift's behavior here has changed quite a bit since 1.0. Not that it was that easy before, but Swift has made it harder to convert between number types because it wants you to be explicit about what to do with precision loss. Your new choices now look like this:
var rating: NSNumber
var ratingDouble: Double
ratingDouble = rating as! Double // 1
ratingDouble = Double(exactly: rating)! // 2
ratingDouble = Double(truncating: rating) // 3
ratingDouble = rating.doubleValue // 4
if let x = rating as? Double { // 5
ratingDouble = x
}
if let x = Double(exactly: rating) { // 6
ratingDouble = x
}
This calls Double._forceBridgeFromObjectiveC which calls Double(exactly:) with Double, Int64, or UInt64 based on the stored type in rating. It will fail and crash the app if the number isn't exactly representable as a Double. E.g. UInt64.max has more digits than Double can store, so it'll crash.
This is exactly the same as 1 except that it may also crash on NaN since that check isn't included.
This function always returns a Double but will lose precision in cases where 1 and 2 would crash. This literally just calls doubleValue when passing in an NSNumber.
Same as 3.
This is like 1 except that instead of crashing the app, it'll return nil and the inside of the statement won't be evaluated.
Same as 5, but like 2 will return nil if the value is NaN.
If you know your data source is dealing in doubles, 1-4 will probably all serve you about the same. 3 and 4 would be my first choices though.
Old Answer for Swift 1 and 2
There are several things you can do:
var rating: NSNumber
var ratingDouble: Double
ratingDouble = rating as Double // 1
ratingDouble = Double(rating) // 2
ratingDouble = rating.doubleValue // 3
The first item takes advantage of Objective-Cbridging which allows AnyObject and NSNumber to be cast as Double|Float|Int|UInt|Bool.
The second item presumably goes through a constructor with the signature init(_ number: NSNumber). I couldn't find it in the module or docs but passing AnyObject in generated an error that it cannot be implicitly downcast to NSNumber so it must be there and not just bridging.
The third item doesn't employ language features in the same way. It just takes advantage of the fact that doubleValue returns a Double.
One benefit of 1 is that it also works for AnyObject so your code could be:
let ratingDouble = self.prodResult!.prodsInfo.prodList[indexPath.row].avgRating! as Double
Note that I removed the ? from your function and moved the ! in. Whenever you use ! you are eschewing the safety of ? so there's no reason to do both together.
I'm reading The Swift Programming Language, in the Simple Values section
“Use let to make a constant and var to make a variable. The value of a
constant doesn’t need to be known at compile time, but you must assign
it a value exactly once”
So I think I can do this
let aConstant:Int
aConstant = 5
But I get let declarations require an initializer expression !!
Why is that ? What does they mean by "The value of a constant doesn’t need to be known at compile time" ?
From the Swift Language Reference:
When a constant is declared at global scope, it must be initialized with a value.
You can only defer initialization of a constant in classes/structs, where you can choose to initialize it in the initializer of the class/struct.
The meaning of "The value of a constant doesn’t need to be known at compile time" refers to the value of the constant. In C/Objective-C a global constant needs to be assigned a value that can be computed by the compiler (usually a literal like 10 or #"Hello"). The following would not be allowed in Objective-C:
static const int foo = 10; // OK
static const int bar = calculate_bar(); // Error: Initializer element is not a compile-time constant
In Swift you don't have this restriction:
let foo = 10 // OK
let bar = calculateBar(); // OK
Edit:
The following statement in the original answer is not correct:
You can only defer initialization of a constant in classes/structs, where you can choose to initialize it in the initializer of the class/struct.
The only place where you cannot defer is in global scope (i.e. top level let expressions). While it's true that you can defer initialization in a class/struct, that's not the only place. The following is also legal for example:
func foo() {
let bar: Int
bar = 1
}
A constant does not need to be known at compile, but it must have a value after initialization:
class MyClass: NSObject {
let aConstant: Integer; // no value
init() {
aConstant = 4; // must have a value before calling super
super.init();
}
}
This allows you to set the constant to a value after it is declared and potentially unknown at compile time.
the let keyword, by definition, defines a constant.
Thus, you can't modify it once its been set.
Since thats the case, they need to be initialized when they are declared!
The solution here is to do either:
let aConstant = 5
or change it to a var
var aNonConstant:Int
aNonConstant = 5
Answer for Swift 2:
You can write constants as follows:
let aConstant:Int
aConstant = 5
Setting the type this way means: "This will be constant and it will have value when you need it". Notice that you cannot use the constant before setting value to it, there is a compile time error:
Constant 'aConstant' used before being initialized
Furthermore you can set value to aConstant only once. If you try to set value for second time, there is compile time error:
Immutable value 'aConstant' may only be initialized once
Anyway you cannot do this for global constants, there is compile time error:
Global 'let' declaration requires an initializer expression