Ben Packard
Ben Packard

Reputation: 26476

Making a Swift Array conform to a Protocol

Let's say that certain items can appear in a Feed, so long as they implement the necessary properties defined by the Feedable protocol. Let's also say that the Photo object is feed-worthy:

extension Photo: Feedable { }

Is it possible to say that an Array of these photos might also be Feedable?

extension [Photo] : Feedable

Or do I always need some kind of wrapper object, such as a PhotoAlbum, to conform to Feedable?

Edit

To re-iterate, I was curious whether I can make only arrays of Photo objects Feedable. Not making Array of any content type Feedable, not making an array of Feedables itself Feedable (both of which are offered as solutions below if that's what you need).

In other words, a solution (which I doubt exists) would allow me to define a variable of type Feedable with the following outcomes:

var feedable: Feedable

//photo is feedable, so this is fine
feedable = Photo() //ok

//arrays of photos are feedable
let photo1 = Photo()
let photo2 = Photo()
feedable = [photo1, photo2]

//arrays of other things are not
feedable = ["no", "dice"] //nope

//even if the contents of an array are themselves Feedable, that's not sufficient. E.g. Video is Feedable, but Array of Videos is not.
let video1 = Video()
let video2 = Video()
feeble = video1 //fine
feedable = [video1, video2] //nope

Perhaps this gist (which doesn't compile of course) shows the intention more clearly.

Upvotes: 17

Views: 5294

Answers (6)

Vicente Garcia
Vicente Garcia

Reputation: 6370

Answers here are almost there for what you want to do. Specifically this one, with just 1 change it can be restricted to elements of a type as opposed to elements confirming to a protocol.

Swift 5.8

extension Array: Feedable where Element == Photo {
   
}

With this extension the code posted on the question shows errors where expected and it ok everywhere else.

Xcode displaying the code from the question

Upvotes: 0

Marie Re
Marie Re

Reputation: 1

You can make an array to conform a protocol like this:

typealias PhotoArray = [Photo]

extension PhotoArray: Feedable {}

Upvotes: 0

Vitalii Gozhenko
Vitalii Gozhenko

Reputation: 9354

You can achieve your goal in this way:

Swift 4:

protocol Feedable {
    func foo()
}

extension String: Feedable {
    func foo() {    
    }
}

extension Array: Feedable where Element: Feedable {
    func foo() {    
    }
}
// or in more generic way to support Array, Set and other collection types
extension Collection: Feedable where Element: Feedable {
    func foo() {    
    }
}

Upvotes: 13

Mikael
Mikael

Reputation: 2395

I didn't try in playground but maybe you can simply make an Array of Feedable:

var myPhotosArray = [Feedable]()

Then everything implementing the Feedable protocol would be allowed in the Array. If you want only a photo array, You can still subclass your Photo object to make a FeedablePhoto object.

Try this in Playground instead of downvoting without even testing. Seriously 3 downvotes without any reasons and explanations...

import UIKit

protocol Tree: class {
  func grow()
}

class BigTree: Tree {
  internal func grow() {
    print("Big tree growing")
  }
}

class SmallTree: Tree {
  internal func grow() {
    print("Small tree growing")
  }
}

class Car {
  //not a tree
}

var manyTrees = [Tree]()

manyTrees.append(BigTree())
manyTrees.append(SmallTree())
manyTrees.append(Car()) //This makes an error "Car doesn't conform to expected type 'Tree'"

Upvotes: -5

fruitcoder
fruitcoder

Reputation: 1218

I think this is currently not possible. In my project I have the same issue with a ModelProducer.

protocol M: ModelType {}
protocol ModelProducerType {
    associatedtype M: ModelType
    var model: M? { get }
    func produce()
}

struct Test: ModelType {}
class TestProducer: ModelProducerType {
    var model: Test?
    func produce() {
        model = Test()
    }
}

I use ModelType as a ghost protocol. The problem is I cannot make a model producer that produces multiple ModelTypes, because of the same reason you discovered. The solution in this case was the following:

protocol M: ModelType {}
protocol ModelProducerType {
    associatedtype M: ModelType
    var model: [M] { get }
    func produce()
}

struct Test: ModelType {}
class TestProducer: ModelProducerType {
    var model: [Test] = []
    func produce() {
        model = [Test()]
    }
}

This is more flexible from the start. I get rid of the optional variable and single model producers just have one item in the array. Maybe you can use a similar approach.

Upvotes: 1

Lumialxk
Lumialxk

Reputation: 6369

If there was an array of Photo and Video,what would you like to be?

1.Every element performs like what they are.

extension Array where Element : Feedable {
    func foo() {
        if Element.self == Photo.self {

        } else {

        }
    }
}

2.The whole array performs as 'Video'.

extension Array where Element : Photo {
    func foo() {

    }
}

Upvotes: 3

Related Questions