Home > Software engineering >  Return specific property when accessing a Struct without referencing its name
Return specific property when accessing a Struct without referencing its name

Time:09-08

Is there a way in Swift that I can get a property from a struct without having to reference it?

For example

public struct ASValue<T: Hashable>: Hashable {
    private let id = UUID()
    public var item: T
    
    public init(item: T) {
        self.item = item
    }
}

let val = ASValue(item: "Example")

print(val) // Prints: Example

CodePudding user response:

It's possible by adopting CustomStringConvertible and constraining T also to CustomStringConvertible

public struct ASValue<T: Hashable>: Hashable, CustomStringConvertible where T: CustomStringConvertible {
    
    private let id = UUID()
    public var item: T
    
    public var description : String {
        return "\(item)"
    }
}

let val = ASValue(item: "Example")

print(val) // Prints: Example

And this is a struct, the init method is for free.

CodePudding user response:

What you are looking for is exactly provided with property wrappers in swift. You have to make your ASValue structproperty wrapper by applying @propertyWrapper to the declaration:

@propertyWrapper
public struct ASValue<T: Hashable>: Hashable {
    private let id = UUID()
    public var wrappedValue: T

    public init(wrappedValue: T) {
        self.wrappedValue = wrappedValue
    }
}

Then you can use this wrapper to wrap any property in another type and access that property directly. You can even access the ASValue wrapper type by prefixing with _:

class SomeClass {
    @ASValue var val = "Example"

    func printVal() {
        // print(_val) for actual value
        print(val) // Prints: Example
    }
}

You can also pass this property with its wrapper identity to other function:

func printVal<T: Hashable>(@ASValue val: T) {
    // print(_val) for actual value
    print(val) // Prints: Example
}

let obj = SomeClass()
printVal(val: obj.val)

If for some constraint you can't use the property wrapper approach you can try the @dynamicCallable attribute as a workaround. This allows values created from your types to be invoked as methods with specified arguments.

@dynamicCallable
public struct ASValue<T: Hashable>: Hashable {
    private let id = UUID()
    public var item: T

    public init(item: T) {
        self.item = item
    }

    // need to have withArguments with argument type confirming
    // to ExpressibleByArrayLiteral
    func dynamicallyCall(withArguments arg: [Int] = []) -> T {
        return item
    }
}

let val = ASValue(item: "Example")
print(val()) // Prints: Example
  • Related