{"id":270,"date":"2021-08-31T14:40:46","date_gmt":"2021-08-31T14:40:46","guid":{"rendered":"https:\/\/fde.cat\/?p=270"},"modified":"2021-08-31T14:40:46","modified_gmt":"2021-08-31T14:40:46","slug":"native-scrolling-in-salesforce-mobile-app","status":"publish","type":"post","link":"https:\/\/fde.cat\/index.php\/2021\/08\/31\/native-scrolling-in-salesforce-mobile-app\/","title":{"rendered":"Native Scrolling in Salesforce mobile app"},"content":{"rendered":"<h3>Native Scrolling in Salesforce Mobile\u00a0App<\/h3>\n<p>The <strong>Salesforce mobile app<\/strong> 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, <a href=\"https:\/\/trailhead.salesforce.com\/en\/content\/learn\/modules\/lightning-web-components-basics\/discover-lightning-web-components\">Lightning Web Components<\/a>, <a href=\"https:\/\/trailhead.salesforce.com\/en\/content\/learn\/modules\/lex_dev_lc_basics\">Aura<\/a>, and <a href=\"https:\/\/trailhead.salesforce.com\/en\/content\/learn\/modules\/visualforce_fundamentals\">VisualForce<\/a>.<\/p>\n<p>UI Scroller was added as a top layer to manage webview scrolling and scrolling-initiated actions, including:<\/p>\n<ul>\n<li>refreshing content via Pull to\u00a0Refresh<\/li>\n<li>loading new content with Pull to Show\u00a0More<\/li>\n<li>infinite scrolling capabilities<\/li>\n<\/ul>\n<p>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.<\/p>\n<p>UI Scroller has served our customers well so far. However, on iOS and Android, this additional layer leads\u00a0to<\/p>\n<ul>\n<li>Slower and unnatural scroll experience<\/li>\n<li>No copy-paste support<\/li>\n<li>Limited Accessibility Support<\/li>\n<\/ul>\n<p>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.<\/p>\n<figure><img decoding=\"async\" alt=\"\" src=\"https:\/\/i0.wp.com\/cdn-images-1.medium.com\/max\/1024\/1*5_ETdHh_T0Oax2rK5kG0qQ.jpeg?w=750&#038;ssl=1\" data-recalc-dims=\"1\"><\/figure>\n<h3>Native Scrolling implementation in the Salesforce mobile\u00a0app<\/h3>\n<p>Below is the high-level design for this implementation:<\/p>\n<figure><img decoding=\"async\" alt=\"\" src=\"https:\/\/i0.wp.com\/cdn-images-1.medium.com\/max\/1024\/1*Blu3TF90A-ZydQbJ7dXJPg.png?w=750&#038;ssl=1\" data-recalc-dims=\"1\"><\/figure>\n<p><em>Native Scrolling Design<\/em><\/p>\n<p>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\u00a0layer.<\/p>\n<h3>iOS<\/h3>\n<p>On iOS, we use WKWebView for loading web pages. UIScrollView associated with WKWebview results in efficient, smoother scrolling. UIScrollView\u2019s bounce effect and the ability to scroll the full page provide users with the seamless native iOS experience they expect from consumer\u00a0apps.<\/p>\n<p>WKWebView\u2019s 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\u2019s delegate\u00a0methods.<\/p>\n<ul>\n<li>Refresh<\/li>\n<li>Infinite Scrolling<\/li>\n<\/ul>\n<pre>private func setupNativePullToRefresh() {<br>    guard let scrollView = pageScrollSettings.scrollView else { return }<br>    <br>    \/\/ Register PullToRefresh Listener<br>    let refreshControl = RefreshControl()<br>    refreshControl.addTarget(self, action: #selector(self.performPullToRefresh), for: .valueChanged)<br>    scrollView.refreshControl = refreshControl<br>}<\/pre>\n<pre>private func setupNativePullToShowMore() {<br>    guard let scrollView = pageScrollSettings.scrollView else { return }<br>        <br>    \/\/ Register PullToShowMore Listener<br>    scrollView.delegate = self<br>    pageScrollSettings.hasPulledToShowMore = false<br>}<\/pre>\n<pre>extension ViewController: UIScrollViewDelegate {<br>    public func scrollViewDidScroll(_ scrollView: UIScrollView) {<br>        if reachedBottomOfPage() &amp;&amp; !pageScrollSettings.hasPulledToShowMore {<br>            \/\/ We have reached the bottom, so perform infinite scrolling<br>            performPullToShowMore()<br>        }<br>    }<br>}<\/pre>\n<h3>Android<\/h3>\n<p>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 <a href=\"https:\/\/developer.android.com\/reference\/android\/view\/View.OnScrollChangeListener.html\">View.OnScrollChangeListener<\/a>. 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\u00a0content.<\/p>\n<ul>\n<li>Refresh<\/li>\n<li>Infinite Scrolling<\/li>\n<\/ul>\n<pre>override fun onCreateView(<br>        inflater: LayoutInflater,<br>        container: ViewGroup?,<br>        savedInstanceState: Bundle?<br>    ): View? {<br>        \/\/ Register PullToRefresh Listener<br>        pullToRefreshView?.setOnRefreshListener {<br>            if (pullToShowMoreProgress?.visibility != View.VISIBLE)       {<br>                    performPullToRefresh()<br>            }<br>        }<br>        \/\/ Register PullToShowMore Listener<br>        webView?.setOnScrollChangeListener(scrollChangeListener(webView, ptsmEnabled))<br>    }<br>    <br>    private fun scrollChangeListener(webView: WebView, ptsmEnabled: Boolean<br>    ): View.OnScrollChangeListener {<br>        return View.OnScrollChangeListener { view: View, _: Int, _: Int, _: Int, _: Int -&gt;<br>            pullToRefreshView?.let {<br>                if (ptsmEnabled &amp;&amp; !view.canScrollVertically(SCROLL_BOTTOM) &amp;&amp; !it.isRefreshing) {<br>                    \/\/ We have reached the bottom, so perform infinite scrolling<br>                    performPullToShowMore()<br>                }<br>            }<br>        }<br>    }<\/pre>\n<h3>Time to quantify the scrolling performance<\/h3>\n<p>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\u00a0tools.<\/p>\n<p>The tool takes a video recording of the scrolling as input. Using the <a href=\"https:\/\/github.com\/tesseract-ocr\/\">tesseract-ocr<\/a>, 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).<\/p>\n<p>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 e<em>xponential decay model<\/em> as it best replicates the scrolling experience.<\/p>\n<figure><img decoding=\"async\" alt=\"\" src=\"https:\/\/i0.wp.com\/cdn-images-1.medium.com\/max\/1024\/1*_F5epwhSa3CM-NxZdzZk3Q.png?w=750&#038;ssl=1\" data-recalc-dims=\"1\"><\/figure>\n<p>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.<\/p>\n<ul>\n<li>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.<\/li>\n<li>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.<\/li>\n<\/ul>\n<h3>To summarize<\/h3>\n<p>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\u00a0stack.<\/p>\n<p>Want to experience the new Native Scrolling of your custom components on mobile? Native scrolling is generally available in the Spring \u201921 <a href=\"https:\/\/help.salesforce.com\/articleView?id=release-notes.rn_mobile_scrolling_improved.htm&amp;type=5&amp;release=230\">release<\/a> of the Salesforce mobile app\u200a\u2014\u200ano admin intervention required.<\/p>\n<p>Happy Scrolling!<\/p>\n<p><img loading=\"lazy\" decoding=\"async\" src=\"https:\/\/medium.com\/_\/stat?event=post.clientViewed&amp;referrerSource=full_rss&amp;postId=4f334b6ad96e\" width=\"1\" height=\"1\" alt=\"\"><\/p>\n<hr>\n<p><a href=\"https:\/\/engineering.salesforce.com\/native-scrolling-in-salesforce-mobile-app-4f334b6ad96e\">Native Scrolling in Salesforce mobile app<\/a> was originally published in <a href=\"https:\/\/engineering.salesforce.com\/\">Salesforce Engineering<\/a> on Medium, where people are continuing the conversation by highlighting and responding to this story.<\/p>\n<p><a href=\"https:\/\/engineering.salesforce.com\/native-scrolling-in-salesforce-mobile-app-4f334b6ad96e?source=rss----cfe1120185d3---4\" target=\"_blank\" rel=\"noopener\">Read More<\/a><\/p>\n","protected":false},"excerpt":{"rendered":"<p>Native Scrolling in Salesforce Mobile\u00a0App 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&hellip; <a class=\"more-link\" href=\"https:\/\/fde.cat\/index.php\/2021\/08\/31\/native-scrolling-in-salesforce-mobile-app\/\">Continue reading <span class=\"screen-reader-text\">Native Scrolling in Salesforce mobile app<\/span><\/a><\/p>\n","protected":false},"author":1,"featured_media":0,"comment_status":"closed","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"spay_email":"","footnotes":""},"categories":[7],"tags":[],"class_list":["post-270","post","type-post","status-publish","format-standard","hentry","category-technology","entry"],"jetpack_featured_media_url":"","jetpack-related-posts":[{"id":536,"url":"https:\/\/fde.cat\/index.php\/2022\/01\/25\/scaling-cross-team-contributions-to-a-native-mobile-app\/","url_meta":{"origin":270,"position":0},"title":"Scaling cross-team contributions to a native mobile app","date":"January 25, 2022","format":false,"excerpt":"By Stephen Goldberg, Alex Sikora, and Jean\u00a0Bovet Flagship applications are home to myriad functionalities that serve different parts of your userbase. Often, adding a new feature unintentionally causes reduced velocity, single points of failure, and monoliths that are hard to navigate. Such flagship apps are built from contributions from multiple\u2026","rel":"","context":"In &quot;Technology&quot;","img":{"alt_text":"","src":"","width":0,"height":0},"classes":[]},{"id":644,"url":"https:\/\/fde.cat\/index.php\/2022\/10\/24\/how-salesforce-built-a-cloud-native-task-execution-service\/","url_meta":{"origin":270,"position":1},"title":"How Salesforce Built a Cloud-Native Task Execution Service","date":"October 24, 2022","format":false,"excerpt":"If you\u2019re paying attention to Salesforce technology, you\u2019ve no doubt heard about\u00a0Hyperforce, our new approach to deploying Salesforce on public cloud providers. Start with\u00a0a look at Hyperforce\u2019s architecture. There are many compelling reasons to move to Hyperforce, both for us and our customers. We\u2019re excited to do it in the\u2026","rel":"","context":"In &quot;Technology&quot;","img":{"alt_text":"","src":"","width":0,"height":0},"classes":[]},{"id":506,"url":"https:\/\/fde.cat\/index.php\/2021\/11\/18\/measuring-the-memory-impact-for-hybrid-apps\/","url_meta":{"origin":270,"position":2},"title":"Measuring the Memory Impact for Hybrid Apps","date":"November 18, 2021","format":false,"excerpt":"Memory problems are always challenging to detect and fix for mobile applications, particularly on Android, due to many hardware profiles, OS versions, and OEM skins. With proper memory reporting and analysis, most issues are caught during the development lifecycle. Yet if your application is delivering an entire platform, such as\u2026","rel":"","context":"In &quot;Technology&quot;","img":{"alt_text":"","src":"","width":0,"height":0},"classes":[]},{"id":625,"url":"https:\/\/fde.cat\/index.php\/2022\/08\/30\/hyperpacks-using-buildpacks-to-build-hyperforce\/","url_meta":{"origin":270,"position":3},"title":"Hyperpacks: Using Buildpacks to Build Hyperforce","date":"August 30, 2022","format":false,"excerpt":"At Salesforce we regularly use our products and services to scale our own business. One example is Buildpacks, which we created nearly a decade ago and is now a part of Hyperforce. Hyperpacks are an innovative new way of using Cloud Native Buildpacks (CNB) to manage our public cloud infrastructure.\u00a0\u2026","rel":"","context":"In &quot;Technology&quot;","img":{"alt_text":"","src":"","width":0,"height":0},"classes":[]},{"id":306,"url":"https:\/\/fde.cat\/index.php\/2021\/08\/31\/blazing-the-trail-one-year-with-openjdk-11\/","url_meta":{"origin":270,"position":4},"title":"Blazing the Trail: One Year with OpenJDK 11","date":"August 31, 2021","format":false,"excerpt":"Early Adoption of Java Runtime Innovations in Production at\u00a0ScaleCo-written by Donna\u00a0ThomasIntroductionSalesforce was one of the first major enterprises to adopt OpenJDK 11 at scale in production, starting our adoption journey shortly after its release in late 2018. Cutting edge? Sure. Safe? Absolutely. You might not know this, but Salesforce has\u2026","rel":"","context":"In &quot;Technology&quot;","img":{"alt_text":"","src":"","width":0,"height":0},"classes":[]},{"id":538,"url":"https:\/\/fde.cat\/index.php\/2022\/02\/01\/behind-the-scenes-of-hyperforce-salesforces-infrastructure-for-the-public-cloud\/","url_meta":{"origin":270,"position":5},"title":"Behind the Scenes of Hyperforce: Salesforce\u2019s Infrastructure for the Public Cloud","date":"February 1, 2022","format":false,"excerpt":"Salesforce has been running cloud infrastructure for over two decades, bringing companies and their customers together. When Salesforce first started out in 1999, the world was very different; back then, the only practical way to provide our brand of Software-As-A-Service was to run everything yourself\u200a\u2014\u200anot just the software, but the\u2026","rel":"","context":"In &quot;Technology&quot;","img":{"alt_text":"","src":"","width":0,"height":0},"classes":[]}],"_links":{"self":[{"href":"https:\/\/fde.cat\/index.php\/wp-json\/wp\/v2\/posts\/270","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/fde.cat\/index.php\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/fde.cat\/index.php\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/fde.cat\/index.php\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/fde.cat\/index.php\/wp-json\/wp\/v2\/comments?post=270"}],"version-history":[{"count":1,"href":"https:\/\/fde.cat\/index.php\/wp-json\/wp\/v2\/posts\/270\/revisions"}],"predecessor-version":[{"id":440,"href":"https:\/\/fde.cat\/index.php\/wp-json\/wp\/v2\/posts\/270\/revisions\/440"}],"wp:attachment":[{"href":"https:\/\/fde.cat\/index.php\/wp-json\/wp\/v2\/media?parent=270"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/fde.cat\/index.php\/wp-json\/wp\/v2\/categories?post=270"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/fde.cat\/index.php\/wp-json\/wp\/v2\/tags?post=270"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}