How to pass a function to another class in Swift - ios

I need some help passing a function to my second class. I have tried many things but it still not work.
class Main {
func one() {
let test = Sub(function: two)
}
func two(val: Int, completion: ((Int, String)?)->()) { }
}
class Sub {
var function: (Int, ((Int, String)?)->())
init(function: (Int, ((Int, String)?)->())) {
self.function = function
}
}
Why is it that i get error on this line
let test = Sub(function: two)
which says: Cannot convert value of type '(Int, ((Int, String)?) -> ()) -> ()' to expected argument type '(Int, ((Int, String)?) -> ())'
What is the reason?

Function has a return value plus the completion , you need to change syntax of function var inisde Sub and the init also
class Main {
func one() {
let test = Sub(function: two)
}
func two(val: Int, completion: ((Int, String)?)->()) { }
}
class Sub {
var function: ((Int, ((Int, String)?)->())) -> ()
init(function:#escaping ((Int, ((Int, String)?)->())) -> ()) {
self.function = function
}
}

#escaping need to be added
class Main {
func one() {
let test = Sub(function: two)
}
func two(val: Int, completion: (Int, String)?)->() { }
}
class Sub {
var function: (Int, (Int, String)?)->()
init(function: #escaping (Int, (Int, String)?)->()) {
self.function = function
}
}

Related

Store a closure with generic type

I am writing tests for a structure that has a func with a closure whom in turn has a child parameter of Result<ModelProtocol>.
However, when I am writing the mock for my struct it refuses to store the closure thinking that <T> != <ModelProtocol>. In turn this is correct, since this is a generic type.
The error I am getting now is:
Playground execution failed:
error: Test.playground:51:49: error: cannot assign value of type '(Result<T>) -> Void' to type '((Result<ModelProtocol>) -> Void)?'
self.doSomethingCompletionHandler = completionHandler
^~~~~~~~~~~~~~~~~
Which is the problem because <T> actually is of type T: ModelProtocol.
How can I store the closure (completionHandler) so I can call it at a later point to run the closure manually (by the test).
This is an example of what my problem in a playground:
public enum Result<Value> {
case success(Value)
case failure(Error)
public var value: Value? {
switch self {
case .success(let value):
return value
case .failure:
return nil
}
}
public var error: Error? {
switch self {
case .success:
return nil
case .failure(let error):
return error
}
}
}
protocol ModelProtocol {
init(aString: String)
}
protocol AnImportantProtocol {
func doSomething<T: ModelProtocol>(firstParameter: String, completionHandler: #escaping((Result<T>)->Void))
}
enum StructureError : Error {
case defaultError
}
struct StructureOne : AnImportantProtocol {
func doSomething<T: ModelProtocol>(firstParameter: String, completionHandler: #escaping ((Result<T>) -> Void)) {
debugPrint("Doing something")
if let model = ExampleModel(aString: "Test") as? T {
completionHandler(.success(model))
} else {
completionHandler(.failure(StructureError.defaultError))
}
}
}
class StructureOneMock : AnImportantProtocol {
var doSomethingInvokeCount: Int = 0
var doSomethingCompletionHandler: ((Result<ModelProtocol>)->Void)? = nil
func doSomething<T: ModelProtocol>(firstParameter: String, completionHandler: #escaping ((Result<T>) -> Void)) {
self.doSomethingInvokeCount += 1
self.doSomethingCompletionHandler = completionHandler
}
func callCompletionHandler(result: Result<ModelProtocol>) {
if let doSomethingCompletionHandler = self.doSomethingCompletionHandler {
doSomethingCompletionHandler(result)
}
}
}
struct ExampleModel {
let someString: String
}
extension ExampleModel : ModelProtocol {
init(aString: String) {
self.someString = aString
}
}
I believe you have a contravariance error that isn't obvious because the variance is happening in the generic parameter.
Consider the following code:
class Cat {}
class Kitten: Cat {}
class Cougar: Cat {}
protocol CatDayCareProtocol {
func setRaiseFunction(raiseFunction: #escaping (Cat) -> Cougar)
}
class CatDayCare: CatDayCareProtocol {
func setRaiseFunction(raiseFunction: #escaping (Cat) -> Cougar) {
self.raiseFunction = raiseFunction
}
private var raiseFunction: ((Cat) -> Cougar)? = nil
func callCougerRaiseFunction() -> Cougar? {
let cougar = Cougar()
return raiseFunction?(cougar)
}
}
let catDayCare = CatDayCare()
catDayCare.setRaiseFunction(raiseFunction: { kitty: Kitten in
return Cougar()
})
In this example, swift throws the following error:
error: Contravariance.playground:23:51: error: expected expression
catDayCare.setRaiseFunction(raiseFunction: { kitty: Kitten in
This seems unintuitive as a kitten is a Cat, so wouldn't this be fine? Let's consider what happens if you tried to do the callCougerRaiseFunction(). It instantiates a cougar, which is a cat, and calls its raise function, which expects a cat so this is legal. However, if you pass a function expecting a kitten as a parameter, suddenly you are passing a cougar to a function that wants a kitten and it is sad.
Now for your example, you have
func doSomething<T: ModelProtocol>(firstParameter: String, completionHandler: #escaping ((Result<T>) -> Void)) {
self.doSomethingInvokeCount += 1
self.doSomethingCompletionHandler = completionHandler
}
In this example, T is strictly just as or more specific than a ModelProtocol (as it could be any protocol that inherits from ModelProtocol), which I believe makes Result<T> as a function parameter contravariant with the data type Result<ModelProtocol>. It is just that the compiler isn't quite smart enough to know it is contravariance, but it does know the conversion isn't legal.
As for actually solving the problem, is it really necessary to use a generic? Why can't you just use:
protocol AnImportantProtocol {
func doSomething(firstParameter: String, completionHandler: #escaping((Result<ModelProtocol>)->Void))
}
You are probably better off using an associatedType constraint in this case, for example:
protocol AnImportantProtocol {
associatedtype MyType: ModelProtocol
func doSomething(firstParameter: String, completionHandler ((Result<MyType>)->Void)?)
}
Then you typealias in the implementations:
class StructureOneMock<T: ModelProtocol> : AnImportantProtocol {
typealias MyType = T
var doSomethingInvokeCount: Int = 0
var doSomethingCompletionHandler: ((Result<MyType>)->Void)? = nil
func doSomething(firstParameter: String, completionHandler: ((Result<MyType>) -> Void)?) {
self.doSomethingInvokeCount += 1
self.doSomethingCompletionHandler = completionHandler
}
func callCompletionHandler(result: Result<MyType>) {
if let doSomethingCompletionHandler = self.doSomethingCompletionHandler {
doSomethingCompletionHandler(result)
}
}
}
You could skip the generic T in the implementations and specify a concrete type there.

Callbacks in iOS swift 3

Hello is it possible to make a callback like such? I want to pass a function as a parameter to be able run the callback function after some task is finished.
class ConnectBLE {
var callBackFunc: ()->()
init(callFunc: #escaping () -> ()){
callBackFunc = callFunc
}
func runCallBackFunc() {
callBackFunc()
}
}
class DelegateARC {
private let object = ConnectBLE(callFunc: RaspakHC05)
func RaspakHC05() {
print("hello from a callback")
}
}
But I'm having an error.
Cannot convert value of type '(DelegateARC) -> () -> ()' to expected argument type '() -> ()'
You cannot run non-lazy code on the top level of the class which requires self (RaspakHC05).
Apart from that you have to call runCallBackFunc() in ConnectBLE somewhere to execute the closure.
You could do (in a Playground)
class ConnectBLE {
var callBackFunc: ()->()
init(callFunc: #escaping () -> ()){
callBackFunc = callFunc
}
func runCallBackFunc() {
callBackFunc()
}
}
class DelegateARC {
init() {
ConnectBLE(callFunc: RaspakHC05).runCallBackFunc()
}
func RaspakHC05() {
print("hello from a callback")
}
}
DelegateARC() // prints "hello from a callback"
Another way (use Optional to delay giving object its real value until initialization has otherwise fully taken place):
class ConnectBLE {
var callBackFunc: ()->()
init(callFunc: #escaping () -> ()){
callBackFunc = callFunc
}
func runCallBackFunc() {
callBackFunc()
}
}
class DelegateARC {
private var object : ConnectBLE! //*
init() {
self.object = ConnectBLE(callFunc: RaspakHC05) // *
}
func RaspakHC05() {
print("hello from a callback")
}
}
You are trying to pass a function instead a closure. You have to use a closure and lazy instantiation to make it work:
class DelegateARC {
private lazy var object: ConnectBLE = {
return ConnectBLE(callFunc: self.RaspakHC05)
}()
var RaspakHC05: () -> () {
return {
print("hello from a callback")
}
}
}

Passing functions as arguments error: Cannot convert value of type "someType.type" to expected argument type "someType"

I have a function, which takes several other functions as arguments:
class Iterator {
func iterateItems(itemArray: [Items], removeItem: (Items) -> Void, addItem: (inout Items) -> Void, calculateEfficiency: () -> Void) -> [Items] {
// function body
}
}
And I call it in its class' subclass like this:
class WPCalculator: Iterator {
func removeWeaponItem(item: WeaponItems) { ... }
func addWeaponItem(item: inout WeaponItems) { ... }
func calcWeaponDamage() { ... }
func iterateWPItems() {
iterateItems(itemArray: WeaponItems.weaponItems, removeItem: removeWeaponItem(item: WeaponItems), addItem: addWeaponItem(item: &WeaponItems), calculateEfficiency: calcWeaponDemage())
}
}
Then Xcode says error on removeItem and addItem parameter:
Cannot convert value of type "WeaponItems.type" to expected argument type "WeaponItems"
Also the WeaponItems class is a subclass of Items class:
class WeaponItems: Items { ... }
Why is that error message?
You are passing a class WeaponItems instead of class objects. Following is more correct version:
func iterateWPItems() {
let itemsToRemove = WeaponItems() //initialize object somehow
var itemsToAdd = WeaponItems() //initialize object somehow
iterateItems(itemArray: WeaponItems.weaponItems, removeItem: removeWeaponItem(item: itemsToRemove), addItem: addWeaponItem(item: &itemsToAdd), calculateEfficiency: calcWeaponDemage())
}
EDIT: Sorry, I've got your problem. Than instead of calling these methods you should just pass them as arguments, so you don't have to put parentheses to a trail of method name:
func iterateWPItems() {
iterateItems(itemArray: WeaponItems.weaponItems, removeItem: removeWeaponItem, addItem: addWeaponItem, calculateEfficiency: calcWeaponDemage)
}
Within your invocation of iterateItems, removeWeaponItem(item: WeaponItems), WeaponItems is a type, since it is the same as the class name WeaponItems. If you create variables of type WeaponItems, and name them differently (e.g., weaponItems) instead of WeaponItems, you probably will not run into this problem.
I found the problem: the way I passed functions as arguments is wrong, it should be like this:
func iterateWPItems() {
iterateItems(itemArray: WeaponItems.weaponItems, removeItem: removeWeaponItem as! (Items) -> Void, addItem: addWeaponItem as! (inout Items) -> Void, calculateEfficiency: calcWeaponDemage())
}

What is return type " () "?

I have a function, which takes several other functions as arguments:
class Iterator {
func iterateItems(itemArray: [Items], removeItem: (Items) -> Void, addItem: (inout Items) -> Void, calculateEfficiency: () -> Void) -> [Items] {
// function body
}
}
And I call it in its class' subclass like this:
class WPCalculator: Iterator {
func removeWeaponItem(item: WeaponItems) { ... }
func addWeaponItem(item: inout WeaponItems) { ... }
func calcWeaponDamage() { ... }
func iterateWPItems() {
return iterateItems(itemArray: WeaponItems.weaponItems, removeItem: removeWeaponItem as! (Items) -> Void, addItem: addWeaponItem as! (inout Items) -> Void, calculateEfficiency: calcWeaponDemage)
}
}
Also the WeaponItems class is a subclass of Items class:
class WeaponItems: Items { ... }
Xcode gives me a warning on addWeaponItem:
Cast from (inout WeaponItems) -> () to unrelated type (inout Items) -> Void always fails
I can't understand the -> () return type, clearly its not what my function does, the other functions passed as arguments without inout parameters just worked fine
Your function iterateItems has removeItem addItem calculateEfficiency parameter is function type. So you must use function name as params when call function.
like this:
iterateItems(itemArray: WeaponItems.weaponItems, removeItem: removeWeaponItem, addItem: addWeaponItem, calculateEfficiency: calcWeaponDamage)
I think () same as Void
The warning occurs because WeaponItems and Items do not match
http://ericasadun.com/2015/05/11/swift-vs-void/

How to make a closure optional with a default value

I have a bunch of functions that I would like to be able to specify a default closure if one is not provided. I can't seem to figure out how to do it without some ugly code.
So for example, I would like the perform function to accept an optional parameter called closure that is executed when provided. Otherwise it will default to executing myClosure. How can I make this better so I don't have to repeat the function calls?
class MyClas {
typealias closureType = ((number: Int) -> Int)?
func myClosure (number: Int) -> Int {
return number * 2
}
func perform(number: Int, closure: closureType = nil) -> Int {
if closure == nil {
return myClosure(number)
} else {
return closure!(number: number)
}
}
}
Ideally, I could do this!
class MyClass {
typealias closureType = ((number: Int) -> Int)?
func myClosure (number: Int) -> Int {
return number * 2
}
func perform(number: Int, closure: closureType = myClosure) -> Int {
return closure(number: number)
}
}
Your problem is that you've made made myClosure a method (or member function), which means it doesn't have the signature you want (it's instead a curried function, of type MyClass->Int->Int).
Either pull it out of the class, or make it a static (or rather "class" in the case of a class) method:
class MyClass {
typealias closureType = (number: Int) -> Int
class func myClosure (number: Int) -> Int {
return number * 2
}
func perform(number: Int, closure: closureType = MyClass.myClosure) -> Int {
return closure(number: number)
}
}
P.S. once you do this, it doesn't need to be optional any more
Just to show it compiling as a non-static method:
class MyClass {
typealias closureType = MyClass -> (number: Int) -> Int
func myClosure (number: Int) -> Int {
return number * 2
}
func perform(number: Int, closure: closureType = myClosure) -> Int {
return closure(self)(number: number)
}
}
let c = MyClass()
println(c.perform(5)) // prints 10
Closure is first-class citizen in Swift. So you can provide default value for it.
class MyClass {
func perform(number: Int, closure: Int -> Int = { $0 * 2 }) -> Int {
return closure(number)
}
}

Resources