Search code examples
swiftpolymorphismwhere-clauseprotocolsextension-methods

Extension that only works in a particular object


I am thinking about an extension that will only work inside a particular object. I don't know if it's possible. Example:

I have a model. This model will get a function on the extension that only works inside a particular class, and it will trigger a method from that class.

Something like this (of course doesn't work but it shows what I want to do):

struct DataModel {}

extension DataModel where Self: MyProtocol  {
    func execute() {
        self.start()
    }
}

protocol MyProtocol {
    func start()
}

class Class_A: MyProtocol {
    func start() {
        print("Model extension worked") // Will work
    }
    
    let model = DataModel()
    
    func doSomething() {
        model.execute()
    }
    
}

class Class_B {
    
    let model = DataModel()
    
    func doSomething() {
        model.execute() // Nothing will happen
    }

}

So my model extension should work depending on the context without type-checking and entering information about the class. It must detect the "self" and run the method accordingly. How can i do that?


Solution

  • Your DataModel is already a definitive type that can't be specified further. The where clause would be valid only if the extended type is generic, i.e. could be specified in some way. More specifically, if DataModel conforms to MyProtocol, it's known at the compile time, there's no room for where.

    The second questionable thing is that you try to fit MyProtocol on DataModel's extension, but below, only classes conform to it.

    My best guess is that you mean something like this:

    final class DataModel<EnclosingType: AnyObject> {
    
        private weak var enclosingObject: EnclosingType? // weak to avoid reference cycles.
    
        init(enclosingObject: EnclosingType) {
            self.enclosingObject = enclosingObject
        }
    
    }
    
    protocol MyProtocol {
        func start()
    }
    
    extension DataModel where EnclosingType: MyProtocol  {
        func execute() {
            enclosingObject?.start()
        }
    }
    
    final class ClassA: MyProtocol {
    
        private lazy var model = DataModel(enclosingObject: self)
    
        func start() {
            print("Model extension worked")
        }
    
        func doSomething() {
            model.execute()
        }
    
    }
    
    final class ClassB {
    
        private lazy var model = DataModel(enclosingObject: self)
    
        func doSomething() {
            // model.execute() – Nope.
        }
    
    }
    

    The thing to keep in mind here is that Swift is strong-typed language, and if the condition where EnclosingType: MyProtocol doesn't stand, the method is not available even at the compile time. You don't need to check types, the compiler will just not allow you to use execute() (see ClassB.)