0

In my model, I have a singleton class which will contain some global properties and methods. I think I've set up the class correctly but I need a way to verify incoming data for the properties. I'm trying to use get and set but these seem to need to return void. I can't use init because it's a singleton.
Am I missing something?

final class Globals {

    private init(){}

    static let sharedInstance = Globals()

    //MARK: Properties
    private var _peopleCount: Int!

    var peopleCount: Int! {
        get {
            return _peopleCount
        }
        set(newPeopleCount) {
            guard newPeopleCount > 0 else {
                return nil  // can't return nil here
            }
        }
    }
}
Rob
  • 14,746
  • 28
  • 47
  • 65
Sean
  • 629
  • 8
  • 24
  • *"Can‘t return nil here"* - do you want to, though? Then change `peopleCount: Int!` to `peopleCount: Int?`. – LinusGeffarth Sep 30 '17 at 12:36
  • You should use didSet instead of using a private property with an underscore prefix. `var peopleCount: Int? { didSet { if let newValue = peopleCount, newValue > 0 { peopleCount = newValue } else { peopleCount = oldValue } } }` – Leo Dabus Sep 30 '17 at 13:12
  • if you really want to discard the oldValue `var peopleCount: Int? { didSet { if let newValue = peopleCount, newValue > 0 { peopleCount = newValue } else { peopleCount = nil } } }` – Leo Dabus Sep 30 '17 at 13:21

1 Answers1

2

You shouldn't define your variables as implicitly unwrapped optionals unless you have a very good reason to do so.

Your immediate error is that you cannot return a value in a setter, you need to assign the value to the variable there. If you want to mark an invalid value by peopleCount being nil, define peopleCount as Int? and assign to it nil when the check fails.

final class Globals {

    private init(){}

    static let sharedInstance = Globals()

    //MARK: Properties
    private var _peopleCount: Int?

    var peopleCount: Int? {
        get {
            return _peopleCount
        }
        set(newPeopleCount) {
            if let newValue = newPeopleCount, newValue > 0 {
                _peopleCount = newValue
            }
        }
    }
}

For most use cases, there is no need for the private backing variable, you can just use didSet to check the value before assigning it. Thanks for @LeoDabus for the idea in comments.

var peopleCount: Int? {
    didSet {
        if let newValue = peopleCount, newValue > 0 {
            peopleCount = newValue
        } else {
            peopleCount = oldValue
        }
    }
}
Dávid Pásztor
  • 51,403
  • 9
  • 85
  • 116
  • You should use didSet instead. – Leo Dabus Sep 30 '17 at 13:25
  • thanks. Can I not preserve the existing value in _peopleCount when the check fails without setting it to nil? ie: returning nil, and leaving the value what it was? – Sean Sep 30 '17 at 13:31
  • @Sean check my comment above `var peopleCount: Int? { didSet { if let newValue = peopleCount, newValue > 0 { peopleCount = newValue } else { peopleCount = oldValue } } }` – Leo Dabus Sep 30 '17 at 13:34
  • you don't need a getter. just use didSet – Leo Dabus Sep 30 '17 at 13:35
  • @Sean check my updated answer, you can just delete the else branch. As already stated in my answer, you cannot return any values from a setter. – Dávid Pásztor Sep 30 '17 at 13:36
  • btw this would double your memory usage – Leo Dabus Sep 30 '17 at 13:39
  • 1
    @LeoDabus thanks for the idea, that's a better approach indeed, I don't see any reason for using the private backing variable in this simple case, so I included your approach in my answer. – Dávid Pásztor Sep 30 '17 at 13:41
  • 1
    Ok great. Thanks very much guys. I have implemented @LeoDabus 's idea. Working great. – Sean Sep 30 '17 at 13:44
  • Thanks @LeoDabus but I think I can't make a struct a singleton? I need to use this class/struct to store global variables and helper functions. I'm looking [here](https://stackoverflow.com/questions/36788169/whats-the-difference-between-struct-based-and-class-based-singletons) – Sean Sep 30 '17 at 15:37