{"id":1177,"date":"2016-07-06T16:14:03","date_gmt":"2016-07-06T16:14:03","guid":{"rendered":"https:\/\/ionic.io\/blog\/?p=1177"},"modified":"2017-04-03T19:50:50","modified_gmt":"2017-04-03T19:50:50","slug":"advanced-workflows-for-building-rock-solid-ionic-apps-part-2","status":"publish","type":"post","link":"https:\/\/ionic.io\/blog\/advanced-workflows-for-building-rock-solid-ionic-apps-part-2","title":{"rendered":"Advanced Workflows for Building Rock-Solid Ionic Apps, Part 2"},"content":{"rendered":"<p><em><a href=\"https:\/\/github.com\/gruppjo\">Jonathan Grupp<\/a> is a software engineer at <a href=\"http:\/\/www.mwaysolutions.com\/en\/enterprise-mobility-management\/\">M-Way Solutions<\/a>, the owner and maintainer of Generator-M-Ionic, and a frequent contributor to Ionic. He has written a three-part series on advanced workflows for building rock-solid Ionic apps. This is Part 2.<\/em><\/p>\n<p><img loading=\"lazy\" decoding=\"async\" width=\"1283\" height=\"721\" data-src=\"https:\/\/ionic.io\/blog\/wp-content\/uploads\/2016\/07\/blog-02.png\" alt=\"blog-02\" class=\"aligncenter size-full wp-image-1179 lazyload\" data-srcset=\"https:\/\/ionic.io\/blog\/wp-content\/uploads\/2016\/07\/blog-02.png 1283w, https:\/\/ionic.io\/blog\/wp-content\/uploads\/2016\/07\/blog-02-300x169.png 300w, https:\/\/ionic.io\/blog\/wp-content\/uploads\/2016\/07\/blog-02-768x432.png 768w, https:\/\/ionic.io\/blog\/wp-content\/uploads\/2016\/07\/blog-02-1024x575.png 1024w\" data-sizes=\"auto, (max-width: 1283px) 100vw, 1283px\" src=\"data:image\/gif;base64,R0lGODlhAQABAAAAACH5BAEKAAEALAAAAAABAAEAAAICTAEAOw==\" style=\"--smush-placeholder-width: 1283px; --smush-placeholder-aspect-ratio: 1283\/721;\" \/><noscript><img loading=\"lazy\" decoding=\"async\" width=\"1283\" height=\"721\" src=\"https:\/\/ionic.io\/blog\/wp-content\/uploads\/2016\/07\/blog-02.png\" alt=\"blog-02\" class=\"aligncenter size-full wp-image-1179\" srcset=\"https:\/\/ionic.io\/blog\/wp-content\/uploads\/2016\/07\/blog-02.png 1283w, https:\/\/ionic.io\/blog\/wp-content\/uploads\/2016\/07\/blog-02-300x169.png 300w, https:\/\/ionic.io\/blog\/wp-content\/uploads\/2016\/07\/blog-02-768x432.png 768w, https:\/\/ionic.io\/blog\/wp-content\/uploads\/2016\/07\/blog-02-1024x575.png 1024w\" sizes=\"auto, (max-width: 1283px) 100vw, 1283px\" \/><\/noscript><\/p>\n<p>In the second part of our series on developing enterprise-level Ionic apps, you&#8217;ll be learning about wonderful ingredients like testing, sub-generators, plugins, and ecosystem integration into the <a href=\"http:\/\/ionic.io\/\">Ionic Platform<\/a>. We are building on top of the project we created in <a href=\"https:\/\/ionic.io\/blog\/advanced-workflows-for-building-rock-solid-ionic-apps-part-1\/\">Part 1 of this series<\/a>. Time to climb the development mountain!<br \/>\n<!--more--><\/p>\n<h3>Quality assurance<\/h3>\n<p>You&#8217;ve set up your project, created your first commit, and are now ready to start coding. Whether you are coding alone or in a team, as a developer worried about professional development, you&#8217;ll want to take some measures to ensure the quality of your code. We&#8217;ve got you covered on this one!<\/p>\n<h4>Linting<\/h4>\n<p>Your Generator-M-Ionic project comes with established coding guidelines and workflows already baked in using <a href=\"http:\/\/eslint.org\/\">ESLint<\/a>. On every iteration of <code>gulp watch<\/code>, Gulp will check all your application JavaScript files for guideline violations.<\/p>\n<p><img loading=\"lazy\" decoding=\"async\" width=\"1412\" height=\"338\" data-src=\"https:\/\/ionic.io\/blog\/wp-content\/uploads\/2016\/07\/eslint.png\" alt=\"eslint\" class=\"aligncenter size-full wp-image-1180 lazyload\" data-srcset=\"https:\/\/ionic.io\/blog\/wp-content\/uploads\/2016\/07\/eslint.png 1412w, https:\/\/ionic.io\/blog\/wp-content\/uploads\/2016\/07\/eslint-300x72.png 300w, https:\/\/ionic.io\/blog\/wp-content\/uploads\/2016\/07\/eslint-768x184.png 768w, https:\/\/ionic.io\/blog\/wp-content\/uploads\/2016\/07\/eslint-1024x245.png 1024w\" data-sizes=\"auto, (max-width: 1412px) 100vw, 1412px\" src=\"data:image\/gif;base64,R0lGODlhAQABAAAAACH5BAEKAAEALAAAAAABAAEAAAICTAEAOw==\" style=\"--smush-placeholder-width: 1412px; --smush-placeholder-aspect-ratio: 1412\/338;\" \/><noscript><img loading=\"lazy\" decoding=\"async\" width=\"1412\" height=\"338\" src=\"https:\/\/ionic.io\/blog\/wp-content\/uploads\/2016\/07\/eslint.png\" alt=\"eslint\" class=\"aligncenter size-full wp-image-1180\" srcset=\"https:\/\/ionic.io\/blog\/wp-content\/uploads\/2016\/07\/eslint.png 1412w, https:\/\/ionic.io\/blog\/wp-content\/uploads\/2016\/07\/eslint-300x72.png 300w, https:\/\/ionic.io\/blog\/wp-content\/uploads\/2016\/07\/eslint-768x184.png 768w, https:\/\/ionic.io\/blog\/wp-content\/uploads\/2016\/07\/eslint-1024x245.png 1024w\" sizes=\"auto, (max-width: 1412px) 100vw, 1412px\" \/><\/noscript><\/p>\n<p>To additionally get linting notifications as you develop in your editor or to learn how to configure the default set of rules, check out our <a href=\"https:\/\/github.com\/mwaylabs\/generator-m-ionic\/blob\/master\/docs\/guides\/eslint.md\">ESLint Guide<\/a>. If you are working with JSON files in your <code>app\/<\/code> folder -for instance to handle translations- the generator&#8217;s linting will validate those too! This keeps your development trouble free.<\/p>\n<h4>Testing<\/h4>\n<p>Another area where you just don&#8217;t have to deal with the hassle of setting up and configuring everything yourself is unit testing with karma and end-to-end testing with protractor. Your sample app even comes with a ready-to-use test-suite that you can try out right now by running:<\/p>\n<pre><code class=\"sh\">gulp karma\n# and\ngulp protractor\n<\/code><\/pre>\n<p>The relevant files for the test setup are these:<\/p>\n<pre><code>test\/\n  \u2514\u2500\u2500 karma\/\n  \u2514\u2500\u2500 protractor\/\nkarma.conf.js\nprotractor.conf.js\n<\/code><\/pre>\n<p>Our <a href=\"https:\/\/github.com\/mwaylabs\/generator-m-ionic\/blob\/master\/docs\/guides\/testing.md\">Testing Guide<\/a> can help you get started with writing your own unit and end-to-end tests for your app. Once you have that mastered, the <a href=\"https:\/\/github.com\/mwaylabs\/generator-m-ionic\/tree\/master\/docs\/guides\/testing_workflow.md\">Husky Hooks Guide<\/a> explains how you can run linting and tests automatically before you <code>git commit<\/code> or <code>git push<\/code>.<\/p>\n<h3>Coding<\/h3>\n<p>Ok, ok, enough already! You want to finally start coding and develop your very own app? Say no more. You&#8217;ll probably want to:<br \/>\n&#8211; add your own Angular components using our <a href=\"https:\/\/github.com\/mwaylabs\/generator-m-ionic\/blob\/master\/docs\/guides\/sub_generators.md\">subgenerators<\/a><br \/>\n  &#8211; controllers, templates, directives, services, filters, constants, &#8230; or even whole modules<br \/>\n&#8211; add some <a href=\"http:\/\/sass-lang.com\/\">Sass<\/a> to spice up your app&#8217;s styling<br \/>\n&#8211; add <a href=\"http:\/\/ngcordova.com\/docs\/plugins\/\">Cordova Plugins<\/a> to use with <a href=\"http:\/\/ngcordova.com\/\">ngCordova<\/a> for that real app-feeling<br \/>\n&#8211; and maybe add some additional <a href=\"http:\/\/bower.io\/search\/\">bower packages<\/a> for special tasks<\/p>\n<p>We will go through each of these tasks briefly to give you a general idea of how things work. For more detailed explanations, visit our <a href=\"https:\/\/github.com\/mwaylabs\/generator-m-ionic\/\">Documentation<\/a>.<\/p>\n<h4>Adding Angular components<\/h4>\n<p>Our array of <a href=\"https:\/\/github.com\/mwaylabs\/generator-m-ionic\/blob\/master\/docs\/guides\/sub_generators.md\">subgenerators<\/a> allows you to create the most important Angular components very easily. If applicable, they also generate sample test files so you can start testing right away.<\/p>\n<p>For instance, we use the pair-subgenerator a lot. It creates a controller, with a test file and a template with the same name.<\/p>\n<pre><code class=\"sh\">yo m-ionic:pair phone\n<\/code><\/pre>\n<p><img loading=\"lazy\" decoding=\"async\" width=\"1400\" height=\"176\" data-src=\"https:\/\/ionic.io\/blog\/wp-content\/uploads\/2016\/07\/sub_generator.png\" alt=\"sub_generator\" class=\"aligncenter size-full wp-image-1182 lazyload\" data-srcset=\"https:\/\/ionic.io\/blog\/wp-content\/uploads\/2016\/07\/sub_generator.png 1400w, https:\/\/ionic.io\/blog\/wp-content\/uploads\/2016\/07\/sub_generator-300x38.png 300w, https:\/\/ionic.io\/blog\/wp-content\/uploads\/2016\/07\/sub_generator-768x97.png 768w, https:\/\/ionic.io\/blog\/wp-content\/uploads\/2016\/07\/sub_generator-1024x129.png 1024w\" data-sizes=\"auto, (max-width: 1400px) 100vw, 1400px\" src=\"data:image\/gif;base64,R0lGODlhAQABAAAAACH5BAEKAAEALAAAAAABAAEAAAICTAEAOw==\" style=\"--smush-placeholder-width: 1400px; --smush-placeholder-aspect-ratio: 1400\/176;\" \/><noscript><img loading=\"lazy\" decoding=\"async\" width=\"1400\" height=\"176\" src=\"https:\/\/ionic.io\/blog\/wp-content\/uploads\/2016\/07\/sub_generator.png\" alt=\"sub_generator\" class=\"aligncenter size-full wp-image-1182\" srcset=\"https:\/\/ionic.io\/blog\/wp-content\/uploads\/2016\/07\/sub_generator.png 1400w, https:\/\/ionic.io\/blog\/wp-content\/uploads\/2016\/07\/sub_generator-300x38.png 300w, https:\/\/ionic.io\/blog\/wp-content\/uploads\/2016\/07\/sub_generator-768x97.png 768w, https:\/\/ionic.io\/blog\/wp-content\/uploads\/2016\/07\/sub_generator-1024x129.png 1024w\" sizes=\"auto, (max-width: 1400px) 100vw, 1400px\" \/><\/noscript><\/p>\n<p>Now, we only need to add a state to the <code>main.js<\/code>:<\/p>\n<pre><code class=\"js\">.state(&#039;main.phone&#039;, {\n  url: &#039;\/phone&#039;,\n  views: {\n    &#039;tab-phone&#039;: {\n      templateUrl: &#039;main\/templates\/phone.html&#039;,\n      controller: &#039;PhoneCtrl as ctrl&#039;\n    }\n  }\n})\n<\/code><\/pre>\n<p>Then add a navigation item in our <code>tabs.html<\/code> file, which you&#8217;ll find in <code>app\/main\/templates\/<\/code>, to bring it all together:<\/p>\n<pre><code class=\"html\">&lt;!-- List Tab --&gt;\n&lt;ion-tab title=&quot;Phone&quot; icon-off=&quot;ion-ios-telephone-outline&quot;\n  icon-on=&quot;ion-ios-telephone&quot;\n  ui-sref=&quot;main.phone&quot;&gt;\n  &lt;ion-nav-view name=&quot;tab-phone&quot;&gt;&lt;\/ion-nav-view&gt;\n&lt;\/ion-tab&gt;\n<\/code><\/pre>\n<p>That&#8217;s it. A new navigation item, a new route, controller, test file, and template in about two minutes. Here&#8217;s the result:<\/p>\n<p><img loading=\"lazy\" decoding=\"async\" width=\"782\" height=\"1366\" data-src=\"https:\/\/ionic.io\/blog\/wp-content\/uploads\/2016\/07\/nav_phone.png\" alt=\"nav_phone\" class=\"aligncenter size-full wp-image-1183 lazyload\" data-srcset=\"https:\/\/ionic.io\/blog\/wp-content\/uploads\/2016\/07\/nav_phone.png 782w, https:\/\/ionic.io\/blog\/wp-content\/uploads\/2016\/07\/nav_phone-172x300.png 172w, https:\/\/ionic.io\/blog\/wp-content\/uploads\/2016\/07\/nav_phone-768x1342.png 768w, https:\/\/ionic.io\/blog\/wp-content\/uploads\/2016\/07\/nav_phone-586x1024.png 586w\" data-sizes=\"auto, (max-width: 782px) 100vw, 782px\" src=\"data:image\/gif;base64,R0lGODlhAQABAAAAACH5BAEKAAEALAAAAAABAAEAAAICTAEAOw==\" style=\"--smush-placeholder-width: 782px; --smush-placeholder-aspect-ratio: 782\/1366;\" \/><noscript><img loading=\"lazy\" decoding=\"async\" width=\"782\" height=\"1366\" src=\"https:\/\/ionic.io\/blog\/wp-content\/uploads\/2016\/07\/nav_phone.png\" alt=\"nav_phone\" class=\"aligncenter size-full wp-image-1183\" srcset=\"https:\/\/ionic.io\/blog\/wp-content\/uploads\/2016\/07\/nav_phone.png 782w, https:\/\/ionic.io\/blog\/wp-content\/uploads\/2016\/07\/nav_phone-172x300.png 172w, https:\/\/ionic.io\/blog\/wp-content\/uploads\/2016\/07\/nav_phone-768x1342.png 768w, https:\/\/ionic.io\/blog\/wp-content\/uploads\/2016\/07\/nav_phone-586x1024.png 586w\" sizes=\"auto, (max-width: 782px) 100vw, 782px\" \/><\/noscript><\/p>\n<h4>Adding Sass<\/h4>\n<p>This is an even easier task. Every module you generate comes with a default Sass file. For your main module, this would be <code>main.scss<\/code>, and it&#8217;s located in <code>app\/main\/styles\/<\/code>. Open it and add some Sass:<\/p>\n<pre><code class=\"scss\">ion-list {\n  ion-item {\n    color:red;\n  }\n}\n<\/code><\/pre>\n<p>Upon saving, your <code>gulp watch<\/code> task will automatically compile and inject the resulting CSS, even reload your browser. Not sassy enough? As your project grows larger, you may want to split your Sass into multiple files. Find out how in our <a href=\"https:\/\/github.com\/mwaylabs\/generator-m-ionic\/blob\/master\/docs\/guides\/sass_integration.md\">Sass integration Guide<\/a>.<\/p>\n<h4>Adding plugins<\/h4>\n<p>We&#8217;ve got to add some nice <a href=\"http:\/\/ngcordova.com\/docs\/plugins\/\">Cordova Plugins<\/a> to our app, to make it a real hybrid app. Let&#8217;s do it!<\/p>\n<p>Your project comes with a local installation of the latest version of the <a href=\"https:\/\/cordova.apache.org\/docs\/en\/latest\/guide\/cli\/index.html\">Cordova CLI<\/a>, which you can invoke through Gulp. We install it locally, so you don&#8217;t have to worry about which project you set up with which version. It&#8217;s always the one it got set up with. The syntax is almost exactly the same as using a global CLI installation. To install the Cordova camera plugin, run:<\/p>\n<pre><code class=\"sh\">gulp --cordova &quot;plugin add cordova-plugin-camera --save&quot;\n<\/code><\/pre>\n<p>You want to develop for Windows, as well? Install the appropriate <a href=\"https:\/\/cordova.apache.org\/docs\/en\/latest\/guide\/platforms\/win8\/index.html\">Cordova Platform requirements<\/a> and type:<\/p>\n<pre><code class=\"sh\">gulp --cordova &quot;platform add windows --save&quot;\n<\/code><\/pre>\n<p>Don&#8217;t forget to call <code>--save<\/code>, in order to persist new plugins and platforms in the <code>config.xml<\/code>! Our Development Introduction has a dedicated part on <a href=\"https:\/\/github.com\/mwaylabs\/generator-m-ionic\/blob\/master\/docs\/guides\/development_intro.md#using-the-cordova-cli\">using the Cordova CLI wrapper<\/a>.<\/p>\n<p>Now that we&#8217;ve installed a new plugin, we want to use it! <a href=\"http:\/\/ngcordova.com\/\">ngCordova<\/a> is declared as a dependency for every module you create using the generator, so you can just start using the plugins right away. Refer to the main module declaration in your <code>main.js<\/code> to see how it&#8217;s done. So in my <code>PhoneCtrl<\/code>, I&#8217;ll only have to inject the <code>$cordovaCamera<\/code> <a href=\"http:\/\/ngcordova.com\/docs\/plugins\/camera\/\">service<\/a> in order to access the plugin:<\/p>\n<pre><code class=\"js\">angular.module(&#039;main&#039;)\n.controller(&#039;PhoneCtrl&#039;, function ($cordovaCamera) {\n\n  var options = {\n    destinationType: Camera.DestinationType.FILE_URI,\n    sourceType: Camera.PictureSourceType.CAMERA,\n  };\n\n  $cordovaCamera.getPicture(options)\n  .then(function(imageURI) {\n    console.log(imageURI);\n  });\n});\n<\/code><\/pre>\n<p>Adding new plugins has never been so simple!<\/p>\n<p>In some cases, you need to extend your ESLint configuration because some plugins expose JavaScript globals that you might want to use (like <code>Camera<\/code> in the example above). Or you might prefer to access your plugins through the <code>cordova<\/code> global, because ngCordova is not always up to date with the latest plugin versions or may not support the plugin you want to use. In order to use those globals without ESLint complaining, augment the <code>globals<\/code> section of your <code>app\/.eslintrc<\/code>:<\/p>\n<pre><code class=\"js\">\/\/..\n&quot;globals&quot;: {\n  &quot;angular&quot;: true,\n  &quot;ionic&quot;: true,\n  &quot;localforage&quot;: true,\n  \/\/ add those\n  &quot;cordova&quot;: true,\n  &quot;Camera&quot;: true\n},\n\/\/..\n<\/code><\/pre>\n<h4>Bower packages<\/h4>\n<p>And last but not least, you&#8217;ll probably want some more <a href=\"http:\/\/bower.io\/search\/\">bower packages<\/a> that go with your app. Maybe your app needs to support different languages. You can use <a href=\"https:\/\/github.com\/angular-translate\/angular-translate\">angular-translate<\/a> for that. Install it by running:<\/p>\n<pre><code class=\"sh\">bower install angular-translate --save\n<\/code><\/pre>\n<p>The <code>--save<\/code> flag will persist that package in the <code>bower.json<\/code>. Sometimes, for all changes to take effect, it is necessary to restart <code>gulp watch<\/code>. Then, the only thing left to do is to mark the module as a dependency in your <code>main.js<\/code> module declaration:<\/p>\n<pre><code class=\"js\">&#039;use strict&#039;;\nangular.module(&#039;main&#039;, [\n  &#039;ionic&#039;,\n  &#039;ngCordova&#039;,\n  &#039;ui.router&#039;,\n  \/\/ add this one\n  &#039;pascalprecht.translate&#039;\n])\n<\/code><\/pre>\n<p>Translations? You&#8217;ve got them now! Refer to the <a href=\"https:\/\/angular-translate.github.io\/\">documentation<\/a> to learn more.<\/p>\n<h3>Browser or device?<\/h3>\n<p>Up until now, we&#8217;ve only seen our app in the browser using <code>gulp watch<\/code>. But at least when you&#8217;re working with plugins, you may want to test your app on a device or emulator. If you have your system correctly set up according to the <a href=\"http:\/\/cordova.apache.org\/docs\/en\/latest\/#develop-for-platforms\">Cordova Platform Guides<\/a>, this should be easy:<\/p>\n<ol>\n<li>connect your device to your machine<\/li>\n<li>make sure they&#8217;re both on the same network<\/li>\n<li>start the livereload command and keep it running:<\/li>\n<\/ol>\n<pre><code class=\"sh\"># run on a connected device with livereload\ngulp --livereload &quot;run ios&quot;\n# run on an emulator with livereload\ngulp --livereload &quot;run --emulate android&quot;\n<\/code><\/pre>\n<p>The best part about using livereload is that you can make changes to your code and see the changes immediately on the device. If you make changes to the Cordova files (<code>config.xml<\/code>, Platforms, Plugins) you&#8217;ll have to run the command again.<\/p>\n<p>Your app is ready to be tested on your device!<\/p>\n<p><img loading=\"lazy\" decoding=\"async\" width=\"3000\" height=\"2000\" data-src=\"https:\/\/ionic.io\/blog\/wp-content\/uploads\/2016\/07\/device.jpg\" alt=\"device\" class=\"aligncenter size-full wp-image-1184 lazyload\" data-srcset=\"https:\/\/ionic.io\/blog\/wp-content\/uploads\/2016\/07\/device.jpg 3000w, https:\/\/ionic.io\/blog\/wp-content\/uploads\/2016\/07\/device-300x200.jpg 300w, https:\/\/ionic.io\/blog\/wp-content\/uploads\/2016\/07\/device-768x512.jpg 768w, https:\/\/ionic.io\/blog\/wp-content\/uploads\/2016\/07\/device-1024x683.jpg 1024w\" data-sizes=\"auto, (max-width: 3000px) 100vw, 3000px\" src=\"data:image\/gif;base64,R0lGODlhAQABAAAAACH5BAEKAAEALAAAAAABAAEAAAICTAEAOw==\" style=\"--smush-placeholder-width: 3000px; --smush-placeholder-aspect-ratio: 3000\/2000;\" \/><noscript><img loading=\"lazy\" decoding=\"async\" width=\"3000\" height=\"2000\" src=\"https:\/\/ionic.io\/blog\/wp-content\/uploads\/2016\/07\/device.jpg\" alt=\"device\" class=\"aligncenter size-full wp-image-1184\" srcset=\"https:\/\/ionic.io\/blog\/wp-content\/uploads\/2016\/07\/device.jpg 3000w, https:\/\/ionic.io\/blog\/wp-content\/uploads\/2016\/07\/device-300x200.jpg 300w, https:\/\/ionic.io\/blog\/wp-content\/uploads\/2016\/07\/device-768x512.jpg 768w, https:\/\/ionic.io\/blog\/wp-content\/uploads\/2016\/07\/device-1024x683.jpg 1024w\" sizes=\"auto, (max-width: 3000px) 100vw, 3000px\" \/><\/noscript><\/p>\n<p>If you don&#8217;t want to rely on your development machine to keep the livereload command running, you can run a full build of your app, which is then pushed onto your device.<\/p>\n<pre><code class=\"sh\">gulp --cordova &quot;run ios&quot;\n# or\ngulp --cordova &quot;run android&quot;\n<\/code><\/pre>\n<p>Two things happen when you run this command:<\/p>\n<ol>\n<li>Gulp will build your app using <code>gulp build<\/code>\n<ul>\n<li>this takes care of all the JavaScript, HTML and CSS and puts it into the <code>www\/<\/code> folder<\/li>\n<\/ul>\n<\/li>\n<li>Gulp will call Cordova with the supplied attributes and\n<ul>\n<li>takes the contents of the <code>www\/<\/code> folder and builds a Cordova app from it<\/li>\n<li>the app is then pushed onto your device<\/li>\n<\/ul>\n<\/li>\n<\/ol>\n<p>The implicit run of <code>gulp build<\/code>, for which Cordova commands it will run as well as build options like minification, are explained in our <a href=\"https:\/\/github.com\/mwaylabs\/generator-m-ionic\/blob\/master\/docs\/guides\/development_intro.md#cordova-build-run-emulate--under-the-hood\">Development Introduction<\/a> in more detail.<\/p>\n<h4>gulp watch-build<\/h4>\n<p>Usually, <code>gulp build<\/code> will build your app without any problems, and everything should work just as if you run <code>gulp watch<\/code>. However, sometimes it will be necessary to debug the web app part of your build. Since this is a little cumbersome to do when the app&#8217;s already on your device, there&#8217;s a nice intermediate step:<\/p>\n<pre><code class=\"sh\">gulp watch-build\n<\/code><\/pre>\n<p>This is just like <code>gulp watch<\/code>, but only for the <code>www\/<\/code> folder. It builds your app into that folder and then opens a browser window showing the built version of your app. This allows you do quickly find out why your app doesn&#8217;t build properly. Adding the <code>--no-build<\/code> flag enables you to watch the current version in the <code>www\/<\/code> folder. Of course, this command also works with <code>--no-open<\/code> and all the other flags that work with <code>gulp watch<\/code>, as well.<\/p>\n<h3>Ecosystems<\/h3>\n<p>There are some things you just don&#8217;t want to build yourself every time, like push services, user management, or other backend services. Luckily, the <a href=\"http:\/\/ionic.io\">Ionic Platform<\/a> can be integrated into your project using the <a href=\"http:\/\/ionicframework.com\/docs\/cli\/\">Ionic CLI<\/a>, as described in the <a href=\"https:\/\/github.com\/mwaylabs\/generator-m-ionic\/blob\/master\/docs\/ecosystems\/ionic_platform.md\">Ionic Platform Integration Guide<\/a>.<\/p>\n<h3>Congratulations!<\/h3>\n<p>You&#8217;ve learned how to season your app with spices like sub-generators, Sass, plugins, ecosystems, and bower packages. In Part 3 of this series, you&#8217;ll take your development skills to out-of-this-world levels by owning environments, proxies, and build tools; and handling app icons, splash screens, continuous integration and build variables.<\/p>\n<h3>Get in touch<\/h3>\n<p>Feedback, ideas, comments regarding this blog post or any of the features discussed here are very welcome in either the comments section below, at our <a href=\"https:\/\/github.com\/mwaylabs\/generator-m-ionic\">Generator-M-Ionic&#8217;s Github repository<\/a> or the <a href=\"https:\/\/gitter.im\/mwaylabs\/generator-m-ionic\">Generator-M-Ionic Gitter Chat<\/a>.<\/p>\n<h3>Credits<\/h3>\n<p>Author: <a href=\"https:\/\/github.com\/gruppjo\">Jonathan Grupp<\/a><br \/>\nHeadline illustrations: <a href=\"http:\/\/www.art-noir.net\/\">Christian Kahl<\/a><br \/>\nSpecial thanks to Volker Hahn, Mathias Maier, and Tim Lancina<\/p>\n","protected":false},"excerpt":{"rendered":"<p>Jonathan Grupp is a software engineer at M-Way Solutions, the owner and maintainer of Generator-M-Ionic, and a frequent contributor to Ionic. He has written a three-part series on advanced workflows for building rock-solid Ionic apps. This is Part 2. In the second part of our series on developing enterprise-level Ionic apps, you&#8217;ll be learning about [&hellip;]<\/p>\n","protected":false},"author":39,"featured_media":0,"comment_status":"open","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":{"_acf_changed":false,"publish_to_discourse":"","publish_post_category":"","wpdc_auto_publish_overridden":"","wpdc_topic_tags":"","wpdc_pin_topic":"","wpdc_pin_until":"","discourse_post_id":"","discourse_permalink":"","wpdc_publishing_response":"","wpdc_publishing_error":"","_jetpack_memberships_contains_paid_content":false,"footnotes":""},"categories":[1],"tags":[6,3],"class_list":["post-1177","post","type-post","status-publish","format-standard","hentry","category-all","tag-cloud","tag-ionic"],"acf":[],"yoast_head":"<!-- This site is optimized with the Yoast SEO Premium plugin v23.0 (Yoast SEO v23.0) - https:\/\/yoast.com\/wordpress\/plugins\/seo\/ -->\n<title>Advanced Workflows for Building Rock-Solid Ionic Apps, Part 2 - Ionic Blog<\/title>\n<meta name=\"robots\" content=\"index, follow, max-snippet:-1, max-image-preview:large, max-video-preview:-1\" \/>\n<link rel=\"canonical\" href=\"https:\/\/ionic.io\/blog\/advanced-workflows-for-building-rock-solid-ionic-apps-part-2\" \/>\n<meta property=\"og:locale\" content=\"en_US\" \/>\n<meta property=\"og:type\" content=\"article\" \/>\n<meta property=\"og:title\" content=\"Advanced Workflows for Building Rock-Solid Ionic Apps, Part 2\" \/>\n<meta property=\"og:description\" content=\"Jonathan Grupp is a software engineer at M-Way Solutions, the owner and maintainer of Generator-M-Ionic, and a frequent contributor to Ionic. He has written a three-part series on advanced workflows for building rock-solid Ionic apps. This is Part 2. In the second part of our series on developing enterprise-level Ionic apps, you&#8217;ll be learning about [&hellip;]\" \/>\n<meta property=\"og:url\" content=\"https:\/\/ionic.io\/blog\/advanced-workflows-for-building-rock-solid-ionic-apps-part-2\" \/>\n<meta property=\"og:site_name\" content=\"Ionic Blog\" \/>\n<meta property=\"article:published_time\" content=\"2016-07-06T16:14:03+00:00\" \/>\n<meta property=\"article:modified_time\" content=\"2017-04-03T19:50:50+00:00\" \/>\n<meta property=\"og:image\" content=\"https:\/\/ionic.io\/blog\/wp-content\/uploads\/2016\/07\/blog-02.png\" \/>\n<meta name=\"author\" content=\"Jonathan Grupp\" \/>\n<meta name=\"twitter:card\" content=\"summary_large_image\" \/>\n<meta name=\"twitter:creator\" content=\"@ionicframework\" \/>\n<meta name=\"twitter:site\" content=\"@ionicframework\" \/>\n<meta name=\"twitter:label1\" content=\"Written by\" \/>\n\t<meta name=\"twitter:data1\" content=\"Jonathan Grupp\" \/>\n\t<meta name=\"twitter:label2\" content=\"Est. reading time\" \/>\n\t<meta name=\"twitter:data2\" content=\"9 minutes\" \/>\n<script type=\"application\/ld+json\" class=\"yoast-schema-graph\">{\"@context\":\"https:\/\/schema.org\",\"@graph\":[{\"@type\":\"Article\",\"@id\":\"https:\/\/ionic.io\/blog\/advanced-workflows-for-building-rock-solid-ionic-apps-part-2#article\",\"isPartOf\":{\"@id\":\"https:\/\/ionic.io\/blog\/advanced-workflows-for-building-rock-solid-ionic-apps-part-2\"},\"author\":{\"name\":\"Jonathan Grupp\",\"@id\":\"https:\/\/ionic.io\/blog\/#\/schema\/person\/8ac19c487b42e3cf084a53c32bbdca65\"},\"headline\":\"Advanced Workflows for Building Rock-Solid Ionic Apps, Part 2\",\"datePublished\":\"2016-07-06T16:14:03+00:00\",\"dateModified\":\"2017-04-03T19:50:50+00:00\",\"mainEntityOfPage\":{\"@id\":\"https:\/\/ionic.io\/blog\/advanced-workflows-for-building-rock-solid-ionic-apps-part-2\"},\"wordCount\":1540,\"commentCount\":5,\"publisher\":{\"@id\":\"https:\/\/ionic.io\/blog\/#organization\"},\"image\":{\"@id\":\"https:\/\/ionic.io\/blog\/advanced-workflows-for-building-rock-solid-ionic-apps-part-2#primaryimage\"},\"thumbnailUrl\":\"https:\/\/ionic.io\/blog\/wp-content\/uploads\/2016\/07\/blog-02.png\",\"keywords\":[\"Cloud\",\"Ionic\"],\"articleSection\":[\"All\"],\"inLanguage\":\"en-US\",\"potentialAction\":[{\"@type\":\"CommentAction\",\"name\":\"Comment\",\"target\":[\"https:\/\/ionic.io\/blog\/advanced-workflows-for-building-rock-solid-ionic-apps-part-2#respond\"]}]},{\"@type\":\"WebPage\",\"@id\":\"https:\/\/ionic.io\/blog\/advanced-workflows-for-building-rock-solid-ionic-apps-part-2\",\"url\":\"https:\/\/ionic.io\/blog\/advanced-workflows-for-building-rock-solid-ionic-apps-part-2\",\"name\":\"Advanced Workflows for Building Rock-Solid Ionic Apps, Part 2 - Ionic Blog\",\"isPartOf\":{\"@id\":\"https:\/\/ionic.io\/blog\/#website\"},\"primaryImageOfPage\":{\"@id\":\"https:\/\/ionic.io\/blog\/advanced-workflows-for-building-rock-solid-ionic-apps-part-2#primaryimage\"},\"image\":{\"@id\":\"https:\/\/ionic.io\/blog\/advanced-workflows-for-building-rock-solid-ionic-apps-part-2#primaryimage\"},\"thumbnailUrl\":\"https:\/\/ionic.io\/blog\/wp-content\/uploads\/2016\/07\/blog-02.png\",\"datePublished\":\"2016-07-06T16:14:03+00:00\",\"dateModified\":\"2017-04-03T19:50:50+00:00\",\"breadcrumb\":{\"@id\":\"https:\/\/ionic.io\/blog\/advanced-workflows-for-building-rock-solid-ionic-apps-part-2#breadcrumb\"},\"inLanguage\":\"en-US\",\"potentialAction\":[{\"@type\":\"ReadAction\",\"target\":[\"https:\/\/ionic.io\/blog\/advanced-workflows-for-building-rock-solid-ionic-apps-part-2\"]}]},{\"@type\":\"ImageObject\",\"inLanguage\":\"en-US\",\"@id\":\"https:\/\/ionic.io\/blog\/advanced-workflows-for-building-rock-solid-ionic-apps-part-2#primaryimage\",\"url\":\"https:\/\/ionic.io\/blog\/wp-content\/uploads\/2016\/07\/blog-02.png\",\"contentUrl\":\"https:\/\/ionic.io\/blog\/wp-content\/uploads\/2016\/07\/blog-02.png\",\"width\":1283,\"height\":721},{\"@type\":\"BreadcrumbList\",\"@id\":\"https:\/\/ionic.io\/blog\/advanced-workflows-for-building-rock-solid-ionic-apps-part-2#breadcrumb\",\"itemListElement\":[{\"@type\":\"ListItem\",\"position\":1,\"name\":\"Home\",\"item\":\"https:\/\/ionic.io\/blog\"},{\"@type\":\"ListItem\",\"position\":2,\"name\":\"Advanced Workflows for Building Rock-Solid Ionic Apps, Part 2\"}]},{\"@type\":\"WebSite\",\"@id\":\"https:\/\/ionic.io\/blog\/#website\",\"url\":\"https:\/\/ionic.io\/blog\/\",\"name\":\"ionic.io\/blog\",\"description\":\"Build amazing native and progressive web apps with the web\",\"publisher\":{\"@id\":\"https:\/\/ionic.io\/blog\/#organization\"},\"potentialAction\":[{\"@type\":\"SearchAction\",\"target\":{\"@type\":\"EntryPoint\",\"urlTemplate\":\"https:\/\/ionic.io\/blog\/?s={search_term_string}\"},\"query-input\":\"required name=search_term_string\"}],\"inLanguage\":\"en-US\"},{\"@type\":\"Organization\",\"@id\":\"https:\/\/ionic.io\/blog\/#organization\",\"name\":\"Ionic\",\"url\":\"https:\/\/ionic.io\/blog\/\",\"logo\":{\"@type\":\"ImageObject\",\"inLanguage\":\"en-US\",\"@id\":\"https:\/\/ionic.io\/blog\/#\/schema\/logo\/image\/\",\"url\":\"https:\/\/ionic.io\/blog\/wp-content\/uploads\/2020\/10\/white-on-color.png\",\"contentUrl\":\"https:\/\/ionic.io\/blog\/wp-content\/uploads\/2020\/10\/white-on-color.png\",\"width\":1920,\"height\":854,\"caption\":\"Ionic\"},\"image\":{\"@id\":\"https:\/\/ionic.io\/blog\/#\/schema\/logo\/image\/\"},\"sameAs\":[\"https:\/\/x.com\/ionicframework\"]},{\"@type\":\"Person\",\"@id\":\"https:\/\/ionic.io\/blog\/#\/schema\/person\/8ac19c487b42e3cf084a53c32bbdca65\",\"name\":\"Jonathan Grupp\",\"image\":{\"@type\":\"ImageObject\",\"inLanguage\":\"en-US\",\"@id\":\"https:\/\/ionic.io\/blog\/#\/schema\/person\/image\/\",\"url\":\"https:\/\/ionic.io\/blog\/wp-content\/uploads\/2016\/07\/IMG_7259_small-150x150.jpg\",\"contentUrl\":\"https:\/\/ionic.io\/blog\/wp-content\/uploads\/2016\/07\/IMG_7259_small-150x150.jpg\",\"caption\":\"Jonathan Grupp\"},\"url\":\"https:\/\/ionic.io\/blog\/author\/jon\"}]}<\/script>\n<!-- \/ Yoast SEO Premium plugin. -->","yoast_head_json":{"title":"Advanced Workflows for Building Rock-Solid Ionic Apps, Part 2 - Ionic Blog","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:\/\/ionic.io\/blog\/advanced-workflows-for-building-rock-solid-ionic-apps-part-2","og_locale":"en_US","og_type":"article","og_title":"Advanced Workflows for Building Rock-Solid Ionic Apps, Part 2","og_description":"Jonathan Grupp is a software engineer at M-Way Solutions, the owner and maintainer of Generator-M-Ionic, and a frequent contributor to Ionic. He has written a three-part series on advanced workflows for building rock-solid Ionic apps. This is Part 2. In the second part of our series on developing enterprise-level Ionic apps, you&#8217;ll be learning about [&hellip;]","og_url":"https:\/\/ionic.io\/blog\/advanced-workflows-for-building-rock-solid-ionic-apps-part-2","og_site_name":"Ionic Blog","article_published_time":"2016-07-06T16:14:03+00:00","article_modified_time":"2017-04-03T19:50:50+00:00","og_image":[{"url":"https:\/\/ionic.io\/blog\/wp-content\/uploads\/2016\/07\/blog-02.png"}],"author":"Jonathan Grupp","twitter_card":"summary_large_image","twitter_creator":"@ionicframework","twitter_site":"@ionicframework","twitter_misc":{"Written by":"Jonathan Grupp","Est. reading time":"9 minutes"},"schema":{"@context":"https:\/\/schema.org","@graph":[{"@type":"Article","@id":"https:\/\/ionic.io\/blog\/advanced-workflows-for-building-rock-solid-ionic-apps-part-2#article","isPartOf":{"@id":"https:\/\/ionic.io\/blog\/advanced-workflows-for-building-rock-solid-ionic-apps-part-2"},"author":{"name":"Jonathan Grupp","@id":"https:\/\/ionic.io\/blog\/#\/schema\/person\/8ac19c487b42e3cf084a53c32bbdca65"},"headline":"Advanced Workflows for Building Rock-Solid Ionic Apps, Part 2","datePublished":"2016-07-06T16:14:03+00:00","dateModified":"2017-04-03T19:50:50+00:00","mainEntityOfPage":{"@id":"https:\/\/ionic.io\/blog\/advanced-workflows-for-building-rock-solid-ionic-apps-part-2"},"wordCount":1540,"commentCount":5,"publisher":{"@id":"https:\/\/ionic.io\/blog\/#organization"},"image":{"@id":"https:\/\/ionic.io\/blog\/advanced-workflows-for-building-rock-solid-ionic-apps-part-2#primaryimage"},"thumbnailUrl":"https:\/\/ionic.io\/blog\/wp-content\/uploads\/2016\/07\/blog-02.png","keywords":["Cloud","Ionic"],"articleSection":["All"],"inLanguage":"en-US","potentialAction":[{"@type":"CommentAction","name":"Comment","target":["https:\/\/ionic.io\/blog\/advanced-workflows-for-building-rock-solid-ionic-apps-part-2#respond"]}]},{"@type":"WebPage","@id":"https:\/\/ionic.io\/blog\/advanced-workflows-for-building-rock-solid-ionic-apps-part-2","url":"https:\/\/ionic.io\/blog\/advanced-workflows-for-building-rock-solid-ionic-apps-part-2","name":"Advanced Workflows for Building Rock-Solid Ionic Apps, Part 2 - Ionic Blog","isPartOf":{"@id":"https:\/\/ionic.io\/blog\/#website"},"primaryImageOfPage":{"@id":"https:\/\/ionic.io\/blog\/advanced-workflows-for-building-rock-solid-ionic-apps-part-2#primaryimage"},"image":{"@id":"https:\/\/ionic.io\/blog\/advanced-workflows-for-building-rock-solid-ionic-apps-part-2#primaryimage"},"thumbnailUrl":"https:\/\/ionic.io\/blog\/wp-content\/uploads\/2016\/07\/blog-02.png","datePublished":"2016-07-06T16:14:03+00:00","dateModified":"2017-04-03T19:50:50+00:00","breadcrumb":{"@id":"https:\/\/ionic.io\/blog\/advanced-workflows-for-building-rock-solid-ionic-apps-part-2#breadcrumb"},"inLanguage":"en-US","potentialAction":[{"@type":"ReadAction","target":["https:\/\/ionic.io\/blog\/advanced-workflows-for-building-rock-solid-ionic-apps-part-2"]}]},{"@type":"ImageObject","inLanguage":"en-US","@id":"https:\/\/ionic.io\/blog\/advanced-workflows-for-building-rock-solid-ionic-apps-part-2#primaryimage","url":"https:\/\/ionic.io\/blog\/wp-content\/uploads\/2016\/07\/blog-02.png","contentUrl":"https:\/\/ionic.io\/blog\/wp-content\/uploads\/2016\/07\/blog-02.png","width":1283,"height":721},{"@type":"BreadcrumbList","@id":"https:\/\/ionic.io\/blog\/advanced-workflows-for-building-rock-solid-ionic-apps-part-2#breadcrumb","itemListElement":[{"@type":"ListItem","position":1,"name":"Home","item":"https:\/\/ionic.io\/blog"},{"@type":"ListItem","position":2,"name":"Advanced Workflows for Building Rock-Solid Ionic Apps, Part 2"}]},{"@type":"WebSite","@id":"https:\/\/ionic.io\/blog\/#website","url":"https:\/\/ionic.io\/blog\/","name":"ionic.io\/blog","description":"Build amazing native and progressive web apps with the web","publisher":{"@id":"https:\/\/ionic.io\/blog\/#organization"},"potentialAction":[{"@type":"SearchAction","target":{"@type":"EntryPoint","urlTemplate":"https:\/\/ionic.io\/blog\/?s={search_term_string}"},"query-input":"required name=search_term_string"}],"inLanguage":"en-US"},{"@type":"Organization","@id":"https:\/\/ionic.io\/blog\/#organization","name":"Ionic","url":"https:\/\/ionic.io\/blog\/","logo":{"@type":"ImageObject","inLanguage":"en-US","@id":"https:\/\/ionic.io\/blog\/#\/schema\/logo\/image\/","url":"https:\/\/ionic.io\/blog\/wp-content\/uploads\/2020\/10\/white-on-color.png","contentUrl":"https:\/\/ionic.io\/blog\/wp-content\/uploads\/2020\/10\/white-on-color.png","width":1920,"height":854,"caption":"Ionic"},"image":{"@id":"https:\/\/ionic.io\/blog\/#\/schema\/logo\/image\/"},"sameAs":["https:\/\/x.com\/ionicframework"]},{"@type":"Person","@id":"https:\/\/ionic.io\/blog\/#\/schema\/person\/8ac19c487b42e3cf084a53c32bbdca65","name":"Jonathan Grupp","image":{"@type":"ImageObject","inLanguage":"en-US","@id":"https:\/\/ionic.io\/blog\/#\/schema\/person\/image\/","url":"https:\/\/ionic.io\/blog\/wp-content\/uploads\/2016\/07\/IMG_7259_small-150x150.jpg","contentUrl":"https:\/\/ionic.io\/blog\/wp-content\/uploads\/2016\/07\/IMG_7259_small-150x150.jpg","caption":"Jonathan Grupp"},"url":"https:\/\/ionic.io\/blog\/author\/jon"}]}},"jetpack_sharing_enabled":true,"jetpack_featured_media_url":"","_links":{"self":[{"href":"https:\/\/ionic.io\/blog\/wp-json\/wp\/v2\/posts\/1177","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/ionic.io\/blog\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/ionic.io\/blog\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/ionic.io\/blog\/wp-json\/wp\/v2\/users\/39"}],"replies":[{"embeddable":true,"href":"https:\/\/ionic.io\/blog\/wp-json\/wp\/v2\/comments?post=1177"}],"version-history":[{"count":0,"href":"https:\/\/ionic.io\/blog\/wp-json\/wp\/v2\/posts\/1177\/revisions"}],"wp:attachment":[{"href":"https:\/\/ionic.io\/blog\/wp-json\/wp\/v2\/media?parent=1177"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/ionic.io\/blog\/wp-json\/wp\/v2\/categories?post=1177"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/ionic.io\/blog\/wp-json\/wp\/v2\/tags?post=1177"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}