简体   繁体   中英

Implementing Swift protocol with a constrained type parameter

I have a couple of Swift protocols that describe a general interface that I'm trying to implement in multiple ways:

protocol Identifiable
{
    var identifier:String { get }
}

protocol ItemWithReference
{
    var resolveReference<T:Identifiable>(callback:(T) -> ())
}

Now I want to implement the ItemWithReference protocol using CloudKit as the back end (this will eventually work with an alternate back-end as well, at which time I expect to provide an alternative implementation of the ItemWithReference protocol.

In my CloudKit implementation, I have something like this:

class CloudKitIdentifiable : Identifiable
{
    ...
}

class CloudKitItemWithReference : ItemWithReference
{
    func resolveReference<T:Identifiable>(callback:(T) -> ())
    {
        // In this implementation, I want to only proceed if `T` is a CloudKitIdentifiable subtype
        // But not sure how to enforce that
    }
} 

What I would like to do is to constrain T to be a CloudKitIdentifiable rather than just a simple Identifiable . I can't do that directly in the resolveReference declaration because then the function wouldn't conform to the ItemWithReference protocol. So instead, I am hoping to confirm that T is indeed a CloudKitIdentifiable and then invoke it's initializer to create a new instance of the class being resolved.

Is there any way in Swift to use T's metatype T.Type and determine if it is a subtype of another type? Furthermore, is there any way to invoke a required initializer that has been declared on that subtype?

try:

class CloudKitIdentifiable : Identifiable {
    var identifier:String = ...

    required init() {}
    // you need `required`.
}

class CloudKitItemWithReference : ItemWithReference {

    func resolveReference<T:Identifiable>(callback:(T) -> ()) {
        if T.self is CloudKitIdentifiable.Type {
            // do work..
            let obj = (T.self as CloudKitIdentifiable.Type)()
            callback(obj as T)
        }
    }
}

OR:

class CloudKitItemWithReference : ItemWithReference {
    func resolveReference<T:Identifiable>(callback:(T) -> ()) {
        if let CKT = T.self as? CloudKitIdentifiable.Type {
            // do work..
            let obj = CKT()
            callback(obj as T)
        }
    }
}

But, In this case, you have to call resolveReference like this:

let ref = CloudKitItemWithReference()
ref.resolveReference { (obj: CloudKitIdentifiable) -> () in
    //                       ^^^^^^^^^^^^^^^^^^^^ explicit type is necessary.
    println(obj.identifier)
    return
}

Rathar than that, I would recommend to use Associated Type :

protocol Identifiable {
    var identifier:String { get }
}

protocol ItemWithReference {

    typealias Item: Identifiable // <-- HERE is associated type

    func resolveReference(callback:(Item) -> ())
}

class CloudKitIdentifiable : Identifiable {
    var identifier:String
    init(identifier: String) {
        self.identifier = identifier
    }
}

class CloudKitItemWithReference : ItemWithReference {

    // `Item` associated type can be inferred from
    // the parameter type of `resolveReference()`
    //
    // typealias Item = CloudKitIdentifiable

    func resolveReference(callback:(CloudKitIdentifiable) -> ()) {
        let obj = CloudKitIdentifiable(identifier: "test")
        callback(obj)
    }
}

let ref = CloudKitItemWithReference()
ref.resolveReference { obj in
    println(obj.identifier)
    return
}

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM