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.
Follow Along with Trail Together
Want to follow along with an expert as you work through this step? Take a look at this video, part of the Trail Together series.
(This clip starts at the 16:05 minute mark, in case you want to rewind and watch the beginning of the step again.)
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()
fetchContactsForAccount(_:)
method.- 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 anyRestClient
method that suits your purpose. - Send the request object to Salesforce.
RestClient
automatically authenticates the request with the current user’s credentials.
- In Xcode, open the ContactsForAccountModel.swift file.
- 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) } } }
RestClient
shared instance. You can see this call in the long statement that begins with: let request = RestClient.shared.request(forQuery:...
RestRequest
object contains a pre-formatted REST request based on the given SOQL string.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
- Developer Guide: Native Swift Template
- Developer Guide: Handling REST Requests
- Developer Guide: Handling REST Responses
- Developer Guide: Using SFRestRequest Methods
- Developer Guide: Supported Operations
- Developer Guide: REST API Developer’s Guide
- Trailhead: API Basics
- External Link: Postman Learning Center