smac89
smac89

Reputation: 43068

In swift, how do I return an object of the same type that conforms to a protocol

I have the following protocol

protocol JSONInitializable {
    static func initialize(fromJSON: NSDictionary) throws -> Self?
}

Now I'm trying to make that function return whatever class has conformed to the protocol. I.e. if I have class Foo conforming to the protocol, I want to return a Foo object from the method. How can I do this?

extension Foo: JSONInitializable {
    static func initialize(fromJSON: NSDictionary) throws -> Foo? {
    }
}

I get the error:

Method 'initialize' in non-final class 'Foo' must return 'Self' to conform to protocol 'JSONInitializable'

Upvotes: 4

Views: 1952

Answers (2)

nhgrif
nhgrif

Reputation: 62052

Autocomplete will help you, but basically, if Foo is a class, you just need a method which matches the exact signature of the method in your protocol:

class Foo {}

protocol JSONInitializable {
    static func initialize(fromJSON: [String : AnyObject]) throws -> Self?
}

extension Foo: JSONInitializable {
    static func initialize(fromJSON: [String : AnyObject]) throws -> Self? {
        return nil
    }
}

As a note here, you will have to, within this method, replace all instances of Foo with Self. If you want to use a constructor, it will have to be one marked as required.

With this in mind, it probably makes more sense to change your protocol to requiring an initializer rather than a static method called initialize, in which case, take a look at Blake Lockley's answer. However, this answer stands to answer the question of how to deal with Self in protocols, as there are certainly cases where we might want a method that returns Self that isn't an initializer.


If Foo is not a class, it is a value type which cannot be subclasses, and as such, we return the name of the type:

struct Foo: JSONInitializable {
    static func initialize(fromJSON: [String : AnyObject]) throws -> Foo? {
        return nil
    }
}
enum Foo: JSONInitializable {
    case One, Two, Three

    static func initialize(fromJSON: [String : AnyObject]) throws -> Foo? {
        return nil
    }
}

The reason you need to return Self? from the method in the case of a class is because of inheritance. The protocol declares that if you conform to it, you will have a method called initialize whose return type will be an optional version of whatever you are.

If we write Foo's initialize method as you wrote it, then if we subclass Foo with Bar, then now we have broken our conformance to the protocol. Bar has inherited the conformance to the protocol from Foo, but it doesn't have a method which is called initialize and returns Bar?. It has one that returns Foo.

Using Self here means that when our subclasses inherit this method, it will return whatever type they are. Self is Swift's equivalent of Objective-C's instancetype.

Upvotes: 3

Blake Lockley
Blake Lockley

Reputation: 2961

As mentioned in nhgrif answer change the return type of Foo? to self?.

Alternatively

In Swift you can declare inits in protocol so try something like this:

protocol JSONInitializable {
    init?(fromJSON: NSDictionary) throws
}

extension Foo: JSONInitializable {
    required init?(fromJSON: NSDictionary) throws {
      //Possibly throws or returns nil
    }
}

Upvotes: 1

Related Questions