Aggressor
Aggressor

Reputation: 13551

Create a Setter Only in Swift

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?

Upvotes: 40

Views: 26304

Answers (8)

Same7Farouk
Same7Farouk

Reputation: 907

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
    }
}

Upvotes: 1

Matjan
Matjan

Reputation: 3607

Use didSet instead:

var masterFrame:CGRect {
    didSet {
        _imageView.frame = masterFrame
        _scrollView.frame = masterFrame
    }
}

Upvotes: 12

eonil
eonil

Reputation: 86165

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.

Upvotes: 28

Sojourner9
Sojourner9

Reputation: 128

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.

Upvotes: 0

seo
seo

Reputation: 1967

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()
      }
  }
}

Upvotes: 6

Zorayr
Zorayr

Reputation: 24962

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
}

Upvotes: 10

Dave Wood
Dave Wood

Reputation: 13353

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
}

Upvotes: 11

David
David

Reputation: 3418

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.

Upvotes: 15

Related Questions