How to return nil in swift in a custom initializer?
Asked Answered
R

4

22

I am trying to write a custom initializer in swift. I have something like this:

convenience init(datasource:SomeProtocol) {
    assert(datasource != nil, "Invalid datasource")
    if(datasource == nil) {
        return nil
    }
    self.init()
}

The "return nil" line gives me this error: "could not find an overload for '__conversion' that accepts the supplied arguments"

So, all I am trying to accomplish is to have this convenience initializer to return nil if the caller does not provide a valid datasource.

What am I doing wrong here?

Thanks

Replete answered 12/6, 2014 at 16:58 Comment(2)
Your program execution will not continue after the Assert. There is no reason to worry about the case that data source is nil afterwardsCracknel
There is now a solution to this problem. You should consider changing your accepted answer.Envision
B
3

It's because initializers actually don't have return any value. You can't return nil if a method don't expect a return type.

To achieve what you are trying to do (i.e. force your class to have a datasource), just make your datasource not an optional. Variables that are not optional must have a value (so cannot be nil) and must be initialized.

Basra answered 12/6, 2014 at 17:1 Comment(1)
This is no longer accurate as Swift now allows optional initializers.Bangalore
T
51

Update: Starting in Xcode 6.1 beta 1 (available in the Mac Dev Center), Swift initializers can be declared to return an optional:

convenience init?(datasource:SomeProtocol) {
    if datasource == nil {
        return nil
    }
    self.init()
}

or an implicitly-unwrapped optional:

convenience init!(datasource:SomeProtocol) {
    if datasource == nil {
        return nil
    }
    self.init()
}
Tryparsamide answered 15/9, 2014 at 2:41 Comment(2)
I think datasource type should be optional "SomeProtocol?"Callboy
@SuryavelTR: It was that way in the question; and it is not really relevant to the question, so I just left it. It was probably meant to be optional, but I guess it's theoretically possible (though highly unlikely) that SomeProtocol extends NilLiteralConvertible or somethingTryparsamide
C
15

I think this would work, though it's not clear from your post where you are working with dataSource:

class func instOrNil(datasource:A) -> Self? {
    // do something with datasource
    if datasource.isValid() {
        return self()
    } else {
        return nil
    }
}

Update Swift 3

You can, of course, now return nil from an init?(...):

init?(datasource: A){
    if datasource.isValid() {
        // initialise your properties from datasource
    } else {
        return nil
    }
}
Crystie answered 12/6, 2014 at 17:53 Comment(4)
You code works, but it's not efficient. You set the datasource has an optional (using ?) and then validate if there is a datasource. If the parameter is not optional (remove the ?), you will make sure that the value is not optional and you won't have to check if the datasource has a value. The use of if let is not useful in this case, because you don't need to unwrap the value (you don't use it anyway).Basra
@Basra you are correct. No need to make it optional then unwrap it. Changed the code.Crystie
is there a way to do it with an initializer instead of a class method?Tryparsamide
As Emilie said earlier, init_xxx does not have a return type, so no, you can't return nil.Crystie
B
3

It's because initializers actually don't have return any value. You can't return nil if a method don't expect a return type.

To achieve what you are trying to do (i.e. force your class to have a datasource), just make your datasource not an optional. Variables that are not optional must have a value (so cannot be nil) and must be initialized.

Basra answered 12/6, 2014 at 17:1 Comment(1)
This is no longer accurate as Swift now allows optional initializers.Bangalore
T
1

If the superclass of this class is an Objective-C class, and you know that there's some input that will cause its initializer to return nil, you can simply pass that input to superclass's initializer, and that will cause your initializer to return nil:

import Foundation
class Foo : NSURL {
  convenience init(x: Int) {
    if x == 42 {
      self.init(string: "http://www.google.com/")
    } else {
      self.init(string: "not a url") // returns nil
    }
  }
}

let a: Foo? = Foo(x: 42)
println(a) // prints "Optional(http://www.google.com/)"
let b: Foo? = Foo(x: 17)
println(b) // prints "nil"

Of course, this only works if you're lucky enough to have a superclass whose initializer will return nil. So this is not a general solution.

Tryparsamide answered 11/8, 2014 at 6:16 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.