aws-amplify / aws-sdk-ios

AWS SDK for iOS. For more information, see our web site:
https://aws-amplify.github.io/docs
Other
1.68k stars 885 forks source link

How to access REST API response when result is of AWSTask type? #1389

Closed motivus closed 5 years ago

motivus commented 5 years ago

State your question I'm having trouble figuring out how to access result when using the sample code provided in the SDK README.md. result is derived from task, which is of type AWSTask. Presumably AWSTask comes from the AWSCore library, but I'm afraid I'm new enough to Swift and the iOS SDK that I'm a bit lost. Would you kindly help me understand which AWSTask and how to access my Lambda response?

Which AWS Services are you utilizing? API Gateway, Lambda

Provide code snippets (if applicable) Here's my code that invokes my REST API, which is based on the "calc" REST API featured in the API Gateway documentation. This is pretty much what is in the README.md:

    @IBAction func userInvokeApi(_ sender: UIButton) {
        print("You clicked invoke api...")
        let client = SVTLambdaGateClient.default()
        client.calcGet(operand2: "3", _operator: "+", operand1: "5").continueWith{ (task: AWSTask?) -> AnyObject? in
            if let error = task?.error {
                print("Error occurred: \(error)")
                return nil
            }

            if let result = task?.result {
                // Do something with result
                print("The raw result is... \(result)")
            }

            return nil
        }
    }

Now, I've verified that the REST API is being successfully called and it is returning the correct result to the Client. Obviously, all I'm doing here is to print the address of result. I'm struggling to understand how to access the "properties" of result, which I would expect to be a, b (for the operands), op (for the operator), and c for the result.

This is what I see when I set a breakpoint on the print statement:

Screen Shot 2019-03-31 at 10 06 55 AM

I've actually posted a full treatment of my problem to the Swift subreddit if you'd like to see more detail.

Environment(please complete the following information):

Device Information (please complete the following information):

If you need help with understanding how to implement something in particular then we suggest that you first look into our developer guide. You can also simplify your process of creating an application, as well as the associated backend setup by using the Amplify CLI.

rohandubal commented 5 years ago

Hello @motivus

Sorry about the confusion you are having when using AWSTask. Here is a stackoverflow post which gives an example of how to use it for asynchronous jobs: https://stackoverflow.com/questions/44688027/what-is-the-proper-way-to-work-with-awstask-objects-in-swift

As a summary-

the task object has two properties: result and error and they are mutually exclusive. When a task completes successfully, the task.result will have the response of the API. If its an error, task.error will be set. You have to use these properties in the completion handler.

Please let us know if you have more questions.

Thanks, Rohan

motivus commented 5 years ago

Regarding your summary of AWSTask and the task object... I do understand that. I'm trying to print result (because a result IS returned after my call to calcGet), but I don't know how to. So, I'm afraid this might be a dumb Swift question from yours truly.

The stackoverflow post is helpful in terms of showing how result and error are what's returned in calls to various AWS services. But in my case, I'm calling API Gateway, and Cloudwatch tells me my REST API call was a success. I can see that the Lambda function is returning the proper values as JSON. My REST API is passthrough on the response. So, I presume printing result should show me the response from Lambda.

Basically, I'm expecting result to have the properties sent back from the Lambda call; namely, the operands, the operator, and the result. How do I access them?

Note: I added my own question on this topic at stackoverflow: https://stackoverflow.com/questions/55542718/what-is-the-proper-way-to-work-with-api-gateway-awstask-objects-in-swift

motivus commented 5 years ago

I am seriously (and embarrassedly) at a standstill here... in fact, I've been stuck for 2 months now since trying to make the switch from using AWS for javascript-based web apps to attempting the same for iOS / mobile apps.

frankmuellr commented 5 years ago

@motivus - What happens when you do this?

if let JSONDictionary = task.result as? NSDictionary {
    print("Result: \(JSONDictionary)")
    print("resultKey: \(JSONDictionary["resultKey"])")
}
motivus commented 5 years ago

Thanks @muellerfr - I'm afraid nothing prints out. But here's what happens:

Firstly, Xcode gives me the following error if I don't put a question mark after task in the first line:

Value of optional type 'AWSTask<Empty>?' must be unwrapped to refer to 
member 'result' of wrapped base type 'AWSTask<Empty>'
Chain the optional using '?' to access member 'result' only for non-'nil' base values... |Fix|

Choosing 'Fix' results in this change:

if let JSONDictionary = task?.result as? NSDictionary {

Having made that correction, I then get the following warning:

Cast from 'Empty?' to unrelated type 'NSDictionary' always fails

I get the following warning for the print("resultKey...") line with two options to Fix:

String interpolation produces a debug description for an optional value; did you 
mean to make this explicit?
Use 'String(describing:)' to silence this warning... |Fix|
Provide a default value to avoid this warning... |Fix|

I left the two warnings and built the project anyway. Clicking my 'Invoke API' button in the Simulator, I'm afraid nothing prints out.

Here is a screenshot from Xcode:

Screen Shot 2019-04-25 at 9 10 56 PM

And here is a screenshot of my Simulator window.. just so you can see how I'm invoking.

Screen Shot 2019-04-25 at 9 10 09 PM
frankmuellr commented 5 years ago

Please try removing the ? after AWSTask in line 83, and then comment out line 84 - 93. Now remove the ? after task in line 95.

What do you see?

motivus commented 5 years ago

I get the same (fatal) warning message as before, unfortunately.

Here's a screenshot showing the same messages with the altered code:

Screen Shot 2019-04-26 at 5 05 42 PM
palpatim commented 5 years ago

This isn't really a Swift/ObjC interoperability issue, it's that the task's result type is an Empty, rather than whatever result type your API is defining. Your API Gateway-generated code for client.calcGet should have a response type based on the model you've assigned in your API Gateway setup. If you've not set up a response type in the resource, then the response would be Empty, as you see here.

In the calc tutorial you reference, there is the following note:

By default, the method response body is assigned an empty model. This will cause the integration response body to be passed through without mapping. However, when you generate an SDK for one of the strongly-type languages, such as Java or Objective-C, your SDK users will receive an empty object as the result. To ensure that both the REST client and SDK clients receive the desired result, you must model the response data using a predefined schema. Here you'll define a model for the method response body and to construct a mapping template to translate the integration response body into the method response body.

I think that may be what's causing your issue. Can you ensure you've followed these steps, re-generate and re-integrate the SDK, and see if that solves your issue?

motivus commented 5 years ago

Thanks @palpatim. Interesting. It was my expectation that the task's result property would be an Empty because that is what the API is defined to return. And I remember reading that section you highlighted several times to make sure "I was doing it right." I was thinking Swift at the time, not Obj-C. But now I see that I likely need to specifically model the response back to the Client so that an Empty is not what's returned.

I will look into doing this and report back. I very much appreciate your calling that to my attention!!