How to regenerate code to instantiate object from instance in Swift

50 Views Asked by At

For unit testing, I would like to generate the minimum of swift code that is needed in order to re-create an instance.

For example, I've got the following struct:

struct User {
    enum Role {
        case admin, marketing, tester, developer, sales
    }

    let name: String
    let email: String

    let icon: URL?
    let roles: [Role]

    public init(name: String, email: String, icon: URL? = nil, roles: [Role]? = nil) {
        self.name = name
        self.email = email
        self.icon = icon
        self.roles = roles
    }
}

And at runtime I've got this user, created from various sources:

▿ User
  - name: "John Appleseed"
  - email: "[email protected]"
  - icon: nil
  ▿ roles: Optional([User.Role.admin])
    ▿ some: 1 element
      - User.Role.admin

I would like to generate the swift code that I need to re-create this user in a unit test.

Reflection gives me a good start, but it's not ideal:

print(String(reflecting: user))

Result:

MyLibrary.User(name: "John Appleseed", email: "[email protected]", icon: nil, roles: Optional([MyLibrary.User.Role.admin]))

It's not ideal because:

  • All types prefixed by their module
  • Explicit optionals
  • Included nil properties
  • No pretty formatting with indentation

What I'm looking for is something like this:

User(
    name: "John Appleseed",
    email: "[email protected]",
    roles: [.admin]
)

I could conform my types to CustomReflectable to improve the optionals and nil's:

extension User: CustomReflectable {
    public var customMirror: Mirror {
        var children: [(label: String?, Any)] = [
            ("name", name as Any),
            ("email", email as Any)
        ]

        if let icon {
            children.append(("icon", icon))
        }

        if let roles, !roles.isEmpty {
            children.append(("roles", roles))
        }

        return Mirror(
            self,
            children: children,
            displayStyle: .struct
        )
    }
}

And perhaps string replace the lib name?

let result = String(reflecting: user).replacingOccurrences(of: "MyLibrary.", with: "")
print(result)

Result:

User(name: "John Appleseed", email: "[email protected]", roles: [User.Role.admin])

This already improves it a lot but the enum value still has a lengthy type, and the code has no indentation.

Also having to conform all the types to CustomReflectable and maintain it would be quite some work.

Any tips on how to improve this?

0

There are 0 best solutions below