{"id":332,"date":"2016-05-12T15:18:09","date_gmt":"2016-05-12T15:18:09","guid":{"rendered":"http:\/\/blog.dexem.com\/en\/?p=332"},"modified":"2025-01-28T14:10:34","modified_gmt":"2025-01-28T14:10:34","slug":"dexem-tested-turbolinks-application","status":"publish","type":"post","link":"https:\/\/blog.dexem.com\/en\/dexem-tested-turbolinks-application\/","title":{"rendered":"A hybrid Android\/iOS application with Rails and Turbolinks 5"},"content":{"rendered":"<p>Since a<a href=\"https:\/\/twitter.com\/dhh\/status\/703248379762118656\" target=\"_blank\" rel=\"noopener noreferrer\"> recent tweet from @DHH<\/a>, we wanted to try the lastest beta of Ruby On Rails for our R&amp;D at Dexem, which introduces the brand new version of Turbolinks.<\/p>\n<p><a href=\"https:\/\/blog.dexem.com\/en\/files\/2016\/05\/tweet.png\"><img decoding=\"async\" class=\"aligncenter size-full wp-image-333\" style=\"border: 1px solid #D4D6D8;\" src=\"https:\/\/blog.dexem.com\/en\/files\/2016\/05\/tweet.png\" alt=\"tweet\" width=\"628\" height=\"327\" \/><\/a><\/p>\n<h2><strong>What is Turbolinks ?<\/strong><\/h2>\n<p>You may already know this name, for the good reason this component already exists in Rails version 4. To be simple, the goal of Turbolinks is to turn your Rails Web application into a Javascript one, in order to let users consult it from a single page. The client doesn\u2019t need to reload all the content each time, the body of the page is replaced by new data provided with an async request to the server. In a way, it looks like the AngularJS approach, because both use HTML History API.<\/p>\n<p>But the new big feature from Turbolinks 5 is the two new wrappers that let developers build simple natives iOS\/Android applications, working with some views of their Rails 5 web-app. This wrappers are similar to native web views. In fact, you\u2019re going to be able to work with Turbolinks callbacks from your Web app to your mobile app and vice versa.<\/p>\n<p>&nbsp;<\/p>\n<h2><strong>How we tested Turbolinks 5 on Dexem products<\/strong><\/h2>\n<p>At Dexem, our customers would like to have an mobile extension of our Web applications. We\u2019ve decided to focus on their needs, and it appeared that the features they wanted, are not too difficult to develop in a mobile context.<\/p>\n<p>From this point, we had different choices :<\/p>\n<ol>\n<li>Start native developments on both iOS and Android<\/li>\n<li>Start a hybrid Javascript development with Titanium or Cordova<\/li>\n<li>Test Turbolinks 5 and Rails 5<\/li>\n<\/ol>\n<p>&nbsp;<\/p>\n<p>We\u2019ve decided to choice the third option. Why ? Because if you try to work with Turbolinks wrapper on both Android and iOS, you\u2019ll realize that\u2019s the integration process is dead easy. In just a few hours, we were able to link our Web application with the mobile application and all was stable. Our team had all the skills required by this operation, and the evolution of the mobile application was going to be easy.<\/p>\n<p>&nbsp;<\/p>\n<h2><strong>How did I make it work?<\/strong><\/h2>\n<p>We needed to be able to build some views of our Web application into a mobile one. This Web application works with MySQL database to fetch data. First, we put four mobiles views in it:<\/p>\n<ul>\n<li>a form<\/li>\n<li>a recent calls list<\/li>\n<li>a contact page<\/li>\n<li>a settings page<\/li>\n<\/ul>\n<p>&nbsp;<\/p>\n<p>Prerequesites:<\/p>\n<ul>\n<li><a href=\"https:\/\/rubygems.org\/pages\/download\" target=\"_blank\" rel=\"noopener noreferrer\">RubyGem<\/a><\/li>\n<li><a href=\"https:\/\/developer.android.com\/studio\" target=\"_blank\" rel=\"noopener noreferrer\">Android Studio<\/a><\/li>\n<li>Ruby On Rails <a href=\"https:\/\/weblog.rubyonrails.org\/2016\/2\/27\/Rails-5-0-beta3\/\" target=\"_blank\" rel=\"noopener noreferrer\">Version 5.0.0.beta3<\/a><\/li>\n<\/ul>\n<p>&nbsp;<\/p>\n<p>First, we have generated a simple Web application project with the following Rails command.<\/p>\n<table width=\"602\">\n<tbody>\n<tr>\n<td>rails _5.0.0.beta3_ generate dexem-webapp<\/td>\n<\/tr>\n<\/tbody>\n<\/table>\n<p>&nbsp;<\/p>\n<p>This allows you to bring automatically the needed Gemfile dependances (like Turbolinks 5) in your project. You can also <a href=\"https:\/\/github.com\/turbolinks\/turbolinks#installation-using-ruby-on-rails\" target=\"_blank\" rel=\"noopener noreferrer\">do it manually<\/a>.<\/p>\n<p><a href=\"https:\/\/blog.dexem.com\/en\/files\/2016\/05\/1.jpg\"><img decoding=\"async\" class=\"aligncenter size-full wp-image-334\" style=\"border: 1px solid #D4D6D8;\" src=\"https:\/\/blog.dexem.com\/en\/files\/2016\/05\/1.jpg\" alt=\"1\" width=\"1380\" height=\"810\" srcset=\"https:\/\/blog.dexem.com\/en\/files\/2016\/05\/1.jpg 1380w, https:\/\/blog.dexem.com\/en\/files\/2016\/05\/1-300x176.jpg 300w, https:\/\/blog.dexem.com\/en\/files\/2016\/05\/1-768x451.jpg 768w, https:\/\/blog.dexem.com\/en\/files\/2016\/05\/1-1024x601.jpg 1024w\" sizes=\"(max-width: 1380px) 100vw, 1380px\" \/><\/a><\/p>\n<p>Next, we defined four methods in a controller with their associated routes and views. Two of them used Active::Support to fetch data in our MySQL database. The other ones displayed the belonging controller\u2019s view.<\/p>\n<p><a href=\"https:\/\/blog.dexem.com\/en\/files\/2016\/05\/2.jpg\"><img decoding=\"async\" class=\"aligncenter size-full wp-image-335\" style=\"border: 1px solid #D4D6D8;\" src=\"https:\/\/blog.dexem.com\/en\/files\/2016\/05\/2.jpg\" alt=\"2\" width=\"1600\" height=\"1004\" srcset=\"https:\/\/blog.dexem.com\/en\/files\/2016\/05\/2.jpg 1600w, https:\/\/blog.dexem.com\/en\/files\/2016\/05\/2-300x188.jpg 300w, https:\/\/blog.dexem.com\/en\/files\/2016\/05\/2-768x482.jpg 768w, https:\/\/blog.dexem.com\/en\/files\/2016\/05\/2-1024x643.jpg 1024w\" sizes=\"(max-width: 1600px) 100vw, 1600px\" \/><\/a><\/p>\n<p>Finally, we made the web-app available from our network with the following Rails command :<\/p>\n<table width=\"602\">\n<tbody>\n<tr>\n<td>rails s &lt;local_ip&gt;:&lt;port&gt;<\/td>\n<\/tr>\n<\/tbody>\n<\/table>\n<p>&nbsp;<\/p>\n<p>Let\u2019s now have a look on the mobile part, and see how the Android wrapper of Turbolinks works. With Android Studio, we first created an empty single view application.<\/p>\n<p>We had to provide the correct permission in the Android manifest :<\/p>\n<table width=\"602\">\n<tbody>\n<tr>\n<td>&lt;uses-permission android:name=&#8221;android.permission.INTERNET&#8221; \/&gt;<\/td>\n<\/tr>\n<\/tbody>\n<\/table>\n<p>&nbsp;<\/p>\n<p>As we said before, using the Android wrapper of Turbolinks proves to be very simple. <a href=\"https:\/\/github.com\/turbolinks\/turbolinks-android#turbolinks-android\" target=\"_blank\" rel=\"noopener noreferrer\">See the minimal requirements on the Turbolinks Github<\/a>. Though, you will need a few skills to understand the life cycle of an Android mobile application.<\/p>\n<p>For the next step, we had to add the Turbolinks Adapter interface, the mandatory methods for our class, and the Turbolinks View.<\/p>\n<ul>\n<li>public void onPageFinished()<\/li>\n<li>public void onReceivedError(int errorCode)<\/li>\n<li>public void pageInvalidated()<\/li>\n<li>public void requestFailedWithStatusCode(int statusCode)<\/li>\n<li>public void visitCompleted()<\/li>\n<li>public void <strong>visitProposedToLocationWithAction <\/strong>(String location, String action)<\/li>\n<\/ul>\n<p>&nbsp;<\/p>\n<p>If you want Turbolinks to work successfully, then you\u2019ll need to create an instance of the <em>TurbolinksSession<\/em> object by following the <strong>onCreate<\/strong> method of your activity. It will activate Turbolinks system in your activity and load the URL in the Turbolinks Webview.<\/p>\n<table width=\"602\">\n<tbody>\n<tr>\n<td>@Override<\/p>\n<p>protected void <strong>onCreate<\/strong>(Bundle savedInstanceState) {<\/p>\n<p>\/\/ Standard activity boilerplate here&#8230;<\/p>\n<p>super.onCreate(savedInstanceState);<\/p>\n<p>setContentView(R.layout.name_of_your_view_activity);<\/p>\n<p>\/\/ Assumes an instance variable is defined. Find the view you added to your<\/p>\n<p>\/\/ layout in step 1.<\/p>\n<p>turbolinksView = (TurbolinksView) findViewById(R.id.turbolinks_view);<\/p>\n<p>location = getIntent().getStringExtra(INTENT_URL) != null ? getIntent().getStringExtra(INTENT_URL) : &#8220;https:\/\/&lt;local_ip&gt;:&lt;port&gt;&#8221;;<\/p>\n<p>TurbolinksSession.getDefault(this)<\/p>\n<p>.activity(this)<\/p>\n<p>.adapter(this)<\/p>\n<p>.view(turbolinksView)<\/p>\n<p>.visit(location);<\/p>\n<p>}<\/td>\n<\/tr>\n<\/tbody>\n<\/table>\n<p>What happens when a link is clicked on the Web application from the Turbolinks View ? This is the role played by the <strong>visitProposedToLocationWithAction <\/strong>method.<\/p>\n<table width=\"602\">\n<tbody>\n<tr>\n<td>@Override<\/p>\n<p>public void <strong>visitProposedToLocationWithAction<\/strong>(String location, String action) {<\/p>\n<p>Intent intent = new Intent(this, MainActivity.class);<\/p>\n<p>intent.putExtra(INTENT_URL, location);<\/p>\n<p>&nbsp;<\/p>\n<p>this.startActivity(intent);<\/p>\n<p>}<\/td>\n<\/tr>\n<\/tbody>\n<\/table>\n<p>This method is called every time that Turbolinks notices a link that has been clicked on in the Web application. Once it\u2019s done, it creates a new activity in our application and attaches the URL to visit. All this stuff will be caught after in the <strong>onCreate<\/strong> method. And so the URL will be loaded in our Turbolinks Webview.<\/p>\n<p>You can also use <strong>visitProposedToLocationWithAction <\/strong>to provide more actions like : opening a native application, start a different activity\u2026<\/p>\n<p>This is what we\u2019ve done at Dexem. As you can see on the screens below, we have implemented two differents activities. One empty, and one with native navigation buttons.<\/p>\n<p style=\"text-align: center;\"><a href=\"https:\/\/blog.dexem.com\/en\/files\/2016\/05\/3.png\"><img decoding=\"async\" class=\"aligncenter size-full wp-image-336\" style=\"border: 1px solid #D4D6D8;\" src=\"https:\/\/blog.dexem.com\/en\/files\/2016\/05\/3.png\" alt=\"3\" width=\"595\" height=\"339\" \/><\/a><\/p>\n<p style=\"text-align: center;\">To let the app history work, we\u2019ve also need to add the <strong>onRestart<\/strong> method in our activity :<\/p>\n<table width=\"602\">\n<tbody>\n<tr>\n<td>@Override<\/p>\n<p>protected void <strong>onRestart<\/strong>() {<\/p>\n<p>super.onRestart();<\/p>\n<p>TurbolinksSession.getDefault(this)<\/p>\n<p>.activity(this)<\/p>\n<p>.adapter(this)<\/p>\n<p>.restoreWithCachedSnapshot(true)<\/p>\n<p>.view(turbolinksView)<\/p>\n<p>.visit(location);<\/p>\n<p>}<\/td>\n<\/tr>\n<\/tbody>\n<\/table>\n<p>No big surprises here, again you have to call the default TurbolinksSession instance, and then add the <strong>restoreWithCachedSnapshot(true)<\/strong> option, to retrieve the user\u2019s navigation history in the WebView.<\/p>\n<p>Your class for a single simple activity should be like the code below :<\/p>\n<table width=\"602\">\n<tbody>\n<tr>\n<td>package com.dexem.callmanager;<\/p>\n<p>import android.support.v7.app.AppCompatActivity;<\/p>\n<p>import android.os.Bundle;<\/p>\n<p>import android.content.Intent;<\/p>\n<p>import com.basecamp.turbolinks.TurbolinksAdapter;<\/p>\n<p>import com.basecamp.turbolinks.TurbolinksSession;<\/p>\n<p>import com.basecamp.turbolinks.TurbolinksView;<\/p>\n<p>public class MainActivity extends AppCompatActivity implements TurbolinksAdapter {<\/p>\n<p>private static final String INTENT_URL = &#8220;intentUrl&#8221;;<\/p>\n<p>private static final String BASE_URL = &#8220;https:\/\/&lt;ip_locale&gt;:&lt;port&gt;&#8221;;<\/p>\n<p>private String location;<\/p>\n<p>private TurbolinksView turbolinksView;<\/p>\n<p>@Override<\/p>\n<p>protected void onCreate(Bundle savedInstanceState) {<\/p>\n<p>super.onCreate(savedInstanceState);<\/p>\n<p>setContentView(R.layout.activity_main);<\/p>\n<p>turbolinksView = (TurbolinksView) findViewById(R.id.turbolinks_view);<\/p>\n<p>location = getIntent().getStringExtra(INTENT_URL) != null ? getIntent().getStringExtra(INTENT_URL) : BASE_URL;<\/p>\n<p>TurbolinksSession.getDefault(this)<\/p>\n<p>.activity(this)<\/p>\n<p>.adapter(this)<\/p>\n<p>.view(turbolinksView)<\/p>\n<p>.visit(location);<\/p>\n<p>}<\/p>\n<p>@Override<\/p>\n<p>protected void onRestart() {<\/p>\n<p>super.onRestart();<\/p>\n<p>TurbolinksSession.getDefault(this)<\/p>\n<p>.activity(this)<\/p>\n<p>.adapter(this)<\/p>\n<p>.restoreWithCachedSnapshot(true)<\/p>\n<p>.view(turbolinksView)<\/p>\n<p>.visit(location);<\/p>\n<p>}<\/p>\n<p>@Override<\/p>\n<p>public void onPageFinished() {<\/p>\n<p>}<\/p>\n<p>@Override<\/p>\n<p>public void onReceivedError(int errorCode) {<\/p>\n<p>}<\/p>\n<p>@Override<\/p>\n<p>public void pageInvalidated() {<\/p>\n<p>}<\/p>\n<p>@Override<\/p>\n<p>public void requestFailedWithStatusCode(int statusCode) {<\/p>\n<p>}<\/p>\n<p>@Override<\/p>\n<p>public void visitCompleted() {<\/p>\n<p>}<\/p>\n<p>@Override<\/p>\n<p>public void visitProposedToLocationWithAction(String location, String action) {<\/p>\n<p>Intent intent = new Intent(this, MainActivity.class);<\/p>\n<p>intent.putExtra(INTENT_URL, location);<\/p>\n<p>this.startActivity(intent);<\/p>\n<p>}<\/p>\n<p>}<\/td>\n<\/tr>\n<\/tbody>\n<\/table>\n<p>&nbsp;<\/p>\n<h2><strong>Want to start using Turbolinks 5 and Rails 5 now?<\/strong><\/h2>\n<p>Turbolinks version 5 may still be a little young, and lack of community, though developing mobile application with Turbolinks represents an interesting perspective. Indeed, by adopting a hybrid development mostly based on a Web application on Rails 5, the cost of maintenance and development is significantly lower than specialized teams in native development. If you have any doubt, know that Basecamp 3 uses Turbolinks 5 and Rails 5 for the mobile application. DHH wrote a <a href=\"https:\/\/signalvnoise.com\/posts\/3743-hybrid-sweet-spot-native-navigation-web-content\" target=\"_blank\" rel=\"noopener noreferrer\">interesting post on his blog<\/a> about managing the development of an application.<\/p>\n","protected":false},"excerpt":{"rendered":"<p>Since a recent tweet from @DHH, we wanted to try the lastest beta of Ruby On Rails for our R&amp;D at Dexem, which introduces the brand new version of Turbolinks. What is Turbolinks ? You may already know this name, for the good reason this component already exists in Rails version 4. To be simple, [&hellip;]<\/p>\n","protected":false},"author":3,"featured_media":1014,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[35],"tags":[],"class_list":["post-332","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-news"],"yoast_head":"<!-- This site is optimized with the Yoast SEO plugin v27.5 - https:\/\/yoast.com\/product\/yoast-seo-wordpress\/ -->\n<title>Dexem tested Turbolinks 5 with an web application on Rails<\/title>\n<meta name=\"description\" content=\"At Dexem, our customers would like to have an mobile extension of our Web applications. We\u2019ve decided to focus on their needs with this application.\" \/>\n<meta name=\"robots\" content=\"index, follow, max-snippet:-1, max-image-preview:large, max-video-preview:-1\" \/>\n<link rel=\"canonical\" href=\"https:\/\/blog.dexem.com\/en\/dexem-tested-turbolinks-application\/\" \/>\n<meta property=\"og:locale\" content=\"en_US\" \/>\n<meta property=\"og:type\" content=\"article\" \/>\n<meta property=\"og:title\" content=\"Dexem tested Turbolinks 5 with an web application on Rails\" \/>\n<meta property=\"og:description\" content=\"At Dexem, our customers would like to have an mobile extension of our Web applications. We\u2019ve decided to focus on their needs with this application.\" \/>\n<meta property=\"og:url\" content=\"https:\/\/blog.dexem.com\/en\/dexem-tested-turbolinks-application\/\" \/>\n<meta property=\"og:site_name\" content=\"EN - Dexem Blog\" \/>\n<meta property=\"article:published_time\" content=\"2016-05-12T15:18:09+00:00\" \/>\n<meta property=\"article:modified_time\" content=\"2025-01-28T14:10:34+00:00\" \/>\n<meta property=\"og:image\" content=\"https:\/\/blog.dexem.com\/en\/files\/2016\/05\/590x252-A-hybrid-AndroidiOS-application-with-Rails-and-Turbolinks-5.png\" \/>\n\t<meta property=\"og:image:width\" content=\"590\" \/>\n\t<meta property=\"og:image:height\" content=\"252\" \/>\n\t<meta property=\"og:image:type\" content=\"image\/png\" \/>\n<meta name=\"author\" content=\"Olivier Jaouen\" \/>\n<meta name=\"twitter:card\" content=\"summary_large_image\" \/>\n<meta name=\"twitter:creator\" content=\"@ojaouen\" \/>\n<meta name=\"twitter:label1\" content=\"Written by\" \/>\n\t<meta name=\"twitter:data1\" content=\"Olivier Jaouen\" \/>\n\t<meta name=\"twitter:label2\" content=\"Est. reading time\" \/>\n\t<meta name=\"twitter:data2\" content=\"7 minutes\" \/>\n<script type=\"application\/ld+json\" class=\"yoast-schema-graph\">{\"@context\":\"https:\\\/\\\/schema.org\",\"@graph\":[{\"@type\":\"Article\",\"@id\":\"https:\\\/\\\/blog.dexem.com\\\/en\\\/dexem-tested-turbolinks-application\\\/#article\",\"isPartOf\":{\"@id\":\"https:\\\/\\\/blog.dexem.com\\\/en\\\/dexem-tested-turbolinks-application\\\/\"},\"author\":{\"name\":\"Olivier Jaouen\",\"@id\":\"https:\\\/\\\/blog.dexem.com\\\/en\\\/#\\\/schema\\\/person\\\/0e642488202f4135f43165e1cd8ecfae\"},\"headline\":\"A hybrid Android\\\/iOS application with Rails and Turbolinks 5\",\"datePublished\":\"2016-05-12T15:18:09+00:00\",\"dateModified\":\"2025-01-28T14:10:34+00:00\",\"mainEntityOfPage\":{\"@id\":\"https:\\\/\\\/blog.dexem.com\\\/en\\\/dexem-tested-turbolinks-application\\\/\"},\"wordCount\":1275,\"commentCount\":0,\"publisher\":{\"@id\":\"https:\\\/\\\/blog.dexem.com\\\/en\\\/#organization\"},\"image\":{\"@id\":\"https:\\\/\\\/blog.dexem.com\\\/en\\\/dexem-tested-turbolinks-application\\\/#primaryimage\"},\"thumbnailUrl\":\"https:\\\/\\\/blog.dexem.com\\\/en\\\/files\\\/2016\\\/05\\\/590x252-A-hybrid-AndroidiOS-application-with-Rails-and-Turbolinks-5.webp\",\"articleSection\":[\"News\"],\"inLanguage\":\"en-US\",\"potentialAction\":[{\"@type\":\"CommentAction\",\"name\":\"Comment\",\"target\":[\"https:\\\/\\\/blog.dexem.com\\\/en\\\/dexem-tested-turbolinks-application\\\/#respond\"]}]},{\"@type\":\"WebPage\",\"@id\":\"https:\\\/\\\/blog.dexem.com\\\/en\\\/dexem-tested-turbolinks-application\\\/\",\"url\":\"https:\\\/\\\/blog.dexem.com\\\/en\\\/dexem-tested-turbolinks-application\\\/\",\"name\":\"Dexem tested Turbolinks 5 with an web application on Rails\",\"isPartOf\":{\"@id\":\"https:\\\/\\\/blog.dexem.com\\\/en\\\/#website\"},\"primaryImageOfPage\":{\"@id\":\"https:\\\/\\\/blog.dexem.com\\\/en\\\/dexem-tested-turbolinks-application\\\/#primaryimage\"},\"image\":{\"@id\":\"https:\\\/\\\/blog.dexem.com\\\/en\\\/dexem-tested-turbolinks-application\\\/#primaryimage\"},\"thumbnailUrl\":\"https:\\\/\\\/blog.dexem.com\\\/en\\\/files\\\/2016\\\/05\\\/590x252-A-hybrid-AndroidiOS-application-with-Rails-and-Turbolinks-5.webp\",\"datePublished\":\"2016-05-12T15:18:09+00:00\",\"dateModified\":\"2025-01-28T14:10:34+00:00\",\"description\":\"At Dexem, our customers would like to have an mobile extension of our Web applications. We\u2019ve decided to focus on their needs with this application.\",\"breadcrumb\":{\"@id\":\"https:\\\/\\\/blog.dexem.com\\\/en\\\/dexem-tested-turbolinks-application\\\/#breadcrumb\"},\"inLanguage\":\"en-US\",\"potentialAction\":[{\"@type\":\"ReadAction\",\"target\":[\"https:\\\/\\\/blog.dexem.com\\\/en\\\/dexem-tested-turbolinks-application\\\/\"]}]},{\"@type\":\"ImageObject\",\"inLanguage\":\"en-US\",\"@id\":\"https:\\\/\\\/blog.dexem.com\\\/en\\\/dexem-tested-turbolinks-application\\\/#primaryimage\",\"url\":\"https:\\\/\\\/blog.dexem.com\\\/en\\\/files\\\/2016\\\/05\\\/590x252-A-hybrid-AndroidiOS-application-with-Rails-and-Turbolinks-5.webp\",\"contentUrl\":\"https:\\\/\\\/blog.dexem.com\\\/en\\\/files\\\/2016\\\/05\\\/590x252-A-hybrid-AndroidiOS-application-with-Rails-and-Turbolinks-5.webp\",\"width\":590,\"height\":252,\"caption\":\"A hybrid Android\\\/iOS application with Rails and Turbolinks 5\"},{\"@type\":\"BreadcrumbList\",\"@id\":\"https:\\\/\\\/blog.dexem.com\\\/en\\\/dexem-tested-turbolinks-application\\\/#breadcrumb\",\"itemListElement\":[{\"@type\":\"ListItem\",\"position\":1,\"name\":\"Home\",\"item\":\"https:\\\/\\\/blog.dexem.com\\\/en\\\/\"},{\"@type\":\"ListItem\",\"position\":2,\"name\":\"A hybrid Android\\\/iOS application with Rails and Turbolinks 5\"}]},{\"@type\":\"WebSite\",\"@id\":\"https:\\\/\\\/blog.dexem.com\\\/en\\\/#website\",\"url\":\"https:\\\/\\\/blog.dexem.com\\\/en\\\/\",\"name\":\"EN - Dexem Blog\",\"description\":\"\",\"publisher\":{\"@id\":\"https:\\\/\\\/blog.dexem.com\\\/en\\\/#organization\"},\"potentialAction\":[{\"@type\":\"SearchAction\",\"target\":{\"@type\":\"EntryPoint\",\"urlTemplate\":\"https:\\\/\\\/blog.dexem.com\\\/en\\\/?s={search_term_string}\"},\"query-input\":{\"@type\":\"PropertyValueSpecification\",\"valueRequired\":true,\"valueName\":\"search_term_string\"}}],\"inLanguage\":\"en-US\"},{\"@type\":\"Organization\",\"@id\":\"https:\\\/\\\/blog.dexem.com\\\/en\\\/#organization\",\"name\":\"EN - Dexem Blog\",\"url\":\"https:\\\/\\\/blog.dexem.com\\\/en\\\/\",\"logo\":{\"@type\":\"ImageObject\",\"inLanguage\":\"en-US\",\"@id\":\"https:\\\/\\\/blog.dexem.com\\\/en\\\/#\\\/schema\\\/logo\\\/image\\\/\",\"url\":\"https:\\\/\\\/blog.dexem.com\\\/en\\\/files\\\/2024\\\/06\\\/dexem-130x36-1.svg\",\"contentUrl\":\"https:\\\/\\\/blog.dexem.com\\\/en\\\/files\\\/2024\\\/06\\\/dexem-130x36-1.svg\",\"width\":130,\"height\":36,\"caption\":\"EN - Dexem Blog\"},\"image\":{\"@id\":\"https:\\\/\\\/blog.dexem.com\\\/en\\\/#\\\/schema\\\/logo\\\/image\\\/\"}},{\"@type\":\"Person\",\"@id\":\"https:\\\/\\\/blog.dexem.com\\\/en\\\/#\\\/schema\\\/person\\\/0e642488202f4135f43165e1cd8ecfae\",\"name\":\"Olivier Jaouen\",\"image\":{\"@type\":\"ImageObject\",\"inLanguage\":\"en-US\",\"@id\":\"https:\\\/\\\/secure.gravatar.com\\\/avatar\\\/3e495d6abc8bafb416b520449319930a633b67bdc250ffaad8692afc580e0ca3?s=96&d=mm&r=g\",\"url\":\"https:\\\/\\\/secure.gravatar.com\\\/avatar\\\/3e495d6abc8bafb416b520449319930a633b67bdc250ffaad8692afc580e0ca3?s=96&d=mm&r=g\",\"contentUrl\":\"https:\\\/\\\/secure.gravatar.com\\\/avatar\\\/3e495d6abc8bafb416b520449319930a633b67bdc250ffaad8692afc580e0ca3?s=96&d=mm&r=g\",\"caption\":\"Olivier Jaouen\"},\"description\":\"En tant que CMO, je couvre l'int\u00e9gralit\u00e9 du cycle de vie de nos clients. Je consacre mes journ\u00e9es aux interactions avec nos leads et clients, \u00e0 l'am\u00e9lioration de nos produits, \u00e0 la mise en place de nos strat\u00e9gies marketing, et \u00e0 l'accompagnement de mon \u00e9quipe.\",\"sameAs\":[\"https:\\\/\\\/www.dexem.com\",\"\\\/in\\\/ojaouen\\\/\",\"https:\\\/\\\/x.com\\\/ojaouen\"],\"url\":\"https:\\\/\\\/blog.dexem.com\\\/en\\\/author\\\/ojaouen\\\/\"}]}<\/script>\n<!-- \/ Yoast SEO plugin. -->","yoast_head_json":{"title":"Dexem tested Turbolinks 5 with an web application on Rails","description":"At Dexem, our customers would like to have an mobile extension of our Web applications. We\u2019ve decided to focus on their needs with this application.","robots":{"index":"index","follow":"follow","max-snippet":"max-snippet:-1","max-image-preview":"max-image-preview:large","max-video-preview":"max-video-preview:-1"},"canonical":"https:\/\/blog.dexem.com\/en\/dexem-tested-turbolinks-application\/","og_locale":"en_US","og_type":"article","og_title":"Dexem tested Turbolinks 5 with an web application on Rails","og_description":"At Dexem, our customers would like to have an mobile extension of our Web applications. We\u2019ve decided to focus on their needs with this application.","og_url":"https:\/\/blog.dexem.com\/en\/dexem-tested-turbolinks-application\/","og_site_name":"EN - Dexem Blog","article_published_time":"2016-05-12T15:18:09+00:00","article_modified_time":"2025-01-28T14:10:34+00:00","og_image":[{"width":590,"height":252,"url":"https:\/\/blog.dexem.com\/en\/files\/2016\/05\/590x252-A-hybrid-AndroidiOS-application-with-Rails-and-Turbolinks-5.png","type":"image\/png"}],"author":"Olivier Jaouen","twitter_card":"summary_large_image","twitter_creator":"@ojaouen","twitter_misc":{"Written by":"Olivier Jaouen","Est. reading time":"7 minutes"},"schema":{"@context":"https:\/\/schema.org","@graph":[{"@type":"Article","@id":"https:\/\/blog.dexem.com\/en\/dexem-tested-turbolinks-application\/#article","isPartOf":{"@id":"https:\/\/blog.dexem.com\/en\/dexem-tested-turbolinks-application\/"},"author":{"name":"Olivier Jaouen","@id":"https:\/\/blog.dexem.com\/en\/#\/schema\/person\/0e642488202f4135f43165e1cd8ecfae"},"headline":"A hybrid Android\/iOS application with Rails and Turbolinks 5","datePublished":"2016-05-12T15:18:09+00:00","dateModified":"2025-01-28T14:10:34+00:00","mainEntityOfPage":{"@id":"https:\/\/blog.dexem.com\/en\/dexem-tested-turbolinks-application\/"},"wordCount":1275,"commentCount":0,"publisher":{"@id":"https:\/\/blog.dexem.com\/en\/#organization"},"image":{"@id":"https:\/\/blog.dexem.com\/en\/dexem-tested-turbolinks-application\/#primaryimage"},"thumbnailUrl":"https:\/\/blog.dexem.com\/en\/files\/2016\/05\/590x252-A-hybrid-AndroidiOS-application-with-Rails-and-Turbolinks-5.webp","articleSection":["News"],"inLanguage":"en-US","potentialAction":[{"@type":"CommentAction","name":"Comment","target":["https:\/\/blog.dexem.com\/en\/dexem-tested-turbolinks-application\/#respond"]}]},{"@type":"WebPage","@id":"https:\/\/blog.dexem.com\/en\/dexem-tested-turbolinks-application\/","url":"https:\/\/blog.dexem.com\/en\/dexem-tested-turbolinks-application\/","name":"Dexem tested Turbolinks 5 with an web application on Rails","isPartOf":{"@id":"https:\/\/blog.dexem.com\/en\/#website"},"primaryImageOfPage":{"@id":"https:\/\/blog.dexem.com\/en\/dexem-tested-turbolinks-application\/#primaryimage"},"image":{"@id":"https:\/\/blog.dexem.com\/en\/dexem-tested-turbolinks-application\/#primaryimage"},"thumbnailUrl":"https:\/\/blog.dexem.com\/en\/files\/2016\/05\/590x252-A-hybrid-AndroidiOS-application-with-Rails-and-Turbolinks-5.webp","datePublished":"2016-05-12T15:18:09+00:00","dateModified":"2025-01-28T14:10:34+00:00","description":"At Dexem, our customers would like to have an mobile extension of our Web applications. We\u2019ve decided to focus on their needs with this application.","breadcrumb":{"@id":"https:\/\/blog.dexem.com\/en\/dexem-tested-turbolinks-application\/#breadcrumb"},"inLanguage":"en-US","potentialAction":[{"@type":"ReadAction","target":["https:\/\/blog.dexem.com\/en\/dexem-tested-turbolinks-application\/"]}]},{"@type":"ImageObject","inLanguage":"en-US","@id":"https:\/\/blog.dexem.com\/en\/dexem-tested-turbolinks-application\/#primaryimage","url":"https:\/\/blog.dexem.com\/en\/files\/2016\/05\/590x252-A-hybrid-AndroidiOS-application-with-Rails-and-Turbolinks-5.webp","contentUrl":"https:\/\/blog.dexem.com\/en\/files\/2016\/05\/590x252-A-hybrid-AndroidiOS-application-with-Rails-and-Turbolinks-5.webp","width":590,"height":252,"caption":"A hybrid Android\/iOS application with Rails and Turbolinks 5"},{"@type":"BreadcrumbList","@id":"https:\/\/blog.dexem.com\/en\/dexem-tested-turbolinks-application\/#breadcrumb","itemListElement":[{"@type":"ListItem","position":1,"name":"Home","item":"https:\/\/blog.dexem.com\/en\/"},{"@type":"ListItem","position":2,"name":"A hybrid Android\/iOS application with Rails and Turbolinks 5"}]},{"@type":"WebSite","@id":"https:\/\/blog.dexem.com\/en\/#website","url":"https:\/\/blog.dexem.com\/en\/","name":"EN - Dexem Blog","description":"","publisher":{"@id":"https:\/\/blog.dexem.com\/en\/#organization"},"potentialAction":[{"@type":"SearchAction","target":{"@type":"EntryPoint","urlTemplate":"https:\/\/blog.dexem.com\/en\/?s={search_term_string}"},"query-input":{"@type":"PropertyValueSpecification","valueRequired":true,"valueName":"search_term_string"}}],"inLanguage":"en-US"},{"@type":"Organization","@id":"https:\/\/blog.dexem.com\/en\/#organization","name":"EN - Dexem Blog","url":"https:\/\/blog.dexem.com\/en\/","logo":{"@type":"ImageObject","inLanguage":"en-US","@id":"https:\/\/blog.dexem.com\/en\/#\/schema\/logo\/image\/","url":"https:\/\/blog.dexem.com\/en\/files\/2024\/06\/dexem-130x36-1.svg","contentUrl":"https:\/\/blog.dexem.com\/en\/files\/2024\/06\/dexem-130x36-1.svg","width":130,"height":36,"caption":"EN - Dexem Blog"},"image":{"@id":"https:\/\/blog.dexem.com\/en\/#\/schema\/logo\/image\/"}},{"@type":"Person","@id":"https:\/\/blog.dexem.com\/en\/#\/schema\/person\/0e642488202f4135f43165e1cd8ecfae","name":"Olivier Jaouen","image":{"@type":"ImageObject","inLanguage":"en-US","@id":"https:\/\/secure.gravatar.com\/avatar\/3e495d6abc8bafb416b520449319930a633b67bdc250ffaad8692afc580e0ca3?s=96&d=mm&r=g","url":"https:\/\/secure.gravatar.com\/avatar\/3e495d6abc8bafb416b520449319930a633b67bdc250ffaad8692afc580e0ca3?s=96&d=mm&r=g","contentUrl":"https:\/\/secure.gravatar.com\/avatar\/3e495d6abc8bafb416b520449319930a633b67bdc250ffaad8692afc580e0ca3?s=96&d=mm&r=g","caption":"Olivier Jaouen"},"description":"En tant que CMO, je couvre l'int\u00e9gralit\u00e9 du cycle de vie de nos clients. Je consacre mes journ\u00e9es aux interactions avec nos leads et clients, \u00e0 l'am\u00e9lioration de nos produits, \u00e0 la mise en place de nos strat\u00e9gies marketing, et \u00e0 l'accompagnement de mon \u00e9quipe.","sameAs":["https:\/\/www.dexem.com","\/in\/ojaouen\/","https:\/\/x.com\/ojaouen"],"url":"https:\/\/blog.dexem.com\/en\/author\/ojaouen\/"}]}},"_links":{"self":[{"href":"https:\/\/blog.dexem.com\/en\/wp-json\/wp\/v2\/posts\/332","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/blog.dexem.com\/en\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/blog.dexem.com\/en\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/blog.dexem.com\/en\/wp-json\/wp\/v2\/users\/3"}],"replies":[{"embeddable":true,"href":"https:\/\/blog.dexem.com\/en\/wp-json\/wp\/v2\/comments?post=332"}],"version-history":[{"count":0,"href":"https:\/\/blog.dexem.com\/en\/wp-json\/wp\/v2\/posts\/332\/revisions"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/blog.dexem.com\/en\/wp-json\/wp\/v2\/media\/1014"}],"wp:attachment":[{"href":"https:\/\/blog.dexem.com\/en\/wp-json\/wp\/v2\/media?parent=332"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/blog.dexem.com\/en\/wp-json\/wp\/v2\/categories?post=332"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/blog.dexem.com\/en\/wp-json\/wp\/v2\/tags?post=332"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}