I've created a error handling class, which is suppose to make my calls easier, however i keep getting an error Extra limit argument in call. I've double checked that their is 7 arguments in both, how come i get this error?
Class
class GetOrganization {
func request(
lastPage: Int?,
limit: Int,
location: CLLocation,
radius: Int?,
success successCallback: (JSON) -> Void,
error errorCallback: (statusCode: Int) -> Void,
failure failureCallback: (error: Error) -> Void
) {
Provider.request(.Organizations(lastPage, limit,location.coordinate.longitude, location.coordinate.latitude, radius)) { result in
switch result {
case let .Success(response):
do {
try response.filterSuccessfulStatusCodes()
let json = try JSON(response.mapJSON())
successCallback(json)
}
catch {
errorCallback(statusCode: response.statusCode)
}
case let .Failure(error):
failureCallback(error: error)
}
}
}
}
*Where i get the error**
GetOrganization.request(
lastPage: lastPage,
limit: limit,
location: location,
radius: nil,
success: { data in
}, error: { err in
print(err)
}, failure: { faillure in
// oh well, no network apparently
})
It is not a static function, so you can't call it like GetOrganization.request. Either set the func request as static func request or create an object and access it via that object.
Related
Dear Advanced Programmers,
Could you please assist a fairly new programmer into editing this code. Seems that the new version of Xcode does not support the below code and displays the error:
**"Contextual closure type '(Directions.Session, Result<RouteResponse, DirectionsError>) -> Void' (aka '((options: DirectionsOptions, credentials: DirectionsCredentials), Result<RouteResponse, DirectionsError>) -> ()') expects 2 arguments, but 3 were used in closure body"**
The code is copied directly from the mapbox documentation website. Any form of assistance would be appreciated. Thanks in advance.
func getRoute(from origin: CLLocationCoordinate2D,
to destination: MGLPointFeature) -> [CLLocationCoordinate2D]{
var routeCoordinates : [CLLocationCoordinate2D] = []
let originWaypoint = Waypoint(coordinate: origin)
let destinationWaypoint = Waypoint(coordinate: destination.coordinate)
let options = RouteOptions(waypoints: [originWaypoint, destinationWaypoint], profileIdentifier: .automobileAvoidingTraffic)
_ = Directions.shared.calculate(options) { (waypoints, routes, error) in
guard error == nil else {
print("Error calculating directions: \(error!)")
return
}
guard let route = routes?.first else { return }
routeCoordinates = route.coordinates!
self.featuresWithRoute[self.getKeyForFeature(feature: destination)] = (destination, routeCoordinates)
}
return routeCoordinates
}
Please, developers, learn to read error messages and/or the documentation.
The error clearly says that the type of the closure is (Directions.Session, Result<RouteResponse, DirectionsError>) -> Void (2 parameters) which represents a session (a tuple) and a custom Result type containing the response and the potential error.
You have to write something like
_ = Directions.shared.calculate(options) { (session, result) in
switch result {
case .failure(let error): print(error)
case .success(let response):
guard let route = response.routes?.first else { return }
routeCoordinates = route.coordinates!
self.featuresWithRoute[self.getKeyForFeature(feature: destination)] = (destination, routeCoordinates)
}
}
Apart from the issue it's impossible to return something from an asynchronous task, you have to add a completion handler for example
func getRoute(from origin: CLLocationCoordinate2D,
to destination: MGLPointFeature,
completion: #escaping ([CLLocationCoordinate2D]) -> Void) {
and call completion(route.coordinates!) at the end of the success case inside the closure
My goal is to set up a completion handler using a standard Moya request call.
Here is my process:
Call backend with with a MoyaProvider that conforms to my own BackendAPI (already set up)
Wrap this call in a completion handler to return [Player] data (Player is a custom class in the project)
Display [Player] data
Here is the actual code:
func getPlayers(orchestraId: String, finished: #escaping () -> [Player]) {
let provider = MoyaProvider<BackendAPI>()
provider.request(.getPlayers(orchestraId: orchestraId)) { (result) in
switch result {
case let .success(moyaResponse):
let statusCode = moyaResponse.statusCode
if statusCode == 200 {
let data = moyaResponse.data
let json = JSON.init(data: data)
let players: [Player] = self.deserializeJSONPlayers(with: json)
return players
} else {
print ("Non 200 for league players data")
self.debugStatementsFromResponse(response: moyaResponse)
}
case let .failure(error):
print ("Error: \(error)")
}
}
}
I am getting an error on the return line, with the note that Unexpected non-void return in void function. However, I have declared my function to be a non-void function. What I am doing incorrectly in structuring my method?
You have the completionHandler which you should use if you want to return the value with the current syntax. If you want to use return players then you must change the syntax.
Use this syntax instead to make your current code work with the completionHandler:
func getPlayers(orchestraId: String, finished: #escaping ([Player]) -> Void) {
finished(players) // Instead of return players
}
Introduction:
I'm introducing a Result framework (antitypical) in some points of my app. In example, given this function:
func findItem(byId: Int, completion: (Item?,Error?) -> ());
foo.findItem(byId: 1) { item, error in
guard let item = item else {
// Error case
handleError(error!)
return;
}
// Success case
handleSuccess(item)
}
I implement it this way with Result:
func findItem(byId: Int, completion: Result<Item,Error>) -> ());
foo.findItem(byId: 1) { result in
swith result {
case let success(item):
// Success case
handleSuccess(item)
case let failure(error):
// Error case
handleError(error!)
}
}
Question
What is the correct way of implementing a result where the success case returns nothing?. Something like:
func deleteItem(byId: Int, completion: (Error?) -> ());
foo.deleteItem(byId: 1) { error in
if let error = error {
// Error case
handleError(error)
return;
}
// Success case
handleSuccess()
}
In java I would implement a Result whats the correct way to do this in Swift
The best way is exactly what you've done: Error? where nil indicates success. It's quite clear and simple.
That said, another answer (and one that I've used) is exactly in your question: "How to handle Void success case with Result." The success case passes Void, so pass Void:
Result<Void, Error>
"Void" doesn't mean "returns nothing." It's a type in Swift, a type that has exactly one value: the empty tuple (). That also happens to be the type:
public typealias Void = ()
As a matter of convention, we use Void to mean the type, and () to mean the value. The one thing that's a bit strange about using Void this way in a Result is the syntax. You wind up with something like:
return .success(())
The double-parentheses are a little ugly and slightly confusing. So even though this is nicely parallel to other Result-using code, I typically just use Error? in this case. If I had a lot of it, though, I'd consider creating a new type for it:
enum VoidResult {
case .success
case .failure(Error)
}
You can add this extension, to simplify your life.
public extension Result where Success == Void {
/// A success, storing a Success value.
///
/// Instead of `.success(())`, now `.success`
static var success: Result {
return .success(())
}
}
// Now
return .success
Gists
I found Rob's answer really interesting and smart. I just want to contribute with a possible working solution to help others:
enum VoidResult {
case success
case failure(Error)
}
/// Performs a request that expects no data back but its success depends on the result code
/// - Parameters:
/// - urlRequest: Url request with the request config
/// - httpMethodType: HTTP method to be used: GET, POST ...
/// - params: Parameters to be included with the request
/// - headers: Headers to be included with the request
/// - completion: Callback trigered upon completion
func makeRequest(url: URL,
httpMethodType: HTTPMethodType,
params: [String:Any],
headers: [String:String],
completion: #escaping (VoidResult) -> Void){
let alamofireHTTPMethod = httpMethodType.toAlamofireHTTPMethod()
let parameterEncoder: ParameterEncoding
switch alamofireHTTPMethod {
case .get:
parameterEncoder = URLEncoding.default
case .post:
parameterEncoder = JSONEncoding.default
default:
parameterEncoder = URLEncoding.default
}
Log.d(message: "Calling: \(url.absoluteString)")
AF.request(url,
method: alamofireHTTPMethod,
parameters: params,
encoding:parameterEncoder,
headers: HTTPHeaders(headers)).response { response in
guard let statusCode = response.response?.statusCode,
(200 ..< 300) ~= statusCode else {
completion(.failure(NetworkFetcherError.networkError))
return
}
completion(.success)
}
}
Try this
Note this is example you can change as per your test
typealias resultHandler = (_ responseItems: AnyObject, _ error: Error) -> Void
func deleteItem(byId: Int, completion: resultHandler){
completion(Items, error)
}
Calling
self.deleteItem(byId: 1) { (result, error) in
if error ==nil{
}
}
I've got a function:
func retrieveArticleWithTopic(topicId id: String, success: ([Newsfeed] -> Void), failure: (NSError -> Void)) {
client.fetchEntries(["content_type": ContentType.Article.rawValue, "include": 10]) { (results) in
// TODO: some more code will go here
var feeds = [Newsfeed]()
success(feeds)
}
}
Which I need to pass a ID as a string to. Im struggling to call this function and pass values to it. I've tried:
ContentManager().retrieveTopics(TopicID:"Hello")
But I get an error that reads Missing argument for parameter failure in call.
How do pass a string to this function?
The success and failure parameters are required (they don't have default values). You must pass two blocks:
retrieveTopics(topicID: "hello", success: { newsfeeds in
print("retrieved \(newsfeeds)")
}, failure: { error in
print("error: \(error)")
})
You should call like this:
retrieveArticleWithTopic(topicId: "", success: { (newsfeeds) in
}) { (error) in
}
Or you don't want to handle success or failure, you can make it optional.
func retrieveArticleWithTopic(topicId id: String, success: ([Newsfeed] -> Void)?, failure: (NSError -> Void)?) {
}
retrieveArticleWithTopic(topicId: "", success: nil, failure: nil)
A pretty handy feature of Swift functions is that function parameters can have default values:
func someFunction(parameterWithDefault: Int = 42) {
//if no arguments are passed to the function call,
//value of parameterWithDefault is 42
}
If a parameter is a closure, is there a way to make it have a default value? See the example below:
func sendBody(
body: NSData? = nil,
success: (data: NSData) -> Void,
failure: (data: NSData?) -> Void) {
}
Is there a way to not force the developer to pass a value for success or failure when calling sendBody?
Yes, functions are just values, so you can supply them as defaults
// just to show you can do it with inline closures or regular functions
func doNothing<T>(t: T) -> Void { }
func sendBody(
body: NSData? = nil,
success: (data: NSData) -> Void = { _ in return },
failure: (data: NSData?) -> Void = doNothing
)
{ }
Alternatively, you could make them optional, that way you can detect if the caller passed one:
func sendBody(
body: NSData? = nil,
success: ((NSData) -> Void)? = nil,
failure: ((NSData?) -> Void)? = nil
)
{ success?(NSData()) }
sendBody(success: { _ in print("ah, yeah!") })
Also worth noting if you’re doing this: if the caller uses the trailing closure syntax, this will be the last closure in the argument list. So you want the last one to be the one the user is most likely to want to supply, which is probably the success closure:
func sendBody(
body: NSData? = nil,
success: ((NSData) -> Void)? = nil,
failure: ((NSData?) -> Void)? = nil
)
{
if success != nil { print("passed a success closure") }
if failure != nil { print("passed a failure closure") }
}
// this prints "passed a failure closure"
sendBody { data in
print("which closure is this?")
}
Other than this, the order in the function declaration doesn’t matter to the caller – defaulted arguments can be supplied in any order.
You could do something like this,
let defaultSuccess: NSData -> Void = {
(data: NSData) in
}
let defaultFailure: NSData? -> Void = {
(data: NSData?) in
}
func sendBody( body: NSData? = nil, success: (data: NSData) -> Void = defaultSuccess, failure: (data: NSData?) -> Void = defaultFailure) {
}
Then, you may be able to call either one of these methods. Notice sendBody which is called with default parameters.
sendBody()
sendBody(body: , success: , failure: )
You can also call with all the variants like passing just one of the argument in the above method, for that you have to call it with named parameter.
sendBody()
sendBody(body:)
sendBody(failure: )
sendBody(success:)
sendBody(body: , success: , failure: )
How to set a default value for a function parameter. Swift 4 and (probably) 5.
func someFunction(age: Int, doSomething:#escaping () -> Void = {}){
//do work here
//
doSomething()
}
Then you can do this
someFunction(age: 18) {
print("hello")
}
someFunction(age: 19)
You may or may not need to use the #escaping keyword. See Swift #escaping and Completion Handler for that.
My preferred way to specify public facing closures - in particular completion closures which you might want to store somewhere for later - is to define a typealias for them, like this:
public typealias FooCompletion = (String) -> Void
Then in the public facing function you can easily make it optional like this:
var onCompletion: FooCompletion? = nil
public func foo(completion: FooCompletion? = nil) {
// Store completion for later
onCompletion = completion
}
The completion parameter is optional, so it's allowed to be nil, and the default value is nil, meaning the caller doesn't have to specify it. Also, because you use the type in more than one place, if you need to change its definition during development there's only one place to do so. It's easy to call too:
private func someBackgroundThing() {
var completionString = "done"
...
onCompletion?(completionString)
}