Home > Enterprise >  Swift protocols with generic collections
Swift protocols with generic collections

Time:11-09

I want to have a protocol that contains a collection of Item. The goal is for it to not matter if the collection of items is an array/set/etc, as long as it conforms to Collection.

protocol Content {
    var items: Collection<Item> // This doesn't work, swift doesn't support generic protocol parameters
}

An example use case, where I use Realm to persist Content, but would like to be able to mock data without needing to import Realm:

// Typical usage with Alamofire and Realm

struct RealmArticle: Object, Content {
   var items = List<Item>()
}


func fetchContent(completion: (Content?, Error) -> ())) {
   session.request(...).responseDecodable { (content: RealmArticle) in 
      completion(content, nil)
   }
}

// Mock without needing to include Realm

func mockFetchContent(completion: (Content?, Error) -> ()) {
   completion(MockArticle(items: (0...5).map { Item() }), nil)
}

struct MockArticle: Content {
    var items: [Item]
}

My current solution is to use an array in the protocol, and modify the realm model:

protocol Content {
    var items: [Item]
}

struct RealmArticle: Object, Content {
   var _items = List<Item>()

   var items: [Item] { Array(_items) }
}

But this isn't ideal because it loads the entire List into memory whenever items is accessed. Is there a better solution or should I take a different approach?

CodePudding user response:

If you can live without the requirement that the element of the collection must be Item then you can use associatedtype to make it work with Collection

protocol Content {
    associatedtype T: Collection
    var items: T { get set }
}

This should be enough to be able to mock as you want.

CodePudding user response:

Why do you need a stand-alone protocol when you could just make this method generic?

func fetchContent<C: Collection>(completion: (Result<C, Error>) -> Void) where C.Iterator.Element == Object {
   
   }

  • Related