简体   繁体   中英

`Identifiable` conformance return type generic as variable

Background:

I'm trying to build an OutlineGroup from this example from Apple: https://developer.apple.com/documentation/swiftui/outlinegroup

But instead of using static types I want to use objects that conform to my protocol. So the question is more general if that's possible in swift what I'm trying to do:

Problem:

I have protocol that gives me people coworkers and name from various objects:

protocol CastProtocol {
    var name: String { get }
    var coworkers: [CastProtocol]? { get } // here will be the build error later
}

And consider two models as example:

class Actor: Identifiable, CastProtocol {
    var id: Self { self }
    var name: String { return "Chuck Norris" }
    
    var coworkers: [CastProtocol]? {
        return [
            Director()
        ]
    }
}

class Director: Identifiable, CastProtocol {
    var id: Self { self }
    var name: String { return "Gina Carey" }
    
    var coworkers: [CastProtocol]? {
        return [
            Actor(),
            Director(),
            Actor()
        ]
    }
}

So till now everything working, but I need to ensure that my CastProtocol conforms to Identifiable , so changing my protocol implementation to:

protocol CastProtocol: Identifiable {
    ...
}

And now this creates a problem:

Protocol 'CastProtocol' can only be used as a generic constraint because it has Self or associated type requirements

Questions:

  1. So there is a way to do this with return not static type in coworkers or maybe I should go with subclassing?
  2. Is that a correct approach? Maybe the only thing I'm missing is declare in protocol level identifier requirements for identifiable? But if so - how to do it?
  3. I cannot really go with associatedType I think. If coworkers in this example can contain Actor or Director associatedType I think cannot be use

Protocols never conform to other protocols. The syntax protocol CastProtocol: Identifiable does not mean "CastProtocol conforms to Identifiable." It means "in order to conform to CastProtocol, a type must also conform to Identifiable."

To the question, though, no, you cannot require that. Identifiable includes an associated type ID . Consider this code:

let coworker in coworkers {
    let id = coworker.id // What type is `id` here?
    ...
}

Swift cannot assign a single type type id in this case. Someday they may add a feature called a Generalized Existential that would allow id to be inferred to be the type Any where Self: Hashable , but it's not currently possible to express that in Swift.

But in your case, it couldn't work anyway. Say you could do this. Since you've made the identifier be the object itself, eventually the system would need to evaluate Director == Actor . That's not possible in Swift. Those aren't the same type.

Instead, I suspect you want this, if you really want Cast members to be classes:

protocol CastMember: class {
    var name: String { get }
    var coworkers: [CastMember] { get } // Optional Arrays are almost always wrong
    var id: ObjectIdentifer { get }
}

extension CastMember {
    var id: ObjectIdentifier { ObjectIdentifier(self) }
}

// You can still put Identifiable here for [Actor] properties
class Actor: CastProtocol, Identifiable {
    // ...
}

Now, this won't let you put these directly into a ForEach(actor.coworkers) call, which I assume is what you want. Personally, I'd just pass the id: \.id parameter, but you can of course make it a little nicer with an extension if you do this a lot:

extension ForEach where Content : View, Data.Element == CastMember {
    public init(_ data: Data, @ViewBuilder content: @escaping (Data.Element) -> Content) {
        self.init(data, id: \.id, content: content)
    }
}

Note the Data.Element == CastMember not : . This applies when it's an array of CastMember, not an array of things that conform to CastMember.

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