This might like a puzzle or educational/investigational purposes but actually it is a part of an app I work on ... Please consider these two structs:
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
}
Container1
, Container2
, ... ContainerN
have a property responseId: String
(ContainerBase protocol).
OtherA
, OtherB
, AnotherA
, AnotherB
are completely unrelated (unrelated to each other and unrelated to parent ContainerN) ... In ContainerN I might have only one inner struct in other ContainerM I might have up to 8 inner unrelated structs. This varies in each case.
All inner structs have a property: "let contactId: String". (No protocol defined yet but I could define one if it is helpful)
My mission:
Get value of property "contactId" of a inner struct whose property name is same as value of property "responseId" of Container.
Example:
let container1 = Container1(
responseId: "otherA",
otherA: Container1.OtherA(contactId: "123", cats: ["figaro"]),
otherB: nil)
container1.findContactId() // expected "123"
let container2 = Container2(
responseId: "anotherB",
anotherA: Container2.AnotherA(contactId: "777", passed: true),
anotherB: nil)
container2.findContactId() // expected <nil>
So at first I was writing a function like this in all containers but now I have dozens of dozens (and increasing) of container structs.... in some of them I made some typos and got bugs in the way ... This is getting out of control.
extension Container1 {
func findContactId() -> String? {
switch responseId {
case "otherA": return otherA.contactId
case "otherB": return otherB.contactId
}
}
}
So I tried to implement this with reflexion but I cannot make it work without generating a warning. Could you guide me on how to find a stable solution without warnings?
My ugly code ahead (works but with a warning):
extension ContainerBase {
func findContactId() -> String? {
let mirror = Mirror(reflecting: self)
guard let tInnerRes = mirror.children.first(where: { $0.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: { $0.label == "contactId" })?.value as? String
return contactId
}
}