Start tracking your progress
Trailhead Home
Trailhead Home

Add Asynchronous Logic

Set Up Your Trailhead Playground

Get your Trailhead Playground set up and ready for building with Swift and the Salesforce Mobile SDK for iOS. 

  1. Enable Knowledge in your Playground.
    1. In Quick Find, enter knowledge, and click Knowledge Settings.
    2. Check the box next to Yes, I understand the impact of enabling Salesforce Knowledge, then click the Enable Salesforce Knowledge button. You may see a pop up message explaining that this action is irreversible. Click to confirm.
    3. Then, click Edit in Knowledge Settings.
    4. Under Lightning Knowledge Settings, check the box to Enable Lightning Knowledge.
    5. Click Save.
  2. Install the package. Click App Launcher to launch the App Launcher, then click Playground Starter and follow the steps. If you don’t see the Playground Starter app, copy this package installation link and check out Find the Username and Password for Your Trailhead Playgroundon Trailhead Help.
    1. Click the Install a Package tab.
    2. Paste 04t2E000003VlMy into the field. 
    3. Click Install.
    4. Select Install for All Users, then click Install
  3. Add Master_Account__cto the Account Layout.
    1. In Setup, click Object Manager.
    2. Click Account on the left.
    3. Click Page Layouts.
    4. Click Account Layout.
    5. Drag the Master Account field onto the page layout under Account Information.
    6. Click Save.
  4. Now let's prepare the mobile layout.
    1. Click on the Object Manager tab once more.
    2. Click Case.
    3. Click Compact Layouts.
    4. Click the Compact Layout Assignment button.
    5. Then, click the Edit Assignment button.
    6. Select mobile from the dropdown list.
    7. Click Save
    8. You can confirm the mobile layout is primary by clicking on Compact Layouts and viewing the checkmark on the mobile row. The Compact Layout menu for the Case object with mobile as the primary, highlighted by a red box. 
  5. Almost there. You're doing great! Edit any account in the Redwoods app and set it as the master account.
    1. Click on the app launcher app launcher icon and select Redwoods Insurance Console.
    2. Select Accounts in the tab dropdown. the Accounts tab selected in the tab dropdown.
    3. Change the list view to All Accounts.
    4. Select any account on the list.
    5. Navigate to Account Details.
    6. Click Edit or the pencil icon next to the Master Account checkbox.
    7. Check the Master Account checkbox.
    8. Click Save.
  6. Make the Redwoods case page the default for your Trailhead Playground.
    1. Select Cases in the tab dropdown.
    2. Change the list view to All Open Cases to show a list of open cases.
    3. Click into a case from the list.
    4. Click the Setup icon setup icon.
    5. Click Edit Page.
    6. In Lightning App Builder, click the Pages menu in the upper-left.
    7. Select Open Page.
    8. Select Case Record Page, then click Open.
    9. Click Activation in the upper right corner.
    10. Click the Assign as Org Default button, then click Next and Save.
    11. Click Save on the Page.
    12. Click Back.

Okay. Next stop, Swift.

Swift and Asynchronous Logic

Swift is a powerful programming language with intuitive, expressive, and concise syntax. To learn more about the basics of Swift, including types, operators, and control flow, take a look at Swift Essentials. In this project we focus on a more advanced Swift feature: asynchronous logic. 

Consider the next headline. Looks messed up? That’s because it is.

Work Asynchronous Hard Is

It’s supposed to be Asynchronous Work Is Hard. But somewhere along the way, the signal dropped and the information fell out of order. This can happen with mobile devices and poorly developed apps.

Mobile devices often have limited bandwidth. Wi-Fi isn’t available, the cellular signal is weak, or maybe there’s just a ton of people on the network. This can make flow control and error handling complex.

Create Asynchronous Logic

The original developers wrote code to upload pictures and capture audio. However, they didn’t include methods to make sure it’s done in sequence. 

The product manager asks you to make a few modifications. 

  • Add asynchronous logic to the photo upload code, so submitting a claim automatically uploads the photos to Salesforce.
  • Ensure the audio file that’s recorded is uploaded as an attachment to the case as well.
  • Make sure it’s all done in sequence.

Refactor the Photo Upload Method

1. Open redwoods-project in Xcode.

2. Click the New Claim folder on the left hand pane.

3. Click the NewClaimViewController+Uploading.swift file. 

4. Add the existing method uploadPhotos to the transaction chain.

a. Navigate to the code block starting at line 120, the createCaseContacts method. It looks like this:

private func createCaseContacts(withContactIDs contactIDs:
 [String], forCaseID caseID: String) {
       let associationRequest =
RestClient.shared.compositeRequestForCreatingAssociations(fromContactIDs: contactIDs, toCaseID: caseID)
RestClient.shared.sendCompositeRequest(associationRequest, onFailure: handleError) { _ in
                                                                            SalesforceLogger.d(type(of: self), message:
"Completed creating \(contactIDs.count) case contact record(s).
Optionally uploading map image as attachment.")
                 self.unwindToClaims(forCaseID: caseID)
           }
}

Currently, the method generates a request, and sends it using the SDK’s RestClient. There is a trailing closure as the final parameter to the sendCompositeRequest method. It currently just logs success to the console and calls the method unwindToClaims(forCaseID: caseID), which takes the user back to the claim list. If this sounds a little confusing, don’t worry. It becomes clear soon.

b. Remove the self.unwindToClaims(forCaseID: caseID) call (the bit of code that at the bottom of this block takes the user back to the claim list).

c. In its place, insert a call to self.uploadPhotos(forCaseID: caseID). Notice we pass along the variable caseID. It's available to us because closures retain access to the variable scope of their outer parent.

When you’re done, it should look like this:

private func createCaseContacts(withContactIDs contactIDs: [String],
forCaseID caseID: String) {
           let associationRequest =
RestClient.shared.compositeRequestForCreatingAssociations(fromContactIDs: contactIDs, toCaseID: caseID)
                 RestClient.shared.sendCompositeRequest(associationRequest, onFailure: handleError) { _ in
                       SalesforceLogger.d(type(of: self), message: "Completed creating \(contactIDs.count) case contact record(s). Optionally uploading map image as attachment.")
                       self.uploadPhotos(forCaseID: caseID)
             }
       }

5. Save your work.

Continue the Chain

The createCaseContact method now calls uploadPhotos. It still needs uploadAudio, however. Refactor the uploadPhotos method to call uploadAudio in its closure. This creates asynchronous logic to make sure you complete photo and audio upload.

  1. In the NewClaimViewController+Uploading.swift file, find the uploadPhotos method starting at line 174.
  2. Add a call to uploadAudio at the end of the method.

When you’re done, your code should look like this:

    private func uploadPhotos(forCaseID caseID: String) {
         for (index, img) in self.selectedImages.enumerated() {
               let attachmentRequest =
RestClient.shared.requestForCreatingImageAttachment(from: img,
relatingToCaseID: caseID)
                 RestClient.shared.send(request: attachmentRequest,
onFailure: self.handleError){ result, _ in
                      SalesforceLogger.d(type(of: self), message:
"Completed upload of photo \(index + 1) of
\(self.selectedImages.count).")
                 }
           }
           self.uploadAudio(forCaseID: caseID)
    }

Don't forget to Save your work.

Note where we put the uploadAudio call. It’s outside the closure for RestClient.shared.send. This is by design. Uploading an unknown number of photographs can take awhile, but we don’t need to know the results of those uploads to continue. Placing the call to uploadAudio at the end of the method means execution continues to uploadAudio even as the loop continues uploading photos.

Refactor the Upload Audio Method

  1. In the NewClaimViewController+Uploading.swift file, find the uploadAudio method starting at line 188.
  2. Since it’s now the last step in our transaction, we need to refactor its completion closure to unwind back to the claims list. Insert self.unwindToClaims(forCaseID: caseID) in the RestClient.shared.send method’s completion closure.
  3. Save your work.

When you’re done with your refactoring, your code should look like this:

private func uploadAudio(forCaseID caseID: String) {
           if let audioData = audioFileAsData() {
                let attachmentRequest =
RestClient.shared.requestForCreatingAudioAttachment(from: audioData,
relatingToCaseID: caseID)
                 RestClient.shared.send(request: attachmentRequest,
onFailure: handleError) { _, _ in
                       SalesforceLogger.d(type(of: self), message:
"Completed uploading audio file. Transaction complete!")
                     self.unwindToClaims(forCaseID: caseID)
                 }
            } else {
                  // Complete upload if there is no audio file.
                  SalesforceLogger.d(type(of: self), message: "No audio
file to upload. Transaction complete!")
                  self.unwindToClaims(forCaseID: caseID)
           }
     }

Nice, you just refactored the code to call  uploadPhotos and uploadAudio asynchronously.  

Does your app now upload photos and audio files? After you submit a claim, does your app redirect the user back to a list of claims? Let's make sure it does. 

Run the Mobile App

At this point you can run your app, either in the simulator or on a device. The following steps walk you through the simulator.

  1. Click the Build and Run button at the top of Xcode Run icon to verify that all of your code is correct and execute your app. Since this is the first time you're running, you may see a number of warnings pop up in the console (there's still some work to do in the next steps). That shouldn't stop the code from building and simulator to open.
  2. In simulator, you are prompted to log in. To do so, you need to use a custom login URL. When your app launches, click the gear icon in the upper right hand corner. iOS app screen shot showing gear icon in upper right
  3. After that, the app asks you to choose a connection. Click the + button. iOS app screenshot showing the plus icon
  4. Enter your playground URL (from the address bar of your browser). Make sure to exclude https:// and end it at .com.
  5. Give your connection a name, then click Done. iOS Screenshot showing how to create a new connection
  6. Once you save, you’ll be directed back to the login screen. Go ahead and log in with your playground credentials. If this is the first time you're logging into the mobile app, it will ask for permission to access your phone's data. Go ahead and Accept.
  7. Then, log a claim.
    1. Click the + button at the top of the Open Claims screen.
    2. Double click into an area of the map to simulate an accident location. You can double click again tif you want to zoom in further.
    3. (optional) Add voice-to-text by clicking the Record button and speaking into your machine's microphone if you have it connected.
    4. Click Add Photo to add a picture. Select one of the photos in Moments.
    5. Click Edit in the Parties Involved section to add a contact. Select John Appleseed, then click Done.
    6. Click Submit at the top of the claim submission screen.

If your submission was successful, you should be taken back to the Open Claims view in the simulator, and your new case should be at the top of the list.

The top of the iOS app Open Claims view, with the latest incident listed at the top.Congratulations are in order—you just built asynchronous work and tested it on the iPhone!

Put It All Together

It’s not just magic that allows your app to do this asynchronous work. It takes advantage of two features called completion blocks and closures

Completion blocks tell the code to do one thing, then when it’s done, do something else. That something else is the closure. This logic makes sure it all stays in order—even if the phone loses signal for a while. Yeah, you just built that.

The app uses closures and completion blocks together to link asynchronous tasks into a single, logical transaction. The app makes the following individual requests to create a new claim.

  • fetchMasterAccountForUser
  • createCase
  • createContacts
  • createCaseContacts

These methods make calls to the RestClient.shared object, which is part of the SDK. And each of these calls accepts a trailing closure (this is a technical term that means a closure is the final parameter of a method). These were the closures you added—uploadPhotos and uploadAudio.

Note

Note

This is complex stuff, but it pays off. A closure has access to the variables and scope of the outer method, even when the program has moved on to do other things. This allows the app, for instance, to pass the accountId from method to method.