3

Here is my case:

I want 2 or more UIControls to conform to a common protocol: For example UISlider, UIStepper, MyCustomControl. This is what they have:

class UIStepper {
  var value: Float
}

class UISlider {
  var value: Double
}

class MyCustomControl {
  var value: Int
}

Now, I'd love something similar to a protocol like that:

protocol Valuable {
  associatedtype T
  var value: T
}

and then be able to use a [Valuable]. But of course I get into the famous PATs problem

protocol Valuable can only be used as a generic constraint because it has Self or associated type requirements

I've seen methods of type erasure and things similar to that online. But I feel it's kind of messy for what I'm trying to do. I want to be able to just have an array of Controls that have a value property, and this property can only be primitive types. Sure I can go ahead and create multiple arrays like [IntValuable], [DoubleValuable], [FloatValuable] to get around it. Or maybe use NSNumber somehow somewhere. or enum with associated values. Or maybe there's something I am not seeing, and hence why I am posting here :) A little guidance is really appreciated! Thanks.

Community
  • 1
  • 1
Guy Daher
  • 5,526
  • 5
  • 42
  • 67
  • Possible duplicate of [How to use generic protocol as a variable type](http://stackoverflow.com/questions/27725803/how-to-use-generic-protocol-as-a-variable-type) – Marco Santarossa Feb 21 '17 at 10:57
  • @MarcoSantarossa Yes I mentioned that I read about doing type erasure and stuff like that. I'm curious if there is another way to deal with it, applied to my specific _simpler_ needs. – Guy Daher Feb 21 '17 at 11:07
  • A simpler solution would depend on how you plan on using the "value" property of the objects in your [Valuable] array. If it has a class dependent type, the compiler may not let you do much with the value property of your array elements unless you do a lot of type casting. If you're ok with type casting on use, you might as well define the value property as Any. – Alain T. Feb 21 '17 at 16:25

2 Answers2

1

It's not clear to me from your question what your needs are. How about:

enum Numeric {
    case int(Int)
    case double(Double)
    case float(Float)

    init(_ int: Int) {
        self = .int(int)
    }

    init(_ double: Double) {
        self = .double(double)
    }

    init(_ float: Float) {
        self = .float(float)
    }        
}

protocol Valuable {
    var numericValue: Numeric { get set }
}

extension UISlider: Valuable {
    var numericValue: Numeric {
        get { return .double(value) }
        set { value = newValue.value }
    }
}

And etc, for the other controls. Then, to assign:

let slider: UISlider()
slider.numericValue = Numeric(3.14159)
Dave Weston
  • 6,527
  • 1
  • 29
  • 44
  • Good suggestion. However, might needs are the ability to do `valuable.value = 123` so that i can change the value of my controls, without knowing whether it's a `UISlider` or `UIStepper` – Guy Daher Feb 22 '17 at 13:12
  • 1
    I updated the code to add `init` methods for the `NumericValue` type. (Though I'm not sure if I got the syntax correct, because I'm typing this on my phone.) – Dave Weston Feb 23 '17 at 03:00
0

The way I ended up doing it is the following:

protocol Valuable {
    func set(value: NSNumber)
}

extension UIStepper: Valuable {
    func set(value: NSNumber) {
        self.value = value.doubleValue
    }
}

extension UISlider: Valuable {
    func set(value: NSNumber) {
        self.value = value.floatValue
    }
}

And then I can do:

var valuables: [Valuable] = [UISlider(), UIStepper()]

for valuable in valuables {
    valuable.set(value: 5)
}

In that way, I can easily add more extensions for any kind of classes, even if the variable in it is not named value.

Guy Daher
  • 5,526
  • 5
  • 42
  • 67