Eunchul Jeon
Eunchul Jeon

Reputation: 179

Reflection from structure type in Swift

Recently I'm developing API parts using GraphQL.

When I call API, I need to generate a query from structure like this.

// from this model
struct ModelA {
   let id: String
   let title: String
....
}
// to this query
query {
  id
  title
}

If I have an instance of ModelA, I can reflect properties from instance using Mirror.

But I don't want to make the instance in this case and I don't want to make the properties to variables because I need to use this model for response.

Additionally class_copyPropertyList is a good solution if the model is NSObject class. However in this case this is a structure in swift.

Is this possible? I appreciate your help in advance.

Upvotes: 1

Views: 869

Answers (2)

Maciej Chrzastek
Maciej Chrzastek

Reputation: 1490

try this

extension Encodable {
    func query() -> String? {
        guard let encodeData: Data = try? JSONEncoder().encode(self) else { return nil }
        guard let jsonRepresentation: [String: Any] = try? JSONSerialization.jsonObject(with: encodeData, options: []) as? [String: Any] else { return nil }
        let keys: String = jsonRepresentation.map{ $0.key }.joined(separator: " ")
        return "query { " + keys + " }"
    }
}

struct ModelA: Encodable {
   let id: String
   let title: String
}

let model = ModelA(id: "abc123", title: "Model title")

if let query = model.query() {
    print(query)
}

EDIT

// query function in Encodable extension stays the same

protocol Querable: Encodable {
    static var dummy: Encodable { get }
}
extension Querable {
    static var query: String? {
        return self.dummy.query()
    }
}

struct ModelA: Querable {
   let id: String
   let title: String

    static var dummy: Encodable {
        return ModelA(id: "", title: "")
    }
}

if let query = ModelA.query {
    print(query)
}

Upvotes: 1

Rob C
Rob C

Reputation: 5073

Unfortunately you're not going to be able to do that with structs. You can, however, do something like this.

protocol Queryable {
    static var queryableProperties: [String] { get }
}

extension Queryable {
    static func makeQuery() -> String {
        return "query {\n"
            + queryableProperties
                .map { "  \($0)" }
                .joined(separator: "\n")
            + "\n}"
    }
}

struct Dog {
    let name: String
    let age: Int
}

extension Dog: Queryable {
    static var queryableProperties: [String] {
        return ["name", "age"]
    }
}

struct Person {
    let firstName: String
    let lastName: String
}

extension Person: Queryable {
    static var queryableProperties: [String] {
        return ["firstName", "lastName"]
    }
}

print(Person.makeQuery())
print(Dog.makeQuery())

Which prints:

query {
  firstName
  lastName
}

query {
  name
  age
}

Upvotes: 0

Related Questions