Erfassen Sie Ihre Fortschritte
Trailhead-Startseite
Trailhead-Startseite

Enable Dynamic Updates

Update the App with Salesforce Metadata

Redwoods Car Insurance wants to move even faster. Your team wants to push changes to the mobile app without having to release a new version and waiting for everyone to upgrade. Wait, you can do that? Yes, you make the change in Salesforce, and through the SDK that change is reflected in the app. All clicks. 

Shift to Declarative Development

OK. Let’s shift and do some point-and-click development. To show how this dynamic update functionality works, log in to your development org and change the layout.

  1. Build and run the app in simulator once more.
  2. Open one of your claims to the claim detail screen. Notice what fields are displayed—Subject, Origin, CaseNumber, Type, Priority, and Status.
  3. Log in to your Trailhead Playground.
  4. Click the Setup icon, then Select Setup.
  5. Click the Object Manager tab.
  6. Click Case in the left-hand list.
  7. Click Compact Layouts in the left-hand menu.
  8. Click the disclosure triangle on the active compact layout and click Edit.
  9. Here you can add or remove fields to show in the iOS app.
    1. Because all claims come from the iOS app, go ahead and remove the Case Origin field from the compact layout.
    2. Because the priority is only related to internal processes, remove the Priority field.
  10. Then click Save.
  11. Now, on your device or in the simulator, use the pull to refresh gesture to refresh the data from Salesforce. You should see these fields are no longer there—an app update without having to go through the App Store.

Two API Calls

This sounds too good to be true. But it's real. We do this with a two-element chain of api calls.

Let’s inspect what’s happening. 

  1. Open the Claim Details folder in Xcode.
  2. Open the ObjectlayoutDataSource.swift file.

Take a look at the  buildRequestFromCompactLayout method (use cmd+f to search for this). It’s here we make two API calls to Salesforce. 

  • The first shows how to make completely custom API requests.
  • The second shows and how to use a retrieve request. Retrieve requests are useful because you don’t have to write the SOQL query. Instead, you specify the object type, it’s ID, and the fields you want retrieved.

Check out the layoutRequest variable at the top of the method (line 105). Unlike previous request methods we’ve seen, this is a completely custom request. Because of this, we set the HTTP method .GET, as well as the path "/v44.0/compactLayouts". 

We want to limit our results to the Case object. To do that we set a query parameter. The final parameter queryParams accepts a dictionary of key-value pairs. In this situation, we use a key of q, and a value of objectType that is: queryParams: ["q": objectType].

Note

Note

How did we know to use "q" as the query parameter? Great question. Those details come from the documentation here.

Altogether the request asks Salesforce to describe the Case Compact Layout. This includes the list of fields displayed on the compact layout.

When we execute this request a couple of lines later, our completion closure comes into play. 

What Do We Do with a Compact Layout Description? Parse It!

As we have seen before, we’re using a guard statement to ensure we have data before we try to parse it. What’s new is that in this case we append as? Data. This tells Swift that we must have information, and that the information needs to be Data. This is a useful feature of guard statements and one of Swifts many tools for driving safe code. After our guard statement, you see a new Swift feature: a Do/Catch block. 

Note

Note

Do/Catch blocks are the Swift way of doing Apex Try/Catch blocks.

In our Do/Catch block, we decode the responseData our guard block makes sure exists. You can read more about decoding JSON in Swift here. Once we’ve decoded the JSON, we extract a comma delimited string of fields. This is done with these lines.

let fields = layout.fieldItems.compactMap { $0.layoutComponents.first?.value }
let fieldList = fields.joined(separator: ", ")

That's a dense code snippet! The call to compactMap iterates over each element of the layouts FieldItems. if the value of the layoutComponents.first key is non-nil, it’s returned. This results in an array of all the fields on the compact layout.

We need a comma-separated list of fields for our retrieve call, so we call the joined method on our fields array.

The Second API Call

Once we have our list of fields, we can generate our second request.

let dataRequest = RestClient.shared.requestForRetrieve(withObjectType:
objectType, objectId: objectId, fieldList: fieldList)
                      completionHandler(dataRequest)

Executing that request requires a trailing closure as a completion block. We’ve seen that setup before. Here’s the twist, and where it gets more complex. As developers we may not always know what completion block we want to run. Looking at the buildRequestFromCompactLayout method definition shows us how to handle this situation.

private func buildRequestFromCompactLayout(forObjectType objectType:
String, objectId: String, completionHandler: @escaping (_ request:
RestRequest) -> Void) {

When we call buildRequestFromCompactLayout we have to provide value for completionHandler. Because it’s the last parameter in our function definition, we can pass in a trailing closure as the completionBlock. After we’ve created our request, we pass that into our completionBlock. That’s right, you can write reusable methods that accept closures as completion blocks for all or part of that method’s asynchronous work.

All this work means that the fields our claim view displays to the iOS app user is dynamically driven by Salesforce metadata. Our first request retrieves the metadata about the case compact layout and pulls the list of fields that are displayed. The second request retrieves those fields from the current record. 

The end result means that we can change what fields are displayed to the iOS user simply by modifying our metadata inside Salesforce. 

Put It All in Perspective

What a journey. You got hands-on with Swift and Xcode. You used completion blocks and closures to create logical, asynchronous work. You used the Salesforce Mobile SDK for iOS to integrate with your iOS app. You took advantage of advanced features like camera, AI talk-to-text, and even dynamically updated your app with Salesforce metadata. Awesome job.