【问题标题】:Swift http request use urlSessionSwift http 请求使用 urlSession
【发布时间】:2026-02-05 21:35:01
【问题描述】:

我想为我的服务器编写 HTTP 请求的 func 并获取一些数据,当我打印它时 (print(responseString)) 它看起来不错,但是当我尝试返回数据时,它总是为空

public func HTTPRequest(dir: String, param: [String:String]?) -> String{
    var urlString = HOST + dir + "?"
    var responseString = ""
    if param != nil{
        for currentParam in param!{
            urlString += currentParam.key + "=" + currentParam.value + "&"
        }
    }

    let url = URL(string: urlString)

    let task = URLSession.shared.dataTask(with: url!) { data, response, error in
        guard error == nil else {
            print("ERROR: HTTP REQUEST ERROR!")
            return
        }
        guard let data = data else {
            print("ERROR: Empty data!")
            return
        }
        responseString = NSString(data: data,encoding: String.Encoding.utf8.rawValue) as! String
        print(responseString)
    }
    task.resume()
    return responseString
}

【问题讨论】:

  • 你需要使用完成块而不是返回值

标签: swift http request urlsession


【解决方案1】:

正如 Rob 的 cmets 中提到的,dataTask 闭包是异步运行的。与其立即返回值,不如提供一个完成闭包,然后在 dataTask 完成时调用它。

这是一个示例(用于测试,可以按原样粘贴到 Xcode Playground):

import Foundation
import PlaygroundSupport
PlaygroundPage.current.needsIndefiniteExecution = true

let HOST = "http://example.org"

public func HTTPRequest(dir: String, param: [String: String]?,  completion: @escaping (String) -> Void) {

    var urlString = HOST + dir + "?"

    if param != nil{
        for currentParam in param! {
            urlString += currentParam.key + "=" + currentParam.value + "&"
        }
    }

    let url = URL(string: urlString)

    let task = URLSession.shared.dataTask(with: url!) { data, response, error in
        guard error == nil else {
            print("ERROR: HTTP REQUEST ERROR!")
            return
        }
        guard let data = data else {
            print("ERROR: Empty data!")
            return
        }
        let responseString = NSString(data: data,encoding: String.Encoding.utf8.rawValue) as! String

        completion(responseString)

    }

    task.resume()

}

let completion: (String) -> Void = { responseString in

    print(responseString)

}

HTTPRequest(dir: "", param: nil, completion: completion)

【讨论】:

    【解决方案2】:

    您需要使用完成块而不是返回值,因为 dataTask 闭包是异步运行的,即稍后,在您从方法返回之后。您不想尝试立即返回该值(因为您还没有它)。您希望 (a) 将此函数更改为不返回任何内容,但 (b) 提供一个完成处理程序闭包,您将在 dataTask 闭包中调用该闭包,并在其中构建 responseString。

    例如,您可以这样定义它:

    public func HTTPRequest(dir: String, param: [String:String]? = nil, completionHandler: @escaping (String?, Error?) -> Void) {
        var urlString = HOST + dir
    
        if let param = param {
            let parameters = param.map { return $0.key.percentEscaped() + "=" + $0.value.percentEscaped() }
            urlString += "?" + parameters.joined(separator: "&")
        }
    
        let url = URL(string: urlString)
    
        let task = URLSession.shared.dataTask(with: url!) { data, response, error in
            guard let data = data, error == nil else {
                completionHandler(nil, error)
                return
            }
            let responseString = String(data: data, encoding: .utf8)
            completionHandler(responseString, nil)
        }
        task.resume()
    }
    

    请注意,我正在使用类似以下方法对参数字典中的值进行转义:

    extension String {
    
        /// Percent escapes values to be added to a URL query as specified in RFC 3986
        ///
        /// This percent-escapes all characters besides the alphanumeric character set and "-", ".", "_", and "~".
        ///
        /// http://www.ietf.org/rfc/rfc3986.txt
        ///
        /// - Returns: Returns percent-escaped string.
    
        func percentEscaped() -> String {
            let allowedCharacters = CharacterSet(charactersIn: "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789-._~")
    
            return self.addingPercentEncoding(withAllowedCharacters: allowedCharacters)!
        }
    
    }
    

    然后你会这样称呼它:

    HTTPRequest(dir: directory, param: parameterDictionary) { responseString, error in
        guard let responseString = responseString else {
            // handle the error here
            print("error: \(error)")
            return
        }
    
        // use `responseString` here
    
        DispatchQueue.main.async {
            // because this is called on background thread, if updating
            // UI, make sure to dispatch that back to the main queue.
        }
    }
    
    // but don't try to use `responseString` here
    

    【讨论】: