Callbacks in iOS swift 3 - ios

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

Related

How to apply [weak self] to a swift function (not closure)

Say I have a network function that has a completion and I use it multiple times in my consumer like this:
class Network {
func getNumber(completion: #escaping (Int) -> ()) {
//some network code
completion(5)
}
}
class MyClass {
var num = 0
let network = Network()
func myFunc() {
network.getNumber { [weak self] (number) in
self?.num = number
}
}
func myFunc2() {
network.getNumber { [weak self] (number) in
self?.num = number
}
}
}
and to avoid duplicate code i replace the closures with a single function
like this:
class MyClass {
var num = 0
let network = Network()
func myFunc() {
network.getNumber(completion: self.handleData)
}
func myFunc2() {
network.getNumber(completion: self.handleData)
}
func handleData(_ number: Int) -> () {
self.num = number
}
}
The problem With this approach is that I am unable to capture self as weak in the handleData function.
The problem could be easily avoided by changing the handleData to be a closure like this:
lazy var handleData: (Int) -> () = { [weak self] in
self?.num = $0
}
So my question is: is there a way to apply weak self for a function and not only a closure?
You could do this if you want to use weak reference to self on it's funcitons:
class MyClass {
var num = 0
func myFunc() {
Network.getNumber { [weak self] in
self?.handleData($0)
}
}
func myFunc2() {
Network.getNumber { [weak self] in
self?.handleData($0)
}
}
func handleData(_ number: Int) {
self.num = number
}
}
Also, you don't have to provide -> () for a function that returns nothing.
Taking into account that handleData can be really big, how about
func myFunc() {
Network.getNumber { [weak self] i in self?.handleData(i) }
}

How to pass a function to another class in Swift

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

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.

Pass a closure to replace delegation in swift

I have used blocks to get callbacks from other classes. I am a beginner in swift. So I need to find a way to define a closure in one class and assign it to a closure variable in another class. I will be calling this closure to get callback of second class in first class.
What I want is something like this,
Class A {
func viewdidload() {
let b:B = B()
b.closure(string:NSString) = {
print string
}
}
}
class B {
var closure(NSString);
func () {
closure(string)
}
}
Here, but you should really learn Swift first
class A {
func viewdidload() {
let b = B()
b.closure = { str in
print(str)
}
}
}
class B {
var closure : ((String) -> Void)?
func t() {
closure?("hi")
}
}
Try this in a Playground:
class B {
var closure: (NSString) -> NSString
init() {
closure = { input in
return "NSString from closure: \(input)"
}
}
}
class A {
init() {
let b:B = B()
print(b.closure("hello from A"))
b.closure = { input in
return "NSString from another closure: \(input)"
}
print(b.closure("hello from A"))
}
}
A();
will print
NSString from closure: hello from A
NSString from another closure: hello from A

Adding Swift Closures as values to Swift dictionary

I want to create a Swift dictionary that holds String type as its keys and Closures as its values. Following is the code that I have but it gives me the error:
'#lvalue is not identical to '(String, () -> Void)'
class CommandResolver {
private var commandDict:[String : () -> Void]!
init() {
self.setUpCommandDict();
}
func setUpCommandDict() {
self.commandDict["OpenAssessment_1"] = {
println("I am inside closure");
}
}
}
I tried looking at other question on StackOverflow regarding closures in dictionaries but it does not give me any satisfactory answer. So I would really appreciate some help here.
Here is the way to go. I am not sure exactly why your implementation does not work though.
class CommandResolver {
typealias MyBlock = () -> Void
private var commandDict:[String : MyBlock] = [String:MyBlock]()
init() {
self.setUpCommandDict();
}
func setUpCommandDict() {
self.commandDict["OpenAssessment_1"] = {
print("I am inside closure");
}
}
}
If you initialize your dictionary in your init before calling your setup function, it should work:
class CommandResolver {
private var commandDict: [String: () -> Void]
init() {
commandDict = [:]
setUpCommandDict()
}
func setUpCommandDict() {
commandDict["OpenAssessment_1"] = {
println("I am inside closure")
}
}
}

Resources