Search code examples
swiftprotocolsswift-extensionscomputed-properties

How to override computed property of protocol extension in Swift


I want to implement a theming in a way, that a feature can add its needed colors to a Theme protocol, so that any actual theme implementation must provide colors for every feature. I also want to have the theme implementation and feature theme requirements in separate files. I don't want to remove lines of code manually, if I move either theming or feature into another project.

import UIKit

protocol Theme {
    static var genericColor: UIColor { get }
}

protocol FeatureTheme {
    static var featureColor: UIColor { get }
}

extension Theme {
    static var feature: FeatureTheme.Type! {
        return nil
    }
}

struct LightTheme: Theme {
    static var genericColor: UIColor { return .white }

    static var feature: FeatureTheme.Type! { return Feature.self }
    struct Feature: FeatureTheme {
        static var featureColor: UIColor { return UIColor.red }
    }
}

let currentTheme: Theme.Type = LightTheme.self

print(currentTheme) // LightTheme
print(currentTheme.feature.featureColor) // error, because feature is nil

So, I wanted to add the FeatureTheme requirement to the Theme protocol via extension. Swift wants to see a default implementation in a protocol extension. I wanted to 'override' it in the actual LightTheme implementation, but that does not work. The property still returns nil. How can I solve this?


Solution

  • You have done is correct but if you observe your code

    let currentTheme: Theme.Type = LightTheme.self
    

    currentTheme is type of Theme however you have assigned LightTheme which is now Theme and in your protocol

    extension Theme {
        static var feature: FeatureTheme.Type! {
            return nil
        }
    }
    

    You have nil returned as default implementation which is executing because currentTheme is Theme Type not LightTheme and it is not required properly as well

    With current implementation Solution is simple again is to declare currentTheme as LightTheme See below answer

    let currentTheme: LightTheme.Type = LightTheme.self
    

    OR

    Keep currentTheme to simply assign LightTheme Like below

    let currentTheme  = LightTheme.self
    

    Hope it is helpful to you

    Output :

    LightTheme UIExtendedSRGBColorSpace 1 0 0 1