How to fulfill nil promise swift - ios

I want to fulfill a promise with nil but I get error message that I cant here is my code
public static func getCurrentDevice() -> Promise<Device>{
if let identity:[String:AnyObject] = auth?.get("identity") as! [String:AnyObject] {
if let uuididentity = identity["uuid"]{
return Promise { fulfill, reject in
Alamofire.request( Router.getDevice(uuididentity as! String) )
.responseObject { (response: Response<Device, NSError>) in
switch response.result{
case .Success(let data):
fulfill(data)
case .Failure(let error):
return reject(error)
}
}
}
}
}
return Promise { fulfill, reject in
fulfill(nil)
}
}
I get compiler error
Cannot invoke initializer for type 'Promise<>' with an argument list of type '((, _) -> _)'

If the promise doesn't return a value you should use () aka Void:
return Promise { fulfill, reject in
fulfill(())
}
If this doesn't work (I didn't test it) you could try annotate it:
return Promise<()> { fulfill, reject in
fulfill(())
}
(Note that () is the only value of type () aka Void)

Here you go. The question mark makes it for you in the return of the method.
public static func getCurrentDevice() -> Promise<Device?> {
//... logic here
let isDeviceEmpty: Bool
if isDeviceEmpty {
fulfill(nil)
} else {
fulfill(device)
}
}

Related

Invalid conversion from throwing function of type '(_) throws -> ()' to non-throwing function type '(Response) -> Void'

I need to handle the api error codes like this and throw error for some status code. But follow code shows the above error. how can i achieve this?
func login(data: [String: Any], completion: #escaping (ResponseModel<SignUpModel>?) -> Void) throws {
NetworkAdapter.request(target: .login(data: data), success: { (response) in
if let responseModel = try? JSONDecoder().decode(ResponseModel<SignUpModel>.self,from: response.data) {
switch responseModel.statusCode {
case 2000:
completion(responseModel)
case 4005:
throw ValidationError.athenticationFailure
case .none,.some:
break
}
completion(responseModel)
} else {
}
})
}
You cannot
throw ValidationError.athenticationFailure
because the request is asynchronous. What you can do is to change the completion type to Result<ResponseModel<SignUpModel>, ValidationError> to return
completion(.success(responseModel))
on success and
completion(.failure(athenticationFailure)
on failure. By the way I buy an u 😉
Update:
Meanwhile – with Swift Concurrency – you are able to throw errors using a ThrowingContinuation
func login(data: [String: Any]) async throws -> ResponseModel<SignUpModel> {
withCheckedThrowingContinuation { continuation in
NetworkAdapter.request(target: .login(data: data), success: { (response) in
do {
let responseModel = try JSONDecoder().decode(ResponseModel<SignUpModel>.self,from: response.data)
switch responseModel.statusCode {
case 2000:
continuation.resume(returning: responseModel)
case 4005:
continuation.resume(throwing: ValidationError.athenticationFailure)
case .none,.some:
continuation.resume(returning: responseModel)
}
} catch {
continuation.resume(throwing: error)
}
})
}
}

RxMoya Network and Service Error handling in the same function

I am trying to implement a function that handles Network & API errors, my problem is how to emit an observable again after filterSuccessfulStatusCodes().
The main issue I have is that I am not sure if this approach is correct, after the first subscribe.
The current error I have in this code is : Extra argument 'onError' in call
func Request<T: Decodable>(_ request: APIManager) ->
Observable<Result<T>> {
provider.rx
.request(request)
.subscribe(onSuccess: { (response) in
try response.filterSuccessfulStatusCodes()
return Observable.just(response)
.subscribe { event in
switch event {
case .success:
.map(RequestResponse<T>.self)
.map { $0.result! }
.asObservable()
.map(Result.success)
.catchError { error in
return .just(.error(withMessage: "Error \(error)"))
}
case .error:
print("error")
}
}
}, onError: { (error) in
print("network request error")
}, onCompleted: {
print("network request on completed")
}).disposed(by: disposeBag)
}
struct RequestResponse<T: Decodable> {
let statusCode: Int
let statusMessage: String
let success: Bool
let result: T?
let errorBag: String?
}
enum Result<T: Decodable> {
case success(T)
case error(withMessage: String)
}
You can try something like, which converts Single to Observable then call filterSuccessfulStatusAndRedirectCodes and you can handle the errors in catchError closure
func Request<T: Decodable>(_ request: APIManager) -> Observable<Result<T>> {
self.sharedProvider.rx
.request(request)
.asObservable()
.filterSuccessfulStatusAndRedirectCodes()
.map(RequestResponse<T>.self)
.map { Result.success }
.catchError { error in
if let moyaError = error as? MoyaError {
return Objservable.error(handleNetworkError(withMoyaError: moyaError))
} else {
return Observable.error(error)
}
}
}

return array of object with Alamofire

In my app I am using AlamofireObjectMapper.
I want to make a method that returns an array of objects. With the help of Alamofire I made a GET request, which gives the response as responseArray.
With using void function array listOfType always has values.
But when I use non-void function that should return array of object MedicineType, array listOfType is nil.
So here is my code.
func getAll() -> [MedicineType] {
var listOfTypes: [MedicineType]?;
Alamofire.request(BASE_URL, method:.get)
.responseArray(keyPath:"value") {(response: DataResponse<[MedicineType]>) in
if let status = response.response?.statusCode {
switch(status) {
case 200:
guard response.result.isSuccess else {
//error handling
return
}
listOfTypes = response.result.value;
default:
log.error("Error", status);
}
}
}
return listOfTypes!;
}
As i said in my comment, you need to do this in closure, instead of return it, because your call for Alamofire is async so your response will be async
This is an example, you need to add your error handle
func getAll(fishedCallback:(_ medicineTypes:[MedicineType]?)->Void){
var listOfTypes: [MedicineType]?;
Alamofire.request(BASE_URL, method:.get)
.responseArray(keyPath:"value") {(response: DataResponse<[MedicineType]>) in
if let status = response.response?.statusCode {
switch(status) {
case 200:
guard response.result.isSuccess else {
//error handling
return
}
finishedCallback(response.result.value as! [MedicineType])
default:
log.error("Error", status);
finishedCallback(nil)
}
}
}
}
Use it
classObject.getAll { (arrayOfMedicines) in
debugPrint(arrayOfMedicines) //do whatever you need
}
Hope this helps
Try closure
func getAll(_ callback :(medicineTypes:[MedicineType]?) -> Void) -> Void {
Alamofire.request(BASE_URL, method:.get)
.responseArray(keyPath:"value") {(response: DataResponse<[MedicineType]>) in
if let status = response.response?.statusCode {
switch(status) {
case 200:
guard response.result.isSuccess else {
//error handling
return
}
listOfTypes = response.result.value;
callback(listOfTypes)
default:
log.error("Error", status);
callback({})
}
}
}
}

How to map RxSwift Observable and Result

I have a quick question:
I have a network request that returns Observable<Result<String, RequestError>>, let’s call it requestToken
if this request succeeds, I want to use the String (token) to do another request that returns Observable<Result<NSDictionary, RequestError>>, let’s call it requestData
when that second request comes back, I wanna merge the token into its dictionary
in the end I wanna map from Observable<Result<String, RequestError>> to Observable<Result<NSDictionary, RequestError>>
How can I achieve that without multiple nested levels in my code?
This is what I have today:
requestToken()
.flatMap({ result -> Observable<Result<NSDictionary, RequestError>> in
switch result {
case .success(let token):
return requestData(token: token).map({ $0.map({ $0 + ["token": token] }) })
case .failure(let error):
return Observable.of(.failure(error))
}
})
Updated:
It's a detailed example, hope this may help:
enum RequestError: Error {
case unknown
}
func requestToken() -> Observable<String> {
return Observable.create { observer in
let success = true
if success {
observer.onNext("MyTokenValue")
observer.onCompleted()
} else {
observer.onError(RequestError.unknown)
}
return Disposables.create()
}
}
func requestData(token: String) -> Observable<[String: Any]> {
return Observable<[String: Any]>.create { observer in
let success = false
if success {
observer.onNext(["uid": 007])
observer.onCompleted()
} else {
observer.onError(RequestError.unknown)
}
return Disposables.create()
}
.map { (data: [String: Any]) in
var newData = data
newData["token"] = token
return newData
}
}
requestToken() // () -> Observable<String>
.flatMapLatest(requestData) // Observable<String> -> Observable<[String: Any]>
.materialize() // Observable<[String: Any]> -> Observable<Event<[String: Any]>>
.subscribe(onNext: { event in
switch event {
case .next(let dictionary):
print("onNext:", dictionary)
case .error(let error as RequestError):
print("onRequestError:", error)
case .error(let error):
print("onOtherError:", error)
case .completed:
print("onCompleted")
}
})
.disposed(by: disposeBag)
Original:
I think it's much easier to achieve it using materialize() with less extra work:
func requestToken() -> Observable<String> { return .empty() }
func requestData(token: String) -> Observable<NSDictionary> { return .empty() }
enum RequestError: Error {}
requestToken()
.flatMapLatest(requestData)
.materialize()
.subscribe(onNext: { event in
switch event {
case .next(let dictionary):
print("onNext:", dictionary)
case .error(let error as RequestError):
print("onRequestError:", error)
case .error(let error):
print("onOtherError:", error)
case .completed:
print("onCompleted")
}
})
.disposed(by: disposeBag)
Hope this may help.
If you use the built in error system, you can save yourself from having to manually pass the error along and all the switches that would entail. You can cast the error at the end.
I would do something more like this:
// this is necessary to handle adding the token to the dictionary.
extension Dictionary {
/// An immutable version of update. Returns a new dictionary containing self's values and the key/value passed in.
func updatedValue(_ value: Value, forKey key: Key) -> Dictionary<Key, Value> {
var result = self
result[key] = value
return result
}
}
// function signatures, note that they don't return Results anymore.
func requestToken() -> Observable<String> { /*...*/ }
func requestData(withToken: String) -> Observable<[String: Any]> { /*...*/ }
requestToken().flatMapLatest {
requestData(token: $0)
.map { $0.updatedValue($0, forKey: "token") }
.map { .success($0) }
}.catchError {
Observable.just(.failure($0 as! RequestError))
}
With the above, the end result would be an Observable<Result<[String: Any], RequestError>> just like in your case, but the error handling is much cleaner.
If you can't change the signatures of the two functions you are using then I would do this:
func throwError<T, U: Error>(result: Result<T, U>) throws -> T {
switch result {
case .success(let token):
return token
case .failure(let error):
throw error
}
}
requestToken().map {
try throwError(result: $0)
}.flatMapLatest {
requestData(token: $0)
.map { try throwError(result: $0) }
.map { $0.updatedValue($0, forKey: "token") }
}
.map { .success($0) }
.catchError {
Observable.just(.failure($0 as! RequestError))
}

How to use standard result type from Alamofile with no-type Success?

I'm trying to reuse Alamofire's Result type for own API callbacks.
Here is a shortened version of result type I'm using:
public enum Result<Value> {
case Success(Value)
case Failure(NSData?, ErrorType)
}
So for my API calls I'm using it in completion blocks:
func getUserContent(userId: String, completion: (result: Result<User>) -> Void) {
Alamofire.request(UserRouter.GetUser(userId))
.validate()
.responseJSON { (request, response, result) -> Void in
switch result {
case .Failure(_, let error):
completion(result: .Failure(nil, error))
case .Success(let value):
if let responseDict = value as? [String: AnyObject] {
do {
// synchronous call which parses response and
// creates User struct instance or throws exception
let user = try self.processUserResponse(responseDict)
completion(result: .Success(user))
} catch(let error) {
completion(result: .Failure(nil, error))
}
} else {
completion(result: .Failure(nil, MyAPIError.WrongResponseFormat))
}
}
}
}
I think its perfectly fits here but there is one issue. I have some calls with completion blocks which supposed to return either .Success with no value or .Failure.
E.g. deleteUser method should look something like:
func deleteUser(userId: String, completion: (result: Result<nil>) -> Void) {
// ... do some stuff here
}
so when I call it later I can do:
deleteUser(userId) { (result) -> Void in
switch result {
case .Success:
print("success")
case .Failure(nil, let error):
print("Error: \(error)")
}
}
But I can't create "empty" .Success. Result<nil> of course gives me a compile error. But I don't have any type to pass to some of .Success cases. Does anyone has a better solution that defining another Result Type with no type on .Success?
#ogreswamp is right! You can omit the type requirement with Void. Type Void is simply an empty tuple, in effect a tuple with zero elements, which can be written as (). Here is an example:
enum Result<T, E: ErrorType> {
case Success(T)
case Failure(E)
init(value: T) {
self = .Success(value)
}
init(error: E) {
self = .Failure(error)
}
}
Use this like
enum AuthenticationError: ErrorType {
case MissingEmail
case InvalidPassword
}
func signUp(email email: String, password: String) -> Result<Void, AuthenticationError>
You can return the result like
// Success
return Result(value: ())
// Failure
return Result(error: .InvalidPassword)
And finally, check the result
switch result {
case .Success(_):
print("Request SignUp was sent")
case .Failure(let error):
switch error {
case .InvalidEmail:
print("Invalid email")
default:
break
}
}
You will need to define your own Result type. Also note that Alamofire 3.0 uses a much different Result type that may better suit your needs.

Resources