Making a Swift Array conform to a Protocol - ios

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.

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() {
}
}

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() {
}
}

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.

You can make an array to conform a protocol like this:
typealias PhotoArray = [Photo]
extension PhotoArray: Feedable {}

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'"

Related

Generic array in protocol without generic constraint

How do you specify a generic property in a protocol where the conforming type specifies the array type?
Info: Lets say we want to create a protocol which defines an array
property where the type of the array is unknown. The conforming types
specify the type of this array. This is a possible solution:
protocol Wallet {
associatedtype Value
var moneyStack: [Value] {get set}
}
struct BitcoinWallet: Wallet {
var moneyStack: [String]
}
struct EthereumWallet: Wallet {
var moneyStack: [Int]
}
The problem with this approach is that we can't access the moneyStack
property:
let bitcoinWallet = BitcoinWallet(moneyStack: ["A","B"])
let etheureumWallet = EthereumWallet(moneyStack: [1,2])
let wallets: [Wallet] = [bitcoinWallet, etheureumWallet]
for wallet in wallets {
print(wallet.moneyStack) // not possible
}
We can try another approach where the type of the array is any:
protocol Wallet {
var moneyStack: [Any] {get set}
}
struct BitcoinWallet: Wallet {
var moneyStack: [Any]
}
struct EthereumWallet: Wallet {
var moneyStack: [Any]
}
This is a possible solution but an array which holds [Any] is too
generic. I can't specify the type. Example:
let bitcoinWallet = BitcoinWallet(moneyStack: ["A","B"])
let ethereumWallet = EthereumWallet(moneyStack: [1,2])
var wallets: [Wallet] = [bitcoinWallet, ethereumWallet]
for wallet in wallets {
print(wallet.moneyStack.count) // this works
print(wallet.moneyStack[0]) // this works
}
let badBitcoinWallet = BitcoinWallet(moneyStack: [12.4, 312312.123]) // BitcoinWallets aren't allowed to hold doubles!
wallets.append(badBitcoinWallet) // This shouldn't be allowed!
The first solution is what I want. The type is specified in every struct but Swift complains I can't use generic constraints. The main problem I want to solve is that I want an array which holds different types of wallets, which all have their own array of a different type.
I thought a protocol would make this easy but it doesn't. Am I doing something wrong with my architecture? Should I even use protocols? Maybe subclassing can solve this?
You can't store 2 objects with different types in array (Wallet<String> and Wallet<Int> are different types).
But you can try something like this:
protocol WalletProtocol {
func printMoneyStack()
}
class Wallet<T> {
var moneyStack: [T] = []
init(moneyStack: [T]) {
self.moneyStack = moneyStack
}
}
class BitcoinWallet: Wallet<String>, WalletProtocol {
func printMoneyStack() {
print(moneyStack)
}
}
class EthereumWallet: Wallet<Int>, WalletProtocol {
func printMoneyStack() {
print(moneyStack)
}
}
let bitcoinWallet = BitcoinWallet(moneyStack: ["A","B"])
let etheureumWallet = EthereumWallet(moneyStack: [1,2])
let wallets: [WalletProtocol] = [bitcoinWallet, etheureumWallet]
for wallet in wallets {
wallet.printMoneyStack()
}

Generic class type doesn't conform to Any

I have a problem with storing my generic classes in an array. How should I format the type for my array while keeping the reference to the original type (I know I could do var myClasses: [Any] = [] but that wouldn't be helpful when retrieving the variable from my array :(
Example is below:
import UIKit
protocol Reusable { }
extension UITableViewCell: Reusable { }
extension UICollectionViewCell: Reusable { }
class SomeClass<T> where T: Reusable {
init() { }
}
var myClasses: [SomeClass<Reusable>] = []
myClasses.append(SomeClass<UITableViewCell>())
myClasses.append(SomeClass<UICollectionViewCell>())
myClasses.append(SomeClass<UITableViewCell>())
myClasses.append(SomeClass<UICollectionViewCell>())
Edit: Just to clarify, I have used the collection and table cells as an example, I am not actually planning on storing them together :)
Edit 2 var myClasses: [SomeClass<Reusable>] = [] generates error: using 'Reusable' as a concrete type conforming to protocol 'Reusable' is not supported
Edit 3 var myClasses: [SomeClass<AnyObject>] = [] generates error: type 'AnyObject' does not conform to protocol 'Reusable'
I think you can create some sort of Holder class that can accept and retrieve your object:
class Holder {
lazy var classes: [Any] = []
func get<T>(_ type: T.Type) -> [T]? {
return classes.filter({ $0 is T }) as? [T]
}
}
And the main part:
let holder = Holder()
holder.classes.append(SomeClass<UITableViewCell>())
if let someTableViewCells = holder.get(SomeClass<UITableViewCell>.self)?.first {
// or filter out again to get specific SomeClass of UITableViewCell
print(someTableViewCells)
}
Or without holder class:
var classes: [Any] = []
classes.append(SomeClass<UITableViewCell>())
if let someTableViewCell = classes.filter({ $0 is SomeClass<UITableViewCell> }).first as? SomeClass<UITableViewCell> {
print(someTableViewCell)
}
You should use array of AnyObject in your case. Because as you know swift is strong typed language and for example
SomeClass<UITableViewCell>
and
SomeClass<UICollectionViewCell>
are different types of objects. As for example Array< Int > and Array< String >, they are both arrays, but still it's a different types of objects. So in this case you'll have to use declaration:
var myClasses: [AnyObject] = []
and check type of object or typecast them every time you'll need.
if (myClasses[0] is SomeClass<UICollectionViewCell>) { do something }
or
if let cell = myClasses[0] as? SomeClass<UICollectionViewCell> { do something }
My suggestion is adding parent protocol SomeClass Container for your SomeClass generic. Then put an array of SomeClass objects inside SomeClass.
protocol Reusable { func cakePlease() }
extension UITableViewCell: Reusable {
func cakePlease() { }
}
extension UICollectionViewCell: Reusable {
func cakePlease() { }
}
protocol SomeClassContainer {
func teaPlease()
func afternoonPlease()
}
class SomeClass<T: Reusable>: SomeClassContainer {
var item: T?
init() { }
func teaPlease() { }
func afternoonPlease() {
teaPlease()
item?.cakePlease()
}
}
var myClasses = [SomeClassContainer]()
myClasses.append(SomeClass<UITableViewCell>())
myClasses.append(SomeClass<UICollectionViewCell>())
myClasses.append(SomeClass<UITableViewCell>())
myClasses.append(SomeClass<UICollectionViewCell>())
myClasses[0].teaPlease()
if let item = (myClasses[0] as? SomeClass<UITableViewCell>)?.item {
item.cakePlease()
}
for myClass in myClasses {
if let tableCell = (myClass as? SomeClass<UITableViewCell>)?.item {
tableCell.cakePlease()
} else if let collectionCell = (myClass as SomeClass<UICollectionViewCell>)?.item {
collectionCell.cakePlease()
}
}
myClasses.forEach({ $0.afternoonPlease() })
Generally the way to type your array would be to go as specific as possible whilst still covering all bases.
What I mean by this for example is storing an array of UIViewController objects, even though each will actually be a different type. You wouldn't use Any here because you really don't need to be that general.
In your case, why not use Reusable? Since all your generic classes conform to it anyway, it is as general as you can go whilst still maintaining convenience.

Swift generic class which could take Array of elements or single element

I would like to make generic class which will be able to take Parsable type, or Array of Parsable type. Logic for both are almost the same so i don't want to make two different class for this operation. Is it possible to solve it using Swift generics, or protocol associatedtype types?
protocol Parsable: class {
associatedtype Type
static func objectFromDictionary(dictionary: Dictionary<String, AnyObject>, inContext context: NSManagedObjectContext) -> Type?
func importFromDictionary(dictionary: Dictionary<String, AnyObject>)
}
class ParseOperation<T: Parsable>: NSOperation {
func execute() -> T {
}
}
class ParseOperation<T where T: SequenceType, T.Generator.Element == Parsable>: NSOperation {
func execute() -> T {
// Do parsing
}
}
This i how i would like to work:
class ParseOperation<T where T: SequenceType, T.Generator.Element == Parsable OR T: Parsable>: NSOperation {
func execute() -> T {
// Do parsing
}
}
In my current implementation i am using enum which looks little bit ugly:
class ParseOperation<T where T: NSManagedObject, T:Parsable>: NSOperation {
var responseToParse: AnyObject?
var parseType: ParseType
var parsedObjects: [T]?
init(parseType: ParseType) {}
func execute() {
var objects: [NSManagedObject] = []
if self.parseType == .Single {
if let responseToParse = self.responseToParse as? Dictionary<String, AnyObject>,
let parsedObject = T.objectFromDictionary(responseToParse, inContext: localContext) {
objects.append(parsedObject)
}
} else if self.parseType == .Array {
if let responseToParse = self.responseToParse as? Array<Dictionary<String, AnyObject>> {
for dictionary in responseToParse {
if let parsedObject = T.objectFromDictionary(dictionary, inContext: localContext) {
objects.append(parsedObject)
}
}
}
}
self.parsedObjects = objects
...
}
}
I modified #RonaldMartin 's answer to show how ParsableArray might help you. It don't need to take input of Parsable elements, just implement parse function this way:
protocol Parsable {
associatedtype T
static func parse(input: AnyObject) -> T?
}
struct ParsableArray<TElement where TElement: Parsable>: Parsable {
static func parse(input: AnyObject) -> [TElement.T]? {
guard let arrayInput = input as? [AnyObject] else {
return nil
}
return arrayInput.flatMap(TElement.parse)
}
}
I've renamed objectFromDictionary to parse because it's need to take AnyObject not the Dictionary to be able to parse array. You can add context or whatever you like to parse method, of course.
If Parsable done this way then ParseOperation becomes very simple:
class ParseOperation<T where T: Parsable>: NSOperation {
let input: AnyObject
var result: T.T?
init(input: AnyObject) {
self.input = input
}
override func main() {
result = T.parse(input)
}
}
Then, you can parse arrays this way (note: this is only to demonstrate how to create ParseOperation; S is just some Parsable struct):
let op = ParseOperation<ParsableArray<S>>(input: [["1": 5, "2": 6], ["3": 10]])
op.main()
var r: [S]? = op.result
I hope, this will help.
As far as I know, the type constraint system is not designed to handle OR constraints. However, it should still be possible to do what you're asking.
One approach is to represent both singleton Parsables and collections of Parsables under a single type that you can use to constrain ParseOperation. The neatest way to do this would be to extend Array (or SequenceType, CollectionType, etc.) to conform to the Parsable type as well, but this is not yet possible as of Xcode 7.3. You can use the same workaround from that linked question and add an intermediate class to represent Parsable arrays:
class ParsableArray: Parsable {
let array: [Parsable]
init(array: [Parsable]) {
self.array = array
}
// Parsable conformance...
}
Now, you can just use the original protocol for your type constraint:
class ParseOperation<T: Parsable>: NSOperation {
func execute() -> T {
// Do parsing
}
}
Ideally you should be able to do this:
// This DOES NOT compile as of XCode 7.3
extension Array: Parsable where Element: Parsable {
// Parsable implementation
}
However it currently doesn't work.
Currently you have to rely on a wrapper struct:
struct ParsableArray<Element: Parsable>: Parsable {
let array: [Element]
init(_ array: [Element]) {
self.array = array
}
// Parsable implementation
}
You can also implement a convenience method for [Parsable] arrays:
extension Array where Element: Parsable {
func toParsable() -> ParsableArray<Element> {
return ParsableArray(self)
}
}
So you could execute your method like this:
let array = [Parsable]()
parse(array.toParsable()) // Equivalent to: parse(ParsableArray(array))

Make Class iterable with a for in Loop?

I have a custom class:
class MyArrayClass {
...
}
This class is a custom list implementation.
I want to do the following:
var arr:MyArrayClass = MyArrayClass()
arr.append("first")
arr.append("second")
arr.append("third")
for entry in arr {
println("entry: \(entry)")
}
Edit: The class I want to make iterable is JavaUtilArrayList it uses this class IOSObjectArray.
Which protocol must be confirmed by my class such that it works in a for in loop?
You should have a look at this blog post on this exact topic. I'll write a summary of it here though:
When you write:
// mySequence is a type that conforms to the SequenceType protocol.
for x in mySequence {
// iterations here
}
Swift converts this to:
var __g: Generator = mySequence.generate()
while let x = __g.next() {
// iterations here
}
Therefore, to be able to enumerate through your custom type you need to make your class implement the SequenceType protocol too. Looking at the SequenceType protocol below, you can see you only need to implement one method that returns an object that conform to the GeneratorType protocol (GeneratorType is covered in the blog post).
protocol SequenceType : _Sequence_Type {
typealias Generator : GeneratorType
func generate() -> Generator
}
Here's an example of how to make MyArrayClass useable in a for loop:
class MyArrayClass {
var array: [String] = []
func append(str: String) {
array.append(str)
}
}
extension MyArrayClass : SequenceType {
// IndexingGenerator conforms to the GeneratorType protocol.
func generate() -> IndexingGenerator<Array<String>> {
// Because Array already conforms to SequenceType,
// you can just return the Generator created by your array.
return array.generate()
}
}
Now to use this in practise:
let arr = MyArrayClass()
arr.append("first")
arr.append("second")
arr.append("third")
for x in arr {
println(x)
}
// Prints:
// First
// Second
// Third
I hope that answers your question.
You can make it much faster using NSFastGenerator:
extension MyArrayClass: SequenceType {
public func generate() -> NSFastGenerator {
return NSFastGenerator(self)
}
}

Casting struct with generic parameter with swift

I'm trying to do the following.
protocol Vehicle {
}
class Car : Vehicle {
}
class VehicleContainer<V: Vehicle> {
}
let carContainer = VehicleContainer<Car>()
let vehicleContainer = carContainer as VehicleContainer<Vehicle>
But I get the compile error on the last line:
'Car' is not identical to 'Vehicle'
Is there any workaround for this?
Also I believe this type of casting should be possible because I can do it with Arrays which are built on generics. The following works:
let carArray = Array<Car>()
let vehicleArray = carArray as Array<Vehicle>
Expanding your array example quickly in playground works as intended
protocol Vehicle {
}
class Car : Vehicle {
}
class Boat: Vehicle {
}
var carArray = [Car]()
var vehicleArray : [Vehicle] = carArray as [Vehicle]
vehicleArray.append(Car()) // [__lldb_expr_183.Car]
vehicleArray.append(Boat()) // [__lldb_expr_183.Car, __lldb_expr_183.Boat]
Haven't done too much work with swift generics but looking quickly at the swift docs
struct Stack<T: Vehicle> {
var items = [Vehicle]()
mutating func push(item: Vehicle) {
items.append(item)
}
mutating func pop() -> Vehicle {
return items.removeLast()
}
}
and using vehicles instead of the generic type T
var vehicleStack = Stack<Vehicle>()
vehicleStack.push(Car())
vehicleStack.push(Boat())
var aVehicle = vehicleStack.pop()
appears to compile aok in an app (playground has some issues handling it though)

Resources