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

zumzum
  • 17,984
  • 26
  • 111
  • 172
  • Your program execution will not continue after the Assert. There is no reason to worry about the case that data source is nil afterwards – drewag Jun 12 '14 at 17:14
  • There is now a solution to this problem. You should consider changing your accepted answer. – Jarsen Feb 12 '15 at 17:55

4 Answers4

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()
}
Mike Seghers
  • 1,925
  • 2
  • 16
  • 12
user102008
  • 30,736
  • 10
  • 83
  • 104
  • I think datasource type should be optional "SomeProtocol?" – Suryavel TR May 11 '15 at 11:57
  • @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 something – user102008 May 11 '15 at 18:49
13

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
    }
}
Grimxn
  • 22,115
  • 10
  • 72
  • 85
  • 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). – Emilie Jun 12 '14 at 19:20
  • @Emilie you are correct. No need to make it optional then unwrap it. Changed the code. – Grimxn Jun 12 '14 at 19:32
  • is there a way to do it with an initializer instead of a class method? – user102008 Jun 12 '14 at 23:25
  • As Emilie said earlier, init_xxx does not have a return type, so no, you can't return nil. – Grimxn Jun 13 '14 at 07:16
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.

David
  • 3,285
  • 1
  • 37
  • 54
Emilie
  • 2,413
  • 13
  • 12
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.

user102008
  • 30,736
  • 10
  • 83
  • 104