Below is code making a request,the completionHandlerForGET was passed to completionHandlerForConvertData handler of function convertDataWithCompletionHandler.But the completionHandlerForConvertData seems doing nothing.Why use it?
func taskForGETMethod(_ method: String, parameters: [String:AnyObject], completionHandlerForGET: #escaping (_ result: AnyObject?, _ error: NSError?) -> Void) -> URLSessionDataTask {
/* 1. Set the parameters */
var parametersWithApiKey = parameters
parametersWithApiKey[ParameterKeys.ApiKey] = Constants.ApiKey as AnyObject?
/* 2/3. Build the URL, Configure the request */
let request = NSMutableURLRequest(url: tmdbURLFromParameters(parametersWithApiKey, withPathExtension: method))
/* 4. Make the request */
let task = session.dataTask(with: request as URLRequest) { (data, response, error) in
func sendError(_ error: String) {
print(error)
let userInfo = [NSLocalizedDescriptionKey : error]
completionHandlerForGET(nil, NSError(domain: "taskForGETMethod", code: 1, userInfo: userInfo))
}
/* GUARD: Was there an error? */
guard (error == nil) else {
sendError("There was an error with your request: \(error!)")
return
}
/* GUARD: Did we get a successful 2XX response? */
guard let statusCode = (response as? HTTPURLResponse)?.statusCode, statusCode >= 200 && statusCode <= 299 else {
sendError("Your request returned a status code other than 2xx!")
return
}
/* GUARD: Was there any data returned? */
guard let data = data else {
sendError("No data was returned by the request!")
return
}
/* 5/6. Parse the data and use the data (happens in completion handler) */
self.convertDataWithCompletionHandler(data, completionHandlerForConvertData: completionHandlerForGET)
}
/* 7. Start the request */
task.resume()
return task
}
The completionHandlerForConvertData isn't doing anything such as printing an error.
private func convertDataWithCompletionHandler(_ data: Data, completionHandlerForConvertData: (_ result: AnyObject?, _ error: NSError?) -> Void) {
var parsedResult: AnyObject! = nil
do {
//class func jsonObject(with data: Data, options opt: JSONSerialization.ReadingOptions = []) throws -> Any
//Returns a Foundation object from given JSON data.
parsedResult = try JSONSerialization.jsonObject(with: data, options: .allowFragments) as AnyObject
} catch {
let userInfo = [NSLocalizedDescriptionKey : "Could not parse the data as JSON: '\(data)'"]
completionHandlerForConvertData(nil, NSError(domain: "convertDataWithCompletionHandler", code: 1, userInfo: userInfo))
}
completionHandlerForConvertData(parsedResult, nil)
}
Related
I am doing migration after 2 years a lots things have been changed, now flagging a lots of error while building. Most are related to Alamofire 5. Now there are many error keeps coming fixing one by one.
Error: // ERROR: Cannot specialize non-generic type
public static func ObjMappingSerializer<T: Mappable>(_ keyPath: String?) -> DataResponseSerializer<T> { 'DataResponseSerializer'
return DataResponseSerializer { request, response, data, error in
//LogResponse(response, data: data, error: error)
Logger._reqresLogger.logResponse(response, data: data, error: error)
guard error == nil else {
return .failure(parseErrorResponse(data: data, response: response, errorType: error!))
}
guard let _ = data else {
return .failure(errorForNilData())
}
let JSONToMap = deserializeJSON(request: request, response: response, data: data, error: error, keyPath: keyPath)
if let json = JSONToMap as? [String:Any], let parsedObject = Mapper<T>().map(JSON:json) {
return .success(parsedObject)
}
let errorCode = response?.statusCode ?? NSURLErrorCannotParseResponse
return .failure(APIError(code: errorCode, errorUserInfo: nil))
}
}
Fixed by autosuggestion however next error comes
Error: Trailing closure passed to parameter of type 'DataPreprocessor' that does not accept a closure
public static func ObjMappingSerializer(_ keyPath: String?) -> DataResponseSerializer {
return DataResponseSerializer { request, response, data, error in
//LogResponse(response, data: data, error: error)
Logger._reqresLogger.logResponse(response, data: data, error: error)
guard error == nil else {
return .failure(parseErrorResponse(data: data, response: response, errorType: error!))
}
guard let _ = data else {
return .failure(errorForNilData())
}
let JSONToMap = deserializeJSON(request: request, response: response, data: data, error: error, keyPath: keyPath)
if let json = JSONToMap as? [String:Any], let parsedObject = Mapper<T>().map(JSON:json) {
return .success(parsedObject)
}
let errorCode = response?.statusCode ?? NSURLErrorCannotParseResponse
return .failure(APIError(code: errorCode, errorUserInfo: nil))
}
}
Now in Alamofire many methods have been removed in Alamofire 5. How can I fix these errors?
You can no longer initialize a DataResponseSerializer with a closure. I suggest you reevaluate your parsing needs and rebuild around responseDecodable. If you need, you can create your own serializer by adopting ResponseSerializer. Your logic would be the same, just copied into the parse method.
Below is code making a request, the completionHandlerForGET was passed to completionHandlerForConvertData handler of function convertDataWithCompletionHandler. But the completionHandlerForConvertData seems doing nothing like printing an error.
Why use it?
func taskForGETMethod(_ method: String, parameters: [String:AnyObject], completionHandlerForGET: #escaping (_ result: AnyObject?, _ error: NSError?) -> Void) -> URLSessionDataTask {
/* 1. Set the parameters */
var parametersWithApiKey = parameters
parametersWithApiKey[ParameterKeys.ApiKey] = Constants.ApiKey as AnyObject?
/* 2/3. Build the URL, Configure the request */
let request = NSMutableURLRequest(url: tmdbURLFromParameters(parametersWithApiKey, withPathExtension: method))
/* 4. Make the request */
let task = session.dataTask(with: request as URLRequest) { (data, response, error) in
func sendError(_ error: String) {
print(error)
let userInfo = [NSLocalizedDescriptionKey : error]
completionHandlerForGET(nil, NSError(domain: "taskForGETMethod", code: 1, userInfo: userInfo))
}
/* GUARD: Was there an error? */
guard (error == nil) else {
sendError("There was an error with your request: \(error!)")
return
}
/* GUARD: Did we get a successful 2XX response? */
guard let statusCode = (response as? HTTPURLResponse)?.statusCode, statusCode >= 200 && statusCode <= 299 else {
sendError("Your request returned a status code other than 2xx!")
return
}
/* GUARD: Was there any data returned? */
guard let data = data else {
sendError("No data was returned by the request!")
return
}
/* 5/6. Parse the data and use the data (happens in completion handler) */
self.convertDataWithCompletionHandler(data, completionHandlerForConvertData: completionHandlerForGET)
}
/* 7. Start the request */
task.resume()
return task
}
The completionHandlerForConvertData isn't doing anything such as printing an error.
private func convertDataWithCompletionHandler(_ data: Data, completionHandlerForConvertData: (_ result: AnyObject?, _ error: NSError?) -> Void) {
var parsedResult: AnyObject! = nil
do {
//class func jsonObject(with data: Data, options opt: JSONSerialization.ReadingOptions = []) throws -> Any
//Returns a Foundation object from given JSON data.
parsedResult = try JSONSerialization.jsonObject(with: data, options: .allowFragments) as AnyObject
} catch {
let userInfo = [NSLocalizedDescriptionKey : "Could not parse the data as JSON: '\(data)'"]
completionHandlerForConvertData(nil, NSError(domain: "convertDataWithCompletionHandler", code: 1, userInfo: userInfo))
}
completionHandlerForConvertData(parsedResult, nil)
}
This question already has answers here:
Run code only after asynchronous function finishes executing
(2 answers)
Closed 5 years ago.
I'm not very familiar with closure. I'm using this function to download a JSON file from a remote server
requestJson(){
// Asynchronous Http call to your api url, using NSURLSession:
NSURLSession.sharedSession().dataTaskWithURL(NSURL(string: "http://api.site.com/json")!, completionHandler: { (data, response, error) -> Void in
// Check if data was received successfully
if error == nil && data != nil {
do {
// Convert NSData to Dictionary where keys are of type String, and values are of any type
let json = try NSJSONSerialization.JSONObjectWithData(data!, options: NSJSONReadingOptions.MutableContainers) as! [String:AnyObject]
// Access specific key with value of type String
let str = json["key"] as! String
} catch {
// Something went wrong
}
}
}).resume()
}
Is it possible to make the function requestJson() return the JSON file when its loaded? Or it's not possible because it's loaded asynchronously and could not be ready? Want I'm trying to do is something like following:
requestJson() -> **[String : AnyObject]**{
// Asynchronous Http call to your api url, using NSURLSession:
NSURLSession.sharedSession().dataTaskWithURL(NSURL(string: "http://api.site.com/json")!, completionHandler: { (data, response, error) -> Void in
// Check if data was received successfully
if error == nil && data != nil {
do {
// Convert NSData to Dictionary where keys are of type String, and values are of any type
let json = try NSJSONSerialization.JSONObjectWithData(data!, options: NSJSONReadingOptions.MutableContainers) as! [String:AnyObject]
// Access specific key with value of type String
**return json**
} catch {
// Something went wrong
}
}
}).resume()
}
You can give the function params an #escaping callback returning an array or whatever you need;
An example of this for a network request;
class func getGenres(completionHandler: #escaping (genres: NSArray) -> ()) {
...
let task = session.dataTask(with:url) {
data, response, error in
...
resultsArray = results
completionHandler(genres: resultsArray)
}
...
task.resume()
}
Then to call it you could do something like this;
override func viewDidLoad() {
getGenres {
genres in
print("View Controller: \(genres)")
}
}
//MARK: Request method to get json
class func requestJSON(completion: #escaping (returnModel: String?) -> Void) {
//here you write code for calling API
}
//MARK: Calling function to retrieve return string
API.requestJSON(completion: { (string) in
//here you can get your string
})
func httpGet(request: NSURLRequest!, callback: (NSString, NSString?) -> Void)
{
var session = NSURLSession.sharedSession()
var task = session.dataTaskWithRequest(request){
(data, response, error) -> Void in
if error != nil {
callback("", error.localizedDescription)
} else {
var result = NSString(data: data, encoding:
NSASCIIStringEncoding)!
callback(result, nil)
}
}
task.resume()
}
func makeRequest(callback: (NSString) ->Void) -> Void {
var request = NSMutableURLRequest(URL: NSURL(string: "http://sample_url")!)
var result:NSString = ""
httpGet(request){
(data, error) -> Void in
if error != nil {
result = error!
} else {
result = data
}
callback(data)
}
}
Usage:-
self.makeRequest(){
(data) -> Void in
println("response data:\(data)")
}
I am writing an app for a Udacity portfolio project which would fetch photos from Flickr and display in a collectionView. When a button is pressed, it will refresh the photos in the collectionView. However I am unable to get new sets of photos despite the fact that the search parameters have different page number after every call. My code as follows:
func getPhotosURLFromFlickr(_ lat: AnyObject, lon: AnyObject, _ completionHandlerForGetPhotosURL: #escaping (_ imageURL: [String]?, _ error: NSError?) -> Void) {
taskForGetPagesFromFlickr(lat, lon: lon) { (parameters, error) in
if let error = error {
completionHandlerForGetPhotosURL(nil, error as NSError)
} else {
if let parameters = parameters {
print(parameters)
self.taskForGetPhotos(parameters, { (imageURLArray, error) in
if let error = error {
completionHandlerForGetPhotosURL(nil, error as NSError)
} else {
if let imageURLArray = imageURLArray {
completionHandlerForGetPhotosURL(imageURLArray, nil)
}
}
})
}
}
}
}
func taskForGetPagesFromFlickr(_ lat: AnyObject, lon: AnyObject, _ completionHandlerForGetPhotosURL: #escaping (_ parameters: [String: AnyObject]?, _ error: NSError?) -> Void) {
let parameters = [
Constants.ParametersKey.Method: Constants.Methods.PhotoSearch as AnyObject,
Constants.ParametersKey.FlickrAPIKey : Constants.APIInfo.APIKey as AnyObject,
Constants.ParametersKey.Format: Constants.ParametersValues.JSON as AnyObject,
Constants.ParametersKey.NoJSONCallback: Constants.ParametersValues.DisableJSONCallback as AnyObject,
Constants.ParametersKey.PerPage: Constants.ParametersValues.Fifteen as AnyObject,
Constants.ParametersKey.Extras: Constants.ParametersValues.MediumURL as AnyObject]
var parametersWithCoord = parameters
parametersWithCoord[Constants.ParametersKey.lat] = lat
parametersWithCoord[Constants.ParametersKey.lon] = lon
let url = flickrURLFromParameters(parametersWithCoord)
let request = NSMutableURLRequest(url: url)
let task = session.dataTask(with: request as URLRequest) { (data, response, error) in
if let error = error {
completionHandlerForGetPhotosURL(nil, error as NSError)
} else {
self.convertDataWithCompletionHandler(data!, completionHandlerForConvertData: { (results, error) in
if let error = error {
completionHandlerForGetPhotosURL(nil, error as NSError)
} else {
// print(results)
guard let photosDict = results?["photos"] as? [String: AnyObject] else {
let userInfo = [NSLocalizedDescriptionKey : "NoPhotosFound"]
completionHandlerForGetPhotosURL(nil, NSError(domain: "NoPhotosFound", code: 1, userInfo: userInfo))
return
}
guard let pages = photosDict[Constants.ParametersKey.Pages] as? Int else {
let userInfo = [NSLocalizedDescriptionKey : "NoPagesFound"]
completionHandlerForGetPhotosURL(nil, NSError(domain: "NoPagesFound", code: 1, userInfo: userInfo))
return
}
let randomPageIndex = Int(arc4random_uniform(UInt32(pages)))
var searchParameters = parametersWithCoord
searchParameters[Constants.ParametersKey.Page] = randomPageIndex as AnyObject?
print("Number of pages: \(pages)")
print("Random page index: \(randomPageIndex)")
completionHandlerForGetPhotosURL(searchParameters, nil)
}
})
}
}
task.resume()
}
func taskForGetPhotos(_ parameters: [String: AnyObject], _ completionHandlerForGetPhotosURL: #escaping (_ imageURL: [String]?, _ error: NSError?) -> Void) {
let url = flickrURLFromParameters(parameters)
let request = NSMutableURLRequest(url: url)
print(url)
let task = session.dataTask(with: request as URLRequest) { (data, response, error) in
if let error = error {
completionHandlerForGetPhotosURL(nil, error as NSError)
} else {
self.convertDataWithCompletionHandler(data!, completionHandlerForConvertData: { (results, error) in
if let error = error {
completionHandlerForGetPhotosURL(nil, error as NSError)
} else {
// print(results)
guard let photosDict = results?["photos"] as? [String: AnyObject] else {
let userInfo = [NSLocalizedDescriptionKey : "NoPhotosFound"]
completionHandlerForGetPhotosURL(nil, NSError(domain: "NoPhotosFound", code: 1, userInfo: userInfo))
return
}
guard let photosArray = photosDict["photo"] as? [[String: AnyObject]] else {
let userInfo = [NSLocalizedDescriptionKey : "NoPhotosArrayFound"]
completionHandlerForGetPhotosURL(nil, NSError(domain: "NoPhotosArray", code: 1, userInfo: userInfo))
return
}
var imageURLArray: [String] = []
if photosArray.count != 0 {
for pics in photosArray {
guard let imageURLString = pics[Constants.ParametersValues.MediumURL] as? String else {
print("NoImageURLString Found")
return
}
imageURLArray.append(imageURLString)
}
}
print("ImageURLArray : \(imageURLArray)")
completionHandlerForGetPhotosURL(imageURLArray, nil)
}
})
}
}
task.resume()
}
When my refresh button is tapped, the above set of networking code runs, and if I tap the button twice, my print statements are as such:
FETCHING NEW COLLECTION...
Number of items 0 Number of pages: 5712
Random page index: 490
https://api.flickr.com/services/rest?page=490&method=flickr.photos.search&format=json&lon=103.7940514843148&api_key=APIKEY&per_page=15&lat=1.411935988414726&extras=url_m&nojsoncallback=1
ImageURLArray :
["https://farm3.staticflickr.com/2059/32542810620_37312d07c9.jpg",
"https://farm3.staticflickr.com/2330/32102051403_46e30a5eec.jpg",
"https://farm3.staticflickr.com/2104/32529883860_17558a0acf.jpg",
"https://farm3.staticflickr.com/2595/32778162441_db4a98d3cd.jpg",
"https://farm3.staticflickr.com/2466/32087213853_257910d32d.jpg",
"https://farm3.staticflickr.com/2029/32891916665_2d2d177e71.jpg",
"https://farm4.staticflickr.com/3685/32878992915_38baaf513e.jpg",
"https://farm1.staticflickr.com/631/32062787803_ed58defea5.jpg",
"https://farm3.staticflickr.com/2332/32873171215_c807db5364.jpg",
"https://farm3.staticflickr.com/2788/32825584606_7d2bff507c.jpg",
"https://farm3.staticflickr.com/2479/32052120503_1317f70f1a.jpg",
"https://farm3.staticflickr.com/2909/32023922664_d276f52369.jpg",
"https://farm1.staticflickr.com/376/32023912434_9b89fc3d7b.jpg",
"https://farm1.staticflickr.com/455/32018845514_22681384ae.jpg",
"https://farm3.staticflickr.com/2833/32734093131_0e8da333f4.jpg"]
FETCHING NEW COLLECTION...
Number of items 0 Number of pages: 5712
Random page index: 5383
https://api.flickr.com/services/rest?page=5383&method=flickr.photos.search&format=json&lon=103.7940514843148&api_key=APIKEY&per_page=15&lat=1.411935988414726&extras=url_m&nojsoncallback=1
ImageURLArray :
["https://farm3.staticflickr.com/2059/32542810620_37312d07c9.jpg",
"https://farm3.staticflickr.com/2330/32102051403_46e30a5eec.jpg",
"https://farm3.staticflickr.com/2104/32529883860_17558a0acf.jpg",
"https://farm3.staticflickr.com/2595/32778162441_db4a98d3cd.jpg",
"https://farm3.staticflickr.com/2466/32087213853_257910d32d.jpg",
"https://farm3.staticflickr.com/2029/32891916665_2d2d177e71.jpg",
"https://farm4.staticflickr.com/3685/32878992915_38baaf513e.jpg",
"https://farm1.staticflickr.com/631/32062787803_ed58defea5.jpg",
"https://farm3.staticflickr.com/2332/32873171215_c807db5364.jpg",
"https://farm3.staticflickr.com/2788/32825584606_7d2bff507c.jpg",
"https://farm3.staticflickr.com/2479/32052120503_1317f70f1a.jpg",
"https://farm3.staticflickr.com/2909/32023922664_d276f52369.jpg",
"https://farm1.staticflickr.com/376/32023912434_9b89fc3d7b.jpg",
"https://farm1.staticflickr.com/455/32018845514_22681384ae.jpg",
"https://farm3.staticflickr.com/2833/32734093131_0e8da333f4.jpg"]
You would realise that despite that the search page is different for both times, but the imageURLArray is actually the same. I can't seem to identify the reason why.
Some help is much appreciated pls, thanks!
Somehow I manage to solve my problem. I limit the randomPageIndex with a maximum number of 100 and it successfully refreshes the page.
Perhaps Flickr has some maximum search pages which I may not be aware of.
Hi I just migrated to alamofire 4 and I just want to send the error coming from the server, I found a couple ways but I just want to make sure that this is the correct way, here is my custom responseobject class
public protocol ResponseObject {
init?(response: HTTPURLResponse, representation: Any)
}
enum BackendError: Error {
case network(error: Error)
case dataSerialization(error: Error)
case jsonSerialization(error: Error)
case xmlSerialization(error: Error)
case objectSerialization(reason: String)
}
extension DataRequest {
public typealias Validation = (URLRequest?, HTTPURLResponse, Data?) -> ValidationResult
func responseObject<T: ResponseObject>(
queue: DispatchQueue? = nil,
completionHandler: #escaping (DataResponse<T>) -> Void)
-> Self
{
let responseSerializer = DataResponseSerializer<T> { request, response, data, error in
guard error == nil else {
let jsonResponseSerializer = DataRequest.jsonResponseSerializer(options: .allowFragments)
let result = jsonResponseSerializer.serializeResponse(request, response, data, nil)
debugPrint(result)
return .failure(BackendError.network(error: error!))
}
let jsonResponseSerializer = DataRequest.jsonResponseSerializer(options: .allowFragments)
let result = jsonResponseSerializer.serializeResponse(request, response, data, nil)
guard case let .success(jsonObject) = result else {
return .failure(BackendError.jsonSerialization(error: result.error!))
}
guard let response = response, let responseObject = T(response: response, representation: jsonObject) else {
return .failure(BackendError.objectSerialization(reason: "JSON could not be serialized: \(jsonObject)"))
}
return .success(responseObject)
}
return response(queue: queue, responseSerializer: responseSerializer, completionHandler: completionHandler)
}
}
I add a debugprint so see the error from the server and I see it but do I have to serialize de data again inside the error?? and how can I pass the message to my custom error?