I'm building a class to act as my API helper and I've nested an enum inside my class to show the response state:
class API {
enum Response {
case Success, Error, ServerError, ClientError
}
func load(handleResponse ((API.Response, JSON?) -> Void)) {
// Load in data with Alamofire
// Call handleResponse(.Success, data) at some point
}
}
To use my API, I am trying to write something like the following:
API.load { (status: API.Response, data: JSON?) in
...
}
I'm getting an error as follows with my code above:
Cannot convert value of type '(API.Response, JSON?) -> ()' to expected argument type 'API'
It seems like the '.' in the type is a problem, but I'm not sure how to fix it.
Please try this
class API {
enum Response {
case Success, Error, ServerError, ClientError
}
class func load(handleResponse: ((API.Response, JSON?) -> ())) {
}
}
API.load { (response, json) -> () in
}
Related
In my iOS project, I want to do the following:
create an Encodable class (named ChannelAnswer) which has multiple attributes, including another generic Encodable object
pass an instance of that class as an argument of a function or return it from a function
Here is what I tried:
class ChannelAnswer<T> : Encodable where T : Encodable
{
let errorCode: String?
let data: T?
let origin: Int = 2
init(_ errorCode: String?, _ data: T? = nil)
{
self.errorCode = errorCode
self.data = data
}
}
Now, if I want to return an instance of that class from a function, like the following:
func test() -> ChannelAnswer
{
...
return ChannelAnswer("abc", anyEncodableObject)
}
I get the following error:
Reference to generic type 'ChannelAnswer' requires arguments in <...>
The thing is: the data attribute could be of any type, I only know that that type is Encodable (the test()function above is just an example for the sake of simplicity).
So how can I create my ChannelAnswer class and successfully pass it as an argument of a function or return it from a function?
Thanks.
What you need is a generic method.
func test<T: Encodable>(data: T) -> ChannelAnswer<T> {
// ...
return ChannelAnswer("abc", data)
}
I have a pseudo RxSwift implementation of a repository pattern that supports offline and remote operations. I'm trying to migrate it from using our homegrown implementation of Dynamic<T> to using promises from the PromiseKit library; the code I'm trying to migrate looks like the following:
class Repository {
private var realmRepo: RealmRepo!
private var httpRepo: HTTPRepo!
private var offlineCache: OfflineCache!
func complete(step: Entity, options: Options) -> Dynamic<Entity> {
let result = Dynamic<Entity>()
realmRepo.complete(step: step, options: options).do(onNext: {[result] value in
guard let realmStep = value else {
return result.complete(with: Error.unknown("realm result was `nil`"))
}
self.httpRepo.complete(step: realmStep, options: options).do(onNext: { value in
result.complete(with: value)
}).do(onError: { error in
switch error {
case HTTPRepo.Error.noNetworkConnection(let request):
try? self.offlineCache.add(object: createOfflineObject(request as! URLRequest))
result.complete(with: realmStep)
default:
result.complete(with: error)
}
})
}).do(onError: {[result] error in
result.complete(with: error)
})
return result
}
The Dynamic<T> class looks like the following (implementation omitted):
class Dynamic<T> {
func `do`(onNext: (T?) -> Void) -> Dynamic<T> // returns `self`
func `do`(onError: (Error) -> Void) -> Dynamic<T> // returns `self`
func complete(with: Error)
func complete(with: T?)
}
I'm trying to rewrite the return values for the repository from Dynamic<Entity> to Promise<Entity>, using the PromiseKit library, I'm not sure how to replicate the following chain of events using promises:
Attempt the Realm call, if it fails send an error to the returned dynamic object (or fail the promise)
If the RealmRepo.complete call succeeded, then attempt the HTTPRepo.complete call.
If the HTTPRepo.complete call succeeds then emit a "next" value to the dynamic (promise succeeds)
If the HTTPRepo.complete call fails then catch the error which will have the failure reason and if it was a lack of network connection then perform another call to the OfflineCache.add method and resolve the Dynamic value with the result from the RealmRepo.complete call.
So far I've managed the following:
import PromiseKit
// ... other code ...
class Repository {
// ... repository fields ...
func complete(step: Entity, options: Options) -> Promise<Entity> {
return self.realmRepo.complete(step: step, options).then {
return self.httpRepo.complete(step: $0, options: options)
}.catch { error in
switch error {
case HTTPRepo.Error.noNetworkConnection(let request):
try? self.offlineCache.add(object: createOfflineObject(request as! URLRequest))
default:
result.complete(with: error)
}
}
}
}
Such code gives me a compile error in the catch block, I'm not even sure of how I'd handle or recover from the error that httpRepo would return if no connection is present.
Any help is really appreciated!
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
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 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.