动态访问 属性 of 属性 of struct

Dynamically access property of property of struct

我有大量的结构,它们都有 responseId: String 并且有一个 属性 与包含 contactId: StringresponseId 的值同名.

下面是两个例子:

protocol ContainerBase {
    var responseId: String { get }
}

struct Container1: ContainerBase {
    struct OtherA {
        let contactId: String
        let cats: [String] // Other info here, not related to OtherB
    }
    struct OtherB {
        let contactId: String
        let dogsNum: Int // Other info here, not related to OtherA
    }
    let responseId: String
    let otherA: OtherA? // Optional
    let otherB: OtherB? // Optional
}

struct Container2: ContainerBase {
    struct AnotherA {
        let contactId: String
        let passed: Bool  // Other info here, not related to AnotherB
    }
    struct AnotherB {
        let contactId: String
        let friend: String // Other info here, not related to AnotherA
    }
    let responseId: String
    let anotherA: AnotherA? // Optional
    let anotherB: AnotherB? // Optional
}

问题:

如何从 Container1Container2 动态访问 contactId? (我尝试了非动态方法,每个 ContainerN 结构都有一个额外的函数,里面有一个开关,但这变得很疯狂,因为我有太多这样的结构,我已经犯了一些错别字,忘记了一些情况,导致了错误,......我想象一下可靠的解决方案是“反射”?)。

示例:

例如,如果 Container1responseId 是“otherA”,那么我应该在 属性 otherA 中寻找 contactId。由于我有几种类型的容器,每种容器都有不同类型的不相关内部对象,因此解决方案不应特定于 Container1 或 Container2,它应该适用于任何 ContainerBase.

我实现了一个肮脏的代码,但它会导致警告,并且在不生成警告的情况下无法正常工作。另外我认为这不能可靠地工作(这是我的 iOS 应用程序,但奇怪的是这在 linux Swift 中不起作用)。这可能吗?还是编译器故障?

let c1 = Container1(
    responseId: "otherA",
    otherA: Container1.OtherA(contactId: "123", cats: ["figaro"]),
    otherB: nil)

c1.findContactId() // expected "123"

前面的丑陋代码:

extension ContainerBase {
    func findContactId() -> String? {
        let mirror = Mirror(reflecting: self)
        guard let tInnerRes = mirror.children.first(where: { [=14=].label == self.responseId })?.value else { return nil }

        // WARN: Conditional cast from 'Any' to 'Optional<Any>' always succeeds
        guard let maybeInnerRes = (tInnerRes as? Optional<Any>) else { return nil }

        guard let innerRes = maybeInnerRes else { return nil }
        let innerMirror = Mirror(reflecting: innerRes)
        let contactId = innerMirror.children.first(where: { [=14=].label == "contactId" })?.value as? String
        return contactId
    }
}

感谢任何帮助

我在 swift 论坛上问了同样的问题(但解释得更好),并从 Alexis Schultz 那里得到了 a great answer

    func findContactId() -> String? {
        Mirror(reflecting: self)
            .children
            .first(where: { [=10=].label == responseId })
            .map(\.value)
            .flatMap(Mirror.init(reflecting:))?
            .children
            .first(where: { [=10=].label == "some" })
            .map(\.value)
            .flatMap(Mirror.init(reflecting:))?
            .children
            .first(where: { [=10=].label == "contactId" })?
            .value as? String
    }

后来,在看到“some”这个词后,我意识到 Mirror's descendant function 可以做完全相同的工作:

    func findContactId() -> String? {
        let mirror = Mirror(reflecting: self)
        let value = mirror.descendant(responseId, "some", "contactId") as? String
        return value
    }