How to Use a Generic Class Without the Type Argument in Swift

How to use a generic class without the type argument in Swift?

Swift doesn’t yet support wildcard-style generics like Java does (i.e., Animal<?>). As such, a common pattern is to define a type-erased superclass, protocol (or wrapper) to enable such usage instead. For instance:

public class AnyAnimal {
/* non-generic methods */
}

and then use it as your superclass:

public class Animal<T: YummyObject>: AnyAnimal {
...
}

Finally, use AnyAnimal in your non-generic code instead:

private static var animal: AnyAnimal!

Examples in the Swift Standard Library. For a practical example, see the KeyPath, PartialKeyPath, and AnyKeyPath classes hierarchy. They follow the same pattern I outlined above. The Collections framework provides even further type-erasing examples, but using wrappers instead.

How to use generic type in function parameter from generic class in Swift

If I understand correctly, you have this syntax:

let sentence = Query<SomeTable>().select(.field1, .field2)

and you want this syntax:

let sentence = Query().select(.field1, .field2)

There are a lot of minor errors in your code, and I think you actually mean you want this syntax (select is a static method):

let sentence = Query.select(.field1, .field2)

In order to do that, Columns need to know their Table. As you've written this, it's legal to have two different Tables that have the same Columns type, and then this is ambiguous. (Note that the above syntax is definitely impossible, because there's no way to know what enum .field1 belongs to, but we can get closer).

So first, we need a ColumnIdentifier that knows its Table:

public protocol ColumnIdentifier: RawRepresentable & CodingKey & CaseIterable & Hashable {
associatedtype TableType: Table
}

Next, Table needs to assert that its ColumnIdentifer belongs to it. This will prevent multiple Table types from referencing the same ColumnIdentifier.

public protocol Table {
associatedtype Columns: ColumnIdentifier where Columns.TableType == Self
static var tablename: String { get }
}

Then Query would look like (slightly simplified):

struct Query<T: Table> {
static func select<C: ColumnIdentifier>(_ columns: C...) -> Query
where C.TableType == T
{
return Query()
}
}

And as an example Table implementation:

struct SomeTable: Table {
enum Columns: String, ColumnIdentifier {
case field1
case field2
typealias TableType = SomeTable
}

static var tablename: String { "table" }
}

Note that I don't believe there's any way to avoid the typealias TableType = SomeTable. Nesting one type inside another does not connect them in any way. You can't say "my containing type" or anything like that.

This approach will prevent tables from cross-linking other table's column identifiers. For example:

struct OtherTable: Table {
typealias Columns = SomeTable.Columns
static var tablename: String { "otherTable" }
}
// 'Table' requires the types 'OtherTable' and 'SomeTable.Columns.TableType' (aka 'SomeTable') be equivalent

With all that, you can get (close to) the syntax you're describing:

let sentence = Query.select(SomeTable.Columns.field1, .field2)

Note that you still need SomeTable here somewhere. Otherwise you don't know what enum .field1 comes from.

Personally, I wouldn't do it this way. I'd use the from version. It's clear and simple.

public protocol Table {
associatedtype Columns: ColumnIdentifier
static var tablename: String { get }
}

public protocol ColumnIdentifier: RawRepresentable & CodingKey & CaseIterable & Hashable {}

struct Query<T: Table> {
static func select(from: T.Type = T.self, columns: T.Columns...) -> Query
{
return Query()
}
}

struct SomeTable: Table {
enum Columns: String, ColumnIdentifier {
case field1
case field2
}
}

let sentence = Query.select(from: SomeTable.self, columns: .field1, .field2)

Note the little trick of from: T.Type = T.self. That means "when the return type is known, you don't need to include it." So for example, this will work without the from:

func f() -> Query<SomeTable> {
return Query.select(columns: .field1, .field2)
}

swift generic function without parameters

In case anyone comes across this question, here is the pattern I am currently using.

func foo<T>(type: T.Type) {}
func bar<T>(type: T.Type = String.self)
{
foo(type:type)
}

This pattern is also used in the standard library.

Swift cast generic without knowing the type

It's a little difficult to understand what you're really trying to do here (please tell me it's something other than JSON parsing; I'm so tired of JSON parsing and it's the only thing people ever ask about), but the short answer is almost certainly no. Some part of that is probably a misuse of types, and some part of that is a current limitation in Swift.

To focus on the limitation in Swift part, Swift lacks higher-kinded types. It is not possible to talk about Array. This is not a type in Swift. You can only work with Array<Int> or Array<String> or even Array<T>, but only in cases where T can be determined at compile time. There are several ways to work through this, but it really depends on what your underlying problem is.

To the misuse of types side, you generally should not have if x is ... in Swift. In the vast majority of cases this should be solved with a protocol. Whatever you were going to do in the if, make it part of the Parent protocol and give it a default empty implementation. Then override that implementation in Child. For example:

protocol Parent {
func doSpecialThing()
}

extension Parent {
func doSpecialThing() {} // nothing by default
}

class Child<LiterallyAnyValue, SameAsThePrevious>: Parent {}

extension Child {
func doSpecialThing() {
print(self)
}
}

func foobar(parent: Parent) {
parent.doSpecialThing()
}

Thanks for the clarification; Promise is a great thing to play with. Your mistake is here:

In my resolution process I have to check if the PromiseSubscriber is a (let's say) PromiseHandler and if it is then call the handler that returns something and then resolve the subscribed promise with that value.

Your resolution process should not need to know if it's a handler or a catcher. If it does, then your PromiseSubscriber protocol is incorrectly defined. The piece it sounds like you're missing is a Result. Most Promise types are built on top of Result, which is an enum bundling either success or failure. In your scheme, handlers would process successful Results and ignore failing results. Catchers would process failing results and ignore successful Results. The promise resolution shouldn't care, though. It should just send the Result to all subscribers and let them do what they do.

You can build this without a Result type by using a protocol as described above.

protocol PromiseSubscriber {
associatedType Wrapped // <=== It's possible you've also missed this piece
func handleSuccess(value: Wrapped)
func handleFailure(failure: Error)
}

extension PromiseSubscriber {
func handleSuccess(value: Wrapped) {} // By default do nothing
func handleFailure(failure: Error) {}
}

class PromiseHandler<Wrapped> {
func handleSuccess(value: Wrapped) { ... do your thing ... }
}

class PromiseCatcher {
func handleFailure(failure: Error) { ... do your thing ... }
}

I recommend studying PinkyPromise. It's a nice, simple Promise library (unlike PromiseKit which adds a lot of stuff that can make it harder to understand). I probably wouldn't use a protocol here; the associatedtype makes things a bit harder and I don't think you get much out of it. I'd use Result.

Can I assign a default type to generic type T in Swift?

There's no support for default generic arguments, but you can fake it by defining the default init() on a type-constrained extension, in which case the compiler will be smart enough to use that type. E.g.:

class MyManager<T> {
let instance: T

init(instance: T) {
self.instance = instance
}
}

extension MyManager where T == NSObject {
convenience init() {
self.init(instance: NSObject())
}
}

And now you can initialize the type with no argument and it will default to MyManager<NSObject>:

let mm1 = MyManager(instance: "Foo") // MyManager<String>
let mm2 = MyManager(instance: 1) // MyManager<Int>
let mm3 = MyManager() // MyManager<NSObject>

SwiftUI uses this technique quite a lot.

Use class as generic type parameter in a method

Yes you can. But I'm confused how you are going to use that.

You need to slightly modify the signature of your getClass<T: ParentClass>() -> T? function. I've purposefully changed the name of the function too because it doesn't make sense to have a name as getClass where you are actually getting child instance.

class SomeClass {

var childInstance: ParentClass?

func getChild<T: ParentClass>(type: T.Type) -> T? {
return childInstance as? T
}

func usage() {
if let child = self.getChild(type: ChildTwo.self) {
child.someMethodOfClassTwo()
}
}
}

Again you can use it without the if-let binding too. But then you've to deal with optional chaining:

SomeClass().getChild(type: ChildTwo.self)?.someMethodOfClassTwo()

Here with ParentClass as a class, you get autocompletion as you pass a generic class type which actually doesn't make much of sense:
ParentClass as class


Edit:

If you slightly modify the design as ParentClass to be a Parent protocol, then Xcode autocompletion will suggest you more meaningful signature. See:

protocol Parent { }
class ChildOne: Parent {
func functionOfChildOne() { }
}
class ChildTwo: Parent {
func functionOfChildTwo() { }
}

class SomeClass {

var childInstance: Parent?

func getChild<T: Parent>(type: T.Type) -> T? {
return childInstance as? T
}

func usage() {
if let child = self.getChild(type: ChildTwo.self) {
child.functionOfChildTwo()
}
}
}

And Xcode autocomplete suggests you pass a type that conforms to Parent protocol
Parent as protocol

how to pass type to a function with a call with generic type in swift

Define a specific handler function:

func resultadosHandler(resultados: resultados<productResponse>) { // productResponse is the type you expect
switch resultados {
case .datos(let dato): print(dato)
case .error(let error): print(error)
}
}

Pass it to loader function:

dat.loadData(params: parameters.Endpoint(endpoint: end), completion: resultadosHandler)

Or if you prefer, you can use it inline mode like this:

loadData(params: parameters.Endpoint(endpoint: end)) { (resultados: resultados<productResponse>) in
switch resultados {
case .datos(let dato): print(dato)
case .error(let error): print(error)
}
}

Don't forget to replace prints with the correct implementation!

What is the difference between passing classes as generic types or types?

In your overly simplified example, there's not much difference.

However, as soon as your type constraints become more complex, the only way to achieve the desired interface is via generics.

A great example if when you want a function to take an input argument whose type is a protocol with an associated type, such as SwiftUI's View.

If you tried to use View as the input argument type, you'd get a compile time error

func modifyView(_ view: View) {

}

Protocol 'View' can only be used as a generic constraint because it has Self or associated type requirements

However, as soon as you make view generic with a type constraint on View, the code compiles.

func modifyView<V: View>(_ view: V) {

}

You don't have to look at custom functions, the standard library is full of generic functions as well. JSONDecoder.decode showcases another common use case for generics, when you want your function to return a specific type based on some input arguments. This enables the developer to only write the function 1x, but make it work on lots of different types, while keeping type safety.

func decode<T: Decodable>(_ type: T.Type, from data: Data) throws -> T


Related Topics



Leave a reply



Submit