iOS/Swift: Can't assign optional String to UILabel text property - ios

UILabel has a text property, which is an optional String, but it seems to be behaving like an implicitly unwrapped optional. Why can't I assign it another optional String? Thanks.
#IBOutlet weak var tweetContent: UILabel!
...
var unopt: String = "foo"
var opt: String? = "bar"
var opt2: String?
opt2 = opt //Works fine
cell.tweetContent.text? = unopt //Works fine
cell.tweetContent.text? = opt //Compile error: Value of optional type 'String?' not unwrapped

You don't need to unwrap text.
Leaving text as an String? (aka Optional<String>)
cell.tweetContent.text = unopt // Works: String implicitly wraps to Optional<String>
cell.tweetContent.text = opt // Works: Optional<String>
Where as unwrapping text turns String? into a String.
cell.tweetContent.text? = unopt // Works: String
cell.tweetContent.text? = opt // Fails: Optional<String> cannot become String
UPDATE
Maybe there needs to be a bit more of an explanation here. text? is worse than I originally thought and should not be used.
Think of text = value and text? = value as function setText.
text = has the signature func setText(value: String?). Remember, String? is Optional<String>. In addition, it's always called no matter the current value of text.
text? = has the signature func setText(value: String). Here is the catch, it's only called when text has a value.
cell.tweetContent.text = nil
cell.tweetContent.text? = "This value is not set"
assert(cell.tweetContent == nil)

Related

What is going on in Apple's first example of an optional value in the Swift tour?

In Apple's "Swift Tour" introduction to swift, they give the following example of optionals:
var optionalString: String? = "Hello"
optionalString == nil
var optionalName: String? = "John Appleseed"
var greeting = "Hello!"
if let name = optionalName {
greeting = "Hello, \(name)"
}
Does the second line serve any purpose?
optionalString == nil
is an expression, and in a compiled program the expressions's value (in this case: false)
is ignored.
However, the Swift Guided Tour is downloadable as a Playground file, and in a Playground
any expression value is displayed in the right column.
You are encouraged to edit the code listings and see the the results immediately.
The first bit of code
var optionalString: String? = "Hello"
optionalString == nil
Is just comparing the optional to nil, when this code is run in a playground false will show up on the right hand side.
The second chunk.
var optionalName: String? = "John Appleseed"
var greeting = "Hello!"
if let name = optionalName {
greeting = "Hello, \(name)"
}
Is using what is called optional binding. The author could just as easily have written
var optionalName: String? = "John Appleseed"
var greeting = "Hello, \(optionalName!)"
Note the exclamation mark at the end of optionalName! this is whats known as Forced Unwrapping, In this particular case it would have been fine since we set the value of the variable immediately before hand. In general though Forced Unwrapping is considered to be unsafe since you're basically saying that you know better than the compiler. Forced Unwrapping is sometimes affectionally referred to as the crash operator ;)
You can find the section on optionals here
var optionalString: String? = nil
optionalString == nil
change your code as above,and you can see the use of second line.
It tell you the optionalValue can be set to nil,and compare to nil.Most of the classes can't do these.

Xcode - Swift Compiler Error: cannot convert the expression's type 'Double' to type 'Double'

How to convert a label outlet string to a double in swift?
I have a timer which is updating the label: labelOutletForSecondsCount.text. The label is initialised with a String "0,00". I than wanted to save the stopped timeString (I'm getting from my timer) as a Double in a variable to use it in some calculation.
I thought that this line of code should do it but I get the following "Swift Compiler Error: cannot convert the expression's type 'Double' to type 'Double'".
var timeStringIntoDoubleValue = (labelOutletForSecondsCount.text as NSString).doubleValue
It worked fine with this line of code:
var textFieldInsertLengthIntoDoubleValue = (textFieldInsertLength.text as NSString).doubleValue
But why is it not working with the labelOutletForSecondsCount.text?
I should mention that I'm doing this in computed properties:
var length : Double {
var textFieldInsertLengthIntoDoubleValue = (textFieldInsertLength.text as NSString).doubleValue
return textFieldInsertLengthIntoDoubleValue
}
var time : Double {
var timeStringIntoDoubleValue = (labelOutletForSecondsCount.text as NSString).doubleValue
return timeStringIntoDoubleValue
}
This most likely adds to the strange error: cannot convert the expression's type 'Double' to type 'Double'.
The error message is misleading. The text property of UILabel is an optional:
var text: String? // default is nil
which can not be converted directly to NSString. You could forcefully unwrap the value:
var timeStringIntoDoubleValue = (labelOutletForSecondsCount.text! as NSString).doubleValue
but this would crash at runtime if the text value is nil. Better use
"optional binding":
var timeStringIntoDoubleValue = 0.0
if let text = labelOutletForSecondsCount.text {
timeStringIntoDoubleValue = (text as NSString).doubleValue
}
Alternatively, use the "nil-coalescing operator" ?? to provide a default value:
var timeStringIntoDoubleValue = (labelOutletForSecondsCount.text ?? "0" as NSString).doubleValue
Here, labelOutletForSecondsCount.text ?? "0" evaluates to the (unwrapped) text
value if that is not nil, and to "0" otherwise.
The reason that it compiles with a text field is that the text property
of UITextField is declared as an implicitly unwrapped optional:
var text: String! // default is nil

Difference between optional and normal String

Difference between String?,String! and String
I am using this code :
var myString:String? = nil
var myString1:String = ""
var myString2:String! = nil
println(myString2)
Here it's giving nil in myString2 instead of run-time error.
There are better explanations out there, but the simple version is:
String is a String. It holds a String value.
String? is an Optional String. It can hold a String value or nil. You can unwrap the optional and immediately try to access the String value by using ! like this:
var str: String? = nil
str!.length
That code will result in a runtime error because str is nil. A safer way to get the value of the optional is to use an if let:
var str: String? = nil
if let myStr = str{
myStr.length
}
That has the same functionality as the above code, but won't crash on nil values.
String! is an implicitly unwrapped optional. It works that same way as a regular optional but it is assumed to be non-nil, so when you call it it tries to access the value like it was a regular optional with ! after.
The reason println works on all these types is because it can take Strings, String Optionals, and nil values and handles them all.
var str: String? = "hello world"
println(str)
println(str!)
println(nil)
All should work.
#connor's answer is correct.
var Amy:Wife? // Amy is your girlfriend, she may or may not be your wife.
var Amy:Wife // Amy is your wife.
var Amy:Wife! // Amy will be your wife, no matter you like it or not.

No need to unwrap optionals in Swift?

I think I'm confused about the concept of 'unwrapping' an optional value, and any guidance would be much appreciated!
According to the Swift Programming Language Guide, an optional requires unwrapping before it can be used, unless it is implicitly unwrapped using an !.
From: Apple Inc. “The Swift Programming Language.” iBooks. https://itun.es/gb/jEUH0.l:
let possibleString: String? = "An optional string."
println(possibleString!) // requires an exclamation mark to access its value
whereas
let assumedString: String! = "An implicitly unwrapped optional string."
println(assumedString) // no exclamation mark is needed to access its value
However, in Xcode, I can do the following and still print the value of an optional string:
let teamname: String? = "Liverpool!"
println("Come on \(teamname)")
What am I missing?
First of all, an optional requires unwrapping only if you are using it somewhere that does not allow an optional. (e.g. assigning a String? to a String)
var optionalString: String? = "Some string"
var absolutelyAString: String = "Another string"
// absolutelyAString = optionalString // Error. Needs to unwrap
absolutelyAString = optionalString! // OK.
"Unwrap" basically means to "guarantee that the value is not nil". Just think of it as casting to a non-nullable type.
Second, println() accepts optionals just fine but because it checks your variable for you. If the value of your variable is nil, it prints "nil"; otherwise it prints the string implemented by the Streamable, Printable, or DebugPrintable protocols.
An optional requires unwrapping, but you don't always have to do it yourself.
var teamname: String? = "Liverpool"
println("Come on \(teamname)")
In this case, the string interpolation will check the optional then unwrap it and add its value to the String. If the Optional is nil, it will add "nil" to the String instead.
In order to access the properties and methods of String you will have to explicitly unwrap it. If you don't know if it is nil or not, you should check with an if statement.
if teamname != nil {
var newName = teamname!.capitalizedString
}

Use of an optional value in Swift

While reading the The Swift Programming Language, I came across this snippet:
You can use if and let together to work with values that might be
missing. These values are represented as optionals. An optional value
either contains a value or contains nil to indicate that the value is
missing. Write a question mark (?) after the type of a value to mark
the value as optional.
// Snippet #1
var optionalString: String? = "Hello"
optionalString == nil
// Snippet #2
var optionalName: String? = "John Appleseed"
var greeting = "Hello!"
if let name = optionalName {
greeting = "Hello, \(name)"
}
Snippet #1 is clear enough, but what is happening in the Snippet #2? Can someone break it down and explain? Is it just an alternative to using an if - else block? what is the exact role of let in this case?
I did read this page, but still a little confused.
if let name = optionalName {
greeting = "Hello, \(name)"
}
This does two things:
it checks if optionalName has a value
if it does, it "unwraps" that value and assigns it to the String called name (which is only available inside of the conditional block).
Note that the type of name is String (not String?).
Without the let (i.e. with just if optionalName), it would still enter the block only if there is a value, but you'd have to manually/explicitly access the String as optionalName!.
// this line declares the variable optionalName which as a String optional which can contain either nil or a string.
//We have it store a string here
var optionalName: String? = "John Appleseed"
//this sets a variable greeting to hello. It is implicity a String. It is not an optional so it can never be nil
var greeting = "Hello!"
//now lets split this into two lines to make it easier. the first just copies optionalName into name. name is now a String optional as well.
let name = optionalName
//now this line checks if name is nil or has a value. if it has a value it executes the if block.
//You can only do this check on optionals. If you try using greeting in an if condition you will get an error
if name{
greeting = "Hello, \(name)"
}
String? is a boxed-type, variable optionalName either contains a String value or nothing(that is nil).
if let name = optionalName is an idiom, it unboxes the value out of optionalName and assign it to name. In the meanwhile, if the name is non-nil, the if branch is executed, otherwise the else branch is executed.

Resources