How to handle Void success case with Result lib (success/failure) - ios

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{
}
}

Related

PromiseKit 6 iOS chaining

I am trying to chain some API calls and I think I am confusing some concepts. Would love some clarification & code samples.
I have implemented these functions...
func promiseFetchPayments(for accountId: String) -> Promise <[OperationResponse]> {
return Promise <[OperationResponse]> { seal in
payments(for: accountId) { (records, error) in
if let recs = records {
seal.resolve(.fulfilled(recs))
return
}
if let e = error {
seal.reject(e)
return
}
}
}
}
and
func payments(for accountId: String, completion: #escaping (_ records: [OperationResponse]?, _ error: Error?) -> Void) {
stellar.payments.getPayments(
forAccount: accountId,
order: Order.descending,
limit: 10
) { response in
switch response {
case .success(let paymentsResponse):
DispatchQueue.main.async {
completion(paymentsResponse.records, nil)
}
case .failure(let error):
DispatchQueue.main.async {
completion(nil, error)
}
}
}
}
I am trying to use it like so:
firstly {
promiseFetchPayments(for: "XXX")
}.done { records in
print(records)
} .catch { error in
print(error)
}
Now this actually ^^^ works OK!!!! My problem is I want to be able to change done to then and be able to chain another function / response or many more.
But the error I keep getting is:
Cannot conform to Thenable.
I am looking for something very similar to this (I know the syntax isn't right just logically follow the chain....
firstly {
stellar.promiseFetchPayments(for: "")
}.done { records in
print(records)
}.then {
// call some other method
}.done { data in
// more data
}.catch { error in
print(error)
}
Is this actually possible? Can't seem to get any tutorials on the interwebs to compile. Seems Swift compiler really doesn't like PMK syntax or something.
Any ideas?
The problem is because you're chaining off of a done, which doesn't like that you're trying to then do a call to then off of that.
Instead, you'll want to save the promise and use it for the later calls. You can do something like this:
let promise = firstly {
stellar.promiseFetchPayments(for: "")
}
promise.done { records in
print(records)
}
promise.then {
// call some other method
}.done { data in
// more data
}.catch { error in
print(error)
}
You can even return that promise from a method to use in other places, or pass it around to another method.

Contextual closure type ... expects 2 arguments, but 3 were used in closure body

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

How to call a method which takes a closure as a parameter in Swfit?

I try to write a method as below:
class ThisIsExample{
func theMethod(inside:((Error?)->Void)->Void){
//some implementation
}
}
But, when I try to call this method, I don't know how to do that.
I wrote code below:
let example = ThisIsExample()
example.theMethod { ({(err) in }) in
print("print something")
}
I try to write another closure, which is { (err) in } inside the closure
But it is not workable, I'll receive error message like
Contextual closure type '((Error?) -> Void) -> Void' expects 1
argument, but 0 were used in closure body
So...could anyone please teach me how to call this method in correct way, thank you so much.
Although not sure what is the purpose of nested closure. But if you want to use this approach then you should call the closure in this way,
example.theMethod { (closure) in
closure(NSError.init())
}
You can do some thing like this:
func theMethod(inside:(Error?) -> ()) {
print("Closure as paramater")
}
This will take Error as closure parameter and return void.
you can call this function as below:
theMethod { (error) in
}
Something Like This
class NewClass
{
class func myCustomFunc(_ parameter1: String, parameterDict : [String : AnyObject]?, success:#escaping (String) -> Void, failure:#escaping (String) -> Void)
{
/// Just example Bool
var result : Bool = false
/// Get the parameter you sent while calling this Clousure block
let myParamString = parameter1
let paramDict = parameterDict
/// Share the output in case of Success and Failure
if (result){
success("success")
}
else{
failure("Failure")
}
}
}
Usage
NewClass.myCustomFunc("demoStr", parameterDict: [:], success: { (succesString) in
if succesString == "success"{
}
}) { (failureStr) in
if failureStr == "failure"{
}
}
This Function Accepts Parameter and Also give Different blocks depend upon closures

Swift networking with non-void completion handler gets error

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
}

iOS - Add A Void SuccessCallback Block In Swift

I'm pretty new to Swift. How do I define a function that has a VOID success callback block and a error callback block? I don't need to pass any objects on success, but I do need to know my error.
Example of a function using both success and error callback blocks that pass objects:
func searchForTweetsWithKeyword(searchString: String, geoCodeParameter: String, successCallback: (tweets :[Tweet]) ->(), errorCallback: (errorDictionary: Dictionary<String, String>) ->()) {
...do stuff
}
I want to accomplish something like this, but I'm not sure if I'm doing it correctly syntax wise:
func validateNewItemWithDescription(description: String, quantity: Int, successCallback: () ->(Void), errorCallback: (errorString: String) ->())
{
}
Do you definitely need two callback blocks? How about like call:
func parseCommand (inText: String) -> CommandParseResult
With CommandParseResult:
enum CommandParseResult{
case ValidCommand (Command)
case ErrorResponse (String) }
Then handle something like:
var validCommand: Command?
let commandResult = self.myDictionary.parseCommand(textCommand)
switch commandResult
{
case .ErrorResponse (let errorResponse):
completionMessage = errorResponse.stringByReplacingCaseFormat("<Person>", withString: self.currentPerson)
case .ValidCommand (let returnedCommand):
validCommand = returnedCommand
}

Resources