example datatask swift nsurlsession

datatask - urlsession swift 4 example



¿Cómo puedo obtener los datos de NSURLSession.sharedSession(). DataTaskWithRequest (2)

class PostFOrData { let url = NSURL( string: "http://210.61.209.194:8088/SmarttvWebServiceTopmsoApi/GetReadlist") var picUrl = NSURL(string : "http://210.61.209.194:8088/SmarttvMedia/img/epi00001.png") var responseString : NSString = "" func forData() -> NSString { let request = NSMutableURLRequest( URL: url!) request.HTTPMethod = "POST" var s : NSString = "" let postString : String = "uid=59" request.HTTPBody = postString.dataUsingEncoding(NSUTF8StringEncoding) let task = NSURLSession.sharedSession().dataTaskWithRequest(request) { data, response, error in if error != nil { println("error=/(error)") return } else { println("response = /(response!)") self.responseString = NSString(data: data, encoding: NSUTF8StringEncoding)! println("responseString = /(self.responseString)") } } // I want to return NSString here, but I always get nothing return self.responseString } }

¿Alguien sabe cómo obtener los datos de la tarea?


Hay algunos requisitos muy genéricos que quisieran que todo buen administrador de API satisfaga: implementará un cliente API orientado a protocolos.

Interfaz inicial APIClient

protocol APIClient { func send(_ request: APIRequest, completion: @escaping (APIResponse?, Error?) -> Void) } protocol APIRequest: Encodable { var resourceName: String { get } } protocol APIResponse: Decodable { }

Ahora compruebe la estructura completa de la API

// ******* This is API Call Class ***** public typealias ResultCallback<Value> = (Result<Value, Error>) -> Void /// Implementation of a generic-based API client public class APIClient { private let baseEndpointUrl = URL(string: "irl")! private let session = URLSession(configuration: .default) public init() { } /// Sends a request to servers, calling the completion method when finished public func send<T: APIRequest>(_ request: T, completion: @escaping ResultCallback<DataContainer<T.Response>>) { let endpoint = self.endpoint(for: request) let task = session.dataTask(with: URLRequest(url: endpoint)) { data, response, error in if let data = data { do { // Decode the top level response, and look up the decoded response to see // if it''s a success or a failure let apiResponse = try JSONDecoder().decode(APIResponse<T.Response>.self, from: data) if let dataContainer = apiResponse.data { completion(.success(dataContainer)) } else if let message = apiResponse.message { completion(.failure(APIError.server(message: message))) } else { completion(.failure(APIError.decoding)) } } catch { completion(.failure(error)) } } else if let error = error { completion(.failure(error)) } } task.resume() } /// Encodes a URL based on the given request /// Everything needed for a public request to api servers is encoded directly in this URL private func endpoint<T: APIRequest>(for request: T) -> URL { guard let baseUrl = URL(string: request.resourceName, relativeTo: baseEndpointUrl) else { fatalError("Bad resourceName: /(request.resourceName)") } var components = URLComponents(url: baseUrl, resolvingAgainstBaseURL: true)! // Common query items needed for all api requests let timestamp = "/(Date().timeIntervalSince1970)" let hash = "/(timestamp)" let commonQueryItems = [ URLQueryItem(name: "ts", value: timestamp), URLQueryItem(name: "hash", value: hash), URLQueryItem(name: "apikey", value: "") ] // Custom query items needed for this specific request let customQueryItems: [URLQueryItem] do { customQueryItems = try URLQueryItemEncoder.encode(request) } catch { fatalError("Wrong parameters: /(error)") } components.queryItems = commonQueryItems + customQueryItems // Construct the final URL with all the previous data return components.url! } } // ****** API Request Encodable Protocol ***** public protocol APIRequest: Encodable { /// Response (will be wrapped with a DataContainer) associatedtype Response: Decodable /// Endpoint for this request (the last part of the URL) var resourceName: String { get } } // ****** This Results type Data Container Struct ****** public struct DataContainer<Results: Decodable>: Decodable { public let offset: Int public let limit: Int public let total: Int public let count: Int public let results: Results } // ***** API Errro Enum **** public enum APIError: Error { case encoding case decoding case server(message: String) } // ****** API Response Struct ****** public struct APIResponse<Response: Decodable>: Decodable { /// Whether it was ok or not public let status: String? /// Message that usually gives more information about some error public let message: String? /// Requested data public let data: DataContainer<Response>? } // ***** URL Query Encoder OR JSON Encoder ***** enum URLQueryItemEncoder { static func encode<T: Encodable>(_ encodable: T) throws -> [URLQueryItem] { let parametersData = try JSONEncoder().encode(encodable) let parameters = try JSONDecoder().decode([String: HTTPParam].self, from: parametersData) return parameters.map { URLQueryItem(name: $0, value: $1.description) } } } // ****** HTTP Pamater Conversion Enum ***** enum HTTPParam: CustomStringConvertible, Decodable { case string(String) case bool(Bool) case int(Int) case double(Double) init(from decoder: Decoder) throws { let container = try decoder.singleValueContainer() if let string = try? container.decode(String.self) { self = .string(string) } else if let bool = try? container.decode(Bool.self) { self = .bool(bool) } else if let int = try? container.decode(Int.self) { self = .int(int) } else if let double = try? container.decode(Double.self) { self = .double(double) } else { throw APIError.decoding } } var description: String { switch self { case .string(let string): return string case .bool(let bool): return String(describing: bool) case .int(let int): return String(describing: int) case .double(let double): return String(describing: double) } } } /// **** This is your API Request Endpoint Method in Struct ***** public struct GetCharacters: APIRequest { public typealias Response = [MyCharacter] public var resourceName: String { return "characters" } // Parameters public let name: String? public let nameStartsWith: String? public let limit: Int? public let offset: Int? // Note that nil parameters will not be used public init(name: String? = nil, nameStartsWith: String? = nil, limit: Int? = nil, offset: Int? = nil) { self.name = name self.nameStartsWith = nameStartsWith self.limit = limit self.offset = offset } } // *** This is Model for Above Api endpoint method **** public struct MyCharacter: Decodable { public let id: Int public let name: String? public let description: String? } // ***** These below line you used to call any api call in your controller or view model **** func viewDidLoad() { let apiClient = APIClient() // A simple request with no parameters apiClient.send(GetCharacters()) { response in response.map { dataContainer in print(dataContainer.results) } } }


No puede devolver datos directamente desde una tarea asincrónica.

La solución con Swift 2 es hacer un controlador de finalización como este:

class PostFOrData { // the completion closure signature is (NSString) -> () func forData(completion: (NSString) -> ()) { if let url = NSURL(string: "http://210.61.209.194:8088/SmarttvWebServiceTopmsoApi/GetReadlist") { let request = NSMutableURLRequest( URL: url) request.HTTPMethod = "POST" let postString : String = "uid=59" request.HTTPBody = postString.dataUsingEncoding(NSUTF8StringEncoding) let task = NSURLSession.sharedSession().dataTaskWithRequest(request) { data, response, error in if let data = data, jsonString = NSString(data: data, encoding: NSUTF8StringEncoding) where error == nil { completion(jsonString) } else { print("error=/(error!.localizedDescription)") } } task.resume() } } } let pfd = PostFOrData() // you call the method with a trailing closure pfd.forData { jsonString in // and here you get the "returned" value from the asynchronous task print(jsonString) }

De esa forma, la finalización solo se llama cuando se completa la tarea asincrónica. Es una manera de "devolver" los datos sin usar realmente return .

Versión Swift 3

class PostFOrData { // the completion closure signature is (String) -> () func forData(completion: @escaping (String) -> ()) { if let url = URL(string: "http://210.61.209.194:8088/SmarttvWebServiceTopmsoApi/GetReadlist") { var request = URLRequest(url: url) request.httpMethod = "POST" let postString : String = "uid=59" request.httpBody = postString.data(using: String.Encoding.utf8) let task = URLSession.shared.dataTask(with: request) { data, response, error in if let data = data, let jsonString = String(data: data, encoding: String.Encoding.utf8), error == nil { completion(jsonString) } else { print("error=/(error!.localizedDescription)") } } task.resume() } } } let pfd = PostFOrData() // you call the method with a trailing closure pfd.forData { jsonString in // and here you get the "returned" value from the asynchronous task print(jsonString) }