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

In your custom views, you access Salesforce org data through the Salesforce API. Let’s take a closer look at how you call the Salesforce API in Mobile SDK Swift apps.

Using the RestClient Publisher

To access Salesforce data, you send REST requests to Salesforce and receive the responses in your app. How do you code network requests, send them, and process their responses?

Swift UI apps use view-model architecture. A view class defines screen layouts in SwiftUI and, at runtime, calls an associated model class for the data that populates their interfaces. In the Mobile SDK Swift template project, model classes are in the Models project folder. For example, the ContactsForAccountModel.swift file defines the code that requests a list of contacts for an account. When the response arrives, the ContactsForAccountModel class processes the returned records to send a list of contact information to the ContactsForAccountListView class. 

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 fetchContactsForAccount(_:)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 ContactsForAccountModel.swift file.
  2. Search for the fetchContactsForAccount(_:)method. The implementation reads something like the following:
    class ContactsForAccountModel: ObservableObject {
        @Published var contacts: [Contact] = []
        var account: Account?
        private var contactsCancellable: AnyCancellable?
        func fetchContactsForAccount(){
            guard let acct = self.account else {return}
            let request = RestClient.shared.request(forQuery: 
                "SELECT id, firstName, lastName, phone, 
                email, mailingStreet, mailingCity, 
                mailingState, mailingPostalCode 
                FROM Contact 
                WHERE AccountID = '\(acct.id)'", apiVersion: nil)
            contactsCancellable = RestClient.shared.publisher(for: request)
                .receive(on: RunLoop.main)
                .tryMap({ (response) -> Data in
                    response.asData()
                })
                .decode(type: ContactResponse.self, decoder: JSONDecoder())
                .map({ (record) -> [Contact] in
                    record.records
                })
                .catch( { error in
                    Just([])
                })
                .assign(to: \.contacts, on:self)
            }
        }
    }
This method creates a REST request, has it sent to Salesforce, and processes the response. To create the request object, it uses the RestClient shared instance. You can see this call in the long statement that begins with: 
let request = RestClient.shared.request(forQuery:... 
The resulting RestRequest object contains a pre-formatted REST request based on the given SOQL string.

How the request is sent, and the response received, are somewhat less obvious. This example simplifies both of those steps by taking advantage of a Mobile SDK Swift Combine publisher--specifically, the RestClient publisher. When you call this publisher, in effect you simply tell it, "here is my request." The publisher then handles the network call to send your request to Salesforce and sorts out the network response before delivering it back to your waiting code.

You can see the publisher call in the line that begins:

contactsCancellable = RestClient.shared.publisher(for: request)

You might recognize the sequence of calls that follows as a call chain, which is a common and useful construction for Swift Combine publishers. This call chain handles the asynchronous network response as a Future object that eventually either finishes or fails. In each of the chain of calls, the action is performed on the current default object. This object can be reset by the return value of the previous call in the chain. If the network call failed or Salesforce returned an error, the processing falls through to the .catch block. 

For a detailed sense of how the call chain works, here's a generously commented listing of the sequence.

// Use the RestClient publisher to send the request and return the raw REST response
contactsCancellable = RestClient.shared.publisher(for: request)
    // Receive the raw REST response object
    .receive(on: RunLoop.main)
    // Try to convert the raw REST response to Data
    // Throw an error in the event of failure
    .tryMap({ (response) -> Data in
        // Return the response as a Data byte buffer
        response.asData()
    })
    // Decode the Data object as JSON to produce an array
    // of Contacts formatted as a ContactResponse object
    .decode(type: ContactResponse.self, decoder: JSONDecoder())
    // Map the JSON array of Contacts
    .map({ (record) -> [Contact] in
        // Copy the Contact array to the records
        // member of ContactResponse
        record.records
    })
    // If an error is thrown, publish an empty array
    .catch( { error in
        Just([]) // Combine publisher that rescues a failed publisher
    })
    // Store the array of contacts in the model’s published contacts
    // property for use by ContactsForAccountListView
    .assign(to: \.contacts, on:self)
}

Other Options

If you peruse other model classes in the template app, you can see that RestClient.shared.publisher isn't the only means of obtaining Salesforce data. For example, AccountsListModel uses two other publishers:

  • A SmartStore publisher
  • A Mobile Sync publisher

At this point you're not expected to know about SmartStore and Mobile Sync, but, simply put, they're the two offline features of Mobile SDK. SmartStore is a local storage setup that serves as an offline repository for Salesforce records. Because this encrypted repository resides on the mobile device, SmartStore enables the customer to continue working even if network connectivity fails. Meanwhile, Mobile Sync does the heavy lifting of 

  • Obtaining Salesforce records
  • Storing the records in SmartStore soups (similar to database tables)
  • Ensuring that SmartStore records and server records remain in sync when the customer downloads fresh records or uploads local changes

Mobile SDK defines these publishers in Swift class extensions. If you're curious to see how the magic happens, in Xcode you can right-click on either of the publisher vars and select Jump to Definition. In both cases, the code is sparse--it defers to SmartStore or Mobile Sync methods for the real functionality. To find out more about these publishers, their underlying technologies, and other means of coding REST API interactions, follow the Resource links. Chances are that you'll come away happy to use the elegant, efficient publishers!

  • Resources
Keep learning for
free!
Sign up for an account to continue.
What’s in it for you?
  • 1 in 4 land a new job
  • 50% receive a promotion or raise
  • 80% learn new technologies that boost their resume
  • 66% say it increases productivity
Source: Trailblazer Community Impact Survey 2019