I have a class implementing Mappable protocol and I want to pass the type as parameter to a function and get an instance of that object, but the type might also be a primitive type.
class MyMappable : Mappable {
required init?(map: Map) {
print("i am custom mappable")
}
}
func getInstance<T>() -> T{
if T.self == Int.self {
return 10 as T
}
if T.self is Mappable.self {
return T(Json : [:]) //this is the problem, T is not Mappable
}
}
What I tried is create 2 override for getInstance like this :
func getInstance<T>() -> T{}
func getInstance<T : Mappable>() -> T{}
but for more types it can be lots of override.
is there a way to let swift know T is mappable in first function?
You can conditionally cast it to a Mappable like this (untested):
if let MappableType = T.self as? Mappable.self {
return MappableType(Json : [:])
}
Usually variable names are lowercase but in this case I can imagine using uppercase since it is used as a type.
You could also do it differently depending on your usage/callsite.
How about constraining the getInstance<T>() -> T method:
getInstance<T: Mappable>() -> T
Related
Is it possible to write a generic function that returns type T and one with the same name that returns [T] ?
Example:
static func processResult<T: Decodable>(_ inputData: Data) -> [T] { ... }
static func processResult<T: Decodable>(_ inputData: Data) -> T { ... }
So when calling I can do:
let myObj: Car = processResult(data)
...
let myObjs: [Car] = processResult(data)
I presume that technically [T] can also be considered a T type, hence the compiler getting confused. There isn't a way of saying that T will NOT be an array type?
When you have a chance to set the constraints in the function definition, you may also do this:
func processResult<T: Decodable & Collection>(_ inputData: Data) -> T { ... }
func processResult<T: Decodable>(_ inputData: Data) -> T { ... }
The compiler will always select the "most specialised" candidate. So, if your result type conforms to Collection, it's always the first candidate that gets selected.
Otherwise, if finds the other candidate.
There isn't a way of saying that T will NOT be an array type?
Well, if you can a find a protocol P to which Array doesn't conform, but all the other Ts that you want to use the second overload with does conform to, then you can do:
func processResult<T: Decodable>(_ inputData: Data) -> [T] {
...
}
func processResult<T: Decodable & P>(_ inputData: Data) -> T {
...
}
struct SomeStruct : Decodable, P { ... }
let x: SomeStruct = processResult(someData) // second overload
let y: [SomeStruct] = processResult(someData) // first overload
Is it possible to write a generic function that returns type T and one with the same name that returns [T]?
Of course, you just need to pass in the type parameter explicitly to help the compiler infer what T is:
func processResult<T: Decodable>(_ inputData: Data, _ type: T.Type) -> [T] {
...
}
func processResult<T: Decodable>(_ inputData: Data, _ type: T.Type) -> T {
...
}
struct SomeStruct : Decodable { ... }
let x: SomeStruct = processResult(someData, SomeStruct.self) // second overload
let y: [SomeStruct] = processResult(someData, SomeStruct.self) // first overload
You might think that saying SomeStruct twice is long-winded, but it is very necessary here - without the first SomeStruct, it is ambiguous which overload you want to call, because the two overloads differ only in return type. Without the second SomeStruct, it is ambiguous what T is, because as you said, T can be an array too.
Yes, it is possible but you don't need to return [T], you can return only T, but you have to cast it like [Car]
func processResult<T: Decodable>(_ inputData: Data) -> T {
return try! JSONDecoder().decode(T.self, from: inputData)
}
struct Car: Decodable {
}
let cars: [Car] = processResult(Data())
let car: Car = processResult(Data())
As you can see in the Swift Language Source Code,
extension Array: Decodable where Element: Decodable { ... }
Array will be a Decodable if all its elements are Decodable. So if you try use generics, compiler only sees the protocol you are passing to the function as the T and that is the Decodable in your case.
So T will be seen as some Decodable
Also, [T] will be seen as some Decodable
To work around this:
You can mask the Swift.Decodable:
protocol Decodable: Swift.Decodable {}
So you code should work without need to any other changes.
p.s: I think Swift should infer it from the left side of an assignment operator or from the right side of a type casting automatically! But I know Swift developers are working hard to make these things happen and even one big enhancement of type inference is coming up with the Swift 5.4 after all these years!
I know that currently it isn't possible to use protocols with associated types as types. Could you please help with workarounds for this case:
protocol ProvidesResult {
associatedtype ResultType
func provideResult(input: String) -> ResultType
}
class IntProvider: ProvidesResult {
typealias ResultType = Int
func provideResult(input: String) -> ResultType {
//Logic
}
}
var resultProviders: [String: ProvidesResult]
I need a dict where key is string and value is type that conforms to "ProvidesResult". I need it to initiate new instances of ResultProvider with provideResult method.
One approach is to use a type eraser:
struct AnyProvidesResult<T>: ProvidesResult {
private _provideResult: (String) -> T
init<PR>(_ pr: ProvidesResult) where PR: ProvidesResult {
_provideResult = pr.provideResult
}
func provideResult(input: String) -> T {
return _provideResult(input)
}
}
You can then use the above construct within your dictionary:
var resultProviders: [String: AnyProvidesResult<TypeOfResult>]
The restriction here is that the values in the dictionary need to be of the same type, so you either need to set an explicit type, or use a common denominator protocol.
I have following code:
protocol Vehicle {
func start()
}
class Car: Vehicle {
func start() {
print("Start car")
}
}
class MotorCycle: Vehicle {
func start() {
print("Start MotorCycle")
}
}
let vehicles: [Vehicle] = [Car(), MotorCycle()]
func get<T: Vehicle>() -> some Vehicle {
let result = vehicles.first {
$0 === T.self
}
return result!
}
// so I should be able to do this!
let car = get<Car>().start()
Inside the get function I want to go iterate through vehicles and return the concrete type of the Vehicle, which is either Car or MotorCycle. Finally, I want to call start on the returned type. How can I achieve it?
This is how get should be written:
func get<T: Vehicle>(_ type: T.Type) -> T? {
vehicles.first(where: { $0 is T }) as? T
}
There might not be any Ts in vehicles, so we should return an optional T.
For the implementation, you should use is to check the type in the first(where:) closure. Then, cast to T.
You can't directly pass type arguments like <Car> to a function in Swift. Type arguments must always be inferred. So I used a formal parameter type to help Swift infer what T is.
Caller:
get(Car.self)?.start()
You can not compare an instance value with a type like this: $0 === T.self. You can use $0 is T instead.
And, when calling a generic function, you cannot explicitly specialize a generic function. The type has to be inferred, like #Sweeper said.
Here's an example:
protocol Feed {
func items<T>() -> [T]? where T: FeedItem
}
protocol FeedItem {}
class FeedModel: Feed, Decodable {
func items<T>() -> [T]? where T : FeedItem {
return [FeedItemModel]() // Error: Cannot convert return expression of type '[FeedItemModel]' to return type '[T]?'
}
}
class FeedItemModel: FeedItem, Decodable {}
Why does it:
A) try to convert to T when T is a generic, not a type?
B) does not recognize FeedItemModel as conforming to FeedItem?
func items<T>() -> [T]? where T : FeedItem
This says that the caller can define T to be whatever they want, as long as T conforms to FeedItemModel, and this function will return an optional array of those.
FeedItemModel is something that conforms to FeedItem, but it is not promised to be the type T that the caller requested.
As an example, consider:
class OtherModel: FeedItem {}
According to your function signature, I can do this:
let ms: [OtherModel]? = FeedModel().items()
But your function won't then return [OtherModel]? to me. I suspect you don't actually mean this to be generic. I expect you mean:
func items() -> [FeedItemModel]?
or possibly
func items() -> [FeedItem]?
(Though I would think very hard before doing the latter one and make sure that the protocol existential is really doing useful work here.)
A)
T is a type, a homogenous concrete type specified at runtime.
Imaging T is class Foo : FeedItem it's obvious that FeedItemModel cannot be converted to Foo
B)
FeedItemModel is recognized as conforming to FeedItem but this is irrelevant.
It's often a mix-up of generics and protocols. Generic types are not covariant. If you need covariant types use an associated type.
Either you can ignore generics because because it only applies to that one function and it isn't needed since directly saying that the return type is [FeedItem]? yields the same result
protocol Feed {
func items() -> [FeedItem]?
}
class FeedModel: Feed, Decodable {
func items() -> [FeedItem]? {
return [OtherModel]()
}
}
If you on the other hand want a generic protocol then you should use a associated type
protocol Feed2 {
associatedtype T: FeedItem
func items() -> [T]?
}
class FeedModel2: Feed2, Decodable {
typealias T = FeedItemModel
func items() -> [T]? {
return [FeedItemModel]()
}
}
I have a protocol my swift code base I have protocol with an associated type and two methods. Both of the methods define different generic constrains for the associated type of the protocol. And I would like to make the struct conform two the protocol but with two different associated types.
protocol Convertable {
associatedtype TargetType
func convert() -> TargetType
}
func show<T : Convertable where T.TargetType == String>(toShow : T) {
print(toShow.convert())
}
func add<T : Convertable where T.TargetType == Int>(a : T, b : T) -> Int {
return a.convert() + b.convert()
}
struct MyData {
var data : Int
}
As an extension I make the struct conform the protocol where the TargetType will be String in order to pass it to the show method:
extension MyData : Convertable {
func convert() -> String { return String(self.data) }
}
So far everything works as expected. But now I also like to have the struct to conform to the Convertable protocol when TargetType is bound to an Int. Which seems to be impossible?
The first thing I tried was to add a second definition of the convert method to the extension:
extension MyData : Convertable {
func convert() -> String { return String(self.data) }
func convert() -> Int { return data }
}
The compiler now complains that MyData does no longer conform to the protocol. Second was to split this into two extensions and bind the TargetType explicitly.
extension MyData : Convertable {
typealias TargetType = Int
func convert() -> Int { return data }
}
extension MyData : Convertable {
typealias TargetType = String
func convert() -> String { return String(data) }
}
This has the effect that the compiler now complains the the TargetType was redefined.
My last try was to define two protocols that extend the Convertable protocol and constrain the TargetType and then implement both of them via extension:
protocol ConvertableString : Convertable {
associatedtype TargetType = String
}
protocol ConvertableInt : Convertable {
associatedtype TargetType = Int
}
extension MyData : ConvertableInt {
func convert() -> Int { return self.data }
}
extension MyData : ConvertableString {
func convert() -> String { return String(self.data) }
}
Which now makes the compiler happy for the extensions but no longer for the call to show because it doesn’t know that it can call the function with MyData.
Is there some thing that I have overseen or is this currently not possible in swift?
I just fund a way to archive this. The trick is to add another associated type in one of the subtypes of the protocol:
protocol ConvertableInt : Convertable {
associatedtype TResI
typealias TargetType = TResI
}
extension MyData : Convertable {
typealias TargetType = String
func convert() -> String { return String(self.data) }
}
extension MyData : ConvertableInt {
typealias TResI = Int
func convert() -> TResI { return self.data }
}
This also allows to get rid of the second subtype for string.
While this passes the compiler it totally crashes at runtime!
The compiler always calls the method defined which was defined at the explicit typealias. In this case:
typealias TargetType = String
Which will result in interpreting the address as a integer and give you totally wrong results. If you define it vice versa it will simply crash because it tries to interpret the integer as a address.