Swift: delegates / events coming from c # background

I have a Business Logic class that returns an object after a simple web service call. It gets called when a button is clicked (in my controller). This call for my Business Logic class might take a few seconds and I don't want to block the main thread.

How do I raise an event in my Business Logic class that my ViewController can connect to so I know it's time to process and display the object?

+3


source to share


3 answers


Different platforms do things differently. If you think

How do I create an event in my BL class that my ViewController can subscribe to ...?

you are going to struggle with frameworks and have both difficult time and overly complex code to do so.

Instead, you should be asking for more about strings

How do I know when an asynchronous operation has completed?

The answer to this question for Cocoa is that you can use a completion block / closure that is called when the operation completes, or a delegate callback with a method that is called by the delegate when the operation is completed. (There are also more alternatives, but these are the two most common).

A useful reading on when to use the various communication mechanisms can be found in this objc.io article

Shutters

It is a common construct in Cocoa to close as the last argument of an asynchronous operation, which is called when the operation completes and passes data as arguments to the closure. If the operation may fail, there is also often a second argument for an additional error (and also makes the data successful)

For example, below (for clarity, I've defined a type alias for closure):



typealias FooCompletion = (data: FooData?, error: NSError?) -> ()

func doSomething(completion: FooCompletion) {
    // do asyncrounous work
    let successful: Bool = // based on the operation success

    if successful {
        completion(data: someValidData, error: nil)
    } else {
        let error = NSError() // fill with information about the error
        completion(data: nil, error: error)
    }
}

      

You can use a method like this (here with a closable lid):

yourBusinessLogic.doSomething { data, error in // FooData?, NSError?
    if let validData = data {
        // it completed (and validData now holds the data from the operation)
    } else {
        // there was an error that should be handled
    }
}

      

Delegates

Another alternative, as Cocoa has done for many years, is to define a delegate protocol and have a property for an optional delegate in the class doing the asynchronous task. If an operation can succeed or fail, it usually makes sense to delegate methods for both:

protocol BusinessLogicDelegate {
    func somethingDidFinish(data: FooData)
    func somethingDidFail(error: NSError)
}

class BusinessLogic {
    var delegate: BusinessLogicDelegate? // optional delegate
    // ...
}

      

Then, when the optional task completes, one of the callbacks is sent to the delegate (if any)

func doSomething() {
    // do asyncrounous work
    let successful: Bool = // based on the operation success

    if successful {
        delegate?.somethingDidFinish(someValidData)
    } else {
        let error = NSError() // fill with information about the error
        delegate?.somethingDidFail(error)
    }
}

      

In this case, you can assign the delegate to the business logic instance, call the asynchronous method, and wait for one of the callbacks:

yourBusinessLogic.delegate = yourDelegate // doesn't have to be set every time
yourBusinessLogic.doSomething()

// later on, one of the callbacks is called on the delegate

      

+5


source


In this particular situation, you probably want to close:

class BusinessClass {
    func someExpensiveOperationWithCompletion(completion: (someArg: AnyObject) -> ()) {
        // Do expensive stuff in background
        completion(someArg: "Pass back info here")
    }
}

      



Then call it like this:

let business = BusinessClass()
business.someExpensiveOperationWithCompletion { (someArg) -> () in
    println("Finished w/ \(someArg)")
}

      

+1


source


You can achieve the desired behavior by accepting a callback function as a method parameter in your Business Logic class. If you need more instruction on how to execute asynchronous code in your class, I recommend that you check out the Grand Central Dispatch documentation .

An example business logic class might look like this:

class BusinessLogic {

    func doSomething(callback : () -> Void) {

        // long running operation

        callback() // call when done with operation
    }
}

      

You would call this from your view controller passing a function to the doSomething method:

let bl = BusinessLogic()
bl.doSomething({
   // execute callback statements 
})

      

-1


source







All Articles