38

When I create a setter such as:

var masterFrame: CGRect {
    set {
        _imageView.frame = newValue
        _scrollView.frame = newValue
    }
}

It's forcing me to make a getter, which I don't want to do.

Is there any way to create a setter in Swift without a getter?

shim
  • 9,289
  • 12
  • 69
  • 108
Aggressor
  • 13,323
  • 24
  • 103
  • 182

8 Answers8

27

Well if I really have to, I would use this.

Swift compiler supports some attributes on getters, so you can use @available(*, unavailable):

public subscript(index: Int) -> T {
    @available(*, unavailable)
    get {
        fatalError("You cannot read from this object.")
    }
    set(v) {
    }
}

This will clearly deliver your intention to the code users.

eonil
  • 83,476
  • 81
  • 317
  • 516
15

From the docs:

The getter is used to read the value, and the setter is used to write the value. The setter clause is optional, and when only a getter is needed, you can omit both clauses and simply return the requested value directly, as described in Read-Only Computed Properties. But if you provide a setter clause, you must also provide a getter clause.

Bryan Chen
  • 45,816
  • 18
  • 112
  • 143
David
  • 3,388
  • 2
  • 21
  • 25
13

Use didSet instead:

var masterFrame:CGRect {
    didSet {
        _imageView.frame = masterFrame
        _scrollView.frame = masterFrame
    }
}
shim
  • 9,289
  • 12
  • 69
  • 108
Matjan
  • 3,591
  • 1
  • 33
  • 31
  • 1
    This would work but could also be confusing / misaligned in terms of API design. It leaves masterFrame public to be read which seems like that's not the goal. – BergerBytes Dec 14 '18 at 19:43
11

A set only property doesn't sound like it makes a lot of sense. You should probably use a method for that instead.

Or, just make the compiler happy and add a getter that you never call:

get {
    return _imageView.frame
}
Dave Wood
  • 13,143
  • 2
  • 59
  • 67
  • Thats my current solution but I wish I didn't have too :( – Aggressor Oct 08 '14 at 01:15
  • 4
    You could also put a ```didSet``` closure on the imageView's frame that updates the scrollView's frame. Or use AutoLayout with constraints to make them equal. – Dave Wood Oct 08 '14 at 01:18
  • 1
    Setters are for setting. Property observers are for performing tasks before/after a property is accessed. You should definitely be using `didSet` for what you're trying to do. – Andrew Oct 08 '14 at 02:52
  • 4
    "A set only property doesn't sound like it makes a lot of sense." Example: Set the output of a motor on a robot, for which no feedback hardware exists. You could remember the last set speed, and hope the hardware is honoring it, but it doesn't mean much – Alexander Mar 16 '18 at 02:03
10

To have a "setter-only property", you can just have a simple function that sets the value; for example, if we have a private property called value, we can have a setter method called, configureWithValue(value:String) that updates the property but doesn't allow outside access to the property:

private var value:String

func configureWithValue(value:String) {
  self.value = value
}
Zorayr
  • 23,770
  • 8
  • 136
  • 129
6

Sure. You can just return a nil and make the type optional:

var color: MyColorEnum? {
    get { return nil }
    set {
      switch newValue! {
      case .Blue:
        view.backgroundColor = UIColor.blueColor()
      case .Red:
        view.backgroundColor = UIColor.redColor()
      }
    }
  }

Alternatively, you may use didSet to avoid the issue all together:

var color: MyColorEnum! {
  didSet {
    switch color {
      case .Blue:
        view.backgroundColor = UIColor.blueColor()
      case .Red:
        view.backgroundColor = UIColor.redColor()
      }
  }
}
seo
  • 1,959
  • 24
  • 18
  • 1
    The problem with this solution is there's nothing stopping someone from setting `nil`, crashing your app (or forcing you to add extra handling for that case). – John Montgomery Nov 20 '18 at 18:38
1

You can give access modifier to set for property and on didSet you can assign it to _imageView.frame & _scrollView.frame

private(set) var masterFrame: CGRect {
    didSet {
        _imageView.frame = masterFrame
        _scrollView.frame = masterFrame
    }
}
shim
  • 9,289
  • 12
  • 69
  • 108
Same7Farouk
  • 837
  • 9
  • 19
0

I have found one exception where I really like a setter only variable which is when I am passing a closure as the only parameter in a method for the sole purpose of saving it of for later execution.

I implement it as a method as follows:

typealias ThemeSetter = () -> ()

class Theme {

    fileprivate var themeSetters:[ThemeSetter] = []

    class var shared: Theme {
        struct Singleton {
            static let instance = Theme()
        }
        return Singleton.instance
    }
        ...

    func setColor(_ entry:@escaping ThemeSetter) {
        entry()
        themeSetters.append(entry)
    }
}

I can then call the method as follows:

    Theme.shared.setColor({
        self.navigationBar.barTintColor = Theme.shared.gameBarTint
    })

But I don't like close paretheses after the close bracket, since I may have many lines in the closure.

So, knowing about Trailing Closures I can just change the code to look like this:

    Theme.shared.setColor() {
        self.navigationBar.barTintColor = Theme.shared.gameBarTint
    }

This is perfectly okay and is the syntax that Apple shows for Trailing Closures. Since there is only the one parameter and being the minimalist that I am I am tempted to make the code even leaner and reduce it to this:

    Theme.shared.setColor {
        self.navigationBar.barTintColor = Theme.shared.gameBarTint
    }

Which is again how Apple shows to do Trailing Closures; Except, this way is usually used for some sort of Predicate where the closure is used to tell the method how to do a sort or when the closure is used at the end of an asynchronous function call as a completion closure. What I want is to assign the closure to ... Something.

If I replace the method for setColor with a variable it looks like this:

    var setColor:ThemeSetter? {
        didSet {
            if setColor != nil {
                setColor?()
                themeSetters.append(setColor!)
                setColor = nil
            }
        }
    }

Now the call looks like this:

    Theme.shared.setColor = {
        self.navigationBar.barTintColor = Theme.shared.gameBarTint
    }

That equals sign means all the difference to be. It shows that I am assigning a closure and not running some function that uses the closure, even though it does. :') <- need emoticon here.

Final notes: This really has nothing to do with closures, it just shows how you can make a setter only variable, except that it still it returns a nil. It is not meant to protect the class from having the user accessing the variable for read like a true setter only would. Also, forgive the tutorial on Trailing Closures and Singletons.

I may not get enough Up Votes to be the correct answer but why is the correct answer, "No you can't! You must use a method call!". I say, "Ni" to that, who needs all that syntax. Just use a variable and don't read it back or if you do, don't have it return anything. kind of like Rudolf Adamkovic answer, which got 0 Up Votes.

Sojourner9
  • 158
  • 11