Swift: Reflection of Protocol Variables

Swift: reflection of protocol variables

Computed properties are not represented in the runtime introspection provided by Mirror

From the Language Reference - Mirror struct (emphasis added by me)

Representation of the sub-structure and optional "display style" of
any arbitrary subject instance.

Describes the parts---such as stored properties, collection elements,
tuple elements, or the active enumeration case---that make up a
particular instance
. May also supply a "display style" property that
suggests how this structure might be rendered.

The properties id (int) and isItTrue (bool) are available to instances of MirrorMe, but only as computed properties, as MirrorMe does not implement these as stored properties, but rather, make use of the default implementation of them as computed properties from extension SomeProtocol where Self: SomeClass { ... }. Hence, the computed properties id and isItTrue of MirrorMe are not contained in the sub-structure representation of MirrorMe instances, as provided by runtime introspection using Mirror.

We can verify this clearly in a more minimal example:

class Foo {
// a stored property
let representedInIntrospection = 0

// a computed property
var notRepresented: Int { return representedInIntrospection }
}

Mirror(reflecting: Foo())
.children
.forEach { print($0.label, $0.value) }
/* Optional("representedInIntrospection") 0 */

To summarize: properties blueprinted in a protocol, with or without an associated default implementation, can never be stored properties on their own (default implementations of blueprinted properties may naturally only contain computed properties). This means that the only properties that are explicitly declared as stored properties in the class/struct that conforms to your protocol will show up when applying runtime introspection of an instance of such a class/struct.

Finally, you never mention the reason as for why you want a dictionary of the stored properties of a class instance, but take care if making use of this a production purpose. Generally, runtime introspection in a type safe language as Swift should only be used for diagnostics and debugging, even if it allows to be used in runtime hacks (e.g. with KVO for classes inheriting from NSObject).

Swift Mirror API - Which protocols an object conforms to

Not possible at all in Swift. Swift reflection is a very limited affair. If you are willing to bridge your class into ObjC, you can use the ObjC Runtime functions to get what you want:

@objc protocol ProtocolA {}
@objc protocol ProtocolB {}
@objc protocol ProtocolC {}

class User : NSObject, ProtocolA, ProtocolC {}

var count: UInt32 = 0
let protocols = class_copyProtocolList(User.self, &count)

for i in 0..<Int(count) {
let cname = protocol_getName(protocols[i])
let name = String.fromCString(cname)

print(name)
}

Each of your protocol must be prefixed with @objc and your class must inherit from NSObject.

How can I check if a property has been set using Swift reflection?

I used @ebluehands technique of reflecting the Any value to modify the original function. It cycles through the properties with an initial mirror, then reflects each one individually using displayStyle to determine if the property is optional.

func allPropertiesHaveValues(obj: AnyObject) -> Bool {
let mirror = Mirror(reflecting: obj)
for child in mirror.children {
let value: Any = child.value
let subMirror = Mirror(reflecting: value)
if subMirror.displayStyle == .Optional {
if subMirror.children.count == 0 {
return false
}
}
}
return true
}

swift reflections: how to construct a new struct instance based on reflections?

You don't need reflection and AableError.

First of all lets add an init to your protocol

protocol Aable {
var name: String { get }
init(name:String)
}

Now you want a + operator where:

  • both params conform to the protocol Aable
  • the params have the same type
  • the return type is the same of the params

You can define this constraints with generics

func +<T:Aable>(left: T, right: T) -> T {
return T(name:left.name + right.name)
}

Now the check is performed at compile time so there's no need of the throws. The compiler will make sure the used values do have the proper type.

Test #1

struct Box:Aable {
let name:String
}

let amazon = Box(name: "Amazon")
let apple = Box(name: "Apple")
let res = amazon + apple
res.name // "AmazonApple"

Test #2

struct Box:Aable {
let name:String
}

struct Box:Aable {
let name:String
}

struct Bottle:Aable {
let name:String
}

let box = Box(name: "Amazon")
let bottle = Bottle(name: "Water")
box + bottle
// ^ compile error

Swift Protocol with Variadic property

Is it not possible to create a Variadic property within a Protocol?

It is not possible, but this is just one reflection of a larger fact, namely, that a variadic is not a type. You are trying here to say products is of type Variadic ProductModel. But there is no such type. No variable ever can be declared as being of that type; it isn't just protocols.

The only place variadic notation may appear is as a parameter type in an actual func declaration, but then it is just a notation, not a type. It is a way of saying that the function can take a series of the actual type (Double, in your example from the docs).

So, if your protocol wants to declare a method with a parameter that's a variadic, fine. But the idea of a variable of variadic type is meaningless.

So just declare that the type of your variable is [ProductModel]. That is how you say "some unknown number of ProductModel objects". And that's all that variadic notation means anyway, really, since the parameter is received inside the function body as an array.

Swift: Failed to assign value to a property of protocol?

You have to define the protocol as a class protocol:

protocol ValueProvider : class {
var value: String {get set}
}

Then

var value: String {
get { return v.value }
set { v.value = newValue }
}

compiles and works as expected (i.e. assigns the new value to the
object referenced by v1 if v1 != nil, and to the object
referenced by v2 otherwise).

v is a read-only computed property of the type ValueProvider.
By defining the protocol as a class protocol the compiler knows
that v is a reference type, and therefore its v.value
property can be modified even if the reference itself is a constant.

Your initial code example works because there the v property has
the type A which is a reference type.

And your workaround

set {
var tmp = v1 ?? v2
tmp.value = newValue
}

works because (read-write) properties of variables can be set in
any case (value type or reference type).

Does Swift support reflection?

Looks like there's the start of some reflection support:

class Fruit {
var name="Apple"
}

reflect(Fruit()).count // 1
reflect(Fruit())[0].0 // "name"
reflect(Fruit())[0].1.summary // "Apple"

From mchambers gist, here:
https://gist.github.com/mchambers/fb9da554898dae3e54f2



Related Topics



Leave a reply



Submit