Swifty way to fetch the struct properties

Karthik Mitta
3 min readOct 31, 2021

--

In this article, I will explain how to fetch all stored properties of an object (class or struct).

Any solution for a problem statement can easily understand with an example. So, let’s discuss the example first…

Use-case:

Let’s say you have the functionality of fetching user addresses in your application and sending it to your backend server.

So generally, you will create a struct with all required address properties like street, city, zip code, etc. And once the user enters the required information through your application interface, you will update the values and send that information to the server by creating a request.

It’s soo simple right?

But here is the twist. The backend server will accept the request only if all the properties have non-empty values. As per the requirement, while submitting the information you have to show an alert message to the user to fill in all the details instead of indicating all the fields as mandatory.

Now you may think, what is the big deal in doing this. Yes, you are right. You can do this easily, with different approaches. But my solution is based on swift Mirror.

What is Swift Mirror?

Apple documentation says..

A mirror describes the parts that make up a particular instance, such as the instance’s stored properties, collection or tuple elements, or its active enumeration case.

I am assuming it is understandable and now let’s see how to use this. I will create a struct named address and fill in the values.

struct Address {
var houseNumber: String
var street: String
var city: String
var state: String
var zipcode: String
}
let userAddress = Address(houseNumber: "#123", street: "", city: "", state: "Karnataka", zipcode: "560008")

If you observe the above code snippet, street & city are empty values. Let’s convert the struct to a dict using Swift Mirror and then iterate the properties to find empty values.

var result: [String: Any] = [:]
let mirror = Mirror(reflecting: userAddress) // This will create a mirror of struct which will list the stored properties.
// mirror.children - A collection of Child elements describing the structure of the reflected subject.for (label, value) in mirror.children {
guard let label = label else {
continue
}
result[label] = value
}
let adressValues = result.filter { ($0.value as? String)?.isEmpty ?? true } ?? []if adressValues.isEmpty {
// Show an alert to user
} else {
// send this information to backend server.
}

Wow.. It’s soo simple and swifty right?

As a final step, let’s optimise this code and make it reusable. To make it reusable, I will create a protocol named PropertyIterable and write an extension to implement the above functionality.

protocol PropertyIterable {
func properties() throws -> [String: Any]
}
extension PropertyIterable {
func properties() throws -> [String: Any] {
let mirror = Mirror(reflecting: self)
// Throwing an error if the the type is not a struct or a
class.
guard let style = mirror.displayStyle, style == .struct ||
style == .class else {
throw NSError(domain: "Error Message", code: 1111, userInfo:
nil)
}
let result = mirror.children.reduce([String: Any]()) { dict,
child -> [String: Any] in
var
dict = dict
guard let label = child.label else { return dict }
dict[label] = child.value
return dict
}
return result
}
}

Now let’s implement this protocol in our address struct and determine the empty values

struct Address: PropertyIterable {
var houseNumber: String
var street: String
var city: String
var state: String
var zipcode: String
}
let userAddress = Address(houseNumber: "#123", street: "", city: "", state: "Karnataka", zipcode: "560008")let properties = try? userAddress.properties()let adressValues = properties?.allSatisfy { ($0.value as? String)?.isEmpty ?? true } ?? falseif adressValues {
// Show an alert to user
} else {
// send this information to backend server.
}

Now you can use this protocol in your projects and enjoy the Swifty way of iterating through class or struct stored properties to perform many operations.

Thank you for reading. If you have any queries or have a better solution, please do comment.

--

--