Compiler error in custom Alamofire response function - ios

I'm attempting to implement a generic responseObject function for Alamofire using Argo. Unfortunately I'm getting a persistent compiler error:
error: missing argument for parameter #2 in call
APIManager.manager.request(APIRouter.Login(username: usernameTextField.text, password: passwordTextField.text)).responseObject { (object, error) -> Void in
I understand this error to usually mean something about tuples, so I'm guessing something in my code is being misinterpreted as a tuple, but I can't figure out what. Here's my responseObject function:
func responseObject<T: Decodable where T == T.DecodedType>(completionHandler: (T?, NSError?) -> Void) -> Self {
return responseJSON(options: .AllowFragments) { (request, response, JSON, error) in
// TODO: Complete error handling.
var responseObject: T?
if let JSON: AnyObject = JSON,
let response: Response = decode(JSON) where response.status == "0" {
responseObject = decode(JSON)
}
completionHandler(responseObject, error)
}
}
My request code and router work fine, it's the addition of this responseObject function that breaks things.

Turns out you need to provide an explicit type to the variables brought into a closure with a generic return type. So instead of (object, error) in I needed (object: SpecificType?, error).

Related

Swift 4 Parse Server Cloud Code Error [duplicate]

PFCloud.callFunctionInBackground("hello", withParameters: ["test":"tester"]) {
(response: AnyObject?, error: NSError?) -> Void in
if error == nil {
let responseString = response as? String
print(responseString)
} else {
print(error!.description)
}
}
I am getting the error:
Cannot convert value of type '(AnyObject?, NSError?) -> Void' to
expected argument type 'PFIdResultBlock?' (aka
'Optional<(Optional, Optional) -> ()>')
Even if I add as! PFIdResultBlock, the error will not go away.
How can I go about fixing this?
I definitely appreciate your help on this one!!
There is no need to specify the variable types while implementing the closure (Block in Objective-C) unlike Objective-C. You just need to change your code to the following:
PFCloud.callFunction(inBackground: "",
withParameters: ["": ""]) { (response, error) in
if error == nil {
let responseString = response as? String
print(responseString)
} else {
print(error?.localizedDescription)
}
}

Parse Cloud with Swift PFIdResultBlock Error

PFCloud.callFunctionInBackground("hello", withParameters: ["test":"tester"]) {
(response: AnyObject?, error: NSError?) -> Void in
if error == nil {
let responseString = response as? String
print(responseString)
} else {
print(error!.description)
}
}
I am getting the error:
Cannot convert value of type '(AnyObject?, NSError?) -> Void' to
expected argument type 'PFIdResultBlock?' (aka
'Optional<(Optional, Optional) -> ()>')
Even if I add as! PFIdResultBlock, the error will not go away.
How can I go about fixing this?
I definitely appreciate your help on this one!!
There is no need to specify the variable types while implementing the closure (Block in Objective-C) unlike Objective-C. You just need to change your code to the following:
PFCloud.callFunction(inBackground: "",
withParameters: ["": ""]) { (response, error) in
if error == nil {
let responseString = response as? String
print(responseString)
} else {
print(error?.localizedDescription)
}
}

ResponseSerializer 'cannot call value of non-function type 'NSHTTPURLResponse?'' with Swift 3

I had been using the following code without issue until updating to Xcode 8 beta 6. It is similar to this example from the Alamofire repository. This morning I updated my Alamofire library to the latest swift3 branch, which is now compatible with beta 6. It shows the error: Cannot call value of non-function type 'HTTPURLResponse?' A similar question exists here, but it is not based on the current version of Swift and Alamofire.
From what I understand, this error is because it thinks that I am trying to return the Request property response instead of the function response(responseSerializer: <T>, completionHandler: <(Response<T.SerializedObject, T.ErrorObject>) -> Void>) and it thinks this because of a type error in either the responseSerializer or completionHandler that I'm passing into the function.
How can I adjust this code to make it compatible with the function declaration and compiler?
I added #escaping to the completionHandler to correct the error.
import Foundation
import Alamofire
import SwiftyJSON
extension Alamofire.Request {
public func responseObject<T: ResponseJSONObjectSerializable>(_ completionHandler: #escaping (Response<T, NSError>) -> Void) -> Self {
let responseSerializer = ResponseSerializer<T, NSError> { request, res, data, error in
guard let responseData = data else {
let error = DFError.error(withDFCode: .dataSerializationFailed, failureReason: "Data could not be serialized because input data was nil.")
return .failure(error)
}
let jsonData: Any?
do {
jsonData = try JSONSerialization.jsonObject(with: responseData, options: [])
} catch {
let error = DFError.error(withDFCode: .jsonSerializationFailed, failureReason: "JSON could not be serialized into response object")
return .failure(error)
}
let json = SwiftyJSON.JSON(jsonData!)
if let newObject = T(json: json) {
return .success(newObject)
}
let error = DFError.error(withDFCode: .jsonSerializationFailed, failureReason: "JSON could not be serialized into response object")
return .failure(error)
}
return response(responseSerializer: responseSerializer, completionHandler: completionHandler)
//Error: Cannot call value of non-function type 'HTTPURLResponse?'
}
}
You need to mark your completionHandler as #escaping.
I was still seeing this error even after adding #escaping to the closure. The issue I had was that I needed to change my extension declaration from extension Alamofire.Request { } to extension Alamofire.DataRequest { }.

Swift 2 try/catch

I've started converting one of my projects to Swift 2 and I ran into this issue. To start this block below is perfectly valid try/catch, in fact it was generated by the Xcode migration tool.
do {
requestData = try NSJSONSerialization.dataWithJSONObject(requestContents, options: [])
} catch var error as NSError {
requestError = error
requestData = nil
}
If I use that same code inside a closure, such as a dataTaskWithRequest I get an error. The error is at the task assignment, but its the catch that causes it. The following also works but I'm not capturing the error.
let task = session.dataTaskWithRequest(request, completionHandler: { (data, response, taskError) -> Void in
if taskError != nil {
NSLog("Error making request: " + taskError!.localizedDescription)
}
else {
do {
let json = try NSJSONSerialization.JSONObjectWithData(data!, options: .MutableLeaves) as? NSDictionary
if let parseJSON = json as NSDictionary? {
// do some parsing here
}
}
catch {
NSLog("Error in JSON serialization")
}
}
})
task.resume()
but if I try to capture the error with:
} catch let e as NSError {
I get this error:
Invalid conversion from throwing function of type '(_, _, _) throws ->
Void' to non-throwing function type '(NSData?, NSURLResponse?,
NSError?) -> Void'
I did discover that:
} catch _ {
works but a lot of good that does me.
Am I missing something or should I be filing a bug?
(This is Xcode 7b5)
The completion handler of dataTaskWithRequest is not designed to throw error like JSONObjectWithData of NSJSONSerialization, whose signature is:
class func JSONObjectWithData(data: NSData, options opt: NSJSONReadingOptions) throws -> AnyObject
Doing the following would work (same as what you have tried):
catch _
But that won't give us any detail about the error from the one that throws, namely, from class func JSONObjectWithData.
As a result, we need a way to consume the non-throwable dataTaskWithRequest while preserving the one that throws - JSONObjectWithData.
I have tried the following:
catch let error as NSError
{
//error specific to JSON serialization
NSLog("Error in JSON serialization \(error)")
}
catch
{
//exhaust the error
NSLog("Some error")
}

Error handling in block with Swift syntax

The error handling here doesn't feel right. Anyone have any suggests for how to improve it? Using optional binding to establish errors and a return value variable.
That cool?
class ChargePointsFetcher {
func getDevices(location: NSURL, completion handler:([ChargeDevice]?, error: NSError?) -> Void) {
let request = NSURLRequest(URL: location)
let session = NSURLSession.sharedSession()
let task = session.dataTaskWithRequest(request, completionHandler: { (let data, let response, let error) -> Void in
var returnValue: NSError?
if let e = error {
returnValue = e
}
var collection: [ChargeDevice]?
if returnValue == nil {
collection = [ChargeDevice]()
var parsingError: NSError?
if let json: NSDictionary = NSJSONSerialization.JSONObjectWithData(data, options: .AllowFragments, error: &parsingError) as? NSDictionary {
if let chargeDevices = json.valueForKey("ChargeDevice") as? NSArray {
for chargeDevice in chargeDevices {
let device = ChargeDevice()
collection!.append(device)
}
}
}
if let e = parsingError {
returnValue = e
}
}
dispatch_async(dispatch_get_main_queue(), { () -> Void in
handler(collection, error: returnValue)
})
})
}
}
In my opinion, you should try very hard to avoid the (Value?, Error?) pattern when you can. It creates bad corner cases since two combinations are illegal. Instead, when possible, I suggest (Value, Error?) any time Value has a sensible "zero". In this case "zero" is "empty array". This matches the ObjC sense very closely, since a nil NSArray is very similar to an empty array.
Doing that, you can substantially simplify this code:
func getDevices(location: NSURL, completion handler:([ChargeDevice], error: NSError?) -> Void) {
// Note that error is marked "var" so we can modify it, and switched from NSError! to NSError?
let task = session.dataTaskWithRequest(request, completionHandler: { (let data, let response, var error: NSError?) -> Void in
var result = [ChargeDevice]()
if error == nil {
// Avoid NSArray and NSDictionary wherever you can in Swift
if let
json = NSJSONSerialization.JSONObjectWithData(data,
options: .AllowFragments,
error: &error
) as? [String:AnyObject],
chargeDevices = json["ChargeDevice"] as? [AnyObject] {
// map is much simpler in this case, but in your full code, for may fine fine
result = chargeDevices.map{ _ in ChargeDevice() }
}
}
dispatch_async(dispatch_get_main_queue(), { () -> Void in
handler(result, error: error)
})
})
}
Note that in your code and my code, incorrect but valid JSON will not generate an error of any kind. It'll just quietly skip through the as? calls. I would probably move all of this JSON work into another function to keep this closure from getting out of hand.
Another approach here is called a Result. My preferred example is Rob Rix's. Even though I've done a lot of work on Result, I've personally find it difficult to bridge to Cocoa cleanly. It's useful if your entire program uses it, and if you encapsulate most Cocoa interaction, but I've found it cumbersome to use as a one-off solution. (Many people would disagree with me here, it's just my experience. Result is definitely worth exploring to form your own opinion.)

Resources