First, I want to declare a protocol that will only apply to final classes that derive from BaseObject:
protocol KeyPathDeclaration where Self : BaseObject
{
static func keyPaths() -> [PartialKeyPath<Self>]
}
This is then implemented in the final class:
final class Person : BaseObject, KeyPathDeclaration
{
static func keyPaths() -> [PartialKeyPath<Person>]
{
return [\Person.name, \Person.age]
}
var name: String = ""
var age: Int = 0
}
I then have a "property bag" which will be held in the BaseObject class and which will be populated by passing in the KeyPaths from the final class:
class AnyPropertyBag
{
var ownerObject: AnyObject
init(ownerObject: AnyObject)
{
self.ownerObject = ownerObject
}
}
class PropertyBag<ownerT> : AnyPropertyBag where ownerT : KeyPathDeclaration
{
var owner: ownerT
{
return ownerObject as! ownerT
}
required init(for owner: ownerT)
{
super.init(ownerObject: owner)
let keyPaths: [PartialKeyPath<ownerT>] = ownerT.keyPaths()
for _ in keyPaths
{
// population code
}
print(owner)
}
}
What I would then like to do is have a "factory" method in the BaseObject class that would supply an instance of the PropertyBag to be held in the "properties" var:
class BaseObject
{
static func createProperties<typeT>(for owner: typeT) -> AnyPropertyBag where typeT : KeyPathDeclaration
{
return PropertyBag<typeT>(for: owner)
}
lazy var properties: AnyPropertyBag = type(of: self).createProperties(for: self as! KeyPathDeclaration)
}
I have made the createProperties method generic to avoid the dreaded Self/AssociatedType problems involved with manipulating KeyPathDeclaration.
But, after several days of experimentation to get this right, I still end up with two errors on the properties declaration:
- Cannot invoke 'createProperties' with an argument list of type '(for: KeyPathDeclaration)'
- Expected an argument list of type '(for: typeT)'
I can do this kind of thing in C# (I have been developing in Obj-C and Swift from before Swift was launched) but, with Swift and its refusal to cope with generic protocols without all manner of convoluted workarounds, I'm still stumped.
Anyone got any ideas what I am missing here?