代理维护代码和回调模式

时间:2016-04-14 19:49:54

标签: swift asynchronous delegates protocols

首先,我只是一个正在开发使用Swift语言的应用程序的初学者,所以请不要过多地考虑我的问题因为我真的需要知道并且我在维护代码时遇到问题我建造的。

关于异步委托模式。

这是我的API类。假设有很多API类,它们会产生异步调用。

protocol InitiateAPIProtocol{
     func didSuccessInitiate(results:JSON)
     func didFailInitiate(err:NSError)
}

class InitiateAPI{

    var delegate : InitiateAPIProtocol
    init(delegate: InitiateAPIProtocol){
         self.delegate=delegate
    }

    func post(wsdlURL:String,action:String,soapMessage : String){

         let request = NSMutableURLRequest(URL: NSURL(string: wsdlURL)!)
         let msgLength  = String(soapMessage.characters.count)
         let data = soapMessage.dataUsingEncoding(NSUTF8StringEncoding, allowLossyConversion: false)

         request.HTTPMethod = "POST"
         request.addValue("text/xml; charset=utf-8", forHTTPHeaderField: "Content-Type")
         request.addValue(msgLength, forHTTPHeaderField: "Content-Length")
         request.addValue(action, forHTTPHeaderField: "SOAPAction")
         request.HTTPBody = data

         let task = session.dataTaskWithRequest(request) {
              data, response, error in

            if error != nil {
                 self.delegate.didFailInitiate(error!)
                 return
            }

            let jsonData = JSON(data: data)
            self.delegate.didSuccessInitiate(jsonData)
        }
        task.resume()

}

func doInitiate(token : String){

    let soapMessage = “”
// WSDL_URL is the main wsdl url i will request.

action = “”

    post(WSDL_URL, action: action, soapMessage: soapMessage)
}
}

这是我的ViewController:

class ViewController : UIViewController,InitiateAPIProtocol{
    var initiateAPI : InitiateAPI!
    var token : String = “sWAFF1”

    override func viewWillAppear(animated: Bool) {
            // Async call start
            initiateAPI = InitiateAPI(delegate:self)
            initiateAPI.doInitiate(token)
    }

    // Here comes call back
    func didSuccessInitiate(results: JSON) {
       //handle results
    }

    func didFailInitiate(err: NSError) {
       //handle errors
    }
}

我的问题是我说有很多这样的API类,所以如果一个视图控制器处理4个API类,我必须在扩展视图控制器时处理许多协议委托方法。视图控制器下面将有许多委托方法。如果其他视图控制器调用相同的API并且必须处理相同的委托,我在维护代码时遇到问题,因为每次更改一些委托参数时,我都必须在使用这些API类的所有视图控制器上修复代码。

还有其他好方法可以处理异步调用吗?

如果我的问题看起来有点复杂,请发表评论,我会回复并清楚解释。

3 个答案:

答案 0 :(得分:3)

代表(OOP)和"完成处理程序" (像编程这样的功能)只是不太适合。

为了增加理解力并使代码更简洁,需要一种替代方法。 @PEEJWEEJ已经使用完全处理程序提出了这种方法之一。

另一种方法是使用"期货或承诺"。这些极大地扩展了完成处理程序的概念,使您的异步代码看起来更像 synchronous

期货工作基本如下。假设您有一个API函数,可以从远程Web服务中获取用户。这个调用是异步的。

// Given a user ID, fetch a user:
func fetchUser(id: Int) -> Future<User> {
    let promise = Promise<User>()
    // a) invoke the asynchronous operation.
    // b) when it finished, complete the promise accordingly:
    doFetchAsync(id, completion: {(user, error) in 
        if error == nil {
            promise.fulfill(user!)
        } else {
            promise.reject(error!)
        }
    })
    return.promise.future
}

首先,这里的重要事实是,没有完成处理程序。相反,异步函数会返回 future 。 future表示底层操作的最终结果。当函数fetchUser返回时,结果尚未计算,未来处于&#34; pending&#34;州。也就是说,您无法从将来获得立即的结果。那么,我们要等? - 不是真的,这将完成类似于具有完成处理程序的异步函数,即注册&#34;继续&#34;:

为了获得结果,您注册一个完成处理程序:

fetchUser(userId).map { user in
    print("User: \(user)")
}.onFailure { error in
    print("Error: \(error)")
}

如果发生错误,它也会处理错误。

函数map是注册延续的函数。它也是一个&#34;组合器&#34;,它返回了另一个未来,你可以将其与其他功能结合起来,组成更复杂的操作。

当未来终于完成时,代码继续,并在未来注册闭包。

如果您有两个依赖操作,请说OP1生成一个应该在OP2中用作输入的结果,并且应该返回组合结果(作为将来),您可以在全面而简洁的方式:

let imageFuture = fetchUser(userId).flatMap { user in
    return user.fetchProfileImage() 
}
imageFuture.onSuccess { image in
    // Update table view on main thread:
    ...
}

这只是一个非常简短的期货介绍。他们可以为你做更多的事情。

如果你想看到未来的行动,你可以启动Xcode游乐场&#34;激励示例&#34;在第三方图书馆FutureLib(我是作者)。您还应该检查其他Future / Promise库,例如BrightFutures。两个库都在Swift中实现Scala-like futures

答案 1 :(得分:1)

你有没有看过NSNotificationCenter?

https://developer.apple.com/library/mac/documentation/Cocoa/Reference/Foundation/Classes/NSNotificationCenter_Class/

您将能够从您的api类发布事件,然后每个视图控制器将订阅事件并相应地通知

这有意义吗?这种模式有很多很好的例子: https://stackoverflow.com/a/24049111/2678994
https://stackoverflow.com/a/28269217/2678994

我已在下面更新了您的代码:

class InitiateAPI{
    // 
    // var delegate : InitiateAPIProtocol
    // init(delegate: InitiateAPIProtocol){
    //      self.delegate=delegate
    // }

    func post(wsdlURL:String,action:String,soapMessage : String){

        let request = NSMutableURLRequest(URL: NSURL(string: wsdlURL)!)
        let msgLength  = String(soapMessage.characters.count)
        let data = soapMessage.dataUsingEncoding(NSUTF8StringEncoding, allowLossyConversion: false)

        request.HTTPMethod = "POST"
        request.addValue("text/xml; charset=utf-8", forHTTPHeaderField: "Content-Type")
        request.addValue(msgLength, forHTTPHeaderField: "Content-Length")
        request.addValue(action, forHTTPHeaderField: "SOAPAction")
        request.HTTPBody = data

        let task = session.dataTaskWithRequest(request) {
        data, response, error in

        if error != nil {
            //  self.delegate.didFailInitiate(error!)

            /* Post notification with error */
            NSNotificationCenter.defaultCenter().postNotificationName("onHttpError", object: error)
            return
        }

        let jsonData = JSON(data: data)
            // self.delegate.didSuccessInitiate(jsonData)

            /* Post notification with json body */
            NSNotificationCenter.defaultCenter().postNotificationName("onHttpSuccess", object: jsonData)
        }
        task.resume()

    }

    func doInitiate(token : String){

        let soapMessage = “”
        // WSDL_URL is the main wsdl url i will request.

        action = “”

        post(WSDL_URL, action: action, soapMessage: soapMessage)
    }
}

您的视图控制器类:

class ViewController : UIViewController { //,InitiateAPIProtocol{
    var initiateAPI : InitiateAPI!
    var token : String = “sWAFF1”

    override func viewDidLoad() {
        super.viewDidLoad()

        NSNotificationCenter.defaultCenter().addObserver(self, selector: #selector(ViewController.didSuccessInitiate(_:)), name: "onHttpSuccess", object: nil)
        NSNotificationCenter.defaultCenter().addObserver(self, selector: #selector(ViewController.didFailInitiate(_:)), name: "onHttpError", object: nil)
    }

    override func viewWillAppear(animated: Bool) {
        // Async call start
        initiateAPI = InitiateAPI(delegate:self)
        initiateAPI.doInitiate(token)
    }

    override func viewWillDisappear(animated: Bool) {
        super.viewWillDisappear(animated)

        /* Remove listeners when view controller disappears */
        NSNotificationCenter.defaultCenter().removeObserver(self, name: "onHttpSuccess", object: nil)
        NSNotificationCenter.defaultCenter().removeObserver(self, name: "onHttpError", object: nil)
    }

    // Here comes call back
    func didSuccessInitiate(notification : NSNotification) { //results: JSON) {

        if let payload = notification.object as? JSON {

            //handle results
        }
    }

    func didFailInitiate(notification : NSNotification) { //err: NSError) {

        if let payload = notification.object as? NSError {

            //handle errors
        }
    }
}

答案 2 :(得分:1)

您可以(应该?)使用闭包器/函数来代替使用委托:

func post(/*any other variables*/ successCompletion: (JSON) -> (), errorCompletion: (NSError) ->()){

    /* do whatever you need to*/

    /*if succeeds*/
    successCompletion("")

    /*if fails*/
    errorCompletion(error)
}

// example using closures
post({ (data) in
    /* handle Success*/
    }) { (error) in
        /* handle error */
}

// example using functions
post(handleData, errorCompletion: handleError)

func handleData(data: JSON) {

}

func handleError(error: NSError) {

}

这也可以让您选择使用一个函数处理所有错误。

此外,在返回JSON之前,最好将JSON解析为所需的对象。这样可以保持ViewControllers的清洁,并清楚地说明解析的位置。