Swift assert() not working with simple comparison to nil - swift

This is driving me banerners.
Here's the XCode 6.4 autocomplete from typing assert in Swift:
assert(condition: Bool, message: String)
Here's how I'm using it in my init:
required init (mainController: MainVC) {
assert(mainController.parameterThatShouldNotBeNil != nil,
"oops parameterThatShouldNotBeNil is nil")
super.init()
}
Here's the error message from the little red exclamation point:
Cannot invoke 'assert' with an argument list of type '(Bool, String)'
...has me pulling my hair out. Double You Tee Eff?
EDIT
Here's a short example that can be pasted into XCode:
class HasNoNil {
var definitelyNotNil: String = "nope not nil"
}
class ChecksForANil{
init (objectToCheck: HasNoNil){
assert(objectToCheck.definitelyNotNil != nil, "whoops it actually is nil")
}
}
That gives the same error I'm seeing.

The underlying problem is that Swift can't compare a non-Optional value with nil, basically because that comparison doesn't make sense: it's not Optional, so it can't possibly be nil, and you can work that out at compile time (or rather, there's no overload for != set up between non-Optional and nil, because there doesn't need to be.)
If you try a simpler example without the assert:
class HasNoNil {
var definitelyNotNil: String = "nope not nil"
}
class ChecksForANil{
init (objectToCheck: HasNoNil){
if (objectToCheck.definitelyNotNil != nil) { println("Not nil") }
// assert(objectToCheck.definitelyNotNil != nil, "Hrm")
}
}
...you'll get a more sensible error: "Could not find an overload for '!=' that accepts the supplied arguments". (Xcode 6.2/Swift 1.2) If you make definitelyNotNil an Optional, then everything works fine.
So, I think this is just a misleading error message. Sadly, this type of misleading error, when the problem is with the parameters to a function, seems quite prevalent in early Swift compilers. You might want to try it under Swift 2 and raise a report with Apple if it's not fixed already.

Related

if statements and optionals in Swift

Is there a difference between something like
if let error = error {
print(error.localizedDescription)
}
and just checking if it is = nil
if error != nil {
print(error.localizedDescription)
}
if I want to check if error has a value? Think of firebase's creating user function.
Yes.
The if let statement allows you to bind the value of error to a variable if it is non-nil and use it in the block. If maybeError is of type Error?, when you do:
if let error = maybeError {
/* block contents */
}
the type of error will be Error within the block - ie: It won't be an optional anymore. If you just do a nil-check using if, error will still be of type Error? within the block. So the code that would actually be equivalent to your first snippet would be:
if error != nil {
print(error!.localizedDescription)
}
(Your second snippet, as it is, won't compile, as you're trying to get the localizedDescription variable of an Error? object, which has no such property)
By the way, in case you haven't seen it before, the !. thing is the unwrap operator. It runs the method on the object if the object is non-nil, but it crashes in case the object is nil. In this case, you generally know it won't crash. (But this might not actually be safe depending on where and how you use it - check #rmaddy's comment)
In the first, error is now a non-optional type, so you can use .. This is also idiomatic -- it more clearly shows what you are trying to do.
In the second, you would need to use ?. to look at error's properties (your code won't compile because you haven't done that).

Swift Compiler Error,Expression type 'Error' is ambiguous without more context

typealias SwiftAMapCompletion = (CLLocation?,AMapLocationReGeocode?,Error) -> Void
var locationResult : SwiftAMapCompletion?
I want to give a nil as Error, but "Swift Compiler Error" is
Expression type 'Error' is ambiguous without more context.(SwiftAMapCompletion can't change)
locationResult!(location, reGeocode, nil as! Error)
You cannot force nil to be an Error, not even if you use as!.
Your options are:
Change the declaration to be Error?, because that would mean that you can pass nil.
Pass an Error.
For example:
enum MyError: Error {
case ok
}
locationResult!(location, reGeocode, MyError.ok)
In my opinion, your SwiftAMapCompletion interface does not make any sense, because normally a callback like this would be "here is the result, or here is the error" so all parameters should be declared as optional (with ?). I would get this interface changed if you can.

How to avoid returning an optional after throwing an exception?

I'm writing a utility function which takes a parameter and always returns a valid non-nil result (notice the returned value is not optional because the possible parameters are all actually hardcoded and valid, so I know the function cannot fail):
func myFunction(param: String) -> NonTrivialObject {...}
Now, during development I want to experiment with possible parameters and, should I make a mistake, I want the function to throw an exception and just crash. I don't want or need to throw Swift errors or catch them, I want to hard-crash and fix the parameter immediately. In Objective C I would just use NSParameterAssert() or do something along these lines:
guard let validatedParam = param where param != nil else {
NSException(...).raise()
return nil
}
// do the actual work and return a non-optional result
However, I cannot return nil because the result is not an optional. Is there a way to somehow tell the compiler that it doesn't need to bother returning anything from the function after an exception is thrown? Or am I doomed to litter my code with unwrapping optionals or try! statements or to return a dummy object just to make the compiler pleased?
You can use Swift assert(_:_file:line:) function as follows
assert(some condition, "Message to display if condition is false (optional)" )
If the condition is verified the app will continue running, otherwise it will terminate
An optional may contain nil, but Swift syntax forces you to safely deal with it using the ? syntax to indicate to the compiler you understand the behavior and will handle it safely.
You can define the function to be a throwing function:
func foo(param: String) throws -> NonTrivialObject {
guard param != nil else {
throw SomeErrorEnum.NilFound
}
doStuff(...)
}
The error enum needs to conform to the ErrorType protocol. The function call is now able to catch errors like this:
do {
try foo(param)
} catch SomeErrorEnum.NilFound {
print("Found nil")
}
In this way the function returns a non-optional but can also throw errors. For more information see: https://developer.apple.com/library/prerelease/ios/documentation/Swift/Conceptual/Swift_Programming_Language/ErrorHandling.html

Can Swift optional be nested?

I cannot find the answer to this question, for example can I have optional of an optional of a String? I tried to write a small test to check it out:
let a : String? = nil;
let b : String?? = a;
if b!=nil { // error
println("has value");
}
else {
println("fail");
}
but since I am not a Swift programmer I don't know what to do with error saying "cannot assign to the result of this expression".
Yes you can; your syntax is incorrect though. This line:
if b!=nil
Is digested by the compiler as:
if (b!) = nil
... so it thinks you're trying to assign nil to the unwrapped optional. Swift doesn't allow you to make assignments within if statements (in contrast to Objective-C). Instead be clearer:
if b != nil
EDIT: and, to finish the thought, proving that the syntactic sugar really is making an optional optional, if you add:
if let b = b {
print("\(b)")
}
You should see nil as the printed output.

Error message: initialiser for conditional binding must have optional type, not '()'

I am getting error:
initialiser for conditional binding must have optional type, not '()'.
Using Swift language, below is the code:
if let result = brain.performOperation(operation)
I think I can answer your question, but I don't know how much it will help you.
The way if let someVar = someOptional { } is used is to check the value of a variable (someOptional) to see if it is nil or a "value". If it is not nil then someVar will be assigned the non-nil value of someOptional and execute the code between { } which can safely reference someVar, knowing it is not nil. If someOptional is nil then the code within { } is bypassed and not executed.
The comment you posted above indicates that the performOperation() method is this:
func performOperation(symbol:String) {
if let operation = knownOps[symbol] {
opStack.append(operation)
}
}
This method does not return anything, or more formally it returns void aka (). void, or () is not a value, nor is it nil.
So when you have this statement
if let result = brain.performOperation(operation) { }
the compiler complains because it expects brain.performOperation(operation)
to return either nil or a value, but not void aka () which is exactly what the method returns.
If you are still confused about optionals, be sure to read as much as you can of the Swift Language Reference. Optionals are a big part of the language and once you get used to them you will find them very invaluable.