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 these objects.

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?

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 User LIMIT 10")        
    
        RestClient.shared.send(request: request, onFailure: { (error, urlResponse) in
            SalesforceLogger.d(type(of:self), message:"Error invoking: \(request)")
        }) { [weak self] (response, urlResponse) in
            
            guard let strongSelf = self,
                let jsonResponse = response as? Dictionary<String,Any>,
                let result = jsonResponse ["records"] as? [Dictionary<String,Any>]  else {
                    return
            }
            
            SalesforceLogger.d(type(of:strongSelf),message:"Invoked: \(request)")
            
            DispatchQueue.main.async {
                strongSelf.dataRows = result
                strongSelf.tableView.reloadData()
            }
        }
    }
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 final two parameters of the send(request:onFailure:onSuccess:) method. With this method, you provide a closure to process failures and another to process successes. The template code adds the failure block as the onFailure: argument, and the success block as the—wait a minute! Where is the onSuccess: argument? This surprising code uses Swift trailing closure syntax. With this feature, if a function’s last argument is a closure, you can omit that parameter. Instead, you add the closure immediately after the call’s closing parenthesis. Trailing closures simplify your syntax and set the final closure apart as the expected result of the call.

That’s how you send requests and handle responses with success and failure closures. You might have questions. For example, what does this line mean?
{[weak self] (response, urlResponse) in ...
And are there other options? Read on.
Note

Note

RestClient is your entry point to all REST services. Always access RestClient through the shared instance: RestClient.shared()

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 send(request:onFailure:onSuccess:) method
You pass in failure and success closures when you call the send function. Salesforce sends response callbacks to this method call.
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 perhaps more elegant to use the SFRestDelegate protocol—the Objective-C equivalent of RestClientDelegate—instead of inline block callbacks. In Swift apps, however, you have the advantage of trailing closure syntax, which 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:onFailure:onSuccess:) Method

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

  1. The RootViewController template class uses the Mobile SDK REST API to create a SOQL request.
    let request = RestClient.shared.request(forQuery: "SELECT Name FROM User LIMIT 10")
  2. On the next line, it calls the send method to send the request to Salesforce. This method expects closures for the onFailure and onSuccess arguments. For example:
    let request = RestClient.shared.request(forQuery: "SELECT Name FROM User LIMIT 10")
    RestClient.shared.send(request: request, onFailure: { },
        onSuccess: { })
  3. For failures, Mobile SDK sends your onFailure closure the following values:
    • A response object (error) of type NSError
    • A network response (urlResponse) of type NSURLResponse.
    This app logs an error message in the debugger and moves on.
    let request = RestClient.shared.request(forQuery: "SELECT Name FROM User LIMIT 10")
    RestClient.shared.send(request: request, onFailure: { (error, urlResponse) in
        SalesforceLogger.d(type(of:self), message:"Error invoking: \(request)")
    },
        onSuccess: { })
    If the onFailure: closure receives the server callback, the send method skips the onSuccess: closure and returns.
If the onSuccess closure gets the callback, the app can unfurl its logic. As you’ve seen, trailing closure syntax allows you to move the onSuccess argument out of the send(request:onFailure:onSuccess:) method call. Instead, you add the closure after the method’s closing parenthesis.
let request = RestClient.shared.request(forQuery: "SELECT Name FROM User LIMIT 10")
RestClient.shared.send(request: request, onFailure: { (error, urlResponse) in
    SalesforceLogger.d(type(of:self), message:"Error invoking: \(request)")
}){ 
    // We closed the send call after the onError: closure
    // and added the opening brace for an anonymous 
    // trailing closure to handle the success logic.
}
Now we have an easy-to-spot success closure. If the request succeeds, the server sends the following values:
  • A response object (response) of type Dictionary<String, Any>
  • A network response (urlResponse) of type NSURLResponse.
You capture these values in the closure’s capture list.
RestClient.shared.send(request: request, onFailure: { (error, urlResponse) in
            SalesforceLogger.d(type(of:self), message:"Error invoking: \(request)")
}) { [weak self] (response, urlResponse) in
    
}
In the trailing success closure, you can apply the response values to your app’s needs. Notice the use of [weak self] for the implied self reference. Because REST responses arrive asynchronously, you can’t be sure the class that defines the success closure remains in memory when it’s called. To avoid unnecessary retain cycles, you change the implied self reference to weak. In the closure body, check to make sure the self reference is not nil before continuing to use it. For example, you can use a guard statement when trying to unwrap the response. Here’s the success handler from the forceios Swift template.
}) { [weak self] (response, urlResponse) in
            
    guard let strongSelf = self,  // Creates a strong ref by setting 
                                  // weak self (possibly nil) to a constant
        // Parse the entire response into a dictionary
        let jsonResponse = response as? Dictionary<String,Any>,
        // Extract the Salesforce records embedded in the response. 
        // If anything fails, get out of here with a return.
        let result = jsonResponse ["records"] as? [Dictionary<String,Any>]  else {
            return
    }
    // Everything is good with strongSelf and the response.
    // Do what you like with the resulting records.
    SalesforceLogger.d(type(of:strongSelf),message:"Invoked: \(request)")

    DispatchQueue.main.async {
        strongSelf.dataRows = result
        strongSelf.tableView.reloadData()
    }
}

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.

retargeting