2

I wish to declare a private variable in a class in Swift and access it with public (actually internal) methods. Is the following code about right?

class someClass {

  // Using underscore here to distinguish variable and method
  private var _privateArray: [String]
  ...
  func privateArray() {
    return _privateArray
  }

  func setPrivateArray(someArray: [String]) {
    _privateArray = someArray
  }
}

The reason I want to use the above is because I want to use the array in any subclasses, but with a name which is meaningful to the subclass.

class someSubclass: someClass {

  var arrayWithMeaningfulName: [String]

  init() {
    arrayWithMeaningfulName = super.privateArray()
    ...
  }
}

Not sure if the above is the best way to achieve what I want. Be grateful for feedback.

By the way, I did try declaring the private variable as follows, but the compiler complained:

class someClass {

  private var _privateArray: [String] {
    get {
      ...
    }

    set {
    ...
  }
  ...
Steve
  • 696
  • 7
  • 15
  • What makes you think it might not be "about right"? – Scott Hunter Mar 11 '16 at 12:44
  • 1
    What would be the advantage/usage of public accessors for a private variable (serious question)? – koen Mar 11 '16 at 13:00
  • `"The reason I want to use the above is because I want to use the array in any subclasses, but with a name which is meaningful to the subclass."` This doesn't make a whole lot of sense to me. Sure, I'm all for well-named variables, but I think I need a more concrete example of what you're trying to accomplish here because it doesn't make a whole lot of sense. – nhgrif Mar 11 '16 at 13:05
  • 1
    Scott Hunter I think Thomas A has answered your quesion; I should have used a computed property instead of the two methods. – Steve Mar 11 '16 at 13:26
  • Valid question Koen, which I asked myself. The only reason for doing this is so the variable in the subclass can have a more meaningful name (revelant to its class), but the code sits in one place in the parent class. – Steve Mar 11 '16 at 13:29
  • nhgrif, the name I would give to the variable in the parent class is quite generic. Whereas if I could use a name specific to the each subclass one could instantly understand what the variable is used for by the name. Another option would be to just add a comment to the superclass or subclasses. – Steve Mar 11 '16 at 13:33
  • @Koen process a private variable with public method, is known as `data encapsulation` which is a best pratice, see this post: http://stackoverflow.com/questions/5673829/what-is-encapsulation-how-does-it-actually-hide-data – Thomas Mar 11 '16 at 13:44
  • @Thomas a: Thanks for the link, if I understand correctly `data encapsulation` is for `getter` only, but the TS also wants to be able to `set` the private variable from outside. That part I don't understand. – koen Mar 11 '16 at 13:55
  • 1
    @Koen You also need to encapsulate your data even when you set it... So you can filter values you don't want i.e: `if myValue < 0, then don't set it and return an error` – Thomas Mar 11 '16 at 13:58

3 Answers3

2

If you want to declare a private member, all you have to do in your class is:

class myClass {

    private var _myVar = [String]() // For example

    var myVar: [String] {

        // Getter
        get {
            return self._myVar
        }

        // Setter
        set(newValue) {

            self._myVar = newValue
        }
    }
}
Thomas
  • 1,289
  • 1
  • 17
  • 29
  • Doh! So just to confirm, myVar is a computed property? I must be too tired...thanks. – Steve Mar 11 '16 at 13:16
  • `myVar` is the member of your class you want to process... so yes it is – Thomas Mar 11 '16 at 13:19
  • while your code is correct it doesn't provide what I want as I don't want public access to _myVar, even through myVar. – Steve Mar 29 '16 at 07:03
2

Note that the Swift book has the following:

You can give a setter a lower access level than its corresponding getter, to restrict the read-write scope of that variable, property, or subscript. You assign a lower access level by writing private(set) or internal(set) before the var or subscript introducer.

so, in your example, you could simply have

public class SomeClass {
    public private(set) var privateArray: [String] = []
    public func setPrivateArray(array: [String]) {
       // do some checking, possibly
       self.privateArray = array
    }
}

Your code has various syntax errors in it - you are missing func on your public functions, and notice that you cannot give a property or function a greater access level than its parent object, so you will have to declare the class itself as public.

Grimxn
  • 22,115
  • 10
  • 72
  • 85
  • Grimxn, thanks for pointing out the errors in my dodgy code, hopefully I have corrected them all. When I say "public" I really mean the default, internal. – Steve Mar 11 '16 at 13:51
1

The following seems to work.

class SomeClass {

    // Using underscore here for consistency with my original post
    private var _privateArray: [String] = ["a", "b", "c"]

    func reverse() {
        _privateArray = _privateArray.reverse()
    }
}

class SomeSubclass: SomeClass {

    // The array in this subclass is a computed property
    var arrayWithMeaningfulName: [String] {
        get {
            return super._privateArray
        }

        set(newArray) {
            super._privateArray = newArray
        }
    }
}

Important: SomeSubclass must be defined in the same source file SomeClass is defined in for it to have access to the private member _privateArray. For example, if SomeClass was defined in the file SomeClass.swift, then SomeSubclass must also be defined in the file SomeClass.swift. If not, the compiler will complain with message "Value of type 'SomeClass' has no member variable '_privateArray'."

The "reverse" method was to demonstrate that common code could be defined in the parent class to avoid duplication, even though each subclass would have a different (more meaningful) name for the member variable.

Steve
  • 696
  • 7
  • 15