Let's say I have a Commander object that executes commands. The return type is not always the same and changes according to the command.
I'd like to be able to use a function that forwards a command to the Commander, tests if the result is of a certain type (passed as a parameter), before calling a success closure if the cast succeeded, and a failure closure otherwise.
I've tried using generic parameters with something like this:
func postCommand<T>(command: String, expectedResponseType: T, success: T -> Void, failure: (NSError?) -> Void) {
Commander.execute(command, completion: { (response: AnyObject?) in
guard let content = response as? T else {
failure(nil)
return
}
success(content)
})
}
Calling it that way
self.postCommand("command", expectedResponseType: [String: AnyObject], success: { (content: [String: AnyObject]) in
print("Success")
}) { (error: NSError?) in
print("Failure")
}
But I get an error from the compiler:
Cannot convert value of type '([String : AnyObject]) -> Void' to expected argument type '_ -> Void'
If I try to do the cast like this:
guard let content = response as? expectedResponseType
the compiler complains that expectedResponseType is not a type.
I can't figure out how to do that. Is it even possible?
The problem is not with the casting, but with the type of the expectedResponseType: parameter.
If you wish to pass a type to a function, you need to use the metatype type as the argument type. In this case, the expectedResponseType: parameter of your function should be of type T.Type – allowing you to pass in a type to define T.
func postCommand<T>(_ command: String, expectedResponseType: T.Type, success: (T) -> Void, failure: (NSError?) -> Void) {
// ...
}
You'll also need to use the postfix .self in order to refer to the actual type of whatever you pass into the expectedResponseType: parameter:
self.postCommand("command", expectedResponseType: [String: AnyObject].self, success: { content in
print("Success")
}) { error in
print("Failure")
}
Although you should note that the type of T can be inferred directly from the success closure you pass to the function:
func postCommand<T>(_ command: String, success: (T) -> Void, failure: (NSError?) -> Void) {
// ...
}
self.postCommand("command", success: { (content: [String: AnyObject]) in
print("Success")
}) { error in
print("Failure")
}
Related
I have a function that makes a service call to fetch data, uses JSONDecoder to decode the JSON response into a model object and then return that object as part of a parameter in the completion block provided to the function. In the object, I am actually using the Result object.
Here is the code -
static func fetchData(_ completion: #escaping (Result<ExampleModelObject, Error>) -> Void)
I am trying to make this function generic, where it can accept the url it needs to call as one parameter and the Model object as another parameter and so I modified it to this -
static func fetchData <T: Decodable>(urlString: String, _ completion: #escaping (Result<T, Error>) -> Void)
This is the code where I use jsondecoder -
let parsedJSON = try jsonDecoder.decode(T.self, from: data)
completion(.success(parsedJSON))
Here is how I am trying to call this function from another class -
DataRetriever. fetchData(urlString: dataURLString) { (result: ExampleModelObject) in
However, I am getting 2 errors during compilation -
Cannot convert value of type '(ExampleModelObject) -> Void' to expected argument type '(Result<_, Error>) -> Void'
Generic parameter 'T' could not be inferred
Would anyone please be able to help me with how I can fix these errors?
You have to specify the full type
DataRetriever.fetchData(urlString: dataURLString) { (result: Result<ExampleModelObject,Error>) in
try to
fetchData(urlString: "") { (model: Result<ExampleModelObject, Error>) in
}
or use two completion blocks like so:
func fetchData <T> (urlString: String, success: #escaping(T) -> Void, failure: #escaping(Error) -> Void) where T: Decodable {
}
execution:
fetchData(urlString: "") { (model: ExampleModelObject) in
} failure: { error in
}
With an additional parameter you can help the combiler to recognise the type.
If you define the function in this way, it is no longer necessary to specify the type in the closure parameter.
static func fetchData <T: Decodable>(type: T.Type, urlString: String, _ completion: #escaping (Result<T, Error>) -> Void)
The call then looks like this:
DataRetriever.fetchData(type: ExampleModelObject.self, urlString: dataURLString) { result in }
I'd like to write a generic function, that fetches some kind of data locally, using the Core Data framework. I wrote the function like this:
func fetch<T:NSManagedObject>(type: T.Type = T.self, completion: #escaping (([T]?) -> ())) {
do {
let data = try context.fetch(T.fetchRequest()) as [T]
completion(data)
} catch {
completion(nil)
}
}
However, the complier tells me this error "Cannot convert value of type '[Any]' to type '[T]' in coercion, arguments to generic parameter 'Element' ('Any' and 'T') are expected to be equal"
Is there the possibility to do this kind of cast?
Try the following code:
func fetch<T:NSManagedObject>(type: T.Type = T.self, completion: #escaping (([T]?) -> ())) {
do {
let data = try context.fetch(T.fetchRequest()) as? [T]
completion(data)
} catch {
completion(nil)
}
}
(As Sweeper has pointed out fetch returns an array, so you have to cast the result to [T] and not just T)
I have a little experience in Swift and facing a problem to pass a closure in a function as a parameter.
//1.
public func changeMyStatus(to f:?, _ completion:#escaping (_ isSucced:Bool)->()){
//
}
//2.
func goLive(_ completion:#escaping (_ isSucced:Bool)->()){
}
//3.
func goNonLive(_ completion:#escaping (_ isSucced:Bool)->()){
}
Now , I want to use first function in my controller and wants to pass second/third function as a parameter. Closure in first will return true/false depending on what returned by closure in second/third.
i)What will be the type I should put in first function?
Also I want to call first function from my class like this
changeMyStatus(to: goNonLive) { (isSuccess) in
}
please help
You need to change the changeMyStatus function signature and implementation like:
public func changeMyStatus(to f: (#escaping (Bool) -> ()) -> () , _ completion:#escaping (_ isSucced:Bool)->()){
f { (status) in
completion(status)
}
}
You can call these function like:
// goLive
changeMyStatus(to: goLive(_:)) { (status) in
print(status)
}
// goNonLive
changeMyStatus(to: goNonLive(_:)) { (status) in
print(status)
}
Your second and third function has a completion parameter with a type of : (Bool -> Void) -> Void
So in order to pass it to your first function, try this way :
public func changeMyStatus(to f: ((Bool) -> ()), _ completion:#escaping (_ isSucced:Bool)->()) { // Your body}
In Swift, you need to see a function as a type like Int, Double, String...
I have a protocol defined as
func get<T: ApiModel, TError: ApiModel>(url: String, params : [String : AnyObject]?, headers : [String : String]?, success:(T)->Void, error:(TError?, NSError)->Void) -> Void;
Trying to call it as
self.webClient.get("http://google.com", params: nil, headers: headers, success: { (response: ConcreteClass) in
}, error: { (errorModel:ConcreteErrorClass, error: NSError) in
})
This results in the following error:
'Cannot convert value of type (ConcreteClass) -> ()' to expected argument type '(_) -> Void'
In the declaration of the method you're calling, the error: closure has the first parameter of optional type (error:(TError?, NSError)->Void), but you declared it with non-optional type. That may be the reason (indeed, Swift sometimes produces pretty unrelated error messages).
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)
}