Start tracking your progress
Trailhead Home
Trailhead Home

Understand Forceios Native Apps

Learning Objectives

After completing this unit, you'll be able to:

  • Understand the overall flow of a native iOS Salesforce app.
  • Understand the flow of events during app launch.
  • Understand how the root view controller directs the flow of execution.

Overview of Application Flow

Let’s take a closer look at native iOS apps.

On the surface, a Mobile SDK native app is just a typical iOS app. You can picture its architecture in Model-View-Controller (MVC) terms:

  • Model is the Lightning Platform database schema.
  • Views come from the nib and implementation files in your project.
  • Controller functionality comes in the form of iOS Cocoa Touch classes, Salesforce Mobile SDK controller classes, and custom controller classes in your app.

Like all iOS apps, Mobile SDK native apps start with a boilerplate main module that sets up the main event loop. Xcode generates this file, and most humans never have reason to touch it. Mobile SDK template apps for iOS also define three classes that you can customize.

Handles Mobile SDK initialization and user authentication. If authentication succeeds, it passes control to RootViewController.
Container for the Salesforce login screen
Entry point for your app

AppDelegate defers to a shared SDK manager object to coordinate Salesforce passcode and authentication tasks. The following diagram illustrates the startup flow for a native app using the most basic SDK manager, SalesforceManager.

Application flow diagram

By default, forceios native apps use SmartSyncSDKManager as the SDK manager class. This class enables the full range of Mobile SDK features.

Application Launch Flow

When a customer opens your app, a fairly complex sequence of events occurs. These events can include:

  • Applying your app’s configuration
  • Validating an app passcode (optional)
  • Performing Salesforce login and authentication
  • Launching Salesforce Mobile SDK and your app
A healthy dose of “wiggle room”—for instance, the ability to defer Salesforce login until after startup—can make handling these events a tricky proposition. Luckily, Mobile SDK keeps you from getting tangled up by providing the SDK manager object. This object enforces the proper bootstrap sequence so that you don’t have to worry about most startup events.

How you initialize Mobile SDK depends on your development language. Native Mobile SDK apps use SmartSyncSDKManager, a specialized subclass of the SalesforceManager Swift class.

Most of the code that uses the SDK manager already exists in a new forceios app. Let’s examine this code.

  1. In Xcode, open the AppDelegate.swift file.
  2. Scroll to the init method.

Here, you can see the app initializing Mobile SDK. For example, in a Swift app you call initializeSDK() on the SmartSyncSDKManager object. Internally, the app sets up handlers for launch events, such as user authentication, PIN code failures, and other error states. Except in extreme cases, you’re never directly involved in these events.

After Mobile SDK is initialized, the init method calls an authentication helper class to define what happens when the current user logs out. Essentially, the helper class resets the view and restarts the app.

The rest of the AppDelegate class implements UIApplicationDelegate protocol methods that you can customize as needed. Each of these methods contains commented instructions that tell you exactly what to do for the intended use case. For example, you can:

Customize the navigation bar
Follow the commented instructions in the application(_:didFinishLaunchingWithOptions:) method.
Register your app for push notifications
Follow the commented instructions in the application(_:didFinishLaunchingWithOptions:) method.
Register the customer’s device for push notifications
If you registered for push notifications, the application(_:didRegisterForRemoteNotificationsWithDeviceToken:) callback method passes you a device token. To register this device token with Mobile SDK and the Salesforce push notification manager, follow the commented instructions
Break the news if push notification registration fails
Sometimes, the app’s attempts to register for push notifications failed. You receive an error message in the application(_:didFailToRegisterForRemoteNotificationsWithError:) method, which you can handle as you wish.
Enable an IDP service
Planning to use this app as an identity provider services for your other Mobile SDK apps? You can enable the IDP service by following the commented instructions in the application(openURL:options:) method. To fully implement this feature, configure your apps as described in the Mobile SDK Development Guide.

The AppDelegate and RootViewController Classes

The AppDelegate class is, in a sense, the Mobile SDK control center of the app—or at least its launch pad. It handles app initialization and login, and then passes execution control to a root view. Let’s look at the source code to see how the app displays its views.

  1. In Xcode, open the AppDelegate.swift file.
  2. Search for “initializeAppViewState”.
    See the instance method implementation? Look at the next to last line of the method:
    self.window!.rootViewController = InitialViewController(nibName: nil, bundle: nil)

    This code sets the self.window.rootViewController property to a newly created InitialViewController object. The InitialViewController class is defined in your app, and it’s pretty bare and boring. This view, which is the first to appear, serves as a container for the Salesforce login screens. That’s about all you need to know about it.

  3. Search for “setupRootViewController”.
    If you visit all the hits, you see that:
    • It’s called in the init method of AppDelegate.
    • It’s implemented as follows:
      func setupRootViewController()
          let rootVC = RootViewController(nibName: nil, bundle: nil)
          let navVC = UINavigationController(rootViewController: rootVC)
          self.window?.rootViewController = navVC

In the setupRootViewController instance method, AppDelegate creates a RootViewController instance as the app’s real first view. Notice how convenient the name “RootViewController” is? It sounds a lot like the rootViewController property of the app’s window. Coincidence? Not likely, but that naming is just a convenience—not a requirement. In fact, if you don’t like that class name, you can use Xcode’s refactoring tool to rename it in this app.

Your root view controller is the starting place for your app’s UI. Before it's assigned to the rootViewController property, though, it's wrapped in a UINavigationController object. This wrapper lets you easily add other views and navigate between them. Because UINavigationController has been initialized with RootViewController, RootViewController always remains at the bottom of the UI stack.

What else does the RootViewController class do? It configures a table view—standard stuff for a class that inherits UITableViewController. RootViewController also uses the SalesforceRestAPI shared instance to handle Salesforce REST API requests and responses. First, let’s examine the UITableViewController behavior.

The UITableViewController Class

After your a user logs into Salesforce through your app, the SDK manager sets up the RootViewController as your root view controller. Let’s look at how this class customizes the app’s main view.

RootViewController inherits the UITableViewController class, which automatically endows it with a table view. RootViewController performs some minor customization of UI details, and at runtime it loads Salesforce data into its tableView property. Usually, though, the parent class handles the display.

To see how RootViewController customizes the table presentation:
  1. In Xcode, open RootViewController.swift.
  2. Scroll to the following method:
    override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell
The most interesting snippet from this method is the two-line sequence that sets a cell’s label to a value from the most recent REST response.
// Configure the cell to show the data.
let obj = dataRows[indexPath.row]
cell.textLabel?.text = obj["Name"] as? String
You’re probably wondering: What is this “dataRows” array, and when did it join this party? In the next unit, we learn that the RootViewController class also handles REST requests and responses. For example, it sets dataRows to contain Salesforce records obtained from a REST request. Here, in the tableView(_:, cellForRowAt:) method, the code sets each cell’s text label to a Name value from the dataRows array. To achieve one-to-one correspondence, the code uses the current cell’s row number—indexPath.row—as the array index.