Last year, my wife and I gained the first-time experience of building a brand-new home. The process was fun and exciting, but we also experienced the unexpected internet service interruptions that often accompany new home subdivisions.
While these outages impacted my family’s ability to stream services, such as Amazon Prime, Hulu, and Netflix, I continued working on my current project, due to the intentional design of being able to work in an isolated (or offline) state. This has always been helpful during airline travel when I work from Nevada and Florida during the year.
This made me wonder why more mobile apps are not designed to continue working during internet service interruptions.
In this publication, I will demonstrate just how easy it is to make your mobile app usable in offline mode. Since I recently spent time with the Salesforce Mobile SDK, I wanted to continue exploring that mobile app option.
Working Offline with Salesforce Mobile SDK
Before we get into writing code, let’s touch briefly on how offline functionality works with the Salesforce Mobile SDK.
The left side of the illustration presents the traditional manner in which the Salesforce application is utilized. The right side provides a high-level overview of the Salesforce Mobile SDK.
Native applications that run on Android or iOS devices can be written in Android Studio, Xcode, or React Native. Those applications include the user interface, some level of business logic, and model objects representing elements that ultimately live in Salesforce.
The Salesforce Mobile SDK includes a SmartStore aspect, which makes data available when the mobile application cannot access the Salesforce service. The model layer can be easily designed to leverage the SmartStore when offline mode is required (or preferred), but ready to synchronize all changes with Salesforce when online status is available.
If you want to read more about this design, check out “Using SmartStore to Securely Store Offline Data.”
Adding Offline-ability to Finny’s Foods
Earlier this year, I explored the Salesforce Mobile SDK and created a fictional app called Finny’s Foods. I created the app three times using Android, iOS, and React Native. I thought I would start from the publication that used the iOS/xCode (Swift) version of the Salesforce Mobile SDK, then build upon it to provide offline functionality.
The GitLab repository for the original article is listed below:
https://gitlab.com/johnjvester/finnys-foods-ios
The first step is to establish the Meal__c
object in the userstore.json
file that is used by the SmartStore functionality.
{
"soups": [
{
"soupName": "Meal__c",
"indexes": [
{ "path": "Id", "type": "string"},
{ "path": "Name", "type": "string"},
{ "path": "Rating__c", "type": "integer"},
{ "path": "__local__", "type": "string"}
]
}
]
}
Next, update the usersyncs.json
file to make the current list of meals available offline:
{
"syncs": [
{
"syncName": "syncDownMeals",
"syncType": "syncDown",
"soupName": "Meal__c",
"target": {"type": "soql", "query":"SELECT Id, Name, Rating__c FROM Meal__c ORDER BY Name ASC"},
"options": {"fieldlist":["Id", "Name", "Rating__c", "LastModifiedDate"], "mergeMode":"LEAVE_IF_CHANGED"}
}
]
}
You can read all about the SmartStore sync configuration in “Registering Soups with Configuration Files.”
With the SmartStore configuration in place, I refactored the MealsListModel.swift
class as shown below:
class MealsListModel: ObservableObject {
@Published var meals: [Meal] = []
var store: SmartStore?
var syncManager: SyncManager?
private var syncTaskCancellable: AnyCancellable?
private var storeTaskCancellable: AnyCancellable?
init() {
store = SmartStore.shared(withName: SmartStore.defaultStoreName)
syncManager = SyncManager.sharedInstance(store: store!)
}
func fetchMeals(){
syncTaskCancellable = syncManager?.publisher(for: "syncDownMeals")
.receive(on: RunLoop.main)
.sink(receiveCompletion: { _ in }, receiveValue: { _ in
self.loadFromSmartStore()
})
self.loadFromSmartStore()
}
private func loadFromSmartStore() {
storeTaskCancellable = self.store?.publisher(for: "select {Meal__c:Id}, {Meal__c:Name}, {Meal__c:Rating__c} from {Meal__c}")
.receive(on: RunLoop.main)
.tryMap {
$0.map { (row) -> Meal in
let r = row as! [String?]
return Meal(id: r[0] ?? "", Name: r[1] ?? "", Rating__c: (r[2]! as NSString).doubleValue)
}
}
.catch { error -> Just<[Meal]> in
print(error)
return Just([Meal]())
}
.assign(to: \MealsListModel.meals, on:self)
}
}
I updated the fetchMeals()
function to interact with the SmartStore by default, calling Salesforce when needed but allowing data stored in the application to be used if the device is in offline mode.
Believe it or not, that’s all that is required.
Finny’s Foods in Offline Action
After launching Finny’s Foods from XCode and an iPhone 8 emulator, we see the following screen displayed as it did in the original publication.
Now, if I disconnect my device from the internet and reload the application, the following logs appear in XCode:
2022-03-14 16:14:15.544412-0400 FinnysFoodsIOS[88679:6546397] [MobileSync] CLASS: SFSyncDownTask runSync failed:{ ... } cause:Server call for sync down failed errorError Domain=NSURLErrorDomain Code=-1009 "The Internet connection appears to be offline."
However, the application still loads and provides the data from the SmartStore. In fact, without looking at the logs in XCode, I would have had no idea that I was working with a local version of the data.
For offline applications which allow data to be added or updated, the SmartStore has the ability to merge offline and online data with a Sync Manager. See the Syncing Data documentation for additional information.
Conclusion
Although internet connectivity continues to improve, a reliable connection is not 100% guaranteed. Forward-thinking feature developers should consider taking the appropriate steps to allow their apps to continue working when an internet connection is not available. Ask yourself: How can my app be better when working in a disconnected state?
Since 2021, I have been trying to live by the following mission statement, which I feel can apply to any IT professional:
“Focus your time on delivering features/functionality which extends the value of your intellectual property. Leverage frameworks, products, and services for everything else.”
- J. Vester
In this article, I provided an example of how easy it is to implement offline functionality using the Salesforce Mobile SDK. Salesforce engineering designed the integration point at the model layer, and that’s the best place to determine if live data can be retrieved or if SmartStore data should be returned.
Clearly, Salesforce adheres to my personal mission statement by offering a design that is easy to employ without a great deal of boilerplate code.
If you are interested in the source code for this article, you can find it on GitLab here:
https://gitlab.com/johnjvester/finnys-foods-ios
Have a really great day!
Top comments (0)