Declare a function in Swift that returns a closure with a generic T.Type - swift

we are trying to make a function in Swift , that returns a closure. The return type is as follows:
func completionBlock() -> (Response<T, NSError>) -> ()
where Response itself is a struct with 2 generics. The second will be always NSError, but the first depends on a switch.
Declaration of Response:
public struct Response<Value, Error: ErrorType> { ...
What we want to achieve is a function that will return a closure with a type that depends on a value of variable.
Depending on this value, and with a switch, it specifies one type or another, the only requirement for the generic parameter is that it must conform to a specific protocol, but we can't find a solution.
What we got so far is:
class BaseClass {
var endpoint: Int
...
func completionBlock() -> (Response<T, NSError>) -> () {
switch endpoint
{
case 1:
return getHandleResponseClosure(Car.self)
case 2:
return getHandleResponseClosure(Truck.self)
}
}
func getHandleResponseClosure<T: Mappable>(_: T.Type) -> (Response<T, NSError>) -> () {
let closure = { (_: Response<T, NSError>) -> () in
// ...
}
return closure
}
}
But this don't compile: "Use of undeclared Type T" at func completionBlock(), the type at this point is generic, it only requires to conform to protocol Mappable, but we don't know how to specify this.
Thanks!!, Greetings

While there are other problems with your code, the fundamental one is that generic types in method signatures must be declared directly after the name of the method. Take a look at completionBlock below. This code compiles and works.
struct Response<T, E: Error> {
}
struct Car {}
struct Truck {}
class BaseClass {
func completionBlock<T>() -> ((Response<T, NSError>) -> ()) {
return { response in /* What goes here? */ }
}
}
let c = BaseClass()
let r: (Response<Car, NSError>) -> () = c.completionBlock()
On a side note, I would replace struct Response<T, E: Error> with…
enum Response<T> {
case success(T)
case error(Error)
}

Related

Constraining a generic initilizer parameter to class generic type

I'm trying to implement a type erasing wrapper for a protocol. I have a protocol with an associatedtype constrainted to CaseIterable and a method using that type.
Given the following definitions:
protocol Foo {
associatedtype A: CaseIterable
func doSomething(input: A)
}
final class AnyFoo<A: CaseIterable>: Foo {
private let _doSomething: (A) -> Void
init<Other: Foo>(wrappedFoo: Other) where Other.A == A {
// "Cannot assign value of type '(Other.A) -> ()' to type '(A) -> Void'"
_doSomething = wrappedFoo.doSomething(input:)
}
func doSomething(input: A) {
_doSomething(input)
}
}
I'm getting the error Cannot assign value of type '(Other.A) -> ()' to type '(A) -> Void' in the initializer of the class. It seems that the compiler interprets A and Other.A as different types, and I can't figure out why, because the initializer has constrained Other.A and A to be the same. If I replace CaseIterable with Hashable, there's no problem.
Can somebody explain why this is happening?
Of course you can't. The _doSomething in class AnyFoo is type of Block which is an Anonymous function. So you can assign wrappedFoo.doSomething(input:) to it. When you call _doSomething(input), it actually call wrappedFoo.doSomething(input: input).
In another way, you can define a Block like:
let completion: (bool) -> void = { finished in
}
but you can't define it like:
let completion: (true) -> void = { finished in
}

Swift 4 extension function on generic protocol which takes a generic function as parameter

swift --version
Swift version 4.1 (swift-4.1-RELEASE)
Target: x86_64-unknown-linux-gnu
Given a simple protocol which defines a generic producer (Source):
protocol Source {
associatedtype T
func produce() -> T
}
And a mapping capable of converting between source types:
struct Mapping<T, U : Source> : Source {
typealias Transformation = (U.T) -> T
private let upstream : U
private let block : Transformation
init(_ upstream: U, _ block : #escaping Transformation) {
self.upstream = upstream
self.block = block
}
func produce() -> T {
return block(upstream.produce())
}
}
And a sample source which produces static text:
struct TextSource : Source {
private let text : String
init(_ text: String) {
self.text = text
}
func produce() -> String {
return text
}
}
I can use this to, e.g., count characters...
let t = TextSource("Hi!")
let f = Mapping(t, { (text: String) -> Int in
return text.count
})
print(f.produce()) // output: 3
But I'd rather like to use a generic map extension function on Source such that transformations can be chained, e.g.:
let t = TextSource("Hi!").map { (text: String) -> Int in
return text.count
}
Approach A
extension Source {
func map<T, U : Source>(_ block: #escaping Mapping<T, U>.Transformation) -> Source {
return Mapping(self, block)
}
}
That is rejected by the swift compiler:
error: generic parameter 'U' is not used in function signature
func map<T, U : Source>(_ block: #escaping Mapping<T, U>.Transformation) -> Source {
^
Approach B
extension Source {
func map<T>(_ block: #escaping Mapping<T, U>.Transformation) -> Source {
return Mapping(self, block)
}
}
In this case the compiler complains about the missing type parameter:
error: use of undeclared type 'U'
func map<T>(_ block: #escaping Mapping<T, U>.Transformation) -> Source {
^
Question
Which type parameters and constraints need to be specified on the map extension function in order to satisfy the compiler?
You cannot use Source as a concrete return type for map because it is a protocol with an associated type requirement.
To solve this, you can have the map function return Mapping<X, Self>:
extension Source {
func map<Result>(_ transform: #escaping (T) -> Result) -> Mapping<Result, Self> {
return Mapping(self, transform)
}
}
The function now has a Self requirement. The resulting Mapping type has a generic type parameter Self that is replaced by the concrete implementation of Source, e.g. Mapping or TextSource.

Swift generic without force unwrap downcast

I try to create some generic based code:
protocol ViewModelsCreator {
associatedtype T: EditItemViewModelType
func editItemViewModel<T>() -> T
}
class PlacesListViewModel: ViewModelsCreator {
typealias T = EditPlaceViewModel
func editItemViewModel<T>() -> T {
return EditPlaceViewModel()
}
}
class EditPlaceViewModel: EditItemViewModelType {}
protocol EditItemViewModelType {}
The playground shows error:
cannot convert return expression of type 'EditPlaceViewModel' to
return type 'T'
and suggest to use
return EditPlaceViewModel() as! T
Is there any solution to avoid this (as! T) force unwrap code? I think compiler should figure out that EditPlaceViewModel is EditItemViewModelType and should satisfy this generic.
You need to remove the <T> in the ViewModelsCreator protocol and the PlacesListViewModel class.
protocol ViewModelsCreator {
associatedtype T: EditItemViewModelType
func editItemViewModel() -> T
}
class PlacesListViewModel: ViewModelsCreator {
typealias T = EditPlaceViewModel
func editItemViewModel() -> T {
return EditPlaceViewModel()
}
}
You can also remove the typealias and replace -> T with -> EditPlaceViewModel in the PlacesListViewModel class. It works either way but this is more explicit.
class PlacesListViewModel: ViewModelsCreator {
func editItemViewModel() -> EditPlaceViewModel {
return EditPlaceViewModel()
}
}

Cannot convert value of type '(T) -> Void'

Example:
struct Wrapper<T> {
var key: Int = 0
var listeners: [Int: (T) -> Void] = Dictionary()
mutating func add(_ handler:#escaping (T) -> Void) {
self.key += 1
self.listeners[self.key] = handler
}
func get(key: Int) -> (T) -> Void {
return self.listeners[key]!
}
}
Test protocol:
protocol CommonProtocol {
}
Class that create Wrapper of test class
class C {
var wrapper: Wrapper = Wrapper<CommonProtocol>()
func add<T: CommonProtocol>(_ handler: #escaping (T) -> Void) {
self.wrapper.add(handler) //Cannot convert value of type '(T) -> Void' to expected argument type '(CommonProtocol) -> Void'
}
}
Image with error
I get error:
Cannot convert value of type '(T) -> Void' to expected argument type '(CommonProtocol) -> Void'
Question:
Why (T) -> Void can't be casted to (CommonProtocol) -> Void ? The T
is explicitly declared as <T: CommonProtocol>
This is my first question, if you have some suggestions please don't hesitate to contact me
You don't need to make func add generic.
When you specify in func add<T: CommonProtocol>... you explicitly telling the compiler that your function accepts all Types that inherit CommonProtocol but your Wrapper specifies that accepts CommonProtocol not inherited types.
Solution
Either type-erase class C:
Class C<T: CommonProtocol> {
var wrapper: Wrapper<T>
....
}
or if type T doesn't actually matter to you then:
func add(_ handler: #escaping (CommonProtocol) -> Void)
but second one doesn't make sense at all. You have to downcast it every-time you'll use this method (and downcasts are very bad :D)
Note: It's actually not related to this question, but one of your options is to type-erase the CommonProtocol too.

How to pass closure with argument as argument and execute it?

Initializer of class A takes an optional closure as argument:
class A {
var closure: ()?
init(closure: closure()?) {
self.closure = closure
self.closure()
}
}
I want to pass a function with an argument as the closure:
class B {
let a = A(closure: action(1)) // This throws the error: Cannot convert value of type '()' to expected argument type '(() -> Void)?'
func action(_ i: Int) {
//...
}
}
Class A should execute the closure action with argument i.
I am not sure about how to write this correctly, see error in code comment above. What has to be changed?
Please make your "what-you-have-now" code error free.
Assuming your class A like this:
class A {
typealias ClosureType = ()->Void
var closure: ClosureType?
init(closure: ClosureType?) {
self.closure = closure
//`closure` would be used later.
}
//To use the closure in class A
func someMethod() {
//call the closure
self.closure?()
}
}
With A given above, you need to rewrite your class B as:
class B {
private(set) var a: A!
init() {
//initialize all instance properties till here
a = A(closure: {[weak self] in self?.action(1)})
}
func action(i: Int) {
//...
}
}
The problem is that closure()? is not a type. And ()? is a type, but it is probably not the type you want.
If you want var closure to have as its value a certain kind of function, you need to use the type of that function in the declaration, e.g.
var closure: (Int) -> Void
Similarly, if you want init(closure:) to take as its parameter a certain kind of function, you need to use the type of that function in the declaration, e.g.
init(closure: (Int) -> Void) {
Types as Parameters
In Swift, every object has a type. For example, Int, String, etc. are likely all types you are extremely familiar with.
So when you declare a function, the explicit type (or sometimes protocols) of any parameters should be specified.
func swallowInt(number: Int) {}
Compound Types
Swift also has a concept of compound types. One example of this is Tuples. A Tuple is just a collection of other types.
let httpStatusCode: (Int, String) = (404, "Not Found")
A function could easily take a tuple as its argument:
func swallowStatusCode(statusCode: (Int, String)) {}
Another compound type is the function type. A function type consists of a tuple of parameters and a return type. So the swallowInt function from above would have the following function type: (Int) -> Void. Similarly, a function taking in an Int and a String and returning a Bool would have the following type: (Int, String) -> Bool.
Function Types As Parameters
So we can use these concepts to re-write function A:
class A {
var closure: (() -> Void)?
init(closure: (() -> Void)?) {
self.closure = closure
self.closure()
}
}
Passing an argument would then just be:
func foo(closure: (Int) -> Void) {
// Execute the closure
closure(1)
}
One way to do what I think you're attempting is with the following code:
class ViewController: UIViewController {
override func viewDidLoad() {
let _ = A.init(){Void in self.action(2)}
}
func action(i: Int) {
print(i)
}
}
class A: NSObject {
var closure : ()?
init(closure: (()->Void)? = nil) {
// Notice how this is executed before the closure
print("1")
// Make sure closure isn't nil
self.closure = closure?()
}
}