Swift: Return Array of Type Self

Return an array of type Self

The only option I can see of doing something like that is using protocols instead of a base class, like this:

protocol Generic {

func all() -> [Self]

init()

}

extension Generic {

func all() -> [Self] {
return [self.dynamicType.init()]
}

}

final class A : Generic {

}

A().all()

You have two limitations doing it like this. First, all classes that conform to your protocol have to be final. Second, all classes must obviously implement the init defined in the protocol, otherwise we wouldn't be able to have the all method defined.

Edit: you don't actually need to define the init as long as you don't define any other initializers

Edit 2: I didn't notice you used class functions, you can modify my example to use class functions instead of instance methods by replacing func all() -> [Self] with static func all() -> [Self] and

func all() -> [Self] {
return [self.dynamicType.init()]
}

with

static func all() -> [Self] {
return [self.init()]
}

Swift return object of type from an array containing class elements

Use foo as? T to try to cast foo as type T.

for aVehicle in myVehicles{
if let bus = aVehicle as? Bus {
print("Bus found", bus.make)
}
}

Your getVehicle can thus be written as:

func getVehicle<T>() -> T? {
for aVehicle in myVehicles {
if let v = aVehicle as? T {
return v
}
}
return nil
}

let bus: Bus? = getVehicle()

or functionally:

func getVehicle<T>() -> T? {
return myVehicles.lazy.flatMap { $0 as? T }.first
}
let bus: Bus? = getVehicle()

(Note that we need to specify the returned variable as Bus? so getVehicle can infer the T.)

Swift Function to Return Array Reference

One possibility is to create a wrapper class to store the array of TrackedItems. Say, for example:

class TrackedItemCollection {
var items = Array<TrackedItem>()
}

and then in your implementation:

class ProgramOptions {
var startDate: Date = Date()
var trackedActivities = TrackedItemCollection()
var trackedFoods = TrackedItemCollection()
var trackedDrugs = TrackedItemCollection()
...
}

func trackedCollectionForSection(_ section: Int) -> TrackedItemCollection? {
switch(section) {
case 1: return programOptions.trackedActivities
case 2: return programOptions.trackedFoods
case 3: return programOptions.trackedDrugs
default: return nil
}
}

When you want the array, you use the items property in TrackedItemCollection. As TrackedItemCollection is a class and thus a reference type, you won't have the arrays copyed.

CollectionType extension with return Type Self instead of Array

There is no promise that an arbitrary CollectionType can be instantiated or copied. So there's no way to implement your extension. A good way to see this is to try to implement this for FlattenCollection or LazyCollection. Or try creating a RandomValueCollection and then try to implement this method.

But you're free to do this on a RangeReplaceableCollectionType, which makes all the promises you need:

extension RangeReplaceableCollectionType {
func otherMap(block: Generator.Element -> Generator.Element) -> Self {
var copy = Self.dynamicType.init()
self.forEach {
copy.append(block($0))
}
return copy
}
}

Not all collections conform to RangeReplaceableCollectionType. There probably is a good reason Set doesn't, so you may have to create a simpler protocol of your own.

Remember, a CollectionType may represent some static thing that isn't meaningful or safe to copy. For example, I might make a CollectionType that represents "the files on this disk." Copying it could invalidate invariants (such as assumptions about caches and file pointers). There's no promise that a CollectionType is a value type. It is free to be a reference.

Swift: Return an array of objects of subclassed type from a superclass

You can achieve this using a protocol and an extension:

protocol BaseProtocol {}

extension BaseProtocol {
static func objects() -> [Self] {
return []
}
}

class Base: NSObject, BaseProtocol {}
class Subclass: Base {}

let objects = Subclass.objects()

Is there a way to get an array of the values of the properties in an object?

You can use Mirror

public final class MyObject: NSObject {
private let firstURL: URL
private let secondURL: URL
private let thirdURL: URL
private let fourthURL: URL

public init(firstURL: URL, secondURL: URL, thirdURL: URL, fourthURL: URL) {
self.firstURL = firstURL
self.secondURL = secondURL
self.thirdURL = thirdURL
self.fourthURL = fourthURL
}

public func values() -> [URL] {
return Mirror(reflecting: self).children.compactMap({ $0.value as? URL })
}
}

let url = URL(string: "https://stackoverflow.com/")!
let myObject = MyObject(firstURL: url, secondURL: url, thirdURL: url, fourthURL: url)
print(myObject.values()
// [https://stackoverflow.com/, https://stackoverflow.com/, https://stackoverflow.com/, https://stackoverflow.com/]

How to make return type of function generic in Swift

This isn't possible.

What generics are for

Suppose you have this function:

func identity(_ value: Any) -> Any {
return value
}

It doesn't actually work:

let i = 5
assert(identity(i) == i) // ❌ binary operator '==' cannot be applied to operands of type 'Any' and 'Int'

Any causes type information to be lost. Even though we see that the type of the parameter and the return value will always be the same, we haven't expressed that to the type system. This is a perfect use-case for a generic type parameter. It allows us to express the relationships between the types of the parameter and the return value.

func identity<T>(_ value: T) -> T {
return value
}

let i = 5
assert(identity(i) == i) // ✅

What generics are not for

Looking back at your problem, you see that there are no type relationships here to be expressed.

  • ClassA.validateModel() always returns [String]
  • ClassB.validateModel() always returns [Int]
  • ClassC.validateModel() always returns [MyCustomEnum]

That's not generic.

How would it even work?

Suppose you had an object of type ElementData. That object could be an instance of ElementData, or of ClassA, or ClassB, or ClassC. Given that all four of these types are possible, and assuming there exists some concoction to do what you want, how would this code work?

let elementData = someElementData()
let validatedModel = elementData.validateModel() // What type is `someValue` supposed to be?

Since we (nor the compiler) knows what concrete type the value of elementData will be (we only know that it's an ElementData, or one of its subclasses), how is the compiler supposed to determine the type of validatedModel ?

Furthermore, your code would be breaking the Liskov Substitution Principle. ClassA needs to support being substituted where ElementData is expected. One of the things that ElementData.validateModel()can do is return a Something. Thus, ClassA.validateModel() needs to either return a Something, or a subclass (strangely, it seems that only inheritence relationships work, not protocol subtype relationships. For example, returning Int where Any is expected doesn't work). Since ClassA.validateModel() returns Array<String>, and Array isn't a class (thus, can't have a superclass), there's no possible type Something that could be used to make the code not violate LSP and compile.

Here's an illustration of LSP, and how Covariance works in return types of overridden methods, and not in parameter types of overridden methods.

// https://www.mikeash.com/pyblog/friday-qa-2015-11-20-covariance-and-contravariance.html

class Animal {}
class Cat: Animal {}

class Person {
func purchaseAnimal() -> Animal {
return Animal()
}
}

class CrazyCatLady: Person {
// Totally legal. `Person` has to be able to return an `Animal`.
// A `Cat` is an animal, so returning a `Cat` where an `Animal` is required is totally valid
override func purchaseAnimal() -> Cat {
return Cat()
}

// This method definition wouldn't be legal, because it violates the Liskov Substitution Principle (LSP).
// A `CrazyCatLady` needs to be able to stand in anywhere a `Person` can be used. One of the things a
// `Person` can do is to `pet(animal: Animal)`. But a `CrazyCatLady` can't, because she can only pet cats.
//
// If this were allowed to compile, this could would be undefined behaviour:
//
// let person: Person = getAPerson()
// let animal: Animal = getAnAnimal()
// person.pet(animal)
//
// override func pet(animal: Cat) { // ❌ method does not override any method from its superclass
//
// }
}

One approach to a solution

First of all, we need to establish what's in common between these return types. If we could do that, then the compiler has an answer to the question of "what type should someModel be?" above.

There are two tools available:

  1. Class inheritance (subclasses are subtypes of their superclasses)
  2. Protocol conformance (protocol conforming types are subtypes of the protocols they conform to)

Both have advantages/disadvantages. Protocols force you in the painful road of dispair that is associated-type, whereas Classes are less flexible (since they can't be subclassed by enums or structs). In this case, the answer lies with what you want this code to do. Fundamentally, you're trying to hook this data up to a table cell. So make a protocol for that:

protocol CellViewDataSource {
func populate(cellView: UICellView) {
// adjust the cell as necessary.
}
}

Now, update your methods to return this type:

class ElementData {
func validateModel() -> CellViewDataSource {
fatalError()
}
}

class ClassA {
func validateModel() -> CellViewDataSource {
fatalError()
}
}

To implement these methods, you'll have to extend Array to conform to CellViewDataSource. However, that's a pretty terrible idea. I suggest instead that you create a new type (probably a struct) that stores the data you need.

struct ModelA {
let name: String
let points: Int
let description: String
let image: UIImage
}

extension ModelA: CellViewDataSource {
func populate(cellView: UICellView) {
// Populate the cell view with my `name`, `points`, `description` and `image`.
}
}

class ElementData {
func validateModel() -> CellViewDataSource {
fatalError("Abstract method.")
}
}

class ClassA {
func validateModel() -> CellViewDataSource {
return ModelA(
name: "Bob Smith",
points: 123,
description: "A dummy model.",
image: someImage()
)
}
}

Create a generic Swift function to return an array of Core Data entities

You were almost there. You only have to change the parameter
type from T to T.Type:

func fetchEntities<T: NSManagedObject>(entity: T.Type) -> [T]?

so that you can pass a type instead of an instance:

let stores = fetchEntities(entity: Store.self)


Related Topics



Leave a reply



Submit