Swift 3 - 为HTTP调用创建一个可重用的类

时间:2016-12-29 09:22:12

标签: swift http

虽然在.net

中有经验,但这里有一个Swift noob

我正在尝试创建我的第一个应用程序,并使代码尽可能模块化和可重用。因此,我尝试创建一个类,允许我发出简单的HTTP GET请求并返回结果文本,如果需要,还可以选择返回JSON。我在下面粘贴了我的代码。我得到的问题是我的函数在HTTP调用完成之前返回,因此返回一个空对象。我想我知道为什么会这样,因为我在正在执行的任务上调用resume并等待调用回调函数 - 但同时代码执行仍在继续,因此我返回一个空对象。那么......这里推荐的方法是什么,拜托?我应该将回调放回到我的原始视图控制器上的函数,而不是尝试以这种方式创建可重用的类,或其他东西!我张开双臂等待任何建议;)

P.S。这些代码中的一些可能看起来很熟悉,因为很多内容来自我在线阅读的其他文章/帖子,然后根据我的需要量身定制。

class WebController {

    func makeGetCall(strURL:String, returnJSON:Bool) -> WebControllerResponse {

        //declare a var to store our return
        let wcr:WebControllerResponse = WebControllerResponse()

        // Set up the URL request
        guard let url = URL(string: strURL) else {
            wcr.successful = false
            wcr.returnData = "Error: cannot create URL"
            return wcr
        }

        let urlRequest = URLRequest(url: url)

        // set up the session
        let config = URLSessionConfiguration.default
        let session = URLSession(configuration: config)

        // make the request

        session.dataTask(with: urlRequest, completionHandler: {
            (data, response, error) in
            // check for any errors
            guard error == nil else {
                wcr.successful = false
                wcr.returnData = "error calling GET - " + String(describing: error)
                return
            }
            // make sure we got data
            guard let responseData = data else {
                wcr.successful = false
                wcr.returnData = "Error: did not receive data"
                return
            }

            if (returnJSON == true) {
            // parse the result as JSON, since that's what the API provides
            do {
                guard let retObj = try JSONSerialization.jsonObject(with: responseData, options: []) as? [String: AnyObject] else {
                        wcr.successful = false
                        wcr.returnData = "error trying to convert data to JSON"
                        return
                    }
                wcr.returnJSON = retObj
                wcr.returnData = String(describing: responseData)
                wcr.successful = true
                }
            catch  {
                wcr.successful = false
                wcr.returnData = "error trying to convert data to JSON"
                return
                }
            }
            else {
                // just return raw data
                wcr.returnData = String(describing: responseData)
                wcr.successful = true
                return
            }
        }).resume()

            return wcr
    }
}

我的回复对象非常简单:

class WebControllerResponse {

    var successful:Bool = false
    var returnData:String = ""
    var returnJSON:[String:AnyObject]?

    init() {

    }

}

以下是根据@dirtydanee的反馈更新的代码:错误:

类CalTest:UIViewController,WebControllerDelegate {

@IBOutlet weak var bigText: UITextView!
@IBOutlet var urlIn: UITextField!
@IBOutlet weak var testLabel: UILabel!

@IBAction func getButton(_ sender: Any) {

    let wc:WebController = WebController()
    wc.delegate = self
    wc.makeGetCall(strURL: urlIn.text!, returnJSON:false)

}


func webController(webController: WebController, didReceiveResponse: WebControllerResponse) {
    bigText.text = didReceiveResponse.returnData
}

}

2 个答案:

答案 0 :(得分:1)

URLSession dataTask是一个异步API,因此您需要从完成块返回结果,而不是之后。

当你调用dataTask完成处理程序时,你需要从WebController创建一个委托来通知观察类。

// Create the protocol
protocol WebControllerDelegate: class {
    func webController(webController: WebController, didReceiveResponse: WebControllerResponse)
}

class WebController {

    // Add delegate variable to class
    weak var delegate: WebControllerDelegate?

    // Initaite the call, but do not return anything from the function directly
    func makeGetCall(strURL:String, returnJSON:Bool) {

        //declare a var to store our return
        let wcr:WebControllerResponse = WebControllerResponse()

        // Set up the URL request
        guard let url = URL(string: strURL) else {
            wcr.successful = false
            wcr.returnData = "Error: cannot create URL"
            self.delegate?.webController(webController: self, didReceiveResponse: wcr)
            return 
        }

        let urlRequest = URLRequest(url: url)

        // set up the session
        let config = URLSessionConfiguration.default
        let session = URLSession(configuration: config)

        // make the request

        session.dataTask(with: urlRequest, completionHandler: {
            (data, response, error) in
            // check for any errors
            guard error == nil else {
                wcr.successful = false
                wcr.returnData = "error calling GET - " + String(describing: error)
                // Add the delegate call to the right places
                self.delegate?.webController(webController: self, didReceiveResponse: wcr)
                return
            }
            // make sure we got data
            guard let responseData = data else {
                wcr.successful = false
                wcr.returnData = "Error: did not receive data"
                return
            }

            if (returnJSON == true) {
                // parse the result as JSON, since that's what the API provides
                do {
                    guard let retObj = try JSONSerialization.jsonObject(with: responseData, options: []) as? [String: AnyObject] else {
                        wcr.successful = false
                        wcr.returnData = "error trying to convert data to JSON"
                        self.delegate?.webController(webController: self, didReceiveResponse: wcr)
                        return
                    }
                    wcr.returnJSON = retObj
                    wcr.returnData = String(describing: responseData)
                    wcr.successful = true
                    self.delegate?.webController(webController: self, didReceiveResponse: wcr)
                }
                catch  {
                    wcr.successful = false
                    wcr.returnData = "error trying to convert data to JSON"
                    self.delegate?.webController(webController: self, didReceiveResponse: wcr)
                    return
                }
            }
            else {
                // just return raw data
                wcr.returnData = String(describing: responseData)
                wcr.successful = true
                self.delegate?.webController(webController: self, didReceiveResponse: wcr)
                return
            }
        }).resume()
    }
}

<强>用法:

class Foo: WebControllerDelegate {
   let webController = WebController() 

    func testWebController() {
        // Assign delegate to self
        webController.delegate = self
        webController.makeGetCall(strURL: "whatever your URL should be", returnJSON: true)
    }

    // Declare the delegate
    func webController(webController: WebController, didReceiveResponse: WebControllerResponse) {

//转到主UI线程    DispatchQueue.main.sync {             将文本分配给UITextView             bigText.text = didReceiveResponse.returnData   }         }     }

答案 1 :(得分:1)

因为HTTP请求是异步发生的,所以函数将在完成之前返回。

解决此问题的最简单方法是在函数上使用完成块:

func makeGetCall(strURL:String, returnJSON:Bool completion: @escaping (WebControllerResponse?) -> Void) {
    // ...
}

然后,当HTTP请求完成后,您可以调用完成处理程序

completion(webControllerResponse)