Pass a closure to replace delegation in swift - ios

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

Related

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

Swift restrict generic type to Type

How can I restrict a generic type to be a type, not an instance of a type?
If I have a class:
class SomeClass<T: SomeProtocol> {}
how can I ensure that T is only an instance of AnyClass (which is just AnyObject.Type)
My protocol only has static methods and in order to call those methods I have to do instance.dynamicType.protocolMethod whereas I want to do someType.protocolMethod
AFAIK, Swift does not allow you to use a metatype as a generic type. (I believe this is along the lines of what Sam Giddins wished for in Swift 3.)
You can, however, use it as a value. Instead of making T a type parameter, make it a property:
protocol SomeProtocol {
static func foo()
}
struct Concrete: SomeProtocol {
static func foo() {
print("I am concrete")
}
}
class SomeClass {
let T: SomeProtocol.Type
init(T: SomeProtocol.Type) {
self.T = T
}
func go() {
T.foo() // no need for dynamicType
}
}
SomeClass(T: Concrete.self).go()
If, as you say, your protocol contains only static methods, then this is sufficient. However, if you need to tie a generic parameter to the type, that’s possible too:
class SomeClass<U: SomeProtocol> {
let T: U.Type
init(T: U.Type) {
self.T = T
}
func go(value: U) {
T.foo()
}
}
SomeClass(T: Concrete.self).go(Concrete())
protocol P {
static func foo()
}
class A : P {
static func foo() {
print("A class")
}
}
class B : P {
static func foo() {
print("C class")
}
}
var type1 = A.self
var type2 = B.self
func check(cls: AnyClass)->Void {
switch cls {
case is A.Type:
A.foo()
case is B.Type:
B.foo()
default:
break
}
}
check(type1) // A class
check(type2) // C class
let i = Int.self // Int.Type
let ao = AnyObject.self // AnyObject.Protocol
let ac = AnyClass.self // AnyClass.Protocol
let p = P.self // P.Protocol
let f = check.self // (AnyClass)->Void
Edit
protocol P {
static func f()
}
class A : P {
static func f() {
print("A")
}
}
class B : P {
static func f() {
print("B")
}
}
func f(cls: P.Type) {
cls.f()
}
f(A) // A
f(B) // B
class Test<T: P> {
func foo() {
T.f()
}
}
Test<A>().foo() // A
Test<B>().foo() // B

Find/Remove Closure in Array

I'm developing first app using Swift.
in one of my Class I need to store closure in an Array. Like an event manager :
typealias eventCallback = (eventName:String, data:[MasterModel]?) -> Void;
class MHttpLoader: NSObject
{
var eventRegister : [String: [eventCallback]];
class var instance : MHttpLoader
{
struct Static {
static let instance : MHttpLoader = MHttpLoader(baseURL:NSURL(string:"http://192.168.0.11:3000"));
}
return Static.instance;
}
class func registerEvent(eventName:String, callback:eventCallback)
{
if var tab = MHttpLoader.instance.eventRegister[eventName]
{
tab.append(callback);
}
else
{
MHttpLoader.instance.eventRegister[eventName] = [callback];
}
}
func fireEvent(eventName: String, data:[MasterModel]?)
{
if let tab = self.eventRegister[eventName]
{
for callback in tab
{
callback(eventName:eventName, data:data);
}
}
}
}
All this code work pretty well, the problem is when i want to remove a closure from my array.
For exemple :
class func removeEvent(eventName:String, callback:eventCallback)
{
if var tab :Array = MHttpLoader.instance.eventRegister[eventName]
{
if let index = find(tab, callback) as? Int
{
tab.removeAtIndex(index);
}
}
}
I have the error which says that my closure is not conform to protocol "Equatable"
I also tried :
class func removeEvent(eventName:String, callback:eventCallback)
{
if var tab :Array = MHttpLoader.instance.eventRegister[eventName]
{
tab = tab.filter({ (currentElement) -> Bool in
return currentElement != callback;
});
}
}
But I have the error : Cannot invoke '!=' with an argument list of type '((eventCallback), eventCallback)'
Here is my question how can i find the index of closure in array or simply compare closure?
Thank you

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

Swift Passing Closure With Params

Currently I'm passing in a closure as a property on an object that accepts no params and has no return value as follows:
class MyClass {
var myClosureProperty: (() -> ())? {
didSet {
doSomeActionWhenClosureIsSet()
}
}
}
var instanceOfMyClass = MyClass()
instanceOfMyClass.myClosureProperty = {
// do some things here...
}
and so far this is working great. I want to be able to pass in a parameter when setting this closure to be used inside the instance of MyClass. I'm looking for SOMETHING like below, although the syntax I'm sure is incorrect:
class MyClass {
var myClosureProperty: ((newString: String) -> ())? {
didSet {
doSomeActionWhenClosureIsSet(newString)
}
}
func doSomeActionWhenClosureIsSet(stringParam: String) -> () {
// create a button with the stringParam title...
}
}
var instanceOfMyClass = MyClass()
instanceOfMyClass.myClosureProperty = {("Action")
exampleFunction()
}
How would I go about passing in a parameter to this closure that can be used inside of MyClass - i.e. a value that can be used inside the didSet portion of the property itself as in the second example?
EDIT: Here's what ended up working for me:
class MyClass {
var myClosurePropertyWithStringAndAction: (buttonName: String, closure: (() -> ()))? {
didSet {
let buttonTitle = myClosurePropertyWithStringAndAction!.buttonName
createButtonWithButtonTitle(buttonTitle)
}
}
func createButtonWithButtonTitle(buttonTitle: String) -> () {
// here I create a button with the buttonTitle as the title and set
// handleButtonPressed as the action on the button
}
func handleButtonPressed() {
self.myClosurePropertyWithStringAndAction?.closure()
}
}
}
And here is how I call it on the instance:
instaceOfMyClass.myClosurePropertyWithStringAndAction = ("Done", {
// do whatever I need to here
})
Since you are trying to set pass 2 things, a closure AND a button name, you won't be able to do that with a simple setter to the closure.
The constraint you have is that these 2 things are dependent on each other, so you must either set both or none.
First of all, adding newString to your closure isn't doing what you think it does. Its a parameter so you can pass a string to your closure when you call it, it isn't a way to pass in a string when you define the closure.
A "Swift way" to do what you want might be to define it as a tuple. You can name the values inside a tuple so it would work how you want it. Try it like this:
class MyClass {
var stringAndClosure: (buttonName: String,closure: (() -> ()))? {
didSet {
//create button named buttonName
let name = stringAndClosure!.buttonName
}
}
}
let instanceOfMyClass = MyClass()
instanceOfMyClass.stringAndClosure = ("Action",{ exampleFunction() })
You should use the in keyword to pass parameters in a closure
{ (someString: String) -> Bool in
//do something with someString
return true
}
Don't think is possible... You can do like this instead :
class MyClass{
var myClosureProperty: (() -> String)?
{
didSet
{
doSomeActionWhenClosureIsSet(myClosureProperty!())
}
}
func doSomeActionWhenClosureIsSet(stringParam: String) -> ()
{
println("it worked: " + stringParam) // hopefully this would print "it worked: SUCCESS"
}
}

Resources