Getting The Parameterised Type of a Generic in Swift

Swift check type against a generic type

You cannot tell a function what the types of its generic placeholders are (unlike with a generic struct). It must infer them from the context e.g. its arguments.

One way to do what you want is to add another argument related to type T. Rather than pass in a dummy value, you could use the metatype of the type you want:

func generic<T>(parameter: AnyObject, type: T.Type) -> Bool {
if parameter is T {
return true
} else {
return false
}
}

let o: AnyObject = "hello"
generic(o, String.self) // true
generic(o, NSString.self) // also true
generic(o, Int.self) // false

However, I would ask you, what is it you think you're achieving here? You've essentially done nothing more than implement is as a function:

o is String     // true
o is NSString // true
o is Int // false

The point of generics is to operate on arguments generically, but you aren't giving the function any argument of a specific type to actually do any work on (hence the inability to infer one).

Getting the parameterised type of a generic in swift?

I suppose, for example, when you have Array<Int> value, what you want is Int type.

I believe there is no generic way to do that.

Some type has typealiased the generic parameter, you can get it with that name. For example:

let array: Array<UInt16> = [1,2,3]
let elementType = array.dynamicType.Element.self // -> Swift.UInt16
sizeof(elementType) // -> 2

This works because Array typealiased Element = T:

struct Array<T> : MutableCollectionType, Sliceable, _DestructorSafeContainer {

/// The type of element stored by this `Array`
typealias Element = T

Even if it does not have the typealias, you can add it by extension:

extension Unmanaged {
typealias Payload = T
}

let something: Unmanaged<NSString> = .passRetained("test")
something.dynamicType.Payload.self // -> NSString

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

How to pass a function parameter to the generic structure in swift

Currently your function getAPICallModelDecodable is defined in such a way that you have to pass in a model of type T. But if I understand it correctly, you only want to pass in (define) the type T.

For this you have to change the function as follows:

func getAPICallModelDecodable<T:Codable> (url: String, type: T.Type,
success: @escaping (_ responseObject:T?)->Void,
failure: @escaping (_ error:String) -> Void ,
method:HTTPMethod = .get)

In the function body you can use T e.g. simply like this:

AF.request(url,method:method).responseDecodable(of: FetchAPI<T>.self)

The function call would then look like this:

getAPICallModelDecodable(url: ..., type: Model.self, ...)

How To constraint parameter of generic type in function, to sibling parameter

PGDev's solution gets to the heart of the question, but IMO the following is a bit easier to use:

enum Error: Swift.Error { case unexpectedType }
mutating func set<T: MyProtocol>(type: T.Type = T.self, at index: Int,
applying: ((inout T) throws -> Void)) throws {
guard var value = arr[index] as? T else { throw Error.unexpectedType }
try applying(&value)
arr[index] = value
}

...

var v = Values()
try v.set(type: StructB.self, at: 1) {
$0.thirdProperty = 20
}

The = T.self syntax allows this to be simplified a little when the type is known:

func updateThirdProperty(v: inout StructB) {
v.thirdProperty = 20
}
try v.set(at: 1, applying: updateThirdProperty)

Another approach that is more flexible, but slightly harder on the caller, would be a closure that returns MyProtocol, so the updating function can modify the type. I'd only add this if it were actually useful in your program:

mutating func set<T: MyProtocol>(type: T.Type = T.self, at index: Int,
applying: ((T) throws -> MyProtocol)) throws {
guard let value = arr[index] as? T else { throw Error.unexpectedType }
arr[index] = try applying(value)
}

...

try v.set(type: StructB.self, at: 1) {
var value = $0
value.thirdProperty = 20
return value // This could return a StructA, or any other MyProtocol
}

(Which is very close to PGDev's example, but doesn't require Optionals.)

Swift function generic parameter

You need to use generics!

static func validateResponse<T>(dataResponse: DataResponse<T>) -> String {
// status code checking here
}

Usage:

validateResponse(dataResponse: response)

The generic parameter T will be inferred be News it will be as if the method is like this:

static func validateResponse(dataResponse: DataResponse<News>) -> String {
// status code checking here
}

Use Swift `is` to check type of generic type

Here's 2 things that might work for you.

Option 1:

Note that child is a tuple containing a String? with the name of the property ("property" in your example) and the item. So you need to look at child.1.

In this case, you should be checking:

if String(describing: type(of: child.1)).hasPrefix("Foo<")

Option 2:

If you create a protocol FooProtocol that is implemented by Foo<T>, you could check if child.1 is FooProtocol:

protocol FooProtocol { }

struct Foo<T>: FooProtocol {}

struct Bar {
var property = Foo<String>()
}

var test = Bar()

let mirror = Mirror(reflecting: test)

// This code is trying to count the number of properties of type Foo
var inputCount = 0
for child in mirror.children {
if child.1 is FooProtocol {
inputCount += 1
}
}


Related Topics



Leave a reply



Submit