Start tracking your progress
Trailhead Home
Trailhead Home

Access Salesforce Data through REST APIs

Learning Objectives

After completing this unit, you'll be able to:

  • Issue a REST request with the RestClient interface.
  • Receive the REST response from Salesforce.
  • Use Mobile SDK to process the REST response.

Sending REST Requests

The RootViewController class gives you a glimpse of how REST API classes work in Mobile SDK. Let’s take a closer look at how you use REST API classes.

Using RestClient and RestRequest Objects

To access Salesforce data through Mobile SDK REST APIs, you send requests to Salesforce and receive the responses in your app. How does RootViewController send network requests?

RestClient is your entry point to all REST services. Mobile SDK apps use RestClient methods to manufacture and send REST requests. You always access this class only through its shared instance:

RestClient.shared()

Here’s the standard “send request” sequence demonstrated in the loadView() method.
  1. Create and initialize a RestRequest instance. RestClient provides a large assortment of factory methods for this purpose. Our example creates a SOQL query request, but you can choose any RestClient method that suits your purpose.
  2. Send the request object to Salesforce. RestClient automatically authenticates the request with the current user’s credentials.
Let's examine the code.
  1. In Xcode, open the RootViewController.swift file.
  2. Search for the loadView() method. The implementation reads something like the following:
    override func loadView() {
        super.loadView()
        self.title = "Mobile SDK Sample App"
        let request = RestClient.shared.request(forQuery: "SELECT Name FROM Contact LIMIT 10")        
        RestClient.shared.send(request: request) { [weak self] (result) in
            switch result {
                case .success(let response):
                    self?.handleSuccess(response: response, request: request)
                case .failure(let error):
                     SalesforceLogger.d(RootViewController.self, 
                         message:"Error invoking: \(request) , \(error)")
            }
        }
    }
After calling the superclass method and setting the view title, this method creates and sends a REST request. To do so, it uses
  • The RestClient shared instance to generate a request object to be sent to over the network to Salesforce.
  • The generated RestRequest object, which contains a preformatted REST API request based on the given SOQL string.
To process REST responses, RootViewController uses the following method:
send(request: Request, _ completionBlock: @escaping (Result<RestResponse, RestClientError>) -> Void)
With this method, you provide a trailing closure to process either success or failure. If you aren’t familiar with trailing closure syntax, here’s how it works. A closure is, syntactically, a function block. It can take arguments and return a value. If the type of a function’s last argument is a closure, you can drop that item from the parameter list. Instead, you add the closure immediately after the call’s closing parenthesis. A trailing closure simplifies your syntax and stands alone as the expected result of the function call it trails.

In this case, the trailing closure determines the response status by reading the result argument’s success and failure members. In the success case, the closure receives the response data and passes it off to a separate handling function. In the error case, the closure receives the error message, logs it, and returns.

That’s how you send requests and route responses using a completion closure. You might have questions. For example, what does this line mean?
{[weak self] (response, urlResponse) in ...
And are there other options? Read on.

Handling REST Responses

Receiving and consuming data from Salesforce requires the ability to intercept asynchronous network callbacks. Mobile SDK defines two options for this task:

RestClient method
You provide a trailing closure when you call the send function. Salesforce sends the server response, wrapped as an iOS object, to your closure.
RestClientDelegate protocol
You define four callback functions that receive success and failure responses. When your app sends a REST request, it designates the class that implements this protocol as the target for the server’s response.

In Objective-C, it’s more elegant to use the SFRestDelegate protocol—the Objective-C equivalent of RestClientDelegate—instead of inline block callbacks. In Swift apps, however, the advantage of trailing closure syntax offers an even better solution. This technique results in a natural, readable flow from request formation to response consumption. Inline closures are also easier to code than the RestClientDelegate protocol.

Using the RestClient send(request:_:) Method

Let’s take a closer look at how the Swift template app codes the send(request:_:) method.

  1. The RootViewController template class uses the Mobile SDK RestClient factory method to create a SOQL request.
    let request = RestClient.shared.request(forQuery: "SELECT Name FROM User LIMIT 10")
  2. The next line calls the send method to forward the request to Salesforce. This method expects a closure argument to receive the server’s response and handle success or failure. For example:
    let request = RestClient.shared.request(forQuery: "SELECT Name FROM User LIMIT 10")
    RestClient.shared.send(request: request) 
    { [weak self] (result) in
        switch result {
            case .success(let response):
                self?.handleSuccess(response: response, request: request)
            case .failure(let error):
                SalesforceLogger.d(RootViewController.self, 
                    message:"Error invoking: \(request) , \(error)")
        }
    }
    Now you have an easy-to-spot completion closure, added just after the method’s closing parenthesis.
  3. When the response arrives, the closure’s capture list receives a response object (result).
    { [weak self] (result) in
        
    }
    Notice the explicit use of [weak self] to replace the implied self reference. Because REST responses are asynchronous, you can’t trust the class that defined the success closure to still be in memory when the response arrives. To avoid unnecessary retain cycles, you use [weak self] and then validate self in the closure before using it.
  4. If the response indicates success, your app can continue its intended flow—to display a list of contact names in its table view. The template app hands off the response to another local method, handleSuccess(response:request:):
    case .success(let response):
        self?.handleSuccess(response: response, request: request)
  5. For failures, Mobile SDK sends your .failure case a response object (error) of type Error. This app logs an error message and then returns.
    case .failure(let error):
        SalesforceLogger.d(RootViewController.self, 
            message:"Error invoking: \(request) , \(error)")
            // Nothing more to do, so just return
Here’s the handleSuccess(response:request:) function.
func handleSuccess(response: RestResponse, request: RestRequest) {
    guard let jsonResponse  = try? response.asJson() as? [String:Any], 
        let records = jsonResponse["records"] as? [[String:Any]]  else {
            SalesforceLogger.d(RootViewController.self, 
                message:"Empty Response for : \(request)")
            return
    }
    SalesforceLogger.d(type(of:self), message:"Invoked: \(request)")
    DispatchQueue.main.async {
        self.dataRows = records
        self.tableView.reloadData()
    }
}
This function takes the response object and also the original request object. If all goes as expected, Mobile SDK sends the .success case a response object (result) of type Dictionary<String, Any>. However, even if a request succeeds, the server can return a valid but unusable result—for example, if you’ve sent a request for delete. The handleSuccess(response:request:) function prepares for the worst by using a guard statement to convert the response to the expected type. If the conversion succeeds, this function stores the requested records in its dataRows local variable and then tells the table view to reload its data. Where does the table view get its data? That’s right—from the dataRows variable.

Using the RestClientDelegate Protocol

Besides offering code clarity, the RestClientDelegate parses a failure’s category and sends the failure response to a category-specific handler. If your class adopts the RestClientDelegate protocol, it implements four response handler functions.

  • request(_:didLoadResponse:)—Request was processed. The delegate receives the response in JSON format. This callback indicates success. It contains the same code that you’d put in the success closure of the send(request:onFailure:onSuccess:) method.
  • request(_:didFailLoadWithError:)—Request couldn’t be processed. The delegate receives an error message. In the non-delegate send(request:onFailure:onSuccess:) method, you’d catch this error in the onFailure closure.
  • requestDidCancelLoad(_:)—Request was canceled due to some external factor, such as administrator intervention, a network glitch, or another unexpected event. The delegate receives no return value. In the non-delegate send(request:onFailure:onSuccess:) method, you’d catch this failure in the onFailure closure.
  • requestDidTimeout(_:)—The Salesforce server failed to respond in time. The delegate receives no return value. In the non-delegate send(request:onFailure:onSuccess:) method, you’d catch this failure in the onFailure closure.

Mobile SDK delivers success responses to the request(_:didLoadResponse:) callback. If you’re worried about how to decode the network response, fear no more: by default, Mobile SDK formats the response as easy-to-read JSON in a dictionary object. That dictionary contains a “records” key whose value is an array of Salesforce records. Error, cancellation, and timeout conditions each go to their own handlers, making it easier to implement specific actions based on failure types.

And, voilà—we’ve come full circle. We’ve initialized the app, logged in a user, issued a REST request, and displayed the REST response. Now the app is at rest, since it doesn’t know how to do anything more. Let's make your app a bit more robust by adding support for user interactions, like swiping left to delete.