Goppinath
Goppinath

Reputation: 10739

How to return `[Self]` from a Swift Protocol?

I am having a Protocol implementation as follows.

protocol DatabaseInjectable {

    static func deriveObjectFromDBRow(row: [String]) -> Self? // Method - 1

    static func collectAllObjectsForDatabaseAction(action: (Database) -> Void) -> [Self]? // Method - 2
}

Where I am successful with the correspondent implementation of Method - 1 like this:

static func deriveObjectFromDBRow(row: [String]) -> Self? {

    ...
}

But I could not implement the Method - 2 like this:

static func collectAllObjectsForDatabaseAction(action: (WWDatabase) -> Void) -> [Self]? {

    ...
}

There I am getting an error like this:

'Self' is only available in a protocol or as the result of a method in a class;

Any help to return the array form of Self (the class it self) would be nice.

Upvotes: 1

Views: 1778

Answers (3)

dfrib
dfrib

Reputation: 73176

You can make use of a typealias in your protocol DatabaseInjectable, and use this as an alias to Self type in the classes conforming to your protocol.

class Database {
    var desc : String = "Default"
}

protocol DatabaseInjectable {
    typealias MySelf

    static func deriveObjectFromDBRow(row: [String]) -> MySelf?

    static func collectAllObjectsForDatabaseAction(action: (Database) -> Void) -> [MySelf]?
}

class MyClass : DatabaseInjectable {
    typealias MySelf = MyClass

    static func deriveObjectFromDBRow(row: [String]) -> MySelf? {
        return MyClass()
    }

    static func collectAllObjectsForDatabaseAction(action: (Database) -> Void) -> [MySelf]? {
        return [MyClass(), MyClass()]
    }
}

/* example */
let closure : (Database) -> () = { print($0.desc) }
var arr : [MyClass]? = MyClass.collectAllObjectsForDatabaseAction(closure)
/* [MyClass, MyClass] */

One drawback here is, however, that you could set e.g. typealias MySelf = Int (in the class) and have your functions return an integer/array of integers (rather than self/[Self]), and still conform to you protocol. Possibly this is a deal-breaker.

Upvotes: 0

phimage
phimage

Reputation: 274

If you can set your class final you can replace Self by the class name

final class SampleClass: DatabaseInjectable {
    init() {
    }
    static func deriveObjectFromDBRow(row: [String]) -> SampleClass? {
        return SampleClass()
    }

    static func collectAllObjectsForDatabaseAction(action: (Database) -> Void) -> [SampleClass]? {
        let array = [SampleClass]()
        return array
    }
}

Upvotes: 1

Ramy Kfoury
Ramy Kfoury

Reputation: 947

There's a very well written answer here, but in short, you can define your protocol as such:


protocol DatabaseInjectable {

    static func deriveObjectFromDBRow(row: [String]) -> DatabaseInjectable? // Method - 1

    static func collectAllObjectsForDatabaseAction(action: (Database) -> Void) -> [DatabaseInjectable]? // Method - 2
}

Upvotes: 0

Related Questions