Swift 4 "Extra argument in call" Rxswift - swift

I updated from Swift 3 to 4, and i am using RxSwift, When updated i came across an error "Extra argument in call" as it expects now of type element. I tried creating tuple of (response, data) but still gives me an error as shown.
public static func deleteCrush(receiver: String, receiver_type: ProviderType) -> Observable<(HTTPURLResponse, NSArray)> {
/*
Delete crush identified by `id`
*/
let parameters: Parameters = [
"receiver": receiver,
"receiver_type": receiver_type.rawValue
]
print("deleteCrush paramreters: \(parameters)")
return Observable.create({ (observer) -> Disposable in
Alamofire.request(Router.deleteCrush(parameters: parameters))
.rx
.responseJSON()
.debug()
.subscribe(onNext: { (response, json) in
print("deleteCrush response code: ", response.statusCode)
if let data = json as? NSArray {
observer.on(.next(response, data))
observer.on(.completed)
}
}, onError: { (error) in
print("deleteCrush error: ", error)
observer.on(.error(error))
}, onCompleted: nil, onDisposed: nil)
})
}
Error: Extra argument in call.
After trying to fix:
var tuple = (response, json)
if let data = json as? NSArray {
observer.on(.next(tuple))
observer.on(.completed)
}
Error:
Event<(HTTPURLResponse, NSDictionary)>' produces result of type 'Event', but context expects 'Event<(HTTPURLResponse, NSDictionary)>

It just seems like you are not passing correct data type to your events.
Either simply wrap your tuple properly,
observer.on(.next((response, data)))
Or then use proper tuple type for your Element type,
if let data = json as? NSArray {
var tuple = (response, data)
observer.on(.next(tuple))
observer.on(.completed)
}
Note that you are setting tuple as tuple = (response, json) which is not correct according to your method return type.

Swift is probably having a hard time figuring out the type of the of the data you are sending in .next. Sometimes this happens and the compiler provides a completely useless error message
Check your function definition. Specifically the return type. The observer you're returning may not be of the same type you are sending to your observers.

Related

Swift error while returning multiple values from function

I can not understand what I did wrong
I have an app which loads posts and its comments. The view controller requests a function from an another file, which returns back (response?, comments?)
I get one error:
Initializer for conditional binding must have Optional type, not '(ActionResult?, [PostComment]?)'
For the line
if let (response, comments) = (response, comments )
What did I wrong?
commentsViewController
postComments.loadCommentForPost(id: postId) { (response, comments) in
// ERROR here: Initializer for conditional binding must have Optional type, not '(ActionResult?, [WorldMessageComment]?)'
if let (response, comments) = (response, comments ) {
if response!.success == 1 {
DispatchQueue.main.async(execute: {() -> Void in
self.comments = comments!
self.tableView.reloadData()
})
} else {
DispatchQueue.main.async(execute: {() -> Void in
self.handleResponses.displayError(title: response!.title, message: response!.message)
})
}
}
}
commentFunctions
func loadCommentsForPost(id: Int, completion: #escaping ((ActionResult?), [PostComment]?)->()){
// downloading the data and then
let comments : [PostComment] = ...
// Succesful
return completion((ActionResult(success: 1, title: responseTitle, message: responseMessage)), comments)
}
The issue is in the line:
if let (response, comments) = (response, comments ) {
What you are basically doing is creating a new non optional tuple on the right hand side of the assignment (with two optional components), so the compilator complains that you can't use if let with a non optional type.
You can in fact consider that the tuple return by loadCommentsForPost is already "split" in the arguments of the callback, so you can handle response and comments separately.
postComments.loadCommentForPost(id: postId) { response, comments in
if let response = response, let comments = comments {
if response.success == 1 {
...

Alamofire and PromiseKit returning a Promise<[T]>

I used Alamofire and PromiseKit as separate Cocoapod installs. I can retrieve the JSON data using Alamofire, but I am receiving the error below when configuring PromiseKit. The error below appears in the line where 'fulfill, reject' are in.
Error message: Contextual closure type '(Resolver<_>) -> Void' expects 1 argument, but 2 were used in closure body
I am using Xcode 9.2 and IOS 11.2 inside of the Simulator. Thank you for your advice in advance!
func wantToReturnAnArrayOfActor() -> Promise<[Actor]> {
return Promise { fulfill, reject in
Alamofire.request(ApiUrl.url.rawValue).responseJSON { (response) in
switch(response.result)
{
case .success(let responseString): print("my response string = \(responseString)")
let actorResponse = ActorApiResponse(JSONString: "\(responseString)")//converts all of the data into the ActorApiResponse model class
return when(fulfilled: actorResponse)
DispatchQueue.main.async {
print("actorResponse = \(String(describing: actorResponse))")
}
case .failure(let error): print("alamofire error = \(error)")
}
}
}
}
Should it rather be like this,
func wantToReturnAnArrayOfActor() -> Promise<[Actor]> {
return Promise() { resolver in
Alamofire.request(ApiUrl.url.rawValue).responseJSON { (response) in
switch(response.result)
{
case .success(let responseObject):
let actorResponse = ActorApiResponse(jsonObject: responseObject)
let actors = actorResponse.getActors()
resolver.fulfill(actors)
case .failure(let error):
resolver.reject(error)
}
}
}
}
The initializer closure for Promise takes in single argument, which is of type Resolver, which is what your error says. Then, you would want to resolve your promise with result which is of type [Actor] when the promise execution is finished or then reject with error if error occurred during the execution.
Few points to note here:
Alamofire.request(_).responseJSON returns json object not json string.
If your ActorApiResponse is the object which transforms the json to [Actor], you should have proper method to convert json object to actual data type ie. [Actor].
You could have your ActorApiResponse something like this,
struct ActorApiResponse {
init(jsonObject: Any) {
}
func getActors() -> [Actor] {
// calculate and return actors
return []
}
}
Then, you can call it from else where,
wantToReturnAnArrayOfActor().done {
// do something with [Actor here]
// You can also chain the multiple promise using .then instead of using done
}.catch { error in
print("Error occurred \(error)")
}

Alamofire responseArray String array for keyPath

I have a rest call which returns array of string [String] for keyPath "data", for example...
{
"data": [
"1",
"3",
"5",
"2",
"4",
"9"
]
}
I am trying to get it via responseArray(keyPath: "data"), but get compile error for line *.responseArray(keyPath: "data") {(response: DataResponse<[String]>) in*
Cannot convert value of type '(DataResponse<[String]>) -> ()' to expected argument type '(DataResponse<[_]>) -> Void'
Part of request example
alamofireManager.request(url)
.responseArray(keyPath: "data") {(response: DataResponse<[String]>) in
if response.result.isSuccess {
if let data = response.result.value {
//process success
} else {
// handle error
}
}
...
Does anybody of you know how to do it ?
The problem is that String isn't Mappable. As per https://github.com/Hearst-DD/ObjectMapper/issues/487, these are the proposed solutions:
In this situation I would recommend accessing the data directly from the Alamofire response. You should be able to simply cast it to [String].
Alternatively, you may be able to subclass String and make the subclass Mappable, however I think that is more work than necessary for the your situation
Using Swift's 4 Codable (no external dependencies):
struct APIResponse: Decodable {
let data: [String]
}
let url = "https://api.myjson.com/bins/1cm14l"
Alamofire.request(url).responseData { (response) in
if response.result.isSuccess {
if let jsonData = response.result.value,
let values = try? JSONDecoder().decode(APIResponse.self, from: jsonData).data {
//process success
print(values)
} else {
// handle error
}
}
}

Generic perform Request, using Generics

I would like to make a perform request function in swift using Generics. I want to make the call and switch on my enum Result based on what I get back. However, I don't understand the : 'cannot invoke performRequest with an argument list of type (NSURLRequest, (Result<__>) -> ())' Why can't I have an unnamed parameter here? I have also tried something like the following : r<MyStruct> --- but I then get an expected expression error. Any help explaining the above Result<_> error would be greatly appreciated. Thanks.
enum Result<A> {
case Value
case Error
}
func performRequest<A>(request:NSURLRequest, callback:(Result<A>) -> ()) {
let task = NSURLSession.sharedSession().dataTaskWithRequest(request) { (data, response, error) -> Void in
callback(parseResponse(data, response: response, error: error))
}
task.resume()
}
class SampleClass {
let request = NSURLRequest(URL: NSURL(string: "www.google.com")!)
init() {
performRequest(request) { r in -------- errors out
switch r {
case .Value:
case .Error:
}
}
}
The problem is that when you use performRequest, you have not given the compiler enough information about the generic parameter you intend to use. The critical part that is missing is that parseResponse needs to return a Result that is parameterised in the same way as the callback. However, in the snippet you provided, parseResponse is not generic.
I believe this will do what you intend. In this scenario, I've parameterised the Result with String, but you can substitute any other type.
// multi-purpose (generic) Result type
enum Result<A>
{
case Value(A) // because you parameterised the enum, you might as well take advantage of the type
case Error
}
// this is a custom parser, you may substitute your own that returns a different type
func parseString( data:NSData?, response:NSURLResponse?, error:NSError? ) -> Result<String> {
if let _ = error {
return Result.Error
}
return Result.Value("Success")
}
// this function is completely generic, but the parser and callback need to be compatible
func performRequest<A>( request:NSURLRequest,
parser:( NSData?, NSURLResponse?, NSError? ) -> Result<A>,
callback:( Result<A> ) -> Void ) {
let task = NSURLSession.sharedSession().dataTaskWithRequest(request) {
( data, response, error ) -> Void in
callback( parser( data, response, error ) )
}
task.resume()
}
let request = NSURLRequest(URL: NSURL(string: "www.google.com")!)
// actual invocation, now I need to pass in a concrete parser and callback with a specific type
performRequest( request, parser: parseString ) { // parseString returns a Result<String>
r in
switch r {
case .Value( let value ):
// because I passed in a parser that returns a Result<String>, I know that "value" is a String here
print( "Succeeded with value: \(value)" )
break;
case .Error:
print( "an error occurred" )
break;
}
}

Cannot convert value of type 'T?' to expected argument type '_?' - Generic types and completion blocks

I'm trying to implement AlamofireObjectMapper (https://github.com/tristanhimmelman/AlamofireObjectMapper) with Alamofire 3 and latest version of ObjectMapper (https://github.com/Hearst-DD/ObjectMapper).
It seems that AlamofireObjectMapper, hasn't been updated to work with Alamofire 3, so I'm trying to do it myself.
I've come to this piece of code and now i'm stuck.
It seems that the Generic Type T is not accesible inside the completion block of the response. Is a Alamofire 3 change or a Swift 2.1 change?
This is the error:
Cannot convert value of type 'T?' to expected argument type '_?'
public func responseObject<T: Mappable>(queue: dispatch_queue_t?, keyPath: String?, completionHandler: (NSURLRequest, NSHTTPURLResponse?, T?, AnyObject?, ErrorType?) -> Void) -> Self {
return response(queue: queue) { (request, response, data, error) -> Void in
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0)) {
let JSONResponseSerializer = Request.JSONResponseSerializer(options: .AllowFragments)
let result = JSONResponseSerializer.serializeResponse(request, response, data, error)
let parsedObject = Mapper<T>().map(keyPath != nil ? result.value?[keyPath!] : result.value)
dispatch_async(queue ?? dispatch_get_main_queue()) {
completionHandler(self.request!, self.response, parsedObject, result.value ?? response.data, result.error) // Here it shows the error: Cannot convert value of type 'T?' to expected argument type '_?'
}
}
}
}
Just found the solution.
It seemed to be a problem of the Xcode 7.1 beta compiler. It was giving the problem on the "parsedObject" parameter and there was a mistake on the next parameter.
completionHandler(self.request!, self.response, parsedObject, **result.value ?? data**, result.error)
So, if you happen to get the same error, review all the other parameters are ok.
Good luck.
Update to mabril's answer for Alamofire 3.0
completionHandler(response.request!, response.response, parsedObject, response.result.value ?? response.data, response.result.error)