Xcode 8.0 and Swift 3.0 conversion: Looking for explanation for a particular conversion error - swift

I am a little confused about a conversion error.
I migrated my project form Swift 2.3 to Swift 3.0
func updateCelsiusLabel() {
if let value = celsiusValue {
//This was the original code (that worked but is) failing after migration
//due to: Argument labels do not match any available overloads
celsiusLabel.text = numberFormatter.string(from: NSNumber(value))
//This is my code trying to fix this issue and the project is now compiling
//and everything is fine
celsiusLabel.text = numberFormatter.string(from: value as NSNumber)
}
else { celsiusLabel.text = "???"
}
}
At first I thought that in Swift 3.0 the cast Type(value) was now forbidden, but I checked and I get absolutely no compiler warning. Can somebody tell me what the problem with NSNumber(value) is?
As far as I understand value as NSNumber and NSNumber(value) should be the same thing.

In Swift 3, NSNumber(value) won't work. Let's say that your value is an Int. In that case, you'd need NSNUmber(value: yourIntValue). In Swift 3, you must have the name of the first (and in this case the only) parameter in the function call. So, your usage of
value as NSNumber
works, but
NSNumber(value: yourNumberValue)
works too.

First of all I have taken some assumption here, I have assumed that -
numberFormatter = NSNumberFormatter() // Now it has been renamed to NumberFormatter
celsiusLabel.text I am taking text as optional string just for example you can use label.text for same.
After the above assumption please see below code which will work in Swift 3 -
var celsiusValue:Double?
var numberFormatter = NumberFormatter()
var text:String?
func updateCelsiusLabel() {
if let value = celsiusValue {
//This was the original code (that worked but is) failing after migration due to: Argument labels do not match any available overloads
text = numberFormatter.string(from: NSNumber(value: value))!
}
else {
text = "???"
}
}
Hope it help feel free to leave comment in case you have any doubt.

Related

Unwanted behaviour with optional binding evaluating optional(nil)

After updating xCode to version 10 (and swift 4.2), I have a strange behaviour on optional bindings
The code is below, and it ´s about reading json file, T is a generic type (here String)
// Are there values for configName ?
if let values = cfg[configName] as? [String: Any] {
print("0 - values[langCode!] = ", values[langCode!] ?? "null")
print("1 - values[langCode!] as? T = ", values[langCode!] as? T)
// is there a value for langCode ?
if let value = values[langCode!] as? T {
print("2 - value to return= ", value)
return value
} else {
print("3 - Do something else ")
}
}
In xCode 9.4.1 and Swift 4.1 I have the following logs:
0 - values[langCode!] = null
1 - values[langCode!] as? T = nil
3 - Do something else
That is what I want, values[langCode!] is nil and the cast also return nil so the else block is executed.
In xCode 10 with Swift 4.2, I have the following logs:
0 - values[langCode!] = null
1 - values[langCode!] as? T = Optional(nil)
2 - value to return= nil
Here the if let block is executed even if values[langCode!] is "null".
One difference is that with swift 4.2 values[langCode!] as? T is an Optional(nil) and on Swift 4.1 values[langCode!] as? T is an nil.
I checked the changelog for version 4.2 and I could not see something that can explain that behaviour, I also checked that no changes have been done on JSONSerialization (used to serialize the json file)
Has someone also experienced that kind of thing when switching to Swift4.2 ?
Does someone have an explanation ? And a work around ?
In this kind of code what is the advantage to use optional binding ? would it be bad to write if (values[langCode!] != nil) {... instead of the optional binding ?
Thanks
If you have not changed the code and it behaves differently, then that is probably a bug in Swift. If you can make a small test case, you should file a bug at https://bugs.swift.org.
Anyway, it sounds like in Swift 4.1, Swift deduces type T as some non-Optional type (e.g. Int), and in Swift 4.2, Swift deduces T as Optional instead (e.g. Int?). You could check by adding this statement:
print("Type T is bound to \(T.self)")
If it prints Int (or whatever) under Swift 4.1 and Optional<Int> under Swift 4.2, then that's the problem.
I answer my question since the correct answer has been given in comments.
The difference observed between Swift4.1 and Swift4.2 is due to an intentional change. #Hamish explains everything in the answer is done here: stackoverflow.com/q/52446097/2976878

Binary operator '>' cannot be applied to two 'String?!' operands

So I have a project, that builds fine. but hen I want to Archive it it throws this error on this line of code:
let pred = NSPredicate(format: "%K in %#", "categoryID", selectedCategoryIDs!)
let selectedAlmanacEntries = almanacEntries.filter { pred.evaluate(with: $0) }.sorted(by: { ($0 as AnyObject).title > ($1 as AnyObject).title })
this hasn't been an issue before (Earlier releases).
Have tried restarting Xcode and cleaning the project before.
Any hints appreciated.
(Xcode 8, Swift 3)
Swift 5
you can simply unwrap optional by this way
let selectedAlmanacEntries = selectedAlmanacEntries.sorted {
var isSorted = false
if let first = $0.title, let second = $1.title {
isSorted = first < second
}
return isSorted
}
?! is a really great construction, isn't it? Perfectly sums up the reaction whenever you see it in an error message.
Anyway, in Swift, you're probably better off casting to the appropriate type that these objects should belong to instead of AnyObject. If you must duck type, then you should be aware that every time Swift calls a method on AnyObject, it gains a level of optionality due to the fact that Swift can't verify that the object actually responds to the message. So you will have to deal with the optionals.
The other problem is that since there exist multiple classes in the frameworks that have properties named title, Swift has no way of knowing which one to use. And some of them have different signatures; for example, NSButton has a title property that is typed as String, NSStatusItem has a title property that is typed as String?, and NSWindowTab has a title property that is typed as String!. Which one the compiler picks is a bit of a luck of the draw, which is why random chance can make it behave differently from compile to compile. So you need to help the compiler out by telling it what type to expect.
So, something like this can work:
let selectedAlmanacEntries = almanacEntries.filter { pred.evaluate(with: $0) }.sorted(by: {
guard let first: String = ($0 as AnyObject).title else { return false }
guard let second: String = ($1 as AnyObject).title else { return true }
return first > second
})
Or, if your heart is set on a long one-liner as in the original:
let selectedAlmanacEntries = almanacEntries.filter { pred.evaluate(with: $0) }.sorted(by: { (($0 as AnyObject).title as String?) ?? "" > (($1 as AnyObject).title as String?) ?? "" })
I'd really recommend casting to the actual type instead, though.

Optional Chaining in one step?

Why does this work (Example 1):
if let numString:String = Model.selectedLocation?.zip{
let callString:String = String(format:"tel:%#",numString)
//more code here
}
But not this (Example 2):
if let numString:String = String(format:"tel:%#",Model.selectedLocation?.zip){
//more code here
}
In the second example, Xcode throws an error and wants zip to be unwrapped like:
String(format:"tel:%#",(Model.selectedLocation?.zip)!)
but if I do that the app will crash when zip is nil.
QUESTION:
Is there a way to make the second example above work or is it not possible/correct?
Avoid redundant type annotations
Avoid String(format:) unless you need it. It's a method of NSString from the Foundation framework, which has several consequences:
It requires Foundation to be imported.
It implicitly bridges your String to NSString.
It won't work in Swift 3, because bridging was made explicit.
The root issue here is that String(format:) returns String? (since the format string could be invalid). You can avoid this entirely by using Swift's string interpolation:
if let numString = Model.selectedLocation?.zip {
let callString = "tel: \(numString)"
//more code here
}
...or simple concatination:
if let numString = Model.selectedLocation?.zip {
let callString = "tel: " + numString
//more code here
}
Strictly spoken Example 2 is neither optional binding nor optional chaining because String(format...) returns a non-optional String and the format parameter must be non-optional, too.
Example 1 is the correct and recommended syntax to handle the optionals.
Edit: I totally agree with Alexander's answer (except that String(format:) returns String?)
UPDATED
It is because in String(format: " ", ), the arguments must be not-nil, hence the !.
when using if-let check for optionals, statements must return optionals
// Assuming Model.selectionLocation.zip is of String type
if let numberString = Model.selectedLocation?.zip {
let formattedString = String(format:"tel:%#", numberString)
}
or use guard
guard let numberString = Model.selectedLocation?.zip else {
return
}
let numberString = String(format:"tel:%#", numberString)

Optional issue converting String to Int in Swift 1.2

I can't figure out why this 'optional' isn't working in scenario 1, but without the optional ? it works in scenario 2.
Using Swift v 1.2, xCode 6.2
var stuff = "6t"
// SCENARIO 1
// Why is this failing when stuff contains non-digit characters?
// i.e. it works if stuff = "45".
if let value: Int? = stuff.toInt() {
println("value \(value!)")
}
// SCENARIO 2
// This works!
if let value = stuff.toInt() {
println("val3 \(value)")
}
For Reference also see these SO Answers:
* I wonder if the Sift 1.2 example/Answer here is just plain wrong?
Swift - Converting String to Int
Converting String to Int in Swift
The first IF is always true.
Infact in both cases:
when the toInt() returns a valid Int
when returns nil
the if let will succeed (and yes, the first IF is useless).
Specifically in your code toInt() returns nil in both scenarios.
But in your first scenario you are simply accepting nil as a valid value to enter the THEN block.
There is no point of using if let value: Int?. If the if let works, then the value is an Int. There is no way that it could be nil. Therefore, you do not need to declare it as an optional.

Why there is a ? mark in if-let statement to see an optional has a value

I am reading the following book, on Page #32 there is a code snippet. December 2014: First Edition.
Swift Development with CocoaJonathon Manning, Paris Buttfield-Addison,
and Tim Nugent
I know we can use ? to make a vairbale optional and ! to unwrap a optional vairbale in Swift
var str: String? = "string"
if let theStr = str? {
println("\(theStr)")
} else {
println("nil")
}
Why do we need ? in this line if let theStr = str? It seems working just fine with out it and making no difference.
You don't need it, and shouldn't have it. The optional binding evaluates the optional. If it's nil, it stops. If it's not nil, it assigns the value to your required variable and then executes the code inside the braces of the if statement.
EDIT:
The language has changed slightly since it was first given out in beta form. My guess is that the ? used to be required.
Some of the sample projects I've used from Github fail to compile and I've had to edit them to get them to work. this might be an example where the syntax has changed slightly.
The current version of Swift does not require it, as it is redundant since your variable is already an optional.
Whatever you put in the if let statement does have to be an optional though or you will receive an error
Bound Value in a conditional binding must be of Optional type
Furthermore, if you are casting to a type, you do need to use as? to cast to an optional type.
var str2: Any = ["some", "example"]
if let theStr = str2 as? [String] {
println("\(theStr)")
} else {
println("nil")
}