Force nested properties to conform to super class or struct like Codable Protocol

public protocol AppEvent {
     var dictionary: [String: Any] { get }
}

extension AppEvent  {
    public var dictionary: [String : Any] {
       let mirror = Mirror(reflecting: self)
        let dictionary = mirror.children.compactMap { $0 }
            .reduce([String : Any]()) { (dict, tuple) in
                var nextDict = dict
                if let appEvent = tuple.value as? AppEvent {
                    nextDict.updateValue(appEvent.dictionary, forKey: tuple.label ?? "")
                } else {
                    nextDict.updateValue(tuple.value, forKey: tuple.label ?? "")
                }
                return nextDict
            }
        return dictionary
    }
}

struct Person: AppEvent {
    let name: String
    let age: Int
    let lastName: String
    let gender: Gender
}

struct Gender {
    let name: String
}

How Can I force "Gender" struct to conform to the AppEvent, like codable do ?

You cannot.

Strictly speaking, the Codable protocol doesn't either. Instead the machinery that provides the Codable conformance synthesis only works if all the stored properties are also Codable. But you can absolutely have a Codable struct with non-Codable fields if you implement the conformance yourself.

1 Like

I was going to say the same, and add that Equatable and Hashable behave the same way: you can get a default synthesized implementation which can be created if all of your properties are Equatable/Hashable, but there's nothing about these protocols which inherently requires that this be true.

1 Like

Depending on the specifics of what exactly you're looking to do in the general case, you could theoretically introduce a protocol with an associatedtype requirement which requires conformance of that type to AppEvent, and then have Person conform to that protocol — but it doesn't sound likely that this is what you want, since presumably you'd need to scale this to multiple properties.

Otherwise, this isn't possible in the language itself: Equatable, Hashable, and Codable all require compiler support to make their default implementations possible, because the behavior can't be expressed within Swift itself yet.

1 Like

One non ideal option would be to force "AppEvent" requirement on all types you store in "AppEvent" types (including Strings, Ints, etc):

 let appEvent = tuple.value as! AppEvent

then the first call to dictionary (which could be as early as its init) would result a runtime crash if the type has children that do not adhere to AppEvent. Unfortunately this is a runtime check, not a compile time check, although might be better than nothing. Other than that, unit testing would help to catch such mistakes.

Your yet is promising :wink:

Bike shedding how it could look (pseudo code):

/// Built-in protocol
protocol CompileTimeCheck {
    static func compileTimeCheck() throws
}

protocol AppEvent: CompileTimeCheck {
    static func compileTimeCheck() throws {
        let mirror = Mirror(forType: Self)
        check children here
        if something is wrong {
            throw "Property \(child) doesn't conform to AppEvent"
        }
    }
}

struct S: AppEvent { // Error: "Property `gender` doesn't conform to AppEvent"
    var gender: Gender
}