2

Let's say I have one parent class called Car with some properties and one method which actually does nothing.

class Car {
    var brandName : String
    var yearOfCreation : Int
    var color : String
    var numberOfDoors : Int

    func manipulateWithCar() {
    }

    init(brandName : String, yearOfCreation : Int, color : String, numberOfDoors : Int) {
        self.brandName = brandName
        self.yearOfCreation = yearOfCreation
        self.color = color
        self.numberOfDoors = numberOfDoors
    }
}

And I have two child classes called SportCar and TankerCar. They have their unique properties and also the method from parent class is overridden and now it does something.

class SportCar : Car {
    var turbo : Bool
    var nitrousOxideEngine : Bool

    override func manipulateWithCar() {
        print("Putting turbo on your car...")
        turbo = true
    }

    init(brandName: String, yearOfCreation: Int, color: String, numberOfDoors: Int, turbo : Bool, nitrousOxideEngine : Bool) {
        self.turbo = turbo
        self.nitrousOxideEngine = nitrousOxideEngine
        super.init(brandName: brandName, yearOfCreation: yearOfCreation, color: color, numberOfDoors: numberOfDoors)
    }
}

And also there is one enum for the method. This is not important but you just want to keep it as in the homework.

enum TankerForm {
    case cilindrical
    case quadratic
}

class TankerCar : Car{
    var tankerFilledSize : Int
    var tankerForm : TankerForm

    init(brandName : String, yearOfCreation : Int,
         color : String, numberOfDoors : Int, tankerVolum : Int, tankerForm : TankerForm) {
        self.tankerForm = tankerForm
        self.tankerFilledSize = tankerVolum
        super.init(brandName: brandName, yearOfCreation: yearOfCreation, color: color, numberOfDoors: numberOfDoors)
    }

    override func manipulateWithCar() {
        print("Loading 200 gallon of oil")
        tankerFilledSize = 200
    }
}

Creating some objects...

var kia = Car(brandName: "Rio", yearOfCreation: 2015, color: "Green", 
numberOfDoors: 4)

var toyota = Car(brandName: "Corolla", yearOfCreation: 2015, color: 
"Red", numberOfDoors: 4)

var lamborgini = SportCar(brandName: "Aventador", yearOfCreation: 2018, 
color: "Black", numberOfDoors: 2, turbo: false, nitrousOxideEngine: 
false)

var mazeratti = SportCar(brandName: "Ghibli", yearOfCreation: 2017, 
color: "Red", numberOfDoors: 4, turbo: true, nitrousOxideEngine: false)

var kamaz = TankerCar(brandName: "Kamaz", yearOfCreation: 2007, color: 
"Yellow", numberOfDoors: 2, tankerVolum: 0, tankerForm: .cilindrical)

var belaz = TankerCar(brandName: "Belaz", yearOfCreation: 2003, color: 
"White", numberOfDoors: 2, tankerVolum: 0, tankerForm: .quadratic)

Creating arrays with objects...

var carrsArray : [Car] = [kia, toyota]
var sportCarsArray : [SportCar] = [lamborgini, mazeratti]
var tankerCarsArray : [TankerCar] = [kamaz, belaz]

And printing all the properties for Car class objects using for loop...

for value in carrsArray {
    print("This car is \(value.brandName)")
    print("The color is \(value.color)")
    print("The car has \(value.numberOfDoors) doors")
    print("The year of creation is \(value.yearOfCreation)")
    print(" ")
}

Printing all the properties for SportCar class objects using for loop...

for value in sportCarsArray {
    print("This car is \(value.brandName)")
    print("The color is \(value.color)")
    print("The car has \(value.numberOfDoors) doors")
    print("The year of creation is \(value.yearOfCreation)")
    if value.nitrousOxideEngine == true {
        print("This car has Nitrous Oxide Engine")
    } else {
         print("There is no Nitrous Oxide Engine on this car.")
    }
    if value.turbo == true {
        print("This car has turbo")
    } else {
        print("There is no turbo in this car.")
    }
    print(" ")
}

Printing all the properties for TankerCar class objects using for loop...

for value in tankerCarsArray {
    print("This car is \(value.brandName)")
    print("The color is \(value.color)")
    print("The car has \(value.numberOfDoors) doors")
    print("The year of creation is \(value.yearOfCreation)")
    print("The tanker is filled with \(value.tankerFilledSize) lb of oil.")

    if value.tankerForm == .cilindrical {
        print("This car has cilindrical tanker form.")
    } else {
        print("This car has quadratic tanker form.")
    }
    print(" ")
}

Now the question. Is there any way to print all the properties for different classes? I tried to create a new array of three arrays:

var allClassesArrays = [carrsArray, sportCarsArray, tankerCarsArray]

But during iteration, it was able to get all the properties of the parent class.

3
  • Perhaps use Mirror? Mirror Commented Jan 28, 2018 at 23:22
  • You could iterate through carrsArray + sportCarsArray + tankerCarsArray (using map to upcast [SportsCar] and [TankerCar] to [Car] if necessary, idr), but your element type during iteration would be Car. If you want SportsCar and TankerCar specific properties, you would need to downcast in the loop bodyy. Commented Jan 28, 2018 at 23:32
  • An alternative solution is to make Car: CustomStringConvertible, and have every Car subclass define how it converts itself to string. Then in your loop, you just access the description, and have the dynamic dispatch automatically resolve the correct conversion for you. Commented Jan 28, 2018 at 23:34

1 Answer 1

0

This is what reflection is for. You can add a property / method to the Car class that list all the properties of the current class, plus those of the superclass. When this property / method is dynamically applied to the subclasses, it will give what you are looking for.

We need an extension to Mirror that returns all properties of the current instance and those of its superclass recursively, taken from Martin R's answer:

extension Mirror {
    func toDictionary() -> [String: Any] {
        var dict = [String: Any]()

        // Attributes of this class
        for attr in self.children {
            if let propertyName = attr.label {
                dict[propertyName] = attr.value
            }
        }

        // Attributes of superclass
        if let superclass = self.superclassMirror {
            for (propertyName, value) in superclass.toDictionary() {
                dict[propertyName] = value
            }
        }

        return dict
    }
}

class Car: CustomDebugStringConvertible {
    // ...

    var debugDescription: String {
        let dict = Mirror(reflecting: self).toDictionary()
        return dict.map { "\($0.key) = \($0.value)" }.joined(separator: "\n")
    }
}

Usage:

var allClassesArrays = [carrsArray, sportCarsArray, tankerCarsArray]
for car in allClassesArrays.flatMap({ $0 }) {
    print(car.debugDescription, terminator: "\n\n")
}

You of course don't have to conform Car to CustomDebugStringConvertible or name the property debugDescription. I just find them convenient since the debugger will use the debugDescription when you type po car.

Sign up to request clarification or add additional context in comments.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.