我推荐使用NSOperation
和NSOperationQueue
来保持它的清洁。
阅读&观看更多:
这是一个基本的设置,你可以自定义,以满足您的需求
免责声明:虽然它看起来很多,但它弥补了更好的API。
首先,让我们定义一个接口来处理我们的API端点:
// Endpoints.swift
let api_base = "https://myserver.com/"
let api_path = "api/"
protocol EndpointGenerator {
func URL() -> NSURL
}
extension EndpointGenerator {
func URL() -> NSURL {
return NSURL(string: api_base)!
}
}
// Represents a null endpoint. It will fail.
struct NullEndpoint: EndpointGenerator { }
enum Endpoint: String, EndpointGenerator {
case Login = "login"
case SignUp = "signup"
func URL() -> NSURL {
return NSURL(string: api_base + api_path + self.rawValue)!
}
}
接下来,让我们来构建我们的定制NSOperation
:
// Operation.swift
public class Operation: NSOperation {
public typealias Completion = Operation ->()
public typealias Error = NSError ->()
var endpoint: EndpointGenerator {
return NullEndpoint()
}
var headerParams: [String:String]? {
return nil
}
var requestBody: [String:AnyObject]? {
return nil
}
var method: HTTPMethod {
return .GET
}
var networkTask: NSURLSessionTask?
var completion: Completion?
var error: Error?
public var parsedObject = [String:AnyObject]()
override public init() { }
public init(completion: Completion, error: Error) {
self.completion = completion
self.error = error
}
override public func start() {
NSURLSessionImplementaion.execute(self)
}
override public func cancel() {
networkTask?.cancel()
networkTask = nil
}
}
是几乎完成,让我们来处理实际的队列:
// OperationQueue.swift
public class OperationQueue: NSOperationQueue {
public static let internalQueue = OperationQueue()
public static func addOperation(operation: NSOperation) {
internalQueue.addOperation(operation)
}
public static func addOperations(operations: NSOperation...) {
for operation in operations {
addOperation(operation)
}
}
public static func cancellAllOperations() {
internalQueue.cancelAllOperations()
}
}
最后,下载部分:
// NSURLSessionImplementation.swift
enum HTTPMethod: String {
case POST = "POST"
case GET = "GET"
case PATCH = "PATCH"
}
public let OSNetworkingErrorDomain = "com.swanros.errordomain"
class NSURLSessionImplementaion {
class func execute(operation: Operation) {
let session = NSURLSession(configuration: NSURLSessionConfiguration.defaultSessionConfiguration())
let request = NSMutableURLRequest(URL: operation.endpoint.URL())
if let headerParams = operation.headerParams {
for element in headerParams {
request.setValue(element.1, forHTTPHeaderField: element.0)
}
}
if let body = operation.requestBody {
do {
request.HTTPBody = try NSJSONSerialization.dataWithJSONObject(body, options: .PrettyPrinted)
} catch {
return
}
}
request.HTTPMethod = operation.method.rawValue
let task = session.dataTaskWithRequest(request) { data, response, error in
if let e = error {
operation.error?(e)
return
}
guard let d = data else {
operation.error?(errorWithDescription("No data"))
return
}
do {
let json = try NSJSONSerialization.JSONObjectWithData(d, options: .MutableLeaves) as? [String:AnyObject]
guard let j = json else {
operation.error?(errorWithDescription("Error parsing JSON."))
return
}
if let errorMessage = string(j, key: "error") {
operation.error?(errorWithDescription(errorMessage))
return
}
operation.parsedObject = j
operation.completion?(operation)
} catch let jsonError as NSError {
operation.error?(jsonError)
}
}
operation.networkTask = task
task.resume()
}
}
func errorWithDescription(desc: String) -> NSError {
return NSError(domain: OSNetworkingErrorDomain, code: 0, userInfo: [NSLocalizedDescriptionKey:desc])
}
你是如何实现这个的?假设你想要点击/login
端点。子类Operation
如下:
// LogInOperation.swift
public class LogInOperation: Operation {
override var endpoint: EndpointGenerator {
// A nice way to represent endpoints: use enums and protocols!
return Endpoint.Login
}
// The headers for this particular request. Maybe you need a token here!
override var headerParams: [String:String]? {
return [
"Content-Type": "application/json",
"Application-Id": "bAAvLosWNeSTHrlYilysdeEYoJHUXs88"
]
}
// The HTTP request body!
override var requestBody: [String:AnyObject]? {
return [
"mail": mail,
"password": password
]
}
// .GET is default
override var method: HTTPMethod {
return .POST
}
private var mail: String
private var password: String
public init(mail m: String, password p: String, completion: Completion, error: Error) {
mail = m
password = p
super.init(completion: completion, error: error)
}
}
并且你使用这样的:
// ViewController.swift
let loginOperation = LogInOperation(
mail: "[email protected]",
password: "123123",
completion: { op in
// parsedObject would be the user's info
print(op.parsedObject?)
}, error: { error in
print(error.localizedDescription)
}
)
OperationQueue.addOperation(loginOperation)
+1提'NSURLSessionDownloadTask'类。虽然与OP所尝试的策略不同,但它几乎肯定是更好的解决方案。 –