Home > Blockchain >  Dynamically defining class properties in Swift
Dynamically defining class properties in Swift

Time:10-18

I have a class Population which upon initialisation gets defined a language. For the sake of this example, every member of that population only speaks this particular language. I then have a Member class defined within the Population for the population members. In this class, I would like to store the language as a static type property to be able to reference if from Member methods like in the example code below.

This seems more efficient to me than (a) always have to pass the language as a parameter when I call a method on a members element or (b) to store the language on each Member instance. However, below code does not quite seem to work and I wonder how to best implement this. Any ideas / best-practices? Apologies if this may sound rather trivial or in case I am completely on the wrong path here.

class Population {
    var language: String
    var members: [Member]
    
    init(language: String) {
        self.language = language
        for _ in 1...10 {members.append(Member())}
    }
    
    class Member {
        static let language: String
        
        func speak() {
            switch self.language {
                case "english": print("speak some english")
            }
        }
        
        func write() {
            switch self.language {
                case "english": print("write some english")
            }
        }
    }
}

CodePudding user response:

I think there is some confusion about what static means, and it probably stems from class Member being nested in class Population. All nesting types do is enclose the nested type in a namespace, which helps a) avoid name collision on types in global scope, and b) enables you to hide it as an implementation detail of the type in which it is nested, if you choose to make it a private type.

A nested type does not belong to an instance of the enclosing type. So if you define language as a static property of Member, then it has one value for class Member, regardless of how many instances of Population you have with different languages.

What you want is for each instance of Member to have the language of the Population to which it belongs. You can accomplish that a couple of different ways, but they all involve storing something in Member.

Option 1. Make language an instance property of Member, and set it in Member.init(language: String).

Option 2. Store a reference to the Population to which the Member belongs (I'll assume it's let population: Population), and make language a computed property which returns population.language.

Option 2 is more flexible, because you can leverage having the population to fetch other Member values that it gets from its Population, but it requires some careful consideration. Does it make sense in your app for a Member to exist outside of a Population? If yes, then you probably want to define it like weak var population: Population?. If no, a Member cannot exist outside of a Population, then unowned let population: Population could be preferred (though the weak version could be used too). The point here is to avoid reference cycles that would cause a memory leak.

So maybe something like this:

class Population {
    var language: String
    var members: [Member] = []
    
    init(language: String) {
        self.language = language
        for _ in 1...10 {members.append(Member(population: self))}
    }
    
    class Member {
        unowned let population: Population
        var language: String { population.language }

        init(population: Population) { self.population = population }
        
        func speak() {
            switch self.language {
                case "english": print("speak some English")
                case "german" : print("spreche ein Bißchen deutsch")
                // other cases go here
                default: print("mmmm mmmm mmm mmm") // I'm a mute
            }
        }
        
        func write() {
            switch self.language {
                case "english": print("write some English")
                case "german" : print("schreibe ein Bißchen deutsch")
                // other cases go here
                default: print("h8hta;lushgaih9732 8@#Y"); // Can't write
            }
        }
    }
}

Though off-topic, I would recommend making language an enum, Then you can remove the default cases from the switch statements, and the compiler will helpfully, though not tactfully, show you all of the switch statements that need to be updated when you add a new language. If you like you can even make the enum based on String so you can still access the String value wherever that might be useful:

    enum Language: String
    {
        case arabic     = "arabic"
        case chinese    = "chinese"
        case english    = "english"
        case french     = "french"
        case german     = "german"
        case japanese   = "japanese"
        case portuguese = "portuguese"
        case spanish    = "spanish"
        // etc...
    }

Then if you want the string value you can refer to language.rawValue

  • Related