0

Let's say I have a color model:

protocol Color {
    var value: String? { get }
}

class UnknownColor: Color {
    let value: String? = nil
}

class KnownColor: Color {
    let value: String?

    init(value: String? = nil) {
        self.value = value
    }
}

In my view file, I add some view-details to my Color model. These details aren't model specific, they're view specific.

fileprivate extension Color {
    fileprivate var representation: String {
        return self.value!
    }
}

fileprivate extension UnknownColor {
    fileprivate var representation: String {
        return "#000"
    }
}

Now, when I use my color model in the view file, I expect my UnknownColors to represent themselves as "#000", but that's not the case when UnknownColor is cast as a Color.

let color1 = KnownColor()
let color2 = KnownColor(value:"#fff")
let color3 = UnknownColor()

color1.representation  // Fatal error  (GOOD)
color2.representation  // "#fff"  (GOOD)
color3.representation  // "#000" (GOOD)

if let color = color3 as? Color {
    color.representation // Fatal error  (BAD, expected "#000")
}

I want to avoid blatant type-checking for UnknownColor, so a solution like this is not ideal:

func colorRepresentation(_ color: Color) {
    if let color = color as? UnknownColor {
        return "#000"
    } else {
        return color.value!
    }
}

I want to avoid making further changes to the Color model at all costs.

There may be many implementations of the Color protocol that want to make use of Color.representation so changing the extension Color to extension KnownColor is not an option.

Is there a way that I can restructure my code so that the UnknownColor.representation gets used when the Color is actually an UnknownColor?

G-P
  • 111
  • 4
  • I found this link which includes some more information on the topic: https://medium.com/ios-os-x-development/swift-protocol-extension-method-dispatch-6a6bf270ba94 – G-P Jun 27 '18 at 17:06
  • You've now radically altered what your question was about without making it clear you've updated your question with that change. My answer now doesn't make any sense because of it. If you're going to do something like this please make it clear you've changed the question by making your changes in an UPDATE section, so people who read this thread will be able to make sense of what's being discussed. – Nima Yousefi Jun 27 '18 at 19:37
  • Sorry I thought my comment under your answer would make that clear enough. There was an issue with my question and I've updated it to be correct. – G-P Jun 27 '18 at 21:54

1 Answers1

2

I copy and pasted your code directly into a Playground and it worked fine (color3.representation == "#000").

Are the extensions in a separate file? If so, the fileprivate keyword is going to make them invisible to the classes.

For reference, here is the entire code I put into the Playground:

protocol Color {
    var value: String? { get }
}

class UnknownColor: Color {
    let value: String? = nil
}

class KnownColor: Color {
    let value: String?

    init(value: String? = nil) {
        self.value = value
    }
}

fileprivate extension Color {
    fileprivate var representation: String {
        return self.value!
    }
}

fileprivate extension UnknownColor {
    fileprivate var representation: String {
        return "#000"
    }
}

let color1 = KnownColor()
let color2 = KnownColor(value:"#fff")
let color3 = UnknownColor()

//color1.representation  // Fatal error  (GOOD)
color2.representation  // "#fff"  (GOOD)
color3.representation  // Fatal error  (BAD, expected "#000")
Nima Yousefi
  • 817
  • 6
  • 11
  • Hi Nima thanks for giving it a try. The extensions are in another file and I would prefer if the method "representation" was only available in that file. Maybe there is another way to achieve that without fileprivate? – G-P Jun 27 '18 at 17:04
  • Okay, this is weird, I tried it the way you described -- Color protocol and classes in one file, and the extension in another file -- and I can access the color3.representation in the other file, no problem. Here's the code I tried for the extensions/usage: https://gist.github.com/nyousefi/4d69f7fac5443a7b9bd48043e0e4f7f1 – Nima Yousefi Jun 27 '18 at 17:28
  • Ahh, I think I see the difference here. In the question as written color3 is defined as an `UnknownColor`. But if we cast color3 as a `Color` we get the behavior that I'm seeing. I'll update the question – G-P Jun 27 '18 at 18:00