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)
}
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With