Protocol with associatedtype Protocol for Generic functions

929 views Asked by At

Is it possible to provide confirming protocol in generic functions of another protocol? I tried make it work like this, but it's impossible, or I made some mistakes.

My code:

protocol DataModelProtocol {
  associatedtype ObjectProtocol: Protocol
  func fetchObjects<T: ObjectProtocol>() -> [T]?
  func fetch<T: ObjectProtocol>(object: T) -> T?
  func delete<T: ObjectProtocol>(allObjectOf type: T.Type)
  func insert<T: ObjectProtocol>(_ object: T)
  func save<T: ObjectProtocol>(_ object: T)
  func update<T: ObjectProtocol>(_ object: T)
  func delete<T: ObjectProtocol>(_ object: T)
}

Error message:

inheritance from non-protocol, non-class type 'Self.ObjectProtocol'

Image of Xcode error

It works only like this, but I want to make it more flexible:

protocol DataModelProtocol {
  typealias ObjectProtocol = NSManagedObject
  ...
}
2

There are 2 answers

0
Alain T. On BEST ANSWER

This would perhaps be easier is you gave responsibility of the return types to the object classes themselves.

You would need two protocols but it would avoid mixing protocols and generics:

// The first protocol is for the base class of data objects
protocol DataProtocol  
{}

// The protocol provides the "typed" equivalents of the model's
// data manipulation methods.
// By using an extension to DataProtocol, this only needs to
// be done once for all models and data objects.
extension DataProtocol
{
   static func fetchObjects(from model:DataModelProtocol) -> [Self]?
   { return model.fetchObjects(object: Self.self) as! [Self]? }

   static func fetch(from model:DataModelProtocol) -> Self? 
   { return model.fetch(object: Self.self) as! Self? }

   // ...
}

// The second protocol is for the data models 
// It requires implementation of the data manipulation methods
// using the general "DataProtocol" rather than any specific class
// The actual instances it produces must be of the appropriate class
// however because they will be type casted by the DataProtocol's
// default methods
protocol DataModelProtocol 
{  
  func fetchObjects(object:DataProtocol.Type) -> [DataProtocol]?
  func fetch(object:DataProtocol.Type) -> DataProtocol?
  // ... and so on
}

... Here is a simple (naive) example of how the protocols can be used. (I intentionally chose not to use core data to illustrate the generality of the solution) ...

// The base class (or each one) can be assigned the DataProtocol
// (it doesn't add any requirement)

class LibraryObject : DataProtocol
{}

class Author: LibraryObject   
{ 
  var name = "" 
}

class Book: LibraryObject
{
   var title  = ""
}

// This is a simple class that implements a DataModelProtocol
// in a naive (and non-core-data way)

struct LibraryModel:DataModelProtocol
{
  var authors:[Author] = [ Author(), Author() ]

  var books:[Book] = [ Book(), Book(), Book(), Book(), Book() ]

  func fetchObjects(object: DataProtocol.Type) -> [DataProtocol]?
  { 
     return object == Book.self   ?  books 
          : object == Author.self ?  authors
          : nil
  }

  func fetch(object:DataProtocol.Type) -> DataProtocol?
  { return nil }

}

... Using the protocols will be a bit different from your approach because you would be starting from the object classes rather than passing them as parameter to the model ...

var library  = LibraryModel()
let allBooks = Book.fetchObjects(from:library) // this almost reads like english
2
Papershine On

If you want generic functions of another protocol to be conformed, just simply create a associatedType of T that conforms to the protocol, no need for making an extra ObjectProtocol:

protocol DataModelProtocol {
  associatedtype T: Protocol
  func fetchObjects<T>() -> [T]?
  func fetch<T>(object: T) -> T?
  func delete<T>(allObjectOf type: T.Type)
  func insert<T>(_ object: T)
  func save<T>(_ object: T)
  func update<T>(_ object: T)
  func delete<T>(_ object: T)
}