10 Code Snippets Every Salesforce iOS Developer Needs

Quinton Wall
8 min readAug 18, 2016

--

I’ve been writing iOS mobile apps for years now. Whenever I build apps that connect to Salesforce, I see a handful of common patterns that show up in just about every app I build. Many people are hesitant to write native apps because they are worried it is too complex. This is where recognizing and understanding common patterns really helps. Once you master these patterns, you can get back to building really slick apps quickly.

Example of a slylish UX using Salesforce for data, and the SOS live video from Service Cloud

Alright, let’s jump into some code. Here are the ten most common patterns (implemented in Swift), every developer building native iOS apps needs.

Cocoapod Configuration

Cocoapods is the way to go for managing dependencies in iOS app development. The Salesforce Mobile SDK can be installed using cocoapods with the following lines added to your Podfile.

target 'MyTargetName' do
source 'https://github.com/forcedotcom/SalesforceMobileSDK-iOS-Specs.git'
source 'https://github.com/CocoaPods/Specs.git'
pod 'SalesforceSDKCore'
pod 'SalesforceNetwork'
pod 'SalesforceRestAPI'
end

Preparing the Mobile SDK

All configuration for the Mobile SDK should be performed in the AppDelegate. Check out the docs for a full list of supported functions and lifecycle, but at a minimum, you need to configure the following variables.

let RemoteAccessConsumerKey = "my-consumer-key"
let OAuthRedirectURI = "sfdc://success"
let scopes = ["full"];

Once configured, you can override the init() func to set everything up.

override init() {
super.init()
SFLogger.sharedLogger().logLevel = SFLogLevel.Debug
SalesforceSDKManager.sharedManager().connectedAppId = RemoteAccessConsumerKey SalesforceSDKManager.sharedManager().connectedAppCallbackUri = OAuthRedirectURI SalesforceSDKManager.sharedManager().authScopes = scopes SalesforceSDKManager.sharedManager().postLaunchAction = {
[unowned self] (launchActionList: SFSDKLaunchAction) in
let launchActionString = SalesforceSDKManager.launchActionsStringRepresentation(launchActionList)
self.log(SFLogLevel.Info, msg:"Post-launch: launch actions taken: \(launchActionString)");
} SalesforceSDKManager.sharedManager().launchErrorAction = { [unowned self] (error: NSError?, launchActionList: SFSDKLaunchAction) in
if let actualError = error {
self.log(SFLogLevel.Error, msg:"Error during SDK launch: \(actualError.localizedDescription)")
}
else {
self.log(SFLogLevel.Error, msg:"Unknown error during SDK launch.")
}
}
SalesforceSDKManager.sharedManager().postLogoutAction = { [unowned self] in
self.handleSdkManagerLogout()
}
SalesforceSDKManager.sharedManager().switchUserAction = { [unowned self] (fromUser: SFUserAccount?, toUser: SFUserAccount?) -> () in self.handleUserSwitch(fromUser, toUser: toUser)
}
}

Handling Deffered Authentication

The Mobile SDK auto-generated starter projects are configured to launch the Salesforce login prompt on app load. For most apps I build, I want to control when I display the login prompt. Usually I display it after a welcome tour, or within a setup screen. Once you have set up your AppDelegate to configure the Mobile SDK, handling deferred login is very easy.

First, we show the login prompt after a particular action, generally a button tap:

@IBAction func getStartedTapped(sender: AnyObject) {
SalesforceSDKManager.sharedManager().launch()
}

Once the user has entered their details into the Salesforce Login screen and followed the prompt, the Mobile SDK triggers a number of Lifecycle events you can handle by making your class a delegate of SFAuthenticationManagerDelegate. The most important of these events can be handled with the authManagerDidFinish func. I generally check to see if a valid session has been created, and if so, dismiss the view that contained the button the user tapped to start the login process.

func authManagerDidFinish(manager: SFAuthenticationManager, info: SFOAuthInfo) {
if SFAuthenticationManager.sharedManager().haveValidSession {
self.dismissViewControllerAnimated(true, completion: {
print("dismiss after tap")
})
}
}

Checking if a User is Logged In

My typical app flow sets the initial controller to some sort of home screen. This screen requires a user to be logged in. If they are not, I’ll pop up a welcome flow or something similar. Then, once the user has successfully logged in, I can use the code snippet above to dismiss the welcome flow, returning the user to the home screen.

To check if the user is logged in, I usually set up an AppDefaults.swift class and create a methods like this. I’m usually too lazy to type the whole SFAuthenticationManger line.

static func isLoggedIn() -> Bool {
return SFAuthenticationManager.sharedManager().haveValidSession
}

Then, in my ViewController.viewDidLoad func, I can add the following.

override func viewDidLoad() {
super.viewDidLoad()
if !AppDefaults.isLoggedIn() {
dispatch_async(dispatch_get_main_queue()) {[unowned self] in
self.performSegueWithIdentifier("welcometour", sender: self)
}
}
...
}

Querying Salesforce

The Mobile SDK makes querying the Salesforce REST API super easy. For 99% of my requirements, I will use a SOQL query to retrieve what I want. I always use blocks to handle the response, rather than delegate methods. I find blocks much cleaner. You should use them too. Blocks also make it much easier to display messages to the user inline with the code you are writing. For example, the code below uses the UIAlertController to display messages.

private func fetchProperties() {
let sharedInstance = SFRestAPI.sharedInstance()
//fetch everything we need here for the details view as well. This way we avoid a second round trip to the server. let query = String("select Address__c, Baths__c, Beds__c, Broker__c, Broker__r.Title__c, Broker__r.Name, Broker__r.Picture__c, City__c, Description__c, Id, Location__c, Name, OwnerId, Picture__c, Price__c, State__c, Thumbnail__c, Title__c, Zip__c, (select id, Property__c from Favorites__r) from Property__c") sharedInstance.performSOQLQuery(query, failBlock: { error in
let alertController = UIAlertController(title: "Error", message: error!.description, preferredStyle: UIAlertControllerStyle.Alert)
let okAction = UIAlertAction(title: "OK", style: UIAlertActionStyle.Default) { (result : UIAlertAction) -> Void in
print("OK")
} alertController.addAction(okAction) self.presentViewController(alertController, animated: true, completion: nil)}) { response in //success //I like to use SwiftyJSON to handle responses.
self.responseJSON = JSON(response!)
if let count = self.responseJSON["totalSize"].int {
self.recordCount = count
}
if self.recordCount == 0 {
let alertController = UIAlertController(title: "Bummer!", message: "There are currently no properties available.", preferredStyle: UIAlertControllerStyle.Alert)
let okAction = UIAlertAction(title: "OK", style: UIAlertActionStyle.Default) { (result : UIAlertAction) -> Void in print("OK")
}
alertController.addAction(okAction) self.presentViewController(alertController, animated: true, completion: nil) } dispatch_async(dispatch_get_main_queue()) {
self.tableView?.reloadData()
}
}
}

You will notice that some of this code assumes we are mapping to a table, and getting JSON responses. I’ve describe these below, but if you are not mapping to a table, the approach to performing queries is exactly the same.

Handling Salesforce JSON Responses

By default, the Salesforce REST APIs return JSON which is great, however, the standard iOS JSON libraries kind of suck. Thankfully there is an open source library, SwiftyJSON, which makes things much easier!

If you remember from the snippet above, there was a line that takes the REST API response JSON and sets it to a SwiftyJSON.JSON type:

self.responseJSON = JSON(response!)

Once you have your response in a SwiftyJSON.JSON object, there is no need to check for nils, perform a bunch of error handling, or additional nested looping complexity. SwiftyJSON takes care of it all for you.

For example, my SOQL query returns a list of properties and associated brokers.

let query = String("select Address__c, Baths__c, Beds__c, Broker__c, Broker__r.Title__c, Broker__r.Name, Broker__r.Picture__c, City__c, Description__c, Id, Location__c, Name, OwnerId, Picture__c, Price__c, State__c, Thumbnail__c, Title__c, Zip__c, (select id, Property__c from Favorites__r) from Property__c")

With SwiftyJSON, I can do something like:

brokerName = p.brokerName = responseJSON["records"[0]["Broker__r"]["Name"].string

Mapping Responses to Tables

The SwiftyJSON example above was working on a single iteration of the response array. A very typical requirement is to map results to a table. To do this, first implement the UITableViewDataSource.numberOfRowsInSection func

func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
if let count = responseJSON["totalSize"].int {
recordCount = count
}
return recordCount
}

Then, implement the UITableViewDataSource.cellForRowAtIndexPath func and loop through your response JSON based on indexPath.row. For example, I have a custom UITableViewCell called PropertiesTableViewCell that I want to map query results to. You will also notice in the snippet below that my cell has a object called Properties. This object contains setters/getters for all of the fields. I like to map results back to a strongly typed, defined object to make it easier to pass data between UIViewControllers. Sure, you can do it with a JSON element, but doing so results in Salesforce column names being embedded throughout your code, rather than centralized in one class where you can easily encapsulate them.

func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCellWithIdentifier("PropertyCell", forIndexPath: indexPath) as! PropertiesTableViewCell
cell.property = self.prepProperty(responseJSON["records"][indexPath.row]) cell.isFavorite = cell.property!.isFavorite cell.propertyImageUrl = responseJSON["records"][indexPath.row]["Thumbnail__c"].string!
cell.propertyId = responseJSON["records"][indexPath.row]["Id"].string
cell.titleLabel.text = responseJSON["records"][indexPath.row]["Title__c"].string cell.cityStateLabel.text = responseJSON["records"][indexPath.row]["City__c"].string! + ", "+responseJSON["records"][indexPath.row]["State__c"].string!

cell.priceLabel.text = responseJSON["records"][indexPath.row]["Price__c"].currency
return cell}

Performing Write Operations

Performing write operations just as create, update, or delete is similar to performing queries with the mobile sdk. The primary difference is that you need to create a Dictionary object with the changes. This dictionary object uses the Salesforce table/object column name as the key. As mentioned above, I like to create typed objects to ecapsulate salesforce field names. This approach also allows me to write helper methods for handling the creation of these Dictionary objects. For example, here is an abbreviated example of my Property class.

class Property {//property infovar propertyId: String?var address: String? ....//broker infovar brokerId: String?var brokerName: String?var brokerTitle: String?var brokerImageURL: String?//favoritevar favoriteId: String?func getDictionaryToSaveFavorite() -> NSDictionary {   let d : NSDictionary = [     "Property__c" : propertyId!,     "User__c" : AppDefaults.getUserId()   ]
return d
}
}

Now, when I want to perform a write operation, I can do this much easier. I’ve left out the full error/response handling here for clarity, but it works exactly the same way as the query example above.

let d : NSDictionary = cell.getDictionaryToSaveFavorite()let request = SFRestAPI.sharedInstance().requestForCreateWithObjectType("Favorite__c", fields: d as? [String : AnyObject] )SFRestAPI.sharedInstance().sendRESTRequest(request, failBlock: { error in
//do error handling
}) { response in
//handle success.
}}

Formatting Salesforce Field Types

Salesforce provides a number of rich field types such as email, checkboxes, currency, dates etc. By default, field types like currency are converted to equivalent ‘generic’ types such as Bool or Double. A typical requirement is to take the response from salesforce and format it in a specific way to display. The most common example is currency, and Salesforce system date/time (which comes back in a pretty complicated UTC format).

Because I use SwiftyJSON almost exclusively to handle JSON responses from Salesforce, I’ve implemented a Swift extension to add additional formating.

import SwiftyJSONextension JSON {  public var date: NSDate? {
get {
if let str = self.string {
let dateFormatter = NSDateFormatter()
dateFormatter.dateFormat = "yyyy-MM-dd"
dateFormatter.timeZone = NSTimeZone(name: "UTC")
let date = dateFormatter.dateFromString(str)
return date
}
return nil
}
}
public var systemdate: NSDate? {
get {
if let str = self.string {
let dateFormatter = NSDateFormatter()
dateFormatter.dateFormat = "yyyy-MM-dd'T'HH:mm:ss.AAZZZZZ"
dateFormatter.timeZone = NSTimeZone(name: "UTC")
let date = dateFormatter.dateFromString(str)
return date
}
return nil
}
}
public var currency: String? {
get {
if let str = self.int {
let formatter = NSNumberFormatter()
formatter.numberStyle = .CurrencyStyle
formatter.maximumFractionDigits = 0
let ccy : String = formatter.stringFromNumber(str)!
return ccy
}
return nil
}
}
}

Now, whenever I receive a rich field type in a response, I can use my extension to format it easily.

p.price = jsonRecord["Price__c"].currency

Handling Image URLs

Generally, customers don’t store images in Salesforce, rather they rely on cheap storage like Amazon S3 buckets. What this means is that Salesforce only has an image URL and it is up to the mobile developer to fetch and display these images. I use a really cool library called SDWebImage for fast async image loading and caching. Then, whenever I have an image URL retrieved from Salesforce, I can make a call like this.

self.propertyImage.sd_setImageWithURL(NSURL(string: propertyImageUrl), placeholderImage: UIImage(named: "my-placeholder-image"))

Summary

The 10 common patterns described above occur in just about every mobile app I write that connects to Salesforce and uses the Mobile SDK. Understanding, and having code snippets, for the patterns should help you blast through creating your app. To make it even easier, check out a complete end to end sample app written in Swift that leverages the dreamhouseapp.io sample use case.

--

--

Quinton Wall

Modern day wandering minstrel, code gypsy, and adventure travel photographer.