0

I have parent and child classes:

class Animal {
   func write(_ block: ((_ animal: Animal) -> Void) ) throws {
      block(self)
   }
}

class Dog: Animal {
   var name: ""
}

I need to change name using write function. Now I do it like:

let dog = Dog()
dog.write { ($0 as! Dog).name = "Bob" }

But I want to do it like (without modification of Dog):

dog.write { $0.name = "Bob" }

How to do this?

Serhii Didanov
  • 2,200
  • 1
  • 16
  • 31
  • Then `Animal` must have the parameter name. Or you have to override the method in `Dog` – Rakesha Shastri Jul 31 '18 at 09:14
  • @RakeshaShastri I want to achieve result without modification of `Dog` or adding parameters to `Animal` – Serhii Didanov Jul 31 '18 at 09:17
  • `Self` is the tool you're looking for – though unfortunately we don't have [universal `Self`](https://github.com/apple/swift-evolution/blob/master/proposals/0068-universal-self.md) yet, so you have to hack it by using a protocol extension. See for example https://stackoverflow.com/a/42356615/2976878 – Hamish Jul 31 '18 at 09:20
  • @SergeyDi i think you need to put name in animal or override method as jay do – Chirag Shah Jul 31 '18 at 09:21
  • 1
    @SergeyDi `name` is Dog-specific property which means that in your particular scenario you *must* cast Animal to Dog. – Yakiv Kovalskyi Jul 31 '18 at 09:30
  • Unrelated but the underscore and the parameter level in the closure are pointless because they are not used at all in Swift 3+ `func write(_ block: ((Animal) -> Void) ) throws {` is sufficient. – vadian Jul 31 '18 at 09:36

2 Answers2

0

Why don't you override the write function in the Dog class as well? Something like below would do.

class Animal {
    func write(_ block: ((_ animal: Animal) -> Void) ) throws {
        block(self)
    }
}

class Dog: Animal {
    var name =  ""

    override func write(_ block: ((Dog) -> Void)) throws {
        block(self)
    }
}

let myDog = Dog()
try?myDog.write{  $0.name = "Bob" }
Jay Mayu
  • 17,023
  • 32
  • 114
  • 148
0

Basically you need the block argument to depend on the class of the object that the method is being called on. One way to achieve this is with a protocol that uses Self:

protocol Writable { }

extension Writable {
    // Self can be used in protocols, and point to the concrete implementation
    // of the protocol
    func write(_ block: ((Self) -> Void) ) throws {
        block(self)
    }
}

// Just need to declare the conformance, all subclasses will inherit it
class Animal: Writable { }

class Dog: Animal {
    var name = "Unnamed"
}

let dog = Dog()
print("Dog name:", dog.name)
try dog.write { $0.name = "Bob" }
print("Dog name:", dog.name)

As expected, the above code will print

Dog name: Unnamed
Dog name: Bob

Note that write is not part of the protocol requirements, you can add it to the list of protocol methods, however using Self as part of the protocol requirements will limit the places where the protocol can be used (collections, non-generic function arguments, etc).

Cristik
  • 30,989
  • 25
  • 91
  • 127