Native Scrolling in Salesforce mobile app

Native Scrolling in Salesforce Mobile App

The Salesforce mobile app is a native app with hybrid functionality available for both iOS and Android platforms. A hybrid app combines the best of both worlds, leveraging native experiences with rich web customizations provided by the Salesforce platform via Flexipages, Lightning Web Components, Aura, and VisualForce.

UI Scroller was added as a top layer to manage webview scrolling and scrolling-initiated actions, including:

  • refreshing content via Pull to Refresh
  • loading new content with Pull to Show More
  • infinite scrolling capabilities

UI Scroller was added over five years ago when mobile web browsers lacked proper scroll events in a webview to implement Pull to Refresh and infinite scrolling natively.

UI Scroller has served our customers well so far. However, on iOS and Android, this additional layer leads to

  • Slower and unnatural scroll experience
  • No copy-paste support
  • Limited Accessibility Support

As mobile platforms evolved their API to support native scrolling, the default OS WebView engine can now manage scrolling. Using OS Webview engine for scrolling leads to more reliable scrolling, better accessibility, and copy-paste support.

Native Scrolling implementation in the Salesforce mobile app

Below is the high-level design for this implementation:

Native Scrolling Design

Native listens for the events sent by the web layer. When these properties change, the web layer re-fires the events. When a user invokes a Pull to Refresh or Pull to Show More gesture, native sends the corresponding event to the web layer.

iOS

On iOS, we use WKWebView for loading web pages. UIScrollView associated with WKWebview results in efficient, smoother scrolling. UIScrollView’s bounce effect and the ability to scroll the full page provide users with the seamless native iOS experience they expect from consumer apps.

WKWebView’s scrollView also ensures consistent scroll events on the web view. Pull to Refresh functionality is enabled by adding UIRefreshControl. Pull to Show More is enabled by adding UIActivityIndicatorView at the bottom of the page while measuring the scroll position using UIScrollView’s delegate methods.

  • Refresh
  • Infinite Scrolling
private func setupNativePullToRefresh() {
guard let scrollView = pageScrollSettings.scrollView else { return }

// Register PullToRefresh Listener
let refreshControl = RefreshControl()
refreshControl.addTarget(self, action: #selector(self.performPullToRefresh), for: .valueChanged)
scrollView.refreshControl = refreshControl
}
private func setupNativePullToShowMore() {
guard let scrollView = pageScrollSettings.scrollView else { return }

// Register PullToShowMore Listener
scrollView.delegate = self
pageScrollSettings.hasPulledToShowMore = false
}
extension ViewController: UIScrollViewDelegate {
public func scrollViewDidScroll(_ scrollView: UIScrollView) {
if reachedBottomOfPage() && !pageScrollSettings.hasPulledToShowMore {
// We have reached the bottom, so perform infinite scrolling
performPullToShowMore()
}
}
}

Android

On Android, we enclosed Chromium-based WebView in SwipeRefreshLayout View to enable Pull to Refresh capability. With Android API Level 23, all views, including webview, can register for scroll events more generically with View.OnScrollChangeListener. We registered the scroll listener on the webview, where we load more content (with Pull to Show More) when scrolled to the bottom of the page. We also enabled accessibility whenever the user invokes refresh and loads more content.

  • Refresh
  • Infinite Scrolling
override fun onCreateView(
inflater: LayoutInflater,
container: ViewGroup?,
savedInstanceState: Bundle?
): View? {
// Register PullToRefresh Listener
pullToRefreshView?.setOnRefreshListener {
if (pullToShowMoreProgress?.visibility != View.VISIBLE) {
performPullToRefresh()
}
}
// Register PullToShowMore Listener
webView?.setOnScrollChangeListener(scrollChangeListener(webView, ptsmEnabled))
}

private fun scrollChangeListener(webView: WebView, ptsmEnabled: Boolean
): View.OnScrollChangeListener {
return View.OnScrollChangeListener { view: View, _: Int, _: Int, _: Int, _: Int ->
pullToRefreshView?.let {
if (ptsmEnabled && !view.canScrollVertically(SCROLL_BOTTOM) && !it.isRefreshing) {
// We have reached the bottom, so perform infinite scrolling
performPullToShowMore()
}
}
}
}

Time to quantify the scrolling performance

So, how well did Native Scrolling perform compared to the UI Scroller, you might ask? While we anecdotally observed smoother scrolling with Native Scrolling, we decided to quantify and visualize this improved scrolling experience on iOS and Android using in-house tools.

The tool takes a video recording of the scrolling as input. Using the tesseract-ocr, the tool analyzes each frame and measures the shift in pixels. It outputs the actual shift over the scroll duration along with the ideal shift (or scrolling deceleration equation).

Scrolling mechanics on mobile are designed so that the initial velocity during the swipe is high and exponentially decelerates to a complete stop. The tool uses an exponential decay model as it best replicates the scrolling experience.

By analyzing the data with UI Scroller and Native scrolling, we could visualize how the scrolling improved. The above charts summarize the scroll behavior on both iOS and Android. The X-axis is the frames captured during the scroll, and the Y-axis is the shift in these frames. The blue lines represent the expected decay model for scrolling.

  • With Native Scroller, you can see that, after the initial spike during finger swipe, scrolling speed decays gradually from start to end. This explains the improved scrolling experience than UI Scroller.
  • We found that the scrolling decelerated linearly with more deviation from the expected model in the UI Scroller case. However, in the Native Scrolling case, the scroll follows the desired exponential deceleration model and provides a more natural user experience.

To summarize

With Native Scrolling, we not only saw a better scrolling experience but also improved performance. Mobile users can finally copy and paste text on record pages and refresh all web pages. Native scrolling led to a better and consistent experience across the hybrid stack.

Want to experience the new Native Scrolling of your custom components on mobile? Native scrolling is generally available in the Spring ’21 release of the Salesforce mobile app — no admin intervention required.

Happy Scrolling!


Native Scrolling in Salesforce mobile app was originally published in Salesforce Engineering on Medium, where people are continuing the conversation by highlighting and responding to this story.

Read More