11

I want to lazy/inline implement a protocol in Swift. So in the point of the implementation I will have access to variables outside the protocol scope ,

Same as implementing a interface in Java without declaring a class:

class MyClass:UIView {
  var someComponent:SomeInnerComponent = SomeInnerComponent();
  var count:Int = 0;

  var a = :SomeProtocol { //<----- IS THIS POSSIBLE, IF YES HOW ?
      func a0() {MyClass.count--}
      func a1() {MyClass.count++}
  } 

  someComponenet.delegate = a;
}

protocol SomeProtocol {
  func a0()
  func a1()
}

editing----

thanks i look at this solution, and i didn't see how to access a variable of the parent class. all the examples show an Anonymous class but no one of the examples is accessing the parent variables .

shay te
  • 1,028
  • 3
  • 12
  • 23
  • Note that all of the answers on that other question that discuss closures also addresses your question about "access to variables outside the protocol scope." Closures can capture and modify those variables. – Rob Napier Sep 07 '14 at 02:38
  • thanks i look at this solution, and i didn't see how to access a variable of the parent class. all the examples show an Anonymous class but no one of the examples is accessing the parent variables . – shay te Sep 07 '14 at 17:43

2 Answers2

5

What you're looking for is an inner class (not necessarily an anonymous one), declared in a scope that lets it access the count variable of a MyClass instance, and that adopts a protocol defined at a different scope. Right now Swift has a few of those pieces, but it doesn't look like you can put them all together in any way that's as concise as what you might be looking for.

You might think about declaring an inner class:

class MyView: UIView {
    let someComponent = SomeInnerComponent() // type SomeInnerComponent is inferred
    var count = 0 // type Int is inferred
    class Helper: SomeProtocol {
        func a0() { count-- } // ERROR
        // ...
    }
    init() {
        someComponent.delegate = Helper()
    }
}

But that won't work, because count is implicitly self.count, where self is a Helper instance, not the MyView instance that "owns" the Helper instance. And there isn't a way to reference that MyView instance (or its properties) from within a Helper's methods, because you could just as well construct a MyView.Helper() without having an existing MyView instance. Inner classes (or nested types in general) in Swift nest only in lexical scope, not in existential ownership. (Or to put it another way, since you referenced Java: all inner classes in Swift are like static inner classes in Java. There's no non-static inner class.) If that's a feature you'd like, though, it's probably worth telling Apple you want it.

You could also try declaring Helper inside MyView.init() -- in Swift you can nest type definitions anywhere, including inside functions or methods of other types. Defined there, it can refer to MyView's properties. However, now the type information for Helper is only visible inside of MyView.init(), so when you assign it to someComponent.delegate (whose type is just SomeProtocol), you can't make use of it... this crashes the compiler, even. (That's another bug to report, but it's hard to say whether the bug is really "compiler crashes on valid usage" or "code is bad, but compiler crashes instead of producing error".)

The closest solution I can come up with looks something like this:

class SomeInnerComponent {
    var delegate: SomeProtocol?
}
protocol SomeProtocol {
    func a0()
    func a1()
}
class MyClass  {
    var someComponent = SomeInnerComponent()
    var count = 0
    struct Helper: SomeProtocol {
        var dec: () -> ()
        var inc: () -> ()
        func a0() { dec() }
        func a1() { inc() }
    }
    init() {
        someComponent.delegate = Helper(
            dec: { self.count -= 1 }, // see note below
            inc: { self.count += 1 }
        )
    }
}

How it works:

  • Helper is an inner struct (could be a class, but a struct is simpler)
  • It implements the a0 and a1 methods, satisfying the requirements of SomeProtocol
  • The implementations of a0 and a1 call through to the closures dec and inc, which are stored properties (aka instance variables) of the Helper struct
  • You write (or otherwise specify) these closures when you construct a Helper instance (using the default member-wise initializer, Helper(dec: (Void -> Void), inc: (Void -> Void)))
  • Because you can write the closures when initializing a Helper, those closures can capture variables where you're calling the initializer, including the implicit self that refers to the MyClass instance creating the Helper.

You need both a0/a1 and dec/inc because you need closures (the latter), not methods, for capturing the enclosing state. And even though closures and funcs/methods are in many ways interchangeable, you can't create a method/func implementation by assigning a closure to a method/func name. (It'd be a different story if SomeProtocol required closure properties instead of methods, but I'm assuming SomeProtocol isn't something under your control.)

Anyway, this is kind of a lot of boilerplate and a layer of abstraction that you might not really need, so it's probably worth looking into other ways to architect your code.


Note: my example uses the closure { self.count -= 1 } where you might expect { self.count-- }. The latter doesn't work because that's an expression with a value, so Swift will interpret it as shorthand for the closure's return value. Then it'll complain that you assigned a () -> Int closure to a property that expects a () -> () (aka Void -> Void) closure. Using -= 1 instead works around this issue.

rickster
  • 124,678
  • 26
  • 272
  • 326
  • thanks!, nice hack :), this example can serves me, why do i need 'dec','inc' methods. can i use a0, 01 directly ?. is this a limitation in swift ? – shay te Sep 09 '14 at 22:22
3

I would go for a different approach, I know this a pretty old topic but just in case someone else struggles with this issue:

class MyClass:UIView {
  var someComponent:SomeInnerComponent = SomeInnerComponent();
  var count:Int = 0;

  init(){
    // Assign the delegate or do it somewhere else to your preference:
    someComponenet.delegate = ProtocolImplementation(myClass: self);
  }

  private class ProtocolImplementation: SomeProtocol {
      let selfReference: MyClass          

      init(myClass: MyClass){
         selfReference = myClass
      } 

      public func a0(){
         selfReference.count--
      }

      public func a1(){
          selfReference.count++
      }
  }
}

protocol SomeProtocol {
  func a0()
  func a1()
}

By following this approach it's also possible to include the same protocol multiple times, lets say your Protocol supports a generic and you want to implement it twice. SomeProtocol< SomeObject > and SomeProtocol< OtherObject > could be both used this way if needed.

Kind regards

Koen
  • 184
  • 1
  • 5