Decoding an Error Response using Alamofire 5 and the responseDecodable function - alamofire

I'm writing an API Client for WooCommerce using Alamofire 5 (beta 1) which will allow me to get orders, coupons etc as well as create them. Note I am using the new .responseDecodable function.
I've set up my API client using the following performRequest function that looks like this:
#discardableResult
private static func performRequest<T:Decodable>(route: APIConfiguration,
decoder: JSONDecoder = JSONDecoder(),
completion: #escaping (Result<T>)->Void) -> DataRequest {
return AF.request(route)
.responseDecodable(decoder: decoder) { (response: DataResponse<T>) in
completion(response.result)
}
}
This works well, since I can, for instance, call a function getCouponForId(_ id: Int) which will execute this function and have the response returned through the completion handler.
The only downfall is that, say the user tries to access a coupon that does not exist, they will receive an error (404 from the server). I can switch on the result to determine either a success or failure case, but Alamofire attempts to decode the body of the error response into the Coupon model I have created.
Going forward, I have created an error model which I intend to have the error decoded using. But with that said, I'm having trouble implementing it into this function.
Does anyone have any ideas on how I could handle this?
(I have created this function through following this guide - hopefully, it might provide a bit more context to what I'm doing. https://github.com/AladinWay/NetworkingExample)

Similiar login function from the article you mentioned, updated for current beta of Alamofire 5 and dealing with 404, 401 etc. (via the guard statement)
static func login(email: String, password: String, completion:#escaping (Result<UserCredentials>)->Void) {
performRequest(router: Router.login(email: email, password: password), completion: completion)
AF.request(Router.login(email: email, password: password))
.validate(statusCode: 200..<300)
.responseDecodable { (response: DataResponse<UserCredentials>) in
guard response.result.isSuccess else {
print("🥶 Error on login: \(String(describing: response.error))")
return
}
completion(response.result)
}
}

Just update
request.responseDecodable(decoder: decoder, completionHandler: { (response: AFDataResponse<T>) in
to
request.responseDecodable(decoder: decoder, completionHandler: { (response: DataResponse<T>) in

Related

How to handle simple error in Kotlin function Android

I'm having trouble handling error in the following function. I'm basically new to Kotlin. Here's my RevenueCat Login Code and I want to handle ::error in this code:
Purchases.sharedInstance.logInWith(
myUserID,
::error // <- How to handle this? I want to retrieve error Code and Error Message.
)
{ customerInfo, created ->
// Handle Successful login here
}
Here's the code behind the function (within RevenueCat SDK)
#Suppress("unused")
fun Purchases.logInWith(
appUserID: String,
onError: (error: PurchasesError) -> Unit = ON_ERROR_STUB,
onSuccess: (customerInfo: CustomerInfo, created: Boolean) -> Unit
) {
logIn(appUserID, logInSuccessListener(onSuccess, onError))
}
The double colon in ::error is a function reference. It is basically a reference to the function error().
And from your logInWith() function, we have onError: (error: PurchasesError) -> Unit = ON_ERROR_STUB, meaning that the function should take PurchasesError as input parameter and does not need to return.
So we can derive a function as the following:
fun error(error: PurchasesError) {
// And you can do something with the error here
}
I solved it like this:
Purchases.sharedInstance.logInWith(
myUserID,
onError = { error ->
// Handle error here
}

Checking authorizations for MPMediaLibrary in Swift 3

I am using the following code to check the MPMediaLibrary authorizations:
func handlePermissions() {
let permissionStatus = MPMediaLibrary.authorizationStatus()
switch (permissionStatus) {
case MPMediaLibraryAuthorizationStatus.authorized:
print("permission status is authorized")
case MPMediaLibraryAuthorizationStatus.notDetermined:
print("permission status is not determined")
MPMediaLibrary.requestAuthorization(MPMediaLibraryAuthorizationStatus -> permissionStatus)
case MPMediaLibraryAuthorizationStatus.denied:
print("permission status is denied")
case MPMediaLibraryAuthorizationStatus.restricted:
print("permission status is restricted")
}
}
Ultimately, I am trying to prompt the user for their authorization (upon launch) prior to calling a query...via the case MPMediaLibraryAuthorizationStatus.notDetermined:. The code above produces the error: Expected type after '->'. When the requestAuthorization() line is commented out, the app crashes upon launch (access has not been authorized) and the authorization prompt view is shown after the launch screen disappears.
I've seen some examples of how to perform requestAuthorization() in Objective C but nothing in Swift. I don't understand:
MPMediaLibrary.requestAuthorization( handler: (MPMediaLibraryAuthorizationStatus) -> Void )
What is the proper way to request authorization for access to the MPMediaLibrary in Swift 3?
You've actually used the prototype of the requestAuthorization method. You need to adapt it to your own use.
MPMediaLibrary.requestAuthorization( handler: (MPMediaLibraryAuthorizationStatus) -> Void )
means that requestAuthorization take a function as parameter and this function takes a MPMediaLibraryAuthorizationStatus as parameter an return nothing.
For example if I want to request the authorisation and then display the result inside my console. I first check if the application is not already authorised :
if authoriationStatus != .authorized {
MPMediaLibrary.requestAuthorization({
(status) in
switch status {
case .notDetermined:
print("notDetermined")
case .denied:
print("denied")
case .restricted:
print("restricted")
case .authorized:
print("authorized")
}
})
}
As you can see, I used a function as a parameter for the method requestAuthorization. The function is described inside {...}. It's called a closure and it's something you always use in Swift.
For swift 4.2 to check authorisations for MPMediaLibrary
import MediaPlayer
let status = MPMediaLibrary.authorizationStatus()
switch status {
case .authorized:
self.openMusicLibrary()
break
case .notDetermined:
MPMediaLibrary.requestAuthorization() { status in
if status == .authorized {
DispatchQueue.main.async {
self.openMusicLibrary()
}
}
}
break
case .denied:
//show alert
print("Please Allow Access to the Media & Apple Music from appliction setting.")
break
case .restricted:
break
}

Cannot call value of non-function type 'HTTPURLResponse?

It is giving me the Error: Cannot call value of non-function type 'HTTPURLResponse?'
It should be caused by #escaping but I cannot make it work in the sentence below. Please help, Swift 3.0, AlamofireImage.
There are other similar answers but cannot make them work with my code below.
func getNetworkImage(_ urlString: String, completion: #escaping ((UIImage) -> Void)) -> (ImageRequest) {
let queue = decoder.queue.underlyingQueue
let request = Alamofire.request(urlString)
let imageRequest = ImageRequest(request: request)
imageRequest.request.response(
queue: queue,
responseSerializer: Request.imageResponseSerializer(),
completionHandler: { response in
guard let image = response.result.value else {
return
}
let decodeOperation = self.decodeImage(image) { image in
completion(image)
self.cacheImage(image, urlString: urlString)
}
imageRequest.decodeOperation = decodeOperation
}
)
return imageRequest
}
The error message: Cannot call value of non-function type 'HTTPURLResponse?' is telling you that the instance variable value can not be accessed. The bad thing is that it is telling you this in the wrong line of code. That may be the reason why it was so difficult to find.
The structure of the response object has changed. You code:
guard let image = response.result.value else {
return
}
Won't work anymore because the response object is of type DefaultDataResponse not HTTPResponse...
In order to access the data/image you need to go with response.response. depending on what you want to access:
response.response?.value(forKey: "")
Check the other method calls and properties. I believe you will find the data you are looking for.

Establish callback in Swift for PubNub 4.0 to receive messages

It appears to me that the documentation PubNub has for getting started in Swift don't apply to versions earlier than PubNub 4.0. I can't successfully establish a callback to register with PubNub.
My code:
class Communicator: NSObject, PNObjectEventListener {
var pubNubClient: PubNub
override init(){
let config = PNConfiguration(
publishKey: "my_publish_key",
subscribeKey: "my_subscribe_key"
)
pubNubClient = PubNub.clientWithConfiguration(config);
super.init()
pubNubClient.addListener(self)
pubNubClient.subscribeToChannels(["my_channel"], withPresence: false)
}
func didReceiveMessage(client: PubNub!, message: PNMessageResult!){
/* THIS METHOD NEVER GETS REACHED */
}
}
Digging into the PubNub source a bit, this is the area that seems to be having problems:
- (void)addListener:(id <PNObjectEventListener>)listener {
dispatch_async(self.resourceAccessQueue, ^{
if ([listener respondsToSelector:#selector(client:didReceiveMessage:)]) {
/* this block is never reached!!! */
[self.messageListeners addObject:listener];
}
/* Remaining Lines Stripped Away */
});
}
I'm still relatively new to Swift and integrating with Objective C. I'm curious if there's a problem with the respondsToSelector since the Objective C code is referencing Swift code.
The messages are definitely getting passed; there's another lower level function in the PubNub library that's logging all the messages received.
Any help would be much appreciated.
Versions prior to 4.0 are deprecated and wont work exactly how they used to.
I would recommend migrating over to the newest (4.0) SDK entirely, the new iOS SDK has removed a lot of bloat and compiles much faster. To get started view this tutorial.
To summarize, instantiating a PubNub client look as follows:
let config = PNConfiguration(
publishKey: "Your_Pub_Key",
subscribeKey: "Your_Sub_Key")
client = PubNub.clientWithConfiguration(config)
client?.addListener(self)
client?.subscribeToChannels(["Your_Channel"], withPresence: false)
And the new didReceiveMessage function looks as follows:
func client(client: PubNub!, didReceiveMessage message: PNMessageResult!, withStatus status: PNErrorStatus!) {
//Do Something like
//println(message)
}
Resolved by adding:
func client(client: PubNub!, didReceiveMessage message: PNMessageResult!) {
}
The documentation on how to parse the received PNMessageResult is scant. Here's how I handled it:
func client(client: PubNub!, didReceiveMessage message: PNMessageResult!) {
let encodedMessage = message.data.valueForKey("message") as! NSDictionary
let messageType = encodedMessage["meta"]! as! String
let messageString = encodedMessage["data"]!["msg"]! as! String
print("PubNub: [\(messageType)] \(messageString)")
}
add _ client works for me!
func client(_ client: PubNub, didReceiveMessage message: PNMessageResult) {
print("Pubnub Message: \(message)")
}

Alamofire put request

Try to get Alamofire put request to work, but system shows "Extra Argument in Call"
Alamofire.request(.PUT, apiUrl,params,ParameterEncoding.JSON)
.responseJOSN{ (request, response, products: [Product]?,error) in
println(request)
println(response)
println(data)
println(error)
}
Anyone can solve this problem?
You have multiple issues in your code sample. Here's a corrected version that should get you going:
let apiURLString = "whatever/your/url/is"
let parameters: [String: AnyObject] = [:] // fill in your params
let request = Alamofire.request(.PUT, apiURLString, parameters: parameters, encoding: .JSON)
request.responseJSON { request, response, json, error in
println(request)
println(response)
println(json)
println(error)
}
I would also encourage you to really read through the Alamofire README in depth. It has some great information and should make it much easier for you to get the basics working.
As 'alamofire' '~2.0' has changed the in number of parameters
You can try with the following block of code:
Alamofire.request(.PUT,apiUrl,params).responseJSON { request, response, result in
print(request)
print(response)
print(result.value)
if(result.isSuccess){
//Do in success block
}else{
//Do in failure block
}
}
Alamofire.request does not have error handler parameter available, hence it was showing you that "Extra Argument in Call"